首页
社区
课程
招聘
[原创]总结一把,较为精确判断SCM加载
2011-6-24 13:53 13609

[原创]总结一把,较为精确判断SCM加载

2011-6-24 13:53
13609
最近做点东西,算作是对自己半年来学习驱动的一点检验,很多代码来自于大牛们的众多作品,由于业余,很多地方处理的不好,蓝屏,死机。。。。

之前还是浮躁了,逆向烂的一塌糊涂,曾经还把360的驱动丢进IDA,按个Ctrl+F5,然后就对着代码狂翻十几分钟,啥都看不出来~~~~

最纠结的是现在搞得Release修改界面都会导致PAGE_FAULT_IN_NONPAGED_AREA错误,定位到我的驱动的GoOrNot函数。。。。。我用的SkinSE库,郁闷得~~~~~~~

本文算是个前期总结吧

基于SSDT Hook
记录两点,尤其是第二点,费了很多时间:

1、拦截远程线程创建

2、拦截SCM方式加载驱动(较为精确判断真实的加载驱动意图者


    希望第二点能给和我一样水平的一点帮助,能够更准确的判断SCM方式加载驱动

1、拦截远程线程创建

    黑防2009.03期上有一篇文章《SSDT Hook拦截远程线程的创建》(文  灰狐  iCoodle)
文中本来已经见得比较详细了,我在实践中遇到了一些问题

    (1)进行拦截的艰难抉择
         想要得到创建的线程ID,必须要先执行原始的ZwCreateThread函数,才能得到各项参数
      所以我实际上也没有进行拦截,只是记录了相关线程参数,以供后期处理

(2)文中有一个缺陷,就是创建进程也会导致ZwCreateThread被调用,他没有处理,于是判断被创建线程的进程是否已经存在线程成了一个问题

    (3)得到进程信息
NTSTATUS HookZwCreateThread(                                                                                
                OUT PHANDLE ThreadHandle,
                IN ACCESS_MASK DesiredAccess,
                IN POBJECT_ATTRIBUTES ObjectAttributes,
                IN HANDLE ProcessHandle,
                OUT PCLIENT_ID ClientId,
                IN PCONTEXT ThreadContext,
                IN PVOID InitialTeb,
                IN BOOLEAN CreateSuspended
                )
{
     ........................
//--------------------------------------------------------//
    ULONG lRet;
    PROCESS_BASIC_INFORMATION* pbi;                     //获取被创建线程的进程ID
    PVOID pBuffer;
    PROCESSINFOCLASS ProcessBasicInformation=0;   
//根据IDA反汇编的内核文件,当获取进程信息时,枚举类型enum PROCESSINFOCLASS (standard),ProcessBasicInformation  = 0 //声明PROCESSINFOCLASS ProcessBasicInformation=0;用来指明第三个参数类型为ProcessBasicInformation      
    uPid.Buffer=(PWSTR)ExAllocatePool(NonPagedPool,256);
    uPid.MaximumLength=256;
    uTid.Buffer=(PWSTR)ExAllocatePool(NonPagedPool,256);
    uTid.MaximumLength=256;
    //------------------------------------------------------------//
    pBuffer=ExAllocatePool(NonPagedPool,sizeof(PROCESS_BASIC_INFORMATION));
ZwQueryInformationProcess(ProcessHandle,ProcessBasicInformation,pBuffer,sizeof(PROCESS_BASIC_INFORMATION),&lRet);
    pbi=(PROCESS_BASIC_INFORMATION*)pBuffer;
        //DbgPrint("%d进程里有线程创建",pbi->UniqueProcessId);

  上面得到的是被创建线程的进程PID

  为了得到相关的线程信息,我还想当然的认为应该也有对应的
  THREAD_BASIC_INFORMATION结构与之对应,也应该有ZwQueryInformationThread与之对应,结果比较郁闷

后来参照了一些代码,正好有输出线程信息和判断功能

By:冰龙 2009.10.1

NTSTATUS ZhuZwCreateThread(OUT PHANDLE ThreadHandle,
                           IN ACCESS_MASK DesiredAccess,
                           IN POBJECT_ATTRIBUTES ObjectAttributes,
                           IN HANDLE ProcessHandle,
                           OUT PCLIENT_ID ClientId,
                           IN PCONTEXT ThreadContext,
                           IN PVOID UserStack,
                           IN BOOLEAN CreateSuspended)
{
       NTSTATUS Status;
       PVOID loaddll=0;
       Status=ObReferenceObjectByHandle(ProcessHandle,(ACCESS_MASK)PROCESS_ALL_ACCESS,NULL,KernelMode,&loaddll,NULL);
        if (NT_SUCCESS(Status))
         {
            if (IoGetCurrentProcess()!=loaddll)
               {
                  if(!FindThread((PEPROCESS)loaddll))
                   {
                     wwe= PsGetProcessImageFileName(loaddll);
                     wwp= PsGetProcessImageFileName(IoGetCurrentProcess());
                     DbgPrint("进程%s要注入线程到进程%s中\n",wwp,wwe);  
                     return STATUS_ACCESS_DENIED;
                    }       
                }
            ObDereferenceObject((PVOID)loaddll);
          }
       Status=OldZwCreateThread(ThreadHandle,DesiredAccess,ObjectAttributes,ProcessHandle,ClientId,ThreadContext,UserStack,CreateSuspended);
       return Status;
}
   DbgPrint("进程%s要注入线程到进程%s中\n",wwp,wwe);  
                     return STATUS_ACCESS_DENIED;
                    }       
                }
            ObDereferenceObject((PVOID)loaddll);
          }
       Status=OldZwCreateThread(ThreadHandle,DesiredAccess,ObjectAttributes,ProcessHandle,ClientId,ThreadContext,UserStack,CreateSuspended);
       return Status;
}
这个ClientId结构让我郁闷了好久,很多地方都用到

