[原创] (向量化异常处理)VEH hook
近期在研究VEH HOOK 参考了网上的网上的一些文章 讲解的均不怎么全 也遇到了部分问题
再次向:cvrock kuty bxc致谢。
本文章内容讲的比较细 请耐心观看。
VEH (向量化异常处理)
VEH 的中文名字: 向量化异常处理(Vectored Exception Handling)
1.VEH 最早出现在XP上 因为只有XP及以上Window版本才支持
目前 Windows 平台下实现和使用的异常处理机制主要有 4 种:
筛选器异常处理,结构化异常处理(Structure Exception Handler, SEH),向量化异常处理(Vectored Exception Handler, VEH),C++异常处理(C++ Exception Handler, C++EH)。
其中 前三种为操作系统提供的异常处理机制,最后一种为C++提供的
VEH通过使用 Win32 API 函数 AddVectoredExceptionHandler可注册新的异常处理函数,函数的参数就是指向 EXCEPTION_POINTERS 结构的指针。同时,增加了函数地址的注册处理程序链表。由于系统中使用一个链表来存储矢量异常处理程序,程序可以安装尽可能多的向量处理器,只要有必要。
在用户模式下发生异常时,异常处理分发函数在内部会先调用遍历 VEH 记录链表的函数, 如果没有找到可以处理异常的注册函数,再开始遍历 SEH 注册链表。
二者之间具体联系:VEH优先权高于SHE,只有所有VEH全不处理某个异常的时候,异常处理权才会到达SHE。只要目标程序中没有利用VEH,那么,你所设计的VEH将是第一个得到控制者。现在采用SEH作为异常处理的普通C/C++程序对你将不会再有干扰,可以通过使用VEH来进行hook处理了。
如果存在调试器,那么控制权转向将会发生新的变化。当异常发生后,首先通知的将会是调试器,调试器不处理才会再返回控制权给VEH;如果VEH不处理,再返回给SHE;若SEH不处理,再给调试器一个机会,如果还不处理,则交由系统处理。
应用层异常处理结构图

VEH回调函数详解
VEH由AddVectoredExceptionHandler添加处理函数 处理函数有一个参数 参数类型为PEXCEPTION_POINTERS结构体
结构PEXCEPTION_POINTERS中保存着当前异常的各个寄存器,堆栈,地址,等多种信息 以下是PEXCEPTION_POINTERS的展开图
PEXCEPTION_POINTERS

VEH Hook原理
1.异常处理结构中 VEH是唯一一个可以接受到所有异常信息的处理 换句话说 所有的异常信息 都会经过VEH
2.异常信息 通常是由[数组越界,内存访问出错,无效参数,int 3]等 造成的
3.一旦发生异常 操作系统会立即便利VEH 如果有处理函数 中断线程 并由处理函数处理。
根据以上信息 可以确定一个思路 比如我们hook 消息框API 给API的首地址写入int 3 断点 当执行时会产生异常 线程暂停 转交给异常处理函数处理 此时则可以在处理函数中做手脚 比如修改堆栈参数等。
相应操作完成后 将int3(0xcc)修改回源代码 修改EIP=addr(异常地址)让此处的代码重新执行一次正确的代码,将Eflags寄存器修改为单步调试模式(Eflags |= 0x100) 返回EXCEPTION_CONTINUE_EXECUTION(-1)让程序继续运行 然后又会产生一次断点(单步异常),此时我们将已经执行过的代码重新修改为int3(0xcc) 返回-1让程序继续运行。
以下部分是代码部分 (代码难看 不喜勿喷)
首先 在DLLMain的初次调用中开始HOOK
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
VEH_HOOK_MessageBoxA();
MessageBoxA hook步骤如下
1.添加异常处理函数 并指定参数为第零个 (第一个执行)
2.备份MessageBoxA的第一个字节
3.在MessageBoxA第一个字节写入int3(cc)
void VEH_HOOK_MessageBoxA()
{
AddVectoredExceptionHandler(0,CALL_MessageBoxA);
MessageBoxA_Addr=(PVOID)&MessageBoxA;
MessageBoxA_Ole_INT=*(BYTE*)MessageBoxA_Addr;
VirtualProtect((void*)MessageBoxA_Addr,BREAKPOINTLEN,dwNewProtect,&dwOldProtect);
*(BYTE*)(MessageBoxA_Addr)=0xcc;
VirtualProtect((void*)MessageBoxA_Addr,BREAKPOINTLEN,dwNewProtect,&dwOldProtect);
}
现在来看一下 异常处理函数的初步处理步骤
0.判断是否是int3异常
1.修改EIP为异常发生处地址(再执行一次)
2.判断异常发生地址不等于0(无意义)
3.修改堆栈的参数
4.修改寄存器上下文
5.备份CPU控制寄存器
6.CPU控制寄存器设置为单步模式
7.恢复先前备份的MessageBoxA第一个字节的代码
8.返回-1 让其继续运行
LONG NTAPI CALL_MessageBoxA(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
if(ExceptionInfo->ExceptionRecord->ExceptionCode==EXCEPTION_BREAKPOINT)
{
addr=ExceptionInfo->ExceptionRecord->ExceptionAddress;
if (addr!=(PVOID)0)
{
//修改参数
char *lpText="Hello World!";
*(int*)((ExceptionInfo->ContextRecord->Esp)+0x8)=(int)lpText;
*(int*)((ExceptionInfo->ContextRecord->Esp)+0xc)=(int)lpText;
sprintf(lpbuff,"My::%s=%s",lpText,0);
OutputDebugStringA(lpbuff);
//修改状态
ExceptionInfo->ContextRecord->ContextFlags=CONTEXT_CONTROL;
ExceptionInfo->ContextRecord->Eip = /*(DWORD)&MessageBoxA;*/(DWORD)addr;
EFlags=ExceptionInfo->ContextRecord->EFlags;
ExceptionInfo->ContextRecord->EFlags |= 0x100;
//恢复代码
VirtualProtect((void*)addr,BREAKPOINTLEN,dwNewProtect,&dwOldProtect);
*(BYTE*)(addr) = MessageBoxA_Ole_INT;
VirtualProtect((void*)addr,BREAKPOINTLEN,dwOldProtect,&dwNewProtect);
OutputDebugStringA("My::单步运行,取消断点");
//AddVectoredExceptionHandler(NULL,(PVECTORED_EXCEPTION_HANDLER)ExceptionInfo);
return EXCEPTION_CONTINUE_EXECUTION;
}
}
首先呢 大家注意 我们上面有设置CPU的寄存器为单步模式 也就是说CPU在运行一条汇编指令的时候会再次产生异常 而后借此机会我们将被恢复为正确代码的MessageBoxA重新改为int3 让其下次依然可以被我们拦截处理。
1.判断异常类型是否等于单步类型
2.恢复CPU控制寄存器
3.恢复MessageBoxA的代码
4.返回让其继续运行
else if ( ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP )
{
//还原断点,标志位。
OutputDebugStringA("My::还原断点");
ExceptionInfo->ContextRecord->EFlags =EFlags;
*(BYTE*)(addr) = 0xcc;
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_EXECUTE_HANDLER;
}
以下是源码 与PPT 如果觉得不错 记得给点感谢之类的哦


VEH_Hook.rar VEH Hook ppt.rar
