不妨先来做个实验。打开 OD,双击第一行,把代码改成 mov ax, 0x20; mov ds, ax
,然后按两下 F8 键(单步),发现很正常。如果你把代码改成 mov ax, 0x10; mov ds, ax
,按两下 F8 后,代码跳转到了一个地址以 7 开头的地方去了。
图1 这两行可以正常执行
图2 第二行不能正常执行
图3 图2中的代码执行异常会跳到这里
为了搞清这个原因,我们需要看段描述符 DPL。
段描述符的 DPL
分析段选择子 0x10 和 0x20,这两个描述符分别是 00cf9300-0000ffff
和00cff300-0000ffff
,可以发现这两个描述符唯一的不同就在于 DPL 不同,第一个描述符的 DPL = 0,而第二个描述符的 DPL = 3.
原因就在这里。
DPL = 0 的数据段,只允许当前特权级为 0 的程序访问,而 DPL= 3 的数据段,允许当前特权级为 0,1,2,3的程序访问。在 OD 中,当前特权级为 3.
实验
原始数据
|--地址--|------------16进制值----------------|
8003f000 00000000`00000000 00cf9b00`0000ffff
8003f010 00cf9300`0000ffff 00cffb00`0000ffff
8003f020 00cff300`0000ffff 80008b04`200020ab
8003f030 ffc093df`f0000001 0040f300`00000fff
8003f040 0000f200`0400ffff 00000000`00000000
8003f050 80008954`a1000068 80008954`a1680068
8003f060 00009302`2f30ffff 0000920b`80003fff
8003f070 ff0092ff`700003ff 80009a40`0000ffff
8003f080 80009240`0000ffff 00009200`00000000
8003f090 00000000`00000000 00000000`00000000
8003f0a0 8200891b`22e00068 00000000`00000000
8003f0b0 00000000`00000000 00000000`00000000
8003f0c0 00000000`00000000 00000000`00000000
8003f0d0 00000000`00000000 00000000`00000000
8003f0e0 f8009f71`a000ffff 00009200`0000ffff
8003f0f0 8000984f`b2e403b7 00009200`0000ffff
8003f100 f8409337`9400ffff f8409337`9400ffff
8003f110 f8409337`9400ffff 00000000`8003f120
在3环能加载的数据段有哪些?
分析:3环只能加载DPL为3的数据段。
数据段有:
00cf9300`0000ffff DPL = 0
00cff300`0000ffff DPL = 3 --> 可以加载
ffc093df`f0000001 DPL = 0
0040f300`00000fff DPL = 3 --> 可以加载
0000f200`0400ffff DPL = 3 --> 可以加载
00009302`2f30ffff DPL = 0
0000920b`80003fff DPL = 0
ff0092ff`700003ff DPL = 0
80009240`0000ffff DPL = 0
00009200`00000000 DPL = 0
00009200`0000ffff DPL = 0
f8409337`9400ffff DPL = 0
f8409337`9400ffff DPL = 0
f8409337`9400ffff DPL = 0
在0环能加载的数据段有哪些?
分析:0环可以加载DPL=0,1,2,3 的数据段,以下数据段均可以加载
数据段有:
00cf9300`0000ffff DPL = 0
00cff300`0000ffff DPL = 3
ffc093df`f0000001 DPL = 0
0040f300`00000fff DPL = 3
0000f200`0400ffff DPL = 3
00009302`2f30ffff DPL = 0
0000920b`80003fff DPL = 0
ff0092ff`700003ff DPL = 0
80009240`0000ffff DPL = 0
00009200`00000000 DPL = 0
00009200`0000ffff DPL = 0
f8409337`9400ffff DPL = 0
f8409337`9400ffff DPL = 0
f8409337`9400ffff DPL = 0
详细描述这下面代码的执行过程
mov ax,0x23
mov ds,ax
1. 0x23 = 00000000 00100011 --> Index = 4, TI = 0, RPL = 3
对应的描述符是:00cff300`0000ffff --> DPL = 3
2. 检查当前 RPL <= DPL and CPL <= DPL ?
3. 若 2 成立,把 0x23 填充到 ds, 把对应的段描述符加载到段寄存器 ds 的隐式的 80 位中。
总结
数据段权限检查,本质上就是检查能不能把段选择子代入到段寄存器。如果代入成功,表明权限检查通过。如果代入不成功,说明权限不够(CPL在数值上太大了)。
大家注意没有,本篇的标题是——数据段权限检查。为什么不是段权限检查?
因为数据段权限检查简单。这确实是一个无懈可击的理由:)
相比于数据段的权限检查,代码段权限检查要更加严格。后文会给出详细的答案。