逆向工程核心原理读书笔记-API钩取之记事本小写转大写

来源:互联网 发布:睡眠计时器软件 编辑:程序博客网 时间:2024/06/05 01:12

我们通过一个示例来练习钩取Notepad.exe的WriteFile,保存文件时将小写字母全部转换为大写字母。下面我们先测试一下代码。
运行notepad.exe并查看PID。


在命令行窗口中输入命令与参数。


输入一串小写字母之后选择保存。


再次打开文件,之前的小写字母全部变成了大写字母。


我们来分析一下源代码,看看是怎么实现的。
首先看一下main函数。main函数通过DebugActiveProcess将调试器附加到该运行的进程上,然后进入DebugLoop处理来自被调试者的调试事件。

[cpp] view plain copy
  1. int main(int argc, char* argv[])  
  2. {  
  3.     DWORD dwPID;  
  4.   
  5.     if( argc != 2 )  
  6.     {  
  7.         printf("\nUSAGE : hookdbg.exe <pid>\n");  
  8.         return 1;  
  9.     }  
  10.   
  11.     // Attach Process  
  12.     dwPID = atoi(argv[1]);  
  13.     if( !DebugActiveProcess(dwPID) )  
  14.     {  
  15.         printf("DebugActiveProcess(%d) failed!!!\n"  
  16.                "Error Code = %d\n", dwPID, GetLastError());  
  17.         return 1;  
  18.     }  
  19.   
  20.     // debugger loops  
  21.     DebugLoop();  
  22.   
  23.     return 0;  
  24. }  
接下来是DebugLoop。它从被调试者处接收事件并处理,然后使被调试者继续运行。ContinueDebugEvent是一个使被调试者继续运行的函数。
[cpp] view plain copy
  1. void DebugLoop()  
  2. {  
  3.     DEBUG_EVENT de;  
  4.     DWORD dwContinueStatus;  
  5.   
  6.     // Waiting for the event occurred by debuggee  
  7.     while( WaitForDebugEvent(&de, INFINITE) )  
  8.     {  
  9.         dwContinueStatus = DBG_CONTINUE;  
  10.   
  11.         // Debuggee process generates or attaches event  
  12.         if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )  
  13.         {  
  14.             OnCreateProcessDebugEvent(&de);  
  15.         }  
  16.         // Exception event  
  17.         else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )  
  18.         {  
  19.             if( OnExceptionDebugEvent(&de) )  
  20.                 continue;  
  21.         }  
  22.         // Debuggee process terminates event  
  23.         else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )  
  24.         {  
  25.             // debuggee stop -> debugger stop  
  26.             break;  
  27.         }  
  28.   
  29.         // Run the debuggee again  
  30.         ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);  
  31.     }  
  32. }  

DebugLoop处理3种调试事件,分别是EXIT_PROCESS_DEBUG_EVENT、CREATE_PROCESS_DEBUG_EVENT、EXCEPTION_DEBUG_EVENT。
1.被调试进程终止时会触发EXIT_PROCESS_DEBUG_EVENT。这里在发生该事件时,调试器与被调试者将一起终止。
2.OnCreateProcessDebugEvent是CREATE_PROCESS_DEBUG_EVENT事件句柄,被调试进程启动(或者附加)时即调用执行该函数。下面看一下它的核心部分。

[cpp] view plain copy
  1. BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)  
  2. {  
  3.     // get WriteFile() API address  
  4.     g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");  
  5.   
  6.     // API Hook - WriteFile()  
  7.     // change first byte to 0xCC(INT 3)   
  8.     memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));  
  9.     ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,   
  10.                       &g_chOrgByte, sizeof(BYTE), NULL);  
  11.     WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,   
  12.                        &g_chINT3, sizeof(BYTE), NULL);  
  13.   
  14.     return TRUE;  
  15. }  

首先获取WriteFile的起始地址,它获取的不是被调试进程的内存地址,而是调试进行的内存地址。对于Windows XP的DLL而言,它们在所有进程中都会加载到相同的地址(虚拟内存)。由于调试器拥有被调试进程的句柄(带有调试权限),所以可以使用ReadProcessMemory和WriteProcessMemory对被调试进程的内存空间自由进行读写操作。
3.OnExceptionDebugEvent是EXCEPTION_DEBUG_EVENT事件句柄,它处理被调试者的INT3指令。下面看一下它的核心部分。

