VEH介绍
全称 vector exception handle,向量化异常处理;
和SEH类似的东西,SEH存放于线程的栈上;
而VEH存放于进程的堆上,且是以双链表的形式,而SEH是单链表;
异常处理顺序为 : 调试器 -> VEH -> SEH;
添加VEH异常处理可以用如下API:
1 2 3 4
| PVOID AddVectoredExceptionHandler( ULONG First, PVECTORED_EXCEPTION_HANDLER Handler );
|
第一个参数非0则添加到第一个处理,否则添加到末尾;
Handler是函数指针,原型如下:
1 2 3 4
| LONG PvectoredExceptionHandler( [in] _EXCEPTION_POINTERS *ExceptionInfo ) {...}
|
返回值可以是0和-1,返回0代表继续处理,返回-1代表返回原本触发异常处继续执行;
其中参数是一个结构体,结构如下所示:
1 2 3 4
| typedef struct _EXCEPTION_POINTERS { PEXCEPTION_RECORD ExceptionRecord; PCONTEXT ContextRecord; } EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
|
第一个参数记录的是异常信息结构体;
第二个参数保存的是异常发生时,线程处理器状态信息(寄存器环境值);
那么当异常发生时,被VEH捕获后,就可以改动寄存器的值来进行异常处理;
如下例子:
当除零时会发生异常,此时如果添加了VEH处理,可以通过更改环境值进行异常绕过,或者处理;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream> #include <windows.h> using namespace std;
LONG WINAPI PvectoredExceptionHandler( _EXCEPTION_POINTERS* ExceptionInfo ) { cout << "触发VEH异常处理." << endl; ExceptionInfo->ContextRecord->Eip += 4; return -1; }
int main() { AddVectoredExceptionHandler(1, PvectoredExceptionHandler); int a = 0; a /= 0; printf("123\n"); return 0; }
|
结果如下图所示:
hook原理
因为VEH处理函数可以拿寄存器,也就可以拿目标api的输入输出,只需要在目标api内触发一个异常,就可以使用ebp以及其他寄存器拿到其输入参数以及修改输出内容;
SEH HOOK原理也如此,它们的核心思想是利用了异常处理的框架,不用自己去构造;
触发断点应选择硬件断点,避免修改代码int3绕过大量检测;
缺陷是只能hook4个地址,因为硬件断点就这么多;
前置知识:
调试寄存器
DR0 ~ DR7
DR0 ~ DR3存放的是硬件断点的断点地址;
DR6存放的是异常信息;
DR7则是控制作用;
其中DR7里, L0-L3对应DR0-DR3的断点是否有效,局部断点;
G0-G3同上,全局断点(Windows没用);
LEN0 - LEN3 对应DR0 - DR3的断点长度,不同类型断点,长度不同,比如执行断点长度为1;
00对应1,01对应2,11对应4;
RW0 - RW3 对应断点类型,00对应执行断点,01对应写入断点,11对应读写断点;
要下断点那么就需要修改调试寄存器,如何修改呢?
1 2 3 4
| BOOL SetThreadContext( [in] HANDLE hThread, [in] const CONTEXT *lpContext );
|
用以上函数设置,自定义context结构和数值,第一个参数用GetCurrentThread来获取句柄;
设置context结构的时候要注意它有一个字段为 contextFlags,标识context哪些属性有效;
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| #include <iostream> #include <windows.h> using namespace std;
HANDLE main_thread = 0;
LONG WINAPI PvectoredExceptionHandler( _EXCEPTION_POINTERS* ExceptionInfo ) { cout << "触发VEH异常处理." << endl; if (ExceptionInfo->ExceptionRecord->ExceptionAddress == MessageBoxA) { cout << "执行hook." << endl; DWORD arg1Addr = ExceptionInfo->ContextRecord->Esp + 4; DWORD arg2Addr = ExceptionInfo->ContextRecord->Esp + 8; DWORD arg3Addr = ExceptionInfo->ContextRecord->Esp + 12; DWORD arg4Addr = ExceptionInfo->ContextRecord->Esp + 16; CONTEXT context = { 0 }; CONTEXT oldcontext = { 0 }; context.ContextFlags = CONTEXT_DEBUG_REGISTERS; SetThreadContext(main_thread, &context); int result = MessageBoxA(*(HWND*)arg1Addr, *(LPCSTR*)arg2Addr, *(LPCSTR*)arg3Addr, *(UINT*)arg4Addr); ExceptionInfo->ContextRecord->Eax = result; LPCSTR re = "你是黑矮星."; *(LPCSTR*)arg2Addr = re; result = MessageBoxA(*(HWND*)arg1Addr, *(LPCSTR*)arg2Addr, *(LPCSTR*)arg3Addr, *(UINT*)arg4Addr); ExceptionInfo->ContextRecord->Eax = result;
ExceptionInfo->ContextRecord->Eip += 70; return -1; }
return 0; }
int main() { AddVectoredExceptionHandler(1, PvectoredExceptionHandler);
DWORD breakPoint0 = 0; HMODULE user32 = LoadLibraryA("user32.dll"); breakPoint0 = (DWORD)GetProcAddress(user32, "MessageBoxA");
CONTEXT context = { 0 }; context.ContextFlags = CONTEXT_DEBUG_REGISTERS; context.Dr7 = 1; context.Dr0 = breakPoint0; main_thread = GetCurrentThread(); SetThreadContext(main_thread, &context);
if (MessageBoxA(0, "我是谁?", 0, 0)) cout << "成功执行..." << endl;
return 0; }
|
以上代码hook了messageBoxA这个函数,hook的时候执行了两次,一次原函数,一次修改输出后的函数,可以正确返回;