內核注入,技術古老但很實用。如今部分RK趨向無進程,玩的是SYS+DLL,有的無文件,所有存在於內存中。可能有部分人會說:「都進內核了.什麼不能幹?」。是啊,要是內核中能夠作包括R3上全部能作的事,軟件開發商們也不必作應用程序了。有時,咱們確實須要R3程序去幹驅動作起來很困難或者不必驅動中去作的事,進程 / DLL是不錯的選擇,但進程目標太大,因此更多的同窗趨向於注DLL。
若要開發安全軟件、小型工具,可借鑑其思路,Anti Rootkits時,在某些極端狀況下,可以使用一樣的技術發現、清除RK,保證用戶電腦的正常使用。在此,我將探討幾種內核注入DLL的思路及實現原理。
(1) APC技術
給一個Alertbale的用戶態線程插APC,讓其執行其中的ShellCode,來執行咱們的代碼。這個方法簡單易行,可是不夠穩定,兼容性很差。測試中發現常常出現Explorer.exe等插崩潰的狀況,並且有殺軟在的狀況下,插入有時會被攔截,起不到應有的效果。(可參考我之前逆過的一個驅動:逆向fuck.sys--編譯經過--源碼)
(2) 內核Patch [url=file://KnownDLLs/Kernel32.dll]\\KnownDLLs\\Kernel32.dll[/url] CreateThread
[url=file://KnownDLLs/]\\KnownDLLs[/url]是系統加載時對象管理器加載最新磁盤DLL到內存的,當其餘進程想調用某個DLL時,就不用重複從磁盤加載了,而會從這裏映射一份到本身的進程空間中去。這樣給咱們作全局Patch提供了一個很好的機會:
ZwOpenSection打開 [url=file://KnownDlls/kernel32.dll]\\KnownDlls\\kernel32.dll[/url],調用ZwMapViewOfSection映射一份到本身進程空間,而後尋找kernel32.dll在內存中代碼節的空隙,選擇這裏做爲咱們fake函數的存儲Buffer。修改CreateThread函數的開頭5字節跳轉到這個間隙,當系統任何一個線程建立時,會走到CreateThread函數,而後執行空隙中的ShellCode,其負責調用LoadLibrary加載咱們的DLL。DLL一經加載,會發IOCTL通知本驅動,讓驅動卸載HOOK。這樣就完成了內核注DLL的過程。測試時發現Svchost.exe進程調用CreateThread函數很頻繁,因此觸發也會很快,基本1秒不到就能將DLL加載進去,而咱們的HOOK也卸掉了。因此穩定性提升很多。示意圖以下:
(3) 內核 HOOK ZwMapViewOfSection
有部分模塊加載時會調用ZwMapViewOfSection,好比進程建立時映射N份DLL到本身的虛擬空間中去.咱們替換SSDT中的這個函數,過濾出是加載Kernel32.dll的狀況,從參數中取得其基址,Inline Hook其EAT中的CreateThread函數,跳轉到在這個進程虛擬地址空間中申請的Buffer,在其中完成DLL的加載過程.
關鍵API:
ZwAllocateVirtualMemory ---- 在此進程空間中分配內存,存放Shellcode
ZwProtectVirtualMemory ---- 使當前內存塊具備可讀可寫屬性
IoAllocateMdl ---- 建立MDL
關鍵Code以下:
同方法2相比,原理相似。但修改時機不一樣,效果差很少,只是注入DLL的時間會慢一些。至於Shellcode的編寫,就大同小異了.蘿蔔白菜各有所愛,主要看我的發揮。要是閒寫shellcode麻煩,請到看雪學院去查資料,模板不少,在這裏就不YY了。
【看雪讀書月】學習ShellCode編寫
[note]一個簡單的Shellcode
shellcode之小小琢磨
Add_Section
(4) 內核 HOOK
NtCreateThread
跟蹤進程建立的流程,會很明晰的發現有多點能夠patch來實現DLL的注入。
進程建立完時是一個空水壺,裏面沒有沸騰的熱水(threads),因而系統調用NtCreateThread建立其主線程(給空水壺注水 – 涼水),在這個暫停的線程裏面折騰了一陣後完事了也厭倦了,因而系統跳了出來,回到進程空間中,調用Kernel32.dll去通知CSRSS.EXE,對它說:「這裏有一個新進程出生了,你在你的表裏標記一下」。而後就開始加載DLL啦,把系統KnownDLLs中的本身須要的DLL都Map一份到這個大水壺中。接着KiThreadStartup加熱水壺中的涼水,因而水就開始沸騰了,此時主線程開始工做。。。
攔截NtCreateThread,取得當前線程上下文,保存它要返回的地址(會回到空水壺中去),劫持爲咱們本身分配的地址,在其中填充ShellCode來加載目的DLL。至於選擇Buffer,思路不少。這裏可簡單的Attach到當前進程,在充足的虛擬2GB進程地址空間中分配屬於你本身的一塊小內存,夠放ShellCode足矣。示意圖以下:
(5) 內核感染經常使用模塊,讓感染模塊幫咱們Load DLL
這個方法就有點繞遠了,開始了最本質最原始的感染,可增長新節,可插空隙,總之,讓別人的模塊Load進內存時順路的幫咱們加載下DLL,DLL一旦加載就能夠恢復感染,清除痕跡。至於感染代碼,網上一堆。只要不是驅動感染驅動(多了個校驗和),其餘性質都同樣,看本身發揮啦。
(6) 攔截NtCreateUserProcess、NtCreateSymbolicLinkObject
前者在Vista下才有. 攔截後經過PsLookupProcessThreadByCid獲得ETHREAD / EPROCESS,判斷是不是CSRSS.EXE引發的,如果則在此進程空間內分配一塊內存,調用NtGetContextThread獲得當前的線程上下文,調用ZwWriteVirtualMemory填充Shellcode區域,取得LdrUnloadDll、LdrGetDllHandle等函數地址,經過他們加載DLL。而後調用NtSetContextThread恢復原始的Context。關於這種方法,可參考DriverDevelop上某人發的BIN。
[」內核實現DLL注入.能夠完美繞過KAV瑞星等殺毒軟件」]
(7) 內核攔截NtResumeThread
(8)NtUserSetWindowsHookEx 注入
順便提下R3上DLL的注入:
1.CreateRemoteThread (or NtCreateThreadEx (Used in Vista))
2.SetThreadContext (change the EIP)
3.NtQueueAPCThread
4.RtlCreateUserThread
5. SetWindowHookEx
小結:
縱觀進程啓動的全過程,可patch的地方不少,只要保證進程、線程上下文不被破壞,注入的手法可多種多樣。只要保證咱們的DLL注入時間足夠短,穩定性足夠高便可。當咱們無可奈何要從內核注入DLL到用戶進程去時,系統已經中毒很深,此時運用相似上面提到的技術來加載DLL,讓DLL作咱們驅動沒法完成的任務,是能夠接受的。
上面提到的思路是我暫時想到而且已經實現了的,詳細過程可參見代碼。歡迎積極探討更好更穩定並且不邪惡的方法。php