我们通过一个示例来练习钩取Notepad.exe的WriteFile,保存文件时将小写字母全部转换为大写字母。下面我们先测试一下代码。
运行notepad.exe并查看PID。
在命令行窗口中输入命令与参数。
输入一串小写字母之后选择保存。
再次打开文件,之前的小写字母全部变成了大写字母。
我们来分析一下源代码,看看是怎么实现的。
首先看一下main函数。main函数通过DebugActiveProcess将调试器附加到该运行的进程上,然后进入DebugLoop处理来自被调试者的调试事件。
- int main(int argc, char* argv[])
- {
- DWORD dwPID;
- if( argc != 2 )
- {
- printf("\nUSAGE : hookdbg.exe <pid>\n");
- return 1;
- }
- // Attach Process
- dwPID = atoi(argv[1]);
- if( !DebugActiveProcess(dwPID) )
- {
- printf("DebugActiveProcess(%d) failed!!!\n"
- "Error Code = %d\n", dwPID, GetLastError());
- return 1;
- }
- // debugger loops
- DebugLoop();
- return 0;
- }
- void DebugLoop()
- {
- DEBUG_EVENT de;
- DWORD dwContinueStatus;
- // Waiting for the event occurred by debuggee
- while( WaitForDebugEvent(&de, INFINITE) )
- {
- dwContinueStatus = DBG_CONTINUE;
- // Debuggee process generates or attaches event
- if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
- {
- OnCreateProcessDebugEvent(&de);
- }
- // Exception event
- else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )
- {
- if( OnExceptionDebugEvent(&de) )
- continue;
- }
- // Debuggee process terminates event
- else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
- {
- // debuggee stop -> debugger stop
- break;
- }
- // Run the debuggee again
- ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
- }
- }
DebugLoop处理3种调试事件,分别是EXIT_PROCESS_DEBUG_EVENT、CREATE_PROCESS_DEBUG_EVENT、EXCEPTION_DEBUG_EVENT。
1.被调试进程终止时会触发EXIT_PROCESS_DEBUG_EVENT。这里在发生该事件时,调试器与被调试者将一起终止。
2.OnCreateProcessDebugEvent是CREATE_PROCESS_DEBUG_EVENT事件句柄,被调试进程启动(或者附加)时即调用执行该函数。下面看一下它的核心部分。
- BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
- {
- // get WriteFile() API address
- g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");
- // API Hook - WriteFile()
- // change first byte to 0xCC(INT 3)
- memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
- ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
- &g_chOrgByte, sizeof(BYTE), NULL);
- WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
- &g_chINT3, sizeof(BYTE), NULL);
- return TRUE;
- }
首先获取WriteFile的起始地址,它获取的不是被调试进程的内存地址,而是调试进行的内存地址。对于Windows XP的DLL而言,它们在所有进程中都会加载到相同的地址(虚拟内存)。由于调试器拥有被调试进程的句柄(带有调试权限),所以可以使用ReadProcessMemory和WriteProcessMemory对被调试进程的内存空间自由进行读写操作。
3.OnExceptionDebugEvent是EXCEPTION_DEBUG_EVENT事件句柄,它处理被调试者的INT3指令。下面看一下它的核心部分。
- BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)
- {
- CONTEXT ctx;
- PBYTE lpBuffer = NULL;
- DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;
- PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;
- // BreakPoint exception (INT 3)
- if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
- {
- // BP at WriteFile() API
- if( g_pfWriteFile == per->ExceptionAddress )
- {
- // #1. Unhook
- // restore 0xCC to original byte
- WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
- &g_chOrgByte, sizeof(BYTE), NULL);
- // #2. Get Thread Context
- ctx.ContextFlags = CONTEXT_CONTROL;
- GetThreadContext(g_cpdi.hThread, &ctx);
- // #3. Get WriteFile() param 2, 3
- // param 2 : ESP + 0x8
- // param 3 : ESP + 0xC
- ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8),
- &dwAddrOfBuffer, sizeof(DWORD), NULL);
- ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC),
- &dwNumOfBytesToWrite, sizeof(DWORD), NULL);
- // #4. Allocates temp buf
- lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);
- memset(lpBuffer, 0, dwNumOfBytesToWrite+1);
- // #5. Copy WriteFile() buf to temp buf
- ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
- lpBuffer, dwNumOfBytesToWrite, NULL);
- printf("\n### original string ###\n%s\n", lpBuffer);
- // #6. Lower case letters -> Upper case letters
- for( i = 0; i < dwNumOfBytesToWrite; i++ )
- {
- if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A )
- lpBuffer[i] -= 0x20;
- }
- printf("\n### converted string ###\n%s\n", lpBuffer);
- // #7. Copy to WriteFile() buf
- WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
- lpBuffer, dwNumOfBytesToWrite, NULL);
- // #8. release temp buf
- free(lpBuffer);
- // #9. Change EIP to WriteFile() address
- ctx.Eip = (DWORD)g_pfWriteFile;
- SetThreadContext(g_cpdi.hThread, &ctx);
- // #10. Run Debuggee process
- ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
- Sleep(0);
- // #11. API Hook
- WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
- &g_chINT3, sizeof(BYTE), NULL);
- return TRUE;
- }
- }
- return FALSE;
- }
完整的代码如下。
- #include "windows.h"
- #include "stdio.h"
- LPVOID g_pfWriteFile = NULL;
- CREATE_PROCESS_DEBUG_INFO g_cpdi;
- BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;
- BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
- {
- // get WriteFile() API address
- g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");
- // API Hook - WriteFile()
- // change first byte to 0xCC(INT 3)
- memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
- ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
- &g_chOrgByte, sizeof(BYTE), NULL);
- WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
- &g_chINT3, sizeof(BYTE), NULL);
- return TRUE;
- }
- BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)
- {
- CONTEXT ctx;
- PBYTE lpBuffer = NULL;
- DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;
- PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;
- // BreakPoint exception (INT 3)
- if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
- {
- // BP at WriteFile() API
- if( g_pfWriteFile == per->ExceptionAddress )
- {
- // #1. Unhook
- // restore 0xCC to original byte
- WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
- &g_chOrgByte, sizeof(BYTE), NULL);
- // #2. Get Thread Context
- ctx.ContextFlags = CONTEXT_CONTROL;
- GetThreadContext(g_cpdi.hThread, &ctx);
- // #3. Get WriteFile() param 2, 3
- // param 2 : ESP + 0x8
- // param 3 : ESP + 0xC
- ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8),
- &dwAddrOfBuffer, sizeof(DWORD), NULL);
- ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC),
- &dwNumOfBytesToWrite, sizeof(DWORD), NULL);
- // #4. Allocates temp buf
- lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);
- memset(lpBuffer, 0, dwNumOfBytesToWrite+1);
- // #5. Copy WriteFile() buf to temp buf
- ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
- lpBuffer, dwNumOfBytesToWrite, NULL);
- printf("\n### original string ###\n%s\n", lpBuffer);
- // #6. Lower case letters -> Upper case letters
- for( i = 0; i < dwNumOfBytesToWrite; i++ )
- {
- if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A )
- lpBuffer[i] -= 0x20;
- }
- printf("\n### converted string ###\n%s\n", lpBuffer);
- // #7. Copy to WriteFile() buf
- WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
- lpBuffer, dwNumOfBytesToWrite, NULL);
- // #8. release temp buf
- free(lpBuffer);
- // #9. Change EIP to WriteFile() address
- ctx.Eip = (DWORD)g_pfWriteFile;
- SetThreadContext(g_cpdi.hThread, &ctx);
- // #10. Run Debuggee process
- ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
- Sleep(0);
- // #11. API Hook
- WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
- &g_chINT3, sizeof(BYTE), NULL);
- return TRUE;
- }
- }
- return FALSE;
- }
- void DebugLoop()
- {
- DEBUG_EVENT de;
- DWORD dwContinueStatus;
- // Waiting for the event occurred by debuggee
- while( WaitForDebugEvent(&de, INFINITE) )
- {
- dwContinueStatus = DBG_CONTINUE;
- // Debuggee process generates or attaches event
- if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
- {
- OnCreateProcessDebugEvent(&de);
- }
- // Exception event
- else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )
- {
- if( OnExceptionDebugEvent(&de) )
- continue;
- }
- // Debuggee process terminates event
- else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
- {
- // debuggee stop -> debugger stop
- break;
- }
- // Run the debuggee again
- ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
- }
- }
- int main(int argc, char* argv[])
- {
- DWORD dwPID;
- if( argc != 2 )
- {
- printf("\nUSAGE : hookdbg.exe <pid>\n");
- return 1;
- }
- // Attach Process
- dwPID = atoi(argv[1]);
- if( !DebugActiveProcess(dwPID) )
- {
- printf("DebugActiveProcess(%d) failed!!!\n"
- "Error Code = %d\n", dwPID, GetLastError());
- return 1;
- }
- // debugger loops
- DebugLoop();
- return 0;
- }