verity_memory/ops/
write.rs1use winapi::um::winnt::PAGE_EXECUTE_READWRITE;
2use winapi::{shared::minwindef::LPVOID, um::memoryapi::VirtualProtect};
3
4#[cfg(feature = "advanced-write")]
5use crate::macros::match_number::{FloatType, IntegerType, IntegralType, NumberType};
6#[cfg(feature = "advanced-write")]
7use crate::types::Instruction;
8use crate::{errors::WriteMemoryError, utils};
9#[cfg(feature = "advanced-write")]
10use crate::match_number;
11
12#[cfg(feature = "advanced-write")]
13use super::asm::{float_ret, get_instruction, integer_ret, integral_ret};
14
15pub unsafe fn write_memory<T: Copy>(dest_ptr: *mut T, value: T) -> Result<(), WriteMemoryError> {
46 if dest_ptr.is_null() {
47 return Err(WriteMemoryError::NullPointer);
48 }
49
50 if !utils::check_alignment(dest_ptr) {
51 return Err(WriteMemoryError::InvalidAlignment);
52 }
53
54 let mut old_protect = 0;
55 let size = std::mem::size_of::<T>();
56
57 let res = VirtualProtect(
58 dest_ptr as LPVOID,
59 size,
60 PAGE_EXECUTE_READWRITE,
61 &mut old_protect,
62 );
63 if res == 0 {
64 return Err(WriteMemoryError::FailedToChangeProtection);
65 }
66
67 *dest_ptr = value;
68
69 let res_restore = VirtualProtect(dest_ptr as LPVOID, size, old_protect, &mut old_protect);
70 if res_restore == 0 {
71 return Err(WriteMemoryError::FailedToRestoreProtection);
72 }
73
74 Ok(())
75}
76
77#[cfg(feature = "advanced-write")]
101pub unsafe fn nop_instructions(dest_ptr: *mut u8, num_instructions: usize) -> Option<Vec<Instruction>> {
102 let mut instructions = Vec::new();
103 let mut current_ptr = dest_ptr;
104
105 if num_instructions <= 0 {
106 panic!("You must nop at least one instruction...");
107 }
108
109 for _ in 0..num_instructions {
110 if let Some(instr) = get_instruction(current_ptr, 16) {
111 instructions.push(instr.clone());
112 current_ptr = current_ptr.add(instr.size);
113 } else {
114 eprintln!("Failed to get instruction at memory address: {:?}", current_ptr);
115 return None;
116 }
117 }
118
119 let total_size: usize = instructions.iter().map(|instr| instr.size).sum();
120
121 let nops = vec![0x90; total_size];
122 let mut written_size = 0;
123 for i in 0..num_instructions {
124 let instruction = &instructions[i];
125 for j in 0..instruction.size {
126 let res = write_memory(dest_ptr.add(written_size + j), nops[written_size + j]);
127 if let Err(e) = res {
128 eprintln!("Failed to write memory at offset {}: {:?}", written_size + j, e);
129 return None;
130 }
131 }
132 written_size += instruction.size;
133 }
134
135 Some(instructions)
136}
137
138#[cfg(feature = "advanced-write")]
162pub unsafe fn replace_return_value<T: Copy + 'static>(
163 dest_ptr: *mut u8,
164 return_value: Option<T>,
165) -> Option<Instruction> {
166 let original_instruction = get_instruction(dest_ptr, 16)?;
167
168 let value = match return_value {
169 Some(val) => val,
170 None => {
171 if let Err(e) = write_memory(dest_ptr, 0xC3) {
172 eprintln!("Failed to write RET instruction: {:?}", e);
173 return None;
174 }
175 return Some(original_instruction);
176 }
177 };
178
179 let result = match_number!(value);
180 if result.is_none() {
181 return None;
182 }
183
184 let number_type = result.unwrap();
185 let instruction_bytes = match number_type {
186 NumberType::Float(float_type) => float_ret(float_type),
187 NumberType::Integer(integer_type) => integer_ret(integer_type),
188 NumberType::Integral(integral_type) => integral_ret(integral_type),
189 NumberType::Unknown => {
190 return None;
191 }
192 };
193
194 let mut current_ptr = dest_ptr;
195 for instruction_byte in instruction_bytes {
196 if write_memory(current_ptr, instruction_byte).is_err() {
197 return None;
198 }
199 current_ptr = current_ptr.add(1);
200 }
201
202 Some(original_instruction)
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use std::ptr;
209
210 fn mock_dest_ptr<T: Copy>(value: T) -> *mut T {
211 let mut boxed_value = Box::new(value);
212 let ptr = &mut *boxed_value as *mut T;
213 std::mem::forget(boxed_value);
214 ptr
215 }
216
217 #[test]
218 fn test_write_memory_success() {
219 let value: u32 = 42;
220 let dest_ptr = mock_dest_ptr(value);
221
222 let result = unsafe { write_memory(dest_ptr, 100_u32) };
223 assert!(result.is_ok());
224 assert_eq!(unsafe { *dest_ptr }, 100_u32);
225 }
226
227 #[test]
228 fn test_write_memory_null_pointer() {
229 let dest_ptr: *mut u32 = ptr::null_mut();
230
231 let result = unsafe { write_memory(dest_ptr, 100_u32) };
232 assert!(matches!(result, Err(WriteMemoryError::NullPointer)));
233 }
234
235 #[test]
236 #[cfg(feature = "advanced-write")]
237 fn test_nop_instructions_success() {
238 let data: Vec<u8> = vec![0x55, 0x48, 0x8B, 0xEC, 0x90];
239 let dest_ptr = data.as_ptr() as *mut u8;
240
241 unsafe {
242 if let Some(instructions) = nop_instructions(dest_ptr, 2) {
243 assert_eq!(instructions.len(), 2);
244 } else {
245 panic!("Failed to retrieve instructions");
246 }
247 }
248 }
249
250 #[test]
251 #[cfg(feature = "advanced-write")]
252 fn test_nop_instructions_failure() {
253 let dest_ptr: *mut u8 = ptr::null_mut();
254
255 unsafe {
256 let instructions = nop_instructions(dest_ptr, 1);
257 assert!(instructions.is_none());
258 }
259 }
260
261 #[test]
262 #[cfg(feature = "advanced-write")]
263 fn test_replace_return_value_integer() {
264 let data: Vec<u8> = vec![0x55, 0x48, 0x8B, 0xEC];
265 let dest_ptr = data.as_ptr() as *mut u8;
266
267 unsafe {
268 let result = replace_return_value(dest_ptr, Some(123_u32));
269 assert!(result.is_some());
270 }
271 }
272
273 #[test]
274 #[cfg(feature = "advanced-write")]
275 fn test_replace_return_value_float() {
276 let data: Vec<u8> = vec![0x55, 0x48, 0x8B, 0xEC];
277 let dest_ptr = data.as_ptr() as *mut u8;
278
279 unsafe {
280 let result = replace_return_value(dest_ptr, Some(123.45_f32));
281 assert!(result.is_some());
282 }
283 }
284
285 #[test]
286 #[cfg(feature = "advanced-write")]
287 fn test_replace_return_value_none() {
288 let data: Vec<u8> = vec![0x55, 0x48, 0x8B, 0xEC];
289 let dest_ptr = data.as_ptr() as *mut u8;
290
291 unsafe {
292 let result = replace_return_value::<i32>(dest_ptr, None);
293 assert!(result.is_some());
294 }
295 }
296}