Windows內核分析索引目錄:https://www.cnblogs.com/onetrainee/p/11675224.htmlhtml
APC的本質異步
1、對於線程關閉問題的啓發函數
線程,自己佔據CPU,對CPU有直接控制權。spa
這就存在一個問題,若是一個線程不想關閉本身,則外界是沒法干涉它的。線程
所以,線程須要本身殺死本身。code
可是線程自己是代碼,其並不知道什麼時候才須要殺死本身,這時須要咱們另外提供代碼,讓其殺死本身。orm
所以,線程的操做機制就是:定時檢查是否有另外執行的代碼,而後去執行。該代碼(函數),就是APC。htm
2、APC介紹blog
1. APC,即Asynchronous procedure call,異步程序調用。
索引
咱們理解「異步」這個詞,線程自己的代碼是「同步」的,在此以外能夠認爲是異步。
2. _KAPC_STATE 結構體(具體做用右側已經標記出來)
其在 _KTHREAD + 0x34 的位置。
kd> dt _KAPC_STATE
ntdll!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY // APC隊列,兩個雙向鏈表,分別指向用戶與內核結構的APC
+0x010 Process : Ptr32 _KPROCESS // 線程所屬的進程或者掛靠的進程
+0x014 KernelApcInProgress : UChar // 表示當前內核中的APC程序是否正在執行
+0x015 KernelApcPending : UChar // 表示APC隊列中是否有內核APC函數,若是有爲1,不然爲0.
+0x016 UserApcPending : UChar // 表示APC隊列中是否用用戶APC函數,若是有爲1,不然爲0.
3. _KAPC 結構體
kd> dt _KAPC
ntdll!_KAPC
+0x000 Type : Int2B
+0x002 Size : Int2B
+0x004 Spare0 : Uint4B
+0x008 Thread : Ptr32 _KTHREAD
+0x00c ApcListEntry : _LIST_ENTRY
+0x014 KernelRoutine : Ptr32 void
+0x018 RundownRoutine : Ptr32 void
+0x01c NormalRoutine : Ptr32 void
+0x020 NormalContext : Ptr32 Void
+0x024 SystemArgument1 : Ptr32 Void
+0x028 SystemArgument2 : Ptr32 Void
+0x02c ApcStateIndex : Char
+0x02d ApcMode : Char
+0x02e Inserted : UChar
在 _KAPC_STATE中前兩個雙向鏈表指向的就是一個個 _KAPC函數成員, 經過+0x01c NormalRoutine,能夠查看代碼(注意並非指向函數地址)
3、何時執行APC函數
1. 先介紹兩個函數
a. KiServiceExit函數:這個函數是系統調用、異常或中斷的必經之路。
b. KiDeliverApc函數: 負責執行APC函數。
2. 在執行 KiServiceExit函數時,其會從線程結構體中拿出 _KAPC_STATE.KernelApcPending是否爲零。
若是不爲零,則會調用KiDeliverApc去執行APC函數,當執行完一次後再跳轉回來進行遍歷。
4、KiServiceExit2部分源碼解讀:
在WindowsXp 專業版的 ntoskrnl.exe 中並未搜索到 KiServiceExit 函數;在有關快速調用的代碼中查看到 KiServiceExit2 (ntkrnlpa.exe)
.text:004667F0 cli .text:004667F1 test dword ptr [ebp+70h], 20000h .text:004667F8 jnz short loc_466800 ; 獲取Kthread .text:004667FA test byte ptr [ebp+6Ch], 1 .text:004667FE jz short loc_466834 .text:00466800 .text:00466800 loc_466800: ; CODE XREF: _KiServiceExit2+8↑j .text:00466800 ; _KiServiceExit2+41↓j .text:00466800 mov ebx, ds:0FFDFF124h ; 獲取Kthread .text:00466806 mov byte ptr [ebx+2Eh], 0 .text:0046680A cmp byte ptr [ebx+4Ah], 0 ; 判斷是否有用戶APC請求 .text:0046680E jz short loc_466834 ; 若是有用戶APC請求會走這裏 .text:00466810 mov ebx, ebp .text:00466812 mov ecx, 1 ; NewIrql .text:00466817 call ds:__imp_@KfRaiseIrql@4 ; KfRaiseIrql(x) .text:0046681D push eax .text:0046681E sti .text:0046681F push ebx .text:00466820 push 0 .text:00466822 push 1 .text:00466824 call _KiDeliverApc@12 ; 該函數實現對APC的處理 .text:00466829 pop ecx ; NewIrql .text:0046682A call ds:__imp_@KfLowerIrql@4 ; KfLowerIrql(x) .text:00466830 cli .text:00466831 jmp short loc_466800 ; 獲取Kthread