码迷,mamicode.com
首页 > 编程语言 > 详细

C语言实现简单的inline hook(windows api)

时间:2018-01-21 11:04:28      阅读:215      评论:0      收藏:0      [点我收藏+]

标签:xxxxxx   read   dwr   指令   mem   main   ret   结果   png   

  本人的第一篇随笔,简单介绍一下经典的inline hook技术。

  钩取(Hooking)是一种截取信息、更改程序流向、添加新功能的技术。钩取技术多种多样,其中钩取Win32 API的技术被称为API钩取。它与消息钩取共同广泛应用于用户模式(ring3)。这里我以MessageBoxW这个简单的API为例,实现简单的程序自身inline hook。

  MessageBoxW函数原型: 

1 int WINAPI MessageBoxW(
2         _In_opt_ HWND hWnd,            // 父窗口句柄
3         _In_opt_ LPCWSTR lpText,       // 文本
4         _In_opt_ LPCWSTR lpCaption,    // 标题
5         _In_ UINT uType);              // 按键类型组合

 

  所谓inline hook,就是将API代码的前5个字节修改为JMP XXXXXXXX指令来钩取API。调用执行被钩取的API时,(修改后的)JMP XXXXXXXX指令就会被执行,转向控制hooking函数。

  hook之前的API代码开头

  技术分享图片

  hook之后的API代码开头

  技术分享图片

  可以看出,在hook之后,当用户调用API时,会直接跳转到hook处理函数,在hook处理函数中,可以进行其他操作,但需要保证函数原型及参数一致,以保证堆栈平衡。如果在hooking函数内部要调用原API函数,则在调用前需要进行“脱钩”处理,否则如果直接调用,API开头又是一个跳转指令,将陷入死循环。在调用之后再进行hook,方便下次钩取,以上就是hook的大致流程。

  详细代码如下:  

#include <windows.h>
#include <stdio.h>

BYTE g_pOrgMSGBOXW[5] = { 0, };        // 用于存储API开头的5字节

// hooking函数原型,保证与原API(MessageBoxW)一致
int WINAPI NewMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);

// 函数指针
typedef int(WINAPI *PFMESSAGEBOXW)(
    HWND hWnd,
    LPCWSTR lpText,
    LPCWSTR lpCaption,
    UINT uType
    );

BOOL inlinehook(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew, PBYTE pOrgBytes)
{
    HMODULE hModule = NULL;
    FARPROC pFunc = NULL;    // API函数指针
    BYTE pBuf[5] = { 0, };    // 形成跳转指令的5字节代码
    PBYTE pByte = NULL;
    DWORD dwOldProtect = 0;
    DWORD dwJmpOffSet = 0;    // 跳转的偏移值(= 新函数地址 - (原函数地址 + 5))

    // 获取目标模块句柄(user32.dll)
    if (!(hModule = GetModuleHandleA(szDllName)))
    {
        printf("GetModuleHandleA error: %d\n", GetLastError());
        return FALSE;
    }

    // 获取要钩取的 API 地址
    if (!(pFunc = GetProcAddress(hModule, szFuncName)))
    {
        printf("GetProcAddress error: %d\n", GetLastError());
        return FALSE;
    }

    // 修改内存属性,因为这边要修改内存中的代码数据
    VirtualProtect((LPVOID)pFunc, 5, PAGE_READWRITE, &dwOldProtect);

    // 如果已经被 hook, 则失败
    pByte = (PBYTE)pFunc;
    if (pByte[0] == 0xE9)
    {
        return FALSE;
    }
    memcpy(pOrgBytes, pFunc, 5);    // 保存原代码

    pBuf[0] = 0xE9;                    // JMP XXXXXXXX指令的第一个字节是0xE9
    dwJmpOffSet = (DWORD)pfnNew - ((DWORD)pFunc + 5);    // 计算跳转的偏移值
    memcpy(&pBuf[1], &dwJmpOffSet, 4);
    memcpy(pFunc, pBuf, 5);            // 修改原API地址处的代码

    VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

    return TRUE;
}

BOOL unhook(LPCSTR szDllName, LPCSTR szFuncName, PBYTE pOrgBytes)
{
    HMODULE hModule = NULL;
    FARPROC pFunc = NULL;
    PBYTE pByte = NULL;
    DWORD dwOldProtect = 0;

    if (!(hModule = GetModuleHandleA(szDllName)))
    {
        printf("GetModuleHandleA error: %d\n", GetLastError());
        return FALSE;
    }

    if (!(pFunc = GetProcAddress(hModule, szFuncName)))
    {
        printf("GetProcAddress error: %d\n", GetLastError());
        return FALSE;
    }

    VirtualProtect((LPVOID)pFunc, 5, PAGE_READWRITE, &dwOldProtect);

    // 如果未被 hook, 则失败
    pByte = (PBYTE)pFunc;
    if (pByte[0] != 0xE9)
    {
        return FALSE;
    }

    memcpy(pFunc, pOrgBytes, 5);    // 将原API的代码还原

    VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect);

    return TRUE;
}

int WINAPI NewMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
    int iRet = 0;
    FARPROC pFunc = NULL;

    // 为调用原API,这里需要进行“脱钩”
    unhook("user32.dll", "MessageBoxW", g_pOrgMSGBOXW);

    if (!(pFunc = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxW")))
    {
        printf("GetProcAddress error: %d\n", GetLastError());
        return FALSE;
    }

    // 调用原API
    iRet = ((PFMESSAGEBOXW)pFunc)(hWnd, L"你被 hook 了!", lpCaption, uType);
    // 再进行hook,方便下次钩取
    inlinehook("user32.dll", "MessageBoxW", (PROC)NewMessageBoxW, g_pOrgMSGBOXW);

    return iRet;
}

int main()
{
    inlinehook("user32.dll", "MessageBoxW", (PROC)NewMessageBoxW, g_pOrgMSGBOXW);
    MessageBoxW(NULL, L"这是正常的", L"提示", MB_OK);

    unhook("user32.dll", "MessageBoxW", g_pOrgMSGBOXW);
    MessageBoxW(NULL, L"这是正常的", L"提示", MB_OK);

    return 0;
}

运行结果:

技术分享图片

技术分享图片

  这里只是程序自身自行hook,没有用到dll注入等技术,以后会写上。

C语言实现简单的inline hook(windows api)

标签:xxxxxx   read   dwr   指令   mem   main   ret   结果   png   

原文地址:https://www.cnblogs.com/bubbletea/p/8322461.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!