使用VEH进行内存对抗
字数 1894 2025-08-09 13:33:40
Windows异常处理机制与VEH内存对抗技术
一、Windows异常处理机制概述
Windows操作系统提供了三种主要的异常处理机制:
- VEH (Vectored Exception Handling) - 向量化异常处理
- SEH (Structured Exception Handling) - 结构化异常处理
- C++ EH (C++ Exception Handling) - C++异常处理
1.1 异常处理流程
Windows异常处理的基本流程如下:
- 首先由CPU捕获异常
- 内核处理部分异常(如页面错误)
- 用户态异常分发流程:
- 首先调用VEH处理程序
- 然后遍历SEH链
- 最后调用未处理异常过滤器
二、向量化异常处理(VEH)
2.1 VEH基本概念
VEH是Windows引入的一种全局异常处理机制,具有以下特点:
- 通过
AddVectoredExceptionHandler函数注册 - 处理程序以链表形式存储
- 执行顺序先于SEH
- 可以处理所有线程的异常
2.2 VEH相关API
// 添加VEH处理程序
PVOID AddVectoredExceptionHandler(
ULONG First, // 0表示添加到链表尾部,1表示添加到头部
PVECTORED_EXCEPTION_HANDLER Handler
);
// 移除VEH处理程序
ULONG RemoveVectoredExceptionHandler(PVOID Handle);
// VEH处理函数原型
LONG CALLBACK VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo);
2.3 VEH处理函数参数
VEH处理函数接收一个EXCEPTION_POINTERS结构指针:
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord; // 异常记录
PCONTEXT ContextRecord; // 线程上下文
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
EXCEPTION_RECORD包含异常代码、地址等信息,CONTEXT结构包含寄存器状态。
三、结构化异常处理(SEH)
3.1 SEH基本概念
SEH是Windows平台特有的异常处理机制:
- 基于栈的异常处理
- 使用
__try、__except、__finally关键字 - 每个线程有自己的SEH链
- 处理顺序后于VEH
3.2 SEH链结构
SEH链是一个链表结构,每个节点包含:
- 处理函数指针
- 指向下一个节点的指针
- 安全cookie等数据
3.3 SEH与编译器
编译器会将__try/__except块转换为SEH结构:
// 源代码
__try {
// 可能引发异常的代码
} __except(filter_expression) {
// 异常处理代码
}
// 编译器生成的伪代码
struct _EXCEPTION_REGISTRATION_RECORD seh;
seh.Handler = exception_handler;
seh.Next = fs:[0]; // 添加到SEH链头部
fs:[0] = &seh;
四、C++异常处理
C++异常处理基于SEH实现,但有自己的特点:
- 使用
try、catch、throw关键字 - 可以处理任意类型的异常对象
- 需要运行时支持(如MSVCRT)
五、VEH在内存对抗中的应用
5.1 内存保护技术
VEH可用于实现以下内存保护技术:
- 内存断点:通过设置页面保护属性,在访问时触发异常
- 代码校验:检查关键代码是否被修改
- 反调试:检测调试器的内存操作
5.2 VEH内存断点实现
实现内存断点的基本步骤:
- 使用
VirtualProtect修改内存页属性为PAGE_GUARD或PAGE_NOACCESS - 注册VEH处理程序捕获访问异常
- 在处理程序中检查访问合法性
- 恢复原始内存属性并继续执行
示例代码片段:
// 设置内存断点
DWORD oldProtect;
VirtualProtect(targetAddr, size, PAGE_NOACCESS | PAGE_GUARD, &oldProtect);
// VEH处理程序
LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
// 检查访问地址是否合法
if (ExceptionInfo->ExceptionRecord->ExceptionInformation[1] == targetAddr) {
// 处理非法访问
// ...
// 恢复执行
ExceptionInfo->ContextRecord->Eip = (DWORD)safe_continue_addr;
return EXCEPTION_CONTINUE_EXECUTION;
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
5.3 反内存补丁技术
利用VEH防止内存被修改:
- 注册VEH处理程序捕获
EXCEPTION_ACCESS_VIOLATION - 在关键代码区域设置写保护
- 当检测到非法写入时,恢复原始数据并记录攻击
5.4 VEH与反调试
VEH可用于检测调试器的常见技术:
- 硬件断点检测:检查
CONTEXT结构中的调试寄存器 - 内存断点检测:检查页面保护属性的异常修改
- 单步执行检测:捕获
EXCEPTION_SINGLE_STEP异常
六、VEH高级应用技巧
6.1 VEH链操作
由于VEH处理程序以链表形式存储,可以:
- 枚举所有已注册的VEH处理程序
- 修改或替换特定处理程序
- 实现VEH处理程序的动态加载/卸载
6.2 VEH与SEH的交互
VEH和SEH可以协同工作:
- VEH处理程序可以返回
EXCEPTION_CONTINUE_SEARCH将异常传递给SEH - 在VEH中修改线程上下文会影响后续SEH处理
- 可以构建多层防御机制
6.3 性能考虑
VEH处理会影响程序性能:
- 频繁的异常触发会导致性能下降
- 应尽量减少VEH处理程序的复杂度
- 考虑使用条件性保护(只在必要时启用)
七、实际案例:使用VEH保护关键函数
以下是一个完整示例,展示如何使用VEH保护关键函数不被挂钩:
#include <windows.h>
#include <stdio.h>
// 要保护的关键函数
void CriticalFunction() {
printf("This is a critical function\n");
}
// VEH处理程序
LONG CALLBACK VectoredHandler(PEXCEPTION_POINTERS ExceptionInfo) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
PVOID faultAddr = (PVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
PVOID funcAddr = (PVOID)CriticalFunction;
// 检查是否是对关键函数的写入操作
if (faultAddr >= funcAddr && faultAddr < (PVOID)((DWORD_PTR)funcAddr + 0x100)) {
printf("Detected attempt to modify critical function!\n");
// 恢复原始页面保护
DWORD oldProtect;
VirtualProtect(funcAddr, 0x100, PAGE_EXECUTE_READ, &oldProtect);
return EXCEPTION_CONTINUE_EXECUTION;
}
}
return EXCEPTION_CONTINUE_SEARCH;
}
int main() {
// 添加VEH处理程序
PVOID hHandler = AddVectoredExceptionHandler(1, VectoredHandler);
// 设置关键函数区域为只读
DWORD oldProtect;
VirtualProtect(CriticalFunction, 0x100, PAGE_EXECUTE_READ, &oldProtect);
// 测试
CriticalFunction();
// 尝试修改关键函数(应该被VEH捕获)
__try {
*(BYTE*)CriticalFunction = 0xC3; // RET指令
} __except(EXCEPTION_EXECUTE_HANDLER) {
printf("Modification attempt blocked\n");
}
// 再次调用关键函数,应该仍然正常工作
CriticalFunction();
// 清理
RemoveVectoredExceptionHandler(hHandler);
return 0;
}
八、总结
VEH是Windows平台强大的异常处理机制,在内存对抗中可以发挥重要作用:
-
防御优势:
- 先于SEH执行,可以提前拦截异常
- 全局作用范围,保护所有线程
- 可以访问完整线程上下文
-
攻击检测:
- 检测内存修改尝试
- 发现调试器活动
- 防止代码注入
-
灵活应用:
- 可以与SEH、C++ EH协同工作
- 支持动态注册和卸载
- 可以构建多层防御体系
通过合理使用VEH,可以显著增强应用程序的安全性,特别是在对抗内存攻击方面。然而,也需要注意性能影响和可能的兼容性问题,在实际应用中需要权衡利弊。