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