寄存器测试
内存地址转换过程
虚拟地址 –>段选择子+偏移地址–>分段管理机制–>线性地址–>分页管理机制–>物理地址
不同段寄存器对应不同作用
ES —> 对串进行操作 [ESI] [EDI] STOSD MOVSD
CS —> EIP EBP
DS —> 通用
FS —> 异常
GS —> 保留
系统段 —> LDTR TR
段寄存器有96位,但在3环仅可使用16位选择子
struct Segment{
WORD selector;//3环用户态只能看到选择子
WORD attributes;//系统
DWORD base;//系统
DWORD limit;//系统
}
获取不同寄段存器的选择子
mov ax, es #0x23
mov cx, cs #0x1b
mov dx, ss #0x23
mov ax, ds #0x23
mov cs, fs #0x3b
mov dx, gs #0x00
ldtr #0x0
tr #0x28
sldt ax #读取ldtr
lldt #写ldtr,只有0环可操作
str cx #读取tr
ltr #写tr,只有0环可操作
修改段寄存器
int var;
int main(){
_asm{
mov ax, ss;
mov ds, ax;
mov var, eax;
}
}
在32位系统中,由于ss和ds的值相同,所以可以运行,若将ss改成cs,则会发生0xc0000005内存冲突
int var;
int main(){
_asm{
mov ax, cs;
mov ds, ax;
mov var, eax; //此步内存冲突
}
}
因为ds对应数据段,可读可写,而cs对应EIP,EIP对应代码段,代码段可读不可写,所以eax无法给var赋值
如下代码,当段寄存器不为0,偏移为0时,可以访问该地址。若将fs改成ds(ds一般为0),gs:[0] = 0x0,访问报错
int var;
int main(){
_asm{
mov ax, fs;
mov gs, ax;
mov eax, gs:[0];
mov var, eax;
}
}
fs的基址为0x7ffdf000,界限为0xfff,超过0xfff会报错
int var;
int main(){
_asm{
mov ax, fs;
mov gs, ax;
mov eax, gs:[0xffd]; //ffd ffe fff 1000
//eax有4个字节,而fs的界限为0xfff,0xffd+4=0x1000超出了0xfff界限,所以报错
mov var, eax;
}
}
struct GDTR{ //一个CPU只有一份
WORD LIMIT;
DOWRD BASE;
}
通用寄存器给段寄存器赋值16位扩展96位
段寄存器赋值规则
mov ax, 0x2b
mov ds, ax
以上代码为例
1、判断ax是否在GDTR限制中
0 < (ax >> 3) < GDTR.limit
2、TI
TI = 0时,表示选择GDT,TI = 1 选择LDT,由于Windows没有LDT,所以TI一定为0
3、GDT中判断
RPL 选择子的权限
ax & 0b11
CPL cs的权限
cs & 0b11
DPL 描述符的权限
描述符 & 0x60000000000
判断各个寄存器和描述符权限
max(CPL, RPL) <= DPL
在保护模式下 3环CS的低两位和SS的低两位一定相同
FS ES DS GS 权限上一定大于等于CS的低两位
在数值上一定要小于等于CS的低两位
判断P
P = 1时,描述符对地址转换有效,反之则无效。为了让GDTR这8字节有效,P必须为1
判断DT
DT = 1 表示为存储段描述符
判断TYPE
判断type是否符合当前段寄存器的属性
Type.c==0,非一致代码段,则要求CPL==DPL
;一致代码段,则要求CPL>=DPL
s==1 && Type.11 == 1
则代表请求代码段;
调用门,type == 1100B(12)
中断门,type == 1110B(14),IDT表中的中断处理函数就是这种门描述符
陷阱门,type == 1111B(15),IDT表中的陷阱处理函数就是这种门描述符
任务门,type == 0101B(5),用于任务切换
windbg> eq 地址 值 //修改内存