Skip to main content

binaryninja/architecture/
hook.rs

1// Copyright 2021-2026 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Hooks that override selected operations of an existing architecture.
16
17use super::*;
18
19// Keep the hook defaults and the Architecture adapter in sync using one signature list.
20macro_rules! architecture_hook {
21    ($(fn $name:ident $(<$lt:lifetime>)? ($($arg:ident: $ty:ty),* $(,)?) -> $ret:ty;)*) => {
22        /// Overrides selected operations of an already registered architecture.
23        ///
24        /// Every method defaults to forwarding to `base`, the previous implementation in the
25        /// hook chain. Override only the methods you need. Use the supplied `base` to delegate:
26        /// looking up the hooked architecture by name inside a callback would call the hook again.
27        ///
28        /// Flag-write operations without a typed representation are delivered as
29        /// [`LowLevelILFlagWriteOp::Raw`], including their complete operand list.
30        ///
31        /// The methods have the same semantics as their counterparts in [`Architecture`].
32        /// Contextual disassembly forwards to the previous contextual implementation; override
33        /// [`ArchitectureHook::instruction_text_with_context`] to change contextual text.
34        ///
35        /// Register implementations with [`register_architecture_hook`].
36        pub trait ArchitectureHook: Send + Sync + 'static {
37            $(
38                #[doc = concat!("See [`Architecture::", stringify!($name), "`].")]
39                fn $name $(<$lt>)? (&self, base: &CoreArchitecture, $($arg: $ty),*) -> $ret {
40                    base.$name($($arg),*)
41                }
42            )*
43        }
44
45        impl<H: ArchitectureHook> Architecture for HookArchitecture<H> {
46            type Handle = CustomArchitectureHandle<Self>;
47            type RegisterInfo = CoreRegisterInfo;
48            type Register = CoreRegister;
49            type RegisterStackInfo = CoreRegisterStackInfo;
50            type RegisterStack = CoreRegisterStack;
51            type Flag = CoreFlag;
52            type FlagWrite = CoreFlagWrite;
53            type FlagClass = CoreFlagClass;
54            type FlagGroup = CoreFlagGroup;
55            type Intrinsic = CoreIntrinsic;
56
57            $(
58                fn $name $(<$lt>)? (&self, $($arg: $ty),*) -> $ret {
59                    self.hook.$name(&self.base, $($arg),*)
60                }
61            )*
62
63            fn handle(&self) -> Self::Handle {
64                self.handle
65            }
66        }
67    };
68}
69
70architecture_hook! {
71    fn endianness() -> Endianness;
72    fn address_size() -> usize;
73    fn default_integer_size() -> usize;
74    fn instruction_alignment() -> usize;
75    fn linear_sweep_initial_alignment() -> usize;
76    fn linear_sweep_analysis_capabilities() -> u32;
77    fn max_instr_len() -> usize;
78    fn opcode_display_len() -> usize;
79    fn associated_arch_by_addr(addr: &mut u64) -> CoreArchitecture;
80    fn instruction_info(data: &[u8], addr: u64) -> Option<InstructionInfo>;
81    fn instruction_text(data: &[u8], addr: u64) -> Option<(usize, Vec<InstructionTextToken>)>;
82    fn instruction_text_with_context(
83        data: &[u8],
84        addr: u64,
85        context: Option<NonNull<c_void>>,
86    ) -> Option<(usize, Vec<InstructionTextToken>)>;
87    fn instruction_llil(
88        data: &[u8],
89        addr: u64,
90        il: &LowLevelILMutableFunction,
91    ) -> Option<(usize, bool)>;
92    fn analyze_basic_blocks(function: &mut Function, context: &mut BasicBlockAnalysisContext) -> ();
93    fn lift_function(
94        function: LowLevelILMutableFunction,
95        context: &mut FunctionLifterContext,
96    ) -> bool;
97    fn flag_write_llil<'a>(
98        flag: CoreFlag,
99        flag_write_type: CoreFlagWrite,
100        op: LowLevelILFlagWriteOp<CoreRegister>,
101        il: &'a LowLevelILMutableFunction,
102    ) -> Option<LowLevelILMutableExpression<'a, ValueExpr>>;
103    fn flags_required_for_flag_condition(
104        condition: FlagCondition,
105        class: Option<CoreFlagClass>,
106    ) -> Vec<CoreFlag>;
107    fn flag_cond_llil<'a>(
108        cond: FlagCondition,
109        class: Option<CoreFlagClass>,
110        il: &'a LowLevelILMutableFunction,
111    ) -> Option<LowLevelILMutableExpression<'a, ValueExpr>>;
112    fn flag_group_llil<'a>(
113        group: CoreFlagGroup,
114        il: &'a LowLevelILMutableFunction,
115    ) -> Option<LowLevelILMutableExpression<'a, ValueExpr>>;
116    fn registers_all() -> Vec<CoreRegister>;
117    fn register_from_id(id: RegisterId) -> Option<CoreRegister>;
118    fn registers_full_width() -> Vec<CoreRegister>;
119    fn registers_global() -> Vec<CoreRegister>;
120    fn registers_system() -> Vec<CoreRegister>;
121    fn stack_pointer_reg() -> Option<CoreRegister>;
122    fn link_reg() -> Option<CoreRegister>;
123    fn register_stacks() -> Vec<CoreRegisterStack>;
124    fn register_stack_from_id(id: RegisterStackId) -> Option<CoreRegisterStack>;
125    fn flags() -> Vec<CoreFlag>;
126    fn flag_from_id(id: FlagId) -> Option<CoreFlag>;
127    fn flag_write_types() -> Vec<CoreFlagWrite>;
128    fn flag_write_from_id(id: FlagWriteId) -> Option<CoreFlagWrite>;
129    fn flag_classes() -> Vec<CoreFlagClass>;
130    fn flag_class_from_id(id: FlagClassId) -> Option<CoreFlagClass>;
131    fn flag_groups() -> Vec<CoreFlagGroup>;
132    fn flag_group_from_id(id: FlagGroupId) -> Option<CoreFlagGroup>;
133    fn intrinsics() -> Vec<CoreIntrinsic>;
134    fn intrinsic_class(id: IntrinsicId) -> BNIntrinsicClass;
135    fn intrinsic_from_id(id: IntrinsicId) -> Option<CoreIntrinsic>;
136    fn can_assemble() -> bool;
137    fn assemble(code: &str, addr: u64) -> Result<Vec<u8>, String>;
138    fn is_never_branch_patch_available(data: &[u8], addr: u64) -> bool;
139    fn is_always_branch_patch_available(data: &[u8], addr: u64) -> bool;
140    fn is_invert_branch_patch_available(data: &[u8], addr: u64) -> bool;
141    fn is_skip_and_return_zero_patch_available(data: &[u8], addr: u64) -> bool;
142    fn is_skip_and_return_value_patch_available(data: &[u8], addr: u64) -> bool;
143    fn convert_to_nop(data: &mut [u8], addr: u64) -> bool;
144    fn always_branch(data: &mut [u8], addr: u64) -> bool;
145    fn invert_branch(data: &mut [u8], addr: u64) -> bool;
146    fn skip_and_return_value(data: &mut [u8], addr: u64, value: u64) -> bool;
147}
148
149struct HookArchitecture<H: ArchitectureHook> {
150    hook: H,
151    base: CoreArchitecture,
152    handle: CustomArchitectureHandle<Self>,
153}
154
155impl<H: ArchitectureHook> AsRef<CoreArchitecture> for HookArchitecture<H> {
156    fn as_ref(&self) -> &CoreArchitecture {
157        &self.base
158    }
159}
160
161/// Installs a hook on `base`, affecting all existing users of that architecture.
162///
163/// Hooks run in reverse registration order. Each callback receives the previous implementation,
164/// which can be called to delegate without recursion. The hook is retained for the lifetime of
165/// the core and cannot be unregistered. The returned reference can be used to access its state.
166///
167/// Like [`register_architecture`], call this during `CorePluginInit`, after the base architecture
168/// is registered and before analysis starts.
169///
170/// ```no_run
171/// use binaryninja::architecture::{
172///     register_architecture_hook, Architecture, ArchitectureHook, CoreArchitecture,
173/// };
174/// use binaryninja::disassembly::{InstructionTextToken, InstructionTextTokenKind};
175///
176/// struct Annotate;
177/// impl ArchitectureHook for Annotate {
178///     fn instruction_text(
179///         &self, base: &CoreArchitecture, data: &[u8], addr: u64,
180///     ) -> Option<(usize, Vec<InstructionTextToken>)> {
181///         let (length, mut tokens) = base.instruction_text(data, addr)?;
182///         tokens.push(InstructionTextToken::new(" ; hooked", InstructionTextTokenKind::Annotation));
183///         Some((length, tokens))
184///     }
185/// }
186/// register_architecture_hook(CoreArchitecture::by_name("x86_64").unwrap(), Annotate);
187/// ```
188pub fn register_architecture_hook<H: ArchitectureHook>(
189    base: CoreArchitecture,
190    hook: H,
191) -> &'static H {
192    unsafe extern "C" fn free_function_arch_context<H: ArchitectureHook>(
193        context: *mut c_void,
194        function_context: *mut c_void,
195    ) {
196        let arch = unsafe { &*context.cast::<HookArchitecture<H>>() };
197        // Each hook must explicitly forward cleanup: copied callback tables do not retain
198        // the core's fallback chain when another hook is registered.
199        unsafe { BNArchitectureFreeFunctionArchContext(arch.base.handle, function_context) };
200    }
201
202    let registered = super::register_architecture_impl(
203        ArchitectureRegistration::Hook(base),
204        |handle, base| HookArchitecture { hook, base, handle },
205        |callbacks| {
206            callbacks.freeFunctionArchContext = Some(free_function_arch_context::<H>);
207        },
208    );
209    &registered.hook
210}