15_TLB中的G屬性

》 TLB 是爲了增長訪問內存的效率c++

即 若是 是 29 9 12 分頁 請求數據 可能須要訪問 4次內存;爲了解決這個問題;出現了 TLB (虛擬地址到物理地址的轉換關係),若是目標地址在TLB緩存中,那麼直接從TLB 取出 物理地址;web

》 這個實驗作起來很麻煩,由於:緩存

  • TLB 是CPU 內部的,無法經過彙編指令訪問TLB;ide

  • 調試器,也沒有辦法知道 TLB 中有哪些項函數

  • 只有經過實驗現象 結果,來證實其存在。測試

內存訪問的步驟:

1570534108746

注意 TLB 產生異常 3; 不會回到 2;而是產生 0x0E 異常。spa

1570534475405

1 簡單實驗查看 快表 帶來的影響:

圖解:

1570535549903

代碼:

// 7_TLB_test.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//

#include "pch.h"
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>

#define PTE(x) ( (DWORD*)(0xc0000000 + ((x >> 12) << 3)))
#define PDE(X) ( (DWORD*)(0xc0600000 + ((x >> 21) << 3)))
DWORD g_out;
#pragma section("data seg", read, write)
_declspec(allocate("data seg"))DWORD pagel[1024]; //41d000
_declspec(allocate("data seg"))DWORD page2[1024]; //41c000
//0x401000
void _declspec(naked) IdtEntry()
{
__asm mov eax, ds: [0x405000]
//確保虛擬地址在TLB中
PTE(0x41c000)[0] = PTE(0x41d000)[0];
PTE(0x41c000)[1] = PTE(0x41d000)[1];

g_out = page2[0];
__asm {
iretd
}
}
void _declspec(naked) go() {
{
pagel[0] = 1; //確保物理頁存在
page2[0] = 2;

}
__asm int 0x20
__asm ret
}
//eq 8003f500 0040ee00~ 00081000
void main()
{
if ((DWORD)IdtEntry != 0x401040)
{
printf("wrong addr: %p", IdtEntry);
exit(-1);
}
go();
printf("%d\n", g_out);
system("pause");
}

效果:

1570536186160

藍屏:

太快了 。。。 截圖不到。。。額3d

解決藍屏:

因爲 是對同一個物理頁釋放了兩次形成了藍屏;調試

因此 保存好 原來的pte值;orm

因此在 中斷返回以前,將那個 虛擬頁的 pte 修改回來便可

刷新TLB,解決 結果和咱們預期差別

1570536902006

這個cr3 的切換 會致使 沒有 g 屬性的tlb 失效。即清除;

緣由很簡單,由於 這個 cr3都切換了 tlb 中的虛擬地址沒有g屬性的;已經在當前cr3 中沒有意義了。雖然 這裏切換的是本身的,可是同樣能夠達到效果。

改後的代碼:

// 7_TLB_test.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//

#include "pch.h"
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>

#define PTE(x) ( (DWORD*)(0xc0000000 + ((x >> 12) << 3)))
#define PDE(X) ( (DWORD*)(0xc0600000 + ((x >> 21) << 3)))
DWORD g_out;
DWORD g_OldPte[2];
#pragma section("data seg", read, write)
_declspec(allocate("data seg"))DWORD pagel[1024]; //41d000
_declspec(allocate("data seg"))DWORD page2[1024]; //41c000
//0x401000
void _declspec(naked) IdtEntry()
{
// 確保虛擬地址在TLB中
__asm mov eax, ds: [0x405000];

// 保存舊的pte ,以用來恢復pte 解決不藍屏
g_OldPte[0] = PTE(0x41c000)[0];
g_OldPte[1] = PTE(0x41c000)[1];

PTE(0x41c000)[0] = PTE(0x41d000)[0];
PTE(0x41c000)[1] = PTE(0x41d000)[1];

__asm{
mov eax,cr3
mov cr3,eax
}
g_out = page2[0];

// 恢復到原來的pte
PTE(0x41c000)[0]=g_OldPte[0];
PTE(0x41c000)[1]=g_OldPte[1];
__asm {
iretd
}
}
void _declspec(naked) go() {
{
pagel[0] = 1; //確保物理頁存在
page2[0] = 2;

}
__asm int 0x20
__asm ret
}
//eq 8003f500 0040ee00~ 00081000
void main()
{
if ((DWORD)IdtEntry != 0x401040)
{
printf("wrong addr: %p", IdtEntry);
exit(-1);
}
go();
printf("%d\n", g_out);
system("pause");
}

