System Calling Mechanism under Win32

System Calling Mechanism under Win32

節譯自: Reversing , Secrets of Reverse Engineering by Eldad Eilam

在NT 的保護模式下, 當user mode的程式需要呼叫kernel function時, 使用一種特殊的機制將CPU目前的權限模式(3->0)提升, 並且呼叫一個特殊的dispatch routine, 這種機制在Win2k和Win2k之後的NT版本, 方式是不一樣的.

在Win2k中, 系統會發出0x2E的中斷藉以進入kernel模式, 這是一個典型的Win2k system call:

ntdll!ZwReadFile:
77f8c552 mov eax, 0xa1
77f8c557 lea edx, [esp+0x4]
77f8c55b int 2e
77f8c55d ret 0x24

EAX 載入的是服務編號, EDX則是指向被呼叫的kernel-mode function所需要的第一個參數的位址, 當int 2e被執行時, CPU就會從IDT找出正確的interrupt handler, 在這裡, 2e在IDT裡的entry指向系統內部的NTOSKRNL函式, 叫做KiSystemService. 如上所說的, 這是一個系統服務的dispatcher, 它會檢查服務編號和堆疊指標是否合法, 然後才會進入使用者所呼叫的系統服務. 至於真正的呼叫則是透過叫做KiServiceTable的陣列, 這個陣列裡包含了各式服務的函式指標, 而KiSystemService只是用EAX裡的值來當做index來呼叫KiServiceTable中的某個函式.

最近版本的NT系統則不是透過interrupt的機制, 而是使用一種特殊的指令SYSENTER. 基本上SYSENTER可以想成是一種高效率的kernel-mode開關指令, 當SYSENTER被執行時, CPU會自動從一個叫做SYSENTER_EIP_MSR的MSR中載入下一個指令的IP位址(不用說這個MSR的內容只能在kernel-mode下存取). 接下來發生的事就很像之前的Win2k所做的了, 這是一個典型的例子:

ntdll!ZwReadFile:
77f4302f mov eax, 0xbf
77f43034 mov edx, 0x7ffe0300
77f43039 call edx
77f4303b ret 0x24

上面的EDX存的是一個叫SharedUserData!SystemCallStub的函式位址, 每一個system call都要透過這個函式, 下面是反組譯0x7ffe0300的結果:

SharedUserData!SystemCallStub:
7ffe0300 mov edx, esp
7ffe0302 sysenter
7ffe0304 ret

或許有人會覺得奇怪, 為什麼要多此一舉地多這一層call呢? 直接在系統API裡執行SYSENTER不行嗎? well, 這是因為SYSENTER並不會記錄任何目前的CPU狀態, 使用int 2e會讓CPU自動儲存目前的EIP和EFLAGS到stack裡, 但SYSENTER則完全不會, 因此OS必需先呼叫SystemCallStub以記錄目前的user-mode stub到stack中, 好在return的時候知道該回到哪兒去. 當kernel完成系統服務要返回user-mode時, 只需要直接jmp到從API呼叫SystemCallStub裡的stack中所記錄的位址就行了, 至於7ffe0304的ret指令事實上永遠不會被執行到.

 

没有更多推荐了,返回首页