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