Linux 中如何使用段寄存器(fs、gs、cs、ss、ds、es)?

逆向工程 linux x86 部件 amd64
2021-07-06 00:15:26

我尝试了解 Linux 上 i386 和 amd64 架构的内存分段过程。这似乎与段寄存器%fs, %gs, %cs, %ss, %ds,密切相关%es

有人可以解释如何在用户和内核程序中使用这些寄存器吗?

3个回答

内核视角:

我将尝试从内核的角度来回答,涵盖各种操作系统。

内存分段是访问内存区域的旧方法。所有主要的操作系统,包括 OSX、Linux(从 0.1 版开始)和 Windows(从 NT 开始)现在都在使用分页,这是访问内存的更好方式(恕我直言)。

英特尔一直在其处理器中引入向后兼容性(IA-64 除外,我们看到它是如何失败的......)因此,在其初始状态(重置后),处理器以称为实模式的模式启动,在这种模式下,默认情况下启用分段以支持旧软件。在操作系统启动过程中,处理器进入保护模式,然后启用分页。

在分页之前,段寄存器是这样使用的

在实模式下,每个逻辑地址直接指向物理内存位置,每个逻辑地址由两个 16 位部分组成: 逻辑地址的段部分包含一个段的基地址,粒度为 16 字节,即一个段可以从物理地址 0, 16, 32, ..., 2 20 -16。逻辑地址的偏移量部分包含段内部的偏移量,即物理地址可以计算为physical_address := segment_part × 16 + offset(如果地址线A20启用),分别(如果A20关闭)每个段的大小为2 16字节。[维基百科](segment_part × 16 + offset`) mod 220

让我们看一些例子(286-386时代):

286架构引入了4个段:CS(代码段)DS(数据段) SS(堆栈段)ES(额外段) 386架构引入了两个新的通用段寄存器FSGS

典型的汇编操作码(采用 Intel 语法)如下所示:

mov dx, 850h
mov es, dx     ; Move 850h to es segment register
mov es:cx, 15h ; Move 15 to es:cx

使用分页(保护模式),段寄存器不再用于寻址内存位置。

在保护模式下,segment_part它被一个 16 位选择器取代,选择器的高 13 位(第 3 位到第 15 位)包含描述符表内条目的索引。下一位(位 2)指定操作是与 GDT 还是 LDT 一起使用。选择器的最低两位(bit 1 和bit 0)组合起来定义请求的权限;其中值为 0 的优先级最高,值为 3 的优先级最低。[维基百科]

然而,这些段仍然用于在 GDT 中强制执行硬件安全

全局描述符表或 GDT 是英特尔 x86 系列处理器使用的一种数据结构,以 80286 开头,用于定义程序执行期间使用的各种内存区域的特征,包括基地址、大小和访问权限,如可执行性和可写性。这些内存区域在 Intel 术语中称为段。[维基百科]

因此,实际上保护模式下的段寄存器用于存储 GDT 的索引。

一些操作系统(例如 Windows 和 Linux)将某些段用于内部使用。例如,Windows x64 使用GS寄存器访问 TLS(线程本地存储),而在 Linux 中它用于访问 cpu 特定内存。

用户视角:

从用户的角度来看,在最近使用分页的操作系统中,内存以所谓的“扁平模式”工作。每个进程以线性方式访问自己的内存(4GB),因此基本上不需要段寄存器。

它们仍然是寄存器,因此它们当然可以用于各种其他组装操作。

FS 指向异常处理链,CS 和 DS 由 OS 填充代码和数据段。SS 是电池/堆栈段。据我所知,GS 和 ES 是免费的。
内核模式还是用户模式应该无关紧要(它们被一些指令使用,例如 XLAT、MOVS 和其他一些指令,因此您必须以相同的方式使用它们),但以防万一我在谈论编程用户空间。

我以前没有注意到,但是您使用的是 %fs 符号,而不是 FS,所以您可能指的是 Linux,这是另一个故事(您也可以更清楚地了解保护/实模式)。您还可以从 stackexchange 的其他答案中看到,linux 在 FS 和 GS 中显然为您提供了“线程本地存储”和“处理器数据区”。CS、DS 和 SS 应该仍然是代码/数据/堆栈。

为了论证起见,我不知道您如何在 Mac 上使用这些寄存器。

对于 64 位,这取决于:如果不是在兼容模式下(您可以在其中执行 64 位和 32 位代码),那么 DS、ES 和 SS 将被忽略,并且像 POP SS 这样的指令会给出错误。没有分段(内存模型是扁平的),应该没有实模式(但我认为你只是指保护模式?),如果我没猜错的话,就没有硬件任务切换。
在 64 位模式下有关于 CS、FS 和 GS(特别是隐藏部分)的更多详细信息,但由于它不经常使用,所以最好省略它们。

您可以查看 AMD 系列处理器的手册,尤其是在 64 位传统模式的情况下:http :
//developer.amd.com/resources/documentation-articles/developer-guides-manuals/

我为一个被标记为重复和关闭的问题写了一个 Windows 特定的答案,关闭标志指的是这个线程,所以我在这里发布了一个答案

os win7 sp1 32 位机器
内核转储使用来自 sysinternals 的 livekd

一个 16 位的段寄存器包含
13 位选择器
1 位表描述符
2 位 requester_privilege_level

Selector        tl  rpl 
0000000000000----0---00 

所以 cs 和 fs 转换为二进制将是

kd> r cs;r fs
cs=00000008  = 0b 00001 0 00
fs=00000030  = 0b 00110 0 00

2 位 rpl 表示 0,1,2,3 环(所以 00 = 0 = 环零)

gdt = 1 位表示 0,1(0 表示GDT,1 表示LDT

全局描述符表和局部描述符表

高 13 位代表段选择器

所以 cs = 0x08 有一个段选择器 0b 001 = 0x1 即 gdtr@1
& fs = 0x30 有一个段选择器 0f 0b 110 = 0x6 即 gdtr@6

内核 cs,fs 与用户 cs,fs 不同,从 Windbg 的 dg 命令可以看出

kd> dg @cs  <<<<<<<--- kernel 
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0008 00000000 ffffffff Code RE Ac 0 Bg Pg P  Nl 00000c9b

0:000> dg @cs <<<<<<<<----user 
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
001B 00000000 ffffffff Code RE Ac 3 Bg Pg P  Nl 00000cfb

kd> dg @fs <<<<<<<<------- kernel
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
0030 82f6dc00 00003748 Data RW Ac 0 Bg By P  Nl 00000493

0:000> dg @fs
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
003B 7ffdf000 00000fff Data RW Ac 3 Bg By P  Nl 000004f3

您可以从
osdevwiki_gdt robert-collins_ddj_article收集有关 gdt 的足够信息

我在这里使用 livekd 手动执行此操作

使用windbg你可以获得描述符和任务门寄存器

kd> rM 100
gdtr=80b95000   gdtl=03ff idtr=80b95400   idtl=07ff tr=0028  ldtr=0000

每个 gdtr 条目是 64 位,因此您可以有 7f 个 gdtr 条目,如您所见 gdtl 为 3ff 0x80*0x08-1 = 0x400-1 = 0x3ff(索引从 0 开始,而不是 1)

所以 gdtr 条目 @1,@2 是 @gdtr+(0x1*0x8) @gdtr+(0x2*0x08=0x10) 等等

kd> dq @gdtr+8 l1    gdtr@1 = gdtr+0n1*0x8 =0n8  = 0x8    
80b95008  00cf9b00`0000ffff = gdtr+0n6*0x8 =0n48 = 0x30    
kd> dq @gdtr+30 l1   
80b95030  824093f6`dc003748   
kd> dq @gdtr+38 l1   
80b95038  7f40f3fd`e0000fff   

让位游戏手动最后两个 gdtr 条目

-------------------------------------------------------------------------------------------
gdtrentry        [63:     [55:  [51:  [47:          [39:                  [15:             
                  56]      52]   48]   40]           16]                    0]             
                 base     gdrs  L     p d  t     Base     Base             Limit           
                 Hi       rb0y  h     r l  y     Mid      Low                              
-------------------------------------------------------------------------------------------
bit position     66665555 5555  5544  4 44 44444 33333333 3322222222221111 1111110000000000
                 32109876 5432  1098  7 65 43210 98765432 1098765432109876 5432109876543210
-------------------------------------------------------------------------------------------
824093f6dc003748 10000010 0100  0000  1 00 10011 11110110 1101110000000000 0011011101001000
as hex           0x82     0100  0     1 0  0x13  0xF6     0xDC00           0x3748          
--------------------------------------- ---------------------------------------------------
7f40f3fde0000fff 01111111 0100  0000  1 11 10011 11111101 1110000000000000 0000111111111111
as hex           0x7F     0100  0     1 3  0x13  0xFD     0xE000           0x0FFF          
-------------------------------------------------------------------------------------------