#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
extern crate alloc;
#[cfg(not(windows))]
compile_error!("dyncvoke-core is Windows-only");
use alloc::{
collections::BTreeMap,
ffi::CString,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use core::{ffi::c_void, mem::size_of, ptr};
use data::lc;
use data::LARGE_INTEGER;
use windows_sys::Win32::Foundation::BOOL;
use windows_sys::Win32::Security::SECURITY_ATTRIBUTES;
#[cfg(target_arch = "x86_64")]
use windows_sys::Win32::System::Diagnostics::Debug::{GetThreadContext, SetThreadContext};
use windows_sys::Win32::System::Memory::MEMORY_BASIC_INFORMATION;
use windows_sys::Win32::System::SystemInformation::SYSTEM_INFO;
use windows_sys::Win32::System::IO::IO_STATUS_BLOCK;
#[cfg(target_arch = "x86_64")]
use windows_sys::Win32::System::Threading::GetCurrentProcessId;
pub mod peb;
pub use data::HANDLE;
pub use data::HINSTANCE;
pub use data::OBJECT_ATTRIBUTES;
pub use data::UNICODE_STRING;
pub use windows_sys::Win32::System::Threading::{GetCurrentProcess, PROCESS_BASIC_INFORMATION};
pub const INVALID_HANDLE_VALUE: HANDLE = -1isize as HANDLE;
pub mod sys;
pub use sys::{extract_ssn, get_syscall_address, resolve_syscall, SyscallError};
#[cfg(target_arch = "x86_64")]
pub use sys::asm::do_syscall;
#[allow(unused_imports)]
use data::{
ApiSetNamespace, ApiSetNamespaceEntry, ApiSetValueEntry, ClientId, EntryPoint,
LptopLevelExceptionFilter, PeMetadata, PsAttributeList, PsCreateInfo,
DLL_PROCESS_ATTACH, EAT, MAX_PATH,
MEM_COMMIT, MEM_RESERVE,
PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_READONLY, PAGE_READWRITE,
PROCESS_QUERY_LIMITED_INFORMATION, PVOID, TLS_OUT_OF_INDEXES,
};
#[cfg(target_arch = "x86_64")]
use data::{
ExceptionHandleFunction, ExceptionPointers,
NtAllocateVirtualMemoryArgs, NtCreateThreadExArgs, NtOpenProcessArgs,
NtProtectVirtualMemoryArgs, NtWriteVirtualMemoryArgs,
};
#[cfg(target_arch = "x86_64")]
use windows_sys::Win32::System::Threading::GetCurrentThread;
#[cfg(target_arch = "x86_64")]
static mut HARDWARE_BREAKPOINTS: bool = false;
#[cfg(target_arch = "x86_64")]
static mut HARDWARE_EXCEPTION_FUNCTION: ExceptionHandleFunction =
ExceptionHandleFunction::NtOpenProcess;
#[cfg(target_arch = "x86_64")]
static mut NT_ALLOCATE_VIRTUAL_MEMORY_ARGS: NtAllocateVirtualMemoryArgs =
NtAllocateVirtualMemoryArgs {
handle: INVALID_HANDLE_VALUE,
base_address: ptr::null_mut(),
};
#[cfg(target_arch = "x86_64")]
static mut NT_OPEN_PROCESS_ARGS: NtOpenProcessArgs = NtOpenProcessArgs {
handle: ptr::null_mut(),
access: 0,
attributes: ptr::null_mut(),
client_id: ptr::null_mut(),
};
#[cfg(target_arch = "x86_64")]
static mut NT_PROTECT_VIRTUAL_MEMORY_ARGS: NtProtectVirtualMemoryArgs =
NtProtectVirtualMemoryArgs {
handle: INVALID_HANDLE_VALUE,
base_address: ptr::null_mut(),
size: ptr::null_mut(),
protection: 0,
};
#[cfg(target_arch = "x86_64")]
static mut NT_WRITE_VIRTUAL_MEMORY_ARGS: NtWriteVirtualMemoryArgs = NtWriteVirtualMemoryArgs {
handle: INVALID_HANDLE_VALUE,
base_address: ptr::null_mut(),
buffer: ptr::null_mut(),
size: 0usize,
};
#[cfg(target_arch = "x86_64")]
static mut NT_CREATE_THREAD_EX_ARGS: NtCreateThreadExArgs = NtCreateThreadExArgs {
thread: ptr::null_mut(),
access: 0,
attributes: ptr::null_mut(),
process: INVALID_HANDLE_VALUE,
};
#[allow(static_mut_refs)]
static mut HOOKED_FUNCTIONS_INFO: Vec<(usize, Vec<u8>)> = vec![];
#[cfg(target_arch = "x86_64")]
pub fn use_hardware_breakpoints(value: bool) {
unsafe {
HARDWARE_BREAKPOINTS = value;
}
}
#[cfg(target_arch = "x86_64")]
pub fn set_hardware_breakpoint(address: usize) {
use windows_sys::Win32::System::Diagnostics::Debug::CONTEXT;
unsafe {
let mut context: CONTEXT = core::mem::zeroed();
context.ContextFlags = 0x100000 | 0x10; let _ = GetThreadContext(GetCurrentThread(), &mut context);
context.Dr0 = address as u64;
context.Dr6 = 0;
context.Dr7 = context.Dr7 & !(((1 << 2) - 1) << 16); context.Dr7 = context.Dr7 & !(((1 << 2) - 1) << 18); context.Dr7 = (context.Dr7 & !(((1 << 1) - 1) << 0)) | (1 << 0);
context.ContextFlags = 0x100000 | 0x10;
let _ = SetThreadContext(GetCurrentThread(), &context);
}
}
#[cfg(target_arch = "x86_64")]
pub unsafe extern "system" fn breakpoint_handler(exceptioninfo: *mut ExceptionPointers) -> i32 {
if (*(*(exceptioninfo)).exception_record).ExceptionCode as u32 == 0x80000004
{
if ((*(*exceptioninfo).context_record).Dr7 & 1) == 1 {
if (*(*exceptioninfo).context_record).Rip == (*(*exceptioninfo).context_record).Dr0 {
(*(*exceptioninfo).context_record).Dr0 = 0; match HARDWARE_EXCEPTION_FUNCTION {
ExceptionHandleFunction::NtAllocateVirtualMemory => {
(*(*exceptioninfo).context_record).R10 =
NT_ALLOCATE_VIRTUAL_MEMORY_ARGS.handle as u64 as u64;
(*(*exceptioninfo).context_record).Rdx =
core::mem::transmute(NT_ALLOCATE_VIRTUAL_MEMORY_ARGS.base_address);
}
ExceptionHandleFunction::NtProtectVirtualMemory => {
(*(*exceptioninfo).context_record).R10 =
NT_PROTECT_VIRTUAL_MEMORY_ARGS.handle as u64 as u64;
(*(*exceptioninfo).context_record).Rdx =
core::mem::transmute(NT_PROTECT_VIRTUAL_MEMORY_ARGS.base_address);
(*(*exceptioninfo).context_record).R8 =
core::mem::transmute(NT_PROTECT_VIRTUAL_MEMORY_ARGS.size);
(*(*exceptioninfo).context_record).R9 =
NT_PROTECT_VIRTUAL_MEMORY_ARGS.protection as u64;
}
ExceptionHandleFunction::NtOpenProcess => {
(*(*exceptioninfo).context_record).R10 =
core::mem::transmute(NT_OPEN_PROCESS_ARGS.handle);
(*(*exceptioninfo).context_record).Rdx = NT_OPEN_PROCESS_ARGS.access as u64;
(*(*exceptioninfo).context_record).R8 =
core::mem::transmute(NT_OPEN_PROCESS_ARGS.attributes);
(*(*exceptioninfo).context_record).R9 =
core::mem::transmute(NT_OPEN_PROCESS_ARGS.client_id);
}
ExceptionHandleFunction::NtWriteVirtualMemory => {
(*(*exceptioninfo).context_record).R10 =
NT_WRITE_VIRTUAL_MEMORY_ARGS.handle as u64 as u64;
(*(*exceptioninfo).context_record).Rdx =
core::mem::transmute(NT_WRITE_VIRTUAL_MEMORY_ARGS.base_address);
(*(*exceptioninfo).context_record).R8 =
core::mem::transmute(NT_WRITE_VIRTUAL_MEMORY_ARGS.buffer);
(*(*exceptioninfo).context_record).R9 =
NT_WRITE_VIRTUAL_MEMORY_ARGS.size as u64;
}
ExceptionHandleFunction::NtCreateThreadEx => {
(*(*exceptioninfo).context_record).R10 =
core::mem::transmute(NT_CREATE_THREAD_EX_ARGS.thread);
(*(*exceptioninfo).context_record).Rdx =
NT_CREATE_THREAD_EX_ARGS.access as u64;
(*(*exceptioninfo).context_record).R8 =
core::mem::transmute(NT_CREATE_THREAD_EX_ARGS.attributes);
(*(*exceptioninfo).context_record).R9 =
NT_CREATE_THREAD_EX_ARGS.process as u64;
}
}
}
}
return -1; }
0 }
#[allow(static_mut_refs)]
pub fn hook_function(src_address: usize, dst_address: usize) -> bool {
unsafe {
let mut original_address = src_address;
let handle = INVALID_HANDLE_VALUE;
let base_address: *mut PVOID = &mut original_address as *mut usize as *mut PVOID;
let mut size: usize = 4096;
let mut old_protection: u32 = 0;
let z = nt_protect_virtual_memory(
handle,
base_address,
&mut size,
PAGE_EXECUTE_READWRITE,
&mut old_protection,
);
if z != 0 {
return false;
}
let ntop_ptr = src_address as *mut u8;
let mut original_bytes: Vec<u8> = vec![];
if cfg!(target_pointer_width = "64") {
for i in 0..=12 {
let b = *(ntop_ptr.add(i));
original_bytes.push(b);
}
*ntop_ptr = 0x49;
*(ntop_ptr.add(1)) = 0xBB;
*(ntop_ptr.add(2) as *mut usize) = dst_address;
*(ntop_ptr.add(10)) = 0x41;
*(ntop_ptr.add(11)) = 0xFF;
*(ntop_ptr.add(12)) = 0xE3;
} else {
for i in 0..=5 {
let b = *(ntop_ptr.add(i));
original_bytes.push(b);
}
*ntop_ptr = 0x68;
*(ntop_ptr.add(1) as *mut usize) = dst_address;
*(ntop_ptr.add(5)) = 0xC3
}
HOOKED_FUNCTIONS_INFO.push((src_address, original_bytes));
let mut unused = 0u32;
let z = nt_protect_virtual_memory(
handle,
base_address,
&mut size,
old_protection,
&mut unused,
);
if z != 0 {
return false;
}
true
}
}
#[allow(static_mut_refs)]
pub fn unhook_function(address: usize) -> bool {
unsafe {
let mut unhook_info = (0, vec![]);
let mut index = 0;
for (i, element) in HOOKED_FUNCTIONS_INFO.iter().enumerate() {
if element.0 == address {
unhook_info = (element.0, element.1.to_vec());
index = i;
break;
}
}
if unhook_info.0 == 0 {
return false;
}
let mut original_address = unhook_info.0;
let handle = INVALID_HANDLE_VALUE;
let base_address: *mut PVOID = &mut original_address as *mut usize as *mut PVOID;
let mut size: usize = 4096;
let mut old_protection: u32 = 0;
let z = nt_protect_virtual_memory(
handle,
base_address,
&mut size,
PAGE_EXECUTE_READWRITE,
&mut old_protection,
);
if z != 0 {
return false;
}
let ptr = unhook_info.0 as *mut u8;
for i in 0..unhook_info.1.len() {
let addr = ptr.add(i);
*addr = unhook_info.1[i];
}
let mut unused = 0u32;
let z = nt_protect_virtual_memory(
handle,
base_address,
&mut size,
old_protection,
&mut unused,
);
if z != 0 {
return false;
}
HOOKED_FUNCTIONS_INFO.remove(index);
true
}
}
pub fn get_module_base_address(module_name: &str) -> usize {
peb::get_module_by_name(module_name)
}
pub fn get_module_base_address_h(name_hash: u32) -> usize {
peb::get_module_by_hash(name_hash)
}
pub fn get_function_address(module_base_address: usize, function: &str) -> usize {
if module_base_address == 0 {
return 0;
}
unsafe {
let mut function_ptr: *mut i32 = ptr::null_mut();
let pe_header = *((module_base_address + 0x3C) as *mut i32);
let opt_header: usize = module_base_address + (pe_header as usize) + 0x18;
let magic = *(opt_header as *mut i16);
let p_export: usize;
if magic == 0x010b {
p_export = opt_header + 0x60;
} else {
p_export = opt_header + 0x70;
}
let export_rva = *(p_export as *mut i32);
let export_size = *((p_export + 4) as *mut i32);
let export_start = export_rva as usize;
let export_end = export_start.wrapping_add(export_size as usize);
let ordinal_base = *((module_base_address + export_rva as usize + 0x10) as *mut i32);
let number_of_names = *((module_base_address + export_rva as usize + 0x18) as *mut i32);
let functions_rva = *((module_base_address + export_rva as usize + 0x1C) as *mut i32);
let names_rva = *((module_base_address + export_rva as usize + 0x20) as *mut i32);
let ordinals_rva = *((module_base_address + export_rva as usize + 0x24) as *mut i32);
for x in 0..number_of_names {
let address =
*((module_base_address + names_rva as usize + x as usize * 4) as *mut i32);
let mut function_name_ptr = (module_base_address + address as usize) as *mut u8;
let mut function_name: String = "".to_string();
while *function_name_ptr as char != '\0'
{
function_name.push(*function_name_ptr as char);
function_name_ptr = function_name_ptr.add(1);
}
if function_name.to_lowercase() == function.to_lowercase() {
let function_ordinal = *((module_base_address
+ ordinals_rva as usize
+ x as usize * 2) as *mut i16) as i32
+ ordinal_base;
let function_rva = *((module_base_address
+ functions_rva as usize
+ (4 * (function_ordinal - ordinal_base)) as usize)
as *mut i32);
function_ptr = (module_base_address + function_rva as usize) as *mut i32;
let rva = function_rva as usize;
if rva >= export_start && rva < export_end {
function_ptr = get_forward_address(function_ptr as *mut u8) as *mut i32;
}
break;
}
}
let mut ret: usize = 0;
if function_ptr != ptr::null_mut() {
ret = function_ptr as usize;
}
ret
}
}
fn get_forward_address(function_ptr: *mut u8) -> usize {
unsafe {
let mut c = 100;
let mut ptr = function_ptr.clone();
let mut forwarded_names = "".to_string();
loop {
if *ptr as char != '\0' {
forwarded_names.push(*ptr as char);
} else {
break;
}
ptr = ptr.add(1);
c = c - 1;
if c == 0 {
return function_ptr as usize;
}
}
let values: Vec<&str> = forwarded_names.split(".").collect();
if values.len() != 2 {
return function_ptr as usize;
}
let mut forwarded_module_name = values[0].to_string();
let forwarded_export_name = values[1].to_string();
if forwarded_module_name.len() < 2 || forwarded_export_name.len() == 0 {
return function_ptr as usize;
}
let api_set = get_api_mapping();
if forwarded_module_name.len() < 2
|| !forwarded_module_name.is_char_boundary(forwarded_module_name.len() - 2)
{
return function_ptr as usize;
}
let lookup_key = format!(
"{}{}",
&forwarded_module_name[..forwarded_module_name.len() - 2],
".dll"
);
if api_set.contains_key(&lookup_key) {
forwarded_module_name = match api_set.get(&lookup_key) {
Some(x) => x.to_string(),
None => forwarded_module_name,
};
} else {
forwarded_module_name = forwarded_module_name + ".dll";
}
let mut module = get_module_base_address(&forwarded_module_name);
if module == 0 {
module = load_library_a(&forwarded_module_name);
}
if module != 0 {
return get_function_address(module, &forwarded_export_name);
}
function_ptr as usize
}
}
pub fn get_api_mapping() -> BTreeMap<String, String> {
unsafe {
let handle = INVALID_HANDLE_VALUE;
let mut p: PROCESS_BASIC_INFORMATION = core::mem::zeroed();
let process_information: *mut c_void = &mut p as *mut PROCESS_BASIC_INFORMATION as *mut c_void;
let _ret = nt_query_information_process(
handle,
0,
process_information,
size_of::<PROCESS_BASIC_INFORMATION>() as u32,
ptr::null_mut(),
);
let process_information_ptr: *mut PROCESS_BASIC_INFORMATION =
core::mem::transmute(process_information);
let api_set_map_offset: usize;
if size_of::<usize>() == 4 {
api_set_map_offset = 0x38;
} else {
api_set_map_offset = 0x68;
}
let mut api_set_dict: BTreeMap<String, String> = BTreeMap::new();
let api_set_namespace_ptr = *(((*process_information_ptr).PebBaseAddress as usize
+ api_set_map_offset) as *mut usize);
let api_set_namespace_ptr: *mut ApiSetNamespace =
core::ptr::with_exposed_provenance_mut::<ApiSetNamespace>(api_set_namespace_ptr);
let namespace = *api_set_namespace_ptr;
for i in 0..namespace.count {
let set_entry_ptr = (api_set_namespace_ptr as usize
+ namespace.entry_offset as usize
+ (i * size_of::<ApiSetNamespaceEntry>() as i32) as usize)
as *mut ApiSetNamespaceEntry;
let set_entry = *set_entry_ptr;
let mut api_set_entry_name_ptr: *mut u8 =
(api_set_namespace_ptr as usize + set_entry.name_offset as usize) as *mut u8;
let mut api_set_entry_name: String = "".to_string();
let mut j = 0;
while j < (set_entry.name_length / 2) {
let c = *api_set_entry_name_ptr as char;
if c != '\0' {
api_set_entry_name.push(c);
j = j + 1;
}
api_set_entry_name_ptr = api_set_entry_name_ptr.add(1);
}
if api_set_entry_name.len() < 2 {
continue;
}
let api_set_entry_key = format!(
"{}{}",
&api_set_entry_name[..api_set_entry_name.len() - 2],
".dll"
);
let mut set_value_ptr: *mut ApiSetValueEntry = ptr::null_mut();
if set_entry.value_length == 1 {
let value =
(api_set_namespace_ptr as usize + set_entry.value_offset as usize) as *mut u8;
set_value_ptr = core::mem::transmute(value);
} else if set_entry.value_length > 1 {
for x in 0..set_entry.value_length {
let host_ptr = (api_set_namespace_ptr as usize
+ set_entry.value_offset as usize
+ size_of::<ApiSetValueEntry>() as usize * x as usize)
as *mut u8;
let mut host_walk: *mut u8 = host_ptr;
let mut host: String = "".to_string();
loop {
let c = *host_walk;
if c == 0 {
break;
}
host.push(c as char);
host_walk = host_walk.add(1);
}
if host != api_set_entry_name {
set_value_ptr = (api_set_namespace_ptr as usize
+ set_entry.value_offset as usize
+ size_of::<ApiSetValueEntry>() as usize * x as usize)
as *mut ApiSetValueEntry;
}
}
if set_value_ptr == ptr::null_mut() {
set_value_ptr = (api_set_namespace_ptr as usize
+ set_entry.value_offset as usize)
as *mut ApiSetValueEntry;
}
}
let set_value = *set_value_ptr;
let mut api_set_value: String = "".to_string();
if set_value.value_count != 0 {
let mut value_ptr =
(api_set_namespace_ptr as usize + set_value.value_offset as usize) as *mut u8;
let mut r = 0;
while r < (set_value.value_count / 2) {
let c = *value_ptr as char;
if c != '\0' {
api_set_value.push(c);
r = r + 1;
}
value_ptr = value_ptr.add(1);
}
}
api_set_dict.insert(api_set_entry_key, api_set_value);
}
api_set_dict
}
}
pub fn call_module_entry_point(
pe_info: PeMetadata,
module_base_address: usize,
) -> Result<(), String> {
let entry_point;
if pe_info.is_32_bit {
entry_point = module_base_address + pe_info.opt_header_32.AddressOfEntryPoint as usize;
} else {
entry_point = module_base_address + pe_info.opt_header_64.address_of_entry_point as usize;
}
unsafe {
let main: EntryPoint = core::mem::transmute(entry_point);
let module = entry_point as HINSTANCE;
let ret = main(module, DLL_PROCESS_ATTACH, ptr::null_mut());
if ret == 0 {
return Err(lc!(
"[x] Failed to call module's entry point (DllMain -> DLL_PROCESS_ATTACH)."
));
}
Ok(())
}
}
pub fn get_function_address_by_ordinal(module_base_address: usize, ordinal: u32) -> usize {
let ret = ldr_get_procedure_address(module_base_address, "", ordinal);
ret
}
pub fn fork() -> i32 {
unsafe {
let mut process_handle: HANDLE = ptr::null_mut();
let mut thread_handle: HANDLE = ptr::null_mut();
let mut create_info: PsCreateInfo = core::mem::zeroed();
create_info.size = size_of::<PsCreateInfo>();
let ps_create_info: *mut PsCreateInfo = core::mem::transmute(&create_info);
let ret = nt_create_user_process(
&mut process_handle,
&mut thread_handle,
(0x000F0000) | (0x00100000) | 0xFFFF, (0x000F0000) | (0x00100000) | 0xFFFF, ptr::null_mut(),
ptr::null_mut(),
0x00000004, 0,
ptr::null_mut(),
ps_create_info, ptr::null_mut(),
);
ret
}
}
pub fn ldr_get_procedure_address(module_handle: usize, function_name: &str, ordinal: u32) -> usize {
unsafe {
let ret: Option<i32>;
let func_ptr: data::LdrGetProcedureAddress;
let hmodule: PVOID = core::ptr::with_exposed_provenance_mut::<c_void>(module_handle);
let mut r = usize::default();
let return_address: *mut PVOID = &mut r as *mut usize as *mut PVOID;
let c_name = if function_name.is_empty() {
None
} else {
CString::new(function_name).ok()
};
let ansi = c_name.as_ref().map(|s| data::ANSI_STRING {
Length: s.as_bytes().len() as u16,
MaximumLength: (s.as_bytes().len() + 1) as u16,
Buffer: s.as_ptr() as *const u8,
});
let name_ptr: *const data::ANSI_STRING = match ansi.as_ref() {
Some(a) => a as *const _,
None => ptr::null(),
};
let module_base_address = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
module_base_address,
&lc!("LdrGetProcedureAddress"),
func_ptr,
ret,
hmodule,
name_ptr,
ordinal,
return_address
);
match ret {
Some(0) => *return_address as usize,
_ => 0,
}
}
}
pub fn set_unhandled_exception_filter(address: usize) -> LptopLevelExceptionFilter {
unsafe {
let ret: Option<LptopLevelExceptionFilter>;
let func_ptr: data::SetUnhandledExceptionFilter;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("SetUnhandledExceptionFilter"),
func_ptr,
ret,
address
);
match ret {
Some(x) => return x,
None => return 0,
}
}
}
pub fn add_vectored_exception_handler(first: u32, address: usize) -> PVOID {
unsafe {
let ret: Option<PVOID>;
let func_ptr: data::AddVectoredExceptionHandler;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("AddVectoredExceptionHandler"),
func_ptr,
ret,
first,
address
);
match ret {
Some(x) => return x,
None => return ptr::null_mut(),
}
}
}
pub fn load_library_a_tp(module: &str) -> usize {
unsafe {
let ret: Option<i32>;
let func_ptr: data::RtlQueueWorkItem;
let name = CString::new(module.to_string()).expect("");
let module_name: PVOID = core::mem::transmute(name.as_ptr());
let k32 = get_module_base_address(&lc!("kernel32.dll"));
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
let load_library = get_function_address(k32, &lc!("LoadLibraryA"));
dynamic_invoke!(
ntdll,
&lc!("RtlQueueWorkItem"),
func_ptr,
ret,
load_library,
module_name,
0
);
match ret {
Some(x) => {
if x != 0 {
return 0;
} else {
nt_sleep_millis(500);
return get_module_base_address(module);
}
}
None => {
return 0;
}
}
}
}
pub fn nt_sleep_millis(ms: u64) {
type NtDelayExecution = unsafe extern "system" fn(u8, *mut LARGE_INTEGER) -> i32;
unsafe {
let mut interval = LARGE_INTEGER {
QuadPart: -(ms as i64 * 10_000),
};
let ret: Option<i32>;
let func_ptr: NtDelayExecution;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtDelayExecution"),
func_ptr,
ret,
0u8,
&mut interval as *mut _
);
let _ = ret;
}
}
pub fn load_library_a(module: &str) -> usize {
unsafe {
let ret: Option<usize>;
let func_ptr: data::LoadLibraryA;
let name = CString::new(module.to_string()).expect("");
let module_name: *mut u8 = core::mem::transmute(name.as_ptr());
let k32 = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(k32, &lc!("LoadLibraryA"), func_ptr, ret, module_name);
match ret {
Some(x) => {
return x;
}
None => {
return 0;
}
}
}
}
pub fn free_library(module_handle: isize) -> usize {
unsafe {
let ret: Option<BOOL>;
let func_ptr: data::FreeLibrary;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("FreeLibrary"),
func_ptr,
ret,
module_handle as HINSTANCE
);
match ret {
Some(x) => return if x != 0 { 1 } else { 0 },
None => return 0,
}
}
}
pub fn create_file_a(
name: *mut u8,
access: u32,
mode: u32,
attributes: *const SECURITY_ATTRIBUTES,
disposition: u32,
flags: u32,
template: HANDLE,
) -> HANDLE {
unsafe {
let ret: Option<HANDLE>;
let func_ptr: data::CreateFileA;
let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
kernel32,
&lc!("CreateFileA"),
func_ptr,
ret,
name,
access,
mode,
attributes,
disposition,
flags,
template
);
match ret {
Some(x) => return x,
None => return core::ptr::null_mut(),
}
}
}
pub fn get_file_size(handle: HANDLE, size: *mut u32) -> u32 {
unsafe {
let ret: Option<u32>;
let func_ptr: data::GetFileSize;
let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(kernel32, &lc!("GetFileSize"), func_ptr, ret, handle, size);
match ret {
Some(x) => return x,
None => return 0,
}
}
}
pub fn create_file_mapping_w(
file: HANDLE,
attributes: *const SECURITY_ATTRIBUTES,
protect: u32,
max_size_high: u32,
max_size_low: u32,
name: *mut u8,
) -> HANDLE {
unsafe {
let ret: Option<HANDLE>;
let func_ptr: data::CreateFileMapping;
let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
kernel32,
&lc!("CreateFileMappingW"),
func_ptr,
ret,
file,
attributes,
protect,
max_size_high,
max_size_low,
name
);
match ret {
Some(x) => return x,
None => return core::ptr::null_mut(),
}
}
}
pub fn map_view_of_file(
file: HANDLE,
access: u32,
off_high: u32,
off_low: u32,
bytes: usize,
) -> PVOID {
unsafe {
let ret: Option<PVOID>;
let func_ptr: data::MapViewOfFile;
let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
kernel32,
&lc!("MapViewOfFile"),
func_ptr,
ret,
file,
access,
off_high,
off_low,
bytes
);
match ret {
Some(x) => return x,
None => return ptr::null_mut(),
}
}
}
pub fn unmap_view_of_file(base_address: PVOID) -> bool {
unsafe {
let ret: Option<BOOL>;
let func_ptr: data::UnmapViewOfFile;
let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
kernel32,
&lc!("UnmapViewOfFile"),
func_ptr,
ret,
base_address
);
match ret {
Some(x) => return x != 0,
None => return false,
}
}
}
pub fn rollback_transaction(transaction: HANDLE) -> bool {
unsafe {
let ret: Option<BOOL>;
let func_ptr: data::RollbackTransaction;
let ktmv = load_library_a(&lc!("KtmW32.dll"));
dynamic_invoke!(
ktmv,
&lc!("RollbackTransaction"),
func_ptr,
ret,
transaction
);
match ret {
Some(x) => return x != 0,
None => return false,
}
}
}
pub fn open_process(desired_access: u32, inherit_handle: i32, process_id: u32) -> HANDLE {
unsafe {
let ret: Option<HANDLE>;
let func_ptr: data::OpenProcess;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("OpenProcess"),
func_ptr,
ret,
desired_access,
inherit_handle,
process_id
);
match ret {
Some(x) => return x,
None => return core::ptr::null_mut(),
}
}
}
pub fn open_thread(desired_access: u32, inherit_handle: i32, thread_id: u32) -> HANDLE {
unsafe {
let ret: Option<HANDLE>;
let func_ptr: data::OpenThread;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("OpenThread"),
func_ptr,
ret,
desired_access,
inherit_handle,
thread_id
);
match ret {
Some(x) => return x,
None => return core::ptr::null_mut(),
}
}
}
pub fn close_handle(handle: HANDLE) -> bool {
unsafe {
let ret: Option<i32>;
let func_ptr: data::CloseHandle;
let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(kernel32, &lc!("CloseHandle"), func_ptr, ret, handle);
match ret {
Some(x) => {
if x == 0 {
return false;
} else {
return true;
}
}
None => return false,
}
}
}
pub fn tls_alloc() -> u32 {
unsafe {
let ret: Option<u32>;
let func_ptr: data::TlsAlloc;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(module_base_address, &lc!("TlsAlloc"), func_ptr, ret,);
match ret {
Some(x) => return x,
None => return TLS_OUT_OF_INDEXES,
}
}
}
pub fn tls_get_value(index: u32) -> PVOID {
unsafe {
let ret: Option<PVOID>;
let func_ptr: data::TlsGetValue;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("TlsGetValue"),
func_ptr,
ret,
index
);
match ret {
Some(x) => return x,
None => return ptr::null_mut(),
}
}
}
pub fn tls_set_value(index: u32, data: PVOID) -> bool {
unsafe {
let ret: Option<bool>;
let func_ptr: data::TlsSetValue;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("TlsSetValue"),
func_ptr,
ret,
index,
data
);
match ret {
Some(x) => return x,
None => return false,
}
}
}
pub fn enum_process_modules(
process: HANDLE,
module: *mut usize,
cb: u32,
needed: *mut u32,
) -> bool {
unsafe {
let ret: Option<bool>;
let func_ptr: data::EnumProcessModules;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("K32EnumProcessModules"),
func_ptr,
ret,
process,
module,
cb,
needed
);
match ret {
Some(x) => return x,
None => return false,
}
}
}
pub fn get_module_handle_ex_a(flags: i32, module_name: *const u8, module: *mut usize) -> bool {
unsafe {
let ret: Option<bool>;
let func_ptr: data::GetModuleHandleExA;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("GetModuleHandleExA"),
func_ptr,
ret,
flags,
module_name,
module
);
match ret {
Some(x) => return x,
None => return false,
}
}
}
pub fn get_module_base_name_w(
process: HANDLE,
module: usize,
base_name: *mut u16,
size: u32,
) -> u32 {
unsafe {
let ret: Option<u32>;
let func_ptr: data::GetModuleBaseNameW;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("K32GetModuleBaseNameW"),
func_ptr,
ret,
process,
module,
base_name,
size
);
match ret {
Some(x) => return x,
None => return 0,
}
}
}
pub fn get_module_file_name_ex_w(
process: HANDLE,
module: usize,
base_name: *mut u16,
size: u32,
) -> u32 {
unsafe {
let ret: Option<u32>;
let func_ptr: data::GetModuleFileNameExW;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("K32GetModuleFileNameExW"),
func_ptr,
ret,
process,
module,
base_name,
size
);
match ret {
Some(x) => return x,
None => return 0,
}
}
}
pub fn get_last_error() -> u32 {
let last_error: u32;
#[cfg(target_arch = "x86_64")]
unsafe {
core::arch::asm!(
"mov {err:e}, gs:[0x68]",
err = out(reg) last_error,
options(nostack, preserves_flags, readonly)
);
}
#[cfg(target_arch = "x86")]
unsafe {
core::arch::asm!(
"mov {err:e}, fs:[0x34]",
err = out(reg) last_error,
options(nostack, preserves_flags, readonly)
);
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
compile_error!("get_last_error only supports x86 / x86_64");
last_error
}
pub fn local_alloc(flags: u32, size: usize) -> PVOID {
unsafe {
let ret: Option<PVOID>;
let func_ptr: data::LocalAlloc;
let module_base_address = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
module_base_address,
&lc!("LocalAlloc"),
func_ptr,
ret,
flags,
size
);
match ret {
Some(x) => return x,
None => return ptr::null_mut(),
}
}
}
pub fn get_system_info(sysinfo: *mut SYSTEM_INFO) {
unsafe {
let _ret: Option<()>;
let func_ptr: data::GetSystemInfo;
let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(kernel32, &lc!("GetSystemInfo"), func_ptr, _ret, sysinfo);
}
}
pub fn virtual_query_ex(
process_handle: HANDLE,
page_address: *const c_void,
buffer: *mut MEMORY_BASIC_INFORMATION,
length: usize,
) -> usize {
unsafe {
let ret: Option<usize>;
let func_ptr: data::VirtualQueryEx;
let kernel32 = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
kernel32,
&lc!("VirtualQueryEx"),
func_ptr,
ret,
process_handle,
page_address,
buffer,
length
);
match ret {
Some(x) => return x,
None => return 0,
}
}
}
pub fn virtual_free(address: PVOID, size: usize, free_type: u32) -> bool {
unsafe {
let ret: Option<bool>;
let func_ptr: data::VirtualFree;
let k32 = get_module_base_address(&lc!("kernel32.dll"));
dynamic_invoke!(
k32,
&lc!("VirtualFree"),
func_ptr,
ret,
address,
size,
free_type
);
match ret {
Some(x) => {
return x;
}
None => return false,
}
}
}
pub fn nt_create_user_process(
process_handle: *mut HANDLE,
thread_handle: *mut HANDLE,
process_access: u32,
thread_access: u32,
object_attributes: *mut OBJECT_ATTRIBUTES,
thread_object_attr: *mut OBJECT_ATTRIBUTES,
process_flags: u32,
thread_flags: u32,
parameters: PVOID,
create_info: *mut PsCreateInfo,
attr_list: *mut PsAttributeList,
) -> i32 {
unsafe {
let ret: Option<i32>;
let func_ptr: data::NtCreateUserProcess;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtCreateUserProcess"),
func_ptr,
ret,
process_handle,
thread_handle,
process_access,
thread_access,
object_attributes,
thread_object_attr,
process_flags,
thread_flags,
parameters,
create_info,
attr_list
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
#[cfg(target_arch = "x86_64")]
pub fn nt_write_virtual_memory(
mut handle: HANDLE,
base_address: PVOID,
mut buffer: PVOID,
mut size: usize,
bytes_written: *mut usize,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtWriteVirtualMemory;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
let _decoy_buf: Vec<u8>;
if HARDWARE_BREAKPOINTS {
let addr = get_function_address(ntdll, &lc!("NtWriteVirtualMemory")) as usize;
HARDWARE_EXCEPTION_FUNCTION = ExceptionHandleFunction::NtWriteVirtualMemory;
NT_WRITE_VIRTUAL_MEMORY_ARGS.handle = handle;
NT_WRITE_VIRTUAL_MEMORY_ARGS.base_address = base_address;
NT_WRITE_VIRTUAL_MEMORY_ARGS.buffer = buffer;
NT_WRITE_VIRTUAL_MEMORY_ARGS.size = size;
set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
handle = INVALID_HANDLE_VALUE;
_decoy_buf = vec![20u8];
buffer = _decoy_buf.as_ptr() as PVOID;
size = _decoy_buf.len();
}
dynamic_invoke!(
ntdll,
&lc!("NtWriteVirtualMemory"),
func_ptr,
ret,
handle,
base_address,
buffer,
size,
bytes_written
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
#[cfg(target_arch = "x86")]
pub fn nt_write_virtual_memory(
handle: HANDLE,
base_address: PVOID,
buffer: PVOID,
size: usize,
bytes_written: *mut usize,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtWriteVirtualMemory;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtWriteVirtualMemory"),
func_ptr,
ret,
handle,
base_address,
buffer,
size,
bytes_written
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
#[cfg(target_arch = "x86_64")]
pub fn nt_allocate_virtual_memory(
mut handle: HANDLE,
mut base_address: *mut PVOID,
zero_bits: usize,
size: *mut usize,
allocation_type: u32,
protection: u32,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtAllocateVirtualMemory;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
if HARDWARE_BREAKPOINTS {
let addr = get_function_address(ntdll, &lc!("NtAllocateVirtualMemory")) as usize;
HARDWARE_EXCEPTION_FUNCTION = ExceptionHandleFunction::NtAllocateVirtualMemory;
NT_ALLOCATE_VIRTUAL_MEMORY_ARGS.handle = handle;
NT_ALLOCATE_VIRTUAL_MEMORY_ARGS.base_address = base_address;
set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
handle = INVALID_HANDLE_VALUE;
base_address = ptr::null_mut();
}
dynamic_invoke!(
ntdll,
&lc!("NtAllocateVirtualMemory"),
func_ptr,
ret,
handle,
base_address,
zero_bits,
size,
allocation_type,
protection
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
#[cfg(target_arch = "x86")]
pub fn nt_allocate_virtual_memory(
handle: HANDLE,
base_address: *mut PVOID,
zero_bits: usize,
size: *mut usize,
allocation_type: u32,
protection: u32,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtAllocateVirtualMemory;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtAllocateVirtualMemory"),
func_ptr,
ret,
handle,
base_address,
zero_bits,
size,
allocation_type,
protection
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
#[cfg(target_arch = "x86_64")]
pub fn nt_protect_virtual_memory(
mut handle: HANDLE,
mut base_address: *mut PVOID,
mut size: *mut usize,
mut new_protection: u32,
old_protection: *mut u32,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtProtectVirtualMemory;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
if HARDWARE_BREAKPOINTS {
let addr = get_function_address(ntdll, &lc!("NtProtectVirtualMemory")) as usize;
HARDWARE_EXCEPTION_FUNCTION = ExceptionHandleFunction::NtProtectVirtualMemory;
NT_PROTECT_VIRTUAL_MEMORY_ARGS.handle = handle;
NT_PROTECT_VIRTUAL_MEMORY_ARGS.base_address = base_address;
NT_PROTECT_VIRTUAL_MEMORY_ARGS.size = size;
NT_PROTECT_VIRTUAL_MEMORY_ARGS.protection = new_protection;
set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
handle = INVALID_HANDLE_VALUE;
base_address = ptr::null_mut();
let s = 10usize;
size = core::mem::transmute(&s);
new_protection = PAGE_READONLY;
}
dynamic_invoke!(
ntdll,
&lc!("NtProtectVirtualMemory"),
func_ptr,
ret,
handle,
base_address,
size,
new_protection,
old_protection
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
#[cfg(target_arch = "x86")]
pub fn nt_protect_virtual_memory(
handle: HANDLE,
base_address: *mut PVOID,
size: *mut usize,
new_protection: u32,
old_protection: *mut u32,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtProtectVirtualMemory;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtProtectVirtualMemory"),
func_ptr,
ret,
handle,
base_address,
size,
new_protection,
old_protection
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
#[cfg(target_arch = "x86_64")]
pub fn nt_open_process(
mut handle: *mut HANDLE,
mut access: u32,
mut attributes: *mut OBJECT_ATTRIBUTES,
mut client_id: *mut ClientId,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtOpenProcess;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
if HARDWARE_BREAKPOINTS {
let addr = get_function_address(ntdll, &lc!("NtOpenProcess")) as usize;
HARDWARE_EXCEPTION_FUNCTION = ExceptionHandleFunction::NtOpenProcess;
NT_OPEN_PROCESS_ARGS.handle = handle;
NT_OPEN_PROCESS_ARGS.access = access;
NT_OPEN_PROCESS_ARGS.attributes = attributes;
NT_OPEN_PROCESS_ARGS.client_id = client_id;
set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
let h = INVALID_HANDLE_VALUE;
handle = core::mem::transmute(&h);
access = PROCESS_QUERY_LIMITED_INFORMATION;
let a: OBJECT_ATTRIBUTES = core::mem::zeroed();
attributes = core::mem::transmute(&a);
let c = ClientId {
unique_process: GetCurrentProcessId() as *mut core::ffi::c_void,
unique_thread: core::ptr::null_mut(),
};
client_id = core::mem::transmute(&c);
}
dynamic_invoke!(
ntdll,
&lc!("NtOpenProcess"),
func_ptr,
ret,
handle,
access,
attributes,
client_id
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
#[cfg(target_arch = "x86")]
pub fn nt_open_process(
handle: *mut HANDLE,
access: u32,
attributes: *mut OBJECT_ATTRIBUTES,
client_id: *mut ClientId,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtOpenProcess;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtOpenProcess"),
func_ptr,
ret,
handle,
access,
attributes,
client_id
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
pub fn nt_query_information_process(
handle: HANDLE,
process_information_class: u32,
process_information: PVOID,
length: u32,
return_length: *mut u32,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtQueryInformationProcess;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtQueryInformationProcess"),
func_ptr,
ret,
handle,
process_information_class,
process_information,
length,
return_length
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
pub fn nt_query_information_thread(
handle: HANDLE,
thread_information_class: u32,
thread_information: PVOID,
length: u32,
return_length: *mut u32,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtQueryInformationThread;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtQueryInformationThread"),
func_ptr,
ret,
handle,
thread_information_class,
thread_information,
length,
return_length
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
pub fn nt_query_information_file(
handle: HANDLE,
io: *mut IO_STATUS_BLOCK,
file_information: PVOID,
length: u32,
file_information_class: u32,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtQueryInformationFile;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtQueryInformationFile"),
func_ptr,
ret,
handle,
io,
file_information,
length,
file_information_class
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
pub fn rtl_adjust_privilege(
privilege: u32,
enable: u8,
current_thread: u8,
enabled: *mut u8,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::RtlAdjustPrivilege;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("RtlAdjustPrivilege"),
func_ptr,
ret,
privilege,
enable,
current_thread,
enabled
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
pub fn rtl_init_unicode_string(
destination_string: *mut UNICODE_STRING,
source_string: *const u16,
) -> () {
unsafe {
let _ret: Option<()>;
let func_ptr: data::RtlInitUnicodeString;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("RtlInitUnicodeString"),
func_ptr,
_ret,
destination_string,
source_string
);
}
}
pub fn rtl_zero_memory(address: PVOID, length: usize) -> () {
unsafe {
let _ret: Option<()>;
let func_ptr: data::RtlZeroMemory;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("RtlZeroMemory"),
func_ptr,
_ret,
address,
length
);
}
}
pub fn nt_open_file(
file_handle: *mut HANDLE,
desired_access: u32,
object_attributes: *mut OBJECT_ATTRIBUTES,
io: *mut IO_STATUS_BLOCK,
share_access: u32,
options: u32,
) -> i32 {
unsafe {
let ret: Option<i32>;
let func_ptr: data::NtOpenFile;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtOpenFile"),
func_ptr,
ret,
file_handle,
desired_access,
object_attributes,
io,
share_access,
options
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
pub fn nt_create_section(
section_handle: *mut HANDLE,
desired_access: u32,
object_attributes: *mut OBJECT_ATTRIBUTES,
size: *mut LARGE_INTEGER,
page_protection: u32,
allocation_attributes: u32,
file_handle: HANDLE,
) -> i32 {
unsafe {
let ret: Option<i32>;
let func_ptr: data::NtCreateSection;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtCreateSection"),
func_ptr,
ret,
section_handle,
desired_access,
object_attributes,
size,
page_protection,
allocation_attributes,
file_handle
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
pub fn nt_map_view_of_section(
section_handle: HANDLE,
process_handle: HANDLE,
base_address: *mut PVOID,
zero: usize,
commit_size: usize,
offset: *mut LARGE_INTEGER,
view_size: *mut usize,
disposition: u32,
allocation_type: u32,
protection: u32,
) -> i32 {
unsafe {
let ret: Option<i32>;
let func_ptr: data::NtMapViewOfSection;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtMapViewOfSection"),
func_ptr,
ret,
section_handle,
process_handle,
base_address,
zero,
commit_size,
offset,
view_size,
disposition,
allocation_type,
protection
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
#[cfg(target_arch = "x86_64")]
pub fn nt_create_thread_ex(
mut thread: *mut HANDLE,
mut access: u32,
mut attributes: *mut OBJECT_ATTRIBUTES,
mut process: HANDLE,
function: PVOID,
args: PVOID,
flags: u32,
zero: usize,
stack: usize,
reserve: usize,
buffer: *mut PsAttributeList,
) -> i32 {
unsafe {
let ret: Option<i32>;
let func_ptr: data::NtCreateThreadEx;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
if HARDWARE_BREAKPOINTS {
let addr = get_function_address(ntdll, &lc!("NtCreateThreadEx")) as usize;
HARDWARE_EXCEPTION_FUNCTION = ExceptionHandleFunction::NtCreateThreadEx;
NT_CREATE_THREAD_EX_ARGS.thread = thread;
NT_CREATE_THREAD_EX_ARGS.access = access;
NT_CREATE_THREAD_EX_ARGS.attributes = attributes;
NT_CREATE_THREAD_EX_ARGS.process = process;
set_hardware_breakpoint(sys::get_syscall_address(addr as *mut _).unwrap_or(0));
let h = INVALID_HANDLE_VALUE;
thread = core::mem::transmute(&h);
access = PROCESS_QUERY_LIMITED_INFORMATION;
let a: OBJECT_ATTRIBUTES = core::mem::zeroed();
attributes = core::mem::transmute(&a);
process = INVALID_HANDLE_VALUE;
}
dynamic_invoke!(
ntdll,
&lc!("NtCreateThreadEx"),
func_ptr,
ret,
thread,
access,
attributes,
process,
function,
args,
flags,
zero,
stack,
reserve,
buffer
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
#[cfg(target_arch = "x86")]
pub fn nt_create_thread_ex(
thread: *mut HANDLE,
access: u32,
attributes: *mut OBJECT_ATTRIBUTES,
process: HANDLE,
function: PVOID,
args: PVOID,
flags: u32,
zero: usize,
stack: usize,
reserve: usize,
buffer: *mut PsAttributeList,
) -> i32 {
unsafe {
let ret: Option<i32>;
let func_ptr: data::NtCreateThreadEx;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtCreateThreadEx"),
func_ptr,
ret,
thread,
access,
attributes,
process,
function,
args,
flags,
zero,
stack,
reserve,
buffer
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
pub fn nt_read_virtual_memory(
handle: HANDLE,
base_address: PVOID,
buffer: PVOID,
size: usize,
bytes_written: *mut usize,
) -> i32 {
unsafe {
let ret;
let func_ptr: data::NtReadVirtualMemory;
let ntdll = get_module_base_address(&lc!("ntdll.dll"));
dynamic_invoke!(
ntdll,
&lc!("NtReadVirtualMemory"),
func_ptr,
ret,
handle,
base_address,
buffer,
size,
bytes_written
);
match ret {
Some(x) => return x,
None => return -1,
}
}
}
#[macro_export]
macro_rules! dynamic_invoke {
($a:expr, $b:expr, $c:expr) => {
let ret = $crate::call_module_entry_point($a,$b);
match ret {
Ok(_) => $c = true,
Err(_) => $c = false,
}
};
($a:expr, $b:expr, $c:expr, $d:expr, $($e:tt)*) => {
let function_ptr = $crate::get_function_address($a, $b);
if function_ptr != 0
{
$c = core::mem::transmute(function_ptr);
$d = Some($c($($e)*));
}
else
{
$d = None;
}
};
}
#[cfg(target_arch = "x86_64")]
#[macro_export]
macro_rules! syscall {
($name:expr $(, $arg:expr)* $(,)?) => {{
$crate::resolve_syscall($name).map(|(ssn, addr)| {
let argc = 0u32 $(+ { let _ = &$arg; 1u32 })*;
unsafe {
$crate::do_syscall(
ssn,
addr,
argc
$(, ::core::mem::transmute::<usize, *mut ::core::ffi::c_void>($arg as usize))*
)
}
})
}};
}
#[cfg(target_arch = "x86_64")]
#[macro_export]
macro_rules! do_syscall {
($ssn:expr, $addr:expr $(, $arg:expr)* $(,)?) => {{
let argc = 0u32 $(+ { let _ = &$arg; 1u32 })*;
unsafe {
$crate::do_syscall(
$ssn,
$addr,
argc
$(, ::core::mem::transmute::<usize, *mut ::core::ffi::c_void>($arg as usize))*
)
}
}};
}
#[cfg(test)]
extern crate std;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_invalid_handle_value() {
assert_eq!(INVALID_HANDLE_VALUE as isize, -1);
}
#[test]
fn test_constants_exist() {
assert_eq!(PAGE_EXECUTE_READ, 0x20);
assert_eq!(PAGE_EXECUTE_READWRITE, 0x40);
assert_eq!(PAGE_READONLY, 0x2);
assert_eq!(PAGE_READWRITE, 0x4);
assert_eq!(MEM_COMMIT, 0x1000);
assert_eq!(MEM_RESERVE, 0x2000);
assert_eq!(PROCESS_QUERY_LIMITED_INFORMATION, 0x1000);
assert_eq!(TLS_OUT_OF_INDEXES, 0xFFFFFFFF);
}
#[test]
fn test_eat_type() {
let mut eat: EAT = std::collections::BTreeMap::new();
eat.insert(0, "NtOpenProcess".to_string());
eat.insert(1, "NtAllocateVirtualMemory".to_string());
assert_eq!(eat.len(), 2);
assert_eq!(eat.get(&0), Some(&"NtOpenProcess".to_string()));
assert_eq!(eat.get(&1), Some(&"NtAllocateVirtualMemory".to_string()));
}
#[test]
fn test_rtl_zero_memory() {
let mut buffer = vec![0xFFu8; 64];
let ptr = buffer.as_mut_ptr() as PVOID;
rtl_zero_memory(ptr, 64);
for byte in &buffer {
assert_eq!(*byte, 0);
}
}
#[test]
fn test_large_integer_creation() {
let li = LARGE_INTEGER { QuadPart: 12345 };
assert_eq!(li.QuadPart, 12345);
let li_default = LARGE_INTEGER::default();
assert_eq!(li_default.QuadPart, 0);
}
#[test]
fn test_object_attributes_creation() {
let oa = OBJECT_ATTRIBUTES::default();
assert_eq!(oa.Length, 0);
assert!(oa.RootDirectory.is_null());
assert!(oa.ObjectName.is_null());
}
#[test]
fn test_client_id_creation() {
let cid = ClientId {
unique_process: 0x1234 as HANDLE,
unique_thread: 0x5678 as HANDLE,
};
assert_eq!(cid.unique_process as usize, 0x1234);
assert_eq!(cid.unique_thread as usize, 0x5678);
let cid_default = ClientId::default();
assert!(cid_default.unique_process.is_null());
assert!(cid_default.unique_thread.is_null());
}
#[test]
fn test_nt_allocate_virtual_memory_args() {
let args = NtAllocateVirtualMemoryArgs {
handle: INVALID_HANDLE_VALUE,
base_address: core::ptr::null_mut(),
};
assert_eq!(args.handle as isize, -1);
assert!(args.base_address.is_null());
}
#[test]
fn test_nt_open_process_args() {
let args = NtOpenProcessArgs {
handle: core::ptr::null_mut(),
access: 0x1000,
attributes: core::ptr::null_mut(),
client_id: core::ptr::null_mut(),
};
assert!(args.handle.is_null());
assert_eq!(args.access, 0x1000);
}
#[test]
fn test_nt_protect_virtual_memory_args() {
let args = NtProtectVirtualMemoryArgs {
handle: INVALID_HANDLE_VALUE,
base_address: core::ptr::null_mut(),
size: core::ptr::null_mut(),
protection: PAGE_EXECUTE_READWRITE,
};
assert_eq!(args.handle as isize, -1);
assert_eq!(args.protection, PAGE_EXECUTE_READWRITE);
}
#[test]
fn test_nt_write_virtual_memory_args() {
let args = NtWriteVirtualMemoryArgs {
handle: INVALID_HANDLE_VALUE,
base_address: core::ptr::null_mut(),
buffer: core::ptr::null_mut(),
size: 0,
};
assert_eq!(args.handle as isize, -1);
}
#[test]
fn test_nt_create_thread_ex_args() {
let args = NtCreateThreadExArgs {
thread: core::ptr::null_mut(),
access: 0x1F03FF,
attributes: core::ptr::null_mut(),
process: INVALID_HANDLE_VALUE,
};
assert!(args.thread.is_null());
assert_eq!(args.process as isize, -1);
}
#[cfg(target_arch = "x86_64")]
#[test]
fn test_use_hardware_breakpoints() {
use_hardware_breakpoints(true);
use_hardware_breakpoints(false);
}
#[test]
fn test_type_sizes() {
assert_eq!(core::mem::size_of::<HANDLE>(), core::mem::size_of::<*mut core::ffi::c_void>());
assert_eq!(core::mem::size_of::<PVOID>(), core::mem::size_of::<*mut core::ffi::c_void>());
}
}