Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
DInvoke_rs — Dynamically invoke arbitrary unmanaged code | Kitploit
Tools/GitHubGitHub/kudaes/dinvoke_rs
IDS/IPS EvasionShellcodePost-ExploitationBinary AnalysisRed TeamingPayload Development
GitHubkudaes/dinvoke_rs

DInvoke_rs

Dynamically invoke arbitrary unmanaged code

View Repository
36643143 months agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

DInvoke_rs

Rust port of Dinvoke. DInvoke_rs may be used for many purposes such as PE parsing, dynamic exported functions resolution, dynamically loading PE plugins at runtime, API hooks evasion and more.

Features:

  • Dynamically resolve and invoke undocumented Windows APIs from Rust.
  • Primitives allowing for strategic API hook evasion.
  • Indirect syscalls. x64 only
  • Manually map PE modules from disk or directly from memory.
  • PE headers parsing.
  • Map PE modules into sections backed by arbitrary modules on disk. Not Opsec
  • Module fluctuation to hide mapped PEs (concurrency supported). Not Opsec
  • Syscall parameters spoofing through exception filter + hardware breakpoints. x64 only
  • Module stomping and shellcode fluctuation.
  • Template stomping.

Credit

All the credits go to the creators of the original C# implementation of this tool:

  • The Wover
  • FuzzySec (b33f)
  • cobbr

Content

  • Resolve exported function
  • Dynamically invoke unmanaged code
  • Execute Indirect Syscall
  • Manually map a PE from disk or memory
  • Overload memory section
  • Module fluctuation
  • Use hardware breakpoints to spoof syscall parameters
  • Module stomping and Shellcode fluctuation
  • Template stomping

Usage

Import this crate into your project by adding the following line to your cargo.toml:

[dependencies]
dinvoke_rs = "0.2.2"

Examples

Resolving Exported APIs

The example below demonstrates how to use DInvoke_rs to dynamically find and call exports of a DLL (ntdll.dll in this case).

  1. Get ntdll's base address.
  2. Use get_function_address() to find an export within ntdll.dll by name. This is achieved by walking and parsing the dll's EAT.
  3. You can also find an export by ordinal by calling get_function_address_by_ordinal().

fn main() {

    // Dynamically obtain ntdll.dll's base address. 
    let ntdll = dinvoke_rs::dinvoke::get_module_base_address("ntdll.dll");

    if ntdll != 0 
    {
        println!("ntdll.dll base address is 0x{:X}", ntdll);
        
        // Dynamically obtain the address of a function by name.
        let nt_create_thread = dinvoke_rs::dinvoke::get_function_address(ntdll, "NtCreateThread");
        if nt_create_thread != 0
        {
            println!("NtCreateThread is at address 0x{:X}", nt_create_thread);
        }

        // Dynamically obtain the address of a function by ordinal.
        let ordinal_8 = dinvoke_rs::dinvoke::get_function_address_by_ordinal(ntdll, 8);
        if ordinal_8 != 0 
        {
            println!("The function with ordinal 8 is located at addresss 0x{:X}", ordinal_8);
        }
    }   
}

Invoking Unmanaged Code

In the example below, we use DInvoke_rs to dynamically call RtlAdjustPrivilege in order to enable SeDebugPrivilege for the current process' token. This kind of execution will bypass any API hooks present in Win32. Also, it won't create any entry on the final PE's Import Address Table, making it harder to detect the PE's behaviour without executing it.


