1use core::ffi::c_void;
4use core::ptr::NonNull;
5use alloc::vec::Vec;
6
7use super::error::{status, KmError, KmResult, NtStatus};
8use super::memory::{AccessMode, Mdl, LockOperation};
9
10pub struct Eprocess {
12 raw: NonNull<c_void>,
13}
14
15impl Eprocess {
16 pub fn lookup(process_id: u32) -> KmResult<Self> {
18 let mut eprocess: *mut c_void = core::ptr::null_mut();
19
20 let status = unsafe {
22 PsLookupProcessByProcessId(process_id as *mut c_void, &mut eprocess)
23 };
24
25 if !status::nt_success(status) {
26 return Err(KmError::ProcessOperationFailed {
27 pid: process_id,
28 reason: "PsLookupProcessByProcessId failed",
29 });
30 }
31
32 NonNull::new(eprocess)
33 .map(|raw| Self { raw })
34 .ok_or(KmError::ProcessOperationFailed {
35 pid: process_id,
36 reason: "process not found",
37 })
38 }
39
40 pub fn as_raw(&self) -> *mut c_void {
42 self.raw.as_ptr()
43 }
44
45 pub fn dereference(&self) {
47 unsafe {
49 ObDereferenceObject(self.raw.as_ptr());
50 }
51 }
52
53 pub fn process_id(&self) -> u32 {
55 unsafe { PsGetProcessId(self.raw.as_ptr()) as u32 }
57 }
58
59 pub fn cr3(&self) -> u64 {
61 unsafe { PsGetProcessCr3(self.raw.as_ptr()) }
64 }
65
66 pub fn image_file_name(&self) -> [u8; 15] {
68 let mut name = [0u8; 15];
69 let ptr = unsafe { PsGetProcessImageFileName(self.raw.as_ptr()) };
71 if !ptr.is_null() {
72 unsafe {
73 for (i, byte) in name.iter_mut().enumerate() {
74 let b = *ptr.add(i);
75 if b == 0 {
76 break;
77 }
78 *byte = b;
79 }
80 }
81 }
82 name
83 }
84
85 pub fn is_terminating(&self) -> bool {
87 unsafe { PsGetProcessExitStatus(self.raw.as_ptr()) != 0x103 } }
90
91 pub fn peb(&self) -> *mut c_void {
93 unsafe { PsGetProcessPeb(self.raw.as_ptr()) }
95 }
96
97 pub fn wow64_peb(&self) -> *mut c_void {
99 unsafe { PsGetProcessWow64Process(self.raw.as_ptr()) }
101 }
102}
103
104impl Drop for Eprocess {
105 fn drop(&mut self) {
106 self.dereference();
107 }
108}
109
110#[repr(u32)]
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113pub enum ProcessAccess {
114 Terminate = 0x0001,
115 CreateThread = 0x0002,
116 VmOperation = 0x0008,
117 VmRead = 0x0010,
118 VmWrite = 0x0020,
119 DupHandle = 0x0040,
120 QueryInformation = 0x0400,
121 SetInformation = 0x0200,
122 All = 0x001F0FFF,
123}
124
125pub struct KmProcess {
127 eprocess: Eprocess,
128 apc_state: ApcState,
129 attached: bool,
130}
131
132#[repr(C)]
134struct ApcState {
135 apc_list_head: [[*mut c_void; 2]; 2],
136 process: *mut c_void,
137 kernel_apc_in_progress: u8,
138 kernel_apc_pending: u8,
139 user_apc_pending: u8,
140}
141
142impl Default for ApcState {
143 fn default() -> Self {
144 Self {
145 apc_list_head: [[core::ptr::null_mut(); 2]; 2],
146 process: core::ptr::null_mut(),
147 kernel_apc_in_progress: 0,
148 kernel_apc_pending: 0,
149 user_apc_pending: 0,
150 }
151 }
152}
153
154impl KmProcess {
155 pub fn open(process_id: u32) -> KmResult<Self> {
157 let eprocess = Eprocess::lookup(process_id)?;
158 Ok(Self {
159 eprocess,
160 apc_state: ApcState::default(),
161 attached: false,
162 })
163 }
164
165 pub fn eprocess(&self) -> &Eprocess {
167 &self.eprocess
168 }
169
170 pub fn id(&self) -> u32 {
172 self.eprocess.process_id()
173 }
174
175 pub fn attach(&mut self) -> KmResult<()> {
177 if self.attached {
178 return Ok(());
179 }
180
181 unsafe {
183 KeStackAttachProcess(self.eprocess.as_raw(), &mut self.apc_state as *mut _ as *mut _);
184 }
185
186 self.attached = true;
187 Ok(())
188 }
189
190 pub fn detach(&mut self) {
192 if self.attached {
193 unsafe {
195 KeUnstackDetachProcess(&mut self.apc_state as *mut _ as *mut _);
196 }
197 self.attached = false;
198 }
199 }
200
201 pub fn read<T: Copy>(&mut self, address: u64) -> KmResult<T> {
203 let mut value = core::mem::MaybeUninit::<T>::uninit();
204
205 self.read_bytes(
206 address,
207 unsafe { core::slice::from_raw_parts_mut(value.as_mut_ptr() as *mut u8, core::mem::size_of::<T>()) },
208 )?;
209
210 Ok(unsafe { value.assume_init() })
211 }
212
213 pub fn read_bytes(&mut self, address: u64, buffer: &mut [u8]) -> KmResult<usize> {
215 if buffer.is_empty() {
216 return Ok(0);
217 }
218
219 self.attach()?;
220
221 let mut bytes_read = 0usize;
222
223 let status = unsafe {
225 MmCopyVirtualMemory(
226 self.eprocess.as_raw(),
227 address as *const c_void,
228 PsGetCurrentProcess(),
229 buffer.as_mut_ptr() as *mut c_void,
230 buffer.len(),
231 AccessMode::KernelMode as u8,
232 &mut bytes_read,
233 )
234 };
235
236 self.detach();
237
238 if !status::nt_success(status) {
239 return Err(KmError::ProcessOperationFailed {
240 pid: self.id(),
241 reason: "MmCopyVirtualMemory read failed",
242 });
243 }
244
245 Ok(bytes_read)
246 }
247
248 pub fn write<T: Copy>(&mut self, address: u64, value: &T) -> KmResult<()> {
250 let bytes = unsafe {
251 core::slice::from_raw_parts(value as *const T as *const u8, core::mem::size_of::<T>())
252 };
253 self.write_bytes(address, bytes)?;
254 Ok(())
255 }
256
257 pub fn write_bytes(&mut self, address: u64, buffer: &[u8]) -> KmResult<usize> {
259 if buffer.is_empty() {
260 return Ok(0);
261 }
262
263 self.attach()?;
264
265 let mut bytes_written = 0usize;
266
267 let status = unsafe {
269 MmCopyVirtualMemory(
270 PsGetCurrentProcess(),
271 buffer.as_ptr() as *const c_void,
272 self.eprocess.as_raw(),
273 address as *mut c_void,
274 buffer.len(),
275 AccessMode::KernelMode as u8,
276 &mut bytes_written,
277 )
278 };
279
280 self.detach();
281
282 if !status::nt_success(status) {
283 return Err(KmError::ProcessOperationFailed {
284 pid: self.id(),
285 reason: "MmCopyVirtualMemory write failed",
286 });
287 }
288
289 Ok(bytes_written)
290 }
291
292 pub fn allocate(
294 &mut self,
295 size: usize,
296 protection: u32,
297 preferred_address: Option<u64>,
298 ) -> KmResult<u64> {
299 let mut base_address = preferred_address.unwrap_or(0) as *mut c_void;
300 let mut region_size = size;
301
302 let process_handle = self.open_handle(ProcessAccess::VmOperation as u32)?;
303
304 let status = unsafe {
306 ZwAllocateVirtualMemory(
307 process_handle,
308 &mut base_address,
309 0,
310 &mut region_size,
311 0x3000, protection,
313 )
314 };
315
316 unsafe { ZwClose(process_handle) };
317
318 if !status::nt_success(status) {
319 return Err(KmError::ProcessOperationFailed {
320 pid: self.id(),
321 reason: "ZwAllocateVirtualMemory failed",
322 });
323 }
324
325 Ok(base_address as u64)
326 }
327
328 pub fn free(&mut self, address: u64) -> KmResult<()> {
330 let mut base_address = address as *mut c_void;
331 let mut region_size = 0usize;
332
333 let process_handle = self.open_handle(ProcessAccess::VmOperation as u32)?;
334
335 let status = unsafe {
337 ZwFreeVirtualMemory(
338 process_handle,
339 &mut base_address,
340 &mut region_size,
341 0x8000, )
343 };
344
345 unsafe { ZwClose(process_handle) };
346
347 if !status::nt_success(status) {
348 return Err(KmError::ProcessOperationFailed {
349 pid: self.id(),
350 reason: "ZwFreeVirtualMemory failed",
351 });
352 }
353
354 Ok(())
355 }
356
357 pub fn protect(&mut self, address: u64, size: usize, protection: u32) -> KmResult<u32> {
359 let mut base_address = address as *mut c_void;
360 let mut region_size = size;
361 let mut old_protection = 0u32;
362
363 let process_handle = self.open_handle(ProcessAccess::VmOperation as u32)?;
364
365 let status = unsafe {
367 ZwProtectVirtualMemory(
368 process_handle,
369 &mut base_address,
370 &mut region_size,
371 protection,
372 &mut old_protection,
373 )
374 };
375
376 unsafe { ZwClose(process_handle) };
377
378 if !status::nt_success(status) {
379 return Err(KmError::ProcessOperationFailed {
380 pid: self.id(),
381 reason: "ZwProtectVirtualMemory failed",
382 });
383 }
384
385 Ok(old_protection)
386 }
387
388 pub fn get_module_base(&mut self, module_name: &[u16]) -> KmResult<u64> {
390 self.attach()?;
391
392 let peb = self.eprocess.peb();
394 if peb.is_null() {
395 self.detach();
396 return Err(KmError::ProcessOperationFailed {
397 pid: self.id(),
398 reason: "PEB is null",
399 });
400 }
401
402 let ldr_offset = if cfg!(target_arch = "x86_64") { 0x18 } else { 0x0C };
404 let ldr_ptr = unsafe {
405 *(peb.cast::<u8>().add(ldr_offset) as *const *const c_void)
406 };
407
408 if ldr_ptr.is_null() {
409 self.detach();
410 return Err(KmError::ProcessOperationFailed {
411 pid: self.id(),
412 reason: "PEB_LDR_DATA is null",
413 });
414 }
415
416 let list_offset = if cfg!(target_arch = "x86_64") { 0x10 } else { 0x0C };
418 let head = unsafe { ldr_ptr.cast::<u8>().add(list_offset) as *const ListEntry };
419 let mut current = unsafe { (*head).flink };
420
421 while current != head as *mut _ {
422 let name_offset = if cfg!(target_arch = "x86_64") { 0x58 } else { 0x2C };
425 let name_ptr = unsafe { current.cast::<u8>().add(name_offset) as *const UnicodeStringKernel };
426
427 let name = unsafe { &*name_ptr };
428 if !name.buffer.is_null() && name.length > 0 {
429 let name_slice = unsafe {
430 core::slice::from_raw_parts(name.buffer, (name.length / 2) as usize)
431 };
432
433 if name_slice.len() == module_name.len() {
435 let matches = name_slice.iter().zip(module_name.iter())
436 .all(|(a, b)| to_lower_u16(*a) == to_lower_u16(*b));
437
438 if matches {
439 let base_offset = if cfg!(target_arch = "x86_64") { 0x30 } else { 0x18 };
441 let base = unsafe {
442 *(current.cast::<u8>().add(base_offset) as *const u64)
443 };
444 self.detach();
445 return Ok(base);
446 }
447 }
448 }
449
450 current = unsafe { (*current).flink };
451 }
452
453 self.detach();
454 Err(KmError::ProcessOperationFailed {
455 pid: self.id(),
456 reason: "module not found",
457 })
458 }
459
460 fn open_handle(&self, access: u32) -> KmResult<*mut c_void> {
462 let mut handle: *mut c_void = core::ptr::null_mut();
463
464 let status = unsafe {
466 ObOpenObjectByPointer(
467 self.eprocess.as_raw(),
468 0x200, core::ptr::null_mut(),
470 access,
471 core::ptr::null_mut(), AccessMode::KernelMode as u8,
473 &mut handle,
474 )
475 };
476
477 if !status::nt_success(status) {
478 return Err(KmError::ProcessOperationFailed {
479 pid: self.id(),
480 reason: "ObOpenObjectByPointer failed",
481 });
482 }
483
484 Ok(handle)
485 }
486}
487
488impl Drop for KmProcess {
489 fn drop(&mut self) {
490 self.detach();
491 }
492}
493
494#[repr(C)]
496struct ListEntry {
497 flink: *mut ListEntry,
498 blink: *mut ListEntry,
499}
500
501#[repr(C)]
503struct UnicodeStringKernel {
504 length: u16,
505 maximum_length: u16,
506 buffer: *const u16,
507}
508
509extern "system" {
511 fn PsLookupProcessByProcessId(ProcessId: *mut c_void, Process: *mut *mut c_void) -> NtStatus;
512 fn PsGetProcessId(Process: *mut c_void) -> usize;
513 fn PsGetProcessCr3(Process: *mut c_void) -> u64;
514 fn PsGetProcessImageFileName(Process: *mut c_void) -> *const u8;
515 fn PsGetProcessExitStatus(Process: *mut c_void) -> NtStatus;
516 fn PsGetProcessPeb(Process: *mut c_void) -> *mut c_void;
517 fn PsGetProcessWow64Process(Process: *mut c_void) -> *mut c_void;
518 fn PsGetCurrentProcess() -> *mut c_void;
519
520 fn ObDereferenceObject(Object: *mut c_void);
521 fn ObOpenObjectByPointer(
522 Object: *mut c_void,
523 HandleAttributes: u32,
524 PassedAccessState: *mut c_void,
525 DesiredAccess: u32,
526 ObjectType: *mut c_void,
527 AccessMode: u8,
528 Handle: *mut *mut c_void,
529 ) -> NtStatus;
530
531 fn KeStackAttachProcess(Process: *mut c_void, ApcState: *mut c_void);
532 fn KeUnstackDetachProcess(ApcState: *mut c_void);
533
534 fn MmCopyVirtualMemory(
535 SourceProcess: *mut c_void,
536 SourceAddress: *const c_void,
537 TargetProcess: *mut c_void,
538 TargetAddress: *mut c_void,
539 BufferSize: usize,
540 PreviousMode: u8,
541 ReturnSize: *mut usize,
542 ) -> NtStatus;
543
544 fn ZwAllocateVirtualMemory(
545 ProcessHandle: *mut c_void,
546 BaseAddress: *mut *mut c_void,
547 ZeroBits: usize,
548 RegionSize: *mut usize,
549 AllocationType: u32,
550 Protect: u32,
551 ) -> NtStatus;
552
553 fn ZwFreeVirtualMemory(
554 ProcessHandle: *mut c_void,
555 BaseAddress: *mut *mut c_void,
556 RegionSize: *mut usize,
557 FreeType: u32,
558 ) -> NtStatus;
559
560 fn ZwProtectVirtualMemory(
561 ProcessHandle: *mut c_void,
562 BaseAddress: *mut *mut c_void,
563 RegionSize: *mut usize,
564 NewProtect: u32,
565 OldProtect: *mut u32,
566 ) -> NtStatus;
567
568 fn ZwClose(Handle: *mut c_void) -> NtStatus;
569}
570
571#[inline]
573fn to_lower_u16(c: u16) -> u16 {
574 if c >= b'A' as u16 && c <= b'Z' as u16 {
575 c + 32
576 } else {
577 c
578 }
579}