PE文件格式详解(3)

就是不想过中秋

就是不想过中秋

2016-02-19 20:19

想要天天向上,就要懂得享受学习。图老师为大家推荐PE文件格式详解(3),精彩的内容需要你们用心的阅读。还在等什么快点来看看吧!
PE可选头部  PE可执行文件中接下来的224个字节组成了PE可选头部。虽然它的名字是“可选头部”,但是请确信:这个头部并非“可选”,而是“必需”的。
  
    OPTHDROFFSET宏可以获得指向可选头部的指针:
   !-- frame contents -- !-- /frame contents --   PEFILE.H
    #define OPTHDROFFSET(a) ((LPVOID)((BYTE *)a +
                          ((PIMAGE_DOS_HEADER)a)-e_lfanew +
                          SIZE_OF_NT_SIGNATURE +
                          sizeof(IMAGE_FILE_HEADER)))
  可选头部包含了很多关于可执行映像的重要信息,例如初始的堆栈大小、程序入口点的位置、首选基地址、操作系统版本、段对齐的信息等等。
  
    IMAGE_OPTIONAL_HEADER结构如下:
    WINNT.H
    typedef strUCt _IMAGE_OPTIONAL_HEADER {
    //
    // 标准域
    //
    USHORT Magic;
    UCHAR MajorLinkerVersion;
    UCHAR MinorLinkerVersion;
    ULONG SizeOfCode;
    ULONG SizeOfInitializedData;
    ULONG SizeOfUninitializedData;
    ULONG AddressOfEntryPoint;
    ULONG BaseOfCode;
    ULONG BaseOfData;
    //
    // NT附加域
    //
    ULONG ImageBase;
    ULONG SectionAlignment;
    ULONG FileAlignment;
    USHORT MajorOperatingSystemVersion;
    USHORT MinorOperatingSystemVersion;
    USHORT MajorImageVersion;
    USHORT MinorImageVersion;
    USHORT MajorSubsystemVersion;
    USHORT MinorSubsystemVersion;
    ULONG Reserved1;
    ULONG SizeOfImage;
    ULONG SizeOfHeaders;
    ULONG CheckSum;
  
  
    USHORT Subsystem;
    USHORT DllCharacteristics;
    ULONG SizeOfStackReserve;
    ULONG SizeOfStackCommit;
    ULONG SizeOfHeapReserve;
    ULONG SizeOfHeapCommit;
    ULONG LoaderFlags;
   !-- frame contents -- !-- /frame contents --   ULONG NumberOfRvaAndSizes;
  
     IMAGE_DATA_DirectorY DataDirectory   [IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
    } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
  
  如你所见,这个结构中所列出的域实在是冗长得过分。为了不让你对所有这些域感到厌烦,我会仅仅讨论有用的——就是说,对于探究PE文件格式而言有用的。  标准域  首先,请注重这个结构被划分为“标准域”和“NT附加域”。所谓标准域,就是和UNIX可执行文件的COFF格式所公共的部分。虽然标准域保留了COFF中定义的名字,但是Windows NT仍然将它们用作了不同的目的——尽管换个名字更好一些。
  
  ·Magic。我不知道这个域是干什么的,对于示例程序EXEVIEW.EXE示例程序而言,这个值是0x010B或267(译注:0x010B为.EXE,0x0107为ROM映像,这个信息我是从eXeScope上得来的)。
  
  ·MajorLinkerVersion、MinorLinkerVersion。表示链接此映像的链接器版本。随Window NT build 438配套的Windows NT SDK包含的链接器版本是2.39(十六进制为2.27)。
  
  ·SizeOfCode。可执行代码尺寸。
  ·SizeOfInitializedData。已初始化的数据尺寸。
  ·SizeOfUninitializedData。未初始化的数据尺寸。
  ·AddressOfEntryPoint。在标准域中,AddressOfEntryPoint域是对PE文件格式来说最为有趣的了。这个域表示应用程序入口点的位置。并且,对于系统黑客来说,这个位置就是导入地址表(IAT)的末尾。以下的函数示范了如何从可选头部获得Windows NT可执行映像的入口点。
  
    PEFILE.C
    LPVOID WINAPI GetModuleEntryPoint(LPVOID lpFile)
    {
    PIMAGE_OPTIONAL_HEADER poh;
    poh = (PIMAGE_OPTIONAL_HEADER)OPTHDROFFSET(lpFile);
    if (poh != NULL)
      return (LPVOID)poh-AddressOfEntryPoint;
    else
      return NULL;
    }
  ·BaseOfCode。已载入映像的代码(“.text”段)的相对偏移量。
  ·BaseOfData。已载入映像的未初始化数据(“.bss”段)的相对偏移量。   Windows NT附加域 !-- frame contents -- !-- /frame contents --   添加到Windows NT PE文件格式中的附加域为Windows NT特定的进程行为提供了装载器的支持,以下为这些域的概述。
  
  ·ImageBase。进程映像地址空间中的首选基地址。Windows NT的Microsoft Win32 SDK链接器将这个值默认设为0x00400000,但是你可以使用-BASE:linker开关改变这个值。
  
  ·SectionAlignment。从ImageBase开始,每个段都被相继的装入进程的地址空间中。SectionAlignment则规定了装载时段能够占据的最小空间数量——就是说,段是关于SectionAlignment对齐的。
  
  Windows NT虚拟内存治理器规定,段对齐不能少于页尺寸(当前的x86平台是4096字节),并且必须是成倍的页尺寸。4096字节是x86链接器的默认值,但是它可以通过-ALIGN: linker开关来设置。
  
  ·FileAlignment。映像文件首先装载的最小的信息块间隔。例如,链接器将一个段实体(段的原始数据)加零扩展为文件中最接近的FileAlignment边界。早先提及的2.39版链接器将映像文件以0x200字节的边界对齐,这个值可以被强制改为512到65535这么多。
  
  ·MajorOperatingSystemVersion。表示Windows NT操作系统的主版本号;通常对Windows NT 1.0而言,这个值被设为1。
  
  ·MinorOperatingSystemVersion。表示Windows NT操作系统的次版本号;通常对Windows NT 1.0而言,这个值被设为0。
  
  ·MajorImageVersion。用来表示应用程序的主版本号;对于Microsoft Excel 4.0而言,这个值是4。
  
  ·MinorImageVersion。用来表示应用程序的次版本号;对于Microsoft Excel 4.0而言,这个值是0。
  
  
  ·MajorSubsystemVersion。表示Windows NT Win32子系统的主版本号;通常对于Windows NT 3.10而言,这个值被设为3。
  
  ·MinorSubsystemVersion。表示Windows NT Win32子系统的次版本号;通常对于Windows NT 3.10而言,这个值被设为10。
  
  ·Reserved1。未知目的,通常不被系统使用,并被链接器设为0。
  
  ·SizeOfImage。表示载入的可执行映像的地址空间中要保留的地址空间大小,这个数字很大程度上受SectionAlignment的影响。   例如,考虑一个拥有固定页尺寸4096字节的系统,假如你有一个11个段的可执行文件,它的每个段都少于4096字节,并且关于65536字节边界对齐,那么SizeOfImage域将会被设为11 * 65536 = 720896(176页)。
  
   !-- frame contents -- !-- /frame contents --   而假如一个相同的文件关于4096字节对齐的话,那么SizeOfImage域的结果将是11 * 4096 = 45056(11页)。这只是个简单的例子,它说明每个段需要少于一个页面的内存。在现实中,链接器通过个别地计算每个段的方法来决定SizeOfImage确切的值。它首先决定每个段需要多少字节,并且最后将页面总数向上取整至最接近的SectionAlignment边界,然后总数就是每个段个别需求之和了。
  
  ·SizeOfHeaders。这个域表示文件中有多少空间用来保存所有的文件头部,包括MS-DOS头部、PE文件头部、PE可选头部以及PE段头部。文件中所有的段实体就开始于这个位置。
  
  ·CheckSum。校验和是用来在装载时验证可执行文件的,它是由链接器设置并检验的。由于创建这些校验和的算法是私有信息,所以在此不进行讨论。
  
  ·Subsystem。用于标识该可执行文件目标子系统的域。每个可能的子系统取值列于WINNT.H的IMAGE_OPTIONAL_HEADER结构之后。
  
  ·DllCharacteristics。用来表示一个DLL映像是否为进程和线程的初始化及终止包含入口点的标记。
  
  ·SizeOfStackReserve、SizeOfStackCommit、SizeOfHeapReserve、SizeOfHeapCommit。这些域控制要保留的地址空间数量,并且负责栈和默认堆的申请。在默认情况下,栈和堆都拥有1个页面的申请值以及16个页面的保留值。这些值可以使用链接器开关-STACKSIZE:与-HEAPSIZE:来设置。
  
  ·LoaderFlags。告知装载器是否在装载时中止和调试,或者默认地正常运行。
  
  ·NumberOfRvaAndSizes。这个域标识了接下来的DataDirectory数组。请注重它被用来标识这个数组,而不是数组中的各个入口数字,这一点非常重要。
  
  ·DataDirectory。数据目录表示文件中其它可执行信息重要组成部分的位置。它事实上就是一个IMAGE_DATA_DIRECTORY结构的数组,位于可选头部结构的末尾。当前的PE文件格式定义了16种可能的数据目录,这之中的11种现在在使用中。   数据目录  WINNT.H之中所定义的数据目录为:
  
    WINNT.H
    // 目录入口
    // 导出目录
    #define IMAGE_DIRECTORY_ENTRY_EXPORT 0
    // 导入目录
   !-- frame contents -- !-- /frame contents --   #define IMAGE_DIRECTORY_ENTRY_IMPORT 1
    // 资源目录
    #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
    // 异常目录
    #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
    // 安全目录
    #define IMAGE_DIRECTORY_ENTRY_SECURITY 4
    // 重定位基本表
    #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
    // 调试目录
    #define IMAGE_DIRECTORY_ENTRY_DEBUG 6
  
     // 描述字串
    #define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
    // 机器值(MIPS GP)
    #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
    // TLS目录
    #define IMAGE_DIRECTORY_ENTRY_TLS 9
    // 载入配置目录
    #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
  
  基本上,每个数据目录都是一个被定义为IMAGE_DATA_DIRECTORY的结构。虽然数据目录入口本身是相同的,但是每个特定的目录种类却是完全唯一的。每个数据目录的定义在本文的以后部分被描述为“预定义段”。
  
    WINNT.H
    typedef struct _IMAGE_DATA_DIRECTORY {
    ULONG VirtualAddress;
    ULONG Size;
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
  
  每个数据目录入口指定了该目录的尺寸和相对虚拟地址。假如你要定义一个特定的目录的话,就需要从可选头部中的数据目录数组中决定相对的地址,然后使用虚拟地址来决定该目录位于哪个段中。一旦你决定了哪个段包含了该目录,该段的段头部就会被用于查找数据目录的精确文件偏移量位置。
  
  所以要获得一个数据目录的话,那么首先你需要了解段的概念。我在下面会对其进行描述,这个讨论之后还有一个有关如何定位数据目录的示例。(未完待续)
展开更多 50%)
分享

猜你喜欢

PE文件格式详解(3)

编程语言 网络编程
PE文件格式详解(3)

PE文件格式详解(4)

编程语言 网络编程
PE文件格式详解(4)

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

PE文件格式详解(1)

编程语言 网络编程
PE文件格式详解(1)

PE文件格式详解(2)

编程语言 网络编程
PE文件格式详解(2)

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

PE文件格式详解(5)

编程语言 网络编程
PE文件格式详解(5)

PE文件格式分析心得

编程语言 网络编程
PE文件格式分析心得

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

Oracle和SQLServer常用函数对比

Oracle和SQLServer常用函数对比

爱在青春非往事

爱在青春非往事
下拉加载更多内容 ↓