本帖最后由 F8LEFT 于 2014-10-7 13:06 编辑
菜鸟在此,大牛飞过~~~~~~~
以上仅为个人的一点小小的心得,有什么不对的地方还请各位指出。 这次我继续来解析PE头,结构非常的多,不过作用基本上可以从名字上面去看出来,因此实际上没有想象中的困难。
那么,下面正式开始:
----------------------------------请叫我分割线-------------------------------------分割线-----------------------------------
鉴于PE头的成员比较多,所以我就不全部介绍呵,就挑重要的部分来讲吧(绝对不是因为懒~~~)。 惯例先讲一点小知识,这次来讲标志位。 我想定义一个区段,并给区段赋予几个属性,因此我定义了一个结构 struct SectionCharacteristics { bool HasCode; //含可执行代码 bool HasData; //含有数据 bool Read; //可读 bool Write; //可写 }; 很简单的结构呵。接着,我发现一个问题了,就是空间非常的浪费。假如这4个数据全部为真的时候,内存中是这样的: 0x01, 0x01, 0x01, 0x01。转换为二进制位就是:00000001, 00000001, 00000001, 00000001。看啊,每个字节,就只有一个位是被用到的。于是我把这几个属性压缩到一个字节中: 00001111,完全可以达到想要的效果,同时又节省了空间,这想法挺棒的。 咦,等等,这又遇到了问题。问题就是这些数据不容易提取啊。这也是可以解决的。比如说,我想提取第3位数据,我就这样 data & 00000100,如果结果不为0的话,那么第3位就是1了。想修改也容易: 写0:data = data & ~00000100,写1: data = data | 00000100;
来,继续讲解PE吧。先来看一下IMAGE_FILE_HEADER的数据: 首先头4个字节为PE标志,”PE\0\0” 先给出IMAGE_FILE_HEADER的定义 typedef struct _IMAGE_FILE_HEADER { WORD Machine; //运行平台 WORD NumberOfSections; //PE中节的数量 DWORD TimeDateStamp; //文件创建日期和时间 DWORD PointerToSymbolTable; //指向符号表(用于调试) DWORD NumberOfSymbols; //符号表中的符号数量(用于调试) WORD SizeOfOptionalHeader; //扩展头结构的长度 WORD Characteristics; //文件属性 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; 这样就基础知识就差不多了,下面来逐个解析。
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
|PE - Start| Signature | Machine | NumberOfSections 000000C0 00 00 00 00 00 00 00 00 | 50 45 00 00 | 4C 01 | 04 00 |PE - Start|幻数”PE\0\0” | 运行平台 | 节数 TimeDataStamp| P~S~Table | N~O~Symbols | S~O~H | Characteristics 000000D0 81 7E 73 4D | 00 00 00 00 | 00 00 00 00 | E0 00 | 0F 01 时间戳 | 指向符号表 | 符号数量 | 扩展头 | 文件属性 解析一下较为重要的部分: Signature: 幻数 “PE\0\0”,是PE头的开始标志。 NumberOfSections: 节的数目,就是在PEID中看到的区段的数目了 TimeDataStamp: 时间戳,是自1970.1.1 到 文件建立时间经过的秒数 SizeOfOptionalHeader: 扩展头长度,标志下一个头(IMAGE_OPTIONAL_HEADER)的大小。 Characteristics: 描述PE文件的属性,可以由多个标志位进行组合,注意的是,这字段并不是描述文件的隐藏,只读之类的属性的。。。
接着是下一个头,IMAGE_OPTIONAL_HEADER。这里我就直接借用WindowsPE权威指南里面的图了,里面说明非常的详细。比我做的要好多了。定义比较长,估计给出了也没有人会看,具体可以参考我的程序代码。来,直接上截图:
可以看到,这个扩展头给出了程序启动的基本的信息,有PE类型、代码段大小,数据段大小、建议装入基址、程序入口RVA。在最后还有一个叫数据目录的信息,是非常重要的,这里我没有截图出来,将放在下一次讲解。
那么这次就到此为止了,代码我会给出,就当做是国庆节给大家的礼物了,虽然可能有点乱就是了。
最后补一下IMAGE_OPTIONAL_HEADER的定义: - typedef struct _IMAGE_OPTIONAL_HEADER {
- //
- // Standard fields.
- //
-
- WORD Magic; //幻数107h = ROM Image,10Bh = exe Image
- BYTE MajorLinkerVersion; //链接器版本号
- BYTE MinorLinkerVersion;
- DWORD SizeOfCode; //所有含代码的节的总大小
- DWORD SizeOfInitializedData; //所有含已初始化数据的节的总大小
- DWORD SizeOfUninitializedData; //所有含未初始化数据的节的大小
- DWORD AddressOfEntryPoint; //程序执行入口RVA
- DWORD BaseOfCode; //代码的节的起始RVA
- DWORD BaseOfData; //数据的节的起始RVA
-
- //
- // NT additional fields.
- //
-
- DWORD ImageBase; //程序的建议装载地址
- DWORD SectionAlignment; //内存中的节的对齐粒度
- DWORD FileAlignment; //文件中的节的对齐粒度
- WORD MajorOperatingSystemVersion;//操作系统版本号
- WORD MinorOperatingSystemVersion;
- WORD MajorImageVersion; //该PE的版本号
- WORD MinorImageVersion;
- WORD MajorSubsystemVersion; //所需子系统的版本号
- WORD MinorSubsystemVersion;
- DWORD Win32VersionValue; //未用
- DWORD SizeOfImage; //内存中的整个PE映像尺寸
- DWORD SizeOfHeaders; //所有头 + 节表的大小
- DWORD CheckSum; //校验和
- WORD Subsystem; //文件的子系统
- WORD DllCharacteristics; //DLL文件特性
- DWORD SizeOfStackReserve; //初始化时的栈大小
- DWORD SizeOfStackCommit; //初始化时实际提交的栈大小
- DWORD SizeOfHeapReserve; //初始化时保留的堆大小
- DWORD SizeOfHeapCommit; //初始化时实际提交的堆大小
- DWORD LoaderFlags; //与调试有关
- DWORD NumberOfRvaAndSizes; //下面的数据目录结构的项目数量
- IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //数据目录
- } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
复制代码
|