zoukankan      html  css  js  c++  java
  • 逆向工程核心原理——第三十章

    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
    ", dll);
        printf("WriteFile地址:%I64x
    ", (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
    ", 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("修改后:
    ");
        for (int i = 0; i < 10; i++)
            printf("%02x ", arr[i]);
        printf("
    ");
        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
    ", 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("
    ");
                // #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("寄存器数据:
    ");
                //printf("rax:%I64x
    ", ctx.Rax);
                //printf("rbx:%I64x
    ", ctx.Rbx);
                printf("rcx:%I64x
    ", ctx.Rcx);
                printf("rdx:%I64x
    ", ctx.Rdx);
                printf("r8:%I64x
    ", ctx.R8);
                printf("r9:%I64x
    ", ctx.R9);
                printf("arg5:%I64x
    ", 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
    ", dwAddrOfBuffer);
                // #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);
                //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
    ");
                // #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
    ");
            }
            // 异常活动
            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:
    ");
        scanf_s("%s", pid, 10);
    
        DWORD dwPID;
        dwPID = atoi(pid);
        if (!DebugActiveProcess(dwPID))
        {
            printf("DebugActiveProcess(%d) failed!!!
    "
                "Error Code = %d
    ", dwPID, GetLastError());
            return 1;
        }
    
        // 调试器循环
        DebugLoop();
        system("pause");
        return 0;
        /*std::cout << "Hello World!
    "; */
    }
    
    // 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
    // 调试程序: F5 或调试 >“开始调试”菜单
    
    // 入门提示:
    //   1. 使用解决方案资源管理器窗口添加/管理文件
    //   2. 使用团队资源管理器窗口连接到源代码管理
    //   3. 使用输出窗口查看生成输出和其他消息
    //   4. 使用错误列表窗口查看错误
    //   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
    //   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
    
  • 相关阅读:
    数学+高精度 ZOJ 2313 Chinese Girls' Amusement
    最短路(Bellman_Ford) POJ 1860 Currency Exchange
    贪心 Gym 100502E Opening Ceremony
    概率 Gym 100502D Dice Game
    判断 Gym 100502K Train Passengers
    BFS POJ 3278 Catch That Cow
    DFS POJ 2362 Square
    DFS ZOJ 1002/HDOJ 1045 Fire Net
    组合数学(全排列)+DFS CSU 1563 Lexicography
    stack UVA 442 Matrix Chain Multiplication
  • 原文地址:https://www.cnblogs.com/lex-shoukaku/p/13739959.html
Copyright © 2011-2022 走看看