PE文件

 Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html

PE文件

1. DOS头文件

DOS中

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

 

2. NT头

NT头包括三类:PE指纹、NT文件头、NT可选头

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

  1)NT文件头

  typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;  // 可以运行在什么CPU上
    WORD    NumberOfSections;  // 表示节的数量
    DWORD   TimeDateStamp;  // 编译时间
    DWORD   PointerToSymbolTable; // 调试相关
    DWORD   NumberOfSymbols; // 调试相关
    WORD    SizeOfOptionalHeader; // 可选头PE的大小
    WORD    Characteristics; // 文件属性(动态机制可以被修改)
  } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

  2)NT文件头

  typedef struct _IMAGE_OPTIONAL_HEADER {
      WORD    Magic; // 32位 或 64位
      BYTE    MajorLinkerVersion; // 链接器主版本号
      BYTE    MinorLinkerVersion; // 链接器次版本号
      DWORD   SizeOfCode;  // 代码节数据总和
      DWORD   SizeOfInitializedData; // 初始化的数据总和
      DWORD   SizeOfUninitializedData; // 未初始化的数据总和
      DWORD   AddressOfEntryPoint; // 代码执行入口
      DWORD   BaseOfCode; //  代码开始的基址 编译器填写 没有
      DWORD   BaseOfData; //  数据开始的基址 编译器填写 没有
      DWORD   ImageBase; // 内存镜像基址
      DWORD   SectionAlignment; // 内存对齐
      DWORD   FileAlignment; // 文件对齐
      WORD    MajorOperatingSystemVersion; // 操作系统主版本号
      WORD    MinorOperatingSystemVersion; // 操作系统次版本号
      WORD    MajorImageVersion; // PE文件自身主版本号
      WORD    MinorImageVersion; // PE文件自身次版本号
      WORD    MajorSubsystemVersion; // 运行时所需子系统版本号
      WORD    MinorSubsystemVersion; // 运行时所需子系统版本号
      DWORD   Win32VersionValue; // 子系统版本的值 必须为0
      DWORD   SizeOfImage;  // 内存中整个PE文件的映射的尺寸
      DWORD   SizeOfHeaders; // 所有头+节表对齐后的大小,否则加载会出错
      DWORD   CheckSum; // 校验和 一些系统文件有要求,用来判断文件是否被修改
      WORD    Subsystem; // 子系统 驱动(1) 图形(2) 控制台、DLL(3)
      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;

 

3.PE节表

  NT可选头最后部分有一个 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]

  typedef struct _IMAGE_SECTION_HEADER {
      BYTE    Name[IMAGE_SIZEOF_SHORT_NAME]; // 节区名称,可自定义,只截取前8个
      union {              // 该节没有对齐前的真实尺寸,该值可以不准确
              DWORD   PhysicalAddress;
              DWORD   VirtualSize;
      } Misc;
      DWORD   VirtualAddress;     // 在内存中的偏移地址,加上ImageBase才是真正的内存地址
      DWORD   SizeOfRawData;    // 节在文件中对齐后的尺寸
      DWORD   PointerToRawData;    // 节在文件中对齐后的尺寸
      DWORD   PointerToRelocations;    // 节区在文件中的偏移
      DWORD   PointerToLinenumbers;   
      WORD    NumberOfRelocations;
      WORD    NumberOfLinenumbers;
      DWORD   Characteristics;              // 节的属性
  } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

  1)没有初始值的全局变量,在文件中没有位置,但一旦加载到内存其会存在位置。

 

4. RVA 与 FOA 的转换

  RVA 相对虚拟地址 FOA 文件偏移地址

  

 

5. 导出表

  typedef struct _IMAGE_EXPORT_DIRECTORY {
      DWORD   Characteristics;
      DWORD   TimeDateStamp;
      WORD    MajorVersion;
      WORD    MinorVersion;
      DWORD   Name;        //
      DWORD   Base;        // 基数
      DWORD   NumberOfFunctions;
      DWORD   NumberOfNames;
      DWORD   AddressOfFunctions;     // 函数地址表
      DWORD   AddressOfNames;         // 名称地址表
      DWORD   AddressOfNameOrdinals;  // 名称序号表
  } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

  其操作如下图所示:

  

  ① 如果直接用序号来找函数,直接搜索函数地址表即可,因为其是按照函数索引来进行排序的。

  ② 函数名称表,其是按照字母顺序排序的,其结合函数序号表来进行找到其地址。

    比如找Plus函数,其通过名字对比其在函数表中的第2个位置,其查表Table[2] = 0,则说明其在函数地址表中第0号位置,很轻松能定位到函数。

 

6. 函数地址表

  

 

  ① 导入表是一串连续的_IMAGE_IMPORT_DESCRIPTOR导出数组,一个DLL存在一个这种数组,直到0为止。

  ② _IMAGE_THUNK_DATA 看最高位,如果是1则表示序号,并且将最高位清0,如果最高位不是1,则其指向如下数据结构

    typedef struct _IMAGE_IMPORT_BY_NAME {
         WORD    Hint;
         CHAR   Name[1];
    } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

    其中连续的名字是以零结尾。

  ③ 注意,我们从函数导出表中并没法看到其对应的函数地址,这是当然的,既然导入表,我们没有DLL,其通过名字和序号完成创建的。

 

7. 重定位表

  注意,全局变量直接计算一个伪地址写入到硬编码中的,因此其必须要重定位。

  重定位的结构为 _IMAGE_BASE_RELOCATION

  typedef struct _IMAGE_BASE_RELOCATION {

    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
  } IMAGE_BASE_RELOCATION;

  其并不是这么简单,其实质是重定位块,SizeOfBlock表示重定位块的大小,当下一个重定位块为0时则表示重定位表的结束。

  

 

8. DLL文件的加载过程

  ① 扫描母进程的输入表,查看需要加载进入母进程地址空间的DLL和数量;

  ② 扫描循环加载所有的DLL文件,当DLL被加载完成后,从导入表中查找所需要的函数,

    在DLL文件的输出表中搜索,定位后,将得到的地址放入母函数的IAT表中,依次循环,直到将所有的DLL函数定位;

  ③ 将DLL中重定位表中的内容重新定位;

  ④ 完成对接。

posted @ 2020-04-06 21:42  OneTrainee  阅读(970)  评论(0编辑  收藏  举报