逆向工程核心原理——第三十章

API钩取——钩取记事本WriteFile()API

打开myhookdbg.exe,按照提示输入PID:

然后在记事本上输入字符,然后保存:


输入的字符被截取,并且变为了大写,API钩取成功。

最后附上源码,这个源码是在博客园的大神修改过可以在x64系统上运行(书上的系统为x86),我的编译器为VS2019.

// myhookdbg.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>
#include <tchar.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <shlobj.h>


LPVOID g_pfWriteFile = NULL;
CREATE_PROCESS_DEBUG_INFO g_cpdi;
BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;
BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
{
    // 查找API地址
    HMODULE dll = GetModuleHandleA("kernel32.dll");
    g_pfWriteFile = GetProcAddress(dll, "WriteFile");
    //g_pfWriteFile =(LPVOID)0x7ffca76b2500;
    printf("kernel32.dll基址:%I64x\n", dll);
    printf("WriteFile地址:%I64x\n", (DWORD64)g_pfWriteFile);
    // API Hook - WriteFile()
    //   将byte更改为0xCC (INT 3)
    //  orginal byte是备份
    memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
    ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
        &g_chOrgByte, sizeof(BYTE), NULL);
    printf("原api调用处字节:%x\n", g_chOrgByte);
    WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
        &g_chINT3, sizeof(BYTE), NULL);
    BYTE arr[10];
    ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
        arr, sizeof(BYTE) * 10, NULL);
    printf("修改后:\n");
    for (int i = 0; i < 10; i++)
        printf("%02x ", arr[i]);
    printf("\n");
    return TRUE;
}

BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)
{
    CONTEXT ctx;
    PBYTE lpBuffer = NULL;
    DWORD i;
    ULONG_PTR dwNumOfBytesToWrite, dwAddrOfBuffer;
    PEXCEPTION_RECORD64 per = (PEXCEPTION_RECORD64)&pde->u.Exception.ExceptionRecord;

    // BreakPoint exception (INT 3) 的情况
    if (EXCEPTION_BREAKPOINT == per->ExceptionCode)
    {
        // 如果BP地址是WriteFile,
        if ((DWORD64)g_pfWriteFile == per->ExceptionAddress)
        {
            printf("发现writefile调用,地址:%I64X\n", g_pfWriteFile);
            // #1. Unhook
            //   如果BP地址是WriteFile(用0xCC覆盖的部分返回original byte)
            WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
                &g_chOrgByte, sizeof(BYTE), NULL);
            BYTE arr[10];
            ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
                arr, sizeof(BYTE) * 10, NULL);
            printf("恢复后:");
            for (int i = 0; i < 10; i++)
                printf("%02x ", arr[i]);
            printf("\n");
            // #2. 寻求Thread Context
            //ctx.ContextFlags = CONTEXT_CONTROL;SegSs栈段, Rsp, SegCs代码段, Rip, and EFlags
            ctx.ContextFlags = CONTEXT_FULL;//要获得全部寄存器
            GetThreadContext(g_cpdi.hThread, &ctx);
            LPOVERLAPPED arg5_lpOverlapped = NULL;
            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Rsp + 0x28), &arg5_lpOverlapped, sizeof(DWORD), NULL);
            printf("寄存器数据:\n");
            //printf("rax:%I64x\n", ctx.Rax);
            //printf("rbx:%I64x\n", ctx.Rbx);
            printf("rcx:%I64x\n", ctx.Rcx);
            printf("rdx:%I64x\n", ctx.Rdx);
            printf("r8:%I64x\n", ctx.R8);
            printf("r9:%I64x\n", ctx.R9);
            printf("arg5:%I64x\n", arg5_lpOverlapped);


            // #3.获取param 2和3的值
            //   x86函数参数存在于此进程的栈中;x64 fastcall 前4个参数存在寄存器中
            //   LPCVOID lpBuffer,//数据缓存区指针 rdx
            //    DWORD   nNumberOfBytesToWrite,//你要写的字节数 r8
            //   param 2 : rdx
            //   param 3 : r8

            //ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.esp + 0x8),&dwAddrOfBuffer, sizeof(DWORD), NULL);
            //ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.esp + 0xC),&dwNumOfBytesToWrite, sizeof(DWORD), NULL);
            dwAddrOfBuffer = ctx.Rdx;
            dwNumOfBytesToWrite = ctx.R8;
            //printf("%s\n", dwAddrOfBuffer);
            // #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);
            //ctx.Rdx=
            // #8. 取消临时缓冲
            free(lpBuffer);

            // #9.将Thread Context的EIP更改为WriteFile()
            //   (现在已经过WriteFile() + 1)

            //BOOL WriteFile(
            //    HANDLE  hFile,//文件句柄  rcx
            //    LPCVOID lpBuffer,//数据缓存区指针 rdx
            //    DWORD   nNumberOfBytesToWrite,//你要写的字节数 r8
            //    LPDWORD lpNumberOfBytesWritten,//用于保存实际写入字节数的存储区域的指针 r9
            //    LPOVERLAPPED lpOverlapped//OVERLAPPED结构体指针 rsp+0x20    [call 前rsp 0 8 10 18 20 28]
            //);
            /*ctx.Rdx += 1;
            ctx.R8 -= 1;*/
            ctx.Rip = (DWORD64)g_pfWriteFile;
            //ctx.Eip = (DWORD)g_pfWriteFile;
            SetThreadContext(g_cpdi.hThread, &ctx);

            // #10. Debuggee 运行被调试进程
            ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
            Sleep(0);
            printf("continue\n");
            // #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;

    // 从Debuggee等待event的到来。
    while (WaitForDebugEvent(&de, INFINITE))
    {
        dwContinueStatus = DBG_CONTINUE;

        // 创建Debuggee进程或attach事件
        if (CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode)
        {
            OnCreateProcessDebugEvent(&de);
            printf("finish creat debuggee\n");
        }
        // 异常活动
        else if (EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode)
        {
            if (OnExceptionDebugEvent(&de))
                continue;
        }
        // Debuggee进程退出事件
        else if (EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode)
        {
            // debuggee结束-> debugger结束
            break;
        }

        // Debuggee的恢复执行。
        ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
    }
}

int main()
{
    //system("tasklist");
    system("tasklist | findstr notepad");
    char pid[10];
    printf("输入要注入的进程pid:\n");
    scanf_s("%s", pid, 10);

    DWORD dwPID;
    dwPID = atoi(pid);
    if (!DebugActiveProcess(dwPID))
    {
        printf("DebugActiveProcess(%d) failed!!!\n"
            "Error Code = %d\n", dwPID, GetLastError());
        return 1;
    }

    // 调试器循环
    DebugLoop();
    system("pause");
    return 0;
    /*std::cout << "Hello World!\n"; */
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门提示:
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
posted @ 2020-09-27 15:46  Kylimi  阅读(295)  评论(0编辑  收藏  举报