保護模式篇——中斷與異常和控制寄存器


寫在前面

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

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

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


🔒 華麗的分割線 🔒


練習及參考

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

1️⃣ 在10-10-12分頁模式下體會TLB的存在。要求:通過代碼掛物理頁,不能通過Windbg掛。原物理頁掛完寫入值讀取,然后把地址換物理頁繼續讀取,看看值是否發生變化。然后用INVLPG指令之后再看看值是否變化。

🔒 點擊查看答案 🔒


  此題目一看就需要一個調用門實現提權,如果有所忘卻請翻看前面的教程。

  首先構造一個調用門:eq 8003f098 0040EC0000081250,注意調用門和裸函數的地址一致。然后運行代碼,可以得到下面的結果:

刷新緩存

不刷新緩存

  通過這個實驗,看到TLB的作用了吧?


🔒 點擊查看代碼 🔒
#include "stdafx.h"
#include <iostream>
#include <windows.h>

int isinv=0;
int num1=0;
int num2=0;

void __declspec(naked) callgate()
{
    _asm
    {
        push 0x30;
        pop fs;

        pushad;
        pushfd;

        mov edi,0xC0000000;

        mov eax,0x10000;
        shr eax,10;
        add eax,edi;
        mov eax,dword ptr ds:[eax];
        mov dword ptr ds:[edi],eax;

        mov edx,dword ptr ds:[0];
        mov [num1],edx;

        mov eax,isinv ;
        test eax,eax;
        jz end;

        invlpg dword ptr ds:[0];

end:

        mov eax,0x20000;
        shr eax,10;
        add eax,edi;
        mov eax,dword ptr ds:[eax];
        mov dword ptr ds:[edi],eax;

        mov edx,dword ptr ds:[0];
        mov [num2],edx;

        popfd;
        popad;
        retf;
    }
}



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

    LPVOID page1 = VirtualAlloc((LPVOID)0x10000,0x1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    LPVOID page2 = VirtualAlloc((LPVOID)0x20000,0x1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE);

    if (!page1||!page2)
    {
        puts("分配內存失敗!!!");
        VirtualFree(page1,0,MEM_FREE);
        VirtualFree(page2,0,MEM_FREE);
        system("pause");
        return 0;
    }

    *(int*)page1 = 0x12345;
    *(int*)page2 = 0x67890;

    puts("是否清理緩存?");
    scanf("%d",&isinv);


    const char buffer[6]={0,0,0,0,0x9B,0};

    _asm
    {
        push fs;
        call fword ptr [buffer];
        pop fs;
    }

    printf("第一次掛頁的值:%x\n換頁后的值:%x\n",num1,num2);

    VirtualFree(page1,0,MEM_FREE);
    VirtualFree(page2,0,MEM_FREE);

    system("pause");
    return 0;
}

中斷

  中斷通常是由CPU外部的輸入輸出設備(硬件)所觸發的,供外部設備通知CPU有事情需要處理,因此又叫中斷請求,英文為Interrupt Request。中斷請求的目的是希望CPU暫時停止執行當前正在執行的程序,轉去執行中斷請求所對應的中斷處理例程,中斷處理程序由哪有IDT表決定。
  80x86有兩條中斷請求線:非屏蔽中斷線,NMI,全稱NonMaskable Interrupt和可屏蔽中斷線,INTR,全稱Interrupt Require

不可屏蔽中斷

  什么是不可屏蔽中斷CPUEFLAG之中有一個位,它是IF位。如果它被置0。如果有可屏蔽中斷告訴CPU有中斷來了,你能先執行我的代碼呢?可是IF位是0,對不起,我聽不見。左耳朵進,右耳朵出。反之,我會處理。常見的不可屏蔽中斷有電腦長按關機、鍵盤輸入等等。當非可屏蔽中斷產生時,CPU在執行完當前指令后會里面進入中斷處理程序,非可屏蔽中斷不受那個位的影響,一旦發生,CPU必須處理。

  那么CPU是如何處理我們的不可屏蔽中斷呢?我們先來看如下表格:

(IDT表)中斷號 NMI 說明
0x2 不可屏蔽中斷 80x86 中固定為 0x2

  如果處理不可屏蔽中斷,CPU會調用2號中斷。涉及的IDT表和中斷門的知識如果忘卻請查看前面的教程。

可屏蔽中斷

  什么是可屏蔽中斷,我就不贅述了。在硬件級,可屏蔽中斷是由一塊專門的芯片來管理的,通常稱為中斷控制器。它負責分配中斷資源和管理各個中斷源發出的中斷請求.為了便於標識各個中斷請求,中斷管理器通常用IRQ,全稱為Interrupt Request,后面加上數字來表示不同的中斷。
  在Windows中,怎么查看某個可屏蔽中斷的IRQ。可以在計算機管理中查看。我以鍵盤的輸入的IRQ為例,如下圖所示,可以看到它的IRQ0x1

  那么CPU是如何處理我們的可屏蔽中斷呢?我們先來看如下表格:

