文 / 温玉洁
生活在网络时代,无论是作为一名程序员抑或是作为一名普通的电脑使用者,对病毒这个词都已经不再陌生。网络不仅仅是传播信息的快速通道,从另外一个角度来看,也是病毒得以传播和滋生的温床,有资料显示,未安装补丁的Windows操作系统连接至internet平均10-15分钟就会被蠕虫或病毒感染。各种类型的病毒,在人们通过网络查阅信息、交换文件、收听视频时正在悄悄地传播。这些病毒或蠕虫不仅在传播过程中消耗大量的带宽资源,而且会干扰系统功能的正常使用或造成数据丢失、甚至是硬件损坏,每个电脑用户几乎都有过系统被病毒感染而无法正常使用的经历,大部分企业用户也都有过因病毒发作致使业务系统不能正常运行的经历。病毒距离我们,其实并不遥远。
然而,不只普通用户在面对各种夸大的报道和宣传后感觉到茫然和恐惧,随着计算机各个领域的细分和专业化,就连一些职业的程序员对病毒技术也缺乏深入的了解。病毒,不过是精心设计的一段程序,是编程技巧和优化技术的集中体现,是挑战技术极限、无所不用其极的一种编程技术。其实病毒技术中的优化和各种精巧的构造,也完全可以在一些特殊的情况下使用,使得某些编程工作得以简化;从另外一个角度来看,只有充分了解病毒技术,才能更好地研究应对之策,知己知彼,方能百战不殆。
病毒不是某个系统下的专属品,事实上现在各种流行的操作系统:从最初的Unix系统到其各种变体如Linux、Solaris、AIX、OS2等,从Windows到CE、Sybian等嵌入式系统,甚至是在某些专业化的大型机系统上,都无一例外地出现了病毒,各种平台下病毒的基本原理类似的,但是针对不同系统的特性,实现可能区别很大,原因在于作为一种无所不用其极的技术,势必利用各种系统相关的功能或弱点以取得各种特权和资源。正如生物的多样性一样,病毒种类繁多:包括源代码病毒、宏病毒、脚本病毒以及与各种系统可执行文件系统相关的病毒等。本文将以使用最为广泛的Windows操作系统下的PE病毒为例,说明病毒技术的原理以及实现技术,驱散笼罩在病毒技术上的迷雾。
* 病毒、蠕虫、恶意代码
传统意义上的病毒是具有类似生物病毒特征的特殊代码或程序,具有两个最基本的特点:自我复制和自动传播。蠕虫,广义上一般被认为是病毒的子类,同样具有自我复制和传播的特性,但鉴于蠕虫通常利用系统漏洞而非感染文件系统进行传播的特殊性,通常将其单独作为一类。一般认为区分蠕虫和传统病毒的分类标准是看其是否依赖于宿主程序进行感染和传播,如果必须依附于宿主程序才能进行感染和传播的才是病毒。不过定义不是绝对的,当今病毒和蠕虫技术的融合愈益深入,界限愈益模糊。很多病毒采用了很多的蠕虫传播技术,蠕虫也不仅仅通过系统漏洞传播,同时也通过感染文件系统进行传播。此外还有有相当一部分程序虽然不具备自我复制和自我传播的特征,但却执行了未经用户许可的代码、做了未经用户许可的事情,比如特洛伊木马等间谍软件、浏览器恶意脚本、一些广告软件等,显然无法将其定义为传统的病毒或蠕虫,他们和蠕虫、病毒一样,同属于一个更大的范畴——恶意代码。本文重点阐述传统病毒经常使用的技术。
* 病毒简史
谈病毒技术,无法回避病毒产生的历史。早在1949年在冯·诺伊曼的一篇论文《复杂自动装置的理论及组织的行为》中,即预见了可自我繁殖程序出现的可能。而现在众所公认的病毒的萌牙于AT&T(贝尔实验室)几个年轻的天才程序员编制的磁芯大战(CoreWar)游戏程序,已经具备了病毒的一些特征。随后相关的实验和研究在一些学者和天才的程序员中开始展开,正是这些创造了计算机系统的天才们,制造了计算机病毒。很难考证第一个真正的病毒出现在何时何地,但在20世纪80年代,随着个人计算机的普及,病毒已经开始流行了,早期的计算机病毒是和当时的文件交换方式和操作系统特点联系在一起的,那个时候发行软件或交换文件主要通过软盘进行,系统是基于文本界面的Unix或DOS,网络尚未普及,因此这一时期的病毒大都是引导区病毒和文件型病毒,前者通过替换系统引导区代码在系统启动时获取执行权,后者通过修改可执行文件嵌入代码以在可执行文件执行时获取控制权,更多病毒的则是二者的结合。IBM-PC的流行和MS DOS系统的普及使得DOS病毒在这一阶段逐渐占据了统治地位。80年代后期因特网开始进入人们的视野,这时也出现了第一个因特网蠕虫——莫里斯蠕虫,借助于系统漏洞通过网络进行快速传播。90年代随着电脑及网络的进一步普及,病毒技术也有了很大的进步,这在很大程度上也是由于病毒受社会的关注程度以及反病毒软件的进步,进一步刺激了病毒制作者群体的创造欲望,多态和变形技术开始出现,以对抗杀毒软件的特征码扫描。DOS操作系统病毒的绝对数量出现了爆炸性增长,但90年代后期随着Windows的出现,DOS病毒和引导区病毒逐渐走向消亡,Windows病毒随之则开始大量涌现,随着微软Office软件的普及宏病毒出现了,各种脚本病毒也日益增多。因特网的普及在给人们带来便利的同时也加快了病毒传播的速度和范围,靠Emai传播的蠕虫开始增多,时至今日仍然是蠕虫的重要传播途径。从2000年至今,在进入21世纪的头几年里,Windows下PE病毒技术已经日益纯熟、数量日益增多,但病毒排行榜的首位已经让位给利用各种系统漏洞进行传播的蠕虫了,安全研究的深入、各种安全漏洞的大量披露给蠕虫作者提供了很好的素材,特洛依木马等恶意软件数量呈现几何级数的增长,病毒作者的关注点重新从Windows桌面系统转向Unix系统、手机等嵌入移动设备上。安全研究也愈益受到社会的关注,病毒和反病毒的战争仍在继续,在可预见的将来,仍将继续。
不过,Windows PE文件病毒仍然占有非常大的比重。
* Windows平台和PE文件格式
Windows平台是当今最为流行的桌面系统,在服务器市场上,也占有相当的份额。其可执行文件(普通的用户程序、共享库以及NT系统的驱动文件)采用的是PE(Portable Executebale)文件格式。病毒要完成各种操作,在Windows系统上一般都是通过调用系统提供的API进行的,以保证在各种Windows版本上都能运行,因此读者应对基本的API比较熟悉。病毒要实现对宿主程序的感染,就不可避免地要修改PE文件,因此要求读者对PE文件格式有一定的了解,PE文件格式是一种复杂的文件格式,本文并不准备详细讲述PE文件格式,仅作在必要处简单的介绍,如必要可进一步参阅相关资料[1][2][3]。PE文件结构和头部部分主要域的格式如下图1所示。由图1可见,PE文件是由文件头、节表、包含各种代码和数据的节构成。文件头中定义了PE文件的引入函数表、引出函数表、节数目、文件版本、文件大小、所属子系统等相关的重要信息。节表则定义了实际数据节的大小、对齐、内存到文件如何进行映射等信息。后面的各个节则包含了实际的可执行代码或数据。
图1 PE文件结构及部分主要域的定义
* PE病毒技术剖析
典型的PE病毒修改PE文件,将病毒体代码写入PE文件文件中,更新头部相关的数据结构,使得修改后的PE文件仍然是合法PE文件,然后将PE入口指针改为指向病毒代码入口,这样在系统加载PE文件后,病毒代码就首先获取了控制权,在执行完感染或破坏代码后,再将控制权转移给正常的程序代码,这样病毒代码就神不知鬼不觉地悄悄运行了。染毒后的PE文件运行过程一般图2所示:
图2 染毒后的程序执行流程
这只是最常见的执行流程,事实上,随着反病毒技术的进展,更多的病毒并不是在程序的入口获取控制权,而是在程序运行中或退出时获取控制权,以逃避杀毒软件的初步扫描,这种技术又被称为EPO技术,将在本文后半部分进行介绍。病毒代码一般分成几个主要功能模块:解码模块、重定位模块、文件搜索模块、感染模块、破坏模块、加密变形模块等,不同的病毒包含模块不一定相同,比如解码、加密变形等就是可选的;但文件搜索和感染模块是几乎每个PE病毒都具备的,因为自我复制我传播是病毒的最基本的特征。有些病毒还可能实现了其他的模块,比如Email发送、网络扫描、内存感染等。一段典型的PE病毒代码执行流程大致如下图3所示:
图3 一段典型的病毒代码执行流程
从原理上看病毒非常简单,但实现起来还有不少困难,其实如果解决了这些技术难点,一个五脏俱全的病毒也就形成了,本文后面将从一个病毒编写者的角度就各个难点分别予以介绍。病毒可采用的技术几乎涉及到Windows程序设计的所有方面,但限于篇幅,本文亦不可能全部介绍,本文将重点介绍Win32用户模式病毒所常用的一些技术。
* 编程语言
任何语言只要表达能力足够强,都可用于编写PE病毒。但现存的绝大部分PE病毒都是直接用汇编编写的,一方面是因为汇编编译后的代码短小精悍,可以充分进行人工优化,以满足隐蔽性的要求;另外一方面之所以用汇编是因为其灵活和可控,病毒要同系统底层有时甚至是硬件打交道,由于编译器的特点不尽相同,用高级语言实现某些功能甚至会更加麻烦,比如用汇编很方便地就可以直接进行自身重定位、自身代码修改以及读写IO端口等操作,而用高级语言实现则相对烦琐。用汇编还可以充分利用底层硬件支持的各种特性,限制非常少。但是用汇编编写病毒的主要缺点就是编写效率低,加上使用各种优化手段使得代码阅读起来相当困难,不过作为一种极限编程技术,对病毒作者而言,这些似乎都已经不再重要。本文假设读者熟悉汇编语言,各种举例使用Intel格式的汇编代码,编译器可使用MASM或FASM进行编译,由于汇编语言表述算法较为不便,因此算法和原理性表述仍然采用C语言。在讲述各种技术时,部分代码直接取自病毒Elkern的源代码,该病毒在2002年曾经大规模流行,其代码被收录于著名病毒杂志29A第7期中,有兴趣的读者可参阅其完整代码。
* 重定位
病毒自身的重定位是病毒代码在得以顺利运行前应解决的最基本问题。病毒代码在运行时同样也要引用一些数据,比如API函数的名字、杀毒软件的黑名单、系统相关的特殊数据等,由于病毒代码在宿主进程中运行时的内存地址是在编译汇编代码时无法预知的,而病毒在感染不同的宿主时其位于宿主中的准确位置同样也无法提前预知,因此病毒就要在运行时动态确定其引用数据的地址,否则,引用数据时几乎肯定会发生错误。对于普通的PE文件比如动态链接库而言,在被加载到不同地址处时由加载器根据PE中一个被称为重定位表的特殊结构动态修正引用数据指令的地址,而重定位表是由编译器在编译阶段生成的,因此动态链接库本身无需为此做任何额外处理。病毒代码则不同,必须自己动态确定需引用数据的地址。比如一段病毒代码被加载在0x400000处,地址0x401000处的一条语句及其引用的数据定义如下所示,相关地址是编译器在编译时计算得到的,这里假设编译时预设的基地址也是0x400000:
401000:
mov eax,dword ptr [402035]
......
402035:
db "hello world!",0
如果病毒代码在宿主中也加载到基地址0x400000,显然是能够正常执行的,但如果这段代码被加载在基地址0x500000运行时则出错,对病毒而言,这是大多数时候都会遇到的情况,因为指令中引用的仍然是0x402035这个地址。如果病毒代码不是在宿主进程中而是作为一个具有重定位表的独立PE文件运行,正常情况下由系统加载器根据重定位表表项将 mov eax,dword ptr [402035]中的0x402035修改为正确值0x502305,这样这句代码就变成了mov eax,dword ptr [5402035],程序也就能准确无误地运行了。不过很可惜,对在其它进程内运行病毒代码而言,必须采取额外的手段、付出额外的代价感染宿主PE文件时就及时加以解决,否则将导致宿主进程无法正常运行。
至少有两种方法可以解决重定位的问题:
A)第一种方法就是利用上述PE文件重定位表项的特殊作用构造相应的重定位表项。在感染目标PE文件时,将引用自身数据的需要被重定位的地址全部写入目标PE文件的重定位表中,如果目标PE无任何重定位表项(如用MS linker的/fixed)则创建重定位表节并插入新的重定位项;若已经存在重定位表项,则在修改已存
恶意代码的亲密接触——病毒编程技术(上)
最新推荐文章于 2023-02-01 17:12:44 发布
本文介绍了Windows平台上的PE病毒技术,包括病毒的历史、PE文件格式、病毒的编程语言选择、重定位、API函数地址获取、文件搜索与感染策略。文章详细探讨了病毒如何利用API函数、PE文件结构进行自我复制和传播,并提到了如何避开杀毒软件的检测。此外,还讨论了PE文件的修改方法,如附加在文件尾部、利用空隙空间等,以及如何处理文件校验以确保病毒代码能够正确执行。最后,文章强调了了解病毒技术对于防范和应对的重要性。
摘要由CSDN通过智能技术生成