[cpp] view plain copy
  1. BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)  
  2. {  
  3.     CONTEXT ctx;  
  4.     PBYTE lpBuffer = NULL;  
  5.     DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;  
  6.     PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;  
  7.   
  8.     // BreakPoint exception (INT 3)   
  9.     if( EXCEPTION_BREAKPOINT == per->ExceptionCode )  
  10.     {  
  11.         // BP at WriteFile() API  
  12.         if( g_pfWriteFile == per->ExceptionAddress )  
  13.         {  
  14.             // #1. Unhook  
  15.             // restore 0xCC to original byte   
  16.             WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,   
  17.                                &g_chOrgByte, sizeof(BYTE), NULL);  
  18.             // #2. Get Thread Context   
  19.             ctx.ContextFlags = CONTEXT_CONTROL;  
  20.             GetThreadContext(g_cpdi.hThread, &ctx);  
  21.             // #3. Get WriteFile() param 2, 3  
  22.             //   param 2 : ESP + 0x8  
  23.             //   param 3 : ESP + 0xC  
  24.             ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8),   
  25.                               &dwAddrOfBuffer, sizeof(DWORD), NULL);  
  26.             ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC),   
  27.                               &dwNumOfBytesToWrite, sizeof(DWORD), NULL);  
  28.             // #4. Allocates temp buf  
  29.             lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);  
  30.             memset(lpBuffer, 0, dwNumOfBytesToWrite+1);  
  31.             // #5. Copy WriteFile() buf to temp buf  
  32.             ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,   
  33.                               lpBuffer, dwNumOfBytesToWrite, NULL);  
  34.             printf("\n### original string ###\n%s\n", lpBuffer);  
  35.             // #6. Lower case letters -> Upper case letters  
  36.             for( i = 0; i < dwNumOfBytesToWrite; i++ )  
  37.             {  
  38.                 if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A )  
  39.                     lpBuffer[i] -= 0x20;  
  40.             }  
  41.   
  42.             printf("\n### converted string ###\n%s\n", lpBuffer);  
  43.   
  44.             // #7. Copy to WriteFile() buf  
  45.             WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,   
  46.                                lpBuffer, dwNumOfBytesToWrite, NULL);  
  47.               
  48.             // #8. release temp buf  
  49.             free(lpBuffer);  
  50.   
  51.             // #9. Change EIP to WriteFile() address  
  52.             ctx.Eip = (DWORD)g_pfWriteFile;  
  53.             SetThreadContext(g_cpdi.hThread, &ctx);  
  54.   
  55.             // #10. Run Debuggee process  
  56.             ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);  
  57.             Sleep(0);  
  58.   
  59.             // #11. API Hook  
  60.             WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,   
  61.                                &g_chINT3, sizeof(BYTE), NULL);  
  62.   
  63.             return TRUE;  
  64.         }  
  65.     }  
  66.   
  67.     return FALSE;  
  68. }  
