寫在前面
此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。
看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。
? 華麗的分割線 ?
APC 結構
上一篇我們簡單介紹了APC
的概念。有些人可能對為什麼執行緒不能被“殺掉”、“掛起”和“恢復”還是有些疑問,我們舉個極端的例子:如果不呼叫API,遮蔽中斷,並保證程式碼不出現異常,執行緒將永久佔用CPU,何談控制呢?所以說執行緒如果想“死”,一定是自己執行程式碼把自己殺死,不存在“他殺”這種情況。我們可以畫個簡單的示意圖:
下面我們來看看儲存APC
的結構體:
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
存在於KTHREAD
結構體的0x34
偏移的位置有一個ApcState
,如下所示:
kd> dt _KTHREAD
nt!_KTHREAD
...
+0x034 ApcState : _KAPC_STATE
...
我們看到這個存著一個結構體,如下所示:
kd> dt _KAPC_STATE
ntdll!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY
+0x010 Process : Ptr32 _KPROCESS
+0x014 KernelApcInProgress : UChar
+0x015 KernelApcPending : UChar
+0x016 UserApcPending : UChar
ApcListHead
第一個成員ApcListHead
是個雙向連結串列的陣列,一共有兩個成員,所謂的APC
就是插入到裡面的,給個示意圖如下:
ApcListHead
一個成員儲存著使用者APC
,使用者APC
的函式地址位於使用者空間,在使用者空間執行;另一個成員儲存著核心APC
,核心APC
函式地址位於核心空間,在核心空間執行。
Process
執行緒執行緒所屬或者所掛靠的程式,這個在逆向執行緒切換的時候我們就用過。具體細節都在程式執行緒篇的總結與提升講過,就不再贅述了。
KernelApcInProgress
指示核心APC
是否正在執行。
KernelApcPending
指示是否有正在等待執行的核心APC
。
UserApcPending
指示是否有正在等待執行的使用者APC
。
小結
上面的介紹僅僅是對APC
的初步講解,裡面所有的詳細細節將在後面的教程講解。後面會詳細介紹KAPC
這個結構體,並研究APC
是誰插入的、插入到哪裡、誰執行APC
和什麼時候執行APC
。本篇是對後面學習的鋪墊。
本節練習
本節的答案將會在下一節進行講解,務必把本節練習做完後看下一個講解內容。不要偷懶,實驗是學習本教程的捷徑。
俗話說得好,光說不練假把式,如下是本節相關的練習。如果練習沒做好,就不要看下一節教程了,越到後面,不做練習的話容易夾生了,開始還明白,後來就真的一點都不明白了。本節練習不多,請保質保量的完成。
1️⃣ 使用現成提供的API
,自己編寫程式碼向某個執行緒插入一個使用者APC
。
2️⃣ 分析TerminateThread
和SuspendThread
是如何實現的(從3環開始分析)。(要求:只需逆向分析到別的程式是如何控制目標程式行為,其他細節暫時不需分析)
下一篇
APC 篇——備用 APC 佇列