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

   2023-02-09 学习力0
核心提示:羽夏看Linux内核之段相关入门知识,介绍学习该系列Linux内核的段相关的知识。写在前面  此系列是本人一个字一个字码出来的,包括示例和实验截图。如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请

写在前面

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

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

前置知识

  在开始正式介绍之前,有一些知识需要讲解一下,否则基本就是听天书。但是,有些知识是本教程的前置知识,也就是说,我不会在该教程介绍,但我们会去使用它:

  • 程序编写和现代操作系统的基本概念,比如虚拟地址、内存、进程线程等;
  • C/C++ 编写以及使用 GCC 编译;
  • 8086汇编的编写以及两种语法;
  • Make 的使用;

基础知识

  下面我们来介绍一些基础知识和硬件的“硬性规定”。

实模式与保护模式

  实模式是Intel 80286和之后的8086兼容CPU的操作模式。实模式的特性是一个20位的存储器地址空间,它寻址具有1MB的存储器的能力,可以直接软件访问BIOS以及周边硬件,没有硬件支持的分页机制和实时多任务概念。从80286开始,所有的8086 CPU的开机状态都是实模式。8086等早期的CPU只有一种操作模式,类似于实模式。

段寄存器

  当我们用汇编读写某一个地址时,比如用下面的代码:

mov dword ptr ds:[0x123456], eax

  其实我们真正读写的地址是:ds.base + 0x123456。并不是0x123456,不过正好的是ds段寄存器的基址是0而已。
  段寄存器有这几个:ES、CS、SS、DS、FS、GS、LDTR、TR,它们各有自己特殊的用途。
  段寄存器的结构可用下图表示:

  段寄存器具有96位,但我们可见的只有16位。我们可以用调试器随意加载一个程序,但由于我是64位系统,无法编译32位程序,也找不到相应的程序,就不给图了。
  既然是寄存器了,那就可以进行读写操作,如下将介绍读写段寄存器的操作:

  • Mov指令:MOV AX,ES,但只能读16位的可见部分;MOV DS,AX写段寄存器,写的是96位。
  • 读写LDTR的指令为:SLDT/LLDT
  • 读写TR的指令为:STR/LTR

CPU分级

  如果要讲段描述符与段选择子,先介绍CPU分级的概念。数值上越小,权限越大。如果低权限访问高权限的东西,会导致失败。0环被内核使用,虽然1环2环存在,但Windows只用了3环注意在学习保护模式是时候不要把操作系统的概念扯进去,还没到操作系统层面。 CPU分级示意图如下:

GDT 与 LDT

  GDT是全局描述符表。LDT为局部描述符表,但Windows并没有使用它,故不再介绍,感兴趣请查询Intel白皮书。当我们执行类似MOV DS,AX指令时,CPU会查表,根据AX的值来决定查找GDT还是LDT,并找到对应的段描述符。段描述符将会在后面部分进行介绍。

  GDT表存在于内存之中。CPU要想找到它,就必须知道它的位置。于是乎CPU有一个寄存器。它被称之为GDTR,存储了GDT表的位置和大小,是一个48位的寄存器,用C语言表示如下:

struct GDTR
{
    DWORD GDTBase;    //GDT表的地址
    SHORT limit;      //GDT表的大小
}

段选择子

  段选择子结构简单,那我先介绍它。它是一个16位的描述符,指向了定义该段的段描述符(段描述符比较复杂,后面将会完整介绍)。段选择子结构如下图所示:

  它的成员解释如下:

  • RPL:请求特权级别,通俗的讲我用什么权限来请求。
  • TI:TI=0时,查GDT表;TI=1时,查LDT表。
  • Index:处理器将索引值乘以8在加上GDT或者LDT的基地址,就是要加载的段描述符。

段描述符

  既然提到段描述符,那我来介绍一下它的结构如下图所示:

  段描述符有很多成员,它的成员将会在下面详细介绍,学习的时候一定要按照我介绍的顺序进行学习:

P位

  P = 1段描述符有效,P = 0段描述符无效。

Base

  Base被分成了三个部分,从图可知:Base的低16位被放到了段描述符的低四个字节,高16位被均分到段描述符的高四个字节的头和尾。把它们依次拼接起来就是完整的Base

Limit

  由图可知,把段描述符中所有的Limit拼接起来就只有20位。上一节教程说它有32位的Limit。那就是要看G位了。

G位

  如果G = 0,说明段描述符中的Limit的单位是字节,段长度Limit范围可从1B~1MB,即在20位的前面补3个0即可;如果G = 1,说明段描述符中的Limit的单位是字节为4KB,即段长度Limit范围可从4KB~4GB,在20位的后面补充FFF即可。举个例子,如果Limit拼接后的为FFFFF,如果G为0则为000FFFFF,反之为FFFFFFF

S位

  S = 1代码段或者数据段描述符,S = 0系统段描述符。

TYPE域

  TYPE域是比较复杂的成员,它表示的含义受S位的影响。

  • 当S位为1时

  此时段描述符表示的是代码段或者数据段,如下图所示:

  对于表格中Type域的属性和含义,如下表格所示:

