《逆向工程核心原理》第30章 记事本WriteFile() API钩取
原文是在x86下,而在x64下函数调用方式为fastcall,前4个参数保存在寄存器中。在原代码基础上进行修改:
1 // myhookdbg.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 2 // 3 4 #include "pch.h" 5 #include <iostream> 6 #include <windows.h> 7 #include <tchar.h> 8 #include <tlhelp32.h> 9 #include <stdio.h> 10 #include <shlobj.h> 11 12 13 LPVOID g_pfWriteFile = NULL; 14 CREATE_PROCESS_DEBUG_INFO g_cpdi; 15 BYTE g_chINT3 = 0xCC, g_chOrgByte = 0; 16 BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde) 17 { 18 // 查找API地址 19 HMODULE dll = GetModuleHandleA("kernel32.dll"); 20 g_pfWriteFile = GetProcAddress(dll, "WriteFile"); 21 //g_pfWriteFile =(LPVOID)0x7ffca76b2500; 22 printf("kernel32.dll基址:%I64x ", dll); 23 printf("WriteFile地址:%I64x ", (DWORD64 )g_pfWriteFile); 24 // API Hook - WriteFile() 25 // 将byte更改为0xCC (INT 3) 26 // orginal byte是备份 27 memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO)); 28 ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile, 29 &g_chOrgByte, sizeof(BYTE), NULL); 30 printf("原api调用处字节:%x ", g_chOrgByte); 31 WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, 32 &g_chINT3, sizeof(BYTE), NULL); 33 BYTE arr[10]; 34 ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile, 35 arr, sizeof(BYTE)*10, NULL); 36 printf("修改后: "); 37 for (int i = 0; i < 10; i++) 38 printf("%02x ", arr[i]); 39 printf(" "); 40 return TRUE; 41 } 42 43 BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde) 44 { 45 CONTEXT ctx; 46 PBYTE lpBuffer = NULL; 47 DWORD i; 48 ULONG_PTR dwNumOfBytesToWrite, dwAddrOfBuffer; 49 PEXCEPTION_RECORD64 per =(PEXCEPTION_RECORD64)&pde->u.Exception.ExceptionRecord; 50 51 // BreakPoint exception (INT 3) 的情况 52 if (EXCEPTION_BREAKPOINT == per->ExceptionCode) 53 { 54 // 如果BP地址是WriteFile, 55 if ((DWORD64)g_pfWriteFile == per->ExceptionAddress) 56 { 57 printf("发现writefile调用,地址:%I64X ", g_pfWriteFile); 58 // #1. Unhook 59 // 如果BP地址是WriteFile(用0xCC覆盖的部分返回original byte) 60 WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, 61 &g_chOrgByte, sizeof(BYTE), NULL); 62 BYTE arr[10]; 63 ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile, 64 arr, sizeof(BYTE)*10, NULL); 65 printf("恢复后:"); 66 for (int i = 0; i < 10; i++) 67 printf("%02x ", arr[i]); 68 printf(" "); 69 // #2. 寻求Thread Context 70 //ctx.ContextFlags = CONTEXT_CONTROL;SegSs栈段, Rsp, SegCs代码段, Rip, and EFlags 71 ctx.ContextFlags = CONTEXT_FULL;//要获得全部寄存器 72 GetThreadContext(g_cpdi.hThread, &ctx); 73 LPOVERLAPPED arg5_lpOverlapped = NULL; 74 ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Rsp + 0x28), &arg5_lpOverlapped, sizeof(DWORD), NULL); 75 printf("寄存器数据: "); 76 //printf("rax:%I64x ", ctx.Rax); 77 //printf("rbx:%I64x ", ctx.Rbx); 78 printf("rcx:%I64x ", ctx.Rcx); 79 printf("rdx:%I64x ", ctx.Rdx); 80 printf("r8:%I64x ", ctx.R8); 81 printf("r9:%I64x ", ctx.R9); 82 printf("arg5:%I64x ",arg5_lpOverlapped); 83 84 85 // #3.获取param 2和3的值 86 // x86函数参数存在于此进程的栈中;x64 fastcall 前4个参数存在寄存器中 87 // LPCVOID lpBuffer,//数据缓存区指针 rdx 88 // DWORD nNumberOfBytesToWrite,//你要写的字节数 r8 89 // param 2 : rdx 90 // param 3 : r8 91 92 //ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.esp + 0x8),&dwAddrOfBuffer, sizeof(DWORD), NULL); 93 //ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.esp + 0xC),&dwNumOfBytesToWrite, sizeof(DWORD), NULL); 94 dwAddrOfBuffer = ctx.Rdx; 95 dwNumOfBytesToWrite = ctx.R8; 96 //printf("%s ", dwAddrOfBuffer); 97 // #4. 临时缓冲配额 98 lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite + 1); 99 memset(lpBuffer, 0, dwNumOfBytesToWrite + 1); 100 101 // #5. 将WriteFile的缓冲复制到临时缓冲 102 ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, 103 lpBuffer, dwNumOfBytesToWrite, NULL); 104 printf(" ### original string ### %s ", lpBuffer); 105 106 // #6.小写->大写转换 107 for (i = 0; i < dwNumOfBytesToWrite; i++) 108 { 109 if (0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A) 110 lpBuffer[i] -= 0x20; 111 } 112 113 printf(" ### converted string ### %s ", lpBuffer); 114 115 // #7. 将转换后的缓冲复制到WriteFile的缓冲 116 WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, 117 lpBuffer, dwNumOfBytesToWrite, NULL); 118 //ctx.Rdx= 119 // #8. 取消临时缓冲 120 free(lpBuffer); 121 122 // #9.将Thread Context的EIP更改为WriteFile() 123 // (现在已经过WriteFile() + 1) 124 125 //BOOL WriteFile( 126 // HANDLE hFile,//文件句柄 rcx 127 // LPCVOID lpBuffer,//数据缓存区指针 rdx 128 // DWORD nNumberOfBytesToWrite,//你要写的字节数 r8 129 // LPDWORD lpNumberOfBytesWritten,//用于保存实际写入字节数的存储区域的指针 r9 130 // LPOVERLAPPED lpOverlapped//OVERLAPPED结构体指针 rsp+0x20 [call 前rsp 0 8 10 18 20 28] 131 //); 132 /*ctx.Rdx += 1; 133 ctx.R8 -= 1;*/ 134 ctx.Rip =(DWORD64)g_pfWriteFile; 135 //ctx.Eip = (DWORD)g_pfWriteFile; 136 SetThreadContext(g_cpdi.hThread, &ctx); 137 138 // #10. Debuggee 运行被调试进程 139 ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE); 140 Sleep(0); 141 printf("continue "); 142 // #11. API Hook 143 WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,&g_chINT3, sizeof(BYTE), NULL); 144 145 return TRUE; 146 } 147 } 148 149 return FALSE; 150 } 151 152 void DebugLoop() 153 { 154 DEBUG_EVENT de; 155 DWORD dwContinueStatus; 156 157 // 从Debuggee等待event的到来。 158 while (WaitForDebugEvent(&de, INFINITE)) 159 { 160 dwContinueStatus = DBG_CONTINUE; 161 162 // 创建Debuggee进程或attach事件 163 if (CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode) 164 { 165 OnCreateProcessDebugEvent(&de); 166 printf("finish creat debuggee "); 167 } 168 // 异常活动 169 else if (EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode) 170 { 171 if (OnExceptionDebugEvent(&de)) 172 continue; 173 } 174 // Debuggee进程退出事件 175 else if (EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode) 176 { 177 // debuggee结束-> debugger结束 178 break; 179 } 180 181 // Debuggee的恢复执行。 182 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus); 183 } 184 } 185 186 int main() 187 { 188 //system("tasklist"); 189 system("tasklist | findstr notepad"); 190 char pid[10]; 191 printf("输入要注入的进程pid: "); 192 scanf_s("%s", pid, 10); 193 194 DWORD dwPID; 195 dwPID = atoi(pid); 196 if (!DebugActiveProcess(dwPID)) 197 { 198 printf("DebugActiveProcess(%d) failed!!! " 199 "Error Code = %d ", dwPID, GetLastError()); 200 return 1; 201 } 202 203 // 调试器循环 204 DebugLoop(); 205 system("pause"); 206 return 0; 207 /*std::cout << "Hello World! "; */ 208 } 209 210 // 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单 211 // 调试程序: F5 或调试 >“开始调试”菜单 212 213 // 入门提示: 214 // 1. 使用解决方案资源管理器窗口添加/管理文件 215 // 2. 使用团队资源管理器窗口连接到源代码管理 216 // 3. 使用输出窗口查看生成输出和其他消息 217 // 4. 使用错误列表窗口查看错误 218 // 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目 219 // 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
vs2017