首页
社区
课程
招聘
Hook过滤架构搭建,仿照360
发表于: 2010-7-1 13:48 38189

Hook过滤架构搭建,仿照360

2010-7-1 13:48
38189
收藏
免费 7
支持
分享
最新回复 (47)
雪    币: 59
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
这,第一次发怎么格式变了.........预览还好好的怎么发出来,间距这么大啊悲剧
2010-7-1 13:49
0
雪    币: 50161
活跃值: (20630)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
3
不用预览,你在记事本排好版,直接复制粘贴上来即可。
2010-7-1 13:56
0
雪    币: 59
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
哦第一次发长段的没经验谢谢了!
2010-7-1 14:05
0
雪    币: 59
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
补发下上面的太难看了

仿照了下360 的过滤架构,搭建了个Hook 框架,360的Hook架构的确很优秀,我觉得很值得我们学习与研究。这里我按照大牛们已经逆向出来的思路实现了下代码(都逆向出来了坐下代码工作不会怎么样吧?….只是学习架构)。不要鄙视我等代码工………,好吧大牛们想BS就BS吧,我表示毫无压力~~~~,我是菜鸟我怕谁!

废话不多说发代码,如果有错误和白痴的地方请指出,我水平有限…….
搭建这个架构大致需要以下几个模块,一是安装KiFastCallEntry的Hook模块,二是FakeKiFastCallEntry代理模块,三是SysCallFilter系统调用是否过滤的判断模块。其余的模块主要是过滤函数了,还有个获取KiFastCallEntry的patch地址的模块,最后是释放模块和初始化模块,这样大致的架构就搭建起来了。
接下来看看每个模块是怎么工作的。

首先是安装KiFastCallEntry的Hook模块,这个原理我就不多说了,大家都懂的
/************************************************************************
* 函数名称:HookKiFastCallEntry
* 功能描述:安装KiFastCallEntry钩子
* 参数列表:

* 返回值:状态
*************************************************************************/
NTSTATUS HookKiFastCallEntry()
{
        NTSTATUS status=STATUS_SUCCESS;
        if (!GetKiFastCallEntryPatchAddr())
        {
                KdPrint(("(HookKiFastCallEntry) GetKiFastCallEntryPatchAddr failed"));
                return STATUS_UNSUCCESSFUL;
        }
        RtlCopyMemory(OriginalHead2,(PVOID)PatchAddr,5);
        *(ULONG *)(ReplaceHead2+1)=(ULONG)FakeKiFastCallEntry-(PatchAddr+5);
        KIRQL Irql;
        Irql=WOFF();
        //写入新的函数头
        RtlCopyMemory((BYTE *)PatchAddr,ReplaceHead2,5);
        WON(Irql);       
        return status;

}
这个模块有个地方就是GetKiFastCallEntryPatchAddr()获取KiFastCallEntry的Patch点这个有点小技巧,大家可以学习下,360是用SetEvent钩子栈回朔实现的,这个大家听了应该都能明白,就是调用函数时候会PUSH 返回到的EIP,这个EIP就是KiFastCallEntry中的了。
要patch的地方是按特征码搜索的,这个是xp sp3的
BOOL GetKiFastCallEntryPatchAddr()
{
        ULONG ulCallNum;
        PULONG pHookAddr;
        PBYTE pCode;
        ULONG i;
        BOOL  bRet=true;
        KIRQL Irql;
        hFakeEvent=(HANDLE)FakeHandle;
        ulCallNum=*(PULONG)((PBYTE)ZwSetEvent+1);
        pHookAddr=(PULONG)(pSysCallFilterInfo->ulSSDTAddr+ulCallNum*4);
        RealNtSetEvent=*pHookAddr;//保存真实地址
        Irql=WOFF();
    *pHookAddr=(ULONG)FakeNtSetEvent; // 写入代理地址
        WON(Irql);
        ZwSetEvent(hFakeEvent,NULL);
        Irql=WOFF();
        *pHookAddr=RealNtSetEvent; // 写回真实地址
        WON(Irql);
        if (MmIsAddressValid((PVOID)BackTrackingAddr))
        {
                pCode=(PBYTE)BackTrackingAddr;
                for (i=0;i<SearchByte;i++)
                {
                        if (*(pCode-i)==0xe1&&*(pCode-i-1)==0x2b)
                        {
                                PatchAddr=(ULONG)(pCode-i-1);
                                break;
                        }
                        if (*(pCode-i)==0xfc&&*(pCode-i-1)==0x8b)
                        {
                                RetAddress=(ULONG)(pCode-i-1);
                        }
                       
                }
        }
        if (!PatchAddr||!RetAddress)
        {
                bRet=false;
        }
        return bRet;
}