属性 含义 属性 含义
A 访问位 E 向下扩展位
R 可读位 W 可写位
C 一致位

  对于比较特殊的属性,我们将进一步介绍:

C位

  C = 1:一致代码段;C = 0:非一致代码段。什么是一致代码段,什么是非一致代码段,将在后面的教程进行介绍。

E位

  什么是向下拓展位,我们以fs为例来看一下如下示意图:

  左边表示向上拓展,右边是向下拓展。即向上拓展basebase+limit之间区域有效,其余无效;向下拓展basebase+limit之间的区域无效,其余有效。这个位针对数据段有效。

  • 当S位为0时

  此时段描述符表示的是系统段,系统段有很多种,将会在后面的教程进行详细讲解。Type域每一个数值的含义如下图所示:

DB位

  DB位对不同的段具有不同的影响,情况如下:

1️⃣ 对CS段的影响
  D = 1采用32位寻址方式,D = 0采用16位寻址方式。

2️⃣ 对SS段的影响
  D = 1隐式堆栈访问指令(如:PUSH POP CALL)使用32位堆栈指针寄存器ESPD = 0隐式堆栈访问指令(如:PUSH POP CALL)使用16位堆栈指针寄存器SP

3️⃣ 向下拓展的数据段
  D = 1段上线为4GBD = 0段上线为64KB。至于是什么意思,我们来看下面一张图。

  红色表示向下拓展能寻址的范围。可以看出,如果D = 0,就算原来能寻址4GB,因为DB位的限制导致最大范围是64KB

DPL

  DPL(Descriptor Privilege Level),即描述符特权级别,规定了访问该段所需要的特权级别是什么。如果通俗的理解,就是:如果你想访问我,那么你应该具备什么权限

AVL

  AVL指示是否可供系统软件使用,由操作系统来使用,CPU并不使用它。

加载段描述符至段寄存器

  除了MOV指令,我们还可以使用LESLSSLDSLFSLGS指令修改寄存器。CS不能通过上述的指令进行修改,CS为代码段,CS的改变会导致EIP的改变,要改CS,必须要保证CSEIP同时改,后面会讲解。

CPL/RPL/DPL

  • CPL:CPU当前的权限级别
  • DPL:如果你想访问我,你应该具备什么样的权限(CPL)
  • RPL:用什么权限去访问一个段

RPL存在的意义

  举个例子,我们本可以用读写的权限去打开一个文件,但为了避免出错,有些时候我们使用只读的权限去打开。

一致代码段与非一致代码段

对于一致代码段,也称为共享段:

  • 特权级高的程序不允许访问特权级低的数据:核心态不允许访问用户态的数据
  • 特权级低的程序可以访问到特权级高的数据,但特权级不会改变:用户态还是用户态

对于非一致代码段:

  • 只允许同级访问
  • 绝对禁止不同级别的访问:核心态不是用户态,用户态也不是核心态

数据段的权限检查

  数值上,CPL<=DPLRPL<=DPL。同时满足上述条件才能通过。

代码段的权限检查

  下面的比较都是数值上的比较:

  • 如果是非一致代码段,要求:CPL==DPLRPL<=DPL
  • 如果是一致代码段,要求:CPL>=DPL

代码跨段基础

  代码跨段本质就是修改CS段寄存器。前面的教程介绍过段寄存器读写,除CS外,其他的段寄存器都可以通过MOV/LES/LSS/LDS/LFS/LGS指令进行修改。但是CS为什么不可以直接修改呢?CS的改变意味着EIP的改变,改变CS的同时必须修改EIP,故我们无法使用上面的指令来进行修改,这个也是CPU不允许的。

代码间的段间跳转

  段间跳转,有2种情况,即要跳转的段是一致代码段还是非一致代码段,它们不同做的权限检查就不同。
  同时修改CSEIP的指令如下:JMP FAR/CALL FAR/RETF/INT/IRETED

  本篇只介绍段间跳转,故只使用JMP FAR,即为长跳转。下面我举个示例来进行讲解:

CPU如何执行这行代码JMP 0x20:0x004183D7

1️⃣ 段选择子拆分
  0x20对应二进制形式:0000 0000 0010 0000

  • 解析结果:
    • RPL = 0
    • TI = 0
    • Index = 4

2️⃣ 查表得到段描述符

  TI=0 所以查GDT表,Index=4找到对应的段描述符。注意四种情况可以跳转:代码段、调用门、TSS任务段、任务门。后面的几种将会在以后的教程详细讲解。

3️⃣ 权限检查

  请参考本节的代码段的权限检查

4️⃣ 加载段描述符
  通过上面的权限检查后,CPU会将段描述符加载到CS段寄存器中。

5️⃣ 代码执行
  CPUCS.Base + Offset的值写入EIP然后跳转到将要执行的CS:EIP处的代码,段间跳转结束。

