写在前面

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

你如果是从中间插过来看的,请仔细阅读 羽夏看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. 技术分享 | 云原生多模型 NoSQL 概述

    作者 朱建平,TEG/云架构平台部/块与表格存储中心副总监.08年加入腾讯后,承担过对象存储.键值存储,先后负责过KV存储-TSSD.对象存储-TFS等多个存储平台. NoSQL 技术和行业背景 No ...

  2. 一文讲透为Power Automate for Desktop (PAD) 实现自定义模块 - 附完整代码

    概述 Power Automate for Desktop (以下简称PAD)是微软推出的一款针对Windows桌面端的免费RPA(机器人流程自动化)工具,它目前默认会随着Windows 11安装,但 ...

  3. Python常用标准库(pickle序列化和JSON序列化)

    常用的标准库 序列化模块 import pickle 序列化和反序列化 把不能直接存储的数据变得可存储,这个过程叫做序列化.把文件中的数据拿出来,回复称原来的数据类型,这个过程叫做反序列化. 在文件中 ...

  4. 【Unity Shader学习笔记】Unity基础纹理-单张纹理

    1 单张纹理 1.1 纹理 使用纹理映射(Texture Mapping)技术,我们把一张图片逐纹素(Texel)地控制模型的颜色. 美术人员建模时,会在建模软件中利用纹理展开技术把纹理映射坐标(Te ...

  5. poj1475 -- Pushing Boxes

    这道题其实挺有趣 的,这让我想起小时候诺基亚手机上的推箱子游戏(虽然一点也不好玩) (英文不好-->)  题意翻译: 初始人(S),箱子(B),目的地(T)用人把箱子推到 T最小步数及其路径(满 ...

  6. django框架3

    内容概要 注册登录功能编写 django请求生命周期流程图 路由层相关知识 1.路由匹配 2.无名有名分组 3.反向解析 4.名称空间 5.路由分发 内容详情 注册登录功能编写 1.使用自带的sqli ...

  7. Linux Cgroup v1(中文翻译)(2):CPUSETS

    英文原文:https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/cpusets.html Copyright (C) 2004 BU ...

  8. printf 输出前导0

    printf ("%3d\n", 5); printf ("%03d\n", 5); 输出为

  9. 【Github】 Github访问不是私密连接问题

    前言 GitHub是一个软件项目的托管平台,是我们经常需要访问的,我原本在学校时候虽然网速比较慢,但是还以能够满足一些代码下载和上传的,在暑假回到家,再去访问的时候就出现了不能访问的问题. 问题描述 ...

  10. 想学嵌入式?要不一起玩 Arduino 吧

    作者:HelloGitHub-Anthony 这里是 HelloGitHub 推出的<讲解开源项目>系列,本期介绍的是如何用开源硬件开发平台 Arduino,自己动手做一个温湿度显示器. ...