
Spoofing de pila de llamadas para Rust
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:
Reconocimientos a los creadores de la técnica SilentMoonWalk:
Y por supuesto, un enorme agradecimiento a namazso por el hilo de Twitter que inspiró todo este proyecto.
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:
call_function!() permite ejecutar cualquier función arbitraria con una pila de llamadas limpia.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.
Esta macro se utiliza para llamar a cualquier función deseada con una pila de llamadas limpia. La macro espera los siguientes parámetros:
usize, isize o un puntero.Esta macro se utiliza para realizar cualquier syscall indirecto deseado con una pila de llamadas limpia. La macro espera los siguientes parámetros:
Para pasar argumentos de diferentes tipos a estas dos macros, se deben tener en cuenta las siguientes consideraciones:
usize (u8-u64, i8-i64, bool, etc.) puede pasarse directamente a las macros.&str y String) deben pasarse como un puntero.ptr::null(), ptr::null_mut(), etc.) se pasan como un 0 (sin importar si es u8, u16, i32 o cualquier otro).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);
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.
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.
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.
}
}
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.