第13章 Hook 技术

13.1 Hook 概述

Hook 技术又叫做钩子函数,钩子函数可以在特定函数执行之前,提前捕获消息,先得到控制权,此时Hook可以改变函数行为,或者强制结束消息的传递。

13.1.1 IAT Hook 篡改MessageBox 消息

示例程序 主要代码

int main(int argc, char *argv[ ])
{
	BOOL bIsWow64 = IsWow64();
	printf("IsWow64 = %d\n",bIsWow64);
	ShowMsgBox("Before IAT Hook");
	IAT_InstallHook();
	ShowMsgBox("After  IAT Hook");
	IAT_UnInstallHook();
	ShowMsgBox("After  IAT Hook UnHooked");
	return 0;
}

//之所以把这个调用单独放在一个函数中,是因为Release模式下对调用进行了优化,第二次调用时直接采用了寄存器寻址而不是导入表
//因此,单独放在一个函数中可以避免这个情况。

VOID ShowMsgBox(char *szMsg)
{
	MessageBoxA(NULL,szMsg,"Test",MB_OK);
}

跟进第一个ShowMessageBox函数

可以看到Messagebox的地址,存于00488448,值为7DCBFD1E

让我们再跟进修改IAT后的 ShowMessageBox函数

可以看到同一个位置存放的地址被更改了,新值为00426CE4

此处是一个My_MessageBoxA函数,代码如下:

int WINAPI My_MessageBoxA(
	HWND hWnd,          // handle of owner window
	LPCTSTR lpText,     // address of text in message box
	LPCTSTR lpCaption,  // address of title of message box
	UINT uType          // style of message box
	)
{	
	//在这里,你可以对原始参数进行任意操作
	int ret;
	char newText[1024]={0};
	char newCaption[256]="pediy.com";
	printf("有人调用MessageBox!\n");
	//在调用原函数之前,可以对IN(输入类)参数进行干涉
	lstrcpy(newText,lpText);//为防止原函数提供的缓冲区不够,这里复制到我们自己的一个缓冲区中再进行操作
	lstrcat(newText,"\n\tMessageBox Hacked by pediy.com!");//篡改消息框内容
	uType|=MB_ICONERROR;//增加一个错误图标
	ret = OldMessageBox(hWnd,newText,newCaption,uType);//调用原MessageBox,并保存返回值
	//调用原函数之后,可以继续对OUT(输出类)参数进行干涉,比如网络函数的recv,可以干涉返回的内容
	return ret;//这里你还可以干涉原始函数的返回值
	
}

13.1.2 Inline Hook 篡改指定 MessageBox消息

进入示例程序的main函数

可以看到由于多次调用MessageBoxA函数,在图中第一行的位置,函数地址被放入了 esi寄存器中。

所以不存在地址被更改的情况。继续跟下去看是什么被改变了

第一次跟进MessageBoxA

第二次跟进

可以看到原来的前三句汇编代码被替换为一个jmp语句,跳转到自定义函数My_MessageBox函数

执行到My_MessageBox函数的结尾,有一个跳转到OriginalMessageBox函数的Call,我们跟进去看一下

可以看到执行的正是被替换的三条语句。

然后跳转到7DCBFD23处,正是原MessageBoxA的第四条语句,即jmp My_MessageBox 语句的下面一句。

所以流程大致为 My_MessageBox → OriginalMessageBox → MessageBoxA 恢复正常执行流程。

13.2 Hook的分类

13.2.1 Address Hook

通过修改函数地址来拿到程序控制权。

1.各类表中的地址

(1)PE的IAT

前面第一个调试的例子即为 通过修改IAT实现Hook。

作用范围:

  • 被Hook的PE模块 (IAT具体指某个PE模块的IAT,若想对已加载的所有模块起作用,必须遍历进程内的模块)
  • 静态调用的API (LoadLibrary 及 GetProcAddress 调用不受影响)

(2)PE 的 EAT

EAT(Export Address Table,输出表)与IAT不同,它存放的不是函数地址,而是函数地址的偏移,使用时需要加上模块基址。

作用范围:动态调用的API (LoadLibrary, GetProcAddress)

(3)user32.dll的回调函数表

在user32.dll中有一个名为 USER32!apfnDispatch的回调函数表,其中存放了各种用于GUI的回调函数,通常与内核中的KeUserModeCallBack 函数配合使用。

(4) IDT

IDT(Interrupt Descriptor Table,系统中断描述符表)是操作系统在处理中断机制(例如调试中断,键盘/鼠标中断、系统调用中断等)使用的一张表。当这些中断发生时,操作系统通过这张表知道把中断交给谁去处理。

(5)SSDT 和Shadow SSDT

Windows 系统大部分功能都是通过调用系统服务实现的,应用程序调用API后转入操作系统内核进行处理,首先要用到SSDT(System Service Descriptor Table, 系统服务描述表)

