panda/plugins/
hooks.rs

1//! Bindings for the PANDA 'hooks' plugin, enabling the ability
2//! to add callbacks for when a certain instruction is hit.
3//!
4//! Recommended usage is via either the [`#[panda::hook]`](macro@crate::hook) macro or
5//! the [`hook`](mod@crate::hook) module.
6//!
7//! ## Example
8//!
9//! ```
10//! use panda::plugins::proc_start_linux::AuxvValues;
11//! use panda::plugins::hooks::Hook;
12//! use panda::prelude::*;
13//!
14//! #[panda::hook]
15//! fn entry_hook(_: &mut CPUState, _: &mut TranslationBlock, _: u8, hook: &mut Hook) {
16//!     println!("\n\nHit entry hook!\n");
17//!
18//!     // only run hook once
19//!     hook.enabled = false;
20//! }
21//!
22//! #[panda::on_rec_auxv]
23//! fn on_proc_start(_: &mut CPUState, _: &mut TranslationBlock, auxv: &AuxvValues) {
24//!     // when a process starts, hook the entrypoint
25//!     entry_hook::hook()
26//!         .after_block_exec()
27//!         .at_addr(auxv.entry)
28//! }
29//!
30//! Panda::new()
31//!     .generic("x86_64")
32//!     .replay("test")
33//!     .run();
34//! ```
35use std::ffi::c_void;
36
37use crate::plugin_import;
38use crate::prelude::*;
39use crate::sys::{self, panda_cb_type};
40
41plugin_import! {
42    static HOOKS: Hooks = extern "hooks" {
43        fn add_hook(hook: &Hook);
44        fn enable_hooking();
45        fn disable_hooking();
46        fn add_symbol_hook(hook: &SymbolHook);
47    };
48}
49
50#[repr(C)]
51#[derive(Copy, Clone, Debug)]
52pub struct Symbol {
53    pub address: target_ulong,
54    pub name: [u8; 256usize],
55    pub section: [u8; 256usize],
56}
57
58#[repr(C)]
59#[derive(Copy, Clone, Debug)]
60pub struct HooksPandaCallback(panda_cb_type, *const ());
61
62type NormalHookType = extern "C" fn(env: &mut CPUState, tb: &mut TranslationBlock, hook: &mut Hook);
63type BeforeTranslateHook = extern "C" fn(env: &mut CPUState, pc: target_ptr_t, hook: &mut Hook);
64type AfterBlockHook =
65    extern "C" fn(env: &mut CPUState, tb: &mut TranslationBlock, exitCode: u8, hook: &mut Hook);
66type InvalidateOpHook =
67    extern "C" fn(env: &mut CPUState, tb: &mut TranslationBlock, hook: &mut Hook) -> bool;
68
69impl HooksPandaCallback {
70    pub fn from_before_tcg_codegen(cb: NormalHookType) -> Self {
71        Self(sys::panda_cb_type_PANDA_CB_BEFORE_TCG_CODEGEN, cb as _)
72    }
73
74    pub fn from_after_block_translate(cb: NormalHookType) -> Self {
75        Self(sys::panda_cb_type_PANDA_CB_AFTER_BLOCK_TRANSLATE, cb as _)
76    }
77
78    pub fn from_before_block_exec(cb: NormalHookType) -> Self {
79        Self(sys::panda_cb_type_PANDA_CB_BEFORE_BLOCK_EXEC, cb as _)
80    }
81
82    pub fn from_start_block_exec(cb: NormalHookType) -> Self {
83        Self(sys::panda_cb_type_PANDA_CB_START_BLOCK_EXEC, cb as _)
84    }
85
86    pub fn from_end_block_exec(cb: NormalHookType) -> Self {
87        Self(sys::panda_cb_type_PANDA_CB_END_BLOCK_EXEC, cb as _)
88    }
89
90    pub fn from_before_block_exec_invalidate_opt(cb: InvalidateOpHook) -> Self {
91        Self(
92            sys::panda_cb_type_PANDA_CB_BEFORE_BLOCK_EXEC_INVALIDATE_OPT,
93            cb as _,
94        )
95    }
96
97    pub fn from_before_block_translate(cb: BeforeTranslateHook) -> Self {
98        Self(sys::panda_cb_type_PANDA_CB_BEFORE_BLOCK_TRANSLATE, cb as _)
99    }
100
101    pub fn from_after_block_exec(cb: AfterBlockHook) -> Self {
102        Self(sys::panda_cb_type_PANDA_CB_AFTER_BLOCK_EXEC, cb as _)
103    }
104}
105
106/// A set of functions for building hooks out of closures.
107///
108/// ## Example
109///
110/// ```
111/// use panda::{hook, prelude::*};
112///
113/// hook::before_block_exec(|_, _, hook| {
114///     println!("hook hit!");
115///     hook.enabled = false;
116/// })
117/// .at_addr(0x5555500ca);
118/// ```
119///
120/// For free functions, it may be easier to use [`#[panda::hook]`](macro@crate::hook)
121pub mod hook {
122    use super::*;
123
124    macro_rules! define_hook_builders {
125        ($(
126            fn $name:ident ( $($arg:ident : $arg_ty:ty ),* ) $(-> $ret_ty:ty)?;
127        )*) => {
128            $(
129                pub fn $name<CallbackFn>(
130                    callback: CallbackFn
131                ) -> HookBuilder<extern "C" fn(
132                    $( $arg : $arg_ty, )*
133                    hook: &mut Hook,
134                ) $( -> $ret_ty )?>
135                    where CallbackFn: FnMut($($arg_ty,)* &mut Hook) $( -> $ret_ty )? + 'static,
136                {
137                    extern "C" fn trampoline(
138                        $( $arg : $arg_ty, )*
139                        hook: &mut Hook,
140                    ) $( -> $ret_ty )? {
141                        let callback: &mut &mut dyn FnMut(
142                            $($arg_ty,)*
143                            &mut Hook,
144                        ) $( -> $ret_ty )? = unsafe {
145                            std::mem::transmute(hook.context)
146                        };
147
148                        callback($($arg, )* hook)
149                    }
150
151                    let cb: &mut &mut dyn FnMut(
152                        $($arg_ty,)* &mut Hook
153                    ) $( -> $ret_ty )? = Box::leak(Box::new(
154                        Box::leak(Box::new(callback) as _)
155                    ));
156
157                    $crate::paste::paste! {
158                        HookBuilder {
159                            hook: trampoline,
160                            callback: HooksPandaCallback::[< from_ $name  >](trampoline),
161                            only_kernel: None,
162                            enabled: true,
163                            asid: None,
164                            context: cb as *mut _ as *mut _,
165                        }
166                    }
167                }
168            )*
169        };
170    }
171
172    define_hook_builders! {
173        fn before_block_exec(env: &mut CPUState, tb: &mut TranslationBlock);
174        fn before_tcg_codegen(env: &mut CPUState, tb: &mut TranslationBlock);
175        fn after_block_translate(env: &mut CPUState, tb: &mut TranslationBlock);
176        fn start_block_exec(env: &mut CPUState, tb: &mut TranslationBlock);
177        fn end_block_exec(env: &mut CPUState, tb: &mut TranslationBlock);
178
179        fn after_block_exec(env: &mut CPUState, tb: &mut TranslationBlock, exit_code: u8);
180        fn before_block_translate(env: &mut CPUState, pc: target_ptr_t);
181        fn before_block_exec_invalidate_opt(env: &mut CPUState, tb: &mut TranslationBlock) -> bool;
182    }
183}
184
185#[repr(u32)]
186#[derive(Copy, Clone, Debug)]
187pub enum KernelMode {
188    Any = 0,
189    KernelOnly = 1,
190    UserOnly = 2,
191}
192
193/// A hook provided by the hooks plugin, describing the address,
194/// asid/process, symbol, etc to hook.
195#[repr(C)]
196#[derive(Copy, Clone, Debug)]
197pub struct Hook {
198    /// The address to hook
199    pub addr: target_ulong,
200
201    /// The address space identifier to hook, with zero representing any asid.
202    /// Defaults to zero.
203    pub asid: target_ulong,
204
205    /// The callback to trigger when the hook is hit
206    pub cb: HooksPandaCallback,
207
208    /// Whether to hook in kernel mode only, user mode only, or neither.
209    /// Defaults to neither.
210    pub km: KernelMode,
211
212    /// Whether the hook is enabled. Defaults to `true`
213    pub enabled: bool,
214
215    /// The symbol of the function to hook
216    pub sym: Symbol,
217
218    /// User-provided context variable
219    pub context: *mut c_void,
220}
221
222#[repr(C)]
223#[derive(Copy, Clone)]
224pub struct SymbolHook {
225    pub name: [u8; 256usize],
226    pub offset: target_ulong,
227    pub hook_offset: bool,
228    pub section: [u8; 256usize],
229    pub cb: HooksPandaCallback,
230}
231
232pub trait IntoHookBuilder {
233    type BuilderType;
234
235    fn hook(self) -> Self::BuilderType;
236}
237
238impl IntoHookBuilder for NormalHookType {
239    type BuilderType = HookBuilder<NormalHookType>;
240
241    fn hook(self) -> Self::BuilderType {
242        HookBuilder {
243            hook: self,
244            callback: HooksPandaCallback::from_start_block_exec(self),
245            only_kernel: None,
246            enabled: true,
247            asid: None,
248            context: std::ptr::null_mut(),
249        }
250    }
251}
252
253impl IntoHookBuilder for BeforeTranslateHook {
254    type BuilderType = HookBuilderCallbackTypeNeeded<Self>;
255
256    fn hook(self) -> Self::BuilderType {
257        HookBuilderCallbackTypeNeeded(self)
258    }
259}
260
261impl IntoHookBuilder for AfterBlockHook {
262    type BuilderType = HookBuilderCallbackTypeNeeded<Self>;
263
264    fn hook(self) -> Self::BuilderType {
265        HookBuilderCallbackTypeNeeded(self)
266    }
267}
268
269impl IntoHookBuilder for InvalidateOpHook {
270    type BuilderType = HookBuilderCallbackTypeNeeded<Self>;
271
272    fn hook(self) -> Self::BuilderType {
273        HookBuilderCallbackTypeNeeded(self)
274    }
275}
276
277/// A builder type for helping construct and install a [`Hook`].
278pub struct HookBuilder<T> {
279    hook: T,
280    callback: HooksPandaCallback,
281    only_kernel: Option<bool>,
282    enabled: bool,
283    asid: Option<target_ulong>,
284    context: *mut c_void,
285}
286
287impl<T> HookBuilder<T> {
288    /// Sets if kernel mode should be used. `true` for kernel-only hooking, `false` for user-only
289    /// hooking. By default the generated hook will hook either.
290    pub fn kernel(mut self, only_kernel: bool) -> Self {
291        self.only_kernel = Some(only_kernel);
292        self
293    }
294
295    /// Sets if the hook is enabled. Defaults to `true`.
296    pub fn enabled(mut self, enabled: bool) -> Self {
297        self.enabled = enabled;
298        self
299    }
300
301    /// Sets the asid to hook. Defaults to any.
302    pub fn asid(mut self, asid: target_ulong) -> Self {
303        self.asid = Some(asid);
304        self
305    }
306
307    /// Installs the hook at a given address
308    pub fn at_addr(self, addr: target_ulong) {
309        HOOKS.add_hook(&Hook {
310            addr,
311            asid: self.asid.unwrap_or(0),
312            enabled: self.enabled,
313            km: match self.only_kernel {
314                Some(true) => KernelMode::KernelOnly,
315                Some(false) => KernelMode::UserOnly,
316                None => KernelMode::Any,
317            },
318            cb: self.callback,
319            sym: unsafe { std::mem::zeroed() },
320            context: self.context,
321        });
322    }
323}
324
325impl HookBuilder<NormalHookType> {
326    pub fn before_tcg_codegen(mut self) -> Self {
327        self.callback = HooksPandaCallback::from_before_tcg_codegen(self.hook);
328        self
329    }
330
331    pub fn after_block_translate(mut self) -> Self {
332        self.callback = HooksPandaCallback::from_after_block_translate(self.hook);
333        self
334    }
335
336    pub fn before_block_exec(mut self) -> Self {
337        self.callback = HooksPandaCallback::from_before_block_exec(self.hook);
338        self
339    }
340
341    pub fn start_block_exec(mut self) -> Self {
342        self.callback = HooksPandaCallback::from_start_block_exec(self.hook);
343        self
344    }
345
346    pub fn end_block_exec(mut self) -> Self {
347        self.callback = HooksPandaCallback::from_end_block_exec(self.hook);
348        self
349    }
350}
351
352pub struct HookBuilderCallbackTypeNeeded<T>(T);
353
354impl HookBuilderCallbackTypeNeeded<BeforeTranslateHook> {
355    pub fn before_block_translate(self) -> HookBuilder<BeforeTranslateHook> {
356        HookBuilder {
357            hook: self.0,
358            callback: HooksPandaCallback::from_before_block_translate(self.0),
359            only_kernel: None,
360            enabled: true,
361            asid: None,
362            context: std::ptr::null_mut(),
363        }
364    }
365}
366
367impl HookBuilderCallbackTypeNeeded<AfterBlockHook> {
368    pub fn after_block_exec(self) -> HookBuilder<AfterBlockHook> {
369        HookBuilder {
370            hook: self.0,
371            callback: HooksPandaCallback::from_after_block_exec(self.0),
372            only_kernel: None,
373            enabled: true,
374            asid: None,
375            context: std::ptr::null_mut(),
376        }
377    }
378}
379
380impl HookBuilderCallbackTypeNeeded<InvalidateOpHook> {
381    pub fn before_block_exec_invalidate_opt(self) -> HookBuilder<InvalidateOpHook> {
382        HookBuilder {
383            hook: self.0,
384            callback: HooksPandaCallback::from_before_block_exec_invalidate_opt(self.0),
385            only_kernel: None,
386            enabled: true,
387            asid: None,
388            context: std::ptr::null_mut(),
389        }
390    }
391}