wraith/manipulation/inline_hook/arch/
mod.rs

1//! Architecture abstraction for inline hooking
2//!
3//! This module provides a trait-based abstraction over different CPU architectures,
4//! allowing the hooking framework to support both x86 and x64 with compile-time
5//! architecture selection.
6
7#[cfg(all(not(feature = "std"), feature = "alloc"))]
8use alloc::vec::Vec;
9
10#[cfg(feature = "std")]
11use std::vec::Vec;
12
13mod x64;
14mod x86;
15
16pub use x64::X64;
17pub use x86::X86;
18
19/// native architecture type alias based on target
20#[cfg(target_arch = "x86_64")]
21pub type NativeArch = X64;
22
23#[cfg(target_arch = "x86")]
24pub type NativeArch = X86;
25
26/// architecture-specific code generation trait
27///
28/// implementors provide the low-level instruction encoding and decoding
29/// needed for inline hooking on their respective architectures.
30pub trait Architecture: Sized + 'static {
31    /// size of a near relative jump instruction (jmp rel32)
32    const JMP_REL_SIZE: usize;
33
34    /// size of an absolute jump stub (varies by architecture)
35    const JMP_ABS_SIZE: usize;
36
37    /// native pointer size in bytes
38    const PTR_SIZE: usize;
39
40    /// required alignment for executable code
41    const CODE_ALIGNMENT: usize;
42
43    /// minimum bytes needed to install a hook
44    /// typically JMP_REL_SIZE for near targets, JMP_ABS_SIZE for far
45    const MIN_HOOK_SIZE: usize;
46
47    /// encode a near relative jump from source to target
48    ///
49    /// returns None if the distance exceeds the rel32 range (±2GB)
50    fn encode_jmp_rel(source: usize, target: usize) -> Option<Vec<u8>>;
51
52    /// encode an absolute jump (architecture-specific stub)
53    fn encode_jmp_abs(target: usize) -> Vec<u8>;
54
55    /// encode a relative call instruction
56    ///
57    /// returns None if the distance exceeds the rel32 range
58    fn encode_call_rel(source: usize, target: usize) -> Option<Vec<u8>>;
59
60    /// encode a NOP sled of the specified size
61    fn encode_nop_sled(size: usize) -> Vec<u8>;
62
63    /// find instruction boundary at or after required_size bytes
64    ///
65    /// scans code bytes and returns the offset where an instruction boundary
66    /// exists that is >= required_size. returns None if decoding fails.
67    fn find_instruction_boundary(code: &[u8], required_size: usize) -> Option<usize>;
68
69    /// relocate an instruction that was moved to a new address
70    ///
71    /// handles relative addressing adjustments for instructions like
72    /// jmp rel32, call rel32, and RIP-relative addressing (x64).
73    ///
74    /// returns the relocated bytes, or None if the instruction cannot be relocated.
75    fn relocate_instruction(
76        instruction: &[u8],
77        old_address: usize,
78        new_address: usize,
79    ) -> Option<Vec<u8>>;
80
81    /// check if an instruction needs relocation when moved
82    fn needs_relocation(instruction: &[u8]) -> bool;
83
84    /// preferred hook method based on distance between target and detour
85    fn preferred_hook_size(target: usize, detour: usize) -> usize {
86        let distance = (target as i64 - detour as i64).abs();
87        if distance <= i32::MAX as i64 {
88            Self::JMP_REL_SIZE
89        } else {
90            Self::JMP_ABS_SIZE
91        }
92    }
93}
94
95/// result of instruction decoding
96#[derive(Debug, Clone)]
97pub struct DecodedInstruction {
98    /// length of the instruction in bytes
99    pub length: usize,
100    /// whether this instruction uses relative addressing
101    pub is_relative: bool,
102    /// for relative instructions, the target address
103    pub relative_target: Option<usize>,
104}
105
106/// relocation result
107#[derive(Debug)]
108pub struct RelocationResult {
109    /// the relocated instruction bytes
110    pub bytes: Vec<u8>,
111    /// whether the instruction grew in size (e.g., short jmp -> long jmp)
112    pub size_changed: bool,
113}