相应的还有一个助理各种GUI服务的表,叫做KeServiceDescriptorTableShadow,也就是上面所说的Shadow SSDT

(6) C++ 类的虚函数表

(7)COM接口的功能函数表

2.处理例程地址

这类结构常见于内核中:

鉴于我对与内核丝毫没有研究…这里就简单记录下

(1) DRIVER_OBJECT 的MajorFunction 及 FastIo 派遣例程地址

(2)StartIo 等待例程的地址

(3)OBJECT_TYPE 中 _OBJECT_TYPE_INITIALIZER 包含的各种例程

3.特殊寄存器中的地址

Windows 使用MSR寄存器 中IA32_SYSENTER_EIP作为内核调用的入口,当在ntdll中调用汇编指令sysenter进入内核时,CPU会首先执行到这里。因此通过修改该处地址,同样可以进行Hook

13.2.2 Inline Hook

Inline Hook 直接修改指令并跳转到指定函数。

主要有5种模式

  1. jmp xxxx 5字节
  2. push xxxx /retn 6字节
  3. move eax,xxxx/jmp eax (7字节)
  4. call Hook (更换指令或输入表)
  5. HotPatch Hook (一个短跳,加一个长跳)

出现这几种方式的原因 主要与函数的开头指令有关,API 开头指令主要有两种形式,

如果是5字节的jmp,那么在未使用SEH时刚好覆盖前三条指令,如果使用了SEH,那么move eax,xxx /jmp指令刚好覆盖前两条指令,push/retn 为6字节,不能刚好全部覆盖原来的指令,但是它和jmp有一个相同的好处是不改变任何寄存器的值。

13.2.3 基于异常处理的Hook

当程序执行过程中发生异常时,系统内核的异常处理过程nt!KiDispatchException 会开始工作。在没有内核调试器存在且异常程序没有被调试的情况下,系统把异常交给用户态的异常处理过程(ntdll!RtlDispatchException)以查找系统中是否安装了异常处理程序。

因此,如果在程序中自行安装SEH处理过程,然后向被Hook的位置写入一条会引发异常的指令,或者改变Hook位置的内存属性引发内存访问异常。

在实际应用中,因为SEH使用有较多限制(基于线程,非全局,基于栈),而VEH有点更多(基于进程,全局,且优先于SEH处理) 所以通过VEH+INT3 进行Hook更具有实用性

13.2.4 不是Hook的Hook

1.PE被感染,修改EntryPoint

2.系统回调机制和分层模型

(1)各类回调机制

进程/线程创建回调,加载映像回调,注册表回调

(2)分层服务和过滤驱动模型

一个典型机制是LSP服务提供者,另一个是驱动模型

13.3 Hook位置的挑选

以ReadFile 为例,每个方框都是一个Hook点

影响最小的Hook: 应用程序中的call Hook,可警觉到特定位置对特定的APIi调用

影响最大的Hook:在系统内核中,大部分Hook的位置都会影响到整个系统的调用过程,越往下越明显

早起各类安全软件都是通过Hook SSDT 和Shadow SSDT 达到主动防御和自我保护的目的。

由于安全软件防护体系的日益完善,需要Hook的系统服务越来越多,很多安全软件就把Hook的位置移动到了KiFastCallEntry内部。


中间章节多为代码…之后慢慢看,就不作为笔记写在这了。


13.8 Hook 技术的应用

  • 1.实现增强的二次开发或者补丁

当要扩展的文件的新功能较为复杂时,在原PE中添加新节并直接写入机器指令的方式就显得比较麻烦了,此时最好的方法是自行编写DLL实现新功能,然后设法使目标进程鸡杂在这个补丁,在补丁DLL中将原功能函数Hook到DLL中对应的功能函数种

  • 2.信息截获
  • 3.安全防护

13.9 Hook的检测、恢复与对抗

13.9.1 Hook的检测与恢复

  1. Addres Hook的检测

基本思路:寻找原始的Address,在与当前的Address 比较

例如:IAT Hook,可以自行加载待检测的PE模块,然后自行填充IAT,与原模块的IAT对比

对SSDT 和Shadow SSDT来说,原始的服务表存在内核映像文件中,可以手动将内核文件加载到内存中,根据当前内核的实际加载位置进行重定位,然后根据当前实际的SSDT相对于内核实际家在位置的偏移量,定位到冲加载的内核中的相应位置,将两者对比,就可以检测出被Hook的项目了

对于某些驱动的Address Hook,获取原始地址是比较困难的,此时比较简单的方法是判断Address 是否在这个驱动模块的地址范围内。

2.Inline Hook的检测

1.自行加载待检测的PE映像,并根据其实际加载基地址进行重定位

2.对代码所在节进行检查和对比。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