这个代理函数里面获取EIP
NTSTATUS FakeNtSetEvent (
                                __in HANDLE EventHandle,
                                __out_opt PLONG PreviousState
                                )
{
        NTSTATUS status=STATUS_SUCCESS;
        if (EventHandle!=hFakeEvent||ExGetPreviousMode()==UserMode)// 不是自己调用,或者调用来自UserMode,直接调用原函数
        {
                status=((NTSETEVENT)RealNtSetEvent)(&EventHandle, PreviousState);
        }
        else
        {
                _asm
                {
                        mov eax,dword ptr [ebp+4h]
                        mov  BackTrackingAddr,eax
                }
        }
        return status;
}

安装好Hook后就是Hook的代理函数了
这段代码 ……..好吧被BS咱也莫有办法,代理函数传入三个参数,这三个参数的含义可以参考内核情景分析一书中有详细介绍,给SysCallFileter来判断是否过滤。
_declspec (naked) NTSTATUS FakeKiFastCallEntry()
{
        _asm
        {
                mov     edi,edi
                pushfd
            pushad
                push    edi
                push    ebx
                push    eax
                call    SysCallfilter
                mov     dword ptr [esp+10h],eax
                popad
                popfd
                sub     esp, ecx
                shr     ecx, 2
                push    RetAddress
                retn

        }
}

接下来就是判断过滤的函数了,这里我略去了SHADOW SSDT,这段代码也……
/************************************************************************
* 函数名称:SysCallfilter
* 功能描述:过滤系统调用
* 参数列表:
ULONG SysCallNum:系统调用号
ULONG FunAddr:系统调用函数入口地址
ULONG ServiceBase:系统调用表指针
* 返回值:过滤则返回代理函数地址,否则返回真实地址
*************************************************************************/
ULONG SysCallfilter(ULONG SysCallNum,ULONG FunAddr,ULONG ServiceBase)
{
               
        if( ServiceBase==pSysCallFilterInfo->ulSSDTAddr&&SysCallNum<=pSysCallFilterInfo->ulSSDTNum)
        {
                if(pSysCallFilterInfo->SSDTSwitchTable[SysCallNum]&&HookOrNot(SysCallNum,FALSE))
                {
                  return pSysCallFilterInfo->ProxySSDTTable[SysCallNum];//
                }
        }
        return FunAddr;       

}

这个模块可以考虑添加适当的过滤规则,但最好效率点,这里我没加什么过滤,主要是搭建框架。

/************************************************************************
* 函数名称:HookOrNot
* 功能描述:判断是否过滤系统调用
* 参数列表:
ULONG SysCallNum:系统调用号
BOOL Flags:SSDT还是SDOWSSDT标志
* 返回值:返回表示不过滤,表示过滤
*************************************************************************/
ULONG HookOrNot(ULONG SysCallNum,BOOL Flags)
{
        if (ExGetPreviousMode()==KernelMode)
        {
                return 0;
        }       
        if (Flags)
        {
                return 1;
        }
        else
                return 1;
}

好了基本功能模块搭建好了,现在就要初始化这些模块内所要使用的数据结构,来运作起来。
初始化里面我直接把savessdttable原始函数表填充为文件获取的原始地址表了,这里大家可以不必这么做。
/************************************************************************
* 函数名称:InitSysCallFilter
* 功能描述:初始化系统调用过滤
* 参数列表:

* 返回值:状态
*************************************************************************/
NTSTATUS InitSysCallFilter()
{
        NTSTATUS status=STATUS_SUCCESS;
        PVOID FileBuffer,FunBuffer;
        ULONG ulSSDTLimit;
        PKSERVICE_TABLE_DESCRIPTOR pServiceDescriptor;
        //init

        //Init SysCallFilterInfo buffer
        pSysCallFilterInfo=(PSYSCALL_FILTER_INFO_TABLE)ExAllocatePoolWithTag(
                NonPagedPool,
                sizeof(SYSCALL_FILTER_INFO_TABLE),
                MM_TAG_FILT);
        RtlZeroMemory(pSysCallFilterInfo,sizeof(SYSCALL_FILTER_INFO_TABLE));
        //Init SSDT address
        pServiceDescriptor=(PKSERVICE_TABLE_DESCRIPTOR)GetKeServiceDescriptorTable();
        pSysCallFilterInfo->ulSSDTAddr=(ULONG)pServiceDescriptor->Base;
        //Init SSDT Table
       
        FileBuffer=ExAllocatePoolWithTag(NonPagedPool,(SSDT_MAX_NUM)*sizeof(ULONG),MM_TAG_FILT);
        FunBuffer=ExAllocatePoolWithTag(NonPagedPool,(SSDT_MAX_NUM)*sizeof(ULONG),MM_TAG_FILT);
        if (!FileBuffer||!FunBuffer)
        {
                KdPrint(("(InitSysCallFilter) MmBuffer FunBuffer failed"));
                return STATUS_UNSUCCESSFUL;
        }
        status=EnumOriginalSSDT(FileBuffer,FunBuffer,&ulSSDTLimit);
        if (!NT_SUCCESS(status))
        {
                KdPrint(("(InitSysCallFilter) EnumOriginalSSDT failed"));
                ExFreePool(FileBuffer);
                ExFreePool(FunBuffer);
                return STATUS_UNSUCCESSFUL;
}
        memcpy(pSysCallFilterInfo->SavedSSDTTable,FileBuffer,ulSSDTLimit*4);
        ExFreePool(FileBuffer);
        ExFreePool(FunBuffer);
        pSysCallFilterInfo->ulSSDTNum=ulSSDTLimit;
        //Init Proxy SSDT table
        pSysCallFilterInfo->ProxySSDTTable[97]=(ULONG)FakeNtLoadDriver;
        //这里就可以随意添加Hook,相当方便
        //Init SSDT Swicth table
        pSysCallFilterInfo->SSDTSwitchTable[97]=1;
        //记得要开开关
        return status;
}

最后是释放清理模块了。
void UnHookKiFastCallEntry()
{
        KIRQL Irql;
        if (*(PULONG)OriginalHead2)
        {
        Irql=WOFF();
        //写回原来的函数头
        RtlCopyMemory((BYTE *)PatchAddr,OriginalHead2,5);
        WON(Irql);
        }
};

NTSTATUS FreeSysCallFilter()
{
        NTSTATUS status=STATUS_SUCCESS;
        UnHookKiFastCallEntry();
        if (pSysCallFilterInfo)
        {
                ExFreePool(pSysCallFilterInfo);
        }
        return status;
}
这里顺带发个过滤函数以及R3通信架构的搭建好了
这个过滤是NtLoadDriver的
NTSTATUS FakeNtLoadDriver( __in PUNICODE_STRING DriverServiceName)
{
        PEPROCESS pCurProcess;
        DRIVER_TRANS_INFO DriverTransInfo;
        if (DriverServiceName==NULL)
        {
                return ((NTLOADDRIVER)pSysCallFilterInfo->SavedSSDTTable[97])(DriverServiceName);
        }
        DriverTransInfo.Size=sizeof(DRIVER_TRANS_INFO);
        pCurProcess=PsGetCurrentProcess();
        if (pCurProcess)
        {
                GetProcessFullPathW((ULONG)pCurProcess,DriverTransInfo.ProcessFullPath);
        }
        RtlStringCchCopyW(DriverTransInfo.WarmReason,MAX_REASON*sizeof(WCHAR),L"尝试加载驱动,一旦加载驱动进程将会获得最高权限,允许此操作将可能导致危险发生,驱动文件为:");
        RtlStringCchCatW(DriverTransInfo.WarmReason,MAX_PATH*sizeof(WCHAR),DriverServiceName->Buffer);
        if (!GoOrNot((PVOID)&DriverTransInfo,TYPE_DRIVER_MONITOR))
        {
                return STATUS_ACCESS_DENIED;
        }
        else
        {
                return ((NTLOADDRIVER)pSysCallFilterInfo->SavedSSDTTable[97])(DriverServiceName);
        }
       
}

