概述
這一篇 hin 重要。
一般來說,我們在 Windows 中寫的代碼,都運作在 3 環(應用層)。隻有在調用一些系統接口的時候,或者中斷等情況的時候,才會進入 0 環(核心)。
為什麼 CPU 要給代碼賦予不同的權限?這是為了防止你任意妄為。在 Windows 中,如果你随意更改了核心的重要資料,作業系統必然面臨着危險——藍屏。
為了防止這種情況發生,CPU 把執行權限分成了4個等級,0,1,2,3. 其中 0 表示最進階,可以執行任意代碼,而執行權限為 3 的程式,隻能執行普通的指令。Windows 和 Linux 隻使用了 0 和 3 這兩種。
而 CPU 的有些指令,隻能在 0 環執行。有些記憶體的通路,也隻能在 0 環。這就是所謂的保護。
之前已經學過,代碼段描述符中,有 DPL 字段,它是專門用來描述代碼段級别的。前面分析過 Windows 的GDT 表中段描述符,就發現有 0 環的代碼段和 3 環代碼段。這裡的 DPL 表示,隻允許相同級别的下的程式跳過來執行,除非被我同意,低特權級的程式才能跳進來執行。
所謂的特定級别下的程式,是指的目前 CPL 的級别。見後文。
CPU 是不允許 CPL = 3 的程式直接跳轉到 DPL=0 的代碼中去執行。
目前特權級
為了防止意外,有必要再次重複下 CPL。
這個值可以始終儲存在 cs 或者 ss 段寄存器的低 2 位。在OD裡打開一個程式,如果你細心觀察,你總會發現,cs 的最低2位一定是2進制 11,也就是3. 你絕對不可能看到這個值變成 0. 因為 OD 本身就是運作在 3 環的調試器。
這是不是意味着,目前特權級永遠不會變成 0?
不會的,後面的實驗将驗證這一點。
如果你想在 VC6.0 中寫程式,直接去讀取高 2G 位址的資料,一定會報錯。如果你目前的特權級被提升為 0 環(簡稱提權),這時候再去讀取高 2G 位址資料,就沒有問題了。然而提權并不是這麼簡單。後文會陸續介紹。
跨段執行——就是改變 CS 段寄存器
跨段執行,就是指改變目前的 cs 段寄存器,把另一個代碼段的描述符加載到 cs 段寄存器。千萬不要想當然的使用
mov ax, 0x08; mov cs, ax
,編譯器都不會讓你通過。前面你也發現,也不存在
lcs
這樣的指令加載段寄存器。
改變 cs 段寄存器,可以使用
jmp far
和
call far
等等。注意,除了這兩個指令,還有别的,這裡不能講太多。本篇隻介紹 jmp,原因還是那樣,因為它簡單。
指令 jmp 可以實作跨段執行代碼,但是它并不能提權(無法改變當權特權級),也就是說,即便你跨到了 DPL = 0 的段,你的 CPL 也不會發生任何改變。 除非你原來就是 0.
使用 jmp 跨到 0 環代碼段,也是有要求的,除非這個0環代碼段描述符同意(這就是所謂的一緻代碼段)。
一緻代碼段與非一緻代碼段
當段描述符描述代碼段時,TYPE 字段的 c 位置 1 說明該段是一緻代碼段,否則是非一緻代碼段。
- 隻有得到段描述符的同意,才允許低權限的程式跳轉進去執行。這種段稱為一緻代碼段。
- 而有些代碼段描述符,絕對不允許低權限的程式跳轉進去執行,這種段稱為非一緻代碼段。
實作跨段:一緻代碼段(比目前權限高的)
要求:CPL >= DPL
步驟:
1. 在8003f048 處添加描述符 00cf9f00`0000ffff,該描述符描述的段為一緻代碼段,DPL = 0
指令 eq 8003f048 00cf9f00`0000ffff
2. 打開 OD,修改目前指令為 jmp 48:0044420c
3. 執行指令後跳轉到了 0044420c 處,且 cs 變為 0x4b
圖1 安裝描述符
圖2 跨段跳轉
實作跨段:非一緻代碼段(與目前權限一緻)
要求:CPL == DPL 并且 RPL <= DPL
步驟:
1. 在8003f048 處添加描述符 00cffb00`0000ffff,該描述符描述的段為非一緻代碼段,DPL = 3
指令 eq 8003f048 00cffb00`0000ffff
2. 打開 OD,修改目前指令為 jmp 48:0044420c
3. 執行指令後跳轉到了 0044420c 處,且 cs 變為 0x4b
圖3 安裝段描述符
圖4 跨段跳轉