[保护模式]中断门

要点回顾

执行调用门的指令: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。由于会修改标志寄存器,所以在进行中断提权的时候需要先保存一份到堆栈中。

调用门和中断门的区别

  1. 调用门通过call far指令执行,但中断门通过int指令执行
  2. 调用门查GDT表 中断门查IDT表
  3. call cs:eip中的CS是段选择子,由三部分组成。但int [index]指令中的index只是索引,中断门不检查RPL,只检查CPL
  4. 调用门可以有参数,但中断门没有
  5. 调用门提权时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指令返回

发布了93 篇原创文章 · 获赞 82 · 访问量 7万+
展开阅读全文
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览