以後的效果:

1570537359279

數據正確了 ,並且沒有藍屏

2 有G屬性的TLB 項

查看 pte 有無G位:

1570537552949

通常3環的 pte 沒有G位。內核屬性爲 G位這樣TLB 就不會被刷新出去。

設置咱們3環的頁G位有效--不被刷出TLB:

1570538464730

代碼:

// 7_TLB_test.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//

#include "pch.h"
#include<stdio.h>
#include<stdlib.h>
#include<Windows.h>

#define PTE(x) ( (DWORD*)(0xc0000000 + ((x >> 12) << 3)))
#define PDE(X) ( (DWORD*)(0xc0600000 + ((x >> 21) << 3)))
DWORD g_out;
DWORD g_OldPte[2];
#pragma section("data seg", read, write)
_declspec(allocate("data seg"))DWORD pagel[1024]; //41d000
_declspec(allocate("data seg"))DWORD page2[1024]; //41c000
//0x401000
void _declspec(naked) IdtEntry()
{
// 確保虛擬地址在TLB中
__asm mov eax, ds: [0x405000];

// 保存舊的pte ,以用來恢復pte 解決不藍屏
g_OldPte[0] = PTE(0x41c000)[0];
g_OldPte[1] = PTE(0x41c000)[1];

PTE(0x41c000)[0] = PTE(0x41d000)[0]|0x100;// 設置G位
PTE(0x41c000)[1] = PTE(0x41d000)[1];

__asm{
mov eax,cr3
mov cr3,eax
}
// 調用調用;確保在 TL B 中
__asm mov eax, ds:[0x41c000];

// 恢復到原來的pte 
//---- 這樣
// 按道理 後面一旦後面刷新 TLB 將 普通 TLB 刷新出去,
// 那麼 g_out = page2[0] 的值就 應該是 正常 的原 pte 對應的數據 -- 2。
PTE(0x41c000)[0] = g_OldPte[0];
PTE(0x41c000)[1] = g_OldPte[1];

__asm
{
mov eax,cr3
mov cr3,eax
}


g_out = page2[0]; // 講道理 在非G位下 應該是2(原PTE解析出的) -- 可是這裏咱們設置了PTE 的G位,
  // so 這裏應該是 TLB快表中 對應的 1;


__asm {
iretd
}
}
void _declspec(naked) go() {
{
pagel[0] = 1; //確保物理頁存在
page2[0] = 2;

}
__asm int 0x20
__asm ret
}
//eq 8003f500 0040ee00~ 00081000
void main()
{
if ((DWORD)IdtEntry != 0x401040)
{
printf("wrong addr: %p", IdtEntry);
exit(-1);
}
go();
printf("%d\n", g_out);
system("pause");
}

測試結果:

果真 G位的TLB 不會被 刷新出去:

1570538889473

固然 還能夠強行更新 TLB 項,無視G位:

__asm invlpg ds: [0x41c000] //強行更新TLB項,無視G位

1570539042616

這時候 TLB 中就沒有以前那個虛擬地址的TLB 虛擬項了。這時候就是正常的值了。

利用:inline Hook

前面 咱們 hook 系統函數 systemfastcallentry 是修改 cr0 無視 WP位 頁寫保護。

如今咱們能夠使用 直接 修改 systemfastcallentry( ) 所在的pte;爲 可寫的狀態;而且使用強行刷新TLB 將那個pte在TLB中的數據刷新

PTE(XXX)[0] =...;
PTE(XXX)[1] = ..;
__asm invlpg ds: [XXX] //強行更新TLB項,無視G位
相關文章
相關標籤/搜索