首页
社区
课程
招聘
[原创]#30天写作挑战#Windows逆向中的API的hook
发表于: 2020-9-7 22:58 13591

[原创]#30天写作挑战#Windows逆向中的API的hook

2020-9-7 22:58
13591

PE文件是windows操作系统下使用的可执行文件格式。PE文件是指32位的可执行文件,也称PE32。64位的可执行文件称为PE+和PE32+,是PE文件格式的一种扩展形式。

可执行类: EXE,SCR
库系列: DLL、OCX、CPL、DRV
驱动程序类: SYS、VXD
对象文件类: OBJ

1.DOS头
2.DOS存根
3.NT头
4.节区头(sectionHeader)

NT头:可选头(IMAGE_OPTIONAL_HEADER32

1.AddressOfEntryPoint : EP的RVA值,程序最先执行的代码起始地址。
2.DataDirectory:IMAGE_DATA_DIRECTORY结构体数组

DataDirectory[0]:EXPORT Directory 导出表(EDT)的RVA地址
DataDirectory[1]:IMPORT Directory 导入表(IAT)的RVA地址
DataDirectory[9]:TLS Directory

IAT
IMAGE_IMPORT_DESCRIPTOR 结构体描述PE装载时的必须信息。每一个dll对应一个IMAGE_IMPORT_DESCRIPTOR 结构体,结构体的结构如下:

OriginalFirstThunk; //INT(Import Name Table) address(RVA)
TimeDateStamp;
ForwarderChain;
Name;//the name(string) of the dll
FirstThunk;//IAT(Import Address Table) address (RVA)

以装载kernel32.dll为例,PE装载器将导入函数输入至IAT的流程:

1.读取IID的Name,获取到库名称(“kernel32.dll”)
2.装载相应库(LoadLibrary(“kernel32.dll”))
3.读取IID的OriginalFirstThunk成员,获取INT地址
4.逐一读取INT中的函数名地址
5.通过函数名地址获取到函数名(eg.GetCurrentThreadId),获取函数的起始地址:GetProcAddress(“GetCurrentThreadId”)
6.读取IID的FirstThunk,获取IAT地址;
7.将函数地址填入到IAT项
8.重复4-7,直到INT结束

EAT
IMAGE_EXPORT_DIRECTORY 结构体描述PE文件的EAT信息,一个PE文件只包含一个IMAGE_EXPORT_DIRECTORY 结构体。其结构如下:

NumberOfFunctions // Export函数的个数
NumberOfNames //Export函数中具名的函数个数
AddressOfFunctions // Export函数地址数组
AddressOfNames // 函数名称地址数组
AddressOfNameOrdinals // Ordinal地址数组

在Windows系统下,HOOK技术的方法比较多,使用比较灵活,常见的HOOK方法有API Hook、Windows消息hook……
HOOK技术涉及DLL相关的知识。实现HOOK,可以有两种技术,一种是Windows的注入技术,把实现HOOK功能的DLL文件加载到目标进程空间中,就要使用DLL注入的技术。另一种是调试法。

以CreateFile()的调用为例,正常情况下的API调用流程

勾取后的执行流程

1.五字节修改法



① 构造跳转指令。

jmp后的偏移量 = 目标地址 − 原地址 − 5

② 在内存中找到欲HOOK函数地址,并保存欲HOOK位置处的前5字节。

③ 将构造的跳转指令写入需HOOK的位置处。④ 当被HOOK位置被执行时会转到自己的流程执行。

⑤ 如果要执行原来的流程,那么取消HOOK,也就是还原被修改的字节。
⑥ 执行原来的流程。
⑦ 继续HOOK住原来的位置。

由于这种方法是在程序流程中直接进行嵌入jmp指令来改变流程的,所以就把它叫作Inline Hook

2.七字节修改法(“热补丁”技术)

从下图可以看出windows的API起始代码有如下2个明显的相似点:
(1)API代码以 “MOV EDI EDI”指令开始(0x8BFF)
(2)API代码上方有5个“NOP”指令(0x90)
也就是说这7个字节没有任何意义。
微软到底为何要这么设计呢?原因是为了方便打“热补丁”。“热补丁”有API钩取实现,在进程处于运行状态时临时更改进程内存中的库文件(重启系统时,修改的目标文件会被完全取代)

Windows下的窗口应用程序是基于消息驱动的,但是在某些情况下需要捕获或者修改消息,从而完成一些特殊的功能。对于捕获消息而言,无法使用IAT或Inline Hook之类的方式去进行捕获,不过Windows提供了专门用于处理消息的钩子函数。

Windows下的应用程序大部分是基于消息模式机制的,一些CUI的程序不是基于消息的。Windows下的应用程序都有一个消息过程函数,根据不同的消息来完成不同的功能。Windows操作系统提供的钩子机制的作用是用来截获、监视系统中的消息。Windows操作系统提供了很多不同种类的钩子,可以处理不同的消息。
Windows系统提供的钩子按照挂钩范围分为局部钩子和全局钩子。局部钩子是针对一个线程的,而全局钩子则是针对整个操作系统内基于消息机制的应用程序的。全局钩子需要使用DLL文件,DLL文件里存放了钩子函数的代码。
在操作系统中安装全局钩子以后,只要进程接收到可以发出钩子的消息后,全局钩子的DLL文件会被操作系统自动或强行地加载到该进程中。由此可见,设置消息钩子也是一种可以进行DLL注入的方法。
Windows下的钩子函数,主要有3个,分别是SetWindowsHookEx()、CallNextHookEx()和UnhookWindowsHookEx()。

该函数的返回值为一个钩子句柄。这个函数有4个参数。
idHook:该参数表示钩子的类型。由于钩子的类型非常多,因此放在所有的参数后面进行介绍。
lpfn:该参数指定为Hook函数的地址。如果dwThreadId参数被赋值为0,或者被设置为一个其他进程中的线程ID,那么lpfn则属于DLL中的函数过程。如果dwThreadId为当前进程中的线程ID,那么lpfn可以是指向当前进程中的函数过程,也可以是属于DLL中的函数过程。
hMod:该参数指定钩子函数所在模块的模块句柄。该模块句柄就是lpfn所在的模块的句柄。如果dwThreadId为当前进程中的线程ID,而且lpfn所指向的函数在当前进程中,那么hMod将被设置为NULL。
dwThreadId:该参数设置为需要被挂钩的线程的ID号。如果设置为0,表示在所有的线程中挂钩。如果指定为具体的线程的ID号,表示要在指定的线程中进行挂钩。

实现DLL注入主要使用以下三种方法

过程如下;
(1)用OpenProcess函数通过程序运行时的PID值获取进程的句柄,并取得PROCESS_ALL_ACCESS权限。

(2).用VirtualAllocEx函数在目标进程的地址空间中分配一块足够大的内存用于保存被注入的dll的路径。

(3).用WriteProcessMemory函数把本进程中保存dll路径的内存中的数据拷贝到第(1)步得到的目标进程的内存中。

(4).用GetProcAddress函数获得LoadLibraryW函数的起始地址。LoadLibraryW函数位于Kernel32.dll中。

(5).用CreateRemoteThread函数让目标进程执行LoadLibraryW来加载被注入的dll。函数结束将返回载入dll后的模块句柄。

(6).用VirtualFreeEx释放第(1)步开辟的内存。

调试法实现hook流程:
①:对目标进程附加调试器,开始调试

②:下断点,将目标API函数的起始地址第一个字节设置为0xCC
③:程序调用目标API,断下,控制权转移给调试器
④:执行我们自己的代码
⑤:恢复0xCC,使API正常执行,并运行无0xCC的正常状态下的API函数
⑥:再次下断,将目标API函数的起始地址第一个字节设置为0xCC

精简工具数量
工具功能简单,使用方便
完全掌握各种功能
不断升级更新
理解工具的核心工作原理

 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 4
支持
分享
最新回复 (1)
雪    币: 26205
活跃值: (63302)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
2

感谢分享!

欢迎更多的小伙伴参与到 #30天写作挑战#中来!活动详情:https://bbs.pediy.com/thread-261705.htm


2020-9-9 10:19
0
游客
登录 | 注册 方可回帖
返回
//