C/C++抗艾第一人龚伦强谈:段寄存器&GDT表

大家好,我是龚伦强,国内C/C++抗艾第一人,也是国内C/C++技术top1,这里友情提请大家,请洁身自好,不要弄得跟我一样的下场

钱,我大把的~(座驾宝马X1)

技术,我牛逼的很,假以时日我绝对能一个人取代C++的stl,可惜时日不多

保护模式

X86 CPU的三个模式:实模式、保护模式和虚拟8086模式。

参考书籍:

《Intel白皮书第三卷》

一、段寄存器

什么是段寄存器?

当我们用汇编读写某一个地址时:

mov dword ptr ds:[0x123456], eax

我们真正读写的地址是:

ds.base + 0x123456

段寄存器有几个,有哪些?

有八个,分别是:

ES CS SS DS FS GS LDTR TR

段寄存器的结构

结构图表示:

段寄存器结构图

结构体表示:

struct SegMent { WORD Selector;// 段选择子 16位 可见 WORD Atrributes;// 段属性 16位 不可见 DWORD Base;// 段起始地址 32位 不可见 DWORD Limit;// 段大小 32位 不可见 }

段寄存器的读写

读:MOV AX,ES

写:MOV DS,AX

注意:段寄存器在读的时候只读16位,但写的时候会写入96位!

思考:如何证明向段寄存器写入时写了96位?

段寄存器的属性

属性图:

在这里插入图片描述

图中红色字体部分在不同环境中可能不同

1)探测Attribute:

在编辑器中尝试编译并执行以下代码:

