寫在前面
此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。
看此教程之前,問幾個問題,基礎知識儲備好了嗎?保護模式篇學會了嗎?練習做完了嗎?沒有的話就不要繼續了。
? 華麗的分割線 ?
小結
本篇章我們從APC
是什麼?是怎樣初始化的,是怎樣插入的,是怎樣執行的這幾個方面學習。總體來說APC
基礎知識也就這些。下面先強調一些我介紹過但沒有指明出來的知識點。
我提出幾個問題,如果你能回答上來,就說明仔細學了或者自己能夠挖掘我沒明說的東西。
- 當處理 APC 的時候,核心 APC 一定會處理嗎?使用者 APC 一定處理嗎?在那個函式點進行處理的?
- 掛 APC 的連結串列有兩個,存放於一個具有兩個成員連結串列陣列中,請問索引是0的是掛核心 APC 的還是使用者 APC 的,索引是1的同理?
- 應用層的使用者 APC 是怎樣到3環執行的?
我給出如下答案:
- 核心 APC 一定會處理,使用者 APC 不一定會處理。處理的點是執行緒交換(只處理核心 APC)和 API呼叫與中斷異常退出函式處(都處理)。
- 索引是0的是掛的是核心 APC ,索引是1的是掛的使用者 APC。
- 通過用 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
得出如下結果:
是不是很奇怪?發現實際是通過KiSystemCallExit2
退出的。這就是靜態分析和動態分析的不同之處。
sysexit
的為我們做哪些事情呢?我給說明一下:
- 將 IA32_SYSENTER_CS + 0x10 裝載到 cs 暫存器,將 edx 暫存器中的指令的指標裝載到 eip ;
- 將IA32_SYSENTER_CS + 0x18 裝載到 ss 暫存器中;
- 將 ecx 暫存器中的指標裝載到 esp 中,切換到3環許可權;
到此係統呼叫怎麼進0環,做了那些事情,怎麼出0環,就結束了。