后来发现  ntoskrnl.exe里面有

00000000 CLIENT_ID       struc ; (sizeof=0x8, standard type)
00000000 UniqueProcess   dd ?                    ; offset
00000004 UniqueThread    dd ?                    ; offset
00000008 CLIENT_ID       ends

没有找到FindThread函数,只能自己实现了,由于底层编程功底不行,一开始是在用户态通过创建快照遍历来判断,结果不行,后来乱翻《Windows内核情景分析》,发现一个地方,在EPROCESS结构里有一个参数ActiveThreads,可以达到目的,此处用的硬编码

#define  ActiveThreads  0x1a0       //活动线程数,硬编码WIN XP SP3 
实现函数

BOOL FindThread(PEPROCESS Process)//是否已经存在线程
{
  UINT * num;
  num=(UINT)Process+ActiveThreads;
  if(*num = =0)
    return 0;
  else
    return 1;
}

判断远程线程创建就到这

2、拦截SCM方式加载驱动并精确判断加载进程(除去services.exe)(此处略过ZwSetSystemInformation)

应该说,很多人都做过SSDT Hook ZwLoadDriver拦截驱动的加载。但是很多个人防火墙,一些主动防御软件都不能准确判断SCM方式加载驱动的进程,只提示services.exe,360做得很好,膜拜一把。

郁闷,最新的源代码删了~~~~~~,只能重写

   (1) 基础不牢,当初还以为是进程创建services.exe来加载,结果发现其父进程是winlogon.exe, 囧  ~~~~
后来查资料说是RPC方式通知services.exe进程,忘了哪位大牛了

原话:

    这些大部分都是通过NdrClientCall来发送RPC请求给 Services.exe实现的

    RPC通讯在各个平台上依赖的API各不相同,基本上
    win2000:NtFsControlFile
    xp,2003:NtRequestWaitReplyPort
vista,2008.win7 :NtAlpcSendWaitReceivePort

于是狂搜NtRequestWaitReplyPort的资料,终于在 DebugMan上看到有人发帖,得到一个结论:
判断RequestMessage->MessageData的数据

 可惜基本不太会WinDbg

 DbgPrint("数据大小:%d,地址%x",RequestMessage->ActualMessageLength,RequestMessage->MessageData);

得到的14f090貌似是地址,但WinDbg看不到。。。
在内存查看窗口输入0014f090提示“无法检索信息, Win32 error 0n30: 系统无法从指定的设备上读取”。

(2)后来发帖求助,得到 鹿剑 的分析文档:

在他的文档的截图里,WinDbg是多么的好用,我也发现了DebugMan上的结论跟他的截图里面一样:
41021F00 =  StatService
41021C00 =  OpenService
41021800 =  CreateService

(3)最后无奈,乱搜了一番,得到这么一段代码:

  ptr=(ULONG *)(pLpcMessage->MessageData);
  for (i=0; i<pLpcMessage->ActualMessageLength/sizeof(ULONG); i++) 
  {
      MyPrintf("%x ", ptr[i]);
  }
心中大喜,之前也曾想过类似的方式,不过写的不行。于是采用此法,得到的StartService数据
1 1F0241,即41201f10跟41021F00有点不一样(不考虑正反),跑了一番,发现可以

判断思路:
根据ZwRequestWaitReplyPort得到最后StartService的进程,记录到变量中,在ZwLoadDriver中进行判断,如果是services.exe进程,则将变量中的信息替换掉ZwLoadDriver中的进程信息,返回到应用层,其它进程不进行处理,直接发往应用层
有人采用ZwRequestWaitReplyPort里面的数据直接进行拦截,可能不太好弄

