文章目录
-
- 要点回顾
- 中断门
-
- IDT
- 中断门描述符
- 中断门实验
-
- 示例代码
- 构造中断门描述符
- 修改IDT表
- 中断现场
- 中断返回
-
- 为什么会PUSH EFLAGS寄存器
- 调用门和中断门的区别
要点回顾
执行调用门的指令:call cs:eip。其中cs是段选择子,包含了查找GDT表的索引。但当CPU遇到了如下指令:int [index],查询的却是另外一张表,这张表叫IDT
中断门
IDT
IDT表全称为中断描述符表,包含三种描述符:任务门描述符 中断门描述符和陷阱门描述符。每个描述符占8个字节。但要注意的是,IDT表的第一个元素不是NULL。
通过int [index]这条指令,CPU会索引到IDT表。后面的index表示查IDT表项的下标。对比调用门,中断门没有了RPL,所以CPU只会校验CPL。
中断门描述符
中断门实验
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>void __declspec(naked) IdtEntry()
{
__asm{
int 3;iretd;}
}void go()
{
__asm int 0x20;
}int main()
{
printf("%p", IdtEntry);__asm{
int 3;}go();system("pause");
}
构造中断门描述符
首先查看一下要跳转到的函数地址
函数地址为00401040
offset31-16:0040
P:1 DPL:11 S:0-->E
TYPE:E
8-0:保留位
Segment Selector:0008
offset:1040
#段描述符
0040EE00`00081040
修改IDT表
首先查看IDT表
kd> dq idtr L40
80b95400 83e88e00`00084fc0 83e88e00`00085150
80b95410 00008500`00580000 83e8ee00`000855c0
80b95420 83e8ee00`00085748 83e88e00`000858a8
80b95430 83e88e00`00085a1c 83e88e00`00086018
80b95440 00008500`00500000 83e88e00`00086478
80b95450 83e88e00`0008659c 83e88e00`000866dc
80b95460 83e88e00`0008693c 83e88e00`00086c2c
80b95470 83e88e00`000872fc 83e88e00`000876b0
80b95480 83e88e00`000877d4 83e88e00`00087914
80b95490 00008500`00a00000 83e88e00`00087a80
80b954a0 83e88e00`000876b0 83e88e00`000876b0
80b954b0 83e88e00`000876b0 83e88e00`000876b0
80b954c0 83e88e00`000876b0 83e88e00`000876b0
80b954d0 83e88e00`000876b0 83e88e00`000876b0
80b954e0 83e88e00`000876b0 83e88e00`000876b0
80b954f0 83e88e00`000876b0 83e28e00`00089af8
80b95500 00000000`00080000 00000000`00080000
80b95510 00000000`00080000 00000000`00080000
80b95520 00000000`00080000 00000000`00080000
80b95530 00000000`00080000 00000000`00080000
80b95540 00000000`00080000 00000000`00080000
80b95550 83e8ee00`0008463a 83e8ee00`000847c0
80b95560 83e8ee00`000848fc 83e8ee00`00085498
80b95570 83e8ee00`00083fee 83e88e00`000876b0
80b95580 83e88e00`000836b0 83e88e00`000836ba
80b95590 83e88e00`000836c4 83e88e00`000836ce
80b955a0 83e88e00`000836d8 83e88e00`000836e2
80b955b0 83e88e00`000836ec 83e28e00`00089104
80b955c0 83e88e00`00083700 83e88e00`0008370a
80b955d0 83e88e00`00083714 83e88e00`0008371e
80b955e0 83e88e00`00083728 83e88e00`00083732
80b955f0 83e88e00`0008373c 83e88e00`00083746
找到下标为0x20的那一项,也就是80b95500这个未被使用的位置。修改IDT表
kd> eq 80b95500 0040EE00`00081040
修改完成之后可以在PC Hunter中看到修改后的效果,软件显示这个地方被HOOK,当前函数地址为我们指定的0x00401040
中断现场
将程序编译后放到虚拟机运行,windbg第一次产生中断,此时中断于用户层,查看一下当前的寄存器
Break instruction exception - code 80000003 (first chance)
001b:00401062 cc int 3
kd> r
eax=00000008 ebx=7ffdf000 ecx=74b49a18 edx=74b910a4 esi=74b92108 edi=001f40b0
eip=00401062 esp=0012ff44 ebp=0012ff88 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
001b:00401062 cc int 3
- eip=00401062
- esp=0012ff44
- cs=001b
- ss=0023
- efl=00000216
继续运行,windbg产生第二次中断,此时中断于内核层,查看一下当前的寄存器环境
kd> g
Break instruction exception - code 80000003 (first chance)
00401040 cc int 3
kd> r
eax=00000008 ebx=7ffdf000 ecx=74b49a18 edx=74b910a4 esi=74b92108 edi=001f40b0
eip=00401040 esp=8d113c9c ebp=0012ff88 iopl=0 nv up di pl nz ac pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000016
00401040 cc int 3
- eip=00401040 我们指定的EIP
- esp=8d113c9c 零环的堆栈
- cs=0008 我们指定的段选择子
- ss=0010 零环的SS段寄存器
接着查看一下堆栈的变化
kd> dd esp
8d113c9c 00401065 0000001b 00000216 0012ff44
8d113cac 00000023 00000000 00000000 00000000
8d113cbc 00000000 0000027f 00000000 00000000
8d113ccc 00000000 00000000 00000000 00001f80
8d113cdc 0000ffff 00000000 00000000 00000000
8d113cec 00000000 00000000 00000000 00000000
8d113cfc 00000000 00000000 00000000 00000000
8d113d0c 00000000 00000000 00000000 00000000
- 00401065 三环的EIP(返回地址)
- 0000001b 三环的CS段选择子
- 00000216 三环的EFLAGS寄存器
- 0012ff44 三环的ESP
- 00000023 三环的SS段寄存器
可以看到和调用门不同的是,中断门比调用门多保存了一个EFLAGS寄存器
中断返回
中断门在没有提权的时候,会向堆栈push3个值,分别是:CS EFLAGS EIP(返回地址)。在提升权限的时候,会向堆栈push5个值,分别是SS ESP EFLAGS CS EIP
在中断门中,栈里面保存的寄存器值发生了变化,不能再用RETF进行返回,而应该通过IRET/IRETD指令返回
为什么会PUSH EFLAGS寄存器
在使用中断门提权的时候,CPU会将EFLAGS寄存器中的IF中断标志位修改为0。由于会修改标志寄存器,所以在进行中断提权的时候需要先保存一份到堆栈中。
调用门和中断门的区别
- 调用门通过call far指令执行,但中断门通过int指令执行
- 调用门查GDT表 中断门查IDT表
- call cs:eip中的CS是段选择子,由三部分组成。但int [index]指令中的index只是索引,中断门不检查RPL,只检查CPL
- 调用门可以有参数,但中断门没有
- 调用门提权时push了四个寄存器:EIP(返回地址) CS ESP SS,返回时用RETF指令返回。中断门提权时push了五个寄存器:EIP(返回地址) CS EFLAGS ESP SS,返回时用IRETD指令返回
l cs:eip中的CS是段选择子,由三部分组成。但int [index]指令中的index只是索引,中断门不检查RPL,只检查CPL
4. 调用门可以有参数,但中断门没有
5. 调用门提权时push了四个寄存器:EIP(返回地址) CS ESP SS,返回时用RETF指令返回。中断门提权时push了五个寄存器:EIP(返回地址) CS EFLAGS ESP SS,返回时用IRETD指令返回