本帖最后由 Panel 于 2022-9-7 00:28 编辑
windows7 x86下hook int3 中断
1.准备工具
windbg x86、windows7 32位虚拟机、勤劳的双手和不怕蓝屏的勇气
2.原理讲解
先来看官方话:
外部中断、软件中断和异常是通过中断描述符表(①DT)处理的,如图2-1。IDT包含了访问中
断和异常处理程序的门描述表的集合。像GDT一样,IDT不是一个段,IDT的线性基地址包含
在IDT寄存器中(IDTR)。IDT中的门描述符包括有中断-、陷阱-、或任务门类型。在运行中
断或异常处理程序时,处理器必须先从内部硬件、外部中断控制器、或通过执行IT,ITO,
INT3或BOUND指令的软件中断中接到一个中断向量(中断数字)。中断向量包含了IDT中的门
描述符的索引。如果选中的门描述符是一个中断门或者陷阱门,相应的处理程序是通过非
常类似于通过调用门调用过程了。如果描述符是一个任务门,处理程序是通过任务切换进
行的。
那我就来翻译成我的话:
中断就是一个系统在特殊时间点触发的一种行为,这种行为通常伴随着一些函数过程的发生
细述原理,一看就会(狗头护体):
以int3中断为例,当系统调用int3中断的时候那么int3中断会触发一个函数来对应该中断的发生,那么这个函数是什么呢?
要知道这个函数的来源之前必须得知道两个知识点的概念:IDT(interrupt describe table)和GDT(global describe table )。
首先IDT:中断系列描述表,该表中存放了所有中断的描述符,用来描述不同中断的权限和对应函数地址等的一张表,可以粗略地理解为每一个系统中断的发生之后都会来解析该IDT中对应的中断描述符,比如int1 则解析IDT中索引为1的那段中断描述符,通过解析该描述符便可以得到该中断过程所具有的权限和函数地址等行为。
其次GDT:GDT则是用来存储几乎所有段描述符等的一张表,比如cs、ds等,描述了这些段所具有的权限,基地址等
那我们开始利用以上粗略的介绍来说明int3的实现过程:
1.通过系统调用不同的int中断来解析在IDT中指定索引的中断描述符。这里我们还是使用windbg来查看一下IDT具体的模样,首先利用命令 r idtr 得到idt表中内核中的地址,再使用 dq idt地址 便可得到idt表,如下图:
1
2.系统又会解析中断描述符中的Segment Selector来得到中断函数将要触发函数的基地址,随后将Segment Selector中解析出来的基地址再加上中断描述符中的offset作为偏移便得到了将要触发函数的地址,再通过检查该中断描述符和对应Segment Selector描述符(GDT中的段描述符)便获取到了该函数的权限等信息,最后系统据此调用指定中断触发函数。
原理图如下:
4
这里附上GDT和IDT描述符略图,大家可以网上查看对应位的详解,如下图:
IDT:
2
GDT:
3
3.实现hook
既然上面我们知道了中断调用函数的原理过程了,那我们就知道我们居然想hook指定中断函数,那么我们就只需要使得系统通过解析IDT中的描述符后得到要触发函数的地址等于我们要执行的函数地址就行了。
查看int3的中断描述符:
索引从0开始,所以int3的中断描述符是第四个,操作指令和结果如下图:
5
83e8ee00 000855c0`,根据下表和网上的信息我们对该描述符进行关键信息解析:
6
offset:0x83e855c0
Segment Selector:0008
此时的Segment Selector就是IDT中的偏移,那么我们进入GDT中去查看该偏移的段描述符,操作指令和结果如下图:
7
00cf9b00 0000ffff` 那此时我们对应表和网上信息解析一下该段描述符所描述的基地址和段权限:
8
Base:00000000
DPL(描述了该base段所具有的执行权限,基本上都是要么是0环权限要么是3环权限):0x9(hex)->1001,那么DPL的只就是0,也就是0环权限
综上所述,那么该中断触发的函数地址就是00000000+0x83e855c0 = 0x83e855c0,那么咱们u一下看看,操作指令和结果如下图:
9
那么我们就开始动手脚:
#include "stdafx.h"
#include <windows.h>
int val = 0;
char *txt = (char *)malloc(sizeof(char)*260);
void __declspec(naked) test()
{
__asm
{
//再次跨段,防止缓存区清空问题
push 0x8;
push NewSeg;
jmp fword ptr [esp];
NewSeg:
//保存现场环境
add esp,8;
pushad;
pushfd;
//自定义逻辑部分
cli;//关闭时钟中断
mov eax,0x840599dc;
mov ecx,[eax];
mov val,ecx;
sti;//开启时钟中断
popfd;
popad;
mov dword ptr [esp-4],0x83e855c0;
jmp dword ptr [esp-4];
retf;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
memset(txt,0,260);
memcpy(txt,"demoless",strlen("demoless")+1);
__asm
{
int 3;
}
printf("%x",val);
system("pause");
}
代码思路以及逻辑:
①调用int3
②跳转我们指定的段,因为缓存大小问题所以我们跳段保险
③保存现场环境,防止hook结束后由于环境问题蓝屏
④编写hook代码:通过读取0x840599dc(实验时NtOpenProcess函数的地址)处字节,如果能读到则说明我们hook成功了,因为内核函数在3环读取不到
⑤跳回int3原触发函数地址,不影响正常中断
⑥通过打印val值是否是NtOpenProcess的字节码来验证是否hook成功
那此时还差最后一步,修改int3的中断描述符:
①修改segment selector为我们构造的一段新描述符,注意,我们想要的只是这段新描述符的base直接等于我们的函数地址,而权限不变,为了不影响其他解析这个段描述符的正常流程,我们就构造新的描述符在idt中空白的位置,这里是0x48偏移处,操作指令和结果如下图:
10
②修改segment selector为0x48,操作指令和结果如下图:
11
4.验证hook是否成功
12
13
所以,兄弟们,hook成功了
5.总结:
这个过程可能会遇到蓝屏等问题,这个我给兄弟们留下坑,自己踩了实验出来以后效果更好,这个重点关注int3触发的环境可以帮助大家找到蓝屏原因,最后,由于第一次写内核帖,如有不对地方请大家斧正,各位大牛嘴下留情!
|