Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
MOEW — 白皮书 | Kitploit
工具/GitHubGitHub/harryeetsource/moew
防御工具漏洞利用逆向工程调试器恶意软件分析学习与教育二进制利用
GitHubharryeetsource/moew

MOEW

白皮书

查看仓库
1621310个月前尚未审核

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

隐形威胁,看得见的解决方案。

未对齐操作码异常瀑布 (MOEW)

3 阶段 SEH 瀑布 PoC(良性,x86/Wow64)

TL;DR: 此 PoC 演示了一个由故意未对齐执行进入原始字节 blob 而引发的 3 阶段结构化异常处理(SEH)级联,其中 blob 的第一条有效指令是除数为零的故障 div reg。
每个异常阶段安装下一个 SEH 处理程序,触发新的未对齐除零错误,并执行一个良性的可观察有效载荷,然后恢复原始 SEH 链并干净地终止。


1. 概述

MOEW(未对齐操作码异常瀑布)是一个防御性研究示例,展示了在 x86/Wow64 Windows 上受控的异常驱动多阶段执行。它演示了:

  • 通过 fs:[0] 手动操作 SEH 链
  • 故意未对齐到手工制作的字节序列(blob1、blob2、blob3)
  • 阶段链式 SEH 处理程序
  • 通过以下方式的多故障递归:
    • KiUserExceptionDispatcher
    • RtlDispatchException
    • 自定义用户模式处理程序
  • 一个干净终止路径,可恢复原始 SEH 链

所有有效载荷都是良性的:

  • 阶段 1: 启动记事本
  • 阶段 2: 在 %TEMP% 中写入标记文件
  • 最终阶段: 启动计算器

此 PoC 已被有意解除攻击性。不会加密、修改或销毁任何数据。


2. 特性

  • 通过 3 个阶段处理程序完全确定性的 SEH 递归
  • 裸 x86 字节 blob,具有多个有效解码路径
  • 在受控偏移处(ECX/EDX/EBX = 0)的未对齐 div
  • 每个阶段的全局计数器,用于日志记录和跟踪
  • 完全恢复原始 SEH 头(ORIGINAL_SEH)
  • Rust nightly + 内联汇编 + 裸函数
  • 良性但可见的“有效载荷”,用于遥测和调试器测试

3. 环境要求

3.1 架构

  • 仅限 x86(32 位)
  • 使用 MSVC 工具链编译
  • 也可在 64 位 Windows 上的 WoW64 下运行

3.2 Rust Nightly

此 PoC 使用仅 nightly 可用的功能:

#![feature(asm_experimental_arch)]
#![feature(naked_functions)]

安装必要的组件:

rustup toolchain install nightly
rustup target add i686-pc-windows-msvc --toolchain nightly

3.3 禁用 SAFESEH

由于 PoC 安装了不在 SAFESEH 表中的自定义 SEH 处理程序,因此必须指示链接器禁用 SAFESEH 验证。

创建 .cargo/config.toml:

[target.i686-pc-windows-msvc]
rustflags = [
  "-C", "link-arg=/SAFESEH:NO",
]

4. 构建说明

构建二进制文件:

cargo +nightly build --target i686-pc-windows-msvc --release

输出位于:

target\i686-pc-windows-msvc\release\seh_waterfall.exe

5. 运行 PoC

执行:

seh_waterfall.exe

预期的控制流:

阶段 0 → 未对齐 blob1 → 阶段 1 处理程序
阶段 1 → 未对齐 blob2 → 阶段 2 处理程序
阶段 2 → 未对齐 blob3 → 最终处理程序
最终  → 恢复 SEH       → 退出

可见的人工产物:

  • notepad.exe 启动(阶段 1)
  • %TEMP%\moew_stage2.txt 被创建(阶段 2)
  • calc.exe 启动(最终处理程序)

6. 技术分解

6.1 SEH 记录布局

在 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

6.2 全局状态

static STAGE_COUNTER: AtomicU32 = AtomicU32::new(0);
static mut ORIGINAL_SEH: *mut SehRec = std::ptr::null_mut();

用于:

  • 计数处理程序递归深度
  • 在瀑布结束时恢复原始 SEH 头

7. 阶段逻辑

阶段 0 — 初始帧设置

  1. 将 fs:[0] 保存为 ORIGINAL_SEH。
  2. 构建一个指向阶段 1 处理程序的 SEH 记录。
  3. 用这个新记录覆盖 fs:[0]。
  4. 未对齐进入 blob1 + 5,该地址解码为 div ecx(在设置 ECX = 0 之后)。

阶段 1 — 第一个 SEH 处理程序

  1. 启动 notepad.exe。
  2. 为阶段 2 处理程序构建一个 SEH 记录并将其链接在顶部。
  3. 未对齐进入 blob2 + 3 → div edx(EDX = 0)。

阶段 2 — 第二个 SEH 处理程序

  1. 写入 %TEMP%\moew_stage2.txt。
  2. 在 SEH 链上安装最终处理程序。
  3. 未对齐进入 blob3 + 3 → div ebx(EBX = 0)。

最终处理程序 — 终止

  1. 启动 calc.exe。
  2. 将 ORIGINAL_SEH 恢复到 fs:[0]。
  3. 通过 process::exit(0) 干净地退出进程。

8. 未对齐故障 Blob

