1.段寄存器结构
段寄存器一共96位,但是可见部分只有16位
Struct SegMent
{
WORD Selector; //16位段选择子
WORD Attributes; //16位属性
DWORD Base; //32位基址
DWORD Limit; //32位段限长
}
其中红色部分就是段选择子(就是做段权限检测的)Selector比如:2B拆分如下 0010 1011 -》0010 1 0 11
00101查找GDT表里面的位置索引5
0 查GDT表,如果为1则查LDT表(LDT能有多张表)
11 所处的请求级别RPL ,3是三环 0 是0环
HEX=转成16进制
那么索引公式是:GDT表首地址(通过 r gdtr获取)+HEX[索引值(5)*8(字节宽度)]
2.段寄存器的读写:
读段寄存器:
比如:MOV AX,ES 只能读16位的可见部分
读写LDTR 的指令为:SLDT/LLDT
读写TR的指令为:STR/LTR
写段寄存器:
比如:MOV DS,AX 写的时候是写96位
除了CS 都是可读可写属性。
3.既然只有16位可见位那怎么判读另外的80位是存在的呢?接下来我们进行探查
一、探测Attribute
attr =0cf3 第一位没有用使用所以补0
#include "stdafx.h"
//两个可读可写段寄存器互换没影响
int var = 0;
int main()
{
__asm
{
mov ax,ss //将SS的值读出来
mov ds,ax //将AX写入,此时ds已经是SS了
mov dword ptr ds:[var],eax //实际上已经是ss:[var]
return 0;
}
//以下代码报错证明Attribute属性起作用了!
#include "stdafx.h"
int var = 0;
int main()
{
__asm
{
mov ax,cs //将CS的值读出来 ,但是CS是可读可执行,并不可写
mov ds,ax //将AX写入,此时就会报错
mov dword ptr ds:[var],eax //两个不同属性的并不能转换
return 0;
}
2.探测BASE:
1 #include "stdafx.h"
2
3 int var = 0;
4 int main()
5 {
6 __asm
7 {
8 mov ax,fs
9 mov gs,ax //此处如果换成ds则会出现编译不过的问题
10 mov eax,gs:[0] //对0地址进行读的操作(如果是0地址不能读也不能写,此时gs已经是fs)
11 //相当于
12 //mov edx,dword ptr ds:[0x7FFDF000]
13 mov dword ptr ds:[var],eax
14 }
15
16 }
3.探测Limit
limt:(fffff+1)*4k -1 =ffffffff
4k = 4096
fffff是从0开始所以应该+1
#include "stdafx.h"
//报错,fs访问越界,证明Limit真实存在
int var = 0;
int main()
{
__asm
{
mov ax,fs
mov gs,ax //此处如果换成ds则会出现编译不过的问题
mov eax,gs:[1000] //fs的limit是FFF 但是读0x1000则越界了
//访问的地址相当于下面的 但是DS的Limit是0xFFFFFFFF
//mov eax,dword ptr ds:[0x7FFDF000+0x1000]
mov dword ptr ds:[var],eax
}
}
4.段寄存器有96位,那剩下的80位从哪来的?
数据是从GDT表里面来的,在线程和进程没有切换的情况下:读取后会把它弄进一个缓冲区,只会读取GDT表一次
1.GDT(全局描述符表) LDT(局部描述符表)
当我们执行类似MOV DS,AX 指令时,CPU会在系统中查表,根据AX的值来决定查找GDT表还是LDT表,查找表的什么位置
查出多少数据;
在WinDbg中 使用 r gdtr 命令来查找gdtr寄存器里的地址,r 则是查看寄存器的意思;
使用 r gdtl 指令则是查看这张表有多大!
使用dd 查看表内容
1: kd> r gdtr gdtr=fffff8800470b4c0 1: kd> r gdtl gdtl=007f 1: kd> dd fffff8800470b4c0 fffff880`0470b4c0 00000000 00000000 00000000 00000000 fffff880`0470b4d0 00000000 00209b00 0000ffff 00cf9300 fffff880`0470b4e0 0000ffff 00cffb00 0000ffff 00cff300 fffff880`0470b4f0 00000000 0020fb00 00000000 00000000 fffff880`0470b500 4ec00067 04008b70 fffff880 00000000 fffff880`0470b510 e0007c00 ff40f3f9 00000000 00000000 fffff880`0470b520 0000ffff 00cf9a00 00000000 00000000 fffff880`0470b530 00000000 00000000 00000000 00000000
|
|
|
|
GDT表内存储的元素称之为:“段描述符”
每个段描述符占8个字节
段描述符结构(必背):
看一个段首先看P为是否可用,然后看S位 -TYPE 看看描述的是个什么东西,其次看D/B ,再看G位
dq指令 则是查看8字节;
如果想要查看更多 则可以使用 dq 地址 L数量
nt!RtlpBreakWithStatusInstruction: fffff800`03ec7fb0 cc int 3 1: kd> r gdtr gdtr=fffff8800470b4c0 1: kd> r gdtl gdtl=007f 1: kd> dd fffff8800470b4c0 fffff880`0470b4c0 00000000 00000000 00000000 00000000 fffff880`0470b4d0 00000000 00209b00 0000ffff 00cf9300 fffff880`0470b4e0 0000ffff 00cffb00 0000ffff 00cff300 fffff880`0470b4f0 00000000 0020fb00 00000000 00000000 fffff880`0470b500 4ec00067 04008b70 fffff880 00000000 fffff880`0470b510 e0007c00 ff40f3f9 00000000 00000000 fffff880`0470b520 0000ffff 00cf9a00 00000000 00000000 fffff880`0470b530 00000000 00000000 00000000 00000000 1: kd> dq fffff8800470b4c0 fffff880`0470b4c0 00000000`00000000 00000000`00000000 fffff880`0470b4d0 00209b00`00000000 00cf9300`0000ffff fffff880`0470b4e0 00cffb00`0000ffff 00cff300`0000ffff fffff880`0470b4f0 0020fb00`00000000 00000000`00000000 fffff880`0470b500 04008b70`4ec00067 00000000`fffff880 fffff880`0470b510 ff40f3f9`e0007c00 00000000`00000000 fffff880`0470b520 00cf9a00`0000ffff 00000000`00000000 fffff880`0470b530 00000000`00000000 00000000`00000000 1: kd> dq fffff8800470b4c0 L50 fffff880`0470b4c0 00000000`00000000 00000000`00000000 fffff880`0470b4d0 00209b00`00000000 00cf9300`0000ffff fffff880`0470b4e0 00cffb00`0000ffff 00cff300`0000ffff fffff880`0470b4f0 0020fb00`00000000 00000000`00000000 fffff880`0470b500 04008b70`4ec00067 00000000`fffff880 fffff880`0470b510 ff40f3f9`e0007c00 00000000`00000000 fffff880`0470b520 00cf9a00`0000ffff 00000000`00000000 fffff880`0470b530 00000000`00000000 00000000`00000000 fffff880`0470b540 03ec8e00`0010cf00 00000000`fffff800 fffff880`0470b550 03ec8e00`0010d000 00000000`fffff800 fffff880`0470b560 03ec8e03`0010d1c0 00000000`fffff800 fffff880`0470b570 03ecee00`0010d540 00000000`fffff800 fffff880`0470b580 03ecee00`0010d640 00000000`fffff800 fffff880`0470b590 03ec8e00`0010d740 00000000`fffff800 fffff880`0470b5a0 03ec8e00`0010d840 00000000`fffff800 fffff880`0470b5b0 03ec8e00`0010da80 00000000`fffff800 fffff880`0470b5c0 03ec8e01`0010db40 00000000`fffff800 fffff880`0470b5d0 03ec8e00`0010dc00 00000000`fffff800 fffff880`0470b5e0 03ec8e00`0010dcc0 00000000`fffff800 fffff880`0470b5f0 03ec8e00`0010dd80 00000000`fffff800 fffff880`0470b600 03ec8e00`0010dec0 00000000`fffff800 fffff880`0470b610 03ec8e00`0010e000 00000000`fffff800 fffff880`0470b620 03ec8e00`0010e140 00000000`fffff800 fffff880`0470b630 04008e00`001090f0 00000000`fffff800 fffff880`0470b640 03ec8e00`0010e500 00000000`fffff800 fffff880`0470b650 03ec8e00`0010e680 00000000`fffff800 fffff880`0470b660 03ec8e02`0010e780 00000000`fffff800 fffff880`0470b670 03ec8e00`0010eb00 00000000`fffff800 fffff880`0470b680 04008e00`00109140 00000000`fffff800 fffff880`0470b690 04008e00`00109150 00000000`fffff800 fffff880`0470b6a0 04008e00`00109160 00000000`fffff800 fffff880`0470b6b0 04008e00`00109170 00000000`fffff800 fffff880`0470b6c0 04008e00`00109180 00000000`fffff800 fffff880`0470b6d0 04008e00`00109190 00000000`fffff800 fffff880`0470b6e0 04008e00`001091a0 00000000`fffff800 fffff880`0470b6f0 04008e00`001091b0 00000000`fffff800 fffff880`0470b700 04008e00`001091c0 00000000`fffff800 fffff880`0470b710 04008e00`001091d0 00000000`fffff800 fffff880`0470b720 04008e00`001091e0 00000000`fffff800 fffff880`0470b730 03f18e00`0010af10 00000000`fffff800
|
|
|
|
段选择子:
段选择子是一个16位的段描述符,该描述符指向了定义该段的段描述符。
如: 1B (001B)00一般省略 拆分后为 0000 0000 0001 1011
0000 0000 0001 1 (这里拼接起来为3则查找GDT表第3相) 0(TI)11(RPL)
加载段描述符至段寄存器:
这里的RPL<=DPL不准确,数值上应该是RPL>=DPL
2019-07-21
三环读取GDT表首地址
unsigned char buf[6]={0};
_asm
{
sgdt buf;
}
printf("%x,%x",*((unsingned int*)&(buf[2])),
*((unsingned short*)&(buf[0])));
)
每次执行代码都要经过CS段!CPL