TLB機制


 Windows內核分析索引目錄:https://www.cnblogs.com/onetrainee/p/11675224.html

TLB

1. CPU尋址模式

2. TLB

3. 緩存

4. shadowwalk技術

5.TLB感知實驗

6.全局頁

 

1. CPU尋址模式

  之前在分頁時介紹過這張圖,里面分析過其對應的尋址模式:

  1)CPU先嘗試從TLB找到物理地址;

  2)如果TLB中不存在對應的物理地址,則手動計算出物理地址;

  3)之后再從緩存中嘗試獲取物理地址對應的數據;

  4)如果緩存中沒有,則去物理內存中找到相應地址來讀取對應的數據。

  

 

 

2. TLB

  TLB與緩存要區分清楚,TLB是通過線性地址找物理地址的,緩存是通過物理地址找數據的。

  TLB中以頁為單位來存儲的,緩存直接按照字節來進行存儲的,要區分他倆之間的關系,不要混為一談。

 

3. 緩存

  以三級緩存為例,當一級緩存滿了之后會往二級緩存中存儲,二級滿了會往三級緩存中存儲,三級緩存定期備份到物理內存中。

  

 

4. shadowwalk技術

  有很多游戲等會存在CRC校驗,其讀取的是數據TLB,但執行的是指令TLB,因此我們保留數據TLB,修改指令TLB,這樣就實現了過CRC校驗。

  shadowwalk等工具就是利用TLB的兩張表分離實現過CRC校驗的。

 

5.TLB感知實驗

  1)實驗大體步驟

    ① 准備兩個地址,在不同的頁上;

    ② 向零地址掛上TLB,

    ③ 讀取數據,加載到TLB中;

    ④ 再向0地址掛另一個物理頁的PTE;

    ⑤ 我再讀零地址,如果讀出來的還是原來的數據,則證明存在TLB。

  2)實驗代碼:

    如下,我們利用windbg創建調用門:eq 8003f048 0040ec00`00081005

// 123.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>

char* p = NULL;
char* p1 = NULL;
int temp = 0;
int temp1 = 0;

// eq 8003f048 0040ec00`00081005
void __declspec(naked)test(){
    _asm{

        pushfd;
        pushad;
        push fs;
        mov ax,0x30;
        mov fs,ax;
        // 掛p位的
        mov eax,dword ptr ds:[p]; // 取出p的地址
        shr eax,9; // 由移9位
        and eax,0x7FFFF8; // 
        add eax,0xc0000000;
        // 取出pte    
        mov ebx,dword ptr [eax]; 
        mov ecx,[eax+4];
        // 往0地址掛
        mov dword ptr ds:[0xc0000000],ebx;
        mov dword ptr ds:[0xc0000004],ecx;
        // 加載到tlb中
        mov eax, dword ptr ds:[0];
        //int 3;
        //取出值
        mov temp,eax;

        /*---------------*/

        // 掛p1的
        mov eax,dword ptr[p1]; // 取出p1的地址
        shr eax,9; // 由移9位
        and eax,0x7FFFF8; // 
        add eax,0xc0000000;
        // 取出pte
        mov ebx,dword ptr [eax]; 
        mov ecx,[eax+4];
        // 往0地址掛
        mov dword ptr ds:[0xc0000000],ebx;
        mov dword ptr ds:[0xc0000004],ecx;
        // 加載到tlb中
        mov eax, dword ptr ds:[0];
        // 取出值
        mov temp1,eax;
        //int 3;

        pop fs;
        popad;
        popfd;
        retf;
    }
}

int main(int argc, char* argv[])
{
    // 分配內存
    p = (char*)VirtualAlloc(NULL,0x1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    p1 = (char*)VirtualAlloc(NULL,0x1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    char buf[]={0,0,0,0,0x48,0};
    printf("%x,p1=%x,p=%x\r\n",test,p1,p);
    system("pause");
    temp = 100;
    temp1 = 200;
    *(int*)p = 0x1000;
    *(int*)p1 = 0x2000;

    _asm{
        call fword ptr buf;
    }
    printf("%x,%x\t\n",temp,temp1);
    printf("hello world!\r\n");
    system("pause");
    return 0;
}

  3)實驗結果:

    實驗結果如下,可以發現第二次雖然我們在0地址處手工掛靠了物理頁,但其因為第一次保存在TLB緩存當中,當第二次再開始讀取的時候,通過TLB獲取第一個PTE的線性地址,因此讀到的是第一個。

    因此通過這個實驗你就可以感知到TLB緩存的存在,下面我們學習如何刷新TLB緩存。

    

   4)TLB刷新

    如何刷新TLB呢?其如果刷新cr3,則就刷新TLB,因此我們如果想刷新,寫上 mov eax,cr3; mov cr3,eax即可。

    

 

