APC 篇——總結與提升

寂靜的羽夏發表於2022-02-07

寫在前面

  此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我

你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。

  看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。


? 華麗的分割線 ?


小結

  本篇章我們從APC是什麼?是怎樣初始化的,是怎樣插入的,是怎樣執行的這幾個方面學習。總體來說APC基礎知識也就這些。下面先強調一些我介紹過但沒有指明出來的知識點。
  我提出幾個問題,如果你能回答上來,就說明仔細學了或者自己能夠挖掘我沒明說的東西。

  1. 當處理 APC 的時候,核心 APC 一定會處理嗎?使用者 APC 一定處理嗎?在那個函式點進行處理的?
  2. 掛 APC 的連結串列有兩個,存放於一個具有兩個成員連結串列陣列中,請問索引是0的是掛核心 APC 的還是使用者 APC 的,索引是1的同理?
  3. 應用層的使用者 APC 是怎樣到3環執行的?

  我給出如下答案:

  1. 核心 APC 一定會處理,使用者 APC 不一定會處理。處理的點是執行緒交換(只處理核心 APC)和 API呼叫與中斷異常退出函式處(都處理)。
  2. 索引是0的是掛的是核心 APC ,索引是1的是掛的使用者 APC。
  3. 通過用 CONTEXT 儲存原來的 TrapFrame ,然後修改返回到一個函式處理。

系統呼叫返回分析

  我們學系統呼叫的時候有一個坑還沒填完,那就是系統API是如何返回的,但是學完APC之後,這就沒什麼好講的了,自己繼續逆向就差不多了,但是還是逆向的結果還是有坑,下面我就把這個坑給你填上。現在我把如下我的反彙編結果給你:

loc_46663A:                             ; CODE XREF: _KiBBTUnexpectedRange+38↑j
                                        ; _KiBBTUnexpectedRange+43↑j
                mov     ecx, ds:0FFDFF124h
                mov     edx, [ebp+_KTRAP_FRAME._Edx]
                mov     [ecx+_KTHREAD.TrapFrame], edx
_KiFastCallEntry endp


_KiServiceExit  proc near               ; CODE XREF: KiCallUserMode(x,x)+E7↑j
                                        ; _KiSetLowWaitHighThread+7D↓j ...

; FUNCTION CHUNK AT .text:00466754 SIZE 00000088 BYTES

                cli
                test    [ebp+_KTRAP_FRAME.EFlags], 20000h
                jnz     short IsVM8086_0
                test    byte ptr [ebp+_KTRAP_FRAME.SegCs], 1
                jz      short IsRing0   ; 開始還原堆疊儲存的環境

IsVM8086_0:                             ; CODE XREF: _KiServiceExit+8↑j
                                        ; _KiServiceExit+63↓j
                mov     ebx, ds:0FFDFF124h
                mov     [ebx+_KTHREAD.Alerted], 0
                cmp     [ebx+_KTHREAD.ApcState.UserApcPending], 0
                jz      short IsRing0   ; 開始還原堆疊儲存的環境
                mov     ebx, ebp
                mov     [ebx+_KTHREAD.ApcState.Process], eax
                mov     dword ptr [ebx+_KTHREAD.IdleSwapBlock], 3Bh ; ';'
                mov     [ebx+_KTHREAD.ApcState.ApcListHead.Blink], 23h ; '#'
                mov     [ebx+_KTHREAD.ApcState.ApcListHead.Flink], 23h ; '#'
                mov     dword ptr [ebx+_KTHREAD.Iopl], 0
                mov     ecx, 1          ; NewIrql
                call    ds:__imp_@KfRaiseIrql@4 ; KfRaiseIrql(x)
                push    eax
                sti
                push    ebx             ; trapframe
                push    0               ; unknown
                push    1               ; CanUserAPC
                call    _KiDeliverApc@12 ; KiDeliverApc(x,x,x)
                pop     ecx             ; NewIrql
                call    ds:__imp_@KfLowerIrql@4 ; KfLowerIrql(x)
                mov     eax, [ebx+_KTHREAD.ApcState.Process]
                cli
                jmp     short IsVM8086_0
; ---------------------------------------------------------------------------
                align 10h

