PE格式详解(四)

 

         特地把Optional header放一篇文章,是因为它比较复杂与庞大,也因为它比较重要。

         老样子,先是IMAGE_OPTIONAL_HEADER结构:

typedef struct _IMAGE_OPTIONAL_HEADER {

    WORD Magic;                     // 又是Magic

    BYTE MajorLinkerVersion;        // 链接器主要版本

    BYTE MinorLinkerVersion;        // 链接器次要版本

    DWORD SizeOfCode;   // 所有代码区段的长度之和

    DWORD SizeOfInitializedData;        // 同上

    DWORD SizeOfUninitializedData;      // 同上

// 代码起始执行处,对DLL可选,没有是0

    DWORD AddressOfEntryPoint;

    DWORD BaseOfCode;       // 载入内存后代码的起始地址,相对ImageBase而言

    DWORD BaseOfData;       // 载入内存后代码的起始地址,相对ImageBase而言

    DWORD ImageBase;        // 期望的内存中映像载入地址

    DWORD SectionAlignment;         // 内存中区段排列基数

    DWORD FileAlignment;        // 磁盘上区段排列基数

// OS最低主要版本号

    WORD MajorOperatingSystemVersion;

// OS最低次要版本号

WORD MinorOperatingSystemVersion;

    WORD MajorImageVersion;         // 映像文件的主要版本号

    WORD MinorImageVersion;         // 映像文件的次要版本号

    WORD MajorSubsystemVersion;     //  子系统的主要版本号

    WORD MinorSubsystemVersion;     //  子系统的次要版本号

    DWORD Win32VersionValue;        //  由编译器定义,PE规定这里是保留位置

// 加载到内存中的映像文件所占内存的大小,一定是SectionAlignment的整数倍

    DWORD SizeOfImage;

    DWORD SizeOfHeaders;    // 所有头与区段表的长度之和

// 映像文件校验和,仅仅针对内核模式驱动和一些系统DLL

    DWORD CheckSum;

    WORD Subsystem;         // 下详

    WORD DllCharacteristics;        // 下详

    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;

    为了方便比对,我把前面那张PE截图再贴一下:

                    PICTURE MISSING

    _IMAGE_OPTIONAL_HEADER占用从0F0h~14Fh,也就是图中第三行开始总共六行。

    同样,下面挑选一些有用的结构成员讲一下。

    Magic。位置F0~F1,值为010BhWinnt.h 为我们定义了如下三个常数:

#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b

#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b

#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107

    也就是符合第一个,表示32位程序。最后一个是ROM,指刷在ROM里面的程序,反正我从来没见过。

    MajorLinkerVersion也就是接下去的一个字节,图中是09h,说明我们的连接器版本是9.0(VC9)MinorLinkerVersion同理。

    SizeOfCode。值为00 00 36 00,即13824字节,指所有代码区段的长度和。

    SizeOfInitializedData。值为00 00 42 00,即16896字节,是所有已初始化数据的长度和。

    AddressOfEntryPoint。截图中的位置,100h~103h,就是右边有个x开始的那里,别找不到咯~值为00 01 10 78,程序将从地址为00011078h处的指令开始执行,也就是俗称的程序入口点。经过加壳的EXE通常入口点处先是一段解密程序,完了才会跳到原始入口点(OEP)真正执行。

    BaseOfCodeBaseOfData分别指代码区段与数据区段的基地址,自己看看是哪里?

    ImageBase指最希望把PE文件载入到地址空间中的ImageBase地址开始的地方,如果这个地址已经被占用,那只好换地方了,但99%的情况下,ImageBase指向的地址肯定是空闲的。

    SectionAlignmentFileAlignment。位置为110~117,值分别为00 00 10 0000 00 02 00。指的是任意区段的起始地址必须是SectionAlignment(或FileAlignment)中值的整数倍,不同的是,前者指在内存中,后者指在文件中。在内存中起始地址是1000h,即4096的整数倍,是因为Windows中一个虚拟内存页面的大小就是4096字节,这样就可以保证,一个新的区段一定会被分配到一个新的页面中,而不会出现两个区段在一个页面中的情况。同样,在磁盘上必须是200h,即512的整数倍,是因为磁盘一个扇区的大小就是512字节。

    SizeOfImagePE文件被载入内存后所占所有内存空间的总和(包括各个区段)。位置在128~12B,值为00 01 B0 00,即110592字节,合108KB。告诉你,这个程序文件只有正好30KB大小,那又怎么会在内存中变大了呢?原因就是上面说的,在内存中区段地址是按4096的整数倍分配的,而在磁盘上是按512的整数倍,这样就必定会造成很多未使用的内存空间浪费,使占用变大。但是,这也是出于性能优化的角度考虑,没什么好说的。

    SizeOfHeaders。所有的头的长度总和,像什么DOS header,Optional header等等,再加上区段表(Section Table,最先的结构图里有)的长度,就是这个值。这里是1024字节,正好1K。注意:这里的Size指文件中(或磁盘中的)Size,而上面一个指内存中的,别混了。

    Subsystem。位置134h~135h,值为00 03,对着下面的常数,发现是Windows控制台程序(Windows CUI)。

#define IMAGE_SUBSYSTEM_UNKNOWN 0

#define IMAGE_SUBSYSTEM_NATIVE  1

#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2

#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3

#define IMAGE_SUBSYSTEM_OS2_CUI     5  /* Not in PECOFF v8 spec */

#define IMAGE_SUBSYSTEM_POSIX_CUI   7

#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS  8  /* Not in PECOFF v8 spec */

#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI  9

#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10

#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11

#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER  12

#define IMAGE_SUBSYSTEM_EFI_ROM 13

#define IMAGE_SUBSYSTEM_XBOX    14

    DllCharacteristicsDLL文件特征,只对作为DLL时有效。值为81 40,计算方法仍然是使用二进制位来标记。这里原文中在胡扯,后来查了MSDN,如下

#define IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE      0x0040 // 允许重定位

#define IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY   0x0080 // 强制完整性检查

#define IMAGE_DLL_CHARACTERISTICS_NX_COMPAT     0x0100 // 开启数据执行保护DEP

#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION       0x0200 // 不被孤立

#define IMAGE_DLLCHARACTERISTICS_NO_SEH         0x0400 // 关闭结构化异常处理

#define IMAGE_DLLCHARACTERISTICS_NO_BIND        0x0800 // 不被绑定

#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER     0x2000 // 这是个WDM Driver

#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE  0x8000 //此映像能得知终端服务 

    由此,我们的这个程序是IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE | IMAGE_DLL_CHARACTERISTICS_NX_COMPAT | IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE,虽然这个参数没什么意义。

    DataDirectory。它是一个IMAGE_DATA_DIRECTORY类型的数组,共有IMAGE_NUMBEROF_DIRECTORY_ENTRIES个元素,一般IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16。每一个元素都指向PE文件内的一个很重要的数据结构,具体IMAGE_DATA_DIRECTORY的结构,我们下回讨论。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值