
Whitepaper
TL;DR: Dieser PoC demonstriert eine 3‑stufige Kaskade der strukturierten Ausnahmebehandlung (SEH), die durch absichtliche fehlausgerichtete Ausführung in rohe Byte-Blobs verursacht wird, deren erste gültige Anweisung ein auslösendes
div regmit einem Divisor von Null ist.
Jede Ausnahmestufe installiert den nächsten SEH-Handler, löst eine neue fehlausgerichtete Division‑durch‑Null aus und führt eine harmlose, beobachtbare Nutzlast aus, bevor die ursprüngliche SEH-Kette wiederhergestellt und sauber beendet wird.
MOEW (Misaligned Opcode Exception Waterfall) ist eine defensive Forschungsprobe, die kontrollierte, ausnahmegetriebene mehrstufige Ausführung auf x86/Wow64-Windows demonstriert. Sie zeigt:
fs:[0]blob1, blob2, blob3)KiUserExceptionDispatcherRtlDispatchExceptionAlle Nutzlasten sind harmlos:
%TEMP%Der PoC ist absichtlich entschärft. Es werden keine Daten verschlüsselt, verändert oder zerstört.
div an kontrollierten Offsets (ECX/EDX/EBX = 0)ORIGINAL_SEH)Der PoC verwendet nur in Nightly verfügbare Features:
#![feature(asm_experimental_arch)]
#![feature(naked_functions)]
Installieren Sie die erforderlichen Komponenten:
rustup toolchain install nightly
rustup target add i686-pc-windows-msvc --toolchain nightly
Da der PoC benutzerdefinierte SEH-Handler installiert, die nicht in der SAFESEH-Tabelle enthalten sind, muss der Linker angewiesen werden, die SAFESEH-Validierung zu deaktivieren.
Erstellen Sie .cargo/config.toml:
[target.i686-pc-windows-msvc]
rustflags = [
"-C", "link-arg=/SAFESEH:NO",
]
Binärdatei bauen:
cargo +nightly build --target i686-pc-windows-msvc --release
Die Ausgabe befindet sich unter:
target\i686-pc-windows-msvc\release\seh_waterfall.exe
Ausführen:
seh_waterfall.exe
Erwarteter Kontrollfluss:
Stage 0 → misaligned blob1 → Stage 1 handler
Stage 1 → misaligned blob2 → Stage 2 handler
Stage 2 → misaligned blob3 → Final handler
Final → restore SEH → exit
Sichtbare Artefakte:
%TEMP%\moew_stage2.txt wird erstellt (Stufe 2)Unter 32‑Bit-Windows bilden SEH-Records eine verkettete Liste, die unter fs:[0] gespeichert ist:
#[repr(C)]
struct SehRec {
next: *mut SehRec,
handler: usize,
}
Jeder Handler verwendet die Standard-SEH-Signatur:
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();
Wird verwendet, um:
fs:[0] als ORIGINAL_SEH speichern.fs:[0] mit diesem neuen Record überschreiben.blob1 + 5, das als div ecx dekodiert (nachdem ECX = 0 gesetzt wurde).notepad.exe starten.blob2 + 3 → div edx (mit EDX = 0).%TEMP%\moew_stage2.txt schreiben.blob3 + 3 → div ebx (mit EBX = 0).calc.exe starten.ORIGINAL_SEH in fs:[0] wiederherstellen.process::exit(0) beenden.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 (mit 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 (mit 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 (mit EBX = 0 → #DE)Jede ausgelöste Stufe tritt erneut in die Windows-Benutzermodus-Ausnahme-Pipeline ein:
KiUserExceptionDispatcher
→ RtlDispatchException
→ SEH chain walk (fs:[0])
→ MOEW handler
Typische Debugger-Ansicht:
seh_waterfall!blobX+offset
ntdll!KiUserExceptionDispatcher
ntdll!RtlDispatchException
seh_waterfall!stageN_handler
Der Waterfall wird vollständig durch echte Hardwarefehler und SEH-Dispatch angetrieben; es werden keine synthetischen oder gefälschten Ausnahmen verwendet.
Die in Stufe 2 geschriebene Datei sieht folgendermaßen aus:
MOEW Stage 2 Marker
-------------------
This file was written by the Stage 2 SEH handler
as a benign demonstration payload.
Ihre Präsenz in %TEMP% dient als einfacher, beobachtbarer Beweis, dass Stufe 2 über die SEH-Kette ausgeführt wurde.
Mögliche Erweiterungen umfassen:
Die vollständige PoC-Implementierung ist in diesem Repository verfügbar (siehe src/main.rs).
Dieses Projekt ist für defensive Forschung und Bildung gedacht. Copyright <2025>
Hiermit wird jeder Person, die eine Kopie dieser Software und der zugehörigen Dokumentationsdateien (die „Software”) erhält, kostenlos die Erlaubnis erteilt, uneingeschränkt mit der Software zu handeln, einschließlich und ohne Einschränkung der Rechte zur Nutzung, zum Kopieren, Ändern, Zusammenführen, Veröffentlichen, Verteilen, Unterlizenzieren und/oder Verkaufen von Kopien der Software, und Personen, denen die Software bereitgestellt wird, dies unter den folgenden Bedingungen zu gestatten:
Der obige Urheberrechtshinweis und dieser Genehmigungshinweis müssen in allen Kopien oder wesentlichen Teilen der Software enthalten sein.
DIE SOFTWARE WIRD „OHNE GEWÄHR” BEREITGESTELLT, OHNE JEGLICHE AUSDRÜCKLICHE ODER IMPLIZITE GEWÄHRLEISTUNG, EINSCHLIESSLICH, ABER NICHT BESCHRÄNKT AUF DIE GEWÄHRLEISTUNG DER MARKTGÄNGIGKEIT, DER EIGNUNG FÜR EINEN BESTIMMTEN ZWECK UND DER NICHTVERLETZUNG VON RECHTEN DRITTER. IN KEINEM FALL SIND DIE AUTOREN ODER URHEBERRECHTSINHABER FÜR ANSPRÜCHE, SCHÄDEN ODER ANDERE HAFTUNG VERANTWORTLICH, SEI ES AUS VERTRAG, UNERLAUBTER HANDLUNG ODER ANDERWEITIG, DIE AUS ODER IM ZUSAMMENHANG MIT DER SOFTWARE ODER DER NUTZUNG ODER ANDEREN GESCHÄFTEN MIT DER SOFTWARE ENTSTEHEN.
Das reale MOEW-Sample, das diesen PoC inspirierte, stellte fs:[0] am Ende der Ausführung nicht wieder her.
Stattdessen:
fs:[0]) mit entweder NULL oder einem Zeiger auf ungültigen Speicher.RtlDispatchException auf einen ungültigen Handler-Zeiger stößt.unknown erschien.unknown erschien.Application Error-Ereignisse in der Ereignisanzeige mit bedeutungslosen Fehler-Offsets.KiUserExceptionDispatcher-Aufrufen,Dieser destruktive SEH-Korruptionsschritt dient dem primären Zweck der forensischen Gegenmaßnahmen des Samples: die kausale Kette zu löschen und einen nicht zuordenbaren Endabsturz zu erzeugen.
Anders als das echte Sample:
Erfasst der PoC den ursprünglichen SEH-Kopf während Stufe 0:
asm!("mov {old}, fs:[0]", old = out(reg) old_head);
ORIGINAL_SEH = old_head;
Stellt der PoC den ursprünglichen SEH-Kopf im Endhandler wieder her:
asm!("mov fs:[0], {p}", p = in(reg) ORIGINAL_SEH);
Beendet der PoC sauber über process::exit(0), anstatt einen unbehandelten Fehler auszulösen.
Dadurch:
Application Error 1000-Einträge in der Ereignisanzeige,Entscheidend ist jedoch: Der PoC eliminiert das ausnahmegetriebene Telemetrie-Degradationsverhalten von MOEW nicht.
Der PoC degradiert die Telemetrie in der Tat absolut auf dieselbe grundlegende Weise:
Er löst absichtlich mehrere fehlausgerichtete Hardwarefehler aus.
Er erzeugt mehrere First-Chance-Exceptions in schneller Folge.
Er zwingt Windows, wiederholt auszuführen:
KiUserExceptionDispatcher
RtlDispatchException
→ custom handler
→ misaligned blob
→ hardware fault
Er erzeugt nichtlineare, ausnahmedominierte Aufrufstapel.
Er verzerrt die Kontrollfluss-Rekonstruktion in Debuggern und EDR, indem die Ausführung durch folgendes geleitet wird:
Somit reproduziert der PoC getreu:
…während der destruktive Endabsturz vermieden wird.
Das macht den PoC ideal für Instrumentierung und Forschung, ohne die vollständige Nutzlast der forensischen Gegenmaßnahmen auszulösen.
Da der PoC das Gesamtverhalten von MOEW minus des SEH-Korruptionsabsturzes bewahrt, ist er:
Der PoC modelliert den Exception-Waterfall (das Wesen von MOEW), während die finale destruktive Signatur entfernt wird. Er zeigt, dass Telemetrie-Degradation nicht nur aus SEH-Korruption entsteht, sondern bereits aus dem ausnahmegetriebenen Kontrollflussmodell selbst.
Dieser Abschnitt formalisiert die Verhaltensunterschiede, während der Zweck des PoC klar bleibt: den MOEW-Waterfall in einer sicheren, forschungsfreundlichen Form ohne den anti-forensischen Endabsturz zu demonstrieren.
| Verhalten | Echtes MOEW-Sample | PoC-Implementierung |
|---|
| Fehlausgerichteter Opcode-Waterfall | ✔ | ✔ |
| Rekursive SEH-getriebene Zustandsmaschine | ✔ | ✔ |
| Dominante Ausnahme-Dispatch-Aufrufstapel | ✔ | ✔ |
| Telemetrie-/Stacktrace-Degradation | ✔ | ✔ |
| Absichtliche SEH-Korruption | ✔ | ❌ |
| Verwaiste oder ungültige SEH-Zeiger | ✔ | ❌ |
| Finale unbehandelte Ausnahme | ✔ | ❌ |
| WER-Absturz „unbekanntes Modul” | ✔ | ❌ |
Saubere Wiederherstellung von fs:[0] | ❌ | ✔ |
| Saubere Beendigung | ❌ | ✔ |