
Livre blanc
TL;DR : Ce PoC démontre une cascade de gestion structurée des exceptions (SEH) en 3 étapes provoquée par une exécution volontairement désalignée dans des blobs d'octets bruts dont la première instruction valide est un
div regfautif avec un diviseur à zéro.
Chaque étape d'exception installe le gestionnaire SEH suivant, déclenche une nouvelle division par zéro désalignée et exécute une charge utile bénigne observable avant de restaurer la chaîne SEH d'origine et de se terminer proprement.
MOEW (Misaligned Opcode Exception Waterfall) est un échantillon de recherche défensive qui illustre une exécution multi-étapes contrôlée par exceptions sur Windows x86/WoW64. Il démontre :
fs:[0]blob1, blob2, blob3)KiUserExceptionDispatcherRtlDispatchExceptionToutes les charges utiles sont bénignes :
%TEMP%Le PoC est volontairement désamorcé. Aucune donnée n'est chiffrée, modifiée ou détruite.
div désaligné à des offsets contrôlés (ECX/EDX/EBX = 0)ORIGINAL_SEH)Le PoC utilise des fonctionnalités exclusives à nightly :
#![feature(asm_experimental_arch)]
#![feature(naked_functions)]
Installez les composants nécessaires :
rustup toolchain install nightly
rustup target add i686-pc-windows-msvc --toolchain nightly
Étant donné que le PoC installe des gestionnaires SEH personnalisés qui ne sont pas présents dans la table SAFESEH, l'éditeur de liens doit être configuré pour désactiver la validation SAFESEH.
Créez .cargo/config.toml :
[target.i686-pc-windows-msvc]
rustflags = [
"-C", "link-arg=/SAFESEH:NO",
]
Compilez le binaire :
cargo +nightly build --target i686-pc-windows-msvc --release
Le résultat se trouve à :
target\i686-pc-windows-msvc\release\seh_waterfall.exe
Exécutez :
seh_waterfall.exe
Flux de contrôle attendu :
Stage 0 → misaligned blob1 → Stage 1 handler
Stage 1 → misaligned blob2 → Stage 2 handler
Stage 2 → misaligned blob3 → Final handler
Final → restore SEH → exit
Artéfacts visibles :
%TEMP%\moew_stage2.txt est créé (étape 2)Sur Windows 32 bits, les enregistrements SEH forment une liste chaînée stockée à fs:[0] :
#[repr(C)]
struct SehRec {
next: *mut SehRec,
handler: usize,
}
Chaque gestionnaire utilise la signature SEH standard :
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();
Utilisé pour :
fs:[0] dans ORIGINAL_SEH.fs:[0] par ce nouvel enregistrement.blob1 + 5, qui se décode comme div ecx (après avoir défini ECX = 0).notepad.exe.blob2 + 3 → div edx (avec EDX = 0).%TEMP%\moew_stage2.txt.blob3 + 3 → div ebx (avec EBX = 0).calc.exe.ORIGINAL_SEH dans 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 (avec 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 (avec 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 (avec EBX = 0 → #DE)Chaque étape fautive rentre à nouveau dans le pipeline d'exceptions en mode utilisateur de Windows :
KiUserExceptionDispatcher
→ RtlDispatchException
→ SEH chain walk (fs:[0])
→ MOEW handler
Vue typique du débogueur :
seh_waterfall!blobX+offset
ntdll!KiUserExceptionDispatcher
ntdll!RtlDispatchException
seh_waterfall!stageN_handler
La cascade est entièrement pilotée par de véritables défauts matériels et la distribution SEH ; aucune exception synthétique ou factice n'est utilisée.
Le fichier écrit à l'étape 2 ressemble à :
MOEW Stage 2 Marker
-------------------
This file was written by the Stage 2 SEH handler
as a benign demonstration payload.
Sa présence dans %TEMP% sert de preuve simple et observable que l'étape 2 s'est exécutée via la chaîne SEH.