diff options
| author | Ryan Snyder <ryan@vector35.com> | 2021-01-21 18:27:48 +0000 |
|---|---|---|
| committer | KyleMiles <krm504@nyu.edu> | 2021-01-21 19:06:55 +0000 |
| commit | d3140edec185f47235b9e4642bdd56d6c585a341 (patch) | |
| tree | a61859c29e4e3539daea2b761bb1439d942beaf4 | |
| parent | c0ddbf0c76d3f1bb7a2b2024f749afc8b9482575 (diff) | |
This is a combination of 23 commits, the work of Ryan Snyder:
Initial fresh repo
Add support for recent calling convention API updates and folds the binaryninjacore-sys crate directly into this one.
Add support for auto function analysis suppression
Finish moving binaryninjacore-sys back into this crate
Update for Symbol/Segment core API changes
Update for Symbol API cleanup
api: advance submodule reference, support Token changes
arch/lifting: support for flags in custom architectures
arch/lifting: support default flag write behaviors, handle more ops
build: enable headless binary support on MacOS via evil hack
bv: add BinaryView wrapper support, remove wrong comment
api: update to latest binja dev branch support
deps: bump dep versions
rust: bump to 2018 edition
api: bump to avoid cargo submodule brokenness
build: improve binaryninja path detection; enable linux linkhack
bv: stub for bv load settings
arch: fix flag related crash, minor llil update
api: update for recent changes
macos: disable linkhack briefly
35 files changed, 10432 insertions, 0 deletions
@@ -73,3 +73,13 @@ build generator types/ plugins/ + +# Rust +rust/**/*.swp +rust/**/*.rs.bk +rust/Cargo.lock +rust/target/ +rust/binaryninjacore-sys/Cargo.lock +rust/binaryninjacore-sys/target/ +rust/examples/*/Cargo.lock +rust/examples/*/target/ @@ -65,3 +65,7 @@ There are many examples available. The [Python examples folder ](https://github. * [print_syscalls](https://github.com/Vector35/binaryninja-api/tree/dev/examples/print_syscalls) is a standalone executable that prints the syscalls used in a given binary (only usable with licenses that support headless API access) * [triage](https://github.com/Vector35/binaryninja-api/tree/dev/examples/triage) is a fully featured plugin that is shipped and enabled by default, demonstrating how to do a wide variety of tasks including extending the UI through QT * [x86 extension](https://github.com/Vector35/binaryninja-api/tree/dev/examples/x86_extension) creates an architecture extension which shows how to modify the behavior of the build-in architectures without creating a complete replacement + +## Licensing + +Some components may be released under compatible but slightly different open source licenses and will have their own LICENSE file as appropriate. diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 00000000..d5e5e639 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "binja" +version = "0.1.0" +authors = ["Ryan Snyder <ryan.snyder.or@gmail.com>"] +edition = "2018" + +[dependencies] +log = "0.4" +libc = "0.2" +rayon = { version = "1.0", optional = true } +binaryninjacore-sys = { path = "binaryninjacore-sys" } diff --git a/rust/LICENSE b/rust/LICENSE new file mode 100644 index 00000000..265883c5 --- /dev/null +++ b/rust/LICENSE @@ -0,0 +1,13 @@ +Copyright 2021 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. diff --git a/rust/binaryninjacore-sys/Cargo.toml b/rust/binaryninjacore-sys/Cargo.toml new file mode 100644 index 00000000..5b4cc063 --- /dev/null +++ b/rust/binaryninjacore-sys/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "binaryninjacore-sys" +version = "0.1.0" +authors = ["Ryan Snyder <ryan.snyder.or@gmail.com>"] +build = "build.rs" + +[build-dependencies] +bindgen = "0.49" diff --git a/rust/binaryninjacore-sys/build.rs b/rust/binaryninjacore-sys/build.rs new file mode 100644 index 00000000..b151fc3b --- /dev/null +++ b/rust/binaryninjacore-sys/build.rs @@ -0,0 +1,130 @@ +extern crate bindgen; + +use std::io::prelude::*; +use std::io::BufReader; +use std::path::PathBuf; +use std::fs::File; +use std::env; + +#[cfg(target_os = "macos")] +static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun"); + +#[cfg(target_os = "linux")] +static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun"); + +#[cfg(windows)] +static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun"); + +fn link_path() -> PathBuf { + let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap()); + let lastrun = PathBuf::from(&home) + .join(LASTRUN_PATH.1); + + File::open(lastrun).and_then(|f| { + let mut binja_path = String::new(); + let mut reader = BufReader::new(f); + + reader.read_line(&mut binja_path)?; + Ok(PathBuf::from(binja_path.trim())) + }).unwrap_or_else(|_| { + #[cfg(target_os = "macos")] + return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS"); + + #[cfg(target_os = "linux")] + return home.join("binaryninja"); + + #[cfg(windows)] + return PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\"); + }) +} + +fn main() { + // Allow the library search path to be overridden for internal dev builds, but + // otherwise search the usual install paths + let out_dir = env::var("OUT_DIR").unwrap(); + let link_path = env::var("BINARYNINJADIR") + .map(PathBuf::from) + .unwrap_or_else(|_| link_path()); + + #[cfg(unix)] + { + use std::process::Command; + + // Spectacularly evil linking hack due to Cargo shortcomings. In a nutshell, + // due to the inability for our .rlib crate to contribute linker arguments + // (or even just an rpath!) to our consumers, any resulting binaries will + // fail to run unless loaded into a process that already has the core loaded. + // + // For plugins this restriction is fine as they'll only be loaded by the core; + // headless binaries on the other hand (consider `cargo test`, too!) will fail + // because libbinaryninjacore is not in the library path on pretty much any + // system anywhere. If only we could trick the linker into linking with an + // absolute path... + // + // LOOK UPON MY WORKS, YE MIGHTY, AND DESPAIR + // + // We build a simple do-nothing library called liblinkhack and add it as a + // native dependency. Crucially, we ensure that LC_ID_DYLIB (macos) or DT_SONAME + // (linux) is set to the absolute path of libbinaryninjacore. This has the effect + // that any binary attempting to link with liblinkhack will have its dependency + // on liblinkhack recorded as the path to the binaryninja core. Later on, the + // actual core will get linked and that dependency will be recorded in the normal + // way. + // + // tl;dr we're linking against a fake, shim library that actually results in + // the binaryninja core getting linked twice, once with an absolute path and once + // correctly. yes, this is as horrifying as you think it is. no, there is no other + // way to make `cargo test` work. + // + // I'm not happy about it either. +/* + #[cfg(target_os = "macos")] + Command::new("clang") + .args(&["-Wl,-dylib", "-o"]) + .arg(&format!("{}/liblinkhack.dylib", out_dir)) + .arg(&format!("-Wl,-install_name,{}/libbinaryninjacore.dylib", &link_path.to_str().unwrap())) + .arg(&format!("{}/linkhack/linkhack.c", env::var("CARGO_MANIFEST_DIR").unwrap())) + .status().unwrap(); + */ + + #[cfg(target_os = "linux")] + { + Command::new("gcc") + .args(&["-shared", "-o"]) + .arg(&format!("{}/liblinkhack.so", out_dir)) + .arg(&format!("-Wl,-soname,{}/libbinaryninjacore.so.1", &link_path.to_str().unwrap())) + .arg(&format!("{}/linkhack/linkhack.c", env::var("CARGO_MANIFEST_DIR").unwrap())) + .status().unwrap(); + + // Linux builds of binaryninja ship with libbinaryninjacore.so.1 in the + // application folder and no symlink. The linker will attempt to link with + // libbinaryninjacore.so. Since this is likely going to fail, we detect this + // ahead of time and create an appropriately named symlink inside of OUT_DIR + // and add it to the library search path. + let symlink_target = PathBuf::from(&out_dir).join("libbinaryninjacore.so"); + if !link_path.join("libbinaryninjacore.so").exists() && !symlink_target.exists() { + use std::os::unix::fs; + fs::symlink(link_path.join("libbinaryninjacore.so.1"), PathBuf::from(&out_dir).join("libbinaryninjacore.so")) + .expect("failed to create required symlink"); + } + } + + //println!("cargo:rustc-link-lib=linkhack"); + //println!("cargo:rustc-link-search={}", out_dir); + } + + println!("cargo:rustc-link-lib=binaryninjacore"); + println!("cargo:rustc-link-search={}", link_path.to_str().unwrap()); + + let bindings = bindgen::builder().header("wrapper.hpp") + .generate_comments(false) + .whitelist_function("BN.*") + .rustified_enum("BN.*") + .rustfmt_bindings(false) // required; things break otherwise + .generate() + .expect("Unable to generate bindings"); + + bindings + .write_to_file(PathBuf::from(out_dir).join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/rust/binaryninjacore-sys/linkhack/linkhack.c b/rust/binaryninjacore-sys/linkhack/linkhack.c new file mode 100644 index 00000000..6d4b3f37 --- /dev/null +++ b/rust/binaryninjacore-sys/linkhack/linkhack.c @@ -0,0 +1,20 @@ +// Have some overlap with likely-called functions so ld on +// linux doesn't skip linking liblinkhack +void BNLog() {} +void BNLogDebug() {} +void BNLogInfo() {} +void BNLogWarn() {} +void BNLogError() {} +void BNLogAlert() {} +void BNShutdown() {} +void BNNewViewReference() {} +void BNFreeBinaryView() {} +void BNInitCorePlugins() {} +void BNAllocString() {} +void BNFreeString() {} +void BNRegisterBinaryViewType() {} +void BNGetArchitectureList() {} +void BNNewFunctionReference() {} +void BNFreeFunction() {} +void BNGetFunctionBasicBlockList() {} +void BNRegisterArchitecture() {} diff --git a/rust/binaryninjacore-sys/src/lib.rs b/rust/binaryninjacore-sys/src/lib.rs new file mode 100644 index 00000000..04d76a43 --- /dev/null +++ b/rust/binaryninjacore-sys/src/lib.rs @@ -0,0 +1,6 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(unused)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/rust/binaryninjacore-sys/wrapper.hpp b/rust/binaryninjacore-sys/wrapper.hpp new file mode 100644 index 00000000..263de01c --- /dev/null +++ b/rust/binaryninjacore-sys/wrapper.hpp @@ -0,0 +1 @@ +#include "binaryninja-api/binaryninjacore.h" diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs new file mode 100644 index 00000000..fc56140c --- /dev/null +++ b/rust/src/architecture.rs @@ -0,0 +1,1885 @@ +// container abstraction to avoid Vec<> (want CoreArchFlagList, CoreArchRegList) +// RegisterInfo purge +use binaryninjacore_sys::*; + +use std::ffi::{CString, CStr}; +use std::ptr; +use std::ops::Drop; +use std::borrow::{Borrow, Cow}; +use std::slice; +use std::ops; +use std::mem::zeroed; +use std::hash::Hash; +use std::collections::HashMap; + +use crate::{BranchType, Endianness}; +use crate::callingconvention::CallingConvention; +use crate::platform::Platform; + +use crate::llil::{Lifter, LiftedExpr, FlagWriteOp}; +use crate::llil::{get_default_flag_write_llil, get_default_flag_cond_llil}; + +use crate::string::*; +use crate::rc::*; + +pub enum BranchInfo { + Unconditional(u64), + False(u64), + True(u64), + Call(u64), + FunctionReturn, + SystemCall, + Indirect, + Exception, + Unresolved, +} + +pub struct BranchIter<'a>(&'a InstructionInfo, ops::Range<usize>); +impl<'a> Iterator for BranchIter<'a> { + type Item = (BranchInfo, Option<CoreArchitecture>); + + fn next(&mut self) -> Option<Self::Item> { + use crate::BranchType::*; + + match self.1.next() { + Some(i) => { + let target = (self.0).0.branchTarget[i]; + let arch = (self.0).0.branchArch[i]; + let arch = if arch.is_null() { None } else { Some(CoreArchitecture(arch)) }; + + let res = match (self.0).0.branchType[i] { + UnconditionalBranch => BranchInfo::Unconditional(target), + FalseBranch => BranchInfo::False(target), + TrueBranch => BranchInfo::True(target), + CallDestination => BranchInfo::Call(target), + FunctionReturn => BranchInfo::FunctionReturn, + SystemCall => BranchInfo::SystemCall, + IndirectBranch => BranchInfo::Indirect, + ExceptionBranch => BranchInfo::Exception, + UnresolvedBranch => BranchInfo::Unresolved, + }; + + Some((res, arch)) + } + _ => None, + } + } +} + +#[repr(C)] +pub struct InstructionInfo(BNInstructionInfo); +impl InstructionInfo { + pub fn new(len: usize, branch_delay: bool) -> Self { + InstructionInfo ( + BNInstructionInfo { + length: len, + archTransitionByTargetAddr: false, + branchDelay: branch_delay, + branchCount: 0usize, + branchType: [BranchType::UnresolvedBranch; 3], + branchTarget: [0u64; 3], + branchArch: [ptr::null_mut(); 3], + } + ) + } + + pub fn len(&self) -> usize { self.0.length } + pub fn branch_count(&self) -> usize { self.0.branchCount } + pub fn branch_delay(&self) -> bool { self.0.branchDelay } + pub fn branches(&self) -> BranchIter { BranchIter(self, 0 .. self.branch_count()) } + + pub fn allow_arch_transition_by_target_addr(&mut self, transition: bool) { + self.0.archTransitionByTargetAddr = transition; + } + + pub fn add_branch(&mut self, branch: BranchInfo, arch: Option<CoreArchitecture>) { + if self.0.branchCount < self.0.branchType.len() { + let idx = self.0.branchCount; + + let ty = match branch { + BranchInfo::Unconditional(t) => { + self.0.branchTarget[idx] = t; + BranchType::UnconditionalBranch + } + BranchInfo::False(t) => { + self.0.branchTarget[idx] = t; + BranchType::FalseBranch + } + BranchInfo::True(t) => { + self.0.branchTarget[idx] = t; + BranchType::TrueBranch + } + BranchInfo::Call(t) => { + self.0.branchTarget[idx] = t; + BranchType::CallDestination + } + BranchInfo::FunctionReturn => BranchType::FunctionReturn, + BranchInfo::SystemCall => BranchType::SystemCall, + BranchInfo::Indirect => BranchType::IndirectBranch, + BranchInfo::Exception => BranchType::ExceptionBranch, + BranchInfo::Unresolved => BranchType::UnresolvedBranch, + }; + + self.0.branchType[idx] = ty; + self.0.branchArch[idx] = match arch { + Some(a) => a.0, + _ => ptr::null_mut(), + }; + + self.0.branchCount += 1; + } else { + error!("Attempt to branch to instruction with no additional branch space!"); + } + } +} + +pub enum InstructionTextTokenContents { + Text, + Instruction, + OperandSeparator, + Register, + Integer(u64), // TODO size? + PossibleAddress(u64), // TODO size? + BeginMemoryOperand, + EndMemoryOperand, + FloatingPoint, + CodeRelativeAddress(u64), +} + +pub use binaryninjacore_sys::BNInstructionTextTokenContext as InstructionTextTokenContext; + +#[repr(C)] +pub struct InstructionTextToken(BNInstructionTextToken); +impl InstructionTextToken { + pub fn new<T: Into<Vec<u8>>>(contents: InstructionTextTokenContents, text: T) -> Self { + use self::BNInstructionTextTokenType::*; + use self::InstructionTextTokenContents::*; + + let mut res: BNInstructionTextToken = unsafe { zeroed() }; + + res.context = InstructionTextTokenContext::NoTokenContext; + res.address = 0; + res.size = 0; // TODO supply? x86 seems to, others don't... + res.operand = 0xffff_ffff; + res.confidence = 0xff; + + match contents { + Integer(v) => res.value = v, + PossibleAddress(v) | + CodeRelativeAddress(v) => { + res.value = v; + res.address = v; + } + _ => {}, + } + + res.type_ = match contents { + + Text => TextToken, + Instruction => InstructionToken, + OperandSeparator => OperandSeparatorToken, + Register => RegisterToken, + Integer(_) => IntegerToken, + PossibleAddress(_) => PossibleAddressToken, + BeginMemoryOperand => BeginMemoryOperandToken, + EndMemoryOperand => EndMemoryOperandToken, + FloatingPoint => FloatingPointToken, + CodeRelativeAddress(_) => CodeRelativeAddressToken, + }; + + res.text = CString::new(text).unwrap().into_raw(); + + InstructionTextToken(res) + } + + pub fn text(&self) -> &CStr { + unsafe { CStr::from_ptr(self.0.text) } + } + + pub fn contents(&self) -> InstructionTextTokenContents { + use self::BNInstructionTextTokenType::*; + use self::InstructionTextTokenContents::*; + + match self.0.type_ { + TextToken => Text, + InstructionToken => Instruction, + OperandSeparatorToken => OperandSeparator, + RegisterToken => Register, + IntegerToken => Integer(self.0.value), + PossibleAddressToken => PossibleAddress(self.0.value), + BeginMemoryOperandToken => BeginMemoryOperand, + EndMemoryOperandToken => EndMemoryOperand, + FloatingPointToken => FloatingPoint, + CodeRelativeAddressToken => CodeRelativeAddress(self.0.value), + _ => unimplemented!("woops"), + } + } + + pub fn context(&self) -> InstructionTextTokenContext { + self.0.context + } + + pub fn size(&self) -> usize { + self.0.size + } + + pub fn operand(&self) -> usize { + self.0.operand + } + + pub fn address(&self) -> u64 { + self.0.address + } +} + +impl Clone for InstructionTextToken { + fn clone(&self) -> Self { + InstructionTextToken ( + BNInstructionTextToken { + type_: self.0.type_, + context: self.0.context, + address: self.0.address, + size: self.0.size, + operand: self.0.operand, + value: self.0.value, + width: 0, + text: self.text().to_owned().into_raw(), + confidence: 0xff, + typeNames: ptr::null_mut(), + namesCount: 0, + } + ) + } +} + +impl Drop for InstructionTextToken { + fn drop(&mut self) { + let _owned = unsafe { CString::from_raw(self.0.text) }; + } +} + +pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend; +pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition; +pub use binaryninjacore_sys::BNFlagRole as FlagRole; + +pub trait RegisterInfo: Sized { + type RegType: Register<InfoType=Self>; + + fn parent(&self) -> Option<Self::RegType>; + fn size(&self) -> usize; + fn offset(&self) -> usize; + fn implicit_extend(&self) -> ImplicitRegisterExtend; +} + +pub trait Register: Sized + Clone + Copy { + type InfoType: RegisterInfo<RegType=Self>; + + fn name(&self) -> Cow<str>; + fn info(&self) -> Self::InfoType; + + /// Unique identifier for this `Register`. + /// + /// *MUST* be in the range [0, 0x7fff_ffff] + fn id(&self) -> u32; +} + +pub trait Flag: Sized + Clone + Copy { + type FlagClass: FlagClass; + + fn name(&self) -> Cow<str>; + fn role(&self, class: Option<Self::FlagClass>) -> FlagRole; + + /// Unique identifier for this `Flag`. + /// + /// *MUST* be in the range [0, 0x7fff_ffff] + fn id(&self) -> u32; +} + +pub trait FlagWrite: Sized + Clone + Copy { + type FlagType: Flag; + type FlagClass: FlagClass; + + fn name(&self) -> Cow<str>; + fn class(&self) -> Option<Self::FlagClass>; + + /// Unique identifier for this `FlagWrite`. + /// + /// *MUST NOT* be 0. + /// *MUST* be in the range [1, 0x7fff_ffff] + fn id(&self) -> u32; + + fn flags_written(&self) -> Vec<Self::FlagType>; +} + +pub trait FlagClass: Sized + Clone + Copy + Hash + Eq { + fn name(&self) -> Cow<str>; + + /// Unique identifier for this `FlagClass`. + /// + /// *MUST NOT* be 0. + /// *MUST* be in the range [1, 0x7fff_ffff] + fn id(&self) -> u32; +} + +pub trait FlagGroup: Sized + Clone + Copy { + type FlagType: Flag; + type FlagClass: FlagClass; + + fn name(&self) -> Cow<str>; + + /// Unique identifier for this `FlagGroup`. + /// + /// *MUST* be in the range [0, 0x7fff_ffff] + fn id(&self) -> u32; + + /// Returns the list of flags that need to be resolved in order + /// to take the clean flag resolution path -- at time of writing, + /// all required flags must have been set by the same instruction, + /// and the 'querying' instruction must be reachable from *one* + /// instruction that sets all of these flags. + fn flags_required(&self) -> Vec<Self::FlagType>; + + /// Returns the mapping of Semantic Flag Classes to Flag Conditions, + /// in the context of this Flag Group. + /// + /// Example: + /// + /// If we have a group representing `cr1_lt` (as in PowerPC), we would + /// have multiple Semantic Flag Classes used by the different Flag Write + /// Types to represent the different comparisons, so for `cr1_lt` we + /// would return a mapping along the lines of: + /// + /// ``` + /// cr1_signed -> LLFC_SLT, + /// cr1_unsigned -> LLFC_ULT, + /// ``` + /// + /// This allows the core to recover the semantics of the comparison and + /// inline it into conditional branches when appropriate. + fn flag_conditions(&self) -> HashMap<Self::FlagClass, FlagCondition>; +} + +pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> { + type Handle: Borrow<Self> + Clone; + + type RegisterInfo: RegisterInfo<RegType=Self::Register>; + type Register: Register<InfoType=Self::RegisterInfo>; + + type Flag: Flag<FlagClass=Self::FlagClass>; + type FlagWrite: FlagWrite<FlagType=Self::Flag, FlagClass=Self::FlagClass>; + type FlagClass: FlagClass; + type FlagGroup: FlagGroup<FlagType=Self::Flag, FlagClass=Self::FlagClass>; + + type InstructionTextContainer: Into<Vec<InstructionTextToken>>; + + fn endianness(&self) -> Endianness; + fn address_size(&self) -> usize; + fn default_integer_size(&self) -> usize; + fn instruction_alignment(&self) -> usize; + fn max_instr_len(&self) -> usize; + fn opcode_display_len(&self) -> usize; + + fn associated_arch_by_addr(&self, addr: &mut u64) -> CoreArchitecture; + + fn instruction_info(&self, data: &[u8], addr: u64) -> Option<InstructionInfo>; + fn instruction_text(&self, data: &[u8], addr: u64) -> Option<(usize, Self::InstructionTextContainer)>; + fn instruction_llil(&self, data: &[u8], addr: u64, il: &mut Lifter<Self>) -> Option<(usize, bool)>; + + /// Fallback flag value calculation path. This method is invoked when the core is unable to + /// recover flag use semantics, and resorts to emitting instructions that explicitly set each + /// observed flag to the value of an expression returned by this function. + /// + /// This function *MUST NOT* append instructions that have side effects. + /// + /// This function *MUST NOT* observe the values of other flags. + /// + /// This function *MUST* return `None` or an expression representing a boolean value. + fn flag_write_llil<'a>(&self, flag: Self::Flag, flag_write_type: Self::FlagWrite, op: FlagWriteOp<Self::Register>, il: &'a mut Lifter<Self>) + -> Option<LiftedExpr<'a, Self>> + { + let role = flag.role(flag_write_type.class()); + Some(get_default_flag_write_llil(self, role, op, il)) + } + + /// Determines what flags need to be examined in order to attempt automatic recovery of the + /// semantics of this flag use. + /// + /// If automatic recovery is not possible, the `flag_cond_llil` method will be invoked to give + /// this `Architecture` implementation arbitrary control over the expression to be evaluated. + fn flags_required_for_flag_condition(&self, condition: FlagCondition, class: Option<Self::FlagClass>) -> Vec<Self::Flag>; + + /// This function *MUST NOT* append instructions that have side effects. + /// + /// This function *MUST NOT* observe the values of flags not returned by + /// `flags_required_for_flag_condition`. + /// + /// This function *MUST* return `None` or an expression representing a boolean value. + fn flag_cond_llil<'a>(&self, cond: FlagCondition, class: Option<Self::FlagClass>, il: &'a mut Lifter<Self>) + -> Option<LiftedExpr<'a, Self>> + { + Some(get_default_flag_cond_llil(self, cond, class, il)) + } + + /// Performs fallback resolution when the core was unable to recover the semantics of a + /// `LLIL_FLAG_GROUP` expression. This occurs when multiple instructions may have set the flags + /// at the flag group query, or when the `FlagGroup::flag_conditions()` map doesn't have an entry + /// for the `FlagClass` associated with the `FlagWrite` type of the expression that last set + /// the flags required by the `FlagGroup` `group`. + /// + /// In this fallback path, the `Architecture` must generate the boolean expression in terms of + /// the values of that flags returned by `group`'s `flags_required` method. + /// + /// This function must return an expression representing a boolean (as in, size of `0`) value. + /// It is not allowed to add any instructions that can cause side effects. + /// + /// This function must not observe the values of any flag not returned by `group`'s + /// `flags_required` method. + fn flag_group_llil<'a>(&self, group: Self::FlagGroup, il: &'a mut Lifter<Self>) -> Option<LiftedExpr<'a, Self>>; + + fn registers_all(&self) -> Vec<Self::Register>; + fn registers_full_width(&self) -> Vec<Self::Register>; + fn registers_global(&self) -> Vec<Self::Register>; + + fn flags(&self) -> Vec<Self::Flag>; + fn flag_write_types(&self) -> Vec<Self::FlagWrite>; + fn flag_classes(&self) -> Vec<Self::FlagClass>; + fn flag_groups(&self) -> Vec<Self::FlagGroup>; + + + fn stack_pointer_reg(&self) -> Option<Self::Register>; + fn link_reg(&self) -> Option<Self::Register>; + + fn register_from_id(&self, id: u32) -> Option<Self::Register>; + fn flag_from_id(&self, id: u32) -> Option<Self::Flag>; + fn flag_write_from_id(&self, id: u32) -> Option<Self::FlagWrite>; + fn flag_class_from_id(&self, id: u32) -> Option<Self::FlagClass>; + fn flag_group_from_id(&self, id: u32) -> Option<Self::FlagGroup>; + + fn handle(&self) -> Self::Handle; +} + +pub struct CoreRegisterInfo(*mut BNArchitecture, u32, BNRegisterInfo); +impl RegisterInfo for CoreRegisterInfo { + type RegType = CoreRegister; + + fn parent(&self) -> Option<CoreRegister> { + if self.1 != self.2.fullWidthRegister { + Some(CoreRegister(self.0, self.2.fullWidthRegister)) + } else { + None + } + } + + fn size(&self) -> usize { + self.2.size + } + + fn offset(&self) -> usize { + self.2.offset + } + + fn implicit_extend(&self) -> ImplicitRegisterExtend { + self.2.extend + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreRegister(*mut BNArchitecture, u32); +impl Register for CoreRegister { + type InfoType = CoreRegisterInfo; + + fn name(&self) -> Cow<str> { + unsafe { + let name = BNGetArchitectureRegisterName(self.0, self.1); + + // We need to guarantee ownership, as if we're still + // a Borrowed variant we're about to free the underlying + // memory. + let res = CStr::from_ptr(name); + let res = res.to_string_lossy().into_owned().into(); + + BNFreeString(name); + + res + } + } + + fn info(&self) -> CoreRegisterInfo { + CoreRegisterInfo(self.0, self.1, unsafe { + BNGetArchitectureRegisterInfo(self.0, self.1) + }) + } + + fn id(&self) -> u32 { + self.1 + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreFlag(*mut BNArchitecture, u32); +impl Flag for CoreFlag { + type FlagClass = CoreFlagClass; + + fn name(&self) -> Cow<str> { + unsafe { + let name = BNGetArchitectureFlagName(self.0, self.1); + + // We need to guarantee ownership, as if we're still + // a Borrowed variant we're about to free the underlying + // memory. + let res = CStr::from_ptr(name); + let res = res.to_string_lossy().into_owned().into(); + + BNFreeString(name); + + res + } + } + + fn role(&self, class: Option<CoreFlagClass>) -> FlagRole { + let class_id = match class { + Some(class) => class.1, + _ => 0 + }; + + unsafe { BNGetArchitectureFlagRole(self.0, self.1, class_id) } + } + + fn id(&self) -> u32 { + self.1 + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreFlagWrite(*mut BNArchitecture, u32); +impl FlagWrite for CoreFlagWrite { + type FlagType = CoreFlag; + type FlagClass = CoreFlagClass; + + fn name(&self) -> Cow<str> { + unsafe { + let name = BNGetArchitectureFlagWriteTypeName(self.0, self.1); + + // We need to guarantee ownership, as if we're still + // a Borrowed variant we're about to free the underlying + // memory. + let res = CStr::from_ptr(name); + let res = res.to_string_lossy().into_owned().into(); + + BNFreeString(name); + + res + } + } + + fn id(&self) -> u32 { + self.1 + } + + fn flags_written(&self) -> Vec<CoreFlag> { + let mut count: usize = 0; + let regs: *mut u32 = unsafe { + BNGetArchitectureFlagsWrittenByFlagWriteType(self.0, self.1, &mut count as *mut _) + }; + + let ret = unsafe { + slice::from_raw_parts_mut(regs, count).iter().map(|reg| CoreFlag(self.0, *reg)).collect() + }; + + unsafe { BNFreeRegisterList(regs); } + + ret + } + + fn class(&self) -> Option<CoreFlagClass> { + let class = unsafe { BNGetArchitectureSemanticClassForFlagWriteType(self.0, self.1) }; + + match class { + 0 => None, + id => Some(CoreFlagClass(self.0, id)), + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreFlagClass(*mut BNArchitecture, u32); +impl FlagClass for CoreFlagClass { + fn name(&self) -> Cow<str> { + unsafe { + let name = BNGetArchitectureSemanticFlagClassName(self.0, self.1); + + // We need to guarantee ownership, as if we're still + // a Borrowed variant we're about to free the underlying + // memory. + let res = CStr::from_ptr(name); + let res = res.to_string_lossy().into_owned().into(); + + BNFreeString(name); + + res + } + } + + fn id(&self) -> u32 { + self.1 + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct CoreFlagGroup(*mut BNArchitecture, u32); +impl FlagGroup for CoreFlagGroup { + type FlagType = CoreFlag; + type FlagClass = CoreFlagClass; + + fn name(&self) -> Cow<str> { + unsafe { + let name = BNGetArchitectureSemanticFlagGroupName(self.0, self.1); + + // We need to guarantee ownership, as if we're still + // a Borrowed variant we're about to free the underlying + // memory. + let res = CStr::from_ptr(name); + let res = res.to_string_lossy().into_owned().into(); + + BNFreeString(name); + + res + } + } + + fn id(&self) -> u32 { + self.1 + } + + fn flags_required(&self) -> Vec<CoreFlag> { + let mut count: usize = 0; + let regs: *mut u32 = unsafe { + BNGetArchitectureFlagsRequiredForSemanticFlagGroup(self.0, self.1, &mut count as *mut _) + }; + + let ret = unsafe { + slice::from_raw_parts_mut(regs, count).iter().map(|reg| CoreFlag(self.0, *reg)).collect() + }; + + unsafe { BNFreeRegisterList(regs); } + + ret + } + + fn flag_conditions(&self) -> HashMap<CoreFlagClass, FlagCondition> { + let mut count: usize = 0; + + unsafe { + let flag_conds = BNGetArchitectureFlagConditionsForSemanticFlagGroup(self.0, self.1, &mut count as *mut _); + + let ret = slice::from_raw_parts_mut(flag_conds, count).iter().map(|class_cond| { + (CoreFlagClass(self.0, class_cond.semanticClass), class_cond.condition) + }).collect(); + + BNFreeFlagConditionsForSemanticFlagGroup(flag_conds); + + ret + } + } +} + +pub struct CoreArchitectureList(*mut *mut BNArchitecture, usize); +impl ops::Deref for CoreArchitectureList { + type Target = [CoreArchitecture]; + + fn deref(&self) -> &Self::Target { + unsafe { slice::from_raw_parts_mut(self.0 as *mut CoreArchitecture, self.1) } + } +} + +impl Drop for CoreArchitectureList { + fn drop(&mut self) { + unsafe { BNFreeArchitectureList(self.0); } + } +} + +pub struct InstructionTextTokenList(*mut BNInstructionTextToken, usize); + +impl ops::Deref for InstructionTextTokenList { + type Target = [InstructionTextToken]; + + fn deref(&self) -> &Self::Target { + unsafe { slice::from_raw_parts(&*(self.0 as *const InstructionTextToken), self.1) } + } +} + +impl Drop for InstructionTextTokenList { + fn drop(&mut self) { + unsafe { BNFreeInstructionText(self.0, self.1) } + } +} + +impl Into<Vec<InstructionTextToken>> for InstructionTextTokenList { + fn into(self) -> Vec<InstructionTextToken> { + self.to_vec() + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] +pub struct CoreArchitecture(pub(crate) *mut BNArchitecture); + +unsafe impl Send for CoreArchitecture {} +unsafe impl Sync for CoreArchitecture {} + +impl CoreArchitecture { + pub(crate) unsafe fn from_raw(raw: *mut BNArchitecture) -> Self { + CoreArchitecture(raw) + } + + pub fn list_all() -> CoreArchitectureList { + let mut count: usize = 0; + let archs = unsafe { BNGetArchitectureList(&mut count as *mut _) }; + + CoreArchitectureList(archs, count) + } + + pub fn by_name<N: Into<Vec<u8>>>(name: N) -> Option<Self> { + let name = match CString::new(name) { + Ok(s) => s, + _ => return None, + }; + + let res = unsafe { BNGetArchitectureByName(name.as_ptr()) }; + + match res.is_null() { + false => Some(CoreArchitecture(res)), + true => None, + } + } + + pub fn name(&self) -> BnString { + unsafe { + BnString::from_raw(BNGetArchitectureName(self.0)) + } + } +} + +impl AsRef<CoreArchitecture> for CoreArchitecture { + fn as_ref(&self) -> &Self { + self + } +} + +impl Architecture for CoreArchitecture { + type Handle = Self; + + type RegisterInfo = CoreRegisterInfo; + type Register = CoreRegister; + type Flag = CoreFlag; + type FlagWrite = CoreFlagWrite; + type FlagClass = CoreFlagClass; + type FlagGroup = CoreFlagGroup; + + type InstructionTextContainer = InstructionTextTokenList; + + fn endianness(&self) -> Endianness { + unsafe { BNGetArchitectureEndianness(self.0) } + } + + fn address_size(&self) -> usize { + unsafe { BNGetArchitectureAddressSize(self.0) } + } + + fn default_integer_size(&self) -> usize { + unsafe { BNGetArchitectureDefaultIntegerSize(self.0) } + } + + fn instruction_alignment(&self) -> usize { + unsafe { BNGetArchitectureInstructionAlignment(self.0) } + } + + fn max_instr_len(&self) -> usize { + unsafe { BNGetArchitectureMaxInstructionLength(self.0) } + } + + fn opcode_display_len(&self) -> usize { + unsafe { BNGetArchitectureOpcodeDisplayLength(self.0) } + } + + fn associated_arch_by_addr(&self, addr: &mut u64) -> CoreArchitecture { + let arch = unsafe { BNGetAssociatedArchitectureByAddress(self.0, addr as *mut _) }; + + CoreArchitecture(arch) + } + + fn instruction_info(&self, data: &[u8], addr: u64) -> Option<InstructionInfo> { + let mut info = unsafe { zeroed::<InstructionInfo>() }; + let success = unsafe { BNGetInstructionInfo(self.0, data.as_ptr(), addr, data.len(), &mut (info.0) as *mut _) }; + + if success { + Some(info) + } else { + None + } + } + + fn instruction_text(&self, data: &[u8], addr: u64) -> Option<(usize, InstructionTextTokenList)> { + let mut consumed = data.len(); + let mut count: usize = 0; + let mut result: *mut BNInstructionTextToken = ptr::null_mut(); + + unsafe { + if BNGetInstructionText(self.0, data.as_ptr(), addr, &mut consumed as *mut _, &mut result as *mut _, + &mut count as *mut _) { + Some((consumed, InstructionTextTokenList(result, count))) + } else { + None + } + } + } + + fn instruction_llil(&self, _data: &[u8], _addr: u64, _il: &mut Lifter<Self>) -> Option<(usize, bool)> { + None + } + + fn flag_write_llil<'a>(&self, _flag: Self::Flag, _flag_write: Self::FlagWrite, _op: FlagWriteOp<Self::Register>, _il: &'a mut Lifter<Self>) + -> Option<LiftedExpr<'a, Self>> + { + None + } + + fn flag_cond_llil<'a>(&self, _cond: FlagCondition, _class: Option<Self::FlagClass>, _il: &'a mut Lifter<Self>) + -> Option<LiftedExpr<'a, Self>> + { + None + } + + fn flag_group_llil<'a>(&self, _group: Self::FlagGroup, _il: &'a mut Lifter<Self>) + -> Option<LiftedExpr<'a, Self>> + { + None + } + + + fn registers_all(&self) -> Vec<CoreRegister> { + unsafe { + let mut count: usize = 0; + let regs = BNGetAllArchitectureRegisters(self.0, &mut count as *mut _); + + let ret = slice::from_raw_parts_mut(regs, count) + .iter() + .map(|reg| CoreRegister(self.0, *reg)) + .collect(); + + BNFreeRegisterList(regs); + + ret + } + } + + fn registers_full_width(&self) -> Vec<CoreRegister> { + unsafe { + let mut count: usize = 0; + let regs = BNGetFullWidthArchitectureRegisters(self.0, &mut count as *mut _); + + let ret = slice::from_raw_parts_mut(regs, count) + .iter() + .map(|reg| CoreRegister(self.0, *reg)) + .collect(); + + BNFreeRegisterList(regs); + + ret + } + } + + fn registers_global(&self) -> Vec<CoreRegister> { + unsafe { + let mut count: usize = 0; + let regs = BNGetArchitectureGlobalRegisters(self.0, &mut count as *mut _); + + let ret = slice::from_raw_parts_mut(regs, count) + .iter() + .map(|reg| CoreRegister(self.0, *reg)) + .collect(); + + BNFreeRegisterList(regs); + + ret + } + } + + fn flags(&self) -> Vec<CoreFlag> { + unsafe { + let mut count: usize = 0; + let regs = BNGetAllArchitectureFlags(self.0, &mut count as *mut _); + + let ret = slice::from_raw_parts_mut(regs, count) + .iter() + .map(|reg| CoreFlag(self.0, *reg)) + .collect(); + + BNFreeRegisterList(regs); + + ret + } + } + + fn flag_write_types(&self) -> Vec<CoreFlagWrite> { + unsafe { + let mut count: usize = 0; + let regs = BNGetAllArchitectureFlagWriteTypes(self.0, &mut count as *mut _); + + let ret = slice::from_raw_parts_mut(regs, count) + .iter() + .map(|reg| CoreFlagWrite(self.0, *reg)) + .collect(); + + BNFreeRegisterList(regs); + + ret + } + } + + fn flag_classes(&self) -> Vec<CoreFlagClass> { + unsafe { + let mut count: usize = 0; + let regs = BNGetAllArchitectureSemanticFlagClasses(self.0, &mut count as *mut _); + + let ret = slice::from_raw_parts_mut(regs, count) + .iter() + .map(|reg| CoreFlagClass(self.0, *reg)) + .collect(); + + BNFreeRegisterList(regs); + + ret + } + } + + fn flag_groups(&self) -> Vec<CoreFlagGroup> { + unsafe { + let mut count: usize = 0; + let regs = BNGetAllArchitectureSemanticFlagGroups(self.0, &mut count as *mut _); + + let ret = slice::from_raw_parts_mut(regs, count) + .iter() + .map(|reg| CoreFlagGroup(self.0, *reg)) + .collect(); + + BNFreeRegisterList(regs); + + ret + } + } + + fn flags_required_for_flag_condition(&self, condition: FlagCondition, class: Option<Self::FlagClass>) -> Vec<Self::Flag> { + let class_id = class.map(|c| c.id()).unwrap_or(0); + + unsafe { + let mut count: usize = 0; + let flags = BNGetArchitectureFlagsRequiredForFlagCondition(self.0, condition, class_id, &mut count as *mut _); + + let ret = slice::from_raw_parts_mut(flags, count) + .iter() + .map(|flag| CoreFlag(self.0, *flag)) + .collect(); + + BNFreeRegisterList(flags); + + ret + } + } + + fn stack_pointer_reg(&self) -> Option<CoreRegister> { + match unsafe { BNGetArchitectureStackPointerRegister(self.0) } { + 0xffff_ffff => None, + reg => Some(CoreRegister(self.0, reg)) + } + } + + fn link_reg(&self) -> Option<CoreRegister> { + match unsafe { BNGetArchitectureLinkRegister(self.0) } { + 0xffff_ffff => None, + reg => Some(CoreRegister(self.0, reg)) + } + } + + fn register_from_id(&self, id: u32) -> Option<CoreRegister> { + // TODO validate in debug builds + Some(CoreRegister(self.0, id)) + } + + fn flag_from_id(&self, id: u32) -> Option<CoreFlag> { + // TODO validate in debug builds + Some(CoreFlag(self.0, id)) + } + + fn flag_write_from_id(&self, id: u32) -> Option<CoreFlagWrite> { + // TODO validate in debug builds + Some(CoreFlagWrite(self.0, id)) + } + + fn flag_class_from_id(&self, id: u32) -> Option<CoreFlagClass> { + // TODO validate in debug builds + Some(CoreFlagClass(self.0, id)) + } + + fn flag_group_from_id(&self, id: u32) -> Option<CoreFlagGroup> { + // TODO validate in debug builds + Some(CoreFlagGroup(self.0, id)) + } + + fn handle(&self) -> CoreArchitecture { + *self + } +} + +macro_rules! cc_func { + ($get_name:ident, $get_api:ident, $set_name:ident, $set_api:ident) => { + fn $get_name(&self) -> Option<Ref<CallingConvention<Self>>> { + let handle = self.as_ref(); + + unsafe { + let cc = $get_api(handle.0); + + if cc.is_null() { + None + } else { + Some(Ref::new(CallingConvention::from_raw(cc, self.handle()))) + } + } + } + + fn $set_name(&self, cc: &CallingConvention<Self>) { + let handle = self.as_ref(); + + assert!(cc.arch_handle.borrow().as_ref().0 == handle.0, "use of calling convention with non-matching architecture!"); + + unsafe { + $set_api(handle.0, cc.handle); + } + } + } +} + +/// Contains helper methods for all types implementing 'Architecture' +pub trait ArchitectureExt: Architecture { + fn register_by_name<S: BnStrCompatible>(&self, name: S) -> Option<Self::Register> { + let name = name.as_bytes_with_nul(); + + match unsafe { BNGetArchitectureRegisterByName(self.as_ref().0, name.as_ref().as_ptr() as *mut _) } { + 0xffff_ffff => None, + reg => self.register_from_id(reg) + } + } + + cc_func!(get_default_calling_convention, BNGetArchitectureDefaultCallingConvention, + set_default_calling_convention, BNSetArchitectureDefaultCallingConvention); + + cc_func!(get_cdecl_calling_convention, BNGetArchitectureCdeclCallingConvention, + set_cdecl_calling_convention, BNSetArchitectureCdeclCallingConvention); + + cc_func!(get_stdcall_calling_convention, BNGetArchitectureStdcallCallingConvention, + set_stdcall_calling_convention, BNSetArchitectureStdcallCallingConvention); + + cc_func!(get_fastcall_calling_convention, BNGetArchitectureFastcallCallingConvention, + set_fastcall_calling_convention, BNSetArchitectureFastcallCallingConvention); + + fn standalone_platform(&self) -> Option<Ref<Platform>> { + unsafe { + let handle = BNGetArchitectureStandalonePlatform(self.as_ref().0); + + if handle.is_null() { + return None; + } + + Some(Ref::new(Platform::from_raw(handle))) + } + } +} + +impl<T: Architecture> ArchitectureExt for T { } + +pub fn register_architecture<S, A, F>(name: S, func: F) -> &'static A +where + S: BnStrCompatible, + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync + Sized, + F: FnOnce(CustomArchitectureHandle<A>, CoreArchitecture) -> A, +{ + use std::os::raw::{c_void, c_char}; + use std::mem; + + #[repr(C)] + struct ArchitectureBuilder<A, F> + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + F: FnOnce(CustomArchitectureHandle<A>, CoreArchitecture) -> A, + { + arch: A, + func: F, + } + + extern "C" fn cb_init<A, F>(ctxt: *mut c_void, obj: *mut BNArchitecture) + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + F: FnOnce(CustomArchitectureHandle<A>, CoreArchitecture) -> A, + { + unsafe { + let custom_arch = &mut *(ctxt as *mut ArchitectureBuilder<A, F>); + let custom_arch_handle = CustomArchitectureHandle { + handle: ctxt as *mut A + }; + + let create = ptr::read(&custom_arch.func); + ptr::write(&mut custom_arch.arch, create(custom_arch_handle, CoreArchitecture(obj))); + } + } + + extern "C" fn cb_endianness<A>(ctxt: *mut c_void) -> BNEndianness + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + custom_arch.endianness() + } + + extern "C" fn cb_address_size<A>(ctxt: *mut c_void) -> usize + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + custom_arch.address_size() + } + + extern "C" fn cb_default_integer_size<A>(ctxt: *mut c_void) -> usize + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + custom_arch.default_integer_size() + } + + extern "C" fn cb_instruction_alignment<A>(ctxt: *mut c_void) -> usize + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + custom_arch.instruction_alignment() + } + + extern "C" fn cb_max_instr_len<A>(ctxt: *mut c_void) -> usize + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + custom_arch.max_instr_len() + } + + extern "C" fn cb_opcode_display_len<A>(ctxt: *mut c_void) -> usize + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + custom_arch.opcode_display_len() + } + + extern "C" fn cb_associated_arch_by_addr<A>(ctxt: *mut c_void, addr: *mut u64) -> *mut BNArchitecture + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let addr = unsafe { &mut *(addr) }; + + custom_arch.associated_arch_by_addr(addr).0 + } + + extern "C" fn cb_instruction_info<A>(ctxt: *mut c_void, data: *const u8, addr: u64, + len: usize, result: *mut BNInstructionInfo) -> bool + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let data = unsafe { slice::from_raw_parts(data, len) }; + let result = unsafe { &mut *(result as *mut InstructionInfo) }; + + match custom_arch.instruction_info(data, addr) { + Some(info) => { + result.0 = info.0; + true + } + None => false, + } + } + + extern "C" fn cb_get_instruction_text<A>(ctxt: *mut c_void, data: *const u8, addr: u64, len: *mut usize, + result: *mut *mut BNInstructionTextToken, count: *mut usize) -> bool + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let data = unsafe { slice::from_raw_parts(data, *len) }; + let result = unsafe { &mut *result }; + + match custom_arch.instruction_text(data, addr) { + Some((res_size, res_tokens)) => { + unsafe { + let mut res_tokens = res_tokens.into(); + res_tokens.shrink_to_fit(); + assert!(res_tokens.capacity() == res_tokens.len()); + + *len = res_size; + *count = res_tokens.len(); + + *result = res_tokens.as_mut_ptr() as *mut _; + mem::forget(res_tokens); + } + true + } + None => false, + } + } + + extern "C" fn cb_free_instruction_text(tokens: *mut BNInstructionTextToken, count: usize) { + let _tokens = unsafe { Vec::from_raw_parts(tokens as *mut InstructionTextToken, count, count) }; + } + + extern "C" fn cb_instruction_llil<A>(ctxt: *mut c_void, data: *const u8, addr: u64, len: *mut usize, + il: *mut BNLowLevelILFunction) -> bool + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let custom_arch_handle = CustomArchitectureHandle { + handle: ctxt as *mut A + }; + + let data = unsafe { slice::from_raw_parts(data, *len) }; + let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) }; + + match custom_arch.instruction_llil(data, addr, &mut lifter) { + Some((res_len, res_value)) => { + unsafe { *len = res_len }; + res_value + } + None => false, + } + } + + extern "C" fn cb_reg_name<A>(ctxt: *mut c_void, reg: u32) -> *mut c_char + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + + match custom_arch.register_from_id(reg) { + Some(reg) => BnString::new(reg.name().as_ref()).into_raw(), + None => BnString::new("invalid_reg").into_raw(), + } + } + + extern "C" fn cb_flag_name<A>(ctxt: *mut c_void, flag: u32) -> *mut c_char + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + + match custom_arch.flag_from_id(flag) { + Some(flag) => BnString::new(flag.name().as_ref()).into_raw(), + None => BnString::new("invalid_flag").into_raw(), + } + } + + extern "C" fn cb_flag_write_name<A>(ctxt: *mut c_void, flag_write: u32) -> *mut c_char + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + + match custom_arch.flag_write_from_id(flag_write) { + Some(flag_write) => BnString::new(flag_write.name().as_ref()).into_raw(), + None => BnString::new("invalid_flag_write").into_raw(), + } + } + + extern "C" fn cb_semantic_flag_class_name<A>(ctxt: *mut c_void, class: u32) -> *mut c_char + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + + match custom_arch.flag_class_from_id(class) { + Some(class) => BnString::new(class.name().as_ref()).into_raw(), + None => BnString::new("invalid_flag_class").into_raw(), + } + } + + extern "C" fn cb_semantic_flag_group_name<A>(ctxt: *mut c_void, group: u32) -> *mut c_char + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + + match custom_arch.flag_group_from_id(group) { + Some(group) => BnString::new(group.name().as_ref()).into_raw(), + None => BnString::new("invalid_flag_group").into_raw(), + } + } + + fn alloc_register_list<I: Iterator<Item=u32> + ExactSizeIterator>(items: I, count: &mut usize) -> *mut u32 { + let len = items.len(); + *count = len; + + if len == 0 { + ptr::null_mut() + } else { + let mut res = Vec::with_capacity(len + 1); + + res.push(len as u32); + + for i in items { + res.push(i.clone().into()); + } + + assert!(res.len() == len + 1); + + let raw = res.as_mut_ptr(); + mem::forget(res); + + unsafe { raw.offset(1) } + } + } + + extern "C" fn cb_registers_full_width<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let regs = custom_arch.registers_full_width(); + + alloc_register_list(regs.iter().map(|r| r.id()), unsafe { &mut *count }) + } + + extern "C" fn cb_registers_all<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let regs = custom_arch.registers_all(); + + alloc_register_list(regs.iter().map(|r| r.id()), unsafe { &mut *count }) + } + + extern "C" fn cb_registers_global<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let regs = custom_arch.registers_global(); + + alloc_register_list(regs.iter().map(|r| r.id()), unsafe { &mut *count }) + } + + extern "C" fn cb_flags<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let flags = custom_arch.flags(); + + alloc_register_list(flags.iter().map(|r| r.id()), unsafe { &mut *count }) + } + + extern "C" fn cb_flag_write_types<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let flag_writes = custom_arch.flag_write_types(); + + alloc_register_list(flag_writes.iter().map(|r| r.id()), unsafe { &mut *count }) + } + + extern "C" fn cb_semantic_flag_classes<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let flag_classes = custom_arch.flag_classes(); + + alloc_register_list(flag_classes.iter().map(|r| r.id()), unsafe { &mut *count }) + } + + extern "C" fn cb_semantic_flag_groups<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let flag_groups = custom_arch.flag_groups(); + + alloc_register_list(flag_groups.iter().map(|r| r.id()), unsafe { &mut *count }) + } + + extern "C" fn cb_flag_role<A>(ctxt: *mut c_void, flag: u32, class: u32) -> BNFlagRole + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + + if let (Some(flag), class) = (custom_arch.flag_from_id(flag), custom_arch.flag_class_from_id(class)) { + flag.role(class) + } else { + FlagRole::SpecialFlagRole + } + } + + extern "C" fn cb_flags_required_for_flag_cond<A>(ctxt: *mut c_void, cond: BNLowLevelILFlagCondition, class: u32, + count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let class = custom_arch.flag_class_from_id(class); + let flags = custom_arch.flags_required_for_flag_condition(cond, class); + + alloc_register_list(flags.iter().map(|r| r.id()), unsafe { &mut *count }) + } + + extern "C" fn cb_flags_required_for_semantic_flag_group<A>(ctxt: *mut c_void, group: u32, count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + + if let Some(group) = custom_arch.flag_group_from_id(group) { + let flags = group.flags_required(); + alloc_register_list(flags.iter().map(|r| r.id()), unsafe { &mut *count }) + } else { + unsafe { *count = 0; } + ptr::null_mut() + } + } + + extern "C" fn cb_flag_conditions_for_semantic_flag_group<A>(ctxt: *mut c_void, group: u32, count: *mut usize) + -> *mut BNFlagConditionForSemanticClass + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + + if let Some(group) = custom_arch.flag_group_from_id(group) { + let flag_conditions = group.flag_conditions(); + + unsafe { + let allocation_size = mem::size_of::<BNFlagConditionForSemanticClass>() * flag_conditions.len(); + let result = libc::malloc(allocation_size) as *mut BNFlagConditionForSemanticClass; + let out_slice = slice::from_raw_parts_mut(result, flag_conditions.len()); + + for (i, (class, cond)) in flag_conditions.iter().enumerate() { + let out = out_slice.get_unchecked_mut(i); + + out.semanticClass = class.id(); + out.condition = *cond; + } + + *count = flag_conditions.len(); + result + } + } else { + unsafe { *count = 0; } + ptr::null_mut() + } + } + + extern "C" fn cb_free_flag_conditions_for_semantic_flag_group<A>(_ctxt: *mut c_void, conds: *mut BNFlagConditionForSemanticClass) + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + unsafe { libc::free(conds as *mut _); } + } + + extern "C" fn cb_flags_written_by_write_type<A>(ctxt: *mut c_void, write_type: u32, count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + + if let Some(write_type) = custom_arch.flag_write_from_id(write_type) { + let written = write_type.flags_written(); + alloc_register_list(written.iter().map(|f| f.id()), unsafe { &mut *count }) + } else { + unsafe { *count = 0; } + ptr::null_mut() + } + } + + extern "C" fn cb_semantic_class_for_flag_write_type<A>(ctxt: *mut c_void, write_type: u32) -> u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + custom_arch.flag_write_from_id(write_type).map(|w| w.id()).unwrap_or(0) + } + + extern "C" fn cb_flag_write_llil<A>(ctxt: *mut c_void, op: BNLowLevelILOperation, size: usize, flag_write: u32, + flag: u32, operands_raw: *mut BNRegisterOrConstant, operand_count: usize, + il: *mut BNLowLevelILFunction) -> usize + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let custom_arch_handle = CustomArchitectureHandle { + handle: ctxt as *mut A + }; + + let flag_write = custom_arch.flag_write_from_id(flag_write); + let flag = custom_arch.flag_from_id(flag); + let operands = unsafe { slice::from_raw_parts(operands_raw, operand_count) }; + let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) }; + + if let (Some(flag_write), Some(flag)) = (flag_write, flag) { + if let Some(op) = FlagWriteOp::from_op(custom_arch, size, op, operands) { + if let Some(expr) = custom_arch.flag_write_llil(flag, flag_write, op, &mut lifter) { + // TODO verify that returned expr is a bool value + return expr.expr_idx; + } + } else { + warn!("unable to unpack flag write op: {:?} with {} operands", op, operands.len()); + } + + let role = flag.role(flag_write.class()); + + unsafe { + BNGetDefaultArchitectureFlagWriteLowLevelIL(custom_arch.as_ref().0, op, size, + role, operands_raw, operand_count, il) + } + } else { + // TODO this should be impossible; requires bad flag/flag_write ids passed in; + // explode more violently + lifter.unimplemented().expr_idx + } + } + + extern "C" fn cb_flag_cond_llil<A>(ctxt: *mut c_void, cond: FlagCondition, class: u32, + il: *mut BNLowLevelILFunction) -> usize + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let custom_arch_handle = CustomArchitectureHandle { + handle: ctxt as *mut A + }; + + let class = custom_arch.flag_class_from_id(class); + + let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) }; + if let Some(expr) = custom_arch.flag_cond_llil(cond, class, &mut lifter) { + // TODO verify that returned expr is a bool value + return expr.expr_idx; + } + + lifter.unimplemented().expr_idx + } + + extern "C" fn cb_flag_group_llil<A>(ctxt: *mut c_void, group: u32, + il: *mut BNLowLevelILFunction) -> usize + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let custom_arch_handle = CustomArchitectureHandle { + handle: ctxt as *mut A + }; + + let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) }; + + if let Some(group) = custom_arch.flag_group_from_id(group) { + if let Some(expr) = custom_arch.flag_group_llil(group, &mut lifter) { + // TODO verify that returned expr is a bool value + return expr.expr_idx; + } + } + + lifter.unimplemented().expr_idx + } + + extern "C" fn cb_free_register_list(_ctxt: *mut c_void, regs: *mut u32) { + if regs.is_null() { + return; + } + + unsafe { + let actual_start = regs.offset(-1); + let len = *actual_start + 1; + let _regs = Vec::from_raw_parts(actual_start, len as usize, len as usize); + } + } + + extern "C" fn cb_register_info<A>(ctxt: *mut c_void, reg: u32, result: *mut BNRegisterInfo) + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + let result = unsafe { &mut *result }; + + if let Some(reg) = custom_arch.register_from_id(reg) { + let info = reg.info(); + + result.fullWidthRegister = match info.parent() { + Some(p) => p.id(), + None => reg.id(), + }; + + result.offset = info.offset(); + result.size = info.size(); + result.extend = info.implicit_extend(); + } + } + + extern "C" fn cb_stack_pointer<A>(ctxt: *mut c_void) -> u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + + if let Some(reg) = custom_arch.stack_pointer_reg() { + reg.id() + } else { + 0xffff_ffff + } + } + + extern "C" fn cb_link_reg<A>(ctxt: *mut c_void) -> u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let custom_arch = unsafe { &*(ctxt as *mut A) }; + + if let Some(reg) = custom_arch.link_reg() { + reg.id() + } else { + 0xffff_ffff + } + } + + extern "C" fn cb_reg_stack_name<A>(ctxt: *mut c_void, _stack: u32) -> *mut c_char + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let _custom_arch = unsafe { &*(ctxt as *mut A) }; + BnString::new("reg_stack").into_raw() + } + + extern "C" fn cb_reg_stacks<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let _custom_arch = unsafe { &*(ctxt as *mut A) }; + + unsafe { *count = 0; } + ptr::null_mut() + } + + extern "C" fn cb_reg_stack_info<A>(ctxt: *mut c_void, _stack: u32, _info: *mut BNRegisterStackInfo) + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let _custom_arch = unsafe { &*(ctxt as *mut A) }; + } + + extern "C" fn cb_intrinsic_name<A>(ctxt: *mut c_void, _intrinsic: u32) -> *mut c_char + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let _custom_arch = unsafe { &*(ctxt as *mut A) }; + BnString::new("intrinsic").into_raw() + } + + extern "C" fn cb_intrinsics<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let _custom_arch = unsafe { &*(ctxt as *mut A) }; + + unsafe { *count = 0; } + ptr::null_mut() + } + + extern "C" fn cb_intrinsic_inputs<A>(ctxt: *mut c_void, _intrinsic: u32, count: *mut usize) -> *mut BNNameAndType + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let _custom_arch = unsafe { &*(ctxt as *mut A) }; + + unsafe { *count = 0; } + ptr::null_mut() + } + + extern "C" fn cb_free_name_and_types<A>(ctxt: *mut c_void, _nt: *mut BNNameAndType, _count: usize) + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let _custom_arch = unsafe { &*(ctxt as *mut A) }; + } + + extern "C" fn cb_intrinsic_outputs<A>(ctxt: *mut c_void, _intrinsic: u32, count: *mut usize) -> *mut BNTypeWithConfidence + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let _custom_arch = unsafe { &*(ctxt as *mut A) }; + + unsafe { *count = 0; } + ptr::null_mut() + } + + extern "C" fn cb_free_type_list<A>(ctxt: *mut c_void, _tl: *mut BNTypeWithConfidence, _count: usize) + where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync, + { + let _custom_arch = unsafe { &*(ctxt as *mut A) }; + } + + extern "C" fn cb_assemble(_ctxt: *mut c_void, _code: *const c_char, _addr: u64, + _result: *mut BNDataBuffer, errors: *mut *mut c_char) -> bool + { + unsafe { *errors = ptr::null_mut(); } + false + } + + extern "C" fn cb_patch_unavailable(_ctxt: *mut c_void, _data: *const u8, _addr: u64, _len: usize) -> bool { + false + } + + extern "C" fn cb_do_patch_unavailable(_ctxt: *mut c_void, _data: *mut u8, _addr: u64, _len: usize) -> bool { + false + } + + extern "C" fn cb_skip_patch_unavailable(_ctxt: *mut c_void, _data: *mut u8, _addr: u64, _len: usize, _val: u64) -> bool { + false + } + + let name = name.as_bytes_with_nul(); + + let uninit_arch = ArchitectureBuilder { + arch: unsafe { mem::uninitialized() }, + func: func, + }; + + let raw = Box::into_raw(Box::new(uninit_arch)); + let mut custom_arch = BNCustomArchitecture { + context: raw as *mut _, + init: Some(cb_init::<A, F>), + getEndianness: Some(cb_endianness::<A>), + getAddressSize: Some(cb_address_size::<A>), + getDefaultIntegerSize: Some(cb_default_integer_size::<A>), + getInstructionAlignment: Some(cb_instruction_alignment::<A>), + getMaxInstructionLength: Some(cb_max_instr_len::<A>), + getOpcodeDisplayLength: Some(cb_opcode_display_len::<A>), + getAssociatedArchitectureByAddress: Some(cb_associated_arch_by_addr::<A>), + getInstructionInfo: Some(cb_instruction_info::<A>), + getInstructionText: Some(cb_get_instruction_text::<A>), + freeInstructionText: Some(cb_free_instruction_text), + getInstructionLowLevelIL: Some(cb_instruction_llil::<A>), + + getRegisterName: Some(cb_reg_name::<A>), + getFlagName: Some(cb_flag_name::<A>), + getFlagWriteTypeName: Some(cb_flag_write_name::<A>), + getSemanticFlagClassName: Some(cb_semantic_flag_class_name::<A>), + getSemanticFlagGroupName: Some(cb_semantic_flag_group_name::<A>), + + getFullWidthRegisters: Some(cb_registers_full_width::<A>), + getAllRegisters: Some(cb_registers_all::<A>), + getAllFlags: Some(cb_flags::<A>), + getAllFlagWriteTypes: Some(cb_flag_write_types::<A>), + getAllSemanticFlagClasses: Some(cb_semantic_flag_classes::<A>), + getAllSemanticFlagGroups: Some(cb_semantic_flag_groups::<A>), + + getFlagRole: Some(cb_flag_role::<A>), + getFlagsRequiredForFlagCondition: Some(cb_flags_required_for_flag_cond::<A>), + + getFlagsRequiredForSemanticFlagGroup: Some(cb_flags_required_for_semantic_flag_group::<A>), + getFlagConditionsForSemanticFlagGroup: Some(cb_flag_conditions_for_semantic_flag_group::<A>), + freeFlagConditionsForSemanticFlagGroup: Some(cb_free_flag_conditions_for_semantic_flag_group::<A>), + + getFlagsWrittenByFlagWriteType: Some(cb_flags_written_by_write_type::<A>), + getSemanticClassForFlagWriteType: Some(cb_semantic_class_for_flag_write_type::<A>), + + getFlagWriteLowLevelIL: Some(cb_flag_write_llil::<A>), + getFlagConditionLowLevelIL: Some(cb_flag_cond_llil::<A>), + getSemanticFlagGroupLowLevelIL: Some(cb_flag_group_llil::<A>), + + freeRegisterList: Some(cb_free_register_list), + getRegisterInfo: Some(cb_register_info::<A>), + getStackPointerRegister: Some(cb_stack_pointer::<A>), + getLinkRegister: Some(cb_link_reg::<A>), + getGlobalRegisters: Some(cb_registers_global::<A>), + + getRegisterStackName: Some(cb_reg_stack_name::<A>), + getAllRegisterStacks: Some(cb_reg_stacks::<A>), + getRegisterStackInfo: Some(cb_reg_stack_info::<A>), + + getIntrinsicName: Some(cb_intrinsic_name::<A>), + getAllIntrinsics: Some(cb_intrinsics::<A>), + getIntrinsicInputs: Some(cb_intrinsic_inputs::<A>), + freeNameAndTypeList: Some(cb_free_name_and_types::<A>), + getIntrinsicOutputs: Some(cb_intrinsic_outputs::<A>), + freeTypeList: Some(cb_free_type_list::<A>), + + assemble: Some(cb_assemble), + + isNeverBranchPatchAvailable: Some(cb_patch_unavailable), + isAlwaysBranchPatchAvailable: Some(cb_patch_unavailable), + isInvertBranchPatchAvailable: Some(cb_patch_unavailable), + isSkipAndReturnZeroPatchAvailable: Some(cb_patch_unavailable), + isSkipAndReturnValuePatchAvailable: Some(cb_patch_unavailable), + + convertToNop: Some(cb_do_patch_unavailable), + alwaysBranch: Some(cb_do_patch_unavailable), + invertBranch: Some(cb_do_patch_unavailable), + skipAndReturnValue: Some(cb_skip_patch_unavailable), + }; + + unsafe { + let res = BNRegisterArchitecture(name.as_ref().as_ptr() as *mut _, &mut custom_arch as *mut _); + + assert!(!res.is_null()); + + &(*raw).arch + } +} + +pub struct CustomArchitectureHandle<A> +where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync +{ + handle: *mut A +} + +unsafe impl<A> Send for CustomArchitectureHandle<A> +where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync +{} + +unsafe impl<A> Sync for CustomArchitectureHandle<A> +where + A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync +{} + +impl<A> Clone for CustomArchitectureHandle<A> +where + A: 'static + Architecture<Handle=Self> + Send + Sync +{ + fn clone(&self) -> Self { + Self { handle: self.handle } + } +} + +impl<A> Copy for CustomArchitectureHandle<A> +where + A: 'static + Architecture<Handle=Self> + Send + Sync +{ +} + +impl<A> Borrow<A> for CustomArchitectureHandle<A> +where + A: 'static + Architecture<Handle=Self> + Send + Sync +{ + fn borrow(&self) -> &A { + unsafe { &*self.handle } + } +} diff --git a/rust/src/basicblock.rs b/rust/src/basicblock.rs new file mode 100644 index 00000000..db7989a0 --- /dev/null +++ b/rust/src/basicblock.rs @@ -0,0 +1,274 @@ +use std::fmt; + +use binaryninjacore_sys::*; +use crate::architecture::CoreArchitecture; +use crate::function::Function; + +use crate::rc::*; + +enum EdgeDirection { + Incoming, + Outgoing, +} + +pub struct Edge<'a, C: 'a + BlockContext> { + branch: super::BranchType, + back_edge: bool, + source: Guard<'a, BasicBlock<C>>, + target: Guard<'a, BasicBlock<C>>, +} + +impl<'a, C: 'a + BlockContext> Edge<'a, C> { + pub fn branch_type(&self) -> super::BranchType { + self.branch + } + + pub fn back_edge(&self) -> bool { + self.back_edge + } + + pub fn source(&self) -> &BasicBlock<C> { + &*self.source + } + + pub fn target(&self) -> &BasicBlock<C> { + &*self.target + } +} + +impl<'a, C: 'a + fmt::Debug + BlockContext> fmt::Debug for Edge<'a, C> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:?} ({}) {:?} -> {:?}", self.branch, self.back_edge, &*self.source, &*self.target) + } +} + +pub struct EdgeContext<'a, C: 'a + BlockContext> { + dir: EdgeDirection, + orig_block: &'a BasicBlock<C>, +} + +unsafe impl<'a, C: 'a + BlockContext> CoreOwnedArrayProvider for Edge<'a, C> { + type Raw = BNBasicBlockEdge; + type Context = EdgeContext<'a, C>; + + unsafe fn free(raw: *mut BNBasicBlockEdge, count: usize, _context: &Self::Context) { + BNFreeBasicBlockEdgeList(raw, count); + } +} + +unsafe impl<'a, C: 'a + BlockContext> CoreOwnedArrayWrapper<'a> for Edge<'a, C> { + type Wrapped = Edge<'a, C>; + + unsafe fn wrap_raw(raw: &'a BNBasicBlockEdge, context: &'a Self::Context) -> Edge<'a, C> { + let edge_target = Guard::new(BasicBlock::from_raw(raw.target, context.orig_block.context.clone()), raw); + let orig_block = Guard::new(BasicBlock::from_raw(context.orig_block.handle, context.orig_block.context.clone()), raw); + + let (source, target) = match context.dir { + EdgeDirection::Incoming => (edge_target, orig_block), + EdgeDirection::Outgoing => (orig_block, edge_target), + }; + + Edge { + branch: raw.type_, + back_edge: raw.backEdge, + source: source, + target: target, + } + } +} + +pub trait BlockContext: Clone + Sync + Send + Sized { + type Instruction; + type Iter: Iterator<Item=Self::Instruction>; + + fn start(&self, block: &BasicBlock<Self>) -> Self::Instruction; + fn iter(&self, block: &BasicBlock<Self>) -> Self::Iter; +} + +#[derive(PartialEq, Eq, Hash)] +pub struct BasicBlock<C: BlockContext> { + pub(crate) handle: *mut BNBasicBlock, + context: C, +} + +unsafe impl<C: BlockContext> Send for BasicBlock<C> {} +unsafe impl<C: BlockContext> Sync for BasicBlock<C> {} + +impl<C: BlockContext> BasicBlock<C> { + pub(crate) unsafe fn from_raw(handle: *mut BNBasicBlock, context: C) -> Self { + Self { handle, context } + } + + // TODO native bb vs il bbs + pub fn function(&self) -> Ref<Function> { + unsafe { + let func = BNGetBasicBlockFunction(self.handle); + Ref::new(Function::from_raw(func)) + } + } + + pub fn arch(&self) -> CoreArchitecture { + unsafe { + let arch = BNGetBasicBlockArchitecture(self.handle); + CoreArchitecture::from_raw(arch) + } + } + + pub fn iter(&self) -> C::Iter { + self.context.iter(self) + } + + pub fn raw_start(&self) -> u64 { + unsafe { BNGetBasicBlockStart(self.handle) } + } + + pub fn raw_end(&self) -> u64 { + unsafe { BNGetBasicBlockEnd(self.handle) } + } + + pub fn raw_length(&self) -> u64 { + unsafe { BNGetBasicBlockLength(self.handle) } + } + + pub fn incoming_edges(&self) -> Array<Edge<C>> { + unsafe { + let mut count = 0; + let edges = BNGetBasicBlockIncomingEdges(self.handle, &mut count); + + Array::new(edges, count, EdgeContext { + dir: EdgeDirection::Incoming, + orig_block: self, + }) + } + } + + pub fn outgoing_edges(&self) -> Array<Edge<C>> { + unsafe { + let mut count = 0; + let edges = BNGetBasicBlockOutgoingEdges(self.handle, &mut count); + + Array::new(edges, count, EdgeContext { + dir: EdgeDirection::Outgoing, + orig_block: self, + }) + } + } + + // is this valid for il blocks? + pub fn has_undetermined_outgoing_edges(&self) -> bool { + unsafe { BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) } + } + + pub fn can_exit(&self) -> bool { + unsafe { BNBasicBlockCanExit(self.handle) } + } + + pub fn index(&self) -> usize { + unsafe { BNGetBasicBlockIndex(self.handle) } + } + + pub fn immediate_dominator(&self) -> Option<Ref<Self>> { + unsafe { + let block = BNGetBasicBlockImmediateDominator(self.handle, false); + + if block.is_null() { + return None; + } + + Some(Ref::new(BasicBlock::from_raw(block, self.context.clone()))) + } + } + + pub fn dominators(&self) -> Array<BasicBlock<C>> { + unsafe { + let mut count = 0; + let blocks = BNGetBasicBlockDominators(self.handle, &mut count, false); + + Array::new(blocks, count, self.context.clone()) + } + } + + pub fn strict_dominators(&self) -> Array<BasicBlock<C>> { + unsafe { + let mut count = 0; + let blocks = BNGetBasicBlockStrictDominators(self.handle, &mut count, false); + + Array::new(blocks, count, self.context.clone()) + } + } + + pub fn dominator_tree_children(&self) -> Array<BasicBlock<C>> { + unsafe { + let mut count = 0; + let blocks = BNGetBasicBlockDominatorTreeChildren(self.handle, &mut count, false); + + Array::new(blocks, count, self.context.clone()) + } + } + + pub fn dominance_frontier(&self) -> Array<BasicBlock<C>> { + unsafe { + let mut count = 0; + let blocks = BNGetBasicBlockDominanceFrontier(self.handle, &mut count, false); + + Array::new(blocks, count, self.context.clone()) + } + } + + // TODO iterated dominance frontier + +} + +impl<'a, C: BlockContext> IntoIterator for &'a BasicBlock<C> { + type Item = C::Instruction; + type IntoIter = C::Iter; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<C: fmt::Debug + BlockContext> fmt::Debug for BasicBlock<C> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "<bb handle {:p} context {:?} contents: {} -> {}>", self.handle, &self.context, self.raw_start(), self.raw_end()) + } +} + +impl<C: BlockContext> ToOwned for BasicBlock<C> { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl<C: BlockContext> RefCountable for BasicBlock<C> { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewBasicBlockReference(handle.handle), + context: handle.context.clone(), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeBasicBlock(handle.handle); + } +} + +unsafe impl<C: BlockContext> CoreOwnedArrayProvider for BasicBlock<C> { + type Raw = *mut BNBasicBlock; + type Context = C; + + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeBasicBlockList(raw, count); + } +} + +unsafe impl<'a, C: 'a + BlockContext> CoreOwnedArrayWrapper<'a> for BasicBlock<C> { + type Wrapped = Guard<'a, BasicBlock<C>>; + + unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped { + Guard::new(BasicBlock::from_raw(*raw, context.clone()), context) + } +} + diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs new file mode 100644 index 00000000..b6d1d1a7 --- /dev/null +++ b/rust/src/binaryview.rs @@ -0,0 +1,1340 @@ +use binaryninjacore_sys::*; + +pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus; + +use std::result; +use std::slice; +use std::ops; +use std::marker::PhantomData; + +use std::os::raw::{c_void, c_char}; +use std::mem; +use std::ptr; + +use crate::Endianness; + +use crate::architecture::Architecture; +use crate::architecture::CoreArchitecture; +use crate::platform::Platform; +use crate::filemetadata::FileMetadata; +use crate::fileaccessor::FileAccessor; +use crate::symbol::{SymType, Symbol}; +use crate::segment::{Segment, SegmentBuilder}; +use crate::section::{Section, SectionBuilder}; +use crate::function::{Function, NativeBlock}; +use crate::basicblock::BasicBlock; +use crate::types::Type; +use crate::settings::Settings; + +use crate::string::*; +use crate::rc::*; + +// TODO +// merge filemetadata/fileaccessor under here? +// general reorg of modules related to bv + +pub type Result<R> = result::Result<R, ()>; + +pub trait BinaryViewBase: AsRef<BinaryView> { + fn read(&self, _buf: &mut [u8], _offset: u64) -> usize { 0 } + fn write(&self, _offset: u64, _data: &[u8]) -> usize { 0 } + fn insert(&self, _offset: u64, _data: &[u8]) -> usize { 0 } + fn remove(&self, _offset: u64, _len: usize) -> usize { 0 } + + fn offset_valid(&self, offset: u64) -> bool { + let mut buf = [0u8; 1]; + + // don't use self.read so that if segments were used we + // check against those as well + self.as_ref().read(&mut buf[..], offset) == buf.len() + } + + fn offset_readable(&self, offset: u64) -> bool { + self.offset_valid(offset) + } + + fn offset_writable(&self, offset: u64) -> bool { + self.offset_valid(offset) + } + + fn offset_executable(&self, offset: u64) -> bool { + self.offset_valid(offset) + } + + fn offset_backed_by_file(&self, offset: u64) -> bool { + self.offset_valid(offset) + } + + fn next_valid_offset_after(&self, offset: u64) -> u64 { + let start = self.as_ref().start(); + + if offset < start { + start + } else { + offset + } + } + + #[allow(unused)] + fn modification_status(&self, offset: u64) -> ModificationStatus { + ModificationStatus::Original + } + + fn start(&self) -> u64 { 0 } + fn len(&self) -> usize { 0 } + + fn executable(&self) -> bool { true } + fn relocatable(&self) -> bool { true } + + fn entry_point(&self) -> u64; + fn default_endianness(&self) -> Endianness; + fn address_size(&self) -> usize; + + // TODO saving fileaccessor + fn save(&self) -> bool { + self.as_ref() + .parent_view() + .map(|bv| bv.save()) + .unwrap_or(false) + } +} + +pub trait BinaryViewExt: BinaryViewBase { + fn metadata(&self) -> Ref<FileMetadata> { + unsafe { + let raw = BNGetFileForView(self.as_ref().handle); + + Ref::new(FileMetadata::from_raw(raw)) + } + } + + fn parent_view(&self) -> Result<Ref<BinaryView>> { + let handle = unsafe { BNGetParentView(self.as_ref().handle) }; + + if handle.is_null() { + return Err(()); + } + + unsafe { Ok(Ref::new(BinaryView { handle })) } + } + + /// Reads up to `len` bytes from address `offset` + fn read_vec(&self, offset: u64, len: usize) -> Vec<u8> { + let mut ret = Vec::with_capacity(len); + + unsafe { + let res; + + { + let dest_slice = ret.get_unchecked_mut(0 .. len); + res = self.read(dest_slice, offset); + } + + ret.set_len(res); + } + + ret + } + + /// Appends up to `len` bytes from address `offset` into `dest` + fn read_into_vec(&self, dest: &mut Vec<u8>, offset: u64, len: usize) -> usize { + let starting_len = dest.len(); + let space = dest.capacity() - starting_len; + + if space < len { + dest.reserve(len - space); + } + + unsafe { + let res; + + { + let dest_slice = dest.get_unchecked_mut(starting_len .. starting_len + len); + res = self.read(dest_slice, offset); + } + + if res > 0 { + dest.set_len(starting_len + res); + } + + res + } + } + + fn notify_data_written(&self, offset: u64, len: usize) { + unsafe { BNNotifyDataWritten(self.as_ref().handle, offset, len); } + } + + fn notify_data_inserted(&self, offset: u64, len: usize) { + unsafe { BNNotifyDataInserted(self.as_ref().handle, offset, len); } + } + + fn notify_data_removed(&self, offset: u64, len: usize) { + unsafe { BNNotifyDataRemoved(self.as_ref().handle, offset, len as u64); } + } + + fn offset_has_code_semantics(&self, offset: u64) -> bool { + unsafe { BNIsOffsetCodeSemantics(self.as_ref().handle, offset) } + } + + fn offset_has_writable_semantics(&self, offset: u64) -> bool { + unsafe { BNIsOffsetWritableSemantics(self.as_ref().handle, offset) } + } + + fn end(&self) -> u64 { + unsafe { BNGetEndOffset(self.as_ref().handle) } + } + + fn update_analysis_and_wait(&self) { + unsafe { BNUpdateAnalysisAndWait(self.as_ref().handle); } + } + + fn default_arch(&self) -> Option<CoreArchitecture> { + unsafe { + let raw = BNGetDefaultArchitecture(self.as_ref().handle); + + if raw.is_null() { + return None; + } + + Some(CoreArchitecture::from_raw(raw)) + } + } + + fn set_default_arch<A: Architecture>(&self, arch: &A) { + unsafe { + BNSetDefaultArchitecture(self.as_ref().handle, arch.as_ref().0); + } + } + + fn default_platform(&self) -> Option<Ref<Platform>> { + unsafe { + let raw = BNGetDefaultPlatform(self.as_ref().handle); + + if raw.is_null() { + return None; + } + + Some(Ref::new(Platform::from_raw(raw))) + } + } + + fn set_default_platform(&self, plat: &Platform) { + unsafe { + BNSetDefaultPlatform(self.as_ref().handle, plat.handle); + } + } + + fn get_instruction_len<A: Architecture>(&self, arch: &A, addr: u64) -> Option<usize> { + unsafe { + let size = BNGetInstructionLength(self.as_ref().handle, arch.as_ref().0, addr); + + if size > 0 { + Some(size) + } else { + None + } + } + } + + fn symbol_by_address(&self, addr: u64) -> Result<Ref<Symbol>> { + unsafe { + let raw_sym = BNGetSymbolByAddress(self.as_ref().handle, addr, ptr::null_mut()); + + if raw_sym.is_null() { + return Err(()); + } + + Ok(Ref::new(Symbol::from_raw(raw_sym))) + } + } + + fn symbol_by_raw_name<S: BnStrCompatible>(&self, raw_name: S) -> Result<Ref<Symbol>> { + let raw_name = raw_name.as_bytes_with_nul(); + + unsafe { + let raw_sym = BNGetSymbolByRawName(self.as_ref().handle, raw_name.as_ref().as_ptr() as *mut _, ptr::null_mut()); + + if raw_sym.is_null() { + return Err(()); + } + + Ok(Ref::new(Symbol::from_raw(raw_sym))) + } + } + + fn symbols(&self) -> Array<Symbol> { + unsafe { + let mut count = 0; + let handles = BNGetSymbols(self.as_ref().handle, &mut count, ptr::null_mut()); + + Array::new(handles, count, ()) + } + } + + fn symbols_by_name<S: BnStrCompatible>(&self, name: S) -> Array<Symbol> { + let raw_name = name.as_bytes_with_nul(); + + unsafe { + let mut count = 0; + let handles = BNGetSymbolsByName(self.as_ref().handle, raw_name.as_ref().as_ptr() as *mut _, &mut count, ptr::null_mut()); + + Array::new(handles, count, ()) + } + } + + fn symbols_in_range(&self, range: ops::Range<u64>) -> Array<Symbol> { + unsafe { + let mut count = 0; + let len = range.end.wrapping_sub(range.start); + let handles = BNGetSymbolsInRange(self.as_ref().handle, range.start, len, &mut count, ptr::null_mut()); + + Array::new(handles, count, ()) + } + } + + fn symbols_of_type(&self, ty: SymType) -> Array<Symbol> { + unsafe { + let mut count = 0; + let handles = BNGetSymbolsOfType(self.as_ref().handle, ty.into(), &mut count, ptr::null_mut()); + + Array::new(handles, count, ()) + } + } + + fn symbols_of_type_in_range(&self, ty: SymType, range: ops::Range<u64>) -> Array<Symbol> { + unsafe { + let mut count = 0; + let len = range.end.wrapping_sub(range.start); + let handles = BNGetSymbolsOfTypeInRange(self.as_ref().handle, ty.into(), range.start, len, &mut count, ptr::null_mut()); + + Array::new(handles, count, ()) + } + } + + fn define_auto_symbol(&self, sym: &Symbol) { + unsafe { BNDefineAutoSymbol(self.as_ref().handle, sym.handle); } + } + + fn define_auto_symbol_with_type<'a, T: Into<Option<&'a Type>>>(&self, sym: &Symbol, plat: &Platform, ty: T) { + let raw_type = if let Some(t) = ty.into() { + t.handle + } else { + ptr::null_mut() + }; + + unsafe { BNDefineAutoSymbolAndVariableOrFunction(self.as_ref().handle, plat.handle, sym.handle, raw_type); } + } + + fn undefine_auto_symbol(&self, sym: &Symbol) { + unsafe { BNUndefineAutoSymbol(self.as_ref().handle, sym.handle); } + } + + fn define_user_symbol(&self, sym: &Symbol) { + unsafe { BNDefineUserSymbol(self.as_ref().handle, sym.handle); } + } + + fn undefine_user_symbol(&self, sym: &Symbol) { + unsafe { BNUndefineUserSymbol(self.as_ref().handle, sym.handle); } + } + + fn segments(&self) -> Array<Segment> { + unsafe { + let mut count = 0; + let segs = BNGetSegments(self.as_ref().handle, &mut count); + + Array::new(segs, count, ()) + } + } + + fn segment_at(&self, addr: u64) -> Option<Segment> { + unsafe { + let raw_seg = BNGetSegmentAt(self.as_ref().handle, addr); + if !raw_seg.is_null() { + Some(Segment::from_raw(raw_seg)) + } else { + None + } + } + } + + fn add_segment(&self, segment: SegmentBuilder) { + segment.create(self.as_ref()); + } + + fn add_section<S: BnStrCompatible>(&self, section: SectionBuilder<S>) { + section.create(self.as_ref()); + } + + fn remove_auto_section<S: BnStrCompatible>(&self, name: S) { + let name = name.as_bytes_with_nul(); + let name_ptr = name.as_ref().as_ptr() as *mut _; + + unsafe { BNRemoveAutoSection(self.as_ref().handle, name_ptr); } + } + + fn remove_user_section<S: BnStrCompatible>(&self, name: S) { + let name = name.as_bytes_with_nul(); + let name_ptr = name.as_ref().as_ptr() as *mut _; + + unsafe { BNRemoveUserSection(self.as_ref().handle, name_ptr); } + } + + fn section_by_name<S: BnStrCompatible>(&self, name: S) -> Result<Section> { + unsafe { + let raw_name = name.as_bytes_with_nul(); + let name_ptr = raw_name.as_ref().as_ptr() as * mut _; + let raw_section = BNGetSectionByName(self.as_ref().handle, name_ptr); + + if raw_section.is_null() { + return Err(()); + } + + Ok(Section::from_raw(raw_section)) + } + } + + fn sections(&self) -> Array<Section> { + unsafe { + let mut count = 0; + let sections = BNGetSections(self.as_ref().handle, &mut count); + + Array::new(sections, count, ()) + } + } + + fn sections_at(&self, addr: u64) -> Array<Section> { + unsafe { + let mut count = 0; + let sections = BNGetSectionsAt(self.as_ref().handle, addr, &mut count); + + Array::new(sections, count, ()) + } + } + + fn add_auto_function(&self, plat: &Platform, addr: u64) { + unsafe { BNAddFunctionForAnalysis(self.as_ref().handle, plat.handle, addr); } + } + + fn add_entry_point(&self, plat: &Platform, addr: u64) { + unsafe { BNAddEntryPointForAnalysis(self.as_ref().handle, plat.handle, addr); } + } + + fn create_user_function(&self, plat: &Platform, addr: u64) { + unsafe { BNCreateUserFunction(self.as_ref().handle, plat.handle, addr); } + } + + fn has_functions(&self) -> bool { + unsafe { BNHasFunctions(self.as_ref().handle) } + } + + fn entry_point_function(&self) -> Result<Ref<Function>> { + unsafe { + let func = BNGetAnalysisEntryPoint(self.as_ref().handle); + + if func.is_null() { + return Err(()); + } + + Ok(Ref::new(Function::from_raw(func))) + } + } + + fn functions(&self) -> Array<Function> { + unsafe { + let mut count = 0; + let functions = BNGetAnalysisFunctionList(self.as_ref().handle, &mut count); + + Array::new(functions, count, ()) + } + } + + /// List of functions *starting* at `addr` + fn functions_at(&self, addr: u64) -> Array<Function> { + unsafe { + let mut count = 0; + let functions = BNGetAnalysisFunctionsForAddress(self.as_ref().handle, addr, &mut count); + + Array::new(functions, count, ()) + } + } + + fn function_at(&self, platform: &Platform, addr: u64) -> Result<Ref<Function>> { + unsafe { + let handle = BNGetAnalysisFunction(self.as_ref().handle, platform.handle, addr); + + if handle.is_null() { + return Err(()); + } + + Ok(Ref::new(Function::from_raw(handle))) + } + } + + fn basic_blocks_containing(&self, addr: u64) -> Array<BasicBlock<NativeBlock>> { + unsafe { + let mut count = 0; + let blocks = BNGetBasicBlocksForAddress(self.as_ref().handle, addr, &mut count); + + Array::new(blocks, count, NativeBlock::new()) + } + } + + fn basic_blocks_starting_at(&self, addr: u64) -> Array<BasicBlock<NativeBlock>> { + unsafe { + let mut count = 0; + let blocks = BNGetBasicBlocksStartingAtAddress(self.as_ref().handle, addr, &mut count); + + Array::new(blocks, count, NativeBlock::new()) + } + } + + fn is_new_auto_function_analysis_suppressed(&self) -> bool { + unsafe { BNGetNewAutoFunctionAnalysisSuppressed(self.as_ref().handle) } + } + + fn set_new_auto_function_analysis_suppressed(&self, suppress: bool) { + unsafe { BNSetNewAutoFunctionAnalysisSuppressed(self.as_ref().handle, suppress); } + } +} + +impl<T: BinaryViewBase> BinaryViewExt for T {} + +#[derive(PartialEq, Eq, Hash)] +pub struct BinaryView { + pub(crate) handle: *mut BNBinaryView, +} + +unsafe impl Send for BinaryView {} +unsafe impl Sync for BinaryView {} + +impl BinaryView { + pub(crate) unsafe fn from_raw(handle: *mut BNBinaryView) -> Self { + debug_assert!(!handle.is_null()); + + Self { handle } + } + + pub fn from_filename<S: BnStrCompatible>(meta: &FileMetadata, filename: S) -> Result<Ref<Self>> { + let file = filename.as_bytes_with_nul(); + + let handle = unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ref().as_ptr() as *mut _) }; + + if handle.is_null() { + return Err(()); + } + + unsafe { Ok(Ref::new(Self { handle })) } + } + + pub fn from_accessor(meta: &FileMetadata, file: &mut FileAccessor) -> Result<Ref<Self>> { + let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut file.api_object as *mut _) }; + + if handle.is_null() { + return Err(()); + } + + unsafe { Ok(Ref::new(Self { handle })) } + } + + pub fn from_data(meta: &FileMetadata, data: &[u8]) -> Result<Ref<Self>> { + let handle = unsafe { BNCreateBinaryDataViewFromData(meta.handle, data.as_ptr() as *mut _, data.len()) }; + + if handle.is_null() { + return Err(()); + } + + unsafe { Ok(Ref::new(Self { handle })) } + } +} + +impl AsRef<BinaryView> for BinaryView { + fn as_ref(&self) -> &Self { + self + } +} + +impl BinaryViewBase for BinaryView { + fn read(&self, buf: &mut [u8], offset: u64) -> usize { + unsafe { + BNReadViewData(self.handle, buf.as_mut_ptr() as *mut _, offset, buf.len()) + } + } + + fn write(&self, offset: u64, data: &[u8]) -> usize { + unsafe { BNWriteViewData(self.handle, offset, data.as_ptr() as *const _, data.len()) } + } + + fn insert(&self, offset: u64, data: &[u8]) -> usize { + unsafe { BNInsertViewData(self.handle, offset, data.as_ptr() as *const _, data.len()) } + } + + fn remove(&self, offset: u64, len: usize) -> usize { + unsafe { BNRemoveViewData(self.handle, offset, len as u64) } + } + + fn modification_status(&self, offset: u64) -> ModificationStatus { + unsafe { BNGetModification(self.handle, offset) } + } + + fn offset_valid(&self, offset: u64) -> bool { + unsafe { BNIsValidOffset(self.handle, offset) } + } + + fn offset_readable(&self, offset: u64) -> bool { + unsafe { BNIsOffsetReadable(self.handle, offset) } + } + + fn offset_writable(&self, offset: u64) -> bool { + unsafe { BNIsOffsetWritable(self.handle, offset) } + } + + fn offset_executable(&self, offset: u64) -> bool { + unsafe { BNIsOffsetExecutable(self.handle, offset) } + } + + fn offset_backed_by_file(&self, offset: u64) -> bool { + unsafe { BNIsOffsetBackedByFile(self.handle, offset) } + } + + fn next_valid_offset_after(&self, offset: u64) -> u64 { + unsafe { BNGetNextValidOffset(self.handle, offset) } + } + + fn default_endianness(&self) -> Endianness { + unsafe { BNGetDefaultEndianness(self.handle) } + } + + fn relocatable(&self) -> bool { + unsafe { BNIsRelocatable(self.handle) } + } + + fn address_size(&self) -> usize { + unsafe { BNGetViewAddressSize(self.handle) } + } + + fn start(&self) -> u64 { + unsafe { BNGetStartOffset(self.handle) } + } + + fn len(&self) -> usize { + unsafe { BNGetViewLength(self.handle) as usize } + } + + fn entry_point(&self) -> u64 { + unsafe { BNGetEntryPoint(self.handle) } + } + + fn executable(&self) -> bool { + unsafe { BNIsExecutableView(self.handle) } + } +} + +impl ToOwned for BinaryView { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for BinaryView { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewViewReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeBinaryView(handle.handle); + } +} + +pub trait BinaryViewTypeBase: AsRef<BinaryViewType> { + fn is_valid_for(&self, data: &BinaryView) -> bool; + + fn load_settings_for_data(&self, data: &BinaryView) -> Ref<Settings> { + unsafe { + Ref::new(Settings::from_raw(BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().0, data.handle))) + } + } +} + +pub trait BinaryViewTypeExt: BinaryViewTypeBase { + fn name(&self) -> BnString { + unsafe { + BnString::from_raw(BNGetBinaryViewTypeName(self.as_ref().0)) + } + } + + fn long_name(&self) -> BnString { + unsafe { + BnString::from_raw(BNGetBinaryViewTypeLongName(self.as_ref().0)) + } + } + + fn register_arch<A: Architecture>(&self, id: u32, endianness: Endianness, arch: &A) + { + unsafe { + BNRegisterArchitectureForViewType(self.as_ref().0, id, endianness, arch.as_ref().0); + } + } + + fn register_platform(&self, id: u32, plat: &Platform) + { + let arch = plat.arch(); + + unsafe { + BNRegisterPlatformForViewType(self.as_ref().0, id, arch.0, plat.handle); + } + } + + fn open(&self, data: &BinaryView) -> Result<Ref<BinaryView>> { + let handle = unsafe { BNCreateBinaryViewOfType(self.as_ref().0, data.handle) }; + + if handle.is_null() { + error!("failed to create BinaryView of BinaryViewType '{}'", self.name()); + return Err(()); + } + + unsafe { + Ok(Ref::new(BinaryView::from_raw(handle))) + } + } +} + +impl<T: BinaryViewTypeBase> BinaryViewTypeExt for T {} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct BinaryViewType(*mut BNBinaryViewType); + +unsafe impl Send for BinaryViewType {} +unsafe impl Sync for BinaryViewType {} + +impl BinaryViewType { + pub fn list_all() -> Array<BinaryViewType> { + unsafe { + let mut count: usize = 0; + let types = BNGetBinaryViewTypes(&mut count as *mut _); + + Array::new(types, count, ()) + } + } + + pub fn list_valid_types_for(data: &BinaryView) -> Array<BinaryViewType> { + unsafe { + let mut count: usize = 0; + let types = BNGetBinaryViewTypesForData(data.handle, &mut count as *mut _); + + Array::new(types, count, ()) + } + } + + /// Looks up a BinaryViewType by its short name + pub fn by_name<N: BnStrCompatible>(name: N) -> Result<Self> { + let bytes = name.as_bytes_with_nul(); + + let res = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) }; + + match res.is_null() { + false => Ok(BinaryViewType(res)), + true => Err(()), + } + } + +} + +impl AsRef<BinaryViewType> for BinaryViewType { + fn as_ref(&self) -> &Self { + self + } +} + +impl BinaryViewTypeBase for BinaryViewType { + fn is_valid_for(&self, data: &BinaryView) -> bool { + unsafe { BNIsBinaryViewTypeValidForData(self.0, data.handle) } + } + + fn load_settings_for_data(&self, data: &BinaryView) -> Ref<Settings> { + unsafe { + Ref::new(Settings::from_raw(BNGetBinaryViewLoadSettingsForData(self.0, data.handle))) + } + } +} + +unsafe impl CoreOwnedArrayProvider for BinaryViewType { + type Raw = *mut BNBinaryViewType; + type Context = (); + + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeBinaryViewTypeList(raw); + } +} + +unsafe impl<'a> CoreOwnedArrayWrapper<'a> for BinaryViewType { + type Wrapped = BinaryViewType; + + unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped { + BinaryViewType(*raw) + } +} + +pub unsafe trait CustomBinaryView: 'static + BinaryViewBase + Sync + Sized { + type Args: Send; + + fn new(handle: BinaryView, args: &Self::Args) -> Result<Self>; + fn init(&self, args: Self::Args) -> Result<()>; +} + +pub trait CustomBinaryViewType: 'static + BinaryViewTypeBase + Sync { + fn create_custom_view<'builder>(&self, data: &BinaryView, builder: CustomViewBuilder<'builder, Self>) -> Result<CustomView<'builder>>; +} + +/// Represents a request from the core to instantiate a custom BinaryView +pub struct CustomViewBuilder<'a, T: CustomBinaryViewType + ?Sized> { + view_type: &'a T, + actual_parent: &'a BinaryView, +} + +/// Represents a partially initialized custom `BinaryView` that should be returned to the core +/// from the `create_custom_view` method of a `CustomBinaryViewType`. +#[must_use] +pub struct CustomView<'builder> { + // this object can't actually be treated like a real + // BinaryView as it isn't fully initialized until the + // core receives it from the BNCustomBinaryViewType::create + // callback. + handle: Ref<BinaryView>, + _builder: PhantomData<&'builder ()>, +} + + +/// Registers a custom `BinaryViewType` with the core. +/// +/// The `constructor` argument is called immediately after successful registration of the type with +/// the core. The `BinaryViewType` argument passed to `constructor` is the object that the +/// `AsRef<BinaryViewType>` +/// implementation of the `CustomBinaryViewType` must return. +pub fn register_view_type<S, T, F>(name: S, long_name: S, constructor: F) -> &'static T +where + S: BnStrCompatible, + T: CustomBinaryViewType, + F: FnOnce(BinaryViewType) -> T, +{ + extern "C" fn cb_valid<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> bool + where + T: CustomBinaryViewType + { + ffi_wrap!("BinaryViewTypeBase::is_valid_for", unsafe { + let view_type = &*(ctxt as *mut T); + let data = BinaryView::from_raw(data); + + view_type.is_valid_for(&data) + }) + } + + extern "C" fn cb_create<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView + where + T: CustomBinaryViewType + { + ffi_wrap!("BinaryViewTypeBase::create", unsafe { + let view_type = &*(ctxt as *mut T); + let data = BinaryView::from_raw(data); + + let builder = CustomViewBuilder { + view_type: view_type, + actual_parent: &data, + }; + + if let Ok(bv) = view_type.create_custom_view(&data, builder) { + // force a leak of the Ref; failure to do this would result + // in the refcount going to 0 in the process of returning it + // to the core -- we're transferring ownership of the Ref here + Ref::into_raw(bv.handle).handle + } else { + error!("CustomBinaryViewType::create_custom_view returned Err"); + + ptr::null_mut() + } + }) + } + + extern "C" fn cb_parse<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView + where + T: CustomBinaryViewType + { + ffi_wrap!("BinaryViewTypeBase::parse", unsafe { + ptr::null_mut() + }) + } + + extern "C" fn cb_load_settings<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNSettings + where + T: CustomBinaryViewType + { + ffi_wrap!("BinaryViewTypeBase::load_settings", unsafe { + let view_type = &*(ctxt as *mut T); + let data = BinaryView::from_raw(data); + + Ref::into_raw(view_type.load_settings_for_data(&data)).handle + }) + } + + let name = name.as_bytes_with_nul(); + let name_ptr = name.as_ref().as_ptr() as *mut _; + + let long_name = long_name.as_bytes_with_nul(); + let long_name_ptr = long_name.as_ref().as_ptr() as *mut _; + + let ctxt = Box::new(unsafe { mem::uninitialized::<T>() }); + let ctxt = Box::into_raw(ctxt); + + let mut bn_obj = BNCustomBinaryViewType { + context: ctxt as *mut _, + create: Some(cb_create::<T>), + parse: Some(cb_parse::<T>), + isValidForData: Some(cb_valid::<T>), + getLoadSettingsForData: Some(cb_load_settings::<T>), + }; + + unsafe { + let res = BNRegisterBinaryViewType(name_ptr, long_name_ptr, &mut bn_obj as *mut _); + + if res.is_null() { + // avoid leaking the space allocated for the type, but also + // avoid running its Drop impl (if any -- not that there should + // be one since view types live for the life of the process) + mem::forget(*Box::from_raw(ctxt)); + + panic!("bvt registration failed"); + } + + ptr::write(ctxt, constructor(BinaryViewType(res))); + + &*ctxt + } +} + +impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { + /// Begins creating a custom BinaryView. + /// + /// This function may only be called from the `create_custom_view` function of a + /// `CustomBinaryViewType`. + /// + /// `parent` specifies the view that the core will treat as the parent view, that + /// Segments created against the created view will be backed by `parent`. It will + /// usually be (but is not required to be) the `data` argument of the `create_custom_view` + /// callback. + /// + /// `constructor` will not be called until well after the value returned by this function + /// has been returned by `create_custom_view` callback to the core, and may not ever + /// be called if the value returned by this function is dropped or leaked. + /// + /// # Errors + /// + /// This function will fail if the `FileMetadata` object associated with the *expected* parent + /// (i.e., the `data` argument passed to the `create_custom_view` function) already has an + /// associated `BinaryView` of the same `CustomBinaryViewType`. Multiple `BinaryView` objects + /// of the same `BinaryViewType` belonging to the same `FileMetadata` object is prohibited and + /// can cause strange, delayed segmentation faults. + /// + /// # Safety + /// + /// `constructor` should avoid doing anything with the object it returns, especially anything + /// that would cause the core to invoke any of the `BinaryViewBase` methods. The core isn't + /// going to consider the object fully initialized until after that callback has run. + /// + /// The `BinaryView` argument passed to the constructor function is the object that is expected + /// to be returned by the `AsRef<BinaryView>` implementation required by the `BinaryViewBase` trait. + /// TODO FIXME welp this is broke going to need 2 init callbacks + pub fn create<V>(self, parent: &BinaryView, view_args: V::Args) -> Result<CustomView<'a>> + where + V: CustomBinaryView, + { + let file = self.actual_parent.metadata(); + let view_type = self.view_type; + + let view_name = view_type.name(); + + if let Ok(bv) = file.get_view_of_type(view_name.as_cstr()) { + // while it seems to work most of the time, you can get really unlucky + // if the a free of the existing view of the same type kicks off while + // BNCreateBinaryViewOfType is still running. the freeObject callback + // will run for the new view before we've even finished initializing, + // and that's all she wrote. + // + // even if we deal with it gracefully in cb_free_object, + // BNCreateBinaryViewOfType is still going to crash, so we're just + // going to try and stop this from happening in the first place. + error!("attempt to create duplicate view of type '{}' (existing: {:?})", view_name.as_str(), bv.handle); + + return Err(()); + } + + // wildly unsafe struct representing the context of a BNCustomBinaryView + // this type should *never* be allowed to drop as the fields are in varying + // states of uninitialized/already consumed throughout the life of the object. + struct CustomViewContext<V> + where + V: CustomBinaryView, + { + view: V, + raw_handle: *mut BNBinaryView, + initialized: bool, + args: V::Args, + } + + extern "C" fn cb_init<V>(ctxt: *mut c_void) -> bool + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::init", unsafe { + let context = &mut *(ctxt as *mut CustomViewContext<V>); + let handle = BinaryView::from_raw(context.raw_handle); + + if let Ok(v) = V::new(handle, &context.args) { + ptr::write(&mut context.view, v); + context.initialized = true; + + if context.view.init(ptr::read(&context.args)).is_ok() { + true + } else { + error!("CustomBinaryView::init failed; custom view returned Err"); + false + } + } else { + error!("CustomBinaryView::new failed; custom view returned Err"); + false + } + }) + } + + extern "C" fn cb_free_object<V>(ctxt: *mut c_void) + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::freeObject", unsafe { + let context = ctxt as *mut CustomViewContext<V>; + let context = *Box::from_raw(context); + + if context.initialized { + mem::forget(context.args); // already consumed + mem::drop(context.view); // cb_init was called + } else { + mem::drop(context.args); // never consumed + mem::forget(context.view); // cb_init was not called, is uninit + + if context.raw_handle.is_null() { + // being called here is essentially a guarantee that BNCreateBinaryViewOfType + // is above above us on the call stack somewhere -- no matter what we do, a crash + // is pretty much certain at this point. + // + // this has been observed when two views of the same BinaryViewType are created + // against the same BNFileMetaData object, and one of the views gets freed while + // the second one is being initialized -- somehow the partially initialized one + // gets freed before BNCreateBinaryViewOfType returns. + // + // multiples views of the same BinaryViewType in a BNFileMetaData object are + // prohibited, so an API contract was violated in order to get here. + // + // if we're here, it's too late to do anything about it, though we can at least not + // run the destructor on the custom view since that memory is unitialized. + error!("BinaryViewBase::freeObject called on partially initialized object! crash imminent!"); + } else if !context.initialized { + // making it here means somebody went out of their way to leak a BinaryView + // after calling BNCreateCustomView and never gave the BNBinaryView handle + // to the core (which would have called cb_init) + // + // the result is a half-initialized BinaryView that the core will happily hand out + // references to via BNGetFileViewofType even though it was never initialized + // all the way. + // + // TODO update when this corner case gets fixed in the core? + // + // we can't do anything to prevent this, but we can at least have the crash + // not be our fault. + error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!"); + } + } + }) + } + + extern "C" fn cb_read<V>(ctxt: *mut c_void, dest: *mut c_void, offset: u64, len: usize) -> usize + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::read", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + let dest = slice::from_raw_parts_mut(dest as *mut u8, len); + + context.view.read(dest, offset) + }) + } + + extern "C" fn cb_write<V>(ctxt: *mut c_void, offset: u64, src: *const c_void, len: usize) -> usize + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::write", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + let src = slice::from_raw_parts(src as *const u8, len); + + context.view.write(offset, src) + }) + } + + extern "C" fn cb_insert<V>(ctxt: *mut c_void, offset: u64, src: *const c_void, len: usize) -> usize + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::insert", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + let src = slice::from_raw_parts(src as *const u8, len); + + context.view.insert(offset, src) + }) + } + + extern "C" fn cb_remove<V>(ctxt: *mut c_void, offset: u64, len: u64) -> usize + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::remove", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.remove(offset, len as usize) + }) + } + + extern "C" fn cb_modification<V>(ctxt: *mut c_void, offset: u64) -> ModificationStatus + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::modification_status", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.modification_status(offset) + }) + } + + extern "C" fn cb_offset_valid<V>(ctxt: *mut c_void, offset: u64) -> bool + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::offset_valid", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.offset_valid(offset) + }) + } + + extern "C" fn cb_offset_readable<V>(ctxt: *mut c_void, offset: u64) -> bool + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::readable", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.offset_readable(offset) + }) + } + + extern "C" fn cb_offset_writable<V>(ctxt: *mut c_void, offset: u64) -> bool + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::writable", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.offset_writable(offset) + }) + } + + extern "C" fn cb_offset_executable<V>(ctxt: *mut c_void, offset: u64) -> bool + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::offset_executable", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.offset_executable(offset) + }) + } + + extern "C" fn cb_offset_backed_by_file<V>(ctxt: *mut c_void, offset: u64) -> bool + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::offset_backed_by_file", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.offset_backed_by_file(offset) + }) + } + + extern "C" fn cb_next_valid_offset<V>(ctxt: *mut c_void, offset: u64) -> u64 + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::next_valid_offset_after", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.next_valid_offset_after(offset) + }) + } + + extern "C" fn cb_start<V>(ctxt: *mut c_void) -> u64 + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::start", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.start() + }) + } + + extern "C" fn cb_length<V>(ctxt: *mut c_void) -> u64 + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::len", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.len() as u64 + }) + } + + extern "C" fn cb_entry_point<V>(ctxt: *mut c_void) -> u64 + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::entry_point", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.entry_point() + }) + } + + extern "C" fn cb_executable<V>(ctxt: *mut c_void) -> bool + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::executable", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.executable() + }) + } + + extern "C" fn cb_endianness<V>(ctxt: *mut c_void) -> Endianness + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::default_endianness", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.default_endianness() + }) + } + + extern "C" fn cb_relocatable<V>(ctxt: *mut c_void) -> bool + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::relocatable", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.relocatable() + }) + } + + extern "C" fn cb_address_size<V>(ctxt: *mut c_void) -> usize + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::address_size", unsafe { + let context = &*(ctxt as *mut CustomViewContext<V>); + + context.view.address_size() + }) + } + + extern "C" fn cb_save<V>(ctxt: *mut c_void, _fa: *mut BNFileAccessor) -> bool + where + V: CustomBinaryView, + { + ffi_wrap!("BinaryViewBase::save", unsafe { + let _context = &*(ctxt as *mut CustomViewContext<V>); + false + }) + } + + let ctxt = Box::new(CustomViewContext::<V> { + view: unsafe { mem::uninitialized() }, + raw_handle: ptr::null_mut(), + initialized: false, + args: view_args, + }); + + let ctxt = Box::into_raw(ctxt); + + let mut bn_obj = BNCustomBinaryView { + context: ctxt as *mut _, + init: Some(cb_init::<V>), + freeObject: Some(cb_free_object::<V>), + externalRefTaken: None, + externalRefReleased: None, + read: Some(cb_read::<V>), + write: Some(cb_write::<V>), + insert: Some(cb_insert::<V>), + remove: Some(cb_remove::<V>), + getModification: Some(cb_modification::<V>), + isValidOffset: Some(cb_offset_valid::<V>), + isOffsetReadable: Some(cb_offset_readable::<V>), + isOffsetWritable: Some(cb_offset_writable::<V>), + isOffsetExecutable: Some(cb_offset_executable::<V>), + isOffsetBackedByFile: Some(cb_offset_backed_by_file::<V>), + getNextValidOffset: Some(cb_next_valid_offset::<V>), + getStart: Some(cb_start::<V>), + getLength: Some(cb_length::<V>), + getEntryPoint: Some(cb_entry_point::<V>), + isExecutable: Some(cb_executable::<V>), + getDefaultEndianness: Some(cb_endianness::<V>), + isRelocatable: Some(cb_relocatable::<V>), + getAddressSize: Some(cb_address_size::<V>), + save: Some(cb_save::<V>), + }; + + unsafe { + let res = BNCreateCustomBinaryView(view_name.as_cstr().as_ptr(), file.handle, parent.handle, &mut bn_obj); + + if res.is_null() { + // TODO not sure when this can even happen, let alone what we're supposed to do about + // it. cb_init isn't normally called until later, and cb_free_object definitely won't + // have been called, so we'd at least be on the hook for freeing that stuff... + // probably. + // + // no idea how to force this to fail so I can test this, so just going to do the + // reasonable thing and panic. + panic!("failed to create custom binary view!"); + } + + (*ctxt).raw_handle = res; + + Ok(CustomView { + handle: Ref::new(BinaryView::from_raw(res)), + _builder: PhantomData, + }) + } + } + + pub fn wrap_existing(self, wrapped_view: Ref<BinaryView>) -> Result<CustomView<'a>> + { + Ok(CustomView { + handle: wrapped_view, + _builder: PhantomData, + }) + } +} + diff --git a/rust/src/callingconvention.rs b/rust/src/callingconvention.rs new file mode 100644 index 00000000..a607b67f --- /dev/null +++ b/rust/src/callingconvention.rs @@ -0,0 +1,657 @@ +use std::mem; +use std::ptr; +use std::slice; +use std::os::raw::c_void; +use std::borrow::Borrow; +use std::marker::PhantomData; + +use binaryninjacore_sys::*; + +use crate::architecture::{Architecture, ArchitectureExt, Register}; +use crate::rc::{Ref, RefCountable}; +use crate::string::*; + +// TODO +// force valid registers once Arch has _from_id methods +// CallingConvention impl +// dataflow callbacks + +pub trait CallingConventionBase: Sync { + type Arch: Architecture; + + fn caller_saved_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>; + fn callee_saved_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>; + fn int_arg_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>; + fn float_arg_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>; + + fn arg_registers_shared_index(&self) -> bool; + fn reserved_stack_space_for_arg_registers(&self) -> bool; + fn stack_adjusted_on_return(&self) -> bool; + + fn return_int_reg(&self) -> Option<<Self::Arch as Architecture>::Register>; + fn return_hi_int_reg(&self) -> Option<<Self::Arch as Architecture>::Register>; + fn return_float_reg(&self) -> Option<<Self::Arch as Architecture>::Register>; + + fn global_pointer_reg(&self) -> Option<<Self::Arch as Architecture>::Register>; + + fn implicitly_defined_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>; +} + +pub fn register_calling_convention<A, N, C>(arch: &A, name: N, cc: C) -> Ref<CallingConvention<A>> +where + A: Architecture, + N: BnStrCompatible, + C: 'static + CallingConventionBase<Arch=A>, +{ + struct CustomCallingConventionContext<C> + where + C: CallingConventionBase, + { + raw_handle: *mut BNCallingConvention, + cc: C, + } + + extern "C" fn cb_free<C>(ctxt: *mut c_void) + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::free", unsafe { + let _ctxt = Box::from_raw(ctxt as *mut CustomCallingConventionContext<C>); + }) + } + + fn alloc_register_list<I: Iterator<Item=u32> + ExactSizeIterator>(items: I, count: &mut usize) -> *mut u32 { + let len = items.len(); + *count = len; + + if len == 0 { + ptr::null_mut() + } else { + let mut res = Vec::with_capacity(len + 1); + + res.push(len as u32); + + for i in items { + res.push(i.clone().into()); + } + + assert!(res.len() == len + 1); + + let raw = res.as_mut_ptr(); + mem::forget(res); + + unsafe { raw.offset(1) } + } + } + + extern "C" fn cb_free_register_list(_ctxt: *mut c_void, regs: *mut u32) { + ffi_wrap!("CallingConvention::free_register_list", unsafe { + if regs.is_null() { + return; + } + + let actual_start = regs.offset(-1); + let len = *actual_start + 1; + let _regs = Vec::from_raw_parts(actual_start, len as usize, len as usize); + }) + } + + extern "C" fn cb_caller_saved<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::caller_saved_registers", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + let regs = ctxt.cc.caller_saved_registers(); + + alloc_register_list(regs.iter().map(|r| r.id()), &mut *count) + }) + } + + extern "C" fn cb_callee_saved<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::callee_saved_registers", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + let regs = ctxt.cc.callee_saved_registers(); + + alloc_register_list(regs.iter().map(|r| r.id()), &mut *count) + }) + } + + extern "C" fn cb_int_args<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::int_arg_registers", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + let regs = ctxt.cc.int_arg_registers(); + + alloc_register_list(regs.iter().map(|r| r.id()), &mut *count) + }) + } + + extern "C" fn cb_float_args<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::float_arg_registers", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + let regs = ctxt.cc.float_arg_registers(); + + alloc_register_list(regs.iter().map(|r| r.id()), &mut *count) + }) + } + + extern "C" fn cb_arg_shared_index<C>(ctxt: *mut c_void) -> bool + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::arg_registers_shared_index", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + + ctxt.cc.arg_registers_shared_index() + }) + } + + extern "C" fn cb_stack_reserved_arg_regs<C>(ctxt: *mut c_void) -> bool + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::reserved_stack_space_for_arg_registers", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + + ctxt.cc.reserved_stack_space_for_arg_registers() + }) + } + + extern "C" fn cb_stack_adjusted_on_return<C>(ctxt: *mut c_void) -> bool + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::stack_adjusted_on_return", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + + ctxt.cc.stack_adjusted_on_return() + }) + } + + extern "C" fn cb_return_int_reg<C>(ctxt: *mut c_void) -> u32 + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::return_int_reg", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + + match ctxt.cc.return_int_reg() { + Some(r) => r.id(), + _ => 0xffff_ffff, + } + }) + } + + extern "C" fn cb_return_hi_int_reg<C>(ctxt: *mut c_void) -> u32 + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::return_hi_int_reg", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + + match ctxt.cc.return_hi_int_reg() { + Some(r) => r.id(), + _ => 0xffff_ffff, + } + }) + } + + extern "C" fn cb_return_float_reg<C>(ctxt: *mut c_void) -> u32 + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::return_float_reg", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + + match ctxt.cc.return_float_reg() { + Some(r) => r.id(), + _ => 0xffff_ffff, + } + }) + } + + extern "C" fn cb_global_pointer_reg<C>(ctxt: *mut c_void) -> u32 + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::global_pointer_reg", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + + match ctxt.cc.global_pointer_reg() { + Some(r) => r.id(), + _ => 0xffff_ffff, + } + }) + } + + extern "C" fn cb_implicitly_defined_registers<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32 + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::implicitly_defined_registers", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + let regs = ctxt.cc.implicitly_defined_registers(); + + alloc_register_list(regs.iter().map(|r| r.id()), &mut *count) + }) + } + + extern "C" fn cb_incoming_reg_value<C>(_ctxt: *mut c_void, _reg: u32, _func: *mut BNFunction, val: *mut BNRegisterValue) + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::incoming_reg_value", unsafe { + //let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + let val = &mut *val; + + val.state = BNRegisterValueType::EntryValue; + val.value = _reg as i64; + }) + } + + extern "C" fn cb_incoming_flag_value<C>(_ctxt: *mut c_void, _flag: u32, _func: *mut BNFunction, val: *mut BNRegisterValue) + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::incoming_flag_value", unsafe { + //let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + let val = &mut *val; + + val.state = BNRegisterValueType::EntryValue; + val.value = _flag as i64; + }) + } + + extern "C" fn cb_incoming_var_for_param<C>(ctxt: *mut c_void, var: *const BNVariable, _func: *mut BNFunction, param: *mut BNVariable) + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::incoming_var_for_param", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + ptr::write(param, BNGetDefaultIncomingVariableForParameterVariable(ctxt.raw_handle, var)); + }) + } + + extern "C" fn cb_incoming_param_for_var<C>(ctxt: *mut c_void, var: *const BNVariable, _func: *mut BNFunction, param: *mut BNVariable) + where + C: CallingConventionBase, + { + ffi_wrap!("CallingConvention::incoming_var_for_param", unsafe { + let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); + ptr::write(param, BNGetDefaultParameterVariableForIncomingVariable(ctxt.raw_handle, var)); + }) + } + + let name = name.as_bytes_with_nul(); + let raw = Box::into_raw(Box::new(CustomCallingConventionContext { + raw_handle: ptr::null_mut(), + cc: cc, + })); + let mut cc = BNCustomCallingConvention { + context: raw as *mut _, + + freeObject: Some(cb_free::<C>), + + getCallerSavedRegisters: Some(cb_caller_saved::<C>), + getCalleeSavedRegisters: Some(cb_callee_saved::<C>), + getIntegerArgumentRegisters: Some(cb_int_args::<C>), + getFloatArgumentRegisters: Some(cb_float_args::<C>), + freeRegisterList: Some(cb_free_register_list), + + areArgumentRegistersSharedIndex: Some(cb_arg_shared_index::<C>), + isStackReservedForArgumentRegisters: Some(cb_stack_reserved_arg_regs::<C>), + isStackAdjustedOnReturn: Some(cb_stack_adjusted_on_return::<C>), + + getIntegerReturnValueRegister: Some(cb_return_int_reg::<C>), + getHighIntegerReturnValueRegister: Some(cb_return_hi_int_reg::<C>), + getFloatReturnValueRegister: Some(cb_return_float_reg::<C>), + getGlobalPointerRegister: Some(cb_global_pointer_reg::<C>), + + getImplicitlyDefinedRegisters: Some(cb_implicitly_defined_registers::<C>), + getIncomingRegisterValue: Some(cb_incoming_reg_value::<C>), + getIncomingFlagValue: Some(cb_incoming_flag_value::<C>), + getIncomingVariableForParameterVariable: Some(cb_incoming_var_for_param::<C>), + getParameterVariableForIncomingVariable: Some(cb_incoming_param_for_var::<C>), + }; + + unsafe { + let cc_name = name.as_ref().as_ptr() as *mut _; + let result = BNCreateCallingConvention(arch.as_ref().0, cc_name, &mut cc); + + assert!(!result.is_null()); + + (*raw).raw_handle = result; + + BNRegisterCallingConvention(arch.as_ref().0, result); + + Ref::new(CallingConvention { + handle: result, + arch_handle: arch.handle(), + _arch: PhantomData, + }) + } +} + +pub struct CallingConvention<A: Architecture> { + pub(crate) handle: *mut BNCallingConvention, + pub(crate) arch_handle: A::Handle, + _arch: PhantomData<*mut A>, +} + +unsafe impl<A: Architecture> Send for CallingConvention<A> {} +unsafe impl<A: Architecture> Sync for CallingConvention<A> {} + +impl<A: Architecture> CallingConvention<A> { + pub(crate) unsafe fn from_raw(handle: *mut BNCallingConvention, arch: A::Handle) -> Self { + CallingConvention { + handle: handle, + arch_handle: arch, + _arch: PhantomData, + } + } +} + +impl<A: Architecture> Eq for CallingConvention<A> {} +impl<A: Architecture> PartialEq for CallingConvention<A> { + fn eq(&self, rhs: &Self) -> bool { + self.handle == rhs.handle + } +} + +use std::hash::{Hash, Hasher}; +impl<A: Architecture> Hash for CallingConvention<A> { + fn hash<H: Hasher>(&self, state: &mut H) { + self.handle.hash(state); + } +} + + +impl<A: Architecture> CallingConventionBase for CallingConvention<A> { + type Arch = A; + + fn caller_saved_registers(&self) -> Vec<A::Register> { + unsafe { + let mut count = 0; + let regs = BNGetCallerSavedRegisters(self.handle, &mut count); + let arch = self.arch_handle.borrow(); + + let res = slice::from_raw_parts(regs, count).iter().map(|&r| { + arch.register_from_id(r).expect("bad reg id from CallingConvention") + }).collect(); + + BNFreeRegisterList(regs); + + res + } + } + + fn callee_saved_registers(&self) -> Vec<A::Register> { + unsafe { + let mut count = 0; + let regs = BNGetCalleeSavedRegisters(self.handle, &mut count); + let arch = self.arch_handle.borrow(); + + let res = slice::from_raw_parts(regs, count).iter().map(|&r| { + arch.register_from_id(r).expect("bad reg id from CallingConvention") + }).collect(); + + BNFreeRegisterList(regs); + + res + } + } + + fn int_arg_registers(&self) -> Vec<A::Register> { + Vec::new() + } + + fn float_arg_registers(&self) -> Vec<A::Register> { + Vec::new() + } + + fn arg_registers_shared_index(&self) -> bool { + unsafe { BNAreArgumentRegistersSharedIndex(self.handle) } + } + + fn reserved_stack_space_for_arg_registers(&self) -> bool { + unsafe { BNIsStackReservedForArgumentRegisters(self.handle) } + } + + fn stack_adjusted_on_return(&self) -> bool { + unsafe { BNIsStackAdjustedOnReturn(self.handle) } + } + + fn return_int_reg(&self) -> Option<A::Register> { + match unsafe { BNGetIntegerReturnValueRegister(self.handle) } { + id if id < 0x8000_0000 => { + self.arch_handle.borrow().register_from_id(id) + } + _ => None, + } + } + + fn return_hi_int_reg(&self) -> Option<A::Register> { + match unsafe { BNGetHighIntegerReturnValueRegister(self.handle) } { + id if id < 0x8000_0000 => { + self.arch_handle.borrow().register_from_id(id) + } + _ => None, + } + } + + fn return_float_reg(&self) -> Option<A::Register> { + match unsafe { BNGetFloatReturnValueRegister(self.handle) } { + id if id < 0x8000_0000 => { + self.arch_handle.borrow().register_from_id(id) + } + _ => None, + } + } + + fn global_pointer_reg(&self) -> Option<A::Register> { + match unsafe { BNGetGlobalPointerRegister(self.handle) } { + id if id < 0x8000_0000 => { + self.arch_handle.borrow().register_from_id(id) + } + _ => None, + } + } + + fn implicitly_defined_registers(&self) -> Vec<A::Register> { + Vec::new() + } +} + +impl<A: Architecture> ToOwned for CallingConvention<A> { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl<A: Architecture> RefCountable for CallingConvention<A> { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewCallingConventionReference(handle.handle), + arch_handle: handle.arch_handle.clone(), + _arch: PhantomData, + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeCallingConvention(handle.handle); + } +} + +pub struct ConventionBuilder<A: Architecture> { + caller_saved_registers: Vec<A::Register>, + callee_saved_registers: Vec<A::Register>, + int_arg_registers: Vec<A::Register>, + float_arg_registers: Vec<A::Register>, + + arg_registers_shared_index: bool, + reserved_stack_space_for_arg_registers: bool, + stack_adjusted_on_return: bool, + + return_int_reg: Option<A::Register>, + return_hi_int_reg: Option<A::Register>, + return_float_reg: Option<A::Register>, + + global_pointer_reg: Option<A::Register>, + + implicitly_defined_registers: Vec<A::Register>, + + arch_handle: A::Handle, + _arch: PhantomData<*const A>, +} + +unsafe impl<A: Architecture> Send for ConventionBuilder<A> {} +unsafe impl<A: Architecture> Sync for ConventionBuilder<A> {} + +macro_rules! bool_arg { + ($name:ident) => { + pub fn $name(mut self, val: bool) -> Self { + self.$name = val; + self + } + } +} + +macro_rules! reg_list { + ($name:ident) => { + pub fn $name(mut self, regs: &[&str]) -> Self { + { // FIXME NLL + let arch = self.arch_handle.borrow(); + let arch_regs = regs.iter().filter_map(|&r| arch.register_by_name(r)); + + self.$name = arch_regs.collect(); + } + + self + } + } +} + +macro_rules! reg { + ($name:ident) => { + pub fn $name(mut self, reg: &str) -> Self { + { // FIXME NLL + let arch = self.arch_handle.borrow(); + self.$name = arch.register_by_name(reg); + } + + self + } + } +} + +impl<A: Architecture> ConventionBuilder<A> { + pub fn new(arch: &A) -> Self { + Self { + caller_saved_registers: Vec::new(), + callee_saved_registers: Vec::new(), + int_arg_registers: Vec::new(), + float_arg_registers: Vec::new(), + + arg_registers_shared_index: false, + reserved_stack_space_for_arg_registers: false, + stack_adjusted_on_return: false, + + return_int_reg: None, + return_hi_int_reg: None, + return_float_reg: None, + + global_pointer_reg: None, + + implicitly_defined_registers: Vec::new(), + + arch_handle: arch.handle(), + _arch: PhantomData, + } + } + + reg_list!(caller_saved_registers); + reg_list!(callee_saved_registers); + reg_list!(int_arg_registers); + reg_list!(float_arg_registers); + + bool_arg!(arg_registers_shared_index); + bool_arg!(reserved_stack_space_for_arg_registers); + bool_arg!(stack_adjusted_on_return); + + reg!(return_int_reg); + reg!(return_hi_int_reg); + reg!(return_float_reg); + + reg!(global_pointer_reg); + + reg_list!(implicitly_defined_registers); + + pub fn register(self, name: &str) -> Ref<CallingConvention<A>> { + let arch = self.arch_handle.clone(); + + register_calling_convention(arch.borrow(), name, self) + } +} + +impl<A: Architecture> CallingConventionBase for ConventionBuilder<A> { + type Arch = A; + + fn caller_saved_registers(&self) -> Vec<A::Register> { + self.caller_saved_registers.clone() + } + + fn callee_saved_registers(&self) -> Vec<A::Register> { + self.caller_saved_registers.clone() + } + + fn int_arg_registers(&self) -> Vec<A::Register> { + self.int_arg_registers.clone() + } + + fn float_arg_registers(&self) -> Vec<A::Register> { + self.float_arg_registers.clone() + } + + fn arg_registers_shared_index(&self) -> bool { + self.arg_registers_shared_index + } + + fn reserved_stack_space_for_arg_registers(&self) -> bool { + self.reserved_stack_space_for_arg_registers + } + + fn stack_adjusted_on_return(&self) -> bool { + self.stack_adjusted_on_return + } + + fn return_int_reg(&self) -> Option<A::Register> { + self.return_int_reg.clone() + } + + fn return_hi_int_reg(&self) -> Option<A::Register> { + self.return_hi_int_reg.clone() + } + + fn return_float_reg(&self) -> Option<A::Register> { + self.return_float_reg.clone() + } + + fn global_pointer_reg(&self) -> Option<A::Register> { + self.global_pointer_reg.clone() + } + + fn implicitly_defined_registers(&self) -> Vec<A::Register> { + self.implicitly_defined_registers.clone() + } +} diff --git a/rust/src/command.rs b/rust/src/command.rs new file mode 100644 index 00000000..70839f6b --- /dev/null +++ b/rust/src/command.rs @@ -0,0 +1,263 @@ +use binaryninjacore_sys::{BNRegisterPluginCommand, + BNRegisterPluginCommandForAddress, + BNRegisterPluginCommandForRange, + BNRegisterPluginCommandForFunction, + BNBinaryView, + BNFunction}; + +use std::ops::Range; +use std::os::raw::c_void; + +use crate::binaryview::BinaryView; +use crate::function::Function; +use crate::string::BnStrCompatible; + +pub trait Command: 'static + Sync { + fn action(&self, view: &BinaryView); + fn valid(&self, view: &BinaryView) -> bool; +} + +impl<T> Command for T +where + T: 'static + Sync + Fn(&BinaryView), +{ + fn action(&self, view: &BinaryView) { + self(view); + } + + fn valid(&self, _view: &BinaryView) -> bool { + true + } +} + +pub fn register<S, C>(name: S, desc: S, command: C) +where + S: BnStrCompatible, + C: Command, +{ + extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView) + where + C: Command, + { + ffi_wrap!("Command::action", unsafe { + let cmd = &*(ctxt as *const C); + let view = BinaryView::from_raw(view); + + cmd.action(&view); + }) + } + + extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView) -> bool + where + C: Command, + { + ffi_wrap!("Command::valid", unsafe { + let cmd = &*(ctxt as *const C); + let view = BinaryView::from_raw(view); + + cmd.valid(&view) + }) + } + + let name = name.as_bytes_with_nul(); + let desc = desc.as_bytes_with_nul(); + + let name_ptr = name.as_ref().as_ptr() as *mut _; + let desc_ptr = desc.as_ref().as_ptr() as *mut _; + + let ctxt = Box::into_raw(Box::new(command)); + + unsafe { + BNRegisterPluginCommand(name_ptr, desc_ptr, + Some(cb_action::<C>), Some(cb_valid::<C>), + ctxt as *mut _); + } +} + +pub trait AddressCommand: 'static + Sync { + fn action(&self, view: &BinaryView, addr: u64); + fn valid(&self, view: &BinaryView, addr: u64) -> bool; +} + +impl<T> AddressCommand for T +where + T: 'static + Sync + Fn(&BinaryView, u64), +{ + fn action(&self, view: &BinaryView, addr: u64) { + self(view, addr); + } + + fn valid(&self, _view: &BinaryView, _addr: u64) -> bool { + true + } +} + +pub fn register_for_address<S, C>(name: S, desc: S, command: C) +where + S: BnStrCompatible, + C: AddressCommand, +{ + extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64) + where + C: AddressCommand, + { + ffi_wrap!("AddressCommand::action", unsafe { + let cmd = &*(ctxt as *const C); + let view = BinaryView::from_raw(view); + + cmd.action(&view, addr); + }) + } + + extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64) -> bool + where + C: AddressCommand, + { + ffi_wrap!("AddressCommand::valid", unsafe { + let cmd = &*(ctxt as *const C); + let view = BinaryView::from_raw(view); + + cmd.valid(&view, addr) + }) + } + + let name = name.as_bytes_with_nul(); + let desc = desc.as_bytes_with_nul(); + + let name_ptr = name.as_ref().as_ptr() as *mut _; + let desc_ptr = desc.as_ref().as_ptr() as *mut _; + + let ctxt = Box::into_raw(Box::new(command)); + + unsafe { + BNRegisterPluginCommandForAddress(name_ptr, desc_ptr, + Some(cb_action::<C>), Some(cb_valid::<C>), + ctxt as *mut _); + } +} + +pub trait RangeCommand: 'static + Sync { + fn action(&self, view: &BinaryView, range: Range<u64>); + fn valid(&self, view: &BinaryView, range: Range<u64>) -> bool; +} + +impl<T> RangeCommand for T +where + T: 'static + Sync + Fn(&BinaryView, Range<u64>), +{ + fn action(&self, view: &BinaryView, range: Range<u64>) { + self(view, range); + } + + fn valid(&self, _view: &BinaryView, _range: Range<u64>) -> bool { + true + } +} + +pub fn register_for_range<S, C>(name: S, desc: S, command: C) +where + S: BnStrCompatible, + C: RangeCommand, +{ + extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64, len: u64) + where + C: RangeCommand, + { + ffi_wrap!("RangeCommand::action", unsafe { + let cmd = &*(ctxt as *const C); + let view = BinaryView::from_raw(view); + + cmd.action(&view, addr .. addr.wrapping_add(len)); + }) + } + + extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64, len: u64) -> bool + where + C: RangeCommand, + { + ffi_wrap!("RangeCommand::valid", unsafe { + let cmd = &*(ctxt as *const C); + let view = BinaryView::from_raw(view); + + cmd.valid(&view, addr .. addr.wrapping_add(len)) + }) + } + + let name = name.as_bytes_with_nul(); + let desc = desc.as_bytes_with_nul(); + + let name_ptr = name.as_ref().as_ptr() as *mut _; + let desc_ptr = desc.as_ref().as_ptr() as *mut _; + + let ctxt = Box::into_raw(Box::new(command)); + + unsafe { + BNRegisterPluginCommandForRange(name_ptr, desc_ptr, + Some(cb_action::<C>), Some(cb_valid::<C>), + ctxt as *mut _); + } +} + +pub trait FunctionCommand: 'static + Sync { + fn action(&self, view: &BinaryView, func: &Function); + fn valid(&self, view: &BinaryView, func: &Function) -> bool; +} + +impl<T> FunctionCommand for T +where + T: 'static + Sync + Fn(&BinaryView, &Function), +{ + fn action(&self, view: &BinaryView, func: &Function) { + self(view, func); + } + + fn valid(&self, _view: &BinaryView, _func: &Function) -> bool { + true + } +} + +pub fn register_for_function<S, C>(name: S, desc: S, command: C) +where + S: BnStrCompatible, + C: FunctionCommand, +{ + extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, func: *mut BNFunction) + where + C: FunctionCommand, + { + ffi_wrap!("FunctionCommand::action", unsafe { + let cmd = &*(ctxt as *const C); + let view = BinaryView::from_raw(view); + let func = Function::from_raw(func); + + cmd.action(&view, &func); + }) + } + + extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView, func: *mut BNFunction) -> bool + where + C: FunctionCommand, + { + ffi_wrap!("FunctionCommand::valid", unsafe { + let cmd = &*(ctxt as *const C); + let view = BinaryView::from_raw(view); + let func = Function::from_raw(func); + + cmd.valid(&view, &func) + }) + } + + let name = name.as_bytes_with_nul(); + let desc = desc.as_bytes_with_nul(); + + let name_ptr = name.as_ref().as_ptr() as *mut _; + let desc_ptr = desc.as_ref().as_ptr() as *mut _; + + let ctxt = Box::into_raw(Box::new(command)); + + unsafe { + BNRegisterPluginCommandForFunction(name_ptr, desc_ptr, + Some(cb_action::<C>), Some(cb_valid::<C>), + ctxt as *mut _); + } +} diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs new file mode 100644 index 00000000..54279233 --- /dev/null +++ b/rust/src/ffi.rs @@ -0,0 +1,13 @@ + +macro_rules! ffi_wrap { + ($n:expr, $b:expr) => {{ + use std::panic; + use std::process; + + panic::catch_unwind(|| $b).unwrap_or_else(|_| { + error!("ffi callback caught panic: {}", $n); + process::abort() + }) + }} +} + diff --git a/rust/src/fileaccessor.rs b/rust/src/fileaccessor.rs new file mode 100644 index 00000000..e3320be2 --- /dev/null +++ b/rust/src/fileaccessor.rs @@ -0,0 +1,78 @@ +use binaryninjacore_sys::BNFileAccessor; +use std::io::{Read, Write, Seek, SeekFrom}; +use std::marker::PhantomData; +use std::slice; + +pub struct FileAccessor<'a> +{ + pub(crate) api_object: BNFileAccessor, + _ref: PhantomData<&'a mut ()>, +} + +impl<'a> FileAccessor<'a> +{ + pub fn new<F>(f: &'a mut F) -> Self + where + F: 'a + Read + Write + Seek + Sized + { + use std::os::raw::c_void; + + extern "C" fn cb_get_length<F>(ctxt: *mut c_void) -> u64 + where + F: Read + Write + Seek + Sized + { + let f = unsafe { &mut *(ctxt as *mut F) }; + + match f.seek(SeekFrom::End(0)) { + Ok(len) => len, + Err(_) => 0, + } + } + + extern "C" fn cb_read<F>(ctxt: *mut c_void, dest: *mut c_void, offset: u64, len: usize) -> usize + where + F: Read + Write + Seek + Sized + { + let f = unsafe { &mut *(ctxt as *mut F) }; + let dest = unsafe { slice::from_raw_parts_mut(dest as *mut u8, len) }; + + if !f.seek(SeekFrom::Start(offset)).is_ok() { + debug!("Failed to seek to offset {:x}", offset); + return 0; + } + + match f.read(dest) { + Ok(len) => len, + Err(_) => 0, + } + } + + extern "C" fn cb_write<F>(ctxt: *mut c_void, offset: u64, src: *const c_void, len: usize) -> usize + where + F: Read + Write + Seek + Sized + { + let f = unsafe { &mut *(ctxt as *mut F) }; + let src = unsafe { slice::from_raw_parts(src as *const u8, len) }; + + if !f.seek(SeekFrom::Start(offset)).is_ok() { + return 0; + } + + match f.write(src) { + Ok(len) => len, + Err(_) => 0, + } + } + + Self { + api_object: BNFileAccessor { + context: f as *mut F as *mut _, + getLength: Some(cb_get_length::<F>), + read: Some(cb_read::<F>), + write: Some(cb_write::<F>), + }, + _ref: PhantomData, + } + } +} + diff --git a/rust/src/filemetadata.rs b/rust/src/filemetadata.rs new file mode 100644 index 00000000..bb09a03f --- /dev/null +++ b/rust/src/filemetadata.rs @@ -0,0 +1,174 @@ +use binaryninjacore_sys::{BNFileMetadata, + BNCreateFileMetadata, + BNNewFileReference, + BNFreeFileMetadata, + BNCloseFile, + //BNSetFileMetadataNavigationHandler, + BNIsFileModified, + BNIsAnalysisChanged, + BNMarkFileModified, + BNMarkFileSaved, + BNIsBackedByDatabase, + BNGetFilename, + BNSetFilename, + BNBeginUndoActions, + BNCommitUndoActions, + BNUndo, + BNRedo, + BNGetCurrentView, + BNGetCurrentOffset, + BNNavigate, + BNGetFileViewOfType}; + +use crate::binaryview::BinaryView; + +use crate::rc::*; +use crate::string::*; + +#[derive(PartialEq, Eq, Hash)] +pub struct FileMetadata { + pub(crate) handle: *mut BNFileMetadata, +} + +unsafe impl Send for FileMetadata {} +unsafe impl Sync for FileMetadata {} + +impl FileMetadata { + pub(crate) fn from_raw(handle: *mut BNFileMetadata) -> Self { + Self { handle } + } + + pub fn new() -> Ref<Self> { + unsafe { + Ref::new( + Self { + handle: BNCreateFileMetadata(), + } + ) + } + } + + pub fn with_filename<S: BnStrCompatible>(name: S) -> Ref<Self> { + let ret = FileMetadata::new(); + ret.set_filename(name); + ret + } + + pub fn close(&self) { + unsafe { BNCloseFile(self.handle); } + } + + pub fn filename(&self) -> BnString { + unsafe { + let raw = BNGetFilename(self.handle); + BnString::from_raw(raw) + } + } + + pub fn set_filename<S: BnStrCompatible>(&self, name: S) { + let name = name.as_bytes_with_nul(); + + unsafe { BNSetFilename(self.handle, name.as_ref().as_ptr() as *mut _); } + } + + pub fn is_modified(&self) -> bool { + unsafe { BNIsFileModified(self.handle) } + } + + pub fn mark_modified(&self) { + unsafe { BNMarkFileModified(self.handle); } + } + + pub fn is_analysis_changed(&self) -> bool { + unsafe { BNIsAnalysisChanged(self.handle) } + } + + pub fn mark_saved(&self) { + unsafe { BNMarkFileSaved(self.handle); } + } + + pub fn is_database_backed(&self) -> bool { + unsafe { BNIsBackedByDatabase(self.handle) } + } + + pub fn begin_undo_actions(&self) { + unsafe { BNBeginUndoActions(self.handle); } + } + + pub fn commit_undo_actions(&self) { + unsafe { BNCommitUndoActions(self.handle); } + } + + pub fn undo(&self) { + unsafe { BNUndo(self.handle); } + } + + pub fn redo(&self) { + unsafe { BNRedo(self.handle); } + } + + pub fn current_view(&self) -> BnString { + unsafe { + BnString::from_raw(BNGetCurrentView(self.handle)) + } + } + + pub fn current_offset(&self) -> u64 { + unsafe { BNGetCurrentOffset(self.handle) } + } + + pub fn navigate_to<S: BnStrCompatible>(&self, view: S, offset: u64) -> Result<(), ()> { + let view = view.as_bytes_with_nul(); + + unsafe { + if BNNavigate(self.handle, view.as_ref().as_ptr() as *const _, offset) { + Ok(()) + } else { + Err(()) + } + } + } + + pub fn get_view_of_type<S: BnStrCompatible>(&self, view: S) -> Result<Ref<BinaryView>, ()> { + let view = view.as_bytes_with_nul(); + + unsafe { + let res = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _); + + if res.is_null() { + Err(()) + } else { + Ok(Ref::new(BinaryView::from_raw(res))) + } + } + } +} + +impl ToOwned for FileMetadata { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for FileMetadata { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewFileReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeFileMetadata(handle.handle); + } +} + + +/* +BNCreateDatabase, +BNCreateDatabaseWithProgress, +BNOpenExistingDatabase, +BNOpenExistingDatabaseWithProgress, +*/ + diff --git a/rust/src/function.rs b/rust/src/function.rs new file mode 100644 index 00000000..9a65f837 --- /dev/null +++ b/rust/src/function.rs @@ -0,0 +1,260 @@ +use std::fmt; + +use binaryninjacore_sys::*; + +use crate::architecture::CoreArchitecture; +use crate::binaryview::{BinaryView, BinaryViewExt}; +use crate::platform::Platform; +use crate::symbol::Symbol; +use crate::basicblock::{BasicBlock, BlockContext}; +use crate::types::Type; + +use crate::llil; + +use crate::string::*; +use crate::rc::*; + +pub struct Location { + pub arch: Option<CoreArchitecture>, + pub addr: u64, +} + +impl From<u64> for Location { + fn from(addr: u64) -> Self { + Location { + arch: None, + addr: addr, + } + } +} + +impl From<(CoreArchitecture, u64)> for Location { + fn from(loc: (CoreArchitecture, u64)) -> Self { + Location { + arch: Some(loc.0), + addr: loc.1, + } + } +} + +pub struct NativeBlockIter { + arch: CoreArchitecture, + bv: Ref<BinaryView>, + cur: u64, + end: u64, +} + +impl Iterator for NativeBlockIter { + type Item = u64; + + fn next(&mut self) -> Option<u64> { + let res = self.cur; + + if res >= self.end { + None + } else { + self.bv.get_instruction_len(&self.arch, res).map(|x| { + self.cur += x as u64; + res + }).or_else(|| { + self.cur = self.end; + None + }) + } + } +} + +#[derive(Clone)] +pub struct NativeBlock { + _priv: (), +} + +impl NativeBlock { + pub(crate) fn new() -> Self { + NativeBlock { _priv: () } + } +} + +impl BlockContext for NativeBlock { + type Iter = NativeBlockIter; + type Instruction = u64; + + fn start(&self, block: &BasicBlock<Self>) -> u64 { + block.raw_start() + } + + fn iter(&self, block: &BasicBlock<Self>) -> NativeBlockIter { + NativeBlockIter { + arch: block.arch(), + bv: block.function().view(), + cur: block.raw_start(), + end: block.raw_end(), + } + } +} + +#[derive(PartialEq, Eq, Hash)] +pub struct Function { + handle: *mut BNFunction, +} + +unsafe impl Send for Function {} +unsafe impl Sync for Function {} + +impl Function { + pub(crate) unsafe fn from_raw(handle: *mut BNFunction) -> Self { + Self { handle } + } + + pub fn arch(&self) -> CoreArchitecture { + unsafe { + let arch = BNGetFunctionArchitecture(self.handle); + CoreArchitecture::from_raw(arch) + } + } + + pub fn platform(&self) -> Ref<Platform> { + unsafe { + let plat = BNGetFunctionPlatform(self.handle); + Ref::new(Platform::from_raw(plat)) + } + } + + pub fn view(&self) -> Ref<BinaryView> { + unsafe { + let view = BNGetFunctionData(self.handle); + Ref::new(BinaryView::from_raw(view)) + } + } + + pub fn symbol(&self) -> Ref<Symbol> { + unsafe { + let sym = BNGetFunctionSymbol(self.handle); + Ref::new(Symbol::from_raw(sym)) + } + } + + pub fn start(&self) -> u64 { + unsafe { BNGetFunctionStart(self.handle) } + } + + pub fn comment(&self) -> BnString { + unsafe { BnString::from_raw(BNGetFunctionComment(self.handle)) } + } + + pub fn set_comment<S: BnStrCompatible>(&self, comment: S) { + let raw = comment.as_bytes_with_nul(); + + unsafe { + BNSetFunctionComment(self.handle, raw.as_ref().as_ptr() as *mut _); + } + } + + pub fn comment_at(&self, addr: u64) -> BnString { + unsafe { BnString::from_raw(BNGetCommentForAddress(self.handle, addr)) } + } + + pub fn set_comment_at<S: BnStrCompatible>(&self, addr: u64, comment: S) { + let raw = comment.as_bytes_with_nul(); + + unsafe { + BNSetCommentForAddress(self.handle, addr, raw.as_ref().as_ptr() as *mut _); + } + } + + pub fn basic_blocks(&self) -> Array<BasicBlock<NativeBlock>> { + unsafe { + let mut count = 0; + let blocks = BNGetFunctionBasicBlockList(self.handle, &mut count); + let context = NativeBlock { _priv: () }; + + Array::new(blocks, count, context) + } + } + + pub fn basic_block_containing(&self, arch: &CoreArchitecture, addr: u64) -> Option<Ref<BasicBlock<NativeBlock>>> { + unsafe { + let block = BNGetFunctionBasicBlockAtAddress(self.handle, arch.0, addr); + let context = NativeBlock { _priv: () }; + + if block.is_null() { + return None; + } + + Some(Ref::new(BasicBlock::from_raw(block, context))) + } + } + + pub fn low_level_il(&self) -> Result<Ref<llil::RegularFunction<CoreArchitecture>>, ()> { + unsafe { + let llil = BNGetFunctionLowLevelIL(self.handle); + + if llil.is_null() { + return Err(()); + } + + Ok(Ref::new(llil::RegularFunction::from_raw(self.arch(), llil))) + } + } + + pub fn lifted_il(&self) -> Result<Ref<llil::LiftedFunction<CoreArchitecture>>, ()> { + unsafe { + let llil = BNGetFunctionLiftedIL(self.handle); + + if llil.is_null() { + return Err(()); + } + + Ok(Ref::new(llil::LiftedFunction::from_raw(self.arch(), llil))) + } + } + + pub fn set_user_type(&self, t: &Type) { + unsafe { + BNSetFunctionUserType(self.handle, t.handle); + } + } +} + +impl fmt::Debug for Function { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "<func '{}' ({}) {:x}>", self.symbol().name(), self.platform().name(), self.start()) + } +} + +impl ToOwned for Function { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for Function { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewFunctionReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeFunction(handle.handle); + } +} + +unsafe impl CoreOwnedArrayProvider for Function { + type Raw = *mut BNFunction; + type Context = (); + + unsafe fn free(raw: *mut *mut BNFunction, count: usize, _context: &()) { + BNFreeFunctionList(raw, count); + } +} + +unsafe impl<'a> CoreOwnedArrayWrapper<'a> for Function { + type Wrapped = Guard<'a, Function>; + + unsafe fn wrap_raw(raw: &'a *mut BNFunction, context: &'a ()) -> Guard<'a, Function> { + Guard::new(Function::from_raw(*raw), context) + } +} diff --git a/rust/src/headless.rs b/rust/src/headless.rs new file mode 100644 index 00000000..e6b7c51c --- /dev/null +++ b/rust/src/headless.rs @@ -0,0 +1,68 @@ +use std::env; +use std::mem; +use std::os::raw; +use std::ffi::{OsStr, CString, CStr}; +use std::path::PathBuf; + +#[repr(C)] +struct DlInfo { + dli_fname: *const raw::c_char, + dli_fbase: *mut raw::c_void, + dli_sname: *const raw::c_char, + dli_saddr: *mut raw::c_void +} + +#[cfg(not(target_os = "windows"))] +fn binja_path() -> PathBuf { + use std::os::unix::ffi::OsStrExt; + + if let Ok(p) = env::var("BINJA_DIR") { + return PathBuf::from(p); + } + + extern { + fn dladdr(addr: *mut raw::c_void, info: *mut DlInfo) -> raw::c_int; + } + + unsafe { + let mut info: DlInfo = mem::uninitialized(); + + if dladdr(BNSetBundledPluginDirectory as *mut _, &mut info) == 0 { + panic!("Failed to find libbinaryninjacore path!"); + } + + if info.dli_fname.is_null() { + panic!("Failed to find libbinaryninjacore path!"); + } + + let path = CStr::from_ptr(info.dli_fname); + let path = OsStr::from_bytes(path.to_bytes()); + let mut path = PathBuf::from(path); + + path.pop(); + path + } +} + + +#[cfg(target_os = "windows")] +fn binja_path() -> PathBuf { + PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\") +} + +use binaryninjacore_sys::{BNSetBundledPluginDirectory, + BNInitCorePlugins, + BNInitUserPlugins, + BNInitRepoPlugins}; + +pub fn init() { + unsafe { + let path = binja_path().join("plugins").into_os_string(); + let path = CString::new(path.into_string().unwrap()).unwrap(); + + BNSetBundledPluginDirectory(path.as_ptr()); + BNInitCorePlugins(); + BNInitUserPlugins(); + BNInitRepoPlugins(); + } +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 00000000..68df9948 --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,171 @@ +#[macro_use] +extern crate log; +#[cfg(feature = "rayon")] +extern crate rayon; +extern crate libc; +extern crate binaryninjacore_sys; + +// TODO +// move some options to results +// replace `fn handle` with `AsRef` bounds +// possible values +// arch rework +// cc possible values +// bv reorg +// core fileaccessor (for bv saving) +// headless wrapper for shutdown +// platform cc + +#[macro_use] +mod ffi; + +pub mod architecture; +pub mod basicblock; +pub mod binaryview; +pub mod segment; +pub mod section; +pub mod symbol; +pub mod callingconvention; +pub mod command; +pub mod fileaccessor; +pub mod filemetadata; +pub mod function; +pub mod platform; +pub mod llil; +pub mod types; +pub mod rc; +pub mod headless; +pub mod string; +pub mod settings; + +pub use binaryninjacore_sys::BNEndianness as Endianness; +pub use binaryninjacore_sys::BNBranchType as BranchType; + +pub mod logger { + use std::os::raw::{c_void, c_char}; + + use log; + + use crate::string::BnStr; + + use binaryninjacore_sys::{BNLogListener, BNUpdateLogListeners}; + pub use binaryninjacore_sys::BNLogLevel as Level; + + struct Logger; + static LOGGER: Logger = Logger; + + impl log::Log for Logger { + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true + } + + fn log(&self, record: &log::Record) { + use binaryninjacore_sys::BNLog; + use std::ffi::CString; + use self::Level::*; + use log::Level; + + let level = match record.level() { + Level::Error => ErrorLog, + Level::Warn => WarningLog, + Level::Info => InfoLog, + Level::Debug | + Level::Trace => DebugLog, + }; + + if let Ok(msg) = CString::new(format!("{}", record.args())) { + unsafe { BNLog(level, msg.as_ptr()); } + }; + } + + fn flush(&self) {} + } + + /// Uses BinaryNinja's logging functionality as the sink for + /// Rust's `log` crate. + pub fn init(filter: log::LevelFilter) -> Result<(), log::SetLoggerError> { + log::set_max_level(filter); + log::set_logger(&LOGGER) + } + + pub trait LogListener: 'static + Sync { + fn log(&self, level: Level, msg: &BnStr); + fn level(&self) -> Level; + fn close(&self) {} + } + + pub struct LogGuard<L: LogListener> { + ctxt: *mut L, + } + + impl<L: LogListener> Drop for LogGuard<L> { + fn drop(&mut self) { + use binaryninjacore_sys::BNUnregisterLogListener; + + let mut bn_obj = BNLogListener { + context: self.ctxt as *mut _, + log: Some(cb_log::<L>), + close: Some(cb_close::<L>), + getLogLevel: Some(cb_level::<L>), + }; + + unsafe { + BNUnregisterLogListener(&mut bn_obj); + BNUpdateLogListeners(); + + let _listener = Box::from_raw(self.ctxt); + } + } + } + + pub fn register_listener<L: LogListener>(listener: L) -> LogGuard<L> { + use binaryninjacore_sys::BNRegisterLogListener; + + let raw = Box::into_raw(Box::new(listener)); + let mut bn_obj = BNLogListener { + context: raw as *mut _, + log: Some(cb_log::<L>), + close: Some(cb_close::<L>), + getLogLevel: Some(cb_level::<L>), + }; + + unsafe { + BNRegisterLogListener(&mut bn_obj); + BNUpdateLogListeners(); + } + + LogGuard { + ctxt: raw, + } + } + + extern "C" fn cb_log<L>(ctxt: *mut c_void, level: Level, msg: *const c_char) + where + L: LogListener, + { + ffi_wrap!("LogListener::log", unsafe { + let listener = &*(ctxt as *const L); + listener.log(level, BnStr::from_raw(msg)); + }) + } + + extern "C" fn cb_close<L>(ctxt: *mut c_void) + where + L: LogListener, + { + ffi_wrap!("LogListener::close", unsafe { + let listener = &*(ctxt as *const L); + listener.close(); + }) + } + + extern "C" fn cb_level<L>(ctxt: *mut c_void) -> Level + where + L: LogListener, + { + ffi_wrap!("LogListener::log", unsafe { + let listener = &*(ctxt as *const L); + listener.level() + }) + } +} diff --git a/rust/src/llil/block.rs b/rust/src/llil/block.rs new file mode 100644 index 00000000..543ab670 --- /dev/null +++ b/rust/src/llil/block.rs @@ -0,0 +1,91 @@ +use std::ops::Range; + +use crate::architecture::Architecture; +use crate::basicblock::{BasicBlock, BlockContext}; + +use super::*; + +pub struct BlockIter<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + function: &'func Function<A, M, F>, + range: Range<u64>, +} + +impl<'func, A, M, F> Iterator for BlockIter<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + type Item = Instruction<'func, A, M, F>; + + fn next(&mut self) -> Option<Self::Item> { + self.range.next().map(|i| Instruction { + function: self.function, + instr_idx: i as usize, + }) + } +} + + + +pub struct Block<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub(crate) function: &'func Function<A, M, F>, +} + +impl<'func, A, M, F> fmt::Debug for Block<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "llil_bb {:?}", self.function) + } +} + +impl<'func, A, M, F> BlockContext for Block<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + type Iter = BlockIter<'func, A, M, F>; + type Instruction = Instruction<'func, A, M, F>; + + fn start(&self, block: &BasicBlock<Self>) -> Instruction<'func, A, M, F> { + Instruction { + function: self.function, + instr_idx: block.raw_start() as usize, + } + } + + fn iter(&self, block: &BasicBlock<Self>) -> BlockIter<'func, A, M, F> { + BlockIter { + function: self.function, + range: block.raw_start() .. block.raw_end(), + } + } +} + +impl<'func, A, M, F> Clone for Block<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + fn clone(&self) -> Self { + Block { function: self.function } + } +} + + diff --git a/rust/src/llil/expression.rs b/rust/src/llil/expression.rs new file mode 100644 index 00000000..1e1edda4 --- /dev/null +++ b/rust/src/llil/expression.rs @@ -0,0 +1,816 @@ +use binaryninjacore_sys::BNGetLowLevelILByIndex; +use binaryninjacore_sys::BNLowLevelILInstruction; + +use std::marker::PhantomData; +use std::fmt; + +use super::*; +use super::operation; +use super::operation::Operation; + +use crate::architecture::Architecture; +use crate::architecture::RegisterInfo; + +// used as a marker for Expressions that can produce a value +#[derive(Copy, Clone, Debug)] +pub struct ValueExpr; + +// used as a marker for Expressions that can not produce a value +#[derive(Copy, Clone, Debug)] +pub struct VoidExpr; + +pub trait ExpressionResultType: 'static {} +impl ExpressionResultType for ValueExpr {} +impl ExpressionResultType for VoidExpr {} + +pub struct Expression<'func, A, M, F, R> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + R: ExpressionResultType, +{ + pub(crate) function: &'func Function<A, M, F>, + pub(crate) expr_idx: usize, + + // tag the 'return' type of this expression + pub(crate) _ty: PhantomData<R>, +} + +impl<'func, A, M, F, R> Expression<'func, A, M, F, R> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + R: ExpressionResultType, +{ + pub fn index(&self) -> usize { + self.expr_idx + } +} + +impl<'func, A, M, V> fmt::Debug for Expression<'func, A, M, NonSSA<V>, ValueExpr> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let op_info = self.info(); + write!(f, "<expr {}: {:?}>", self.expr_idx, op_info) + } +} + +fn common_info<'func, A, M, F>(function: &'func Function<A, M, F>, op: BNLowLevelILInstruction) + -> ExprInfo<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + use binaryninjacore_sys::BNLowLevelILOperation::*; + + match op.operation { + LLIL_CONST => ExprInfo::Const(Operation::new(function, op)), + LLIL_CONST_PTR => ExprInfo::ConstPtr(Operation::new(function, op)), + + LLIL_ADD => ExprInfo::Add(Operation::new(function, op)), + LLIL_ADC => ExprInfo::Adc(Operation::new(function, op)), + LLIL_SUB => ExprInfo::Sub(Operation::new(function, op)), + LLIL_SBB => ExprInfo::Sbb(Operation::new(function, op)), + LLIL_AND => ExprInfo::And(Operation::new(function, op)), + LLIL_OR => ExprInfo::Or (Operation::new(function, op)), + LLIL_XOR => ExprInfo::Xor(Operation::new(function, op)), + LLIL_LSL => ExprInfo::Lsl(Operation::new(function, op)), + LLIL_LSR => ExprInfo::Lsr(Operation::new(function, op)), + LLIL_ASR => ExprInfo::Asr(Operation::new(function, op)), + LLIL_ROL => ExprInfo::Rol(Operation::new(function, op)), + LLIL_RLC => ExprInfo::Rlc(Operation::new(function, op)), + LLIL_ROR => ExprInfo::Ror(Operation::new(function, op)), + LLIL_RRC => ExprInfo::Rrc(Operation::new(function, op)), + LLIL_MUL => ExprInfo::Mul(Operation::new(function, op)), + + LLIL_MULU_DP => ExprInfo::MuluDp(Operation::new(function, op)), + LLIL_MULS_DP => ExprInfo::MulsDp(Operation::new(function, op)), + + LLIL_DIVU => ExprInfo::Divu(Operation::new(function, op)), + LLIL_DIVS => ExprInfo::Divs(Operation::new(function, op)), + + LLIL_DIVU_DP => ExprInfo::DivuDp(Operation::new(function, op)), + LLIL_DIVS_DP => ExprInfo::DivsDp(Operation::new(function, op)), + + LLIL_MODU => ExprInfo::Modu(Operation::new(function, op)), + LLIL_MODS => ExprInfo::Mods(Operation::new(function, op)), + + LLIL_MODU_DP => ExprInfo::ModuDp(Operation::new(function, op)), + LLIL_MODS_DP => ExprInfo::ModsDp(Operation::new(function, op)), + + LLIL_NEG => ExprInfo::Neg(Operation::new(function, op)), + LLIL_NOT => ExprInfo::Not(Operation::new(function, op)), + + LLIL_SX => ExprInfo::Sx(Operation::new(function, op)), + LLIL_ZX => ExprInfo::Zx(Operation::new(function, op)), + LLIL_LOW_PART => ExprInfo::LowPart(Operation::new(function, op)), + + LLIL_CMP_E => ExprInfo::CmpE(Operation::new(function, op)), + LLIL_CMP_NE => ExprInfo::CmpNe(Operation::new(function, op)), + LLIL_CMP_SLT => ExprInfo::CmpSlt(Operation::new(function, op)), + LLIL_CMP_ULT => ExprInfo::CmpUlt(Operation::new(function, op)), + LLIL_CMP_SLE => ExprInfo::CmpSle(Operation::new(function, op)), + LLIL_CMP_ULE => ExprInfo::CmpUle(Operation::new(function, op)), + LLIL_CMP_SGE => ExprInfo::CmpSge(Operation::new(function, op)), + LLIL_CMP_UGE => ExprInfo::CmpUge(Operation::new(function, op)), + LLIL_CMP_SGT => ExprInfo::CmpSgt(Operation::new(function, op)), + LLIL_CMP_UGT => ExprInfo::CmpUgt(Operation::new(function, op)), + + LLIL_BOOL_TO_INT => ExprInfo::BoolToInt(Operation::new(function, op)), + + LLIL_UNIMPL => ExprInfo::Unimpl(Operation::new(function, op)), + LLIL_UNIMPL_MEM => ExprInfo::UnimplMem(Operation::new(function, op)), + + // TODO TEST_BIT ADD_OVERFLOW + _ => { + #[cfg(debug_assertions)] + { + error!("Got unexpected operation {:?} in value expr at 0x{:x}", + op.operation, op.address); + } + + ExprInfo::Undef(Operation::new(function, op)) + } + } +} + +use super::VisitorAction; + +macro_rules! visit { + ($f:expr, $($e:expr),*) => { + if let VisitorAction::Halt = $f($($e,)*) { + return VisitorAction::Halt; + } + } +} + +fn common_visit<'func, A, M, F, CB>(info: &ExprInfo<'func, A, M, F>, f: &mut CB) + -> VisitorAction +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + CB: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction, +{ + use self::ExprInfo::*; + + match *info { + CmpE(ref op) | CmpNe(ref op) | + CmpSlt(ref op) | CmpUlt(ref op) | + CmpSle(ref op) | CmpUle(ref op) | + CmpSge(ref op) | CmpUge(ref op) | + CmpSgt(ref op) | CmpUgt(ref op) => { + visit!(f, &op.left()); + visit!(f, &op.right()); + } + + Adc(ref op) | + Sbb(ref op) | + Rlc(ref op) | + Rrc(ref op) => { + visit!(f, &op.left()); + visit!(f, &op.right()); + visit!(f, &op.carry()); + } + + Add(ref op) | + Sub(ref op) | + And(ref op) | + Or (ref op) | + Xor(ref op) | + Lsl(ref op) | + Lsr(ref op) | + Asr(ref op) | + Rol(ref op) | + Ror(ref op) | + Mul(ref op) | + MulsDp(ref op) | + MuluDp(ref op) | + Divu(ref op) | + Divs(ref op) | + Modu(ref op) | + Mods(ref op) => { + visit!(f, &op.left()); + visit!(f, &op.right()); + } + + DivuDp(ref op) | + DivsDp(ref op) | + ModuDp(ref op) | + ModsDp(ref op) => { + visit!(f, &op.high()); + visit!(f, &op.low()); + visit!(f, &op.right()); + } + + Neg(ref op) | + Not(ref op) | + Sx(ref op) | + Zx(ref op) | + LowPart(ref op) | + BoolToInt(ref op) => { + visit!(f, &op.operand()); + } + + UnimplMem(ref op) => { + visit!(f, &op.mem_expr()); + } + + _ => {} + }; + + VisitorAction::Sibling +} + +impl<'func, A, M, V> Expression<'func, A, M, NonSSA<V>, ValueExpr> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub(crate) unsafe fn info_from_op(&self, op: BNLowLevelILInstruction) -> ExprInfo<'func, A, M, NonSSA<V>> { + use binaryninjacore_sys::BNLowLevelILOperation::*; + + match op.operation { + LLIL_LOAD => ExprInfo::Load(Operation::new(self.function, op)), + LLIL_POP => ExprInfo::Pop(Operation::new(self.function, op)), + LLIL_REG => ExprInfo::Reg(Operation::new(self.function, op)), + LLIL_FLAG => ExprInfo::Flag(Operation::new(self.function, op)), + LLIL_FLAG_BIT => ExprInfo::FlagBit(Operation::new(self.function, op)), + LLIL_FLAG_COND => ExprInfo::FlagCond(Operation::new(self.function, op)), // TODO lifted only + LLIL_FLAG_GROUP => ExprInfo::FlagGroup(Operation::new(self.function, op)), // TODO lifted only + _ => common_info(self.function, op), + } + } + + pub fn info(&self) -> ExprInfo<'func, A, M, NonSSA<V>> { + unsafe { + let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx); + self.info_from_op(op) + } + } + + pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction + where + F: FnMut(&Self, &ExprInfo<'func, A, M, NonSSA<V>>) -> VisitorAction, + { + use self::ExprInfo::*; + + let info = self.info(); + + match f(self, &info) { + VisitorAction::Descend => {}, + action => return action, + }; + + match info { + Load(ref op) => visit!(Self::visit_tree, &op.source_mem_expr(), f), + _ => { + let mut fb = |e: &Self| e.visit_tree(f); + visit!(common_visit, &info, &mut fb); + } + }; + + VisitorAction::Sibling + } +} + +impl<'func, A, M> Expression<'func, A, M, SSA, ValueExpr> +where + A: 'func + Architecture, + M: FunctionMutability, +{ + pub(crate) unsafe fn info_from_op(&self, op: BNLowLevelILInstruction) -> ExprInfo<'func, A, M, SSA> { + use binaryninjacore_sys::BNLowLevelILOperation::*; + + match op.operation { + LLIL_LOAD_SSA => ExprInfo::Load(Operation::new(self.function, op)), + LLIL_REG_SSA | + LLIL_REG_SSA_PARTIAL => ExprInfo::Reg(Operation::new(self.function, op)), + LLIL_FLAG_SSA => ExprInfo::Flag(Operation::new(self.function, op)), + LLIL_FLAG_BIT_SSA => ExprInfo::FlagBit(Operation::new(self.function, op)), + _ => common_info(self.function, op), + } + } + + pub fn info(&self) -> ExprInfo<'func, A, M, SSA> { + unsafe { + let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx); + self.info_from_op(op) + } + } + + pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction + where + F: FnMut(&Self, &ExprInfo<'func, A, M, SSA>) -> VisitorAction, + { + use self::ExprInfo::*; + + let info = self.info(); + + match f(self, &info) { + VisitorAction::Descend => {}, + action => return action, + }; + + match info { + // TODO ssa + Load(ref _op) => {} //visit!(Self::visit_tree, &op.source_mem_expr(), f), + _ => { + let mut fb = |e: &Self| e.visit_tree(f); + visit!(common_visit, &info, &mut fb); + } + }; + + VisitorAction::Sibling + } +} + +impl<'func, A, F> Expression<'func, A, Finalized, F, ValueExpr> +where + A: 'func + Architecture, + F: FunctionForm, +{ + // TODO possible values +} + + + +pub enum ExprInfo<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + Load(Operation<'func, A, M, F, operation::Load>), + Pop(Operation<'func, A, M, F, operation::Pop>), + Reg(Operation<'func, A, M, F, operation::Reg>), + Const(Operation<'func, A, M, F, operation::Const>), + ConstPtr(Operation<'func, A, M, F, operation::Const>), + Flag(Operation<'func, A, M, F, operation::Flag>), + FlagBit(Operation<'func, A, M, F, operation::FlagBit>), + + Add(Operation<'func, A, M, F, operation::BinaryOp>), + Adc(Operation<'func, A, M, F, operation::BinaryOpCarry>), + Sub(Operation<'func, A, M, F, operation::BinaryOp>), + Sbb(Operation<'func, A, M, F, operation::BinaryOpCarry>), + And(Operation<'func, A, M, F, operation::BinaryOp>), + Or (Operation<'func, A, M, F, operation::BinaryOp>), + Xor(Operation<'func, A, M, F, operation::BinaryOp>), + Lsl(Operation<'func, A, M, F, operation::BinaryOp>), + Lsr(Operation<'func, A, M, F, operation::BinaryOp>), + Asr(Operation<'func, A, M, F, operation::BinaryOp>), + Rol(Operation<'func, A, M, F, operation::BinaryOp>), + Rlc(Operation<'func, A, M, F, operation::BinaryOpCarry>), + Ror(Operation<'func, A, M, F, operation::BinaryOp>), + Rrc(Operation<'func, A, M, F, operation::BinaryOpCarry>), + Mul(Operation<'func, A, M, F, operation::BinaryOp>), + + MulsDp(Operation<'func, A, M, F, operation::BinaryOp>), + MuluDp(Operation<'func, A, M, F, operation::BinaryOp>), + + Divu(Operation<'func, A, M, F, operation::BinaryOp>), + Divs(Operation<'func, A, M, F, operation::BinaryOp>), + + DivuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), + DivsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), + + Modu(Operation<'func, A, M, F, operation::BinaryOp>), + Mods(Operation<'func, A, M, F, operation::BinaryOp>), + + ModuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), + ModsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), + + Neg(Operation<'func, A, M, F, operation::UnaryOp>), + Not(Operation<'func, A, M, F, operation::UnaryOp>), + Sx(Operation<'func, A, M, F, operation::UnaryOp>), + Zx(Operation<'func, A, M, F, operation::UnaryOp>), + LowPart(Operation<'func, A, M, F, operation::UnaryOp>), + + FlagCond(Operation<'func, A, M, F, operation::FlagCond>), + FlagGroup(Operation<'func, A, M, F, operation::FlagGroup>), + + CmpE(Operation<'func, A, M, F, operation::Condition>), + CmpNe(Operation<'func, A, M, F, operation::Condition>), + CmpSlt(Operation<'func, A, M, F, operation::Condition>), + CmpUlt(Operation<'func, A, M, F, operation::Condition>), + CmpSle(Operation<'func, A, M, F, operation::Condition>), + CmpUle(Operation<'func, A, M, F, operation::Condition>), + CmpSge(Operation<'func, A, M, F, operation::Condition>), + CmpUge(Operation<'func, A, M, F, operation::Condition>), + CmpSgt(Operation<'func, A, M, F, operation::Condition>), + CmpUgt(Operation<'func, A, M, F, operation::Condition>), + + //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + + BoolToInt(Operation<'func, A, M, F, operation::UnaryOp>), + + // TODO ADD_OVERFLOW + + Unimpl(Operation<'func, A, M, F, operation::NoArgs>), + UnimplMem(Operation<'func, A, M, F, operation::UnimplMem>), + + Undef(Operation<'func, A, M, F, operation::NoArgs>), +} + +impl<'func, A, M, F> ExprInfo<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + /// Returns the size of the result of this expression + /// + /// If the expression is malformed or is `Unimpl` there + /// is no meaningful size associated with the result. + pub fn size(&self) -> Option<usize> { + use self::ExprInfo::*; + + match *self { + Undef(..) | + Unimpl(..) => None, + + FlagCond(..) | FlagGroup(..) | + CmpE(..) | CmpNe(..) | + CmpSlt(..) | CmpUlt(..) | + CmpSle(..) | CmpUle(..) | + CmpSge(..) | CmpUge(..) | + CmpSgt(..) | CmpUgt(..) => Some(0), + + _ => Some(self.raw_struct().size), + + //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + } + } + + pub fn address(&self) -> u64 { + self.raw_struct().address + } + + /// Determines if the expressions represent the same operation + /// + /// It does not examine the operands for equality. + pub fn is_same_op_as(&self, other: &Self) -> bool { + use self::ExprInfo::*; + + match (self, other) { + (&Reg(..), &Reg(..)) => true, + _ => self.raw_struct().operation == other.raw_struct().operation, + } + } + + pub fn as_cmp_op(&self) -> Option<&Operation<'func, A, M, F, operation::Condition>> { + use self::ExprInfo::*; + + match *self { + CmpE (ref op) | CmpNe (ref op) | + CmpSlt(ref op) | CmpUlt(ref op) | + CmpSle(ref op) | CmpUle(ref op) | + CmpSge(ref op) | CmpUge(ref op) | + CmpSgt(ref op) | CmpUgt(ref op) => Some(op), + _ => None, + } + } + + pub fn as_binary_op(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOp>> { + use self::ExprInfo::*; + + match *self { + Add(ref op) | + Sub(ref op) | + And(ref op) | + Or (ref op) | + Xor(ref op) | + Lsl(ref op) | + Lsr(ref op) | + Asr(ref op) | + Rol(ref op) | + Ror(ref op) | + Mul(ref op) | + MulsDp(ref op) | + MuluDp(ref op) | + Divu(ref op) | + Divs(ref op) | + Modu(ref op) | + Mods(ref op) => Some(op), + _ => None, + } + } + + pub fn as_binary_op_carry(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOpCarry>> { + use self::ExprInfo::*; + + match *self { + Adc(ref op) | + Sbb(ref op) | + Rlc(ref op) | + Rrc(ref op) => Some(op), + _ => None, + } + } + + pub fn as_double_prec_div_op(&self) -> Option<&Operation<'func, A, M, F, operation::DoublePrecDivOp>> { + use self::ExprInfo::*; + + match *self { + DivuDp(ref op) | + DivsDp(ref op) | + ModuDp(ref op) | + ModsDp(ref op) => Some(op), + _ => None, + } + } + + pub fn as_unary_op(&self) -> Option<&Operation<'func, A, M, F, operation::UnaryOp>> { + use self::ExprInfo::*; + + match *self { + Neg(ref op) | + Not(ref op) | + Sx(ref op) | + Zx(ref op) | + LowPart(ref op) | + BoolToInt(ref op) => Some(op), + _ => None, + } + } + + pub(crate) fn raw_struct(&self) -> &BNLowLevelILInstruction { + use self::ExprInfo::*; + + match *self { + Undef(ref op) => &op.op, + + Unimpl(ref op) => &op.op, + + FlagCond(ref op) => &op.op, + FlagGroup(ref op) => &op.op, + + CmpE (ref op) | CmpNe (ref op) | + CmpSlt(ref op) | CmpUlt(ref op) | + CmpSle(ref op) | CmpUle(ref op) | + CmpSge(ref op) | CmpUge(ref op) | + CmpSgt(ref op) | CmpUgt(ref op) => &op.op, + + Load(ref op) => &op.op, + + Pop(ref op) => &op.op, + + Reg(ref op) => &op.op, + + Flag(ref op) => &op.op, + + FlagBit(ref op) => &op.op, + + Const(ref op) | + ConstPtr(ref op) => &op.op, + + Adc(ref op) | + Sbb(ref op) | + Rlc(ref op) | + Rrc(ref op) => &op.op, + + Add(ref op) | + Sub(ref op) | + And(ref op) | + Or (ref op) | + Xor(ref op) | + Lsl(ref op) | + Lsr(ref op) | + Asr(ref op) | + Rol(ref op) | + Ror(ref op) | + Mul(ref op) | + MulsDp(ref op) | + MuluDp(ref op) | + Divu(ref op) | + Divs(ref op) | + Modu(ref op) | + Mods(ref op) => &op.op, + + DivuDp(ref op) | + DivsDp(ref op) | + ModuDp(ref op) | + ModsDp(ref op) => &op.op, + + Neg(ref op) | + Not(ref op) | + Sx(ref op) | + Zx(ref op) | + LowPart(ref op) | + BoolToInt(ref op) => &op.op, + + UnimplMem(ref op) => &op.op, + + //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + } + } +} + +impl<'func, A> ExprInfo<'func, A, Mutable, NonSSA<LiftedNonSSA>> +where + A: 'func + Architecture, +{ + + pub fn flag_write(&self) -> Option<A::FlagWrite> { + use self::ExprInfo::*; + + match *self { + Undef(ref op) => None, + + Unimpl(ref op) => None, + + FlagCond(ref op) => None, + FlagGroup(ref op) => None, + + CmpE (ref op) | CmpNe (ref op) | + CmpSlt(ref op) | CmpUlt(ref op) | + CmpSle(ref op) | CmpUle(ref op) | + CmpSge(ref op) | CmpUge(ref op) | + CmpSgt(ref op) | CmpUgt(ref op) => None, + + Load(ref op) => op.flag_write(), + + Pop(ref op) => op.flag_write(), + + Reg(ref op) => op.flag_write(), + + Flag(ref op) => op.flag_write(), + + FlagBit(ref op) => op.flag_write(), + + Const(ref op) | + ConstPtr(ref op) => op.flag_write(), + + Adc(ref op) | + Sbb(ref op) | + Rlc(ref op) | + Rrc(ref op) => op.flag_write(), + + Add(ref op) | + Sub(ref op) | + And(ref op) | + Or (ref op) | + Xor(ref op) | + Lsl(ref op) | + Lsr(ref op) | + Asr(ref op) | + Rol(ref op) | + Ror(ref op) | + Mul(ref op) | + MulsDp(ref op) | + MuluDp(ref op) | + Divu(ref op) | + Divs(ref op) | + Modu(ref op) | + Mods(ref op) => op.flag_write(), + + DivuDp(ref op) | + DivsDp(ref op) | + ModuDp(ref op) | + ModsDp(ref op) => op.flag_write(), + + Neg(ref op) | + Not(ref op) | + Sx(ref op) | + Zx(ref op) | + LowPart(ref op) | + BoolToInt(ref op) => op.flag_write(), + + UnimplMem(ref op) => op.flag_write(), + + //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + } + } +} + +impl<'func, A, M, V> fmt::Debug for ExprInfo<'func, A, M, NonSSA<V>> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::ExprInfo::*; + + match *self { + Undef(..) => f.write_str("undefined"), + + Unimpl(..) => f.write_str("unimplemented"), + + FlagCond(..) => f.write_str("some_flag_cond"), + FlagGroup(..) => f.write_str("some_flag_group"), + + CmpE(ref op) | CmpNe(ref op) | + CmpSlt(ref op) | CmpUlt(ref op) | + CmpSle(ref op) | CmpUle(ref op) | + CmpSge(ref op) | CmpUge(ref op) | + CmpSgt(ref op) | CmpUgt(ref op) => { + let left = op.left(); + let right = op.right(); + + write!(f, "{:?}({}, {:?}, {:?})", op.op.operation, op.size(), left, right) + } + + Load(ref op) => { + let source = op.source_mem_expr(); + let size = op.size(); + + write!(f, "[{:?}].{}", source, size) + } + + Pop(ref op) => write!(f, "pop.{}", op.size()), + + Reg(ref op) => { + let reg = op.source_reg(); + let size = op.size(); + + let size = match reg { + Register::Temp(_) => Some(size), + Register::ArchReg(ref r) if r.info().size() != size => Some(size), + _ => None + }; + + match size { + Some(s) => write!(f, "{:?}.{}", reg, s), + _ => write!(f, "{:?}", reg), + } + } + + Flag(ref _op) => write!(f, "flag"), // TODO + + FlagBit(ref _op) => write!(f, "flag_bit"), // TODO + + Const(ref op) | + ConstPtr(ref op) => write!(f, "0x{:x}", op.value()), + + Adc(ref op) | + Sbb(ref op) | + Rlc(ref op) | + Rrc(ref op) => { + let left = op.left(); + let right = op.right(); + let carry = op.carry(); + + write!(f, "{:?}({}, {:?}, {:?}, carry: {:?})", + op.op.operation, op.size(), left, right, carry) + } + + Add(ref op) | + Sub(ref op) | + And(ref op) | + Or (ref op) | + Xor(ref op) | + Lsl(ref op) | + Lsr(ref op) | + Asr(ref op) | + Rol(ref op) | + Ror(ref op) | + Mul(ref op) | + MulsDp(ref op) | + MuluDp(ref op) | + Divu(ref op) | + Divs(ref op) | + Modu(ref op) | + Mods(ref op) => { + let left = op.left(); + let right = op.right(); + + write!(f, "{:?}({}, {:?}, {:?})", + op.op.operation, op.size(), left, right) + } + + DivuDp(ref op) | + DivsDp(ref op) | + ModuDp(ref op) | + ModsDp(ref op) => { + let high = op.high(); + let low = op.low(); + let right = op.right(); + + write!(f, "{:?}({}, {:?}:{:?},{:?})", + op.op.operation, op.size(), high, low, right) + } + + Neg(ref op) | + Not(ref op) | + Sx(ref op) | + Zx(ref op) | + LowPart(ref op) | + BoolToInt(ref op) => { + write!(f, "{:?}({}, {:?})", op.op.operation, op.size(), op.operand()) + } + + UnimplMem(ref op) => write!(f, "unimplemented_mem({:?})", op.mem_expr()), + + //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + } + } +} diff --git a/rust/src/llil/function.rs b/rust/src/llil/function.rs new file mode 100644 index 00000000..ac50e72c --- /dev/null +++ b/rust/src/llil/function.rs @@ -0,0 +1,198 @@ +use binaryninjacore_sys::BNLowLevelILFunction; +use binaryninjacore_sys::BNNewLowLevelILFunctionReference; +use binaryninjacore_sys::BNFreeLowLevelILFunction; + +use std::borrow::Borrow; +use std::marker::PhantomData; + +use crate::basicblock::BasicBlock; +use crate::rc::*; + +use super::*; + +#[derive(Copy, Clone, Debug)] +pub struct Mutable; +#[derive(Copy, Clone, Debug)] +pub struct Finalized; + +pub trait FunctionMutability: 'static {} +impl FunctionMutability for Mutable {} +impl FunctionMutability for Finalized {} + + +#[derive(Copy, Clone, Debug)] +pub struct LiftedNonSSA; +#[derive(Copy, Clone, Debug)] +pub struct RegularNonSSA; + +pub trait NonSSAVariant: 'static {} +impl NonSSAVariant for LiftedNonSSA {} +impl NonSSAVariant for RegularNonSSA {} + +#[derive(Copy, Clone, Debug)] +pub struct SSA; +#[derive(Copy, Clone, Debug)] +pub struct NonSSA<V: NonSSAVariant>(V); + +pub trait FunctionForm: 'static {} +impl FunctionForm for SSA {} +impl<V: NonSSAVariant> FunctionForm for NonSSA<V> {} + + +pub struct Function<A: Architecture, M: FunctionMutability, F: FunctionForm> { + pub(crate) borrower: A::Handle, + pub(crate) handle: *mut BNLowLevelILFunction, + _arch: PhantomData<*mut A>, + _mutability: PhantomData<M>, + _form: PhantomData<F>, +} + +unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Send for Function<A, M, F> {} +unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Sync for Function<A, M, F> {} + +impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Eq for Function<A, M, F> {} +impl<A: Architecture, M: FunctionMutability, F: FunctionForm> PartialEq for Function<A, M, F> { + fn eq(&self, rhs: &Self) -> bool { + self.handle == rhs.handle + } +} + +use std::hash::{Hash, Hasher}; +impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Hash for Function<A, M, F> { + fn hash<H: Hasher>(&self, state: &mut H) { + self.handle.hash(state); + } +} + +impl<'func, A, M, F> Function<A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm +{ + pub(crate) unsafe fn from_raw(borrower: A::Handle, handle: *mut BNLowLevelILFunction) -> Self { + debug_assert!(!handle.is_null()); + + Self { + borrower, + handle, + _arch: PhantomData, + _mutability: PhantomData, + _form: PhantomData, + } + } + + pub(crate) fn arch(&self) -> &A { + self.borrower.borrow() + } + + pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<Instruction<A, M, F>> { + use binaryninjacore_sys::BNLowLevelILGetInstructionStart; + use binaryninjacore_sys::BNGetLowLevelILInstructionCount; + + let loc: Location = loc.into(); + let arch_handle = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); + + unsafe { + let instr_idx = BNLowLevelILGetInstructionStart(self.handle, arch_handle.0, loc.addr); + + if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) { + None + } else { + Some(Instruction { + function: self, + instr_idx: instr_idx, + }) + } + } + } + + pub fn instruction_from_idx(&self, instr_idx: usize) -> Instruction<A, M, F> { + unsafe { + use binaryninjacore_sys::BNGetLowLevelILInstructionCount; + if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) { + panic!("instruction index {} out of bounds", instr_idx); + } + + Instruction { + function: self, + instr_idx: instr_idx, + } + } + } + + pub fn instruction_count(&self) -> usize { + unsafe { + use binaryninjacore_sys::BNGetLowLevelILInstructionCount; + BNGetLowLevelILInstructionCount(self.handle) + } + } + +} + +// LLIL basic blocks are not available until the function object +// is finalized, so ensure we can't try requesting basic blocks +// during lifting +impl<'func, A, F> Function<A, Finalized, F> +where + A: 'func + Architecture, + F: FunctionForm +{ + pub fn basic_blocks(&self) -> Array<BasicBlock<LowLevelBlock<A, Finalized, F>>> { + use binaryninjacore_sys::BNGetLowLevelILBasicBlockList; + + unsafe { + let mut count = 0; + let blocks = BNGetLowLevelILBasicBlockList(self.handle, &mut count); + let context = LowLevelBlock { function: self }; + + Array::new(blocks, count, context) + } + } +} + +impl<'func, A, M, F> ToOwned for Function<A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm +{ + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + + +unsafe impl<'func, A, M, F> RefCountable for Function<A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm +{ + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + borrower: handle.borrower.clone(), + handle: BNNewLowLevelILFunctionReference(handle.handle), + _arch: PhantomData, + _mutability: PhantomData, + _form: PhantomData, + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeLowLevelILFunction(handle.handle); + } +} + +impl<'func, A, M, F> fmt::Debug for Function<A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "<llil func handle {:p}>", self.handle) + } +} diff --git a/rust/src/llil/instruction.rs b/rust/src/llil/instruction.rs new file mode 100644 index 00000000..4d1554df --- /dev/null +++ b/rust/src/llil/instruction.rs @@ -0,0 +1,175 @@ +use binaryninjacore_sys::BNGetLowLevelILByIndex; +use binaryninjacore_sys::BNGetLowLevelILIndexForInstruction; +use binaryninjacore_sys::BNLowLevelILInstruction; + +use std::marker::PhantomData; + +use super::*; +use super::operation; +use super::operation::Operation; + +use crate::architecture::Architecture; + +pub struct Instruction<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub(crate) function: &'func Function<A, M, F>, + pub(crate) instr_idx: usize, +} + +fn common_info<'func, A, M, F>(function: &'func Function<A, M, F>, op: BNLowLevelILInstruction) + -> Option<InstrInfo<'func, A, M, F>> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + use binaryninjacore_sys::BNLowLevelILOperation::*; + + match op.operation { + LLIL_NOP => InstrInfo::Nop(Operation::new(function, op)).into(), + LLIL_JUMP => InstrInfo::Jump(Operation::new(function, op)).into(), + LLIL_JUMP_TO => InstrInfo::JumpTo(Operation::new(function, op)).into(), + LLIL_RET => InstrInfo::Ret(Operation::new(function, op)).into(), + LLIL_NORET => InstrInfo::NoRet(Operation::new(function, op)).into(), + LLIL_IF => InstrInfo::If(Operation::new(function, op)).into(), + LLIL_GOTO => InstrInfo::Goto(Operation::new(function, op)).into(), + LLIL_BP => InstrInfo::Bp(Operation::new(function, op)).into(), + LLIL_TRAP => InstrInfo::Trap(Operation::new(function, op)).into(), + LLIL_UNDEF => InstrInfo::Undef(Operation::new(function, op)).into(), + _ => None, + } +} + +use super::VisitorAction; + +macro_rules! visit { + ($f:expr, $($e:expr),*) => { + if let VisitorAction::Halt = $f($($e,)*) { + return VisitorAction::Halt; + } + } +} + +fn common_visit<'func, A, M, F, CB>(info: &InstrInfo<'func, A, M, F>, f: &mut CB) + -> VisitorAction +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + CB: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction, +{ + use self::InstrInfo::*; + + match *info { + Jump(ref op) => visit!(f, &op.target()), + JumpTo(ref op) => visit!(f, &op.target()), + Ret(ref op) => visit!(f, &op.target()), + If(ref op) => visit!(f, &op.condition()), + Value(ref e, _) => visit!(f, e), + _ => {}, + }; + + VisitorAction::Sibling +} + +impl<'func, A, M, V> Instruction<'func, A, M, NonSSA<V>> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn info(&self) -> InstrInfo<'func, A, M, NonSSA<V>> { + use binaryninjacore_sys::BNLowLevelILOperation::*; + + let expr_idx = unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.instr_idx) }; + let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, expr_idx) }; + + match op.operation { + LLIL_SET_REG => InstrInfo::SetReg(Operation::new(self.function, op)), + LLIL_SET_REG_SPLIT => InstrInfo::SetRegSplit(Operation::new(self.function, op)), + LLIL_SET_FLAG => InstrInfo::SetFlag(Operation::new(self.function, op)), + LLIL_STORE => InstrInfo::Store(Operation::new(self.function, op)), + LLIL_PUSH => InstrInfo::Push(Operation::new(self.function, op)), + LLIL_CALL | + LLIL_CALL_STACK_ADJUST => InstrInfo::Call(Operation::new(self.function, op)), + LLIL_SYSCALL => InstrInfo::Syscall(Operation::new(self.function, op)), + _ => { + common_info(self.function, op).unwrap_or_else(|| { + // Hopefully this is a bare value. If it isn't (expression + // from wrong function form or similar) it won't really cause + // any problems as it'll come back as undefined when queried. + let expr = Expression { + function: self.function, + expr_idx: expr_idx, + _ty: PhantomData, + }; + + let info = unsafe { expr.info_from_op(op) }; + + InstrInfo::Value(expr, info) + }) + } + } + } + + pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction + where + F: FnMut(&Expression<'func, A, M, NonSSA<V>, ValueExpr>, &ExprInfo<'func, A, M, NonSSA<V>>) -> VisitorAction, + { + use self::InstrInfo::*; + let info = self.info(); + + let fb = &mut |e: &Expression<'func, A, M, NonSSA<V>, ValueExpr>| e.visit_tree(f); + + match info { + SetReg(ref op) => visit!(fb, &op.source_expr()), + SetRegSplit(ref op) => visit!(fb, &op.source_expr()), + SetFlag(ref op) => visit!(fb, &op.source_expr()), + Store(ref op) => { + visit!(fb, &op.dest_mem_expr()); + visit!(fb, &op.source_expr()); + } + Push(ref op) => visit!(fb, &op.operand()), + Call(ref op) => visit!(fb, &op.target()), + _ => visit!(common_visit, &info, fb), + } + + VisitorAction::Sibling + } +} + +pub enum InstrInfo<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + Nop(Operation<'func, A, M, F, operation::NoArgs>), + SetReg(Operation<'func, A, M, F, operation::SetReg>), + SetRegSplit(Operation<'func, A, M, F, operation::SetRegSplit>), + SetFlag(Operation<'func, A, M, F, operation::SetFlag>), + Store(Operation<'func, A, M, F, operation::Store>), + Push(Operation<'func, A, M, F, operation::UnaryOp>), // TODO needs a real op + + Jump(Operation<'func, A, M, F, operation::Jump>), + JumpTo(Operation<'func, A, M, F, operation::JumpTo>), + + Call(Operation<'func, A, M, F, operation::Call>), + + Ret(Operation<'func, A, M, F, operation::Ret>), + NoRet(Operation<'func, A, M, F, operation::NoArgs>), + + If(Operation<'func, A, M, F, operation::If>), + Goto(Operation<'func, A, M, F, operation::Goto>), + + Syscall(Operation<'func, A, M, F, operation::Syscall>), + Bp(Operation<'func, A, M, F, operation::NoArgs>), + Trap(Operation<'func, A, M, F, operation::Trap>), + Undef(Operation<'func, A, M, F, operation::NoArgs>), + + Value(Expression<'func, A, M, F, ValueExpr>, ExprInfo<'func, A, M, F>), +} diff --git a/rust/src/llil/lifting.rs b/rust/src/llil/lifting.rs new file mode 100644 index 00000000..e50eaaf1 --- /dev/null +++ b/rust/src/llil/lifting.rs @@ -0,0 +1,1292 @@ +use std::marker::PhantomData; +use std::mem; + +use crate::architecture::Register as ArchReg; +use crate::architecture::{FlagWrite, Flag, FlagClass, FlagGroup, FlagRole, FlagCondition}; +use crate::architecture::Architecture; + + +use super::*; + +pub trait Liftable<'func, A: 'func + Architecture> { + type Result: ExpressionResultType; + + fn lift(il: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self) + -> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>; +} + +pub trait LiftableWithSize<'func, A: 'func + Architecture>: Liftable<'func, A, Result=ValueExpr> { + fn lift_with_size(il: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, size: usize) + -> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>; +} + +use binaryninjacore_sys::BNRegisterOrConstant; + +#[derive(Copy, Clone)] +pub enum RegisterOrConstant<R: ArchReg> { + Register(usize, Register<R>), + Constant(usize, u64), +} + +impl<R: ArchReg> RegisterOrConstant<R> { + pub(crate) fn into_api(self) -> BNRegisterOrConstant { + match self { + RegisterOrConstant::Register(_, r) => BNRegisterOrConstant { + constant: false, + reg: r.id(), + value: 0, + }, + RegisterOrConstant::Constant(_, value) => BNRegisterOrConstant { + constant: true, + reg: 0, + value: value, + } + } + } +} + +// TODO flesh way out +#[derive(Copy, Clone)] +pub enum FlagWriteOp<R: ArchReg> { + SetReg(usize, RegisterOrConstant<R>), + SetRegSplit(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + + Sub(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Add(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + + Load(usize, RegisterOrConstant<R>), + + Push(usize, RegisterOrConstant<R>), + Neg(usize, RegisterOrConstant<R>), + Not(usize, RegisterOrConstant<R>), + Sx(usize, RegisterOrConstant<R>), + Zx(usize, RegisterOrConstant<R>), + LowPart(usize, RegisterOrConstant<R>), + BoolToInt(usize, RegisterOrConstant<R>), + FloatToInt(usize, RegisterOrConstant<R>), + + Store(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + + And(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Or(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Xor(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Lsl(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Lsr(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Asr(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Rol(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Ror(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Mul(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + MuluDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + MulsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Divu(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Divs(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Modu(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Mods(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + DivuDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + DivsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + ModuDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + ModsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + + TestBit(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + AddOverflow(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + + Adc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>), + Sbb(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>), + Rlc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>), + Rrc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>), + + Pop(usize), + + // TODO: floating point stuff, llil comparison ops that set flags, intrinsics +} + +impl<R: ArchReg> FlagWriteOp<R> { + pub(crate) fn from_op<A>(arch: &A, size: usize, op: BNLowLevelILOperation, operands: &[BNRegisterOrConstant]) + -> Option<Self> + where + A: Architecture<Register=R>, + R: ArchReg<InfoType=A::RegisterInfo>, + { + use binaryninjacore_sys::BNLowLevelILOperation::*; + use self::FlagWriteOp::*; + + fn build_op<A, R>(arch: &A, size: usize, operand: &BNRegisterOrConstant) -> RegisterOrConstant<R> + where + A: Architecture<Register=R>, + R: ArchReg<InfoType=A::RegisterInfo>, + { + if operand.constant { + RegisterOrConstant::Constant(size, operand.value) + } else { + let il_reg = if 0x8000_0000 & operand.reg == 0 { + Register::ArchReg(arch.register_from_id(operand.reg).unwrap()) + } else { + Register::Temp(operand.reg) + }; + + RegisterOrConstant::Register(size, il_reg) + } + } + + macro_rules! op { + ($x:ident, $($ops:expr),*) => { + ( $x(size, $( build_op(arch, size, &operands[$ops]), )* ) ) + }; + } + + Some(match (operands.len(), op) { + (1, LLIL_SET_REG) => op!(SetReg, 0), + (2, LLIL_SET_REG_SPLIT) => op!(SetRegSplit, 0, 1), + + (2, LLIL_SUB) => op!(Sub, 0, 1), + (2, LLIL_ADD) => op!(Add, 0, 1), + + (1, LLIL_LOAD) => op!(Load, 0), + + (1, LLIL_PUSH) => op!(Push, 0), + (1, LLIL_NEG) => op!(Neg, 0), + (1, LLIL_NOT) => op!(Not, 0), + (1, LLIL_SX) => op!(Sx, 0), + (1, LLIL_ZX) => op!(Zx, 0), + (1, LLIL_LOW_PART) => op!(LowPart, 0), + (1, LLIL_BOOL_TO_INT) => op!(BoolToInt, 0), + (1, LLIL_FLOAT_TO_INT) => op!(FloatToInt, 0), + + (2, LLIL_STORE) => op!(Store, 0, 1), + + (2, LLIL_AND) => op!(And, 0, 1), + (2, LLIL_OR) => op!(Or, 0, 1), + (2, LLIL_XOR) => op!(Xor, 0, 1), + (2, LLIL_LSL) => op!(Lsl, 0, 1), + (2, LLIL_LSR) => op!(Lsr, 0, 1), + (2, LLIL_ASR) => op!(Asr, 0, 1), + (2, LLIL_ROL) => op!(Rol, 0, 1), + (2, LLIL_ROR) => op!(Ror, 0, 1), + (2, LLIL_MUL) => op!(Mul, 0, 1), + (2, LLIL_MULU_DP) => op!(MuluDp, 0, 1), + (2, LLIL_MULS_DP) => op!(MulsDp, 0, 1), + (2, LLIL_DIVU) => op!(Divu, 0, 1), + (2, LLIL_DIVS) => op!(Divs, 0, 1), + (2, LLIL_MODU) => op!(Modu, 0, 1), + (2, LLIL_MODS) => op!(Mods, 0, 1), + (2, LLIL_DIVU_DP) => op!(DivuDp, 0, 1), + (2, LLIL_DIVS_DP) => op!(DivsDp, 0, 1), + (2, LLIL_MODU_DP) => op!(ModuDp, 0, 1), + (2, LLIL_MODS_DP) => op!(ModsDp, 0, 1), + + (2, LLIL_TEST_BIT) => op!(TestBit, 0, 1), + (2, LLIL_ADD_OVERFLOW) => op!(AddOverflow, 0, 1), + + (3, LLIL_ADC) => op!(Adc, 0, 1, 2), + (3, LLIL_SBB) => op!(Sbb, 0, 1, 2), + (3, LLIL_RLC) => op!(Rlc, 0, 1, 2), + (3, LLIL_RRC) => op!(Rrc, 0, 1, 2), + + (0, LLIL_POP) => op!(Pop, ), + + _ => return None, + }) + } + + pub(crate) fn size_and_op(&self) -> (usize, BNLowLevelILOperation) { + use binaryninjacore_sys::BNLowLevelILOperation::*; + use self::FlagWriteOp::*; + + match *self { + SetReg(size, ..) => (size, LLIL_SET_REG), + SetRegSplit(size, ..) => (size, LLIL_SET_REG_SPLIT), + + Sub(size, ..) => (size, LLIL_SUB), + Add(size, ..) => (size, LLIL_ADD), + + Load(size, ..) => (size, LLIL_LOAD), + + Push(size, ..) => (size, LLIL_PUSH), + Neg(size, ..) => (size, LLIL_NEG), + Not(size, ..) => (size, LLIL_NOT), + Sx(size, ..) => (size, LLIL_SX), + Zx(size, ..) => (size, LLIL_ZX), + LowPart(size, ..) => (size, LLIL_LOW_PART), + BoolToInt(size, ..) => (size, LLIL_BOOL_TO_INT), + FloatToInt(size, ..) => (size, LLIL_FLOAT_TO_INT), + + Store(size, ..) => (size, LLIL_STORE), + + And(size, ..) => (size, LLIL_AND), + Or(size, ..) => (size, LLIL_OR), + Xor(size, ..) => (size, LLIL_XOR), + Lsl(size, ..) => (size, LLIL_LSL), + Lsr(size, ..) => (size, LLIL_LSR), + Asr(size, ..) => (size, LLIL_ASR), + Rol(size, ..) => (size, LLIL_ROL), + Ror(size, ..) => (size, LLIL_ROR), + Mul(size, ..) => (size, LLIL_MUL), + MuluDp(size, ..) => (size, LLIL_MULU_DP), + MulsDp(size, ..) => (size, LLIL_MULS_DP), + Divu(size, ..) => (size, LLIL_DIVU), + Divs(size, ..) => (size, LLIL_DIVS), + Modu(size, ..) => (size, LLIL_MODU), + Mods(size, ..) => (size, LLIL_MODS), + DivuDp(size, ..) => (size, LLIL_DIVU_DP), + DivsDp(size, ..) => (size, LLIL_DIVS_DP), + ModuDp(size, ..) => (size, LLIL_MODU_DP), + ModsDp(size, ..) => (size, LLIL_MODS_DP), + + TestBit(size, ..) => (size, LLIL_TEST_BIT), + AddOverflow(size, ..) => (size, LLIL_ADD_OVERFLOW), + + Adc(size, ..) => (size, LLIL_ADC), + Sbb(size, ..) => (size, LLIL_SBB), + Rlc(size, ..) => (size, LLIL_RLC), + Rrc(size, ..) => (size, LLIL_RRC), + + Pop(size) => (size, LLIL_POP), + } + } + + pub(crate) fn api_operands(&self) -> (usize, [BNRegisterOrConstant; 5]) { + use self::FlagWriteOp::*; + + let mut operands: [BNRegisterOrConstant; 5] = unsafe { mem::zeroed() }; + + let count = match *self { + Pop(_) => 0, + + SetReg(_, op0) | + Load(_, op0) | + Push(_, op0) | + Neg(_, op0) | + Not(_, op0) | + Sx(_, op0) | + Zx(_, op0) | + LowPart(_, op0) | + BoolToInt(_, op0) | + FloatToInt(_, op0) => { + operands[0] = op0.into_api(); + 1 + } + + SetRegSplit(_, op0, op1) | + Sub(_, op0, op1) | + Add(_, op0, op1) | + Store(_, op0, op1) | + And(_, op0, op1) | + Or(_, op0, op1) | + Xor(_, op0, op1) | + Lsl(_, op0, op1) | + Lsr(_, op0, op1) | + Asr(_, op0, op1) | + Rol(_, op0, op1) | + Ror(_, op0, op1) | + Mul(_, op0, op1) | + MuluDp(_, op0, op1) | + MulsDp(_, op0, op1) | + Divu(_, op0, op1) | + Divs(_, op0, op1) | + Modu(_, op0, op1) | + Mods(_, op0, op1) | + DivuDp(_, op0, op1) | + DivsDp(_, op0, op1) | + ModuDp(_, op0, op1) | + ModsDp(_, op0, op1) | + TestBit(_, op0, op1) | + AddOverflow(_, op0, op1) => { + operands[0] = op0.into_api(); + operands[1] = op1.into_api(); + 2 + } + + Adc(_, op0, op1, op2) | + Sbb(_, op0, op1, op2) | + Rlc(_, op0, op1, op2) | + Rrc(_, op0, op1, op2) => { + operands[0] = op0.into_api(); + operands[1] = op1.into_api(); + operands[2] = op2.into_api(); + 3 + } + }; + + (count, operands) + } +} + + +pub fn get_default_flag_write_llil<'func, A>(arch: &A, role: FlagRole, op: FlagWriteOp<A::Register>, il: &'func Lifter<A>) + -> LiftedExpr<'func, A> +where + A: 'func + Architecture +{ + let (size, operation) = op.size_and_op(); + let (count, operands) = op.api_operands(); + + let expr_idx = unsafe { + use binaryninjacore_sys::BNGetDefaultArchitectureFlagWriteLowLevelIL; + BNGetDefaultArchitectureFlagWriteLowLevelIL(arch.as_ref().0, operation, size, role, + operands.as_ptr() as *mut _, count, il.handle) + }; + + Expression { + function: il, + expr_idx: expr_idx, + _ty: PhantomData, + } +} + +pub fn get_default_flag_cond_llil<'func, A>(arch: &A, cond: FlagCondition, class: Option<A::FlagClass>, il: &'func Lifter<A>) + -> LiftedExpr<'func, A> +where + A: 'func + Architecture +{ + use binaryninjacore_sys::BNGetDefaultArchitectureFlagConditionLowLevelIL; + + let handle = arch.as_ref(); + let class_id = class.map(|c| c.id()).unwrap_or(0); + + unsafe { + let expr_idx = BNGetDefaultArchitectureFlagConditionLowLevelIL(handle.0, cond, class_id, il.handle); + + Expression { + function: il, + expr_idx: expr_idx, + _ty: PhantomData, + } + } +} + +macro_rules! prim_int_lifter { + ($x:ty) => { + impl<'a, A: 'a + Architecture> Liftable<'a, A> for $x { + type Result = ValueExpr; + + fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, val: Self) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + il.const_int(mem::size_of::<Self>(), val as i64 as u64) + } + } + + impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> for $x { + fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, val: Self, size: usize) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + let raw = val as i64; + + #[cfg(debug_assertions)] + { + let is_safe = match raw.overflowing_shr(size as u32 * 8) { + (_, true) => true, + (res, false) => [-1, 0].contains(&res), + }; + + if !is_safe { + error!("il @ {:x} attempted to lift constant 0x{:x} as {} byte expr (won't fit!)", + il.current_address(), val, size); + } + } + + il.const_int(size, raw as u64) + } + } + } +} + +prim_int_lifter!(i8); +prim_int_lifter!(i16); +prim_int_lifter!(i32); +prim_int_lifter!(i64); + +prim_int_lifter!(u8); +prim_int_lifter!(u16); +prim_int_lifter!(u32); +prim_int_lifter!(u64); + +impl<'a, R: ArchReg, A: 'a + Architecture> Liftable<'a, A> for Register<R> + where R: Liftable<'a, A, Result=ValueExpr> + Into<Register<R>> +{ + type Result = ValueExpr; + + fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + match reg { + Register::ArchReg(r) => R::lift(il, r), + Register::Temp(t) => il.reg(il.arch().default_integer_size(), Register::Temp(t)), + } + } +} + +impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for Register<R> + where R: LiftableWithSize<'a, A> + Into<Register<R>> +{ + fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self, size: usize) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + match reg { + Register::ArchReg(r) => R::lift_with_size(il, r, size), + Register::Temp(t) => il.reg(size, Register::Temp(t)), + } + } +} + + +impl<'a, R: ArchReg, A: 'a + Architecture> Liftable<'a, A> for RegisterOrConstant<R> + where R: LiftableWithSize<'a, A, Result=ValueExpr> + Into<Register<R>> +{ + type Result = ValueExpr; + + fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + match reg { + RegisterOrConstant::Register(size, r) => Register::<R>::lift_with_size(il, r, size), + RegisterOrConstant::Constant(size, value) => u64::lift_with_size(il, value, size), + } + } +} + +impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for RegisterOrConstant<R> + where R: LiftableWithSize<'a, A> + Into<Register<R>> +{ + fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self, size: usize) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + // TODO ensure requested size is compatible with size of this constant + match reg { + RegisterOrConstant::Register(_, r) => Register::<R>::lift_with_size(il, r, size), + RegisterOrConstant::Constant(_, value) => u64::lift_with_size(il, value, size), + } + } +} + + +impl<'a, A, R> Liftable<'a, A> for Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> +where + A: 'a + Architecture, + R: ExpressionResultType, +{ + type Result = R; + + fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + debug_assert!(expr.function.handle == il.handle); + expr + } +} + +impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> for Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> { + fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, _size: usize) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + #[cfg(debug_assertions)] + { + if let Some(expr_size) = expr.info().size() { + if expr_size != _size { + warn!("il @ {:x} attempted to lift {} byte expression as {} bytes", + il.current_address(), expr_size, _size); + } + } + } + + Liftable::lift(il, expr) + } +} + + +impl<'func, A, R> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, R> +where + A: 'func + Architecture, + R: ExpressionResultType, +{ + pub fn with_source_operand(self, op: u32) -> Self { + use binaryninjacore_sys::BNLowLevelILSetExprSourceOperand; + + unsafe { + BNLowLevelILSetExprSourceOperand(self.function.handle, self.expr_idx, op) + } + + self + } + + pub fn append(self) { + let il = self.function; + il.instruction(self); + } +} + + +use binaryninjacore_sys::BNLowLevelILOperation; +pub struct ExpressionBuilder<'func, A, R> +where + A: 'func + Architecture, + R: ExpressionResultType, +{ + function: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>, + op: BNLowLevelILOperation, + size: usize, + flags: u32, + op1: u64, + op2: u64, + op3: u64, + op4: u64, + _ty: PhantomData<R>, +} + +impl<'a, A, R> ExpressionBuilder<'a, A, R> +where + A: 'a + Architecture, + R: ExpressionResultType, +{ + pub fn with_flag_write(mut self, flag_write: A::FlagWrite) -> Self { + // TODO verify valid id + self.flags = flag_write.id(); + self + } + + pub fn into_expr(self) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> { + self.into() + } + + pub fn with_source_operand(self, op: u32) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> { + let expr = self.into_expr(); + expr.with_source_operand(op) + } + + pub fn append(self) { + let expr = self.into_expr(); + let il = expr.function; + + il.instruction(expr); + } +} + +impl<'a, A, R> Into<Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>> for ExpressionBuilder<'a, A, R> +where + A: 'a + Architecture, + R: ExpressionResultType, +{ + fn into(self) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> { + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.function.handle, + self.op, self.size, self.flags, + self.op1, self.op2, self.op3, self.op4) + }; + + Expression { + function: self.function, + expr_idx: expr_idx, + _ty: PhantomData, + } + } +} + +impl<'a, A, R> Liftable<'a, A> for ExpressionBuilder<'a, A, R> +where + A: 'a + Architecture, + R: ExpressionResultType, +{ + type Result = R; + + fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + debug_assert!(expr.function.handle == il.handle); + + expr.into() + } +} + +impl<'a, A> LiftableWithSize<'a, A> for ExpressionBuilder<'a, A, ValueExpr> +where + A: 'a + Architecture, +{ + fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, _size: usize) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + #[cfg(debug_assertions)] + { + use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_UNIMPL, LLIL_UNIMPL_MEM}; + + if expr.size != _size && ![LLIL_UNIMPL, LLIL_UNIMPL_MEM].contains(&expr.op) { + warn!("il @ {:x} attempted to lift {} byte expression builder as {} bytes", + il.current_address(), expr.size, _size); + } + } + + Liftable::lift(il, expr) + } +} + + +macro_rules! no_arg_lifter { + ($name:ident, $op:ident, $result:ty) => { + pub fn $name(&self) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, $result> { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, $op, + 0, 0, 0, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + } +} + +macro_rules! sized_no_arg_lifter { + ($name:ident, $op:ident, $result:ty) => { + pub fn $name(&self, size: usize) -> ExpressionBuilder<A, $result> + { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + + ExpressionBuilder { + function: self, + op: $op, + size: size, + flags: 0, + op1: 0, + op2: 0, + op3: 0, + op4: 0, + _ty: PhantomData, + } + } + } +} + +macro_rules! unsized_unary_op_lifter { + ($name:ident, $op:ident, $result:ty) => { + pub fn $name<'a, E>(&'a self, expr: E) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, $result> + where + E: Liftable<'a, A, Result=ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr = E::lift(self, expr); + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, $op, 0, 0, + expr.expr_idx as u64, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + } +} + +macro_rules! sized_unary_op_lifter { + ($name:ident, $op:ident, $result:ty) => { + pub fn $name<'a, E>(&'a self, size: usize, expr: E) + -> ExpressionBuilder<'a, A, $result> + where + E: LiftableWithSize<'a, A> + { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + + let expr = E::lift_with_size(self, expr, size); + + ExpressionBuilder { + function: self, + op: $op, + size: size, + flags: 0, + op1: expr.expr_idx as u64, + op2: 0, + op3: 0, + op4: 0, + _ty: PhantomData, + } + } + } +} + +macro_rules! size_changing_unary_op_lifter { + ($name:ident, $op:ident, $result:ty) => { + pub fn $name<'a, E>(&'a self, size: usize, expr: E) + -> ExpressionBuilder<'a, A, $result> + where + E: LiftableWithSize<'a, A> + { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + + let expr = E::lift(self, expr); + + ExpressionBuilder { + function: self, + op: $op, + size: size, + flags: 0, + op1: expr.expr_idx as u64, + op2: 0, + op3: 0, + op4: 0, + _ty: PhantomData, + } + } + } +} + +macro_rules! binary_op_lifter { + ($name:ident, $op:ident) => { + pub fn $name<'a, L, R>(&'a self, size: usize, left: L, right: R) + -> ExpressionBuilder<'a, A, ValueExpr> + where + L: LiftableWithSize<'a, A>, + R: LiftableWithSize<'a, A> + { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + + let left = L::lift_with_size(self, left, size); + let right = R::lift_with_size(self, right, size); + + ExpressionBuilder { + function: self, + op: $op, + size: size, + flags: 0, + op1: left.expr_idx as u64, + op2: right.expr_idx as u64, + op3: 0, + op4: 0, + _ty: PhantomData, + } + } + } +} + +macro_rules! binary_op_carry_lifter { + ($name:ident, $op:ident) => { + pub fn $name<'a, L, R, C>(&'a self, size: usize, left: L, right: R, carry: C) + -> ExpressionBuilder<'a, A, ValueExpr> + where + L: LiftableWithSize<'a, A>, + R: LiftableWithSize<'a, A>, + C: LiftableWithSize<'a, A>, + { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + + let left = L::lift_with_size(self, left, size); + let right = R::lift_with_size(self, right, size); + let carry = C::lift_with_size(self, carry, 1); // TODO 0? + + ExpressionBuilder { + function: self, + op: $op, + size: size, + flags: 0, + op1: left.expr_idx as u64, + op2: right.expr_idx as u64, + op3: carry.expr_idx as u64, + op4: 0, + _ty: PhantomData, + } + } + } +} + +impl<A> Function<A, Mutable, NonSSA<LiftedNonSSA>> +where + A: Architecture, +{ + pub fn expression<'a, E: Liftable<'a, A>>(&'a self, expr: E) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, E::Result> + { + E::lift(self, expr) + } + + pub fn instruction<'a, E: Liftable<'a, A>>(&'a self, expr: E) + { + let expr = self.expression(expr); + + unsafe { + use binaryninjacore_sys::BNLowLevelILAddInstruction; + BNLowLevelILAddInstruction(self.handle, expr.expr_idx); + } + } + + + pub unsafe fn replace_expression<'a, E: Liftable<'a, A>>( + &'a self, + replaced_expr_index: usize, + replacement: E) + { + unsafe { + use binaryninjacore_sys::BNReplaceLowLevelILExpr; + use binaryninjacore_sys::BNGetLowLevelILExprCount; + + if replaced_expr_index >= BNGetLowLevelILExprCount(self.handle) { + panic!("bad expr idx used: {} exceeds function bounds", replaced_expr_index); + } + + let expr = self.expression(replacement); + BNReplaceLowLevelILExpr(self.handle, replaced_expr_index, expr.expr_idx); + } + } + + pub fn const_int(&self, size: usize, val: u64) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_CONST, size, 0, + val, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn const_ptr_sized(&self, size: usize, val: u64) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST_PTR; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_CONST_PTR, size, 0, + val, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn const_ptr(&self, val: u64) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + self.const_ptr_sized(self.arch().address_size(), val) + } + + pub fn trap(&self, val: u64) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_TRAP; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_TRAP, 0, 0, + val, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + no_arg_lifter!(unimplemented, LLIL_UNIMPL, ValueExpr); + no_arg_lifter!(undefined, LLIL_UNDEF, VoidExpr); + no_arg_lifter!(nop, LLIL_NOP, VoidExpr); + + no_arg_lifter!(no_ret, LLIL_NORET, VoidExpr); + no_arg_lifter!(syscall, LLIL_SYSCALL, VoidExpr); + no_arg_lifter!(bp, LLIL_BP, VoidExpr); + + unsized_unary_op_lifter!(call, LLIL_CALL, VoidExpr); + unsized_unary_op_lifter!(ret, LLIL_RET, VoidExpr); + unsized_unary_op_lifter!(jump, LLIL_JUMP, VoidExpr); + // JumpTo TODO + + pub fn if_expr<'a: 'b, 'b, C>(&'a self, cond: C, t: &'b Label, f: &'b Label) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> + where + C: Liftable<'b, A, Result=ValueExpr>, + { + use binaryninjacore_sys::BNLowLevelILIf; + + let cond = C::lift(self, cond); + + let expr_idx = unsafe { + BNLowLevelILIf(self.handle, cond.expr_idx as u64, + &t.0 as *const _ as *mut _, + &f.0 as *const _ as *mut _) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn goto<'a: 'b, 'b>(&'a self, l: &'b Label) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> + { + use binaryninjacore_sys::BNLowLevelILGoto; + + let expr_idx = unsafe { + BNLowLevelILGoto(self.handle, &l.0 as *const _ as *mut _) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn reg<R: Into<Register<A::Register>>>(&self, size: usize, reg: R) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + // TODO verify valid id + let reg = match reg.into() { + Register::ArchReg(r) => r.id(), + Register::Temp(r) => 0x8000_0000 | r, + }; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_REG, size, 0, + reg as u64, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn set_reg<'a, R, E>(&'a self, size: usize, dest_reg: R, expr: E) + -> ExpressionBuilder<'a, A, VoidExpr> + where + R: Into<Register<A::Register>>, + E: LiftableWithSize<'a, A> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG; + + // TODO verify valid id + let dest_reg = match dest_reg.into() { + Register::ArchReg(r) => r.id(), + Register::Temp(r) => 0x8000_0000 | r, + }; + + let expr = E::lift_with_size(self, expr, size); + + ExpressionBuilder { + function: self, + op: LLIL_SET_REG, + size: size, + flags: 0, + op1: dest_reg as u64, + op2: expr.expr_idx as u64, + op3: 0, + op4: 0, + _ty: PhantomData, + } + } + + pub fn set_reg_split<'a, H, L, E>(&'a self, size: usize, hi_reg: H, lo_reg: L, expr: E) + -> ExpressionBuilder<'a, A, VoidExpr> + where + H: Into<Register<A::Register>>, + L: Into<Register<A::Register>>, + E: LiftableWithSize<'a, A> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG_SPLIT; + + // TODO verify valid id + let hi_reg = match hi_reg.into() { + Register::ArchReg(r) => r.id(), + Register::Temp(r) => 0x8000_0000 | r, + }; + + // TODO verify valid id + let lo_reg = match lo_reg.into() { + Register::ArchReg(r) => r.id(), + Register::Temp(r) => 0x8000_0000 | r, + }; + + let expr = E::lift_with_size(self, expr, size); + + ExpressionBuilder { + function: self, + op: LLIL_SET_REG_SPLIT, + size: size, + flags: 0, + op1: hi_reg as u64, + op2: lo_reg as u64, + op3: expr.expr_idx as u64, + op4: 0, + _ty: PhantomData, + } + } + + pub fn flag(&self, flag: A::Flag) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + // TODO verify valid id + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_FLAG, 0, 0, + flag.id() as u64, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn flag_cond(&self, cond: FlagCondition) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_COND; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + // TODO verify valid id + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_FLAG_COND, 0, 0, + cond as u64, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn flag_group(&self, group: A::FlagGroup) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_GROUP; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + // TODO verify valid id + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_FLAG_GROUP, 0, 0, + group.id() as u64, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn set_flag<'a, E>(&'a self, dest_flag: A::Flag, expr: E) + -> ExpressionBuilder<'a, A, VoidExpr> + where + E: LiftableWithSize<'a, A> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_FLAG; + + // TODO verify valid id + + let expr = E::lift_with_size(self, expr, 0); + + ExpressionBuilder { + function: self, + op: LLIL_SET_FLAG, + size: 0, + flags: 0, + op1: dest_flag.id() as u64, + op2: expr.expr_idx as u64, + op3: 0, + op4: 0, + _ty: PhantomData, + } + } + + /* + * TODO + FlagBit(usize, Flag<A>, u64), + */ + + pub fn load<'a, E>(&'a self, size: usize, source_mem: E) + -> ExpressionBuilder<'a, A, ValueExpr> + where + E: Liftable<'a, A, Result=ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_LOAD; + + let expr = E::lift(self, source_mem); + + ExpressionBuilder { + function: self, + op: LLIL_LOAD, + size: size, + flags: 0, + op1: expr.expr_idx as u64, + op2: 0, + op3: 0, + op4: 0, + _ty: PhantomData, + } + } + + pub fn store<'a, D, V>(&'a self, size: usize, dest_mem: D, value: V) + -> ExpressionBuilder<'a, A, VoidExpr> + where + D: Liftable<'a, A, Result=ValueExpr>, + V: LiftableWithSize<'a, A> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_STORE; + + let dest_mem = D::lift(self, dest_mem); + let value = V::lift_with_size(self, value, size); + + ExpressionBuilder { + function: self, + op: LLIL_STORE, + size: size, + flags: 0, + op1: dest_mem.expr_idx as u64, + op2: value.expr_idx as u64, + op3: 0, + op4: 0, + _ty: PhantomData, + } + } + + sized_unary_op_lifter!(push, LLIL_PUSH, VoidExpr); + sized_no_arg_lifter!(pop, LLIL_POP, ValueExpr); + + size_changing_unary_op_lifter!(unimplemented_mem, LLIL_UNIMPL_MEM, ValueExpr); + + sized_unary_op_lifter!(neg, LLIL_NEG, ValueExpr); + sized_unary_op_lifter!(not, LLIL_NOT, ValueExpr); + + size_changing_unary_op_lifter!(sx, LLIL_SX, ValueExpr); + size_changing_unary_op_lifter!(zx, LLIL_ZX, ValueExpr); + size_changing_unary_op_lifter!(low_part, LLIL_LOW_PART, ValueExpr); + + binary_op_lifter!(add, LLIL_ADD); + binary_op_lifter!(add_overflow, LLIL_ADD_OVERFLOW); + binary_op_lifter!(sub, LLIL_SUB); + binary_op_lifter!(and, LLIL_AND); + binary_op_lifter!(or, LLIL_OR); + binary_op_lifter!(xor, LLIL_XOR); + binary_op_lifter!(lsl, LLIL_LSL); + binary_op_lifter!(lsr, LLIL_LSR); + binary_op_lifter!(asr, LLIL_ASR); + + binary_op_lifter!(rol, LLIL_ROL); + binary_op_lifter!(rlc, LLIL_RLC); + binary_op_lifter!(ror, LLIL_ROR); + binary_op_lifter!(rrc, LLIL_RRC); + binary_op_lifter!(mul, LLIL_MUL); + binary_op_lifter!(muls_dp, LLIL_MULS_DP); + binary_op_lifter!(mulu_dp, LLIL_MULU_DP); + binary_op_lifter!(divs, LLIL_DIVS); + binary_op_lifter!(divu, LLIL_DIVU); + binary_op_lifter!(mods, LLIL_MODS); + binary_op_lifter!(modu, LLIL_MODU); + + binary_op_carry_lifter!(adc, LLIL_ADC); + binary_op_carry_lifter!(sbb, LLIL_SBB); + + + /* + DivsDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>), + DivuDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>), + ModsDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>), + ModuDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>), + */ + + // FlagCond(u32), // TODO + + binary_op_lifter!(cmp_e, LLIL_CMP_E); + binary_op_lifter!(cmp_ne, LLIL_CMP_NE); + binary_op_lifter!(cmp_slt, LLIL_CMP_SLT); + binary_op_lifter!(cmp_ult, LLIL_CMP_ULT); + binary_op_lifter!(cmp_sle, LLIL_CMP_SLE); + binary_op_lifter!(cmp_ule, LLIL_CMP_ULE); + binary_op_lifter!(cmp_sge, LLIL_CMP_SGE); + binary_op_lifter!(cmp_uge, LLIL_CMP_UGE); + binary_op_lifter!(cmp_sgt, LLIL_CMP_SGT); + binary_op_lifter!(cmp_ugt, LLIL_CMP_UGT); + binary_op_lifter!(test_bit, LLIL_TEST_BIT); + + // TODO no flags + size_changing_unary_op_lifter!(bool_to_int, LLIL_BOOL_TO_INT, ValueExpr); + + pub fn current_address(&self) -> u64 { + use binaryninjacore_sys::BNLowLevelILGetCurrentAddress; + unsafe { + BNLowLevelILGetCurrentAddress(self.handle) + } + } + + pub fn set_current_address<L: Into<Location>>(&self, loc: L) { + use binaryninjacore_sys::BNLowLevelILSetCurrentAddress; + + let loc: Location = loc.into(); + let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); + + unsafe { BNLowLevelILSetCurrentAddress(self.handle, arch.0, loc.addr); } + } + + pub fn label_for_address<L: Into<Location>>(&self, loc: L) -> Option<&Label> { + use binaryninjacore_sys::BNGetLowLevelILLabelForAddress; + + let loc: Location = loc.into(); + let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); + + let res = unsafe { + BNGetLowLevelILLabelForAddress(self.handle, arch.0, loc.addr) + }; + + if res.is_null() { + None + } else { + Some(unsafe { &*(res as *mut Label) }) + } + } + + pub fn mark_label(&self, label: &mut Label) { + use binaryninjacore_sys::BNLowLevelILMarkLabel; + + unsafe { + BNLowLevelILMarkLabel(self.handle, &mut label.0 as *mut _); + } + } +} + +use binaryninjacore_sys::BNLowLevelILLabel; + +#[repr(C)] +pub struct Label(BNLowLevelILLabel); +impl Label { + pub fn new() -> Self { + use binaryninjacore_sys::BNLowLevelILInitLabel; + + unsafe { + let mut res = Label(mem::uninitialized()); + BNLowLevelILInitLabel(&mut res.0 as *mut _); + res + } + } +} + + diff --git a/rust/src/llil/mod.rs b/rust/src/llil/mod.rs new file mode 100644 index 00000000..c3b1b350 --- /dev/null +++ b/rust/src/llil/mod.rs @@ -0,0 +1,80 @@ +use std::fmt; + +// TODO provide some way to forbid emitting register reads for certain registers +// also writing for certain registers (e.g. zero register must prohibit il.set_reg and il.reg +// (replace with nop or const(0) respectively) +// requirements on load/store memory address sizes? +// can reg/set_reg be used with sizes that differ from what is in BNRegisterInfo? + +use crate::architecture::Register as ArchReg; +use crate::architecture::Architecture; +use crate::function::Location; + +mod function; +mod instruction; +mod expression; +mod lifting; +mod block; +pub mod operation; + +pub use self::function::*; +pub use self::instruction::*; +pub use self::expression::*; +pub use self::lifting::{Liftable, LiftableWithSize, Label, ExpressionBuilder, FlagWriteOp, RegisterOrConstant}; +pub use self::lifting::get_default_flag_write_llil; +pub use self::lifting::get_default_flag_cond_llil; + +pub use self::block::Block as LowLevelBlock; +pub use self::block::BlockIter as LowLevelBlockIter; + +pub type Lifter<Arch> = Function<Arch, Mutable, NonSSA<LiftedNonSSA>>; +pub type LiftedFunction<Arch> = Function<Arch, Finalized, NonSSA<LiftedNonSSA>>; +pub type LiftedExpr<'a, Arch> = Expression<'a, Arch, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>; +pub type RegularFunction<Arch> = Function<Arch, Finalized, NonSSA<RegularNonSSA>>; +pub type SSAFunction<Arch> = Function<Arch, Finalized, SSA>; + +#[derive(Copy, Clone)] +pub enum Register<R: ArchReg> { + ArchReg(R), + Temp(u32), +} + +impl<R: ArchReg> Register<R> { + fn id(&self) -> u32 { + match *self { + Register::ArchReg(ref r) => r.id(), + Register::Temp(id) => 0x8000_0000 | id, + } + } +} + +impl<R: ArchReg> fmt::Debug for Register<R> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Register::ArchReg(ref r) => write!(f, "{}", r.name().as_ref()), + Register::Temp(id) => write!(f, "temp{}", id), + } + } +} + +#[derive(Copy, Clone, Debug)] +pub enum SSARegister<R: ArchReg> { + Full(Register<R>, u32), // no such thing as partial access to a temp register, I think + Partial(R, u32, R), // partial accesses only possible for arch registers, I think +} + +impl<R: ArchReg> SSARegister<R> { + pub fn version(&self) -> u32 { + match *self { + SSARegister::Full(_, ver) | + SSARegister::Partial(_, ver, _) => ver + } + } +} + +pub enum VisitorAction { + Descend, + Sibling, + Halt, +} + diff --git a/rust/src/llil/operation.rs b/rust/src/llil/operation.rs new file mode 100644 index 00000000..81d72d57 --- /dev/null +++ b/rust/src/llil/operation.rs @@ -0,0 +1,765 @@ +use binaryninjacore_sys::BNLowLevelILInstruction; + +use std::marker::PhantomData; +use std::mem; + +use super::*; + +pub struct Operation<'func, A, M, F, O> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + O: OperationArguments, +{ + pub(crate) function: &'func Function<A, M, F>, + pub(crate) op: BNLowLevelILInstruction, + _args: PhantomData<O>, +} + +impl<'func, A, M, F, O> Operation<'func, A, M, F, O> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + O: OperationArguments, +{ + pub(crate) fn new(function: &'func Function<A, M, F>, op: BNLowLevelILInstruction) -> Self { + Self { + function: function, + op: op, + _args: PhantomData, + } + } + + pub fn address(&self) -> u64 { + self.op.address + } +} + +impl<'func, A, M, O> Operation<'func, A, M, NonSSA<LiftedNonSSA>, O> +where + A: 'func + Architecture, + M: FunctionMutability, + O: OperationArguments, +{ + pub fn flag_write(&self) -> Option<A::FlagWrite> { + match self.op.flags { + 0 => None, + id => self.function.arch().flag_write_from_id(id) + } + } +} + +// LLIL_NOP, LLIL_NORET, LLIL_BP, LLIL_UNDEF, LLIL_UNIMPL +pub struct NoArgs; + + + +// LLIL_POP +pub struct Pop; + +impl<'func, A, M, F> Operation<'func, A, M, F, Pop> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } +} + + + + +// LLIL_SYSCALL, LLIL_SYSCALL_SSA +pub struct Syscall; + + + + +// LLIL_SET_REG, LLIL_SET_REG_SSA, LLIL_SET_REG_PARTIAL_SSA +pub struct SetReg; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetReg> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_reg(&self) -> Register<A::Register> { + let raw_id = self.op.operands[0] as u32; + + if raw_id >= 0x8000_0000 { + Register::Temp(raw_id & 0x7fff_ffff) + } else { + self.function.arch().register_from_id(raw_id) + .map(Register::ArchReg) + .unwrap_or_else(|| { + error!("got garbage register from LLIL_SET_REG @ 0x{:x}", + self.op.address); + + Register::Temp(0) + }) + } + } + + pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } +} + + +// LLIL_SET_REG_SPLIT, LLIL_SET_REG_SPLIT_SSA +pub struct SetRegSplit; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetRegSplit> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_reg_high(&self) -> Register<A::Register> { + let raw_id = self.op.operands[0] as u32; + + if raw_id >= 0x8000_0000 { + Register::Temp(raw_id & 0x7fff_ffff) + } else { + self.function.arch().register_from_id(raw_id) + .map(Register::ArchReg) + .unwrap_or_else(|| { + error!("got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}", + self.op.address); + + Register::Temp(0) + }) + } + } + + pub fn dest_reg_low(&self) -> Register<A::Register> { + let raw_id = self.op.operands[1] as u32; + + if raw_id >= 0x8000_0000 { + Register::Temp(raw_id & 0x7fff_ffff) + } else { + self.function.arch().register_from_id(raw_id) + .map(Register::ArchReg) + .unwrap_or_else(|| { + error!("got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}", + self.op.address); + + Register::Temp(0) + }) + } + } + + pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[2] as usize, + _ty: PhantomData, + } + } +} + + + + +// LLIL_SET_FLAG, LLIL_SET_FLAG_SSA +pub struct SetFlag; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetFlag> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } +} + + +// LLIL_LOAD, LLIL_LOAD_SSA +pub struct Load; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Load> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn source_mem_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_STORE, LLIL_STORE_SSA +pub struct Store; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Store> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_mem_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_REG, LLIL_REG_SSA, LLIL_REG_SSA_PARTIAL +pub struct Reg; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Reg> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn source_reg(&self) -> Register<A::Register> { + let raw_id = self.op.operands[0] as u32; + + if raw_id >= 0x8000_0000 { + Register::Temp(raw_id & 0x7fff_ffff) + } else { + self.function.arch().register_from_id(raw_id) + .map(Register::ArchReg) + .unwrap_or_else(|| { + error!("got garbage register from LLIL_REG @ 0x{:x}", + self.op.address); + + Register::Temp(0) + }) + } + } +} + + + +// LLIL_FLAG, LLIL_FLAG_SSA +pub struct Flag; + + + + +// LLIL_FLAG_BIT, LLIL_FLAG_BIT_SSA +pub struct FlagBit; + + + +// LLIL_JUMP +pub struct Jump; + +impl<'func, A, M, F> Operation<'func, A, M, F, Jump> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_JUMP_TO +pub struct JumpTo; + +impl<'func, A, M, F> Operation<'func, A, M, F, JumpTo> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + // TODO target list +} + + + +// LLIL_CALL, LLIL_CALL_SSA +pub struct Call; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Call> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn target(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn stack_adjust(&self) -> Option<u64> { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CALL_STACK_ADJUST; + + if self.op.operation == LLIL_CALL_STACK_ADJUST { + Some(self.op.operands[1]) + } else { + None + } + } +} + + + +// LLIL_RET +pub struct Ret; + +impl<'func, A, M, F> Operation<'func, A, M, F, Ret> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_IF +pub struct If; + +impl<'func, A, M, F> Operation<'func, A, M, F, If> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn condition(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn true_target(&self) -> Instruction<'func, A, M, F> { + Instruction { + function: self.function, + instr_idx: self.op.operands[1] as usize, + } + } + + pub fn false_target(&self) -> Instruction<'func, A, M, F> { + Instruction { + function: self.function, + instr_idx: self.op.operands[2] as usize, + } + } +} + + + +// LLIL_GOTO +pub struct Goto; + +impl<'func, A, M, F> Operation<'func, A, M, F, Goto> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn target(&self) -> Instruction<'func, A, M, F> { + Instruction { + function: self.function, + instr_idx: self.op.operands[0] as usize, + } + } +} + + + +// LLIL_FLAG_COND +pub struct FlagCond; + + + +// LLIL_FLAG_GROUP +pub struct FlagGroup; + +impl<'func, A, M> Operation<'func, A, M, NonSSA<LiftedNonSSA>, FlagGroup> +where + A: 'func + Architecture, + M: FunctionMutability, +{ + pub fn flag_group(&self) -> A::FlagGroup { + let id = self.op.operands[0] as u32; + self.function.arch().flag_group_from_id(id).unwrap() + } +} + + + +// LLIL_TRAP +pub struct Trap; + +impl<'func, A, M, F> Operation<'func, A, M, F, Trap> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn vector(&self) -> u64 { + self.op.operands[0] + } +} + + + +// LLIL_REG_PHI +pub struct RegPhi; + + + +// LLIL_FLAG_PHI +pub struct FlagPhi; + + + +// LLIL_MEM_PHI +pub struct MemPhi; + + + +// LLIL_CONST, LLIL_CONST_PTR +pub struct Const; + +impl<'func, A, M, F> Operation<'func, A, M, F, Const> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn value(&self) -> u64 { + #[cfg(debug_assertions)] + { + let raw = self.op.operands[0] as i64; + + let is_safe = match raw.overflowing_shr(self.op.size as u32 * 8) { + (_, true) => true, + (res, false) => [-1, 0].contains(&res), + }; + + if !is_safe { + error!("il expr @ {:x} contains constant 0x{:x} as {} byte value (doesn't fit!)", + self.op.address, self.op.operands[0], self.op.size); + } + } + + let mut mask = -1i64 as u64; + + if self.op.size < mem::size_of::<u64>() { + mask <<= self.op.size * 8; + mask = !mask; + } + + self.op.operands[0] & mask + } +} + + + +// LLIL_ADD, LLIL_SUB, LLIL_AND, LLIL_OR +// LLIL_XOR, LLIL_LSL, LLIL_LSR, LLIL_ASR +// LLIL_ROL, LLIL_ROR, LLIL_MUL, LLIL_MULU_DP, +// LLIL_MULS_DP, LLIL_DIVU, LLIL_DIVS, LLIL_MODU, +// LLIL_MODS +pub struct BinaryOp; + +impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOp> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_ADC, LLIL_SBB, LLIL_RLC, LLIL_RRC +pub struct BinaryOpCarry; + +impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOpCarry> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } + + pub fn carry(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[2] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_DIVS_DP, LLIL_DIVU_DP, LLIL_MODU_DP, LLIL_MODS_DP +pub struct DoublePrecDivOp; + +impl<'func, A, M, F> Operation<'func, A, M, F, DoublePrecDivOp> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn high(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn low(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } + + pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[2] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_PUSH, LLIL_NEG, LLIL_NOT, LLIL_SX, +// LLIL_ZX, LLIL_LOW_PART, LLIL_BOOL_TO_INT, LLIL_UNIMPL_MEM +pub struct UnaryOp; + +impl<'func, A, M, F> Operation<'func, A, M, F, UnaryOp> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn operand(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_CMP_X +pub struct Condition; + +impl<'func, A, M, F> Operation<'func, A, M, F, Condition> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } +} + + +// LLIL_UNIMPL_MEM +pub struct UnimplMem; + +impl<'func, A, M, F> Operation<'func, A, M, F, UnimplMem> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn mem_expr(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } +} + +// TODO TEST_BIT + +pub trait OperationArguments: 'static {} + +impl OperationArguments for NoArgs {} +impl OperationArguments for Pop {} +impl OperationArguments for Syscall {} +impl OperationArguments for SetReg {} +impl OperationArguments for SetRegSplit {} +impl OperationArguments for SetFlag {} +impl OperationArguments for Load {} +impl OperationArguments for Store {} +impl OperationArguments for Reg {} +impl OperationArguments for Flag {} +impl OperationArguments for FlagBit {} +impl OperationArguments for Jump {} +impl OperationArguments for JumpTo {} +impl OperationArguments for Call {} +impl OperationArguments for Ret {} +impl OperationArguments for If {} +impl OperationArguments for Goto {} +impl OperationArguments for FlagCond {} +impl OperationArguments for FlagGroup {} +impl OperationArguments for Trap {} +impl OperationArguments for RegPhi {} +impl OperationArguments for FlagPhi {} +impl OperationArguments for MemPhi {} +impl OperationArguments for Const {} +impl OperationArguments for BinaryOp {} +impl OperationArguments for BinaryOpCarry {} +impl OperationArguments for DoublePrecDivOp {} +impl OperationArguments for UnaryOp {} +impl OperationArguments for Condition {} +impl OperationArguments for UnimplMem {} diff --git a/rust/src/platform.rs b/rust/src/platform.rs new file mode 100644 index 00000000..3343bece --- /dev/null +++ b/rust/src/platform.rs @@ -0,0 +1,227 @@ +use std::borrow::Borrow; + +use binaryninjacore_sys::*; + +use crate::architecture::{Architecture, CoreArchitecture}; +use crate::callingconvention::CallingConvention; +use crate::types::QualifiedNameAndType; +use crate::rc::*; +use crate::string::*; + +#[derive(PartialEq, Eq, Hash)] +pub struct Platform { + pub(crate) handle: *mut BNPlatform, +} + +unsafe impl Send for Platform {} +unsafe impl Sync for Platform {} + +macro_rules! cc_func { + ($get_name:ident, $get_api:ident, $set_name:ident, $set_api:ident) => { + pub fn $get_name(&self) -> Option<Ref<CallingConvention<CoreArchitecture>>> { + let arch = self.arch(); + + unsafe { + let cc = $get_api(self.handle); + + if cc.is_null() { + None + } else { + Some(Ref::new(CallingConvention::from_raw(cc, arch))) + } + } + } + + pub fn $set_name<A: Architecture>(&self, cc: &CallingConvention<A>) { + let arch = self.arch(); + + assert!(cc.arch_handle.borrow().as_ref().0 == arch.0, "use of calling convention with non-matching Platform architecture!"); + + unsafe { + $set_api(self.handle, cc.handle); + } + } + } +} + +impl Platform { + pub(crate) unsafe fn from_raw(handle: *mut BNPlatform) -> Self { + debug_assert!(!handle.is_null()); + + Self { handle } + } + + pub fn by_name<S: BnStrCompatible>(name: S) -> Option<Ref<Self>> { + let raw_name = name.as_bytes_with_nul(); + unsafe { + let res = BNGetPlatformByName(raw_name.as_ref().as_ptr() as *mut _); + + if res.is_null() { + None + } else { + Some(Ref::new(Self { handle: res })) + } + } + } + + pub fn list_all() -> Array<Platform> { + unsafe { + let mut count = 0; + let handles = BNGetPlatformList(&mut count); + + Array::new(handles, count, ()) + } + } + + pub fn list_by_arch(arch: &CoreArchitecture) -> Array<Platform> { + unsafe { + let mut count = 0; + let handles = BNGetPlatformListByArchitecture(arch.0, &mut count); + + Array::new(handles, count, ()) + } + } + + pub fn list_by_os<S: BnStrCompatible>(name: S) -> Array<Platform> { + let raw_name = name.as_bytes_with_nul(); + + unsafe { + let mut count = 0; + let handles = BNGetPlatformListByOS(raw_name.as_ref().as_ptr() as *mut _, &mut count); + + Array::new(handles, count, ()) + } + } + + pub fn list_by_os_and_arch<S: BnStrCompatible>(name: S, arch: &CoreArchitecture) -> Array<Platform> { + let raw_name = name.as_bytes_with_nul(); + + unsafe { + let mut count = 0; + let handles = BNGetPlatformListByOSAndArchitecture(raw_name.as_ref().as_ptr() as *mut _, + arch.0, &mut count); + + Array::new(handles, count, ()) + } + } + + pub fn list_available_os() -> Array<BnString> { + unsafe { + let mut count = 0; + let list = BNGetPlatformOSList(&mut count); + + Array::new(list, count, ()) + } + } + + pub fn new<A: Architecture, S: BnStrCompatible>(arch: &A, name: S) -> Ref<Self> { + let name = name.as_bytes_with_nul(); + unsafe { + let handle = BNCreatePlatform(arch.as_ref().0, name.as_ref().as_ptr() as *mut _); + + assert!(!handle.is_null()); + + Ref::new(Self { handle }) + } + } + + pub fn name(&self) -> BnString { + unsafe { + let raw_name = BNGetPlatformName(self.handle); + BnString::from_raw(raw_name) + } + } + + pub fn arch(&self) -> CoreArchitecture { + unsafe { + CoreArchitecture::from_raw(BNGetPlatformArchitecture(self.handle)) + } + } + + pub fn register_os<S: BnStrCompatible>(&self, os: S) { + let os = os.as_bytes_with_nul(); + + unsafe { + BNRegisterPlatform(os.as_ref().as_ptr() as *mut _, self.handle); + } + } + + cc_func!(get_default_calling_convention, BNGetPlatformDefaultCallingConvention, + set_default_calling_convention, BNRegisterPlatformDefaultCallingConvention); + + cc_func!(get_cdecl_calling_convention, BNGetPlatformCdeclCallingConvention, + set_cdecl_calling_convention, BNRegisterPlatformCdeclCallingConvention); + + cc_func!(get_stdcall_calling_convention, BNGetPlatformStdcallCallingConvention, + set_stdcall_calling_convention, BNRegisterPlatformStdcallCallingConvention); + + cc_func!(get_fastcall_calling_convention, BNGetPlatformFastcallCallingConvention, + set_fastcall_calling_convention, BNRegisterPlatformFastcallCallingConvention); + + cc_func!(get_syscall_convention, BNGetPlatformSystemCallConvention, + set_syscall_convention, BNSetPlatformSystemCallConvention); + + pub fn types(&self) -> Array<QualifiedNameAndType> { + unsafe { + let mut count = 0; + let handles = BNGetPlatformTypes(self.handle, &mut count); + + Array::new(handles, count, ()) + } + } + + pub fn variables(&self) -> Array<QualifiedNameAndType> { + unsafe { + let mut count = 0; + let handles = BNGetPlatformVariables(self.handle, &mut count); + + Array::new(handles, count, ()) + } + } + + pub fn functions(&self) -> Array<QualifiedNameAndType> { + unsafe { + let mut count = 0; + let handles = BNGetPlatformFunctions(self.handle, &mut count); + + Array::new(handles, count, ()) + } + } +} + +impl ToOwned for Platform { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for Platform { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewPlatformReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreePlatform(handle.handle); + } +} + +unsafe impl CoreOwnedArrayProvider for Platform { + type Raw = *mut BNPlatform; + type Context = (); + + unsafe fn free(raw: *mut *mut BNPlatform, count: usize, _context: &()) { + BNFreePlatformList(raw, count); + } +} + +unsafe impl<'a> CoreOwnedArrayWrapper<'a> for Platform { + type Wrapped = Guard<'a, Platform>; + + unsafe fn wrap_raw(raw: &'a *mut BNPlatform, context: &'a ()) -> Guard<'a, Platform> { + Guard::new(Platform::from_raw(*raw), context) + } +} diff --git a/rust/src/rc.rs b/rust/src/rc.rs new file mode 100644 index 00000000..3302fb3f --- /dev/null +++ b/rust/src/rc.rs @@ -0,0 +1,361 @@ +use std::marker::PhantomData; +use std::borrow::Borrow; +use std::ops::{Deref, DerefMut}; +use std::slice; +use std::mem; +use std::ptr; + +// RefCountable provides an abstraction over the various +// core-allocated refcounted resources. +// +// It is important that consumers don't acquire ownership +// of a `RefCountable` object directly -- they should only +// ever get their hands on a `Ref<T>` or `&T`, otherwise it +// would be possible for the allocation in the core to +// be trivially leaked, as `T` does not have the `Drop` impl +// +// `T` does not have the `Drop` impl in order to allow more +// efficient handling of core owned objects we receive pointers +// to in callbacks +pub unsafe trait RefCountable: ToOwned<Owned=Ref<Self>> + Sized { + unsafe fn inc_ref(handle: &Self) -> Ref<Self>; + unsafe fn dec_ref(handle: &Self); +} + +// Represents an 'owned' reference tracked by the core +// that we are responsible for cleaning up once we're +// done with the encapsulated value. +pub struct Ref<T: RefCountable> { + contents: T, +} + +impl<T: RefCountable> Ref<T> { + pub(crate) unsafe fn new(contents: T) -> Self { + Self { contents } + } + + pub(crate) unsafe fn into_raw(obj: Self) -> T { + let res = ptr::read(&obj.contents); + + mem::forget(obj); + + res + } +} + +impl<T: RefCountable> AsRef<T> for Ref<T> { + fn as_ref(&self) -> &T { + &self.contents + } +} + +impl<T: RefCountable> Deref for Ref<T> { + type Target = T; + + fn deref(&self) -> &T { + &self.contents + } +} + +impl<T: RefCountable> DerefMut for Ref<T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.contents + } +} + +impl<T: RefCountable> Borrow<T> for Ref<T> { + fn borrow(&self) -> &T { + &self.contents + } +} + +impl<T: RefCountable> Drop for Ref<T> { + fn drop(&mut self) { + unsafe { RefCountable::dec_ref(&self.contents); } + } +} + +impl <T: RefCountable> Clone for Ref<T> { + fn clone(&self) -> Self { + unsafe { RefCountable::inc_ref(&self.contents) } + } +} + +// Guard provides access to a core-allocated resource whose +// reference is held indirectly (e.g. a core-allocated array +// of raw `*mut BNRawT`). +// +// This wrapper is necessary because `binja-rs` wrappers around +// core objects can be bigger than the raw pointer to the core +// object. This lets us create the full wrapper object and ensure +// that it does not outlive the core-allocated array (or similar) +// that our object came from. +pub struct Guard<'a, T> { + contents: T, + _guard: PhantomData<&'a ()>, +} + +impl<'a, T> Guard<'a, T> { + pub(crate) unsafe fn new<O: 'a>(contents: T, _owner: &O) -> Self { + Self { + contents, + _guard: PhantomData + } + } +} + +impl<'a, T> AsRef<T> for Guard<'a, T> { + fn as_ref(&self) -> &T { + &self.contents + } +} + +impl<'a, T> Deref for Guard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + &self.contents + } +} + +impl<'a, T> DerefMut for Guard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.contents + } +} + +impl<'a, T> Borrow<T> for Guard<'a, T> { + fn borrow(&self) -> &T { + &self.contents + } +} + +pub unsafe trait CoreOwnedArrayProvider { + type Raw; + type Context; + + unsafe fn free(raw: *mut Self::Raw, count: usize, context: &Self::Context); +} + +pub unsafe trait CoreOwnedArrayWrapper<'a>: CoreOwnedArrayProvider +where + Self::Raw: 'a, + Self::Context: 'a, +{ + type Wrapped: 'a; + + unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped; +} + +pub struct Array<P> +where + P: CoreOwnedArrayProvider +{ + contents: *mut P::Raw, + count: usize, + context: P::Context, +} + +unsafe impl<P> Sync for Array<P> where P: CoreOwnedArrayProvider, P::Context: Sync {} +unsafe impl<P> Send for Array<P> where P: CoreOwnedArrayProvider, P::Context: Send {} + +impl<P: CoreOwnedArrayProvider> Array<P> { + pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self { + Self { + contents: raw, + count: count, + context: context, + } + } + + #[inline] + pub fn len(&self) -> usize { + self.count + } +} + +impl<'a, P: 'a + CoreOwnedArrayWrapper<'a>> Array<P> { + #[inline] + pub fn get(&'a self, index: usize) -> P::Wrapped { + unsafe { + let backing = slice::from_raw_parts(self.contents, self.count); + P::wrap_raw(&backing[index], &self.context) + } + } + + pub fn iter(&'a self) -> ArrayIter<'a, P> { + ArrayIter { + it: unsafe { slice::from_raw_parts(self.contents, self.count).iter() }, + context: &self.context + } + } +} + + +impl<'a, P: 'a + CoreOwnedArrayWrapper<'a>> IntoIterator for &'a Array<P> { + type Item = P::Wrapped; + type IntoIter = ArrayIter<'a, P>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + + +impl<P: CoreOwnedArrayProvider> Drop for Array<P> { + fn drop(&mut self) { + unsafe { P::free(self.contents, self.count, &self.context); } + } +} + +pub struct ArrayIter<'a, P> +where + P: 'a + CoreOwnedArrayWrapper<'a> +{ + it: slice::Iter<'a, P::Raw>, + context: &'a P::Context, +} + +unsafe impl<'a, P> Send for ArrayIter<'a, P> where P: CoreOwnedArrayWrapper<'a>, P::Context: Sync {} + +impl<'a, P> Iterator for ArrayIter<'a, P> +where + P: 'a + CoreOwnedArrayWrapper<'a> +{ + type Item = P::Wrapped; + + #[inline] + fn next(&mut self) -> Option<P::Wrapped> { + self.it.next().map(|r| unsafe { P::wrap_raw(r, &self.context) }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.it.size_hint() + } +} + +impl<'a, P> ExactSizeIterator for ArrayIter<'a, P> +where + P: 'a + CoreOwnedArrayWrapper<'a> +{ + #[inline] + fn len(&self) -> usize { + self.it.len() + } +} + +impl<'a, P> DoubleEndedIterator for ArrayIter<'a, P> +where + P: 'a + CoreOwnedArrayWrapper<'a> +{ + #[inline] + fn next_back(&mut self) -> Option<P::Wrapped> { + self.it.next_back().map(|r| unsafe { P::wrap_raw(r, &self.context) }) + } +} + +#[cfg(feature = "rayon")] +use rayon::prelude::*; + +#[cfg(feature = "rayon")] +use rayon::iter::plumbing::*; + +#[cfg(feature = "rayon")] +impl<'a, P> Array<P> +where + P: 'a + CoreOwnedArrayWrapper<'a>, + P::Context: Sync, + P::Wrapped: Send, +{ + pub fn par_iter(&'a self) -> ParArrayIter<'a, P> { + ParArrayIter { + it: self.iter(), + } + } +} +#[cfg(feature = "rayon")] +pub struct ParArrayIter<'a, P> +where + P: 'a + CoreOwnedArrayWrapper<'a>, + ArrayIter<'a, P>: Send, +{ + it: ArrayIter<'a, P>, +} + +#[cfg(feature = "rayon")] +impl<'a, P> ParallelIterator for ParArrayIter<'a, P> +where + P: 'a + CoreOwnedArrayWrapper<'a>, + P::Wrapped: Send, + ArrayIter<'a, P>: Send, +{ + type Item = P::Wrapped; + + fn drive_unindexed<C>(self, consumer: C) -> C::Result + where + C: UnindexedConsumer<Self::Item> + { + bridge(self, consumer) + } + + fn opt_len(&self) -> Option<usize> { + Some(self.it.len()) + } +} + +#[cfg(feature = "rayon")] +impl<'a, P> IndexedParallelIterator for ParArrayIter<'a, P> +where + P: 'a + CoreOwnedArrayWrapper<'a>, + P::Wrapped: Send, + ArrayIter<'a, P>: Send, +{ + fn drive<C>(self, consumer: C) -> C::Result + where + C: Consumer<Self::Item> + { + bridge(self, consumer) + } + + fn len(&self) -> usize { + self.it.len() + } + + fn with_producer<CB>(self, callback: CB) -> CB::Output + where + CB: ProducerCallback<Self::Item> + { + callback.callback(ArrayIterProducer { it: self.it }) + } +} + +#[cfg(feature = "rayon")] +struct ArrayIterProducer<'a, P> +where + P: 'a + CoreOwnedArrayWrapper<'a>, + ArrayIter<'a, P>: Send, +{ + it: ArrayIter<'a, P>, +} + +#[cfg(feature = "rayon")] +impl<'a, P> Producer for ArrayIterProducer<'a, P> +where + P: 'a + CoreOwnedArrayWrapper<'a>, + ArrayIter<'a, P>: Send, +{ + type Item = P::Wrapped; + type IntoIter = ArrayIter<'a, P>; + + fn into_iter(self) -> ArrayIter<'a, P> { + self.it + } + + fn split_at(self, index: usize) -> (Self, Self) { + let (l, r) = self.it.it.as_slice().split_at(index); + + (Self { it: ArrayIter { it: l.iter(), context: self.it.context } }, + Self { it: ArrayIter { it: r.iter(), context: self.it.context } }) + } +} diff --git a/rust/src/section.rs b/rust/src/section.rs new file mode 100644 index 00000000..d936e227 --- /dev/null +++ b/rust/src/section.rs @@ -0,0 +1,251 @@ +use std::fmt; +use std::ops::Range; + +use binaryninjacore_sys::*; + +use crate::binaryview::BinaryView; +use crate::string::*; +use crate::rc::*; + +pub enum Semantics { + DefaultSection, + ReadOnlyCode, + ReadOnlyData, + ReadWriteData, + External, +} + +impl From<BNSectionSemantics> for Semantics { + fn from(bn: BNSectionSemantics) -> Self { + use self::BNSectionSemantics::*; + + match bn { + DefaultSectionSemantics => Semantics::DefaultSection, + ReadOnlyCodeSectionSemantics => Semantics::ReadOnlyCode, + ReadOnlyDataSectionSemantics => Semantics::ReadOnlyData, + ReadWriteDataSectionSemantics => Semantics::ReadWriteData, + ExternalSectionSemantics => Semantics::External, + } + } +} + +impl Into<BNSectionSemantics> for Semantics { + fn into(self) -> BNSectionSemantics { + use self::BNSectionSemantics::*; + + match self { + Semantics::DefaultSection => DefaultSectionSemantics, + Semantics::ReadOnlyCode => ReadOnlyCodeSectionSemantics, + Semantics::ReadOnlyData => ReadOnlyDataSectionSemantics, + Semantics::ReadWriteData => ReadWriteDataSectionSemantics, + Semantics::External => ExternalSectionSemantics, + } + } +} + +#[derive(PartialEq, Eq, Hash)] +pub struct Section { + handle: *mut BNSection, +} + +impl Section { + pub(crate) unsafe fn from_raw(raw: *mut BNSection) -> Self { + Self { handle: raw } + } + + pub fn new<S: BnStrCompatible>(name: S, range: Range<u64>) -> SectionBuilder<S> { + SectionBuilder { + is_auto: false, + name: name, + range: range, + semantics: Semantics::DefaultSection, + _ty: None, + align: 1, + entry_size: 1, + linked_section: None, + info_section: None, + info_data: 0 + } + } + + pub fn name(&self) -> BnString { + unsafe { BnString::from_raw(BNSectionGetName(self.handle)) } + } + + pub fn section_type(&self) -> BnString { + unsafe { BnString::from_raw(BNSectionGetType(self.handle)) } + } + + pub fn start(&self) -> u64 { + unsafe { BNSectionGetStart(self.handle) } + } + + pub fn end(&self) -> u64 { + unsafe { BNSectionGetEnd(self.handle) } + } + + pub fn len(&self) -> usize { + unsafe { BNSectionGetLength(self.handle) as usize } + } + + pub fn address_range(&self) -> Range<u64> { + self.start() .. self.end() + } + + pub fn semantics(&self) -> Semantics { + unsafe { BNSectionGetSemantics(self.handle).into() } + } + + pub fn linked_section(&self) -> BnString { + unsafe { BnString::from_raw(BNSectionGetLinkedSection(self.handle)) } + } + + pub fn info_section(&self) -> BnString { + unsafe { BnString::from_raw(BNSectionGetInfoSection(self.handle)) } + } + + pub fn info_data(&self) -> u64 { + unsafe { BNSectionGetInfoData(self.handle) } + } + + pub fn align(&self) -> u64 { + unsafe { BNSectionGetAlign(self.handle) } + } + + pub fn entry_size(&self) -> usize { + unsafe { BNSectionGetEntrySize(self.handle) as usize } + } + + pub fn auto_defined(&self) -> bool { + unsafe { BNSectionIsAutoDefined(self.handle) } + } +} + +impl fmt::Debug for Section { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "<section '{}' @ {:x}-{:x}>", self.name(), self.start(), self.end()) + } +} + +impl ToOwned for Section { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for Section { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewSectionReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeSection(handle.handle); + } +} + +unsafe impl CoreOwnedArrayProvider for Section { + type Raw = *mut BNSection; + type Context = (); + + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeSectionList(raw, count); + } +} + +unsafe impl<'a> CoreOwnedArrayWrapper<'a> for Section { + type Wrapped = Guard<'a, Section>; + + unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped { + Guard::new(Section::from_raw(*raw), context) + } +} + +#[must_use] +pub struct SectionBuilder<S: BnStrCompatible> { + is_auto: bool, + name: S, + range: Range<u64>, + semantics: Semantics, + _ty: Option<S>, + align: u64, + entry_size: u64, + linked_section: Option<S>, + info_section: Option<S>, + info_data: u64, +} + +impl<S: BnStrCompatible> SectionBuilder<S> { + pub fn semantics(mut self, semantics: Semantics) -> Self { + self.semantics = semantics; + self + } + + pub fn section_type(mut self, ty: S) -> Self { + self._ty = Some(ty); + self + } + + pub fn align(mut self, align: u64) -> Self { + self.align = align; + self + } + + pub fn entry_size(mut self, entry_size: u64) -> Self { + self.entry_size = entry_size; + self + } + + pub fn linked_section(mut self, linked_section: S) -> Self { + self.linked_section = Some(linked_section); + self + } + + pub fn info_section(mut self, info_section: S) -> Self { + self.info_section = Some(info_section); + self + } + + pub fn info_data(mut self, info_data: u64) -> Self { + self.info_data = info_data; + self + } + + pub fn is_auto(mut self, is_auto: bool) -> Self { + self.is_auto = is_auto; + self + } + + pub(crate) fn create(self, view: &BinaryView) { + let name = self.name.as_bytes_with_nul(); + let ty = self._ty.map(|s| s.as_bytes_with_nul()); + let linked_section = self.linked_section.map(|s| s.as_bytes_with_nul()); + let info_section = self.info_section.map(|s| s.as_bytes_with_nul()); + + let start = self.range.start; + let len = self.range.end.wrapping_sub(start); + + unsafe { + use std::ffi::CStr; + + let nul_str = CStr::from_bytes_with_nul_unchecked(b"\x00").as_ptr(); + let name_ptr = name.as_ref().as_ptr() as *mut _; + let ty_ptr = ty.as_ref().map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _); + let linked_section_ptr = linked_section.as_ref().map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _); + let info_section_ptr = info_section.as_ref().map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _); + + if self.is_auto { + BNAddAutoSection(view.handle, name_ptr, start, len, self.semantics.into(), + ty_ptr, self.align, self.entry_size, linked_section_ptr, + info_section_ptr, self.info_data); + } else { + BNAddUserSection(view.handle, name_ptr, start, len, self.semantics.into(), + ty_ptr, self.align, self.entry_size, linked_section_ptr, + info_section_ptr, self.info_data); + } + } + } +} diff --git a/rust/src/segment.rs b/rust/src/segment.rs new file mode 100644 index 00000000..0985d0d3 --- /dev/null +++ b/rust/src/segment.rs @@ -0,0 +1,190 @@ +use binaryninjacore_sys::*; + + +use std::ops::Range; + +use crate::binaryview::BinaryView; +use crate::rc::*; + +fn set_bit(val: u32, bit_mask: u32, new_val: bool) -> u32 { + (val & !bit_mask) | if new_val { bit_mask } else { 0 } +} + +#[must_use] +pub struct SegmentBuilder { + ea: Range<u64>, + parent_backing: Option<Range<u64>>, + flags: u32, + is_auto: bool, +} + +impl SegmentBuilder { + pub fn parent_backing(mut self, parent_backing: Range<u64>) -> Self { + self.parent_backing = Some(parent_backing); + self + } + + pub fn executable(mut self, executable: bool) -> Self { + self.flags = set_bit(self.flags, 0x01, executable); + self + } + + pub fn writable(mut self, writable: bool) -> Self { + self.flags = set_bit(self.flags, 0x02, writable); + self + } + + pub fn readable(mut self, readable: bool) -> Self { + self.flags = set_bit(self.flags, 0x04, readable); + self + } + + pub fn contains_data(mut self, contains_data: bool) -> Self { + self.flags = set_bit(self.flags, 0x08, contains_data); + self + } + + pub fn contains_code(mut self, contains_code: bool) -> Self { + self.flags = set_bit(self.flags, 0x10, contains_code); + self + } + + pub fn deny_write(mut self, deny_write: bool) -> Self { + self.flags = set_bit(self.flags, 0x20, deny_write); + self + } + + pub fn deny_execute(mut self, deny_execute: bool) -> Self { + self.flags = set_bit(self.flags, 0x40, deny_execute); + self + } + + pub fn is_auto(mut self, is_auto: bool) -> Self { + self.is_auto = is_auto; + self + } + + pub(crate) fn create(self, view: &BinaryView) { + let ea_start = self.ea.start; + let ea_len = self.ea.end.wrapping_sub(ea_start); + let (b_start, b_len) = self.parent_backing.map_or((0, 0), |s| (s.start, s.end.wrapping_sub(s.start))); + + unsafe { + if self.is_auto { + BNAddAutoSegment(view.handle, ea_start, ea_len, b_start, b_len, self.flags); + } else { + BNAddUserSegment(view.handle, ea_start, ea_len, b_start, b_len, self.flags); + } + } + } +} + +#[derive(PartialEq, Eq, Hash)] +pub struct Segment { + handle: *mut BNSegment, +} + +impl Segment { + pub(crate) unsafe fn from_raw(raw: *mut BNSegment) -> Self { + Self { handle: raw } + } + + pub fn new(ea_range: Range<u64>) -> SegmentBuilder { + SegmentBuilder { + ea: ea_range, + parent_backing: None, + flags: 0, + is_auto: false, + } + } + + pub fn address_range(&self) -> Range<u64> { + let start = unsafe { BNSegmentGetStart(self.handle) }; + let end = unsafe { BNSegmentGetEnd(self.handle) }; + start .. end + } + + pub fn parent_backing(&self) -> Option<Range<u64>> { + let start = unsafe { BNSegmentGetDataOffset(self.handle) }; + let end = unsafe { BNSegmentGetDataEnd(self.handle) }; + + if start != end { + Some(start .. end) + } else { + None + } + } + + fn flags(&self) -> u32 { + unsafe { BNSegmentGetFlags(self.handle) } + } + + pub fn executable(&self) -> bool { + self.flags() & 0x01 != 0 + } + + pub fn writable(&self) -> bool { + self.flags() & 0x02 != 0 + } + + pub fn readable(&self) -> bool { + self.flags() & 0x04 != 0 + } + + pub fn contains_data(&self) -> bool { + self.flags() & 0x08 != 0 + } + + pub fn contains_code(&self) -> bool { + self.flags() & 0x10 != 0 + } + + pub fn deny_write(&self) -> bool { + self.flags() & 0x20 != 0 + } + + pub fn deny_execute(&self) -> bool { + self.flags() & 0x40 != 0 + } + + pub fn auto_defined(&self) -> bool { + unsafe { BNSegmentIsAutoDefined(self.handle) } + } +} + +impl ToOwned for Segment { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for Segment { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewSegmentReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeSegment(handle.handle); + } +} + +unsafe impl CoreOwnedArrayProvider for Segment { + type Raw = *mut BNSegment; + type Context = (); + + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeSegmentList(raw, count); + } +} + +unsafe impl<'a> CoreOwnedArrayWrapper<'a> for Segment { + type Wrapped = Guard<'a, Segment>; + + unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped { + Guard::new(Segment::from_raw(*raw), context) + } +} diff --git a/rust/src/settings.rs b/rust/src/settings.rs new file mode 100644 index 00000000..4f4ae58e --- /dev/null +++ b/rust/src/settings.rs @@ -0,0 +1,49 @@ +use binaryninjacore_sys::*; + +pub use binaryninjacore_sys::BNSettingsScope as SettingsScope; + +use crate::rc::*; + +#[derive(PartialEq, Eq, Hash)] +pub struct Settings { + pub(crate) handle: *mut BNSettings, +} + +unsafe impl Send for Settings {} +unsafe impl Sync for Settings {} + +impl Settings { + pub(crate) unsafe fn from_raw(handle: *mut BNSettings) -> Self { + debug_assert!(!handle.is_null()); + + Self { handle } + } +} + +impl AsRef<Settings> for Settings { + fn as_ref(&self) -> &Self { + self + } +} + +impl ToOwned for Settings { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for Settings { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewSettingsReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeSettings(handle.handle); + } +} + + diff --git a/rust/src/string.rs b/rust/src/string.rs new file mode 100644 index 00000000..1b2e0913 --- /dev/null +++ b/rust/src/string.rs @@ -0,0 +1,201 @@ +use std::fmt; +use std::borrow::Borrow; +use std::ops::Deref; +use std::ffi::{CStr, CString}; +use std::os::raw; +use std::mem; + +use crate::rc::*; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[repr(C)] +pub struct BnStr { + raw: [u8] +} + +impl BnStr { + pub(crate) unsafe fn from_raw<'a>(ptr: *const raw::c_char) -> &'a Self { + mem::transmute(CStr::from_ptr(ptr).to_bytes_with_nul()) + } + + pub fn as_str(&self) -> &str { + self.as_cstr().to_str().unwrap() + } + + pub fn as_cstr(&self) -> &CStr { + unsafe { CStr::from_bytes_with_nul_unchecked(&self.raw) } + } +} + +impl Deref for BnStr { + type Target = str; + + fn deref(&self) -> &str { + self.as_str() + } +} + +impl AsRef<[u8]> for BnStr { + fn as_ref(&self) -> &[u8] { + &self.raw + } +} + +impl AsRef<str> for BnStr { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl Borrow<str> for BnStr { + fn borrow(&self) -> &str { + self.as_str() + } +} + +impl fmt::Display for BnStr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.as_cstr().to_string_lossy()) + } +} + +#[repr(C)] +pub struct BnString { + raw: *mut raw::c_char, +} + +/// A nul-terminated C string allocated by the core. +/// +/// Received from a variety of core function calls, and +/// must be used when giving strings to the core from many +/// core-invoked callbacks. +impl BnString { + pub fn new<S: BnStrCompatible>(s: S) -> Self { + use binaryninjacore_sys::BNAllocString; + + let raw = s.as_bytes_with_nul(); + + unsafe { + let ptr = raw.as_ref().as_ptr() as *mut _; + + Self { raw: BNAllocString(ptr) } + } + } + + pub(crate) unsafe fn from_raw(raw: *mut raw::c_char) -> Self { + Self { raw } + } + + pub(crate) fn into_raw(self) -> *mut raw::c_char { + let res = self.raw; + + // we're surrendering ownership over the *mut c_char to + // the core, so ensure we don't free it + mem::forget(self); + + res + } +} + +impl Drop for BnString { + fn drop(&mut self) { + use binaryninjacore_sys::BNFreeString; + + unsafe { + BNFreeString(self.raw); + } + } +} + +impl Deref for BnString { + type Target = BnStr; + + fn deref(&self) -> &BnStr { + unsafe { BnStr::from_raw(self.raw) } + } +} + +impl AsRef<[u8]> for BnString { + fn as_ref(&self) -> &[u8] { + self.as_cstr().to_bytes_with_nul() + } +} + +impl fmt::Display for BnString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.as_cstr().to_string_lossy()) + } +} + +unsafe impl CoreOwnedArrayProvider for BnString { + type Raw = *mut raw::c_char; + type Context = (); + + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + use binaryninjacore_sys::BNFreeStringList; + BNFreeStringList(raw, count); + } +} + +unsafe impl<'a> CoreOwnedArrayWrapper<'a> for BnString { + type Wrapped = &'a BnStr; + + unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped { + BnStr::from_raw(*raw) + } +} + +pub unsafe trait BnStrCompatible { + type Result: AsRef<[u8]>; + fn as_bytes_with_nul(self) -> Self::Result; +} + +unsafe impl<'a> BnStrCompatible for &'a BnStr { + type Result = &'a [u8]; + + fn as_bytes_with_nul(self) -> Self::Result { + self.as_cstr().to_bytes_with_nul() + } +} + +unsafe impl BnStrCompatible for BnString { + type Result = Self; + + fn as_bytes_with_nul(self) -> Self::Result { + self + } +} + +unsafe impl<'a> BnStrCompatible for &'a CStr { + type Result = &'a [u8]; + + fn as_bytes_with_nul(self) -> Self::Result { + self.to_bytes_with_nul() + } +} + +unsafe impl BnStrCompatible for CString { + type Result = Vec<u8>; + + fn as_bytes_with_nul(self) -> Self::Result { + self.into_bytes_with_nul() + } +} + +unsafe impl<'a> BnStrCompatible for &'a str { + type Result = Vec<u8>; + + fn as_bytes_with_nul(self) -> Self::Result { + let ret = CString::new(self).expect("can't pass strings with internal nul bytes to core!"); + ret.into_bytes_with_nul() + } +} + +unsafe impl BnStrCompatible for String { + type Result = Vec<u8>; + + fn as_bytes_with_nul(self) -> Self::Result { + let ret = CString::new(self).expect("can't pass strings with internal nul bytes to core!"); + ret.into_bytes_with_nul() + } +} diff --git a/rust/src/symbol.rs b/rust/src/symbol.rs new file mode 100644 index 00000000..44021426 --- /dev/null +++ b/rust/src/symbol.rs @@ -0,0 +1,241 @@ +use std::fmt; +use std::ptr; + +use binaryninjacore_sys::*; +use crate::string::*; +use crate::rc::*; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum SymType { + Function, + LibraryFunction, + ImportAddress, + ImportedFunction, + Data, + ImportedData, + External, +} + +impl From<BNSymbolType> for SymType { + fn from(bn: BNSymbolType) -> SymType { + use self::BNSymbolType::*; + + match bn { + FunctionSymbol => SymType::Function, + LibraryFunctionSymbol => SymType::LibraryFunction, + ImportAddressSymbol => SymType::ImportAddress, + ImportedFunctionSymbol => SymType::ImportedFunction, + DataSymbol => SymType::Data, + ImportedDataSymbol => SymType::ImportedData, + ExternalSymbol => SymType::External, + } + } +} + +impl Into<BNSymbolType> for SymType { + fn into(self) -> BNSymbolType { + use self::BNSymbolType::*; + + match self { + SymType::Function => FunctionSymbol, + SymType::LibraryFunction => LibraryFunctionSymbol, + SymType::ImportAddress => ImportAddressSymbol, + SymType::ImportedFunction => ImportedFunctionSymbol, + SymType::Data => DataSymbol, + SymType::ImportedData => ImportedDataSymbol, + SymType::External => ExternalSymbol, + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum Binding { + None, + Local, + Global, + Weak, +} + +impl From<BNSymbolBinding> for Binding { + fn from(bn: BNSymbolBinding) -> Binding { + use self::BNSymbolBinding::*; + + match bn { + NoBinding => Binding::None, + LocalBinding => Binding::Local, + GlobalBinding => Binding::Global, + WeakBinding => Binding::Weak, + } + } +} + +impl Into<BNSymbolBinding> for Binding { + fn into(self) -> BNSymbolBinding { + use self::BNSymbolBinding::*; + + match self { + Binding::None => NoBinding, + Binding::Local => LocalBinding, + Binding::Global => GlobalBinding, + Binding::Weak => WeakBinding, + } + } +} + +#[must_use] +pub struct SymbolBuilder<S: BnStrCompatible> { + ty: SymType, + binding: Binding, + addr: u64, + raw_name: S, + short_name: Option<S>, + full_name: Option<S>, + ordinal: u64, +} + +impl<S: BnStrCompatible> SymbolBuilder<S> { + pub fn binding(mut self, binding: Binding) -> Self { + self.binding = binding; + self + } + + pub fn short_name(mut self, short_name: S) -> Self { + self.short_name = Some(short_name); + self + } + + pub fn full_name(mut self, full_name: S) -> Self { + self.full_name = Some(full_name); + self + } + + pub fn ordinal(mut self, ordinal: u64) -> Self { + self.ordinal = ordinal; + self + } + + pub fn create(self) -> Ref<Symbol> { + let raw_name = self.raw_name.as_bytes_with_nul(); + let short_name = self.short_name.map(|s| s.as_bytes_with_nul()); + let full_name = self.full_name.map(|s| s.as_bytes_with_nul()); + + unsafe { + let raw_name = raw_name.as_ref().as_ptr() as *mut _; + let short_name = short_name.as_ref().map_or(raw_name, |s| s.as_ref().as_ptr() as *mut _); + let full_name = full_name.as_ref().map_or(raw_name, |s| s.as_ref().as_ptr() as *mut _); + + let res = BNCreateSymbol(self.ty.into(), + short_name, full_name, raw_name, + self.addr, self.binding.into(), ptr::null_mut(), + self.ordinal); + + Ref::new(Symbol::from_raw(res)) + } + } +} + +#[derive(PartialEq, Eq, Hash)] +pub struct Symbol { + pub(crate) handle: *mut BNSymbol, +} + +unsafe impl Send for Symbol {} +unsafe impl Sync for Symbol {} + +impl Symbol { + pub(crate) unsafe fn from_raw(raw: *mut BNSymbol) -> Self { + Self { handle: raw } + } + + pub fn new<S: BnStrCompatible>(ty: SymType, raw_name: S, addr: u64) -> SymbolBuilder<S> { + SymbolBuilder { + ty: ty, + binding: Binding::None, + addr: addr, + raw_name: raw_name, + short_name: None, + full_name: None, + ordinal: 0, + } + } + + pub fn sym_type(&self) -> SymType { + unsafe { BNGetSymbolType(self.handle).into() } + } + + pub fn binding(&self) -> Binding { + unsafe { BNGetSymbolBinding(self.handle).into() } + } + + pub fn name(&self) -> BnString { + unsafe { + let name = BNGetSymbolFullName(self.handle); + BnString::from_raw(name) + } + } + + pub fn short_name(&self) -> BnString { + unsafe { + let name = BNGetSymbolShortName(self.handle); + BnString::from_raw(name) + } + } + + pub fn raw_name(&self) -> BnString { + unsafe { + let name = BNGetSymbolRawName(self.handle); + BnString::from_raw(name) + } + } + + pub fn address(&self) -> u64 { + unsafe { BNGetSymbolAddress(self.handle) } + } + + pub fn auto_defined(&self) -> bool { + unsafe { BNIsSymbolAutoDefined(self.handle) } + } +} + +impl fmt::Debug for Symbol { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "<sym {:?} '{}' @ {:x} (handle: {:?})>", self.sym_type(), self.name(), self.address(), self.handle) + } +} + +impl ToOwned for Symbol { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for Symbol { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewSymbolReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeSymbol(handle.handle); + } +} + +unsafe impl CoreOwnedArrayProvider for Symbol { + type Raw = *mut BNSymbol; + type Context = (); + + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeSymbolList(raw, count); + } +} + +unsafe impl<'a> CoreOwnedArrayWrapper<'a> for Symbol { + type Wrapped = Guard<'a, Symbol>; + + unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped { + Guard::new(Symbol::from_raw(*raw), context) + } +} diff --git a/rust/src/types.rs b/rust/src/types.rs new file mode 100644 index 00000000..80046f87 --- /dev/null +++ b/rust/src/types.rs @@ -0,0 +1,109 @@ +use std::mem; +use std::slice; +use binaryninjacore_sys::*; + +use crate::rc::*; + +#[derive(PartialEq, Eq, Hash)] +pub struct Type { + pub(crate) handle: *mut BNType, +} + +unsafe impl Send for Type {} +unsafe impl Sync for Type {} + +impl Type { + pub(crate) unsafe fn from_raw(handle: *mut BNType) -> Self { + debug_assert!(!handle.is_null()); + + Self { handle } + } +} + +impl ToOwned for Type { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl RefCountable for Type { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewTypeReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeType(handle.handle); + } +} + +#[repr(C)] +pub struct QualifiedName { + object: BNQualifiedName, +} + +impl QualifiedName { + pub fn string(&self) -> String { + use std::ffi::CStr; + + unsafe { + slice::from_raw_parts(self.object.name, self.object.nameCount) + .iter() + .map(|c| CStr::from_ptr(*c).to_string_lossy()) + .collect::<Vec<_>>() + .join("::") + } + } +} + +impl Drop for QualifiedName { + fn drop(&mut self) { + unsafe { BNFreeQualifiedName(&mut self.object); } + } +} + +#[repr(C)] +pub struct QualifiedNameAndType { + object: BNQualifiedNameAndType, +} + +impl QualifiedNameAndType { + pub fn name(&self) -> &QualifiedName { + unsafe { + mem::transmute(&self.object.name) + } + } + + pub fn type_object(&self) -> Guard<Type> { + unsafe { + Guard::new(Type::from_raw(self.object.type_), self) + } + } +} + +impl Drop for QualifiedNameAndType { + fn drop(&mut self) { + unsafe { BNFreeQualifiedNameAndType(&mut self.object); } + } +} + +unsafe impl CoreOwnedArrayProvider for QualifiedNameAndType { + type Raw = BNQualifiedNameAndType; + type Context = (); + + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeTypeList(raw, count); + } +} + +unsafe impl<'a> CoreOwnedArrayWrapper<'a> for QualifiedNameAndType { + type Wrapped = &'a QualifiedNameAndType; + + unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped { + mem::transmute(raw) + } +} + |
