一直以来都想深入学习PE结构,以达到能灵活运用的程度。最近因为在了解BINFS的细节,所以利用这个机会来研究,在学习的过程中通过针对具体一个文件coredll.dll并结合romimage的日志进行分析可以说达到了预期目标。
WINCE下PE结构和WINDOWS下的差不多,不同的只是加载有点不一样:在WINCE上EXE/DLL的PE头没有映射到内存,而是OS在加载阶段把导入/导出表读出来放到一个单独的结构中(关于此结构在后面会有介绍),跟WINDOWS上的处理不一样。
PE结构的文件由四部分组成:
一、DOS头部分
1、DOS MZ header:以MZ做为开始标志,大小一般为0x40
2、DOS stub:"program must be run under Microsoft Windows"信息,大小是0x30
二、加密数据,会有一个"Rich"的字符串。大小是0x60
三、PE头部分
1、Signature:PE头的标识。双字结构。为50h, 45h, 00h, 00h. 即“PE”。
2、FileHeader:20字节的数据。包含了文件的物理层信息及文件属性。
(1)NumberOfSections:定义PE文件Section的个数
(2)SizeOfOptionalHeader:定义OptionHeader(稍后具体描述)结构的大小
(3)Characteristics:一此标识位,主要用来标识当前的PE文件是执行文件还是DLL
3、OptionHeader:总共224个字节。最后128个字节为数据目录(Data Directory)
(1)AddressOfEntryPoint:程序入口点地址。但加载器要运行加载的PE文件时要执行的第一个指令的地址。它是一个RVA(相对虚拟地址)地址。一些对PE文件插入代码的程序就是修改此处的地址为要运行的代码,然后再跳转回此处原来的地址。
(2)ImageBase:PE文件被加载到内存的期望的基地址。对于EXE文件,通常加载后的地址就期望的地址。但是DLL却可能是其他的。因为如果这个地址被占,系统就会重新分配一块新的内存,同时会修改此处加载后的地址。EXE文件通常是400000h.
(3)SectionAlignment:每一个Section的内存对齐粒度。比如:此值为4096(1000h),那么每一个Section的起始地址都应该是4096(1000h)的整数倍。如果第一个Section的地址是401000h,大小为100个字节。那么下一个Section的起始地址为402000h.。两个Section之间的空间大部分是空的,未用的。
(4)FileAlignment:每一个Section的磁盘对齐粒度。比如,此值为512(200h),那么每一个Section在文件内的偏移位置都是512(200h)的整数倍。与SectionAlignment同理。
(5)SizeOfImage:PE文件在内存空间整个映像的大小。包含所有的头及按SectinAlignment对齐的所有的Section。
(6)SizeOfHeaders:所有的头加上Section表的大小。也就是文件大小减去文件中所有Section的大小。可以用这个值获取PE文件中第一Section的位置。
(7)DataDiretory:16个IMAGE_DATA_DIRECTORY结构的数组。每一个成员都对应一个重要的数据结构,比如输入表,输出表等。数据结构及宏定义如下:
数据结构如下
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; //数据项的RVA
DWORD Size; //数据项的大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
//索引定义如下
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECU,RITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
//#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
如果无此项,则IMAGE_DATA_DIRECTORY中的成员变量值为0
四、Section表,WINCE一般有.text、.data、.pdata、.rsrc、.reloc五个Section
由IMAGE_SECTION_HEADER数据结构组成的数组。每一个包含了对应Section在PE文件中的属性和偏移位置等信息。
针对具体的文件coredll.dll,它的大小为560640,romimage的日志信息如下:
MODULES Section
Module Section Start Length psize vsize Filler
---------------------- -------- --------- ------- ------- ------- ------
。。。。。。
coredll.dll .text 803ce000 495616 492544 492405 o32_rva=00001000
coredll.dll .rsrc 80447000 28672 28160 28012 o32_rva=00082000
。。。。。。
coredll.dll .data 8049a95c 1416 1416 4740 FILLER
coredll.dll .pdata 804bb000 9361 9361 20568
。。。。。。
coredll.dll E32 803caf30 112 FILLER
coredll.dll O32 803cafa0 96 FILLER
。。。。。。
coredll.dll FileName 8044dfec 12 FILLER
。。。。。。
psize:是Section的长度(按PE头中FileAlignment对齐,可能存在padding数据),如果是压缩的数据则是压缩后的长度
vsize:是Section能正常工作需要的长度(去掉了padding数据),如果是压缩的数据则是解压后需要的长度
Length:psize和vsize中的最小数,对于压缩的Section一般是psize,但是对于非压缩的Section则是vsize按0x1000的页面对齐
从.text和.rsrc信息可以看出是没有压缩的,.pdata的数据进行了压缩,.data的数据也进行了压缩,和.pdata不同的是,压缩前实际大小为3224(不包括padding),压缩后为1416,visze刚好为两者之和,是不是指.data的压缩数据在内存中要保存一份拷贝?
而对coredll.dll根据PE的结构定义进行的分析,可以得出如下信息
1、DOS MZ header: 0x00 - 0x3F
DOS stub: 0x40 - 0x7F
2、加密数据: 0x80 - 0xDF
3、PE header: 0xE0 - 0x1D7
其中 Signature : 0xE0 - 0xE3
FileHeader: 0xE4 - 0xF7
OptionHeader: 0xF8 - 0x1D7
4、Sections: 0x1D8 - 0x29F //共5个Sections,5*sizeof(section)=200
padding: 0x2A0 - 0x3FF //应该是为更多的section预留,共352字节理论上可以加8个section
//到此处共0x400字节是PE文件的相关信息,下面开始则是具体的section数据了
//section的起始和终止位置根据section信息中的PointerToRawData和SizeOfRawData得出
//而模块名及导入/导出函数名位于.text内,用ImageRvaToVa获得其地址。
.text: 0x400 - 0x787FF,大小为0x78400
.data: 0x78800 - 0x795FF,大小为0xE00
.pdata: 0x79600 - 0x7E7FF,大小为0x5200
.rsrc: 0x7E800 - 0x855FF,大小为0x6E00
.reloc: 0x85600 - end,大小为0x3800
E32填充的数据结构定义如下,是将部分PE头的信息保存:
#define ROM_EXTRA 9
typedef struct e32_rom {
unsigned short e32_objcnt; /* Number of memory objects */
unsigned short e32_imageflags; /* Image flags */
unsigned long e32_entryrva; /* Relative virt. addr. of entry point */
unsigned long e32_vbase; /* Virtual base address of module */
unsigned short e32_subsysmajor;/* The subsystem major version number */
unsigned short e32_subsysminor;/* The subsystem minor version number */
unsigned long e32_stackmax; /* Maximum stack size */
unsigned long e32_vsize; /* Virtual size of the entire image */
unsigned long e32_sect14rva; /* section 14 rva */
unsigned long e32_sect14size; /* section 14 size */
unsigned long e32_timestamp; /* Time EXE/DLL was created/modified */
struct info e32_unit[ROM_EXTRA]; /* Array of extra info units */
unsigned short e32_subsys; /* The subsystem type */
} e32_rom;
O32填充的数据结构定义如下,是将所有section的部分信息保存:
typedef struct o32_rom {
unsigned long o32_vsize; /* Virtual memory size */
unsigned long o32_rva; /* Object relative virtual address */
unsigned long o32_psize; /* Physical file size of init. data */
unsigned long o32_dataptr; /* Image pages offset */
unsigned long o32_realaddr; /* pointer to actual */
unsigned long o32_flags; /* Attribute flags for the object */
} o32_rom;
关于这个结构的使用,可能看loader.c中的代码,由此也可以看到对MODULES和非MODULES下的文件加载的时候处理是不一样的。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/alien75/archive/2010/03/26/5420667.aspx
联系客服