參考:張銀奎《軟件調試》第八章
Int 2e:
Windows將2e號向量專門用作系統調用,在啟動早起初始化中斷描述表時便注冊好了適合的服務例程。因此當NtDll中的NtReadFile發出int 2e指令后,cpu便會通過idt表找到KisystemService函數。因為KiSystemService函數是位於內核空間的,所以cpu在把執行權交給KiSystemService函數前,會做好從用戶態換到內核態的各種工作,包括:
1:權限檢查 即檢查源位置和目標位置所在的代碼段權限,核實是否可以轉移。
2:准備內核態使用的棧,為了保證內核安全,所有線程在內核態執行時都必須使用位於內核的內核棧(kernel stack),內核棧的大小一般為8KB或者12KB.
KiSystemService會根據服務ID從系統服務分發表(System Service Dispatch Table)中查找到需要的服務函數地址和參數描述,然后將參數從用戶態復制到該線程的內核棧中,最后KiSystemService調用內核中真正的NtReadFile()函數,執行讀文件操作,操作結束后會返回到KiSystemService(),KISystemService()會將操作結果復制回線程用戶態棧,最后通過IRET指令將執行權交回給NtDll.dll中的NtReadFile()函數,繼續執行INT 2E后面的那條指令。
快速系統調用(Sysenter)
准備工作:
1,在GDT中建立4個段描述符,分別用來描述供SYSENTER指令進入內核模式時使用的代碼段(CS)和棧段(SS),以及分別用來描述供SYSENTER指令進入內核模式時使用的代碼段(CS)和棧(SS).這四個描述符在GDT表中的排列應該嚴格按照以上順序,這樣只要指定一個段描述符的位置便能計算出其他的。
2,設置專門用於系統調用的MSR寄存器。
3,將一小段名為SystemCallStub的代碼復制到SharedUserData內存區,該內存區會被映射到每個Win32進程的進程空間中。這樣當應用程序每次進入系統調用時,NtDll中的殘根(stub)函數便調用這段SystemCallStub代碼。SystemCallStub中的內容會因系統硬件的不同而不同。
逆向調用(Ring0 -> Ring3)
首先內核代碼使用內核函數KiCallUserMode發起調用。接下來的執行過程與從系統調用返回(KiServiceExit)時類似,不過進入用戶態時執行的是NtDll中的KiUserCallbackDispatcher。而后KiUserCallbackDispatcher會調用內核希望調用的內核態函數。當用戶態的工作完成后,執行返回動作的函數會執行INT 2B指令,也就是觸發一個0x2B異常。這個異常的處理函數是內核態的KiCallbackReturn函數。於是,通過INT 2B異常,CPU便又跳回到內核態繼續執行了。
lkd>!idt 2b
Dumping IDT:
2b:8053d070 nt!KiCallbackReturn