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