然后是GoOrNot与R3通信等待R3命令
BOOL GoOrNot(__in PVOID pMonitorInfo,__in ULONG Type)
{
        BOOL bRet=false;
        switch (Type)
        {
        case TYPE_DRIVER_MONITOR:
        bRet=GetUserCommand(g_DeviceExtension->DriverMonitorInfo.pNotifyEvent,
                        g_DeviceExtension->DriverMonitorInfo.SharedMemInfo.pShareMemory,
                        pMonitorInfo,
                        sizeof(DRIVER_TRANS_INFO));
                break;
default:
                                ;
        }
        return bRet;
               
}
//获取用户层命令
BOOL GetUserCommand(__in PKEVENT pNotifyEvent,
                                        __in PVOID pShareMemory,
                                        __in PVOID pTransInfo,
                                        __in ULONG pTransLen)
{
        BOOL bRet;
        PDRIVER_TRANS_INFO pDriverTransInfo;
        memcpy(pShareMemory,pTransInfo,pTransLen);
        KeSetEvent(pNotifyEvent,0,false);
        KeWaitForSingleObject(
                pNotifyEvent,
                Executive,
                KernelMode,
                false,
                NULL);
        pDriverTransInfo=(PDRIVER_TRANS_INFO)pShareMemory;
        if (pDriverTransInfo->Command==COMMAND_GO)
        {
                bRet=true;
        }
        else if (pDriverTransInfo->Command==COMMAND_STOP)
        {
                bRet=false;
        }
        return bRet;
}

这里发段R3和R0共享内存的,用的是内核创建pool在建MDL映射到用户空间的方法。
BOOL CreateSharedMemory(__out  PSHARE_MEMORY_INFO pShareMemInfo,
                              __in  ULONG MemorySize)
{
         BOOL bRet=true;
         PMDL pMdl;
        PVOID UserVAToReturn;
         PIO_STACK_LOCATION pIoStackLocation;
        ULONG ulBufferLengthOut;
        PVOID pSharedBuffer;
        pSharedBuffer=ExAllocatePoolWithTag(NonPagedPool,MemorySize,MM_TAG_ANTI);
        if (!pSharedBuffer)
        {
         KdPrint(("(IrpCreateSharedMemory) pSharedBuffer allocate failed"));
          return false;
         }
         pMdl=IoAllocateMdl(pSharedBuffer,MemorySize,false,false,NULL);
         if (!pMdl)
        {
         KdPrint(("(IrpCreateSharedMemory) IoAllocateMdl( failed"));
         ExFreePool(pSharedBuffer);
         return false;
        }
         MmBuildMdlForNonPagedPool(pMdl);
         UserVAToReturn=MmMapLockedPagesSpecifyCache(pMdl,
                 UserMode,
                 MmCached,
                  NULL,
                  false,
                  NormalPagePriority);
        if (!UserVAToReturn)
        {
          IoFreeMdl(pMdl);
          ExFreePool(pSharedBuffer);
          return false;
        }
        RtlZeroMemory(pSharedBuffer,MemorySize);
        KdPrint(("UserVAToReturn:0x%08x",UserVAToReturn));
        //输出
        pShareMemInfo->pShareMemory=pSharedBuffer;
        pShareMemInfo->pSharedMdl=pMdl;
        pShareMemInfo->UserVA=(ULONG)UserVAToReturn;
        return bRet;
}
R3的创建事件和开线程我就不发了,很简单大家可以自己尝试下。
最后附下整个架构的部分数据结构

//GoOrNot Type宏定义
#define TYPE_DRIVER_MONITOR 0x01
//GoOrNot Command宏定义
#define COMMAND_GO 0x01
#define COMMAND_STOP 0x02
//危险拦截提示语句
#define WARM_DRI_LOAD      L"尝试加载驱动,一旦加载驱动进程将会获得系统最高权限,允许此操作将可能导致危险发生,驱动文件路径:"
//************数据定义***************************************************
typedef struct _SYSCALL_FILTER_INFO_TABLE
{
        ULONG ulSSDTAddr;
        ULONG ulSHADOWSSDTAddr;
        ULONG ulSSDTNum;
        ULONG ulSHADOWSSDTNum;
        ULONG SavedSSDTTable[SSDT_FILTER_NUM];                //SSDT原始函数地址表
        ULONG ProxySSDTTable[SHADOWSSDT_FILTER_NUM];          //SSDT代理函数地址表
        ULONG SavedShadowSSDTTable[SSDT_FILTER_NUM];    //ShadowSSDT原始函数地址表
        ULONG ProxyShadowSSDTTable[SHADOWSSDT_FILTER_NUM];   //ShadowSSDT代理函数地址表
        ULONG SSDTSwitchTable[SSDT_FILTER_NUM];              //SSDT Hook开关表
        ULONG ShadowSSDTSwitchTable[SHADOWSSDT_FILTER_NUM];//ShadowSSDT Hook开关表
}SYSCALL_FILTER_INFO_TABLE,*PSYSCALL_FILTER_INFO_TABLE;
好的宏定义也可以简化工程,这里大家可自行考虑。

