寫在前面
此系列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統內核的復雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閑錢,可以打賞支持我的創作。如想轉載,請把我的轉載信息附在文章后面,並聲明我的個人信息和本人博客地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看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。
不可屏蔽中斷
什么是不可屏蔽中斷?CPU的EFLAG之中有一個位,它是IF位。如果它被置0。如果有可屏蔽中斷告訴CPU有中斷來了,你能先執行我的代碼呢?可是IF位是0,對不起,我聽不見。左耳朵進,右耳朵出。反之,我會處理。常見的不可屏蔽中斷有電腦長按關機、鍵盤輸入等等。當非可屏蔽中斷產生時,CPU在執行完當前指令后會里面進入中斷處理程序,非可屏蔽中斷不受那個位的影響,一旦發生,CPU必須處理。
那么CPU是如何處理我們的不可屏蔽中斷呢?我們先來看如下表格:
| (IDT表)中斷號 | NMI | 說明 |
|---|---|---|
| 0x2 | 不可屏蔽中斷 | 80x86 中固定為 0x2 |
如果處理不可屏蔽中斷,CPU會調用2號中斷。涉及的IDT表和中斷門的知識如果忘卻請查看前面的教程。
可屏蔽中斷
什么是可屏蔽中斷,我就不贅述了。在硬件級,可屏蔽中斷是由一塊專門的芯片來管理的,通常稱為中斷控制器。它負責分配中斷資源和管理各個中斷源發出的中斷請求.為了便於標識各個中斷請求,中斷管理器通常用IRQ,全稱為Interrupt Request,后面加上數字來表示不同的中斷。
在Windows中,怎么查看某個可屏蔽中斷的IRQ。可以在計算機管理中查看。我以鍵盤的輸入的IRQ為例,如下圖所示,可以看到它的IRQ為0x1。
那么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雖然被稱為“軟件中斷”,但其本質是異常,EFLAG的IF位對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的操作模式。控制寄存器有Cr0、Cr1、Cr2、Cr3、Cr4。Cr1被保留了,Cr3用於頁目錄表基址,其他的將繼續詳細講解。
Cr0
Cr0是一個十分重要的寄存器,可以說它是總開關的集合體。如下圖所示:
PE位是啟用保護模式(Protection Enable)標志。若PE = 1是開啟保護模式,反之為實地址模式。這個標志僅開啟段級保護,而並沒有啟用分頁機制。若要啟用分頁機制,那么PE和PG標志都要置位。
PG位是啟用分頁機制。在開啟這個標志之前必須已經或者同時開啟PE標志。PG = 0且PE = 0,處理器工作在實地址模式下。PG = 0且PE = 1,處理器工作在沒有開啟分頁機制的保護模式下。PG = 1且PE = 0,在PE沒有開啟的情況下無法開啟PG。PG = 1且PE = 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 = 0是10-10-12分頁。PSE是大頁是否開啟的總開關,如果置0,就算PDE中設置了大頁你也得是普通的頁。
小節
有些結構的位我並沒有詳細介紹,詳情請查看白皮書的控制寄存器的篇章,如下圖所示:
練習
本節的答案將會在下一節進行講解,務必把本節練習做完后看下一個講解內容。不要偷懶,實驗是學習本教程的捷徑。
俗話說得好,光說不練假把式,如下是本節相關的練習。如果練習沒做好,就不要看下一節教程了,越到后面,不做練習的話容易夾生了,開始還明白,后來就真的一點都不明白了。本節練習不多,請保質保量的完成。
1️⃣ 分析IDT表中0x2號中斷的執行流程。
2️⃣ 分析IDT表中0x8號中斷的執行流程。
