Expand description
§dinvk 🦀
Dynamically invoke arbitrary code with Rust tricks, #[no_std] support, and compatibility for x64, x86, ARM64 and WoW64 (DInvoke)
This tool is a Rust version of DInvoke, originally written in C#, with additional features added.
§Table of Contents
§Features
- ✅ Dynamically invoke arbitrary code (x64, x86, Wow64, ARM64)
- ✅ Indirect Syscall (x64, x86, Wow64)
- ✅ Redirecting Syscall Invocation to Different DLLs
- ✅ Tampered Syscalls Via Hardware BreakPoints (x64, x86, Wow64)
- ✅ PE headers parsing
- ✅ Library Proxy Loading
- ✅ Support
#[no_std]projects - ✅ Retrieve exported API addresses via string, ordinal, and hashing
- ✅ Retrieve module addresses via string and hashing
- ✅ Supports multiple 32-bit hash algorithms for API Hashing using
GetModuleHandleandGetProcAddress: Jenkins3, Jenkins One-at-a-Time, DJB2, Murmur3, FNV-1a, SDBM, Lose, PJW, JS, and AP
§Installation
Add dinvk to your project by updating your Cargo.toml:
cargo add dinvk§Usage
dinvk provides several features for invoking code dynamically, performing indirect syscalls and manipulating exported modules and APIs. Below are detailed examples of how to use each feature.
§Dynamically Invoke Arbitrary Code
Allows resolving and calling a function dynamically at runtime, avoiding static linking.
- This example demonstrates the dynamic invocation of arbitrary code using
dinvoke!, resolving function addresses at runtime without direct linking. In this case,HeapAllocis dynamically called to allocate memory. - Using this macro is beneficial if you want to avoid having APIs directly listed in the
Import Address Table (IAT)of your PE file.
#![allow(unused)]
use dinvk::{
data::HeapAlloc,
dinvoke,
GetModuleHandle
};
const HEAP_ZERO_MEMORY: u32 = 8u32;
fn main() {
let peb = dinvk::NtCurrentPeb();
let kernel32 = GetModuleHandle("KERNEL32.DLL", None);
let addr = dinvoke!(
kernel32,
"HeapAlloc",
HeapAlloc,
(*peb).ProcessHeap,
HEAP_ZERO_MEMORY,
0x200
);
println!("@ Address: {:?}", addr);
}§Retrieving Module Addresses and Exported APIs
Retrieves the base address of a module and resolves exported APIs using different methods: by string, ordinal, or hash.
- In this example, the address of the
KERNEL32module is retrieved using both a string and a hash (Jenkins hash). - Then, the
LoadLibraryfunction address is resolved using the same methods, with an additional example using an ordinal number.
use dinvk::{hash::jenkins, GetModuleHandle, GetProcAddress};
fn main() {
// Retrieving module address via string and hash
let kernel32 = GetModuleHandle("KERNEL32.DLL", None);
let kernel32 = GetModuleHandle(3425263715u32, Some(jenkins));
// Retrieving exported API address via string, ordinal and hash
let addr = GetProcAddress(kernel32, "LoadLibraryA", None);
let addr = GetProcAddress(kernel32, 3962820501u32, Some(jenkins));
let addr = GetProcAddress(kernel32, 997, None);
}§Indirect syscall
Executes syscalls indirectly, bypassing user-mode API hooks and security monitoring tools.
- Currently supporting x64, x86 and WoW64.
- It uses techniques such as Hells Gate, Halos Gate, and Tartarus Gate to dynamically locate the System Service Number (SSN) and invoke the syscall indirectly.
use std::{ffi::c_void, ptr::null_mut};
use dinvk::{
data::{HANDLE, NTSTATUS, NT_SUCCESS},
syscall
};
fn main() -> Result<(), NTSTATUS> {
let mut addr = null_mut::<c_void>();
let mut size = (1 << 12) as usize;
let status = syscall!(
"NtAllocateVirtualMemory",
-1isize as HANDLE,
&mut addr,
0,
&mut size,
0x3000,
0x40
).ok_or(-1)?;
if !NT_SUCCESS(status) {
eprintln!("@ NtAllocateVirtualMemory Failed With Status: {:?}", status);
return Err(status)
}
Ok(())
}§Redirecting Syscall Invocation to Different DLLs
By default, syscalls in Windows are invoked via ntdll.dll. However, on x86_64 architectures, other DLLs such as win32u.dll, vertdll.dll and iumdll.dll also contain syscall instructions, allowing you to avoid indirect calls via ntdll.dll. On x86, only win32u.dll has these instructions.
The code below demonstrates how to invoke NtAllocateVirtualMemory using different DLLs to execute the syscall:
use std::{ffi::c_void, ptr::null_mut};
use dinvk::{
data::{HANDLE, NTSTATUS, NT_SUCCESS},
syscall, Dll
};
fn main() -> Result<(), NTSTATUS> {
// Alternatively, you can use Dll::Vertdll or Dll::Iumdll on x86_64
Dll::use_dll(Dll::Win32u);
// Memory allocation using a syscall
let mut addr = null_mut::<c_void>();
let mut size = (1 << 12) as usize;
let status = syscall!("NtAllocateVirtualMemory", -1isize as HANDLE, &mut addr, 0, &mut size, 0x3000, 0x04).ok_or(-1)?;
if !NT_SUCCESS(status) {
eprintln!("@ NtAllocateVirtualMemory Failed With Status: {}", status);
return Err(status);
}
Ok(())
}This method can be useful to avoid indirect invocations in ntdll.dll, diversifying the points of origin of the syscalls in the process.
§Different Hash Methods for API Hashing
Supports various hashing algorithms for API resolution, improving stealth and flexibility.
- Currently, the library only supports 32-bit hashes for API lookup.
use dinvk::hash::*;
fn main() {
println!("{}", jenkins("dinvk"));
println!("{}", jenkins3("dinvk"));
println!("{}", ap("dinvk"));
println!("{}", js("dinvk"));
println!("{}", murmur3("dinvk"));
println!("{}", fnv1a("dinvk"));
println!("{}", djb2("dinvk"));
println!("{}", crc32ba("dinvk"));
println!("{}", loselose("dinvk"));
println!("{}", pjw("dinvk"));
println!("{}", sdbm("dinvk"));
}§Library Proxy Loading
Allows DLLs to be loaded indirectly using an API call as an intermediary to clean the call stack and act as a proxy.
use dinvk::LdrProxy;
fn main() {
// RtlQueueWorkItem
LdrProxy::new("xpsservices.dll").work();
// RtlCreateTimer
LdrProxy::new("xpsservices.dll").timer();
// RtlRegisterWait
LdrProxy::new("xpsservices.dll").register_wait();
}§Tampered Syscalls Via Hardware BreakPoints
Utilizes hardware breakpoints to manipulate syscall parameters before execution, bypassing security hooks.
- The library includes several API wrappers that leverage DInvoke and support hardware breakpoints to spoof syscall arguments dynamically.
- These breakpoints modify syscall parameters after security monitoring tools inspect them but before the syscall executes, effectively bypassing detection.
- Currently supporting x64, x86 and WoW64.
- You can find the full list of wrapped functions in the wrappers module.
use dinvk::{
breakpoint::{set_use_breakpoint, veh_handler},
data::{HANDLE, NT_SUCCESS},
AddVectoredExceptionHandler,
NtAllocateVirtualMemory,
RemoveVectoredExceptionHandler,
};
fn main() {
// Enabling breakpoint hardware
set_use_breakpoint(true);
let handle = AddVectoredExceptionHandler(0, Some(veh_handler));
// Allocating memory and using breakpoint hardware
let mut addr = std::ptr::null_mut();
let mut size = 1 << 12;
let status = NtAllocateVirtualMemory(-1isize as HANDLE, &mut addr, 0, &mut size, 0x3000, 0x04);
if !NT_SUCCESS(status) {
eprintln!("@ NtAllocateVirtualMemory Failed With Status: {}", status);
return;
}
// Disabling breakpoint hardware
set_use_breakpoint(false);
RemoveVectoredExceptionHandler(handle);
}§Support for #[no_std] Environments
Enables #[no_std] compatibility for environments without the Rust standard library.
- To enable
#[no_std]support, define the required features in yourCargo.toml.
[dependencies]
dinvk = { version = "<version>", features = ["alloc", "dinvk_panic"] }- Running in
#[no_std]Mode.
#![no_std]
#![no_main]
use dinvk::allocator::WinHeap;
use dinvk::{
get_ntdll_address, dprintln,
GetProcAddress
};
#[global_allocator]
static ALLOCATOR: WinHeap = WinHeap::new();
#[no_mangle]
fn main() -> u8 {
let addr = GetProcAddress(get_ntdll_address(), "NtOpenProcess", None);
dprintln!("@ NtOpenProcess: {:?}", addr);
0
}
#[cfg(not(test))]
#[panic_handler]
fn pan(info: &core::panic::PanicInfo) -> ! {
dinvk::panic::dinvk_handler(info)
}§Contributing to dinvk
To contribute to dinvk, follow these steps:
- Fork this repository.
- Create a branch:
git checkout -b <branch_name>. - Make your changes and commit them:
git commit -m '<commit_message>'. - Push your changes to your branch:
git push origin <branch_name>. - Create a pull request.
Alternatively, consult the GitHub documentation on how to create a pull request.
§References
I want to express my gratitude to these projects that inspired me to create dinvk:
§License
This project is licensed under the MIT License. See the LICENSE file for details.
Modules§
- breakpoint
- Hardware breakpoint management utilities (only for x86/x86_64 targets).
- data
- Structures and types used across the library.
- hash
- Runtime hash functions.
- ldr
- Module containing dynamic module loader proxy.
- parse
- PE Parsing
Macros§
- dinvoke
- Macro to dynamically invoke a function from a specified module.
- dprintln
- Prints output to the Windows console using
ConsoleWriter. - link
- Declares an external function from a dynamically linked library.
- syscall
- Macro to perform a system call (syscall) by dynamically resolving its function name.
Structs§
- Console
Writer ConsoleWriteris a custom implementation ofcore::fmt::Writethat writes formatted strings directly to the Windows console.- LdrProxy
- A helper struct to interact with dynamic module loading with Windows APIs via Proxy.
Enums§
- Dll
- Represents different dynamic link libraries (DLLs) that contain system call functions.
- Symbol
- Represents a symbol reference by name or hash.
Functions§
- AddVectored
Exception Handler - Wrapper for the
AddVectoredExceptionHandlerfunction fromKERNEL32.DLL. - GetCurrent
Process Id - Returns the process ID of the calling process from the TEB.
- GetCurrent
Thread Id - Returns the thread ID of the calling thread from the TEB.
- GetModule
Handle - Resolves the base address of a module loaded in memory by name or hash.
- GetProc
Address - Retrieves the address of a function exported by a given module.
- GetProcess
Heap - Returns the default heap handle for the current process from the PEB.
- GetStd
Handle - Wrapper for the
GetStdHandlefunction fromKERNEL32.DLL. - Heap
Alloc - Wrapper for the
HeapAllocfunction fromKERNEL32.DLL. - Heap
Create - Wrapper for the
HeapCreatefunction fromKERNEL32.DLL. - Heap
Free - Wrapper for the
HeapFreefunction fromKERNEL32.DLL. - Load
LibraryA - Wrapper for the
LoadLibraryAfunction fromKERNEL32.DLL. - NtAllocate
Virtual Memory - Wrapper for the
NtAllocateVirtualMemoryfunction fromNTDLL.DLL. - NtCreate
Thread Ex - Wrapper for the
NtCreateThreadExfunction fromNTDLL.DLL. - NtCurrent
Peb - Retrieves a pointer to the Process Environment Block (PEB) of the current process.
- NtCurrent
Process - Returns a pseudo-handle to the current process ((HANDLE)-1).
- NtCurrent
Teb - Retrieves a pointer to the Thread Environment Block (TEB) of the current thread.
- NtCurrent
Thread - Returns a pseudo-handle to the current thread ((HANDLE)-2).
- NtGet
Thread Context - Wrapper for the
NtGetThreadContextfunction fromNTDLL.DLL. - NtProtect
Virtual Memory - Wrapper for the
NtProtectVirtualMemoryfunction fromNTDLL.DLL. - NtSet
Thread Context - Wrapper for the
NtSetThreadContextfunction fromNTDLL.DLL. - NtWrite
Virtual Memory - Wrapper for the
NtWriteVirtualMemoryfunction fromNTDLL.DLL. - Remove
Vectored Exception Handler - Wrapper for the
RemoveVectoredExceptionHandlerfunction fromKERNEL32.DLL. - __
readgsqword - Reads a
u64value from the GS segment at the specified offset. - canon
- Canonicalizes a UTF-16 path slice by removing
.dllsuffix and returning just the file name. - eq_
nocase - Compares two UTF-16 slices for case-insensitive ASCII equality.
- get_
ntdll_ address - Retrieves the base address of the
ntdll.dllmodule. - get_
syscall_ address - Retrieves the syscall address from a given function address.
- shuffle
- Randomly shuffles the elements of a mutable slice in-place using a pseudo-random
number generator seeded by the CPU’s timestamp counter (
rdtsc). - ssn
- Resolves the System Service Number (SSN) for a given function name within a module.
- upper
- Converts a UTF-16 slice into uppercase ASCII bytes and stores in a destination buffer.