diff options
Diffstat (limited to 'rust/src/architecture')
| -rw-r--r-- | rust/src/architecture/basic_block.rs | 261 | ||||
| -rw-r--r-- | rust/src/architecture/branches.rs | 153 | ||||
| -rw-r--r-- | rust/src/architecture/flag.rs | 447 | ||||
| -rw-r--r-- | rust/src/architecture/instruction.rs | 114 | ||||
| -rw-r--r-- | rust/src/architecture/intrinsic.rs | 150 | ||||
| -rw-r--r-- | rust/src/architecture/register.rs | 333 |
6 files changed, 1458 insertions, 0 deletions
diff --git a/rust/src/architecture/basic_block.rs b/rust/src/architecture/basic_block.rs new file mode 100644 index 00000000..1867f50a --- /dev/null +++ b/rust/src/architecture/basic_block.rs @@ -0,0 +1,261 @@ +use crate::architecture::{CoreArchitecture, IndirectBranchInfo}; +use crate::basic_block::BasicBlock; +use crate::function::{Function, Location, NativeBlock}; +use crate::rc::Ref; +use binaryninjacore_sys::*; +use std::collections::{HashMap, HashSet}; + +pub struct BasicBlockAnalysisContext { + pub(crate) handle: *mut BNBasicBlockAnalysisContext, + contextual_returns_dirty: bool, + + // In + pub indirect_branches: Vec<IndirectBranchInfo>, + pub indirect_no_return_calls: HashSet<Location>, + pub analysis_skip_override: BNFunctionAnalysisSkipOverride, + pub guided_analysis_mode: bool, + pub trigger_guided_on_invalid_instruction: bool, + pub translate_tail_calls: bool, + pub disallow_branch_to_string: bool, + pub max_function_size: u64, + + // In/Out + pub max_size_reached: bool, + contextual_returns: HashMap<Location, bool>, + + // Out + direct_code_references: HashMap<u64, Location>, + direct_no_return_calls: HashSet<Location>, + halted_disassembly_addresses: HashSet<Location>, + inlined_unresolved_indirect_branches: HashSet<Location>, +} + +impl BasicBlockAnalysisContext { + pub unsafe fn from_raw(handle: *mut BNBasicBlockAnalysisContext) -> Self { + debug_assert!(!handle.is_null()); + + let ctx_ref = &*handle; + + let indirect_branches = (0..ctx_ref.indirectBranchesCount) + .map(|i| { + let raw: BNIndirectBranchInfo = + unsafe { std::ptr::read(ctx_ref.indirectBranches.add(i)) }; + IndirectBranchInfo::from(raw) + }) + .collect::<Vec<_>>(); + + let indirect_no_return_calls = (0..ctx_ref.indirectNoReturnCallsCount) + .map(|i| { + let raw = unsafe { std::ptr::read(ctx_ref.indirectNoReturnCalls.add(i)) }; + Location::from(raw) + }) + .collect::<HashSet<_>>(); + + let contextual_returns = (0..ctx_ref.contextualFunctionReturnCount) + .map(|i| { + let loc = unsafe { + let raw = std::ptr::read(ctx_ref.contextualFunctionReturnLocations.add(i)); + Location::from(raw) + }; + let val = unsafe { *ctx_ref.contextualFunctionReturnValues.add(i) }; + (loc, val) + }) + .collect::<HashMap<_, _>>(); + + let direct_code_references = (0..ctx_ref.directRefCount) + .map(|i| { + let src = unsafe { + let raw = std::ptr::read(ctx_ref.directRefSources.add(i)); + Location::from(raw) + }; + let tgt = unsafe { *ctx_ref.directRefTargets.add(i) }; + (tgt, src) + }) + .collect::<HashMap<_, _>>(); + + let direct_no_return_calls = (0..ctx_ref.directNoReturnCallsCount) + .map(|i| { + let raw = unsafe { std::ptr::read(ctx_ref.directNoReturnCalls.add(i)) }; + Location::from(raw) + }) + .collect::<HashSet<_>>(); + + let halted_disassembly_addresses = (0..ctx_ref.haltedDisassemblyAddressesCount) + .map(|i| { + let raw = unsafe { std::ptr::read(ctx_ref.haltedDisassemblyAddresses.add(i)) }; + Location::from(raw) + }) + .collect::<HashSet<_>>(); + + let inlined_unresolved_indirect_branches = (0..ctx_ref + .inlinedUnresolvedIndirectBranchCount) + .map(|i| { + let raw = + unsafe { std::ptr::read(ctx_ref.inlinedUnresolvedIndirectBranches.add(i)) }; + Location::from(raw) + }) + .collect::<HashSet<_>>(); + + BasicBlockAnalysisContext { + handle, + contextual_returns_dirty: false, + indirect_branches, + indirect_no_return_calls, + analysis_skip_override: ctx_ref.analysisSkipOverride, + guided_analysis_mode: ctx_ref.guidedAnalysisMode, + trigger_guided_on_invalid_instruction: ctx_ref.triggerGuidedOnInvalidInstruction, + translate_tail_calls: ctx_ref.translateTailCalls, + disallow_branch_to_string: ctx_ref.disallowBranchToString, + max_function_size: ctx_ref.maxFunctionSize, + max_size_reached: ctx_ref.maxSizeReached, + contextual_returns, + direct_code_references, + direct_no_return_calls, + halted_disassembly_addresses, + inlined_unresolved_indirect_branches, + } + } + + pub fn add_contextual_return(&mut self, loc: impl Into<Location>, value: bool) { + let loc = loc.into(); + if !self.contextual_returns.contains_key(&loc) { + self.contextual_returns_dirty = true; + } + + self.contextual_returns.insert(loc, value); + } + + pub fn add_direct_code_reference(&mut self, target: u64, src: impl Into<Location>) { + self.direct_code_references + .entry(target) + .or_insert(src.into()); + } + + pub fn add_direct_no_return_call(&mut self, loc: impl Into<Location>) { + self.direct_no_return_calls.insert(loc.into()); + } + + pub fn add_halted_disassembly_address(&mut self, loc: impl Into<Location>) { + self.halted_disassembly_addresses.insert(loc.into()); + } + + pub fn add_inlined_unresolved_indirect_branch(&mut self, loc: impl Into<Location>) { + self.inlined_unresolved_indirect_branches.insert(loc.into()); + } + + pub fn create_basic_block( + &self, + arch: CoreArchitecture, + start: u64, + ) -> Option<Ref<BasicBlock<NativeBlock>>> { + let raw_block = + unsafe { BNAnalyzeBasicBlocksContextCreateBasicBlock(self.handle, arch.handle, start) }; + + if raw_block.is_null() { + return None; + } + + unsafe { Some(BasicBlock::ref_from_raw(raw_block, NativeBlock::new())) } + } + + pub fn add_basic_block(&self, block: Ref<BasicBlock<NativeBlock>>) { + unsafe { + BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self.handle, block.handle); + } + } + + pub fn add_temp_outgoing_reference(&self, target: &Function) { + unsafe { + BNAnalyzeBasicBlocksContextAddTempReference(self.handle, target.handle); + } + } + + pub fn finalize(&mut self) { + if !self.direct_code_references.is_empty() { + let total = self.direct_code_references.len(); + let mut sources: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); + let mut targets: Vec<u64> = Vec::with_capacity(total); + for (target, src) in &self.direct_code_references { + sources.push(BNArchitectureAndAddress::from(src)); + targets.push(*target); + } + unsafe { + BNAnalyzeBasicBlocksContextSetDirectCodeReferences( + self.handle, + sources.as_mut_ptr(), + targets.as_mut_ptr(), + total, + ); + } + } + + if !self.direct_no_return_calls.is_empty() { + let total = self.direct_no_return_calls.len(); + let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); + for loc in &self.direct_no_return_calls { + locations.push(BNArchitectureAndAddress::from(loc)); + } + unsafe { + BNAnalyzeBasicBlocksContextSetDirectNoReturnCalls( + self.handle, + locations.as_mut_ptr(), + total, + ); + } + } + + if !self.halted_disassembly_addresses.is_empty() { + let total = self.halted_disassembly_addresses.len(); + let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); + for loc in &self.halted_disassembly_addresses { + locations.push(BNArchitectureAndAddress::from(loc)); + } + unsafe { + BNAnalyzeBasicBlocksContextSetHaltedDisassemblyAddresses( + self.handle, + locations.as_mut_ptr(), + total, + ); + } + } + + if !self.inlined_unresolved_indirect_branches.is_empty() { + let total = self.inlined_unresolved_indirect_branches.len(); + let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); + for loc in &self.inlined_unresolved_indirect_branches { + locations.push(BNArchitectureAndAddress::from(loc)); + } + unsafe { + BNAnalyzeBasicBlocksContextSetInlinedUnresolvedIndirectBranches( + self.handle, + locations.as_mut_ptr(), + total, + ); + } + } + + unsafe { + (*self.handle).maxSizeReached = self.max_size_reached; + } + + if self.contextual_returns_dirty { + let total = self.contextual_returns.len(); + let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); + let mut values: Vec<bool> = Vec::with_capacity(total); + for (loc, value) in &self.contextual_returns { + locations.push(BNArchitectureAndAddress::from(loc)); + values.push(*value); + } + unsafe { + BNAnalyzeBasicBlocksContextSetContextualFunctionReturns( + self.handle, + locations.as_mut_ptr(), + values.as_mut_ptr(), + total, + ); + } + } + + unsafe { BNAnalyzeBasicBlocksContextFinalize(self.handle) }; + } +} diff --git a/rust/src/architecture/branches.rs b/rust/src/architecture/branches.rs new file mode 100644 index 00000000..ff0ab516 --- /dev/null +++ b/rust/src/architecture/branches.rs @@ -0,0 +1,153 @@ +use crate::architecture::CoreArchitecture; +use crate::function::Location; +use crate::rc::{CoreArrayProvider, CoreArrayProviderInner}; +use binaryninjacore_sys::*; + +pub use binaryninjacore_sys::BNBranchType as BranchType; + +#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum BranchKind { + #[default] + Unresolved, + Unconditional(u64), + False(u64), + True(u64), + Call(u64), + FunctionReturn, + SystemCall, + Indirect, + Exception, + UserDefined, +} + +#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct BranchInfo { + /// If `None` the target architecture is the same as the branch instruction. + pub arch: Option<CoreArchitecture>, + pub kind: BranchKind, +} + +impl BranchInfo { + /// Branches to an instruction with the current architecture. + pub fn new(kind: BranchKind) -> Self { + Self { arch: None, kind } + } + + /// Branches to an instruction with an explicit architecture. + /// + /// Use this if your architecture can transition to another architecture with a branch. + pub fn new_with_arch(kind: BranchKind, arch: CoreArchitecture) -> Self { + Self { + arch: Some(arch), + kind, + } + } + + pub fn target(&self) -> Option<u64> { + match self.kind { + BranchKind::Unconditional(target) => Some(target), + BranchKind::False(target) => Some(target), + BranchKind::True(target) => Some(target), + BranchKind::Call(target) => Some(target), + _ => None, + } + } +} + +impl From<BranchInfo> for BNBranchType { + fn from(value: BranchInfo) -> Self { + match value.kind { + BranchKind::Unresolved => BNBranchType::UnresolvedBranch, + BranchKind::Unconditional(_) => BNBranchType::UnconditionalBranch, + BranchKind::False(_) => BNBranchType::FalseBranch, + BranchKind::True(_) => BNBranchType::TrueBranch, + BranchKind::Call(_) => BNBranchType::CallDestination, + BranchKind::FunctionReturn => BNBranchType::FunctionReturn, + BranchKind::SystemCall => BNBranchType::SystemCall, + BranchKind::Indirect => BNBranchType::IndirectBranch, + BranchKind::Exception => BNBranchType::ExceptionBranch, + BranchKind::UserDefined => BNBranchType::UserDefinedBranch, + } + } +} + +impl From<BranchKind> for BranchInfo { + fn from(value: BranchKind) -> Self { + Self { + arch: None, + kind: value, + } + } +} + +impl From<BranchKind> for BranchType { + fn from(value: BranchKind) -> Self { + match value { + BranchKind::Unresolved => BranchType::UnresolvedBranch, + BranchKind::Unconditional(_) => BranchType::UnconditionalBranch, + BranchKind::True(_) => BranchType::TrueBranch, + BranchKind::False(_) => BranchType::FalseBranch, + BranchKind::Call(_) => BranchType::CallDestination, + BranchKind::FunctionReturn => BranchType::FunctionReturn, + BranchKind::SystemCall => BranchType::SystemCall, + BranchKind::Indirect => BranchType::IndirectBranch, + BranchKind::Exception => BranchType::ExceptionBranch, + BranchKind::UserDefined => BranchType::UserDefinedBranch, + } + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct IndirectBranchInfo { + pub source: Location, + pub dest: Location, + pub auto_defined: bool, +} + +impl From<BNIndirectBranchInfo> for IndirectBranchInfo { + fn from(value: BNIndirectBranchInfo) -> Self { + Self { + source: Location::from_raw(value.sourceAddr, value.sourceArch), + dest: Location::from_raw(value.destAddr, value.destArch), + auto_defined: value.autoDefined, + } + } +} + +impl From<IndirectBranchInfo> for BNIndirectBranchInfo { + fn from(value: IndirectBranchInfo) -> Self { + let source_arch = value + .source + .arch + .map(|a| a.handle) + .unwrap_or(std::ptr::null_mut()); + let dest_arch = value + .source + .arch + .map(|a| a.handle) + .unwrap_or(std::ptr::null_mut()); + Self { + sourceArch: source_arch, + sourceAddr: value.source.addr, + destArch: dest_arch, + destAddr: value.dest.addr, + autoDefined: value.auto_defined, + } + } +} + +impl CoreArrayProvider for IndirectBranchInfo { + type Raw = BNIndirectBranchInfo; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for IndirectBranchInfo { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeIndirectBranchList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from(*raw) + } +} diff --git a/rust/src/architecture/flag.rs b/rust/src/architecture/flag.rs new file mode 100644 index 00000000..7c0f5deb --- /dev/null +++ b/rust/src/architecture/flag.rs @@ -0,0 +1,447 @@ +use crate::architecture::CoreArchitecture; +use binaryninjacore_sys::*; +use std::borrow::Cow; +use std::collections::HashMap; +use std::ffi::CStr; +use std::fmt::Debug; +use std::hash::Hash; + +pub use binaryninjacore_sys::BNFlagRole as FlagRole; +pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition; + +crate::new_id_type!(FlagId, u32); +// TODO: Make this NonZero<u32>? +crate::new_id_type!(FlagWriteId, u32); +crate::new_id_type!(FlagClassId, u32); +crate::new_id_type!(FlagGroupId, u32); + +pub trait Flag: Debug + Sized + Clone + Copy + Hash + Eq { + 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) -> FlagId; +} + +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) -> FlagWriteId; + + 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) -> FlagClassId; +} + +pub trait FlagGroup: Debug + 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) -> FlagGroupId; + + /// 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: + /// + /// ```text + /// 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>; +} + +/// Type for architectures that do not use flags. Will panic if accessed as a flag. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnusedFlag; + +impl Flag for UnusedFlag { + type FlagClass = Self; + fn name(&self) -> Cow<'_, str> { + unreachable!() + } + fn role(&self, _class: Option<Self::FlagClass>) -> FlagRole { + unreachable!() + } + fn id(&self) -> FlagId { + unreachable!() + } +} + +impl FlagWrite for UnusedFlag { + type FlagType = Self; + type FlagClass = Self; + fn name(&self) -> Cow<'_, str> { + unreachable!() + } + fn class(&self) -> Option<Self> { + unreachable!() + } + fn id(&self) -> FlagWriteId { + unreachable!() + } + fn flags_written(&self) -> Vec<Self::FlagType> { + unreachable!() + } +} + +impl FlagClass for UnusedFlag { + fn name(&self) -> Cow<'_, str> { + unreachable!() + } + fn id(&self) -> FlagClassId { + unreachable!() + } +} + +impl FlagGroup for UnusedFlag { + type FlagType = Self; + type FlagClass = Self; + fn name(&self) -> Cow<'_, str> { + unreachable!() + } + fn id(&self) -> FlagGroupId { + unreachable!() + } + fn flags_required(&self) -> Vec<Self::FlagType> { + unreachable!() + } + fn flag_conditions(&self) -> HashMap<Self, FlagCondition> { + unreachable!() + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreFlag { + arch: CoreArchitecture, + id: FlagId, +} + +impl CoreFlag { + pub fn new(arch: CoreArchitecture, id: FlagId) -> Option<Self> { + let flag = Self { arch, id }; + flag.is_valid().then_some(flag) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the flag is actually valid. + let name = unsafe { BNGetArchitectureFlagName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl Flag for CoreFlag { + type FlagClass = CoreFlagClass; + + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureFlagName(self.arch.handle, self.id.into()); + + // 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 { + unsafe { + BNGetArchitectureFlagRole( + self.arch.handle, + self.id.into(), + class.map(|c| c.id.0).unwrap_or(0), + ) + } + } + + fn id(&self) -> FlagId { + self.id + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreFlagWrite { + arch: CoreArchitecture, + id: FlagWriteId, +} + +impl CoreFlagWrite { + pub fn new(arch: CoreArchitecture, id: FlagWriteId) -> Option<Self> { + let flag_write = Self { arch, id }; + flag_write.is_valid().then_some(flag_write) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the flag write is actually valid. + let name = unsafe { BNGetArchitectureFlagWriteTypeName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl FlagWrite for CoreFlagWrite { + type FlagType = CoreFlag; + type FlagClass = CoreFlagClass; + + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureFlagWriteTypeName(self.arch.handle, self.id.into()); + + // 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 class(&self) -> Option<CoreFlagClass> { + let class = unsafe { + BNGetArchitectureSemanticClassForFlagWriteType(self.arch.handle, self.id.into()) + }; + + match class { + 0 => None, + class_id => Some(CoreFlagClass::new(self.arch, class_id.into())?), + } + } + + fn id(&self) -> FlagWriteId { + self.id + } + + fn flags_written(&self) -> Vec<CoreFlag> { + let mut count: usize = 0; + let regs: *mut u32 = unsafe { + BNGetArchitectureFlagsWrittenByFlagWriteType( + self.arch.handle, + self.id.into(), + &mut count, + ) + }; + + let ret = unsafe { + std::slice::from_raw_parts(regs, count) + .iter() + .map(|id| FlagId::from(*id)) + .filter_map(|reg| CoreFlag::new(self.arch, reg)) + .collect() + }; + + unsafe { + BNFreeRegisterList(regs); + } + + ret + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreFlagClass { + arch: CoreArchitecture, + id: FlagClassId, +} + +impl CoreFlagClass { + pub fn new(arch: CoreArchitecture, id: FlagClassId) -> Option<Self> { + let flag = Self { arch, id }; + flag.is_valid().then_some(flag) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the flag is actually valid. + let name = + unsafe { BNGetArchitectureSemanticFlagClassName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl FlagClass for CoreFlagClass { + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureSemanticFlagClassName(self.arch.handle, self.id.into()); + + // 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) -> FlagClassId { + self.id + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct CoreFlagGroup { + arch: CoreArchitecture, + id: FlagGroupId, +} + +impl CoreFlagGroup { + pub fn new(arch: CoreArchitecture, id: FlagGroupId) -> Option<Self> { + let flag_group = Self { arch, id }; + flag_group.is_valid().then_some(flag_group) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the flag group is actually valid. + let name = + unsafe { BNGetArchitectureSemanticFlagGroupName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl FlagGroup for CoreFlagGroup { + type FlagType = CoreFlag; + type FlagClass = CoreFlagClass; + + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureSemanticFlagGroupName(self.arch.handle, self.id.into()); + + // 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) -> FlagGroupId { + self.id + } + + fn flags_required(&self) -> Vec<CoreFlag> { + let mut count: usize = 0; + let regs: *mut u32 = unsafe { + BNGetArchitectureFlagsRequiredForSemanticFlagGroup( + self.arch.handle, + self.id.into(), + &mut count, + ) + }; + + let ret = unsafe { + std::slice::from_raw_parts(regs, count) + .iter() + .map(|id| FlagId::from(*id)) + .filter_map(|reg| CoreFlag::new(self.arch, reg)) + .collect() + }; + + unsafe { + BNFreeRegisterList(regs); + } + + ret + } + + fn flag_conditions(&self) -> HashMap<CoreFlagClass, FlagCondition> { + let mut count: usize = 0; + + unsafe { + let flag_conds = BNGetArchitectureFlagConditionsForSemanticFlagGroup( + self.arch.handle, + self.id.into(), + &mut count, + ); + + let ret = std::slice::from_raw_parts_mut(flag_conds, count) + .iter() + .filter_map(|class_cond| { + Some(( + CoreFlagClass::new(self.arch, class_cond.semanticClass.into())?, + class_cond.condition, + )) + }) + .collect(); + + BNFreeFlagConditionsForSemanticFlagGroup(flag_conds); + + ret + } + } +} diff --git a/rust/src/architecture/instruction.rs b/rust/src/architecture/instruction.rs new file mode 100644 index 00000000..94cb788b --- /dev/null +++ b/rust/src/architecture/instruction.rs @@ -0,0 +1,114 @@ +use crate::architecture::{BranchInfo, BranchKind, CoreArchitecture}; +use binaryninjacore_sys::*; + +/// This is the number of branches that can be specified in an [`InstructionInfo`]. +pub const NUM_BRANCH_INFO: usize = 3; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct InstructionInfo { + pub length: usize, + // TODO: This field name is really long... + pub arch_transition_by_target_addr: bool, + pub delay_slots: u8, + pub branches: [Option<BranchInfo>; NUM_BRANCH_INFO], +} + +impl InstructionInfo { + // TODO: `new_with_delay_slot`? + pub fn new(length: usize, delay_slots: u8) -> Self { + Self { + length, + arch_transition_by_target_addr: false, + delay_slots, + branches: Default::default(), + } + } + + /// Add a branch to this [`InstructionInfo`], maximum of 3 branches may be added (as per [`NUM_BRANCH_INFO`]). + pub fn add_branch(&mut self, branch_info: impl Into<BranchInfo>) { + // Will go through each slot and attempt to add the branch info. + // TODO: Return a result with BranchInfoSlotsFilled error. + for branch in &mut self.branches { + if branch.is_none() { + *branch = Some(branch_info.into()); + return; + } + } + } +} + +impl From<BNInstructionInfo> for InstructionInfo { + fn from(value: BNInstructionInfo) -> Self { + // TODO: This is quite ugly, but we destructure the branch info so this will have to do. + let mut branch_info = [None; NUM_BRANCH_INFO]; + #[allow(clippy::needless_range_loop)] + for i in 0..value.branchCount.min(NUM_BRANCH_INFO) { + let branch_target = value.branchTarget[i]; + branch_info[i] = Some(BranchInfo { + kind: match value.branchType[i] { + BNBranchType::UnconditionalBranch => BranchKind::Unconditional(branch_target), + BNBranchType::FalseBranch => BranchKind::False(branch_target), + BNBranchType::TrueBranch => BranchKind::True(branch_target), + BNBranchType::CallDestination => BranchKind::Call(branch_target), + BNBranchType::FunctionReturn => BranchKind::FunctionReturn, + BNBranchType::SystemCall => BranchKind::SystemCall, + BNBranchType::IndirectBranch => BranchKind::Indirect, + BNBranchType::ExceptionBranch => BranchKind::Exception, + BNBranchType::UnresolvedBranch => BranchKind::Unresolved, + BNBranchType::UserDefinedBranch => BranchKind::UserDefined, + }, + arch: if value.branchArch[i].is_null() { + None + } else { + Some(unsafe { CoreArchitecture::from_raw(value.branchArch[i]) }) + }, + }); + } + Self { + length: value.length, + arch_transition_by_target_addr: value.archTransitionByTargetAddr, + delay_slots: value.delaySlots, + branches: branch_info, + } + } +} + +impl From<InstructionInfo> for BNInstructionInfo { + fn from(value: InstructionInfo) -> Self { + let branch_count = value.branches.into_iter().filter(Option::is_some).count(); + // TODO: This is quite ugly, but we destructure the branch info so this will have to do. + let branch_info_0 = value.branches[0].unwrap_or_default(); + let branch_info_1 = value.branches[1].unwrap_or_default(); + let branch_info_2 = value.branches[2].unwrap_or_default(); + Self { + length: value.length, + branchCount: branch_count, + archTransitionByTargetAddr: value.arch_transition_by_target_addr, + delaySlots: value.delay_slots, + branchType: [ + branch_info_0.into(), + branch_info_1.into(), + branch_info_2.into(), + ], + branchTarget: [ + branch_info_0.target().unwrap_or_default(), + branch_info_1.target().unwrap_or_default(), + branch_info_2.target().unwrap_or_default(), + ], + branchArch: [ + branch_info_0 + .arch + .map(|a| a.handle) + .unwrap_or(std::ptr::null_mut()), + branch_info_1 + .arch + .map(|a| a.handle) + .unwrap_or(std::ptr::null_mut()), + branch_info_2 + .arch + .map(|a| a.handle) + .unwrap_or(std::ptr::null_mut()), + ], + } + } +} diff --git a/rust/src/architecture/intrinsic.rs b/rust/src/architecture/intrinsic.rs new file mode 100644 index 00000000..4ad5ee2a --- /dev/null +++ b/rust/src/architecture/intrinsic.rs @@ -0,0 +1,150 @@ +use crate::architecture::CoreArchitecture; +use crate::confidence::Conf; +use crate::rc::Ref; +use crate::types::{NameAndType, Type}; +use binaryninjacore_sys::{ + BNFreeNameAndTypeList, BNFreeOutputTypeList, BNFreeString, BNGetArchitectureIntrinsicClass, + BNGetArchitectureIntrinsicInputs, BNGetArchitectureIntrinsicName, + BNGetArchitectureIntrinsicOutputs, BNIntrinsicClass, +}; +use std::borrow::Cow; +use std::ffi::CStr; +use std::fmt::{Debug, Formatter}; + +crate::new_id_type!(IntrinsicId, u32); + +pub trait Intrinsic: Debug + Sized + Clone + Copy { + fn name(&self) -> Cow<'_, str>; + + /// Unique identifier for this `Intrinsic`. + fn id(&self) -> IntrinsicId; + + /// The intrinsic class for this `Intrinsic`. + fn class(&self) -> BNIntrinsicClass { + BNIntrinsicClass::GeneralIntrinsicClass + } + + // TODO: Maybe just return `(String, Conf<Ref<Type>>)`? + /// List of the input names and types for this intrinsic. + fn inputs(&self) -> Vec<NameAndType>; + + /// List of the output types for this intrinsic. + fn outputs(&self) -> Vec<Conf<Ref<Type>>>; +} + +/// Type for architrectures that do not use intrinsics. Will panic if accessed as an intrinsic. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnusedIntrinsic; + +impl Intrinsic for UnusedIntrinsic { + fn name(&self) -> Cow<'_, str> { + unreachable!() + } + fn id(&self) -> IntrinsicId { + unreachable!() + } + fn inputs(&self) -> Vec<NameAndType> { + unreachable!() + } + fn outputs(&self) -> Vec<Conf<Ref<Type>>> { + unreachable!() + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +pub struct CoreIntrinsic { + pub arch: CoreArchitecture, + pub id: IntrinsicId, +} + +impl CoreIntrinsic { + pub fn new(arch: CoreArchitecture, id: IntrinsicId) -> Option<Self> { + let intrinsic = Self { arch, id }; + intrinsic.is_valid().then_some(intrinsic) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the intrinsic is actually valid. + let name = unsafe { BNGetArchitectureIntrinsicName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl Intrinsic for CoreIntrinsic { + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureIntrinsicName(self.arch.handle, self.id.into()); + + // We need to guarantee ownership, as if we're still + // a Borrowed variant we're about to free the underlying + // memory. + // TODO: ^ the above assertion nullifies any benefit to passing back Cow tho? + let res = CStr::from_ptr(name); + let res = res.to_string_lossy().into_owned().into(); + + BNFreeString(name); + + res + } + } + + fn id(&self) -> IntrinsicId { + self.id + } + + fn class(&self) -> BNIntrinsicClass { + unsafe { BNGetArchitectureIntrinsicClass(self.arch.handle, self.id.into()) } + } + + fn inputs(&self) -> Vec<NameAndType> { + let mut count: usize = 0; + unsafe { + let inputs = + BNGetArchitectureIntrinsicInputs(self.arch.handle, self.id.into(), &mut count); + + let ret = std::slice::from_raw_parts_mut(inputs, count) + .iter() + .map(NameAndType::from_raw) + .collect(); + + BNFreeNameAndTypeList(inputs, count); + + ret + } + } + + fn outputs(&self) -> Vec<Conf<Ref<Type>>> { + let mut count: usize = 0; + unsafe { + let inputs = + BNGetArchitectureIntrinsicOutputs(self.arch.handle, self.id.into(), &mut count); + + let ret = std::slice::from_raw_parts_mut(inputs, count) + .iter() + .map(Conf::<Ref<Type>>::from_raw) + .collect(); + + BNFreeOutputTypeList(inputs, count); + + ret + } + } +} + +impl Debug for CoreIntrinsic { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CoreIntrinsic") + .field("id", &self.id) + .field("name", &self.name()) + .field("class", &self.class()) + .field("inputs", &self.inputs()) + .field("outputs", &self.outputs()) + .finish() + } +} diff --git a/rust/src/architecture/register.rs b/rust/src/architecture/register.rs new file mode 100644 index 00000000..50bb38ba --- /dev/null +++ b/rust/src/architecture/register.rs @@ -0,0 +1,333 @@ +use crate::architecture::CoreArchitecture; +use crate::rc::{CoreArrayProvider, CoreArrayProviderInner}; +use binaryninjacore_sys::*; +use std::borrow::Cow; +use std::ffi::CStr; +use std::fmt::{Debug, Formatter}; +use std::hash::Hash; + +pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend; + +crate::new_id_type!(RegisterId, u32); + +impl RegisterId { + pub fn is_temporary(&self) -> bool { + self.0 & 0x8000_0000 != 0 + } +} + +crate::new_id_type!(RegisterStackId, u32); + +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: Debug + Sized + Clone + Copy + Hash + Eq { + 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) -> RegisterId; +} + +pub trait RegisterStackInfo: Sized { + type RegStackType: RegisterStack<InfoType = Self>; + type RegType: Register<InfoType = Self::RegInfoType>; + type RegInfoType: RegisterInfo<RegType = Self::RegType>; + + fn storage_regs(&self) -> (Self::RegType, usize); + fn top_relative_regs(&self) -> Option<(Self::RegType, usize)>; + fn stack_top_reg(&self) -> Self::RegType; +} + +/// Type for architectures that do not use register stacks. Will panic if accessed as a register stack. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnusedRegisterStackInfo<R: Register> { + _reg: std::marker::PhantomData<R>, +} + +impl<R: Register> RegisterStackInfo for UnusedRegisterStackInfo<R> { + type RegStackType = UnusedRegisterStack<R>; + type RegType = R; + type RegInfoType = R::InfoType; + + fn storage_regs(&self) -> (Self::RegType, usize) { + unreachable!() + } + fn top_relative_regs(&self) -> Option<(Self::RegType, usize)> { + unreachable!() + } + fn stack_top_reg(&self) -> Self::RegType { + unreachable!() + } +} + +pub trait RegisterStack: Debug + Sized + Clone + Copy { + type InfoType: RegisterStackInfo< + RegType = Self::RegType, + RegInfoType = Self::RegInfoType, + RegStackType = Self, + >; + type RegType: Register<InfoType = Self::RegInfoType>; + type RegInfoType: RegisterInfo<RegType = Self::RegType>; + + fn name(&self) -> Cow<'_, str>; + fn info(&self) -> Self::InfoType; + + /// Unique identifier for this `RegisterStack`. + /// + /// *MUST* be in the range [0, 0x7fff_ffff] + fn id(&self) -> RegisterStackId; +} + +/// Type for architectures that do not use register stacks. Will panic if accessed as a register stack. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnusedRegisterStack<R: Register> { + _reg: std::marker::PhantomData<R>, +} + +impl<R: Register> RegisterStack for UnusedRegisterStack<R> { + type InfoType = UnusedRegisterStackInfo<R>; + type RegType = R; + type RegInfoType = R::InfoType; + + fn name(&self) -> Cow<'_, str> { + unreachable!() + } + fn info(&self) -> Self::InfoType { + unreachable!() + } + fn id(&self) -> RegisterStackId { + unreachable!() + } +} + +#[derive(Debug, Copy, Clone)] +pub struct CoreRegisterInfo { + arch: CoreArchitecture, + id: RegisterId, + info: BNRegisterInfo, +} + +impl CoreRegisterInfo { + pub fn new(arch: CoreArchitecture, id: RegisterId, info: BNRegisterInfo) -> Self { + Self { arch, id, info } + } +} + +impl RegisterInfo for CoreRegisterInfo { + type RegType = CoreRegister; + + fn parent(&self) -> Option<CoreRegister> { + if self.id != RegisterId::from(self.info.fullWidthRegister) { + Some(CoreRegister::new( + self.arch, + RegisterId::from(self.info.fullWidthRegister), + )?) + } else { + None + } + } + + fn size(&self) -> usize { + self.info.size + } + + fn offset(&self) -> usize { + self.info.offset + } + + fn implicit_extend(&self) -> ImplicitRegisterExtend { + self.info.extend + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreRegister { + arch: CoreArchitecture, + id: RegisterId, +} + +impl CoreRegister { + pub fn new(arch: CoreArchitecture, id: RegisterId) -> Option<Self> { + let register = Self { arch, id }; + register.is_valid().then_some(register) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the register is actually valid. + let name = unsafe { BNGetArchitectureRegisterName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl Register for CoreRegister { + type InfoType = CoreRegisterInfo; + + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureRegisterName(self.arch.handle, self.id.into()); + + // 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::new(self.arch, self.id, unsafe { + BNGetArchitectureRegisterInfo(self.arch.handle, self.id.into()) + }) + } + + fn id(&self) -> RegisterId { + self.id + } +} + +impl Debug for CoreRegister { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CoreRegister") + .field("id", &self.id) + .field("name", &self.name()) + .finish() + } +} + +impl CoreArrayProvider for CoreRegister { + type Raw = u32; + type Context = CoreArchitecture; + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for CoreRegister { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeRegisterList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::new(*context, RegisterId::from(*raw)).expect("Register list contains valid registers") + } +} + +#[derive(Debug, Copy, Clone)] +pub struct CoreRegisterStackInfo { + arch: CoreArchitecture, + // TODO: Wrap BNRegisterStackInfo + info: BNRegisterStackInfo, +} + +impl CoreRegisterStackInfo { + pub fn new(arch: CoreArchitecture, info: BNRegisterStackInfo) -> Self { + Self { arch, info } + } +} + +impl RegisterStackInfo for CoreRegisterStackInfo { + type RegStackType = CoreRegisterStack; + type RegType = CoreRegister; + type RegInfoType = CoreRegisterInfo; + + fn storage_regs(&self) -> (Self::RegType, usize) { + ( + CoreRegister::new(self.arch, RegisterId::from(self.info.firstStorageReg)) + .expect("Storage register is valid"), + self.info.storageCount as usize, + ) + } + + fn top_relative_regs(&self) -> Option<(Self::RegType, usize)> { + if self.info.topRelativeCount == 0 { + None + } else { + Some(( + CoreRegister::new(self.arch, RegisterId::from(self.info.firstTopRelativeReg)) + .expect("Top relative register is valid"), + self.info.topRelativeCount as usize, + )) + } + } + + fn stack_top_reg(&self) -> Self::RegType { + CoreRegister::new(self.arch, RegisterId::from(self.info.stackTopReg)) + .expect("Stack top register is valid") + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreRegisterStack { + arch: CoreArchitecture, + id: RegisterStackId, +} + +impl CoreRegisterStack { + pub fn new(arch: CoreArchitecture, id: RegisterStackId) -> Option<Self> { + let register_stack = Self { arch, id }; + register_stack.is_valid().then_some(register_stack) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the stack register is actually valid. + let name = unsafe { BNGetArchitectureRegisterStackName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl RegisterStack for CoreRegisterStack { + type InfoType = CoreRegisterStackInfo; + type RegType = CoreRegister; + type RegInfoType = CoreRegisterInfo; + + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureRegisterStackName(self.arch.handle, self.id.into()); + + // 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) -> CoreRegisterStackInfo { + CoreRegisterStackInfo::new(self.arch, unsafe { + BNGetArchitectureRegisterStackInfo(self.arch.handle, self.id.into()) + }) + } + + fn id(&self) -> RegisterStackId { + self.id + } +} |
