Loading... 向量化异常处理(Vectored Exception Handler, VEH) 结构化异常处理(Structure Exception Handler, SEH) ## VEH 在用户模式下发生异常时,异常处理分发函数在内部会先调用遍历 VEH 记录链表的函数, 如果没有找到可以处理异常的注册函数,再开始遍历 SEH 注册链表。 二者之间具体联系:VEH优先权高于SHE,只有所有VEH全不处理某个异常的时候,异常处理权才会到达SHE。只要目标程序中没有利用VEH,那么,你所设计的VEH将是第一个得到控制者。现在采用SEH作为异常处理的普通C/C++程序对你将不会再有干扰,可以通过使用VEH来进行hook处理了。 如果存在调试器,那么控制权转向将会发生新的变化。当异常发生后,首先通知的将会是调试器,调试器不处理才会再返回控制权给VEH;如果VEH不处理,再返回给SHE;若SEH不处理,再给调试器一个机会,如果还不处理,则交由系统处理。 VEH通过使用 Win32 API 函数 AddVectoredExceptionHandler可注册新的异常处理函数,函数的参数就是指向 EXCEPTION_POINTERS 结构的指针。同时,增加了函数地址的注册处理程序链表。由于系统中使用一个链表来存储矢量异常处理程序,程序可以安装尽可能多的向量处理器,只要有必要。 异常处理步骤: ``` 0.判断是否是int3异常 1.修改EIP为异常发生处地址(再执行一次) 2.判断异常发生地址不等于0(无意义) 3.修改堆栈的参数 4.修改寄存器上下文 5.备份CPU控制寄存器 6.CPU控制寄存器设置为单步模式 7.恢复先前备份的MessageBoxA第一个字节的代码 8.返回-1 让其继续运行 ``` 首先准备一个VectoredHandler函数: `LONG CALLBACK VectoredHandler(__in PEXCEPTION_POINTERS ExceptionInfo);` ExceptionInfo的结构: ``` typedef struct _EXCEPTION_POINTERS { PEXCEPTION_RECORD ExceptionRecord; //记录了本次异常的相关信息 PCONTEXT ContextRecord; //记录了异常发生时的线程上下文 会保存寄存器等,主要修改的对象 } EXCEPTION_POINTERS, *PEXCEPTION_POINTERS; typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; //异常代码,表明了异常的相关信息 DWORD ExceptionFlags; struct _EXCEPTION_RECORD *ExceptionRecord; PVOID ExceptionAddress; //发生异常时当前的eip DWORD NumberParameters; ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; } EXCEPTION_RECORD; ``` 然后实现VectoredHandler函数。 最后安装VectoredHandler函数。 ``` 0012FF40 004010C4 call到MessageBoxA的地址,VEHHook.004010BE 0012FF44 00000000 hOwner = NULL 0012FF48 00407050 TEXT = "VEH Hook Test" 0012FF4C 00407104 Title = "Test" 0012FF50 00000000 Style = MB_OK|MB_APPLMODAL ``` nt 3中断修改完数据后,会再次回到文件头进行执行,会再次执行int 3, 所以要还原MessageBoxA函数的第一个字节,如果不进行还原程序会发生死循环一直调用VectoredHandler函数,或者添加一个TrampoLine。 如果进行还原势必导致下次调用被Hook函数时,不会进行Hook,需要再次安装,解决: 1)修改Eip(Rip)使其去调用一个TrampoLine函数,使程序继续运行。(相当于InlineHook) 2)再程序修改完Hook函数后,添加一个单步中断,然后将被Hook函数我们修改的指令还原回去,此时,函数会执行正确的指令,之后触发单步中断,再次进去我们的VectoredHandler函数,在VectoredHandler函数中通过判断单步中断再次将被Hook函数的第一个字节改为int 3。 ## 通过TrampoLine函数进行的Hook ``` #include "stdafx.h" #include <Windows.h> #include <string> using namespace std; PVOID g_AddrofMessageBoxA; //MessageBoxA函数的地址 BYTE AddrMessageBoxA_Old; //保存之前的值 ULONG g_jmpBackAddr = 0; _declspec(naked) int WINAPI OriginalMessageBox() { _asm { //写入的int 3指令破坏了原来的指令,因此在这里执行原函数的指令 mov edi, edi jmp g_jmpBackAddr //跳回原函数被Hook指令之后的位置,绕过自己安装的Hook } } //安装Hook--int 3 void HookMessageBoxA() { PBYTE AddrMessageBoxA = (PBYTE)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA"); g_AddrofMessageBoxA = AddrMessageBoxA; //保存之前的值 AddrMessageBoxA_Old = (BYTE)*AddrMessageBoxA; g_jmpBackAddr = (ULONG)g_AddrofMessageBoxA + 2; //之前指令2个字节 //向原函数添加跳转到DetourFun的jmp BYTE newEntry[1] = { 0 }; newEntry[0] = 0xcc; //int 3 的机器码 ,此处MessageBox第一行指令占2个字节最后加个nop //修改MessageBoxA函数开头,写入我们的 int 3 DWORD dwOldProtect; MEMORY_BASIC_INFORMATION mbi = { 0 }; VirtualQuery(AddrMessageBoxA, &mbi, sizeof(MEMORY_BASIC_INFORMATION)); VirtualProtect(mbi.BaseAddress, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect); memcpy(AddrMessageBoxA, newEntry, 1); //写入int 3指令的机器码 VirtualProtect(mbi.BaseAddress, 5, dwOldProtect, &dwOldProtect); /* VirtualProtect((void*)AddrMessageBoxA, MAX_PATH,dwOldProtect,&dwOldProtect); *(BYTE*)(AddrMessageBoxA)=0xcc; VirtualProtect((void*)AddrMessageBoxA, MAX_PATH,dwOldProtect,&dwOldProtect); */ } //要实现的这个VectoredHandler函数,在捕获异常后,会通过异常代码和发生异常的地址来 //判断这里是不是预先埋伏的断点 LONG WINAPI VectoredHandlerNew(struct _EXCEPTION_POINTERS *ExceptionInfo) { char szNewMsg[1024] = { 0 }; LONG lResult = EXCEPTION_CONTINUE_SEARCH; //在不处理该异常时默认的返回值 继续执行 PEXCEPTION_RECORD pPexceptionRecord = ExceptionInfo->ExceptionRecord; PCONTEXT pContextRecord = ExceptionInfo->ContextRecord; int ret = 0; ULONG_PTR* uESP = 0; PVOID g_Trampoline; DWORD EFlags = 0; g_Trampoline = pPexceptionRecord->ExceptionAddress; printf("进入VectoredHandlerNew函数\n"); printf("Exception Address = %p\n", pPexceptionRecord->ExceptionAddress); //判断异常的类型和异常发生时的Eip if (pPexceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT && pPexceptionRecord->ExceptionAddress == g_AddrofMessageBoxA) { printf("int 3 BreakPoint Hited.\n"); //中断命中 #ifdef _WIN64 //在x64下函数调用的前4个参数总是放在寄存器中传递,剩余的参数则压入堆栈中。 //在x64中,前4个参数依次RCX、RDX、R8、R9 ,按照参数表声明的顺序,从左向右,前4个参数依次放入RCX,RDX,R8,R9中。 printf("lpText = 0x%p %s\n", pContextRecord->Rdx, (char*) pContextRecord->Rdx); //Rip x64体系 pContextRecord->Rdx = (ULONG_PTR)szNewMsg; pContextRecord->Rip = (ULONG_PTR)g_Trampoline; //跳转到Trampoline继续执行 #else //86体系,会将函数参数放入栈中 //修改参数 printf("ESP = 0x%p\n", pContextRecord->Esp); uESP = (ULONG_PTR*) pContextRecord->Esp; lstrcpyA(szNewMsg, (LPSTR)uESP[2]); //移动2个字节是函数第二个参数 lstrcatA(szNewMsg, "\n\n Hacked by VectoredHandler."); uESP[2] = (ULONG_PTR)szNewMsg; pContextRecord->Eip = (ULONG_PTR)OriginalMessageBox; #endif lResult = EXCEPTION_CONTINUE_EXECUTION; } return lResult; } //安装Hook void InstallHook() { AddVectoredExceptionHandler(0, VectoredHandlerNew); HookMessageBoxA(); } int _tmain(int argc, _TCHAR* argv[]) { MessageBoxA(NULL, "zhongchang", "Test", MB_OK); InstallHook(); MessageBoxA(NULL, "zhongchang", "Test", MB_OK); MessageBoxA(NULL, "zhongchang22222", "Test", MB_OK); return 0; } ``` ## 拦截调用(调用不下发,直接返回调用者) 设置Eip为返回地址。 注意:此时栈中还有这次调用压入的参数和返回地址,因此必须把栈还原到调用前的状态,也就是真正的MessageBoxA函数完成后的清栈和返回两个动作。 ``` uESP = (ULONG*) pContextRecord->Esp; PContextRecord->Eip = uESP[0]; //此时栈顶为返回地址,将其设置为新的Eip pContexeRecord->Esp += (4 + 1)*sizeof(ULONG_PTR);//清理压入栈的4个参数和1个返回地址 lResult = EXCEPTION_CONTINUE_EXECUTION; //返回值一定是“继续执行” ``` 注意:尽管该方法在x64下也可以正常工作,但因为在x64下的调用约定不同,所以参数不在栈里(取参数和恢复栈时都要注意这一点)。 ## Hook Sleep 运行 CS中的shellcode ```cpp // BypassDiag.cpp : 定义控制台应用程序的入口点。 // #include <Windows.h> #include <string> #include <thread> //获取Sleep函数地址 DWORD64 lpSleepAdr = (DWORD64)&Sleep - 1;// 函数地址 减去1 就是覆盖EB DWORD OldProtect = 0; BYTE Int3[] = { 0xcc,0xcc,0xcc,0xcc }; BYTE Old_Code[4] = { 0x0 }; void DIYFUNC(); void* Buf16ToMem16(std::string sBuf, _Outptr_ DWORD &BufSize); std::string hexdump(void* addr, size_t length); DWORD TOOL_Buffer_to_File(char* Buffer, DWORD dwSize, WCHAR *FilePath); //加密后的shellcode char X64ShellCode[] = { 0xED,0x48,0x83,0xE4,0x00 }; DWORD DecodeKey[4][4] = { 0x0 }; class LoadShell { public: LoadShell(); ~LoadShell(); public: void* Buf16ToMem16(std::string sBuf, _Outptr_ DWORD &BufSize); }; long __stdcall callback(_EXCEPTION_POINTERS* excp) { //备份当前寄存器 CONTEXT _TmpContext{ 0 }; memcpy(&_TmpContext, excp->ContextRecord,sizeof(CONTEXT)); PVOID g_Trampoline; g_Trampoline = excp->ExceptionRecord->ExceptionAddress; //DWORD64 _Rip = (DWORD64)excp->ContextRecord->Rip; //char ss[0x100]{ 0 }; //sprintf_s(ss,0x100,"Rip = %x---Chage = %x", _Rip, lpSleepAdr); //MessageBoxA(NULL, ss, ss, NULL); //解密 DWORD h1 = 0; for (int i = 0; i < sizeof(X64ShellCode); ++i) { h1 = i % 3; X64ShellCode[i] = X64ShellCode[i] ^ DecodeKey[h1][0]; X64ShellCode[i + 1] = X64ShellCode[i + 1] ^ DecodeKey[h1][1]; X64ShellCode[i + 2] = X64ShellCode[i + 2] ^ DecodeKey[h1][2]; i += 3; } //Sleep 修改回去 VirtualProtect((void*)lpSleepAdr, 0x4, PAGE_EXECUTE_READWRITE, &OldProtect); //设置属性 memcpy((void*)lpSleepAdr, Old_Code, sizeof(Old_Code)); //恢复属性 VirtualProtect((void*)lpSleepAdr, 0x4, OldProtect, NULL); DWORD oodd = 0; ////修改函数内容 如果函数被优化这一段将会出错 并引发异常导致一直在异常中 VirtualProtect((void*)&DIYFUNC, 0x1000, PAGE_EXECUTE_READWRITE, &oodd); memcpy(&DIYFUNC, X64ShellCode, sizeof(X64ShellCode)); VirtualProtect((void*)&DIYFUNC, 0x1000, oodd, NULL); //void* Memory = VirtualAlloc(NULL, sizeof(X64ShellCode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); // //for (int i = 0; i < sizeof(X64ShellCode); ++i) //{ // ((char*)Memory)[i] = X64ShellCode[i]; // //} //((void(*)())Memory)(); //--------------------------------------------------------------- 重新设置RIP excp->ContextRecord->Rcx = _TmpContext.Rcx; excp->ContextRecord->Rdx = _TmpContext.Rdx; //excp->ContextRecord->Rip--; excp->ContextRecord->Rip = (ULONG_PTR)g_Trampoline; //SetThreadContext(GetCurrentThread(), excp->ContextRecord); //继续运行 return EXCEPTION_CONTINUE_EXECUTION; } int main(int arg, char** argv) { char Key1[4] = { 0x11,0x22,0x33 }; char Key2[4] = { 0x1,0x3,0x4,0x0 }; char Key3[4] = { 0x4,0x5,0x5,0x0 }; if (arg <= 1) { memcpy_s(DecodeKey + 0, 0x4, Key1, 3); memcpy_s(DecodeKey + 1, 0x4, Key2, 3); memcpy_s(DecodeKey + 2, 0x4, Key3, 3); //隐藏顶层窗口 //HWND hwnd = GetForegroundWindow(); //ShowWindow(hwnd, SW_HIDE); } else if (arg == 2) { memcpy_s(DecodeKey + 0, 0x3, Key1, 3); memcpy_s(DecodeKey + 1, 0x3, Key1, 3); memcpy_s(DecodeKey + 2, 0x3, Key1, 3); } else if (arg == 3) { memcpy_s(DecodeKey + 0, 0x3, Key2, 3); memcpy_s(DecodeKey + 1, 0x3, Key2, 3); memcpy_s(DecodeKey + 2, 0x3, Key2, 3); } //设置异常函数 AddVectoredExceptionHandler(true, callback); //获取Sleep函数地址 //void* lpSleepAdr = &Sleep; HMODULE hKer = LoadLibrary(L"Kernel32.dll"); lpSleepAdr = (DWORD64)GetProcAddress(hKer, "Sleep"); DWORD OldProtect = 0; char Int3[] = { 0xcc,0xcc,0xcc,0xcc }; //改一下属性 , 可以直接改属性,但是触发属性的地址有点多就先用int3 VirtualProtect((void*)lpSleepAdr, 0x4, PAGE_EXECUTE_READWRITE, &OldProtect); //备份原本命令 memcpy_s(Old_Code, sizeof(Old_Code), (void*)lpSleepAdr, 4); //设置属性 memcpy((void*)lpSleepAdr, Int3, sizeof(Int3)); //恢复属性 VirtualProtect((void*)lpSleepAdr, 0x4, OldProtect, NULL); Sleep(10); MessageBox(NULL, NULL, NULL, NULL); std::chrono::seconds t = std::chrono::seconds(10); std::this_thread::sleep_for(t); DIYFUNC(); return 0; } void DIYFUNC() { float jdxezfwo1fd = 3595785; if (jdxezfwo1fd = 17104435)jdxezfwo1fd = 7179609; if (jdxezfwo1fd = 6727346)jdxezfwo1fd = 5186313; if (jdxezfwo1fd = 2642530)jdxezfwo1fd = 6098365; if (jdxezfwo1fd = 100804505)jdxezfwo1fd = 18155918; if (jdxezfwo1fd = 13639432)jdxezfwo1fd = 1309472; if (jdxezfwo1fd = 20163483)jdxezfwo1fd = 72897350; if (jdxezfwo1fd = 4537353)jdxezfwo1fd = 17846170; if (jdxezfwo1fd = 3012330)jdxezfwo1fd = 17363813; if (jdxezfwo1fd = 7726324)jdxezfwo1fd = 17223047; if (jdxezfwo1fd = 21198746)jdxezfwo1fd = 8367300; if (jdxezfwo1fd = 17805906)jdxezfwo1fd = 19763965; if (jdxezfwo1fd = 71442650)jdxezfwo1fd = 3389300; if (jdxezfwo1fd = 72795400)jdxezfwo1fd = 20021901; if (jdxezfwo1fd = 1669214)jdxezfwo1fd = 3343457; if (jdxezfwo1fd = 11587996)jdxezfwo1fd = 28890194; float jdxezfwo1fd1 = 3595785; if (jdxezfwo1fd1 = 17104435)jdxezfwo1fd1 = 7179609; if (jdxezfwo1fd1 = 6727346)jdxezfwo1fd1 = 5186313; if (jdxezfwo1fd1 = 2642530)jdxezfwo1fd1 = 6098365; if (jdxezfwo1fd1 = 100804505)jdxezfwo1fd1 = 18155918; if (jdxezfwo1fd1 = 13639432)jdxezfwo1fd1 = 1309472; if (jdxezfwo1fd1 = 20163483)jdxezfwo1fd1 = 72897350; if (jdxezfwo1fd1 = 4537353)jdxezfwo1fd1 = 17846170; if (jdxezfwo1fd1 = 3012330)jdxezfwo1fd1 = 17363813; if (jdxezfwo1fd1 = 7726324)jdxezfwo1fd1 = 17223047; if (jdxezfwo1fd1 = 21198746)jdxezfwo1fd1 = 8367300; if (jdxezfwo1fd1 = 17805906)jdxezfwo1fd1 = 19763965; if (jdxezfwo1fd1 = 71442650)jdxezfwo1fd1 = 3389300; if (jdxezfwo1fd1 = 72795400)jdxezfwo1fd1 = 20021901; if (jdxezfwo1fd1 = 1669214)jdxezfwo1fd1 = 3343457; if (jdxezfwo1fd1 = 11587996)jdxezfwo1fd1 = 28890194; float jdxezfwo1fd2 = 3595785; if (jdxezfwo1fd2 = 17104435)jdxezfwo1fd2 = 7179609; if (jdxezfwo1fd2 = 6727346)jdxezfwo1fd2 = 5186313; if (jdxezfwo1fd2 = 2642530)jdxezfwo1fd2 = 6098365; if (jdxezfwo1fd2 = 100804505)jdxezfwo1fd2 = 18155918; if (jdxezfwo1fd2 = 13639432)jdxezfwo1fd2 = 1309472; if (jdxezfwo1fd2 = 20163483)jdxezfwo1fd2 = 72897350; if (jdxezfwo1fd2 = 4537353)jdxezfwo1fd2 = 17846170; if (jdxezfwo1fd2 = 3012330)jdxezfwo1fd2 = 17363813; if (jdxezfwo1fd2 = 7726324)jdxezfwo1fd2 = 17223047; if (jdxezfwo1fd2 = 21198746)jdxezfwo1fd2 = 8367300; if (jdxezfwo1fd2 = 17805906)jdxezfwo1fd2 = 19763965; if (jdxezfwo1fd2 = 71442650)jdxezfwo1fd2 = 3389300; if (jdxezfwo1fd2 = 72795400)jdxezfwo1fd2 = 20021901; if (jdxezfwo1fd2 = 1669214)jdxezfwo1fd2 = 3343457; if (jdxezfwo1fd2 = 11587996)jdxezfwo1fd2 = 28890194; float jdxezfwo1fd3 = 3595785; if (jdxezfwo1fd3 = 17104435)jdxezfwo1fd3 = 7179609; if (jdxezfwo1fd3 = 6727346)jdxezfwo1fd3 = 5186313; if (jdxezfwo1fd3 = 2642530)jdxezfwo1fd3 = 6098365; if (jdxezfwo1fd3 = 100804505)jdxezfwo1fd3 = 18155918; if (jdxezfwo1fd3 = 13639432)jdxezfwo1fd3 = 1309472; if (jdxezfwo1fd3 = 20163483)jdxezfwo1fd3 = 72897350; if (jdxezfwo1fd3 = 4537353)jdxezfwo1fd3 = 17846170; if (jdxezfwo1fd3 = 3012330)jdxezfwo1fd3 = 17363813; if (jdxezfwo1fd3 = 7726324)jdxezfwo1fd3 = 17223047; if (jdxezfwo1fd3 = 21198746)jdxezfwo1fd3 = 8367300; if (jdxezfwo1fd3 = 17805906)jdxezfwo1fd3 = 19763965; if (jdxezfwo1fd3 = 71442650)jdxezfwo1fd3 = 3389300; if (jdxezfwo1fd3 = 72795400)jdxezfwo1fd3 = 20021901; if (jdxezfwo1fd3 = 1669214)jdxezfwo1fd3 = 3343457; if (jdxezfwo1fd3 = 11587996)jdxezfwo1fd3 = 28890194; }; void* Buf16ToMem16(std::string sBuf, _Outptr_ DWORD &BufSize) { std::string strBuf = sBuf; std::string strtmp; DWORD dwSt = 0, dwEd = 0, dwCount = 0, dwStrSz = 0; BYTE* bTmp = new BYTE{ 0 }; void* TmpVoid; void* RetVoid; dwStrSz = strBuf.size(); dwSt = strBuf.find("\\", dwSt ? dwSt + 1 : 0); TmpVoid = new char[dwStrSz] {0}; while (true) { dwEd = strBuf.find("\\", dwSt + 1); strtmp = strBuf.substr(dwSt + 1, (dwEd - dwSt - 1)); sscanf_s(strtmp.c_str(), "x%x", &bTmp); memcpy_s((char*)TmpVoid + dwCount, 1, &bTmp, 1); if (dwEd == std::string::npos) { break; } ++dwCount; dwSt = dwEd; } RetVoid = new char[dwCount + 1]{ 0 }; memcpy_s(RetVoid, dwCount + 1, TmpVoid, dwCount + 1); delete TmpVoid; TmpVoid = NULL; BufSize = dwCount + 1; return RetVoid; } std::string hexdump(void* addr, size_t length) { std::string sRet; char* pcaddr = (char*)addr; char temp[512]; size_t put_size = 0; size_t used_size = 0; size_t remain_size = sizeof(temp); for (unsigned long i = 0; i < length; ++i) { unsigned char var = *(unsigned char*)(pcaddr + i); put_size = snprintf(temp, remain_size, "0x%02X", var); sRet += temp; if (i == length - 1) { return sRet; } sRet += ","; memset(temp, 0, sizeof(temp)); } printf("%s\n", sRet.c_str()); return sRet; } DWORD TOOL_Buffer_to_File(char* Buffer, DWORD dwSize, WCHAR *FilePath) { //---------------------------------------写入文件 HANDLE NFile = 0; DWORD dwRetBuf = 0; do { NFile = CreateFile(FilePath, GENERIC_ALL, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, FALSE); } while (GetLastError() == ERROR_ALREADY_EXISTS && DeleteFile(FilePath)); // if (GetLastError() == ERROR_ACCESS_DENIED) { //MessageBox(NULL, L"出现错误,请用管理员权限重试", L"ERROR!", 0); return 0; } //生成的是ShellCode WriteFile(NFile, Buffer, dwSize, &dwRetBuf, FALSE); CloseHandle(NFile); return dwRetBuf; } ``` 最后修改:2022 年 06 月 22 日 © 允许规范转载 赞 1 如果觉得我的文章对你有用,请随意赞赏