寫在前面
此系列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統內核的復雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閑錢,可以打賞支持我的創作。如想轉載,請把我的轉載信息附在文章后面,並聲明我的個人信息和本人博客地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看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
號中斷的執行流程。