隐藏是病毒的天性,在业界对病毒的定义里,“隐蔽性”就是病毒的一个最基本特征,任何病毒都希望在被感染的计算机中隐藏起来不被发现,因为病毒都只 有在不被发现的情况下,才能实施其破坏行为。为了达到这个目的,许多病毒使用了各种不同的技术来躲避反病毒软件的检验,这样就产生了各种各样令普通用户头 痛的病毒隐藏形式。由于木马后门的行为特征已具备病毒条件,因此这里把木马后门也统一归纳为病毒来描述。
开山鼻祖:隐藏窗口 & 隐藏进程 & 隐藏文件
在计算机流行的早期,计算机病毒和木马后门等危害程序在普通用户范围的普及并不是很广泛,这个时期的用户群对计算机和网络安全的防范意识可以说 是几乎没有的,普通用户的系统也多为脆弱的Windows 95/98系列和电话线拨号的慢速网络,而那一段时间正是外国木马“bo”和国产木马雏形“冰河”、“netspy”等在如今看来各方面技术都颇为简单的 远程控制软件大行其道的黄金时期,很多用户根本就没有防火墙和杀毒软件(即使有,也是以杀cih的为主),即使远方的黑客把用户的计算机翻了个底朝天,用 户也不会有所察觉,这一时期接触此类技术的人相对较少,因此并未造成如今这个病毒到处蔓延的局面。
因为这个阶段国内用户的机器环境仍然以Windows 9x为主流,所以病毒编写者们并不需要消耗太多的脑筋就可以做到让病毒悄无声息运行的效果,并让它在alt+del+ctrl呼出的任务管理器中不可见。
我们都知道,在Windows下运行的程序界面都被定义为“窗口”,程序通过这个途径与用户产生交互,每个完整的程序都必须拥有至少一个窗口, 但是如果编写者将这个窗口在运行期间设置为“不可见”呢?这样一来,用户就不会察觉到这个程序在桌面上运行了,但是如果有一定经验的用户打开任务管理器, 他就会因为发现系统里多出来的进程而产生怀疑,因此病毒编写者在这个时期采取了初级形式的隐藏手段:隐藏进程。
其实所谓隐藏进程,是利用微软未公开的一个api(application programming interface,应用程序接口)函数“registerserviceprocess”将自身注册为“服务进程”,而恰巧Windows 9x中的任务管理器是不会显示此类进程的,结果就被病毒钻了空子,让“冰河”等木马在国内大部分普通用户的机器上安家落户。
而早期后门技术里,还有一个最基本的行为就是隐藏文件,与今天的各种隐藏手段相比,它可谓是“不入流”级别了——这里提到的“隐藏”,就是简单 的将文件属性设置为“隐藏”而已,除此之外,再无别的保护手段了,然而,由于系统设计时为了避免初学者胡乱删除文件而默认“不显示系统和隐藏文件”的做法 (到了Windows 2000/xp时代,这个做法更升级到“隐藏受保护的系统文件”了),却恰好给这些病毒提供了天然的隐身场所——大部分对电脑操作不熟悉的用户根本不知道 “隐藏文件”的含义,更别提设置为“显示所有文件”了,在那个安全软件厂商刚开始探索市场的时代,用户更是不会留意太多安全产品及其实际含义,因而这个时 期成了各种初期木马技术发展的重要阶段,利用这种手段制作的木马被统称为“第一代木马”。
以现在的技术和眼光看来,这些早期技术作品的发现和清理是相对较简单的了,因为它们采用的“进程隐藏”技术在NT体系上的Windows2000/xp/2003等操作系统上已经无效了,直接使用系统自带的任务管理器便能发现和迅速终止进程运行,而后在“控制面板” ——“文件夹选项”里面设置“显示所有文件”和取消“隐藏受保护的系统文件”,就能发现那个被隐藏起来的木马程序了。对于Windows 9x用户,使用任意一款第三方的进程管理工具如“Windows优化大师”的进程管理组件即可轻松发现。
继续发展:使用线程注射技术的dll木马
虽然现在使用“线程注射”的木马病毒和流氓软件已经遍地开花了,但是从那个混沌时代经历过来的人都不会忘记首个采用“线程注射”的dll木马“广外幽灵”在当时所带来的恐惧,“线程注射”到底是种什么东西呢?下面就让我们来详细讲解一下。
首先,用户可能不会了解“线程”(thread)的意思,而要讲解“线程”,就不能不先提到“进程”(process)的概念。许多刚接触计算 机的用户无法理解“进程”是什么东西:常常听到高手说打开任务管理器关闭某某进程,但是一看到任务管理器列表里的一堆东西,头就大了。许多用户知道使用任 务管理器关闭一些失去响应的任务,但是如果某个任务没有在“应用程序”列表里出现,用户就不知所措了。到底什么是“进程”呢?“进程”是指一个可执行文件 在运行期间请求系统在内存里开辟给它的数据信息块,系统通过控制这个数据块为运行中的程序提供数据交换和决定程序生存期限,任何程序都必须拥有至少一个进 程,否则它不被系统承认。
进程从某一方面而言就是可执行文件把自身从存储介质复制在内存中的映像,它通常和某个在磁盘上的文件保持着对应关系,一个完整的进程信息包括很 多方面的数据,我们使用进程查看工具看到的“应用程序”选项卡包含的是进程的标题,而“进程”选项卡包含的是进程文件名、进程标识符、占用内存等,其中 “进程文件名”和“进程标识符”是必须掌握的关键,“进程标识符”是系统分配给进程内存空间时指定的唯一数字,进程从载入内存到结束运行的期间里这个数字 都是保持不变的,而“进程文件名”则是对应着的介质存储文件名称,根据“进程文件名”我们就可以找到最初的可执行文件位置。
任务管理器的“应用程序”项里列出来的“任务”,是指进程在桌面上显示出来的窗口对象,例如用户打开word 2003撰写文档,它的进程“winword.exe”会创建一个在桌面上显示的前台窗口,这个窗口就是任务管理器里看得见的“任务”了,而实际上真正在 运行的是进程“winword.exe”。并不是所有的进程都会在任务管理器里留下“任务”的,像QQ、msn 和所有后台程序,它们并不会在任务列表里出现,但是你会在进程列表里找到它们,如果要它们在任务列表里出现该怎么办呢?只要让它们产生一个在桌面上出现的 窗体就可以了,随便打开一个好友聊天,就会发现任务列表里终于出现了qq的任务。因此,真正科学的终止程序执行方案是针对“进程”来结束程序的运行,而不 是在任务列表里关闭程序,因为木马作者们是不会让自己的木马在任务列表里出现的,但是进程列表里一般人都是逃不过的。
而“线程”,则是在一个进程里产生的多个执行进度实例,举个简单例子,一个网络文件传输程序如果只有一个线程(单线程)运作,那么它的执行效率 会非常低下,因为它既需要从网络上读取文件数据,又需要把文件保存到磁盘,同时还需要绘制当前传输进度条,由于在代码的角度里这些操作只能一条条的顺序执 行,程序就不能很好的做到在保存数据的同时绘制传输进度条,即使程序员将其勉强凑到一块执行,在用户方面看来,这个程序的响应会非常缓慢甚至直接崩溃,而 “多线程”技术则是为了解决这种问题而产生的,采用“多线程”技术编写的应用程序在运行时可以产生多个同时执行的操作实例,例如一个采用“多线程”技术的 网络文件传输程序就能同时分出三个进度来同时执行网络数据传输、文件保存操作和绘制传输进度条的操作,于是在用户看来,这个程序运行非常流畅,这就是线程 的作用。在程序运行时,只能产生一个进程,但是在这个进程的内存空间(系统为程序能正常执行而开辟的独立内存领域)里,可以产生多个线程,其中至少有一个 默认的线程,被称为“主线程”,它是程序主要代码的运行部分。
那么,“线程注射”又是什么含义呢?其实它的全称是“远程线程注射”(remotethread injection),通常情况下,各个进程的内存空间是不可以相互访问的,这也是为程序能够稳定运行打下基础,这个访问限制让所有进程之间互相独立,这 样一来,任何一个非系统关键进程发生崩溃时都不会影响到其他内存空间里的进程执行,从而使nt架构的稳定性远远高于win9x架构。但是在一些特定的场合 里,必须让进程之间可以互相访问和管理,这就是“远程线程”技术的初衷,这个技术实现了进程之间的跨内存空间访问,其核心是产生一个特殊的线程,这个线程 能够将一段执行代码连接到另一个进程所处的内存空间里,作为另一个进程的其中一个非核心线程来运行,从而达到交换数据的目的,这个连接的过程被称为“注 射”(injection)。远程线程技术好比一棵寄生在大树上的蔓藤,一旦目标进程被注射,这段新生的线程就成为目标进程的一部分代码了,只要目标进程 不被终止,原进程无论是否还在运行都不会再影响到执行结果了。
与“线程注射”离不开的是“hook”技术,这个“hook”,又是什么呢?其官方定义如下:
钩子(hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。
钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩 子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。
在这里,木马编写者首先把一个实际为木马主体的dll文件载入内存,然后通过“线程注射”技术将其注入其他进程的内存空间,最后这个dll里的 代码就成为其他进程的一部分来实现了自身的隐藏执行,通过调用“hook”机制,这个dll木马便实现了监视用户的输入输出操作,截取有用的资料等操作。 这种木马的实际执行体是一个dll文件,由于Windows系统自身就包含着大量的dll文件,谁也无法一眼看出哪个dll文件不是系统自带的,所以这种 木马的隐蔽性又提高了一级,而且它的执行方式也更加隐蔽,这是由Windows系统自身特性决定的,Windows自身就是大量使用dll的系统,许多 dll文件在启动时便被相关的应用程序加载进内存里执行了,可是有谁在进程里直接看到过某个dll在运行的?因为系统是把dll视为一种模块性质的执行体 来调用的,它内部只包含了一堆以函数形式输出的模块,也就是说每个dll都需要由一个用到它的某个函数的exe来加载,当dll里的函数执行完毕后就会返 回一个运行结果给调用它的exe,然后dll进程退出内存结束这次执行过程,这就是标准的dll运行周期,而采用了“线程注射”技术的dll则不是这样, 它们自身虽然也是导出函数,但是它们的代码是具备执行逻辑的,这种模块就像一个普通exe,只是它不能直接由自身启动,而是需要有一个特殊作用的程序(称 为加载者)产生的进程把这个dll的主体函数载入内存中执行,从而让它成为一个运行中的木马程序。