一年前看过一次,但没有实实在在的理解,这次争取结合实际的PE文件再看看.
一般来说,很多文件为了提高使用率,都会使用一个"最小基本单位"的概念.PE文件中各节区的起始位置在某个最小单位的倍数上.
若不够,则空白区域用NULL填充.
相对虚拟地址(RVA):从某个基准地址开始的相对地址.
虚拟地址(VA):进程虚拟内存的绝对地址.
两个都指的是在装载入内存之后的地址.
随着所处位置的不同,其最小基本单位也会发生变化,导致NULL填充区变大.
PE 头
DOS头
首先前40H节是一个IMAGE_DOS_HEADER的结构体--DOS头:
结构体包含如下几个字段:
注意:WORD占两字节,DWORD占四字节
e_magic是DOS签名,该结构体的最后一个字段e_lfanew是NT头的偏移,此处偏移值是e0.
紧跟着的是DOS存根(stub),是一个可选项.由代码和数据混合而成,大小不确定.此处40-4D处是代码,int 21执行时由
eax寄存器中的值来决定执行哪个中断.此处debug模式下,将数据(This ...)识别为了代码.故40-7F为DOS存根段.
若使用得当,可以在DOS存根处写入代码,使得在DOS模式下运行16位程序,在Windows下运行32/64位程序.
对DOS头,DOS存根做一个总结:
1.DOS存根是可选的,大小变化的,可以在里面写入汇编程序以运行在16位DOS模式下.
2.DOS头是固定的40H字节.
3.DOS头第一个是DOS签名--MZ,最后一个是e_lfanew,是NT头的偏移(从地址0开始),因为DOS存根大小不确定.
NT头
即IMAGE_NT_HEADERS.有三个成员:PE,文件头(File Header)和可选头(Optional Header).总大小为F8字节.
识别出来并不困难,找到PE即可.
接下来是文件头结构体(黑色划线部分),重要的有如下几个
首先是机器码(Machine),此处位014C,表明是intel 386的CPU
第二个是节区数(NumberOfSections),此处为3.
第三个是可选头大小(SizeOfOptionalHeader),用来指出IMAGE_OPTIONAL_HEADER32/64的长度,此处是e0
第四个是Characteristics,用来标识文件属性此处是10f,即包含了重定向信息删除,可执行文件,行数移除,本地符号表移除以及系统文件等属性
可选头(Image_Optional_Header)是PE结构体中最大的.
1#.Magic
可选头是32结构体时位,该值为10B;可选头是64位结构体时,该值为20B.
2#.AddressOfEntryPoint
标识处EP的RVA值,指出程序最先执行的代码起始地址.动态反汇编软件会从其中读取出值.
3#.ImageBase
指出PE文件优先装入的地址.EXE,DLL被装载到0~FFFFFFFF(32位系统),SYS文件被装载到80000000~FFFFFFFF.一般来说,EXE的基址是00400000,DLL的基址位10000000.执行PE文件时,PE转载器先创建进程,再将文件载入内存,最后EIP=ImageBase+AddressOfEntryPoint.
4#.SectionAlignment,FileAlignment
SectionAlignment指定了节区在磁盘文件中的最小单位,FileAlignment指定了节区在内存中的最小单位.
5#.SizeOfImage
PE文件在虚拟内存中所占空间的大小.
6#.SizeOfHeaders
指出PE头的大小.
7#.Subsystem
区分系统文件与普通的可执行文件.
8#.NumberOfRvaAndSizes
指定最后一个成员DataDirectory数组的个数,以这个为准,而不是IMAGE_NUMBEROF_Directory_Entries(16)
9#.DataDirectory
非常重要的是Export / Import / Resource Directory,TLS Direction.
对NT头做一个总结:
1.NT头包含三个元素:PE,File Header,Optional Header.
2.文件头中的第一个元素是机器码,I386的CPU是0x014C,倒数第二个是可选头大小,32/64位系统的可选头大小不同,最后一个是文件属性,0x2是exe,0x20000是DLL
3.从PE开始,数18H字节就到了可选头的第一个元素.
4.DataDirectory[n]结构体中的每一个都有两个属性:一个是相对虚拟地址,一个是大小.其中每个结构体的大小为8字节
5.从Magic(10B/20B)开始,加60H即到DataDirectory的第一个结构体,每个结构有两个占4字节的元素--VirtualAddress,Size.
6.从Magic开始,加20H即到SectionAlignment和FileAlignment,每个占4字节
节区头
即IMAGE_SECTION_HEADER结构体.它定义了每个节区的相关信息.每个节区头的大小固定为40字节.
重要的元素有下面几个:
1#.VirtualSize
指示内存中节区所占的实际大小,通常这个值会被对齐到某个整数.
2#.VirtualAddress
内存中节区的起始地址.其实是相对虚拟地址,程序运行时会加上一个ImageBase值.通常第一个节区的值会被置为1000H.会按照SectionAlignmnet的值对齐.
3#SizeOfRawData
磁盘文件中节区所占的大小.这个值通常是对VirtualSize依照FileAlignment的值进行对齐后所得到的.
4#.PointerToRawData
指示该节区在文件中的起始位置.已按照FileAlignment对齐.
5#.Characteristics
文件属性.由bits组合而成.
节区头name字段占8个字节.