int var = 0; __asm { mov ax,ss// 此处不能为CS CS可读 可执行 但不可写 mov ds,ax mov dword ptr ds:[var],eax }

编译器能成功编译上述代码,并且程序运行过程中没有报错


再在编辑器中尝试编译并执行以下代码:

int var = 0; __asm { mov ax,cs// SS 改成了 CS mov ds,ax mov dword ptr ds:[var],eax }

编译器能成功编译上述代码,但程序运行过程中报错

上面的两个例子说明段寄存器的Attribute在写入时会被更改!

2)探测Base:

在编辑器中尝试编译并执行以下代码:

int var = 1; __asm { mov ax,fs mov gs,ax mov eax,gs:[0]// 不要使用DS 否则编译不过去 mov dword ptr ds:[var],eax // = mov edx,dword ptr ds:[0x7FFDF000] }

编译器能成功编译上述代码,并且程序运行过程中没有报错

说明段寄存器的Base在写入时会被更改!

3)探测Limit:

int var = 1; __asm { mov ax,fs mov gs,ax mov eax,gs:[0x1000] // 访问的地址相当于下面这行注释的代码 但DS的Limit是0xFFFFFFFF // mov eax,dword ptr ds:[0x7FFDF000+0x1000] mov dword ptr ds:[var],eax }

编译器能成功编译上述代码,但程序运行过程中报错

这是因为 FS 段寄存器的 Limit 为 0xFFF,而我们输入的段偏移为0x1000

思考:段寄存器在写入时,只给了16位,剩下的80位填什么?数据从哪里来?

二、GDT表与LDT表

GDT:全局描述符表

LDT :局部描述符表

当我们执行类似MOV DS, AX指令时,CPU会查表,根据AX的值来决定查找GDT还是LDT,查找表的什么位置,以及查出多少数据

GDT表

工具:WinDbg

WinDbg中查看GDT与LDT命令

gdtr是一个寄存器,存储了GDT表所在位置

gdtl也是一个寄存器,存储了GDT表的大小

在内存中查看GDT表

gdt1

第一排的数据为内存地址,红框中的数据才是真正的内存数据,即GDT表

我们已经知道如何查看GDT表了,但是如果想要看懂这张表,得先学习段描述符和段选择子

1)段描述符

描述:GDT表中存储的元素称为段描述符

大小:每个段描述符占用空间为8个字节

为了更方便观察,在WinDbg中使用dq命令查看GDT表:

gdt2

段描述符结构图:

段描述符

段描述符高位在前,低位在后

例如GDT表的第二项:00cf9b00`0000ffff

00cf9b00对应结构图的高四字节(上面一行),0000ffff对应结构图的低四字节(下面一行)

段描述符的属性

P位

P = 1:段描述符有效

P = 0:段描述符无效

段描述符加载时,首先看P位是否为1

G位

G=0:段寄存器的Limit元素单位为字节,最大值为0x000FFFFF

G=1:段寄存器的Limit元素单位为4KB,最大值为0xFFFFFFFF

S位

S = 1:段描述符为代码段或数据段描述符

S = 0:段描述符为系统段描述符

Type域

当S = 1时,即段描述符为代码段或数组段描述符时,Type域结构图如下:

Type域-代码段&数据段

第11位为0:段描述符为数据段描述符

第11位为1:段描述符为代码段描述符

A位:若该代码段/数据段未被访问过,则值为0,否则为1

W位:若为1,表示该段可写

E位:若为0,则向上拓展,若为1,则向下拓展

图例:

拓展方向

向上拓展:有效范围为fs.Base ~ fs.Base+Limit

向下拓展:有效范围除了fs.Base ~ fs.Base+Limit

R位:若为1,表示该段可读

C位:一致位。若为1,则是一致代码段;若为0,则是非一致代码段


当S = 0时,即段描述符为系统段描述符时,Type域结构图如下:

Type域-系统段描述符

D\B位

情况1:对CS段的影响

D=1:采用32位寻址方式

D=0:采用16位寻址方式

情况2:对SS段的影响

D=1:隐式堆栈访问指令(如:PUSH POP CALL)使用32位堆栈指针寄存器ESP

D=0:隐式堆栈访问指令(如:PUSH POP CALL)使用16位堆栈指针寄存器SP

情况3:向下拓展的数据段

D=1:段上限为4GB

D=0:段上限为64KB

D_B位情况3

DPL

描述:

DPL存储在段描述符中,规定了访问所在段描述符所需要的特权级别是多少

DPL数值越大,访问所在段描述符所需要的权限越低

注意:在Windows中,DPL只会出现两种情况,要么全为0,要么全为1

例:

若AX指向的段描述符的DPL=0,但当前程序的CPL=3,那么这条指令是不会成功的!

段描述符与段寄存器结构的对应关系

Attribute:位于段描述符高四字节的第8-23位

Base:由三部分组成

第一部分:位于段描述符高四字节的第24-31位

第二部分:位于段描述符高四字节的第0-7位

第三部分:位于段描述符低四字节的第16-31位

Limit:由两部分组成

第一部分:位于段描述符高四字节的第16-19位

第二部分:位于段描述符低四字节的第0-15位

2)段选择子

描述:

段选择子是一个16位的段描述符,该描述符指向了定义该段的段描述符

段选择子结构图:

段选择子

字段说明:

RPL:请求特权级别

TI:TI=0 查GDT表;TI=1 查LDT表

Index:处理器将索引值乘以8在加上GDT或者LDT的基地址,就是要加载的段描述符

加载段描述符到段寄存器

除了MOV指令,还可以使用LES、LSS、LDS、LFS、LGS指令修改段寄存器

注意:不存在LCS指令,因为CS不可写

例:

char buffer[6]; __asm { les ecx,fword ptr ds:[buffer] //高2个字节给es,低四个字节给ecx }

实验:为buffer赋值,并成功执行以上代码

注意:RPL(在数值上)

段权限检查

1) CPU分级概念

CPU分级

平时我们称应用程序为3环,系统程序为0环,前面这句话只与CPU有关,与操作系统无关

思考:如何判断某个程序处于哪一环?

2) 进程特权级别

当前特权级(CPL)

描述:

段寄存器 CS 的后两位比特位称为当前特权级

注意:段选择子SS和CS的后两位比特位相同

如:

→ CS = 0x001B

→ 0x001B = 二进制:0000 0000 0001 1011

→ 二进制:11 = 十进制:3

→ 因此:当前进程处于3环

请求特权级(RPL)

描述:

RPL是段选择子结构中的一部分

RPL是针对段选择子而言的,每个段的选择子都有自己的RPL

RPL表示用什么权限去访问一个段

例:

MOV AX,0008 MOV DS,AX 与 MOV AX,000B MOV DS,AX 指向的是同一个段描述符,但RPL不同

3)数据段的权限检查

检查:CPL并且 RPL(数值上的比较)

例:

当CPL = 0时执行以下指令: MOV AX,000B // RPL=3,请求权限为3 MOV DS,AX // 假设ax指向的段描述符的DPL=0 上述指令虽然满足了CPLDPL,因此执行失败

注意:代码段和系统端描述符的检查方式不一样

思考:既然已经有CPL(当前特权级别)了,为什么还要有RPL(请求特权级别)

回答:我们本可以用“读写”的权限去打开一个文件,但为了避免出错,有些时候我们使用“只读”的权限去打开

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值