预计阅读本页时间:-
9.8.2 反病毒和抑制反病毒技术
正如上文所提到的,防火墙会尽量地阻止入侵者进入电脑,但是在很多情况下防火墙会失败。在这种情况下,下一道防线是由反恶意软件的程序(antimalware program)组成的。尽管这种反恶意软件的程序同样可以对抗蠕虫和间谍软件,但是它们通常称做反病毒程序(antivirus program)。病毒尽量地隐藏自己,而用户则是努力地发现它们,这就像是一个猫捉老鼠的游戏。在这方面,病毒很像rootkit,不同的地方是病毒的制造者更强调的是病毒的传播速度而不是像rookit一样注重于捉迷藏。现在,让我们来看看反病毒软件所使用的技术,以及病毒的制造者Virgil是怎么应对这些技术的。
1.病毒扫描器
显然,一般用户没有去查找竭尽全力藏身的大多数病毒,所以市场上出现了反病毒软件。下面我们将讨论一下反病毒软件的工作原理。反病毒软件公司拥有一流的实验室,在那里许多专家长时间地跟踪并研究不断涌现出的新病毒。第一步是让病毒感染不执行任何操作的程序,这类程序叫做诱饵文件,然后获取病毒的完整内容。下一步是列出病毒的完全代码表把它输入已知病毒的数据库。公司之间为其数据库的容量而竞争。发现新的病毒就放到数据库中与体育竞赛是完全不同的。
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
一旦反病毒软件安装在用户的计算机里,第一件事就是在硬盘里扫描所有可执行文件,看看是否能发现病毒库里已知的病毒。大多数反病毒公司都建有网站,从那里客户可以下载新发现病毒的特征码到自己的病毒库里。如果用户有10 000个文件,而病毒库里有10 000种病毒,当然需要一些高效的代码使得程序得以更快地运行。
由于有些已知病毒总是在不断发生细微变化,所以人们需要一种模糊查询软件,这样即便3个字节的改变也不会让病毒逃避检测。但是,模糊查询不仅比正常查询慢,而且容易导致错误报警(误测)。7年前在巴基斯坦,有些合法的文件恰巧包含了与病毒代码极为相像的字符,结果导致了病毒报警。用户这时往往会看到下面的信息:
WARNING!File xyz.exe may contain the lahore-9x virus.Delete?
数据库里的病毒越多,扫描标准越宽松,误报警的可能性就越大。如果出现了太多的误报警,用户会因为厌烦而放弃使用。但是如果病毒扫描器坚持严格匹配病毒码,它就会错过许多变形病毒。解决办法是要达到一种微妙的平衡,完美的扫描软件应该识别病毒的核心代码,这些核心代码不会轻易改变,从而能够作为病毒的特征签名来查找。
由于磁盘里的文件上周被宣布无病毒感染后并不意味着现在仍未被感染,所以人们需要经常使用病毒扫描。因为扫描速度很慢,所以要保持效率就应该仅对上次扫描后被改动的文件进行检查。但是,聪明的病毒会把感染过的文件日期重置为初始日期以逃避检验。于是,反病毒程序修改校验文件所在目录的日期。但是病毒接着又把目录的日期也改掉。这就像我们上面所提到的猫捉老鼠游戏一样。
反病毒软件的另一种方法是检测文件,记录和存放所有文件的长度。如果一个文件自上周以来突然增加了许多,就有可能被感染,如图9-32a所示。但是,聪明的病毒可通过程序压缩原有文件并将其填充到原有长度来逃避检查。要使这种方法奏效,病毒必须还要包含压缩和解压缩过程,如图9-32c所示。

