Windows保護模式下的分頁機制(PART1:10-10-12模式)

1.概述

一個進程的虛擬地址空間是4GB,它的物理大小並不是真的4GB,如果真的是4GB,那運行中的所有進程都加起來佔用的空間是一個天文數字,事實上系統會對這4GB的地址空間進行轉換,然後映射到物理內存中。換句話說虛擬地址本身並不是真實存在的。
將虛擬地址轉換爲物理地址依賴的規則就是分頁機制。
物理地址也並不是內存條地址,物理地址需要再進行一層轉換才能找到真正的內存條地址。
我們只要會轉換到物理地址就夠用了。

1.1 邏輯地址

假如有指令:

MOV eax,dword ptr ds:[0x123456]

0x123456就是邏輯地址

1.2 線性地址

線性地址是對於虛擬空間來說的,還是上面的例子,ds.Base + 0x123456就是線性地址

1.3 物理地址

通過線性地址可以轉換爲物理地址。

1.4 分頁模式

分頁模式分爲10-10-12和2-9-9-12,前者最多可以支持4GB內存,後者在32位操作系統下可以支持16GB內存,64位操作系統下可以支持128G內存,後者也叫PAE分頁。
在Windows XP下,找到C盤下的boot.ini文件,將裏面的noexecute改爲execute即可將PAE分頁改爲10-10-12分頁。

2. 10-10-12模式

2.1 10-10-12模式概述

一個32位的虛擬地址會被解釋爲三個部分:

  1. 頁目錄索引(PDT,10位)
  2. 頁表索引(PTT,10位)
  3. 字節索引(12位)

系統中有一個CR3寄存器,它存儲了頁目錄索引的地址,頁目錄佔4KB,裏面的成員佔4個字節,因此有1024個成員,PDT裏面的成員被稱作PDE,PDE裏面保存的是一個32位的物理頁地址。

每個PDE又指向一個頁表索引,頁表索引裏面的成員被稱作PTE,PTE可以沒有物理頁(線性地址0一般就沒有),多個PTE也可以指向同一個物理頁。

10-10-12結構的第1個10是PDT中的索引,第2個10是PTT中的索引,最後的12是指物理頁上的偏移,所以簡單來說轉換成物理地址只需要將10-10-12的結構分別在PDT和PTT中找到對應的位置再加上物理頁的偏移就是最終的物理地址了。

在這裏插入圖片描述

(上面的圖其實不是很準確,不過爲了便於理解先以這個圖爲準吧,其實頁目錄表也是頁表,只不過它比較特殊,後面會介紹)

2.2 線性地址轉物理地址實驗

本實驗是在10-10-12模式下進行的,因此要將PAE關閉,PAE就是2-9-9-12模式,可以簡單地理解它爲增強型的分頁模式。

bcdedit /set pae forcedisable

重新打開PAE模式

bcdedit /set pae forceenable

1.找到數據在內存中的線性地址

  • 新建一個.txt文件,文件中 有字符串“hello,world”,並保持.txt文件處於打開狀態。
  • 使用CE附加到notepad.exe
  • 在Text欄中搜“hello,world”,找到可以找到多個跟它有關的地址,通過修改.txt文件中的字符串最終篩選出真正的內存地址0x000AA8A0,注意勾選上Unicode以及Value Type的類型,如下圖所示:

在這裏插入圖片描述
在這裏插入圖片描述

在進程中查找字符串還有一方法:

  • 在WinDbg中執行!process 0 0 notepad.exe
    在這裏插入圖片描述
  • 繼續執行 .process 9050c4e0,切換到記事本進程中
  • 執行s -u 0x00000000 L0x01000000 "hello,world"搜索內存中的字符串
  • 結果可能會有多個,但是隻有一個是正確的線性地址,可以使用du 0x28e090指令查看Unicode字符串。

2.分解線性地址
000AA8A0轉成二進制是32位,剛好可以按照10-10-12的格式分解成:

