保护模式篇——长调用与调用门、中断门、陷阱门
写在前面
此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。
你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。
看此教程之前,问几个问题,基础知识储备好了吗?上一节教程学会了吗?上一节课的练习做了吗?没有的话就不要继续了。
🔒 华丽的分割线 🔒
练习及参考
本次答案均为参考,可以与我的答案不一致,但必须成功通过。第一题的答案可以比我的更详细。
1️⃣ 记住代码段间跳转的执行流程。
🔒 点击查看答案 🔒
1、段选择子拆分
2、查表得到段描述符
3、权限检查
4、加载段描述符
5、代码执行
2️⃣ 自己实现一致代码段的段间跳转。
🔒 点击查看答案 🔒
1. 自行构造一个段描述符 00CF9B00`0000FFFF ,把它填写到 GDT 表中,我填写到索引为 18 的位置。
2. 打开一个OD随便调试一个应用程序,在 EIP 的位置修改一条指令,如图1所示。
3. 修改完毕后,单步走一步,看看是否成功,如果成功则会跳转到指定位置,如图2所示。
(图1)
(图2)
3️⃣ 自己实现非一致代码段的段间跳转。
🔒 点击查看答案 🔒
请根据题目2的答案,将段描述符改为 00CF9C00`0000FFFF ,其余保持不变继续即可。
门
本篇文章涉及调用门、中断门、陷阱门这三个重要的“门”,那么“门”到底是什么。打个比方,“门”就类似于你去办理身份证必须要进入派出所的门一样。你想办理身份证就必须通过这个门,如果你进不了门就办理不了。调用门、中断门、陷阱门也是如此,通过它们你可以修改段的属性,甚至能提权去做一些应用层做不了的事情。
调用门
在将所有的知识之前,先讲一下调用门,因为后面的知识频繁用到了调用门的概念,调用门的结构如下图所示。它和普通的段描述符结构十分相似。低四个字节改为段选择子,如果指向的段描述符的DPL
小于CPL
,则会提权。高四个字节低5个位是调用调用门需要的参数数目。低四个字节的低16位和高四个字节的高16位拼接为跳转后新的在段中的偏移,也就是调用后EIP
的位置。
它的具体细节将会在长调用讲完后继续讲解。
长调用
介绍长调用,我先来讲一下什么是短调用
。短调用就是我们在汇编常见的CALL
指令,调用格式为:CALL 立即数/寄存器/内存
。为什么是短调用,我们来看一下执行该指令时堆栈的变化:
调用CALL
指令之后,CPU
只将当前的EIP
压入堆栈后跳转到目标地址,发生改变的寄存器只有ESP
和EIP
,即所谓的短调用。
长调用分为两种,一种提权,一种不提权,调用格式为:指令格式:CALL CS:EIP
,其中EIP
是废弃的,CS
为指向调用门
的段选择子。但是值得注意的是CS
一旦更换,它的EIP
和SS
要同时更换。为什么EIP
需要更换我就不说了。在代码执行的时候,一定会用到堆栈,堆栈的段权限必须与CS
匹配,这就是为什么SS
必须更换。我们接下来看看长调用到底是何方神圣。
长调用不提权
当段选择子指向的调用门不提权时。发生改变的寄存器有ESP
、EIP
和CS
,比短调用多一个CS
。执行情况如下图所示:
长调用提权
当段选择子指向的调用门不提权时。发生改变的寄存器有ESP
、EIP
、CS
和SS
。执行情况如下图所示:
心细的你获取发现,SS
并没有压入堆栈之中,还有ESP0
也没用通过已知方式获取得到。它从哪里来呢?下一节教程将会讲解。
调用门(续)
本篇开头简单介绍了调用门,接下来将会详细介绍它的调用流程。先把上面的调用门结构图贴到下面,以供方便学习:
调用门执行流程如下所示:
-
指令格式:
CALL CS:EIP (EIP是废弃的)
-
执行步骤(具体详情请看长调用):
- 根据
CS
的值查GDT表
,找到对应的段描述符且该描述符是一个调用门。 - 在调用门描述符中存储另一个代码段的段选择子,将其加载到
CS
中。 - 选择子指向的段的
Base + 偏移地址
就是真正要执行的地址。
- 根据
当然段选择子加载段描述符的过程都会有权限检查,不懂的话请不要再继续了,回去查看上一篇复习,懂了再回来学习。
中断门
讲中断门之前,我先来介绍一个新的表,称之为IDT表
。IDT表
与GDT表
不同,它的第一个元素不是NULL
。IDT表
包含3种门描述符:任务门描述符、中断门描述符、陷阱门描述符,这里面的几种类型都会在后面讲到。中断门的结构如下图所示(图中的D
表示是否为32位
,如果是则为1
):
IDT也是由一系列描述符组成的,每个描述符占8个字节
。Windows没有使用调用门,但是使用了中断门用于系统调用和调试用途。在WinDbg
下可用r idtr
读取IDT表
的首地址,r idtl
读取IDT表
的大小。
中断门的结构和调用门结构几乎一样,只是调用门用来写参数数目的位被清空不再使用和Type域不一样而已。
既然中断门的结构知道了,我们来看一下中断门的执行流程:
-
指令格式:
INT N (N为中断门索引号)
-
执行步骤:
- 在没有权限切换时,会向堆栈顺次压入
EFLAG
、CS
和EIP
;如果有权限切换,会向堆栈顺次压入SS
、ESP
、EFLAG
、CS
和EIP
。 CPU
会索引到IDT
表。后面的N
表示查IDT表
项的下标。对比调用门,中断门没有了RPL
,故CPU
只会校验CPL
。- 在中断门中,不能通过
RETF
返回,而应该通过IRET
/IRETD
指令返回。
- 在没有权限切换时,会向堆栈顺次压入
陷阱门
陷阱门的结构和中断门结构几乎一样,只是Type域不同而已,如下图所示(图中的D
表示是否为32位
,如果是则为1
):
陷阱门执行流程一模一样。与中断门的区别,中断门执行时,将IF位
清零,但陷阱门不会。
本篇小结
- CS的权限一旦改变,SS的权限也要随着改变,CS与SS的等级必须一样。
JMP FAR
只能跳转到同级非一致代码段,但CALL FAR
可以通过调用门提权,提升CPL
的权限。- 调用门总结:
- 当通过门,权限不变的时候,只会
PUSH
两个值:CS
和返回地址
,新的CS的值由调用门决定。 - 当通过门,权限改变的时候,会
PUSH
四个值:SS
、ESP
、CS
和返回地址
,新的CS
的值由调用门决定,新的SS
和ESP
由TSS
提供。 - 通过门调用时,要执行哪行代码由调用门决定,但使用
RETF
返回时,由堆栈中压入的值决定,这就是说,进门时只能按指定路线走,出门时可以FQ,即只要改变堆栈里面的值就可以想去哪去哪。 - 可不可以再建个门出去呢?当然可以了。前门进,后门出。
- 当通过门,权限不变的时候,只会
练习
本节的答案将会在下一节进行讲解,务必把本节练习做完后看下一个讲解内容。不要偷懒,实验是学习本教程的捷径。
俗话说得好,光说不练假把式,如下是本节相关的练习。如果练习没做好,就不要看下一节教程了,越到后面,不做练习的话容易夹生了,开始还明白,后来就真的一点都不明白了。本节练习不多但坑很多,需要花费大量的时间,请保质保量的完成。
1️⃣ 构造无参的调用门,实现提权后读取高2G的地址并分析堆栈情况。
2️⃣ 构造有参的调用门,实现提权后正确依次取出参数并分析堆栈情况。
3️⃣ 构造调用门,提权后,实现“FQ”,即不按原函数地址返回。
4️⃣ 构造中断门,实现提权后读取高2G的地址并分析堆栈情况。
5️⃣ 构造陷阱门,实现提权后读取高2G的地址并分析堆栈情况。
下一篇
本文来自博客园,作者:寂静的羽夏 ,一个热爱计算机技术的菜鸟
转载请注明原文链接:https://www.cnblogs.com/wingsummer/p/15318162.html