IsRing0:                                ; CODE XREF: _KiServiceExit+E↑j
                                        ; _KiServiceExit+1E↑j
                mov     edx, [esp+_KTRAP_FRAME.ExceptionList] ; 開始還原堆疊儲存的環境
                mov     ebx, large fs:_KPCR.DebugActive
                mov     large fs:_KPCR, edx
                mov     ecx, [esp+_KTRAP_FRAME.PreviousPreviousMode]
                mov     esi, large fs:_KPCR.PrcbData.CurrentThread
                mov     [esi+_KTHREAD.PreviousMode], cl
                test    ebx, 0FFh
                jnz     short IsDebugging

FillDebugInfoBack:                      ; CODE XREF: _KiServiceExit+11B↓j
                                        ; _KiServiceExit+14A↓j
                test    [esp+_KTRAP_FRAME.EFlags], 20000h
                jnz     IsVM8086_Exit
                test    word ptr [esp+_KTRAP_FRAME.SegCs], 1111111111111000b
                jz      loc_4667AA
                cmp     word ptr [esp+_KTRAP_FRAME.SegCs], 1Bh
                bt      word ptr [esp+_KTRAP_FRAME.SegCs], 0 ; CF = CS & 1
                cmc                     ; CF = !CF
                ja      IsRing3_Exit
                cmp     word ptr [ebp+_KTRAP_FRAME.SegCs], 8
                jz      short IsRing0_0

loc_466711:                             ; CODE XREF: _KiServiceExit+15C↓j
                lea     esp, [ebp+_KTRAP_FRAME.SegFs]
                pop     fs
                assume fs:nothing

IsRing0_0:                              ; CODE XREF: _KiServiceExit+C6↑j
                lea     esp, [ebp+_KTRAP_FRAME._Edi]
                pop     edi
                pop     esi
                pop     ebx
                pop     ebp
                cmp     word ptr [esp+8], 80h ; '€' ; cmp tf.cs , 80
                ja      loc_466FF0      ; 這個地方一定不會跳轉
                add     esp, 4
                test    dword ptr [esp+4], 1 ; test tf.cs,1,判斷是3環還是0環
_KiServiceExit  endp ; sp-analysis failed

  好,也就是這裡開始有坑,我繼續把反彙編結果列上:

_KiSystemCallExitBranch proc near       ; DATA XREF: KiDisableFastSyscallReturn()+9↑w
                                        ; KiEnableFastSyscallReturn():loc_425D2C↑r ...

; FUNCTION CHUNK AT .text:0046673C SIZE 00000001 BYTES

                jnz     short _KiSystemCallExit
                pop     edx
                pop     ecx
                popf
                jmp     edx
_KiSystemCallExitBranch endp

; ---------------------------------------------------------------------------
; START OF FUNCTION CHUNK FOR _KiSystemCallExit2
;   ADDITIONAL PARENT FUNCTION _KiSystemCallExitBranch

_KiSystemCallExit:                      ; CODE XREF: _KiSystemCallExitBranch↑j
                                        ; _KiSystemCallExit2+5↓j
                                        ; DATA XREF: ...
                iret
; END OF FUNCTION CHUNK FOR _KiSystemCallExit2

; =============== S U B R O U T I N E =======================================


_KiSystemCallExit2 proc near            ; DATA XREF: KiRestoreFastSyscallReturnState()+16↑o

arg_5           = byte ptr  9

; FUNCTION CHUNK AT .text:0046673C SIZE 00000001 BYTES

                test    byte ptr [esp+9], 1
                jnz     short _KiSystemCallExit
                pop     edx
                add     esp, 4
                and     byte ptr [esp+1], 0FDh
                popf
                pop     ecx
                sti
                sysexit
                iret
_KiSystemCallExit2 endp ; sp-analysis failed

  通過test判斷是否是3環,如果是的話跳走用iret返回,看似沒啥問題。但是如果我是快速呼叫進入的,這就有問題了,因為有對應的sysexit退出的。我們用WinDbg得出如下結果:

APC 篇——總結與提升

  是不是很奇怪?發現實際是通過KiSystemCallExit2退出的。這就是靜態分析和動態分析的不同之處。
  sysexit的為我們做哪些事情呢?我給說明一下:

  1. 將 IA32_SYSENTER_CS + 0x10 裝載到 cs 暫存器,將 edx 暫存器中的指令的指標裝載到 eip ;
  2. 將IA32_SYSENTER_CS + 0x18 裝載到 ss 暫存器中;
  3. 將 ecx 暫存器中的指標裝載到 esp 中,切換到3環許可權;

  到此係統呼叫怎麼進0環,做了那些事情,怎麼出0環,就結束了。

下一篇

  羽夏看Win系統核心——控制程式碼表篇

相關文章