diff options
| author | Glenn Smith <glenn@vector35.com> | 2024-12-27 16:14:46 -0500 |
|---|---|---|
| committer | Glenn Smith <glenn@vector35.com> | 2025-01-30 17:20:05 -0500 |
| commit | 8862696926173104957729683832591438161557 (patch) | |
| tree | 78ba6d7dc8144430136086c8dc84726171eec8ab /rust/src | |
| parent | 5a5426d030b6be26d4564ba1eba2d8a275533256 (diff) | |
Render Layers
Diffstat (limited to 'rust/src')
| -rw-r--r-- | rust/src/basic_block.rs | 43 | ||||
| -rw-r--r-- | rust/src/binary_view.rs | 4 | ||||
| -rw-r--r-- | rust/src/collaboration/sync.rs | 6 | ||||
| -rw-r--r-- | rust/src/disassembly.rs | 7 | ||||
| -rw-r--r-- | rust/src/flowgraph.rs | 134 | ||||
| -rw-r--r-- | rust/src/function.rs | 6 | ||||
| -rw-r--r-- | rust/src/lib.rs | 1 | ||||
| -rw-r--r-- | rust/src/linear_view.rs | 164 | ||||
| -rw-r--r-- | rust/src/low_level_il.rs | 2 | ||||
| -rw-r--r-- | rust/src/medium_level_il/function.rs | 4 | ||||
| -rw-r--r-- | rust/src/render_layer.rs | 410 | ||||
| -rw-r--r-- | rust/src/workflow.rs | 4 |
12 files changed, 729 insertions, 56 deletions
diff --git a/rust/src/basic_block.rs b/rust/src/basic_block.rs index b880d28f..8846a90b 100644 --- a/rust/src/basic_block.rs +++ b/rust/src/basic_block.rs @@ -19,6 +19,7 @@ use crate::BranchType; use binaryninjacore_sys::*; use std::fmt; use std::fmt::Debug; +use std::hash::{Hash, Hasher}; enum EdgeDirection { Incoming, @@ -97,7 +98,14 @@ pub trait BlockContext: Clone + Sync + Send + Sized { fn iter(&self, block: &BasicBlock<Self>) -> Self::Iter; } -#[derive(PartialEq, Eq, Hash)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)] +pub enum BasicBlockType { + Native, + LowLevelIL, + MediumLevelIL, + HighLevelIL, +} + pub struct BasicBlock<C: BlockContext> { pub(crate) handle: *mut BNBasicBlock, context: C, @@ -127,6 +135,19 @@ impl<C: BlockContext> BasicBlock<C> { } } + pub fn block_type(&self) -> BasicBlockType { + if unsafe { !BNIsILBasicBlock(self.handle) } { + BasicBlockType::Native + } else if unsafe { BNIsLowLevelILBasicBlock(self.handle) } { + BasicBlockType::LowLevelIL + } else if unsafe { BNIsMediumLevelILBasicBlock(self.handle) } { + BasicBlockType::MediumLevelIL + } else { + // We checked all other IL levels, so this is safe. + BasicBlockType::HighLevelIL + } + } + pub fn iter(&self) -> C::Iter { self.context.iter(self) } @@ -194,7 +215,7 @@ impl<C: BlockContext> BasicBlock<C> { if block.is_null() { return None; } - Some(Ref::new(BasicBlock::from_raw(block, self.context.clone()))) + Some(BasicBlock::ref_from_raw(block, self.context.clone())) } } @@ -237,6 +258,24 @@ impl<C: BlockContext> BasicBlock<C> { // TODO iterated dominance frontier } +impl<C: BlockContext> Hash for BasicBlock<C> { + fn hash<H: Hasher>(&self, state: &mut H) { + self.function().hash(state); + self.block_type().hash(state); + state.write_usize(self.index()); + } +} + +impl<C: BlockContext> PartialEq for BasicBlock<C> { + fn eq(&self, other: &Self) -> bool { + self.function() == other.function() + && self.index() == other.index() + && self.block_type() == other.block_type() + } +} + +impl<C: BlockContext> Eq for BasicBlock<C> {} + impl<C: BlockContext> IntoIterator for &BasicBlock<C> { type Item = C::Instruction; type IntoIter = C::Iter; diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index 43e913d5..af4709e7 100644 --- a/rust/src/binary_view.rs +++ b/rust/src/binary_view.rs @@ -1209,8 +1209,8 @@ pub trait BinaryViewExt: BinaryViewBase { /// Retrieves a list of the next disassembly lines. /// - /// `get_next_linear_disassembly_lines` retrieves an [Array] over [LinearDisassemblyLine] objects for the - /// next disassembly lines, and updates the [LinearViewCursor] passed in. This function can be called + /// Retrieves an [`Array`] over [`LinearDisassemblyLine`] objects for the + /// next disassembly lines, and updates the [`LinearViewCursor`] passed in. This function can be called /// repeatedly to get more lines of linear disassembly. /// /// # Arguments diff --git a/rust/src/collaboration/sync.rs b/rust/src/collaboration/sync.rs index 14e18856..1c11b8f0 100644 --- a/rust/src/collaboration/sync.rs +++ b/rust/src/collaboration/sync.rs @@ -701,7 +701,7 @@ pub fn is_type_archive_snapshot_ignored<S: BnStrCompatible>( pub fn download_type_archive<S: BnStrCompatible>( file: &RemoteFile, location: S, -) -> Result<Option<TypeArchive>, ()> { +) -> Result<Option<Ref<TypeArchive>>, ()> { download_type_archive_with_progress(file, location, NoProgressCallback) } @@ -711,7 +711,7 @@ pub fn download_type_archive_with_progress<S: BnStrCompatible, F: ProgressCallba file: &RemoteFile, location: S, mut progress: F, -) -> Result<Option<TypeArchive>, ()> { +) -> Result<Option<Ref<TypeArchive>>, ()> { let mut value = std::ptr::null_mut(); let db_path = location.into_bytes_with_nul(); let success = unsafe { @@ -724,7 +724,7 @@ pub fn download_type_archive_with_progress<S: BnStrCompatible, F: ProgressCallba ) }; success - .then(|| NonNull::new(value).map(|handle| unsafe { TypeArchive::from_raw(handle) })) + .then(|| NonNull::new(value).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) })) .ok_or(()) } diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index 99938e5f..5f9ed559 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -30,9 +30,10 @@ pub type DisassemblyOption = BNDisassemblyOption; pub type InstructionTextTokenType = BNInstructionTextTokenType; pub type StringType = BNStringType; -#[derive(Clone, PartialEq, Debug, Default)] +#[derive(Clone, PartialEq, Debug, Default, Eq)] pub struct DisassemblyTextLine { pub address: u64, + // TODO: This is not always available. pub instruction_index: usize, pub tokens: Vec<InstructionTextToken>, pub highlight: HighlightColor, @@ -234,7 +235,7 @@ impl DisassemblyTextLineTypeInfo { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct InstructionTextToken { pub address: u64, pub text: String, @@ -882,6 +883,8 @@ impl From<InstructionTextTokenKind> for BNInstructionTextTokenType { } } +impl Eq for InstructionTextTokenKind {} + #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum InstructionTextTokenContext { Normal, diff --git a/rust/src/flowgraph.rs b/rust/src/flowgraph.rs index ead984f9..f5d4ed80 100644 --- a/rust/src/flowgraph.rs +++ b/rust/src/flowgraph.rs @@ -14,12 +14,15 @@ //! Interfaces for creating and displaying pretty CFGs in Binary Ninja. -use binaryninjacore_sys::*; - use crate::disassembly::DisassemblyTextLine; +use binaryninjacore_sys::*; +use std::slice; use crate::rc::*; +use crate::basic_block::{BasicBlock, BlockContext}; +use crate::function::HighlightColor; +use crate::render_layer::CoreRenderLayer; use std::marker::PhantomData; pub type BranchType = BNBranchType; @@ -37,14 +40,61 @@ impl FlowGraph { Self { handle: raw } } + pub(crate) unsafe fn ref_from_raw(raw: *mut BNFlowGraph) -> Ref<Self> { + Ref::new(Self { handle: raw }) + } + pub fn new() -> Ref<Self> { - unsafe { Ref::new(FlowGraph::from_raw(BNCreateFlowGraph())) } + unsafe { FlowGraph::ref_from_raw(BNCreateFlowGraph()) } + } + + pub fn nodes<'a>(&self) -> Vec<Ref<FlowGraphNode<'a>>> { + let mut count: usize = 0; + let nodes_ptr = unsafe { BNGetFlowGraphNodes(self.handle, &mut count as *mut usize) }; + + let nodes = unsafe { slice::from_raw_parts_mut(nodes_ptr, count) }; + + let mut result = vec![]; + result.reserve(count); + + for i in 0..count { + result.push(unsafe { RefCountable::inc_ref(&FlowGraphNode::from_raw(nodes[i])) }); + } + + unsafe { BNFreeFlowGraphNodeList(nodes_ptr, count) }; + + result + } + + pub fn get_node<'a>(&self, i: usize) -> Option<Ref<FlowGraphNode<'a>>> { + let node_ptr = unsafe { BNGetFlowGraphNode(self.handle, i) }; + if node_ptr.is_null() { + None + } else { + Some(unsafe { Ref::new(FlowGraphNode::from_raw(node_ptr)) }) + } + } + + pub fn get_node_count(&self) -> usize { + unsafe { BNGetFlowGraphNodeCount(self.handle) } + } + + pub fn has_nodes(&self) -> bool { + unsafe { BNFlowGraphHasNodes(self.handle) } } pub fn append(&self, node: &FlowGraphNode) -> usize { unsafe { BNAddFlowGraphNode(self.handle, node.handle) } } + pub fn replace(&self, index: usize, node: &FlowGraphNode) { + unsafe { BNReplaceFlowGraphNode(self.handle, index, node.handle) } + } + + pub fn clear(&self) { + unsafe { BNClearFlowGraphNodes(self.handle) } + } + pub fn set_option(&self, option: FlowGraphOption, value: bool) { unsafe { BNSetFlowGraphOption(self.handle, option, value) } } @@ -52,6 +102,27 @@ impl FlowGraph { pub fn is_option_set(&self, option: FlowGraphOption) -> bool { unsafe { BNIsFlowGraphOptionSet(self.handle, option) } } + + /// A list of the currently applied [`CoreRenderLayer`]'s + pub fn render_layers(&self) -> Array<CoreRenderLayer> { + let mut count: usize = 0; + unsafe { + let handles = BNGetFlowGraphRenderLayers(self.handle, &mut count); + Array::new(handles, count, ()) + } + } + + /// Add a Render Layer to be applied to this [`FlowGraph`]. + /// + /// NOTE: Layers will be applied in the order in which they are added. + pub fn add_render_layer(&self, layer: &CoreRenderLayer) { + unsafe { BNAddFlowGraphRenderLayer(self.handle, layer.handle.as_ptr()) }; + } + + /// Remove a Render Layer from being applied to this [`FlowGraph`]. + pub fn remove_render_layer(&self, layer: &CoreRenderLayer) { + unsafe { BNRemoveFlowGraphRenderLayer(self.handle, layer.handle.as_ptr()) }; + } } unsafe impl RefCountable for FlowGraph { @@ -88,8 +159,37 @@ impl<'a> FlowGraphNode<'a> { } } - pub fn new(graph: &FlowGraph) -> Self { - unsafe { FlowGraphNode::from_raw(BNCreateFlowGraphNode(graph.handle)) } + pub(crate) unsafe fn ref_from_raw(raw: *mut BNFlowGraphNode) -> Ref<Self> { + Ref::new(Self { + handle: raw, + _data: PhantomData, + }) + } + + pub fn new(graph: &FlowGraph) -> Ref<Self> { + unsafe { FlowGraphNode::ref_from_raw(BNCreateFlowGraphNode(graph.handle)) } + } + + pub fn basic_block<C: BlockContext>(&self, context: C) -> Option<Ref<BasicBlock<C>>> { + let block_ptr = unsafe { BNGetFlowGraphBasicBlock(self.handle) }; + if block_ptr.is_null() { + return None; + } + Some(unsafe { BasicBlock::ref_from_raw(block_ptr, context) }) + } + + pub fn set_basic_block<C: BlockContext>(&self, block: Option<&BasicBlock<C>>) { + match block { + Some(block) => unsafe { BNSetFlowGraphBasicBlock(self.handle, block.handle) }, + None => unsafe { BNSetFlowGraphBasicBlock(self.handle, std::ptr::null_mut()) }, + } + } + + pub fn lines(&self) -> Array<DisassemblyTextLine> { + let mut count = 0; + let result = unsafe { BNGetFlowGraphNodeLines(self.handle, &mut count) }; + assert!(!result.is_null()); + unsafe { Array::new(result, count, ()) } } pub fn set_lines(&self, lines: impl IntoIterator<Item = DisassemblyTextLine>) { @@ -106,6 +206,30 @@ impl<'a> FlowGraphNode<'a> { } } + /// Returns the graph position of the node in X, Y form. + pub fn position(&self) -> (i32, i32) { + let pos_x = unsafe { BNGetFlowGraphNodeX(self.handle) }; + let pos_y = unsafe { BNGetFlowGraphNodeY(self.handle) }; + (pos_x, pos_y) + } + + /// Sets the graph position of the node. + pub fn set_position(&self, x: i32, y: i32) { + unsafe { BNFlowGraphNodeSetX(self.handle, x) }; + unsafe { BNFlowGraphNodeSetX(self.handle, y) }; + } + + pub fn highlight_color(&self) -> HighlightColor { + let raw = unsafe { BNGetFlowGraphNodeHighlight(self.handle) }; + HighlightColor::from(raw) + } + + pub fn set_highlight_color(&self, highlight: HighlightColor) { + unsafe { BNSetFlowGraphNodeHighlight(self.handle, highlight.into()) }; + } + + // TODO: Add getters and setters for edges + pub fn add_outgoing_edge( &self, type_: BranchType, diff --git a/rust/src/function.rs b/rust/src/function.rs index d4eb1d53..63ebc82e 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -128,7 +128,7 @@ impl Iterator for NativeBlockIter { } } -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct NativeBlock { _priv: (), } @@ -2346,7 +2346,7 @@ impl Function { /// Flow graph of unresolved stack adjustments pub fn unresolved_stack_adjustment_graph(&self) -> Option<Ref<FlowGraph>> { let graph = unsafe { BNGetUnresolvedStackAdjustmentGraph(self.handle) }; - (!graph.is_null()).then(|| unsafe { Ref::new(FlowGraph::from_raw(graph)) }) + (!graph.is_null()).then(|| unsafe { FlowGraph::ref_from_raw(graph) }) } pub fn create_graph( @@ -2358,7 +2358,7 @@ impl Function { let raw_view_type = FunctionViewType::into_raw(view_type); let result = unsafe { BNCreateFunctionGraph(self.handle, raw_view_type, settings_raw) }; FunctionViewType::free_raw(raw_view_type); - unsafe { Ref::new(FlowGraph::from_raw(result)) } + unsafe { FlowGraph::ref_from_raw(result) } } pub fn parent_components(&self) -> Array<Component> { diff --git a/rust/src/lib.rs b/rust/src/lib.rs index eb89ff0c..0cb0484e 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -68,6 +68,7 @@ pub mod project; pub mod rc; pub mod references; pub mod relocation; +pub mod render_layer; pub mod section; pub mod segment; pub mod settings; diff --git a/rust/src/linear_view.rs b/rust/src/linear_view.rs index dc3ca597..6d2347b3 100644 --- a/rust/src/linear_view.rs +++ b/rust/src/linear_view.rs @@ -18,26 +18,37 @@ use binaryninjacore_sys::*; use crate::binary_view::BinaryView; use crate::disassembly::{DisassemblySettings, DisassemblyTextLine}; -use crate::function::Function; +use crate::function::{Function, NativeBlock}; +use crate::basic_block::BasicBlock; use crate::rc::*; +use crate::render_layer::CoreRenderLayer; +use crate::string::{raw_to_string, BnString}; use std::ops::Deref; -use std::mem; - pub type LinearDisassemblyLineType = BNLinearDisassemblyLineType; +pub type LinearViewObjectIdentifierType = BNLinearViewObjectIdentifierType; -// TODO: Rename to LinearView? pub struct LinearViewObject { pub(crate) handle: *mut BNLinearViewObject, } impl LinearViewObject { + pub(crate) unsafe fn from_raw(handle: *mut BNLinearViewObject) -> Self { + debug_assert!(!handle.is_null()); + Self { handle } + } + pub(crate) unsafe fn ref_from_raw(handle: *mut BNLinearViewObject) -> Ref<Self> { debug_assert!(!handle.is_null()); Ref::new(Self { handle }) } + pub fn identifier(&self) -> LinearViewObjectIdentifier { + let raw = unsafe { BNGetLinearViewObjectIdentifier(self.handle) }; + LinearViewObjectIdentifier::from_owned_raw(raw) + } + pub fn data_only(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> { unsafe { let handle = BNCreateLinearViewDataOnly(view.handle, settings.handle); @@ -214,6 +225,45 @@ impl ToOwned for LinearViewObject { unsafe impl Send for LinearViewObject {} unsafe impl Sync for LinearViewObject {} +#[derive(Clone, PartialEq, Debug)] +pub struct LinearViewObjectIdentifier { + pub name: String, + pub ty: LinearViewObjectIdentifierType, + pub start: u64, + pub end: u64, +} + +impl LinearViewObjectIdentifier { + pub fn from_raw(value: &BNLinearViewObjectIdentifier) -> Self { + Self { + name: raw_to_string(value.name).unwrap(), + ty: value.type_, + start: value.start, + end: value.end, + } + } + + pub fn from_owned_raw(value: BNLinearViewObjectIdentifier) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub fn into_raw(value: Self) -> BNLinearViewObjectIdentifier { + let bn_name = BnString::new(value.name); + BNLinearViewObjectIdentifier { + name: BnString::into_raw(bn_name), + type_: value.ty, + start: value.start, + end: value.end, + } + } + + pub fn free_raw(value: BNLinearViewObjectIdentifier) { + let _ = unsafe { BnString::from_raw(value.name) }; + } +} + #[derive(Eq)] pub struct LinearViewCursor { pub(crate) handle: *mut BNLinearViewCursor, @@ -252,15 +302,15 @@ impl LinearViewCursor { !(self.before_begin() || self.after_end()) } - pub fn seek_to_start(&self) { + pub fn seek_to_start(&mut self) { unsafe { BNSeekLinearViewCursorToBegin(self.handle) } } - pub fn seek_to_end(&self) { + pub fn seek_to_end(&mut self) { unsafe { BNSeekLinearViewCursorToEnd(self.handle) } } - pub fn seek_to_address(&self, address: u64) { + pub fn seek_to_address(&mut self, address: u64) { unsafe { BNSeekLinearViewCursorToAddress(self.handle, address) } } @@ -275,15 +325,15 @@ impl LinearViewCursor { unsafe { BNGetLinearViewCursorOrderingIndexTotal(self.handle) } } - pub fn seek_to_ordering_index(&self, idx: u64) { + pub fn seek_to_ordering_index(&mut self, idx: u64) { unsafe { BNSeekLinearViewCursorToAddress(self.handle, idx) } } - pub fn previous(&self) -> bool { + pub fn previous(&mut self) -> bool { unsafe { BNLinearViewCursorPrevious(self.handle) } } - pub fn next(&self) -> bool { + pub fn next(&mut self) -> bool { unsafe { BNLinearViewCursorNext(self.handle) } } @@ -294,6 +344,27 @@ impl LinearViewCursor { Array::new(handles, count, ()) } } + + /// A list of the currently applied [`CoreRenderLayer`]'s + pub fn render_layers(&self) -> Array<CoreRenderLayer> { + let mut count: usize = 0; + unsafe { + let handles = BNGetLinearViewCursorRenderLayers(self.handle, &mut count); + Array::new(handles, count, ()) + } + } + + /// Add a Render Layer to be applied to this [`LinearViewCursor`]. + /// + /// NOTE: Layers will be applied in the order in which they are added. + pub fn add_render_layer(&self, layer: &CoreRenderLayer) { + unsafe { BNAddLinearViewCursorRenderLayer(self.handle, layer.handle.as_ptr()) }; + } + + /// Remove a Render Layer from being applied to this [`LinearViewCursor`]. + pub fn remove_render_layer(&self, layer: &CoreRenderLayer) { + unsafe { BNRemoveLinearViewCursorRenderLayer(self.handle, layer.handle.as_ptr()) }; + } } impl PartialEq for LinearViewCursor { @@ -341,55 +412,81 @@ impl ToOwned for LinearViewCursor { unsafe impl Send for LinearViewCursor {} unsafe impl Sync for LinearViewCursor {} +#[derive(Clone, PartialEq, Debug, Eq)] pub struct LinearDisassemblyLine { - t: LinearDisassemblyLineType, - - // These will be cleaned up by BNFreeLinearDisassemblyLines, so we - // don't drop them in the relevant deconstructors. - // TODO: This is insane! - function: mem::ManuallyDrop<Ref<Function>>, - contents: mem::ManuallyDrop<DisassemblyTextLine>, + pub ty: LinearDisassemblyLineType, + pub function: Option<Ref<Function>>, + pub basic_block: Option<Ref<BasicBlock<NativeBlock>>>, + pub contents: DisassemblyTextLine, } impl LinearDisassemblyLine { - pub(crate) unsafe fn from_raw(raw: &BNLinearDisassemblyLine) -> Self { - let linetype = raw.type_; - // TODO: We must remove this behavior. - let function = mem::ManuallyDrop::new(Function::ref_from_raw(raw.function)); - let contents = mem::ManuallyDrop::new(DisassemblyTextLine::from_raw(&raw.contents)); + pub(crate) unsafe fn from_raw(value: &BNLinearDisassemblyLine) -> Self { + let function = if !value.function.is_null() { + Some(unsafe { Function::from_raw(value.function).to_owned() }) + } else { + None + }; + let basic_block = if !value.block.is_null() { + Some(unsafe { BasicBlock::from_raw(value.block, NativeBlock::new()).to_owned() }) + } else { + None + }; Self { - t: linetype, + ty: value.type_, function, - contents, + basic_block, + contents: DisassemblyTextLine::from_raw(&value.contents), } } - pub fn function(&self) -> &Function { - self.function.as_ref() + #[allow(unused)] + pub(crate) unsafe fn from_owned_raw(value: BNLinearDisassemblyLine) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNLinearDisassemblyLine { + let function_ptr = value + .function + .map(|f| unsafe { Ref::into_raw(f) }.handle) + .unwrap_or(std::ptr::null_mut()); + let block_ptr = value + .basic_block + .map(|b| unsafe { Ref::into_raw(b) }.handle) + .unwrap_or(std::ptr::null_mut()); + BNLinearDisassemblyLine { + type_: value.ty, + function: function_ptr, + block: block_ptr, + contents: DisassemblyTextLine::into_raw(value.contents), + } } - pub fn line_type(&self) -> LinearDisassemblyLineType { - self.t + pub(crate) fn free_raw(value: BNLinearDisassemblyLine) { + let _ = unsafe { Function::ref_from_raw(value.function) }; + DisassemblyTextLine::free_raw(value.contents); } } impl Deref for LinearDisassemblyLine { type Target = DisassemblyTextLine; fn deref(&self) -> &Self::Target { - self.contents.deref() + &self.contents } } impl std::fmt::Display for LinearDisassemblyLine { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.deref()) + write!(f, "{}", self.contents) } } impl CoreArrayProvider for LinearDisassemblyLine { type Raw = BNLinearDisassemblyLine; type Context = (); - type Wrapped<'a> = Guard<'a, LinearDisassemblyLine>; + type Wrapped<'a> = LinearDisassemblyLine; } unsafe impl CoreArrayProviderInner for LinearDisassemblyLine { @@ -397,8 +494,7 @@ unsafe impl CoreArrayProviderInner for LinearDisassemblyLine { BNFreeLinearDisassemblyLines(raw, count); } - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - // TODO: Cant remove this guard until we remove those manual drops... INSANE! - Guard::new(Self::from_raw(raw), context) + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from_raw(raw) } } diff --git a/rust/src/low_level_il.rs b/rust/src/low_level_il.rs index 5df7252b..750bcfd1 100644 --- a/rust/src/low_level_il.rs +++ b/rust/src/low_level_il.rs @@ -24,7 +24,7 @@ use crate::architecture::Register as ArchReg; use crate::architecture::{Architecture, RegisterId}; use crate::function::Location; -mod block; +pub mod block; pub mod expression; pub mod function; pub mod instruction; diff --git a/rust/src/medium_level_il/function.rs b/rust/src/medium_level_il/function.rs index a9802005..8f91c731 100644 --- a/rust/src/medium_level_il/function.rs +++ b/rust/src/medium_level_il/function.rs @@ -548,10 +548,10 @@ impl MediumLevelILFunction { unsafe { BNGetMediumLevelILSSAVarValue(self.handle, &raw_var, ssa_variable.version) }.into() } - pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> FlowGraph { + pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> Ref<FlowGraph> { let settings = settings.map(|x| x.handle).unwrap_or(std::ptr::null_mut()); let graph = unsafe { BNCreateMediumLevelILFunctionGraph(self.handle, settings) }; - unsafe { FlowGraph::from_raw(graph) } + unsafe { FlowGraph::ref_from_raw(graph) } } /// This gets just the MLIL variables - you may be interested in the union diff --git a/rust/src/render_layer.rs b/rust/src/render_layer.rs new file mode 100644 index 00000000..fa617fe7 --- /dev/null +++ b/rust/src/render_layer.rs @@ -0,0 +1,410 @@ +//! Customize the presentation of Linear and Graph view output. + +use crate::basic_block::{BasicBlock, BasicBlockType}; +use crate::disassembly::DisassemblyTextLine; +use crate::flowgraph::FlowGraph; +use crate::function::{Function, NativeBlock}; +use crate::linear_view::{LinearDisassemblyLine, LinearDisassemblyLineType, LinearViewObject}; +use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner}; +use crate::string::BnStrCompatible; +use binaryninjacore_sys::*; +use std::ffi::{c_char, c_void}; +use std::ptr::NonNull; + +pub type RenderLayerDefaultEnableState = BNRenderLayerDefaultEnableState; + +/// Register a [`RenderLayer`] with the API. +pub fn register_render_layer<S: BnStrCompatible, T: RenderLayer>( + name: S, + render_layer: T, + enable_state: RenderLayerDefaultEnableState, +) -> (&'static mut T, CoreRenderLayer) { + let render_layer = Box::leak(Box::new(render_layer)); + let mut callback = BNRenderLayerCallbacks { + context: render_layer as *mut _ as *mut c_void, + applyToFlowGraph: Some(cb_apply_to_flow_graph::<T>), + applyToLinearViewObject: Some(cb_apply_to_linear_view_object::<T>), + freeLines: Some(cb_free_lines), + }; + let result = unsafe { + BNRegisterRenderLayer( + name.into_bytes_with_nul().as_ref().as_ptr() as *const _, + &mut callback, + enable_state, + ) + }; + let core = CoreRenderLayer::from_raw(NonNull::new(result).unwrap()); + (render_layer, core) +} + +pub trait RenderLayer: Sized { + /// Apply this Render Layer to a Flow Graph. + fn apply_to_flow_graph(&self, graph: &mut FlowGraph) { + for node in graph.nodes() { + if let Some(block) = node.basic_block(NativeBlock::new()) { + let new_lines = self.apply_to_block(&block, node.lines().to_vec()); + node.set_lines(new_lines); + } + } + } + + /// Apply this Render Layer to the lines produced by a LinearViewObject for rendering in Linear View. + fn apply_to_linear_object( + &self, + object: &mut LinearViewObject, + _prev_object: Option<&mut LinearViewObject>, + _next_object: Option<&mut LinearViewObject>, + lines: Vec<LinearDisassemblyLine>, + ) -> Vec<LinearDisassemblyLine> { + let text_to_lines = + |function: &Function, block: &BasicBlock<NativeBlock>, text: DisassemblyTextLine| { + LinearDisassemblyLine { + ty: LinearDisassemblyLineType::CodeDisassemblyLineType, + function: Some(function.to_owned()), + basic_block: Some(block.to_owned()), + contents: text, + } + }; + + // Hack: HLIL bodies don't have basic blocks. + let obj_ident = object.identifier(); + if !lines.is_empty() + && (obj_ident.name.starts_with("HLIL") || obj_ident.name.starts_with("Language")) + { + // Apply to HLIL body. + let function = lines[0] + .function + .to_owned() + .expect("HLIL body has no function"); + return self.apply_to_hlil_body(&function, lines); + } + + // Collect the "line blocks". + // Line blocks are contiguous lines with the same backing basic block (or lack thereof). + // Line blocks also group by line type. + let mut line_blocks: Vec<Vec<LinearDisassemblyLine>> = Vec::new(); + for line in lines { + let Some(last_block) = line_blocks.last_mut() else { + // No last block, create the first block. + line_blocks.push(vec![line]); + continue; + }; + + let Some(last_line) = last_block.last() else { + // No last line, create the first line. + last_block.push(line); + continue; + }; + + // TODO: If we want to allow a block with multiple line types we need to specifically check + // TODO: If the last line type was Code, if it is and the last line is not we make a new block. + if last_line.basic_block == line.basic_block && last_line.ty == line.ty { + // Same basic block and line type, this is a part of the same line block. + last_block.push(line); + } else { + // Not the same line block, create a new block. + line_blocks.push(vec![line]); + } + } + + line_blocks + .into_iter() + .filter_map(|line_block| { + let probe_line = line_block.first()?; + Some((probe_line.ty, probe_line.basic_block.to_owned(), line_block)) + }) + .map(|(line_ty, basic_block, lines)| { + match line_ty { + LinearDisassemblyLineType::CodeDisassemblyLineType => { + // Dealing with code lines. + let block = basic_block.expect("Code line has no basic block"); + let function = block.function(); + let text_lines = lines.into_iter().map(|line| line.contents).collect(); + let new_text_lines = self.apply_to_block(&block, text_lines); + let new_lines = new_text_lines + .into_iter() + .map(|line| text_to_lines(&function, &block, line)) + .collect(); + new_lines + } + _ => { + // Dealing with misc lines. + self.apply_to_misc_lines( + object, + _prev_object.as_deref(), + _next_object.as_deref(), + lines, + ) + } + } + }) + .flatten() + .collect() + } + + /// Apply this Render Layer to a single Basic Block of Disassembly lines. + /// + /// Modify the lines to change the presentation of the block. + fn apply_to_disassembly_block( + &self, + _block: &BasicBlock<NativeBlock>, + lines: Vec<DisassemblyTextLine>, + ) -> Vec<DisassemblyTextLine> { + lines + } + + /// Apply this Render Layer to a single Basic Block of Low Level IL lines. + /// + /// Modify the lines to change the presentation of the block. + fn apply_to_llil_block( + &self, + _block: &BasicBlock<NativeBlock>, + lines: Vec<DisassemblyTextLine>, + ) -> Vec<DisassemblyTextLine> { + lines + } + + /// Apply this Render Layer to a single Basic Block of Medium Level IL lines. + /// + /// Modify the lines to change the presentation of the block. + fn apply_to_mlil_block( + &self, + _block: &BasicBlock<NativeBlock>, + lines: Vec<DisassemblyTextLine>, + ) -> Vec<DisassemblyTextLine> { + lines + } + + /// Apply this Render Layer to a single Basic Block of High Level IL lines. + /// + /// Modify the lines to change the presentation of the block. + /// + /// This function will NOT apply to High Level IL bodies as displayed in Linear View! + /// Those are handled by [`RenderLayer::apply_to_hlil_body`] instead as they do not + /// have a [`BasicBlock`] associated with them. + fn apply_to_hlil_block( + &self, + _block: &BasicBlock<NativeBlock>, + lines: Vec<DisassemblyTextLine>, + ) -> Vec<DisassemblyTextLine> { + lines + } + + /// Apply this Render Layer to the entire body of a High Level IL function. + /// + /// Modify the lines to change the presentation of the block. + /// + /// This function only applies to Linear View, and not to Graph View! If you want to + /// handle Graph View too, you will need to use [`RenderLayer::apply_to_hlil_block`] and handle + /// the lines one block at a time. + fn apply_to_hlil_body( + &self, + _function: &Function, + lines: Vec<LinearDisassemblyLine>, + ) -> Vec<LinearDisassemblyLine> { + lines + } + + // TODO: We might want to just go ahead and pass the line type. + /// Apply to lines generated by Linear View that are not part of a function. + /// + /// Modify the lines to change the presentation of the block. + fn apply_to_misc_lines( + &self, + _object: &mut LinearViewObject, + _prev_object: Option<&LinearViewObject>, + _next_object: Option<&LinearViewObject>, + lines: Vec<LinearDisassemblyLine>, + ) -> Vec<LinearDisassemblyLine> { + lines + } + + /// Apply this Render Layer to all IL blocks and disassembly blocks. + /// + /// If not implemented this will handle calling the view specific apply functions: + /// + /// - [`RenderLayer::apply_to_disassembly_block`] + /// - [`RenderLayer::apply_to_llil_block`] + /// - [`RenderLayer::apply_to_mlil_block`] + /// - [`RenderLayer::apply_to_hlil_block`] + /// + /// Modify the lines to change the presentation of the block. + fn apply_to_block( + &self, + block: &BasicBlock<NativeBlock>, + lines: Vec<DisassemblyTextLine>, + ) -> Vec<DisassemblyTextLine> { + match block.block_type() { + BasicBlockType::Native => self.apply_to_disassembly_block(block, lines), + BasicBlockType::LowLevelIL => self.apply_to_llil_block(block, lines), + BasicBlockType::MediumLevelIL => self.apply_to_mlil_block(block, lines), + BasicBlockType::HighLevelIL => self.apply_to_hlil_block(block, lines), + } + } +} + +#[repr(transparent)] +pub struct CoreRenderLayer { + pub(crate) handle: NonNull<BNRenderLayer>, +} + +impl CoreRenderLayer { + pub fn from_raw(handle: NonNull<BNRenderLayer>) -> Self { + Self { handle } + } + + pub fn render_layers() -> Array<CoreRenderLayer> { + let mut count = 0; + let result = unsafe { BNGetRenderLayerList(&mut count) }; + unsafe { Array::new(result, count, ()) } + } + + pub fn render_layer_by_name<S: BnStrCompatible>(name: S) -> Option<CoreRenderLayer> { + let name_raw = name.into_bytes_with_nul(); + let result = unsafe { BNGetRenderLayerByName(name_raw.as_ref().as_ptr() as *const c_char) }; + NonNull::new(result).map(|x| Self::from_raw(x)) + } + + pub fn default_enable_state(&self) -> RenderLayerDefaultEnableState { + unsafe { BNGetRenderLayerDefaultEnableState(self.handle.as_ptr()) } + } + + pub fn apply_to_flow_graph(&self, graph: &FlowGraph) { + unsafe { BNApplyRenderLayerToFlowGraph(self.handle.as_ptr(), graph.handle) } + } + + pub fn apply_to_linear_view_object( + &self, + object: &LinearViewObject, + prev_object: Option<&LinearViewObject>, + next_object: Option<&LinearViewObject>, + lines: Vec<LinearDisassemblyLine>, + ) -> Vec<LinearDisassemblyLine> { + let mut lines_raw: Vec<_> = lines + .into_iter() + // NOTE: Freed after the core call + .map(LinearDisassemblyLine::into_raw) + .collect(); + + let prev_object_ptr = prev_object + .map(|o| o.handle) + .unwrap_or(std::ptr::null_mut()); + let next_object_ptr = next_object + .map(|o| o.handle) + .unwrap_or(std::ptr::null_mut()); + + let mut new_lines = std::ptr::null_mut(); + let mut new_line_count = 0; + + unsafe { + BNApplyRenderLayerToLinearViewObject( + self.handle.as_ptr(), + object.handle, + prev_object_ptr, + next_object_ptr, + lines_raw.as_mut_ptr(), + lines_raw.len(), + &mut new_lines, + &mut new_line_count, + ) + }; + + for line in lines_raw { + LinearDisassemblyLine::free_raw(line); + } + + let raw: Array<LinearDisassemblyLine> = + unsafe { Array::new(new_lines, new_line_count, ()) }; + raw.to_vec() + } +} + +impl CoreArrayProvider for CoreRenderLayer { + type Raw = *mut BNRenderLayer; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for CoreRenderLayer { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeRenderLayerList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + // TODO: Because handle is a NonNull we should prob make Self::Raw that as well... + let handle = NonNull::new(*raw).unwrap(); + CoreRenderLayer::from_raw(handle) + } +} + +unsafe extern "C" fn cb_apply_to_flow_graph<T: RenderLayer>( + ctxt: *mut c_void, + graph: *mut BNFlowGraph, +) { + let ctxt: &mut T = &mut *(ctxt as *mut T); + // SAFETY: We do not own the flowgraph, do not take it as Ref. + let mut flow_graph = FlowGraph::from_raw(graph); + ctxt.apply_to_flow_graph(&mut flow_graph); +} + +unsafe extern "C" fn cb_apply_to_linear_view_object<T: RenderLayer>( + ctxt: *mut c_void, + object: *mut BNLinearViewObject, + prev: *mut BNLinearViewObject, + next: *mut BNLinearViewObject, + in_lines: *mut BNLinearDisassemblyLine, + in_line_count: usize, + out_lines: *mut *mut BNLinearDisassemblyLine, + out_line_count: *mut usize, +) { + let ctxt: &mut T = &mut *(ctxt as *mut T); + // SAFETY: We do not own the flowgraph, do not take it as Ref. + let mut object = LinearViewObject::from_raw(object); + let mut prev_object = if !prev.is_null() { + Some(LinearViewObject::from_raw(prev)) + } else { + None + }; + let mut next_object = if !next.is_null() { + Some(LinearViewObject::from_raw(next)) + } else { + None + }; + + let raw_lines = std::slice::from_raw_parts(in_lines, in_line_count); + // NOTE: The caller is owned of the inLines. + let lines: Vec<_> = raw_lines + .iter() + .map(|line| LinearDisassemblyLine::from_raw(line)) + .collect(); + + let new_lines = ctxt.apply_to_linear_object( + &mut object, + prev_object.as_mut(), + next_object.as_mut(), + lines, + ); + + unsafe { + *out_line_count = new_lines.len(); + let boxed_new_lines: Box<[_]> = new_lines + .into_iter() + // NOTE: Freed by cb_free_lines + .map(LinearDisassemblyLine::into_raw) + .collect(); + // NOTE: Dropped by cb_free_lines + *out_lines = Box::leak(boxed_new_lines).as_mut_ptr(); + } +} + +unsafe extern "C" fn cb_free_lines( + _ctxt: *mut c_void, + lines: *mut BNLinearDisassemblyLine, + line_count: usize, +) { + let lines_ptr = std::ptr::slice_from_raw_parts_mut(lines, line_count); + let boxed_lines = Box::from_raw(lines_ptr); + for line in boxed_lines { + LinearDisassemblyLine::free_raw(line); + } +} diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs index 68cd3e18..e95beb48 100644 --- a/rust/src/workflow.rs +++ b/rust/src/workflow.rs @@ -565,7 +565,7 @@ impl Workflow { &self, activity: A, sequential: Option<bool>, - ) -> Option<FlowGraph> { + ) -> Option<Ref<FlowGraph>> { let sequential = sequential.unwrap_or(false); let activity_name = activity.into_bytes_with_nul(); let graph = unsafe { @@ -578,7 +578,7 @@ impl Workflow { if graph.is_null() { return None; } - Some(unsafe { FlowGraph::from_raw(graph) }) + Some(unsafe { FlowGraph::ref_from_raw(graph) }) } /// Not yet implemented. |
