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 文件
    
  • 相关阅读:
    JWT(json web token)--.JwtBearer jwt
    NET LOG日志的使用以及设置文件大小和数量限制
    Xshell、MobaXterm等5款主流SSH客户端对比
    RabbitMQ使用交换机处理异步消息队列------发布与订阅
    RabbitMQ使用交换机处理异步消息队列------分布式事务处理案例
    RabbitMQ使用交换机处理异步消息队列案例
    SqlServer Microsoft SQL Server 2005 使用复制(发布、订阅)的方式使主从数据库同步
    Hangfire-执行定时任务框架
    Catalina 默认使用zsh了,你可习惯
    你几点睡,就是什么命
  • 原文地址:https://www.cnblogs.com/lex-shoukaku/p/13739959.html
Copyright © 2011-2022 走看看