8.1 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 填充
    }
}
  • 对齐解码:mov eax, 0x10; div ecx; ret
  • 在 +5 处未对齐:div ecx(ECX = 0 → #DE)

8.2 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 填充
    }
}
  • 对齐解码:push ebp; mov ebp, esp; div edx; ret
  • 在 +3 处未对齐:div edx(EDX = 0 → #DE)

8.3 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 填充
    }
}
  • 对齐解码:push ebx; mov ebx, eax; div ebx; ret
  • 在 +3 处未对齐:div ebx(EBX = 0 → #DE)

9. 异常管道

每个故障阶段重新进入 Windows 用户模式异常管道:

KiUserExceptionDispatcher
    → RtlDispatchException
        → SEH 链遍历 (fs:[0])
            → MOEW 处理程序

典型的调试器视图:

seh_waterfall!blobX+偏移
ntdll!KiUserExceptionDispatcher
ntdll!RtlDispatchException
seh_waterfall!stageN_handler

瀑布完全由真实的硬件故障和 SEH 调度驱动;不使用合成或虚假异常。


10. 标记文件输出

阶段 2 写入的文件内容如下:

MOEW 阶段 2 标记
-------------------
该文件由阶段 2 SEH 处理程序写入
作为良性演示有效载荷。

其在 %TEMP% 中的存在作为一个简单、可观察的证据,表明阶段 2 已通过 SEH 链执行。


11. 安全说明

  • PoC 是良性的,仅用于防御性研究。
  • 没有持久化、注册表修改或加密。
  • 所有异常都被捕获和处理。
  • 在终止前恢复原始 SEH 链。

12. 未来增强

潜在的扩展包括:

  • 用于 SEH 瀑布和递归异常模式的 YARA 和行为规则。
  • 针对分阶段硬件故障的 ETW / EDR 信号映射。
  • 随时间演变的 SEH 链的图形化图表。
  • 与真实世界恶意软件异常链的并排比较。

13. 完整源代码

完整的 PoC 实现可在本仓库中找到(参见 src/main.rs)。


14. 许可证

本项目旨在用于防御性研究和教育。 Copyright <2025>

特此免费授予任何获得本软件及相关文档文件(“软件”)副本的人,不受限制地处理本软件的权利,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或出售本软件副本的权利,并允许获得本软件的人这样做,但须满足以下条件:

上述版权声明和本许可声明应包含在本软件的所有副本或重要部分中。

本软件按“原样”提供,不提供任何明示或默示的担保,包括但不限于适销性、特定用途适用性和非侵权性的担保。在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论是合同行为、侵权行为还是其他行为,均由本软件或本软件的使用或其他交易引起或与之相关。


15. 真实样本与 PoC:SEH 破坏、瀑布行为和遥测影响

15.1 真实样本的行为:故意的 SEH 破坏

启发此 PoC 的真实世界 MOEW 样本在执行结束时没有恢复 fs:[0]。
相反,其最终阶段:

  1. 用 NULL 或指向无效内存的指针覆写 SEH 头(fs:[0])。
  2. 触发最后一个未对齐异常,保证硬件故障。
  3. 强制 Windows 遍历一个无效或截断的 SEH 链。
  4. 导致 RtlDispatchException 遇到无效的处理程序指针。
  5. 导致崩溃,其中:
    • EIP/RIP 指向非映像内存(堆、栈或匿名区域)。
    • 故障模块无法解析,显示为 unknown。
    • 故障路径也显示为 unknown。
  6. 生成 Windows 错误报告(WER)签名,这些签名无法归因于任何已加载的模块。
  7. 记录事件查看器 Application Error 事件,带有无意义的故障偏移。
  8. 产生的 EDR 遥测主要由以下内容主导:
    • 重复的 KiUserExceptionDispatcher 调用,
    • 递归的 SEH 转换,
    • 最终异常崩溃,缺少模块归属。

这种破坏性的 SEH 破坏步骤服务于样本的主要反取证目的:
消除因果链并产生无法归因的终端崩溃。


15.2 PoC 的行为:干净恢复 SEH,无终端崩溃

与真实样本不同,此 PoC:

  • 在阶段 0 期间捕获原始 SEH 头:

    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:

  • 不会留下损坏的 SEH 链。
  • 不会产生最终的未处理异常。
  • 不会生成:
    • WER 崩溃报告,
    • 事件查看器 Application Error 1000 条目,
    • “故障模块:未知”签名,
    • 无效的异常处置,
    • 或悬空的 SEH 记录。

然而——关键是——PoC 并没有消除 MOEW 的异常驱动遥测退化行为。


15.3 真实样本和 PoC 共有的遥测退化

PoC 绝对以相同的基本方式降低遥测质量:

  • 它故意触发多个未对齐的硬件故障。

  • 它快速连续产生多个首次机会异常。

  • 它强制 Windows 重复执行:

    KiUserExceptionDispatcher
    RtlDispatchException
        → 自定义处理程序
        → 未对齐 blob
        → 硬件故障
    
  • 它生成非线性、以异常为主的调用栈。

  • 它通过以下方式扭曲调试器和 EDR 中的控制流重建:

    • 递归的 SEH 处理程序,
    • 未对齐的解码路径,
    • 非函数对齐的地址,
    • 部分指令边界。

因此,PoC 忠实地再现了:

  • 递归瀑布模式,
  • 异常驱动的状态机,
  • 未对齐操作码入口行为,
  • 和控制流混淆,

……同时避免了破坏性的最终崩溃。

这使得 PoC 非常适合仪器化和研究,而无需调用完整的反取证有效载荷。


15.4 为什么 PoC 适合防御性研究

由于 PoC 保留了 MOEW 的整体行为 减去 SEH 破坏崩溃,因此:

  • 在实验室环境中可安全重复运行。
  • 具有确定性和稳定性。
  • 适用于:
    • EDR 管道分析,
    • 遥测研究,
    • 事件响应培训,
    • 调试器行为测试,
    • 与真实恶意样本并排比较。

PoC 模拟了异常瀑布(MOEW 的本质),同时移除了最终的破坏性签名。
它证明遥测退化不仅仅源于 SEH 破坏,还源于异常驱动的控制流模型本身。


15.5 摘要

下载工具