病毒还有一种逃避检测办法,那就是让自己在磁盘里呈现出的特征与病毒数据库里的特性不尽相同。要达到这一目标,方法之一是每感染一个文件就用不同的密钥将自身加密。在复制新的病毒体之前,病毒先随机产生一个32位的加密密钥,如将当前时间与内存里诸如72 008和319 992等数字进行异或。然后将病毒代码与这一密钥逐字节地异或,加密后的结果值储存在被感染文件中,如图9-32d所示。密钥也同时存放在文件中。从保密性角度来说,把密钥放进文件是不明智的。这样做的目的无非是为了对付病毒扫描,但却不能防止专家在反病毒实验室里逆向破解出病毒代码。当然,病毒在运行时必须首先对自己解密,所以在文件里也同时需要解密过程。
上述策略实际上并不完善,因为压缩、解压缩、加密和解密等过程在复制每个病毒体时都是一样的,反病毒软件可以利用这一特征来查杀病毒。把压缩、解压缩和加密过程隐藏起来较为容易:只要对它们加密并存放在病毒体里,如图9-32e所示。但是,解密过程不能被加密,它必须运行在硬件上以便将病毒体的其余部分解密,所以必须用明文格式存放。反病毒软件当然知道这些,所以它们专门搜索解密过程。
然而,Virgil喜欢笑到最后,所以他采用了下面的步骤。假设解密过程需要进行如下运算:
X=(A+B+C-4)
在普通的双地址计算机上可以运用汇编语言编写该运算,如图9-33a所示。第一个地址是源地址;第二个地址是目标地址,所以MOV A,R1是把变量A放入寄存器R1中。图9-33b的代码也是同样的意思,不同之处仅仅在于代码中插入了NOP(无操作)指令而降低了效率。
现在整个编码工作还未完成。为了伪装解密代码,可以用许多方法来替代NOP。例如,把0加入寄存器、自身异或、左移0位、跳转到下一个指令等,所有的都不做任何操作。所以,图9-33c在功能上与图9-33a是相同的。当病毒复制自身时,往往采用图9-33c的代码而不是图9-33a,这样在日后运行时还能工作。这种每次复制时都发生变异的病毒叫做多形态病毒(polymorphic virus)。
现在假设在这段代码里不再需要R5寄存器。也就是说,图9-33d与图9-33a的功能一致。最后,在许多情况下,可以交换指令而不会改变程序功能,我们用图9-33e作为另一种与图9-33a在逻辑上保持一致的代码段。这种能够交换机器码指令而不影响程序功能的代码叫做变异引擎(mutation engine)。较复杂的病毒在复制病毒体时,可以通过变异引擎产生不同的解密代码。变异的手段包括插入一些没用而且没有危害的代码,改变代码的顺序,交换寄存器,把某条指令用它的等价指令替换。变异引擎本身与病毒体一起也可以通过加密的方法隐藏起来。

