《逆向工程核心原理》<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
原创粉丝点击