《逆向工程核心原理》<04-30> 通过Debug修改代码实现API钩取的技术
来源:互联网 发布:android eventbus源码 编辑:程序博客网 时间:2024/06/05 15:01
原理: 通过Debug修改代码实现API钩取的技术
(技术流程: 代码-> 调试-> DebugActiveProcesss.etc)
在”Debugger–Debuggee”的状态下, 将Debuggee的API其实部分修改为0xCC (INT3), 控制权转移到Debugger后执行指定操作, 最后使Debuggee重新进入运行状态
Detail: 1. 对想钩取的进程进行附加操作, 使之成为Debugee;2. "钩子": 将API的初始地址的第一个字节修改为0xcc;3. 调用相应API时, 控制权转移到Debugger4. 执行需要的操作(操作参数, 返回值.etc)5. "脱钩": 将0xcc恢复原值(为了正常运行API)6. 运行相关API(无0xcc的正常状态)7. "钩子": 再次修改为0xcc(为了继续钩取)8. 控制权返回Debuggee
代码:
01- main()
#include "windows.h"#include "stdio.h"LPVOID g_pfWriteFile = NULL;CREATE_PROCESS_DEBUG_INFO g_cpdi; // CreateProcessInfoBYTE g_chINT3 = 0xCC, g_chOrgByte = 0;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; } // 调试器循环 DebugLoop(); return 0;}
02- DebugLoop()
void DebugLoop(){ DEBUG_EVENT de; // DEBUG_EVENT结构体 DWORD dwContinueStatus; // 等待被调试者发生事件 while( WaitForDebugEvent(&de, INFINITE) ) { dwContinueStatus = DBG_CONTINUE; // 被调试进程生成或者附加事件 if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode ) // CREATE_PROCESS_DEBUG_EVENT 在DEBUG_EVENT结构体 { OnCreateProcessDebugEvent(&de); } // 异常事件 else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode ) // EXCEPTION_DEBUG_EVENT 在DEBUG_EVENT结构体 { if( OnExceptionDebugEvent(&de) ) continue; } // 被调试进程终止事件 else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode ) { // 被调试者终止-调试器终止 break; } // 再次运行被调试者 ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus); // dwContinueStatus 若处理正常, 值为DBG_CONTINUE; 若无法处理, 或者希望在应用程序的SEH中处理, 值为DBG_EXCEPTION_NOT_HANDLED }}
03- OnCreateProcessDebugEvent()
BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde) { // 获取WriteFile() API地址 g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile"); // API"钩子" - WriteFile() // 更改第一个字节为0xCC(INT3) // orginal byte是g_ch0rgByte备份 memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO)); // CREATE_PROCESS_DEBUG_INFO g_cpdi; ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chOrgByte, sizeof(BYTE), NULL); // g_cpdi.hProcess 为Debuggee进程 // g_pfWriteFile 为WriteFile()地址 // Read BYTE大小的数据放到 g_chOrgByte WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chINT3, sizeof(BYTE), NULL); // Write BYTE大小的数据放到地址首位置 return TRUE;}
04- OnExceptionDebugEvent()
BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde){ CONTEXT ctx; PBYTE lpBuffer = NULL; DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i; PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord; // 是断点异常(INT3)时 if( EXCEPTION_BREAKPOINT == per->ExceptionCode ) { // 断点地址为WriteFile() API地址时 if( g_pfWriteFile == per->ExceptionAddress ) { // #1. Unhook // 将0xCC恢复为orignal byte WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile, &g_chOrgByte, sizeof(BYTE), NULL); // #2. 获取线程上下文 ctx.ContextFlags = CONTEXT_CONTROL; GetThreadContext(g_cpdi.hThread, &ctx); // #3. 获取WriteFile()的param 2、3值 // 函数参数存在于相应进程的栈 // param 2 : ESP + 0x8 // param 3 : ESP + 0xC ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8), &dwAddrOfBuffer, sizeof(DWORD), NULL); // Read DWORD大小的数据放到 dwAddrOfBuffer ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC), &dwNumOfBytesToWrite, sizeof(DWORD), NULL); // // Read DWORD大小的数据放到dwNumOfBytesToWrite // #4. 分配临时缓冲区 lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1); memset(lpBuffer, 0, dwNumOfBytesToWrite+1); // #5. 复制WriteFile()缓冲区到临时缓冲区 ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, lpBuffer, dwNumOfBytesToWrite, NULL); printf("\n### original string ###\n%s\n", lpBuffer); // #6. 将小写字母转换为大写字母 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. 将变换后的缓冲区复制到WriteFile()缓冲区 WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, lpBuffer, dwNumOfBytesToWrite, NULL); // #8. 释放临时缓冲区 free(lpBuffer); // #9. 将线程上下文的EIP更改为WriteFile()的首地址 // (当前为WriteFile()+1位置, INT3命令之后) // 简而言之: 执行了INT3后, EIP+1, 需要把EIP+1再还原为EIP原来的值 ctx.Eip = (DWORD)g_pfWriteFile; // 还原EIP SetThreadContext(g_cpdi.hThread, &ctx); // #10. 运行被调试进程 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;}
05- DEBUG_EVENT
typedef struct _DEBUG_EVENT { DWORD dwDebugEventCode; DWORD dwProcessId; DWORD dwThreadId; union { EXCEPTION_DEBUG_INFO Exception; CREATE_THREAD_DEBUG_INFO CreateThread; CREATE_PROCESS_DEBUG_INFO CreateProcessInfo; EXIT_THREAD_DEBUG_INFO ExitThread; EXIT_PROCESS_DEBUG_INFO ExitProcess; LOAD_DLL_DEBUG_INFO LoadDll; UNLOAD_DLL_DEBUG_INFO UnlaodDll; OUTPUT_DEBUG_STRING_INFO RipInfo; }u;}DEBUG_EVENT, *LPDEBUG_EVENT;
06- CREATE_THREAD_DEBUG_INFO
typedef struct _CREATE_THREAD_DEBUG_INFO { HANDLE hFile; HANDLE hProcess; HANDLE hThread; LPVOID lpBaseOfImage; DWORD dwDebugINfoFileOffset; DWORD nDebugInfoSize; LPVOID lpThreadLocalBase; LPTHREAD_START_ROUNTINE lpStartAddress; LPVOID lpImageName; WORD fUnicode; }CREATE_THREAD_DEBUG_INFO, *LPCREATE_THREAD_DEBUG_INFO;
07- CONTEXT
typedef struct _CONTEXT { DWORD ContextFlag; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; byte ExtendedRegisters[MAXTINUM_SUPPORTED_EXTENSION];}CONTEXT;
Debug:
Debug_notepad:
这里目的是观察栈的数据, 找到需要覆盖的数据缓冲区的位置
1. OD-> Attach notepad.exe-> 查找-> 所有模块中的名称-> WriteFile() f2后f9
2. 在notepad输入文本后, save
3. OD断在 WriteFile()
//注: 如果直接attach后 f9运行notepad卡死, 可以先输入文本后再attach
WriteFile()定义:4```BOOL WriteFile(){ HANDLE hFile; LPCVOID lpBuffer; DWORD nNumberOfBytesWritten; LPOVERLAPPED lpoverlapped;}```断下后, [esp]为返回地址, [esp+4]为hFile, [esp+8]为lpBuffer
0 0
- 《逆向工程核心原理》<04-30> 通过Debug修改代码实现API钩取的技术
- 《逆向工程核心原理》<04-33> 通过DLL注入修改API代码实现API钩取的技术
- 《逆向工程核心原理》<04-32> 通过DLL注入实现IAT钩取的技术
- 《逆向工程核心原理》<04-34> 高级全局API钩取
- 逆向工程核心原理读书笔记-API钩取之记事本小写转大写
- 逆向工程核心原理读书笔记-API钩取之计算器显示中文数字
- 逆向工程核心原理读书笔记-API钩取之隐藏进程(一)
- 逆向工程核心原理读书笔记-API钩取之隐藏进程(二)
- 逆向工程核心原理读书笔记-API钩取之IE浏览器连接控制
- 逆向工程核心原理读书笔记-API钩取之记事本小写转大写
- 逆向工程核心原理读书笔记-API钩取之计算器显示中文数字
- 逆向工程核心原理读书笔记-API钩取之隐藏进程(一)
- 逆向工程核心原理读书笔记-API钩取之隐藏进程(二)
- 逆向工程核心原理读书笔记-API钩取之IE浏览器连接控制
- 逆向工程核心原理读书笔记-代码注入
- 逆向工程核心原理读书笔记-代码注入
- 《逆向工程核心原理》》<06> 高级逆向分析技术
- 《逆向工程核心原理》<03-25> 通过修改PE加载DLL
- 最小费用最大流
- Linux下select, poll和epoll IO模型的详解
- Android Study 之玩转高德地图一部曲[实现显示地图以及定位功能]
- 【剑指offer】替换空格
- 学习springmvc的第三天(1:属性注入)
- 《逆向工程核心原理》<04-30> 通过Debug修改代码实现API钩取的技术
- Linux运维笔记-文档总结-ldap网络帐号
- 从输入URL到页面加载完成的过程详解
- C++类、对象等的基本概念
- 今天遇到这样一个面试题,在此记录一下
- codeforces 306C White, Black and White Again
- CCF NOI1142 质数
- 小菜鸟学习SpringMVC(一)
- 库(lib)与jar包有什么区别啊?