要求较差的反病毒软件意识到图9-33a至图9-33e具有相同的代码功能是相当困难的,特别是当变异引擎有能力“狡兔三窟”时。反病毒软件可以分析病毒代码,了解病毒原理,甚至可以试图模拟代码操作,但我们必须记住有成千上万的病毒和成千上万的文件需要分析,所以每次测试不能花费太多的时间,否则运行起来会惊人地慢。
另外,储存在变量Y里的值是为了让人们难以发现与R5有关的代码是死码的事实,死码不会做任何事情。如果其他代码段对Y进行了读写,代码就会看上去十分合法。一个写得十分好的变异引擎代码会产生极强的变种,会给反病毒软件的作者带来噩梦般的麻烦。惟一让人安慰的是这样的引擎很难编写,所以Virgil的朋友都使用他的代码,结果在病毒界里并没有种类繁多的变异引擎。
到目前为止,我们讨论的是如何识别被感染的可执行文件里的病毒。而且,反病毒扫描器必须检查MBR、引导扇区、坏扇区列表、闪速ROM、CMOS等区域。但是如果有内存驻留病毒在运行会怎样呢?该内存驻留病毒不会被发现。更糟的是假设运行的病毒正在控制所有的系统调用,它就能轻易地探测到反病毒程序正在读引导扇区(用以查找病毒)。为了阻止反病毒程序,病毒进行系统调用,相反它把真正的引导区从坏扇区列表的藏身之地返回。它也可以作记录,在被扫描器检查以后会再次感染所有的文件。
为了防止被病毒欺骗,反病毒程序也可以会跳过操作系统直接去读物理磁盘。不过这样做需要具有用于IDE、SCSI和其他种类硬盘的内置设备驱动程序,这样会降低反病毒程序的可移植性,遇到不通用的硬盘就会一筹莫展。而且,跳过操作系统来读取引导扇区是可以的,但是跳过操作系统来读取所有的可执行文件却是不可能的,所以仍然存在病毒产生出与可执行文件相关的欺骗性数据的危险。
2.完整性检查程序
另一种完全不同的病毒检测方法是实施完整性检查(integrity checking)。采用这种方法的反病毒程序首先扫描硬盘上的病毒,一旦确信硬盘是干净的,它就开始为每个可执行文件计算一个校验和。计算校验和的算法应该是很简单的,就像把程序段中的所有字作为32位或者64位整数加起来求和一样简单,但是这种算法也要像加密的散列算法一样,是不可能逆向求解的。然后,要把一个目录中的所有相关文件的校验和写到一个文件中去。下一次运行的时候,程序重新计算校验值,看是否与校验和文件里的值相匹配。这样被感染的文件会立刻被查出。
问题在于Virgil并不愿意让病毒被查出,他可以写一段病毒代码把校验和文件移走。更糟的是,他可以计算已感染病毒的文件校验值,用这一值替代校验和文件里的正常值。为了保护校验值不被更改,反病毒程序可以尝试把该文件藏起来,但对长时间研究反病毒程序的Virgil来说,这种方法也难以奏效。比较好的方法是对文件加密以便使得其上的破坏容易被发现。理想状态是加密采用了智能卡技术,加密密钥被放在芯片里使得程序无法读到。
3.行为检查程序
第三种反病毒程序使用的方法是实施行为检查(behavioral checking)。通过这种方法,反病毒程序在系统运行时驻留在内存里,并自己捕捉所有的系统调用。这一方法能够监视所有的系统活动,并试图捕捉任何可能被怀疑的行为。例如,通常没有程序会覆盖引导扇区,所以有这种企图的程序几乎可以肯定是病毒。同理,改变闪速ROM的内容也值得怀疑。
但是也有些情况比较难以判断。例如,覆盖可执行文件是一个特殊的操作,除非是编译器。如果反病毒程序检测到了这样一个写的动作并发出了警告,它希望用户能根据当时情形决定是否要覆盖可执行文件。同样,当Word用一个全是宏的新文件重写.doc文件时不一定是病毒的杰作。在Windows中程序可以从可执行文件里分离出来,并使用特殊的系统调用驻留内存。当然,这也可能是合法的,但是给出警告还是是十分有用的。
病毒并不会被动地等着反病毒程序杀死自己,它们也会反击。一场特别有趣的战斗会发生在内存驻留病毒和内存驻留反病毒程序之间。多年以前,有一个叫做Core Wars的游戏,在游戏里两个程序员各自放置程序到空余的地址空间里。程序依次抢夺内存,目的是把对手的程序清理出去来扩大自己的地盘。病毒与反病毒程序之间的战斗就有点像这个游戏,而战场转换到了那些并不希望战斗发生的受害者的机器里。更糟的是,病毒有一个优势,它可以去买反病毒软件来了解对手。当然,一旦病毒出现,反病毒小组也会修改软件,从而逼迫Virgil不得不再买新的版本。
4.病毒避免
每一个好的故事都需要理念。这里的理念是:
与其遗憾不如尽量安全。
避免病毒比起在计算机感染后去试图追踪它们要容易得多。下面是一些个人用户的使用指南,这也是整个产业界为减轻病毒问题所做的努力。
用户该怎样做来避免病毒感染呢?第一,选择能提供高度安全保障的操作系统,这样的系统应该拥有强大的核心-用户态边界,分离提供每个用户和系统管理员的登录密码。在这些条件下,溜进来的病毒无法感染系统代码。
第二,仅安装从可靠的供应商处购买的最小配置的软件。有时,即使这样也不能保证有些软件公司雇员会在商业软件产品里放置病毒,但这样做会有较大的帮助。从Web站点和公告板下载软件是十分冒险的行为。
第三,购买性能良好的反病毒软件并按指定要求使用。确保能够经常从厂商站点下载更新版本。
第四,不要点击电子邮件里的附件,告诉他人不要发送附件给自己。使用简明ASCII文本的邮件比较安全,而附件在打开时可能会启动病毒程序。
第五,定期将重要文件备份到外部存储介质,如软磁盘、CD-R或磁带等。在一系列的备份介质中应该保存不同的版本。这样,当发现病毒时就有机会还原被感染前的文件。例如,假设还原昨天已被感染的备份版本不成功的话,还原上一周的版本也许会有用。
最后一点,抵抗住诱惑,不要从一个不了解的地方下载并运行那些吸引人的新免费软件。或许这些软件免费的原因是:它的制造者想让你的机器加入他的僵尸机器的大军中来。然而,如果你有虚拟机软件的话,在虚拟机中运行这些不了解的软件是安全的。
整个业界应该重视病毒并改变一些危险的做法。第一,制造简单的操作系统。铃声和口哨声越多,安全漏洞也越多,这就是现实。
第二,不要使用动态文本。从安全角度来说,动态文本是可怕的。浏览别人提供的文档时最好不要运行别人提供的程序。例如,JPEG文件就不包含程序,所以也就不会含有病毒。所有的文档都应该以这样的方式工作。
第三,应该采取措施将重要的磁盘柱面有选择性地写保护,防止病毒感染程序。这种方法必须在控制器内部放置位图说明,位图里含有受保护磁盘柱面的分布图。只有当用户拨动了计算机面板上的机械拨动开关后,位图才能够被改动。
第四,使用闪存是个好主意,但只有用户拨动了外部开关后才能被改动,如当用户有意识地安装BIOS升级程序的时候。当然,所有这些措施在没有遭受病毒的强烈攻击时,是不会引起重视的。例如,有些病毒会攻击金融领域,把所有银行账户的金额重置为0。当然,那时候再采取措施就太晚了。