Windows保护模式学习笔记(一)—— 段寄存器GDT表
Windows保護模式學習筆記(一)—— 段寄存器&GDT表
- 保護模式
- 參考書籍:
- 一、段寄存器
- 段寄存器的結構
- 段寄存器的讀寫
- 段寄存器的屬性
- 1)探測Attribute:
- 2)探測Base:
- 3)探測Limit:
- 二、GDT表與LDT表
- GDT表
- 1)段描述符
- 段描述符的屬性
- 段描述符與段寄存器結構的對應關系
- 2)段選擇子
- 加載段描述符到段寄存器
- 段權限檢查
- 1) CPU分級概念
- 2) 進程特權級別
- 當前特權級(CPL)
- 請求特權級(RPL)
- 3)數據段的權限檢查
保護模式
X86 CPU的三個模式:實模式、保護模式和虛擬8086模式。
參考書籍:
《Intel白皮書第三卷》
一、段寄存器
什么是段寄存器?
當我們用匯編讀寫某一個地址時:
mov dword ptr ds:[0x123456], eax我們真正讀寫的地址是:
ds.base + 0x123456段寄存器有幾個,有哪些?
有八個,分別是:
ES CS SS DS FS GS LDTR TR段寄存器的結構
結構圖表示:
結構體表示:
段寄存器的讀寫
讀:MOV AX,ES
寫:MOV DS,AX
注意:段寄存器在讀的時候只讀16位,但寫的時候會寫入96位!
思考:如何證明向段寄存器寫入時寫了96位?
段寄存器的屬性
屬性圖:
圖中紅色字體部分在不同環境中可能不同
1)探測Attribute:
在編輯器中嘗試編譯并執行以下代碼:
int var = 0; __asm {mov ax,ss // 此處不能為CS CS可讀 可執行 但不可寫mov ds,axmov dword ptr ds:[var],eax }編譯器能成功編譯上述代碼,并且程序運行過程中沒有報錯
再在編輯器中嘗試編譯并執行以下代碼:
int var = 0; __asm {mov ax,cs // SS 改成了 CSmov ds,axmov dword ptr ds:[var],eax }編譯器能成功編譯上述代碼,但程序運行過程中報錯
上面的兩個例子說明段寄存器的Attribute在寫入時會被更改!
2)探測Base:
在編輯器中嘗試編譯并執行以下代碼:
int var = 1; __asm { mov ax,fs mov gs,ax mov eax,gs:[0] // 不要使用DS 否則編譯不過去mov dword ptr ds:[var],eax // = mov edx,dword ptr ds:[0x7FFDF000] }編譯器能成功編譯上述代碼,并且程序運行過程中沒有報錯
說明段寄存器的Base在寫入時會被更改!
3)探測Limit:
int var = 1; __asm { mov ax,fs mov gs,ax mov eax,gs:[0x1000]// 訪問的地址相當于下面這行注釋的代碼 但DS的Limit是0xFFFFFFFF// mov eax,dword ptr ds:[0x7FFDF000+0x1000]mov dword ptr ds:[var],eax }編譯器能成功編譯上述代碼,但程序運行過程中報錯
這是因為 FS 段寄存器的 Limit 為 0xFFF,而我們輸入的段偏移為0x1000
思考:段寄存器在寫入時,只給了16位,剩下的80位填什么?數據從哪里來?
二、GDT表與LDT表
GDT:全局描述符表
LDT :局部描述符表
當我們執行類似MOV DS, AX指令時,CPU會查表,根據AX的值來決定查找GDT還是LDT,查找表的什么位置,以及查出多少數據
GDT表
工具:WinDbg
gdtr是一個寄存器,存儲了GDT表所在位置
gdtl也是一個寄存器,存儲了GDT表的大小
在內存中查看GDT表
第一排的數據為內存地址,紅框中的數據才是真正的內存數據,即GDT表
我們已經知道如何查看GDT表了,但是如果想要看懂這張表,得先學習段描述符和段選擇子
1)段描述符
描述:GDT表中存儲的元素稱為段描述符
大小:每個段描述符占用空間為8個字節
為了更方便觀察,在WinDbg中使用dq命令查看GDT表:
段描述符結構圖:
段描述符高位在前,低位在后
例如GDT表的第二項:00cf9b00`0000ffff
00cf9b00對應結構圖的高四字節(上面一行),0000ffff對應結構圖的低四字節(下面一行)
段描述符的屬性
P位
P = 1:段描述符有效
P = 0:段描述符無效
段描述符加載時,首先看P位是否為1
G位
G=0:段寄存器的Limit元素單位為字節,最大值為0x000FFFFF
G=1:段寄存器的Limit元素單位為4KB,最大值為0xFFFFFFFF
S位
S = 1:段描述符為代碼段或數據段描述符
S = 0:段描述符為系統段描述符
Type域
當S = 1時,即段描述符為代碼段或數組段描述符時,Type域結構圖如下:
第11位為0:段描述符為數據段描述符
第11位為1:段描述符為代碼段描述符
A位:若該代碼段/數據段未被訪問過,則值為0,否則為1
W位:若為1,表示該段可寫
E位:若為0,則向上拓展,若為1,則向下拓展
圖例:
向上拓展:有效范圍為fs.Base ~ fs.Base+Limit
向下拓展:有效范圍除了fs.Base ~ fs.Base+Limit
R位:若為1,表示該段可讀
C位:一致位。若為1,則是一致代碼段;若為0,則是非一致代碼段
當S = 0時,即段描述符為系統段描述符時,Type域結構圖如下:
D\B位
情況1:對CS段的影響
D=1:采用32位尋址方式
D=0:采用16位尋址方式
情況2:對SS段的影響
D=1:隱式堆棧訪問指令(如:PUSH POP CALL)使用32位堆棧指針寄存器ESP
D=0:隱式堆棧訪問指令(如:PUSH POP CALL)使用16位堆棧指針寄存器SP
情況3:向下拓展的數據段
D=1:段上限為4GB
D=0:段上限為64KB
DPL
描述:
DPL存儲在段描述符中,規定了訪問所在段描述符所需要的特權級別是多少
DPL數值越大,訪問所在段描述符所需要的權限越低
注意:在Windows中,DPL只會出現兩種情況,要么全為0,要么全為1
例:
若AX指向的段描述符的DPL=0,但當前程序的CPL=3,那么這條指令是不會成功的!
段描述符與段寄存器結構的對應關系
Attribute:位于段描述符高四字節的第8-23位
Base:由三部分組成
第一部分:位于段描述符高四字節的第24-31位
第二部分:位于段描述符高四字節的第0-7位
第三部分:位于段描述符低四字節的第16-31位
Limit:由兩部分組成
第一部分:位于段描述符高四字節的第16-19位
第二部分:位于段描述符低四字節的第0-15位
2)段選擇子
描述:
段選擇子是一個16位的段描述符,該描述符指向了定義該段的段描述符
段選擇子結構圖:
字段說明:
RPL:請求特權級別
TI:TI=0 查GDT表;TI=1 查LDT表
Index:處理器將索引值乘以8在加上GDT或者LDT的基地址,就是要加載的段描述符
加載段描述符到段寄存器
除了MOV指令,還可以使用LES、LSS、LDS、LFS、LGS指令修改段寄存器
注意:不存在LCS指令,因為CS不可寫
例:
char buffer[6]; __asm { les ecx,fword ptr ds:[buffer] //高2個字節給es,低四個字節給ecx }實驗:為buffer賦值,并成功執行以上代碼
注意:RPL<=DPL(在數值上)
段權限檢查
1) CPU分級概念
平時我們稱應用程序為3環,系統程序為0環,前面這句話只與CPU有關,與操作系統無關
思考:如何判斷某個程序處于哪一環?
2) 進程特權級別
當前特權級(CPL)
描述:
段寄存器 CS 的后兩位比特位稱為當前特權級
注意:段選擇子SS和CS的后兩位比特位相同
如:
→ CS = 0x001B
→ 0x001B = 二進制:0000 0000 0001 1011
→ 二進制:11 = 十進制:3
→ 因此:當前進程處于3環
請求特權級(RPL)
描述:
RPL是段選擇子結構中的一部分
RPL是針對段選擇子而言的,每個段的選擇子都有自己的RPL
RPL表示用什么權限去訪問一個段
例:
MOV AX,0008 MOV DS,AX 與 MOV AX,000B MOV DS,AX 指向的是同一個段描述符,但RPL不同3)數據段的權限檢查
檢查:CPL<= DPL并且 RPL<= DPL(數值上的比較)
例:
當CPL = 0時執行以下指令:MOV AX,000B // RPL=3,請求權限為3MOV DS,AX // 假設ax指向的段描述符的DPL=0 上述指令雖然滿足了CPL<=DPL,但RPL>DPL,因此執行失敗注意:代碼段和系統端描述符的檢查方式不一樣
思考:既然已經有CPL(當前特權級別)了,為什么還要有RPL(請求特權級別)
回答:我們本可以用“讀寫”的權限去打開一個文件,但為了避免出錯,有些時候我們使用“只讀”的權限去打開
總結
以上是生活随笔為你收集整理的Windows保护模式学习笔记(一)—— 段寄存器GDT表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 015 已接任务链表和任务库二叉树遍历
- 下一篇: Windows保护模式学习笔记(二)——