zoukankan      html  css  js  c++  java
  • 360HOOK表,Hook过滤架构搭建

    分析了一下360的HOOK,通过直接hook KiFastCallEntry实现以所有系统调用的过滤。
    我分析的版本如下:
    主程序版本: 6.0.1.1003
    HookPort.sys版本: 1, 0, 0, 1005
    HookPort.sys的TimeStamp: 4A8D4AB8

    简单说明:360把所有被hook的系统服务的过滤函数放在了一个表里,索引即对应的系统服务在该过滤函数表中的索引。
    所有列出来的函数都会被hook掉的,是否处理指某个系统服务有没有相应的过滤函数进行处理,拒绝还是放行就是在过滤函数中完成判断的。
    不处理的系统服务,将会直接调用原始服务例程。
    函数如下:
    服务名称          索引  是否处理  备注
    ==============================================================================
    NtCreateKey          0x00  否
    NtQueryValueKey        0x01  是
    NtDeleteKey          0x02  是
    NtDeleteValueKey      0x03  是
    NtRenameKey          0x04  是
    NtReplaceKey        0x05  是
    NtRestoreKey        0x06  是
    NtSetValueKey        0x07  是
    NtCreateFile        0x08  是
    NtFsControl          0x09  是
    NtSetInformationFile     0x0A  是
    NtWriteFile          0x0B  是
    NtWriteFileGather      0x0B  是    //和NtWriteFile共用一个过滤函数
    NtCreateProcess        0x0D  是
    NtCreateProcessEx      0x0E  是
    NtCreateUserProcess      0x0F  是    //Only on Vista or later
    NtCreateThread        0x10  是
    NtCreateThreadEx      0x10  是    //和NtCreateThread共用一个过滤函数,for vista or later
    NtOpenThread        0x11  是
    NtDeleteFile        0x12  是
    NtOpenFile          0x13  是
    NtReadVirtualMemory      0x14  否
    NtTerminateProcess      0x15  是
    NtQueueApcThread      0x16  是
    NtSetContextThread      0x17  是
    NtSetInformationThread    0x18  否
    NtProtectVirtualMemory    0x19  否 
    NtWriteVirtualMemory    0x1A  是
    NtAdjustGroupToken      0x1B  否
    NtAdjustPrivilegesToken   0x1C  否
    NtRequestWaitReplyPort    0x1D  是
    NtCreateSection        0x1E  是
    NtOpenSecton        0x1F  是
    NtCreateSymbolicLinkObject  0x20  是
    NtOpenSymbolicLinkObject  0x21  否
    NtLoadDriver        0x22  是
    NtUnloadDriver        0x22  是    //和NtLoadDriver共用一个过滤函数
    NtQuerySystemInformation  0x23  是
    NtSetSystemTime        0x25  否
    NtSystemDebugControl    0x26  是
    NtUserBuildHwndList      0x27  是
    NtUserQueryWindow      0x28  是
    NtUserFindWindowEx      0x29  是
    NtUserWindowFromPoint    0x2A  是
    NtUserMessageCall      0x2B  是
    NtUserPostMessage      0x2C  是
    NtUserSetWindowsHookEx    0x2D  是
    NtUserPostThreadMessage    0x2E  是
    NtOpenProcess        0x2F  是
    NtDeviceIoControlFile    0x30  是
    NtUserSetParent        0x31  是
    NtOpenKey          0x32  是
    NtDuplicateObject      0x33  是
    NtResumeThread        0x34  否
    NtUserChildWindowFromPointEx 0x35  是
    NtUserDestroyWindow      0x36  是
    NtUserInternalGetWindowText  0x37  否
    NtUserMoveWindow      0x38  是    //和NtSetParent共用一个过滤函数
    NtUserRealChildWindowFromPoint 0x39 是    //和NtUserChildWindowFromPointEx共用一个过滤函数
    NtUserSetInformationThread  0x3A  否
    NtUserSetInternalWindowPos  0x3B  是    //和NtSetParent共用一个过滤函数
    NtUserSetWindowLong      0x3C  是    //和NtSetParent共用一个过滤函数
    NtUserSetWindowPlacement  0x3D  是    //和NtSetParent共用一个过滤函数    
    NtUserSetWindowPos      0x3E  是    //和NtSetParent共用一个过滤函数
    NtUserSetWindowRgn      0x3F  是    //和NtSetParent共用一个过滤函数    
    NtUserShowWindow      0x40  是
    NtUserShowWindowAsync    0x41  是    //和NtUserShowWindow共用一个过滤函数
    NtQueryAttributesFile    0x42  否
    NtUserSendInput        0x43  否
    NtAlpcSendWaitReceivePort  0x44  是    //for vista or later
    NtUnmapViewOfSection    0x46  是
    NtUserSetWinEventHook    0x47  否
    NtSetSecurityObject      0x48  是
    NtUserCallHwndParamLock    0x49  是
    NtUserRegisterUserApiHok  0x4A  否

    仿照了下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;
    好的宏定义也可以简化工程,这里大家可自行考虑。

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

  • 相关阅读:
    00 vue源码里面常见方法
    vue 组件
    vue 样式
    vue 表单
    网络技术:NAT 网络地址转换
    网络技术:ACL 访问控制列表
    网络技术:VLAN 虚拟局域网
    Java程序设计——购物车系统
    网络 2011-2012 C 语言第三次作业批改总结
    第一用CSS
  • 原文地址:https://www.cnblogs.com/mayingkun/p/7764393.html
Copyright © 2011-2022 走看看