Crates.io | dinvk |
lib.rs | dinvk |
version | 0.2.7 |
created_at | 2025-02-24 13:58:32.156142+00 |
updated_at | 2025-09-04 01:27:48.764826+00 |
description | Dynamically invoke arbitrary code and use various tricks written idiomatically in Rust (Dinvoke) |
homepage | https://github.com/joaoviictorti/dinvk |
repository | https://github.com/joaoviictorti/dinvk |
max_upload_size | |
id | 1567533 |
size | 151,603 |
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.
#[no_std]
environments (with alloc
).GetModuleHandle
and GetProcAddress
: Jenkins3, Jenkins One-at-a-Time, DJB2, Murmur3, FNV-1a, SDBM, Lose, PJW, JS, and AP.Add dinvk
to your project by updating your Cargo.toml
:
cargo add dinvk
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.
Allows resolving and calling a function dynamically at runtime, avoiding static linking.
dinvoke!
, resolving function addresses at runtime without direct linking. In this case, HeapAlloc
is dynamically called to allocate memory.Import Address Table (IAT)
of your PE file.use dinvk::{
data::HeapAllocFn,
dinvoke, GetModuleHandle,
GetProcessHeap
};
const HEAP_ZERO_MEMORY: u32 = 8u32;
fn main() {
let kernel32 = GetModuleHandle("KERNEL32.DLL", None);
let addr = dinvoke!(
kernel32,
"HeapAlloc",
HeapAllocFn,
GetProcessHeap(),
HEAP_ZERO_MEMORY,
0x200
);
println!("[+] Address: {:?}", addr);
}
Retrieves the base address of a module and resolves exported APIs using different methods: by string, ordinal, or hash.
KERNEL32
module is retrieved using both a string and a hash (Jenkins hash).LoadLibrary
function 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);
}
Executes syscalls indirectly, bypassing user-mode API hooks and security monitoring tools.
use std::{ffi::c_void, ptr::null_mut};
use dinvk::{
data::{HANDLE, NTSTATUS},
syscall, NtCurrentProcess,
NT_SUCCESS
};
fn main() -> Result<(), NTSTATUS> {
let mut addr = null_mut::<c_void>();
let mut size = (1 << 12) as usize;
let status = syscall!(
"NtAllocateVirtualMemory",
NtCurrentProcess(),
&mut addr,
0,
&mut size,
0x3000,
0x40
).ok_or(-1)?;
if !NT_SUCCESS(status) {
eprintln!("[-] NtAllocateVirtualMemory Failed With Status: {:?}", status);
return Err(status)
}
Ok(())
}
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},
syscall, Dll, NtCurrentProcess
NT_SUCCESS
};
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", NtCurrentProcess(), &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.
Supports various hashing algorithms for API resolution, improving stealth and flexibility.
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"));
}
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();
}
Utilizes hardware breakpoints to manipulate syscall parameters before execution, bypassing security hooks.
use dinvk::{
data::HANDLE,
breakpoint::{
set_use_breakpoint,
veh_handler
},
};
use dinvk::{
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);
}
Enables #[no_std]
compatibility for environments without the Rust standard library.
#[no_std]
support, define the required features in your Cargo.toml
.[dependencies]
dinvk = { version = "<version>", features = ["alloc", "dinvk_panic"] }
#[no_std]
Mode.#![no_std]
#![no_main]
use dinvk::allocator::WinHeap;
use dinvk::{
get_ntdll_address, println,
GetProcAddress
};
#[unsafe(no_mangle)]
fn main() -> u8 {
let addr = GetProcAddress(get_ntdll_address(), "NtOpenProcess", None);
println!("[+] NtOpenProcess: {:?}", addr);
0
}
#[global_allocator]
static ALLOCATOR: WinHeap = WinHeap;
#[cfg(not(test))]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
dinvk::panic::dinvk_handler(info)
}
I want to express my gratitude to these projects that inspired me to create dinvk
and contribute with some features:
This project is licensed under the MIT License. See the LICENSE file for details.