(IDT表)中斷號 IRQ 說明
0x30 IRQ0 時鍾中斷
0x31-0x3F IRQ1-IRQ15 其他硬件設備的中斷

  如果自己的程序執行時不希望CPU去處理這些中斷,可以用CLI指令清空EFLAG寄存器中的IF位,用STI指令設置EFLAG寄存器中的IF位。
  硬件中斷與IDT表中的對應關系並非固定不變的,可以參考白皮書的Chapter 10 Advanced Programmable Interrupt Controller(APIC)進行了解。

異常

  異常通常是CPU在執行指令時檢測到的某些錯誤,比如除0、訪問無效頁等。中斷與異常之間有一些相似之處,但它們是不一樣的:中斷來自於外部設備,是中斷源(比如鍵盤)發起的,CPU是被動的;而異常來自於CPU本身,是CPU主動產生的。INT N雖然被稱為“軟件中斷”,但其本質是異常,EFLAGIF位對INT N是無效。

異常處理

  無論是由硬件設備觸發的中斷請求還是由CPU產生的異常,處理程序都在IDT表。常見的異常處理程序如下表所示:

錯誤類型 (IDT表)中斷號
頁錯誤 0xE
段錯誤 0xD
除零錯誤 0x0
雙重錯誤 0x8

  有些異常比較特別,我們在來略微講解一下:

缺頁異常

  缺頁異常當PDE/PTE的P=0時或當PDE/PTE的屬性為只讀但程序試圖寫入的時就會觸發。一旦發生缺頁異常,CPU會執行IDT表中的0xE中斷處理程序,由操作系統來接管。
  你或許不清楚操作系統來接管。一個程序要是使用內存,必須有一個物理頁。但物理頁被掛上之后,這個物理頁也不一定是永久屬於你的。如果不經常用,操作系統看到后,就操作PDE/PTE的P位為0,把它放到稱之為虛擬內存交換文件之中。畢竟內存寶貴,不能養“閑人”嘛。當這個程序過了好長時間又想要它了,結果發現PDE/PTE不合法,觸發缺頁異常。然后操作系統過來,通過查看PDE/PTE看看是不是我自己裁的員(如下圖所示),如果是的話我再招一個物理頁,然后把數據寫進入,然后重新掛上,然后告訴CPU我處理妥善,繼續干活,然后程序就像沒啥事情一樣正常使用。可以說,缺頁異常無時無刻發生着。

  那么我們如何查看虛擬內存交換文件呢?如何設置它的大小呢?看下面的示意圖就知道了。

文件位置

大小設置

控制寄存器

  控制寄存器用於控制和確定CPU的操作模式。控制寄存器有Cr0Cr1Cr2Cr3Cr4Cr1被保留了,Cr3用於頁目錄表基址,其他的將繼續詳細講解。

Cr0

  Cr0是一個十分重要的寄存器,可以說它是總開關的集合體。如下圖所示:

  PE位是啟用保護模式(Protection Enable)標志。若PE = 1是開啟保護模式,反之為實地址模式。這個標志僅開啟段級保護,而並沒有啟用分頁機制。若要啟用分頁機制,那么PEPG標志都要置位。
  PG位是啟用分頁機制。在開啟這個標志之前必須已經或者同時開啟PE標志。PG = 0PE = 0,處理器工作在實地址模式下。PG = 0PE = 1,處理器工作在沒有開啟分頁機制的保護模式下。PG = 1PE = 0,在PE沒有開啟的情況下無法開啟PGPG = 1PE = 1,處理器工作在開啟了分頁機制的保護模式下。
  WP位對於Intel 80486或以上的CPU,是寫保護(Write Proctect)標志。當設置該標志時,處理器會禁止超級用戶程序(例如特權級0的程序)向用戶級只讀頁面執行寫操作;當CPL < 3的時候,如果WP = 0可以讀寫任意用戶級物理頁,只要線性地址有效。如果WP = 1可以讀取任意用戶級物理頁,但對於只讀的物理頁,則不能寫。

Cr2

  當CPU訪問某個無效頁面時,會產生缺頁異常,此時,CPU會將引起異常的線性地址存放在CR2中,如下圖所示:

Cr4

  Cr4的結構如下圖所示:

  VME用於虛擬8086模式。PAE用於確認是哪個分頁,PAE = 1,是2-9-9-12分頁,PAE = 010-10-12分頁。PSE是大頁是否開啟的總開關,如果置0,就算PDE中設置了大頁你也得是普通的頁。

小節

  有些結構的位我並沒有詳細介紹,詳情請查看白皮書的控制寄存器的篇章,如下圖所示:

練習

本節的答案將會在下一節進行講解,務必把本節練習做完后看下一個講解內容。不要偷懶,實驗是學習本教程的捷徑。

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

1️⃣ 分析IDT表中0x2號中斷的執行流程。
2️⃣ 分析IDT表中0x8號中斷的執行流程。

下一篇

  保護模式篇——總結與提升


免責聲明!

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



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