保護模式篇之段描述符與段選擇子,詳細介紹段權限檢查與代碼跨段跳轉。
寫在前面
此系列是本人一個字一個字碼出來的,包括示例和實驗截圖。由于系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程将會長期更新。
練習及參考
如下是從虛拟機讀取的GDT表的前18個段描述符,下面的實驗均按照此進行練習。
1️⃣ 練習讀取GDT表的位置和長度,并顯示GDT表前48個段描述符。
???? 點選檢視答案 ????
2️⃣ 在給定的段描述符中,進行拆分練習(至少10個)。
3️⃣ 拆分如下段選擇子。
4️⃣ 快速辨識給定段描述符是否可用以及段基址、段長(至少10個)。
5️⃣ 從給定段描述符,請按照下面的要求進行練習(全部):
快速找出所有資料段,并分析該段屬性:隻讀、已通路、可讀可寫、拓展方向
快速找出所有代碼段,并分析該段屬性:隻執行、可讀可執行、已通路、一緻代碼
快速找出所有系統段,并分析屬性
???? 技巧 ????
我拿出一個段描述符為例進行介紹,學會後可以自行找别的段描述符練習,如下圖所示:
表示不同含義的位或十六進制數我都加以區分。由于直接标注在段描述符上非黑色數字通過結構示意圖直接能對應上,故不再詳細講解。我們先看<code>c</code>,它的二進制就是<code>1100</code>,這個位置大于<code>8</code>就說明G位有效。同理看<code>9</code>,這個位置大于<code>8</code>就說明段描述符有效;為奇數說明為代碼段或者資料段,反之為系統段。其他的位不太常用于快速判斷,規律可自行總結。
6️⃣ 自行構造段選擇子和段描述符,并用<code>加載段描述符至段寄存器</code>中的代碼模闆和要求取得成功。如果有時間同樣把<code>LSS</code>、<code>LDS</code>、<code>LFS</code>、<code>LGS</code>的實驗也類比做了。
本題答案不唯一,合理并能夠正常運作即可,注意小端存儲。此題目有一個坑,自行踩坑。
7️⃣ 如何在調試器中快速判斷程式在幾環權限。
8️⃣ 自學修改GDT表的相關知識,并思考如下問題。
<code>8003f090</code>是GDT表中的一個段描述符的位址,更改後發現并沒有更改,請思考為什麼會這樣。
你看我之前寫讀取GDT表為什麼用位址了嗎?你再用位址<code>dq</code>以下看看。
CPL/RPL/DPL
CPL:CPU目前的權限級别
DPL:如果你想通路我,你應該具備什麼樣的權限(CPL)
RPL:用什麼權限去通路一個段
舉個例子,我們本可以用<code>讀寫</code>的權限去打開一個檔案,但為了避免出錯,有些時候我們使用<code>隻讀</code>的權限去打開。
一緻代碼段與非一緻代碼段
對于一緻代碼段,也稱為共享段:
特權級高的程式不允許通路特權級低的資料:核心态不允許通路使用者态的資料
特權級低的程式可以通路到特權級高的資料,但特權級不會改變:使用者态還是使用者态
對于非一緻代碼段:
隻允許同級通路
絕對禁止不同級别的通路:核心态不是使用者态,使用者态也不是核心态
資料段的權限檢查
數值上,<code>CPL</code><=<code>DPL</code>且<code>RPL</code><=<code>DPL</code>。同時滿足上述條件才能通過。
代碼段的權限檢查
下面的比較都是數值上的比較:
如果是非一緻代碼段,要求:<code>CPL</code>==<code>DPL</code>且<code>RPL</code><=<code>DPL</code>
如果是一緻代碼段,要求:<code>CPL</code>>=<code>DPL</code>
代碼跨段基礎
代碼跨段本質就是修改CS段寄存器。前面的教程介紹過段寄存器讀寫,除CS外,其他的段寄存器都可以通過<code>MOV</code>/<code>LES</code>/<code>LSS</code>/<code>LDS</code>/<code>LFS</code>/<code>LGS</code>指令進行修改。但是<code>CS</code>為什麼不可以直接修改呢?<code>CS</code>的改變意味着<code>EIP</code>的改變,改變<code>CS</code>的同時必須修改<code>EIP</code>,故我們無法使用上面的指令來進行修改,這個也是CPU不允許的。
代碼間的段間跳轉
段間跳轉,有2種情況,即要跳轉的段是一緻代碼段還是非一緻代碼段,它們不同做的權限檢查就不同。
同時修改<code>CS</code>與<code>EIP</code>的指令如下:<code>JMP FAR</code>/<code>CALL FAR</code>/<code>RETF</code>/<code>INT</code>/<code>IRETED</code>
本篇隻介紹段間跳轉,故隻使用<code>JMP FAR</code>,即為長跳轉。下面我舉個示例來進行講解:
1️⃣ 段選擇子拆分
<code>0x20</code>對應二進制形式:0000 0000 0010 0000
解析結果:
RPL = 0
TI = 0
Index = 4
2️⃣ 查表得到段描述符
<code>TI=0</code> 是以查GDT表,<code>Index=4</code>找到對應的段描述符。注意四種情況可以跳轉:代碼段、調用門、TSS任務段、任務門。後面的幾種将會在以後的教程詳細講解。
3️⃣ 權限檢查
請參考本節的代碼段的權限檢查
4️⃣ 加載段描述符
通過上面的權限檢查後,CPU會将段描述符加載到CS段寄存器中。
5️⃣ 代碼執行
<code>CPU</code>将<code>CS.Base + Offset</code>的值寫入<code>EIP</code>然後跳轉到将要執行的<code>CS:EIP</code>處的代碼,段間跳轉結束。
直接對代碼段進行<code>JMP</code>或者<code>CALL</code>的操作,無論目标是一緻代碼段還是非一緻代碼段,<code>CPL</code>都不會發生改變。如果要提升<code>CPL</code>的權限,隻能通過調用門。
本節練習
本節的答案将會在下一節進行講解,務必把本節練習做完後看下一個講解内容。不要偷懶,實驗是學習本教程的捷徑。
俗話說得好,光說不練假把式,如下是本節相關的練習。如果練習沒做好,就不要看下一節教程了,越到後面,不做練習的話容易夾生了,開始還明白,後來就真的一點都不明白了。本節練習比隻有很少,請保質保量的完成。
1️⃣ 記住代碼段間跳轉的執行流程。
2️⃣ 自己實作一緻代碼段的段間跳轉。
3️⃣ 自己實作非一緻代碼段的段間跳轉。
下一篇
保護模式篇——長調用與調用門、中斷門、陷阱門
作者:寂靜的羽夏