牙龈肿有脓包一直不消:PE学习笔记(病毒的感染) - 虫子的专栏 - CSDN博客

来源:百度文库 编辑:中财网 时间:2024/04/27 11:10:32

PE学习笔记(病毒的感染)收藏

  通过一段时间对PE病毒的学习,对PE病毒的感染过程有了初步的了解。   PE文件格式概述    PE文件结构的总体层次分布如下所示 -----------------------|DOS MZ Header ||------------------------||DOS Stub     ||------------------||PE Header   ||------------------||Section Table ||------------------||Section 1      ||------------------||Section 2      ||------------------||Section ...   ||--------------||Section n     | --------------1.1 DOS HeaderPE文件最开始是一个简单的 DOS MZ header,它是一个 IMAGE_DOS_HEADER 结构。有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随 MZ Header 之后的 DOS Stub。 1.2  DOS Stub   DOS Stub 是一个有效的 DOS 程序。当程序在DOS下运时,输出象 "This program cannot be run in DOS mode" 这样的提示。 1.3 PE Header紧接着DOS Stub 的是 PE Header。它是一个 IMAGE_NT_HEADERS结构。其中包含了很多PE文件被载入内存时需要用到的重要域。执行体在支持PE文件结构的操作系统中执行时,PE装载器将从 DOS MZheader 中找到 PE header 的起始偏移量。 1.4 Section TablePE Header 接下来的数组结构 Section Table (节表)。如果PE文件里有5个节,那么此 Section Table 结构数组内就有5个成员,每个成员包含对应节的属性、文件偏移量、虚拟偏移量等。1.5 SectionsPE文件的真正内容划分成块,称之为sections(节)。Sections 是以其起始位址来排列,而不是以其字母次序来排列。通过节表提供的信息,我们可以找到这些节。  PE病毒的感染原理和方法1) 获取kernel32.dll的基地址。2) 通过所获得的kernel32的基地址通过dll的PE文件格式中的输出表获得以后要使用到的API的地址,并将他们存放在变量或是栈中,将来所调用的API直接通过CALL这些地址来实现。3) 查找要感染的文件并获得他们的基地址(通过CreateFile,CreateFileMapping,ViewOfMapFile将文件打开,当然调用的是getapi所找到的地址。其他过程和一般的程序没什么区别,所以就不写在这里了)。4) 通过获得的文件的基地址来根据文件的PE文件格式来将病毒代码注入到该文件中。 那么,病毒何以藏身? 2.1 病毒藏身之处——PE文件中大量的空白在PEHeader结构中的OptionalHeader结构中的成员FileAlignment的值是文件中节的对齐度,单位是字节,这个值应该是2的n次方,范围从512到64k。如果这里的值是512,那么PE文件中的节的长度都是512字节的整数倍,内容不够的部分用0填充。比如一个PE文件的FileAlignment为200h(十进制512),它的第一个节在400h处,长度为100h,那么从文件400h到500h中为这一节的内容,而文件对齐粒度是200h,所以为了使这一节长度为FileAlignment的整数倍,500h到600h会被用零填充。而下一个节的开始地址为600h。用16进制编辑器打开PE文件,就可以看到这种情况,PE文件头的内容结束到第一个节开始之间的地方,每一个节中内容结束到下一节开始的地方都会有大量的空白。VC6编译链接时默认的FileAlignment为1000h(4k),可以使用链接选项 /ALIGN:number来改变这个值。比如把4k改成512时,可以明显减小生成文件的大小。 2.2 PE病毒感染的两种方法:1.是通过给程序末尾添加一个节( section )来将代码写入搜索到最后一个节,计算新的的节大小,修改相应PE结构变量,并且把程序入口改为最后一个节的末尾(这就是准备写入病毒体的地方)。程序原入口要保留,虽然这里暂时不需要,但是病毒执行完毕后会需要。修改节属性,“代码节、可读、可写、可执行”。对齐,唯一要强调的还是对齐。一切就绪后,就开始copy病毒体了。上面这个方法是增加最后一个节的空间来寄生病毒,很初级可是不一定稳定:不能保证最后一个节就一定是你想要的“代码节、可读、可写、可执行”,毕竟这里除了你的代码,还有原程序的信息。稍微强一些的办法是增加一个节到最后(自己的节就好控制多了),然后修改相关PE结构变量。这个方法不错,不过问题在于,不能保证SECTIONTABLE里就一定有大于28h字节的空间给你来新建一个Image_Section_Table,一般这个空隙是因为对齐或者别的原因造成的。顺便提及一点,如果这个执行文件正在被使用,那么,可以通过“改名”+“新建”+“下次运行时删除”的办法替换原文件。 感染文件的基本步骤:
