Unverified Commit b8b08a42 by Melvin Klimke Committed by GitHub

Speed up communication between Ghidra and cwe_checker (#145)

... also disabled CWE 78 check for standard runs until performance problems of the check are solved.
parent ca67076f
......@@ -8,4 +8,5 @@ edition = "2018"
structopt = "0.3"
cwe_checker_rs = { path = "../cwe_checker_rs" }
serde_json = "1.0"
directories = "3.0"
\ No newline at end of file
directories = "3.0"
nix = "0.19.1"
\ No newline at end of file
......@@ -3,9 +3,11 @@ use cwe_checker_rs::utils::log::print_all_messages;
use cwe_checker_rs::utils::{get_ghidra_plugin_path, read_config_file};
use cwe_checker_rs::AnalysisResults;
use cwe_checker_rs::{intermediate_representation::Project, utils::log::LogMessage};
use nix::{sys::stat, unistd};
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::thread;
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
......@@ -124,6 +126,13 @@ fn run_with_ghidra(args: CmdlineArgs) {
// Filter the modules to be executed if the `--partial` parameter is set.
if let Some(ref partial_module_list) = args.partial {
filter_modules_for_partial_run(&mut modules, partial_module_list);
} else {
// TODO: CWE78 is disabled on a standard run for now,
// because it uses up huge amounts of RAM and computation time on some binaries.
modules = modules
.into_iter()
.filter(|module| module.name != "CWE78")
.collect();
}
let binary_file_path = PathBuf::from(args.binary.unwrap());
......@@ -241,49 +250,66 @@ fn get_project_from_ghidra(file_path: &Path, binary: &[u8], quiet_flag: bool) ->
let filename = file_path
.file_name()
.expect("Invalid file name")
.to_string_lossy();
let output_filename = format!("{}_{}.json", filename, timestamp_suffix);
let output_path = tmp_folder.join(output_filename);
.to_string_lossy()
.to_string();
let ghidra_plugin_path = get_ghidra_plugin_path("p_code_extractor");
// Execute Ghidra
let output = match Command::new(&headless_path)
.arg(&tmp_folder) // The folder where temporary files should be stored
.arg(format!("PcodeExtractor_{}_{}", filename, timestamp_suffix)) // The name of the temporary Ghidra Project.
.arg("-import") // Import a file into the Ghidra project
.arg(file_path) // File import path
.arg("-postScript") // Execute a script after standard analysis by Ghidra finished
.arg(ghidra_plugin_path.join("PcodeExtractor.java")) // Path to the PcodeExtractor.java
.arg(&output_path) // Output file path
.arg("-scriptPath") // Add a folder containing additional script files to the Ghidra script file search paths
.arg(ghidra_plugin_path) // Path to the folder containing the PcodeExtractor.java (so that the other java files can be found.)
.arg("-deleteProject") // Delete the temporary project after the script finished
.arg("-analysisTimeoutPerFile") // Set a timeout for how long the standard analysis can run before getting aborted
.arg("3600") // Timeout of one hour (=3600 seconds) // TODO: The post-script can detect that the timeout fired and react accordingly.
.output() // Execute the command and catch its output.
{
Ok(output) => output,
Err(err) => {
eprintln!("Error: Ghidra could not be executed:\n{}", err);
std::process::exit(101);
}
};
if !output.status.success() {
match output.status.code() {
Some(code) => {
eprintln!("{}", String::from_utf8(output.stdout).unwrap());
eprintln!("{}", String::from_utf8(output.stderr).unwrap());
eprintln!("Execution of Ghidra plugin failed with exit code {}", code);
// Create a unique name for the pipe
let fifo_path = tmp_folder.join(format!("pcode_{}.pipe", timestamp_suffix));
// Create a new fifo and give read and write rights to the owner
if let Err(err) = unistd::mkfifo(&fifo_path, stat::Mode::from_bits(0o600).unwrap()) {
eprintln!("Error creating FIFO pipe: {}", err);
std::process::exit(101);
}
let thread_fifo_path = fifo_path.clone();
let thread_file_path = file_path.to_path_buf();
let thread_tmp_folder = tmp_folder.to_path_buf();
// Execute Ghidra in a new thread and return a Join Handle, so that the thread is only joined
// after the output has been read into the cwe_checker
let ghidra_subprocess = thread::spawn(move || {
let output = match Command::new(&headless_path)
.arg(&thread_tmp_folder) // The folder where temporary files should be stored
.arg(format!("PcodeExtractor_{}_{}", filename, timestamp_suffix)) // The name of the temporary Ghidra Project.
.arg("-import") // Import a file into the Ghidra project
.arg(thread_file_path) // File import path
.arg("-postScript") // Execute a script after standard analysis by Ghidra finished
.arg(ghidra_plugin_path.join("PcodeExtractor.java")) // Path to the PcodeExtractor.java
.arg(thread_fifo_path) // The path to the named pipe (fifo)
.arg("-scriptPath") // Add a folder containing additional script files to the Ghidra script file search paths
.arg(ghidra_plugin_path) // Path to the folder containing the PcodeExtractor.java (so that the other java files can be found.)
.arg("-deleteProject") // Delete the temporary project after the script finished
.arg("-analysisTimeoutPerFile") // Set a timeout for how long the standard analysis can run before getting aborted
.arg("3600") // Timeout of one hour (=3600 seconds) // TODO: The post-script can detect that the timeout fired and react accordingly.
.output() // Execute the command and catch its output.
{
Ok(output) => output,
Err(err) => {
eprintln!("Error: Ghidra could not be executed:\n{}", err);
std::process::exit(101);
}
None => {
eprintln!("Execution of Ghidra plugin failed: Process was terminated.");
std::process::exit(101);
};
if !output.status.success() {
match output.status.code() {
Some(code) => {
eprintln!("{}", String::from_utf8(output.stdout).unwrap());
eprintln!("{}", String::from_utf8(output.stderr).unwrap());
eprintln!("Execution of Ghidra plugin failed with exit code {}", code);
std::process::exit(101);
}
None => {
eprintln!("Execution of Ghidra plugin failed: Process was terminated.");
std::process::exit(101);
}
}
}
}
// Read the results from the Ghidra script
let file =
std::fs::File::open(&output_path).expect("Could not read results of the Ghidra script");
});
// Open the FIFO
let file = std::fs::File::open(fifo_path.clone()).expect("Could not open FIFO.");
let mut project_pcode: cwe_checker_rs::pcode::Project =
serde_json::from_reader(std::io::BufReader::new(file)).unwrap();
project_pcode.normalize();
......@@ -301,7 +327,12 @@ fn get_project_from_ghidra(file_path: &Path, binary: &[u8], quiet_flag: bool) ->
project
}
};
// delete the temporary file again.
std::fs::remove_file(output_path).unwrap();
ghidra_subprocess
.join()
.expect("The Ghidra thread to be joined has panicked!");
std::fs::remove_file(fifo_path).unwrap();
project
}
......@@ -2,7 +2,6 @@ import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.stream.StreamSupport;
import java.util.concurrent.TimeUnit;
import bil.*;
import term.*;
......@@ -58,7 +57,6 @@ public class PcodeExtractor extends GhidraScript {
String jsonPath = getScriptArgs()[0];
Serializer ser = new Serializer(project, jsonPath);
ser.serializeProject();
TimeUnit.SECONDS.sleep(3);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment