<基于交叉引用的搜索检测object hook>
Author: sudami [sudami@163.com]
Time: 08/09/12
昨天和VXK同学探讨了些关于object hook检测.基于PDB文件解析的方法虽简单见效,但不实用,因为写个普通程序就要自带符号的话很臃肿不方便.于是打算从文件搜索,下下策是暴搜(最坏的打算,不稳定通用,而且obj函数巨多,肯定搜不过来), 恰巧dummy同学去年时候放了个"通过重定位表找到对某个特定地址的所有引用地址"的代码, 在一些方面适用于搜索Object Origal Address.当然,dummy的R0代码在Load PE到内存方面有部分bug,需要自己修复.
思路如下:
1. 准备表1,存放系统当前的Object Function Address.打开"\\ObjectTypes",得到RootDirectory,遍历其中的HashBuckets,根据不同的对象得到不同的地址,存到表1中
2. 准备表2,存放系统原始的Object Function Address.
把系统内核ntosxx加载一份到nonpagepool,在这块区域内进行搜索操作;
① 对于已导出的type(eg.IoDeviceObjectType、PsProcessType),遍历ntosXX'EAT,得到RVA -- uAddr,再调用LookupImageXRef((ULONG)NtosCopy, uAddr, LookupXRefCallback);
在LookupXRefCallback回调函数中对地址进行搜索,若是我们要找的函数模块,判断时候进行特征匹配,举个例子:
BOOLEAN
NTAPI
LookupXRefCallback(
PULONG RefAddr
)
{
ULONG tmp, address;
PBYTE lpAddr = (PBYTE)RefAddr;
ULONG x, i;
int time = 0;
// 返回 True 继续寻找,返回 False 停止寻找
// DbgPrint("%08X --> %08X\n", RefAddr, RefAddr[0]);
__try
{
//
// 因为调用的是V大的LoadPeFile,所以跟系统加载到内存中的情况是一样的
// 里面已经进行了对齐.所以 (ULONG)RefAddr - (ULONG)NtosCopy;
// 就是RVA,不需要考虑ImageBase了.很好很强大; 而RefAddr即是我们自己加载
// 文件到内存,得到的某个函数的地址,比如我要得到内核函数A开头的原始N字节
// 直接在RefAddr处往后取出便是了.哈哈,很方便~
// sudami 08/09/11 凌晨
//
tmp = (ULONG)RefAddr - (ULONG)NtosCopy; //RVA
// DbgPrint("0x%08lx\n", (ULONG)ulKernelBase + tmp );
//
// 对于每个RefAddr ,地址+4开始反汇编,最终找到匹配XX函数内部调用的代码.
// 在它里面就有IopXX,PspXX,CmpXX...系列的函数原始地址啦.
// sudami 08/09/11 凌晨
//
//
// coding
//
lpAddr+=4;
switch ( g_nMethod )
{
case 1: // IopCreateObjectTypes函数
/*++ <IoDeviceObjectType>
1. C7 45 D4 B8 00 00 00 mov [ebp+var_2C], 0B8h
2. C7 45 E8 4D 57 4A 00 mov [ebp+var_18], offset IopParseDevice
C6 45 AF 01 mov byte ptr [ebp+var_54+3], 1
3. C7 45 E4 9A 71 4C 00 mov [ebp+var_1C], offset IopDeleteDevice
4. C7 45 EC 8A 90 4D 00 mov [ebp+var_14], offset IopGetSetSecurityObject
89 5D F0 mov [ebp+var_10], ebx
E8 1F 40 F2 FF call ObCreateObjectType
--*/
time = 0;
for(i=0;i<70;i++)
{
if ( !MmIsAddressValid( &lpAddr[i]) ){
continue ;
}
if(lpAddr[i]==0xC7 && lpAddr[i+1]==0x45)
{
time+=1;
if (time==1)
{
x = *((DWORD *)((ULONG)RefAddr+i+3+4));
if(x!=0xB8)
return FALSE;
}
if(time==2)
{
x = *((DWORD *)((ULONG)RefAddr+i+3+4));
DbgPrint("IopParseDevice - Orig: 0x%08lx\n", \
(ULONG)x + (ULONG)ulKernelBase - (ULONG)pNtH->OptionalHeader.ImageBase);
continue ;
}
if(time==3)
{
x = *((DWORD *)((ULONG)RefAddr+i+3+4));
DbgPrint("IopDeleteDevice - Orig: 0x%08lx\n", \
(ULONG)x + (ULONG)ulKernelBase - (ULONG)pNtH->OptionalHeader.ImageBase);
continue ;
}
if(time==4)
{
x = *((DWORD *)((ULONG)RefAddr+i+3+4));
DbgPrint("IopGetSetSecurityObject - Orig: 0x%08lx\n", \
(ULONG)x + (ULONG)ulKernelBase - (ULONG)pNtH->OptionalHeader.ImageBase);
break ;
}
}
}
...
这样,就实现了对部分Object函数的原始地址的查找,相比暴力搜索,要快速稳定的多.
② 对未导出的type(eg.CmpKeyObjectType、ObpTypeObjectType),要想用dummy的方法,就有些繁琐了.我是这样分析认为的:
; 找到指向这些 未导出的全局变量type 的指针,就好办了.对于已经导出的type,比如IoDeviceObjectType, 在ntoskrnl.exe的EAT取得的地址,就是指向
; IoDeviceObjectType的指针的地址,而不是IoDeviceObjectType的地址. 所以,用MJ的方法得到CmpKeyObjectType的真实地址也没用,要得到的指向
; CmpKeyObjectType这个变量的指针的地址,才能用用dummy的方法.见下:
;
; lkd> dd IoDeviceObjectType
; 80558ee4 817a9ca0 817a9900 817a9e70 817f0428
; | |
; | |-- IoDeviceObjectType本身的地址
; |-- 引用地址, 是指向IoDeviceObjectType的指针的地址
;
; 68 64 8D 48 00 push offset IoDeviceObjectType ; -->在callback函数中要找的引用地址是这样的
; ........... 即是80558ee4,而不是817a9ca0
; C7 45 E8 4D 57 4A 00 mov [ebp+ParseProcedure], offset IopParseDevice
; 89 5D F0 mov [ebp+QueryNameProcedure], ebx
; E8 1F 40 F2 FF call ObCreateObjectType
; -- sudami 08/09/12
;
; 只要这个问题解决,就完全不用暴搜了. 遍历完所有的type,找到这些指向这些type的指针的地址,再用dummy的方法找引用,
; 再就可以找到真实的obj地址了... 但我目前没有找到解决方法. 以下是部分同学的建议:
;
/*
sudami<sudami@163.com> 13:09:11
得到CMPxx系列的只能用暴搜的方法啦.搜 CmpCreateObjectTypes,然后再在里面去地址...
sudami<sudami@163.com> 13:09:23
各种方法都试过,只能这样咯~
神奇的MJ0011(353729104) 13:09:26
太挫了
sudami<sudami@163.com> 13:09:28
米办法
神奇的MJ0011(353729104) 13:09:58
动态获取就可以了 o(∩_∩)o...
sudami<sudami@163.com> 13:10:01
找引用不可能找到,因为形式都是这样的:
push offset CmpKeyObjectType ; int
神奇的MJ0011(353729104) 13:10:26
谁让你只会找重定位引用的 o(∩_∩)o...
sudami<sudami@163.com> 13:10:54
MJ大姐再深入提示一下啊
sudami<sudami@163.com> 13:11:02
免得我做体力活儿啊
神奇的MJ0011(353729104) 13:11:05
动态啊
sudami<sudami@163.com> 13:11:19
原始地址,
sudami<sudami@163.com> 13:11:23
老V说过了
sudami<sudami@163.com> 13:11:29
那样获取不行
sudami<sudami@163.com> 13:11:33
dokey的方法吧?
sudami<sudami@163.com> 13:11:47
hook下,然后setvalue下
神奇的MJ0011(353729104) 13:12:08
不是
sudami<sudami@163.com> 13:12:09
然后取得的CmpKeyObjectType.还是....
神奇的MJ0011(353729104) 13:12:14
利用cm callback o(∩_∩)o...
sudami<sudami@163.com> 13:12:19
哦
神奇的MJ0011(353729104) 13:12:29
CmpKeyObjectType直接用我那个obj hook里的方法就可以取到啊
sudami<sudami@163.com> 13:12:38
CmpKeyObjectType的地址找到了没用
神奇的MJ0011(353729104) 13:12:45
object header->obj type~
sudami<sudami@163.com> 13:12:45
我不需要CmpKeyObjectType的原始地址
sudami<sudami@163.com> 13:13:53
lkd> dd CmpKeyObjectType
8068ec68 817b6b40 00000000 00010000 00000000
8068ec78 02300000 00000004 00000004 02300000
sudami<sudami@163.com> 13:14:11
实际要得到8068ec68 这个地址,而不是CmpKeyObjectType的原始地址817b6b40
神奇的MJ0011(353729104) 13:14:24
反查啊~
sudami<sudami@163.com> 13:14:23
哎,自己还是先暴力吧
sudami<sudami@163.com> 13:14:42
反查,这么反查啊?
神奇的MJ0011(353729104) 13:14:55
暴搜反插
神奇的MJ0011(353729104) 13:14:59
obrefxxxx
sudami<sudami@163.com> 15:50:08
汗,光说不行,V同学,你自己写点儿code,测试下就知道了
主动防御毁灭者(86879759) 15:50:34
找引用不是找重定位里的么?
sudami<sudami@163.com> 15:50:49
是呀
神奇的MJ0011(353729104) 15:51:06
太傻了太傻了 找重定位时取一下值
神奇的MJ0011(353729104) 15:51:14
比较下是不是CMXXX
神奇的MJ0011(353729104) 15:51:17
就结局了嘛
主动防御毁灭者(86879759) 15:51:18
是啊
sudami<sudami@163.com> 15:51:44
好,我看看
主动防御毁灭者(86879759) 15:52:02
取得是内存里的值不是文件中的
主动防御毁灭者(86879759) 15:52:11
否则还是不能找到的...
主动防御毁灭者(86879759) 15:53:47
纯文件的搜索也行,马上搞定~~
主动防御毁灭者(86879759) 15:53:49
嘿嘿
sudami<sudami@163.com> 15:53:57
那样太累了
sudami<sudami@163.com> 15:53:59
何必呀
主动防御毁灭者(86879759) 15:57:11
是枚举所有的重定位,每个地址向下搜索
C7 45 B4 3F 00 0F 00
sudami<sudami@163.com> 15:57:47
你太疯狂了
主动防御毁灭者(86879759) 15:59:24
纯ring3,纯ring3
sudami<sudami@163.com> 16:00:37
我暂时不钻牛角尖了
主动防御毁灭者(86879759) 16:00:54
看了xp/2003 vista都是这个特征串
*/
③ 找不到方便的解决方案,只能用下下策啦.于是我做了点笨活儿,暴搜文件,关键代码如下:
// 经过一系列的读PE后,得到一些偏移值。计算PE在内存中需要的空间。
// 为其分配一个非分页内存
// 将文件的内容都读取到这里
// 获取文件的大小,申请一块内存来存放它
//DbgPrint("获取文件的大小,申请一块内存来存放它\n");
ZwQueryInformationFile (ntFileHandle, &ioStatus, &fsi,
sizeof(FILE_STANDARD_INFORMATION), FileStandardInformation);
FileContent = ExAllocatePool (NonPagedPool, fsi.EndOfFile.LowPart);
if (FileContent == NULL)
{
ntStatus = STATUS_UNSUCCESSFUL;
ZwClose(ntFileHandle);
DbgPrint("ExAllocatePool Error\n");
goto End;
}
byteOffset.LowPart = 0;
byteOffset.HighPart = 0;
ntStatus = ZwReadFile(ntFileHandle,
NULL,
NULL,
NULL,
&ioStatus,
FileContent,
fsi.EndOfFile.LowPart,
&byteOffset,
NULL);
if (!NT_SUCCESS(ntStatus))
{
ZwClose(ntFileHandle);
ExFreePool(FileContent);
DbgPrint("ZwReadFile 将要读的内容,读到一片非分页内存失败 Error\n");
goto End;
}
if (fsi.EndOfFile.LowPart <= 0)
{
ntStatus = STATUS_NOT_FOUND;
ZwClose(ntFileHandle);
ExFreePool(FileContent);
DbgPrint("NeedSize <= 0 Error\n");
goto End;
}
GetHeaders (FileContent, &pfh, &poh, &psh);
//DbgPrint("psh: %08lx\n", (PVOID)psh);
//DbgPrint("start search....\n");
// g_CmpCloseKeyObject_addr
for (i = 0; i < fsi.EndOfFile.LowPart; i++)
{
if ( (FileContent[i] == 0x8B) && (FileContent[i+1] == 0xFF) && (FileContent[i+2] == 0x55) && (FileContent[i+3] == 0x8B) &&
(FileContent[i+4] == 0xEC) && (FileContent[i+5] == 0x83) && (FileContent[i+6] == 0x7D) && (FileContent[i+7] == 0x18) &&
(FileContent[i+8] == 0x01) && (FileContent[i+9] == 0x77) && (FileContent[i+10] == 0x24) && (FileContent[i+11] == 0x56)
)
{
//DbgPrint("文件偏移i: %08lx\n", (PVOID)i);
sudami_1 = Offset2RVA( i, psh, pfh->NumberOfSections );
if (sudami_1 == 0) {
DbgPrint("sudami_1 == 0 Error\n");
goto NotFound;
}
if (sudami_1 > SizeOfImage) {
DbgPrint("sudami_1 > SizeOfImage Error\n");
goto NotFound;
}
sudami_1 += ModuleBase;
if (!MmIsAddressValid((PVOID)sudami_1 )) {
DbgPrint("!MmIsAddressValid((PVOID)sudami_1 ) Error\n");
goto NotFound;
}
g_CmpCloseKeyObject_addr = (DWORD)sudami_1;
DbgPrint( "CmpCloseKeyObject - Orig:\t0x%08x\n", (ULONG)g_CmpCloseKeyObject_addr );
break;
}
}
总之,做object hook的检测,我没有找到一个相对轻松的方法.做了一部分,还有大部分函数没去找,那也只是体力活.
觉得这个思路可行,所以拿出来说说,你可以参考创新...
ps: 期间VXK大牛的思路一直很新颖很神奇,帮了不少忙,thanks a lot!他BLOG里面的科普文章很值得大家借鉴:
http://hi.baidu.com/killvxk
附件是凌乱的代码,和一个简单的sys.代码写的不规范,我没有认真的去整理,稍稍改动,防止被坏人copy做坏事.
--------------------------------------------------
参考文章:
1.http://hi.baidu.com/killvxk/blog/item/e43a9598ff40db0e6e068c03.html
基于交叉引用的搜索未导出的某些东西~
2.http://hi.baidu.com/killvxk/blog/item/ced0eefcfb3b0743d6887d39.html
3.http://hi.baidu.com/killvxk/blog/item/e6e6374ddb694affd72afce8.html
关于重定向的hook到底是哪个?
4.http://hi.baidu.com/sudami/blog/item/3b164cf76c041124720eecf7.html
保护干涉文件的玩意
5.http://hi.baidu.com/sudami/blog/item/ec16d2dd81bd39325882dd51.html
VXK同学的文件重定向
- 标 题:基于交叉引用的搜索检测object hook
- 作 者:sudami
- 时 间:2008-09-12 17:01
- 链 接:http://bbs.pediy.com/showthread.php?t=72608