描述:
在所有的暫存器中,只有Cr3儲存的是實體地址,其它暫存器存的都是線性地址
Cr3所儲存的實體地址指向了一個頁目錄表(PDT)
在Windows中,一個頁的大小通常爲4KB,即一個頁可以儲存1024個頁目錄表項(PDE)
下面 下麪是手冊中的描述在第3卷2.5 CONTROL REGISTERS
物理頁結構圖:
注意:但其實上面這種結構方式是錯誤的,這個以後再解釋,先這樣理解
描述:
頁目錄表(PDT)的每一項元素稱爲頁目錄表項(PDE)
每個頁目錄表項指向一個頁表(PTT)
每個頁表的大小爲4KB,即一個頁表可以儲存1024個頁表項(PTE)
描述:
頁表(PTT)的每一個元素稱爲頁表項(PTE)
頁表項(PTE)所指向的纔是真正的物理頁
特點:
物理頁的屬性
=PDE屬性
& PTE屬性
下面 下麪兩張圖看得更直觀一些
PDE(頁目錄表項)
PTE(頁表項)
P 位
– 存在位,表示當前條目是否在實體記憶體中
R/W 位
– 讀寫許可權位,爲 0 表示只讀,爲 1 表示可讀寫
U/S 位
– 也稱爲許可權位,頁或一組頁的特權級,爲 0 表示系統級,對應 CPL 0、1、2,爲 1 表示使用者級,對應 CPL 3。
PWT
– 頁表緩衝寫入機制 機製,爲 0 表示 write-back 模式,更新頁表緩衝區時,只標記爲已更新,不同步寫記憶體,只有被新進入的數據取代時才更新到記憶體,爲 1 表示 write-through 模式,更新頁表緩衝區時,同步寫記憶體,保證緩衝區與記憶體一致
PCD
– 是否拒絕被緩衝,爲 0 表示可以被緩衝,爲 1 表示不可以被緩衝
A 位
– 是否被存取,CPU 會在存取到頁面時將該位置 1,但不會清除,只有軟體可以將 A 位復位
D 位
– 是否被寫入,CPU 會在寫入頁面時將該位置 1,但不會清除,只有軟體可以將 D 位復位
PS
– PDE特有,頁大小位,爲 0 表示頁大小爲 4KB,且 PDE 指向頁表,爲 1 表示頁大小爲 4MB,且 PDE 指向 4MB 的整塊記憶體
PAT
– 奔騰3以後的 CPU 引入的頁屬性表標識位,爲 1 開啓頁屬性表後,通過一系列專用暫存器(MBR)爲每個頁提供了詳細的屬性設定
G 位
– 全域性位,也稱爲髒位,如果該位與 CR4 暫存器的 PGE 位同時被置爲 1,則該頁或頁目錄項將不會在 TLB 中被逐出
20bits 基地址
– PDE 與 PTE 的高 20bits 都是下級頁基址,無論是頁目錄表還是頁表還是在記憶體中的頁,他們都是 4KB 對齊的,也就是說他們的首地址低12位元均爲0,這樣,只需要通過 20bits 的基地址 * 12 就可以得到計算後的 32 位實體地址了
爲什麼要按10-10-12分頁:
證明PTE特徵
PTE可以指向一個物理頁,也可以不指向物理頁
可以發現有許多頁表項都爲0,沒有指向任何物理頁
實驗2
通過修改頁表使C語言能在0地址處讀寫
編譯如下程式碼
#include <iostream>
#include <windows.h>
int main(int argc, char* argv[])
{
int x = 1;
printf("x的地址:%x\n", &x);
getchar();
// 向0地址寫入數據
*(int*)0 = 123;
// 從0地址讀出數據
printf("0地址的數據:%x\n", *(int*)0);
getchar();
return 0;
}
執行
得到地址後中斷進入windbg
使用WinDbg將虛擬機器中斷,將變數x所在的物理頁掛載到線性地址0的PTE
拆分
0012ff3c
0000 0000 0001 0010 1111 1111 0011 1100
0000 0000 00 //0
01 0010 1111 //0x12F
1111 0011 1100 //0xF3C
修改
返回繼續執行
成功對0地址進行了讀寫
由此可見,0地址是否能讀寫和其他無關只與PDE 和 PTE有關
實驗3
通過修改物理頁屬性使字串常數可修改
編譯如下程式碼
#include <iostream>
#include <windows.h>
int main(int argc, char* argv[])
{
char *str = (char*)"Hello World";
printf("線性地址:%x\n", str);
getchar(); // 讓程式執行到這裏
//修改只讀變數
str[0] = 'M';
printf("修改後的值:%s\n", str);
getchar();
}
開啓x32dbg,我們可以看到
去記憶體佈局裡尋找字串位置,發現字串再.rdata段,而rdata段只有可讀屬性,並沒有可寫屬性
如果繼續執行到修改的時候,就會產生一個0xc0000005異常
我們再次執行程式
得到str地址,進入windbg中斷,得到Cr3
拆分地址
00413160
0000 0000 0100 0001 0011 0001 0110 0000
0000 0000 01 //1
00 0001 0011 //0x13
0001 0110 0000 //0x160
拆分
0x2277E005
0010 0010 0111 0111 1110 0000 0000 0101
從上面的圖片可知道PTE的第1位是R/W 位
,所以修改後爲
0x2277E007
0010 0010 0111 0111 1110 0000 0000 0111
然後繼續執行程式
成功修改str,所以可以看出記憶體的讀寫只與PDE 和 PTE有關於其他無關,這裏可以看出一個有意思的東西,就是PTE雖然有R/W位
但是沒有標記記憶體是否可以執行,其實對於CPU來說任何它能識別的code都是可以執行的,但是爲什麼再程式裡卻有些記憶體有執行屬性有些記憶體卻沒有,這裏有興趣的可以自行拓展一下
實驗4
通過修改物理頁屬性使普通使用者讀取高2G記憶體
編譯如下程式碼
#include <iostream>
#include <windows.h>
int main(int argc, char* argv[])
{
PDWORD p = (PDWORD)0x84279a7a;
getchar(); // 讓程式執行到這裏
printf("讀取高2G記憶體:%x \n", *p);
getchar();
return 0;
}
再次啓動程式,執行,中斷進入windbg,檢視cr3
拆分
0x84279a7a
1000 0100 0010 0111 1001 1010 0111 1010
1000 0100 00 //0x210
10 0111 1001 //0x279
1010 0111 1010 //0xA7A
然後修改PDE與PTE的U/S位
通過上圖我們可以知道U/S位
在第二位
拆分
0x001C3063
000111000011000001100011
0x4279125
0100001001111001000100100101
修改後
0x001C3067
000111000011000001100111
0x4279125
0100001001111001000100100101
返回繼續執行程式
成功讀取高2G記憶體,由此可見讀取的許可權只與PDE 和 PTE有關,與其他無關