文章目錄
0. 發展過程
- 8086-實模式,1M內存,65K段長;
- 80286-保護模式;
- 80386-第一款32位cpu。
保護模式
段寄存器不再存儲段基址,而是存儲段選擇子,不再需要段寄存器左移加偏移。真正的段基址存在描述符高速緩存中。
但80286作爲16位cpu,段長度仍然是64K.
80386
1985 intel發佈。80386及以後的cpu都是兼容實模式的(包括現在的i9)。加電時,運行在實模式,一番設置之後進入保護模式。
80386後的32位cpu成爲x86體系:
- 兼容16位實模式
- os處於保護模式
- 虛擬8086模式,可模擬多個8086執行多任務
我們需要了解x86體系的分段機制和分頁機制。
1. 段寄存器
CS/DS/ES/SS + 32位FS/GS.
分段機制涉及特權等級(PL)劃分。
每個段寄存器一共96位,分爲兩部分:
- 16位可見部分,又稱爲選擇子(selector)
- 80位不可見部分,稱爲描述符高速緩存器:
32位基址+32位段長度+16位屬性
。無法通過任何指令來操作它。
可見的選擇子用來找到GDT的一個描述符,用這個描述符填充並不可見部分。
1.0 選擇子
16位由低到高:
- 2位RPL(Request Priviledge Level);
- 1位TI
- 13位描述符表索引
| 13位index | 1位TI | 2位RPL |
段寄存器80位不可見的描述符高速緩存器的值,來自於描述符表,這個表由CPU維護。選擇子的高13位就是這個表的索引。
段寄存器賦值時,會從描述符表的一個描述符讀取數據,賦給80位不可見部分。
TI:
- 0,則查找全局描述符表(GDT);
- 1,則查找局部描述符表(LDT)。
windows沒有LDT,所以TI始終爲0,
GDT是由GDTR找到的。
RPL,Request Priviledge Level. 在CS中,RPL就變成了CPL(當前特權等級),
2. 段描述符
實模式任意內存地址都能執行代碼,可以被讀寫。這非常不安全。CPU爲了提供限制/禁止的手段,提出了保護模式。
保護模式實際上就是保護了內存,使得內存中能夠被執行代碼,能夠被讀寫的地址可以被人爲得控制。在保護模式中,系統和用戶進程是被隔離開的。用戶進程無法修改系統的內存,也無法執行系統的代碼.這些也是通過保護模式達成的功能。
保護模式所實現的功能,很大程度上依賴分段機制。
在實模式下,分段機制很簡單,沒有屬性和權限的概念,只是規定了要使用 段基地址x16+段內偏 的尋址方式。
在保護模式下,它兼容實模式下的尋址方式(段基地址x16+段內偏移),並且在這基礎之上給一個段增加了段基地址,段的長度,段的屬性這三個屬性以實現保護模式下的部分功能。
在保護模式下,描述一段的基地址在哪裏、段有多長、段有何屬性的結構被稱之爲段描述符.其結構如下所示:
typedef struct Descriptor{
unsigned int base ; // 段基址
unsigned int limit; // 段限長
unsigned short attribute; // 段屬性
}
在保護模式下,增加了很多機制,使得段產生了不少種類:
- 數據段
- 代碼段
- 系統段
每個段描述既能夠描述出一段內存從哪開始,到哪結束,還能描述這個段是什麼類型(代碼段,數據段,系統段),當然,也能夠描述這個段是否可讀,是否可寫,是否可執行,甚至還能描述這個段的權限是什麼,在什麼權限下才能使用這一段內存。
段描述符是GDT和LDT的數據結構項,8個字節,3個關鍵字段:
- 段基地址
- 段限長
- 段屬性
需要重點關注的:
- P位,爲1則改描述符有效;
- S:描述符類型,應用程序由數據段、代碼段,cpu有系統段和門描述符,
- Type:段的屬性。type含義由S位決定
- 基地址:3部分,8+8+16;
- 段限長:2部分,16+4(32-12),即1M;
並非所有描述符都描述一個段,門描述符就存儲了指向過程入口點的指針,S和Type表明了描述符類型。
其它:
- G位:Granularity,段限長粒度/單位,爲1則單位是4KB,0則是字節;
- L位:保留給64位;
- AVL:os使用;
- DPL:描述本段內存所需權限。
- D/B位:操作大小,0則是16位,1是32位。
D/B位的會作用到代碼段( CS 段寄存器),棧段( SS 段寄存器),數據段( DS , ES 段寄存器),當使用這些段寄存器時,將會受到不同的影響:
- 尋址模式
- esp使用
2.0 S+Type
S爲1,則type描述代碼段或數據段。S爲0,則是系統段。
type最高位,位於段的0xB位,後面就簡稱Type.11。
數據段描述符
S==1 && Type.11==0
則該段是數據段。
S | Type |
| 0| E| W| A|
E是擴展方向:
- 0,向下擴展,也叫向外擴展,通常是棧段;
- 1,向上擴展,也叫向內擴展。
向上擴展,就是指[Base, Base + Limit]
爲有效範圍。
windows只有向上擴展。
W/A就是可寫/是否已經訪問。
D/B標誌位此時被稱爲B位。
如果是棧段,則
- 0,默認使用16位的 SP 寄存器操作棧,段最大64K;
- 1,默認使用32位的 ESP 寄存器操作棧,段最大4GB.
如果是向下展開的數據段,CPU雖然提供了這個機制,但是操作系統並沒有使用這個機制。
代碼段描述符
S==1 && Type.11==1
則該段是代碼段。
S | Type |
| 1| C| R| A|
C就是Consistency,一致性。
- 0,非一致性代碼段,這樣的代碼段可被同級代碼使用,或通過門調用;
- 1, 一致性代碼段,R3可以執行該段(R0),但R3部分保持自己的特權等級。
R:
- 0,不可讀,只能執行;
- 1,可讀可執行;
- 保護模式下,代碼段不可寫。
D/B標誌位此時被稱爲 D 位,這個位會影響指令和操作數的尋址模式:
- 0,指令默認的尋址模式是16位,操作默認大小爲16位或8位。
- 1,指令默認的尋址模式是32位,操作數默認爲32位或8位;
opcode前綴67H可以用來切換尋址模式。
例如,當前 D ==1時,尋址模式是32位,切換之後,尋址模式就變成16位模式
opcode前綴 66H 可以用來切換操作數大小。
例如,當前 D==0 時,操作數大小默認爲32,切換之後,操作數大小爲16位.
系統段描述符
s==0
則該段是系統段。
系統段中的描述符類型一般是門描述符。後面詳細整理。
2.1 賦值原理
先看一句32位指令:
mov eax , dword ptr ds:[0x403000]
如果在16位的實模式下,一般就是 ds*16+0x403000,但是在32位的保護模
式下,16位的段寄存器並不能存儲一個64位的段描述符。
系統的衆多描述符被統一打包存儲在內存中,形成的一個數組被稱爲全局描述符表GDT,由GDTR寄存器找到。
mov ax,2Bh
mov ds,ax
這條指令可看成將 0x2B 賦值給ds,實際不是.
按照13:1:2的格式二進制展開0x2B:
0000000000101 0 11
含義:請求權限爲最低的3,GDT中第5個段描述符。
cpu這時會做權限檢查,通過檢查後mov ds,ax
,將GDT[5]段描述符存儲在段寄存器隱藏部分,將段選擇子存儲到16位可見部分。
上面的檢查其實就是一個公式:max(CPL,RPL) <= DPL
2.2 段權限檢查
3個概念:
- CPL,CS段低兩位(RPL);
- RPL,段寄存器加載時的段選擇子中,描述以什麼權限訪問目標;
- DPL,Descriptor…, 目標GDT段描述符中的2位(B13-14),描述了訪問本段所需的最低權限。
1.4.0 數據段權限檢查
mov ds,ax
,ax(0x2B)作爲段選擇子,低兩位作爲RPL,高13位(0x05)作爲索引獲取GDT[5],找到DPL。
滿足max(CPL,RPL) <= DPL
即通過檢查。
也就是說,目標段權限要最小。
1.4.1 代碼段權限檢查
僅限16位。
jmp/call/ret段內跳轉不會檢查,jmp far這樣的段間跳轉纔會。
在彙編中,有一些指令是可以跨段跳轉的,例如:
jmp 33:401000
call 33:401000
這兩條指令在執行後,會將操作數中的段選擇子對應的段描述符加載到 CS 中:
s==1 && Type.11 == 1
則代表請求代碼段;- Type.c==0,非一致代碼段,則要求
CPL==DPL
;一致代碼段,則要求CPL>=DPL
。
//段選擇子的結構:
// [描述符下標:13 | T1:1 | RPL:2]
unsigned short segSel = 0x33;// 0x33就是要切換的段選擇子,在指令jmp 33:401000 中給
unsigned int RPL = segSel & 0x11; // 取段選擇自的低2位作爲RPL
unsigned int CPL = CS & 0x11 ; // 取`CS`段寄存器的值的低2位作爲CPL
if( SegDes.S == 1 && SegDes.Type & 0x1000 ){
if(SegDes.Type.E == 1){ // 一致代碼段
if( CPL >= segDes.DPL){
CS = segSel; // 可以切換。
}else{
throw "異常";
}
}else{ // 非一致代碼段
if(CPL == SegDes.DPL && RPL <= SegDes.DPL){
CS = segSel;
}else{
throw "異常";
}
}
}else{
throw "異常";
}
2.2 其它檢查
有效位檢查
p爲0則觸發異常。
段類型檢查
加載段選擇符到段寄存器:
- cs只能存放可執行段選擇符
- ss存放可寫數據段選擇符
訪問段時:
- 代碼段不可寫
- 可寫位沒有設置的數據段不可寫
- 可讀位沒有設置的數據段不可讀
3. 系統段描述符-門描述符
當段描述符中的 S 位等於0,表示這個描述符是一個門描述符。門描述符一般用於從3環進入到0環,並能夠將3環權限切換成0環權限。
很多時候,用戶層的代碼需要切換到內核層執行代碼。因爲有些代碼執行時需要用到0環權限。切換0環權限實際就是將CS段寄存器的CPL改成0(也就是0環權限)。
但在3環時, CS 的 CPL是3,是無法直接修改的。如果要修改,就需要切換段選擇子,切換段選擇子,就需要判斷MAX(CPL , RPL)<= DPL
。
門描述符的種類有:
- 調用門,type == 1100B(12),Windows操作系統沒有使用此機制
- 中斷門,type == 1110B(14),IDT表中的中斷處理函數就是這種門描述符
- 陷阱門,type == 1111B(15),IDT表中的陷阱處理函數就是這種門描述符
- 任務門,type == 0101B(5),用於任務切換
3.0 調用門
調用門的出現是爲了便於在不同的權限直接切換.
一個調用門中,保存了以下信息:
- 要執行的函數的地址
- 要執行的函數的參數個數(每個參數應當是4字節)
- 段選擇子(這個段選擇子用於切換權限),使用調用門時,這個選擇子就是被切換成 CS 的選擇子
如果發生了權限切換,系統也會將用戶棧切換成內核棧,也就是權限切換時,CS 段寄存器的值會被改掉(不改掉就切換不了切換段描述符),還會將 SS 段選擇子,切換爲內核的段選擇子,將 ESP 切換成內核的 ESP ,切換前, SS , ESP 的值都會被保存。在切換回來之後,才進行還原。
其實就是切換cs,ss,esp.
因爲棧要進行切換,當函數被調用時,用戶棧中的函數參數會被拷貝到內核棧因此需要在調用門描述符中指定參數的個數是多少,否則系統將無法爲函數拷貝參數到內核棧中.
設置和使用
因爲在Windows中沒有使用調用門, 因此, 在GDT表中是沒有調用門描述符的.
想要試驗一個調用門,就需要自己使用 windbg開啓雙擊調試,並自行在GDT中設置一個.
具體步驟看最後面編程。
3.1 任務門、中斷門和陷阱門
除了GDT,IDT也存儲了三種門描述符:
- 中斷門-可屏蔽中斷
- 陷阱門-異常
- 任務門-不可屏蔽中斷
中斷門和陷阱門的描述符其實和調用門一模一樣.
產生中斷或異常後:
- CPU會使用中斷號找到 IDT 表中的中斷描述符/陷阱描述符,
- 取出描述符後,得到門描述符中的段選擇子.
- 通過此段選擇子找到 GDT 表中的段描述符,
- 從GDT表中取出的段描述符中得到段基地址
- 使用段基地址 + 門描述符 中的函數偏移,得到函數地址.
- 調用該函數.
4. 其它寄存器
4.0. 調試寄存器
DR0-DR7
- DR-DR3,用於保存斷點的線性地址.
- DR7,分別保存4個斷點的中斷條件.
- DR6,保存斷點命中後的信息.
4.1. 控制寄存器
CR0-4,CR1保留,CR2專門用於保存缺頁異常時的線性地址。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-qRV4mSMx-1576737214295)(http://r.photo.store.qq.com/psb?/V11kR0B91ocBaQ/OiaJ*5kDaCmYCrQ5vJ9dqaSQNSI9bw90rfXkmDDvbFA!/r/dFMBAAAAAAAA)]
CR0
包含處理器的大量控制標誌位。下面列出常用的三種:
- PE,是否啓用保護模式,置1則啓用
- PG,是否使用分頁模式,置1則開啓分頁模式,此標誌置1時,PE標誌也必須置1,否則CPU報異常.
- WP,WP爲1時,不能修改只讀的內存頁;WP爲0 時,可以修改只讀的內存頁。
PE==1 && PG==1
則cpu功能工作在開啓分頁機制的保護模式下,也就是我們使用的系統。
不存在這兩種情況:
PE==1 && PG==0
,不開啓分頁的保護模式;PE==0 && PG==1
,無保護的分頁模式
CR3
專門用於保存一個進程的頁目錄地址(此寄存器存儲的是物理地址)
此寄存器的低12位一般都設置爲0,因爲 CR3 寄存器所保存的地址是一個頁目錄的地址,該地址必須在一個以分頁粒度對其的地址上。
- 在使用非 PAE 模式下的分頁結構時,指向 PDT 基地址,線性地址是 10‐10‐12 格
式 - 使用 PAE 模式的分頁結構時,指向
PDPT
基地址,線性地址是2‐9‐9‐12
格式
CR4
提供了一些擴展的控制功能.
- PAE==1,表示使用物理地址擴展模式;
- PAE==0,表示不使用物理地址擴展模式。
4.3. GDTR,IDTR寄存器
GDTR寄存器是48位的,高32位保存GDT表的內存地址,低16位保存的是GDT表的大
小.
LGDT:fword [地址] ; 將內存地址中存儲的48位數據加載到GDTR寄存器總.高32位表示GDT表基地
SGDT:fword [地址] ; 將GDTR寄存器中的值存入到內存地址中.高32位表示GDT表基地址,低16位
4.4 MSR寄存器
全稱:特殊模組寄存器組
這組寄存器它們沒有名字,只有編號,它們保存了大量的信息,這些信息可以通過兩條指令和一個編號來讀寫:
RDMSR
,讀特殊寄存器的值,指令默認使用 ecx 寄存器來保存要讀取的 MSR 寄存器的編號,使用edx:eax來保存讀取結果。WRMSR
,寫特殊寄存器,指令默認使用 ecx 寄存器來保存要寫入的 MSR 寄存器的編號,使用edx:eax來保存要寫入的值。
這兩個指令R0才能執行。
常見 MSR 編號見Intel手冊第三卷第35章.