存储段描述符表


存储段描述符表

GDT和GDTR

GDT

GDT(Global Descriptor Table)全局描述符表,一个处理器仅有一个GDT,主要用于内存转换,所有程序的内存访问都需要用到GDT中的有关内存区域即x86内存分段的信息。描述符每一项都为8个字节,长度可变,最长支持213个描述符。

GDTR

GDTR(Global Descriptor Table Register)全局描述符表寄存器,用来获取全局描述符表地址和大小。一个GDTR有48位,结构如下:

#pragma pack(2) //设定两个字节对齐

struct GDTR{
    DWORD Base; //0x8003f000
    WORD Limit; //0x3ff
}

int main(){
    GDTR gdtr;
    _asm{
        SGDT gdtr; //读取GDTR表信息
    }
}

执行mov ds, ax,在给段寄存器赋值时,需要查询GDT表,将ax低三位清零, 加上GDTR.Base,取出8个字节给DS其他位。

windbg获取GDTR:dq gdtr //8字节对齐

存储段描述符

由于历史原因,80286时期,Seg.Selector 为16位 ,Seg.Attributes也是16位,Seg.Base为24位,Seg.Limit为16位。

而为了向下兼容,80386的Seg.Selector 为16位 Seg.Attributes也是16位,Seg.Base为32位,Seg.Limit为32位。

存储段描述符格式

Attributes中各项介绍

汇编杨季文p367

G

G(Granularity)段界限粒度位,G = 0 表示段界限以字节为单位,G = 1 表示段界限以4K字节为单位

在limit中,当G位=0时,在limit最高12位填0 ,当G位=1时,在limit最高12位填1,扩展成32位

P

P(Present)存在位,P = 1时,描述符对地址转换有效,反之则无效。为了让GDTR这8字节有效,P必须为1

DPL

DPL(Descriptor Privilege Level)描述符特权级位,用于特权检查,决定该段是否能访问。

CS的低两位是当前权限 00 01 10 11

在保护模式下 3环CS的低两位和SS的低两位一定相同,若CS低两位被修改,SS低两位也一定会被修改

FS ES DS GS 权限上一定大于等于CS的低两位
在数值上一定要小于等于CS的低两位

TYPE

第0位:A(Descriptor is Accessed)描述符是否被访问,0 = 未访问, 1 = 已访问

第1位:E = 0, 第一位用W表示,是否可写,0 = 不可写,1 = 可写

​ E = 1, 第一位用R表示,是否可读,0 = 不可读,1 = 可读

第2位:E = 0,第二位用ED表示,描述数据段的扩展方向,0 = 数据段向高扩展,段内偏移小于等于段界限,1 = 数据段向低扩展,段内偏移大于段界限

​ E = 1,第二位用C表示,所描述的代码段是否位一致代码段,0 = 普通代码段,1 = 一致代码段

ED位范围

例:fs –>

ED = 0:mov eax,fs:[3ff]—>有效

ED = 1:mov eax,fs:[0x1000]–>有效

第3位:E,表示是代码段还是数据段,0 = 数据段(包括堆栈段)描述符,1 = 代码段描述符

存储段描述符类型

D

D在描述可执行段、向低扩展数据段或SS寄存器寻址段(堆栈段)意义均不相同。

在描述可执行段的描述符中,D为指令使用的地址及操作数默认的大小(寻址大小),仅对CS和SS有效。0 = 默认情况下使用16位地址、16位或8位操作数,即16位代码段; 1 = 默认情况下指令使用32位地址及32位或8位操作数,即32位代码段。

cs.d = 0 16位寻址:[bx+si] [bx+di] [bp+si] [bp+di] [si] [di] [bp] [bx]

cs.d = 1 32位寻址:[eax+edx]

ss.d = 1 push eax ==> lea esp, [esp - 4]

​ mov [esp], eax

ss.d = 0 push eax ==> lea sp, [sp - 4]

​ mov [sp], eax

AVL

软件可利用位,保留参数

DT

DT = 1 表示为存储段描述符

DT = 0 表示系统段描述符和门描述符

通用寄存器赋值给段寄存器过程

寄存器赋值 16位赋给96位 –> 寄存器低三位清零,加上GDTR.Base,取出8个字节 –>

若GDT中取出的是00cff300 0000ffff,则:

base高 attributes base低 limit低
00 cff3 000000 ffff

seg.selector –> ax
seg.attributes –> 0xcff3
seg.base –> 0x0
seg.limit –> 如下

g d 0 AVL limit高4 P DPL DT TYPE
1 1 0 0 1111 1 11 1 0011

当G为1时填1
则limit为0xffffffff

计算代码

#include <stdio.h>

typedef struct {
    unsigned int TYPE: 4;
    unsigned int DT: 1;
    unsigned int DPL: 2;
    unsigned int P: 1;
    unsigned int Limit: 4;
    unsigned int AVL: 1;
    unsigned int O: 1;
    unsigned int D: 1;
    unsigned int G: 1;
} Attributes, *PAttributes;

int main(){
    unsigned short data = 0xcff3;
    PAttributes attr = (PAttributes)&data;
    printf(
        "P: %d\nDPL: %d\nDT: %d\nTYPE: %d\n", 
        attr->P, 
        attr->DPL, 
        attr->DT,
        attr->TYPE
    );
    printf(
        "G: 0x%x\nD: 0x%x\nO: 0x%x\nAVL: 0x%x\nLimit: 0x%x\n", 
        attr->G,
        attr->D, 
        attr->O,
        attr->AVL,
        attr->Limit
    );
    return 0;
}

同时改变CS和EIP指令

jmp far(ea)、call far(9a)、retf(ca\cb)、int、ireted(cf)

其中iretd 按顺序同时改变eflag、cs、eip

retf 先pop栈到EIP,再pop到CS

有函数

_declspec(naked) int fun()
{
    _asm
    {
        push ebp
        mov ebp,esp
        sub esp,0x40
    }

    _asm
    {
        mov ax,cs
    }

    _asm
    {
        mov esp,ebp
        pop ebp
        retf
    }
}

call far调用函数:

lea eax,fun
push 0x18
push eax
call fword ptr [esp] # fword 6字节
mov cx,cs
add esp,0x8

jmp far调用函数:

方法1:

lea eax,fun
lea ecx,lab

push 0x1b
push ecx
push 0x1b
push eax
lea esp,[esp+0x8]
jmp fword ptr [esp-8]

lab:
add esp,0x8

方法2:

lea eax,fun
lea ecx,lab

push 0x1b
push eax
push 0x1b
push ecx
jmp fword ptr [esp+8]

lab:
add esp,0x8

文章作者: Kevin。
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Kevin。 !
评论
 上一篇
利用门描述符从3环进入0环 利用门描述符从3环进入0环
利用门描述符从3环进入0环门描述符格式在开始前先了解一下门描述符的作用 门描述符不描述某种内存段,而是描述控制转移的入口点,简单来说就是中介,通过这道门,可实现任务内特权级的变换和任务间的切换,获取最后的访问地址。 Attributesm
2022-04-12
下一篇 
段寄存器测试 段寄存器测试
寄存器测试内存地址转换过程虚拟地址 –>段选择子+偏移地址–>分段管理机制–>线性地址–>分页管理机制–>物理地址 不同段寄存器对应不同作用 ES —> 对串进行操作 [ESI] [EDI] STOSD
2022-04-07
  目录