1.判断目标文件开始的两个字节是否为“MZ”。
2.判断PE文件标记“PE”。
3.判断感染标记,如果已被感染过则跳出继续执行HOST程序,否则继续。
4.获得Directory(数据目录)的个数,(每个数据目录信息占8个字节)。
5.得到节表起始位置。(Directory的偏移地址+数据目录占用的字节数=节表起始位置)
6.得到目前最后节表的末尾偏移(紧接其后用于写入一个新的病毒节)
节表起始位置+节的个数*(每个节表占用的字节数28H)=目前最后节表的末尾偏移。
7.开始写入节表
a)写入节名(8字节)。
b)写入节的实际字节数(4字节)。
c)写入新节在内存中的开始偏移地址(4字节),同时可以计算出病毒入口位置
上节在内存中的开始偏移地址+(上节大小/节对齐+1)×节对齐=本节在内存中的开始偏移地址。
d) 写入本节(即病毒节)在文件中对齐后的大小。
e) 写入本节在文件中的开始位置。
上节在文件中的开始位置+上节对齐后的大小=本节(即病毒)在文件中的开始位置。
f) 修改映像文件头中的节表数目。
g) 修改AddressOfEntryPoint(即程序入口点指向病毒入口位置),同时保存旧的AddressOfEntryPoint,以便返回HOST继续执行。
h) 更新SizeOfImage(内存中整个PE映像尺寸=原SizeOfImage+病毒节经过内存节对齐后的大小)。
i) 写入感染标记(后面例子中是放在PE头中)。
j) 写入病毒代码到新添加的节中。
ECX =病毒长度
ESI =病毒代码位置(并不一定等于病毒执行代码开始位置)
EDI=病毒节写入位置(后面例子是在内存映射文件中的相应位置)
k) 将当前文件位置设为文件末尾。 2.另一种是选取一个未用完的节将代码写入PE的代码映象分为几个SECTION,在文件中会对齐页边界(4K)。一般来说,文件会加载在400000h开始的空间,而第一个SECTION在401000h处,同时入口地址也是401000h。由高级语言编写的程序,每个SECTIO-N的长度不可能刚好是4K的倍数,因此在SECTION的末尾将会存在一段未用的空间,大小可由Section的PHYSICALSIZE-VIRTUALSIZE得到,在文件中起始位置可由 PHYSICAL OFFSET得到,这段空间可以用来存放病毒代码。此外一般来说,MZ Header+DOS STUD+PEHEADER+OPTIONAL HEADER+SECTION TABLE不过1K左右,而SECTION 1由4K开始,空出来的地方足够存放一个设计精良的病毒。CIH就是将代码存放在这些空闲空间里。 相对第一种“后缀式”,这种感染方式称为“散落式”,英文叫cavity:“把病毒体切成小块分散插入到宿主的空隙中,病毒执行时再把他们组合起来。似乎人们从CIH才开始认识这种方式,事实上这种方式古已有之,一些DOS病毒就用这种方式,只是没有引起人们注意--人们通常只推崇轰动的东西!PE文件由于结构关系,天然就有很多空隙,适合一个小病毒存在,而DOS可执行文件则没有什么Section的概念,也没有什么天然空隙,似乎看起来不可能插入。其实不然,由于编译器的缘故,文件里很可能有一些用于保存数据的连续的0,这些空间只在运行时才有用,和程序的初始化没关系。所以病毒可以统计这些连续的0,如果发现这样的空间足够大,就可以把病毒块放在里面,运行时把病毒块摘出,然后重新把那块内存清零就可以了——这种技术在DOS时代算是比较高级的技术,实现起来比较困难。……这种感染方式还有衍生。比如不利用宿主已有的空隙,而是在宿主代码里硬生生地挖洞,把病毒代码插进去,病毒执行后再把洞填回去。这样的好处是可以把病毒分解成很小的碎片,这样就不容易被发现,缺点是实现有些复杂,效果未必比利用已有空隙好。”                    ——摘自CVC的Vancheer的《病毒感染技术分析》