VB小子玩转驱动程序(9):进程保护
作者:胡文亮
注1:本文只适合并针对初级的VB编程爱好者阅读,请所谓的“大牛”飘过。 注2:本文可能会有技术性错误,但是我尽可能保证本文的正确性。 注3:欢迎各位转载,转载最好注明出处和作者。
进程保护是很老的话题了,但也是很重要的话题,一切的杀毒软件/Rootkit,都有各种各样的进程保护。广义的进程保护很多,进程防杀/进程隐藏/进程注入/线程注入都算。不过只讲狭义的进程保护:进程防杀。 我们看看著名软件“冰刃”的作者“pjf”在文章中是怎样描述进程被结束的的过程的: 首先获得目标的进程ID,接着利用OpenProcess获取进程句柄(确保足够权限),最后将句柄传给TerminateProcess了结那个进程.
1: OpenProcess通过本机系统服务接口进入核心态,随后调用ntoskrnl的NtOpenProcess.在服务函数里,系统使用 SeSinglePrivilegeCheck检查调用者是否有DEBUG权限(SeDebugPrivilege),若有,则修改 AccessState使得在后面的操作中获取允许任意进程访问操作的句柄.最后通ObOpenObjectByName或者 PsLookupProcess***+ObOpenObjectByPointer来打开进程(创建并返回进程句柄).
2: TerminateProcess通过本机系统服务接口进入核心态,随后调用ntoskrnl的NtTerminateProcess.系统首先调用 ObReferenceObjectByHandle获取进程执行体块,执行体块的DebugPort指出进程是否处于调试状态,若处于调试状态且传入的 ExitStatus为DBG_TERMINATE_PROCESS则返回失败禁止结束进程.如果不是的话则转入系统服务进入正题:
系统利用ThreadListHead枚举进程的每一个线程,使用PspTerminateThreadByPointer来结束它们.注意并不是对每个线程系统都会忠实地执行你的命令:若枚举到的线程是系统线程则不会继续执行而是返回STATUS_INVALID_PARAMETER.判断的方法是线程的Teb为零或者Teb的值在内核地址空间.有人问2000下为何csrss.exe进程杀不死,很简单,打开IceSword,在进程栏利用右键菜单的"线程信息"看一下,看到那几个Teb为零的线程没有?(注意是针对windows2000,XP下不同.另外一点就是csrss中其它非系统线程的线程是很容易被杀死的,试图结束csrss时也可以看到在Teb为零的线程前面的线程已被杀掉,只是操作停在了Teb为零的线程这里) 再看看system进程,呵呵.IceSword也并未提供杀除这种进程的功能,因为觉得没有需求.在最后一个线程结束时,进程的生命也结束了,随着 PspExitProcess/ObKillProcess灰飞烟灭.
另一方面,线程是怎样结束的呢.PspTerminateThreadByPointer并不是直接"杀掉"指定线程,实质上线程是"自杀"的,呵呵.系统简单的使用KeInitializeApc/KeInsertQueueApc插入了一个核心态的APC调用,若是用户线程,会再插入用户态的 APC调用,最终线程在自己的执行环境中使用PspExitThread(...=>KeTerminateThread=> KiSwapThread)悲壮的自行了断.
具体名词看不懂没关系,注意几个函数就可以了:NtOpenProcess、PsLookupProcess***、ObOpenObjectByPointer、NtTerminateProcess、ObReferenceObjectByHandle、PspTerminateThreadByPointer、KeInsertQueueApc。在上述的几个函数中,随便找一个做手脚的话,就不能顺利地结束进程了,看清楚,是“不能顺利地结束进程”,不是“不能结束进程”。我个人比较喜欢挂钩ObReferenceObjectByHandle,因为挂钩这个函数的简单,而且效果很好,在用户态下几乎没有任何办法可以杀死了(“设置父母”和“线程消息洪水攻击”不算)。 我们的处理流程是这样的:修改ObReferenceObjectByHandle的前五字节,使别的程序使用到这个函数时无条件转跳到我们的伪函数(Fake_ObReferenceObjectByHandle)中。当发现有别的进程使用到ObReferenceObjectByHandle时,先使用真正的ObReferenceObjectByHandle(Real_ObReferenceObjectByHandle)获取对象。如果发现获取的对象正是我们要保护进程的EPROCESS,把对象“解除参照”(使用ObDereferenceObject),并返回“查询失败”,这样别人就无法用普通手段结束我们的进程了;如果不是的话,就返回执行Real_ObReferenceObjectByHandle函数的执行结果。 HOOK一个函数比较复杂,稍有不慎就会导致蓝屏(原因很复杂,这里不解释)。但是有句话不知道大家注意了没有: 注意并不是对每个线程系统都会忠实地执行你的命令:若枚举到的线程是系统线程则不会继续执行而是返回STATUS_INVALID_PARAMETER
怎么判断是不是系统线程呢?PspTerminateThreadByPointer简单检查ETHREAD的CrossThreadFlags偏移的值是不是0x10,如果是,那就认为是系统线程。这就好办了,我们直接把自身线程ETHREAD的CrossThreadFlags偏移的值改为0x10即可: *(PULONG)((ULONG)ETHREAD+0x248)=0x10;
附件是Hook ObReferenceObjectByHandle的例子,修改自身线程为系统线程的代码还请读者自行添加。 附件中在Fake_ObReferenceObjectByHandle中的处理方式有点特别,不是对比EPROCESS,而是对比PID。 |