保護模式篇——長調用與調用門、中斷門、陷阱門

寫在前面

  此系列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統內核的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閑錢,可以打賞支持我的創作。如想轉載,請把我的轉載信息附在文章後面,並聲明我的個人信息和本人博客地址即可,但必須事先通知我

你如果是從中間插過來看的,請仔細閱讀 羽夏看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壓入堆棧後跳轉到目標地址,發生改變的寄存器只有ESPEIP,即所謂的短調用。
  長調用分為兩種,一種提權,一種不提權,調用格式為:指令格式:CALL CS:EIP,其中EIP是廢棄的,CS為指向調用門的段選擇子。但是值得注意的是CS一旦更換,它的EIPSS要同時更換。為什麼EIP需要更換我就不說了。在代碼執行的時候,一定會用到堆棧,堆棧的段權限必須與CS匹配,這就是為什麼SS必須更換。我們接下來看看長調用到底是何方神聖。

長調用不提權

  當段選擇子指向的調用門不提權時。發生改變的寄存器有ESPEIPCS,比短調用多一個CS。執行情況如下圖所示:

長調用提權

  當段選擇子指向的調用門不提權時。發生改變的寄存器有ESPEIPCSSS。執行情況如下圖所示:

  心細的你獲取發現,SS並沒有壓入堆棧之中,還有ESP0也沒用通過已知方式獲取得到。它從哪裡來呢?下一節教程將會講解。

調用門(續)

  本篇開頭簡單介紹了調用門,接下來將會詳細介紹它的調用流程。先把上面的調用門結構圖貼到下面,以供方便學習:

  調用門執行流程如下所示:

  • 指令格式:CALL CS:EIP (EIP是廢棄的)

  • 執行步驟(具體詳情請看長調用):

    1. 根據CS的值查GDT表,找到對應的段描述符且該描述符是一個調用門。
    2. 在調用門描述符中存儲另一個代碼段的段選擇子,將其加載到CS中。
    3. 選擇子指向的段的Base + 偏移地址就是真正要執行的地址。

  當然段選擇子加載段描述符的過程都會有權限檢查,不懂的話請不要再繼續了,回去查看上一篇複習,懂了再回來學習。

中斷門

  講中斷門之前,我先來介紹一個新的表,稱之為IDT表IDT表GDT表不同,它的第一個元素不是NULLIDT表包含3種門描述符:任務門描述符、中斷門描述符、陷阱門描述符,這裏面的幾種類型都會在後面講到。中斷門的結構如下圖所示(圖中的D表示是否為32位,如果是則為1):

  IDT也是由一系列描述符組成的,每個描述符占8個位元組。Windows沒有使用調用門,但是使用了中斷門用於系統調用和調試用途。在WinDbg下可用r idtr讀取IDT表的首地址,r idtl讀取IDT表的大小。
  中斷門的結構和調用門結構幾乎一樣,只是調用門用來寫參數數目的位被清空不再使用和Type域不一樣而已。
  既然中斷門的結構知道了,我們來看一下中斷門的執行流程:

  • 指令格式:INT N (N為中斷門索引號)

  • 執行步驟:

    1. 在沒有權限切換時,會向堆棧順次壓入EFLAGCSEIP;如果有權限切換,會向堆棧順次壓入SSESPEFLAGCSEIP
    2. CPU會索引到IDT表。後面的N表示查IDT表項的下標。對比調用門,中斷門沒有了RPL,故CPU只會校驗CPL
    3. 在中斷門中,不能通過RETF返回,而應該通過IRET/IRETD指令返回。

陷阱門

  陷阱門的結構和中斷門結構幾乎一樣,只是Type域不同而已,如下圖所示(圖中的D表示是否為32位,如果是則為1):

  陷阱門執行流程一模一樣。與中斷門的區別,中斷門執行時,將IF位清零,但陷阱門不會。

本篇小結

  1. CS的權限一旦改變,SS的權限也要隨着改變,CS與SS的等級必須一樣。
  2. JMP FAR只能跳轉到同級非一致代碼段,但CALL FAR可以通過調用門提權,提升CPL的權限。
  3. 調用門總結:
    • 當通過門,權限不變的時候,只會PUSH兩個值:CS返回地址,新的CS的值由調用門決定。
    • 當通過門,權限改變的時候,會PUSH四個值:SSESPCS返回地址,新的CS的值由調用門決定,新的SSESPTSS提供。
    • 通過門調用時,要執行哪行代碼由調用門決定,但使用RETF返回時,由堆棧中壓入的值決定,這就是說,進門時只能按指定路線走,出門時可以FQ,即只要改變堆棧裏面的值就可以想去哪去哪。
    • 可不可以再建個門出去呢?當然可以了。前門進,後門出。

練習

本節的答案將會在下一節進行講解,務必把本節練習做完後看下一個講解內容。不要偷懶,實驗是學習本教程的捷徑。

  俗話說得好,光說不練假把式,如下是本節相關的練習。如果練習沒做好,就不要看下一節教程了,越到後面,不做練習的話容易夾生了,開始還明白,後來就真的一點都不明白了。本節練習不多但坑很多,需要花費大量的時間,請保質保量的完成。

1️⃣ 構造無參的調用門,實現提權後讀取高2G的地址並分析堆棧情況。
2️⃣ 構造有參的調用門,實現提權後正確依次取出參數並分析堆棧情況。
3️⃣ 構造調用門,提權後,實現「FQ」,即不按原函數地址返回。
4️⃣ 構造中斷門,實現提權後讀取高2G的地址並分析堆棧情況。
5️⃣ 構造陷阱門,實現提權後讀取高2G的地址並分析堆棧情況。

下一篇

  保護模式篇——任務段與任務門