存储段描述符表
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 = 一致代码段
例: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 ffffseg.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