首页
论坛
课程
招聘
[原创]基于交叉引用的搜索检测object hook
2008-9-12 17:01 12234

[原创]基于交叉引用的搜索检测object hook

2008-9-12 17:01
12234
<基于交叉引用的搜索检测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同学的文件重定向

第五届安全开发者峰会(SDC 2021)10月23日上海召开!限时2.5折门票(含自助午餐1份)

收藏
点赞0
打赏
分享
最新回复 (13)
雪    币: 478
活跃值: 活跃值 (640)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 活跃值 25 2008-9-12 17:02
2
0



上传的附件:
雪    币: 5536
活跃值: 活跃值 (51)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
forgot 活跃值 26 2008-9-12 17:24
3
0
没有仔细看,难道只是跟文件比较了一下?
雪    币: 478
活跃值: 活跃值 (640)
能力值: ( LV12,RANK:1010 )
在线值:
发帖
回帖
粉丝
sudami 活跃值 25 2008-9-12 17:28
4
0
看完再说~~~~

完整版代码,请参考这里:
http://www.debugman.com/read.php?tid=1962
雪    币: 2520
活跃值: 活跃值 (641)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
小菜鸟一 活跃值 2008-9-12 18:05
5
0
又是一篇精华诞生  
雪    币: 502
活跃值: 活跃值 (36)
能力值: ( LV12,RANK:441 )
在线值:
发帖
回帖
粉丝
yangjt 活跃值 10 2008-9-12 18:32
6
0
正在学驱动……收藏下…… 支持大米~~
雪    币: 333
活跃值: 活跃值 (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
marshalx 活跃值 2008-9-12 18:34
7
0
dami vs dummy
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
lunglungyu 活跃值 1 2008-9-12 18:52
8
0
反正又是一篇看不明白的精华。
打个记号 将来回头
雪    币: 521
活跃值: 活跃值 (134)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
笨笨雄 活跃值 14 2008-9-12 20:23
9
0
又是原理+代码+聊天记录,不错
雪    币: 202
活跃值: 活跃值 (12)
能力值: ( LV9,RANK:290 )
在线值:
发帖
回帖
粉丝
newjueqi 活跃值 7 2008-9-12 21:21
10
0
牛人,仰慕中
雪    币: 5536
活跃值: 活跃值 (51)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
forgot 活跃值 26 2008-9-12 21:43
11
0
看完了,真的是跟文件比较一下
雪    币: 35757
活跃值: 活跃值 (153657)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
linhanshi 活跃值 2008-9-12 22:39
12
0
support.
雪    币: 79
活跃值: 活跃值 (21)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
cqzj70 活跃值 2014-6-3 20:08
13
0
mark
雪    币: 44
活跃值: 活跃值 (37)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
天涯何处 活跃值 2014-6-3 20:26
14
0
LZ可以果断占个位不,此贴一出必火.顶
游客
登录 | 注册 方可回帖
返回