天马阁

 找回密码
 立即注册
                                        →→→→→→→→→→→→ 1点击查看所有VIP教程目录长列表(总教程数269个) 2办理VIP详情进入 ←←←←←←←←←←←←
1 x64CE与x64dbg入门基础教程 7课 已完结 2 x64汇编语言基础教程 16课 已完结 3 x64辅助入门基础教程 9课 已完结 4 C++x64内存辅助实战技术教程 149课 已完结
5 C++x64内存检测与过检测技术教程 10课 已完结 6 C+x64二叉树分析遍历与LUA自动登陆教程 19课已完结 7 C++BT功能原理与x64实战教程 29课 已完结 8 C+FPS框透视与自瞄x64实现原理及防护思路 30课完结
64驱?封? 9 64反驱? 10 64位V? 11 绝? 12 ???课?
13 64透 ? 14 64U ? 15 64Q ? 16 64功 ?
17 64U ? 18 64模 ? 19 64多 ? 20 64网 ?
21 64注 ? 22 64火 ? 23 64棋 ? 24 64自二链L?
25 64破 ? VIP会员办理QQ: 89986068   
【请先加好友,然后到好友列表双击联系客服办理,不然可能无法接受到信息。】
27 加入2000人交流群637034024 3 28 免责声明?
查看: 1382|回复: 0

Ring3下通用API HOOK源码

[复制链接]

9

主题

1

回帖

12

积分

编程入门

Rank: 1

天马币
18
发表于 2024-3-5 09:26:03 | 显示全部楼层 |阅读模式

说道API HOOK ,这已经是老掉牙的技术了,但是它的实用性却是不能忽视的。虽然在网上这类的文章很多,但大多数的实现方法是将一个函数的前5字节直接改为 jmp XXXX 。这种方法虽然简单可行,但是不通用,因为一些特殊函数的前5字节不是完整的,如果直接修改,就会截断指令,导致出错。以前在一个论坛上看到过一个用反汇编引擎实现的通用例子,感觉想法很好,于是自己也写了一个。其中用到了一个头文件LDasm.h(里面实际就是一个轻量级的反汇编引擎)。

第一个函数用于对指定API进行HOOK,并返回一个进入原函数的代理函数地址。




PVOID HookFunction(PCHAR ModuleName,PCHAR FunctionName,PVOID pNewFunction)
{

    UCHAR JumpCode[6] = {0x68,0x00,0x00,0x00,0x00,0xC3};     //push xxxxxxxx ret
     UCHAR JumpBackCode[6] = {0x68,0x00,0x00,0x00,0x00,0xC3}; //push xxxxxxxx ret
      
    PVOID pSourceFunction = GetProcAddress(GetModuleHandleA(ModuleName),FunctionName);
    if (!pSourceFunction)return NULL;

    *(ULONG *)((ULONG)JumpCode + 1) = (ULONG)pNewFunction;

    PVOID pProxyFunction = 0;
    PUCHAR pOpCode;
    ULONG BackupLength = 0;
     
    while (BackupLength < 6)
    {
        BackupLength += SizeOfCode((PVOID)((ULONG)pSourceFunction + BackupLength),&pOpCode);
    }

    pProxyFunction = VirtualAlloc(NULL,BackupLength + 6,MEM_RESERVE|MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    if (!pProxyFunction)return NULL;

    *(ULONG *)((ULONG)JumpBackCode + 1) = (ULONG)pSourceFunction + BackupLength;
     
    RtlCopyMemory(pProxyFunction,pSourceFunction,BackupLength);
    RtlCopyMemory((PVOID)((ULONG)pProxyFunction + BackupLength),JumpBackCode,6);

    FlushInstructionCache((HANDLE)-1,pProxyFunction,BackupLength + 6);

    DWORD OldProtect = 0;
    VirtualProtect(pSourceFunction,6,PAGE_EXECUTE_READWRITE,&OldProtect);

    RtlCopyMemory(pSourceFunction,JumpCode,6);

    VirtualProtect(pSourceFunction,6,OldProtect,&OldProtect);

    FlushInstructionCache((HANDLE)-1,pSourceFunction,6);

    return pProxyFunction;
}

注意,在JumpCode和JumpBackCode中,我使用的是push+ret的方式进行跳转(跟网上一牛人学的),其目的是不用计算相对偏移,也不容易被一些安全工具发现。其中还有一个重要的函数——SizeOfCode(),它就是反汇编引擎里的一个函数,用于计算一个完整指令的长度,防止指令被截断,它是实现通用性的关键。

第二个函数用于恢复被HOOK的函数,它需要HookFunction函数返回的代理函数地址作为参数。

BOOL UnHookFunction(PCHAR ModuleName,PCHAR FunctionName,PVOID pProxyFunction)
{

    PVOID pSourceFunction = GetProcAddress(GetModuleHandleA(ModuleName),FunctionName);
    if (!pSourceFunction)return FALSE;
     
    DWORD OldProtect = 0;
    VirtualProtect(pSourceFunction,6,PAGE_EXECUTE_READWRITE,&OldProtect);

    RtlCopyMemory(pSourceFunction,pProxyFunction,6);

    VirtualProtect(pSourceFunction,6,OldProtect,&OldProtect);

    FlushInstructionCache((HANDLE)-1,pSourceFunction,6);

    BOOL res = VirtualFree(pProxyFunction,NULL,MEM_RELEASE);
    if (!res)return FALSE;

    return TRUE;

这个函数很简单,只是做一些清理工作。

最后,我还写了个函数用于检查某个函数是否被HOOK,但只能检查出两种形式的HOOK,一种jmp形式,一种push+ret形式,不过也够用了。

BOOL IsFuncHooked(PCHAR ModuleName,PCHAR FunctionName)
{
    PVOID pFunction = GetProcAddress(GetModuleHandleA(ModuleName),FunctionName);
     
    if (!pFunction)return FALSE;

    DWORD OldProtect = 0;
    VirtualProtect(pFunction,6,PAGE_EXECUTE_READWRITE,&OldProtect);

    UCHAR chas = *(UCHAR *)((ULONG)pFunction + 5);

    if (((*(UCHAR *)pFunction == 0x68)&&(*(UCHAR *)((ULONG)pFunction + 5) == 0xC3))||(*(UCHAR *)pFunction == 0xEB)||(*(UCHAR *)pFunction == 0xEA))
    {
        VirtualProtect(pFunction,6,OldProtect,&OldProtect);
        return TRUE;
    }
    else
    {
        VirtualProtect(pFunction,6,OldProtect,&OldProtect);
        return FALSE;
    }
     
}
好了,以上就是主要的三个函数。在说一下,我没有采用先HOOK,到调用原函数时又恢复HOOK的方法。因为这种方法效率不高,而且还不安全(尤其在多线程环境下)。所以我将原函数的前X个字节保存到另一个地方(即返回的代理函数地址),并在其后面加上回条指令(即JumpBackCode),跳到原函数的第X个指令后继续执行,这样就避免了对原函数的反复读写。

最后,有不足之处还请高手指点。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

天马阁|C/C++辅助教程|安卓逆向安全| 论坛导航|免责申明|Archiver||网站地图
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表天马阁立场!
任何人不得以任何方式翻录、盗版或出售本站视频,一经发现我们将追究其相关责任!
我们一直在努力成为最好的编程论坛!
Copyright© 2010-2021 All Right Reserved.
快速回复 返回顶部 返回列表