fn main() {

    // Dynamically obtain ntdll.dll's base address. 
    let ntdll = dinvoke_rs::dinvoke::get_module_base_address("ntdll.dll");

    if ntdll != 0 
    {
        unsafe 
        {
            let func_ptr:  unsafe extern "system" fn (u32, u8, u8, *mut u8) -> i32; // Function header available at data::RtlAdjustPrivilege
            let ret: Option<i32>; // RtlAdjustPrivilege returns an NSTATUS value, which in Rust can be represented as an i32
            let privilege: u32 = 20; // This value matches with SeDebugPrivilege
            let enable: u8 = 1; // Enable the privilege
            let current_thread: u8 = 0; // Enable the privilege for the current process, not only for the current thread
            let e = u8::default(); // https://github.com/Kudaes/rust_tips_and_tricks/tree/main#transmute
            let enabled: *mut u8 = std::mem::transmute(&e); 
            dinvoke_rs::dinvoke::dynamic_invoke!(ntdll,"RtlAdjustPrivilege",func_ptr,ret,privilege,enable,current_thread,enabled); 

            match ret {
                Some(x) => 
                	if x == 0 { println!("NTSTATUS == Success. Privilege enabled."); } 
                  	else { println!("[x] NTSTATUS == {:X}", x as u32); },
                None => panic!("[x] Error!"),
            }
        } 
    }   
}

Executing indirect syscall

In the next example, we use DInvoke_rs to execute the syscall corresponding to the function NtQueryInformationProcess. Since the macro execute_syscall!() dynamically allocates and executes the shellcode required to perform the desired syscall, all hooks present in ntdll.dll are bypassed. The memory allocated is release once the syscall returns, avoiding the permanent presence of memory pages with execution permission.


use std::mem::size_of;
use windows::Win32::System::Threading::{GetCurrentProcess, PROCESS_BASIC_INFORMATION};
use dinvoke_rs::data::{NtQueryInformationProcess, PVOID};

fn main() {

    unsafe 
    {
        let function_type:NtQueryInformationProcess;
        let ret: Option<i32>; //NtQueryInformationProcess returns a NTSTATUS, which is a i32.
        let handle = GetCurrentProcess();
        let p = PROCESS_BASIC_INFORMATION::default();
        let process_information: PVOID = std::mem::transmute(&p); 
        let r = u32::default();
        let return_length: *mut u32 = std::mem::transmute(&r);
        dinvoke_rs::dinvoke::execute_syscall!(
            "NtQueryInformationProcess",
            function_type,
            ret,
            handle,
            0,
            process_information,
            size_of::<PROCESS_BASIC_INFORMATION>() as u32,
            return_length
        );

        let pbi: *mut PROCESS_BASIC_INFORMATION;
        match ret {
            Some(x) => 
                if x == 0 {
                    pbi = std::mem::transmute(process_information);
                    let pbi = *pbi;
                    println!("The Process Environment Block base address is 0x{:X}", pbi.PebBaseAddress as u64);
                },
            None => println!("[x] Error executing direct syscall for NtQueryInformationProcess."),
        }  

    }
}

Manual PE mapping

In this example, DInvoke_rs is used to manually map a fresh copy of ntdll.dll, without any EDR hooks. Then that fresh ntdll.dll copy can be used to execute any desired function.

This manual map can also be executed from memory (use manually_map_module() in that case), allowing to perform the classic reflective dll injection.


use dinvoke_rs::data::PeMetadata;

fn main() {

    unsafe 
    {

        let ntdll: (PeMetadata, usize) = dinvoke_rs::manualmap::read_and_map_module(r"C:\Windows\System32\ntdll.dll", true, false).unwrap();

        let func_ptr:  unsafe extern "system" fn (u32, u8, u8, *mut u8) -> i32; // Function header available at data::RtlAdjustPrivilege
        let ret: Option<i32>; // RtlAdjustPrivilege returns an NSTATUS value, which is an i32
        let privilege: u32 = 20; // This value matches with SeDebugPrivilege
        let enable: u8 = 1; // Enable the privilege
        let current_thread: u8 = 0; // Enable the privilege for the current process, not only for the current thread
        let e = u8::default();
        let enabled: *mut u8 = std::mem::transmute(&e); 
        dinvoke_rs::dinvoke::dynamic_invoke!(ntdll.1,"RtlAdjustPrivilege",func_ptr,ret,privilege,enable,current_thread,enabled);
Download Tool