Skip to content
KitploitKITPLOIT
HerramientasBlog
Log in
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
Unwinder — Spoofing de pila de llamadas para Rust | Kitploit
Herramientas/GitHubGitHub/kudaes/unwinder
Evasión de IDS/IPSPost-ExplotaciónRed TeamingDesarrollo de PayloadsAtaque Adversario
GitHubkudaes/unwinder

Unwinder

Spoofing de pila de llamadas para Rust

Ver Repositorio
3843616hace 1 añoRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

Contenido

  • SilentMoonWalk
    • Descripción
    • Créditos
    • Uso
      • Macro call_function!()
      • Macro indirect_syscall!()
      • Paso de parámetros
    • Ejemplos
      • Llamando a kernel32.dll!Sleep()
      • Llamando a kernel32.dll!OpenProcess()
      • Llamando a NtDelayExecution() como syscall indirecto
      • Concatenar llamadas a macros
    • Consideraciones
      • Frame inicial
      • PoC
  • Reemplazo de pila
    • Descripción de la técnica
    • Uso
    • Ejemplo práctico
    • Observaciones

SilentMoonWalk

Descripción

Unwinder proporciona una weaponización completa de la técnica SilentMoonWalk, permitiendo obtener un spoofing completo y estable de la pila de llamadas en Rust.

Esta técnica presenta las siguientes características:

  • Permite ejecutar cualquier función arbitraria con hasta 11 parámetros.
  • Permite ejecutar syscalls indirectos (sin asignaciones adicionales en el heap) con hasta 11 parámetros.
  • El crate permite recuperar el valor devuelto por las funciones invocadas a través de él.
  • El proceso de spoofing puede concatenarse cualquier número de veces sin aumentar el tamaño de la pila de llamadas.
  • Se usa TLS para aumentar la eficiencia durante el proceso de spoofing.
  • dinvoke_rs se utiliza para realizar cualquier llamada a la API de Windows requerida por el crate.

Créditos

Reconocimientos a los creadores de la técnica SilentMoonWalk:

  • KlezVirus
  • Waldo-IRC
  • Trickster0

Y por supuesto, un enorme agradecimiento a namazso por el hilo de Twitter que inspiró todo este proyecto.

Uso

Importa este crate en tu proyecto añadiendo la siguiente línea a tu cargo.toml y compila en modo release:

[dependencies]
unwinder = "=0.1.4"

La funcionalidad principal de este crate se ha envuelto en dos macros:

  • La macro call_function!() permite ejecutar cualquier función arbitraria con una pila de llamadas limpia.
  • La macro indirect_syscall!() ejecuta el syscall (indirecto) especificado con una pila de llamadas limpia.

Para usar cualquiera de estas macros es necesario importar el tipo de datos std::ffi::c_void.

Ambas macros devuelven un *mut c_void que puede usarse para recuperar el valor devuelto por la función ejecutada. En la sección de ejemplos se proporciona información más detallada.

Macro call_function

Esta macro se utiliza para llamar a cualquier función deseada con una pila de llamadas limpia. La macro espera los siguientes parámetros:

  • El primer parámetro es la dirección de memoria a la que llamar después de falsear la pila de llamadas. Este parámetro debe pasarse como usize, isize o un puntero.
  • El segundo parámetro es un bool que indica si se debe mantener o no el frame de la función de inicio. Si no estás seguro de esto, establécelo en false, lo que siempre garantiza una buena pila de llamadas.
  • Los siguientes parámetros son los argumentos que se enviarán a la función una vez que la pila de llamadas haya sido falseada.

Macro indirect_syscall

Esta macro se utiliza para realizar cualquier syscall indirecto deseado con una pila de llamadas limpia. La macro espera los siguientes parámetros:

  • El primer parámetro es una cadena que contiene el nombre de la función NT cuyo syscall se desea ejecutar.
  • El segundo parámetro es un bool que indica si se debe mantener o no el frame de la función de inicio. Si no estás seguro de esto, establécelo en false, lo que siempre garantiza una buena pila de llamadas.
  • Los siguientes parámetros son los argumentos que se enviarán a la función NT.

