Skip to main content

dyncvoke_core/
lib.rs

1//! PEB walking, export parsing, Tartarus Gate syscalls, and `nt_*` wrappers.
2//!
3//! This is the default `dyncvoke` backend (`feature = "syscall"`). The usual
4//! entry points are [`syscall!`], [`do_syscall!`], [`resolve_syscall`],
5//! [`get_module_base_address`], and [`get_function_address`].
6//!
7//! Indirect syscalls:
8//!
9//! 1. [`resolve_syscall`] finds the ntdll stub, extracts the SSN
10//!    (Hell's / Halo's / Tartarus Gate), and locates a `syscall; ret`
11//!    gadget.
12//! 2. [`syscall!`] does that and dispatches in one step.
13//! 3. [`do_syscall!`] dispatches a cached `(ssn, addr)` pair with no
14//!    extra resolution.
15//!
16//! ```ignore
17//! use dyncvoke_core::syscall;
18//!
19//! let status = syscall!("NtClose", handle).unwrap() as i32;
20//! ```
21//!
22//! Module lookup walks `PEB->Ldr->InLoadOrderModuleList`. It does not call
23//! `EnumProcessModules` or `GetModuleHandle`.
24
25// dyncvoke - Dynamic Windows API Invocation Library
26// @5mukx
27
28#![no_std]
29#![cfg_attr(docsrs, feature(doc_cfg))]
30extern crate alloc;
31
32#[cfg(not(windows))]
33compile_error!("dyncvoke-core is Windows-only");
34
35use alloc::{
36    collections::BTreeMap,
37    ffi::CString,
38    format,
39    string::{String, ToString},
40    vec,
41    vec::Vec,
42};
43use core::{ffi::c_void, mem::size_of, ptr};
44
45use data::lc;
46use data::LARGE_INTEGER;
47use windows_sys::Win32::Foundation::BOOL;
48use windows_sys::Win32::Security::SECURITY_ATTRIBUTES;
49#[cfg(target_arch = "x86_64")]
50use windows_sys::Win32::System::Diagnostics::Debug::{GetThreadContext, SetThreadContext};
51use windows_sys::Win32::System::Memory::MEMORY_BASIC_INFORMATION;
52use windows_sys::Win32::System::SystemInformation::SYSTEM_INFO;
53use windows_sys::Win32::System::IO::IO_STATUS_BLOCK;
54#[cfg(target_arch = "x86_64")]
55use windows_sys::Win32::System::Threading::GetCurrentProcessId;
56
57pub mod peb;
58
59pub use data::HANDLE;
60pub use data::HINSTANCE;
61pub use data::OBJECT_ATTRIBUTES;
62pub use data::UNICODE_STRING;
63pub use windows_sys::Win32::System::Threading::{GetCurrentProcess, PROCESS_BASIC_INFORMATION};
64
65pub const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE;
66
67pub mod sys;
68pub use sys::{extract_ssn, get_syscall_address, resolve_syscall, SyscallError};
69#[cfg(target_arch = "x86_64")]
70pub use sys::asm::do_syscall;
71
72#[allow(unused_imports)]
73use data::{
74    ApiSetNamespace, ApiSetNamespaceEntry, ApiSetValueEntry, ClientId, EntryPoint,
75    LptopLevelExceptionFilter, PeMetadata, PsAttributeList, PsCreateInfo,
76    DLL_PROCESS_ATTACH, EAT, MAX_PATH,
77    MEM_COMMIT, MEM_RESERVE,
78    PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READONLY, PAGE_READWRITE,
79    PROCESS_QUERY_LIMITED_INFORMATION, PVOID, TLS_OUT_OF_INDEXES,
80};
81
82#[cfg(target_arch = "x86_64")]
83use data::{
84    ExceptionHandleFunction, ExceptionPointers,
85    NtAllocateVirtualMemoryArgs, NtCreateThreadExArgs, NtOpenProcessArgs,
86    NtProtectVirtualMemoryArgs, NtWriteVirtualMemoryArgs,
87};
88#[cfg(target_arch = "x86_64")]
89use windows_sys::Win32::System::Threading::GetCurrentThread;
90
91#[cfg(target_arch = "x86_64")]
92static mut HARDWARE_BREAKPOINTS: bool = false;
93#[cfg(target_arch = "x86_64")]
94static mut HARDWARE_EXCEPTION_FUNCTION: ExceptionHandleFunction =
95    ExceptionHandleFunction::NtOpenProcess;
96#[cfg(target_arch = "x86_64")]
97static mut NT_ALLOCATE_VIRTUAL_MEMORY_ARGS: NtAllocateVirtualMemoryArgs =
98    NtAllocateVirtualMemoryArgs {
99        handle: INVALID_HANDLE_VALUE,
100        base_address: ptr::null_mut(),
101    };
102#[cfg(target_arch = "x86_64")]
103static mut NT_OPEN_PROCESS_ARGS: NtOpenProcessArgs = NtOpenProcessArgs {
104    handle: ptr::null_mut(),
105    access: 0,
106    attributes: ptr::null_mut(),
107    client_id: ptr::null_mut(),
108};
109#[cfg(target_arch = "x86_64")]
110static mut NT_PROTECT_VIRTUAL_MEMORY_ARGS: NtProtectVirtualMemoryArgs =
111    NtProtectVirtualMemoryArgs {
112        handle: INVALID_HANDLE_VALUE,
113        base_address: ptr::null_mut(),
114        size: ptr::null_mut(),
115        protection: 0,
116    };
117#[cfg(target_arch = "x86_64")]
118static mut NT_WRITE_VIRTUAL_MEMORY_ARGS: NtWriteVirtualMemoryArgs = NtWriteVirtualMemoryArgs {
119    handle: INVALID_HANDLE_VALUE,
120    base_address: ptr::null_mut(),
121    buffer: ptr::null_mut(),
122    size: 0usize,
123};
124#[cfg(target_arch = "x86_64")]
125static mut NT_CREATE_THREAD_EX_ARGS: NtCreateThreadExArgs = NtCreateThreadExArgs {
126    thread: ptr::null_mut(),
127    access: 0,
128    attributes: ptr::null_mut(),
129    process: INVALID_HANDLE_VALUE,
130};
131#[allow(static_mut_refs)]
132static mut HOOKED_FUNCTIONS_INFO: Vec<(usize, Vec<u8>)> = vec![];
133
134/// Enables or disables the use of exception handlers in
135/// combination with hardware breakpoints.
136#[cfg(target_arch = "x86_64")]
137pub fn use_hardware_breakpoints(value: bool) {
138    unsafe {
139        HARDWARE_BREAKPOINTS = value;
140    }
141}
142
143/// It sets a hardware breakpoint on a certain memory address.
144///
145/// # Examples
146///
147/// ```ignore
148/// let ntdll = dyncvoke_core::get_module_base_address("ntdll.dll");
149/// let nt_open_process = dyncvoke_core::get_function_address(ntdll, "NtOpenProcess");
150/// let instruction_addr = dyncvoke_core::get_syscall_address(nt_open_process as *mut _).unwrap();
151/// dyncvoke_core::set_hardware_breakpoint(instruction_addr);
152#[cfg(target_arch = "x86_64")]
153pub fn set_hardware_breakpoint(address: usize) {
154    use windows_sys::Win32::System::Diagnostics::Debug::CONTEXT;
155
156    unsafe {
157        let mut context: CONTEXT = core::mem::zeroed();
158        context.ContextFlags = 0x100000 | 0x10; // CONTEXT_DEBUG_REGISTERS
159        let _ = GetThreadContext(GetCurrentThread(), &mut context);
160
161        context.Dr0 = address as u64;
162        context.Dr6 = 0;
163        context.Dr7 = context.Dr7 & !(((1 << 2) - 1) << 16); // 0xfffcffff ->  Break on instruction execution only
164        context.Dr7 = context.Dr7 & !(((1 << 2) - 1) << 18); // 0xfff3ffff
165        context.Dr7 = (context.Dr7 & !(((1 << 1) - 1) << 0)) | (1 << 0); // 0xfffffffe
166
167        context.ContextFlags = 0x100000 | 0x10;
168
169        let _ = SetThreadContext(GetCurrentThread(), &context);
170    }
171}
172
173/// This function acts as an Exception Handler, and should be combined with a hardware breakpoint.
174///
175/// Whenever the HB gets triggered, this function will be executed. This is meant to be used in order
176/// to spoof syscalls parameters.
177#[cfg(target_arch = "x86_64")]
178pub unsafe extern "system" fn breakpoint_handler(exceptioninfo: *mut ExceptionPointers) -> i32 {
179    if (*(*(exceptioninfo)).exception_record).ExceptionCode as u32 == 0x80000004
180    // STATUS_SINGLE_STEP
181    {
182        if ((*(*exceptioninfo).context_record).Dr7 & 1) == 1 {
183            if (*(*exceptioninfo).context_record).Rip == (*(*exceptioninfo).context_record).Dr0 {
184                (*(*exceptioninfo).context_record).Dr0 = 0; // Remove the breakpoint
185                match HARDWARE_EXCEPTION_FUNCTION {
186                    ExceptionHandleFunction::NtAllocateVirtualMemory => {
187                        (*(*exceptioninfo).context_record).R10 =
188                            NT_ALLOCATE_VIRTUAL_MEMORY_ARGS.handle as u64 as u64;
189                        (*(*exceptioninfo).context_record).Rdx =
190                            core::mem::transmute(NT_ALLOCATE_VIRTUAL_MEMORY_ARGS.base_address);
191                    }
192                    ExceptionHandleFunction::NtProtectVirtualMemory => {
193                        (*(*exceptioninfo).context_record).R10 =
194                            NT_PROTECT_VIRTUAL_MEMORY_ARGS.handle as u64 as u64;
195                        (*(*exceptioninfo).context_record).Rdx =
196                            core::mem::transmute(NT_PROTECT_VIRTUAL_MEMORY_ARGS.base_address);
197                        (*(*exceptioninfo).context_record).R8 =
198                            core::mem::transmute(NT_PROTECT_VIRTUAL_MEMORY_ARGS.size);
199                        (*(*exceptioninfo).context_record).R9 =
200                            NT_PROTECT_VIRTUAL_MEMORY_ARGS.protection as u64;
201                    }
202                    ExceptionHandleFunction::NtOpenProcess => {
203                        (*(*exceptioninfo).context_record).R10 =
204                            core::mem::transmute(NT_OPEN_PROCESS_ARGS.handle);
205                        (*(*exceptioninfo).context_record).Rdx = NT_OPEN_PROCESS_ARGS.access as u64;
206                        (*(*exceptioninfo).context_record).R8 =
207                            core::mem::transmute(NT_OPEN_PROCESS_ARGS.attributes);
208                        (*(*exceptioninfo).context_record).R9 =
209                            core::mem::transmute(NT_OPEN_PROCESS_ARGS.client_id);
210                    }
211                    ExceptionHandleFunction::NtWriteVirtualMemory => {
212                        (*(*exceptioninfo).context_record).R10 =
213                            NT_WRITE_VIRTUAL_MEMORY_ARGS.handle as u64 as u64;
214                        (*(*exceptioninfo).context_record).Rdx =
215                            core::mem::transmute(NT_WRITE_VIRTUAL_MEMORY_ARGS.base_address);
216                        (*(*exceptioninfo).context_record).R8 =
217                            core::mem::transmute(NT_WRITE_VIRTUAL_MEMORY_ARGS.buffer);
218                        (*(*exceptioninfo).context_record).R9 =
219                            NT_WRITE_VIRTUAL_MEMORY_ARGS.size as u64;
220                    }
221                    ExceptionHandleFunction::NtCreateThreadEx => {
222                        (*(*exceptioninfo).context_record).R10 =
223                            core::mem::transmute(NT_CREATE_THREAD_EX_ARGS.thread);
224                        (*(*exceptioninfo).context_record).Rdx =
225                            NT_CREATE_THREAD_EX_ARGS.access as u64;
226                        (*(*exceptioninfo).context_record).R8 =
227                            core::mem::transmute(NT_CREATE_THREAD_EX_ARGS.attributes);
228                        (*(*exceptioninfo).context_record).R9 =
229                            NT_CREATE_THREAD_EX_ARGS.process as u64;
230                    }
231                }
232            }
233        }
234        return -1; // EXCEPTION_CONTINUE_EXECUTION
235    }
236    0 // EXCEPTION_CONTINUE_SEARCH
237}
238
239/// Patch `src_address` with a trampoline to `dst_address`. Original bytes are
240/// stored so [`unhook_function`] can restore them.
241#[allow(static_mut_refs)]
242pub fn hook_function(src_address: usize, dst_address: usize) -> bool {
243    unsafe {
244        let mut original_address = src_address;
245        let handle = INVALID_HANDLE_VALUE;
246        let base_address: *mut PVOID = &mut original_address as *mut usize as *mut PVOID;
247        let mut size: usize = 4096;
248        let mut old_protection: u32 = 0;
249
250        let z = nt_protect_virtual_memory(
251            handle,
252            base_address,
253            &mut size,
254            PAGE_EXECUTE_READWRITE,
255            &mut old_protection,
256        );
257
258        if z != 0 {
259            return false;
260        }
261
262        let ntop_ptr = src_address as *mut u8;
263        let mut original_bytes: Vec<u8> = vec![];
264        if cfg!(target_pointer_width = "64") {
265            for i in 0..=12 {
266                let b = *(ntop_ptr.add(i));
267                original_bytes.push(b);
268            }
269
270            *ntop_ptr = 0x49;
271            *(ntop_ptr.add(1)) = 0xBB;
272            *(ntop_ptr.add(2) as *mut usize) = dst_address;
273            *(ntop_ptr.add(10)) = 0x41;
274            *(ntop_ptr.add(11)) = 0xFF;
275            *(ntop_ptr.add(12)) = 0xE3;
276        } else {
277            for i in 0..=5 {
278                let b = *(ntop_ptr.add(i));
279                original_bytes.push(b);
280            }
281
282            *ntop_ptr = 0x68;
283            *(ntop_ptr.add(1) as *mut usize) = dst_address;
284            *(ntop_ptr.add(5)) = 0xC3
285        }
286
287        HOOKED_FUNCTIONS_INFO.push((src_address, original_bytes));
288
289        let mut unused = 0u32;
290        let z = nt_protect_virtual_memory(
291            handle,
292            base_address,
293            &mut size,
294            old_protection,
295            &mut unused,
296        );
297
298        if z != 0 {
299            return false;
300        }
301
302        true
303    }
304}
305
306/// Restore bytes previously overwritten by [`hook_function`].
307#[allow(static_mut_refs)]
308pub fn unhook_function(address: usize) -> bool {
309    unsafe {
310        let mut unhook_info = (0, vec![]);
311        let mut index = 0;
312        for (i, element) in HOOKED_FUNCTIONS_INFO.iter().enumerate() {
313            if element.0 == address {
314                unhook_info = (element.0, element.1.to_vec());
315                index = i;
316                break;
317            }
318        }
319
320        if unhook_info.0 == 0 {
321            return false;
322        }
323
324        let mut original_address = unhook_info.0;
325        let handle = INVALID_HANDLE_VALUE;
326        let base_address: *mut PVOID = &mut original_address as *mut usize as *mut PVOID;
327        let mut size: usize = 4096;
328        let mut old_protection: u32 = 0;
329
330        let z = nt_protect_virtual_memory(
331            handle,
332            base_address,
333            &mut size,
334            PAGE_EXECUTE_READWRITE,
335            &mut old_protection,
336        );
337
338        if z != 0 {
339            return false;
340        }
341
342        let ptr = unhook_info.0 as *mut u8;
343        for i in 0..unhook_info.1.len() {
344            let addr = ptr.add(i);
345            *addr = unhook_info.1[i];
346        }
347
348        let mut unused = 0u32;
349        let z = nt_protect_virtual_memory(
350            handle,
351            base_address,
352            &mut size,
353            old_protection,
354            &mut unused,
355        );
356
357        if z != 0 {
358            return false;
359        }
360
361        HOOKED_FUNCTIONS_INFO.remove(index);
362
363        true
364    }
365}
366
367/// Retrieves the base address of a module loaded in the current process.
368///
369/// In case that the module can't be found in the current process, it will
370/// return 0.
371///
372/// # Examples
373///
374/// ```
375/// let ntdll = dyncvoke_core::get_module_base_address("ntdll.dll");
376///
377/// if ntdll != 0
378/// {
379///     println!("The base address of ntdll.dll is 0x{:X}.", ntdll);
380/// }
381/// ```
382pub fn get_module_base_address(module_name: &str) -> usize {
383    peb::get_module_by_name(module_name)
384}
385
386/// Hash-based module lookup. Pass the djb2 hash of the lowercased ascii name
387/// (compute via [`peb::hash_name`]). The plaintext never lands in `.rdata`.
388pub fn get_module_base_address_h(name_hash: u32) -> usize {
389    peb::get_module_by_hash(name_hash)
390}
391
392/// Retrieves the address of an exported function from the specified module.
393///
394/// This functions is analogous to GetProcAddress from Win32. The exported
395/// function's address is obtained by walking and parsing the EAT of the  
396/// specified module.
397///
398/// In case that the function's address can't be retrieved, it will return 0.
399///
400/// # Examples
401///
402/// ```
403/// let ntdll = dyncvoke_core::get_module_base_address("ntdll.dll");
404///
405/// if ntdll != 0
406/// {
407///     let addr = dyncvoke_core::get_function_address(ntdll, "NtCreateThread");    
408///     println!("The address where NtCreateThread is located at is 0x{:X}.", addr);
409/// }
410/// ```
411pub fn get_function_address(module_base_address: usize, function: &str) -> usize {
412    if module_base_address == 0 {
413        return 0;
414    }
415    unsafe {
416        let mut function_ptr: *mut i32 = ptr::null_mut();
417        let pe_header = *((module_base_address + 0x3C) as *mut i32);
418        let opt_header: usize = module_base_address + (pe_header as usize) + 0x18;
419        let magic = *(opt_header as *mut i16);
420        let p_export: usize;
421
422        if magic == 0x010b {
423            p_export = opt_header + 0x60;
424        } else {
425            p_export = opt_header + 0x70;
426        }
427
428        let export_rva = *(p_export as *mut i32);
429        // PE forwarders are identified by the resolved RVA falling inside
430        // the export directory's data-directory range. Without this check,
431        // get_forward_address was reading byte sequences out of real code
432        // and producing false-positive forwarder lookups for any exported
433        // function whose first 100 bytes happened to contain "." and a NUL.
434        let export_size = *((p_export + 4) as *mut i32);
435        let export_start = export_rva as usize;
436        let export_end = export_start.wrapping_add(export_size as usize);
437        let ordinal_base = *((module_base_address + export_rva as usize + 0x10) as *mut i32);
438        let number_of_names = *((module_base_address + export_rva as usize + 0x18) as *mut i32);
439        let functions_rva = *((module_base_address + export_rva as usize + 0x1C) as *mut i32);
440        let names_rva = *((module_base_address + export_rva as usize + 0x20) as *mut i32);
441        let ordinals_rva = *((module_base_address + export_rva as usize + 0x24) as *mut i32);
442
443        for x in 0..number_of_names {
444            let address =
445                *((module_base_address + names_rva as usize + x as usize * 4) as *mut i32);
446            let mut function_name_ptr = (module_base_address + address as usize) as *mut u8;
447            let mut function_name: String = "".to_string();
448
449            while *function_name_ptr as char != '\0'
450            // null byte
451            {
452                function_name.push(*function_name_ptr as char);
453                function_name_ptr = function_name_ptr.add(1);
454            }
455
456            if function_name.to_lowercase() == function.to_lowercase() {
457                let function_ordinal = *((module_base_address
458                    + ordinals_rva as usize
459                    + x as usize * 2) as *mut i16) as i32
460                    + ordinal_base;
461                let function_rva = *((module_base_address
462                    + functions_rva as usize
463                    + (4 * (function_ordinal - ordinal_base)) as usize)
464                    as *mut i32);
465                function_ptr = (module_base_address + function_rva as usize) as *mut i32;
466
467                let rva = function_rva as usize;
468                if rva >= export_start && rva < export_end {
469                    function_ptr = get_forward_address(function_ptr as *mut u8) as *mut i32;
470                }
471
472                break;
473            }
474        }
475
476        let mut ret: usize = 0;
477
478        if function_ptr != ptr::null_mut() {
479            ret = function_ptr as usize;
480        }
481
482        ret
483    }
484}
485
486fn get_forward_address(function_ptr: *mut u8) -> usize {
487    unsafe {
488        let mut c = 100;
489        let mut ptr = function_ptr.clone();
490        let mut forwarded_names = "".to_string();
491
492        loop {
493            if *ptr as char != '\0' {
494                forwarded_names.push(*ptr as char);
495            } else {
496                break;
497            }
498
499            ptr = ptr.add(1);
500            c = c - 1;
501
502            if c == 0 {
503                return function_ptr as usize;
504            }
505        }
506
507        let values: Vec<&str> = forwarded_names.split(".").collect();
508        if values.len() != 2 {
509            return function_ptr as usize;
510        }
511
512        let mut forwarded_module_name = values[0].to_string();
513        let forwarded_export_name = values[1].to_string();
514
515        if forwarded_module_name.len() < 2 || forwarded_export_name.len() == 0 {
516            return function_ptr as usize;
517        }
518
519        let api_set = get_api_mapping();
520
521        if forwarded_module_name.len() < 2
522            || !forwarded_module_name.is_char_boundary(forwarded_module_name.len() - 2)
523        {
524            return function_ptr as usize;
525        }
526
527        let lookup_key = format!(
528            "{}{}",
529            &forwarded_module_name[..forwarded_module_name.len() - 2],
530            ".dll"
531        );
532
533        if api_set.contains_key(&lookup_key) {
534            forwarded_module_name = match api_set.get(&lookup_key) {
535                Some(x) => x.to_string(),
536                None => forwarded_module_name,
537            };
538        } else {
539            forwarded_module_name = forwarded_module_name + ".dll";
540        }
541
542        let mut module = get_module_base_address(&forwarded_module_name);
543
544        if module == 0 {
545            module = load_library_a(&forwarded_module_name);
546        }
547
548        if module != 0 {
549            return get_function_address(module, &forwarded_export_name);
550        }
551
552        function_ptr as usize
553    }
554}
555
556pub fn get_api_mapping() -> BTreeMap<String, String> {
557    unsafe {
558        let handle = INVALID_HANDLE_VALUE;
559        let mut p: PROCESS_BASIC_INFORMATION = core::mem::zeroed();
560        let process_information: *mut c_void = &mut p as *mut PROCESS_BASIC_INFORMATION as *mut c_void;
561        let _ret = nt_query_information_process(
562            handle,
563            0,
564            process_information,
565            size_of::<PROCESS_BASIC_INFORMATION>() as u32,
566            ptr::null_mut(),
567        );
568
569        let process_information_ptr: *mut PROCESS_BASIC_INFORMATION =
570            core::mem::transmute(process_information);
571
572        let api_set_map_offset: usize;
573
574        if size_of::<usize>() == 4 {
575            api_set_map_offset = 0x38;
576        } else {
577            api_set_map_offset = 0x68;
578        }
579
580        let mut api_set_dict: BTreeMap<String, String> = BTreeMap::new();
581
582        let api_set_namespace_ptr = *(((*process_information_ptr).PebBaseAddress as usize
583            + api_set_map_offset) as *mut usize);
584        let api_set_namespace_ptr: *mut ApiSetNamespace =
585            core::ptr::with_exposed_provenance_mut::<ApiSetNamespace>(api_set_namespace_ptr);
586        let namespace = *api_set_namespace_ptr;
587
588        for i in 0..namespace.count {
589            let set_entry_ptr = (api_set_namespace_ptr as usize
590                + namespace.entry_offset as usize
591                + (i * size_of::<ApiSetNamespaceEntry>() as i32) as usize)
592                as *mut ApiSetNamespaceEntry;
593            let set_entry = *set_entry_ptr;
594
595            let mut api_set_entry_name_ptr: *mut u8 =
596                (api_set_namespace_ptr as usize + set_entry.name_offset as usize) as *mut u8;
597            let mut api_set_entry_name: String = "".to_string();
598            let mut j = 0;
599            while j < (set_entry.name_length / 2) {
600                let c = *api_set_entry_name_ptr as char;
601                if c != '\0' {
602                    api_set_entry_name.push(c);
603                    j = j + 1;
604                }
605
606                api_set_entry_name_ptr = api_set_entry_name_ptr.add(1);
607            }
608
609            if api_set_entry_name.len() < 2 {
610                continue;
611            }
612            let api_set_entry_key = format!(
613                "{}{}",
614                &api_set_entry_name[..api_set_entry_name.len() - 2],
615                ".dll"
616            );
617            let mut set_value_ptr: *mut ApiSetValueEntry = ptr::null_mut();
618
619            if set_entry.value_length == 1 {
620                let value =
621                    (api_set_namespace_ptr as usize + set_entry.value_offset as usize) as *mut u8;
622                set_value_ptr = core::mem::transmute(value);
623            } else if set_entry.value_length > 1 {
624                for x in 0..set_entry.value_length {
625                    let host_ptr = (api_set_namespace_ptr as usize
626                        + set_entry.value_offset as usize
627                        + size_of::<ApiSetValueEntry>() as usize * x as usize)
628                        as *mut u8;
629                    let mut host_walk: *mut u8 = host_ptr;
630                    let mut host: String = "".to_string();
631                    loop {
632                        let c = *host_walk;
633                        if c == 0 {
634                            break;
635                        }
636                        host.push(c as char);
637                        host_walk = host_walk.add(1);
638                    }
639
640                    if host != api_set_entry_name {
641                        set_value_ptr = (api_set_namespace_ptr as usize
642                            + set_entry.value_offset as usize
643                            + size_of::<ApiSetValueEntry>() as usize * x as usize)
644                            as *mut ApiSetValueEntry;
645                    }
646                }
647
648                if set_value_ptr == ptr::null_mut() {
649                    set_value_ptr = (api_set_namespace_ptr as usize
650                        + set_entry.value_offset as usize)
651                        as *mut ApiSetValueEntry;
652                }
653            }
654
655            let set_value = *set_value_ptr;
656            let mut api_set_value: String = "".to_string();
657            if set_value.value_count != 0 {
658                let mut value_ptr =
659                    (api_set_namespace_ptr as usize + set_value.value_offset as usize) as *mut u8;
660                let mut r = 0;
661                while r < (set_value.value_count / 2) {
662                    let c = *value_ptr as char;
663                    if c != '\0' {
664                        api_set_value.push(c);
665                        r = r + 1;
666                    }
667
668                    value_ptr = value_ptr.add(1);
669                }
670            }
671
672            api_set_dict.insert(api_set_entry_key, api_set_value);
673        }
674
675        api_set_dict
676    }
677}
678
679
680/// Calls the module's entry point with the option DLL_ATTACH_PROCESS.
681///
682/// # Examples
683///
684/// ```ignore
685///    let pe = manualmap::read_and_map_module("c:\\some\\random\\file.dll").unwrap();
686///    let ret = dyncvoke_core::call_module_entry_point(pe.0, pe.1);
687///
688///    match ret
689///    {
690///         Ok(()) => println!("Module entry point successfully executed."),
691///         Err(e) => println!("Error ocurred: {}", e)
692///    }
693/// ```
694pub fn call_module_entry_point(
695    pe_info: PeMetadata,
696    module_base_address: usize,
697) -> Result<(), String> {
698    let entry_point;
699    if pe_info.is_32_bit {
700        entry_point = module_base_address + pe_info.opt_header_32.AddressOfEntryPoint as usize;
701    } else {
702        entry_point = module_base_address + pe_info.opt_header_64.address_of_entry_point as usize;
703    }
704
705    unsafe {
706        let main: EntryPoint = core::mem::transmute(entry_point);
707        let module = entry_point as HINSTANCE;
708        let ret = main(module, DLL_PROCESS_ATTACH, ptr::null_mut());
709
710        if ret == 0 {
711            return Err(lc!(
712                "[x] Failed to call module's entry point (DllMain -> DLL_PROCESS_ATTACH)."
713            ));
714        }
715
716        Ok(())
717    }
718}
719
720/// Retrieves the address of an exported function from the specified module by its ordinal.
721///
722/// In case that the function's address can't be retrieved, it will return 0.
723///
724/// This functions internally calls LdrGetProcedureAddress.
725///
726/// # Examples
727///
728/// ```ignore
729/// let ntdll = dyncvoke_core::get_module_base_address("ntdll.dll");
730///
731/// if ntdll != 0
732/// {
733///     let ordinal: u32 = 8;
734///     let addr = dyncvoke_core::get_function_address_by_ordinal(ntdll, ordinal);
735///     if addr != 0
736///     {
737///         println!("The function with ordinal 8 is located at 0x{:X}.", addr);
738///     }
739/// }
740/// ```
741pub fn get_function_address_by_ordinal(module_base_address: usize, ordinal: u32) -> usize {
742    let ret = ldr_get_procedure_address(module_base_address, "", ordinal);
743    ret
744}
745
746/// Call NtCreateUserProcess to fork the current process.
747/// Inheritable objects are inherited by the child process (PROCESS_CREATE_FLAGS_INHERIT_FROM_PARENT).
748///
749/// The function returns an NTSTATUS.
750pub fn fork() -> i32 {
751    unsafe {
752        let mut process_handle: HANDLE = ptr::null_mut();
753        let mut thread_handle: HANDLE = ptr::null_mut();
754        let mut create_info: PsCreateInfo = core::mem::zeroed();
755        create_info.size = size_of::<PsCreateInfo>();
756        let ps_create_info: *mut PsCreateInfo = core::mem::transmute(&create_info);
757
758        let ret = nt_create_user_process(
759            &mut process_handle,
760            &mut thread_handle,
761            (0x000F0000) | (0x00100000) | 0xFFFF, //PROCESS_ALL_ACCESS
762            (0x000F0000) | (0x00100000) | 0xFFFF, //THREAD_ALL_ACCESS
763            ptr::null_mut(),
764            ptr::null_mut(),
765            0x00000004, //PROCESS_CREATE_FLAGS_INHERIT_FROM_PARENT
766            0,
767            ptr::null_mut(),
768            ps_create_info, // Default PS_CREATE_INFO struct
769            ptr::null_mut(),
770        );
771
772        ret
773    }
774}
775
776/// Retrieves the address of an exported function from the specified module either by its name
777/// or by its ordinal number.
778///
779/// This functions internally calls LdrGetProcedureAddress.
780///
781/// In case that the function's address can't be retrieved, it will return 0.
782///
783/// # Examples
784///
785/// ```
786/// let ntdll = dyncvoke_core::get_module_base_address("ntdll.dll");
787///
788/// if ntdll != 0
789/// {
790///     let ordinal: u32 = 8; // Ordinal 8 represents the function RtlDispatchAPC
791///     let addr = dyncvoke_core::ldr_get_procedure_address(ntdll,"", 8);
792///     if addr != 0
793///     {
794///         println!("The function with ordinal 8 is located at 0x{:X}.", addr);
795///     }
796/// }
797/// ```
798pub fn ldr_get_procedure_address(module_handle: usize, function_name: &str, ordinal: u32) -> usize {
799    unsafe {
800        let ret: Option<i32>;
801        let func_ptr: data::LdrGetProcedureAddress;
802        let hmodule: PVOID = core::ptr::with_exposed_provenance_mut::<c_void>(module_handle);
803        let mut r = usize::default();
804        let return_address: *mut PVOID = &mut r as *mut usize as *mut PVOID;
805
806        // LdrGetProcedureAddress wants a PANSI_STRING (16 bytes on x64), not
807        // a Rust String (24 bytes, different layout). Hold the CString alive
808        // for the duration of the call so .as_ptr() stays valid; the
809        // ANSI_STRING borrows from it.
810        let c_name = if function_name.is_empty() {
811            None
812        } else {
813            CString::new(function_name).ok()
814        };
815        let ansi = c_name.as_ref().map(|s| data::ANSI_STRING {
816            Length: s.as_bytes().len() as u16,
817            MaximumLength: (s.as_bytes().len() + 1) as u16,
818            Buffer: s.as_ptr() as *const u8,
819        });
820        let name_ptr: *const data::ANSI_STRING = match ansi.as_ref() {
821            Some(a) => a as *const _,
822            None => ptr::null(),
823        };
824
825        let module_base_address = get_module_base_address(&lc!("ntdll.dll"));
826        dynamic_invoke!(
827            module_base_address,
828            &lc!("LdrGetProcedureAddress"),
829            func_ptr,
830            ret,
831            hmodule,
832            name_ptr,
833            ordinal,
834            return_address
835        );
836
837        match ret {
838            Some(0) => *return_address as usize,
839            _ => 0,
840        }
841    }
842}
843
844/// Dynamically calls SetUnhandledExceptionFilter.
845pub fn set_unhandled_exception_filter(address: usize) -> LptopLevelExceptionFilter {
846    unsafe {
847        let ret: Option<LptopLevelExceptionFilter>;
848        let func_ptr: data::SetUnhandledExceptionFilter;
849        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
850        dynamic_invoke!(
851            module_base_address,
852            &lc!("SetUnhandledExceptionFilter"),
853            func_ptr,
854            ret,
855            address
856        );
857
858        match ret {
859            Some(x) => return x,
860            None => return 0,
861        }
862    }
863}
864
865/// Dynamically calls AddVectoredExceptionHandler.
866pub fn add_vectored_exception_handler(first: u32, address: usize) -> PVOID {
867    unsafe {
868        let ret: Option<PVOID>;
869        let func_ptr: data::AddVectoredExceptionHandler;
870        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
871        dynamic_invoke!(
872            module_base_address,
873            &lc!("AddVectoredExceptionHandler"),
874            func_ptr,
875            ret,
876            first,
877            address
878        );
879
880        match ret {
881            Some(x) => return x,
882            None => return ptr::null_mut(),
883        }
884    }
885}
886
887/// Uses the Thread Pool to call LoadLibraryA.
888///
889/// It will return either the module's base address or 0.
890///
891/// # Examples
892///
893/// ```ignore
894/// let ret = dyncvoke_core::load_library_a_tp("ntdll.dll");
895///
896/// if ret != 0 {println!("ntdll.dll base address is 0x{:X}.", ret)};
897/// ```
898pub fn load_library_a_tp(module: &str) -> usize {
899    unsafe {
900        let ret: Option<i32>;
901        let func_ptr: data::RtlQueueWorkItem;
902        let name = CString::new(module.to_string()).expect("");
903        let module_name: PVOID = core::mem::transmute(name.as_ptr());
904        let k32 = get_module_base_address(&lc!("kernel32.dll"));
905        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
906        let load_library = get_function_address(k32, &lc!("LoadLibraryA"));
907        dynamic_invoke!(
908            ntdll,
909            &lc!("RtlQueueWorkItem"),
910            func_ptr,
911            ret,
912            load_library,
913            module_name,
914            0
915        );
916
917        match ret {
918            Some(x) => {
919                if x != 0 {
920                    return 0;
921                } else {
922                    nt_sleep_millis(500);
923                    return get_module_base_address(module);
924                }
925            }
926            None => {
927                return 0;
928            }
929        }
930    }
931}
932
933/// Block the current thread for `ms` milliseconds via NtDelayExecution.
934pub fn nt_sleep_millis(ms: u64) {
935    type NtDelayExecution = unsafe extern "system" fn(u8, *mut LARGE_INTEGER) -> i32;
936    unsafe {
937        let mut interval = LARGE_INTEGER {
938            QuadPart: -(ms as i64 * 10_000),
939        };
940        let ret: Option<i32>;
941        let func_ptr: NtDelayExecution;
942        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
943        dynamic_invoke!(
944            ntdll,
945            &lc!("NtDelayExecution"),
946            func_ptr,
947            ret,
948            0u8,
949            &mut interval as *mut _
950        );
951        let _ = ret;
952    }
953}
954
955/// Dynamically calls LoadLibraryA.
956///
957/// It will return either the module's base address or 0.
958///
959/// # Examples
960///
961/// ```ignore
962/// let ret = dyncvoke_core::load_library_a("ntdll.dll");
963///
964/// if ret != 0 {println!("ntdll.dll base address is 0x{:X}.", ret)};
965/// ```
966pub fn load_library_a(module: &str) -> usize {
967    unsafe {
968        let ret: Option<usize>;
969        let func_ptr: data::LoadLibraryA;
970        let name = CString::new(module.to_string()).expect("");
971        let module_name: *mut u8 = core::mem::transmute(name.as_ptr());
972        let k32 = get_module_base_address(&lc!("kernel32.dll"));
973        dynamic_invoke!(k32, &lc!("LoadLibraryA"), func_ptr, ret, module_name);
974
975        match ret {
976            Some(x) => {
977                return x;
978            }
979            None => {
980                return 0;
981            }
982        }
983    }
984}
985
986/// Frees the loaded dll. The function expects the module's base address.
987///
988/// If the function succeeds, the return value is nonzero.
989///
990/// # Examples
991///
992/// ```
993/// let module_handle: usize = dyncvoke_core::load_library_a("somedll.dll");
994/// let ret = dyncvoke_core::free_library(module_handle as isize);
995///
996/// if ret == 0 {println!("somedll.dll sucessfully freed.")};
997/// ```
998pub fn free_library(module_handle: isize) -> usize {
999    unsafe {
1000        let ret: Option<BOOL>;
1001        let func_ptr: data::FreeLibrary;
1002        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
1003        dynamic_invoke!(
1004            module_base_address,
1005            &lc!("FreeLibrary"),
1006            func_ptr,
1007            ret,
1008            module_handle as HINSTANCE
1009        );
1010
1011        match ret {
1012            Some(x) => return if x != 0 { 1 } else { 0 },
1013            None => return 0,
1014        }
1015    }
1016}
1017
1018/// Dynamically calls CreateFileA.
1019/// On success, it returns a valid handle to the specified file. Otherwise, a null handle is returned.
1020pub fn create_file_a(
1021    name: *mut u8,
1022    access: u32,
1023    mode: u32,
1024    attributes: *const SECURITY_ATTRIBUTES,
1025    disposition: u32,
1026    flags: u32,
1027    template: HANDLE,
1028) -> HANDLE {
1029    unsafe {
1030        let ret: Option<HANDLE>;
1031        let func_ptr: data::CreateFileA;
1032        let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
1033        dynamic_invoke!(
1034            kernel32,
1035            &lc!("CreateFileA"),
1036            func_ptr,
1037            ret,
1038            name,
1039            access,
1040            mode,
1041            attributes,
1042            disposition,
1043            flags,
1044            template
1045        );
1046
1047        match ret {
1048            Some(x) => return x,
1049            None => return core::ptr::null_mut(),
1050        }
1051    }
1052}
1053
1054/// Dynamically calls GetFileSize.
1055/// It returns either the specified file size (success) or 0 (an error ocurred).
1056pub fn get_file_size(handle: HANDLE, size: *mut u32) -> u32 {
1057    unsafe {
1058        let ret: Option<u32>;
1059        let func_ptr: data::GetFileSize;
1060        let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
1061        dynamic_invoke!(kernel32, &lc!("GetFileSize"), func_ptr, ret, handle, size);
1062
1063        match ret {
1064            Some(x) => return x,
1065            None => return 0,
1066        }
1067    }
1068}
1069
1070/// Dynamically calls CreateFileMappingW.
1071///
1072pub fn create_file_mapping_w(
1073    file: HANDLE,
1074    attributes: *const SECURITY_ATTRIBUTES,
1075    protect: u32,
1076    max_size_high: u32,
1077    max_size_low: u32,
1078    name: *mut u8,
1079) -> HANDLE {
1080    unsafe {
1081        let ret: Option<HANDLE>;
1082        let func_ptr: data::CreateFileMapping;
1083        let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
1084        dynamic_invoke!(
1085            kernel32,
1086            &lc!("CreateFileMappingW"),
1087            func_ptr,
1088            ret,
1089            file,
1090            attributes,
1091            protect,
1092            max_size_high,
1093            max_size_low,
1094            name
1095        );
1096
1097        match ret {
1098            Some(x) => return x,
1099            None => return core::ptr::null_mut(),
1100        }
1101    }
1102}
1103
1104/// Dynamically calls MapViewOfFile.
1105///
1106pub fn map_view_of_file(
1107    file: HANDLE,
1108    access: u32,
1109    off_high: u32,
1110    off_low: u32,
1111    bytes: usize,
1112) -> PVOID {
1113    unsafe {
1114        let ret: Option<PVOID>;
1115        let func_ptr: data::MapViewOfFile;
1116        let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
1117        dynamic_invoke!(
1118            kernel32,
1119            &lc!("MapViewOfFile"),
1120            func_ptr,
1121            ret,
1122            file,
1123            access,
1124            off_high,
1125            off_low,
1126            bytes
1127        );
1128
1129        match ret {
1130            Some(x) => return x,
1131            None => return ptr::null_mut(),
1132        }
1133    }
1134}
1135
1136/// Dynamically calls UnmapViewOfFile.
1137///
1138pub fn unmap_view_of_file(base_address: PVOID) -> bool {
1139    unsafe {
1140        let ret: Option<BOOL>;
1141        let func_ptr: data::UnmapViewOfFile;
1142        let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
1143        dynamic_invoke!(
1144            kernel32,
1145            &lc!("UnmapViewOfFile"),
1146            func_ptr,
1147            ret,
1148            base_address
1149        );
1150
1151        match ret {
1152            Some(x) => return x != 0,
1153            None => return false,
1154        }
1155    }
1156}
1157
1158/// Dynamically calls RollbackTransaction.
1159///
1160pub fn rollback_transaction(transaction: HANDLE) -> bool {
1161    unsafe {
1162        let ret: Option<BOOL>;
1163        let func_ptr: data::RollbackTransaction;
1164        let ktmv = load_library_a(&lc!("KtmW32.dll"));
1165        dynamic_invoke!(
1166            ktmv,
1167            &lc!("RollbackTransaction"),
1168            func_ptr,
1169            ret,
1170            transaction
1171        );
1172
1173        match ret {
1174            Some(x) => return x != 0,
1175            None => return false,
1176        }
1177    }
1178}
1179
1180/// Opens a HANDLE to a process.
1181///
1182/// If the function fails, it will return a null HANDLE.
1183///
1184/// # Examples
1185///
1186/// ```ignore
1187/// let pid = 792u32;
1188/// let handle = dyncvoke_core::open_process(0x0040, 0, pid); //PROCESS_DUP_HANDLE access right.
1189///
1190/// if !handle.is_null()
1191/// {
1192///     println!("Handle to process with id {} with PROCESS_DUP_HANDLE access right successfully obtained.", pid);
1193/// }
1194/// ```
1195pub fn open_process(desired_access: u32, inherit_handle: i32, process_id: u32) -> HANDLE {
1196    unsafe {
1197        let ret: Option<HANDLE>;
1198        let func_ptr: data::OpenProcess;
1199        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
1200        dynamic_invoke!(
1201            module_base_address,
1202            &lc!("OpenProcess"),
1203            func_ptr,
1204            ret,
1205            desired_access,
1206            inherit_handle,
1207            process_id
1208        );
1209
1210        match ret {
1211            Some(x) => return x,
1212            None => return core::ptr::null_mut(),
1213        }
1214    }
1215}
1216
1217/// Opens a HANDLE to a thread.
1218///
1219/// If the function fails, it will return a null HANDLE.
1220///
1221/// # Examples
1222///
1223/// ```ignore
1224/// let thread_id = 792u32;
1225/// let handle = dyncvoke_core::open_thread(0x0002, 0, thread_id); //THREAD_SUSPEND_RESUME access right.
1226///
1227/// if !handle.is_null()
1228/// {
1229///     println!("Handle to thread with id {} with THREAD_SUSPEND_RESUME access right successfully obtained.", thread_id);
1230/// }
1231/// ```
1232pub fn open_thread(desired_access: u32, inherit_handle: i32, thread_id: u32) -> HANDLE {
1233    unsafe {
1234        let ret: Option<HANDLE>;
1235        let func_ptr: data::OpenThread;
1236        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
1237        dynamic_invoke!(
1238            module_base_address,
1239            &lc!("OpenThread"),
1240            func_ptr,
1241            ret,
1242            desired_access,
1243            inherit_handle,
1244            thread_id
1245        );
1246
1247        match ret {
1248            Some(x) => return x,
1249            None => return core::ptr::null_mut(),
1250        }
1251    }
1252}
1253
1254/// Closes a HANDLE object.
1255///
1256/// It will return either a boolean value or an Err with a descriptive error message. If the function
1257/// fails the bool value returned will be false.
1258///
1259/// # Examples
1260///
1261/// ```ignore
1262/// let pid = 792u32;
1263/// let handle = dyncvoke_core::open_process(0x0040, 0, pid); //PROCESS_DUP_HANDLE access right.
1264///
1265/// if !handle.is_null()
1266/// {
1267///     let r = dyncvoke_core::close_handle(handle);
1268///     if r
1269///     {
1270///         println!("Handle to process with id {} closed.", pid);
1271///     }
1272/// }
1273/// ```
1274pub fn close_handle(handle: HANDLE) -> bool {
1275    unsafe {
1276        let ret: Option<i32>;
1277        let func_ptr: data::CloseHandle;
1278        let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
1279        dynamic_invoke!(kernel32, &lc!("CloseHandle"), func_ptr, ret, handle);
1280
1281        match ret {
1282            Some(x) => {
1283                if x == 0 {
1284                    return false;
1285                } else {
1286                    return true;
1287                }
1288            }
1289            None => return false,
1290        }
1291    }
1292}
1293
1294/// Dynamically calls TlsAlloc.
1295pub fn tls_alloc() -> u32 {
1296    unsafe {
1297        let ret: Option<u32>;
1298        let func_ptr: data::TlsAlloc;
1299        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
1300        dynamic_invoke!(module_base_address, &lc!("TlsAlloc"), func_ptr, ret,);
1301
1302        match ret {
1303            Some(x) => return x,
1304            None => return TLS_OUT_OF_INDEXES,
1305        }
1306    }
1307}
1308
1309/// Dynamically calls TlsGetValue.
1310pub fn tls_get_value(index: u32) -> PVOID {
1311    unsafe {
1312        let ret: Option<PVOID>;
1313        let func_ptr: data::TlsGetValue;
1314        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
1315        dynamic_invoke!(
1316            module_base_address,
1317            &lc!("TlsGetValue"),
1318            func_ptr,
1319            ret,
1320            index
1321        );
1322
1323        match ret {
1324            Some(x) => return x,
1325            None => return ptr::null_mut(),
1326        }
1327    }
1328}
1329
1330/// Dynamically calls TlsSetValue.
1331pub fn tls_set_value(index: u32, data: PVOID) -> bool {
1332    unsafe {
1333        let ret: Option<bool>;
1334        let func_ptr: data::TlsSetValue;
1335        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
1336        dynamic_invoke!(
1337            module_base_address,
1338            &lc!("TlsSetValue"),
1339            func_ptr,
1340            ret,
1341            index,
1342            data
1343        );
1344
1345        match ret {
1346            Some(x) => return x,
1347            None => return false,
1348        }
1349    }
1350}
1351
1352/// Dynamically calls EnumProcessModules.
1353pub fn enum_process_modules(
1354    process: HANDLE,
1355    module: *mut usize,
1356    cb: u32,
1357    needed: *mut u32,
1358) -> bool {
1359    unsafe {
1360        let ret: Option<bool>;
1361        let func_ptr: data::EnumProcessModules;
1362        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
1363        dynamic_invoke!(
1364            module_base_address,
1365            &lc!("K32EnumProcessModules"),
1366            func_ptr,
1367            ret,
1368            process,
1369            module,
1370            cb,
1371            needed
1372        );
1373
1374        match ret {
1375            Some(x) => return x,
1376            None => return false,
1377        }
1378    }
1379}
1380
1381/// Dynamically calls GetModuleHandleExA.
1382pub fn get_module_handle_ex_a(flags: i32, module_name: *const u8, module: *mut usize) -> bool {
1383    unsafe {
1384        let ret: Option<bool>;
1385        let func_ptr: data::GetModuleHandleExA;
1386        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
1387        dynamic_invoke!(
1388            module_base_address,
1389            &lc!("GetModuleHandleExA"),
1390            func_ptr,
1391            ret,
1392            flags,
1393            module_name,
1394            module
1395        );
1396
1397        match ret {
1398            Some(x) => return x,
1399            None => return false,
1400        }
1401    }
1402}
1403
1404/// Dynamically calls GetModuleBaseNameW.
1405pub fn get_module_base_name_w(
1406    process: HANDLE,
1407    module: usize,
1408    base_name: *mut u16,
1409    size: u32,
1410) -> u32 {
1411    unsafe {
1412        let ret: Option<u32>;
1413        let func_ptr: data::GetModuleBaseNameW;
1414        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
1415        dynamic_invoke!(
1416            module_base_address,
1417            &lc!("K32GetModuleBaseNameW"),
1418            func_ptr,
1419            ret,
1420            process,
1421            module,
1422            base_name,
1423            size
1424        );
1425
1426        match ret {
1427            Some(x) => return x,
1428            None => return 0,
1429        }
1430    }
1431}
1432
1433/// Dynamically calls GetModuleBaseNameW.
1434pub fn get_module_file_name_ex_w(
1435    process: HANDLE,
1436    module: usize,
1437    base_name: *mut u16,
1438    size: u32,
1439) -> u32 {
1440    unsafe {
1441        let ret: Option<u32>;
1442        let func_ptr: data::GetModuleFileNameExW;
1443        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
1444        dynamic_invoke!(
1445            module_base_address,
1446            &lc!("K32GetModuleFileNameExW"),
1447            func_ptr,
1448            ret,
1449            process,
1450            module,
1451            base_name,
1452            size
1453        );
1454
1455        match ret {
1456            Some(x) => return x,
1457            None => return 0,
1458        }
1459    }
1460}
1461
1462/// Read `TEB.LastErrorValue` (`gs:[0x68]` x64 / `fs:[0x34]` x86).
1463///
1464/// Source: Geoff Chappell TEB layout, LastErrorValue at 0x68 (x64) / 0x34 (x86).
1465pub fn get_last_error() -> u32 {
1466    let last_error: u32;
1467    #[cfg(target_arch = "x86_64")]
1468    unsafe {
1469        core::arch::asm!(
1470            "mov {err:e}, gs:[0x68]",
1471            err = out(reg) last_error,
1472            options(nostack, preserves_flags, readonly)
1473        );
1474    }
1475    #[cfg(target_arch = "x86")]
1476    unsafe {
1477        core::arch::asm!(
1478            "mov {err:e}, fs:[0x34]",
1479            err = out(reg) last_error,
1480            options(nostack, preserves_flags, readonly)
1481        );
1482    }
1483    #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
1484    compile_error!("get_last_error only supports x86 / x86_64");
1485    last_error
1486}
1487
1488/// Dynamically calls LocalAlloc.
1489pub fn local_alloc(flags: u32, size: usize) -> PVOID {
1490    unsafe {
1491        let ret: Option<PVOID>;
1492        let func_ptr: data::LocalAlloc;
1493        let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
1494        dynamic_invoke!(
1495            module_base_address,
1496            &lc!("LocalAlloc"),
1497            func_ptr,
1498            ret,
1499            flags,
1500            size
1501        );
1502
1503        match ret {
1504            Some(x) => return x,
1505            None => return ptr::null_mut(),
1506        }
1507    }
1508}
1509
1510/// Dynamically calls GetSystemInfo.
1511pub fn get_system_info(sysinfo: *mut SYSTEM_INFO) {
1512    unsafe {
1513        let _ret: Option<()>;
1514        let func_ptr: data::GetSystemInfo;
1515        let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
1516        dynamic_invoke!(kernel32, &lc!("GetSystemInfo"), func_ptr, _ret, sysinfo);
1517    }
1518}
1519
1520/// Dynamically calls VirtualQueryEx.
1521pub fn virtual_query_ex(
1522    process_handle: HANDLE,
1523    page_address: *const c_void,
1524    buffer: *mut MEMORY_BASIC_INFORMATION,
1525    length: usize,
1526) -> usize {
1527    unsafe {
1528        let ret: Option<usize>;
1529        let func_ptr: data::VirtualQueryEx;
1530        let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
1531        dynamic_invoke!(
1532            kernel32,
1533            &lc!("VirtualQueryEx"),
1534            func_ptr,
1535            ret,
1536            process_handle,
1537            page_address,
1538            buffer,
1539            length
1540        );
1541
1542        match ret {
1543            Some(x) => return x,
1544            None => return 0,
1545        }
1546    }
1547}
1548
1549/// Dynamically calls VirtualFree.
1550pub fn virtual_free(address: PVOID, size: usize, free_type: u32) -> bool {
1551    unsafe {
1552        let ret: Option<bool>;
1553        let func_ptr: data::VirtualFree;
1554        let k32 = get_module_base_address(&lc!("kernel32.dll"));
1555        dynamic_invoke!(
1556            k32,
1557            &lc!("VirtualFree"),
1558            func_ptr,
1559            ret,
1560            address,
1561            size,
1562            free_type
1563        );
1564
1565        match ret {
1566            Some(x) => {
1567                return x;
1568            }
1569            None => return false,
1570        }
1571    }
1572}
1573
1574/// Dynamically calls NtCreateUserProcess.
1575///
1576/// It will return the NTSTATUS value returned by the call.
1577pub fn nt_create_user_process(
1578    process_handle: *mut HANDLE,
1579    thread_handle: *mut HANDLE,
1580    process_access: u32,
1581    thread_access: u32,
1582    object_attributes: *mut OBJECT_ATTRIBUTES,
1583    thread_object_attr: *mut OBJECT_ATTRIBUTES,
1584    process_flags: u32,
1585    thread_flags: u32,
1586    parameters: PVOID,
1587    create_info: *mut PsCreateInfo,
1588    attr_list: *mut PsAttributeList,
1589) -> i32 {
1590    unsafe {
1591        let ret: Option<i32>;
1592        let func_ptr: data::NtCreateUserProcess;
1593        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
1594        dynamic_invoke!(
1595            ntdll,
1596            &lc!("NtCreateUserProcess"),
1597            func_ptr,
1598            ret,
1599            process_handle,
1600            thread_handle,
1601            process_access,
1602            thread_access,
1603            object_attributes,
1604            thread_object_attr,
1605            process_flags,
1606            thread_flags,
1607            parameters,
1608            create_info,
1609            attr_list
1610        );
1611
1612        match ret {
1613            Some(x) => return x,
1614            None => return -1,
1615        }
1616    }
1617}
1618
1619/// Dynamically calls NtWriteVirtualMemory.
1620///
1621/// It will return the NTSTATUS value returned by the call.
1622#[cfg(target_arch = "x86_64")]
1623pub fn nt_write_virtual_memory(
1624    mut handle: HANDLE,
1625    base_address: PVOID,
1626    mut buffer: PVOID,
1627    mut size: usize,
1628    bytes_written: *mut usize,
1629) -> i32 {
1630    unsafe {
1631        let ret;
1632        let func_ptr: data::NtWriteVirtualMemory;
1633        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
1634
1635        let _decoy_buf: Vec<u8>;
1636        if HARDWARE_BREAKPOINTS {
1637            let addr = get_function_address(ntdll, &lc!("NtWriteVirtualMemory")) as usize;
1638            HARDWARE_EXCEPTION_FUNCTION = ExceptionHandleFunction::NtWriteVirtualMemory;
1639            NT_WRITE_VIRTUAL_MEMORY_ARGS.handle = handle;
1640            NT_WRITE_VIRTUAL_MEMORY_ARGS.base_address = base_address;
1641            NT_WRITE_VIRTUAL_MEMORY_ARGS.buffer = buffer;
1642            NT_WRITE_VIRTUAL_MEMORY_ARGS.size = size;
1643            set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
1644
1645            handle = INVALID_HANDLE_VALUE;
1646            _decoy_buf = vec![20u8];
1647            buffer = _decoy_buf.as_ptr() as PVOID;
1648            size = _decoy_buf.len();
1649        }
1650
1651        dynamic_invoke!(
1652            ntdll,
1653            &lc!("NtWriteVirtualMemory"),
1654            func_ptr,
1655            ret,
1656            handle,
1657            base_address,
1658            buffer,
1659            size,
1660            bytes_written
1661        );
1662
1663        match ret {
1664            Some(x) => return x,
1665            None => return -1,
1666        }
1667    }
1668}
1669
1670/// Dynamically calls NtWriteVirtualMemory.
1671///
1672/// It will return the NTSTATUS value returned by the call.
1673#[cfg(target_arch = "x86")]
1674pub fn nt_write_virtual_memory(
1675    handle: HANDLE,
1676    base_address: PVOID,
1677    buffer: PVOID,
1678    size: usize,
1679    bytes_written: *mut usize,
1680) -> i32 {
1681    unsafe {
1682        let ret;
1683        let func_ptr: data::NtWriteVirtualMemory;
1684        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
1685        dynamic_invoke!(
1686            ntdll,
1687            &lc!("NtWriteVirtualMemory"),
1688            func_ptr,
1689            ret,
1690            handle,
1691            base_address,
1692            buffer,
1693            size,
1694            bytes_written
1695        );
1696
1697        match ret {
1698            Some(x) => return x,
1699            None => return -1,
1700        }
1701    }
1702}
1703
1704/// Dynamically calls NtAllocateVirtualMemory.
1705///
1706/// It will return the NTSTATUS value returned by the call.
1707#[cfg(target_arch = "x86_64")]
1708pub fn nt_allocate_virtual_memory(
1709    mut handle: HANDLE,
1710    mut base_address: *mut PVOID,
1711    zero_bits: usize,
1712    size: *mut usize,
1713    allocation_type: u32,
1714    protection: u32,
1715) -> i32 {
1716    unsafe {
1717        let ret;
1718        let func_ptr: data::NtAllocateVirtualMemory;
1719        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
1720
1721        if HARDWARE_BREAKPOINTS {
1722            let addr = get_function_address(ntdll, &lc!("NtAllocateVirtualMemory")) as usize;
1723            HARDWARE_EXCEPTION_FUNCTION = ExceptionHandleFunction::NtAllocateVirtualMemory;
1724            NT_ALLOCATE_VIRTUAL_MEMORY_ARGS.handle = handle;
1725            NT_ALLOCATE_VIRTUAL_MEMORY_ARGS.base_address = base_address;
1726            set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
1727
1728            handle = INVALID_HANDLE_VALUE;
1729            base_address = ptr::null_mut();
1730        }
1731
1732        dynamic_invoke!(
1733            ntdll,
1734            &lc!("NtAllocateVirtualMemory"),
1735            func_ptr,
1736            ret,
1737            handle,
1738            base_address,
1739            zero_bits,
1740            size,
1741            allocation_type,
1742            protection
1743        );
1744
1745        match ret {
1746            Some(x) => return x,
1747            None => return -1,
1748        }
1749    }
1750}
1751
1752/// Dynamically calls NtAllocateVirtualMemory.
1753///
1754/// It will return the NTSTATUS value returned by the call.
1755#[cfg(target_arch = "x86")]
1756pub fn nt_allocate_virtual_memory(
1757    handle: HANDLE,
1758    base_address: *mut PVOID,
1759    zero_bits: usize,
1760    size: *mut usize,
1761    allocation_type: u32,
1762    protection: u32,
1763) -> i32 {
1764    unsafe {
1765        let ret;
1766        let func_ptr: data::NtAllocateVirtualMemory;
1767        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
1768        dynamic_invoke!(
1769            ntdll,
1770            &lc!("NtAllocateVirtualMemory"),
1771            func_ptr,
1772            ret,
1773            handle,
1774            base_address,
1775            zero_bits,
1776            size,
1777            allocation_type,
1778            protection
1779        );
1780
1781        match ret {
1782            Some(x) => return x,
1783            None => return -1,
1784        }
1785    }
1786}
1787
1788/// Dynamically calls NtProtectVirtualMemory.
1789///
1790/// It will return the NTSTATUS value returned by the call.
1791#[cfg(target_arch = "x86_64")]
1792pub fn nt_protect_virtual_memory(
1793    mut handle: HANDLE,
1794    mut base_address: *mut PVOID,
1795    mut size: *mut usize,
1796    mut new_protection: u32,
1797    old_protection: *mut u32,
1798) -> i32 {
1799    unsafe {
1800        let ret;
1801        let func_ptr: data::NtProtectVirtualMemory;
1802        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
1803
1804        if HARDWARE_BREAKPOINTS {
1805            let addr = get_function_address(ntdll, &lc!("NtProtectVirtualMemory")) as usize;
1806            HARDWARE_EXCEPTION_FUNCTION = ExceptionHandleFunction::NtProtectVirtualMemory;
1807            NT_PROTECT_VIRTUAL_MEMORY_ARGS.handle = handle;
1808            NT_PROTECT_VIRTUAL_MEMORY_ARGS.base_address = base_address;
1809            NT_PROTECT_VIRTUAL_MEMORY_ARGS.size = size;
1810            NT_PROTECT_VIRTUAL_MEMORY_ARGS.protection = new_protection;
1811            set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
1812
1813            handle = INVALID_HANDLE_VALUE;
1814            base_address = ptr::null_mut();
1815            let s = 10usize;
1816            size = core::mem::transmute(&s);
1817            new_protection = PAGE_READONLY;
1818        }
1819
1820        dynamic_invoke!(
1821            ntdll,
1822            &lc!("NtProtectVirtualMemory"),
1823            func_ptr,
1824            ret,
1825            handle,
1826            base_address,
1827            size,
1828            new_protection,
1829            old_protection
1830        );
1831
1832        match ret {
1833            Some(x) => return x,
1834            None => return -1,
1835        }
1836    }
1837}
1838
1839/// Dynamically calls NtProtectVirtualMemory.
1840///
1841/// It will return the NTSTATUS value returned by the call.
1842#[cfg(target_arch = "x86")]
1843pub fn nt_protect_virtual_memory(
1844    handle: HANDLE,
1845    base_address: *mut PVOID,
1846    size: *mut usize,
1847    new_protection: u32,
1848    old_protection: *mut u32,
1849) -> i32 {
1850    unsafe {
1851        let ret;
1852        let func_ptr: data::NtProtectVirtualMemory;
1853        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
1854        dynamic_invoke!(
1855            ntdll,
1856            &lc!("NtProtectVirtualMemory"),
1857            func_ptr,
1858            ret,
1859            handle,
1860            base_address,
1861            size,
1862            new_protection,
1863            old_protection
1864        );
1865
1866        match ret {
1867            Some(x) => return x,
1868            None => return -1,
1869        }
1870    }
1871}
1872
1873/// Dynamically calls NtOpenProcess.
1874///
1875/// It will return the NTSTATUS value returned by the call.
1876#[cfg(target_arch = "x86_64")]
1877pub fn nt_open_process(
1878    mut handle: *mut HANDLE,
1879    mut access: u32,
1880    mut attributes: *mut OBJECT_ATTRIBUTES,
1881    mut client_id: *mut ClientId,
1882) -> i32 {
1883    unsafe {
1884        let ret;
1885        let func_ptr: data::NtOpenProcess;
1886        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
1887
1888        if HARDWARE_BREAKPOINTS {
1889            let addr = get_function_address(ntdll, &lc!("NtOpenProcess")) as usize;
1890            HARDWARE_EXCEPTION_FUNCTION = ExceptionHandleFunction::NtOpenProcess;
1891            NT_OPEN_PROCESS_ARGS.handle = handle;
1892            NT_OPEN_PROCESS_ARGS.access = access;
1893            NT_OPEN_PROCESS_ARGS.attributes = attributes;
1894            NT_OPEN_PROCESS_ARGS.client_id = client_id;
1895            set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
1896
1897            let h = INVALID_HANDLE_VALUE;
1898            handle = core::mem::transmute(&h);
1899            access = PROCESS_QUERY_LIMITED_INFORMATION;
1900            let a: OBJECT_ATTRIBUTES = core::mem::zeroed();
1901            attributes = core::mem::transmute(&a);
1902            let c = ClientId {
1903                unique_process: GetCurrentProcessId() as *mut core::ffi::c_void,
1904                unique_thread: core::ptr::null_mut(),
1905            };
1906            client_id = core::mem::transmute(&c);
1907        }
1908
1909        dynamic_invoke!(
1910            ntdll,
1911            &lc!("NtOpenProcess"),
1912            func_ptr,
1913            ret,
1914            handle,
1915            access,
1916            attributes,
1917            client_id
1918        );
1919
1920        match ret {
1921            Some(x) => return x,
1922            None => return -1,
1923        }
1924    }
1925}
1926
1927/// Dynamically calls NtOpenProcess.
1928///
1929/// It will return the NTSTATUS value returned by the call.
1930#[cfg(target_arch = "x86")]
1931pub fn nt_open_process(
1932    handle: *mut HANDLE,
1933    access: u32,
1934    attributes: *mut OBJECT_ATTRIBUTES,
1935    client_id: *mut ClientId,
1936) -> i32 {
1937    unsafe {
1938        let ret;
1939        let func_ptr: data::NtOpenProcess;
1940        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
1941        dynamic_invoke!(
1942            ntdll,
1943            &lc!("NtOpenProcess"),
1944            func_ptr,
1945            ret,
1946            handle,
1947            access,
1948            attributes,
1949            client_id
1950        );
1951
1952        match ret {
1953            Some(x) => return x,
1954            None => return -1,
1955        }
1956    }
1957}
1958
1959/// Dynamically calls NtQueryInformationProcess.
1960///
1961/// It will return the NTSTATUS value returned by the call.
1962pub fn nt_query_information_process(
1963    handle: HANDLE,
1964    process_information_class: u32,
1965    process_information: PVOID,
1966    length: u32,
1967    return_length: *mut u32,
1968) -> i32 {
1969    unsafe {
1970        let ret;
1971        let func_ptr: data::NtQueryInformationProcess;
1972        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
1973        dynamic_invoke!(
1974            ntdll,
1975            &lc!("NtQueryInformationProcess"),
1976            func_ptr,
1977            ret,
1978            handle,
1979            process_information_class,
1980            process_information,
1981            length,
1982            return_length
1983        );
1984
1985        match ret {
1986            Some(x) => return x,
1987            None => return -1,
1988        }
1989    }
1990}
1991
1992/// Dynamically calls NtQueryInformationThread.
1993///
1994/// It will return the NTSTATUS value returned by the call.
1995pub fn nt_query_information_thread(
1996    handle: HANDLE,
1997    thread_information_class: u32,
1998    thread_information: PVOID,
1999    length: u32,
2000    return_length: *mut u32,
2001) -> i32 {
2002    unsafe {
2003        let ret;
2004        let func_ptr: data::NtQueryInformationThread;
2005        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
2006        dynamic_invoke!(
2007            ntdll,
2008            &lc!("NtQueryInformationThread"),
2009            func_ptr,
2010            ret,
2011            handle,
2012            thread_information_class,
2013            thread_information,
2014            length,
2015            return_length
2016        );
2017
2018        match ret {
2019            Some(x) => return x,
2020            None => return -1,
2021        }
2022    }
2023}
2024
2025/// Dynamically calls NtQueryInformationFile.
2026///
2027/// It will return the NTSTATUS value returned by the call.
2028pub fn nt_query_information_file(
2029    handle: HANDLE,
2030    io: *mut IO_STATUS_BLOCK,
2031    file_information: PVOID,
2032    length: u32,
2033    file_information_class: u32,
2034) -> i32 {
2035    unsafe {
2036        let ret;
2037        let func_ptr: data::NtQueryInformationFile;
2038        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
2039        dynamic_invoke!(
2040            ntdll,
2041            &lc!("NtQueryInformationFile"),
2042            func_ptr,
2043            ret,
2044            handle,
2045            io,
2046            file_information,
2047            length,
2048            file_information_class
2049        );
2050
2051        match ret {
2052            Some(x) => return x,
2053            None => return -1,
2054        }
2055    }
2056}
2057
2058/// Dynamically calls RtlAdjustPrivilege.
2059///
2060/// It will return the NTSTATUS value returned by the call.
2061pub fn rtl_adjust_privilege(
2062    privilege: u32,
2063    enable: u8,
2064    current_thread: u8,
2065    enabled: *mut u8,
2066) -> i32 {
2067    unsafe {
2068        let ret;
2069        let func_ptr: data::RtlAdjustPrivilege;
2070        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
2071        dynamic_invoke!(
2072            ntdll,
2073            &lc!("RtlAdjustPrivilege"),
2074            func_ptr,
2075            ret,
2076            privilege,
2077            enable,
2078            current_thread,
2079            enabled
2080        );
2081
2082        match ret {
2083            Some(x) => return x,
2084            None => return -1,
2085        }
2086    }
2087}
2088
2089/// Dynamically calls RtlInitUnicodeString.
2090///
2091/// It will return the NTSTATUS value returned by the call.
2092pub fn rtl_init_unicode_string(
2093    destination_string: *mut UNICODE_STRING,
2094    source_string: *const u16,
2095) -> () {
2096    unsafe {
2097        let _ret: Option<()>;
2098        let func_ptr: data::RtlInitUnicodeString;
2099        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
2100        dynamic_invoke!(
2101            ntdll,
2102            &lc!("RtlInitUnicodeString"),
2103            func_ptr,
2104            _ret,
2105            destination_string,
2106            source_string
2107        );
2108    }
2109}
2110
2111/// Dynamically calls RtlZeroMemory.
2112///
2113/// It will return the NTSTATUS value returned by the call.
2114pub fn rtl_zero_memory(address: PVOID, length: usize) -> () {
2115    unsafe {
2116        let _ret: Option<()>;
2117        let func_ptr: data::RtlZeroMemory;
2118        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
2119        dynamic_invoke!(
2120            ntdll,
2121            &lc!("RtlZeroMemory"),
2122            func_ptr,
2123            _ret,
2124            address,
2125            length
2126        );
2127    }
2128}
2129
2130/// Dynamically calls NtOpenFile.
2131///
2132/// It will return the NTSTATUS value returned by the call.
2133pub fn nt_open_file(
2134    file_handle: *mut HANDLE,
2135    desired_access: u32,
2136    object_attributes: *mut OBJECT_ATTRIBUTES,
2137    io: *mut IO_STATUS_BLOCK,
2138    share_access: u32,
2139    options: u32,
2140) -> i32 {
2141    unsafe {
2142        let ret: Option<i32>;
2143        let func_ptr: data::NtOpenFile;
2144        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
2145        dynamic_invoke!(
2146            ntdll,
2147            &lc!("NtOpenFile"),
2148            func_ptr,
2149            ret,
2150            file_handle,
2151            desired_access,
2152            object_attributes,
2153            io,
2154            share_access,
2155            options
2156        );
2157
2158        match ret {
2159            Some(x) => return x,
2160            None => return -1,
2161        }
2162    }
2163}
2164
2165/// Dynamically calls NtCreateSection.
2166///
2167/// It will return the NTSTATUS value returned by the call.
2168pub fn nt_create_section(
2169    section_handle: *mut HANDLE,
2170    desired_access: u32,
2171    object_attributes: *mut OBJECT_ATTRIBUTES,
2172    size: *mut LARGE_INTEGER,
2173    page_protection: u32,
2174    allocation_attributes: u32,
2175    file_handle: HANDLE,
2176) -> i32 {
2177    unsafe {
2178        let ret: Option<i32>;
2179        let func_ptr: data::NtCreateSection;
2180        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
2181        dynamic_invoke!(
2182            ntdll,
2183            &lc!("NtCreateSection"),
2184            func_ptr,
2185            ret,
2186            section_handle,
2187            desired_access,
2188            object_attributes,
2189            size,
2190            page_protection,
2191            allocation_attributes,
2192            file_handle
2193        );
2194
2195        match ret {
2196            Some(x) => return x,
2197            None => return -1,
2198        }
2199    }
2200}
2201
2202/// Dynamically calls NtMapViewOfSection.
2203///
2204/// It will return the NTSTATUS value returned by the call.
2205pub fn nt_map_view_of_section(
2206    section_handle: HANDLE,
2207    process_handle: HANDLE,
2208    base_address: *mut PVOID,
2209    zero: usize,
2210    commit_size: usize,
2211    offset: *mut LARGE_INTEGER,
2212    view_size: *mut usize,
2213    disposition: u32,
2214    allocation_type: u32,
2215    protection: u32,
2216) -> i32 {
2217    unsafe {
2218        let ret: Option<i32>;
2219        let func_ptr: data::NtMapViewOfSection;
2220        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
2221        dynamic_invoke!(
2222            ntdll,
2223            &lc!("NtMapViewOfSection"),
2224            func_ptr,
2225            ret,
2226            section_handle,
2227            process_handle,
2228            base_address,
2229            zero,
2230            commit_size,
2231            offset,
2232            view_size,
2233            disposition,
2234            allocation_type,
2235            protection
2236        );
2237
2238        match ret {
2239            Some(x) => return x,
2240            None => return -1,
2241        }
2242    }
2243}
2244
2245/// Dynamically calls NtCreateThreadEx.
2246///
2247/// It will return the NTSTATUS value returned by the call.
2248#[cfg(target_arch = "x86_64")]
2249pub fn nt_create_thread_ex(
2250    mut thread: *mut HANDLE,
2251    mut access: u32,
2252    mut attributes: *mut OBJECT_ATTRIBUTES,
2253    mut process: HANDLE,
2254    function: PVOID,
2255    args: PVOID,
2256    flags: u32,
2257    zero: usize,
2258    stack: usize,
2259    reserve: usize,
2260    buffer: *mut PsAttributeList,
2261) -> i32 {
2262    unsafe {
2263        let ret: Option<i32>;
2264        let func_ptr: data::NtCreateThreadEx;
2265        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
2266
2267        if HARDWARE_BREAKPOINTS {
2268            let addr = get_function_address(ntdll, &lc!("NtCreateThreadEx")) as usize;
2269            HARDWARE_EXCEPTION_FUNCTION = ExceptionHandleFunction::NtCreateThreadEx;
2270            NT_CREATE_THREAD_EX_ARGS.thread = thread;
2271            NT_CREATE_THREAD_EX_ARGS.access = access;
2272            NT_CREATE_THREAD_EX_ARGS.attributes = attributes;
2273            NT_CREATE_THREAD_EX_ARGS.process = process;
2274            set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
2275
2276            let h = INVALID_HANDLE_VALUE;
2277            thread = core::mem::transmute(&h);
2278            access = PROCESS_QUERY_LIMITED_INFORMATION;
2279            let a: OBJECT_ATTRIBUTES = core::mem::zeroed();
2280            attributes = core::mem::transmute(&a);
2281            process = INVALID_HANDLE_VALUE;
2282        }
2283
2284        dynamic_invoke!(
2285            ntdll,
2286            &lc!("NtCreateThreadEx"),
2287            func_ptr,
2288            ret,
2289            thread,
2290            access,
2291            attributes,
2292            process,
2293            function,
2294            args,
2295            flags,
2296            zero,
2297            stack,
2298            reserve,
2299            buffer
2300        );
2301
2302        match ret {
2303            Some(x) => return x,
2304            None => return -1,
2305        }
2306    }
2307}
2308
2309/// Dynamically calls NtCreateThreadEx.
2310///
2311/// It will return the NTSTATUS value returned by the call.
2312#[cfg(target_arch = "x86")]
2313pub fn nt_create_thread_ex(
2314    thread: *mut HANDLE,
2315    access: u32,
2316    attributes: *mut OBJECT_ATTRIBUTES,
2317    process: HANDLE,
2318    function: PVOID,
2319    args: PVOID,
2320    flags: u32,
2321    zero: usize,
2322    stack: usize,
2323    reserve: usize,
2324    buffer: *mut PsAttributeList,
2325) -> i32 {
2326    unsafe {
2327        let ret: Option<i32>;
2328        let func_ptr: data::NtCreateThreadEx;
2329        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
2330        dynamic_invoke!(
2331            ntdll,
2332            &lc!("NtCreateThreadEx"),
2333            func_ptr,
2334            ret,
2335            thread,
2336            access,
2337            attributes,
2338            process,
2339            function,
2340            args,
2341            flags,
2342            zero,
2343            stack,
2344            reserve,
2345            buffer
2346        );
2347
2348        match ret {
2349            Some(x) => return x,
2350            None => return -1,
2351        }
2352    }
2353}
2354
2355/// Dynamically calls NtReadVirtualMemory.
2356///
2357/// It will return the NTSTATUS value returned by the call.
2358pub fn nt_read_virtual_memory(
2359    handle: HANDLE,
2360    base_address: PVOID,
2361    buffer: PVOID,
2362    size: usize,
2363    bytes_written: *mut usize,
2364) -> i32 {
2365    unsafe {
2366        let ret;
2367        let func_ptr: data::NtReadVirtualMemory;
2368        let ntdll = get_module_base_address(&lc!("ntdll.dll"));
2369        dynamic_invoke!(
2370            ntdll,
2371            &lc!("NtReadVirtualMemory"),
2372            func_ptr,
2373            ret,
2374            handle,
2375            base_address,
2376            buffer,
2377            size,
2378            bytes_written
2379        );
2380
2381        match ret {
2382            Some(x) => return x,
2383            None => return -1,
2384        }
2385    }
2386}
2387
2388/// Dynamically calls an exported function from the specified module.
2389///
2390/// This macro will use the dyncvoke crate functions to obtain an exported
2391/// function address of the specified module at runtime by walking process structures
2392/// and PE headers.
2393///
2394/// In case that this macro is used to call a dll entry point (DllMain), it will return true
2395/// or false (using the 3rd argument passed to the macro) depending on the success of the call.
2396/// In any other case, it will return the same data type that the called function would return
2397/// using the 4th argument passed to the macro.
2398///
2399/// # Example - Calling a dll entry point
2400///
2401/// ```ignore
2402/// let a = manualmap::read_and_map_module("c:\\some\\random\\file.dll").unwrap();
2403/// let ret: bool = false;
2404/// dyncvoke_core::dynamic_invoke(&a.0, a.1, ret); // dyncvoke_core::dynamic_invoke(&PeMetadata, usize, bool)
2405/// if ret { println!("Entry point successfully called.");}
2406/// ```
2407/// # Example - Dynamically calling LoadLibraryA
2408///
2409/// ```ignore
2410/// let kernel32 = manualmap::read_and_map_module("c:\\windows\\system32\\kernel32.dll").unwrap();
2411/// let mut ret:Option<HINSTANCE>;
2412/// let function_ptr: data::LoadLibraryA;
2413/// let name = CString::new("ntdll.dll").expect("CString::new failed");
2414/// let module_name = PSTR{0: name.as_ptr() as *mut u8};
2415/// //dyncvoke_core::dynamic_invoke(usize,&str,<function_type>,Option<return_type>,[arguments])
2416/// dyncvoke_core::dynamic_invoke!(kernel32.1, "LoadLibraryA", function_ptr, ret, module_name);
2417///
2418/// match ret {
2419///     Some(x) => {println!("ntdll base address is 0x{:X}",x.0);},
2420///     None => println!("Error calling LoadLibraryA"),
2421/// }
2422/// ```
2423/// # Example - Dynamically calling with referenced arguments
2424///
2425/// ```ignore
2426/// let ptr = dyncvoke_core::get_module_base_address("ntdll.dll");
2427/// let function_ptr: LdrGetProcedureAddress;
2428/// let ret: Option<i32>;
2429/// let hmodule: PVOID = core::mem::transmute(ptr);
2430/// let fun_name: *mut String = ptr::null_mut();
2431/// let ordinal = 8 as u32;
2432/// let return_address: *mut c_void = core::mem::transmute(&usize::default());
2433/// let return_address: *mut PVOID = core::mem::transmute(return_address);
2434/// //dyncvoke_core::dynamic_invoke(usize,&str,<function_type>,Option<return_type>,[arguments])
2435/// dyncvoke_core::dynamic_invoke!(ptr,"LdrGetProcedureAddress",function_ptr,ret,hmodule,fun_name,ordinal,return_address);
2436///
2437/// match ret {
2438///     Some(x) => if x == 0 {println!("RtlDispatchAPC is located at the address: 0x{:X}",*return_address as usize);},
2439///     None => println!("Error calling LdrGetProcedureAddress"),
2440/// }
2441/// ```
2442#[macro_export]
2443macro_rules! dynamic_invoke {
2444
2445    ($a:expr, $b:expr, $c:expr) => {
2446
2447        let ret = $crate::call_module_entry_point($a,$b);
2448
2449        match ret {
2450            Ok(_) => $c = true,
2451            Err(_) => $c = false,
2452        }
2453
2454    };
2455
2456    ($a:expr, $b:expr, $c:expr, $d:expr, $($e:tt)*) => {
2457
2458        let function_ptr = $crate::get_function_address($a, $b);
2459        if function_ptr != 0
2460        {
2461            $c = core::mem::transmute(function_ptr);
2462            $d = Some($c($($e)*));
2463        }
2464        else
2465        {
2466            $d = None;
2467        }
2468
2469    };
2470}
2471
2472/// Resolve a Zw/Nt syscall by name (Tartarus Gate) and dispatch via the
2473/// variadic Hell's Hall gateway.
2474///
2475/// Every argument is cast `as usize` and then transmuted to `*mut c_void`
2476/// before the call, so the kernel sees uniform 64-bit slots no matter
2477/// what mix of integer and pointer types you pass at the call site. This
2478/// matches the flow used by `spoof_syscall!` and `spoof!` so all three
2479/// macros take the same shape of arguments and return the same shape of
2480/// result.
2481///
2482/// Returns `Result<*mut c_void, SyscallError>`. `Err` means name or SSN
2483/// resolution failed and the kernel transition never happened. `Ok(ptr)`
2484/// returns the raw NTSTATUS the kernel gave us, in pointer-width form.
2485/// Convert it with `.unwrap() as i32` or `(... as usize) as i32`.
2486///
2487/// # Examples
2488///
2489/// ```ignore
2490/// use dyncvoke_core::syscall;
2491/// use core::ffi::c_void;
2492/// use core::ptr::null_mut;
2493///
2494/// let mut p_tmp_address: *mut c_void = null_mut();
2495/// let mut s_chunk: usize = 0x1000;
2496/// let mut old_prot: u32 = 0;
2497///
2498/// let status = syscall!(
2499///     "NtProtectVirtualMemory",
2500///     -1isize as *mut c_void,                                          // NtCurrentProcess
2501///     &mut p_tmp_address as *mut *mut c_void as *mut c_void,
2502///     &mut s_chunk as *mut usize as *mut c_void,
2503///     0x20u32 as *mut c_void,                                          // PAGE_EXECUTE_READ
2504///     &mut old_prot as *mut u32 as *mut c_void,
2505///     null_mut::<c_void>(),
2506///     null_mut::<c_void>(),
2507///     null_mut::<c_void>(),
2508///     null_mut::<c_void>(),
2509///     null_mut::<c_void>(),
2510///     null_mut::<c_void>(),
2511/// )
2512/// .ok()
2513/// .unwrap() as i32;
2514/// ```
2515///
2516/// Trailing `null_mut::<c_void>()` slots are optional. The macro counts
2517/// what you actually pass and tells the gateway the right argument count.
2518/// Supports arbitrary arity, zero-arg syscalls (e.g. `NtYieldExecution`),
2519/// and trailing commas.
2520#[cfg(target_arch = "x86_64")]
2521#[macro_export]
2522macro_rules! syscall {
2523    ($name:expr $(, $arg:expr)* $(,)?) => {{
2524        $crate::resolve_syscall($name).map(|(ssn, addr)| {
2525            let argc = 0u32 $(+ { let _ = &$arg; 1u32 })*;
2526            unsafe {
2527                $crate::do_syscall(
2528                    ssn,
2529                    addr,
2530                    argc
2531                    $(, ::core::mem::transmute::<usize, *mut ::core::ffi::c_void>($arg as usize))*
2532                )
2533            }
2534        })
2535    }};
2536}
2537
2538/// Low-level escape hatch when you already have a resolved `(ssn, addr)`.
2539///
2540/// Skips resolution entirely. Useful for caching SSNs at init or for
2541/// resolving via a non-default mechanism (e.g. a hand-rolled SSDT walker
2542/// or a remote-process resolver). Returns `*mut c_void` directly, no
2543/// `Result` wrapping. Convert to NTSTATUS with `as i32`.
2544///
2545/// Every argument is cast `as usize` and then transmuted to `*mut c_void`
2546/// so the call site stays uniform with `syscall!`, `spoof!`, and
2547/// `spoof_syscall!`.
2548///
2549/// # Examples
2550///
2551/// ```ignore
2552/// use dyncvoke_core::{do_syscall, resolve_syscall};
2553///
2554/// let (ssn, addr) = resolve_syscall("NtClose").unwrap();
2555/// // later, possibly thousands of times, with no per-call resolution cost:
2556/// let status = do_syscall!(ssn, addr, handle) as i32;
2557/// ```
2558#[cfg(target_arch = "x86_64")]
2559#[macro_export]
2560macro_rules! do_syscall {
2561    ($ssn:expr, $addr:expr $(, $arg:expr)* $(,)?) => {{
2562        let argc = 0u32 $(+ { let _ = &$arg; 1u32 })*;
2563        unsafe {
2564            $crate::do_syscall(
2565                $ssn,
2566                $addr,
2567                argc
2568                $(, ::core::mem::transmute::<usize, *mut ::core::ffi::c_void>($arg as usize))*
2569            )
2570        }
2571    }};
2572}
2573
2574
2575#[cfg(test)]
2576extern crate std;
2577
2578#[cfg(test)]
2579mod tests {
2580    use super::*;
2581
2582    #[test]
2583    fn test_invalid_handle_value() {
2584        assert_eq!(INVALID_HANDLE_VALUE as isize, -1);
2585    }
2586
2587    #[test]
2588    fn test_constants_exist() {
2589        // These constants are imported from data
2590        assert_eq!(PAGE_EXECUTE_READ, 0x20);
2591        assert_eq!(PAGE_EXECUTE_READWRITE, 0x40);
2592        assert_eq!(PAGE_READONLY, 0x2);
2593        assert_eq!(PAGE_READWRITE, 0x4);
2594        assert_eq!(MEM_COMMIT, 0x1000);
2595        assert_eq!(MEM_RESERVE, 0x2000);
2596        assert_eq!(PROCESS_QUERY_LIMITED_INFORMATION, 0x1000);
2597        assert_eq!(TLS_OUT_OF_INDEXES, 0xFFFFFFFF);
2598    }
2599
2600    #[test]
2601    fn test_eat_type() {
2602        let mut eat: EAT = std::collections::BTreeMap::new();
2603        eat.insert(0, "NtOpenProcess".to_string());
2604        eat.insert(1, "NtAllocateVirtualMemory".to_string());
2605
2606        assert_eq!(eat.len(), 2);
2607        assert_eq!(eat.get(&0), Some(&"NtOpenProcess".to_string()));
2608        assert_eq!(eat.get(&1), Some(&"NtAllocateVirtualMemory".to_string()));
2609    }
2610
2611    #[test]
2612    fn test_rtl_zero_memory() {
2613        let mut buffer = vec![0xFFu8; 64];
2614        let ptr = buffer.as_mut_ptr() as PVOID;
2615        rtl_zero_memory(ptr, 64);
2616        for byte in &buffer {
2617            assert_eq!(*byte, 0);
2618        }
2619    }
2620
2621    #[test]
2622    fn test_large_integer_creation() {
2623        let li = LARGE_INTEGER { QuadPart: 12345 };
2624        assert_eq!(li.QuadPart, 12345);
2625
2626        let li_default = LARGE_INTEGER::default();
2627        assert_eq!(li_default.QuadPart, 0);
2628    }
2629
2630    #[test]
2631    fn test_object_attributes_creation() {
2632        let oa = OBJECT_ATTRIBUTES::default();
2633        assert_eq!(oa.Length, 0);
2634        assert!(oa.RootDirectory.is_null());
2635        assert!(oa.ObjectName.is_null());
2636    }
2637
2638    #[test]
2639    fn test_client_id_creation() {
2640        let cid = ClientId {
2641            unique_process: 0x1234 as HANDLE,
2642            unique_thread: 0x5678 as HANDLE,
2643        };
2644        assert_eq!(cid.unique_process as usize, 0x1234);
2645        assert_eq!(cid.unique_thread as usize, 0x5678);
2646
2647        let cid_default = ClientId::default();
2648        assert!(cid_default.unique_process.is_null());
2649        assert!(cid_default.unique_thread.is_null());
2650    }
2651
2652    #[test]
2653    fn test_nt_allocate_virtual_memory_args() {
2654        let args = NtAllocateVirtualMemoryArgs {
2655            handle: INVALID_HANDLE_VALUE,
2656            base_address: core::ptr::null_mut(),
2657        };
2658        assert_eq!(args.handle as isize, -1);
2659        assert!(args.base_address.is_null());
2660    }
2661
2662    #[test]
2663    fn test_nt_open_process_args() {
2664        let args = NtOpenProcessArgs {
2665            handle: core::ptr::null_mut(),
2666            access: 0x1000,
2667            attributes: core::ptr::null_mut(),
2668            client_id: core::ptr::null_mut(),
2669        };
2670        assert!(args.handle.is_null());
2671        assert_eq!(args.access, 0x1000);
2672    }
2673
2674    #[test]
2675    fn test_nt_protect_virtual_memory_args() {
2676        let args = NtProtectVirtualMemoryArgs {
2677            handle: INVALID_HANDLE_VALUE,
2678            base_address: core::ptr::null_mut(),
2679            size: core::ptr::null_mut(),
2680            protection: PAGE_EXECUTE_READWRITE,
2681        };
2682        assert_eq!(args.handle as isize, -1);
2683        assert_eq!(args.protection, PAGE_EXECUTE_READWRITE);
2684    }
2685
2686    #[test]
2687    fn test_nt_write_virtual_memory_args() {
2688        let args = NtWriteVirtualMemoryArgs {
2689            handle: INVALID_HANDLE_VALUE,
2690            base_address: core::ptr::null_mut(),
2691            buffer: core::ptr::null_mut(),
2692            size: 0,
2693        };
2694        assert_eq!(args.handle as isize, -1);
2695    }
2696
2697    #[test]
2698    fn test_nt_create_thread_ex_args() {
2699        let args = NtCreateThreadExArgs {
2700            thread: core::ptr::null_mut(),
2701            access: 0x1F03FF,
2702            attributes: core::ptr::null_mut(),
2703            process: INVALID_HANDLE_VALUE,
2704        };
2705        assert!(args.thread.is_null());
2706        assert_eq!(args.process as isize, -1);
2707    }
2708
2709    #[cfg(target_arch = "x86_64")]
2710    #[test]
2711    fn test_use_hardware_breakpoints() {
2712        use_hardware_breakpoints(true);
2713        use_hardware_breakpoints(false);
2714    }
2715
2716    #[test]
2717    fn test_type_sizes() {
2718        assert_eq!(core::mem::size_of::<HANDLE>(), core::mem::size_of::<*mut core::ffi::c_void>());
2719        assert_eq!(core::mem::size_of::<PVOID>(), core::mem::size_of::<*mut core::ffi::c_void>());
2720    }
2721}