From dbd12258716746f8bd63baa4f08d253fe5a2d7ce Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Wed, 23 Oct 2024 20:27:18 -0400 Subject: [PATCH] Refactor crate for use as a library --- .cargo/config.toml | 4 - Cargo.toml | 40 +- LICENSE | 13 + fbcg_rust/build.rs => build.rs | 4 +- fbcg_rust/Cargo.toml | 10 - pyproject.toml | 20 - {typebuild => rust}/benches/void.rs | 0 {typebuild => rust}/examples/simple.rs | 7 +- {fbcg_rust/src => rust}/lib.rs | 12 +- .../src/lib.rs => rust/signature.rs | 14 +- .../src => rust/signature}/basic_block.rs | 2 +- .../src => rust/signature}/function.rs | 61 +- .../signature}/function/constraints.rs | 6 +- rust/symbol.rs | 83 +++ rust/symbol/class.rs | 67 ++ {typebuild/src => rust}/type.rs | 112 ++-- {typebuild/src => rust}/type/class.rs | 32 +- {typebuild/src => rust}/type/class/array.rs | 40 +- {typebuild/src => rust}/type/class/boolean.rs | 30 +- .../src => rust}/type/class/character.rs | 30 +- .../src => rust}/type/class/enumeration.rs | 63 +- {typebuild/src => rust}/type/class/float.rs | 30 +- .../src => rust}/type/class/function.rs | 54 +- {typebuild/src => rust}/type/class/integer.rs | 36 +- {typebuild/src => rust}/type/class/pointer.rs | 34 +- .../src => rust}/type/class/referrer.rs | 35 +- .../src => rust}/type/class/structure.rs | 45 +- {typebuild/src => rust}/type/class/union.rs | 60 +- {typebuild/src => rust}/type/class/void.rs | 20 +- {typebuild/src => rust/type}/guid.rs | 0 {typebuild/src => rust}/type/modifier.rs | 2 +- sigem/Cargo.toml | 20 - sigem/build.rs | 68 -- sigem/fixtures/bin/librustlibrary.rlib | Bin 32312 -> 0 bytes sigem/fixtures/src/library.c | 33 - sigem/fixtures/src/library.h | 11 - sigem/fixtures/src/library.rs | 22 - sigem/fixtures/src/simple.c | 15 - sigem/scripts/README.md | 49 -- sigem/scripts/downpad.py | 127 ---- sigem/scripts/downwind.py | 219 ------- sigem/scripts/extract_archives.py | 52 -- sigem/src/main.rs | 177 ----- signaturebuild/Cargo.toml | 15 - sigview/Cargo.toml | 2 +- sigview/src/graph.rs | 2 +- sigview/src/main.rs | 3 +- symbolbuild/Cargo.toml | 11 - symbolbuild/src/lib.rs | 180 ------ typebuild/Cargo.toml | 18 - typebuild/src/lib.rs | 27 - warp_binja/Cargo.toml | 37 -- warp_binja/benches/convert.rs | 46 -- warp_binja/benches/function.rs | 42 -- warp_binja/benches/guid.rs | 22 - warp_binja/build.rs | 34 - warp_binja/fixtures/library.c | 33 - warp_binja/fixtures/library.h | 11 - warp_binja/fixtures/simple.c | 15 - warp_binja/src/cache.rs | 272 -------- warp_binja/src/convert.rs | 607 ------------------ warp_binja/src/lib.rs | 163 ----- warp_binja/src/matcher.rs | 433 ------------- warp_binja/src/plugin.rs | 151 ----- warp_binja/src/plugin/apply.rs | 83 --- warp_binja/src/plugin/copy.rs | 35 - warp_binja/src/plugin/create.rs | 73 --- warp_binja/src/plugin/find.rs | 58 -- warp_binja/src/plugin/types.rs | 52 -- warp_binja/src/plugin/workflow.rs | 38 -- 70 files changed, 529 insertions(+), 3693 deletions(-) delete mode 100644 .cargo/config.toml create mode 100644 LICENSE rename fbcg_rust/build.rs => build.rs (75%) delete mode 100644 fbcg_rust/Cargo.toml delete mode 100644 pyproject.toml rename {typebuild => rust}/benches/void.rs (100%) rename {typebuild => rust}/examples/simple.rs (93%) rename {fbcg_rust/src => rust}/lib.rs (87%) rename signaturebuild/src/lib.rs => rust/signature.rs (86%) rename {signaturebuild/src => rust/signature}/basic_block.rs (98%) rename {signaturebuild/src => rust/signature}/function.rs (78%) rename {signaturebuild/src => rust/signature}/function/constraints.rs (97%) create mode 100644 rust/symbol.rs create mode 100644 rust/symbol/class.rs rename {typebuild/src => rust}/type.rs (94%) rename {typebuild/src => rust}/type/class.rs (88%) rename {typebuild/src => rust}/type/class/array.rs (88%) rename {typebuild/src => rust}/type/class/boolean.rs (77%) rename {typebuild/src => rust}/type/class/character.rs (80%) rename {typebuild/src => rust}/type/class/enumeration.rs (80%) rename {typebuild/src => rust}/type/class/float.rs (80%) rename {typebuild/src => rust}/type/class/function.rs (92%) rename {typebuild/src => rust}/type/class/integer.rs (85%) rename {typebuild/src => rust}/type/class/pointer.rs (92%) rename {typebuild/src => rust}/type/class/referrer.rs (77%) rename {typebuild/src => rust}/type/class/structure.rs (89%) rename {typebuild/src => rust}/type/class/union.rs (92%) rename {typebuild/src => rust}/type/class/void.rs (77%) rename {typebuild/src => rust/type}/guid.rs (100%) rename {typebuild/src => rust}/type/modifier.rs (99%) delete mode 100644 sigem/Cargo.toml delete mode 100644 sigem/build.rs delete mode 100644 sigem/fixtures/bin/librustlibrary.rlib delete mode 100644 sigem/fixtures/src/library.c delete mode 100644 sigem/fixtures/src/library.h delete mode 100644 sigem/fixtures/src/library.rs delete mode 100644 sigem/fixtures/src/simple.c delete mode 100644 sigem/scripts/README.md delete mode 100644 sigem/scripts/downpad.py delete mode 100644 sigem/scripts/downwind.py delete mode 100644 sigem/scripts/extract_archives.py delete mode 100644 sigem/src/main.rs delete mode 100644 signaturebuild/Cargo.toml delete mode 100644 symbolbuild/Cargo.toml delete mode 100644 symbolbuild/src/lib.rs delete mode 100644 typebuild/Cargo.toml delete mode 100644 typebuild/src/lib.rs delete mode 100644 warp_binja/Cargo.toml delete mode 100644 warp_binja/benches/convert.rs delete mode 100644 warp_binja/benches/function.rs delete mode 100644 warp_binja/benches/guid.rs delete mode 100644 warp_binja/build.rs delete mode 100644 warp_binja/fixtures/library.c delete mode 100644 warp_binja/fixtures/library.h delete mode 100644 warp_binja/fixtures/simple.c delete mode 100644 warp_binja/src/cache.rs delete mode 100644 warp_binja/src/convert.rs delete mode 100644 warp_binja/src/lib.rs delete mode 100644 warp_binja/src/matcher.rs delete mode 100644 warp_binja/src/plugin.rs delete mode 100644 warp_binja/src/plugin/apply.rs delete mode 100644 warp_binja/src/plugin/copy.rs delete mode 100644 warp_binja/src/plugin/create.rs delete mode 100644 warp_binja/src/plugin/find.rs delete mode 100644 warp_binja/src/plugin/types.rs delete mode 100644 warp_binja/src/plugin/workflow.rs diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index c915d59..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,4 +0,0 @@ -[env] -# HACK: To get the workspace directory. (See: https://github.com/rust-lang/cargo/issues/3946#issuecomment-973132993) -# TODO: If we had &[u8] api in flatbuffers-build we could include the file with relative path like that. -CARGO_WORKSPACE_DIR = { value = "", relative = true } \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index c706ec1..c3558b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,29 @@ -[workspace] -resolver = "2" -members = [ - "fbcg_rust", - "sigem", - "signaturebuild", - "sigview", - "symbolbuild", - "typebuild", - "warp_binja" -] +[package] +name = "warp" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" -[workspace.dependencies] -#binaryninja = { path = "../../binaryninja/api/rust", features = ["rayon"] } -# NOTE: You must update the revision manually when making changes in the `binaryninja` crate. -binaryninja = { git = "https://github.com/Vector35/binaryninja-api.git", rev = "bca07d7", features = ["rayon"] } +[lib] +path = "rust/lib.rs" + +[dependencies] +flatbuffers = "24.3.25" +bon = "2.3.0" +uuid = { version = "1.11.0", features = ["v5"]} +rand = "0.8.5" + +[dev-dependencies] +criterion = "0.5.1" + +[build-dependencies] +flatbuffers-build = { git = "https://github.com/emesare/flatbuffers-build" } [profile.release] panic = "abort" lto = true -debug = "full" \ No newline at end of file +debug = "full" + +[[example]] +name = "simple" +path = "rust/examples/simple.rs" \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8d2dadd --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ +Copyright 2020-2024 Vector 35 Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/fbcg_rust/build.rs b/build.rs similarity index 75% rename from fbcg_rust/build.rs rename to build.rs index 54a2e22..6483120 100644 --- a/fbcg_rust/build.rs +++ b/build.rs @@ -4,13 +4,13 @@ pub fn main() { // Remove leftover symlink dir. let _ = std::fs::remove_dir_all("src/gen_flatbuffers"); - let workspace_dir: PathBuf = std::env::var("CARGO_WORKSPACE_DIR").unwrap().into(); + let workspace_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into(); BuilderOptions::new_with_files([ workspace_dir.join("type.fbs"), workspace_dir.join("symbol.fbs"), workspace_dir.join("signature.fbs"), ]) - .set_symlink_directory("src/gen_flatbuffers") + .set_symlink_directory("rust/gen_flatbuffers") .compile() .expect("flatbuffer compilation failed"); } diff --git a/fbcg_rust/Cargo.toml b/fbcg_rust/Cargo.toml deleted file mode 100644 index 4240e02..0000000 --- a/fbcg_rust/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "fbcg_rust" -version = "0.1.0" -edition = "2021" - -[dependencies] -flatbuffers = "24.3.25" - -[build-dependencies] -flatbuffers-build = { git = "https://github.com/emesare/flatbuffers-build" } \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 65f87d4..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,20 +0,0 @@ -[tool.poetry] -name = "warp" -version = "0.1.0" -description = "" -authors = ["Mason Reed "] -readme = "README.md" - -[tool.poetry.dependencies] -python = "^3.9" -aiohttp = "^3.10.8" -beautifulsoup4 = "^4.12.3" -ar = "^1.0.0" -zstandard = "^0.23.0" -xtarfile = "^0.2.1" - - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" - diff --git a/typebuild/benches/void.rs b/rust/benches/void.rs similarity index 100% rename from typebuild/benches/void.rs rename to rust/benches/void.rs diff --git a/typebuild/examples/simple.rs b/rust/examples/simple.rs similarity index 93% rename from typebuild/examples/simple.rs rename to rust/examples/simple.rs index 6184836..e37f87f 100644 --- a/typebuild/examples/simple.rs +++ b/rust/examples/simple.rs @@ -1,4 +1,9 @@ -use typebuild::prelude::*; +use warp::r#type::class::{ + ArrayClass, BooleanClass, EnumerationClass, EnumerationMember, FunctionClass, FunctionMember, + IntegerClass, PointerClass, StructureClass, StructureMember, TypeClass, UnionClass, + UnionMember, +}; +use warp::r#type::Type; fn main() { let void_type = Type::builder() diff --git a/fbcg_rust/src/lib.rs b/rust/lib.rs similarity index 87% rename from fbcg_rust/src/lib.rs rename to rust/lib.rs index dba800a..bd8787e 100644 --- a/fbcg_rust/src/lib.rs +++ b/rust/lib.rs @@ -1,10 +1,14 @@ +pub mod signature; +pub mod symbol; +pub mod r#type; + #[allow(warnings)] #[rustfmt::skip] -pub mod gen_flatbuffers; +mod gen_flatbuffers; -pub use gen_flatbuffers::sig_bin as fb_sig; -pub use gen_flatbuffers::symbol_bin as fb_symbol; -pub use gen_flatbuffers::type_bin as fb_type; +use gen_flatbuffers::sig_bin as fb_sig; +use gen_flatbuffers::symbol_bin as fb_symbol; +use gen_flatbuffers::type_bin as fb_type; impl From for gen_flatbuffers::type_bin::BitWidth { fn from(value: u16) -> Self { diff --git a/signaturebuild/src/lib.rs b/rust/signature.rs similarity index 86% rename from signaturebuild/src/lib.rs rename to rust/signature.rs index 729b22c..3730450 100644 --- a/signaturebuild/src/lib.rs +++ b/rust/signature.rs @@ -1,19 +1,11 @@ -use crate::function::Function; -use fbcg_rust::fb_sig as fb; +use crate::fb_sig as fb; +use crate::r#type::ComputedType; +use crate::signature::function::Function; use flatbuffers::{FlatBufferBuilder, WIPOffset}; -use typebuild::prelude::ComputedType; pub mod basic_block; pub mod function; -pub mod prelude { - pub use crate::{ - basic_block::{BasicBlock, BasicBlockGUID}, - function::{constraints::FunctionConstraints, Function, FunctionGUID}, - Data, - }; -} - #[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct Data { pub functions: Vec, diff --git a/signaturebuild/src/basic_block.rs b/rust/signature/basic_block.rs similarity index 98% rename from signaturebuild/src/basic_block.rs rename to rust/signature/basic_block.rs index a8e7bd1..72750a0 100644 --- a/signaturebuild/src/basic_block.rs +++ b/rust/signature/basic_block.rs @@ -1,4 +1,4 @@ -use fbcg_rust::fb_sig as fb; +use crate::fb_sig as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use std::fmt::{Display, Formatter}; use std::str::FromStr; diff --git a/signaturebuild/src/function.rs b/rust/signature/function.rs similarity index 78% rename from signaturebuild/src/function.rs rename to rust/signature/function.rs index 895f869..0206352 100644 --- a/signaturebuild/src/function.rs +++ b/rust/signature/function.rs @@ -1,12 +1,11 @@ -use crate::basic_block::BasicBlock; -use crate::prelude::{BasicBlockGUID, FunctionConstraints}; -use fbcg_rust::fb_sig as fb; +use crate::fb_sig as fb; +use crate::r#type::Type; +use crate::signature::basic_block::{BasicBlock, BasicBlockGUID}; +use crate::signature::function::constraints::FunctionConstraints; +use crate::symbol::Symbol; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use std::fmt::{Display, Formatter}; use std::str::FromStr; -use symbolbuild::{Create, Symbol}; -use typebuild::prelude::Type; -use typebuild::Build; use uuid::{uuid, Uuid}; pub mod constraints; @@ -126,9 +125,13 @@ impl From> for Function { #[cfg(test)] mod tests { - use crate::prelude::*; - use symbolbuild::prelude::*; - use typebuild::prelude::*; + use crate::r#type::class::TypeClass; + use crate::r#type::Type; + use crate::signature::function::constraints::FunctionConstraints; + use crate::signature::function::{Function, FunctionGUID}; + use crate::signature::Data; + use crate::symbol::class::SymbolClass; + use crate::symbol::{Symbol, SymbolModifiers}; use uuid::{uuid, Uuid}; const EMPTY_FN_UUID: Uuid = uuid!("db867a3e-416a-5d7f-aa6d-b8ae6be36da2"); @@ -142,18 +145,22 @@ mod tests { assert_eq!(FunctionGUID::from(EMPTY_FN_UUID), empty_fn_guid()); } - #[test] - fn test_data_from_bytes() { - let signature = Function { + fn empty_function() -> Function { + Function { guid: empty_fn_guid(), - symbol: Symbol::new("test", SymbolClass::Data, vec![]), + symbol: Symbol::new("test", SymbolClass::Data, SymbolModifiers::empty()), ty: Type::builder() .name("aghhgh") .class(TypeClass::Void) .build(), constraints: FunctionConstraints::default(), entry: None, - }; + } + } + + #[test] + fn test_data_from_bytes() { + let signature = empty_function(); let data = Data { functions: vec![signature.clone()], types: vec![], @@ -165,33 +172,9 @@ mod tests { #[test] fn test_function_from_bytes() { - let signature = Function { - guid: empty_fn_guid(), - symbol: Symbol::new("test", SymbolClass::Data, vec![]), - ty: Type::builder() - .name("aghhgh") - .class(TypeClass::Void) - .build(), - constraints: FunctionConstraints::default(), - entry: None, - }; + let signature = empty_function(); let bytes = signature.to_bytes(); let from_bytes_sig = Function::from_bytes(&bytes).unwrap(); assert_eq!(signature, from_bytes_sig) } - - #[test] - fn it_works() { - let signature = Function { - guid: empty_fn_guid(), - symbol: Symbol::new("test", SymbolClass::Data, vec![]), - ty: Type::builder() - .name("aghhgh") - .class(TypeClass::Void) - .build(), - constraints: FunctionConstraints::default(), - entry: None, - }; - dbg!(&signature); - } } diff --git a/signaturebuild/src/function/constraints.rs b/rust/signature/function/constraints.rs similarity index 97% rename from signaturebuild/src/function/constraints.rs rename to rust/signature/function/constraints.rs index 7a3bd41..cc8e713 100644 --- a/signaturebuild/src/function/constraints.rs +++ b/rust/signature/function/constraints.rs @@ -1,8 +1,8 @@ -use crate::function::FunctionGUID; -use fbcg_rust::fb_sig as fb; +use crate::fb_sig as fb; +use crate::signature::function::FunctionGUID; +use crate::symbol::Symbol; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use std::collections::HashSet; -use symbolbuild::{Create, Symbol}; #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct FunctionConstraint { diff --git a/rust/symbol.rs b/rust/symbol.rs new file mode 100644 index 0000000..ff77e9f --- /dev/null +++ b/rust/symbol.rs @@ -0,0 +1,83 @@ +pub mod class; + +use crate::fb_symbol as fb; +use crate::symbol::class::SymbolClass; +use flatbuffers::{FlatBufferBuilder, WIPOffset}; + +pub use fb::SymbolModifiers; + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct Symbol { + pub name: String, + pub modifiers: SymbolModifiers, + pub class: SymbolClass, +} + +impl Symbol { + pub fn new(name: impl Into, class: SymbolClass, modifiers: SymbolModifiers) -> Self { + Self { + name: name.into(), + modifiers, + class, + } + } + + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { + let name = builder.create_string(&self.name); + let class_type = self.class.ty(); + let class = self.class.create(builder); + + fb::Symbol::create( + builder, + &fb::SymbolArgs { + name: Some(name), + modifiers: self.modifiers, + class_type, + class: Some(class), + }, + ) + } +} + +impl From> for Symbol { + fn from(value: fb::Symbol<'_>) -> Self { + let name = value.name().unwrap().to_string(); + // TODO: I would like this conversion to be on `SymbolClass` instead. + let class = match value.class_type() { + fb::SymbolClass::FunctionSymbolClass => { + SymbolClass::from(value.class_as_function_symbol_class().unwrap()) + } + fb::SymbolClass::DataSymbolClass => { + SymbolClass::from(value.class_as_data_symbol_class().unwrap()) + } + _ => unreachable!(), + }; + + Self { + name, + modifiers: value.modifiers(), + class, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use flatbuffers::FlatBufferBuilder; + + #[test] + fn it_works() { + let mut builder = FlatBufferBuilder::with_capacity(100); + let symbol = Symbol { + name: "".to_string(), + modifiers: SymbolModifiers::empty(), + class: SymbolClass::Data, + }; + let _created_symbol = symbol.create(&mut builder); + // TODO: Add actual tests. + } +} diff --git a/rust/symbol/class.rs b/rust/symbol/class.rs new file mode 100644 index 0000000..e124715 --- /dev/null +++ b/rust/symbol/class.rs @@ -0,0 +1,67 @@ +use flatbuffers::{FlatBufferBuilder, UnionWIPOffset, WIPOffset}; + +#[derive(Clone, Debug, Default)] +pub struct FunctionSymbolClass; + +impl FunctionSymbolClass { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { + crate::gen_flatbuffers::symbol_bin::FunctionSymbolClass::create( + builder, + &crate::gen_flatbuffers::symbol_bin::FunctionSymbolClassArgs {}, + ) + } +} + +#[derive(Clone, Debug, Default)] +pub struct DataSymbolClass; + +impl DataSymbolClass { + fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { + crate::gen_flatbuffers::symbol_bin::DataSymbolClass::create( + builder, + &crate::gen_flatbuffers::symbol_bin::DataSymbolClassArgs {}, + ) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum SymbolClass { + Function, + Data, +} + +impl SymbolClass { + pub fn ty(&self) -> crate::gen_flatbuffers::symbol_bin::SymbolClass { + match self { + SymbolClass::Function => { + crate::gen_flatbuffers::symbol_bin::SymbolClass::FunctionSymbolClass + } + SymbolClass::Data => crate::gen_flatbuffers::symbol_bin::SymbolClass::DataSymbolClass, + } + } + + pub fn create(&self, builder: &mut FlatBufferBuilder) -> WIPOffset { + match self { + SymbolClass::Function => FunctionSymbolClass.create(builder).as_union_value(), + SymbolClass::Data => DataSymbolClass.create(builder).as_union_value(), + } + } +} + +impl From> for SymbolClass { + fn from(_value: crate::gen_flatbuffers::symbol_bin::FunctionSymbolClass<'_>) -> Self { + Self::Function + } +} + +impl From> for SymbolClass { + fn from(_value: crate::gen_flatbuffers::symbol_bin::DataSymbolClass<'_>) -> Self { + Self::Data + } +} diff --git a/typebuild/src/type.rs b/rust/type.rs similarity index 94% rename from typebuild/src/type.rs rename to rust/type.rs index b07888a..9321dcf 100644 --- a/typebuild/src/type.rs +++ b/rust/type.rs @@ -1,16 +1,15 @@ use bon::bon; -use crate::guid::TypeGUID; -use crate::prelude::TypeModifierClass; +use crate::fb_type as fb; use crate::r#type::class::TypeClass; -use crate::r#type::modifier::TypeModifier; -use crate::Build; -use fbcg_rust::fb_type as fb; +use crate::r#type::guid::TypeGUID; +use crate::r#type::modifier::{TypeModifier, TypeModifierClass}; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::{Alphanumeric, DistString, Distribution, Standard}; use rand::Rng; pub mod class; +pub mod guid; pub mod modifier; #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -67,7 +66,6 @@ pub struct Type { pub class: Box, // TODO: Type confidence? pub confidence: u8, - // TODO: Flatten out modififers to directly just use TypeModifier (see is_const) pub modifiers: Vec, pub alignment: Alignment, pub ancestors: Vec, @@ -123,6 +121,54 @@ impl Type { self.modifiers .contains(&TypeModifier::new(TypeModifierClass::Volatile)) } + + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { + let name = self.name.as_ref().map(|n| builder.create_string(n)); + let class_type = self.class.ty(); + let class = self.class.create(builder); + + let mut ancestors = None; + if !self.ancestors.is_empty() { + let _ancestors = self + .ancestors + .iter() + .map(|a| builder.create_string(&a.to_string())) + .collect::>(); + ancestors = Some(builder.create_vector(&_ancestors)); + } + + let mut modifiers = None; + if !self.modifiers.is_empty() { + let _modifiers = self + .modifiers + .iter() + .map(|modifier| modifier.create(builder)) + .collect::>(); + modifiers = Some(builder.create_vector(&_modifiers)); + } + + fb::Type::create( + builder, + &fb::TypeArgs { + name, + class_type, + class: Some(class), + confidence: self.confidence, + ancestors, + modifiers, + // TODO: Alignment + alignment_type: Default::default(), + alignment_: None, + }, + ) + } + + pub fn size(&self) -> Option { + self.class.size() + } } impl From> for Type { @@ -133,7 +179,7 @@ impl From> for Type { fb::TypeClass::Void => Some(TypeClass::Void), fb::TypeClass::Boolean => { let bool = value.class_as_boolean()?; - Some(TypeClass::Bool(bool.into())) + Some(TypeClass::Boolean(bool.into())) } fb::TypeClass::Integer => { let int = value.class_as_integer()?; @@ -198,55 +244,6 @@ impl From> for Type { } } -impl Build for Type { - type FBType<'a> = fb::Type<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { - let name = self.name.as_ref().map(|n| builder.create_string(n)); - let class_type = self.class.ty(); - let class = self.class.create(builder); - - let mut ancestors = None; - if !self.ancestors.is_empty() { - let _ancestors = self - .ancestors - .iter() - .map(|a| builder.create_string(&a.to_string())) - .collect::>(); - ancestors = Some(builder.create_vector(&_ancestors)); - } - - let mut modifiers = None; - if !self.modifiers.is_empty() { - let _modifiers = self - .modifiers - .iter() - .map(|modifier| modifier.create(builder)) - .collect::>(); - modifiers = Some(builder.create_vector(&_modifiers)); - } - - fb::Type::create( - builder, - &fb::TypeArgs { - name, - class_type, - class: Some(class), - confidence: self.confidence, - ancestors, - modifiers, - // TODO: Alignment - alignment_type: Default::default(), - alignment_: None, - }, - ) - } - - fn size(&self) -> Option { - self.class.size() - } -} - impl Distribution for Standard { fn sample(&self, rng: &mut R) -> Type { // 90% chance this type will have a name. @@ -271,7 +268,8 @@ impl Distribution for Standard { #[cfg(test)] mod tests { - use crate::prelude::*; + use crate::r#type::guid::TypeGUID; + use crate::r#type::Type; use rand::Rng; use std::collections::HashSet; diff --git a/typebuild/src/type/class.rs b/rust/type/class.rs similarity index 88% rename from typebuild/src/type/class.rs rename to rust/type/class.rs index 7fa487a..c5cbcea 100644 --- a/typebuild/src/type/class.rs +++ b/rust/type/class.rs @@ -11,6 +11,7 @@ pub mod structure; pub mod union; pub mod void; +use crate::fb_type as fb; pub use crate::r#type::class::array::ArrayClass; pub use crate::r#type::class::boolean::BooleanClass; pub use crate::r#type::class::character::CharacterClass; @@ -23,18 +24,14 @@ pub use crate::r#type::class::referrer::ReferrerClass; pub use crate::r#type::class::structure::{StructureClass, StructureMember}; pub use crate::r#type::class::union::{UnionClass, UnionMember}; pub use crate::r#type::class::void::VoidClass; -use crate::Build; -use fbcg_rust::fb_type as fb; use flatbuffers::{FlatBufferBuilder, UnionWIPOffset, WIPOffset}; use rand::distributions::{Distribution, Standard}; use rand::Rng; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum TypeClass { - // TODO: Make void class internally, make this Void no attached item. Void, - // TOOD: Rename Boolean - Bool(BooleanClass), + Boolean(BooleanClass), Integer(IntegerClass), Character(CharacterClass), Float(FloatClass), @@ -51,7 +48,7 @@ impl TypeClass { pub fn ty(&self) -> fb::TypeClass { match self { TypeClass::Void => fb::TypeClass::Void, - TypeClass::Bool(_) => fb::TypeClass::Boolean, + TypeClass::Boolean(_) => fb::TypeClass::Boolean, TypeClass::Integer(_) => fb::TypeClass::Integer, TypeClass::Character(_) => fb::TypeClass::Character, TypeClass::Float(_) => fb::TypeClass::Float, @@ -64,15 +61,11 @@ impl TypeClass { TypeClass::Referrer(_) => fb::TypeClass::Referrer, } } -} - -impl Build for TypeClass { - type FBType<'a> = UnionWIPOffset; - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { + pub(crate) fn create(&self, builder: &mut FlatBufferBuilder) -> WIPOffset { match self { TypeClass::Void => VoidClass.create(builder).as_union_value(), - TypeClass::Bool(c) => c.create(builder).as_union_value(), + TypeClass::Boolean(c) => c.create(builder).as_union_value(), TypeClass::Integer(c) => c.create(builder).as_union_value(), TypeClass::Character(c) => c.create(builder).as_union_value(), TypeClass::Float(c) => c.create(builder).as_union_value(), @@ -86,10 +79,10 @@ impl Build for TypeClass { } } - fn size(&self) -> Option { + pub fn size(&self) -> Option { match self { - TypeClass::Void => VoidClass.size(), - TypeClass::Bool(c) => c.size(), + TypeClass::Void => None, + TypeClass::Boolean(c) => c.size(), TypeClass::Integer(c) => c.size(), TypeClass::Character(c) => c.size(), TypeClass::Float(c) => c.size(), @@ -98,17 +91,18 @@ impl Build for TypeClass { TypeClass::Structure(c) => c.size(), TypeClass::Enumeration(c) => c.size(), TypeClass::Union(c) => c.size(), - TypeClass::Function(c) => c.size(), - TypeClass::Referrer(c) => c.size(), + TypeClass::Function(_) => None, + TypeClass::Referrer(_) => None, } } } + impl Distribution for Standard { fn sample(&self, rng: &mut R) -> TypeClass { // TODO: We need more types than Standard to randomly generate structures. match rng.gen_range(0..15) { 0 => TypeClass::Void, - 1 => TypeClass::Bool(rng.gen()), + 1 => TypeClass::Boolean(rng.gen()), 2 => TypeClass::Integer(rng.gen()), 3 => TypeClass::Character(rng.gen()), 4 => TypeClass::Float(rng.gen()), @@ -126,7 +120,7 @@ impl Distribution for Standard { impl From for TypeClass { fn from(value: BooleanClass) -> Self { - Self::Bool(value) + Self::Boolean(value) } } diff --git a/typebuild/src/type/class/array.rs b/rust/type/class/array.rs similarity index 88% rename from typebuild/src/type/class/array.rs rename to rust/type/class/array.rs index 41d0cf4..8b296a3 100644 --- a/typebuild/src/type/class/array.rs +++ b/rust/type/class/array.rs @@ -1,8 +1,7 @@ use bon::bon; +use crate::fb_type as fb; use crate::r#type::Type; -use crate::Build; -use fbcg_rust::fb_type as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::Standard; use rand::prelude::*; @@ -29,23 +28,11 @@ impl ArrayClass { } } -impl From> for ArrayClass { - fn from(value: fb::Array<'_>) -> Self { - Self { - length: match value.length() { - 0 => None, - len => Some(len), - }, - member_type: value.type_().into(), - modifiers: value.modifiers(), - } - } -} - -impl Build for ArrayClass { - type FBType<'a> = fb::Array<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl ArrayClass { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { let member_type = self.member_type.create(builder); fb::Array::create( builder, @@ -59,11 +46,24 @@ impl Build for ArrayClass { ) } - fn size(&self) -> Option { + pub fn size(&self) -> Option { Some(self.length? * self.member_type.size()?) } } +impl From> for ArrayClass { + fn from(value: fb::Array<'_>) -> Self { + Self { + length: match value.length() { + 0 => None, + len => Some(len), + }, + member_type: value.type_().into(), + modifiers: value.modifiers(), + } + } +} + impl Distribution for Standard { fn sample(&self, rng: &mut R) -> ArrayClass { let mut modifiers = ArrayModifiers::empty(); diff --git a/typebuild/src/type/class/boolean.rs b/rust/type/class/boolean.rs similarity index 77% rename from typebuild/src/type/class/boolean.rs rename to rust/type/class/boolean.rs index 9fa5e51..bed1c5e 100644 --- a/typebuild/src/type/class/boolean.rs +++ b/rust/type/class/boolean.rs @@ -1,6 +1,5 @@ -use crate::Build; +use crate::fb_type as fb; use bon::Builder; -use fbcg_rust::fb_type as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::{Distribution, Standard}; use rand::Rng; @@ -10,18 +9,11 @@ pub struct BooleanClass { pub width: Option, } -impl From> for BooleanClass { - fn from(value: fb::Boolean<'_>) -> Self { - Self { - width: value.width().map(Into::into), - } - } -} - -impl Build for BooleanClass { - type FBType<'a> = fb::Boolean<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl BooleanClass { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { fb::Boolean::create( builder, &fb::BooleanArgs { @@ -30,11 +22,19 @@ impl Build for BooleanClass { ) } - fn size(&self) -> Option { + pub fn size(&self) -> Option { self.width.map(|w| w as u64) } } +impl From> for BooleanClass { + fn from(value: fb::Boolean<'_>) -> Self { + Self { + width: value.width().map(Into::into), + } + } +} + impl Distribution for Standard { fn sample(&self, rng: &mut R) -> BooleanClass { // TODO: Maybe we should restrict this to "normal" widths? diff --git a/typebuild/src/type/class/character.rs b/rust/type/class/character.rs similarity index 80% rename from typebuild/src/type/class/character.rs rename to rust/type/class/character.rs index ba2819f..fe1bdd9 100644 --- a/typebuild/src/type/class/character.rs +++ b/rust/type/class/character.rs @@ -1,6 +1,5 @@ -use crate::Build; +use crate::fb_type as fb; use bon::Builder; -use fbcg_rust::fb_type as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::{Distribution, Standard}; use rand::Rng; @@ -11,18 +10,11 @@ pub struct CharacterClass { pub width: Option, } -impl From> for CharacterClass { - fn from(value: fb::Character<'_>) -> Self { - Self { - width: value.width().map(Into::into), - } - } -} - -impl Build for CharacterClass { - type FBType<'a> = fb::Character<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl CharacterClass { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { fb::Character::create( builder, &fb::CharacterArgs { @@ -31,11 +23,19 @@ impl Build for CharacterClass { ) } - fn size(&self) -> Option { + pub fn size(&self) -> Option { self.width.map(|w| w as u64) } } +impl From> for CharacterClass { + fn from(value: fb::Character<'_>) -> Self { + Self { + width: value.width().map(Into::into), + } + } +} + impl Distribution for Standard { fn sample(&self, rng: &mut R) -> CharacterClass { // 90% chance this type will have a width. diff --git a/typebuild/src/type/class/enumeration.rs b/rust/type/class/enumeration.rs similarity index 80% rename from typebuild/src/type/class/enumeration.rs rename to rust/type/class/enumeration.rs index bb0c2bc..3112536 100644 --- a/typebuild/src/type/class/enumeration.rs +++ b/rust/type/class/enumeration.rs @@ -1,8 +1,7 @@ use bon::Builder; +use crate::fb_type as fb; use crate::r#type::Type; -use crate::Build; -use fbcg_rust::fb_type as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::{Alphanumeric, DistString, Distribution, Standard}; use rand::Rng; @@ -14,19 +13,11 @@ pub struct EnumerationMember { pub constant: u64, } -impl From> for EnumerationMember { - fn from(value: fb::EnumerationMember<'_>) -> Self { - Self { - name: value.name().map(str::to_string), - constant: value.constant(), - } - } -} - -impl Build for EnumerationMember { - type FBType<'a> = fb::EnumerationMember<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl EnumerationMember { + fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { let name = self.name.as_ref().map(|n| builder.create_string(n)); fb::EnumerationMember::create( builder, @@ -36,11 +27,14 @@ impl Build for EnumerationMember { }, ) } +} - // TODO: This is another reason why this trait should be split out - /// [EnumerationMember]'s do not know their type. - fn size(&self) -> Option { - None +impl From> for EnumerationMember { + fn from(value: fb::EnumerationMember<'_>) -> Self { + Self { + name: value.name().map(str::to_string), + constant: value.constant(), + } } } @@ -68,19 +62,11 @@ impl EnumerationClass { } } -impl From> for EnumerationClass { - fn from(value: fb::Enumeration<'_>) -> Self { - Self { - member_type: value.member_type().into(), - members: value.members().unwrap().iter().map(Into::into).collect(), - } - } -} - -impl Build for EnumerationClass { - type FBType<'a> = fb::Enumeration<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl EnumerationClass { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { let enum_type = self.member_type.create(builder); // Resolve then create all member constants. Take the prior constant when `None`. let created_members: Vec<_> = self @@ -98,8 +84,17 @@ impl Build for EnumerationClass { ) } - fn size(&self) -> Option { - None + pub fn size(&self) -> Option { + self.member_type.size() + } +} + +impl From> for EnumerationClass { + fn from(value: fb::Enumeration<'_>) -> Self { + Self { + member_type: value.member_type().into(), + members: value.members().unwrap().iter().map(Into::into).collect(), + } } } diff --git a/typebuild/src/type/class/float.rs b/rust/type/class/float.rs similarity index 80% rename from typebuild/src/type/class/float.rs rename to rust/type/class/float.rs index 43b091d..86bb1b7 100644 --- a/typebuild/src/type/class/float.rs +++ b/rust/type/class/float.rs @@ -1,6 +1,5 @@ -use crate::Build; +use crate::fb_type as fb; use bon::Builder; -use fbcg_rust::fb_type as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::{Distribution, Standard}; use rand::Rng; @@ -11,18 +10,11 @@ pub struct FloatClass { pub width: Option, } -impl From> for FloatClass { - fn from(value: fb::Float<'_>) -> Self { - Self { - width: value.width().map(Into::into), - } - } -} - -impl Build for FloatClass { - type FBType<'a> = fb::Float<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl FloatClass { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { fb::Float::create( builder, &fb::FloatArgs { @@ -31,11 +23,19 @@ impl Build for FloatClass { ) } - fn size(&self) -> Option { + pub fn size(&self) -> Option { self.width.map(|w| w as u64) } } +impl From> for FloatClass { + fn from(value: fb::Float<'_>) -> Self { + Self { + width: value.width().map(Into::into), + } + } +} + impl Distribution for Standard { fn sample(&self, rng: &mut R) -> FloatClass { // 90% chance this type will have a width. diff --git a/typebuild/src/type/class/function.rs b/rust/type/class/function.rs similarity index 92% rename from typebuild/src/type/class/function.rs rename to rust/type/class/function.rs index 2226f81..23c7fab 100644 --- a/typebuild/src/type/class/function.rs +++ b/rust/type/class/function.rs @@ -1,7 +1,6 @@ +use crate::fb_type as fb; use crate::r#type::Type; -use crate::Build; use bon::{bon, Builder}; -use fbcg_rust::fb_type as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::{Alphanumeric, DistString, Distribution, Standard}; use rand::Rng; @@ -90,10 +89,8 @@ impl FunctionMember { } } -impl Build for FunctionMember { - type FBType<'a> = fb::FunctionMember<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl FunctionMember { + fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { let name = self.name.as_ref().map(|n| builder.create_string(n)); let member_type = self.ty.create(builder); fb::FunctionMember::create( @@ -106,10 +103,6 @@ impl Build for FunctionMember { }, ) } - - fn size(&self) -> Option { - self.ty.size() - } } impl Distribution for Standard { @@ -175,27 +168,11 @@ impl FunctionClass { out_members, } } -} - -impl From> for FunctionClass { - fn from(value: fb::Function<'_>) -> Self { - Self { - calling_convention: value.calling_convention().map(Into::into), - in_members: value.in_members().unwrap().iter().map(Into::into).collect(), - out_members: value - .out_members() - .unwrap() - .iter() - .map(Into::into) - .collect(), - } - } -} - -impl Build for FunctionClass { - type FBType<'a> = fb::Function<'a>; - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { let calling_convention = self .calling_convention .as_ref() @@ -222,11 +199,20 @@ impl Build for FunctionClass { }, ) } +} - // TODO: Add reason. - // Functions are without size. - fn size(&self) -> Option { - None +impl From> for FunctionClass { + fn from(value: fb::Function<'_>) -> Self { + Self { + calling_convention: value.calling_convention().map(Into::into), + in_members: value.in_members().unwrap().iter().map(Into::into).collect(), + out_members: value + .out_members() + .unwrap() + .iter() + .map(Into::into) + .collect(), + } } } diff --git a/typebuild/src/type/class/integer.rs b/rust/type/class/integer.rs similarity index 85% rename from typebuild/src/type/class/integer.rs rename to rust/type/class/integer.rs index 9cf1996..02c74c9 100644 --- a/typebuild/src/type/class/integer.rs +++ b/rust/type/class/integer.rs @@ -1,6 +1,5 @@ -use crate::Build; +use crate::fb_type as fb; use bon::Builder; -use fbcg_rust::fb_type as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::{Distribution, Standard}; use rand::Rng; @@ -12,19 +11,11 @@ pub struct IntegerClass { pub signed: bool, } -impl From> for IntegerClass { - fn from(value: fb::Integer<'_>) -> Self { - Self { - width: value.width().map(Into::into), - signed: value.signed(), - } - } -} - -impl Build for IntegerClass { - type FBType<'a> = fb::Integer<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl IntegerClass { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { fb::Integer::create( builder, &fb::IntegerArgs { @@ -34,11 +25,20 @@ impl Build for IntegerClass { ) } - fn size(&self) -> Option { + pub fn size(&self) -> Option { self.width.map(|w| w as u64) } } +impl From> for IntegerClass { + fn from(value: fb::Integer<'_>) -> Self { + Self { + width: value.width().map(Into::into), + signed: value.signed(), + } + } +} + impl Distribution for Standard { fn sample(&self, rng: &mut R) -> IntegerClass { // 90% chance this type will have a width. @@ -55,7 +55,9 @@ impl Distribution for Standard { #[cfg(test)] mod tests { - use crate::prelude::*; + use crate::r#type::class::{IntegerClass, TypeClass}; + use crate::r#type::guid::TypeGUID; + use crate::r#type::{Alignment, Type}; use uuid::{uuid, Uuid}; const INT_TYPE_UUID: Uuid = uuid!("ec8805ad-b101-5d8c-a033-c94610d036c1"); diff --git a/typebuild/src/type/class/pointer.rs b/rust/type/class/pointer.rs similarity index 92% rename from typebuild/src/type/class/pointer.rs rename to rust/type/class/pointer.rs index 1ee13ad..c255908 100644 --- a/typebuild/src/type/class/pointer.rs +++ b/rust/type/class/pointer.rs @@ -1,7 +1,6 @@ +use crate::fb_type as fb; use crate::r#type::Type; -use crate::Build; use bon::bon; -use fbcg_rust::fb_type as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::{Distribution, Standard}; use rand::Rng; @@ -69,20 +68,11 @@ impl PointerClass { } } -impl From> for PointerClass { - fn from(value: fb::Pointer<'_>) -> Self { - Self { - width: value.width().map(Into::into), - child_type: value.child().map(Into::into).unwrap(), - addressing: value.addressing().into(), - } - } -} - -impl Build for PointerClass { - type FBType<'a> = fb::Pointer<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl PointerClass { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { let child_type = self.child_type.create(builder); fb::Pointer::create( builder, @@ -99,11 +89,21 @@ impl Build for PointerClass { ) } - fn size(&self) -> Option { + pub fn size(&self) -> Option { self.width.map(|w| w as u64) } } +impl From> for PointerClass { + fn from(value: fb::Pointer<'_>) -> Self { + Self { + width: value.width().map(Into::into), + child_type: value.child().map(Into::into).unwrap(), + addressing: value.addressing().into(), + } + } +} + impl Distribution for Standard { fn sample(&self, rng: &mut R) -> PointerClass { // 90% chance this type will have a width. diff --git a/typebuild/src/type/class/referrer.rs b/rust/type/class/referrer.rs similarity index 77% rename from typebuild/src/type/class/referrer.rs rename to rust/type/class/referrer.rs index 832c9a7..cac4793 100644 --- a/typebuild/src/type/class/referrer.rs +++ b/rust/type/class/referrer.rs @@ -1,8 +1,7 @@ use bon::Builder; -use crate::guid::TypeGUID; -use crate::Build; -use fbcg_rust::fb_type as fb; +use crate::fb_type as fb; +use crate::r#type::guid::TypeGUID; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::{Alphanumeric, DistString, Distribution, Standard}; use rand::Rng; @@ -17,22 +16,11 @@ impl ReferrerClass { pub fn new(guid: Option, name: Option) -> Self { Self { guid, name } } -} - -// TODO: We really should make this TryFrom -impl From> for ReferrerClass { - fn from(value: fb::Referrer<'_>) -> Self { - Self { - guid: value.guid().map(|s| s.parse().unwrap()), - name: value.name().map(|x| x.to_owned()), - } - } -} - -impl Build for ReferrerClass { - type FBType<'a> = fb::Referrer<'a>; - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { let guid = self .guid .as_ref() @@ -40,10 +28,15 @@ impl Build for ReferrerClass { let name = self.name.as_ref().map(|x| builder.create_string(x)); fb::Referrer::create(builder, &fb::ReferrerArgs { guid, name }) } +} - // NOTE: Must resolve the reference to get the size. - fn size(&self) -> Option { - None +// TODO: We really should make this TryFrom +impl From> for ReferrerClass { + fn from(value: fb::Referrer<'_>) -> Self { + Self { + guid: value.guid().map(|s| s.parse().unwrap()), + name: value.name().map(|x| x.to_owned()), + } } } diff --git a/typebuild/src/type/class/structure.rs b/rust/type/class/structure.rs similarity index 89% rename from typebuild/src/type/class/structure.rs rename to rust/type/class/structure.rs index 915b833..58e71dc 100644 --- a/typebuild/src/type/class/structure.rs +++ b/rust/type/class/structure.rs @@ -1,8 +1,7 @@ use bon::{bon, Builder}; +use crate::fb_type as fb; use crate::r#type::Type; -use crate::Build; -use fbcg_rust::fb_type as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::{Alphanumeric, DistString, Distribution, Standard}; use rand::Rng; @@ -36,21 +35,11 @@ impl StructureMember { } } -impl From> for StructureMember { - fn from(value: fb::StructureMember<'_>) -> Self { - Self { - name: value.name().map(str::to_string), - offset: value.offset().into(), - ty: value.type_().into(), - modifiers: value.modifiers(), - } - } -} - -impl Build for StructureMember { - type FBType<'a> = fb::StructureMember<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl StructureMember { + fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { let name = self.name.as_ref().map(|n| builder.create_string(n)); let member_type = self.ty.create(builder); fb::StructureMember::create( @@ -69,6 +58,17 @@ impl Build for StructureMember { } } +impl From> for StructureMember { + fn from(value: fb::StructureMember<'_>) -> Self { + Self { + name: value.name().map(str::to_string), + offset: value.offset().into(), + ty: value.type_().into(), + modifiers: value.modifiers(), + } + } +} + impl Distribution for Standard { fn sample(&self, rng: &mut R) -> StructureMember { let mut modifiers = StructureMemberModifiers::empty(); @@ -103,10 +103,11 @@ impl StructureClass { } } -impl Build for StructureClass { - type FBType<'a> = fb::Structure<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl StructureClass { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { let created_members: Vec<_> = self .members .iter() @@ -121,7 +122,7 @@ impl Build for StructureClass { ) } - fn size(&self) -> Option { + pub fn size(&self) -> Option { self.members.iter().fold(None, |size, member| { // If an unknown member size is encountered, the structure size has to be unknown. Some((member.offset + member.size()?).max(size.unwrap_or(0))) diff --git a/typebuild/src/type/class/union.rs b/rust/type/class/union.rs similarity index 92% rename from typebuild/src/type/class/union.rs rename to rust/type/class/union.rs index 03fe072..abda063 100644 --- a/typebuild/src/type/class/union.rs +++ b/rust/type/class/union.rs @@ -1,8 +1,7 @@ use bon::Builder; +use crate::fb_type as fb; use crate::r#type::Type; -use crate::Build; -use fbcg_rust::fb_type as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; use rand::distributions::{Alphanumeric, DistString, Distribution, Standard}; use rand::Rng; @@ -18,21 +17,8 @@ impl UnionMember { pub fn new(name: String, ty: Type) -> Self { Self { name, ty } } -} - -impl From> for UnionMember { - fn from(value: fb::UnionMember<'_>) -> Self { - Self { - name: value.name().unwrap().to_string(), - ty: value.type_().into(), - } - } -} -impl Build for UnionMember { - type FBType<'a> = fb::UnionMember<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { + fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { let name = builder.create_string(&self.name); let member_type = self.ty.create(builder); fb::UnionMember::create( @@ -49,6 +35,15 @@ impl Build for UnionMember { } } +impl From> for UnionMember { + fn from(value: fb::UnionMember<'_>) -> Self { + Self { + name: value.name().unwrap().to_string(), + ty: value.type_().into(), + } + } +} + impl Distribution for Standard { fn sample(&self, rng: &mut R) -> UnionMember { UnionMember { @@ -67,20 +62,11 @@ impl UnionClass { pub fn new(members: Vec) -> Self { Self { members } } -} - -impl From> for UnionClass { - fn from(value: fb::Union<'_>) -> Self { - Self { - members: value.members().unwrap().iter().map(Into::into).collect(), - } - } -} - -impl Build for UnionClass { - type FBType<'a> = fb::Union<'a>; - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { let resolved_members: Vec<_> = self .members .iter() @@ -95,15 +81,23 @@ impl Build for UnionClass { ) } - fn size(&self) -> Option { + pub fn size(&self) -> Option { // Get the largest union member. self.members .iter() - .max_by(|x, y| x.ty.size().cmp(&y.ty.size())) + .max_by(|x, y| x.size().cmp(&y.size())) .map(|z| z.ty.size())? } } +impl From> for UnionClass { + fn from(value: fb::Union<'_>) -> Self { + Self { + members: value.members().unwrap().iter().map(Into::into).collect(), + } + } +} + impl Distribution for Standard { fn sample(&self, rng: &mut R) -> UnionClass { let rand_in_member_len = rng.gen_range(0..20); @@ -115,7 +109,9 @@ impl Distribution for Standard { #[cfg(test)] mod tests { - use crate::prelude::*; + use crate::r#type::class::{IntegerClass, TypeClass, UnionClass, UnionMember}; + use crate::r#type::guid::TypeGUID; + use crate::r#type::{Alignment, Type}; use uuid::{uuid, Uuid}; const UNION_TYPE_UUID: Uuid = uuid!("1570a765-3626-5c97-a18e-4b4652a77398"); diff --git a/typebuild/src/type/class/void.rs b/rust/type/class/void.rs similarity index 77% rename from typebuild/src/type/class/void.rs rename to rust/type/class/void.rs index 4d2ea7d..d561a5d 100644 --- a/typebuild/src/type/class/void.rs +++ b/rust/type/class/void.rs @@ -1,25 +1,23 @@ -use crate::Build; -use fbcg_rust::fb_type as fb; +use crate::fb_type as fb; use flatbuffers::{FlatBufferBuilder, WIPOffset}; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct VoidClass; -impl Build for VoidClass { - type FBType<'a> = fb::Void<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { +impl VoidClass { + pub(crate) fn create<'a>( + &self, + builder: &mut FlatBufferBuilder<'a>, + ) -> WIPOffset> { fb::Void::create(builder, &fb::VoidArgs {}) } - - fn size(&self) -> Option { - None - } } #[cfg(test)] mod tests { - use crate::prelude::*; + use crate::r#type::class::TypeClass; + use crate::r#type::guid::TypeGUID; + use crate::r#type::{Alignment, Type}; use uuid::{uuid, Uuid}; const VOID_TYPE_UUID: Uuid = uuid!("c37a394d-750b-5e89-b09e-539859c7a9bd"); diff --git a/typebuild/src/guid.rs b/rust/type/guid.rs similarity index 100% rename from typebuild/src/guid.rs rename to rust/type/guid.rs diff --git a/typebuild/src/type/modifier.rs b/rust/type/modifier.rs similarity index 99% rename from typebuild/src/type/modifier.rs rename to rust/type/modifier.rs index ae68cbc..81f3f98 100644 --- a/typebuild/src/type/modifier.rs +++ b/rust/type/modifier.rs @@ -1,6 +1,6 @@ use bon::Builder; -use fbcg_rust::fb_type as fb; +use crate::fb_type as fb; use flatbuffers::{FlatBufferBuilder, UnionWIPOffset, WIPOffset}; #[derive(Clone, Debug, PartialEq, Hash, Eq)] diff --git a/sigem/Cargo.toml b/sigem/Cargo.toml deleted file mode 100644 index af74447..0000000 --- a/sigem/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "sigem" -version = "0.1.0" -edition = "2021" - -[dependencies] -binaryninja = { workspace = true } -env_logger = "0.11.5" -log = "0.4" -signaturebuild = { path = "../signaturebuild" } -warp_binja = { path = "../warp_binja" } -clap = { version = "4.5.16", features = ["derive"] } -rayon = "1.10.0" -ar = { git = "https://github.com/mdsteele/rust-ar" } -tempdir = "0.3.7" -serde_json = "1.0.132" - - -[build-dependencies] -cc = "1.1.28" \ No newline at end of file diff --git a/sigem/build.rs b/sigem/build.rs deleted file mode 100644 index d89860a..0000000 --- a/sigem/build.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::path::PathBuf; -use std::process::Command; - -fn compile_rust(file: PathBuf) -> bool { - let out_dir = std::env::var_os("OUT_DIR").unwrap(); - let rustc = std::env::var_os("RUSTC").unwrap(); - let rustc = rustc.to_str().unwrap(); - let mut rustc = rustc.split('\x1f'); - let mut cmd = Command::new(rustc.next().unwrap()); - cmd.args(rustc) - .arg("--crate-type=rlib") - .arg("--out-dir") - .arg(out_dir) - .arg(file); - cmd.status().expect("failed to invoke rustc").success() -} - -fn main() { - let link_path = - std::env::var_os("DEP_BINARYNINJACORE_PATH").expect("DEP_BINARYNINJACORE_PATH specified"); - let out_dir = std::env::var_os("OUT_DIR").expect("OUT_DIR specified"); - let out_dir_path = PathBuf::from(out_dir); - - println!("cargo::rustc-link-lib=dylib=binaryninjacore"); - println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); - - #[cfg(not(target_os = "windows"))] - { - println!( - "cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", - link_path.to_string_lossy() - ); - } - - // Copy all binaries to OUT_DIR for unit tests. - let bin_dir: PathBuf = "fixtures/bin".into(); - if let Ok(entries) = std::fs::read_dir(bin_dir) { - for entry in entries { - let entry = entry.unwrap(); - let path = entry.path(); - if path.is_file() { - let file_name = path.file_name().unwrap(); - let dest_path = out_dir_path.join(file_name); - std::fs::copy(&path, &dest_path).expect("failed to copy binary to OUT_DIR"); - } - } - } - - // Compile all .c files in fixtures/src directory for unit tests. - let src_dir: PathBuf = "fixtures/src".into(); - if let Ok(entries) = std::fs::read_dir(src_dir) { - for entry in entries { - let entry = entry.unwrap(); - let path = entry.path(); - match path.extension().map(|s| s.to_str().unwrap()) { - Some("c") => { - cc::Build::new() - .file(&path) - .compile(path.file_stem().unwrap().to_str().unwrap()); - } - Some("rs") => { - compile_rust(path); - } - _ => {} - } - } - } -} diff --git a/sigem/fixtures/bin/librustlibrary.rlib b/sigem/fixtures/bin/librustlibrary.rlib deleted file mode 100644 index 95b8e70ed6ffb75acf3ea462d9d8198ce9c059af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32312 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 HYGVHf^CJUx diff --git a/sigem/fixtures/src/library.c b/sigem/fixtures/src/library.c deleted file mode 100644 index 66a84ad..0000000 --- a/sigem/fixtures/src/library.c +++ /dev/null @@ -1,33 +0,0 @@ -#include - -#include "library.h" - -int myFunction(int x) -{ - printf("%d\n", x); - return x; -} - -int recursiveFunc(int x); -int otherFunction(int x) -{ - x += 5; - if (x < 10) return otherFunction(x); - return recursiveFunc(x); -} - -int recursiveFunc(int x) -{ - if (x <= 0) return 0; - return x + otherFunction(x - 1); -} - -struct MyStruct myFunction2(int x) -{ - printf("MyStruct %d\n", x); - struct MyStruct myStruct; - myStruct.a = recursiveFunc(x); - myStruct.b = x * 10; - myStruct.c = "my struct"; - return myStruct; -} \ No newline at end of file diff --git a/sigem/fixtures/src/library.h b/sigem/fixtures/src/library.h deleted file mode 100644 index ec7a6e6..0000000 --- a/sigem/fixtures/src/library.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -struct MyStruct { - int a; - int b; - const char* c; - struct MyStruct* d; -}; - -int myFunction(int x); -struct MyStruct myFunction2(int x); \ No newline at end of file diff --git a/sigem/fixtures/src/library.rs b/sigem/fixtures/src/library.rs deleted file mode 100644 index 4e45bc9..0000000 --- a/sigem/fixtures/src/library.rs +++ /dev/null @@ -1,22 +0,0 @@ -#[derive(Debug, Hash, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] -pub struct MyStruct { - a: i32, - b: u32, - c: *mut MyStruct, -} - -impl MyStruct { - pub fn new(a: i32, b: u32, c: *mut MyStruct) -> MyStruct { - MyStruct { a, b, c } - } - - pub fn hello(&self) { - println!("hello from MyStruct! a: {} b: {}", self.a, self.b); - } -} - -pub fn main() { - let my_struct = MyStruct::new(1, 2, 0xfffff as *mut MyStruct); - my_struct.hello(); - println!("{:#?}", my_struct); -} \ No newline at end of file diff --git a/sigem/fixtures/src/simple.c b/sigem/fixtures/src/simple.c deleted file mode 100644 index 6e7fbd4..0000000 --- a/sigem/fixtures/src/simple.c +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#include "library.h" - -int simple() { - printf("This is main!\n"); - - printf("calling myFunction\n"); - int returnVal = myFunction(55); - - printf("calling myFunction2\n"); - struct MyStruct myStruct = myFunction2(returnVal); - - return myStruct.b; -} \ No newline at end of file diff --git a/sigem/scripts/README.md b/sigem/scripts/README.md deleted file mode 100644 index d2f4369..0000000 --- a/sigem/scripts/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# TODO - -## `downpad.py` - -### Purpose - -The `downpad.py` script is used to download `.deb` files from launchpad, these `.deb` files include the static libraries (namely `libc.a`) we are interested in. - -### Examples - -To download all `opensc` packages for arch `arm64`: - -```bash -python downpad.py --package opensc --arch arm64 -``` - -To download all `downloaded_packages` for `downloaded_archs`: - -```bash -python downpad.py -``` - -## `downwind.py` - -### Purpose - -The `downwind.py` script is used to download msvc files from microsoft, these files include the static libraries (namely `msvcrt.lib`) we are interested in. - -### Examples - -Download all targets (`x86`, `arm`, `arm64`, `x64`) for the latest version: - -```bash -python downpad.py --accept-license -``` - -## `extract_archives.py` - -### Purpose - -The `extract_archives.py` script is used after running `downpad.py` to extract `.deb` files into their contents, namely the static libraries. - -### Examples - -To extract all `.deb` (and other formats) in the CWD: - -```bash -python extract_archives.py -``` \ No newline at end of file diff --git a/sigem/scripts/downpad.py b/sigem/scripts/downpad.py deleted file mode 100644 index fb259b1..0000000 --- a/sigem/scripts/downpad.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python3 -""" -This script downloads .debs for the packages off of Ubuntu launchpad. -""" - -# TODO: Support debian - -import sys, os - -import urllib - -import aiohttp -import asyncio - -from bs4 import BeautifulSoup - -downloaded_packages = ['libc6-dev', 'libgcc-8-dev', 'libgcc-7-dev', 'libgcc-6-dev', 'libgcc-5-dev'] -downloaded_archs = ['i386', 'amd64', 'arm64'] - -session, sem = None, None -async def must(f): - global session, sem - await sem.put(None) - retries = 0 - while True: - try: - r = await f(session) - if r.status == 200: break - print(r.status) - except: pass - retries += 1 - if retries > 10: - print('Maximum retry count exceeded') - sys.exit(1) - await asyncio.sleep(1.0) - await sem.get() - return r - -async def get_html(url): - async with (await must(lambda session: session.get(url))) as resp: - sys.stderr.write('GET ' + url + '\n') - return BeautifulSoup(await resp.text(), features="html.parser") - -async def get_series(): - series = set() - soup = await get_html('https://launchpad.net/ubuntu/+series') - for strong in soup.find_all('strong'): - for a in strong.find_all('a'): - series.add(a['href']) - return series - -async def get_archs(series): - soup = await get_html('https://launchpad.net' + series + '/+builds') - for select in soup.find_all('select', {'id': 'arch_tag'}): - for option in select.find_all('option'): - arch = option['value'] - if arch == 'all': continue - if arch not in downloaded_archs: continue - yield series + '/' + arch - -async def get_versions(arch, package): - soup = await get_html('https://launchpad.net' + arch + '/' + package) - for tr in soup.find_all('tr'): - if len(tr.find_all('td')) != 10: continue - yield tr.find_all('td')[9].find_all('a')[0]['href'] - -async def get_deb_link(version): - soup = await get_html('https://launchpad.net' + version) - for a in soup.find_all('a', {'class': 'sprite'}): - if a['href'].endswith('.deb'): - return a['href'] - -async def download_deb(version, deb_url): - filename = urllib.parse.urlparse(deb_url).path - filename = filename[filename.rindex('/') + 1:] - version = os.curdir + version - filename = os.path.join(version, filename) - if os.path.exists(filename): - print('Skipping existing file', filename) - return - os.makedirs(version, exist_ok=True) - async with (await must(lambda session: session.get(deb_url))) as resp: - data = await resp.read() - if not data: - print('FAILED DOWNLOAD', filename, 'from', deb_url) - return - with open(filename, 'wb') as f: - f.write(data) - print('Downloaded', filename) - -async def process_version(version): - deb_link = await get_deb_link(version) - if deb_link: - await download_deb(version, deb_link) - else: - print('No .deb for', version) - -async def process_arch(arch): - await asyncio.gather(*[asyncio.create_task(process_version(version)) for package in downloaded_packages async for version in get_versions(arch, package)]) - -async def process_series(series): - await asyncio.gather(*[asyncio.create_task(process_arch(arch)) async for arch in get_archs(series)]) - -async def main(): - global session - async with aiohttp.ClientSession() as session: - await asyncio.gather(*[asyncio.create_task(process_series(series)) for series in await get_series()]) - - -import argparse - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--package', action='append', help="Specify additional packages to download") - parser.add_argument('--arch', action='append', help="Specify additional architectures to download") - args = parser.parse_args() - - if args.package: - downloaded_packages = args.package - if args.arch: - downloaded_archs = args.arch - - MAX_CONCURRENT = 16 - loop = asyncio.get_event_loop() - sem = asyncio.Queue(loop=loop, maxsize=MAX_CONCURRENT) - loop.run_until_complete(main()) - loop.close() diff --git a/sigem/scripts/downwind.py b/sigem/scripts/downwind.py deleted file mode 100644 index 5bec390..0000000 --- a/sigem/scripts/downwind.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/env python3 -""" -This script downloads msvc toolchains (including libraries), adapted from https://gist.github.com/mmozeiko/7f3162ec2988e81e56d5c4e22cde9977 -""" - -import io -import json -import hashlib -import zipfile -import argparse -import urllib.error -import urllib.request -from pathlib import Path - -OUTPUT = Path("msvc") # output folder -DOWNLOADS = Path("downloads") # temporary download files - -# NOTE: not all host & target architecture combinations are supported - -DEFAULT_HOST = "x64" -ALL_HOSTS = "x64 x86 arm64".split() - -DEFAULT_TARGET = "x64,x86,arm,arm64" -ALL_TARGETS = "x64 x86 arm arm64".split() - -MANIFEST_URL = "https://aka.ms/vs/17/release/channel" -MANIFEST_PREVIEW_URL = "https://aka.ms/vs/17/pre/channel" - -ssl_context = None - -def download(url): - with urllib.request.urlopen(url, context=ssl_context) as res: - return res.read() - -total_download = 0 - -def download_progress(url, check, filename): - fpath = DOWNLOADS / filename - if fpath.exists(): - data = fpath.read_bytes() - if hashlib.sha256(data).hexdigest() == check.lower(): - print(f"\r{filename} ... OK") - return data - - global total_download - with fpath.open("wb") as f: - data = io.BytesIO() - with urllib.request.urlopen(url, context=ssl_context) as res: - total = int(res.headers["Content-Length"]) - size = 0 - while True: - block = res.read(1<<20) - if not block: - break - f.write(block) - data.write(block) - size += len(block) - perc = size * 100 // total - print(f"\r{filename} ... {perc}%", end="") - print() - data = data.getvalue() - digest = hashlib.sha256(data).hexdigest() - if check.lower() != digest: - exit(f"Hash mismatch for f{pkg}") - total_download += len(data) - return data - -# super crappy msi format parser just to find required .cab files -def get_msi_cabs(msi): - index = 0 - while True: - index = msi.find(b".cab", index+4) - if index < 0: - return - yield msi[index-32:index+4].decode("ascii") - -def first(items, cond = lambda x: True): - return next((item for item in items if cond(item)), None) - - -### parse command-line arguments - -ap = argparse.ArgumentParser() -ap.add_argument("--show-versions", action="store_true", help="Show available MSVC") -ap.add_argument("--accept-license", action="store_true", help="Automatically accept license") -ap.add_argument("--msvc-version", help="Get specific MSVC version") -ap.add_argument("--preview", action="store_true", help="Use preview channel for Preview versions") -ap.add_argument("--target", default=DEFAULT_TARGET, help=f"Target architectures, comma separated ({','.join(ALL_TARGETS)})") -ap.add_argument("--host", default=DEFAULT_HOST, help=f"Host architecture", choices=ALL_HOSTS) -args = ap.parse_args() - -host = args.host -targets = args.target.split(',') -for target in targets: - if target not in ALL_TARGETS: - exit(f"Unknown {target} target architecture!") - - -### get main manifest - -URL = MANIFEST_PREVIEW_URL if args.preview else MANIFEST_URL - -try: - manifest = json.loads(download(URL)) -except urllib.error.URLError as err: - import ssl - if isinstance(err.args[0], ssl.SSLCertVerificationError): - # for more info about Python & issues with Windows certificates see https://stackoverflow.com/a/52074591 - print("ERROR: ssl certificate verification error") - try: - import certifi - except ModuleNotFoundError: - print("ERROR: please install 'certifi' package to use Mozilla certificates") - print("ERROR: or update your Windows certs, see instructions here: https://woshub.com/updating-trusted-root-certificates-in-windows-10/#h2_3") - exit() - print("NOTE: retrying with certifi certificates") - ssl_context = ssl.create_default_context(cafile=certifi.where()) - manifest = json.loads(download(URL)) - else: - raise - -### download VS manifest - -ITEM_NAME = "Microsoft.VisualStudio.Manifests.VisualStudioPreview" if args.preview else "Microsoft.VisualStudio.Manifests.VisualStudio" - -vs = first(manifest["channelItems"], lambda x: x["id"] == ITEM_NAME) -payload = vs["payloads"][0]["url"] - -vsmanifest = json.loads(download(payload)) - - -### find MSVC versions - -packages = {} -for p in vsmanifest["packages"]: - packages.setdefault(p["id"].lower(), []).append(p) - -msvc = {} - -for pid,p in packages.items(): - if pid.startswith("Microsoft.VisualStudio.Component.VC.".lower()) and pid.endswith(".x86.x64".lower()): - pver = ".".join(pid.split(".")[4:6]) - if pver[0].isnumeric(): - msvc[pver] = pid - -if args.show_versions: - print("MSVC versions:", " ".join(sorted(msvc.keys()))) - exit(0) - -msvc_ver = args.msvc_version or max(sorted(msvc.keys())) - -if msvc_ver in msvc: - msvc_pid = msvc[msvc_ver] - msvc_ver = ".".join(msvc_pid.split(".")[4:-2]) -else: - exit(f"Unknown MSVC version: f{args.msvc_version}") - -print(f"Downloading MSVC v{msvc_ver}") - - -### agree to license - -tools = first(manifest["channelItems"], lambda x: x["id"] == "Microsoft.VisualStudio.Product.BuildTools") -resource = first(tools["localizedResources"], lambda x: x["language"] == "en-us") -license = resource["license"] - -if not args.accept_license: - accept = input(f"Do you accept Visual Studio license at {license} [Y/N] ? ") - if not accept or accept[0].lower() != "y": - exit(0) - -OUTPUT.mkdir(exist_ok=True) -DOWNLOADS.mkdir(exist_ok=True) - - -### download MSVC - -msvc_packages = [ - f"microsoft.visualcpp.dia.sdk", - f"microsoft.vc.{msvc_ver}.crt.headers.base", - f"microsoft.vc.{msvc_ver}.crt.source.base", - f"microsoft.vc.{msvc_ver}.asan.headers.base", - f"microsoft.vc.{msvc_ver}.pgo.headers.base", -] - -for target in targets: - msvc_packages += [ - f"microsoft.vc.{msvc_ver}.tools.host{host}.target{target}.base", - f"microsoft.vc.{msvc_ver}.tools.host{host}.target{target}.res.base", - f"microsoft.vc.{msvc_ver}.crt.{target}.desktop.base", - f"microsoft.vc.{msvc_ver}.crt.{target}.store.base", - f"microsoft.vc.{msvc_ver}.premium.tools.host{host}.target{target}.base", - f"microsoft.vc.{msvc_ver}.pgo.{target}.base", - ] - if target in ["x86", "x64"]: - msvc_packages += [f"microsoft.vc.{msvc_ver}.asan.{target}.base"] - - redist_suffix = ".onecore.desktop" if target == "arm" else "" - redist_pkg = f"microsoft.vc.{msvc_ver}.crt.redist.{target}{redist_suffix}.base" - if redist_pkg not in packages: - redist_name = f"microsoft.visualcpp.crt.redist.{target}{redist_suffix}" - redist = first(packages[redist_name]) - redist_pkg = first(redist["dependencies"], lambda dep: dep.endswith(".base")).lower() - msvc_packages += [redist_pkg] - -for pkg in sorted(msvc_packages): - if pkg not in packages: - print(f"\r{pkg} ... !!! MISSING !!!") - continue - p = first(packages[pkg], lambda p: p.get("language") in (None, "en-US")) - for payload in p["payloads"]: - filename = payload["fileName"] - download_progress(payload["url"], payload["sha256"], filename) - with zipfile.ZipFile(DOWNLOADS / filename) as z: - for name in z.namelist(): - if name.startswith("Contents/"): - out = OUTPUT / Path(name).relative_to("Contents") - out.parent.mkdir(parents=True, exist_ok=True) - out.write_bytes(z.read(name)) \ No newline at end of file diff --git a/sigem/scripts/extract_archives.py b/sigem/scripts/extract_archives.py deleted file mode 100644 index fc037d0..0000000 --- a/sigem/scripts/extract_archives.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python3 - -import os -import xtarfile -import tarfile -from operator import contains -from ar import Archive - - -def extract_deb_files_in_dir(directory): - for root, dirs, files in os.walk(directory): - for file in files: - if file.endswith('.deb'): - file_path = os.path.join(root, file) - try: - with open(file_path, 'rb') as f: - archive = Archive(f) - entry_dir = file_path.strip(".deb") + "/" - os.makedirs(entry_dir, exist_ok=True) - for entry in archive: - entry_path = entry_dir + entry.name - if os.path.exists(entry_path): - print('Skipping existing deb entry', entry_path) - continue - with open(entry_path, 'wb') as output: - content = archive.open(entry, 'rb').read() - output.write(content) - except IOError as e: - print(f"Failed to extract {file_path}: {e}") - -def extract_tar_files_in_dir(directory): - for root, dirs, files in os.walk(directory): - for file in files: - file_path = os.path.join(root, file) - if contains(file_path, '.tar'): - extract_dir = file_path.split('.tar')[0] - try: - if os.path.exists(extract_dir): - print('Skipping existing tar extract', extract_dir) - continue - os.makedirs(extract_dir, exist_ok=True) - with xtarfile.open(file_path, mode='r') as tar: - tar.extractall(path=extract_dir) - except tarfile.TarError as e: - print(f"Failed to extract {file_path}: {e}") - -# Run the extraction in the current working directory -if __name__ == "__main__": - cwd = os.getcwd() - extract_deb_files_in_dir(cwd) - extract_tar_files_in_dir(cwd) - diff --git a/sigem/src/main.rs b/sigem/src/main.rs deleted file mode 100644 index c3b22e0..0000000 --- a/sigem/src/main.rs +++ /dev/null @@ -1,177 +0,0 @@ -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::binaryview::{BinaryView, BinaryViewExt}; -use serde_json::json; - -#[derive(Parser, Debug)] -#[command(version, about, long_about = None)] -struct Args { - /// Path of the binary/BNDB to generate signatures of - #[arg(index = 1)] - binary: PathBuf, - - /// The signature output file - #[arg(index = 2)] - output: Option, - - /// Should we overwrite output file - /// - /// NOTE: If the file exists we will exit early to prevent wasted effort. - /// NOTE: If the file is created while we are running it will still be overwritten. - #[arg(short, long)] - overwrite: Option, - - /// The external debug information file to use - #[arg(short, long)] - debug_info: Option, -} - -fn main() { - let args = Args::parse(); - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); - - // If no output file was given, just prepend binary with extension sbin - let output_file = args.output.unwrap_or(args.binary.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(); - - log::info!("Creating functions for {:?}...", args.binary); - let start = std::time::Instant::now(); - let data = data_from_file(&args.binary).expect("Failed to read data"); - 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.binary); - } -} - -fn data_from_view(view: &BinaryView) -> signaturebuild::Data { - let mut data = signaturebuild::Data::default(); - - let functions = view - .functions() - .par_iter() - .filter(|f| !f.symbol().short_name().as_str().contains("sub_") || f.has_user_annotations()) - .filter_map(|f| { - let llil = f.low_level_il().ok()?; - warp_binja::cache::cached_function(&f, &llil) - }) - .collect::>(); - - data.functions = functions; - data -} - -fn data_from_archive(mut archive: Archive) -> Option { - let mut data = signaturebuild::Data::default(); - // 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 = Vec::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); - 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.push(output_path); - } - Err(e) => { - log::error!("Failed to read archive entry: {}", e); - } - } - } - - // Create the data. - // TODO: into_par_iter will corrupt the heap frequently, you should basically always restrict - // TODO: With RAYON_NUM_THREADS (set to like... 1) - let entry_data = entry_files - .into_par_iter() - .filter_map(|path| { - log::debug!("Creating data for ENTRY {:?}...", path); - data_from_file(&path) - }) - .collect::>(); - - // TODO: Cloning here is unnecessary - data.functions = entry_data - .iter() - .flat_map(|d| d.functions.to_owned()) - .collect(); - data.types = entry_data.iter().flat_map(|d| d.types.to_owned()).collect(); - - Some(data) -} - -// TODO: Pass settings. -fn data_from_file(path: &Path) -> Option { - // TODO: Add external debug info files. - // TODO: Support IDB's through debug info - let settings_json = json!({ - "analysis.linearSweep.autorun": true, - "analysis.signatureMatcher.autorun": false, - "analysis.plugins.WARPMatcher": true, - }); - - 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(archive) - } - _ => { - let path_str = path.to_str().unwrap(); - let view = - binaryninja::load_with_options(path_str, true, Some(settings_json.to_string()))?; - Some(data_from_view(&view)) - } - } -} - -#[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::().unwrap(); - let _headless_session = binaryninja::headless::Session::new(); - 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(&path); - assert!(result.is_some()); - } - } - } -} diff --git a/signaturebuild/Cargo.toml b/signaturebuild/Cargo.toml deleted file mode 100644 index 1456c7d..0000000 --- a/signaturebuild/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "signaturebuild" -version = "0.1.0" -edition = "2021" - -[dependencies] -fbcg_rust = { path = "../fbcg_rust" } -flatbuffers = "24.3.25" -bon = "2.3.0" -uuid = { version = "1.11.0", features = ["v5"]} -typebuild = { path = "../typebuild" } -symbolbuild = { path = "../symbolbuild" } - -[dev-dependencies] -criterion = "0.5.1" \ No newline at end of file diff --git a/sigview/Cargo.toml b/sigview/Cargo.toml index c103f58..aa13230 100644 --- a/sigview/Cargo.toml +++ b/sigview/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -signaturebuild = { path = "../signaturebuild" } +warp = { path = "../" } log = "0.4" env_logger = "0.11.5" eframe = "0.29.1" diff --git a/sigview/src/graph.rs b/sigview/src/graph.rs index 049c209..0142e65 100644 --- a/sigview/src/graph.rs +++ b/sigview/src/graph.rs @@ -2,7 +2,7 @@ use eframe::egui; use eframe::egui::Ui; use egui_graphs::{Graph, GraphView, SettingsInteraction, SettingsNavigation, SettingsStyle}; use petgraph::prelude::StableGraph; -use signaturebuild::prelude::{Function, FunctionGUID}; +use warp::signature::function::{Function, FunctionGUID}; #[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct FunctionNode { diff --git a/sigview/src/main.rs b/sigview/src/main.rs index d9d63f1..58921f0 100644 --- a/sigview/src/main.rs +++ b/sigview/src/main.rs @@ -6,7 +6,8 @@ use crate::graph::FunctionGraph; use eframe::egui; use eframe::egui::{Direction, Layout, ScrollArea, Ui}; use egui_virtual_list::VirtualList; -use signaturebuild::prelude::*; +use warp::signature::Data; +use warp::signature::function::Function; // TODO: Add some collision viewer and graph viewer // TODO: Load multiple Data for collision. // TODO: Type viewer (data.types) diff --git a/symbolbuild/Cargo.toml b/symbolbuild/Cargo.toml deleted file mode 100644 index 907e43e..0000000 --- a/symbolbuild/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "symbolbuild" -version = "0.1.0" -edition = "2021" - -[dependencies] -fbcg_rust = { path = "../fbcg_rust" } -flatbuffers = "24.3.25" - -[dev-dependencies] -criterion = "0.5.1" \ No newline at end of file diff --git a/symbolbuild/src/lib.rs b/symbolbuild/src/lib.rs deleted file mode 100644 index 0daabfd..0000000 --- a/symbolbuild/src/lib.rs +++ /dev/null @@ -1,180 +0,0 @@ -use fbcg_rust::fb_symbol as fb; -use flatbuffers::{FlatBufferBuilder, UnionWIPOffset, WIPOffset}; - -pub mod prelude { - pub use crate::{FunctionSymbolClass, Symbol, SymbolClass, SymbolModifier}; -} - -pub trait Create { - type FBType<'a>; - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset>; -} - -#[derive(Clone, Debug, Default)] -pub struct FunctionSymbolClass; - -impl Create for FunctionSymbolClass { - type FBType<'a> = fb::FunctionSymbolClass<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { - fb::FunctionSymbolClass::create(builder, &fb::FunctionSymbolClassArgs {}) - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub enum SymbolModifier { - External, - Exported, -} - -impl From for fb::SymbolModifiers { - fn from(value: SymbolModifier) -> Self { - match value { - SymbolModifier::External => Self::External, - SymbolModifier::Exported => Self::Exported, - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct DataSymbolClass; - -impl Create for DataSymbolClass { - type FBType<'a> = fb::DataSymbolClass<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { - fb::DataSymbolClass::create(builder, &fb::DataSymbolClassArgs {}) - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub enum SymbolClass { - Function, - Data, -} - -impl SymbolClass { - pub fn ty(&self) -> fb::SymbolClass { - match self { - SymbolClass::Function => fb::SymbolClass::FunctionSymbolClass, - SymbolClass::Data => fb::SymbolClass::DataSymbolClass, - } - } -} - -impl From> for SymbolClass { - fn from(_value: fb::FunctionSymbolClass<'_>) -> Self { - Self::Function - } -} - -impl From> for SymbolClass { - fn from(_value: fb::DataSymbolClass<'_>) -> Self { - Self::Data - } -} - -impl Create for SymbolClass { - type FBType<'a> = UnionWIPOffset; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { - match self { - SymbolClass::Function => FunctionSymbolClass.create(builder).as_union_value(), - SymbolClass::Data => DataSymbolClass.create(builder).as_union_value(), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -pub struct Symbol { - pub name: String, - pub modifiers: Vec, - pub class: SymbolClass, -} - -impl Symbol { - pub fn new( - name: impl Into, - class: SymbolClass, - modifiers: Vec, - ) -> Self { - Self { - name: name.into(), - modifiers, - class, - } - } -} - -impl From> for Symbol { - fn from(value: fb::Symbol<'_>) -> Self { - // TODO: I would like this conversion to be on `SymbolClass` instead. - let class = match value.class_type() { - fb::SymbolClass::FunctionSymbolClass => { - SymbolClass::from(value.class_as_function_symbol_class().unwrap()) - } - fb::SymbolClass::DataSymbolClass => { - SymbolClass::from(value.class_as_data_symbol_class().unwrap()) - } - _ => unreachable!(), - }; - - let name = value.name().unwrap().to_string(); - - // TODO: This is so ugly. - let mut modifiers: Vec = Vec::new(); - if value.modifiers().contains(fb::SymbolModifiers::External) { - modifiers.push(SymbolModifier::External); - } - if value.modifiers().contains(fb::SymbolModifiers::Exported) { - modifiers.push(SymbolModifier::Exported); - } - - Self { - name, - modifiers, - class, - } - } -} - -impl Create for Symbol { - type FBType<'a> = fb::Symbol<'a>; - - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset> { - let name = builder.create_string(&self.name); - let class_type = self.class.ty(); - let class = self.class.create(builder); - - let modifiers = self - .modifiers - .iter() - .fold(fb::SymbolModifiers::default(), |m, &b| m | b.into()); - fb::Symbol::create( - builder, - &fb::SymbolArgs { - name: Some(name), - modifiers, - class_type, - class: Some(class), - }, - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use flatbuffers::FlatBufferBuilder; - - #[test] - fn it_works() { - let mut builder = FlatBufferBuilder::with_capacity(100); - let symbol = Symbol { - name: "".to_string(), - modifiers: vec![], - class: SymbolClass::Data, - }; - let created_symbol = symbol.create(&mut builder); - } -} diff --git a/typebuild/Cargo.toml b/typebuild/Cargo.toml deleted file mode 100644 index c6219e6..0000000 --- a/typebuild/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "typebuild" -version = "0.1.0" -edition = "2021" - -[dependencies] -fbcg_rust = { path = "../fbcg_rust" } -flatbuffers = "24.3.25" -bon = "2.3.0" -uuid = { version = "1.11.0", features = ["v5"] } -rand = "0.8.5" - -[dev-dependencies] -criterion = "0.5.1" - -[[bench]] -name = "void" -harness = false \ No newline at end of file diff --git a/typebuild/src/lib.rs b/typebuild/src/lib.rs deleted file mode 100644 index ee96d9a..0000000 --- a/typebuild/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ -use flatbuffers::{FlatBufferBuilder, WIPOffset}; - -pub mod guid; -pub mod r#type; - -pub mod prelude { - pub use crate::guid::TypeGUID; - pub use crate::r#type::class::{ - ArrayClass, BooleanClass, CallingConvention, CharacterClass, EnumerationClass, - EnumerationMember, FloatClass, FunctionClass, FunctionMember, IntegerClass, PointerClass, - ReferrerClass, StructureClass, StructureMember, TypeClass, UnionClass, UnionMember, - VoidClass, - }; - pub use crate::r#type::modifier::{ - DescriptorModifierClass, MetadataModifierClass, TypeModifier, TypeModifierClass, - }; - pub use crate::r#type::{Alignment, ComputedType, Type}; - pub use crate::Build; -} - -// TODO: Remove this trait, its useless. -pub trait Build { - type FBType<'a>; - fn create<'a>(&self, builder: &mut FlatBufferBuilder<'a>) -> WIPOffset>; - // If None we must reason about the size using a tuple of (class, architecture, platform), and other context to infer the size... - fn size(&self) -> Option; -} diff --git a/warp_binja/Cargo.toml b/warp_binja/Cargo.toml deleted file mode 100644 index 7ebabd9..0000000 --- a/warp_binja/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "warp_binja" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["lib", "cdylib"] - -[dependencies] -binaryninja = { workspace = true } -typebuild = { path = "../typebuild" } -symbolbuild = { path = "../symbolbuild" } -signaturebuild = { path = "../signaturebuild" } -log = "0.4" -arboard = "3.4" -rayon = "1.10" -dashmap = "6.1" -walkdir = "2.5" -fastbloom = "0.7" - -[build-dependencies] -cc = "1.1.28" - -[dev-dependencies] -criterion = "0.5.1" - -[[bench]] -name = "guid" -harness = false - -[[bench]] -name = "convert" -harness = false - -[[bench]] -name = "function" -harness = false \ No newline at end of file diff --git a/warp_binja/benches/convert.rs b/warp_binja/benches/convert.rs deleted file mode 100644 index b6b8fb3..0000000 --- a/warp_binja/benches/convert.rs +++ /dev/null @@ -1,46 +0,0 @@ -use binaryninja::binaryview::BinaryViewExt; -use binaryninja::headless::Session; -use binaryninja::types::Conf; -use criterion::{criterion_group, criterion_main, Criterion}; -use warp_binja::convert::from_bn_type; - -pub fn type_conversion_benchmark(c: &mut Criterion) { - let session = Session::new(); - let bv = session.load(env!("TEST_BIN_LIBRARY_OBJ")).unwrap(); - let functions = bv.functions(); - assert_eq!(functions.len(), 6); - let mut function_iter = functions.into_iter(); - let first_function = function_iter.next().unwrap(); - let first_function_ty = Conf::new(first_function.function_type(), u8::MAX); - - // TODO: Add a macro benchmark. - - c.bench_function("type conversion first function", |b| { - b.iter(|| { - from_bn_type( - &bv, - first_function_ty.contents.clone(), - first_function_ty.confidence, - ); - }) - }); - - c.bench_function("type conversion all functions", |b| { - b.iter(|| { - for func in &functions { - from_bn_type(&bv, func.function_type(), u8::MAX); - } - }) - }); - - c.bench_function("type conversion all types", |b| { - b.iter(|| { - for ty in &bv.types() { - from_bn_type(&bv, ty.type_object().clone(), u8::MAX); - } - }) - }); -} - -criterion_group!(benches, type_conversion_benchmark); -criterion_main!(benches); diff --git a/warp_binja/benches/function.rs b/warp_binja/benches/function.rs deleted file mode 100644 index 6e667ad..0000000 --- a/warp_binja/benches/function.rs +++ /dev/null @@ -1,42 +0,0 @@ -use binaryninja::binaryview::BinaryViewExt; -use binaryninja::headless::Session; -use criterion::{criterion_group, criterion_main, Criterion}; -use rayon::prelude::*; -use warp_binja::build_function; -use warp_binja::cache::FunctionCache; - -pub fn function_benchmark(c: &mut Criterion) { - let session = Session::new(); - let bv = session.load(env!("TEST_BIN_LIBRARY_OBJ")).unwrap(); - let functions = bv.functions(); - assert_eq!(functions.len(), 6); - let mut function_iter = functions.into_iter(); - let first_function = function_iter.next().unwrap(); - - c.bench_function("signature first function", |b| { - b.iter(|| { - let _ = build_function(&first_function, &first_function.low_level_il().unwrap()); - }) - }); - - c.bench_function("signature all functions", |b| { - b.iter(|| { - for func in &functions { - let _ = build_function(&func, &func.low_level_il().unwrap()); - } - }) - }); - - let cache = FunctionCache::default(); - c.bench_function("signature all functions rayon", |b| { - b.iter(|| { - functions - .par_iter() - .map_with(cache.clone(), |par_cache, func| par_cache.function(&func)) - .collect::>() - }) - }); -} - -criterion_group!(benches, function_benchmark); -criterion_main!(benches); diff --git a/warp_binja/benches/guid.rs b/warp_binja/benches/guid.rs deleted file mode 100644 index 0e007f0..0000000 --- a/warp_binja/benches/guid.rs +++ /dev/null @@ -1,22 +0,0 @@ -use binaryninja::binaryview::BinaryViewExt; -use binaryninja::headless::Session; -use criterion::{criterion_group, criterion_main, Criterion}; -use warp_binja::function_guid; - -pub fn guid_benchmark(c: &mut Criterion) { - let session = Session::new(); - let bv = session.load(env!("TEST_BIN_LIBRARY_OBJ")).unwrap(); - let functions = bv.functions(); - assert_eq!(functions.len(), 6); - let mut function_iter = functions.into_iter(); - let first_function = function_iter.next().unwrap(); - - c.bench_function("function guid", |b| { - b.iter(|| { - function_guid(&first_function, &vec![]); - }) - }); -} - -criterion_group!(benches, guid_benchmark); -criterion_main!(benches); diff --git a/warp_binja/build.rs b/warp_binja/build.rs deleted file mode 100644 index 44393db..0000000 --- a/warp_binja/build.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::path::PathBuf; - -fn main() { - let link_path = - std::env::var_os("DEP_BINARYNINJACORE_PATH").expect("DEP_BINARYNINJACORE_PATH specified"); - - println!("cargo::rustc-link-lib=dylib=binaryninjacore"); - println!("cargo::rustc-link-search={}", link_path.to_str().unwrap()); - - #[cfg(not(target_os = "windows"))] - { - println!( - "cargo::rustc-link-arg=-Wl,-rpath,{0},-L{0}", - link_path.to_string_lossy() - ); - } - - // Generate test binaries. - let test_dir: PathBuf = "fixtures/".into(); - let object_files = cc::Build::new() - .file(test_dir.join("library.c")) - .compile_intermediates(); - - // We need to have the object file paths passed as keys? - println!( - "cargo:rustc-env=TEST_BIN_LIBRARY_OBJ={}", - object_files[0].to_string_lossy() - ); - - // TODO: How do we get this lib file? - cc::Build::new() - .file(test_dir.join("simple.c")) - .compile("simple"); -} diff --git a/warp_binja/fixtures/library.c b/warp_binja/fixtures/library.c deleted file mode 100644 index 66a84ad..0000000 --- a/warp_binja/fixtures/library.c +++ /dev/null @@ -1,33 +0,0 @@ -#include - -#include "library.h" - -int myFunction(int x) -{ - printf("%d\n", x); - return x; -} - -int recursiveFunc(int x); -int otherFunction(int x) -{ - x += 5; - if (x < 10) return otherFunction(x); - return recursiveFunc(x); -} - -int recursiveFunc(int x) -{ - if (x <= 0) return 0; - return x + otherFunction(x - 1); -} - -struct MyStruct myFunction2(int x) -{ - printf("MyStruct %d\n", x); - struct MyStruct myStruct; - myStruct.a = recursiveFunc(x); - myStruct.b = x * 10; - myStruct.c = "my struct"; - return myStruct; -} \ No newline at end of file diff --git a/warp_binja/fixtures/library.h b/warp_binja/fixtures/library.h deleted file mode 100644 index ec7a6e6..0000000 --- a/warp_binja/fixtures/library.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -struct MyStruct { - int a; - int b; - const char* c; - struct MyStruct* d; -}; - -int myFunction(int x); -struct MyStruct myFunction2(int x); \ No newline at end of file diff --git a/warp_binja/fixtures/simple.c b/warp_binja/fixtures/simple.c deleted file mode 100644 index 6e7fbd4..0000000 --- a/warp_binja/fixtures/simple.c +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#include "library.h" - -int simple() { - printf("This is main!\n"); - - printf("calling myFunction\n"); - int returnVal = myFunction(55); - - printf("calling myFunction2\n"); - struct MyStruct myStruct = myFunction2(returnVal); - - return myStruct.b; -} \ No newline at end of file diff --git a/warp_binja/src/cache.rs b/warp_binja/src/cache.rs deleted file mode 100644 index ff80941..0000000 --- a/warp_binja/src/cache.rs +++ /dev/null @@ -1,272 +0,0 @@ -use dashmap::try_result::TryResult; -use dashmap::DashMap; -use std::collections::HashSet; -use std::hash::{DefaultHasher, Hash, Hasher}; -use std::sync::OnceLock; - -use binaryninja::architecture::Architecture; -use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt}; -use binaryninja::function::Function as BNFunction; -use binaryninja::llil; -use binaryninja::llil::{FunctionMutability, NonSSA, NonSSAVariant}; -use binaryninja::rc::Guard; -use binaryninja::rc::Ref as BNRef; - -use signaturebuild::function::constraints::FunctionConstraint; -use signaturebuild::prelude::*; - -use crate::convert::from_bn_symbol; -use crate::{build_function, function_guid}; - -pub static FUNCTION_CACHE: OnceLock> = OnceLock::new(); -pub static GUID_CACHE: OnceLock> = OnceLock::new(); - -pub fn cached_function( - function: &BNFunction, - llil: &llil::Function>, -) -> Option { - let view = function.view(); - let view_id = ViewID::from(view.as_ref()); - let function_cache = FUNCTION_CACHE.get_or_init(Default::default); - match function_cache.get(&view_id) { - Some(cache) => cache.function(function, llil), - None => { - let cache = FunctionCache::default(); - let function = cache.function(function, llil); - function_cache.insert(view_id, cache); - function - } - } -} - -pub fn cached_call_site_constraints(function: &BNFunction) -> HashSet { - let view = function.view(); - let view_id = ViewID::from(view); - let guid_cache = GUID_CACHE.get_or_init(Default::default); - match guid_cache.get(&view_id) { - Some(cache) => cache.call_site_constraints(function), - None => { - let cache = GUIDCache::default(); - let constraints = cache.call_site_constraints(function); - guid_cache.insert(view_id, cache); - constraints - } - } -} - -pub fn cached_adjacency_constraints(function: &BNFunction) -> HashSet { - let view = function.view(); - let view_id = ViewID::from(view); - let guid_cache = GUID_CACHE.get_or_init(Default::default); - match guid_cache.get(&view_id) { - Some(cache) => cache.adjacency_constraints(function), - None => { - let cache = GUIDCache::default(); - let constraints = cache.adjacency_constraints(function); - guid_cache.insert(view_id, cache); - constraints - } - } -} - -pub fn cached_function_guid( - function: &BNFunction, - llil: &llil::Function>, -) -> Option { - let view = function.view(); - let view_id = ViewID::from(view); - let guid_cache = GUID_CACHE.get_or_init(Default::default); - match guid_cache.get(&view_id) { - Some(cache) => cache.function_guid(function, llil), - None => { - let cache = GUIDCache::default(); - let guid = cache.function_guid(function, llil); - guid_cache.insert(view_id, cache); - guid - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct FunctionCache { - pub cache: DashMap>, -} - -impl FunctionCache { - pub fn function( - &self, - function: &BNFunction, - llil: &llil::Function>, - ) -> Option { - let function_id = FunctionID::from(function); - match self.cache.try_get_mut(&function_id) { - TryResult::Present(function) => function.value().to_owned(), - TryResult::Absent => { - let function = build_function(function, llil); - self.cache.insert(function_id, function.clone()); - function - } - TryResult::Locked => build_function(function, llil), - } - } -} - -#[derive(Clone, Debug, Default)] -pub struct GUIDCache { - pub cache: DashMap>, -} - -impl GUIDCache { - pub fn call_site_constraints(&self, function: &BNFunction) -> HashSet { - let view = function.view(); - let func_id = FunctionID::from(function); - let func_start = function.start(); - let mut constraints = HashSet::new(); - for call_site in &function.call_sites() { - for cs_ref in &view.get_code_refs(call_site.address) { - let cs_ref_func = cs_ref.function(); - let cs_ref_func_id = FunctionID::from(cs_ref_func); - if cs_ref_func_id != func_id { - if let Some(cs_ref_func_llil) = cs_ref_func.low_level_il_if_available() { - // Function references another function, constrain on the pattern. - // TODO: If function is trivial thunk we should _also_ insert the tailcall target as a constraint. - let call_site_offset: i64 = func_start as i64 - call_site.address as i64; - constraints.insert(self.function_constraint( - cs_ref_func, - &cs_ref_func_llil, - call_site_offset, - )); - } - } - } - } - constraints - } - - pub fn adjacency_constraints(&self, function: &BNFunction) -> HashSet { - let view = function.view(); - let func_id = FunctionID::from(function); - let func_start = function.start(); - let mut constraints = HashSet::new(); - - let mut func_addr_constraint = |func_start_addr| { - // NOTE: We could potentially have dozens of functions all at the same start address. - for curr_func in &view.functions_at(func_start_addr) { - let curr_func_id = FunctionID::from(curr_func.as_ref()); - if curr_func_id != func_id { - // NOTE: We have to get the llil here for the function which is problematic for running - // NOTE: within a workflow (before analysis has finished) - if let Some(curr_func_llil) = curr_func.low_level_il_if_available() { - // Function adjacent to another function, constrain on the pattern. - let curr_addr_offset = (func_start_addr as i64) - func_start as i64; - constraints.insert(self.function_constraint( - &curr_func, - &curr_func_llil, - curr_addr_offset, - )); - } - } - } - }; - - let mut before_func_start = func_start; - for _ in 0..2 { - before_func_start = view.function_start_before(before_func_start); - func_addr_constraint(before_func_start); - } - - let mut after_func_start = func_start; - for _ in 0..2 { - after_func_start = view.function_start_after(after_func_start); - func_addr_constraint(after_func_start); - } - - constraints - } - - /// Construct a function constraint, must pass the offset at which it is located. - pub fn function_constraint( - &self, - function: &BNFunction, - llil: &llil::Function>, - offset: i64, - ) -> FunctionConstraint { - let guid = self.function_guid(function, llil); - let symbol = from_bn_symbol(&function.symbol()); - FunctionConstraint { - guid, - symbol: Some(symbol), - offset, - } - } - - pub fn function_guid( - &self, - function: &BNFunction, - llil: &llil::Function>, - ) -> Option { - let function_id = FunctionID::from(function); - match self.cache.try_get_mut(&function_id) { - TryResult::Present(function_guid) => function_guid.value().to_owned(), - TryResult::Absent => { - let function_guid = function_guid(function, llil); - self.cache.insert(function_id, function_guid); - function_guid - } - TryResult::Locked => function_guid(function, llil), - } - } -} - -/// A unique view ID, used for caching. -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct ViewID(u64); - -impl From<&BinaryView> for ViewID { - fn from(value: &BinaryView) -> Self { - let mut hasher = DefaultHasher::new(); - hasher.write_u64(value.original_image_base()); - hasher.write(value.view_type().to_bytes()); - hasher.write_u64(value.entry_point()); - hasher.write(value.file().filename().to_bytes()); - Self(hasher.finish()) - } -} - -impl From> for ViewID { - fn from(value: BNRef) -> Self { - Self::from(value.as_ref()) - } -} - -impl From> for ViewID { - fn from(value: Guard<'_, BinaryView>) -> Self { - Self::from(value.as_ref()) - } -} - -/// A unique function ID, used for caching. -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct FunctionID(u64); - -impl From<&BNFunction> for FunctionID { - fn from(value: &BNFunction) -> Self { - let mut hasher = DefaultHasher::new(); - hasher.write_u64(value.start()); - hasher.write_u64(value.lowest_address()); - hasher.write_u64(value.highest_address()); - Self(hasher.finish()) - } -} - -impl From> for FunctionID { - fn from(value: BNRef) -> Self { - Self::from(value.as_ref()) - } -} - -impl From> for FunctionID { - fn from(value: Guard<'_, BNFunction>) -> Self { - Self::from(value.as_ref()) - } -} diff --git a/warp_binja/src/convert.rs b/warp_binja/src/convert.rs deleted file mode 100644 index 9ab7558..0000000 --- a/warp_binja/src/convert.rs +++ /dev/null @@ -1,607 +0,0 @@ -use std::collections::HashSet; - -use binaryninja::architecture::Architecture as BNArchitecture; -use binaryninja::architecture::ArchitectureExt; -use binaryninja::binaryview::{BinaryView, BinaryViewExt}; -use binaryninja::callingconvention::CallingConvention as BNCallingConvention; -use binaryninja::rc::Ref as BNRef; -use binaryninja::symbol::{Symbol as BNSymbol, SymbolType as BNSymbolType}; -use binaryninja::types::{ - BaseStructure as BNBaseStructure, Conf as BNConf, EnumerationBuilder as BNEnumerationBuilder, - FunctionParameter as BNFunctionParameter, MemberAccess as BNMemberAccess, MemberAccess, - MemberScope as BNMemberScope, NamedTypeReference, NamedTypeReference as BNNamedTypeReference, - NamedTypeReferenceClass, StructureBuilder as BNStructureBuilder, - StructureMember as BNStructureMember, -}; -use binaryninja::types::{ - StructureType as BNStructureType, Type as BNType, TypeClass as BNTypeClass, -}; - -use symbolbuild::{Symbol, SymbolClass, SymbolModifier}; -use typebuild::prelude::*; -use typebuild::r#type::class::array::ArrayModifiers; -use typebuild::r#type::class::function::{Location, RegisterLocation}; -use typebuild::r#type::class::pointer::PointerAddressing; -use typebuild::r#type::class::structure::StructureMemberModifiers; -use typebuild::Build; - -pub fn from_bn_symbol(raw_symbol: &BNSymbol) -> Symbol { - // TODO: Use this? - let _is_export = raw_symbol.external(); - let symbol_name = raw_symbol.raw_name().to_string(); - match raw_symbol.sym_type() { - BNSymbolType::ImportAddress => { - todo!() - } - BNSymbolType::Data => { - Symbol::new( - symbol_name, - // TODO: Data? - SymbolClass::Data, - vec![], - ) - } - BNSymbolType::Symbolic => { - todo!() - } - BNSymbolType::LocalLabel => { - todo!() - } - // BN External is our Exported - BNSymbolType::External => Symbol::new( - symbol_name, - // TODO: Data? - SymbolClass::Data, - vec![SymbolModifier::Exported], - ), - BNSymbolType::ImportedData => Symbol::new( - symbol_name, - SymbolClass::Data, - vec![SymbolModifier::External], - ), - BNSymbolType::LibraryFunction | BNSymbolType::Function => { - Symbol::new(symbol_name, SymbolClass::Function, vec![]) - } - // BN Imported is our External - BNSymbolType::ImportedFunction => Symbol::new( - symbol_name, - SymbolClass::Function, - vec![SymbolModifier::External], - ), - } -} - -pub fn to_bn_symbol_at_address(view: &BinaryView, symbol: &Symbol, addr: u64) -> BNRef { - let is_external = symbol.modifiers.contains(&SymbolModifier::External); - let _is_exported = symbol.modifiers.contains(&SymbolModifier::Exported); - let symbol_type = match symbol.class { - SymbolClass::Function if is_external => BNSymbolType::ImportedFunction, - // TODO: We should instead make it a Function, however due to the nature of the imports we are setting them to library for now. - SymbolClass::Function => BNSymbolType::LibraryFunction, - SymbolClass::Data if is_external => BNSymbolType::ImportedData, - SymbolClass::Data => BNSymbolType::Data, - }; - let raw_name = symbol.name.as_str(); - let mut symbol_builder = BNSymbol::builder(symbol_type, &symbol.name, addr); - // Demangle symbol name (short is with simplifications). - if let Some(arch) = view.default_arch() { - if let Ok((_, full_name_list)) = - binaryninja::demangle::demangle_generic(&arch, raw_name, Some(view), false) - { - let full_name = full_name_list.join("::"); - symbol_builder = symbol_builder.full_name(&full_name); - } - if let Ok((_, short_name_list)) = - binaryninja::demangle::demangle_generic(&arch, raw_name, Some(view), false) - { - let short_name = short_name_list.join("::"); - symbol_builder = symbol_builder.short_name(&short_name); - } - } - symbol_builder.create() -} - -pub fn from_bn_type(view: &BinaryView, raw_ty: BNRef, confidence: u8) -> Type { - from_bn_type_internal(view, &mut HashSet::new(), raw_ty, confidence) -} - -fn from_bn_type_internal( - view: &BinaryView, - visited_refs: &mut HashSet, - raw_ty: BNRef, - confidence: u8, -) -> Type { - let bytes_to_bits = |val| val * 8; - let raw_ty_bit_width = bytes_to_bits(raw_ty.width()); - let type_class = match raw_ty.type_class() { - BNTypeClass::VoidTypeClass => TypeClass::Void, - BNTypeClass::BoolTypeClass => { - let bool_class = BooleanClass { width: None }; - TypeClass::Bool(bool_class) - } - BNTypeClass::IntegerTypeClass => { - let signed = raw_ty.is_signed().contents; - let width = Some(raw_ty_bit_width as u16); - if signed && width == Some(8) { - // NOTE: if its an i8, its a char. - let char_class = CharacterClass { width: None }; - TypeClass::Character(char_class) - } else { - let int_class = IntegerClass { width, signed }; - TypeClass::Integer(int_class) - } - } - BNTypeClass::FloatTypeClass => { - let float_class = FloatClass { - width: Some(raw_ty_bit_width as u16), - }; - TypeClass::Float(float_class) - } - // TODO: Union????? - BNTypeClass::StructureTypeClass => { - let raw_struct = raw_ty.get_structure().unwrap(); - - let mut members = raw_struct - .members() - .unwrap() - .into_iter() - .map(|raw_member| { - let bit_offset = bytes_to_bits(raw_member.offset); - let mut modifiers = StructureMemberModifiers::empty(); - // If this member is not public mark it as internal. - modifiers.set( - StructureMemberModifiers::Internal, - !matches!(raw_member.access, MemberAccess::PublicAccess), - ); - StructureMember { - name: Some(raw_member.name), - offset: bit_offset, - ty: from_bn_type_internal( - view, - visited_refs, - raw_member.ty.contents, - raw_member.ty.confidence, - ), - modifiers, - } - }) - .collect::>(); - - // Add base structures as flattened members - if let Ok(base_structs) = raw_struct.base_structures() { - let base_to_member_iter = base_structs.iter().map(|base_struct| { - let bit_offset = bytes_to_bits(base_struct.offset); - let mut modifiers = StructureMemberModifiers::empty(); - modifiers.set(StructureMemberModifiers::Flattened, true); - let base_struct_ty = from_bn_type_internal( - view, - visited_refs, - BNType::named_type(&base_struct.ty), - 255, - ); - StructureMember { - name: base_struct_ty.name.to_owned(), - offset: bit_offset, - ty: base_struct_ty, - modifiers, - } - }); - members.extend(base_to_member_iter); - } - - // TODO: Check if union - let struct_class = StructureClass::new(members); - TypeClass::Structure(struct_class) - } - BNTypeClass::EnumerationTypeClass => { - let raw_enum = raw_ty.get_enumeration().unwrap(); - - let enum_ty_signed = raw_ty.is_signed().contents; - let enum_ty = Type::builder::() - .class(TypeClass::Integer(IntegerClass { - width: Some(raw_ty_bit_width as u16), - signed: enum_ty_signed, - })) - .build(); - - let members = raw_enum - .members() - .into_iter() - .map(|raw_member| EnumerationMember { - name: Some(raw_member.name), - constant: raw_member.value, - }) - .collect(); - - let enum_class = EnumerationClass::new(enum_ty, members); - TypeClass::Enumeration(enum_class) - } - BNTypeClass::PointerTypeClass => { - let raw_child_ty = raw_ty.target().unwrap(); - let ptr_class = PointerClass { - width: Some(raw_ty_bit_width as u16), - child_type: from_bn_type_internal( - view, - visited_refs, - raw_child_ty.contents, - raw_child_ty.confidence, - ), - // TODO: Handle addressing. - addressing: PointerAddressing::Absolute, - }; - TypeClass::Pointer(ptr_class) - } - BNTypeClass::ArrayTypeClass => { - let length = raw_ty.count(); - let raw_member_ty = raw_ty.element_type().unwrap(); - let array_class = ArrayClass { - length: Some(length), - member_type: from_bn_type_internal( - view, - visited_refs, - raw_member_ty.contents, - raw_member_ty.confidence, - ), - modifiers: ArrayModifiers::empty(), - }; - TypeClass::Array(array_class) - } - BNTypeClass::FunctionTypeClass => { - let in_members = raw_ty - .parameters() - .unwrap() - .into_iter() - .map(|raw_member| { - // TODO: Location... - let _location = Location::Register(RegisterLocation {}); - FunctionMember { - name: Some(raw_member.name), - ty: from_bn_type_internal( - view, - visited_refs, - raw_member.t.contents, - raw_member.t.confidence, - ), - // TODO: Just omit location for now? - // TODO: Location should be optional... - locations: vec![], - } - }) - .collect(); - - let mut out_members = Vec::new(); - if let Ok(return_ty) = raw_ty.return_value() { - out_members.push(FunctionMember { - name: None, - ty: from_bn_type_internal( - view, - visited_refs, - return_ty.contents, - return_ty.confidence, - ), - locations: vec![], - }); - } - - let calling_convention = raw_ty - .calling_convention() - .map(|bn_cc| from_bn_calling_convention(bn_cc.contents)) - .ok(); - - let func_class = FunctionClass { - calling_convention, - in_members, - out_members, - }; - TypeClass::Function(func_class) - } - BNTypeClass::VarArgsTypeClass => TypeClass::Void, - BNTypeClass::ValueTypeClass => { - // What the is this. - TypeClass::Void - } - BNTypeClass::NamedTypeReferenceClass => { - let raw_ntr = raw_ty.get_named_type_reference().unwrap(); - let ref_id_str = raw_ntr.id().to_string(); - let raw_ntr_ty = raw_ntr.target(view); - if raw_ntr_ty.is_none() || !visited_refs.insert(ref_id_str.clone()) { - let ref_class = ReferrerClass::new(None, Some(raw_ntr.name().to_string())); - TypeClass::Referrer(ref_class) - } else { - use dashmap::DashMap; - use std::sync::Arc; - use std::sync::OnceLock; - static REF_CACHE: OnceLock>> = OnceLock::new(); - let ref_cache = REF_CACHE.get_or_init(|| Arc::new(DashMap::new())); - // Check the cache first before proceeding - if let Some(cached_type) = ref_cache.get(&ref_id_str) { - cached_type.value().to_owned() - } else { - let ntr_ty = - from_bn_type_internal(view, visited_refs, raw_ntr_ty.unwrap(), confidence); - visited_refs.remove(&ref_id_str); - // NOTE: The GUID here must always equal the same for any given type for this to work effectively. - let ntr_guid = TypeGUID::from(&ntr_ty); - let ref_class = ReferrerClass::new(Some(ntr_guid), ntr_ty.name); - let ntr_ty_class = TypeClass::Referrer(ref_class); - ref_cache.insert(ref_id_str, ntr_ty_class.clone()); - ntr_ty_class - } - } - } - BNTypeClass::WideCharTypeClass => { - let char_class = CharacterClass { - width: Some(raw_ty_bit_width as u16), - }; - TypeClass::Character(char_class) - } - }; - - let name = raw_ty.registered_name().map(|n| n.name().to_string()).ok(); - - Type { - name, - class: Box::new(type_class), - confidence, - // TODO: Fill these out... - modifiers: vec![], - alignment: Default::default(), - // TODO: Filling this out is... weird. - // TODO: we _do_ want this for lumina (this is the only way we can update type is if we fill this out) - ancestors: vec![], - } -} - -pub fn from_bn_calling_convention( - raw_cc: BNRef>, -) -> CallingConvention { - // NOTE: Currently calling convention just stores the name. - CallingConvention::new(raw_cc.name().as_str()) -} - -pub fn to_bn_calling_convention( - arch: &A, - calling_convention: &CallingConvention, -) -> BNRef> { - for cc in &arch.calling_conventions() { - if cc.name().as_str() == calling_convention.name { - return cc.clone(); - } - } - arch.get_default_calling_convention().unwrap() -} - -pub fn to_bn_type(arch: &A, ty: &Type) -> BNRef { - let bits_to_bytes = |val: u64| (val / 8); - let addr_size = arch.address_size() as u64; - match ty.class.as_ref() { - TypeClass::Void => BNType::void(), - TypeClass::Bool(_) => BNType::bool(), - TypeClass::Integer(c) => { - let width = c.width.map(|w| bits_to_bytes(w as _)).unwrap_or(4); - BNType::int(width as usize, c.signed) - } - TypeClass::Character(c) => match c.width { - Some(w) => BNType::wide_char(bits_to_bytes(w as _) as usize), - None => BNType::char(), - }, - TypeClass::Float(c) => { - let width = c.width.map(|w| bits_to_bytes(w as _)).unwrap_or(4); - BNType::float(width as usize) - } - TypeClass::Pointer(ref c) => { - let child_type = to_bn_type(arch, &c.child_type); - let ptr_width = c.width.map(|w| bits_to_bytes(w as _)).unwrap_or(addr_size); - // TODO: Child type confidence - let constant = ty.is_const(); - let volatile = ty.is_volatile(); - // TODO: If the pointer is to a null terminated array of chars, make it a pointer to char - // TODO: Addressing mode - BNType::pointer_of_width(&child_type, ptr_width as usize, constant, volatile, None) - } - TypeClass::Array(c) => { - let member_type = to_bn_type(arch, &c.member_type); - // TODO: How to handle DST array (length is None) - BNType::array(&member_type, c.length.unwrap_or(0)) - } - TypeClass::Structure(c) => { - let builder = BNStructureBuilder::new(); - // TODO: Structure type class? - // TODO: Alignment - // TODO: Other modifiers? - let mut base_structs: Vec = Vec::new(); - for member in &c.members { - let member_type = BNConf::new(to_bn_type(arch, &member.ty), u8::MAX); - let member_name = member.name.to_owned().unwrap_or("field_OFFSET".into()); - let member_offset = bits_to_bytes(member.offset); - let member_access = if member - .modifiers - .contains(StructureMemberModifiers::Internal) - { - BNMemberAccess::PrivateAccess - } else { - BNMemberAccess::PublicAccess - }; - // TODO: Member scope - let member_scope = BNMemberScope::NoScope; - if member - .modifiers - .contains(StructureMemberModifiers::Flattened) - { - // Add member as a base structure to inherit its fields. - match member.ty.class.as_ref() { - TypeClass::Referrer(c) => { - // We only support base structures with a referrer right now. - let base_struct_ntr_name = - c.name.to_owned().unwrap_or("base_UNKNOWN".into()); - let base_struct_ntr = match c.guid { - Some(guid) => BNNamedTypeReference::new_with_id( - NamedTypeReferenceClass::UnknownNamedTypeClass, - guid.to_string(), - base_struct_ntr_name.into(), - ), - None => BNNamedTypeReference::new( - NamedTypeReferenceClass::UnknownNamedTypeClass, - base_struct_ntr_name.into(), - ), - }; - base_structs.push(BNBaseStructure::new( - base_struct_ntr, - member_offset, - member.ty.size().unwrap_or(0), - )) - } - _ => { - log::error!( - "Adding base {:?} with invalid ty: {:?}", - ty.name, - member.ty - ); - } - } - } else { - builder.insert_member( - &BNStructureMember::new( - member_type, - member_name, - member_offset, - member_access, - member_scope, - ), - false, - ); - } - } - builder.set_base_structures(base_structs); - BNType::structure(&builder.finalize()) - } - TypeClass::Enumeration(c) => { - let builder = BNEnumerationBuilder::new(); - for member in &c.members { - // TODO: Add default name? - let member_name = member.name.to_owned().unwrap_or("enum_VAL".into()); - let member_value = member.constant; - builder.insert(member_name, member_value); - } - // TODO: Warn if enumeration has no size. - let width = bits_to_bytes(c.member_type.size().unwrap()) as _; - let signed = matches!(*c.member_type.class, TypeClass::Integer(c) if c.signed); - BNType::enumeration(&builder.finalize(), width, signed) - } - TypeClass::Union(c) => { - let builder = BNStructureBuilder::new(); - builder.set_structure_type(BNStructureType::UnionStructureType); - for member in &c.members { - let member_type = BNConf::new(to_bn_type(arch, &member.ty), u8::MAX); - let member_name = member.name.to_owned(); - // TODO: Member access - let member_access = BNMemberAccess::PublicAccess; - // TODO: Member scope - let member_scope = BNMemberScope::NoScope; - let structure_member = BNStructureMember::new( - member_type, - member_name, - 0, // Union members all exist at 0 right? - member_access, - member_scope, - ); - builder.insert_member(&structure_member, false); - } - BNType::structure(&builder.finalize()) - } - TypeClass::Function(c) => { - let return_type = if !c.out_members.is_empty() { - // TODO: WTF - to_bn_type(arch, &c.out_members[0].ty) - } else { - BNType::void() - }; - let params: Vec<_> = c - .in_members - .iter() - .map(|member| { - let member_type = to_bn_type(arch, &member.ty); - let name = member.name.clone(); - // TODO: Location AND fix default param name - BNFunctionParameter::new(member_type, name.unwrap_or("param_IDK".into()), None) - }) - .collect(); - // TODO: Variable arguments - let variable_args = false; - // If we have a calling convention we run the extended function type creation. - match c.calling_convention.as_ref() { - Some(cc) => { - let calling_convention = to_bn_calling_convention(arch, cc); - BNType::function_with_options( - &return_type, - ¶ms, - variable_args, - &BNConf::new(calling_convention, u8::MAX), - BNConf::new(0, 0), - ) - } - None => BNType::function(&return_type, ¶ms, variable_args), - } - } - TypeClass::Referrer(c) => { - let ntr = match c.guid { - Some(guid) => { - let guid_str = guid.to_string(); - let ntr_name = c.name.to_owned().unwrap_or(guid_str.clone()); - NamedTypeReference::new_with_id( - NamedTypeReferenceClass::UnknownNamedTypeClass, - guid_str, - ntr_name.into(), - ) - } - None => match c.name.as_ref() { - Some(ntr_name) => NamedTypeReference::new( - NamedTypeReferenceClass::UnknownNamedTypeClass, - ntr_name.into(), - ), - None => { - log::error!("Referrer with no reference! {:?}", c); - NamedTypeReference::new( - NamedTypeReferenceClass::UnknownNamedTypeClass, - "AHHHHHH".into(), - ) - } - }, - }; - BNType::named_type(&ntr) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use binaryninja::binaryview::BinaryViewExt; - use binaryninja::headless::Session; - use std::sync::OnceLock; - - static INIT: OnceLock = OnceLock::new(); - - fn get_session<'a>() -> &'a Session { - INIT.get_or_init(|| Session::new()) - } - - #[test] - fn type_conversion() { - let session = get_session(); - let bv = session.load(env!("TEST_BIN_LIBRARY_OBJ")).unwrap(); - // TODO: This will fail for some odd reason, im thinking the other test runs and stops the test file - // TODO: From being able to see the object file. - assert_eq!(bv.types().len(), 23); - let converted_types: Vec<_> = bv - .types() - .iter() - .map(|t| { - let ty = from_bn_type(&bv, t.type_object().clone(), u8::MAX); - (TypeGUID::from(&ty), ty) - }) - .collect(); - assert_eq!(converted_types.len(), 23); - dbg!(converted_types); - } -} diff --git a/warp_binja/src/lib.rs b/warp_binja/src/lib.rs deleted file mode 100644 index 617d41b..0000000 --- a/warp_binja/src/lib.rs +++ /dev/null @@ -1,163 +0,0 @@ -use binaryninja::architecture::Architecture; -use binaryninja::basicblock::BasicBlock as BNBasicBlock; -use binaryninja::binaryview::BinaryViewExt; -use binaryninja::function::{Function as BNFunction, NativeBlock}; -use binaryninja::llil; -use binaryninja::llil::{ExprInfo, FunctionMutability, NonSSA, NonSSAVariant, VisitorAction}; -use binaryninja::rc::Ref as BNRef; -use signaturebuild::prelude::*; - -use crate::cache::{ - cached_adjacency_constraints, cached_call_site_constraints, cached_function_guid, -}; -use crate::convert::{from_bn_symbol, from_bn_type}; - -pub mod cache; -pub mod convert; -mod matcher; -/// Only used when compiled for cdylib target. -mod plugin; - -pub fn build_function( - func: &BNFunction, - llil: &llil::Function>, -) -> Option { - let bn_fn_ty = func.function_type(); - Some(Function { - guid: cached_function_guid(func, llil)?, - symbol: from_bn_symbol(&func.symbol()), - // TODO: Confidence should be derived from function type. - ty: from_bn_type(&func.view(), bn_fn_ty, 255), - constraints: FunctionConstraints { - // NOTE: Adding adjacent only works if analysis is complete. - adjacent: cached_adjacency_constraints(func), - call_sites: cached_call_site_constraints(func), - // TODO: Add caller sites (when adjacent and call sites are minimal) - // NOTE: Adding caller sites only works if analysis is complete. - caller_sites: Default::default(), - }, - // TODO: We need more than one entry block. - entry: entry_basic_block_guid(func, llil).map(BasicBlock::new), - }) -} - -pub fn entry_basic_block_guid( - func: &BNFunction, - llil: &llil::Function>, -) -> Option { - // NOTE: This is not actually the entry point. This is the highest basic block. - let first_basic_block = sorted_basic_blocks(func).into_iter().next()?; - basic_block_guid(&first_basic_block, llil) -} - -/// Basic blocks sorted from high to low. -pub fn sorted_basic_blocks(func: &BNFunction) -> Vec>> { - let mut basic_blocks = func - .basic_blocks() - .iter() - .map(|bb| bb.clone()) - .collect::>(); - basic_blocks.sort_by_key(|f| f.raw_start()); - basic_blocks -} - -pub fn function_guid( - func: &BNFunction, - llil: &llil::Function>, -) -> Option { - // TODO: Sort the basic blocks. - let basic_blocks = sorted_basic_blocks(func); - let basic_block_guids = basic_blocks - .iter() - .filter_map(|bb| basic_block_guid(bb, llil)) - .collect::>(); - Some(FunctionGUID::from_basic_blocks(&basic_block_guids)) -} - -pub fn basic_block_guid( - basic_block: &BNBasicBlock, - llil: &llil::Function>, -) -> Option { - let func = basic_block.function(); - let view = func.view(); - let arch = func.arch(); - let max_instr_len = arch.max_instr_len(); - // TODO: Add all the hacks here to remove stuff like function prolog... - // TODO mov edi, edi on windows x86 - // TODO: Ugh i really dislike the above and REALLY don't wanna do that. - // TODO: The above invalidates our "all function bytes" approach. - // TODO: Could we keep the bytes and just zero mask them? At least then we don't completely get rid of them. - - let basic_block_range = basic_block.raw_start()..basic_block.raw_end(); - let mut basic_block_bytes = Vec::with_capacity(basic_block_range.count()); - for instr_addr in basic_block.into_iter() { - let mut instr_bytes = view.read_vec(instr_addr, max_instr_len); - if let Some(instr_info) = arch.instruction_info(&instr_bytes, instr_addr) { - let instr_len = instr_info.len(); - instr_bytes.truncate(instr_len); - if let Some(instr_llil) = llil.instruction_at(instr_addr) { - if instr_llil.visit_tree(&mut |_expr, expr_info| match expr_info { - ExprInfo::ConstPtr(_) | ExprInfo::ExternPtr(_) => VisitorAction::Halt, - _ => VisitorAction::Descend, - }) == VisitorAction::Halt - { - // Found a variant instruction, mask off entire instruction. - instr_bytes.fill(0); - } - } - // Add the instructions bytes to the functions bytes - basic_block_bytes.extend(instr_bytes); - } - } - - Some(BasicBlockGUID::from(basic_block_bytes.as_slice())) -} - -#[cfg(test)] -mod tests { - use super::*; - use binaryninja::binaryview::BinaryViewExt; - use binaryninja::headless::Session; - use std::sync::OnceLock; - - static INIT: OnceLock = OnceLock::new(); - - fn get_session<'a>() -> &'a Session { - // TODO: This is not shared between other test modules, should still be fine (mutex in core now). - INIT.get_or_init(|| Session::new()) - } - - #[test] - fn simple_signature() { - let session = get_session(); - let bv = session.load(env!("TEST_BIN_LIBRARY_OBJ")).unwrap(); - assert_eq!(bv.functions().len(), 11); - - let mut valid_guids: Vec<&str> = vec![ - "405b94b7-6d73-5af5-9192-dd615a67afc5", - "623a8338-34d6-5a6e-8c4e-36a1a071117e", - "6cd81a21-6967-5c90-b73e-5a810f835a84", - "89aaed99-1b17-5938-8be1-046825d89071", - "905fa3b0-3571-58ed-b81f-7cf62bdcfe49", - "9a3e480c-5ebd-5278-8e33-4a6e982167fb", - "a25a06fb-fb60-542c-9b11-4c286dbc607b", - "c8a64fb9-841b-5759-ab80-739b407ba7bc", - "f631b282-0174-5bdd-846d-c0514f2539e1", - "fa2d7ebf-d187-5592-bfc1-ee41614437b3", - "fa2d7ebf-d187-5592-bfc1-ee41614437b3", - ]; - - // TODO: Do not use caching here. Test non cached. - let mut guids = bv - .functions() - .into_iter() - .filter_map(|func| cached_function_guid(&func, func.low_level_il().ok()?.as_ref())) - .map(|guid| guid.to_string()) - .collect::>(); - - valid_guids.sort(); - guids.sort(); - - assert_eq!(valid_guids, guids); - } -} diff --git a/warp_binja/src/matcher.rs b/warp_binja/src/matcher.rs deleted file mode 100644 index 92af044..0000000 --- a/warp_binja/src/matcher.rs +++ /dev/null @@ -1,433 +0,0 @@ -use dashmap::DashMap; -use fastbloom::BloomFilter; -use std::cmp::Ordering; -use std::collections::{HashMap, HashSet}; -use std::hash::{DefaultHasher, Hasher}; -use std::path::PathBuf; -use std::sync::OnceLock; -use walkdir::{DirEntry, WalkDir}; - -use binaryninja::architecture::{Architecture as BNArchitecture, Architecture}; -use binaryninja::backgroundtask::BackgroundTask; -use binaryninja::binaryview::{BinaryView, BinaryViewExt}; -use binaryninja::function::{Function as BNFunction, FunctionUpdateType}; -use binaryninja::llil; -use binaryninja::llil::{FunctionMutability, NonSSA, NonSSAVariant}; -use binaryninja::platform::Platform; -use binaryninja::rc::Guard; -use binaryninja::rc::Ref as BNRef; - -use signaturebuild::function::{Function, FunctionGUID}; -use signaturebuild::prelude::BasicBlock; -use signaturebuild::Data; -use typebuild::guid::TypeGUID; -use typebuild::prelude::{Type, TypeClass}; - -use crate::cache::{cached_call_site_constraints, cached_function_guid, FunctionID}; -use crate::convert::to_bn_type; -use crate::entry_basic_block_guid; -use crate::plugin::on_matched_function; - -pub const TRIVIAL_LLIL_THRESHOLD: usize = 8; - -pub static PLAT_MATCHER_CACHE: OnceLock> = OnceLock::new(); - -pub fn cached_function_match( - function: &BNFunction, - llil: &llil::Function>, -) { - let platform = function.platform(); - let platform_id = PlatformID::from(platform.as_ref()); - let matcher_cache = PLAT_MATCHER_CACHE.get_or_init(Default::default); - match matcher_cache.get(&platform_id) { - Some(matcher) => matcher.match_function(function, llil), - None => { - let matcher = Matcher::from_platform(platform); - matcher.match_function(function, llil); - matcher_cache.insert(platform_id, matcher); - } - } -} - -pub struct Matcher { - pub matched_functions: DashMap, - pub functions: DashMap>, - pub types: DashMap, - pub named_types: DashMap, - /// This is used to fast-fail on functions not in the dataset. - /// NOTE: This can only handle one basic block classification, right now that is the entry block. - basic_block_filter: BloomFilter, -} - -impl Matcher { - /// Create a matcher from the platforms signature subdirectory. - pub fn from_platform(platform: BNRef) -> Self { - let platform_name = platform.name().to_string(); - let task = BackgroundTask::new( - format!("Getting platform matcher data... {}", platform_name), - false, - ) - .unwrap(); - // Get core signatures for the given platform - let core_dir = binaryninja::install_directory().unwrap(); - let root_core_sig_dir = core_dir.join("signatures"); - let plat_core_sig_dir = root_core_sig_dir.join(&platform_name); - let mut data = get_data_from_dir(&plat_core_sig_dir); - - // Get user signatures for the given platform - let user_dir = binaryninja::user_directory().unwrap(); - let root_user_sig_dir = user_dir.join("signatures"); - let plat_user_sig_dir = root_user_sig_dir.join(&platform_name); - let user_data = get_data_from_dir(&plat_user_sig_dir); - - data.extend(user_data); - - // TODO: If a user signature has the same name as a core signature, remove the core signature. - - task.set_progress_text("Gathering entry blocks for matcher filtering..."); - - // Get entry_blocks for filtering. - let entry_blocks = data - .iter() - .flat_map(|(_, data)| data.functions.iter().map(|function| &function.entry)) - .collect::>(); - // TODO: We need to disable this if we get a None basic block, as it will then fail to match all cases. - let basic_block_filter = BloomFilter::with_false_pos(0.1).items(entry_blocks); - - task.set_progress_text("Gathering matcher functions..."); - - // TODO: Merge like functions, right now we just hope and pray. - - // Get functions for comprehensive matching. - let functions = data - .iter() - .flat_map(|(_, data)| { - data.functions.iter().fold(DashMap::new(), |map, func| { - #[allow(clippy::unwrap_or_default)] - map.entry(func.guid) - .or_insert_with(Vec::new) - .push(func.clone()); - map - }) - }) - .map(|(guid, mut funcs)| { - funcs.sort_by_key(|f| f.symbol.name.to_owned()); - funcs.dedup_by_key(|f| f.symbol.name.to_owned()); - (guid, funcs) - }) - .collect(); - - task.set_progress_text("Gathering matcher types..."); - - let types = data - .iter() - .flat_map(|(_, data)| { - data.types.iter().fold(DashMap::new(), |map, comp_ty| { - map.insert(comp_ty.guid, comp_ty.ty.clone()); - map - }) - }) - .collect(); - - task.set_progress_text("Gathering matcher named types..."); - - // TODO: We store a duplicate lookup for named references. - let named_types = data - .iter() - .flat_map(|(_, data)| { - data.types.iter().fold(DashMap::new(), |map, comp_ty| { - if let Some(ty_name) = &comp_ty.ty.name { - map.insert(ty_name.to_owned(), comp_ty.ty.clone()); - } - map - }) - }) - .collect(); - - task.finish(); - - log::debug!("Loaded signatures: {:?}", data.keys()); - - Self { - matched_functions: Default::default(), - functions, - basic_block_filter, - types, - named_types, - } - } - - pub fn add_type_to_view(&self, view: &BinaryView, arch: &A, ty: &Type) { - fn inner_add_type_to_view( - matcher: &Matcher, - view: &BinaryView, - arch: &A, - visited_refs: &mut HashSet, - ty: &Type, - ) { - let ty_id_str = TypeGUID::from(ty).to_string(); - if view.get_type_by_id(&ty_id_str).is_some() { - // Type already added. - return; - } - // Type not already added to the view. - // Verify all nested types are added before adding type. - match ty.class.as_ref() { - TypeClass::Pointer(c) => { - inner_add_type_to_view(matcher, view, arch, visited_refs, &c.child_type) - } - TypeClass::Array(c) => { - inner_add_type_to_view(matcher, view, arch, visited_refs, &c.member_type) - } - TypeClass::Structure(c) => { - for member in &c.members { - inner_add_type_to_view(matcher, view, arch, visited_refs, &member.ty) - } - } - TypeClass::Enumeration(c) => { - inner_add_type_to_view(matcher, view, arch, visited_refs, &c.member_type) - } - TypeClass::Union(c) => { - for member in &c.members { - inner_add_type_to_view(matcher, view, arch, visited_refs, &member.ty) - } - } - TypeClass::Function(c) => { - for out_member in &c.out_members { - inner_add_type_to_view(matcher, view, arch, visited_refs, &out_member.ty) - } - for in_member in &c.in_members { - inner_add_type_to_view(matcher, view, arch, visited_refs, &in_member.ty) - } - } - TypeClass::Referrer(c) => { - // Check to see if the referrer has been added to the view. - let mut resolved = false; - if let Some(ref_guid) = c.guid { - // NOTE: We do not need to check for cyclic reference here because - // NOTE: GUID references are unable to be referenced. - if view.get_type_by_id(ref_guid.to_string()).is_none() { - // Add the ref to the view if it is in the Matcher types - if let Some(ref_ty) = matcher.types.get(&ref_guid) { - inner_add_type_to_view(matcher, view, arch, visited_refs, &ref_ty); - resolved = true; - } - } - } - - if let Some(ref_name) = &c.name { - // Only try and resolve by name if not already visiting. - if !resolved - && visited_refs.insert(ref_name.to_string()) - && view.get_type_by_name(ref_name).is_none() - { - // Add the ref to the view if it is in the Matcher types - if let Some(ref_ty) = matcher.named_types.get(ref_name) { - inner_add_type_to_view(matcher, view, arch, visited_refs, &ref_ty); - } - // No longer visiting type. - visited_refs.remove(ref_name); - } - } - } - _ => {} - } - // All nested types _should_ be added now, we can add this type. - let ty_name = ty.name.to_owned().unwrap_or_else(|| ty_id_str.clone()); - view.define_auto_type_with_id(ty_name, ty_id_str, &to_bn_type(arch, ty)); - } - inner_add_type_to_view(self, view, arch, &mut HashSet::new(), ty) - } - - pub fn match_function( - &self, - function: &BNFunction, - llil: &llil::Function>, - ) { - let function_id = FunctionID::from(function); - if let Some(matched_function) = self.matched_functions.get(&function_id) { - // Skip computing the match for already matched function. - // We do still need to apply the match data through analysis updates. - return on_matched_function(function, &matched_function); - } - - let on_new_match = |matched: &Function| { - // We also want to resolve the types here. - if let TypeClass::Function(c) = matched.ty.class.as_ref() { - // Recursively go through the function type and resolve the uuids - let view = function.view(); - let arch = function.arch(); - for out_member in &c.out_members { - self.add_type_to_view(&view, &arch, &out_member.ty); - } - for in_member in &c.in_members { - self.add_type_to_view(&view, &arch, &in_member.ty); - } - } else { - // This should never happen. - log::error!( - "Matched function is not of function type class... 0x{:x}", - function.start() - ); - } - on_matched_function(function, matched); - - // We matched on the function, great! Now make sure we don't do this again :3 - self.matched_functions - .insert(function_id, matched.to_owned()); - // Also mark this for updates. - // TODO: Does this do anything? - function.mark_updates_required(FunctionUpdateType::UserFunctionUpdate); - }; - - // TODO: Expand this check to be less broad. - let is_function_trivial = { llil.instruction_count() < TRIVIAL_LLIL_THRESHOLD }; - - // Check to see if the functions entry block is even in the dataset - let entry_block = entry_basic_block_guid(function, llil).map(BasicBlock::new); - if self.basic_block_filter.contains(&entry_block) { - // Build the full function guid now - if let Some(warp_func_guid) = cached_function_guid(function, llil) { - if let Some(matched) = self.functions.get(&warp_func_guid) { - if matched.len() == 1 && !is_function_trivial { - on_new_match(&matched[0]); - } else if let Some(matched_function) = - self.match_function_from_constraints(function, &matched) - { - log::info!( - "Found best matching function `{}`... 0x{:x}", - matched_function.symbol.name, - function.start() - ); - on_new_match(matched_function); - } else { - log::error!( - "Failed to find matching function `{}`... 0x{:x}", - matched.len(), - function.start() - ); - } - } - } - } - } - - pub fn match_function_from_constraints<'a>( - &self, - function: &BNFunction, - matched_functions: &'a [Function], - ) -> Option<&'a Function> { - // TODO: To prevent invoking adjacent constraint function analysis, we must call call_site constraints specifically. - let call_sites = cached_call_site_constraints(function); - - // NOTE: We are only matching with call_sites for now, as adjacency requires we run after all analysis has completed. - if call_sites.is_empty() { - return None; - } - - // Check call site guids - let mut highest_guid_count = 0; - let mut matched_guid_func = None; - let call_site_guids = call_sites - .iter() - .filter_map(|c| c.guid) - .collect::>(); - for matched in matched_functions { - let matched_call_site_guids = matched - .constraints - .call_sites - .iter() - .filter_map(|c| c.guid) - .collect::>(); - let common_guid_count = call_site_guids - .intersection(&matched_call_site_guids) - .count(); - match common_guid_count.cmp(&highest_guid_count) { - Ordering::Equal => { - // Multiple matches with same count, don't match on ONE of them. - matched_guid_func = None; - } - Ordering::Greater => { - highest_guid_count = common_guid_count; - matched_guid_func = Some(matched); - } - Ordering::Less => {} - } - } - - // Check call site symbol names - let mut highest_symbol_count = 0; - let mut matched_symbol_func = None; - let call_site_symbol_names = call_sites - .into_iter() - .filter_map(|c| Some(c.symbol?.name)) - .collect::>(); - for matched in matched_functions { - let matched_call_site_symbol_names = matched - .constraints - .call_sites - .iter() - .filter_map(|c| Some(c.symbol.to_owned()?.name)) - .collect::>(); - let common_symbol_count = call_site_symbol_names - .intersection(&matched_call_site_symbol_names) - .count(); - match common_symbol_count.cmp(&highest_symbol_count) { - Ordering::Equal => { - // Multiple matches with same count, don't match on ONE of them. - matched_symbol_func = None; - } - Ordering::Greater => { - highest_symbol_count = common_symbol_count; - matched_symbol_func = Some(matched); - } - Ordering::Less => {} - } - } - - match highest_guid_count.cmp(&highest_symbol_count) { - Ordering::Less => matched_symbol_func, - Ordering::Greater => matched_guid_func, - Ordering::Equal => None, - } - } -} - -fn get_data_from_dir(dir: &PathBuf) -> HashMap { - let data_from_entry = |entry: DirEntry| { - let path = entry.path(); - let contents = std::fs::read(path).ok()?; - Data::from_bytes(&contents) - }; - - WalkDir::new(dir) - .into_iter() - .filter_map(|e| e.ok()) - .filter(|e| e.file_type().is_file()) - .filter_map(|e| Some((e.clone().into_path(), data_from_entry(e)?))) - .collect() -} - -/// A unique platform ID, used for caching. -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct PlatformID(u64); - -impl From<&Platform> for PlatformID { - fn from(value: &Platform) -> Self { - let mut hasher = DefaultHasher::new(); - hasher.write(value.name().to_bytes()); - Self(hasher.finish()) - } -} - -impl From> for PlatformID { - fn from(value: BNRef) -> Self { - Self::from(value.as_ref()) - } -} - -impl From> for PlatformID { - fn from(value: Guard<'_, Platform>) -> Self { - Self::from(value.as_ref()) - } -} diff --git a/warp_binja/src/plugin.rs b/warp_binja/src/plugin.rs deleted file mode 100644 index da75b3f..0000000 --- a/warp_binja/src/plugin.rs +++ /dev/null @@ -1,151 +0,0 @@ -use log::LevelFilter; - -use binaryninja::binaryview::{BinaryView, BinaryViewExt}; -use binaryninja::command::{Command, FunctionCommand}; -use binaryninja::function::Function; -use binaryninja::rc::Ref; -use binaryninja::tags::TagType; - -use crate::build_function; -use crate::cache::{ViewID, FUNCTION_CACHE, GUID_CACHE}; -use crate::convert::{to_bn_symbol_at_address, to_bn_type}; -use crate::matcher::{PlatformID, PLAT_MATCHER_CACHE}; - -mod apply; -mod copy; -mod create; -mod find; -mod types; -mod workflow; - -// TODO: This icon is a little much -const TAG_ICON: &str = "🌏"; -const TAG_NAME: &str = "WARP"; - -fn get_warp_tag_type(view: &BinaryView) -> Ref { - view.get_tag_type(TAG_NAME) - .unwrap_or_else(|| view.create_tag_type(TAG_NAME, TAG_ICON)) -} - -// What happens to the function when it is matched. -// TODO: add user: bool -// TODO: Rename to markup_function or something. -pub fn on_matched_function(function: &Function, matched: &signaturebuild::function::Function) { - let view = function.view(); - view.define_auto_symbol(&to_bn_symbol_at_address( - &view, - &matched.symbol, - function.symbol().address(), - )); - function.set_auto_type(&to_bn_type(&function.arch(), &matched.ty)); - // TODO: Add metadata. (both binja metadata and warp metadata) - function.add_tag( - &get_warp_tag_type(&view), - matched.guid.to_string(), - None, - true, - None, - ); -} - -struct DebugFunction; - -impl FunctionCommand for DebugFunction { - fn action(&self, _view: &BinaryView, func: &Function) { - if let Ok(llil) = func.low_level_il() { - if let Some(function) = build_function(func, &llil) { - log::info!("{:#?}", function); - } - } - } - - fn valid(&self, _view: &BinaryView, _func: &Function) -> bool { - true - } -} - -struct DebugCache; - -impl Command for DebugCache { - fn action(&self, view: &BinaryView) { - let function_cache = FUNCTION_CACHE.get_or_init(Default::default); - let view_id = ViewID::from(view); - if let Some(cache) = function_cache.get(&view_id) { - log::info!("View functions: {}", cache.cache.len()); - } - - let function_guid_cache = GUID_CACHE.get_or_init(Default::default); - if let Some(cache) = function_guid_cache.get(&view_id) { - log::info!("View function guids: {}", cache.cache.len()); - } - - let plat_cache = PLAT_MATCHER_CACHE.get_or_init(Default::default); - if let Some(plat) = view.default_platform() { - let platform_id = PlatformID::from(plat); - if let Some(cache) = plat_cache.get(&platform_id) { - log::info!("Platform functions: {}", cache.functions.len()); - log::info!("Platform types: {}", cache.types.len()); - log::info!( - "Platform matched functions: {}", - cache.matched_functions.len() - ); - } - } - } - - fn valid(&self, _view: &BinaryView) -> bool { - true - } -} - -#[no_mangle] -#[allow(non_snake_case)] -pub extern "C" fn CorePluginInit() -> bool { - binaryninja::logger::init(LevelFilter::Debug).unwrap(); - - workflow::insert_matcher_workflow(); - - binaryninja::command::register( - "WARP\\Apply Signature File Types", - "Load all types from a signature file and ignore functions", - types::LoadTypesCommand {}, - ); - - binaryninja::command::register( - "WARP\\Debug Cache", - "Debug cache sizes... because...", - DebugCache {}, - ); - - binaryninja::command::register_for_function( - "WARP\\Debug Signature", - "Print the entire signature for the function", - DebugFunction {}, - ); - - binaryninja::command::register_for_function( - "WARP\\Copy Pattern", - "Copy the computed pattern for the function", - copy::CopyFunctionGUID {}, - ); - - binaryninja::command::register( - "WARP\\Find Function From GUID", - "Locate the function in the view using a GUID", - find::FindFunctionFromGUID {}, - ); - - binaryninja::command::register( - "WARP\\Generate Signature File", - "Generates a signature file containing all binary view functions", - create::CreateSignatureFile {}, - ); - - binaryninja::command::register( - "WARP\\Apply Signature File", - "Applies a signature file to the current view", - apply::ApplySignatureFile {}, - ); - - true -} diff --git a/warp_binja/src/plugin/apply.rs b/warp_binja/src/plugin/apply.rs deleted file mode 100644 index f8b3408..0000000 --- a/warp_binja/src/plugin/apply.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::collections::HashMap; -use std::time::Instant; - -use binaryninja::binaryview::{BinaryView, BinaryViewExt}; -use binaryninja::command::Command; -use rayon::iter::ParallelIterator; -use signaturebuild::function::FunctionGUID; - -use crate::cache::cached_function_guid; -use crate::plugin::on_matched_function; - -pub struct ApplySignatureFile; - -// TODO: All this should do is insert data into the Matcher. this is leftover code. -impl Command for ApplySignatureFile { - fn action(&self, view: &BinaryView) { - // TODO: Start bulk modification - // TODO: view.begin_bulk_modify_symbols(); - let Some(file) = - binaryninja::interaction::get_open_filename_input("Apply Signature File", "*.sbin") - else { - return; - }; - - // TODO: signature files also need to store type information. - - let Ok(data) = std::fs::read(&file) else { - log::error!("Could not read signature file: {:?}", file); - return; - }; - - let Some(data) = signaturebuild::Data::from_bytes(&data) else { - log::error!("Could not get data from signature file: {:?}", file); - return; - }; - - // TODO: Turn Vec to HashSet so that functions with the same symbol and type get eliminated. - let data_functions: HashMap> = data - .functions - .into_iter() - .fold(HashMap::new(), |mut acc, func| { - #[allow(clippy::unwrap_or_default)] - acc.entry(func.guid).or_insert_with(Vec::new).push(func); - acc - }); - - let background_task = binaryninja::backgroundtask::BackgroundTask::new( - format!("Applying signatures from {:?}", file), - true, - ) - .unwrap(); - - let funcs = view.functions(); - let start = Instant::now(); - - background_task - .set_progress_text(format!("Building {} patterns to lookup...", funcs.len())); - - // TODO: Redo this. - let single_matched = funcs - .par_iter() - .filter_map(|func| { - let llil = func.low_level_il_if_available()?; - let pattern = cached_function_guid(&func, &llil)?; - Some((func, data_functions.get(&pattern)?)) - }) - .filter(|(_, sig)| sig.len() == 1) - .collect::>(); - - background_task.set_progress_text(format!("Applying {} matches...", single_matched.len())); - for (func, matched) in single_matched { - on_matched_function(&func, &matched[0]); - } - - log::info!("Signature application took {:?}", start.elapsed()); - - background_task.finish(); - } - - fn valid(&self, _view: &BinaryView) -> bool { - true - } -} diff --git a/warp_binja/src/plugin/copy.rs b/warp_binja/src/plugin/copy.rs deleted file mode 100644 index ee91fb4..0000000 --- a/warp_binja/src/plugin/copy.rs +++ /dev/null @@ -1,35 +0,0 @@ -use binaryninja::binaryview::BinaryView; -use binaryninja::command::FunctionCommand; -use binaryninja::function::Function; - -use crate::cache::cached_function_guid; - -pub struct CopyFunctionGUID; - -impl FunctionCommand for CopyFunctionGUID { - fn action(&self, _view: &BinaryView, func: &Function) { - let Ok(llil) = func.low_level_il() else { - log::error!("Could not get low level il for copied function"); - return; - }; - if let Some(guid) = cached_function_guid(func, &llil) { - log::info!( - "Function GUID for {}... {}", - func.symbol().short_name().to_string(), - guid - ); - if let Ok(mut clipboard) = arboard::Clipboard::new() { - let _ = clipboard.set_text(guid.to_string()); - } - } else { - log::error!( - "Failed to create GUID for function... 0x{:0x}", - func.start() - ); - } - } - - fn valid(&self, _view: &BinaryView, _func: &Function) -> bool { - true - } -} diff --git a/warp_binja/src/plugin/create.rs b/warp_binja/src/plugin/create.rs deleted file mode 100644 index 5eb529d..0000000 --- a/warp_binja/src/plugin/create.rs +++ /dev/null @@ -1,73 +0,0 @@ -use rayon::prelude::*; -use std::io::Write; -use std::thread; -use std::time::Instant; - -use crate::cache::cached_function; -use crate::convert::from_bn_type; -use binaryninja::binaryview::{BinaryView, BinaryViewExt}; -use binaryninja::command::Command; -use signaturebuild::Data; -use typebuild::r#type::ComputedType; - -pub struct CreateSignatureFile; - -// TODO: Prompt the user to add the newly created signature file to the signature blacklist (so that it doesn't keep getting applied) - -impl Command for CreateSignatureFile { - fn action(&self, view: &BinaryView) { - let mut signature_dir = binaryninja::user_directory().unwrap().join("signatures/"); - // TODO: This needs to split out each platform into its own bucket... - if let Some(default_plat) = view.default_platform() { - // If there is a default platform, put the signature in there. - signature_dir.push(default_plat.name().to_string()); - } - let view = view.to_owned(); - thread::spawn(move || { - let background_task = binaryninja::backgroundtask::BackgroundTask::new( - format!("Generating {} signatures... ", view.functions().len()), - true, - ) - .unwrap(); - - let start = Instant::now(); - - let mut data = Data::default(); - data.functions.par_extend( - view.functions() - .par_iter() - .filter_map(|func| cached_function(&func, func.low_level_il().ok()?.as_ref())), - ); - data.types.extend(view.types().iter().map(|ty| { - let ref_ty = ty.type_object().to_owned(); - ComputedType::new(from_bn_type(&view, ref_ty, u8::MAX)) - })); - - // And type generation :3 - log::info!("Signature generation took {:?}", start.elapsed()); - - if let Some(sig_file_name) = binaryninja::interaction::get_text_line_input( - "Signature File", - "Create Signature File", - ) { - let save_file = signature_dir.join(sig_file_name + ".sbin"); - log::info!("Saving to signatures to {:?}...", &save_file); - // TODO: Should we overwrite? Prompt user. - if let Ok(mut file) = std::fs::File::create(&save_file) { - match file.write_all(&data.to_bytes()) { - Ok(_) => log::info!("Signature file saved successfully."), - Err(e) => log::error!("Failed to write data to signature file: {:?}", e), - } - } else { - log::error!("Could not create signature file: {:?}", save_file); - } - } - - background_task.finish(); - }); - } - - fn valid(&self, _view: &BinaryView) -> bool { - true - } -} diff --git a/warp_binja/src/plugin/find.rs b/warp_binja/src/plugin/find.rs deleted file mode 100644 index 4ae8864..0000000 --- a/warp_binja/src/plugin/find.rs +++ /dev/null @@ -1,58 +0,0 @@ -use rayon::prelude::*; -use signaturebuild::function::FunctionGUID; -use std::thread; - -use binaryninja::binaryview::{BinaryView, BinaryViewExt}; -use binaryninja::command::Command; - -use crate::cache::cached_function_guid; - -pub struct FindFunctionFromGUID; - -impl Command for FindFunctionFromGUID { - fn action(&self, view: &BinaryView) { - let Some(guid_str) = binaryninja::interaction::get_text_line_input( - "Function GUID", - "Find Function from GUID", - ) else { - return; - }; - - let Ok(searched_guid) = guid_str.parse::() else { - log::error!("Failed to parse function guid... {}", guid_str); - return; - }; - - log::info!("Searching functions for GUID... {}", searched_guid); - let funcs = view.functions(); - thread::spawn(move || { - let background_task = binaryninja::backgroundtask::BackgroundTask::new( - format!("Searching functions for GUID... {}", searched_guid), - true, - ) - .unwrap(); - - // TODO: While background_task has not finished. - let matched = funcs - .par_iter() - .filter_map(|func| { - Some(( - func.clone(), - cached_function_guid(&func, func.low_level_il_if_available()?.as_ref())?, - )) - }) - .filter(|(_func, guid)| guid.eq(&searched_guid)) - .collect::>(); - - for (func, _) in matched { - log::info!("Match found at function... 0x{:0x}", func.start()); - } - - background_task.finish(); - }); - } - - fn valid(&self, _view: &BinaryView) -> bool { - true - } -} diff --git a/warp_binja/src/plugin/types.rs b/warp_binja/src/plugin/types.rs deleted file mode 100644 index 2bcb76d..0000000 --- a/warp_binja/src/plugin/types.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::convert::to_bn_type; -use binaryninja::binaryview::{BinaryView, BinaryViewExt}; -use binaryninja::command::Command; -use std::time::Instant; - -pub struct LoadTypesCommand; - -impl Command for LoadTypesCommand { - fn action(&self, view: &BinaryView) { - let Some(file) = binaryninja::interaction::get_open_filename_input( - "Apply Signature File Types", - "*.sbin", - ) else { - return; - }; - - let Ok(data) = std::fs::read(&file) else { - log::error!("Could not read signature file: {:?}", file); - return; - }; - - let Some(data) = signaturebuild::Data::from_bytes(&data) else { - log::error!("Could not get data from signature file: {:?}", file); - return; - }; - - let view = view.to_owned(); - std::thread::spawn(move || { - let background_task = binaryninja::backgroundtask::BackgroundTask::new( - format!("Applying {} types...", data.types.len()), - true, - ) - .unwrap(); - - let start = Instant::now(); - for comp_ty in data.types { - let ty_id = comp_ty.guid.to_string(); - let ty_name = comp_ty.ty.name.to_owned().unwrap_or_else(|| ty_id.clone()); - // TODO: Using arch here is problematic. - let arch = view.default_arch().unwrap(); - view.define_auto_type_with_id(ty_name, ty_id, &to_bn_type(&arch, &comp_ty.ty)); - } - - log::info!("Type application took {:?}", start.elapsed()); - background_task.finish(); - }); - } - - fn valid(&self, _view: &BinaryView) -> bool { - true - } -} diff --git a/warp_binja/src/plugin/workflow.rs b/warp_binja/src/plugin/workflow.rs deleted file mode 100644 index da8473f..0000000 --- a/warp_binja/src/plugin/workflow.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::matcher::cached_function_match; -use binaryninja::llil; -use binaryninja::workflow::{Activity, AnalysisContext, Workflow}; - -const MATCHER_ACTIVITY_NAME: &str = "analysis.plugins.WARPMatcher"; -// NOTE: runOnce is off because previously matched functions need info applied. -const MATCHER_ACTIVITY_CONFIG: &str = r#"{ - "name": "analysis.plugins.WARPMatcher", - "title" : "WARP Matcher", - "description": "This analysis step applies WARP info to matched functions...", - "eligibility": { - "auto": { "default": true }, - "runOnce": false - } -}"#; - -pub fn insert_matcher_workflow() { - let matcher_activity = |ctx: &AnalysisContext| { - let function = ctx.function(); - if function.has_user_annotations() { - // User has touched the function, stop trying to match on it! - return; - } - - if let Some(llil) = unsafe { ctx.llil_function::>() } { - cached_function_match(&function, &llil); - } - }; - - let meta_workflow = Workflow::new_from_copy("core.function.metaAnalysis"); - let activity = Activity::new_with_action(MATCHER_ACTIVITY_CONFIG, matcher_activity); - meta_workflow.register_activity(&activity).unwrap(); - meta_workflow.insert( - "core.function.runFunctionRecognizers", - [MATCHER_ACTIVITY_NAME], - ); - meta_workflow.register().unwrap(); -}