0000 0000 00
0010 1010 10
1000 1010 0000

3.根據分解後的線性地址找到物理地址

  • 首先在WinDbg中使用!process 0 0,在一堆進程中找到記事本進程,然後可以找到它的CR3寄存器0x146a0000,這個地址就是PDT的基址,它保存的是物理地址,所以下面的指令都是以!開頭的。
    在這裏插入圖片描述

  • 10-10-12格式的第1個10部分是0000 0000 00,這個值指向了它在PDT中的下標,所以使用!dd 146a0000+0找到對應的PDE。
    在這裏插入圖片描述

  • 10-10-12格式的第2個10部分是0010 1010 10,上面找到的PDE是0x14dc0067,因此通過使用!dd 14dc0000+aa*4找到對應的PTE,爲什麼不是14dc0067而是14dc0000?因爲PDE的最後12位是屬性,下面PTE的末尾12位同樣也是屬性,PDE的屬性&PTE屬性=物理頁的屬性。
    PDE和PTE的屬性如下:
    在這裏插入圖片描述
    在這裏插入圖片描述

P:1可用,0不可用
R/W:R/W=0只讀,R/W=1可讀可寫
U/S:0特權用戶,1普通用戶
A:是否被訪問,訪問置1
D:是否被寫過,寫過置1
P/S:PageSize,只對PDE有意義,1直接指向物理頁無PTE,低22位是頁內偏移,頁大小爲4MB,俗稱爲“大頁”
PAT:只對PTE有意義
G:CR3改變時,TLB會立即刷新,但不會刷新PDE/PTE的G位爲1的頁,一般高2G線性地址G爲1,因爲高2G是系統通用的,刷新影響效率

PWT、PCD、PAT在下篇介紹PAE時的時候會解釋。

0010 1010 10對應aa,乘以4的原因是每個元素佔4個字節
在這裏插入圖片描述

  • 找到的PTE是0x13fdd067,最後再加上物理頁偏移1000 1010 0000(轉換成十六進制是0x8a0)就是最終的物理地址了,使用!dd 13fdd000+8a0。
    在這裏插入圖片描述
  • 使用上面的指令還看不出是字符串“hello,world”,使用!db 13fdd000+8a0就可以看到字符串了

2.3 頁目錄表基址

系統要保證一個線性地址有效,必須提前將這個線性地址對應的PDE和PTE填充,那麼系統是怎麼填充PDE和PTE的呢?

PDE和PTE都是物理頁,我們在程序中是無法直接訪問物理頁的,我們要訪問任何東西必須要通過線性地址。

系統通過0xC0300000這個線性地址找到的物理頁就是頁目錄表,這個物理頁比較特殊,它是頁目錄表PDT,但是本身也是頁表,即PTT,換句話說,其實所謂的PDT表本身也是一個PTT表。0xC030000指向了PDT,所以系統可以通過它填充PDE,PTE是怎麼填充的呢?看下一節頁表基址

2.4 頁表基址

系統通過0xC0000000訪問第一個PTT,0xC0001000指向第二個PTT。
一個4GB的進程中只能有一個PDT(上面說過,其實PDT是一種特殊的PTT),但是有1024個PTT,每個PTT都佔4KB。

總結一下:

  1. 整個頁表佔4M地址空間,從0xC0000000到0xC03FFFFF,頁表有1024個成員,每個成員都是一個PTT,佔4KB。
  2. 在這1024個成員中,有一個PTT比較特殊,它就是PDT。

真正的頁表是這樣的:

在這裏插入圖片描述
訪問頁目錄表的公式:

0xC0300000 + PDI * 4(PDI是頁目錄表的索引)

訪問頁表的公式:

0xC0000000 + PDI * 4096 + PTI * 4(PDI * 4096是因爲每個PTT的大小是4K,PTI是頁表的索引,PTI*4,每個PTE佔4個字節)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章