Windows Hook经验总结之一:API Hook方法汇总

本文总结了Windows Hook技术,包括静态HOOK和动态HOOK。静态HOOK在进程运行前进行,如修改IAT或函数入口点,动态HOOK则在运行时挂钩,如DLL注入。介绍了使用IAT挂钩本进程、改写入口点挂钩本进程以及挂钩其它进程的策略,详细讲解了各种方法的实现原理和注意事项。
摘要由CSDN通过智能技术生成

HOOK的目的是用我们自己的代码取代一些函数的代码以改变程序的行为。
静态HOOK:在进程运行前挂钩,采用用户级进程即可完成。比如:有些程序会在启动时需要原光盘,如果我们修改获取驱动类型的函数则可以从硬盘启动。
动态HOOK:挂钩系统进程(如服务)时要动态挂钩(运行时挂钩)。

静态HOOK即运行前挂钩

这里修改我们想要修改函数来自的物理模块(大多数时候是.exe或.dll)。在这里我们至少有3种可能的做法。
第一种可能是找到函数的入口点然后重写它的代码这会因为函数的大小而受限制,但只要能动态加载其它一些模块(API LoadLibrary)就足够了。内核函数(kernel32.dll)是通用的,Windows中每个进程都有其拷贝,如果知道哪些模块在某版本中会修改,就可以在一些API如LoadLibraryA中直接使用指针(因为相同Windows版本中kernel模块在内存中地址是固定的)。
第二种可能是在模块中被代替的函数只是原函数的扩展。可以选择修改开始的5个字节(CPU跳转指令长度)为跳转指令或者改写IAT。如果改跳转指令,那么将会改变指令执行流程转为执行我们的代码。如果调用了IAT记录被修改的函数,我们的代码能在调用结束后被执行。
第三种是修改整个模块。即创建自己的模块版本,它能够加载原始的模块并调用原始的函数,当然我们对这个不感兴趣,但重要的函数都是被更新的。这种方法对于有的模块过大有几百个导出函数的很不方便。

动态HOOK即运行时挂钩

在运行前挂钩通常都非常特殊,并且是在内部面向具体的应用程序(或模块)。如果我们更换了kernel32.dll或ntdll.dll里的函数(只在NT操作系统里),我们就能完美地做到在所有将要运行的进程中替换这个函数。但说来容易做起来却非常难,不但得考虑精确性和需要编写比较完善的新函数或新模块,更主要的问题是只有即将运行的进程才能被挂钩(要挂钩所有进程只能重启电脑)。另一个问题是如何进入这些文件,因为NT操作系统保护了它们。比较好的解决方法是在进程运行时挂钩(只针对能够写入它们内存的进程)。为了能写入进程可使用API函数WriteProcessMemory

使用IAT挂钩本进程

这里有很多种可能性。首先介绍如何用改写IAT挂钩函数的方法。接下来这张图描述了PE文件的结构:
这里写图片描述

这里比较重要的是.idata部分的导入地址表(IAT:Import Address Table)。这个部分包含了导入的相关信息和函数地址。有一点很重要,我们必须知道PE文件是如何创建的。当在编程语言里间接调用任意API(这意味着我们是用函数的名字来调用它,而不是用它的地址),编译器并不直接把调用连接到模块,而是用jmp指令连接调用到IAT,IAT在系统把进程调入内存时时会由进程载入器填满。这就是我们可以在两个不同版本的Windows里使用相同的二进制代码的原因,虽然模块可能会加载到不同的地址。进程载入器会在程序代码里调用所使用的IAT里填入直接跳转的jmp指令。所以只要能在IAT里找到想要挂钩的指定函数,就能很容易改变那里的jmp指令并重定向代码到新的地址。完成之后每次调用都会执行新的代码了。这种方法的缺点是经常有很多函数要被挂钩(比如:要在搜索文件的API中改变程序的行为就得修改函数FindFirstFileFindNextFile,但这些函数都有ANSI和UNICODE版本,所以就不得不修改FindFirstFileAFindFirstFileWFindNextFileAFileNextFileW的IAT地址。但还有其它类似的函数如FindFirstFileExA和它的UNICODE版本FindFirstFileExW,也都是由前面提到的函数调用的。我们知道FindFirstFileW调用FindFirstFileExW,但这是直接调用,而不是使用IAT。再比如说ShellAPI的函数SHGetDesktopFolder也会直接调用FindFirstFilwWFindFirstFileExW)
我们通过使用imagehlp.dll里的ImageDirectoryEntryToData来很容易地找到IAT。

    PVOID ImageDirectoryEntryToData(
        IN LPVOID           Base,    
        IN BOOLEAN      MappedAsImage,    
        IN USHORT           DirectoryEntry,    
        OUT PULONG      Size    
    );

在这里Base参数可以用我们程序的Instance(Instance通过调用GetModuleHandle获得):
hInstance = GetModuleHandleA(NULL);
DirectoryEntry我们可以使用恒量IMAGE_DIRECTORY_ENTRY_IMPORT

 #define IMAGE_DIRECTORY_ENTRY_IMPORT 1

函数的结果是指向第一个IAT记录指针。IAT的所有记录是由IMAGE_IMPORT_DESCRIPTOR定义的结构。所以函数结果是指向IMAGE_IMPORT_DESCRIPTOR的指针。

 typedef struct _IMAGE_THUNK_DATA {
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值