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