@@ -6,7 +6,7 @@ use binaryninja::architecture::{
66 Architecture , ImplicitRegisterExtend , Register as BNRegister , RegisterInfo ,
77} ;
88use binaryninja:: basic_block:: BasicBlock as BNBasicBlock ;
9- use binaryninja:: binary_view:: BinaryViewExt ;
9+ use binaryninja:: binary_view:: { BinaryView , BinaryViewExt } ;
1010use binaryninja:: confidence:: MAX_CONFIDENCE ;
1111use binaryninja:: function:: { Function as BNFunction , NativeBlock } ;
1212use binaryninja:: low_level_il:: expression:: { ExpressionHandler , LowLevelILExpressionKind } ;
@@ -18,6 +18,7 @@ use binaryninja::low_level_il::instruction::{
1818} ;
1919use binaryninja:: low_level_il:: { LowLevelILRegister , VisitorAction } ;
2020use binaryninja:: rc:: Ref as BNRef ;
21+ use std:: ops:: Range ;
2122use std:: path:: PathBuf ;
2223use warp:: signature:: basic_block:: BasicBlockGUID ;
2324use warp:: signature:: function:: constraints:: FunctionConstraints ;
@@ -80,15 +81,18 @@ pub fn function_guid<A: Architecture, M: FunctionMutability>(
8081 func : & BNFunction ,
8182 llil : & LowLevelILFunction < A , M , NonSSA < RegularNonSSA > > ,
8283) -> FunctionGUID {
84+ // TODO: We might want to make this configurable, or otherwise _not_ retrieve from the view here.
85+ let relocatable_regions = relocatable_regions ( & func. view ( ) ) ;
8386 let basic_blocks = sorted_basic_blocks ( func) ;
8487 let basic_block_guids = basic_blocks
8588 . iter ( )
86- . map ( |bb| basic_block_guid ( bb, llil) )
89+ . map ( |bb| basic_block_guid ( & relocatable_regions , bb, llil) )
8790 . collect :: < Vec < _ > > ( ) ;
8891 FunctionGUID :: from_basic_blocks ( & basic_block_guids)
8992}
9093
9194pub fn basic_block_guid < A : Architecture , M : FunctionMutability > (
95+ relocatable_regions : & [ Range < u64 > ] ,
9296 basic_block : & BNBasicBlock < NativeBlock > ,
9397 llil : & LowLevelILFunction < A , M , NonSSA < RegularNonSSA > > ,
9498) -> BasicBlockGUID {
@@ -97,67 +101,6 @@ pub fn basic_block_guid<A: Architecture, M: FunctionMutability>(
97101 let arch = func. arch ( ) ;
98102 let max_instr_len = arch. max_instr_len ( ) ;
99103
100- // NOPs and useless moves are blacklisted to allow for hot-patchable functions.
101- let is_blacklisted_instr = |instr : & LowLevelILInstruction < A , M , NonSSA < RegularNonSSA > > | {
102- match instr. kind ( ) {
103- LowLevelILInstructionKind :: Nop ( _) => true ,
104- LowLevelILInstructionKind :: SetReg ( op) => {
105- match op. source_expr ( ) . kind ( ) {
106- LowLevelILExpressionKind :: Reg ( source_op)
107- if op. dest_reg ( ) == source_op. source_reg ( ) =>
108- {
109- match op. dest_reg ( ) {
110- LowLevelILRegister :: ArchReg ( r) => {
111- // If this register has no implicit extend then we can safely assume it's a NOP.
112- // Ex. on x86_64 we don't want to remove `mov edi, edi` as it will zero the upper 32 bits.
113- // Ex. on x86 we do want to remove `mov edi, edi` as it will not have a side effect like above.
114- matches ! (
115- r. info( ) . implicit_extend( ) ,
116- ImplicitRegisterExtend :: NoExtend
117- )
118- }
119- LowLevelILRegister :: Temp ( _) => false ,
120- }
121- }
122- _ => false ,
123- }
124- }
125- _ => false ,
126- }
127- } ;
128-
129- let is_variant_instr = |instr : & LowLevelILInstruction < A , M , NonSSA < RegularNonSSA > > | {
130- let is_variant_expr = |expr : & LowLevelILExpressionKind < A , M , NonSSA < RegularNonSSA > > | {
131- // TODO: Checking the section here is slow, we should gather all section ranges outside of this.
132- match expr {
133- LowLevelILExpressionKind :: ConstPtr ( op)
134- if !view. sections_at ( op. value ( ) ) . is_empty ( ) =>
135- {
136- // Constant Pointer must be in a section for it to be relocatable.
137- // NOTE: We cannot utilize segments here as there will be a zero based segment.
138- true
139- }
140- LowLevelILExpressionKind :: ExternPtr ( _) => true ,
141- LowLevelILExpressionKind :: Const ( op) if !view. sections_at ( op. value ( ) ) . is_empty ( ) => {
142- // Constant value must be in a section for it to be relocatable.
143- // NOTE: We cannot utilize segments here as there will be a zero based segment.
144- true
145- }
146- _ => false ,
147- }
148- } ;
149-
150- // Visit instruction expressions looking for variant expression, [VisitorAction::Halt] means variant.
151- instr. visit_tree ( & mut |expr| {
152- if is_variant_expr ( & expr. kind ( ) ) {
153- // Found a variant expression
154- VisitorAction :: Halt
155- } else {
156- VisitorAction :: Descend
157- }
158- } ) == VisitorAction :: Halt
159- } ;
160-
161104 let basic_block_range = basic_block. start_index ( ) ..basic_block. end_index ( ) ;
162105 let mut basic_block_bytes = Vec :: with_capacity ( basic_block_range. count ( ) ) ;
163106 for instr_addr in basic_block. into_iter ( ) {
@@ -166,8 +109,8 @@ pub fn basic_block_guid<A: Architecture, M: FunctionMutability>(
166109 instr_bytes. truncate ( instr_info. length ) ;
167110 if let Some ( instr_llil) = llil. instruction_at ( instr_addr) {
168111 // If instruction is blacklisted don't include the bytes.
169- if !is_blacklisted_instr ( & instr_llil) {
170- if is_variant_instr ( & instr_llil) {
112+ if !is_blacklisted_instruction ( & instr_llil) {
113+ if is_variant_instruction ( relocatable_regions , & instr_llil) {
171114 // Found a variant instruction, mask off entire instruction.
172115 instr_bytes. fill ( 0 ) ;
173116 }
@@ -181,6 +124,96 @@ pub fn basic_block_guid<A: Architecture, M: FunctionMutability>(
181124 BasicBlockGUID :: from ( basic_block_bytes. as_slice ( ) )
182125}
183126
127+ /// Is the instruction not included in the masked byte sequence?
128+ ///
129+ /// Blacklisted instructions will make an otherwise identical function GUID fail to match.
130+ ///
131+ /// Example: NOPs and useless moves are blacklisted to allow for hot-patchable functions.
132+ pub fn is_blacklisted_instruction < A : Architecture , M : FunctionMutability > (
133+ instr : & LowLevelILInstruction < A , M , NonSSA < RegularNonSSA > > ,
134+ ) -> bool {
135+ match instr. kind ( ) {
136+ LowLevelILInstructionKind :: Nop ( _) => true ,
137+ LowLevelILInstructionKind :: SetReg ( op) => {
138+ match op. source_expr ( ) . kind ( ) {
139+ LowLevelILExpressionKind :: Reg ( source_op)
140+ if op. dest_reg ( ) == source_op. source_reg ( ) =>
141+ {
142+ match op. dest_reg ( ) {
143+ LowLevelILRegister :: ArchReg ( r) => {
144+ // If this register has no implicit extend then we can safely assume it's a NOP.
145+ // Ex. on x86_64 we don't want to remove `mov edi, edi` as it will zero the upper 32 bits.
146+ // Ex. on x86 we do want to remove `mov edi, edi` as it will not have a side effect like above.
147+ matches ! ( r. info( ) . implicit_extend( ) , ImplicitRegisterExtend :: NoExtend )
148+ }
149+ LowLevelILRegister :: Temp ( _) => false ,
150+ }
151+ }
152+ _ => false ,
153+ }
154+ }
155+ _ => false ,
156+ }
157+ }
158+
159+ pub fn is_variant_instruction < A : Architecture , M : FunctionMutability > (
160+ relocatable_regions : & [ Range < u64 > ] ,
161+ instr : & LowLevelILInstruction < A , M , NonSSA < RegularNonSSA > > ,
162+ ) -> bool {
163+ let is_variant_expr = |expr : & LowLevelILExpressionKind < A , M , NonSSA < RegularNonSSA > > | {
164+ match expr {
165+ LowLevelILExpressionKind :: ConstPtr ( op)
166+ if is_address_relocatable ( relocatable_regions, op. value ( ) ) =>
167+ {
168+ // Constant Pointer must be in a section for it to be relocatable.
169+ // NOTE: We cannot utilize segments here as there will be a zero based segment.
170+ true
171+ }
172+ LowLevelILExpressionKind :: Const ( op)
173+ if is_address_relocatable ( relocatable_regions, op. value ( ) ) =>
174+ {
175+ // Constant value must be in a section for it to be relocatable.
176+ // NOTE: We cannot utilize segments here as there will be a zero based segment.
177+ true
178+ }
179+ LowLevelILExpressionKind :: ExternPtr ( _) => true ,
180+ _ => false ,
181+ }
182+ } ;
183+
184+ // Visit instruction expressions looking for variant expression, [VisitorAction::Halt] means variant.
185+ instr. visit_tree ( & mut |expr| {
186+ if is_variant_expr ( & expr. kind ( ) ) {
187+ // Found a variant expression.
188+ VisitorAction :: Halt
189+ } else {
190+ // Keep looking for a variant expression.
191+ VisitorAction :: Descend
192+ }
193+ } ) == VisitorAction :: Halt
194+ }
195+
196+ /// If the address is inside any of the given ranges we will assume the address to be relocatable.
197+ pub fn is_address_relocatable ( relocatable_regions : & [ Range < u64 > ] , address : u64 ) -> bool {
198+ relocatable_regions
199+ . iter ( )
200+ . any ( |range| range. contains ( & address) )
201+ }
202+
203+ // TODO: This might need to be configurable, in that case we better remove this function.
204+ /// Get the relocatable regions of the view.
205+ ///
206+ /// Currently, this is all the sections, however this might be refined later.
207+ pub fn relocatable_regions ( view : & BinaryView ) -> Vec < Range < u64 > > {
208+ view. sections ( )
209+ . iter ( )
210+ . map ( |s| Range {
211+ start : s. start ( ) ,
212+ end : s. end ( ) ,
213+ } )
214+ . collect ( )
215+ }
216+
184217#[ cfg( test) ]
185218mod tests {
186219 use crate :: cache:: cached_function_guid;
0 commit comments