Debug Hook
- 命名
关于这个命名是我自己这样说的,至于这种HOOK技术,先前在一个开源项目中叫做RemoteHook,我比较喜欢自己的这种命名,所以就叫Debug Hook。如果有错误,请指出。
-
先来说说调试的原理
在Windows操作系统,有两种方法可以来调试一个进程。
1: CreateProcess()
可以使用此函数来启动调试一个进程。
CreateProcess(FileFullPath,NULL,NULL,NULL, false,DEBUG_PROCESS |DEBUG_ONLY_THIS_PROCESS| CREATE_NEW_CONSOLE, NULL,NULL,&StartupInfo,&ProcessInfo)
2:DebugActiveProcess(ProcessID)
可以使用此函数来附加到一个进程来进行调试。
我们使用以上两种方法中的任何一种方法来调试一个进程,每当被调试进程发生调试事件的时候,OS都会暂停其运行。并向调试器报告相应的事件,调试器处理之后就可以继续运行。
-
利用调试技术来HOOK API函数的相关步骤如下
利用调试技术来HOOK API函数的相关步骤如下
1对想要钩取的进程进行附加操作,使之成为被调试者。
2将要钩取的API的起始地址的第一个字节修改为0xcc(或者使用硬件断点)。
3当调用目标API的时候,控制权就转移到调试器进程。
4执行需要的操作。
5脱钩,将API 函数的第一个字节恢复。
6运行相应的API。
1 #include<Windows.h> 2 #include<iostream> 3 #include<stdio.h> 4 5 using namespace std; 6 7 LPVOID WriteFileAddress = NULL; 8 CREATE_PROCESS_DEBUG_INFO CreateProcessDebugInfomation; 9 BYTE INT3 = 0xCC, OldByte = 0; 10 11 BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde) 12 { 13 // WriteFile()函数地址 14 WriteFileAddress = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile"); 15 16 // API Hook - WriteFile() 17 //将WriteFile函数的首个字节改为0xcc 18 memcpy(&CreateProcessDebugInfomation, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO)); 19 ReadProcessMemory(CreateProcessDebugInfomation.hProcess, WriteFileAddress, 20 &OldByte, sizeof(BYTE), NULL); 21 WriteProcessMemory(CreateProcessDebugInfomation.hProcess, WriteFileAddress, 22 &INT3, sizeof(BYTE), NULL); 23 24 return TRUE; 25 } 26 27 BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pDebugEvent) 28 { 29 CONTEXT Context; 30 PBYTE lpBuffer = NULL; 31 DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i; 32 PEXCEPTION_RECORD pExceptionRecord = &pDebugEvent->u.Exception.ExceptionRecord; 33 34 // BreakPoint exception 35 if( EXCEPTION_BREAKPOINT == pExceptionRecord->ExceptionCode ) 36 { 37 // 发生异常的地方是否为我们要钩取的函数 38 if( WriteFileAddress == pExceptionRecord->ExceptionAddress ) 39 { 40 // #1. Unhook 41 // 先恢复,以免进入死循环 42 WriteProcessMemory(CreateProcessDebugInfomation.hProcess, WriteFileAddress, 43 &OldByte, sizeof(BYTE), NULL); 44 45 // #2. 获得线程上下背景文 为了修改EIp的值,来使进程恢复正常运行 46 Context.ContextFlags = CONTEXT_CONTROL; 47 GetThreadContext(CreateProcessDebugInfomation.hThread, &Context); 48 49 // #3. WriteFile() 根据ESP来获得WriteFile 函数的参数,以达到修改数据的目的 50 51 ReadProcessMemory(CreateProcessDebugInfomation.hProcess, (LPVOID)(Context.Esp + 0x8), 52 &dwAddrOfBuffer, sizeof(DWORD), NULL); 53 54 ReadProcessMemory(CreateProcessDebugInfomation.hProcess, (LPVOID)(Context.Esp + 0xC), 55 &dwNumOfBytesToWrite, sizeof(DWORD), NULL); 56 57 // #4. 58 lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1); 59 memset(lpBuffer, 0, dwNumOfBytesToWrite+1); 60 61 // #5. WriteFile() 62 ReadProcessMemory(CreateProcessDebugInfomation.hProcess, (LPVOID)dwAddrOfBuffer, 63 lpBuffer, dwNumOfBytesToWrite, NULL); 64 printf("\n### original string ###\n%s\n", lpBuffer); 65 66 // #6. 修改数据 67 for( i = 0; i < dwNumOfBytesToWrite; i++ ) 68 { 69 if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A ) 70 lpBuffer[i] -= 0x20; 71 } 72 73 printf("\n### converted string ###\n%s\n", lpBuffer); 74 75 // #7. 调用原函数 76 WriteProcessMemory(CreateProcessDebugInfomation.hProcess, (LPVOID)dwAddrOfBuffer, 77 lpBuffer, dwNumOfBytesToWrite, NULL); 78 79 80 free(lpBuffer); 81 82 // 设置EIP的值来实现正常运行,注意EIP的值为0xcc的下一条指令的地址。 83 Context.Eip = (DWORD)WriteFileAddress; 84 SetThreadContext(CreateProcessDebugInfomation.hThread, &Context); 85 86 // 运行 87 ContinueDebugEvent(pDebugEvent->dwProcessId, pDebugEvent->dwThreadId, DBG_CONTINUE); 88 Sleep(0); 89 90 // 再次钩取 91 WriteProcessMemory(CreateProcessDebugInfomation.hProcess, WriteFileAddress, 92 &INT3, sizeof(BYTE), NULL); 93 94 return TRUE; 95 } 96 } 97 98 return FALSE; 99 } 100 101 void DebugLoop() 102 { 103 DEBUG_EVENT DebugEvent; 104 DWORD dwContinueStatus; 105 106 // 等待调试事件 107 while( WaitForDebugEvent(&DebugEvent, INFINITE) ) 108 { 109 dwContinueStatus = DBG_CONTINUE; 110 111 // 调试事件为创建进程 112 if( CREATE_PROCESS_DEBUG_EVENT == DebugEvent.dwDebugEventCode ) 113 { 114 OnCreateProcessDebugEvent(&DebugEvent); 115 } 116 // 调试事件 117 else if( EXCEPTION_DEBUG_EVENT == DebugEvent.dwDebugEventCode ) 118 { 119 if( OnExceptionDebugEvent(&DebugEvent) ) 120 continue; 121 } 122 // 调试进程退出 123 else if( EXIT_PROCESS_DEBUG_EVENT == DebugEvent.dwDebugEventCode ) 124 { 125 126 break; 127 } 128 129 130 ContinueDebugEvent(DebugEvent.dwProcessId, DebugEvent.dwThreadId, dwContinueStatus); 131 } 132 } 133 134 int main(int argc, char* argv[]) 135 { 136 DWORD dwProcessID; 137 cout << "Input ProcessID" << endl; 138 cin >> dwProcessID; 139 140 // Attach Process 141 142 if( !DebugActiveProcess(dwProcessID) ) 143 { 144 printf("DebugActiveProcess(%d) failed!!!\n" 145 "Error Code = %d\n", dwProcessID, GetLastError()); 146 return 1; 147 } 148 149 // 调试事件循环 150 DebugLoop(); 151 152 return 0; 153 }
参考《逆向工程核心原理》