Paso de parámetros

Para pasar argumentos de diferentes tipos a estas dos macros, se deben tener en cuenta las siguientes consideraciones:

  • Cualquier tipo de dato básico que pueda convertirse a usize (u8-u64, i8-i64, bool, etc.) puede pasarse directamente a las macros.
  • Las estructuras y uniones de tamaño 8, 16, 32 o 64 bits se pasan como si fueran enteros del mismo tamaño.
  • Las estructuras y uniones con un tamaño superior a 64 bits deben pasarse como un puntero.
  • Las cadenas (&str y String) deben pasarse como un puntero.
  • Los punteros nulos (ptr::null(), ptr::null_mut(), etc.) se pasan como un 0 (sin importar si es u8, u16, i32 o cualquier otro).
  • Los parámetros de coma flotante y doble precisión no están soportados actualmente.
  • Cualquier otro tipo de dato debe pasarse como un puntero.

Ejemplos

Llamando a Sleep

let k32 = dinvoke_rs::dinvoke::get_module_base_address("kernel32.dll");
let sleep = dinvoke_rs::dinvoke::get_function_address(k32, "Sleep"); // Memory address of kernel32.dll!Sleep() 
let miliseconds = 1000i32;
unwinder::call_function!(sleep, false, miliseconds);

Llamando a OpenProcess

let k32 = dinvoke_rs::dinvoke::get_module_base_address("kernel32.dll"); 
let open_process: isize = dinvoke_rs::dinvoke::get_function_address(k32, "Openprocess");
let desired_access: u32 = 0x1000;
let inherit = 0i32;
let pid = 20628i32;
let handle = unwinder::call_function!(open_process, false, desired_access, inherit, pid); // returns *mut c_void
let handle: HANDLE = std::mem::transmute(handle);
println!("Handle id: {:x}", handle.0);

Observa que la macro devuelve un *mut c_void que puede convertirse directamente a un HANDLE ya que ambos tipos de datos tienen el mismo tamaño. Esto permite acceder al valor devuelto por OpenProcess, que es el nuevo handle al proceso objetivo.

Llamando a NtDelayExecution como syscall indirecto

let large = 0x8000000000000000 as u64; // Sleep indefinitely
let large: *mut i64 = std::mem::transmute(&large);
let alertable = false;
let ntstatus = unwinder::indirect_syscall!("NtDelayExecution", false, alertable, large); // returns *mut c_void
println!("ntstatus: {:x}", ntstatus as i32);

Observa que la macro devuelve un *mut c_void que puede utilizarse para recuperar el NTSTATUS devuelto por NtDelayExecution.

Concatenar llamadas a macros

El proceso de spoofing puede concatenarse cualquier número de veces sin un incremento anómalo del tamaño de la pila de llamadas. El flujo de ejecución también se conservará. El siguiente código es un ejemplo de ello:

fn main()
{
	function_a();
}

fn function_a()
{
	unsafe
	{
		let func_b = function_b as usize;
		call_function!(func_b, false);
		println!("function_a done.");
	}
}

fn function_b()
{
	unsafe
	{
		let func_c = function_c as usize;
		call_function!(func_c, false);
		println!("function_b done.")
	}
}

fn function_c()
{
	unsafe
	{
		let large = 0x0000000000000000 as u64; // Don't sleep so we return to function_b, allowing to check the execution flow preservation.
		let large: *mut i64 = std::mem::transmute(&large);
		let alertable = false;
		let ntstatus = unwinder::indirect_syscall!("NtDelayExecution", false, alertable, large);
		println!("ntstatus: {:x}", (ntstatus as usize) as i32); //NTSTATUS is a i32, although that second casting is not really required in this case.
	}
}

Consideraciones

Frame inicial

Si estableces el segundo parámetro en true (en ambas macros), el proceso de spoofing intentará mantener el frame de la dirección de inicio del hilo en la pila de llamadas para aumentar la legitimidad.

Descargar herramienta