HookZwLoadDriver:
if(strstr(aProcessName,"services.exe"))
{
  strcpy(aProcessName,LastCalled_Path);
  PId=LastCalled_Pid;
}
else
{
  PId=PsGetCurrentProcessId();
}
HookZwRequestWaitReplyPort:

//---------------------------------------------------------------------//
NTSTATUS HookZwRequestWaitReplyPort(HANDLE PortHandle, PLPCMESSAGE RequestMessage, PLPCMESSAGE ReplyMessage)
{
  if(TurnOnProMon==1)
  {
    ULONG *ptr;
       ULONG i;
    ULONG uactLength;
    PLPCP_PORT_OBJECT  LPCProt;
    PCHAR aProcessName=ExAllocatePool(NonPagedPool,256);
    PUNICODE_STRING pustr;//LPC设备名
    PUNICODE_STRING uRealName=ExAllocatePool(NonPagedPool,1024);//字符串".\\RPC Control\\ntsvcs"
    ANSI_STRING aPustr={0};//LPC设备名
    UNICODE_STRING uProcessPath={0};//进程路径
    ANSI_STRING aProcessPath={0};//进程路径
    HANDLE PId;//进程PID
    PCWSTR ProcessName=ExAllocatePool(NonPagedPool,256);//进程名
    pustr = ExAllocatePool(NonPagedPool,1024+4);//LPC设备名
    RtlInitUnicodeString(uRealName,L"\\RPC Control\\ntsvcs");
    ObReferenceObjectByHandle(PortHandle,(ACCESS_MASK)PROCESS_ALL_ACCESS,NULL,KernelMode,(PVOID *)&LPCProt,NULL);//获取对象
    ObQueryNameString(LPCProt->ConnectionPort,pustr,512,&uactLength);
    RtlUnicodeStringToAnsiString(&aPustr,pustr,TRUE);
    //--------------------------------------------------------------------------//进程信息
    PId=PsGetCurrentProcessId();
    ProcessName = GetCurrentProcessFileName();//获得当前进程完整路径   
    RtlInitUnicodeString(&uProcessPath,ProcessName);
    RtlUnicodeStringToAnsiString(&aProcessPath,&uProcessPath,TRUE);
    strcpy(aProcessName,aProcessPath.Buffer);
    if(!(RtlCompareUnicodeString(pustr,uRealName,TRUE)))
    {
      {
        ptr=(ULONG *)(RequestMessage->MessageData);
              for (i=0; i<RequestMessage->ActualMessageLength/sizeof(ULONG); i++)
        {
                DbgPrint("%x ", ptr[i]);//输出数据
                }
        //if(ptr[0]==0x01&&ptr[1]==0x1f0241)//有点问题
                   if(ptr[1]==0x1f0241)
        {
          DbgPrint("进程%s开启服务",aProcessName);//输出数据
          strcpy(LastCalled_Path,aProcessName);
          LastCalled_Pid=PId;
        }
      }
    }
    return RealZwRequestWaitReplyPort(PortHandle,RequestMessage,ReplyMessage);
  }
  else
        return RealZwRequestWaitReplyPort(PortHandle,RequestMessage,ReplyMessage);
}

结果:


[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

上传的附件:
收藏
点赞6
打赏
分享
打赏 + 10.00雪花
打赏次数 1 雪花 + 10.00
 
赞赏  黑手鱼   +10.00 2020/07/08 非常棒
最新回复 (3)
雪    币: 860
活跃值: (329)
能力值: ( LV9,RANK:165 )
在线值:
发帖
回帖
粉丝
futosky 3 2011-6-24 22:17
2
0
大牛们给点意见吧,不知道这个思路是不是已经很流行了,反正我之前搜不到,包括一些代码细节
雪    币: 210
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
灰狐 2011-6-25 01:00
3
0
囧,现在的主动防御还有分不清哪个进程创建的服务?拦截个RPC就行了,我曾经用这个方法获取系统对外的DNS请求,很好用。
雪    币: 860
活跃值: (329)
能力值: ( LV9,RANK:165 )
在线值:
发帖
回帖
粉丝
futosky 3 2011-6-25 12:20
4
1
呵呵,就见到过360可以,更多的HIPS可能我测试的不够多,可供参考的源代码至少的我没见到过.

包括置顶的那个KsBinSword,当然,业余作品不可与专业产品相提并论,还有一些个人版的主动防御,都是services.exe,做个总结而已,因为找不到有详细解释的代码,可能都不屑于吧,呵呵
游客
登录 | 注册 方可回帖
返回