APC的本質

 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
相關文章
相關標籤/搜索