保护模式 2讲 -段 -段寄存器结构

2020-07-02 15:11:35 浏览数 (1)

保护模式 -段 -段寄存器结构

一丶保护模式学习

保护模式,主要学习的就是段 与 页 的关系. 学习段的时候先学习段寄存器

  • 何为段 根据Intel 手册第三卷所属. 分段和分页是操作系统提供的机制. 这种机制可以为每个 程序或者任务提供单独的代码. 数据 和栈. 也就是我们 常常所说的进程隔离 保证了多个任务何以运行在一个处理器之上,且不会互相影响. 这就是分段.
  • 何为页 分页机制 是实现了传统的请求 调用 页虚拟机内存系统. 简单来说就是内存是如何管理的. 分页机制同样可以实现任务隔离. 在保护模式下. 分段机制必须的 而分页是可选的.

所以先从段开始学.

二丶段寄存器

2.1 什么是段寄存器

先看一段汇编 代码如下

代码语言:javascript复制
mov eax,fs:[0]
mov eax,fs:[eax   0x30]

上面的代码学过 TEB PEB 结构的人应该知道.是在做什么. 而我么你这里所说不讲 PEB TEB

看汇编代码.我们操作了FS; FS就是段寄存器 我们这里操作的地址分别就是 FS:[0]fs:[0x30]

而其实展开来说. 我们真正的操作的地址是 FS.base 0 或者 FS.base 0x30 这样来操作的.

出了FS寄存器.还有 ES CS SS DS GS LDTR TR等段寄存器

我们上面所说的 段.base 其实是段寄存器的一个成员. 可以理解为段寄存器就是一个结构体

PS: 在inter手册中也称为段寄存器为段描述符

2.2 段寄存器结构

在我们x86平台下.我们知道一个寄存器是 4个字节. 32位. 可以表达一个32位的数据. 但是我们的段

却很少有人关注 其实我们的段 是有96位的.是一个结构体.

结构如下:

代码语言:javascript复制
struct Segment
{
  WORD Selecter;  //16位段选择子           可见部分.  使用OD 或者X64dbg看段寄存器只会显示16位的段选择子可见部分.
  WORD Attribute; //16位表示的段属性, 表示了当前段寄存器是可读的可写的还是可执行的
  DWORD Base;     //32位表示的基址,表示段从哪里开始
  DWORD limit;    //32位表示,表示的是基址的长度. base   limit 可以确定一个段的大小
}

2.3 段寄存器的读写

使用汇编可以对段寄存器进行读写.

代码语言:javascript复制
读取
mov ax,ss  段寄存器的可见部分只有16位.所以读出来之后只能放到16位寄存器中

写入
mov ss,ax  读寄存器只是读了可见部分的16位.而写入寄存器则是写入了96位

inter手册对段寄存器的操作寄存器指令有以下

  • 直接载入的指令
代码语言:javascript复制
mov pop LDS LES LSS LGS LFS  L是Load的意思
  • 其它隐含改变的指令 Call JMP RET Sysenter sysExit iret intn int0 int3

2.4 段寄存器的探测

2.4.1 段寄存器结构

在x86下.我们可以看如下寄存器表示图.

寄存器名称

段选择子(Select)

段属性(Attributes)

段基址(Base)

段长(Limit)

ES(附加扩展段)

0x0023

可读,可写

0x0000000

0xFFFFFFFF

CS(代码段)

0x001B

可读,可执行

0x00000000

0xFFFFFFFF

SS(堆栈段)

0x0023

可读,可写

0x00000000

0xFFFFFFFF

DS(数据段)

0x0023

可读,可写

0x00000000

0xFFFFFFFF

FS(分段机制)

0x003B

可读,可写

0x7FFDF000

0xFFF

GS

未使用

未使用

未使用

未使用

2.4.2 段寄存器中的 段属性探测

我们可以巧妙的利用汇编.来探测一下段属性 是否存在的. 依靠段寄存器结构所知. 出了CS段 不能写之外.其余段都是可以写的.

那么看一下内敛汇编代码.

  • CS代码段 段属性探测 int Var = 0; int main() { __asm { mov ax,cs mov ds,ax mov dword ptr ds:[Var],eax ;等价于 mov dword ptr cs:[Var],eax } } 将CS赋值到AX中. AX赋值给DS. 此时DS就代表CS了. 如果以把eax之给 CS.则会出现错误. 可以将代码放到VS2019中.直接编译生成. 运行之后会崩溃. 而下方的则不会崩溃. 说明权限确实是不可写.
  • 其它代码段段属性探测 int Var = 0; int main() { __asm { mov ax,ss mov ds,ax mov dword ptr ds:[Var],eax } }
2.4.3 段基地址探测

根据上面我们可以轻而一举的探测出 CS段 带有不可写的属性. 那么同样我们可以探测一下基地址.

在上图我们得知. 有基地址的只有FS. 看如下代码.

代码语言:javascript复制
int main()
{
    __asm
    {
        mov ax,fs
        mov gs,ax
        mov eax,gs:[0]
    }
}

看汇编我们可以看到. 为啥访问 gs:[0]不会出错那. 有基础的都应该知道. 0地址是不可读的. 其实我们说过. 我们对任何一个地址的操作,操作的都是它的 段.base 偏移的方式. 在实模式下.这个概念应该知道.到了保护模式下. 段base为0了. 所以偏移就是我们看到的虚拟地址.

上面的汇编是一样的. FS是有基地址的. 当其赋值给gs的时候. gs代表的就是fs. 所以用 gs去操作[0]地址是有效的. 等价于操作 fs:[0]

而FS:[0] 就是我们的TEB结构.

2.4.4 段长探测

在段地址探测中,我们学习到了 访问有效地址 都等价于 段.base 偏移地址 x86下.偏移地址就是我们所看到的虚拟地址. 也称为逻辑地址.

那么我们如何探测 段长? 根据段寄存器表格得知. FS的 limit 只有0xFFF大小. 我们可以写汇编代码验证一下

代码语言:javascript复制
int main()
{
    __asm
    {
        mov ax, fs
        mov gs, ax
        mov eax, gs: [0]  //fs.base   0 读取
        mov eax,gs:[0x1000]//fs.base   0x1000
        mov eax, dword ptr ds : [eax   0xFFF] ;
       // mov eax,gs:[0x1000] //fs.base   0x1000 读取
      
    }
}

可能是我们win764上测试的. 读取长度.越界读取.都会导致程序崩溃.

0 人点赞