仿照了下360 的过滤架构,搭建了个Hook 框架,360Hook架构的确很优秀,我觉得很值得我们学习与研究。这里我按照大牛们已经逆向出来的思路实现了下代码(都逆向出来了坐下代码工作不会怎么样吧?….只是学习架构)。不要鄙视我等代码工………,好吧大牛们想BSBS吧,我表示毫无压力~~~~,我是菜鸟我怕谁!
 
废话不多说发代码,如果有错误和白痴的地方请指出,我水平有限…….
搭建这个架构大致需要以下几个模块,一是安装KiFastCallEntryHook模块,二是FakeKiFastCallEntry代理模块,三是SysCallFilter系统调用是否过滤的判断模块。其余的模块主要是过滤函数了,还有个获取KiFastCallEntrypatch地址的模块,最后是释放模块和初始化模块,这样大致的架构就搭建起来了。
接下来看看每个模块是怎么工作的。

 

首先是安装KiFastCallEntryHook模块,这个原理我就不多说了,大家都懂的

/************************************************************************


函数名称: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)(&EventHandlePreviousState);


    }


    else


    {


        _asm


        {


            mov eax,dword ptr [ebp+4h]


            mov  BackTrackingAddr,eax


        }


    }


    return status;

}
 

安装好Hook后就是Hook的代理函数了


这段代码 ……..好吧被BS咱也莫有办法,代理函数传入三个参数,这三个参数的含义可以参考内核情景分析一书中有详细介绍,SysCallFileter来判断是否过滤。


_declspec (nakedNTSTATUS FakeKiFastCallEntry()


{


    _asm


    {


        mov     edi,edi


        pushfd


        pushad


        push    edi


        push    ebx


        push    eax


        call    SysCallfilter


        mov     dword ptr [esp+10h],eax


        popad


        popfd


        sub     especx


        shr     ecx, 2


        push    RetAddress


        retn


 


    }

}
 

接下来就是判断过滤的函数了,这里我略去了SHADOW SSDT,这段代码也……


/************************************************************************


函数名称:SysCallfilter


功能描述:过滤系统调用


参数列表:


ULONG SysCallNum:系统调用号


ULONG FunAddr:系统调用函数入口地址


ULONG ServiceBase:系统调用表指针


返回值:过滤则返回代理函数地址,否则返回真实地址


*************************************************************************/


ULONG SysCallfilter(ULONG SysCallNum,ULONG FunAddr,ULONG ServiceBase)


{


        


ifServiceBase==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);


    }


    

}

 


然后是GoOrNotR3通信等待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;

好的宏定义也可以简化工程,这里大家可自行考虑。


 


这样差不多整个架构就搭建起来了,一个小型的监控系统就可以完成了。优秀的架构的确可以事半功倍,不过过滤函数的规则其实才是重中之重呀……………..。大家可以多讨论下,这个过滤规则是很需要仔细研究的。当然你要藏着咱也没办法呵…………


最后说下:


由于这个源代码是我一个大文件里面的一部分,所以整个也不好给出,其实也没必要给出来,毕竟没多少技术含量,大家都可以写得出的,这里只是就框架总结下而已。全部发了也没意思一大堆你也不想看,还不如发点核心的,然后你也可以尝试搭建下自己的更有乐趣呢!

第一次发这种贴,如果有不当之处敬请谅解