1use core::ffi::c_void;
2use core::ptr::addr_of_mut;
3use core::sync::atomic::{Ordering, AtomicBool};
4
5use crate::{NtGetThreadContext, NtSetThreadContext};
6use crate::data::{
7 CONTEXT, CONTEXT_DEBUG_REGISTERS_AMD64, EXCEPTION_SINGLE_STEP,
8 EXCEPTION_CONTINUE_EXECUTION, EXCEPTION_CONTINUE_SEARCH,
9 EXCEPTION_POINTERS, HANDLE, OBJECT_ATTRIBUTES,
10 CONTEXT_DEBUG_REGISTERS_X86
11};
12
13static USE_BREAKPOINT: AtomicBool = AtomicBool::new(false);
15
16#[inline(always)]
29pub fn set_use_breakpoint(enabled: bool) {
30 USE_BREAKPOINT.store(enabled, Ordering::SeqCst);
31}
32
33#[inline(always)]
50pub fn is_breakpoint_enabled() -> bool {
51 USE_BREAKPOINT.load(Ordering::SeqCst)
52}
53
54pub(crate) fn set_breakpoint<T: Into<u64>>(address: T) {
69 let mut ctx = CONTEXT {
70 ContextFlags: if cfg!(target_arch = "x86_64") { CONTEXT_DEBUG_REGISTERS_AMD64 } else { CONTEXT_DEBUG_REGISTERS_X86 },
71 ..Default::default()
72 };
73
74 NtGetThreadContext(-2isize as HANDLE, &mut ctx);
75
76 cfg_if::cfg_if! {
77 if #[cfg(target_arch = "x86_64")] {
78 ctx.Dr0 = address.into();
79 ctx.Dr6 = 0x00;
80 ctx.Dr7 = set_dr7_bits(ctx.Dr7, 0, 1, 1);
81 } else {
82 ctx.Dr0 = address.into() as u32;
83 ctx.Dr6 = 0x00;
84 ctx.Dr7 = set_dr7_bits(ctx.Dr7 as u64, 0, 1, 1) as u32;
85 }
86 }
87
88 NtSetThreadContext(-2isize as HANDLE, &ctx);
89}
90
91fn set_dr7_bits<T: Into<u64>>(current: T, start_bit: i32, nmbr_bits: i32, new_bit: u64) -> u64 {
112 let current = current.into();
113 let mask = (1u64 << nmbr_bits) - 1;
114 (current & !(mask << start_bit)) | (new_bit << start_bit)
115}
116
117pub static mut CURRENT_API: Option<WINAPI> = None;
119
120#[derive(Debug)]
122pub enum WINAPI {
123 NtAllocateVirtualMemory {
125 ProcessHandle: HANDLE,
126 Protect: u32,
127 },
128
129 NtProtectVirtualMemory {
131 ProcessHandle: HANDLE,
132 NewProtect: u32,
133 },
134
135 NtCreateThreadEx {
137 ProcessHandle: HANDLE,
138 ThreadHandle: *mut HANDLE,
139 DesiredAccess: u32,
140 ObjectAttributes: *mut OBJECT_ATTRIBUTES
141 },
142
143 NtWriteVirtualMemory {
145 ProcessHandle: HANDLE,
146 Buffer: *mut c_void,
147 NumberOfBytesToWrite: *mut usize,
148 },
149}
150
151#[cfg(target_arch = "x86_64")]
163#[allow(unsafe_op_in_unsafe_fn)]
164pub unsafe extern "system" fn veh_handler(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32 {
165 if !is_breakpoint_enabled() || (*(*exceptioninfo).ExceptionRecord).ExceptionCode != EXCEPTION_SINGLE_STEP {
166 return EXCEPTION_CONTINUE_SEARCH;
167 }
168
169 let context = (*exceptioninfo).ContextRecord;
170 if (*context).Rip == (*context).Dr0 && (*context).Dr7 & 1 == 1 {
171 if let Some(current) = (*addr_of_mut!(CURRENT_API)).take() {
172 match current {
173 WINAPI::NtAllocateVirtualMemory {
174 ProcessHandle,
175 Protect
176 } => {
177 (*context).R10 = ProcessHandle as u64;
178 *(((*context).Rsp + 0x30) as *mut u32) = Protect;
179 },
180
181 WINAPI::NtProtectVirtualMemory {
182 ProcessHandle,
183 NewProtect,
184 } => {
185 (*context).R10 = ProcessHandle as u64;
186 (*context).R9 = NewProtect as u64;
187 },
188
189 WINAPI::NtCreateThreadEx {
190 ProcessHandle,
191 ThreadHandle,
192 DesiredAccess,
193 ObjectAttributes
194 } => {
195 (*context).R10 = ThreadHandle as u64;
196 (*context).Rdx = DesiredAccess as u64;
197 (*context).R8 = ObjectAttributes as u64;
198 (*context).R9 = ProcessHandle as u64;
199 },
200
201 WINAPI::NtWriteVirtualMemory {
202 ProcessHandle,
203 Buffer,
204 NumberOfBytesToWrite,
205 } => {
206 (*context).R10 = ProcessHandle as u64;
207 (*context).R8 = Buffer as u64;
208 (*context).R9 = NumberOfBytesToWrite as u64;
209 }
210 }
211
212 (*context).Dr0 = 0x00;
213 (*context).Dr6 = 0x00;
214 (*context).Dr7 = set_dr7_bits((*context).Dr7, 0, 1, 0);
215 }
216
217 return EXCEPTION_CONTINUE_EXECUTION;
218 }
219
220 EXCEPTION_CONTINUE_SEARCH
221}
222
223#[cfg(target_arch = "x86")]
235#[allow(unsafe_op_in_unsafe_fn)]
236pub unsafe extern "system" fn veh_handler(exceptioninfo: *mut EXCEPTION_POINTERS) -> i32 {
237 if !is_breakpoint_enabled() || (*(*exceptioninfo).ExceptionRecord).ExceptionCode != EXCEPTION_SINGLE_STEP {
238 return EXCEPTION_CONTINUE_SEARCH;
239 }
240
241 let context = (*exceptioninfo).ContextRecord;
242 if (*context).Eip == (*context).Dr0 && (*context).Dr7 & 1 == 1 {
243 if let Some(current) = (*addr_of_mut!(CURRENT_API)).take() {
244 match current {
245 WINAPI::NtAllocateVirtualMemory {
246 ProcessHandle,
247 Protect
248 } => {
249 *(((*context).Esp + 0x4) as *mut u32) = ProcessHandle as u32;
250 *(((*context).Esp + 0x18) as *mut u32) = Protect;
251 },
252
253 WINAPI::NtProtectVirtualMemory {
254 ProcessHandle,
255 NewProtect,
256 } => {
257 *(((*context).Esp + 0x4) as *mut u32) = ProcessHandle as u32;
258 *(((*context).Esp + 0x10) as *mut u32) = NewProtect as u32;
259 },
260
261 WINAPI::NtCreateThreadEx {
262 ProcessHandle,
263 ThreadHandle,
264 DesiredAccess,
265 ObjectAttributes
266 } => {
267 *(((*context).Esp + 0x4) as *mut u32) = ThreadHandle as u32;
268 *(((*context).Esp + 0x8) as *mut u32) = DesiredAccess as u32;
269 *(((*context).Esp + 0xC) as *mut u32) = ObjectAttributes as u32;
270 *(((*context).Esp + 0x10) as *mut u32) = ProcessHandle as u32;
271 },
272
273 WINAPI::NtWriteVirtualMemory {
274 ProcessHandle,
275 Buffer,
276 NumberOfBytesToWrite,
277 } => {
278 *(((*context).Esp + 0x4) as *mut u32) = ProcessHandle as u32;
279 *(((*context).Esp + 0xC) as *mut u32) = Buffer as u32;
280 *(((*context).Esp + 0x10) as *mut u32) = NumberOfBytesToWrite as u32;
281 }
282 }
283
284 (*context).Dr0 = 0x00;
285 (*context).Dr6 = 0x00;
286 (*context).Dr7 = set_dr7_bits((*context).Dr7, 0, 1, 0) as u32;
287 }
288
289 return EXCEPTION_CONTINUE_EXECUTION;
290 }
291
292 EXCEPTION_CONTINUE_SEARCH
293}