保護模式-第4講-段-段跨越段許可權
目錄
保護模式-第4講-段-段跨越段許可權
一丶段描述符 段選擇子 等段特權級講解
1.1 CPU層面的模式
這一點主要是瞭解下. 我們很多時候都聽別人說 ring3 ring0 其實就是 CPU的等級劃分.
不同的級別可以執行不同的 特權指令. 比如 IN OUT
而保護模式下就不讓你執行了. 原因就是 CPU分了等級了. 一共四個等級. ring3 - ring 0 而作業系統只使用了 ring3 與 ring0 所以 ring3就是應用程式. ring0 就是核心程式. 應用程式不可以執行特權指令
核心程式可以執行特權指令. 請注意. CPU是有4個等級的. ring3 ring 2 ring 1 ring0 作業系統使用了兩個. 也就是微軟使用了兩個. 所以不要搞混. 我們要知道 特權級別是CPU提供. 微軟只是使用而已.
1.2 CPL RPL DPL 講解
1.2.1 CPL RPL DPL 介紹
根據inter手冊圖表示
-
CPL
CPL 是在CS 與 SS 段暫存器中的 bit 0位與 bit1位 記住是CS段暫存器. 它來表示我們的程式的當前特權級別. 也就是說 表示你是 執行在ring3的許可權. 還是 ring0的許可權.
-
RPL bit 0 bit1
RPL是段選擇子的特權級別. 舉個例子.看如下程式碼
mov ds,ax
ax裡面是一個值.我們知道這個值是一個 段選擇子 拆分開來可以是索引. 根據這個索引去GDT表中尋找對應的段描述符 然後段描述符 賦值給ds. 但是我們有沒有想過. CPU執行這條指令的時候.為什麼要去查.難道不該限制嗎.不管是ring3 還是 ring0都可以查表嗎. 不是這樣的. RPL就是一個限制. 表示你想請求的特權級別. 當你請求的特權級別不滿足.那麼CPU肯定不會執行這條指令的.
那麼不滿足什麼那. 就是我們下面所說的DPL
-
DPL 高13 14位
DPL是描述段描述符的. 是指明瞭我們這個段描述符 你想訪問我你需要什麼許可權. 也就是說我們的RPL想訪問段描述符. 會跟 DPL做對比. <= DPL才可以訪問. 進而操作這個段描述符.
還是以程式碼為例子
mov ds,ax
這條指令是執行在ring3的. 所以不用說 CS 的低兩位 = 11 也就是3
但是我們知道 ax是一個段選擇子.回去找GDT表中的段描述符. 而ax指向的這個段描述符的DPL正好是0 就是特權很高. 0是最高特權. 那麼這條指令不會成功. 原因就是 CPL > DPL CPL必須<= DPL才能成功. 就是是一次段特權級別的 比較.
核心就是DPL 任何CPL RPL 都是為訪問DPL(段描述符)來進行準備的. 如果不與DPL相等或者小於 那麼 就可以訪問. 如果大於 那麼久拒絕你訪問
-
三種特權級別的表示方法
CPL bit0 bit1 == 1 表示 ring3應用 也就是CPL = 3 代表你是處於ring3的程式
CPL bit0 bit1 == 0 表示 ring0 程式. 執行在ring0中.
CS/SS CPL在同一時間特權都是一樣的.X86規定的.也就是說 CS = 3 那麼SS 也是3 CS = 0 SS 也是0
RPL 選擇子 RPL特選級別 要麼是0 要麼是3 也就是 RPL 要麼兩位都是0 要麼都是1
DPL DPL == 0 級別許可權很高.
DPL = 3 級別許可權低. 只要 CPL RPL <= 3都可以訪問.
1.2.2 CPL RPL DPL的檢查
CPL RPL DPL我麼知道是啥了.那麼我麼可以進行模擬.
-
DPL針對資料段的許可權檢查
CPL <= DPL 且 RPL <= DPL 才可以執行指令成功
舉個例子
CS = 8 代表當前特權級別是ring0 mov ax,0xF 段選擇子為 1111 二進位制 RPL是 3 mov dx,ax 執行指令操作
執行指令操作有. 優先與CPL比.然後在於RPL比.
根據指令我們知道 我們是在核心層執行的特權指令. mov dx,ax
首先檢查CPL CPL = 0 DPL = 0 CPL<DPL 成立. 所以代表當前指令可以執行
繼續比較
RPL = 3 DPL = 0 RPL<=DPL 不成立. 拒絕指令. 所以這三行指令表達了
指令可以執行.但是 你訪問這個段描述符的許可權不足. 所以不讓你訪問.
二丶程式碼跨段
3.1 程式碼跨段原理以及本質
3.1.1 原理及其本質
程式碼跨段的原理是修改CS 暫存器. 但是CS暫存器比較特殊. 因為CS暫存器跟EIP是配套的. 如果CS改了. EIP沒改. 那麼就是非一致程式碼段. 然後EIP跳轉的時候就會出錯.
要想同時修改CS 與 EIP的話 有以下幾條指令
JMP FAR / CALL FAR /RETF /INT /IRETED
長跳轉 長Call 長返回 int指令 iRetEd指令
但是隻修改EIP的話就很簡單了. 指令如下
JMP /CALL /JCC /RET
這些都可以進行修改EIP. 包括x64下也是一樣的.
-
一致程式碼段與非一致程式碼段的介紹
一致程式碼段: 可以理解為是共享段.資料是共享的.可以允許低許可權去訪問
非一直程式碼段: 如果不想讓低許可權訪問.想對這一塊加限制.那麼可以用非一致程式碼段修飾
修飾的本質還是 校驗特權位
非一致: 要求CPL == DPL 且 RPL<= DPL 才可以訪問
一致: CPL>=DPL 則可以. cpl > 則代表權限是低的.
3.1.2 JMP Far CPU執行流程
JMP far指令執行流程
截圖如下
彙編指令如下
jmp far 0x0020:A50000
當CPU執行這條指令的時候會經過五個步驟
-
1.段選擇子拆分查表
cpu 首先會將段選擇程序拆分. 如我們的彙編的段選擇子 0x0020
拆分為:
二進位制: 0000 0000 0010 0000
RPL = 00
TI = 0
index = 4
-
2.查表獲取段描述符
CPU則會根據 TI 判斷查詢是否是GDT表. 如果是查詢GDT表. 然後根據index 去GDT表中查詢對應的段描述符. 但是請注意,當我們查詢的是 程式碼段 呼叫門 TSS任務段 任務門 的時候才能繼續跳轉
-
進行許可權檢查
查表之後當然首先第一步要進行許可權檢查.看是否你能訪問我. 是否能訪問段描述符 . 此時段描述符裡面有DPL記錄. 記錄著段描述符的許可權.
一致程式碼段的情況下: 當CPL== DPL 且 RPL <= DPL 的時候.才能訪問 資料段. 我們知道 許可權要麼是0 要麼是3
二進位制分別對應的是 00 11 CPL 與RPL DPL都是兩位表示. 而數字越低許可權越高. 所以我們說的非一致程式碼段的情況下. 當前執行的特權級別(CPL) 許可權必須與 段描述符許可權相等(DPL) 否則不讓你訪問. 第一步通過之後. 然後判斷
段選擇子 (RPL) 是否 與DPL許可權相當. 如果不是也不讓你訪問.
-
4.載入段描述符
當前三不執行過後. 此時CPU 才會真正的將 段描述符載入到CS段暫存器中
-
5.程式碼執行
我們說過 jmp far 執行要跨段必須修改 CS與EIP才可以. 我們CS已經通過第四步獲取到了. 那麼EIP的填寫便是
將 cs.base + offset 填寫到EIP中. 最後執行 cs:eip的指令. 這樣我們流程才會結束
比如我們的 指令為
jmp far 0x0020:A50000
CS.base 已經通過段描述符獲取了(第四步) 那麼偏移就是 A50000 cs.base + A50000 然後進行程式碼執行
3.1.3 總結
當我們使用 JMP FAR指令的時候 CPU總會執行五步
1.拆分段選擇子
2.通過拆分的段選擇子 查詢對應的表. 得出對應的段描述符
3.進行許可權檢查. CPL RPL DPL等檢查.
4.載入段描述符
5.填寫EIP 執行程式碼.
3.2 雙機除錯 程式碼跨段實驗
原理就是根據五步步驟來操作.
其實本質還是 構造段選擇子. 段選擇子指向一個你自己定義的段描述符.
段描述符裡面可以設定許可權等.
- 1.從GDT表中尋找程式碼段
這個簡單.根據段描述符.來找 我們上一篇已經說過了. s = 1 代表是程式碼段或者是資料段. 然後再根據s解析type來
具體的判斷是程式碼段還是資料段.
其實簡單的方法就是看 段描述符的 16進製表示的低五位來判斷尋找即可. 或者根據講解段描述符的時候寫的程式碼來尋找.
檢視段描述符
我們可以看到.s跟type是緊密相連的. 所以s和type可以看作是一個整體的二進位制位. 並且這4位可以組成一個整體.
也就是一個16進位制位 正好就是 高32位的第5個位元組表示 表示了s與type. 而s = 1的時候代表是系統段或者資料段.
所以可以肯定如為1 那麼組成的這個16進位制位必然是大於8的.
舉個例子. Windbg檢視段描述符
r gdtr
dq gdtrbase
.formats gdtrbase[1]
我們直接檢視的是第二項.看下它的高4個位元組的第五位 00CF9 9就是一個16進位制位. 包含了s與type 我們說過
s = 1那麼必然會導致 s與type的組合肯定 > 8 根據我們解析的二進位制資料我們看一下 看一下第五位
解析出來是 1001 1代表s = 1 後面的001代表的type解析 分別是 C RA 也就是code程式碼段 可以看一下上篇的
type解析與s解析.
那麼確定了這個段是程式碼段. 那麼我們就可以將這個程式碼段 拷貝到GDT表陣列中要給新的地方. 最後構造段選擇子
讓其訪問即可.
- 從GDT表中拷貝一個程式碼段到GDT中沒有使用的位置
觀察上圖.我們看GDT的時候. 第一項是沒有使用的. 我們將一個段描述符(程式碼段)拷貝到GDT表中第16項中.
程式碼段怎麼找也是同上. 看s = 1還是0 確定是是程式碼或者資料段. 然後緊接著看s後面跟著的type值. 由type值確定是程式碼段還是資料段. 這裡我們直接用GDT表中的第四項來進行操作
00cffb00~0000ffff
windbg如下
r gdtr
dq GDTbase
eq 地址 拷貝的內容
dq GDTBASE
圖中遮擋的不要看.因為這是我實驗之後沒有修改回去而產生的錯誤項. 只需要看黑框一欄即可.
通過以上指令我們可以看到 將GDT表中的第四項,拷貝到了 第16項中.
下面就是構造段選擇子 然後進行跨段實驗
- 3.段選擇子構造 以及跨段實驗
我們所新增的新項在 GDT表中的第16項. 我們知道段選擇子的構成 所以按照段選擇子進行構造
但是GDT表的我們說過本質是一個數組. 這個數組裡面是8個位元組大小.儲存著段描述符.
所以我們下標要從0開始. 所有構造段選擇子的時候就要按照下表的方式來.
16項 = 下表15
15 = 二進位制 1111
RPL以及TI 構造位 011 RPL = 3
結合起來 =
1111 011
按照從右向左 4個位元組分組. 構成16進位制的段選擇子
111 1011 = 7B
所以我們段選擇子是0x7B
跨段實驗
我們直接用上面搜說的 jmp far指令來進行跳轉 模擬段操作.當這條指令執行的時候就會執行我們的五步步驟
顯然這條指令是可以執行成功了. 如果執行失敗就會跑飛. 跳轉到異常處理位置.
-
4.段許可權檢查
上面我們偽造的程式碼段描述符 = 00cffb00~0000ffff
其結果如下 我們直接洗DPL所在的高32位即可. 如下
kd> .formats 0x00cffb00 Evaluate expression: Hex: 00cffb00 Decimal: 13630208 Octal: 00063775400 Binary: 00000000 11001111 11 11(DPL) 1011 00000000 Chars: .... Time: Mon Jun 08 02:10:08 1970 Float: low 1.91e-038 high 0 Double: 6.73422e-317
通過解析.我們的DPL = 二進位制的11 十六進位制的3 我們構造的段選擇子的許可權也是11 所以執行我們指令的時候
許可權通過. 可以進行執行.
但是我們可以嘗試以下.將段選擇子的RPL修改為0特權看一下.
程式碼段選擇子改為 0x78 經過嘗試也可以進行訪問的. 原因就是 許可權檢查的時候 RPL <= DPL 既然DPL = 3那麼就代表 許可權小於或者等於我就可以訪問了. RPL = 0 代表高許可權 DPL = 3 代表地許可權 搞特權是肯定能訪問低特權描述符的.
- 5.修改描述符段DPL 重新嘗試
嘗試將DPL的11修改為00 然後重新執行程式碼跨段流程 從右邊向左.從0開始都 讀取到第13 14 DPL位 修改為00
0000 0000 1100 1111 11 11(DPL) 1011 0000 0000 0 0 C F 1001(9) B 0 0 0x00CF9B00~xxx
經過嘗試是不可以進行訪問了.且會跳到異常處理 就算段選擇子的RPL = 0 也不可以. 因為我們的CPL特權級別不夠. 而要修改CPL只能通過門來修改.
至此我們可以進行總結以及程式碼新增段描述符了. 並且進行測試
三丶總結
首先當訪問段描述符的時候.作業系統會經過五個步驟
1.拆分段選擇子
2.查表
3.DPL許可權對比
4.通過之後載入段描述符
5.段描述符中的base+當前的偏移修改到EIP中進行執行
也經過windbg除錯進行校驗.發現確實可以偽造並且跳轉. 注意一點的是
構造段選擇子的index 是下標. 還要構建RPL TI等