
TL;DR: Este PoC demuestra una cascada de Manejo Estructurado de Excepciones (SEH) de 3 etapas causada por ejecución desalineada deliberada dentro de blobs de bytes crudos cuya primera instrucción válida es un
div regque provoca una falta con divisor cero. Cada etapa de excepción instala el siguiente manejador SEH, provoca una nueva división por cero desalineada y ejecuta una carga útil benigna observable antes de restaurar la cadena SEH original y terminar limpiamente.
MOEW (Cascada de Excepciones por Opcode Desalineado, por sus siglas en inglés Misaligned Opcode Exception Waterfall) es una muestra de investigación defensiva que demuestra ejecución multietapa controlada y dirigida por excepciones en Windows x86/Wow64. Demuestra:
fs:[0]blob1, blob2, blob3)KiUserExceptionDispatcherRtlDispatchExceptionTodas las cargas útiles son benignas:
%TEMP%El PoC está intencionalmente desactivado. No se cifra, modifica ni destruye ningún dato.
div desalineado en offsets controlados (ECX/EDX/EBX = 0)ORIGINAL_SEH)El PoC utiliza características exclusivas de nightly:
#![feature(asm_experimental_arch)]
#![feature(naked_functions)]
Instala los componentes necesarios:
rustup toolchain install nightly
rustup target add i686-pc-windows-msvc --toolchain nightly
Dado que el PoC instala manejadores SEH personalizados que no están presentes en la tabla SAFESEH, es necesario indicar al enlazador que deshabilite la validación SAFESEH.
Crea .cargo/config.toml:
[target.i686-pc-windows-msvc]
rustflags = [
"-C", "link-arg=/SAFESEH:NO",
]
Compila el binario:
cargo +nightly build --target i686-pc-windows-msvc --release
La salida se encuentra en:
target\i686-pc-windows-msvc\release\seh_waterfall.exe
Ejecuta:
seh_waterfall.exe
Flujo de control esperado:
Stage 0 → misaligned blob1 → Stage 1 handler
Stage 1 → misaligned blob2 → Stage 2 handler
Stage 2 → misaligned blob3 → Final handler
Final → restore SEH → exit
Artefactos visibles:
%TEMP%\moew_stage2.txt (Etapa 2)En Windows de 32 bits, los registros SEH forman una lista enlazada almacenada en fs:[0]:
#[repr(C)]
struct SehRec {
next: *mut SehRec,
handler: usize,
}
Cada manejador utiliza la firma SEH estándar:
extern "system" fn handler(
record: *mut u8,
frame: *mut u8,
context: *mut u8,
dispatcher: *mut u8,
) -> i32
static STAGE_COUNTER: AtomicU32 = AtomicU32::new(0);
static mut ORIGINAL_SEH: *mut SehRec = std::ptr::null_mut();
Se utiliza para:
fs:[0] como ORIGINAL_SEH.fs:[0] con este nuevo registro.blob1 + 5, que se decodifica como div ecx (después de establecer ECX = 0).notepad.exe.blob2 + 3 → div edx (con EDX = 0).%TEMP%\moew_stage2.txt.blob3 + 3 → div ebx (con EBX = 0).calc.exe.ORIGINAL_SEH en fs:[0].process::exit(0).blob1#[unsafe(naked)]
pub extern "C" fn blob1() {
naked_asm! {
".byte 0xB8, 0x10, 0x00, 0x00, 0x00", // mov eax, 0x10
".byte 0xF7, 0xF1", // div ecx
".byte 0xC3", // ret
".byte 0x90, 0x90, 0x90", // nop padding
}
}
mov eax, 0x10; div ecx; ret+5: div ecx (con ECX = 0 → #DE)blob2#[unsafe(naked)]
pub extern "C" fn blob2() {
naked_asm! {
".byte 0x55", // push ebp
".byte 0x8B, 0xEC", // mov ebp, esp
".byte 0xF7, 0xF2", // div edx
".byte 0xC3", // ret
".byte 0x90, 0x90, 0x90", // nop padding
}
}
push ebp; mov ebp, esp; div edx; ret+3: div edx (con EDX = 0 → #DE)blob3#[unsafe(naked)]
pub extern "C" fn blob3() {
naked_asm! {
".byte 0x53", // push ebx
".byte 0x8B, 0xD8", // mov ebx, eax
".byte 0xF7, 0xF3", // div ebx
".byte 0xC3", // ret
".byte 0x90, 0x90, 0x90", // nop padding
}
}
push ebx; mov ebx, eax; div ebx; ret+3: div ebx (con EBX = 0 → #DE)Cada etapa con fallo vuelve a entrar en la canalización de excepciones en modo usuario de Windows:
KiUserExceptionDispatcher
→ RtlDispatchException
→ SEH chain walk (fs:[0])
→ MOEW handler
Vista típica en el depurador:
seh_waterfall!blobX+offset
ntdll!KiUserExceptionDispatcher
ntdll!RtlDispatchException
seh_waterfall!stageN_handler
La cascada está impulsada por completo por fallos de hardware reales y despacho de SEH; no se utilizan excepciones sintéticas ni falsas.
El archivo escrito en la Etapa 2 tiene este aspecto:
MOEW Stage 2 Marker
-------------------
This file was written by the Stage 2 SEH handler
as a benign demonstration payload.
Su presencia en %TEMP% sirve como prueba simple y observable de que la Etapa 2 se ejecutó a través de la cadena SEH.
Las posibles extensiones incluyen: