wraith/manipulation/inline_hook/arch/
x86.rs1#[cfg(all(not(feature = "std"), feature = "alloc"))]
4use alloc::vec::Vec;
5
6#[cfg(feature = "std")]
7use std::vec::Vec;
8
9use super::Architecture;
10use crate::manipulation::inline_hook::asm::{
11 iced_decoder::InstructionDecoder,
12 iced_relocator::InstructionRelocator,
13};
14
15pub struct X86;
17
18impl Architecture for X86 {
19 const JMP_REL_SIZE: usize = 5;
21
22 const JMP_ABS_SIZE: usize = 6;
24
25 const PTR_SIZE: usize = 4;
26 const CODE_ALIGNMENT: usize = 4;
27
28 const MIN_HOOK_SIZE: usize = 5;
30
31 fn encode_jmp_rel(source: usize, target: usize) -> Option<Vec<u8>> {
32 let offset = (target as i32).wrapping_sub((source as i32).wrapping_add(5));
34
35 let mut bytes = Vec::with_capacity(5);
36 bytes.push(0xE9);
37 bytes.extend_from_slice(&offset.to_le_bytes());
38 Some(bytes)
39 }
40
41 fn encode_jmp_abs(target: usize) -> Vec<u8> {
42 let mut bytes = Vec::with_capacity(6);
44 bytes.push(0x68); bytes.extend_from_slice(&(target as u32).to_le_bytes());
46 bytes.push(0xC3); bytes
48 }
49
50 fn encode_call_rel(source: usize, target: usize) -> Option<Vec<u8>> {
51 let offset = (target as i32).wrapping_sub((source as i32).wrapping_add(5));
52
53 let mut bytes = Vec::with_capacity(5);
54 bytes.push(0xE8);
55 bytes.extend_from_slice(&offset.to_le_bytes());
56 Some(bytes)
57 }
58
59 fn encode_nop_sled(size: usize) -> Vec<u8> {
60 let mut bytes = Vec::with_capacity(size);
61 let mut remaining = size;
62
63 while remaining > 0 {
64 match remaining {
65 1 => {
66 bytes.push(0x90);
67 remaining -= 1;
68 }
69 2 => {
70 bytes.extend_from_slice(&[0x66, 0x90]);
71 remaining -= 2;
72 }
73 3 => {
74 bytes.extend_from_slice(&[0x0F, 0x1F, 0x00]);
75 remaining -= 3;
76 }
77 4 => {
78 bytes.extend_from_slice(&[0x0F, 0x1F, 0x40, 0x00]);
79 remaining -= 4;
80 }
81 5 => {
82 bytes.extend_from_slice(&[0x0F, 0x1F, 0x44, 0x00, 0x00]);
83 remaining -= 5;
84 }
85 6 => {
86 bytes.extend_from_slice(&[0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00]);
87 remaining -= 6;
88 }
89 _ => {
90 bytes.extend_from_slice(&[0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00]);
91 remaining -= 7;
92 }
93 }
94 }
95
96 bytes
97 }
98
99 fn find_instruction_boundary(code: &[u8], required_size: usize) -> Option<usize> {
100 let decoder = InstructionDecoder::x86();
102 decoder.find_boundary(0, code, required_size)
103 }
104
105 fn relocate_instruction(
106 instruction: &[u8],
107 old_address: usize,
108 new_address: usize,
109 ) -> Option<Vec<u8>> {
110 if instruction.is_empty() {
111 return None;
112 }
113
114 let relocator = InstructionRelocator::x86();
116 let result = relocator.relocate_instruction(
117 instruction,
118 old_address as u64,
119 new_address as u64,
120 );
121
122 if result.success {
123 Some(result.bytes)
124 } else {
125 None
126 }
127 }
128
129 fn needs_relocation(instruction: &[u8]) -> bool {
130 if instruction.is_empty() {
131 return false;
132 }
133
134 crate::manipulation::inline_hook::asm::iced_relocator::instruction_needs_relocation(
136 instruction,
137 0,
138 )
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_encode_jmp_rel() {
148 let bytes = X86::encode_jmp_rel(0x1000, 0x1100).unwrap();
149 assert_eq!(bytes.len(), 5);
150 assert_eq!(bytes[0], 0xE9);
151 let offset = i32::from_le_bytes(bytes[1..5].try_into().unwrap());
152 assert_eq!(offset, 0xFB);
153 }
154
155 #[test]
156 fn test_encode_jmp_abs() {
157 let bytes = X86::encode_jmp_abs(0xDEADBEEF);
158 assert_eq!(bytes.len(), 6);
159 assert_eq!(bytes[0], 0x68); let addr = u32::from_le_bytes(bytes[1..5].try_into().unwrap());
161 assert_eq!(addr, 0xDEADBEEF);
162 assert_eq!(bytes[5], 0xC3); }
164
165 #[test]
166 fn test_find_instruction_boundary() {
167 let code = [0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x10];
169 let boundary = X86::find_instruction_boundary(&code, 5).unwrap();
170 assert!(boundary >= 5);
171 }
172
173 #[test]
174 fn test_relocate_jmp_rel32() {
175 let jmp = [0xE9, 0x00, 0x01, 0x00, 0x00];
177
178 let result = X86::relocate_instruction(&jmp, 0x1000, 0x2000).unwrap();
180 assert_eq!(result.len(), 5);
181 assert_eq!(result[0], 0xE9);
182 }
183
184 #[test]
185 fn test_needs_relocation() {
186 assert!(X86::needs_relocation(&[0xE9, 0x00, 0x00, 0x00, 0x00]));
188
189 assert!(X86::needs_relocation(&[0xE8, 0x00, 0x00, 0x00, 0x00]));
191
192 assert!(!X86::needs_relocation(&[0x55]));
194
195 assert!(!X86::needs_relocation(&[0x90]));
197 }
198}