
Кратко: Данный PoC демонстрирует 3‑стадийный каскад обработки структурных исключений (SEH), вызванный намеренным невыровненным исполнением необработанных байтовых последовательностей, первая корректная инструкция которых — вызывающая сбой
div regс нулевым делителем.
Каждая стадия исключения устанавливает следующий SEH-обработчик, инициирует новое невыровненное деление на ноль и выполняет безвредную наблюдаемую нагрузку, после чего восстанавливает исходную SEH-цепочку и корректно завершает работу.
MOEW (Misaligned Opcode Exception Waterfall) — это образец оборонительного исследования, демонстрирующий управляемое многостадийное исполнение на основе исключений в среде x86/Wow64 Windows. Он показывает:
fs:[0]blob1, , )blob2blob3KiUserExceptionDispatcherRtlDispatchExceptionВся нагрузка безвредна:
%TEMP%PoC намеренно «обезврежен». Никакие данные не шифруются, не изменяются и не уничтожаются.
div по управляемым смещениям (ECX/EDX/EBX = 0)ORIGINAL_SEH)PoC использует возможности, доступные только в nightly:
#![feature(asm_experimental_arch)]
#![feature(naked_functions)]
Установите необходимые компоненты:
rustup toolchain install nightly
rustup target add i686-pc-windows-msvc --toolchain nightly
Поскольку PoC устанавливает пользовательские SEH-обработчики, отсутствующие в таблице SAFESEH, необходимо указать линковщику отключить проверку SAFESEH.
Создайте .cargo/config.toml:
[target.i686-pc-windows-msvc]
rustflags = [
"-C", "link-arg=/SAFESEH:NO",
]
Соберите бинарный файл:
cargo +nightly build --target i686-pc-windows-msvc --release
Результат находится по пути:
target\i686-pc-windows-msvc\release\seh_waterfall.exe
Выполните:
seh_waterfall.exe
Ожидаемый порядок управления:
Stage 0 → misaligned blob1 → Stage 1 handler
Stage 1 → misaligned blob2 → Stage 2 handler
Stage 2 → misaligned blob3 → Final handler
Final → restore SEH → exit
Видимые артефакты:
%TEMP%\moew_stage2.txt (Стадия 2)В 32‑битной Windows SEH-записи образуют связный список, хранящийся по адресу fs:[0]:
#[repr(C)]
struct SehRec {
next: *mut SehRec,
handler: usize,
}
Каждый обработчик использует стандартную сигнатуру SEH:
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();
Используется для:
fs:[0] как ORIGINAL_SEH.fs:[0] этой новой записью.blob1 + 5, который декодируется как div ecx (после установки ECX = 0).notepad.exe.blob2 + 3 → div edx (при EDX = 0).%TEMP%\moew_stage2.txt.blob3 + 3 → div ebx (при EBX = 0).calc.exe.ORIGINAL_SEH в 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 (при 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 (при 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 (при EBX = 0 → #DE)Каждая сбойная стадия повторно входит в пользовательский конвейер исключений Windows:
KiUserExceptionDispatcher
→ RtlDispatchException
→ обход SEH-цепочки (fs:[0])
→ обработчик MOEW
Типичный вид в отладчике:
seh_waterfall!blobX+offset
ntdll!KiUserExceptionDispatcher
ntdll!RtlDispatchException
seh_waterfall!stageN_handler
Каскад полностью основан на реальных аппаратных сбоях и диспетчеризации SEH; никакие синтетические или поддельные исключения не используются.
Файл, записываемый на Стадии 2, выглядит так:
MOEW Stage 2 Marker
-------------------
This file was written by the Stage 2 SEH handler
as a benign demonstration payload.
Его наличие в %TEMP% служит простым наблюдаемым доказательством того, что Стадия 2 была выполнена через SEH-цепочку.
Потенциальные расширения включают:
Полная реализация PoC доступна в этом репозитории (см. src/main.rs).
Этот проект предназначен для оборонительных исследований и образования. Copyright <2025>
Настоящим предоставляется разрешение, бесплатно, любому лицу, получившему копию этого программного обеспечения и связанных файлов документации («Программное обеспечение»), на использование Программного обеспечения без ограничений, включая, без ограничений, права использовать, копировать, изменять, объединять, публиковать, распространять, сублицензировать и/или продавать копии Программного обеспечения, а также разрешать лицам, которым предоставлено Программное обеспечение, делать это, при соблюдении следующих условий:
Вышеуказанное уведомление об авторских правах и данное уведомление о разрешении должны быть включены во все копии или существенные части Программного обеспечения.
ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИЯМИ ТОВАРНОЙ ПРИГОДНОСТИ, ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННОЙ ЦЕЛИ И НЕНАРУШЕНИЯ ПРАВ. НИ ПРИ КАКИХ ОБСТОЯТЕЛЬСТВАХ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО ЛЮБЫМ ИСКАМ, УБЫТКАМ ИЛИ ИНОЙ ОТВЕТСТВЕННОСТИ, БУДЬ ТО В ДЕЙСТВИЯХ ДОГОВОРА, ДЕЛИКТА ИЛИ ИНЫМ ОБРАЗОМ, ВОЗНИКАЮЩИМ ИЗ, ВНЕ ИЛИ В СВЯЗИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ ИЛИ ИСПОЛЬЗОВАНИЕМ ИЛИ ИНЫМИ ДЕЙСТВИЯМИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
Реальный образец MOEW, вдохновивший этот PoC, не восстанавливал fs:[0] в конце своего выполнения.
Вместо этого его финальная стадия:
fs:[0]) значением NULL или указателем на недопустимую память.RtlDispatchException сталкивался с некорректным указателем на обработчик.unknown.unknown.Application Error с бессмысленными смещениями сбоя.KiUserExceptionDispatcher,Этот разрушительный шаг повреждения SEH служит главной антикриминалистической цели образца:
стирание причинно-следственной цепочки и создание неатрибутируемого финального краха.
В отличие от реального образца, этот PoC:
Захватывает исходную голову SEH-цепочки на Стадии 0:
asm!("mov {old}, fs:[0]", old = out(reg) old_head);
ORIGINAL_SEH = old_head;
Восстанавливает исходную голову SEH-цепочки в финальном обработчике:
asm!("mov fs:[0], {p}", p = in(reg) ORIGINAL_SEH);
Завершает работу корректно через process::exit(0) вместо инициирования необработанного сбоя.
В результате PoC:
Application Error 1000 в Просмотре событий,Однако, что критически важно, PoC не устраняет вызванную исключениями деградацию телеметрии MOEW.
PoC абсолютно точно деградирует телеметрию тем же фундаментальным образом:
Он намеренно инициирует множественные невыровненные аппаратные сбои.
Он создаёт множественные first-chance исключения в быстрой последовательности.
Он заставляет Windows многократно выполнять:
KiUserExceptionDispatcher
RtlDispatchException
→ custom handler
→ misaligned blob
→ hardware fault
Он генерирует нелинейные, доминируемые исключениями стеки вызовов.
Он искажает реконструкцию потока управления в отладчиках и EDR, направляя исполнение через:
Таким образом, PoC достоверно воспроизводит:
…избегая при этом разрушительного финального краха.
Это делает PoC идеальным для инструментирования и исследований без применения полной антикриминалистической нагрузки.
Поскольку PoC сохраняет общее поведение MOEW за вычетом краха из-за повреждения SEH, он:
PoC моделирует каскад исключений (суть MOEW), убирая при этом финальную разрушительную сигнатуру.
Он демонстрирует, что деградация телеметрии возникает не только из-за повреждения SEH, но и из самой модели потока управления, управляемой исключениями.
| Поведение | Реальный образец MOEW | Реализация PoC |
|---|---|---|
| Каскад невыровненных опкодов | ✔ | ✔ |
| Рекурсивная SEH-управляемая машина состояний | ✔ | ✔ |
| Доминирующие стеки вызовов с диспетчеризацией исключений | ✔ | ✔ |
| Деградация телеметрии / трассировки стека | ✔ | ✔ |
| Намеренное повреждение SEH | ✔ | ❌ |
| Зависшие или недопустимые SEH-указатели | ✔ | ❌ |
| Финальное необработанное исключение | ✔ | ❌ |
| Крах WER «unknown module» | ✔ | ❌ |
Чистое восстановление fs:[0] | ❌ | ✔ |
| Чистое завершение | ❌ | ✔ |
Этот раздел формализует поведенческие различия, сохраняя при этом ясность назначения PoC:
продемонстрировать каскад MOEW в безопасной, пригодной для исследований форме без антикриминалистического финального краха.