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