浅谈系统服务分发
歡迎轉載,轉載請注明出處:http://www.cnblogs.com/uAreKongqi/p/6597701.html
0x00.說在前面
就我們所知,Windows操作系統內核的陷阱處理器會分發中斷、異常和系統服務調用,這里我們就其中的系統服務分發簡單解析一下。
?
0x01.粗看不同處理器進入系統調用
(1).在PentiumII 之前的x86處理器上,Windows使用int2e指令產生一個陷阱,導致執行線程轉到內核模式,進入系統服務分發器,eax保存系統服務號,edx指向參數列表。最后通過iret指令返回用戶模式;
??我們查看IDT的2e成員,得知該成員保存的地址是系統調用分發器的地址,緊接著u一下KiSystemService,會發現在保存完寄存器等狀態之后,他走到了KiFastCallEntry里面!(在Win7 x86下測試)
(2).在x86Pentium II 處理器上,Windows使用了sysenter指令,內核的系統服務分發器例程的地址保存在與該指令相關聯的一個MSR中,eax,edx保存與int2e相同的內容。最后通過sysexit指令返回用戶模式;
這里讀取MSR的0x176處,其中包含了系統服務分發器地址,發現實際上調用的就是KiFastCallEntry(入口)!(在Win7 x86下測試)
(3).在x64體系架構上,Windows使用syscall指令,將系統調用號保存在eax中,前四個參數放在寄存器(rcx/rdx/r8/r9)中,剩下的參數在棧中。
64位平臺讀取MSR的0xC0000082處,里面保存的是64位的syscall,當我們u一下這個地址,發現這個就是x64系統調用分發的入口KiSystemCall64(在Win7 x64下測試)
?
Ps:通過KiSystemCall64的地址可以通過硬編碼得到SSDT、SSSDT地址
?......
?
? 我們發現,32位下,系統調用分發操作都會走到KiFastCallEntry里面,而64位下,系統調用分發操作會走到KiSystemCall64,然后去完成相應的系統服務調用。那么我們有個疑問,系統是怎么進入到這些系統調用的呢?
?
0x02.舉例查看系統調用如何發生
這里以Win7 x86 平臺下的 NtOpenProcess為例,切換到一個進程內(如explorer.exe),u一下NtOpenProcess,這里顯示的是ntdll里的NtOpenProcess的反匯編:
我們有兩個收獲,一個是看到了系統服務號放在了eax里了,另一個是它呼叫了一個地址,call指令將執行由內核建立起來的系統服務分發代碼,該地址保存在ntdll!_KUSER_SHARED_DATA+0x300處,我們接著進這個地址看看:
我們似乎有了點兒眉目了,系統調用在ntdll里面發生了,也就是說,在ntdll里面完成了從ring3到ring0的切換,其中eax保存服務號,edx保存參數列表首地址,通過服務號可以在SSDT中定位目標服務例程
32位下的KeServiceDescriptorTable每個成員就是目標系統服務的絕對地址,64位下的目標系統服務真實地址是KeServiceDescriptorTable每個成員保存的偏移量(右移4位后的)+KeServiceDescriptorTable基地址;
一開始線程的系統服務表地址指向Ntoskrnl.exe中的SSDT表,但當調用了一個USER或GDI服務時,服務表地址被修改成指向win32k.sys中的系統服務表。
?
0x03.從用戶層到內核層完整的調用過程
(1). 當一個Windows應用程序調用Kernel32.dll中的OpenProcess時,其導入并調用了API-MS-Win-Core-File-L1-1-0.dll(一個MinWin重定向Dll)中的NtOpenProcess函數;
(2). 接著上述的NtOpenProcess函數又會調用KernelBase.dll中的OpenProcess函數,這里是函數的真正實現,它會對子系統相關的參數做了檢查;
(3). 然后KernelBase!OpenProcess就會調用ntdll.dll中的NtOpenProcess函數,在這兒就會觸發系統調用(ntdll!KiFastSystemCall),傳遞NtOpenProcess的系統服務號和參數列表;
(4). 系統服務分發器(Ntoskrnl.exe中的KiSystemService函數)就會調用真正的NtOpenProcess函數來處理該I/O請求。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
0x04.內核模式下的系統分發
在系統調用中,如果原先模式為用戶模式,在給系統服務傳遞的參數指向了用戶空間緩沖區時,內核模式代碼在操作該緩沖區前會檢查是否可以訪問該緩沖區,而原先模式就是內核模式的時候,默認參數有效,則不會對參數進行檢查。而既然已經在內核模式了,那就不需要int2e中斷或者sysenter之類的操作了,但如果直接像調用API一般直接調用NtOpenProcess之類的系統服務函數時,內核保存的原先模式值仍然是用戶模式(進內核之前當然是用戶模式咯~),但又檢測到傳遞來的地址是一個內核模式地址(因為在當前內核模式下調用),于是會導致調用失敗(STATUS_ACCESS_VIOLATION)。
這里要介紹內核的Zw系列函數了,他們不僅是Nt版本函數的別名或包裝,而是對應Nt系列系統調用的翻版,使用了同樣的系統調用分發機制。他們會建立一個假的中斷棧(CPU在中斷后生成的棧),并直接調用KiSystemService例程,這個過程就在模擬CPU中斷,仿佛調用來自用戶模式一般,而在檢測到該調用的實際特權級后將原先模式修改為內核模式,這樣也省去了參數校驗,成功調用到NtOpenProcess!
?
?0x05.簡單小結
? Ring3 ---> Ring0 的系統調用: Kernel32.dll(API)--->ntdll.dll(Nt/Zw)--->用戶模式轉內核模式--->Ntoskrnl.exe(Nt)--->完成I/O請求(原路返回)
Ring0 ---> Ring0 的系統調用:Ntoskrnl.exe(Zw)--->Ntoskrnl.exe(Nt)
? 以上理解參考自《深入解析Windows操作系統6》第三章,如果有不正確的地方還請指出,我將虛心請教!
轉載于:https://www.cnblogs.com/uAreKongqi/p/6597701.html
總結
- 上一篇: 函数调用
- 下一篇: 解决url请求参数带中文时后台获取出现乱