记事本WriteFile()API钩取

工作原理

首先了解WriteFile()API

BOOL WriteFile(

[in] HANDLE hFile, //文件或设备的句柄

[in] LPCVOID lpBuffer, //指向包含要写入文件或设备的数据的缓冲区的指针。

[in] DWORD nNumberOfBytesToWrite,//要写入文件或设备的字节数。

[out, optional] LPDWORD lpNumberOfBytesWritten,

//指向变量的指针,该变量接收使用同步 hFile 参数时写入的字节数。写入文件集 在执行任何工作或错误检查之前,此值为零。为此使用 NULL 参数,如果这是异步操作,以避免潜在的错误结果

[in, out, optional] LPOVERLAPPED lpOverlapped

);

记事本WriteFile()API钩取_第1张图片

返回值

如果函数成功,则返回值为非零值 (TRUE)。

如果函数失败或异步完成,则返回值为零 ()。

然后在OD中打开notepad.exe,在运行程序之前,在WriteFile()API处下断点

首先查找模块中的WriteFile()API,右键——search for ——name in current module

记事本WriteFile()API钩取_第2张图片

下断点

记事本WriteFile()API钩取_第3张图片

然后运行notepad,在其中输入内容并保存,保存时会触发在writeFile处的断点。

记事本WriteFile()API钩取_第4张图片

在堆栈中找到数据缓冲区的指针Buffer,然后在数据窗口中跟随

记事本WriteFile()API钩取_第5张图片

可以看到之前保存在记事本中的内容

记事本WriteFile()API钩取_第6张图片

接下来要做的就是钩取WriteFile()API,用指定字符串覆盖数据缓冲区的字符串。

具体代码和解析如下:

#include "windows.h"
#include "stdio.h"

LPVOID g_pfWriteFile = NULL;// 获取WriteFile() API地址,
CREATE_PROCESS_DEBUG_INFO g_cpdi;
BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;

BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
{
    // 获取WriteFile() API地址,此时获取的是调试进程的内存地址,因为是系统dll
    g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");

    // API Hook - WriteFile()
    // 更改第一个字节为0xCC
    //  originalbyte是g_ch0rgByte备份
    //(目标,源,长度)
    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;

    // 是断点异常时
    if (EXCEPTION_BREAKPOINT == per->ExceptionCode)
    {
        // 断点地址为WriteFile()API地址时
        if (g_pfWriteFile == per->ExceptionAddress)
        {
            // #1. Unhook
            // 将0xCC恢复为original byte
            WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
                &g_chOrgByte, sizeof(BYTE), NULL);

            // #2. 获取线程上下文
            ctx.ContextFlags = CONTEXT_CONTROL;
            //g_cpdi.hThread是被调试者的主线程句柄
            GetThreadContext(g_cpdi.hThread, &ctx);

            // #3. 获取WriteFile()的param2、3值 
            // 分别是数据缓冲区的地址和数据缓冲区的大小
            //   函数参数存在于进程的栈,通过线程上下文的context结构体中的ESP成员可以获取
            //   param 2 : ESP + 0x8
            //   param 3 : ESP + 0xC
            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8),
                &dwAddrOfBuffer, sizeof(DWORD), NULL);
            // &dwAddrOfBuffer指的是被调试者虚拟内存空间中的地址
            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC),
                &dwNumOfBytesToWrite, sizeof(DWORD), NULL);

            // #4.分配临时缓冲区
            lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite + 1);
            //将 lpBuffer所指向区域的前dwNumOfBytesToWrite+1个字节用0填充
            memset(lpBuffer, 0, dwNumOfBytesToWrite + 1);

            // #5.复制WriteFile()缓冲区到临时缓冲区
            ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
                lpBuffer, dwNumOfBytesToWrite, NULL);
            printf("\n原始\n%s\n", lpBuffer);

            // #6. 将小写字母转换为大写字母
            for (i = 0; i < dwNumOfBytesToWrite; i++)
            {
                if (0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A)
                    lpBuffer[i] -= 0x20;
            }

            printf("\n###目前 ###\n%s\n", 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))
    {
        //发生调试事件后,WaitForDebugEvent()API 会将相关信息设置到DEBUG_EVENT结构体对象
        dwContinueStatus = DBG_CONTINUE;
        //若处理正常,则为DBG_CONTINUE
        // 若无法处理,或希望在SEH中进行处理,DBG_EXCEPTION_NOT_HANDLED
        // 
        //被调试者进程生成或者附加事件
        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)
    {
        //告诉使用者该exe的正确用法
        printf("\nUSAGE : hookdbg.exe \n");
        return 1;
    }

    // Attach Process,字符串转化成int型数据
    dwPID = atoi(argv[1]);
    //使调试器能够附加活动进程并对其进行调试
    if (!DebugActiveProcess(dwPID))
    {
        printf("DebugActiveProcess failed!!!\n");
        return 1;
    }

    // 调试器循环
    DebugLoop();
  

    return 0;
}

others:

win7中快捷键打开任务管理器:

win+r——taskmgr

右键下方任务栏

Ctrl +Alt+del

你可能感兴趣的:(windows)