zoukankan      html  css  js  c++  java
  • [记事本API Hook] 调试器 勾取 WriteFile()Api

     调试器的工作原理:

      调试进程经过注册后,每当被调试者发生调试事件(DebugEvent)时,OS就会暂停其运行,并向调试器报告相应事件,然后调试器对相应事件进行处理,使被调试者继续运行。

      具体调试流程如下:

    1. 对想Hook的进程进行附加操作,使进程成为被调试者。
    2. Hook:将API起始的第一个字节修改成0xcc 也就是int3  断点。
    3. 调用相应API 时,控制权转移到调试器上。
    4. 执行需要的操作(参数,返回值等等)。
    5. 脱钩: 将0xcc 恢复成原本的字节(为了正常运行API)。
    6. Hook:再次修改为0xcc 也就是int3  断点(为了继续Hook)。
    7. 控制权返还给被调试者(进程本身)。

     

     

    源代码例子分析:

    1.首先判断是否附加成功,如果没有附加成功则提示错误信息,并退出;

    int main(int argc, char* argv[])
    {
        DWORD dwPID;    
    
        if( argc != 2 )  // argc=2,表示除了程序名外还有一个参数。 
        {
            printf("
    USAGE : hookdbg.exe <pid>
    ");
            return 1;
        }
    
        // Attach Process
        dwPID = atoi(argv[1]);  //atoi把字符串转换成整型数
        if( !DebugActiveProcess(dwPID) )   //如果调试器没有附加到的进程并且调试它
        {
            printf("Error:DebugActiveProcess(%d) failed!!!
    "
                   "Error Code = %d
    ", dwPID, GetLastError());
            return 1;
        }
    
        // 调试器循环
        DebugLoop();
    
        return 0;
    }

    2.当被调试者发生调试事件(DebugEvent)时,向调试器报告相应事件

    void DebugLoop()
    { 
        DEBUG_EVENT de;    //描述调试事件。
        DWORD dwContinueStatus;
    
        // 等待被调试者发生事件
        while( WaitForDebugEvent(&de, INFINITE) )
        { 
            dwContinueStatus = DBG_CONTINUE;  // DBG_CONTINUE表示已处理异常,继续执行在异常代码
    
            // 被调试进程生成或者附加事件(报告创建进程调试事件)
            if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
            {
                OnCreateProcessDebugEvent(&de);
            }
            // 异常事件(报告异常调试事件)
            else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )
            {
                if( OnExceptionDebugEvent(&de) )
                    continue;
            }
            // 被调试进程终止事件
            else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
            {
                // 被调试者终止-》调试器终止
                break;
            }
    
            // 再次运行被调试者
            ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
        }
    }

    3.被调试进程生成或者附加事件(报告创建进程调试事件)将API起始的第一个字节修改成0xcc 也就是int3  断点方便调试。

    /************************************************************************/
    /*    OnCreateProcessDebugEvent是CREATE_PROCESS_DEBUG_EVENT事件句柄被调试进程启动(或附加)时即调用执行该函数                                                                 */
    /************************************************************************/
    BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
    {
        // 获取WriterFile()API地址
        g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");
    
        // API Hook - WriteFile()
        //   更改第一个字节为0xCC(int3)
        //   (orginalbyte是g_chOrgByte的备份)
        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;
    }

    4.异常事件(报告异常调试事件)上面修改成了0xcc 则会出发int3异常,那么我们在接收到异常后则可以开始我们自己带代码处理,处理完成后恢复原有运行环境。

    //此段代码主要是我们将小写转换为大写字母的操作
    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 ) { //断点地址为WriterFile()API地址时 if( g_pfWriteFile == per->ExceptionAddress ) { // #1. Unhook // 将0xCC恢复成Originalbyte 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 : 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.分配零时缓冲区 lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1); memset(lpBuffer, 0, dwNumOfBytesToWrite+1); // #5.复制 WriteFile()缓冲区到临时缓冲区 ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, lpBuffer, dwNumOfBytesToWrite, NULL); printf(" ### original string ### %s ", lpBuffer); // #6. 将小写字母转换为大写字母 for( i = 0; i < dwNumOfBytesToWrite; i++ ) { if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A ) lpBuffer[i] -= 0x20; } printf(" ### converted string ### %s ", lpBuffer); // #7. 将变换或的缓冲区复制到Writefile()缓冲区 WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, lpBuffer, dwNumOfBytesToWrite, NULL); // #8. 释放临时缓冲区 free(lpBuffer); // #9. 将线程上下文的EIP更改为WriteFile()首地址 // (当前为writefile()+1的位置,INT3命令之后) ctx.Eip = (DWORD)g_pfWriteFile; 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; }

     以下为完整代码:

    #include "stdafx.h"
    
    LPVOID g_pfWriteFile = NULL;
    CREATE_PROCESS_DEBUG_INFO g_cpdi;     //被调试进程的EXE文件被映射到内存中的内存文件映射句柄
    BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;
    
    
    /************************************************************************/
    /*    OnCreateProcessDebugEvent是CREATE_PROCESS_DEBUG_EVENT事件句柄被调试进程启动(或附加)时即调用执行该函数                                                                 */
    /************************************************************************/
    BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
    {
        // 获取WriterFile()API地址
        g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");
    
        // API Hook - WriteFile()
        //   更改第一个字节为0xCC(int3)
        //   (orginalbyte是g_chOrgByte的备份)
        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;
    
        // 是INT3 断点时
        if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
        {
            //断点地址为WriterFile()API地址时
            if( g_pfWriteFile == per->ExceptionAddress )
            {
                // #1. Unhook
                //  将0xCC恢复成Originalbyte
                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 : 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.分配零时缓冲区
                lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);
                memset(lpBuffer, 0, dwNumOfBytesToWrite+1);
    
                // #5.复制 WriteFile()缓冲区到临时缓冲区 
                ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, 
                                  lpBuffer, dwNumOfBytesToWrite, NULL);
                printf("
    ### original string ###
    %s
    ", lpBuffer);
    
                // #6. 将小写字母转换为大写字母
                for( i = 0; i < dwNumOfBytesToWrite; i++ )
                {
                    if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A )
                        lpBuffer[i] -= 0x20;
                }
    
                printf("
    ### converted string ###
    %s
    ", lpBuffer);
    
                // #7. 将变换或的缓冲区复制到Writefile()缓冲区
                WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer, 
                                   lpBuffer, dwNumOfBytesToWrite, NULL);
                
                // #8. 释放临时缓冲区
                free(lpBuffer);
    
                // #9. 将线程上下文的EIP更改为WriteFile()首地址
                //  (当前为writefile()+1的位置,INT3命令之后)
                ctx.Eip = (DWORD)g_pfWriteFile;
                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;
    }
    
    void DebugLoop()
    { 
        DEBUG_EVENT de;    //描述调试事件。
        DWORD dwContinueStatus;
    
        // 等待被调试者发生事件
        while( WaitForDebugEvent(&de, INFINITE) )
        { 
            dwContinueStatus = DBG_CONTINUE;  // DBG_CONTINUE表示已处理异常,继续执行在异常代码
    
            // 被调试进程生成或者附加事件(报告创建进程调试事件)
            if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
            {
                OnCreateProcessDebugEvent(&de);
            }
            // 异常事件(报告异常调试事件)
            else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )
            {
                if( OnExceptionDebugEvent(&de) )
                    continue;
            }
            // 被调试进程终止事件
            else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
            {
                // 被调试者终止-》调试器终止
                break;
            }
    
            // 再次运行被调试者
            ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
        }
    }
    
    int main(int argc, char* argv[])
    {
        DWORD dwPID;    
    
        if( argc != 2 )  // argc=2,表示除了程序名外还有一个参数。 
        {
            printf("
    USAGE : hookdbg.exe <pid>
    ");
            return 1;
        }
    
        // Attach Process
        dwPID = atoi(argv[1]);  //atoi把字符串转换成整型数
        if( !DebugActiveProcess(dwPID) )   //如果调试器没有附加到的进程并且调试它
        {
            printf("Error:DebugActiveProcess(%d) failed!!!
    "
                   "Error Code = %d
    ", dwPID, GetLastError());
            return 1;
        }
    
        // 调试器循环
        DebugLoop();
    
        return 0;
    }
  • 相关阅读:
    java学习day35-三大框架-MyBatis(一)
    java学习day35-数据库事务
    java学习day34-Cookie和Session的区别
    java学习day34-Session技术
    Hadoop Java Versions
    如何下载和安装预构建的OpenJDK软件包
    How to install and configure samba on RHEL 8 / CentOS 8
    Win10系统下安装的Office版本的说明和注意事项
    Apache Hadoop
    nmap常用命令整理(长期更新)
  • 原文地址:https://www.cnblogs.com/hanhandaren/p/11498129.html
Copyright © 2011-2022 走看看