保护模式(四)| 中断门、陷阱门与任务段
# 前言
上一篇我们学习了调用门的原理和实验。本篇将介绍中断门与陷阱门,以及任务状态段(TSS)和任务门。
# 中断门与陷阱门
| 特性 | 中断门(Interrupt Gate) | 陷阱门(Trap Gate) |
|---|---|---|
| 功能 | 用于处理硬件中断和异步事件 | 用于处理软件异常和同步事件 |
| IF 标志行为 | 自动清除 IF 标志,禁止进一步中断嵌套 | 不清除 IF 标志,允许中断嵌套 |
| 使用场景 | 处理硬件中断(如键盘、时钟) | 处理系统调用或软件异常 |
| 中断响应 | 防止嵌套中断,确保中断处理代码不被打断 | 允许嵌套中断,提高系统灵活性 |
| 类型标识 | Type = 0xE |
Type = 0xF |
# 中断描述符表(IDT)
描述:中断描述符表(Interrupt Descriptor Table,IDT)与 GDT 类似,也是由一系列描述符组成的,每个描述符占 8 个字节。但要注意,IDT 表中的第一个元素不是 NULL。
使用 WinDbg 查看 IDT 表的基址和大小:
IDT 表可以包含三种门描述符:
- 任务门描述符
- 中断门描述符
- 陷阱门描述符
# 中断门
中断门是用于处理硬件中断的机制,通过中断描述符表(IDT)定义。它允许 CPU 根据中断号跳转到对应的中断服务例程(ISR),并自动清除 IF 标志以屏蔽其他中断。中断门支持从低特权级切换到高特权级,保存执行上下文,保证中断处理安全可靠,常用于处理外部设备的中断请求。
中断门执行前后堆栈变化:
# 实验 1:构造一个中断门
一、初步构造门描述符
1 | Offset in Segment 31:16 = 0x0000 ; 暂定 |
由上述参数构造出的门描述符为: 0000EE00'00080000 。
二、确定 Offset in Segment
在 VC6 中执行以下代码并中断。
1 |
|
右键进入反汇编窗口,查看 GetH2GValue 函数起始地址。
至此,门描述符最终确定为: 0040EE00'0008DE30 。
三、将门描述符写入 IDT 表
查看 IDT 表:
红框标注处描述符无效,将构造的描述符写入。
四、继续执行第二步的代码,成功读取高 2G 内存的值。
# 陷阱门
陷阱门是用于处理同步异常和系统调用的机制,通过中断描述符表(IDT)定义。触发陷阱时,CPU 跳转到指定的服务例程并保存上下文,但不清除 IF 标志,允许中断嵌套。陷阱门支持特权级切换,常用于处理异常(如除零、非法指令)和系统调用。
# 实验 2:构造一个陷阱门
一、初步构造门描述符
1 | Offset in Segment 31:16 = 0x0000 ; 暂定 |
由上述参数构造出的门描述符为: 0000EF00'00080000 。
注意:与中断门唯一的区别是 Type 域为
0xF(而非0xE)。
二、三、四:按照实验 1 的对应步骤操作即可。
# 小结
IF 位是否会被清零是陷阱门与中断门唯一的区别。
# 任务状态段(TSS)
在调用门、中断门与陷阱门中,一旦出现权限切换,就会有堆栈切换;由于 CS 的 CPL 发生改变,SS 也必须随之切换。
思考:堆栈切换时,新的 ESP 和 SS 从哪里来?
答案:从任务状态段(Task-state Segment,TSS)获取。
# TSS 的结构
TSS 是用于存储任务上下文的内存结构,大小固定为 104 字节。TSS 保存寄存器值、段选择子和各特权级的栈指针等信息,支持任务切换和从用户态到内核态的特权级转换。
CPU 通过 TR 段寄存器寻找 TSS。
# TSS 的作用
- Intel 的设计思想:通过使用 TSS 达到任务(线程)的切换。
- 操作系统的设计思想:Windows 并没有完全按照 Intel 的设计思想来做,甚至 Linux 也没有这样做。
# TSS 的本质
- 不要把 TSS 与 " 任务切换 " 联系到一起。
- TSS 的意义就在于可以同时换掉 "一堆" 寄存器。
# TR 段寄存器
TR 段寄存器用于存储当前任务的 TSS 选择子,在任务切换时指向 TSS。
# TR 段寄存器的读写
TR 段寄存器的初始值由操作系统在启动时设置,通常指向操作系统为每个任务或线程创建的 TSS。
1 | TR.Base = TSS 起始地址 |
将 TSS 段描述符加载到 TR 寄存器:
指令: LTR
说明:
- 用
LTR指令装载时,仅改变 TR 寄存器的值(96 位),并没有真正改变 TSS。 LTR指令只能在系统层使用。- 加载后 TSS 段描述符的状态位会发生改变。
读 TR 寄存器:
指令: STR
说明:只能读到 TR 的 16 位(即段选择子)。
# TSS 段描述符
TSS 段描述符是系统段描述符中的一种:
- Type =
1001:说明该 TSS 段描述符未被加载到 TR 段寄存器中 - Type =
1011:说明该 TSS 段描述符已被加载到 TR 段寄存器中
# TSS、TSS 段描述符、TR 段寄存器之间的关系
# 实验 1:加载自定义 TSS
一、获取必要参数
运行以下代码并在 main 函数头部设置断点。
1 |
|
二、通过反汇编窗口查看 func 函数起始地址。
三、修改代码
将地址填入 iTss 数组注释为 eip 的位置,表示 TSS 切换后 EIP 的值。
注意:代码如有修改,需要先停止程序,重新编译。
四、查看 iTss 数组所在地址,记下来备用。
五、构造 TSS 段描述符
1 | G = 0 |
由上述参数构造出的 TSS 段描述符为: 0000E900'00000068 。
iTss 数组地址为 0x12FDCC ,因此 TSS 段描述符最终为: 0000E912'FDCC0068 。
六、将 TSS 段描述符写入 GDT 表(地址选择 0x8003f0c0 )。
若写入其他地址,则需要修改 buff 数组的后两个字节。
先不要急着运行代码,先在 WinDbg 中输入 !process 0 0 获得当前进程的 Cr3。
七、解除中断,输入上一步得到的 Cr3,回车。
WinDbg 成功获得了中断信号。
查看反汇编代码,可以确定正在执行 func 函数的代码。
寄存器也都符合预期的结果,说明 TSS 切换成功。
思考:TSS 切换完成后,如何回到切换前的下一行继续执行?
# 任务门
任务门是一种特殊的中断描述符,用于触发硬件任务切换。通过任务门调用时,CPU 自动保存当前任务的上下文到当前 TSS,然后加载目标 TSS 并切换到目标任务。
特性:
- 任务门存在于 IDT 表
- 任务门中包含 TSS 段选择子
- 可以通过访问任务门达到切换 TSS 的目的
任务门结构:
任务门执行过程:
- INT N(N 为 IDT 表索引号)
- 系统通过用户指定的索引查找 IDT 表,找到对应的门描述符
- 门描述符若为任务门描述符,则根据其中的 TSS 段选择子查找 GDT 表,找到 TSS 段描述符
- 将 TSS 段描述符中的内容加载到 TR 段寄存器
- TR 段寄存器通过 Base 和 Limit 找到 TSS
- 使用 TSS 中的值修改寄存器
IRETD返回
# 实验 2:通过任务门切换 TSS
一、构造任务门描述符
任务门描述符结构图灰色部分默认填充为 0。
1 | P = 1 |
由上述参数构造出的门描述符为: 0000E500'00C30000 。
二、将任务门描述符写入 IDT 表
三、构造 TSS 段描述符
TSS 段描述符的构造过程参照实验 1,这里不再详解,只给出测试代码。
1 |
|
四、运行代码
需要输入 Cr3(获取流程参照实验 1)。
运行后,TSS 成功切换并返回。