写在前面

  此系列是本人一个字一个字码出来的,包括示例和实验截图。如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

你如果是从中间插过来看的,请仔细阅读 羽夏看Linux系统内核——简述 ,方便学习本教程。

练习及参考

  1. 为什么20位的寻址可以达到1MB
点击查看答案
2^20 B = 2^10 * 2^10 B = 1 MB
  1. 拆分如下的段描述符:
00000000`00000000 00cf9b00`0000ffff
00cf9300`0000ffff 00cffb00`0000ffff
00cff300`0000ffff 80008b04`200020ab
ffc093df`f0000001 0040f300`00000fff
0000f200`0400ffff 00000000`00000000
80008955`22000068 80008955`22680068
00009302`2f40ffff 0000920b`80003fff
ff0092ff`700003ff 80009a40`0000ffff
80009240`0000ffff 00009200`00000000
点击查看答案
本答案仅提供一个样例,其余自行校验:
对于段描述符 0040f300`00000fff :
P:1
S:1
G:0
Type:3
DB:1
Base:0
Limit:FFF
DPL:3
  1. 拆分如下段选择子:
002B 0023 0010 001B 003B
点击查看答案
002B:
RPL:3
TI:0
Index:5
0023:
RPL:3
TI:0
Index:4
0010:
RPL:0
TI:0
Index:2
001B:
RPL:3
TI:0
Index:3
003B:
RPL:3
TI:0
Index:7
  1. 快速辨别问题2给定段描述符是否可用以及段基址、段长(至少10个)
点击查看答案

  1. 记住代码段间跳转的执行流程。
点击查看答案

  本篇文章涉及调用门、中断门、陷阱门这三个重要的“门”,那么“门”到底是什么。打个比方,“门”就类似于你去办理身份证必须要进入派出所的门一样。你想办理身份证就必须通过这个门,如果你进不了门就办理不了。调用门、中断门、陷阱门也是如此,通过它们你可以修改段的属性,甚至能提权去做一些应用层做不了的事情。

调用门

  在将所有的知识之前,先讲一下调用门,因为后面的知识频繁用到了调用门的概念,调用门的结构如下图所示。它和普通的段描述符结构十分相似。低四个字节改为段选择子,如果指向的段描述符的DPL小于CPL,则会提权。高四个字节低5个位是调用调用门需要的参数数目。低四个字节的低16位和高四个字节的高16位拼接为跳转后新的在段中的偏移,也就是调用后EIP的位置。

  它的具体细节将会在长调用讲完后继续讲解。

长调用

  介绍长调用,我先来讲一下什么是短调用。短调用就是我们在汇编常见的CALL指令,调用格式为:CALL 立即数/寄存器/内存。为什么是短调用,我们来看一下执行该指令时堆栈的变化:

  调用CALL指令之后,CPU只将当前的EIP压入堆栈后跳转到目标地址,发生改变的寄存器只有ESPEIP,即所谓的短调用。

  长调用分为两种,一种提权,一种不提权,调用格式为:指令格式:CALL CS:EIP,其中EIP是废弃的,CS为指向调用门的段选择子。但是值得注意的是CS一旦更换,它的EIPSS要同时更换。为什么EIP需要更换我就不说了。在代码执行的时候,一定会用到堆栈,堆栈的段权限必须与CS匹配,这就是为什么SS必须更换。我们接下来看看长调用到底是何方神圣。

长调用不提权

  当段选择子指向的调用门不提权时。发生改变的寄存器有ESPEIPCS,比短调用多一个CS。执行情况如下图所示:

长调用提权

  当段选择子指向的调用门不提权时。发生改变的寄存器有ESPEIPCSSS。执行情况如下图所示:

  心细的你获取发现,SS并没有压入堆栈之中,还有ESP0也没用通过已知方式获取得到。它从哪里来呢?下一节教程将会讲解。

调用门(续)

  本篇开头简单介绍了调用门,接下来将会详细介绍它的调用流程。先把上面的调用门结构图贴到下面,以供方便学习:

  调用门执行流程如下所示:

  • 指令格式:CALL CS:EIP (EIP是废弃的)

  • 执行步骤(具体详情请看长调用):

    1. 根据CS的值查GDT表,找到对应的段描述符且该描述符是一个调用门。
    2. 在调用门描述符中存储另一个代码段的段选择子,将其加载到CS中。
    3. 选择子指向的段的Base + 偏移地址就是真正要执行的地址。

  当然段选择子加载段描述符的过程都会有权限检查,不懂的话请不要再继续了,回去查看上一篇复习,懂了再回来学习。

中断门

  讲中断门之前,我先来介绍一个新的表,称之为IDT表IDT表GDT表不同,它的第一个元素不是NULLIDT表包含3种门描述符:任务门描述符、中断门描述符、陷阱门描述符,这里面的几种类型都会在后面讲到。中断门的结构如下图所示(图中的D表示是否为32位,如果是则为1):

  IDT也是由一系列描述符组成的,每个描述符占8个字节。Windows没有使用调用门,但是使用了中断门用于系统调用和调试用途。在WinDbg下可用r idtr读取IDT表的首地址,r idtl读取IDT表的大小。

  中断门的结构和调用门结构几乎一样,只是调用门用来写参数数目的位被清空不再使用和Type域不一样而已。

  既然中断门的结构知道了,我们来看一下中断门的执行流程:

  • 指令格式:INT N (N为中断门索引号)

  • 执行步骤:

    1. 在没有权限切换时,会向堆栈顺次压入EFLAGCSEIP;如果有权限切换,会向堆栈顺次压入SSESPEFLAGCSEIP
    2. CPU会索引到IDT表。后面的N表示查IDT表项的下标。对比调用门,中断门没有了RPL,故CPU只会校验CPL
    3. 在中断门中,不能通过RETF返回,而应该通过IRET/IRETD指令返回。

陷阱门

  陷阱门的结构和中断门结构几乎一样,只是Type域不同而已,如下图所示(图中的D表示是否为32位,如果是则为1):

  陷阱门执行流程一模一样。与中断门的区别,中断门执行时,将IF位清零,但陷阱门不会。

本篇小结

  1. CS的权限一旦改变,SS的权限也要随着改变,CS与SS的等级必须一样。
  2. JMP FAR只能跳转到同级非一致代码段,但CALL FAR可以通过调用门提权,提升CPL的权限。
  3. 调用门总结:
    • 当通过门,权限不变的时候,只会PUSH两个值:CS返回地址,新的CS的值由调用门决定。
    • 当通过门,权限改变的时候,会PUSH四个值:SSESPCS返回地址,新的CS的值由调用门决定,新的SSESPTSS提供。
    • 通过门调用时,要执行哪行代码由调用门决定,但使用RETF返回时,由堆栈中压入的值决定,这就是说,进门时只能按指定路线走,出门时可以FQ,即只要改变堆栈里面的值就可以想去哪去哪。
    • 可不可以再建个门出去呢?当然可以了。前门进,后门出。

任务段

什么是任务段

  我们回顾一下之前所学内容,在调用门、中断门与陷阱门中,一旦出现权限切换,那么就会有堆栈的切换。而且,由于CSCPL发生改变,也导致了SS也必须要切换。切换时,会有新的ESPSS从哪里来的呢?那就是任务状态段提供的。任务状态段简称任务段,英文缩写为TSSTask-state segment

  TSS是一块内存,大小为104字节,内存结构如下图所示:

TSS 的作用

  Intel的设计TSS目的,用官方的话说就是实现所谓的任务切换。CPU的任务在操作系统的方面就是线程。任务一切换,执行需要的环境就变了,即所有寄存器里面的值,需要保存供下一次切换到该任务的时候再换回去重新执行。

  说到底,TSS的意义就在于可以同时换掉一堆寄存器。本质上和所谓的任务切换没啥根本联系。而操作系统嫌弃Intel的设计过于麻烦,自己实现了所谓的任务切换,即线程切换。具体将会在后面的教程进行讲解。

CPU 如何找到 TSS

  TSS是一个内存块,并不在CPU中,那么它是怎样找到正确的TSS呢?那就是之前提到的TR段寄存器。CPU通过TR寄存器索引TSS是示意图如下图所示:

TSS段描述符

  TSS段描述符的结构和普通的段描述符没啥区别,就不详细介绍了,如下图所示:

TR寄存器读写

加载TSS

  • 指令:LTR
  • 说明:用LTR指令去装载,仅仅是改变TR寄存器的值(96位),并没有真正改变TSSLTR指令只能在系统层使用,加载后TSS段描述符会状态位会发生改变。

读取TR寄存器

  • 指令:STR
  • 说明:如果用STR去读的话,只读了TR的16位,即选择子。

修改TR寄存器途径

  1. 在0环可以通过LTR指令去修改TR寄存器。
  2. 在3环可以通过CALL FAR或者JMP FAR指令来修改。用JMP去访问一个任务段的时候,如果是TSS段描述符,先修改TR寄存器,在用TR.Base指向的TSS中的值修改当前的寄存器。

CALL 和 JMP 实现任务切换的不同之处

  用CALLJMP实现任务切换,它们之间有什么不同呢?答案就不用说了。如果用CALL,它会把Previous Task Link填写数值,并EFLAGS寄存器的NT位改为1。如果这个位被改为1iret指令会被当做任务返回,从TSS里的取出Previous Task Link返回;反之则为正常的中断返回,从堆栈读值返回。而JMP指令不会做上述事情。

任务门

  任务门的结构如下图所示:

  任务门的结构我就不想再赘述了,来看看它的执行过程:

  1. 通过INT N的指令进行触发任务门
  2. IDT表,找到任务门描述符
  3. 通过任务门描述符,查GDT表,找到TSS段描述符
  4. 使用TSS段中的值修改TR寄存器
  5. IRETD返回

练习与思考

本节的答案将会在下一节进行讲解,务必把本节练习做完后看下一个讲解内容。不要偷懒,实验是学习本教程的捷径。

  俗话说得好,光说不练假把式,如下是本节相关的练习。如果练习没做好,就不要看下一节教程了,越到后面,不做练习的话容易夹生了,开始还明白,后来就真的一点都不明白了。本节练习只是概念性的,就不单独给出参考了。

  1. 熟练掌握门提权/非提权的堆栈变化过程。

下一篇

  羽夏看Linux内核——中断相关入门知识

羽夏看Linux内核——门相关入门知识的更多相关文章

  1. 羽夏看Linux内核——段相关入门知识

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  2. 羽夏看Linux内核——启动那些事

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  3. 羽夏看Linux内核——中断与分页相关入门知识

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  4. 羽夏看Linux内核——引导启动(下)

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  5. 羽夏看Linux内核——环境搭建

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  6. 羽夏看Linux内核——引导启动(上)

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.如有好的建议,欢迎反馈.码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作.如想转载,请把我的转载信息附在文章后面,并 ...

  7. 羽夏看Win系统内核—— VT 入门番外篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  8. 羽夏看Win系统内核——驱动篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  9. 羽夏看Win系统内核——同步篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

随机推荐

  1. 【leetcode】42. 接雨水

    目录 题目 题解 题目 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水. 示例 1: 输入:height = [0,1,0,2,1,0,1,3,2,1 ...

  2. socket套接字补充、操作系统发展史、进程

    目录 socket套接字之UDP协议 操作系统的发展史 手工操作 批处理系统 联机批处理系统 脱机批处理系统 多道技术 进程理论 并发与并行 同步与异步 阻塞与非阻塞 同步异步与阻塞非阻塞总结 soc ...

  3. 每天一个 HTTP 状态码 205

    205 Reset Content 205 Reset Content 表示服务器成功地处理了客户端的请求,要求客户端重置它发送请求时的文档视图.这个响应码跟 204 No Content 类似,也不 ...

  4. KMP算法(改进的模式匹配算法)——next函数

    KMP算法简介 KMP算法是在基础的模式匹配算法的基础上进行改进得到的算法,改进之处在于:每当匹配过程中出现相比较的字符不相等时,不需要回退主串的字符位置指针,而是利用已经得到的部分匹配结果将模式串向 ...

  5. C# 四舍五入中一处易错点

    ,你没看错,四舍五入的结果 和我们期待的不太一样 Why?? 进入源码看下,注释中解释的很清楚.. 默认情况下,Math.Round()方法返回的是最接近的整数,这个没问题,问题是当要转换的数据在 两 ...

  6. 爷青回,canal 1.1.6来了,几个重要特性和bug修复

    刚刚在群里看到消息说,时隔一年,canal 1.1.6正式release了,赶紧上去看看有什么新特性. (居然才发布了6个小时,前排围观) 1.什么是canal canal [kə'næl],译意为水 ...

  7. vue大型电商项目尚品汇(前台篇)day05终结篇

    前台部分到此结束,一路走来还挺怀念,今天主要是对整个项目的完成做一个最后的收尾工作,对于功能上的需求没有什么了,主要就是项目上线的一些注意事项. 一.个人中心二级路由 当我们点击查看订单应该跳转到个人 ...

  8. 用树莓派USB摄像头做个监控

    [前言] 看着阴暗的角落里吃灰噎到嗓子眼的树莓派,一起陪伴的时光历历在目,往事逐渐涌上心头,每每触及此处,内心总会升腾起阵阵怜悯之情... 我这有两个设备,一个是积灰已久的树莓派,另一个是积灰已久的U ...

  9. 第6章 字符串(下)——C++字符串

    6.5 C++ strings(C++字符串) C风格字符串常见错误:试图去访问数组范围以外的元素:没有使用函数strcpy( )来实现字符串之间的复制:没有使用函数strcmp( )来比较两个字符串 ...

  10. 【Spring】AOP实现原理(一):AOP基础知识

    AOP相关概念 在学习AOP实现原理之前,先了解下AOP相关基础知识. AOP面向切面编程,它可以通过预编译方式或者基于动态代理对我们编写的代码进行拦截(也叫增强处理),在方法执行前后可以做一些操作, ...