寫在前面
此系列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統內核的復雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閑錢,可以打賞支持我的創作。如想轉載,請把我的轉載信息附在文章后面,並聲明我的個人信息和本人博客地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統內核——簡述 ,方便學習本教程。
看此教程之前,問幾個問題,基礎知識儲備好了嗎?上一節教程學會了嗎?上一節課的練習做了嗎?沒有的話就不要繼續了。
🔒 華麗的分割線 🔒
練習及參考
本次答案均為參考,可以與我的答案不一致,但必須成功通過。第一題的答案可以比我的更詳細。
1️⃣ 記住代碼段間跳轉的執行流程。
🔒 點擊查看答案 🔒
1、段選擇子拆分
2、查表得到段描述符
3、權限檢查
4、加載段描述符
5、代碼執行
2️⃣ 自己實現一致代碼段的段間跳轉。
🔒 點擊查看答案 🔒
1. 自行構造一個段描述符 00CF9B00`0000FFFF ,把它填寫到 GDT 表中,我填寫到索引為 18 的位置。
2. 打開一個OD隨便調試一個應用程序,在 EIP 的位置修改一條指令,如圖1所示。
3. 修改完畢后,單步走一步,看看是否成功,如果成功則會跳轉到指定位置,如圖2所示。
(圖1)
(圖2)
3️⃣ 自己實現非一致代碼段的段間跳轉。
🔒 點擊查看答案 🔒
請根據題目2的答案,將段描述符改為 00CF9C00`0000FFFF ,其余保持不變繼續即可。
門
本篇文章涉及調用門、中斷門、陷阱門這三個重要的“門”,那么“門”到底是什么。打個比方,“門”就類似於你去辦理身份證必須要進入派出所的門一樣。你想辦理身份證就必須通過這個門,如果你進不了門就辦理不了。調用門、中斷門、陷阱門也是如此,通過它們你可以修改段的屬性,甚至能提權去做一些應用層做不了的事情。
調用門
在將所有的知識之前,先講一下調用門,因為后面的知識頻繁用到了調用門的概念,調用門的結構如下圖所示。它和普通的段描述符結構十分相似。低四個字節改為段選擇子,如果指向的段描述符的DPL
小於CPL
,則會提權。高四個字節低5個位是調用調用門需要的參數數目。低四個字節的低16位和高四個字節的高16位拼接為跳轉后新的在段中的偏移,也就是調用后EIP
的位置。
它的具體細節將會在長調用講完后繼續講解。
長調用
介紹長調用,我先來講一下什么是短調用
。短調用就是我們在匯編常見的CALL
指令,調用格式為:CALL 立即數/寄存器/內存
。為什么是短調用,我們來看一下執行該指令時堆棧的變化:
調用CALL
指令之后,CPU
只將當前的EIP
壓入堆棧后跳轉到目標地址,發生改變的寄存器只有ESP
和EIP
,即所謂的短調用。
長調用分為兩種,一種提權,一種不提權,調用格式為:指令格式:CALL CS:EIP
,其中EIP
是廢棄的,CS
為指向調用門
的段選擇子。但是值得注意的是CS
一旦更換,它的EIP
和SS
要同時更換。為什么EIP
需要更換我就不說了。在代碼執行的時候,一定會用到堆棧,堆棧的段權限必須與CS
匹配,這就是為什么SS
必須更換。我們接下來看看長調用到底是何方神聖。
長調用不提權
當段選擇子指向的調用門不提權時。發生改變的寄存器有ESP
、EIP
和CS
,比短調用多一個CS
。執行情況如下圖所示:
長調用提權
當段選擇子指向的調用門不提權時。發生改變的寄存器有ESP
、EIP
、CS
和SS
。執行情況如下圖所示:
心細的你獲取發現,SS
並沒有壓入堆棧之中,還有ESP0
也沒用通過已知方式獲取得到。它從哪里來呢?下一節教程將會講解。
調用門(續)
本篇開頭簡單介紹了調用門,接下來將會詳細介紹它的調用流程。先把上面的調用門結構圖貼到下面,以供方便學習:
調用門執行流程如下所示:
-
指令格式:
CALL CS:EIP (EIP是廢棄的)
-
執行步驟(具體詳情請看長調用):
- 根據
CS
的值查GDT表
,找到對應的段描述符且該描述符是一個調用門。 - 在調用門描述符中存儲另一個代碼段的段選擇子,將其加載到
CS
中。 - 選擇子指向的段的
Base + 偏移地址
就是真正要執行的地址。
- 根據
當然段選擇子加載段描述符的過程都會有權限檢查,不懂的話請不要再繼續了,回去查看上一篇復習,懂了再回來學習。
中斷門
講中斷門之前,我先來介紹一個新的表,稱之為IDT表
。IDT表
與GDT表
不同,它的第一個元素不是NULL
。IDT表
包含3種門描述符:任務門描述符、中斷門描述符、陷阱門描述符,這里面的幾種類型都會在后面講到。中斷門的結構如下圖所示(圖中的D
表示是否為32位
,如果是則為1
):
IDT也是由一系列描述符組成的,每個描述符占8個字節
。Windows沒有使用調用門,但是使用了中斷門用於系統調用和調試用途。在WinDbg
下可用r idtr
讀取IDT表
的首地址,r idtl
讀取IDT表
的大小。
中斷門的結構和調用門結構幾乎一樣,只是調用門用來寫參數數目的位被清空不再使用和Type域不一樣而已。
既然中斷門的結構知道了,我們來看一下中斷門的執行流程:
-
指令格式:
INT N (N為中斷門索引號)
-
執行步驟:
- 在沒有權限切換時,會向堆棧順次壓入
EFLAG
、CS
和EIP
;如果有權限切換,會向堆棧順次壓入SS
、ESP
、EFLAG
、CS
和EIP
。 CPU
會索引到IDT
表。后面的N
表示查IDT表
項的下標。對比調用門,中斷門沒有了RPL
,故CPU
只會校驗CPL
。- 在中斷門中,不能通過
RETF
返回,而應該通過IRET
/IRETD
指令返回。
- 在沒有權限切換時,會向堆棧順次壓入
陷阱門
陷阱門的結構和中斷門結構幾乎一樣,只是Type域不同而已,如下圖所示(圖中的D
表示是否為32位
,如果是則為1
):
陷阱門執行流程一模一樣。與中斷門的區別,中斷門執行時,將IF位
清零,但陷阱門不會。
本篇小結
- CS的權限一旦改變,SS的權限也要隨着改變,CS與SS的等級必須一樣。
JMP FAR
只能跳轉到同級非一致代碼段,但CALL FAR
可以通過調用門提權,提升CPL
的權限。- 調用門總結:
- 當通過門,權限不變的時候,只會
PUSH
兩個值:CS
和返回地址
,新的CS的值由調用門決定。 - 當通過門,權限改變的時候,會
PUSH
四個值:SS
、ESP
、CS
和返回地址
,新的CS
的值由調用門決定,新的SS
和ESP
由TSS
提供。 - 通過門調用時,要執行哪行代碼由調用門決定,但使用
RETF
返回時,由堆棧中壓入的值決定,這就是說,進門時只能按指定路線走,出門時可以FQ,即只要改變堆棧里面的值就可以想去哪去哪。 - 可不可以再建個門出去呢?當然可以了。前門進,后門出。
- 當通過門,權限不變的時候,只會
練習
本節的答案將會在下一節進行講解,務必把本節練習做完后看下一個講解內容。不要偷懶,實驗是學習本教程的捷徑。
俗話說得好,光說不練假把式,如下是本節相關的練習。如果練習沒做好,就不要看下一節教程了,越到后面,不做練習的話容易夾生了,開始還明白,后來就真的一點都不明白了。本節練習不多但坑很多,需要花費大量的時間,請保質保量的完成。
1️⃣ 構造無參的調用門,實現提權后讀取高2G的地址並分析堆棧情況。
2️⃣ 構造有參的調用門,實現提權后正確依次取出參數並分析堆棧情況。
3️⃣ 構造調用門,提權后,實現“FQ”,即不按原函數地址返回。
4️⃣ 構造中斷門,實現提權后讀取高2G的地址並分析堆棧情況。
5️⃣ 構造陷阱門,實現提權后讀取高2G的地址並分析堆棧情況。