6.全局頁

  1)將其修改為全局頁

    如果將頁置為全局頁,則需要修改PTE的第8位,G位,表示全局頁。

    

    因此我們在寫入PTE前,使用 or ebx,0x100將該位置1,哪怕再刷新CR3也沒有效果。

    

  2)通過cr4來禁止修改全局頁

    cr4存在一個PGE位,當該位為1,則允許使用全局頁,現在我們將該位關掉。

    

    因為在vc6.0環境下不支持cr4的修改,因此我們借助IDA來直接硬編碼:

// cr4 PGE位關閉全局頁

__emit 0x0f; // mov eax,cr4;
__emit 0x20;
__emit 0xE0;

mov ecx,0x80;
not ecx;
and eax,ecx;

__emit 0x0f; // mov cr4,eax;
__emit 0x22;
__emit 0xe0; 

  之后我們發現及時設置全局頁,其也是無效的,會被置換出來

  

  其實CPU中存在一個強制刷新地址的指令,哪怕是全局頁表也會被刷新

  invlpg dword ptr ds:[0],使用該條指令則會強制刷新緩存

 

全部試驗代碼如下:

// 123.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>

char* p = NULL;
char* p1 = NULL;
int temp = 0;
int temp1 = 0;

// eq 8003f048 0040ec00`00081005
void __declspec(naked)test(){
    _asm{

        pushfd;
        pushad;
        push fs;
        mov ax,0x30;
        mov fs,ax;
        // 掛p位的
        mov eax,dword ptr ds:[p]; // 取出p的地址
        shr eax,9; // 由移9位
        and eax,0x7FFFF8; // 
        add eax,0xc0000000;
        // 取出pte    
        mov ebx,dword ptr [eax]; 
        mov ecx,[eax+4];
        // 往0地址掛
        xor ebx,0x100;
        mov dword ptr ds:[0xc0000000],ebx;
        mov dword ptr ds:[0xc0000004],ecx;
        // 加載到tlb中
        mov eax, dword ptr ds:[0];
        //int 3;
        //取出值
        mov temp,eax;

        mov eax,cr3;
        mov cr3,eax;
        /*---------------*/

        // cr4 PGE位關閉全局頁
        /*
        __emit 0x0f; // mov eax,cr4;
        __emit 0x20;
        __emit 0xE0;

        mov ecx,0x80;
        not ecx;
        and eax,ecx;

        __emit 0x0f; // mov cr4,eax;
        __emit 0x22;
        __emit 0xe0;
        */


        // 掛p1的
        mov eax,dword ptr[p1]; // 取出p1的地址
        shr eax,9; // 由移9位
        and eax,0x7FFFF8; // 
        add eax,0xc0000000;
        // 取出pte
        mov ebx,dword ptr [eax]; 
        mov ecx,[eax+4];
        // 往0地址掛
        mov dword ptr ds:[0xc0000000],ebx;
        mov dword ptr ds:[0xc0000004],ecx;
        // 加載到tlb中
        invlpg dword ptr ds:[0];
        mov eax, dword ptr ds:[0];
        // 取出值
        mov temp1,eax;
        //int 3;
        pop fs;
        popad;
        popfd;
        retf;
    }
}

int main(int argc, char* argv[])
{
    // 分配內存
    p = (char*)VirtualAlloc(NULL,0x1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    p1 = (char*)VirtualAlloc(NULL,0x1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    char buf[]={0,0,0,0,0x48,0};
    printf("%x,p1=%x,p=%x\r\n",test,p1,p);
    system("pause");
    temp = 100;
    temp1 = 200;
    *(int*)p = 0x1000;
    *(int*)p1 = 0x2000;

    _asm{
        call fword ptr buf;
    }
    printf("%x,%x\t\n",temp,temp1);
    printf("hello world!\r\n");
    system("pause");
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM