summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-02-02 22:08:16 -0500
committerMason Reed <mason@vector35.com>2025-07-02 01:56:53 -0400
commit265b2984b0fad7332680ffd5d44cc9d25f9ad457 (patch)
treefbfc51287ae67872d866fe631337383edb62dadd /plugins
parentdb8f0a498947c6bbd93526520b4a51f758cae540 (diff)
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
Diffstat (limited to 'plugins')
-rw-r--r--plugins/warp/src/lib.rs163
1 files changed, 98 insertions, 65 deletions
diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs
index 62af0102..6d147bdc 100644
--- a/plugins/warp/src/lib.rs
+++ b/plugins/warp/src/lib.rs
@@ -6,7 +6,7 @@ use binaryninja::architecture::{
Architecture, ImplicitRegisterExtend, Register as BNRegister, RegisterInfo,
};
use binaryninja::basic_block::BasicBlock as BNBasicBlock;
-use binaryninja::binary_view::BinaryViewExt;
+use binaryninja::binary_view::{BinaryView, BinaryViewExt};
use binaryninja::confidence::MAX_CONFIDENCE;
use binaryninja::function::{Function as BNFunction, NativeBlock};
use binaryninja::low_level_il::expression::{ExpressionHandler, LowLevelILExpressionKind};
@@ -16,6 +16,7 @@ use binaryninja::low_level_il::instruction::{
};
use binaryninja::low_level_il::{LowLevelILRegisterKind, VisitorAction};
use binaryninja::rc::Ref as BNRef;
+use std::ops::Range;
use std::path::PathBuf;
use warp::signature::basic_block::BasicBlockGUID;
use warp::signature::function::constraints::FunctionConstraints;
@@ -78,15 +79,18 @@ pub fn function_guid<M: FunctionMutability>(
func: &BNFunction,
llil: &LowLevelILFunction<M, NonSSA>,
) -> FunctionGUID {
+ // TODO: We might want to make this configurable, or otherwise _not_ retrieve from the view here.
+ let relocatable_regions = relocatable_regions(&func.view());
let basic_blocks = sorted_basic_blocks(func);
let basic_block_guids = basic_blocks
.iter()
- .map(|bb| basic_block_guid(bb, llil))
+ .map(|bb| basic_block_guid(&relocatable_regions, bb, llil))
.collect::<Vec<_>>();
FunctionGUID::from_basic_blocks(&basic_block_guids)
}
pub fn basic_block_guid<M: FunctionMutability>(
+ relocatable_regions: &[Range<u64>],
basic_block: &BNBasicBlock<NativeBlock>,
llil: &LowLevelILFunction<M, NonSSA>,
) -> BasicBlockGUID {
@@ -95,67 +99,6 @@ pub fn basic_block_guid<M: FunctionMutability>(
let arch = func.arch();
let max_instr_len = arch.max_instr_len();
- // NOPs and useless moves are blacklisted to allow for hot-patchable functions.
- let is_blacklisted_instr = |instr: &LowLevelILInstruction<M, NonSSA>| {
- match instr.kind() {
- LowLevelILInstructionKind::Nop(_) => true,
- LowLevelILInstructionKind::SetReg(op) => {
- match op.source_expr().kind() {
- LowLevelILExpressionKind::Reg(source_op)
- if op.dest_reg() == source_op.source_reg() =>
- {
- match op.dest_reg() {
- LowLevelILRegisterKind::Arch(r) => {
- // If this register has no implicit extend then we can safely assume it's a NOP.
- // Ex. on x86_64 we don't want to remove `mov edi, edi` as it will zero the upper 32 bits.
- // Ex. on x86 we do want to remove `mov edi, edi` as it will not have a side effect like above.
- matches!(
- r.info().implicit_extend(),
- ImplicitRegisterExtend::NoExtend
- )
- }
- LowLevelILRegisterKind::Temp(_) => false,
- }
- }
- _ => false,
- }
- }
- _ => false,
- }
- };
-
- let is_variant_instr = |instr: &LowLevelILInstruction<M, NonSSA>| {
- let is_variant_expr = |expr: &LowLevelILExpressionKind<M, NonSSA>| {
- // TODO: Checking the section here is slow, we should gather all section ranges outside of this.
- match expr {
- LowLevelILExpressionKind::ConstPtr(op)
- if !view.sections_at(op.value()).is_empty() =>
- {
- // Constant Pointer must be in a section for it to be relocatable.
- // NOTE: We cannot utilize segments here as there will be a zero based segment.
- true
- }
- LowLevelILExpressionKind::ExternPtr(_) => true,
- LowLevelILExpressionKind::Const(op) if !view.sections_at(op.value()).is_empty() => {
- // Constant value must be in a section for it to be relocatable.
- // NOTE: We cannot utilize segments here as there will be a zero based segment.
- true
- }
- _ => false,
- }
- };
-
- // Visit instruction expressions looking for variant expression, [VisitorAction::Halt] means variant.
- instr.visit_tree(&mut |expr| {
- if is_variant_expr(&expr.kind()) {
- // Found a variant expression
- VisitorAction::Halt
- } else {
- VisitorAction::Descend
- }
- }) == VisitorAction::Halt
- };
-
let basic_block_range = basic_block.start_index()..basic_block.end_index();
let mut basic_block_bytes = Vec::with_capacity(basic_block_range.count());
for instr_addr in basic_block.into_iter() {
@@ -164,8 +107,8 @@ pub fn basic_block_guid<M: FunctionMutability>(
instr_bytes.truncate(instr_info.length);
if let Some(instr_llil) = llil.instruction_at(instr_addr) {
// If instruction is blacklisted don't include the bytes.
- if !is_blacklisted_instr(&instr_llil) {
- if is_variant_instr(&instr_llil) {
+ if !is_blacklisted_instruction(&instr_llil) {
+ if is_variant_instruction(relocatable_regions, &instr_llil) {
// Found a variant instruction, mask off entire instruction.
instr_bytes.fill(0);
}
@@ -179,6 +122,96 @@ pub fn basic_block_guid<M: FunctionMutability>(
BasicBlockGUID::from(basic_block_bytes.as_slice())
}
+/// Is the instruction not included in the masked byte sequence?
+///
+/// Blacklisted instructions will make an otherwise identical function GUID fail to match.
+///
+/// Example: NOPs and useless moves are blacklisted to allow for hot-patchable functions.
+pub fn is_blacklisted_instruction<A: Architecture, M: FunctionMutability>(
+ instr: &LowLevelILInstruction<A, M, NonSSA<RegularNonSSA>>,
+) -> bool {
+ match instr.kind() {
+ LowLevelILInstructionKind::Nop(_) => true,
+ LowLevelILInstructionKind::SetReg(op) => {
+ match op.source_expr().kind() {
+ LowLevelILExpressionKind::Reg(source_op)
+ if op.dest_reg() == source_op.source_reg() =>
+ {
+ match op.dest_reg() {
+ LowLevelILRegister::ArchReg(r) => {
+ // If this register has no implicit extend then we can safely assume it's a NOP.
+ // Ex. on x86_64 we don't want to remove `mov edi, edi` as it will zero the upper 32 bits.
+ // Ex. on x86 we do want to remove `mov edi, edi` as it will not have a side effect like above.
+ matches!(r.info().implicit_extend(), ImplicitRegisterExtend::NoExtend)
+ }
+ LowLevelILRegister::Temp(_) => false,
+ }
+ }
+ _ => false,
+ }
+ }
+ _ => false,
+ }
+}
+
+pub fn is_variant_instruction<A: Architecture, M: FunctionMutability>(
+ relocatable_regions: &[Range<u64>],
+ instr: &LowLevelILInstruction<A, M, NonSSA<RegularNonSSA>>,
+) -> bool {
+ let is_variant_expr = |expr: &LowLevelILExpressionKind<A, M, NonSSA<RegularNonSSA>>| {
+ match expr {
+ LowLevelILExpressionKind::ConstPtr(op)
+ if is_address_relocatable(relocatable_regions, op.value()) =>
+ {
+ // Constant Pointer must be in a section for it to be relocatable.
+ // NOTE: We cannot utilize segments here as there will be a zero based segment.
+ true
+ }
+ LowLevelILExpressionKind::Const(op)
+ if is_address_relocatable(relocatable_regions, op.value()) =>
+ {
+ // Constant value must be in a section for it to be relocatable.
+ // NOTE: We cannot utilize segments here as there will be a zero based segment.
+ true
+ }
+ LowLevelILExpressionKind::ExternPtr(_) => true,
+ _ => false,
+ }
+ };
+
+ // Visit instruction expressions looking for variant expression, [VisitorAction::Halt] means variant.
+ instr.visit_tree(&mut |expr| {
+ if is_variant_expr(&expr.kind()) {
+ // Found a variant expression.
+ VisitorAction::Halt
+ } else {
+ // Keep looking for a variant expression.
+ VisitorAction::Descend
+ }
+ }) == VisitorAction::Halt
+}
+
+/// If the address is inside any of the given ranges we will assume the address to be relocatable.
+pub fn is_address_relocatable(relocatable_regions: &[Range<u64>], address: u64) -> bool {
+ relocatable_regions
+ .iter()
+ .any(|range| range.contains(&address))
+}
+
+// TODO: This might need to be configurable, in that case we better remove this function.
+/// Get the relocatable regions of the view.
+///
+/// Currently, this is all the sections, however this might be refined later.
+pub fn relocatable_regions(view: &BinaryView) -> Vec<Range<u64>> {
+ view.sections()
+ .iter()
+ .map(|s| Range {
+ start: s.start(),
+ end: s.end(),
+ })
+ .collect()
+}
+
#[cfg(test)]
mod tests {
use crate::cache::cached_function_guid;