1. 程式人生 > 實用技巧 >保護模式-第4講-段-段跨越段許可權

保護模式-第4講-段-段跨越段許可權

目錄

保護模式-第4講-段-段跨越段許可權

一丶段描述符 段選擇子 等段特權級講解

1.1 CPU層面的模式

​ 這一點主要是瞭解下. 我們很多時候都聽別人說 ring3 ring0 其實就是 CPU的等級劃分.

不同的級別可以執行不同的 特權指令. 比如 IN OUT

等指令.在16位 真實模式下就可以直接執行.

而保護模式下就不讓你執行了. 原因就是 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等