保護模式篇——PAE分頁

寂靜的羽夏發表於2021-10-22

寫在前面

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

你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。

  看此教程之前,問幾個問題,基礎知識儲備好了嗎?上一節教程學會了嗎?上一節課的練習做了嗎?沒有的話就不要繼續了。


? 華麗的分割線 ?


練習及參考

本次答案均為參考,可以與我的答案不一致,但必須成功通過。

1️⃣ 拆兩個程式的4GB物理頁。

? 點選檢視答案 ?


  自己拆吧,也不是真讓你把所有的都拆了。這玩意建議還是寫個驅動來進行拆解。不過我的教程沒寫,之後才會講解。故拆幾個比較有特點的就行了,之後學了驅動後可以回來再把這道題給完整地做了。

  首先討論我們怎麼拆,一個物理頁有4KB的大小,如果線性地址在同一個物理頁,那麼它的高20位一定是相同的。故如果完整的拆完,我們需要拆0x1000 * n線性地址。怎麼拆已經給你說明白了。

  需要拆解的線性地址型別:0x0 - 0x10000 和 0x10000 - 0x7FFFFFFF 和 高2G三部分,每個部分拆解2-3個即可。這裡就不拆了。


2️⃣ 定義一個只讀型別的變數,再另一個線性地址指向相同的物理頁,通過修改PDE/PTE屬性,實現可寫。

? 點選檢視答案 ?


  這題目說實話還是有一些坑,主要是獨自編寫驗證程式碼上。可以點選下方“檢視程式碼”進行檢視。

  首先執行我們的程式碼,報記憶體訪問錯誤,這個是正常現象,因為它在所謂的常量區。

保護模式篇——PAE分頁

  先讓它跑起來,看到常量所在的線性地址,如下圖所示:

保護模式篇——PAE分頁

  然後我們拆分線性地址,為了圖省事,可以用計算器或者自己寫個工具。如下圖所示:

保護模式篇——PAE分頁

  然後轉到WinDbg中,找到它的程式結構體,找到CR3

保護模式篇——PAE分頁

  根據10-10-12分頁結構進行檢視,最終看到如下圖所示結果:

保護模式篇——PAE分頁

  最後發現是因PTE屬性限制導致資料無法修改,故用!ed 41795088 410e4027修改它,使它具有可寫屬性。

  然後我們就能成功的訪問常量只讀地址了:

保護模式篇——PAE分頁


? 點選檢視程式碼 ?
#include "stdafx.h"

const int test = 5;

int main(int argc, char* argv[])
{
    int* tmp=(int*)&test;
    printf("%p\n",tmp);
    *tmp=8;
    printf("%d\n",*tmp);
    return 0;
}

3️⃣ 分析0x8043F00C線性地址的PDE屬性。

? 點選檢視答案 ?


  在虛擬機器開啟一個notepad程式,然後轉到Windbg暫停虛擬機器找到它的CR3(我的是0x1b262000)。然後輸入!dd 1b262000+0x201*4找到該線性地址對應的PDE,值為004001e3,故該PDE為一個大頁,有效可寫,併為僅超級使用者可用,是全域性的,且被訪問過和寫過。


4️⃣ 修改一個高2G線性地址的PDE/PTE屬性,實現Ring3可讀。

? 點選檢視答案 ?


  既然是做了第三題了,那我們直接拿第三題給的線性地址開刀好了。首先必須在合適的地方下斷點,執行我們的程式碼,獲取的CR34a818000,故!dd 4a818000+0x201*4得到PDE後值為004001e3,但為了更好的實驗,故把它指向的物理頁的偏移的值改為0x1234。那麼如何改呢?這個是個大頁,故後22位直接是物理頁的偏移。故!ed 0043F00C 1234即可。

  然後我們繼續進行檢驗,如下圖所示:

保護模式篇——PAE分頁

  4660就是16進位制的0x1234,故實驗成功。


? 點選檢視程式碼 ?
#include "stdafx.h"
#include <iostream>

int main(int argc, char* argv[])
{

    int* test=(int*)0x8043F00C;

    printf("讀取到值了:%d",*test);
    system("pause");
    return 0;
}

5️⃣在0線性地址掛上物理頁並執行shellcode呼叫MessageBox

? 點選檢視答案 ?


  這道題還是有一點坑,不過坑不太深,自己能跳出來。程式碼我提供了且有註釋,自己開啟看看。

  通過檢視0 地址不能訪問是因為沒有正確的PTE,如果掛上正確的PTE,那麼這個地址就可以訪問了。那我們開始用程式碼進行試驗。首先在合適的地方下斷點,執行我們的程式碼,獲取的CR33daa3000,且申請的一個物理頁的地址是0x3D0000。故按照分頁模式找到物理頁的PTE,值為3db54067。然後把它填到0 地址的地方。

  然後我們繼續進行檢驗,如下圖所示:

保護模式篇——PAE分頁

  我們成功彈出了一個資訊框,故實驗成功。


? 點選檢視程式碼 ?
#include "stdafx.h"
#include <iostream>
#include <windows.h>

char shellcode[]={
    0x6A,0x00,        //PUSH 0
    0x6A,0x00,        //PUSH 0
    0x6A, 0x00 ,      //PUSH 0
    0x6A ,0x00 ,      //PUSH 0
    0xB8, 0x00 ,0x00 ,0x00, 0x00 ,    //MOV EAX,0000
    0xFF, 0xD0 ,        //CALL EAX
    0xC3    //RET
};

int main(int argc, char* argv[])
{

    int msgbox=(int)MessageBoxW;
    *(int*)&shellcode[9]=msgbox;

    //我們並不能直接把 shellcode 的 PTE 掛到 0 地址上。
    //因為還有偏移,需要單獨申請一個獨立的物理頁。
    LPVOID scaddr = VirtualAlloc(NULL,1024,MEM_COMMIT,PAGE_READWRITE);
    memcpy(scaddr,shellcode,sizeof(shellcode));

    printf("Shellcode物理頁:%p\n",scaddr);

    //下斷點,掛物理頁
    _asm
    {
        mov eax,0;
        call eax;
    }

    system("pause");
    return 0;
}

6️⃣ 逆向分析MmIsAddressValid函式。

? 點選檢視答案 ?


  本題目主要目的是為了如何查詢PDEPTE。逆向參考結果如下:

保護模式篇——PAE分頁


PAE 分頁

  PAE分頁是啥,其實他就是2-9-9-12分頁的英文縮寫。為什麼要有2-9-9-12分頁,其實還是物理頁不夠用了,需要擴充套件。但想要足夠的物理頁,位數在那裡,你想大也大不了。那麼我就需要擴充套件物理頁地址的位數,於是乎2-9-9-12分頁誕生了,它整體分頁的結構如下:

保護模式篇——PAE分頁

  與10-10-12分頁不同的地方就是,多了一層名為頁目錄指標表的東西,英文縮寫為PDPTT。每個PDEPTE被擴充套件為8個位元組,實體地址描述的位數擴充套件為24位,故可以描述更多的物理頁,但個數減半,變成了512個。下面詳細檢視它們的結構。
  首先看PDPTT的結構。由2-9-9-122可知第一部分由兩位二進位制組成,那麼最多有4種結果。也就是為什麼有五個成員,它的結構如下圖所示:

保護模式篇——PAE分頁

  然後是`PDE`,既然學過了`10-10-12`分頁,直接看下面的結構示意圖吧:

保護模式篇——PAE分頁

非大頁

保護模式篇——PAE分頁

大頁

  然後是PTE,同理不多說了:

保護模式篇——PAE分頁

  我們之前做一道作業題目知道,我寫的shellcode寫到一個頁上,它並沒有執行許可權,但它不是程式碼,仍然可以被我執行。為了彌補這個漏洞,Intel給我們補了一個硬體層面上的漏洞,它是一個位,處於PDEPTE的最高位,如下圖所示:

保護模式篇——PAE分頁

  如果最高位是1,說明被保護。如果這個是資料,且這個X位被置為1,則會被報出異常不能執行。反之,和正常的10-10-12分頁沒什麼兩樣。
  一個程式的線性地址仍是4GB的線性空間,有再多的物理頁有啥用呢?在10-10-12分頁下,假設程式一啟動,就把所有的物理頁都掛上,且沒有任何交換。那麼只能啟動一個;如果在2-9-9-12分頁下,同樣的情況,它可以啟動4個程式。這個就是2-9-9-12分頁的意義。
  Windows也提供了基於硬體層面X位的保護,如下圖所示:

保護模式篇——PAE分頁

  可以看出你自己寫的普通程式壓根和這個位無緣,但是還以啟用的。但是有些開啟之後發現提出不支援基於硬體的保護,那是因為虛擬機器沒開這個選項,如下圖所示,轉中如下圖選項即可:

保護模式篇——PAE分頁

練習

由於本節內容和10-10-12分頁相似,答案不會提供,自己實現,務必把本節練習做完後看下一個講解內容。不要偷懶,實驗是學習本教程的捷徑。

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

1️⃣ 定義一個只讀型別的變數,再另一個線性地址指向相同的物理頁,通過修改PDE/PTE屬性,實現可寫。
2️⃣ 自己實驗有和沒有DEP保護的程式,看看效果。(本題將在下一篇提供參考)
3️⃣ 修改一個高2G線性地址的PDE/PTE屬性,實現Ring3可讀。
4️⃣ 在0線性地址掛上物理頁並執行shellcode呼叫MessageBox
5️⃣ 逆向分析MmIsAddressValid函式。

下一篇

  保護模式篇——TLB與CPU快取

相關文章