重点: +0h(2) e_magic 文件标记=5A4D,根据它判断为PE文件 (WinHex:4D 5A | ME)
+3ch(4) e_lfanew 指向PE文件头的文件偏移位置,根据它PE文件头起始位置
+0h(4) Signature=00004550h ,PE文件标识,判断为PE文件头 (WinHex:50 45 00 00 | PE..)
+4h IMAGE_FILE_HEADER 文件头 重点: +04h(2) Machine=14Ch,判断为intel系统 (WinHex:4C 01) +06h(2) NumberOfSections=后面块表的数目 +08h(4) TimeDateStamp=从1970年1月1日到现在的秒数 +16h(2) Characteristics=010fh(WinHex:0F 01) 判断为EXE文件 =210Eh 判断为DLL文件
+18h IMAGE_OPTIONAL_HEADER32 可选头 重点: +28h(4) AddressOfEntryPoint 程序执行入口RVA(相对虚拟地址:装入地址+RVA=内存地址) +34h(4) ImageBase 内存中的首选装入地址,即基地址 +38h(4) SectionAlignment 内存中区块的对齐值(内存中的装入地址必须为这个值的整数倍) +3Ch(4) FileAlignment 磁盘文件中区块的对齐值(磁盘中文件内的开始地址为该值的整数倍) (设置区块的对齐值:磁盘文件的对齐值一般为200h,不足用00h填充;内存文件中一般为一个内存页的大小,32位系统为4KB即1000h,64位系统为2000h)
+78h DataDirectory 数据目录表(共16项,每项8个字节) 数据目录表的结构:
数据目录表的成员: 重点: +78h 输出表 +80h 输入表 +88h 资源表 +A0h 基址重定位表
每个IMAGE_SECTION_HEADERS结构与下面的区块(.text、.data、.rdata)一一对应,包含其各种属性。 重点: +0h(8) Name:区块名(Winhex中直接找ASCLL名字) +12h(4) VirtualSize:RVA地址(虚拟地址) +20h(4) SizeOfRawData:文件中地址 计算△k: 找到相应的区块–>找到RVA地址–>找到文件中地址–>△k = RVA - File Offset
1.输入函数:被程序调用,但执行代码不在程序中,在DLL文件中 2.Windows加载器:加载程序附加的DLL文件,调用DLL中的API 3. 输入名称表(INT) 输入地址表(IAT):包含输入函数的地址 本质相同的两个数组。PE加载器根据INT,用函数真正入口地址替代,形成IAT 4.结构为IMAGE_IMPORT_DESCRIPTOR(IID):每个DLL文件对应一个IID(5个双字,最后以NULL结束) OriginalFirstThunk指向INT,FirstThunk指向IAT
计算输入表位置:PE文件头起始位置(DOS头部的参数)+80h = 输入表在内存中地址(RVA) - △k =输入表在磁盘中地址(用WinHex查看的地址) △k 区块(.text等)从磁盘映射到内存后,文件偏移地址(物理偏移)(磁盘)和虚拟地址(RVA)(内存)之间的差值 不同区块,差值不同。 计算△k: 找到相应的区块–>找到RVA地址–>找到文件中地址–>△k = RVA - File Offset
分析输入表内容: 每个IID包含5个双字(WinHex中,一个十六进制数表示一个字节,故双字为4个16进制数)
1.第四个双字:00002174h - △k = 地址(DLL文件名)
2.第一个双字:0000208C - △k = 地址(DLL中调用的函数,以一串00结束。每4个16进制数为一个函数) 第五个双字:00002010 - △k = 地址(DLL中调用的函数,以一串00结束。每4个16进制数为一个函数) ( 两个中的内容是一样的)
3.第一个函数:00002110 - △k = 地址(函数名) 前面两个字节,表示引用,可以为0
DLL文件被调用时,即为输出。EXE文件一般没有输出表。为了方便PE装载器的工作。 1.结构为IMAGE_EXPORT_DIRECTORY(IED) 重点: +0ch(4) Name:对应DLL的名字 +14h(4) NumberOfFunctions:EAT中有几个元素 +18h(4) NumberOfNames:ENT中有几个元素 +1ch(4) AddressOfFunctions:EAT +20h(4) AddressOfNames:ENT +24h(4) AddressOfNameOrdinals:在EAT和ENT之间的索引 一个名字对应一个地址,但一个地址可对应多个名字(别名),所以索引数组和名字数组成对出现。
计算输出表位置: PE文件头起始位置(DOS头部的参数)+78h = 输出表在内存中地址(RVA) - △k =输出表在磁盘中地址(用WinHex查看的地址) 分析输出表: 看到DLL文件名称和函数名称(也可计算地址得到)
1.结构IMAGE_BASE_RELOCATION STRUC +0h(4) VirtualAddress:该组数据的RVA +4h(4) SizeOfBlock:当前重定位表的大小 TypeOffset:数组,每项两个字节(高4位,重定位类型(0、3、10);低12位,重定位地址)
2.分析: 计算表开始地址: PE文件头起始位置(DOS头部的参数)+A0h = 输出表在内存中地址(RVA) - △k =输出表在磁盘中地址(用WinHex查看的地址) 计算需要重定位的数据有几项: (SizeOfBlock - VirtualAddress)/2h 分析重定位数据: 每个数据低12位 + VirtualAddress = 该项实际RVA -
1.资源的树形结构:分三层
2.目录块:由一个 资源目录 和数个 资源目录入口 组成
IMAGE_RESOURCE_DIRECTORY 资源目录 重点: NumberOfNamedEntries + NumberOfIdEntries = 本目录中的目录项总和 = 资源目录入口的数量
IMAGE_RESOURCE_DIRECTORY_ENTRY 资源目录入口 Name:目录项的名称或ID; OffsetToData:指针;
找到目录块: PE文件头起始位置(DOS头部的参数)+88h = 资源表在内存中地址(RVA) - △k =资源表在磁盘中地址(用WinHex查看的地址) 分析目录块: 第一层(根目录) 第一行,资源目录结构: +0ch(2) NumberOfNamedEntries = 00 00 +0eh(2) NumberOfIdEntries = 03 00 可得目录项有3个,即3个资源入口结构 第二行,每个目录项8位,共3个: Name表示资源类型:00000003h,图标类型 OffsetToData:80000028h,开头8转化为二进制为1000,最高位1表示指针,指向下一层的地址28h+4000h(资源块首地址)。
第二层 第一行,8位,资源目录结构(同上) 第二行,目录项 Name表示资源名称:800000E8,开头8表示二进制1000,最高位1表示指针,指向资源名称地址E8h+4000h 前四位表示长度,5;后面表示资源:PEDIY OffsetToData(同上,表示还有下一层)
第三层 第一行,8位,资源目录结构同上 第二行,目录项 Name表示资源代码页编号:00000409h表示英语 OffsetToData:000000C8,最高位0,表示指向IMAGE_RESOURCE_DATA_ENTRY结构的地址C8h+4000h
表示该资源的RVA为4400h,大小为5Ah。
本文参考了http://blog.csdn.net/obuyiseng/article/details/49911085作者关于PE文件的一系列文章,讲得很清楚明白,需要更多细节的可以去看看。
常用PE文件分析工具:LordPE–>Sections查看区块信息。