diff --git a/rust/src/basic_block.rs b/rust/src/basic_block.rs index b880d28f5..8846a90be 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::Iter; } -#[derive(PartialEq, Eq, Hash)] +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)] +pub enum BasicBlockType { + Native, + LowLevelIL, + MediumLevelIL, + HighLevelIL, +} + pub struct BasicBlock { pub(crate) handle: *mut BNBasicBlock, context: C, @@ -127,6 +135,19 @@ impl BasicBlock { } } + 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 BasicBlock { 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 BasicBlock { // TODO iterated dominance frontier } +impl Hash for BasicBlock { + fn hash(&self, state: &mut H) { + self.function().hash(state); + self.block_type().hash(state); + state.write_usize(self.index()); + } +} + +impl PartialEq for BasicBlock { + fn eq(&self, other: &Self) -> bool { + self.function() == other.function() + && self.index() == other.index() + && self.block_type() == other.block_type() + } +} + +impl Eq for BasicBlock {} + impl IntoIterator for &BasicBlock { type Item = C::Instruction; type IntoIter = C::Iter; diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index 43e913d59..6b8e323d1 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 @@ -1218,8 +1218,8 @@ pub trait BinaryViewExt: BinaryViewBase { fn get_next_linear_disassembly_lines( &self, pos: &mut LinearViewCursor, - ) -> Array { - let mut result = unsafe { Array::new(std::ptr::null_mut(), 0, ()) }; + ) -> Array> { + let mut result = unsafe { Array::new(std::ptr::null_mut(), 0, NativeBlock::new()) }; while result.is_empty() { result = pos.lines(); @@ -1242,8 +1242,8 @@ pub trait BinaryViewExt: BinaryViewBase { fn get_previous_linear_disassembly_lines( &self, pos: &mut LinearViewCursor, - ) -> Array { - let mut result = unsafe { Array::new(std::ptr::null_mut(), 0, ()) }; + ) -> Array> { + let mut result = unsafe { Array::new(std::ptr::null_mut(), 0, NativeBlock::new()) }; while result.is_empty() { if !pos.previous() { return result; diff --git a/rust/src/collaboration/sync.rs b/rust/src/collaboration/sync.rs index 14e188566..1c11b8f06 100644 --- a/rust/src/collaboration/sync.rs +++ b/rust/src/collaboration/sync.rs @@ -701,7 +701,7 @@ pub fn is_type_archive_snapshot_ignored( pub fn download_type_archive( file: &RemoteFile, location: S, -) -> Result, ()> { +) -> Result>, ()> { download_type_archive_with_progress(file, location, NoProgressCallback) } @@ -711,7 +711,7 @@ pub fn download_type_archive_with_progress Result, ()> { +) -> Result>, ()> { 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 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 ead984f99..4458645cd 100644 --- a/rust/src/flowgraph.rs +++ b/rust/src/flowgraph.rs @@ -14,12 +14,14 @@ //! 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 std::marker::PhantomData; pub type BranchType = BNBranchType; @@ -37,14 +39,61 @@ impl FlowGraph { Self { handle: raw } } + pub(crate) unsafe fn ref_from_raw(raw: *mut BNFlowGraph) -> Ref { + Ref::new(Self { handle: raw }) + } + pub fn new() -> Ref { - unsafe { Ref::new(FlowGraph::from_raw(BNCreateFlowGraph())) } + unsafe { FlowGraph::ref_from_raw(BNCreateFlowGraph()) } + } + + pub fn nodes<'a>(&self) -> Vec>> { + 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>> { + 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) } } @@ -88,8 +137,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 { + Ref::new(Self { + handle: raw, + _data: PhantomData, + }) + } + + pub fn new(graph: &FlowGraph) -> Ref { + unsafe { FlowGraphNode::ref_from_raw(BNCreateFlowGraphNode(graph.handle)) } + } + + pub fn basic_block(&self, context: C) -> Option>> { + 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(&self, block: Option<&BasicBlock>) { + match block { + Some(block) => unsafe { BNSetFlowGraphBasicBlock(self.handle, block.handle) }, + None => unsafe { BNSetFlowGraphBasicBlock(self.handle, std::ptr::null_mut()) }, + } + } + + pub fn lines(&self) -> Array { + 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) { @@ -106,6 +184,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 d4eb1d532..63ebc82e5 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> { 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 { diff --git a/rust/src/lib.rs b/rust/src/lib.rs index eb89ff0c8..0cb0484e6 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 dc3ca5970..aa5cf8264 100644 --- a/rust/src/linear_view.rs +++ b/rust/src/linear_view.rs @@ -18,26 +18,36 @@ 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, BlockContext}; use crate::rc::*; +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 { 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 { unsafe { let handle = BNCreateLinearViewDataOnly(view.handle, settings.handle); @@ -214,6 +224,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 +301,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,23 +324,23 @@ 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) } } - pub fn lines(&self) -> Array { + pub fn lines(&self) -> Array> { let mut count: usize = 0; unsafe { let handles = BNGetLinearViewCursorLines(self.handle, &mut count); - Array::new(handles, count, ()) + Array::new(handles, count, NativeBlock::new()) } } } @@ -341,64 +390,74 @@ impl ToOwned for LinearViewCursor { unsafe impl Send for LinearViewCursor {} unsafe impl Sync for LinearViewCursor {} -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>, - contents: mem::ManuallyDrop, +#[derive(Clone, PartialEq, Debug, Eq)] +pub struct LinearDisassemblyLine { + pub ty: LinearDisassemblyLineType, + pub function: Ref, + pub basic_block: Ref>, + 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)); +impl LinearDisassemblyLine { + pub(crate) unsafe fn from_raw(value: &BNLinearDisassemblyLine, block_context: C) -> Self { Self { - t: linetype, - function, - contents, + ty: value.type_, + function: unsafe { Function::from_raw(value.function).to_owned() }, + basic_block: unsafe { BasicBlock::from_raw(value.block, block_context).to_owned() }, + 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, block_context: C) -> Self { + let owned = Self::from_raw(&value, block_context); + Self::free_raw(value); + owned + } + + pub(crate) fn into_raw(value: Self) -> BNLinearDisassemblyLine { + BNLinearDisassemblyLine { + type_: value.ty, + function: unsafe { Ref::into_raw(value.function) }.handle, + block: unsafe { Ref::into_raw(value.basic_block) }.handle, + 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 { +impl Deref for LinearDisassemblyLine { type Target = DisassemblyTextLine; fn deref(&self) -> &Self::Target { - self.contents.deref() + &self.contents } } -impl std::fmt::Display for LinearDisassemblyLine { +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 { +impl CoreArrayProvider for LinearDisassemblyLine { type Raw = BNLinearDisassemblyLine; - type Context = (); - type Wrapped<'a> = Guard<'a, LinearDisassemblyLine>; + type Context = C; + type Wrapped<'a> + = LinearDisassemblyLine + where + C: 'a; } -unsafe impl CoreArrayProviderInner for LinearDisassemblyLine { +unsafe impl CoreArrayProviderInner for LinearDisassemblyLine { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { 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) + Self::from_raw(raw, context.clone()) } } diff --git a/rust/src/low_level_il.rs b/rust/src/low_level_il.rs index 5df7252b9..750bcfd1a 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 a98020055..8f91c7311 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) -> FlowGraph { + pub fn create_graph(&self, settings: Option) -> Ref { 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 000000000..e11f77141 --- /dev/null +++ b/rust/src/render_layer.rs @@ -0,0 +1,372 @@ +//! 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::collections::HashMap; +use std::ffi::{c_char, c_void}; +use std::ptr::NonNull; + +/// Register a [`RenderLayer`] with the API. +pub fn register_render_layer( + name: S, + render_layer: T, +) -> (&'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::), + applyToLinearViewObject: Some(cb_apply_to_linear_view_object::), + freeLines: Some(cb_free_lines), + }; + let result = unsafe { + BNRegisterRenderLayer( + name.into_bytes_with_nul().as_ref().as_ptr() as *const _, + &mut callback, + ) + }; + 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>, + ) -> Vec> { + let text_to_lines = + |function: &Function, block: &BasicBlock, text: DisassemblyTextLine| { + LinearDisassemblyLine { + ty: LinearDisassemblyLineType::CodeDisassemblyLineType, + function: function.to_owned(), + basic_block: 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(); + self.apply_to_hlil_body(&function, lines) + } else { + // Apply to Native/LLIL/MLIL/HLIL block. + let mut basic_blocks: HashMap>> = + HashMap::new(); + // TODO: I dislike this. + for line in lines.iter() { + basic_blocks + .entry(line.basic_block.index()) + .or_default() + .push(line.clone()); + } + + let mut text_lines: Vec<_> = basic_blocks + .into_iter() + .map(|(_, lines)| { + let function = lines[0].function.to_owned(); + let block = lines[0].basic_block.to_owned(); + let (code_lines, other_lines): (Vec<_>, Vec<_>) = lines + .into_iter() + .partition(|line| matches!(line.ty, LinearDisassemblyLineType::CodeDisassemblyLineType)); + let code_text_lines: Vec<_> = code_lines.into_iter().map(|line| line.contents).collect(); + let new_code_text_lines = self.apply_to_block(&block, code_text_lines); + let new_misc_text_lines = self.apply_to_misc_lines(object, _prev_object.as_deref(), _next_object.as_deref(), other_lines); + new_code_text_lines + .into_iter() + .map(move |line| text_to_lines(&function, &block, line)) + .chain(new_misc_text_lines.into_iter()) + }) + .flatten() + .collect(); + + // Sort the text lines by instruction index + text_lines.sort_by_key(|line| line.contents.instruction_index); + + text_lines + } + } + + /// 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, + lines: Vec, + ) -> Vec { + 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, + lines: Vec, + ) -> Vec { + 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, + lines: Vec, + ) -> Vec { + 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, + lines: Vec, + ) -> Vec { + 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>, + ) -> Vec> { + lines + } + + /// 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>, + ) -> Vec> { + 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, + lines: Vec, + ) -> Vec { + 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 { + handle: NonNull, +} + +impl CoreRenderLayer { + pub fn from_raw(handle: NonNull) -> Self { + Self { handle } + } + + pub fn render_layers() -> Array { + let mut count = 0; + let result = unsafe { BNGetRenderLayerList(&mut count) }; + unsafe { Array::new(result, count, ()) } + } + + pub fn render_layer_by_name(name: S) -> Option { + 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 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>, + ) -> Vec> { + 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> = + unsafe { Array::new(new_lines, new_line_count, NativeBlock::new()) }; + 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( + 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( + 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, NativeBlock::new())) + .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 68cd3e18d..e95beb48e 100644 --- a/rust/src/workflow.rs +++ b/rust/src/workflow.rs @@ -565,7 +565,7 @@ impl Workflow { &self, activity: A, sequential: Option, - ) -> Option { + ) -> Option> { 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. diff --git a/rust/tests/render_layer.rs b/rust/tests/render_layer.rs new file mode 100644 index 000000000..b5f82eac7 --- /dev/null +++ b/rust/tests/render_layer.rs @@ -0,0 +1,101 @@ +use binaryninja::basic_block::BasicBlock; +use binaryninja::binary_view::BinaryViewExt; +use binaryninja::disassembly::{DisassemblyOption, DisassemblySettings, DisassemblyTextLine}; +use binaryninja::function::NativeBlock; +use binaryninja::headless::Session; +use binaryninja::linear_view::LinearViewObject; +use binaryninja::render_layer::{register_render_layer, CoreRenderLayer, RenderLayer}; +use rstest::{fixture, rstest}; +use std::path::PathBuf; + +#[fixture] +#[once] +fn session() -> Session { + Session::new().expect("Failed to initialize session") +} + +#[rstest] +fn test_render_layer_register(_session: &Session) { + struct EmptyRenderLayer; + impl RenderLayer for EmptyRenderLayer {} + register_render_layer("Test Render Layer", EmptyRenderLayer); + CoreRenderLayer::render_layer_by_name("Test Render Layer").expect("Failed to get render layer"); +} + +#[rstest] +fn test_render_layer_linear_view(_session: &Session) { + let out_dir = env!("OUT_DIR").parse::().unwrap(); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + + struct NopRenderLayer; + impl RenderLayer for NopRenderLayer { + fn apply_to_disassembly_block( + &self, + _block: &BasicBlock, + lines: Vec, + ) -> Vec { + println!("Nothing added to disassembly block"); + lines + } + } + let (_, nop_render_layer) = register_render_layer("Nop Render Layer", NopRenderLayer); + + // Create linear view object stuff + let settings = DisassemblySettings::new(); + settings.set_option(DisassemblyOption::ShowAddress, false); + settings.set_option(DisassemblyOption::WaitForIL, true); + settings.set_option(DisassemblyOption::IndentHLILBody, false); + settings.set_option(DisassemblyOption::ShowCollapseIndicators, false); + settings.set_option(DisassemblyOption::ShowFunctionHeader, false); + + let linear_view = LinearViewObject::disassembly(&view, &settings); + let mut cursor = linear_view.create_cursor(); + + let entry_function = view.entry_point_function().unwrap(); + + cursor.seek_to_address(entry_function.start()); + let current_object = cursor.current_object(); + let current_lines = cursor.lines().to_vec(); + + let new_lines = nop_render_layer.apply_to_linear_view_object( + ¤t_object, + None, + None, + current_lines.clone(), + ); + + // These should 100% be in the same order. If not that is a bug. + + for (i, (current_line, new_line)) in current_lines.iter().zip(new_lines.iter()).enumerate() { + if current_line != new_line { + assert_eq!( + current_line, new_line, + "Line mismatch at index {}", i + ); + } + } + + struct AddRenderLayer; + impl RenderLayer for AddRenderLayer { + fn apply_to_disassembly_block( + &self, + _block: &BasicBlock, + mut lines: Vec, + ) -> Vec { + println!("Adding to disassembly block"); + lines.push(DisassemblyTextLine::from("heyyyyy")); + lines + } + } + let (_, add_render_layer) = register_render_layer("Add Render Layer", AddRenderLayer); + + let new_lines = add_render_layer.apply_to_linear_view_object( + ¤t_object, + None, + None, + current_lines.clone(), + ); + + // Assert that new_lines is one longer than current_lines (we added on token) + assert_eq!(new_lines.len(), current_lines.len() + 1,); +}