IAT Hook
通过修改IAT的方式实现对Notepad的WriteFile()函数钩取,使得保存的文件中所有小写字母变成大写字母。
文章目录
实验环境
VMware Workstation Pro 16.2.4 + Windows 7(32位)+ Microsoft Visual Studio 2010 Express,环境配置参考《VM安装windows7 32》
实验原理
- 钩取(Hooking)是一种更改程序流向,以获取程序运行时的信息或使程序具备新功能的技术。它允许程序在运行时注入自己的代码,以便在特定的事件或条件下执行自己的逻辑。
- 一方面,Hook技术可以用于许多不同的应用程序场景,例如调试、性能分析、安全保护等。在计算机系统中,Hook技术通常用于调试和性能分析。例如,调试器可以使用Hook技术来捕获程序的运行时信息,例如函数调用、变量值等,以便开发人员可以更好地了解程序的行为。性能分析器可以使用Hook技术来监视程序的运行时行为,例如函数调用次数、CPU使用率等,以便开发人员可以优化程序的性能。Hook技术还可以用于安全保护。例如,防病毒软件可以使用Hook技术来检测和防止恶意软件的运行。Hook技术可以检测程序的行为是否异常,并采取相应的安全措施,例如终止程序的执行或采取防护措施。
- 另一方面,它允许攻击者在运行时拦截和修改系统中的函数或API调用,从而影响系统的行为和安全性。Hook技术通常用于攻击和防御,例如在恶意软件中,攻击者可能会使用Hook技术来劫持系统中的API调用,从而绕过防御机制,或者在系统中植入恶意代码,从而影响系统的安全性和稳定性。Hook技术可以用于各种类型的系统,包括操作系统、应用程序、网络和通信设备等。
- Windows OS中,用户程序要使用系统资源(内存、文件、网络、视频、音频等)时无法直接访问这些资源都是由Windows OS直接管理的,出于多种考虑(稳定性、安全、效率等),Windows OS禁止用户程序直接访问系统资源(内存、文件、网络、视频、音频等) 。需要使用这些资源时,用户程序必须向系统内核( Kernel )申请,申请的方法就是使用微软提供的Win32 API(或是其他OS开发公司提供的API )。没有API函数,则不能创建出任何有意义的应用程序(因为它不能访问进程、线程、内存、文件、网络、注册表、图片、音频以及其他系统资源)。使用API函数时OS将会逐级向下调用系统函数,处理相关请求后,然后再返回到请求访问资源的线程。
- PE文件双桥结构、导入函数表INT、导入地址表IAT:
如图所示,在PE文件加载前,INT和IAT都指向IMAGE_IMPORT_BY_NAME。在磁盘中时,INT和IAT都一样,在内存中指向的是IMAGE_IMPORT_BY_NAME数组的RVA,存放着函数真实地址。
但是在PE加载的时候,双桥结构会断裂,IAT 会被PE加载器重写,PE加载器先搜索INT,PE加载器迭代搜索INT数组中的每个指针,找出 INT所指向的IMAGE_IMPORT_BY_NAME结构中的函数在内存中的真正的地址,并把它替代原来IAT中的值。此时IAT中存放的就是函数的真实地址。
PE中导入表,也就是IMAGE_IMPORT_DESCRIPTOR结构在一个数组中,意味着一个PE文件中可能有多个导入表,每个导入表中只有一个OriginalFirstThunk和FirstThunk,但是他们指向的IMAGE_THUNK_DATA是一个数组,数组的元素个数代表函数的个数,如果是IMAGE_THUNK_DATA中的AddressOfData字段生效,它指向的是一个IMAGE_IMPORT_BY_NAME数组,这个数组中的元素个数跟IMAGE_THUNK_DATA中的可能不一样,因为有的函数没有名字。 - 在进行IAT Hook之前需要根据IID结构体和ITD结构体找到想要Hook的原函数的地址(具体到代码实现就是两个for循环)。IMAGE_IMPORT_DESCRIPTOR(IID)结构体记录着PE文件要导入哪些库文件,每个导入的DLL都对应一个IID结构体,通过IID可以找到原函数在哪个DLL中被导入,该结构体定义如下:
INT和IAT指向一个相同类型的 IMAGE_THUNK_DATA(ITD)结构体数组,该数组以一个全0结构体结尾,该结构体与导入函数相对应,有多少个ITD结构体就有多少个函数被导入,通过修改IDT结构体中联合体u1成员变量Function的值为我们自己定义的钩取函数(如上图 PE装载后),即可实现IAT Hook,IDT结构体定义如下:
实验过程
采用创建远程线程的方式注入dll
因为要操作目标进程Notepad.exe的内容,所以注入一个dll会比较方便,注入的dll会有目标进程的全部权限,而且也能很好的把Hook函数放到目标进程中去,代码注入也可以使用,但是实现过程比较困难,所以为简单起见我采用创建远程线程的方式注入dll,参考《DLL注入-创建远程线程》
编写hookiat.dll实现IAT Hook
在hookiat.dll中实现对Notepad.exe的IAT Hook操作。
DllMain()主函数的代码如下:
自定义钩取函数MyWriteFile()
自己定义钩取函数MyWriteFile()。若要将Notepad.exe中的小写字母修改为大写字母,在MyWriteFile()中修改第二个参数lpBuffer即可,然后将修改后的lpBuffer传入原来Notepad.exe的WriteFile()函数中,进行调用。
核心:hook_iat()函数实现IAT修改
这里需要弄清楚INT,IAT,IID结构体和ITD结构体这些概念以及它们之间的关系,并且了解PE结构基地址、虚拟地址(VA)、相对虚拟地址(RVA)的定义及换算公式,详见实验原理4和关于PE文件结构的参考博文。
① 寻找IID结构体表:
找Notepad.exe的IMAGE_IMPORT_DESCRIPTOR(IID)结构体表,它记录着PE文件要导入哪些库文件,每个导入的DLL都对应一个IID结构体。寻找IID结构体表,首先要获取Notepad.exe进程IMAGE_NT_HEADERS(NT头)的相对虚拟地址(RVA)。如图,通过PEview查看Notepad.exe文件结构,在IMAGE_DOS_HEADER中可以看到其基地址加上偏移0x3C(60字节)即为一个4字节的PE文件签名的偏移地址(NT头第一个部分)。
IID表在PE文件结构中的RVA为0x160。IMAGE_NT_HEADERS的虚拟地址0x3c的基础加上0x80,就找到IID表的相对虚拟地址。
② 寻找ITD结构体实现IAT表修改
- 在IID结构体表中进行遍历寻找kernel32.dll对应的IID结构体(第一层循环)。
- 找到kernel32.dll对应的IID结构体后,可以通过访问结构体中的成员FirstThunk址,它指向kernel32.dll IAT的第一个IMAGE_THUNK_DATA(ITD)结构体,这样我们就找到了IDT表,有多少个ITD结构体就有多少个函数被导入。
- 在IDT表中进行遍历,找到要钩取的原始WriteFile() API函数对应的ITD结构体(第二层循环)。IDT结构的成员变量Function对应的是原始WriteFile() API函数在内存中的地址,只要修改Function的值为我们自定义的钩取函数新地址即可实现IAT Hook。需要注意的是由于Notepad.exe进程原有IAT内存区域是只可读的,所以修改Function的值之前要通过VirtualProtect()函数将相应的IAT的内存区域更改为可读写模式。
实现演示
IAT-Hook演示
参考
-
《【PE文件】导入表》,https://www.cnblogs.com/SunsetR/p/11192555.html
-
《PE文件:导入表》,https://blog.youkuaiyun.com/weixin_43742894/article/details/105155489
-
《PE结构:导入表中的双桥结构》,https://blog.youkuaiyun.com/weixin_43742894/article/details/106150321
-
《Hook-通过修改IAT实现》,https://www.kn0sky.com/?p=32
-
《逆向工程核心原理》