CPU通过段和页的机制来限制内存访问,共8个分别为ES CS SS DS FS GS LDTR TR
段寄存器结构
当我们用汇编读写某一个地址时:
mov dword ptr ds:[0x123456],eax
我们真正读写的地址是:ds.base + 0x123456
2、段寄存器成员
XP上一般如下
我的Win10上
注意:GS 一走调试就必须进0环,进0环后被内核清空
3、段描述符与段选择子
1)先说两张表
GDT(全局描述符表)和LDT(局部描述符表)
当我们执行类似MOV DS,AX指令时,CPU会查表,根据AX的值来决定查找GDT还是LDT、查找表的什么位置、查多少数据。
2)gdtr寄存器:存放GDT的位置和GDT的大小
3)查看GDT
查看GDT的位置
查看GDT的大小
查看GDT数据
4)段描述符
GDT表里存放的数据就是段描述符,段描述符有8个字节64位,结构如下
用dq指令查看
对应关系
P=0段描述符无效
P=1段描述符有效
S=1是代码或数据段描述符
S=0是系统段描述符
当S=1时根据Type区分是代码段还是数据段,如下表
注意:expand-up的意思是段的有效范围Base~Base+Limit
expand-down段的有效范围是除了(Base~Base+Limit)以外的区域;C位为1 时是一致代码段(conforming)也叫共享段,C位为0时是非一致代码段(unconforming)
一个经验区分方法:
S=0是系统段描述符,系统描述符有如下类型
段描述符与段寄存器的对应关系
WORD Selector 16位 对应段选择子
WORD Attribute 对应段描述符高4字节的 23:8位
DWORD BASE 32位 对应段描述符Base31:24+Base23:16+Base Address31:16
DWORD Limit 32位 对应段描述符 高4字节19:16+Segment Limit15:00
如果G=0,则在余下高位补0,则最高为000FFFFF;如果G=1,则在余下高位补1,则最高为FFFFFFFF
5)段选择子Selector
16位的段描述符,该描述符指向了定义该段的段描述符,结构如下
6)举例
MOV DS,AX 其中AX的值位0x0023
0x0023二进制为 0000 0000 0010 0011
对应的段选择子
RPL:11
TI:0 查GDT
Index:0000 0000 0010 0 查GDT的第4个
查的结果为:00cff300`0000ffff
按段描述符进行拆分
0x00cff300
Base31:24 0000 0000
G:1
D/B:1
0:0
AVL:0
Seg-Limit19:16 1111
P:1
DPL:11
S:1
Type:0011
Base23:16 0000 0000
0x0000ffff
Baseaddress:0x0000
SegmentLimit:0xFFFF
可以得到以下信息
第一,P=1有效
第二,S=1,说明是代码段或数据段
第三,Type为 0011说明是数据段、expand-up、Read/Write、Accessed
第四,DPL=RPL 如果CPL<=DPL(CPL后面介绍)则可以将GDT中的这个段描述符加载到段寄存器DS中
7)除了MOV指令,我们还可以使用LES、LSS、LDS、LFS、LGS指令修改寄存器.
CS不能通过上述的指令进行修改,CS为代码段,CS的改变会导致EIP的改变,要改CS,必须要保证CS与EIP一起改。
4、段描述符的D/B位
情况一:对CS段的影响
D = 1 采用32位寻址方式
D = 0 采用16位寻址方式
前缀67 改变寻址方式
情况二:对SS段的影响
D = 1 隐式堆栈访问指令(如:PUSH POP CALL) 使用32位堆栈指针寄存器ESP
D = 0 隐式堆栈访问指令(如:PUSH POP CALL) 使用16位堆栈指针寄存器SP
情况三:向下拓展的数据段
D = 1 段上限为4GB
D = 0 段上限为64KB
4、段寄存器CS和SS的选择子Selector中的低两位RPL,又叫做CPL(Current Privilege Level)即当前进程的特权级别。这两个的Selector的低两位相同。
用OD打开一个程序
低两位为11,说明当前程序为ring3的。
5、DPL(Descriptor Privilege Level)描述符特权级别
DPL存储在段寄存器中,规定了访问该段所需要的特权级别,描述的是访问这个段需要什么权限。
例如
Mov DS,AX
如果AX指向的段DPL=0,当前程序的CPL=3,则这条指令会执行失败。
6、RPL(Request Privilege Level)请求特权级别
RPL是针对段选择子而言的,每个段的选择子都有自己的RPL,描述的是用什么权限去访问一个段。
Mov ax,0x0008
Mov ds,ax
和下面的指令
Mov ax,0x000B
Mov ds,ax
指向的是同一个段描述符,但RPL是不一样的。
7、数据段的权限检查
假设当前的程序处于ring0,也就是CPL=0
Mov ax,0x000B //RPL=3
Mov DS,ax //ax指向的段描述符中的DPL=0
只有CPL<=DPL并且RPL<=DPL时mov DS,ax 才会执行成功。上述不满足RPL<=DPL所以执行失败。
8、代码间的跳转(段间跳转)
1)指令介绍
只改变EIP的指令
JMP/CALL/JCC/RET
同时修改CS和EIP的指令
JMP FAR/ CALL FAR / RETF / INT / IRETED
2)JMP FAR
指令格式 JMP 段选择子:要跳转的地址
例如:JMP 0x20:0x004183D7
0x20:段选择子
0x004183D7:要跳转的地址
执行过程
第一步,拆分段选择子0x20
对应的二进制 0000 0000 0010 0000
得到RPL=0 TI=0 Index=4
第二步,查表得到段描述符
TI=0 查询GDT表
Index = 4 对应的段描述符,如果找到的段描述符是代码段、调用门、TSS任务段、任务门这四种,是可以跳转的。注意:后面三种都是系统段描述符。
第三步,权限检查
如果是非一致代码段,要求CPL==DPL 并且 RPL<=DPL
如果是一致代码段,要求 CPL>=DPL
第四步,加载段描述符,通过上面的权限检查后,CPU会将段描述符加载到CS段寄存器中。
第五步,代码执行,CPU将CS.Base+Offset的值写入EIP,然后执行CS:EIP处的代码。
段间跳转结束。
9、关于一致代码段和非一致代码段
1)一致代码段,也叫共享段,特权级高的程序不允许访问特权级低的数据(核心态不能访问用户态的程序);特权级低的程序可以访问特权级高的数据,但特权级不会改变。
2)非一致代码段,也叫普通代码段,只允许同级访问。
3)直接对代码段进行JMP或者CALL的操作,无论目标是一致代码段还是非一致代码段,CPL都不会发生改变;如果要提升CPL权限,只能通过调用门。
10、一个例子
1)找一个非一致代码段描述符
00cffb00`0000ffff
807d4c68修改为上述值
2)OD中使用jmp far 注意EIP和CS前后变化
可以发现EIP和CS同时发生了变化。
3)选择下面的段描述符 00cf9b00`0000ffff
段选择子应该为 0x00B ,段选择子的RPL=3
上述段描述符的DPL=0,是非一致代码段,跳转应该会失败,如下
4)挑一个或者构造一个一致代码段00cf9d00`0000ffff,如下图
DPL=0,段选择子构造为0x004B RPL=3,Index=9 ,程序的CPL=3,由于是一致代码段可以执行成功,如下
------------------------------------------------------------------------未完~待续~-------------------------------------------------------------------------