首先脱钩,因为在将小写字母转换为大写字母之后需要正常调用WriteFile。接着获取线程的上下文,获取WriteFile的第二个参数和第三个参数的值。调用WriteFile时,我们要在传递过来的参数中知道param2(缓冲区地址)和param3(缓冲区大小)这两个参数。函数参数存储在栈中,通过CONTEXT.Esp成员可以获得它们的值。然后把小写字母转换为大写字母之后覆写WriteFile缓冲区,把线程上下文的EIP修改为WriteFile起始地址。因为在WriteFile的起始地址处设置了断点,被调试者内部调用WriteFile时,会在起始地址处遇到INT3指令。执行该指令时,EIP的值会增加一个字节,所以EIP的当前地址为WriteFile+1。修改好CONTEXT.Eip成员后,调用SetThreadContext。现在运行调试进程。如果没有Sleep(0)语句,Notepad.exe在调用WriteFile的过程中我们的程序会尝试将WriteFile的首字节修改为0xCC,这可能导致内存访问异常。最后设置钩子,方便下次钩取操作。
完整的代码如下。
[cpp] view plain copy
  1. #include "windows.h"  
  2. #include "stdio.h"  
  3.   
  4. LPVOID g_pfWriteFile = NULL;  
  5. CREATE_PROCESS_DEBUG_INFO g_cpdi;  
  6. BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;  
  7.   
  8. BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)  
  9. {  
  10.     // get WriteFile() API address  
  11.     g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");  
  12.   
  13.     // API Hook - WriteFile()  
  14.     // change first byte to 0xCC(INT 3)   
  15.     memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));  
  16.     ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,   
  17.                       &g_chOrgByte, sizeof(BYTE), NULL);  
  18.     WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,   
  19.                        &g_chINT3, sizeof(BYTE), NULL);  
  20.   
  21.     return TRUE;  
  22. }  
  23.   
  24. BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)  
  25. {  
  26.     CONTEXT ctx;  
  27.     PBYTE lpBuffer = NULL;  
  28.     DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;  
  29.     PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;  
  30.   
  31.     // BreakPoint exception (INT 3)   
  32.     if( EXCEPTION_BREAKPOINT == per->ExceptionCode )  
  33.     {  
  34.         // BP at WriteFile() API  
  35.         if( g_pfWriteFile == per->ExceptionAddress )  
  36.         {  
  37.             // #1. Unhook  
  38.             // restore 0xCC to original byte   
  39.             WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,   
  40.                                &g_chOrgByte, sizeof(BYTE), NULL);  
  41.             // #2. Get Thread Context   
  42.             ctx.ContextFlags = CONTEXT_CONTROL;  
  43.             GetThreadContext(g_cpdi.hThread, &ctx);  
  44.             // #3. Get WriteFile() param 2, 3  
  45.             //   param 2 : ESP + 0x8  
  46.             //   param 3 : ESP + 0xC  
  47.             ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8),   
  48.                               &dwAddrOfBuffer, sizeof(DWORD), NULL);  
  49.             ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC),   
  50.                               &dwNumOfBytesToWrite, sizeof(DWORD), NULL);  
  51.             // #4. Allocates temp buf  
  52.             lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);  
  53.             memset(lpBuffer, 0, dwNumOfBytesToWrite+1);  
  54.             // #5. Copy WriteFile() buf to temp buf  
  55.             ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,   
  56.                               lpBuffer, dwNumOfBytesToWrite, NULL);  
  57.             printf("\n### original string ###\n%s\n", lpBuffer);  
  58.             // #6. Lower case letters -> Upper case letters  
  59.             for( i = 0; i < dwNumOfBytesToWrite; i++ )  
  60.             {  
  61.                 if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A )  
  62.                     lpBuffer[i] -= 0x20;  
  63.             }  
  64.   
  65.             printf("\n### converted string ###\n%s\n", lpBuffer);  
  66.   
  67.             // #7. Copy to WriteFile() buf  
  68.             WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,   
  69.                                lpBuffer, dwNumOfBytesToWrite, NULL);  
  70.               
  71.             // #8. release temp buf  
  72.             free(lpBuffer);  
  73.   
  74.             // #9. Change EIP to WriteFile() address  
  75.             ctx.Eip = (DWORD)g_pfWriteFile;  
  76.             SetThreadContext(g_cpdi.hThread, &ctx);  
  77.   
  78.             // #10. Run Debuggee process  
  79.             ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);  
  80.             Sleep(0);  
  81.   
  82.             // #11. API Hook  
  83.             WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,   
  84.                                &g_chINT3, sizeof(BYTE), NULL);  
  85.   
  86.             return TRUE;  
  87.         }  
  88.     }  
  89.   
  90.     return FALSE;  
  91. }  
  92.   
  93. void DebugLoop()  
  94. {  
  95.     DEBUG_EVENT de;  
  96.     DWORD dwContinueStatus;  
  97.   
  98.     // Waiting for the event occurred by debuggee  
  99.     while( WaitForDebugEvent(&de, INFINITE) )  
  100.     {  
  101.         dwContinueStatus = DBG_CONTINUE;  
  102.   
  103.         // Debuggee process generates or attaches event  
  104.         if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )  
  105.         {  
  106.             OnCreateProcessDebugEvent(&de);  
  107.         }  
  108.         // Exception event  
  109.         else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )  
  110.         {  
  111.             if( OnExceptionDebugEvent(&de) )  
  112.                 continue;  
  113.         }  
  114.         // Debuggee process terminates event  
  115.         else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )  
  116.         {  
  117.             // debuggee stop -> debugger stop  
  118.             break;  
  119.         }  
  120.   
  121.         // Run the debuggee again  
  122.         ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);  
  123.     }  
  124. }  
  125.   
  126. int main(int argc, char* argv[])  
  127. {  
  128.     DWORD dwPID;  
  129.   
  130.     if( argc != 2 )  
  131.     {  
  132.         printf("\nUSAGE : hookdbg.exe <pid>\n");  
  133.         return 1;  
  134.     }  
  135.   
  136.     // Attach Process  
  137.     dwPID = atoi(argv[1]);  
  138.     if( !DebugActiveProcess(dwPID) )  
  139.     {  
  140.         printf("DebugActiveProcess(%d) failed!!!\n"  
  141.                "Error Code = %d\n", dwPID, GetLastError());  
  142.         return 1;  
  143.     }  
  144.   
  145.     // debugger loops  
  146.     DebugLoop();  
  147.   
  148.     return 0;  
  149. }  
阅读全文
0 0
原创粉丝点击