Skip to content

Commit 1a45f7a

Browse files
committed
Move instruction filters out of basic_block_guid and make them public
Need to do this for exposing to FFI and things like render layers
1 parent d434432 commit 1a45f7a

File tree

2 files changed

+102
-67
lines changed

2 files changed

+102
-67
lines changed

plugins/warp/src/lib.rs

Lines changed: 98 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use binaryninja::architecture::{
66
Architecture, ImplicitRegisterExtend, Register as BNRegister, RegisterInfo,
77
};
88
use binaryninja::basic_block::BasicBlock as BNBasicBlock;
9-
use binaryninja::binary_view::BinaryViewExt;
9+
use binaryninja::binary_view::{BinaryView, BinaryViewExt};
1010
use binaryninja::confidence::MAX_CONFIDENCE;
1111
use binaryninja::function::{Function as BNFunction, NativeBlock};
1212
use binaryninja::low_level_il::expression::{ExpressionHandler, LowLevelILExpressionKind};
@@ -18,6 +18,7 @@ use binaryninja::low_level_il::instruction::{
1818
};
1919
use binaryninja::low_level_il::{LowLevelILRegister, VisitorAction};
2020
use binaryninja::rc::Ref as BNRef;
21+
use std::ops::Range;
2122
use std::path::PathBuf;
2223
use warp::signature::basic_block::BasicBlockGUID;
2324
use 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

9194
pub 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)]
185218
mod tests {
186219
use crate::cache::cached_function_guid;

plugins/warp/src/plugin/ffi.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use crate::basic_block_guid;
21
use crate::cache::{cached_function_guid, insert_cached_function_match, try_cached_function_match};
32
use crate::convert::{to_bn_symbol_at_address, to_bn_type};
43
use crate::matcher::cached_possible_function_matches;
4+
use crate::{basic_block_guid, relocatable_regions};
55
use binaryninja::basic_block::BasicBlock;
66
use binaryninja::function::{Function, NativeBlock};
77
use binaryninja::platform::Platform;
@@ -32,7 +32,9 @@ pub extern "C" fn BNWARPGetBasicBlockGUID(basic_block: *mut BNBasicBlock) -> *co
3232
let Ok(llil) = function.low_level_il() else {
3333
return std::ptr::null();
3434
};
35-
let basic_block_guid = basic_block_guid(&basic_block, &llil);
35+
// TODO: This should be the callers responsibility IMO to get relocatable ranges.
36+
let relocatable_regions = relocatable_regions(&function.view());
37+
let basic_block_guid = basic_block_guid(&relocatable_regions, &basic_block, &llil);
3638
let basic_block_guid_str = BnString::new(basic_block_guid.to_string());
3739
// NOTE: Leak the guid string to be freed by BNFreeString
3840
BnString::into_raw(basic_block_guid_str)

0 commit comments

Comments
 (0)