直接对代码段进行JMP或者CALL的操作,无论目标是一致代码段还是非一致代码段,CPL都不会发生改变。如果要提升CPL的权限,只能通过调用门。

练习与思考

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

  俗话说得好,光说不练假把式,如下是本节相关的练习。如果练习没做好,就不要看下一节教程了,越到后面,不做练习的话容易夹生了,开始还明白,后来就真的一点都不明白了。本节练习不多,请保质保量的完成。

  1. 为什么20位的寻址可以达到1MB
  2. 拆分如下的段描述符:
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
  1. 拆分如下段选择子:
002B 0023 0010 001B 003B
  1. 快速辨别问题2给定段描述符是否可用以及段基址、段长(至少10个)
  2. 记住代码段间跳转的执行流程。

下一篇

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

 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 【强转】QEMU+GDB调试linux内核全过程
    【强转】QEMU+GDB调试linux内核全过程
    昨天更新了一篇名为《QEMU+GDB调试linux内核全过程》[link][https://blog.csdn.net/weixin_37867857/article/details/88138432]的博客,发现排版比较混乱,而且思维也比较混乱。咋一看下来简直是惨不忍睹,而且会给读者在安装过程中一种云里雾里的感觉,加上
    03-08
  • Linux下Bochs,NASM安装和使用 linux bom
    Linux下Bochs,NASM安装和使用 linux bom
    以Ubuntu为例,先更新一下:sudo apt-get updatesudo apt-get upgrade然后安装Bochs环境:sudo apt-get install build-essential xorg-dev libgtk2.0-dev安装NASMNASM官网下载,这以nasm-2.14.02.tar.gz为例:用tar zxvf nasm-2.14.02.tar.gz解压后编译安装cd
    03-08
  • 把玩Alpine linux(一):安装
    把玩Alpine linux(一):安装
    导读Alpine Linux是一个面向安全应用的轻量级Linux发行版。它采用了musl libc和busybox以减小系统的体积和运行时资源消耗,同时还提供了自己的包管理工具apk。Alpine 的内核都打了grsecurity/PaX补丁,并且所有的程序都编译为Position Independent Executabl
    03-08
  • 日志审计与分析实验三(rsyslog服务器端和客户端配置)(Linux日志收集)
    日志审计与分析实验三(rsyslog服务器端和客户
     Linux日志收集一、实验目的:1、掌握rsyslog配置方法2、配置rsyslog服务收集其他Linux服务器日志:C/S架构:客户端将其日志上传到服务器端,通过对服务器端日志的查询,来实现对其他客户端的日志进行集中管理;下面实现就是通过两套机器来实现,(server:19
    03-08
  • Linux学习系列--如何在Linux中进行文件的管理
    Linux学习系列--如何在Linux中进行文件的管理
    文件在常见的Linux的文件系统中,经常使用能了解到的文件管理系统是分为多个文件夹进行管理的。如何查看文件路径 pwd ,在文件目录中,会有一个点(.)代表的是当前目录,两个点(..)代表的是当前目录的上层目录在Linux下,所有以点开始的文件都是“隐藏文件
    03-08
  • [JetBrains] 我想在 Linux 上使用 macOS 键绑定!
    [JetBrains] 我想在 Linux 上使用 macOS 键绑定
    很高兴认识你,我的名字是kitakkun。我最近开始实习,是工程界的新手。顺便说一句,这是我的第一篇文章。你最喜欢的操作系统是什么?视窗?苹果系统?还是Linux?我将它们全部用于不同的目的,但感觉就像 macOS ≒ LinuxWindows。一两个月前,我最喜欢 Linux
    03-08
  • linux 配置Socks51
    linux 配置Socks51
    ***大家耳熟能详,但是socks用到的人比较少,那什么是socks呢?请看第二段或者百度百科,socks分别有4和5两个版本,现在5为主流。工作中经常用***访问国外,但是同时国内的速度又慢了,让人很纠结,实际上这个时候可以考虑使用socks。指定某一个程序使用国外s
    02-10
  • linux下如何单独编译设备树? linux设备树是什
    答: make vendor/device_name.dtb  如: make freescale/fsl-1043a-rdb.dtb
    02-10
  • linux下mysql开启远程访问权限及防火墙开放3306端口
    linux下mysql开启远程访问权限及防火墙开放3306
    开启mysql的远程访问权限默认mysql的用户是没有远程访问的权限的,因此当程序跟数据库不在同一台服务器上时,我们需要开启mysql的远程访问权限。主流的有两种方法,改表法和授权法。相对而言,改表法比较容易一点,个人也是比较倾向于使用这种方法,因此,这
    02-10
  • 移植linux3.7到nuc900系列开发板遇到的问题
    通过移植学习linux新版本内核,大概了解一下内核变化。记录一下移植过程中遇到的问题或值得注意的地方。1,添加一款arm9芯片的支持首先修改\arch\arm\tools\mach-types文件添加一行w90p950evbMACH_W90P950EVBW90P950EVB同目录下的脚本文件在编译内核时会根据
    02-10
点击排行