summaryrefslogtreecommitdiff
path: root/plugins/warp/src/bin
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/warp/src/bin')
-rw-r--r--plugins/warp/src/bin/sigem.rs269
1 files changed, 0 insertions, 269 deletions
diff --git a/plugins/warp/src/bin/sigem.rs b/plugins/warp/src/bin/sigem.rs
deleted file mode 100644
index a50b4ae2..00000000
--- a/plugins/warp/src/bin/sigem.rs
+++ /dev/null
@@ -1,269 +0,0 @@
-use std::collections::HashSet;
-use std::fs::File;
-use std::io::Read;
-use std::path::{Path, PathBuf};
-
-use ar::Archive;
-use clap::{arg, Parser};
-use rayon::prelude::*;
-
-use binaryninja::binary_view::{BinaryView, BinaryViewExt};
-use binaryninja::function::Function as BNFunction;
-use binaryninja::rc::Guard as BNGuard;
-use binaryninja::settings::Settings;
-use serde_json::{json, Value};
-use walkdir::WalkDir;
-use warp::signature::Data;
-use warp_ninja::cache::{cached_type_references, register_cache_destructor};
-
-#[derive(Parser, Debug)]
-#[command(about, long_about)]
-/// A simple CLI utility to generate WARP signature files headlessly using Binary Ninja.
-///
-/// NOTE: This requires a headless compatible Binary Ninja, make sure it's in your path.
-struct Args {
- /// Path to create signatures from, this can be:
- /// - A binary (that can be opened with Binary Ninja)
- /// - A directory (all files will be merged)
- /// - An archive (with ext: a, lib, rlib)
- /// - A BNDB
- /// - A Signature file (sbin)
- #[arg(index = 1, verbatim_doc_comment)]
- path: PathBuf,
-
- /// The signature output file
- ///
- /// NOTE: If not specified the output will be the input path with the sbin extension
- /// as an example `mylib.a` will output `mylib.sbin`.
- #[arg(index = 2)]
- output: Option<PathBuf>,
-
- /// Should we overwrite output file
- ///
- /// NOTE: If the file exists we will exit early to prevent wasted effort.
- #[arg(short, long)]
- overwrite: Option<bool>,
-
- /// The external debug information file to use
- #[arg(short, long)]
- debug_info: Option<PathBuf>,
- // TODO: Add a file filter and default to filter out files starting with "."
-}
-
-fn default_settings(bn_settings: &Settings) -> Value {
- // TODO: Make these settings configurable through the CLI
- let mut settings = json!({
- "analysis.linearSweep.autorun": false,
- "analysis.signatureMatcher.autorun": false,
- "analysis.mode": "full",
- // The reason we need to do this is a little unfortunate.
- // Basically some of the COFF's have really low image bases that confuses
- // Analysis and also our basic block GUID when a constant value points to a low address section.
- // TODO: This might not exist, we should set this based on the view.
- "loader.imageBase": 0x1000000,
- });
-
- // If WARP is enabled we must turn it off to prevent matching on other stuff.
- if bn_settings.contains("analysis.warp.matcher") {
- settings["analysis.warp.matcher"] = json!(false);
- settings["analysis.warp.guid"] = json!(false);
- }
-
- settings
-}
-
-fn main() {
- let args = Args::parse();
- env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
-
- // TODO: After analysis finishes for a file we should save off the bndb to another directory called the bndb cache
- // TODO: This cache should be used before opening a file for first analysis.
-
- // TODO: We should resolve the path to something sensible in cases where user is passing CWD.
- // If no output file was given, just prepend binary with extension sbin
- let output_file = args
- .output
- .unwrap_or(args.path.to_owned())
- .with_extension("sbin");
-
- if output_file.exists() && !args.overwrite.unwrap_or(false) {
- log::info!("Output file already exists, skipping... {:?}", output_file);
- return;
- }
-
- log::debug!("Starting Binary Ninja session...");
- let _headless_session =
- binaryninja::headless::Session::new().expect("Failed to initialize session");
-
- // Adjust the amount of worker threads so that we can actually free BinaryViews.
- let worker_count = rayon::current_num_threads() * 4;
- log::debug!("Adjusting Binary Ninja worker count to {}...", worker_count);
- binaryninja::worker_thread::set_worker_thread_count(worker_count);
-
- // Make sure caches are flushed when the views get destructed.
- register_cache_destructor();
-
- let bn_settings = Settings::new();
- let settings = default_settings(&bn_settings);
-
- log::info!("Creating functions for {:?}...", args.path);
- let start = std::time::Instant::now();
- let data = data_from_file(&settings, &args.path)
- .expect("Failed to read data, check your license and Binary Ninja version!");
- log::info!("Functions created in {:?}", start.elapsed());
-
- // TODO: Add a way to override the symbol type to make it a different function symbol.
- // TODO: Right now the consumers must dictate that.
- // TODO: The binja_warp consumer sets this to library function fwiw
-
- if !data.functions.is_empty() {
- std::fs::write(&output_file, data.to_bytes()).expect("Failed to write functions to file");
- log::info!(
- "{} functions written to {:?}...",
- data.functions.len(),
- output_file
- );
- } else {
- log::warn!("No functions found for binary {:?}...", args.path);
- }
-}
-
-fn data_from_view(view: &BinaryView) -> Data {
- let mut data = Data::default();
- let is_function_named = |f: &BNGuard<BNFunction>| {
- !f.symbol().short_name().to_string_lossy().contains("sub_") || f.has_user_annotations()
- };
-
- data.functions = view
- .functions()
- .iter()
- .filter(is_function_named)
- .filter_map(|f| {
- let llil = f.low_level_il().ok()?;
- Some(warp_ninja::cache::cached_function(&f, &llil))
- })
- .collect::<Vec<_>>();
-
- if let Some(ref_ty_cache) = cached_type_references(view) {
- let referenced_types = ref_ty_cache
- .cache
- .iter()
- .filter_map(|t| t.to_owned())
- .collect::<Vec<_>>();
-
- data.types.extend(referenced_types);
- }
-
- data
-}
-
-fn data_from_archive<R: Read>(settings: &Value, mut archive: Archive<R>) -> Option<Data> {
- // TODO: I feel like this is a hack...
- let temp_dir = tempdir::TempDir::new("tmp_archive").ok()?;
- // Iterate through the entries in the ar file and make a temp dir with them
- let mut entry_files: HashSet<PathBuf> = HashSet::new();
- while let Some(entry) = archive.next_entry() {
- match entry {
- Ok(mut entry) => {
- let name = String::from_utf8_lossy(entry.header().identifier()).to_string();
- // Write entry data to a temp directory
- let output_path = temp_dir.path().join(&name);
- if !entry_files.contains(&output_path) {
- let mut output_file =
- File::create(&output_path).expect("Failed to create entry file");
- std::io::copy(&mut entry, &mut output_file).expect("Failed to read entry data");
- entry_files.insert(output_path);
- } else {
- log::debug!("Skipping already inserted entry: {}", name);
- }
- }
- Err(e) => {
- log::error!("Failed to read archive entry: {}", e);
- }
- }
- }
-
- // Create the data.
- let entry_data = entry_files
- .into_par_iter()
- .filter_map(|path| {
- log::debug!("Creating data for ENTRY {:?}...", path);
- data_from_file(settings, &path)
- })
- .collect::<Vec<_>>();
-
- Some(Data::merge(entry_data))
-}
-
-fn data_from_directory(settings: &Value, dir: PathBuf) -> Option<Data> {
- let files = WalkDir::new(dir)
- .into_iter()
- .filter_map(|e| {
- let path = e.ok()?.into_path();
- if path.is_file() {
- Some(path)
- } else {
- None
- }
- })
- .collect::<Vec<_>>();
-
- let unmerged_data = files
- .into_par_iter()
- .filter_map(|path| {
- log::info!("Creating data for FILE {:?}...", path);
- data_from_file(settings, &path)
- })
- .collect::<Vec<_>>();
-
- if !unmerged_data.is_empty() {
- Some(Data::merge(unmerged_data))
- } else {
- None
- }
-}
-
-fn data_from_file(settings: &Value, path: &Path) -> Option<Data> {
- match path.extension() {
- Some(ext) if ext == "a" || ext == "lib" || ext == "rlib" => {
- let archive_file = File::open(path).expect("Failed to open archive file");
- let archive = Archive::new(archive_file);
- data_from_archive(settings, archive)
- }
- Some(ext) if ext == "sbin" => {
- let contents = std::fs::read(path).ok()?;
- Data::from_bytes(&contents)
- }
- _ if path.is_dir() => data_from_directory(settings, path.into()),
- _ => {
- let path_str = path.to_str().unwrap();
- let view = binaryninja::load_with_options(path_str, true, Some(settings.to_string()))?;
- let data = data_from_view(&view);
- view.file().close();
- Some(data)
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- #[test]
- fn test_data_from_file() {
- env_logger::init();
- // TODO: Store oracles here to get more out of this test.
- let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap();
- let _headless_session =
- binaryninja::headless::Session::new().expect("Failed to initialize session");
- let bn_settings = Settings::new();
- let settings = default_settings(&bn_settings);
- for entry in std::fs::read_dir(out_dir).expect("Failed to read OUT_DIR") {
- let entry = entry.expect("Failed to read directory entry");
- let path = entry.path();
- if path.is_file() {
- let result = data_from_file(&settings, &path);
- assert!(result.is_some());
- }
- }
- }
-}