这样差不多整个架构就搭建起来了,一个小型的监控系统就可以完成了。优秀的架构的确可以事半功倍,不过过滤函数的规则其实才是重中之重呀……………..。大家可以多讨论下,这个过滤规则是很需要仔细研究的。当然你要藏着咱也没办法呵…………
最后说下:
由于这个源代码是我一个大文件里面的一部分,所以整个也不好给出,其实也没必要给出来,毕竟没多少技术含量,大家都可以写得出的,这里只是就框架总结下而已。全部发了也没意思一大堆你也不想看,还不如发点核心的,然后你也可以尝试搭建下自己的更有乐趣呢!
第一次发这种贴,如果有不当之处敬请谅解。
2010-7-1 14:11
0
雪    币: 7651
活跃值: (523)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
6
不错。。。。
2010-7-1 14:16
0
雪    币: 59
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
哇居然有精华激动ing..........版主神速.........
2010-7-1 14:36
0
雪    币: 155
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
你觉得不错?我也觉得不错 ,不过我不会写驱动 呵呵
2010-7-1 14:49
0
雪    币: 211
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
够XX,这架构也够精致的啊!哈哈,自己慢慢充东西吧
2010-7-1 15:07
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
顶一个

确实不错~
2010-7-1 15:21
0
雪    币: 308
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
11
支持下。。。
2010-7-1 15:21
0
雪    币: 210
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
好东西,把完整工程共享一下吧,其实我主要是想找一个高效的GoOrNot,纠结的很啊
2010-7-1 15:52
0
雪    币: 70
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
嗯,lz加油,不错
2010-7-1 18:43
0
雪    币: 59
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
完整工程目前没办法给呢,请谅解,能发我就发了
2010-7-1 18:45
0
雪    币: 293
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
精品文章!!!
2010-7-1 18:47
0
雪    币: 243
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
不错。思路很重要
2010-7-1 20:46
0
雪    币: 66
活跃值: (1015)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
强贴要留名啊。

期待一个可以编译的,呵呵
2010-7-1 22:24
0
雪    币: 51
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
ULONG SavedSSDTTable[SSDT_FILTER_NUM];                //SSDT原始函数地址表
  ULONG ProxySSDTTable[SHADOWSSDT_FILTER_NUM];          //SSDT代理函数地址表
  ULONG SavedShadowSSDTTable[SSDT_FILTER_NUM];    //ShadowSSDT原始函数地址表
  ULONG ProxyShadowSSDTTable[SHADOWSSDT_FILTER_NUM];   //ShadowSSDT代理函数地址表

这个地方应该是这样子吧?
  ULONG SavedSSDTTable[SSDT_FILTER_NUM];                //SSDT原始函数地址表
  ULONG ProxySSDTTable[SSDT_FILTER_NUM];          //SSDT代理函数地址表
  ULONG SavedShadowSSDTTable[SHADOWSSDT_FILTER_NUM];    //ShadowSSDT原始函数地址表
  ULONG ProxyShadowSSDTTable[SHADOWSSDT_FILTER_NUM];   //ShadowSSDT代理函数地址表
2010-7-2 09:01
0
雪    币: 59
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
19
呵你真仔细,打反了谢谢提醒
2010-7-2 09:05
0
雪    币: 5474
活跃值: (3859)
能力值: ( LV13,RANK:283 )
在线值:
发帖
回帖
粉丝
20
多谢楼主分享,有时间学习一下。
2010-7-2 09:43
0
雪    币: 492
活跃值: (53)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
21
很牛啊,要是有完整工程就好了~~~
2010-7-2 09:55
0
雪    币: 266
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
框架,不错啊。
2010-7-2 10:32
0
雪    币: 234
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
楼主辛苦了。。看看先。。
2010-7-2 13:41
0
雪    币: 33
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
全部发了也没意思一大堆你也不想看,还不如发点核心的,


我非常 愿意看全部的
2010-7-2 21:29
0
雪    币: 329
活跃值: (343)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
25
这个。。。。看着很眼熟
2010-7-5 12:01
0
游客
登录 | 注册 方可回帖
返回
//