zoukankan      html  css  js  c++  java
  • PE文件基础

    PE (Portable Executable):微软参考COFF(Common Object File Format)规范,在Windows NT系统上制定的一种标准,

    用于exe可执行文件、obj目标文件和dll动态链接库等文件格式。PE32+是PE的64位扩展,其并未添加额外结构,只是把原来32位的字段变成了64位。

    与COFF一样,PE也是基于段(Segment,注:有时也被叫节Section)的结构,

    按照不同属性将信息分段存放,常见的段有:代码段(.text)、数据段(.data)、只读数据段(.rdata)、资源表(.rsrc)、重定位表(.reloc)等。

    因为PE文件在装载时被直接映射到进程的虚拟空间中运行,它是进程的虚拟空间的映像。所以PE文件很多时侯被叫做映像文件(Image File)。

    RVA (Relative Virtual Address):相对于PE文件装载基地址(Base Address)的一个地址偏移。

    ③ windows系统使用的x86的处理器,为小端序;WORD的二进制1A15实际的数值为:5402(数学写法为:151A);

    DWORD的二进制B2356A18实际的数值为:409613746(数学写法为:186A35B2

    PE文件结构:

    注1:IMAGE_DOS_HEADER、IMAGE_NT_HEADER、IMAGE_SECTION_HEADER等都定义在winNT.h中
    注2:IMAGE_DOS_HEADER和DOS Stub是兼容DOS应用而存在的结构,PE文件的内容从IMAGE_NT_HEADER开始

    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;
    
    ///////////////////////////////////////////////////////////////////////////
    typedef struct _IMAGE_FILE_HEADER {
        WORD    Machine;
        WORD    NumberOfSections;
        DWORD   TimeDateStamp;
        DWORD   PointerToSymbolTable;
        DWORD   NumberOfSymbols;
        WORD    SizeOfOptionalHeader;
        WORD    Characteristics;
    } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
    
    typedef struct _IMAGE_DATA_DIRECTORY {
        DWORD   VirtualAddress;
        DWORD   Size;
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
    
    #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16
    
    typedef struct _IMAGE_OPTIONAL_HEADER64 {
        WORD        Magic;
        BYTE        MajorLinkerVersion;
        BYTE        MinorLinkerVersion;
        DWORD       SizeOfCode;
        DWORD       SizeOfInitializedData;
        DWORD       SizeOfUninitializedData;
        DWORD       AddressOfEntryPoint;
        DWORD       BaseOfCode;
        ULONGLONG   ImageBase;
        DWORD       SectionAlignment;
        DWORD       FileAlignment;
        WORD        MajorOperatingSystemVersion;
        WORD        MinorOperatingSystemVersion;
        WORD        MajorImageVersion;
        WORD        MinorImageVersion;
        WORD        MajorSubsystemVersion;
        WORD        MinorSubsystemVersion;
        DWORD       Win32VersionValue;
        DWORD       SizeOfImage;
        DWORD       SizeOfHeaders;
        DWORD       CheckSum;
        WORD        Subsystem;
        WORD        DllCharacteristics;
        ULONGLONG   SizeOfStackReserve;
        ULONGLONG   SizeOfStackCommit;
        ULONGLONG   SizeOfHeapReserve;
        ULONGLONG   SizeOfHeapCommit;
        DWORD       LoaderFlags;
        DWORD       NumberOfRvaAndSizes;
        IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
    } IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
    
    typedef struct _IMAGE_OPTIONAL_HEADER {
        //
        // Standard fields.
        //
    
        WORD    Magic;
        BYTE    MajorLinkerVersion;
        BYTE    MinorLinkerVersion;
        DWORD   SizeOfCode;
        DWORD   SizeOfInitializedData;
        DWORD   SizeOfUninitializedData;
        DWORD   AddressOfEntryPoint;
        DWORD   BaseOfCode;
        DWORD   BaseOfData;
    
        //
        // NT additional fields.
        //
    
        DWORD   ImageBase;
        DWORD   SectionAlignment;
        DWORD   FileAlignment;
        WORD    MajorOperatingSystemVersion;
        WORD    MinorOperatingSystemVersion;
        WORD    MajorImageVersion;
        WORD    MinorImageVersion;
        WORD    MajorSubsystemVersion;
        WORD    MinorSubsystemVersion;
        DWORD   Win32VersionValue;
        DWORD   SizeOfImage;
        DWORD   SizeOfHeaders;
        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;
    
    typedef struct _IMAGE_NT_HEADERS64 {
        DWORD Signature;
        IMAGE_FILE_HEADER FileHeader;
        IMAGE_OPTIONAL_HEADER64 OptionalHeader;
    } IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
    
    typedef struct _IMAGE_NT_HEADERS {
        DWORD Signature;
        IMAGE_FILE_HEADER FileHeader;
        IMAGE_OPTIONAL_HEADER32 OptionalHeader;
    } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
    
    typedef struct _IMAGE_ROM_HEADERS {
        IMAGE_FILE_HEADER FileHeader;
        IMAGE_ROM_OPTIONAL_HEADER OptionalHeader;
    } IMAGE_ROM_HEADERS, *PIMAGE_ROM_HEADERS;
    
    #ifdef _WIN64
    typedef IMAGE_NT_HEADERS64                  IMAGE_NT_HEADERS;
    typedef PIMAGE_NT_HEADERS64                 PIMAGE_NT_HEADERS;
    #else
    typedef IMAGE_NT_HEADERS32                  IMAGE_NT_HEADERS;
    typedef PIMAGE_NT_HEADERS32                 PIMAGE_NT_HEADERS;
    #endif
    
    ///////////////////////////////////////////////////////////////////////////
    #define IMAGE_SIZEOF_SHORT_NAME              8
    
    typedef struct _IMAGE_SECTION_HEADER {
        BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
        union {
            DWORD   PhysicalAddress;
            DWORD   VirtualSize;
        } Misc;
        DWORD   VirtualAddress;
        DWORD   SizeOfRawData;
        DWORD   PointerToRawData;
        DWORD   PointerToRelocations;
        DWORD   PointerToLinenumbers;
        WORD    NumberOfRelocations;
        WORD    NumberOfLinenumbers;
        DWORD   Characteristics;
    } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

    程序编译后,变量与函数存储在PE文件中的位置如下: 

    注:BSS段存放的是未初始化的全局变量与静态变量(含全局和局部);以上代码,vc编译器会去除BSS段,将这部分内容直接放到数据段中

           主要的原因是段的最小单元为4KB,所以为了节省PE文件、内存占用及加快PE文件的映射过程,vc编译器会尽量将属性一致的段进行合并

    数据目录

    ① 一般来说,导出表、导入表、导入地址表存放在.rdata段(只读数据段)、重定位表存放在独立的.reloc段(重定位表段)

    ② 在PE文件中,将段表中各段的VirtualAddress(段的RVA)及VirtualSize(段的实际大小)与所求RVA比较,就可知该RVA落在哪个段中

        假设落在段A中,则所求RVA在PE文件中offset为:段A的PointerToRawData + (所求RVA - 段A的VirtualAddress)

    (1)导出表

    导出符号:当前模块(DLL)导出供其他模块使用的函数和变量。

    注1:Base一般为1,AddressOfNames、AddressOfNameOrdinals指向的表的size一致,且里面元素一一对应

    注2:AddressOfFunctions指向的表的size >= AddressOfNames指向的表的size,主要有以下两点原因导致:

            ① 当在def文件中指定的符号Ordinal不连续时,会导致一些空地址(即:0地址)元素

            如:导出2个Orinal为1,3的符号,那么AddressOfFunctions指向的表的size为3(第2个元素为空地址),AddressOfNames指向的表的size为2

            def文件符号导出如下:

                   DllAdd @ 1

                   g_nDll @ 3

            ② 在def中只导出Ordinal而不导出符号名称  如:DllAdd @ 1 NONAME

    注3:符号在导出地址表(EAT)中的索引号为:名字序号对应表中的Ordinal - Base

    导出方法:

    ① 在代码中使用__declspec(dllexport)关键字

    ② def文件

     

    ③ 链接器/export参数

    (2)导入表:

    导入符号:在当前模块使用,但在其他模块(DLL)中定义实现的函数和变量。

    导入方法:

    ① 在dll的头文件中,对导出的函数和变量使用__declspec(dllimport)

    ② 链接dll的导入库

    每个导入模块(dll)使用一个IMAGE_IMPORT_DESCRIPTOR结构体存放,并以全0内容的IMAGE_IMPORT_DESCRIPTOR作为结尾。 

    IAT、INT中的元素使用IMAGE_THUNK_DATA结构体存放, 并以全0内容的IMAGE_THUNK_DATA作为结尾。 

    IAT中IMAGE_THUNK_DATA最高位为1,则低31位为导入符号的序号值;否则IMAGE_THUNK_DATA为指向IMAGE_IMPORT_BY_NAME结构的RVA。

    动态连接器使用IMAGE_IMPORT_BY_NAME的Hint值去定位符号在目标导出表中的位置,若刚好找到则命中,如果没命中就按照二分查找方法进行符号查找。

    INT未绑定时,内容与IAT一样;绑定时,IMAGE_THUNK_DATA中存放的是导入符号运行时的虚拟内存地址,如果外部模块的时间戳与TimeDataStamp一致,

    且在载入外部模块未发生重定基址,则直接使用该地址来访问该符号;否则,使用IAT进行符号解析与查找。

    注1:在动态链接器刚完成映射还没有开始重定位和符号解析时,IAT中元素值表示相对应的导入符号的序号或者是符号名;当完成该模块链接时,元素值会被动态链接器改成该符号的真正地址。

    注2:程序每次运行时,所有被依赖的dll都会被装载,然后一系列的导入导出符号依赖关系都会被重新解析。然而在大多数情况下,这些dll都会以同样的顺序被装载到同样的内存地址,所以它们的导出符号的地址都是不变的。

            若将这些导出函数的地址保存到模块的导入表中,就可以省去每次启动时符号解析过程,这种DLL性能优化方式被叫做DLL绑定(DLLBinding)。

    (3)导入地址表

    注:导入地址表(IAT)存放着所有导入模块的地址信息(外部地址),各个模块之间用一个4字节的0隔开。模块的先后顺序不保证与导入表模块的顺序一致。 

    (4)重定位表

    按照基址生成出的绝对地址,如果目标地址被占用或基于安全考虑,则这些地址就需要重定位。

    类型(高4位) 含义
    0 无意义,仅用来做对齐用
    1 高16位需要修正
    2 低16位需要修正
    3 32位都需要修正

    PE文件工具

    https://down.52pojie.cn/Tools/PEtools/

     (1)PE-Explorer

    (2)LordPE

    (3)ExeinfoPe

  • 相关阅读:
    找细胞(题解)
    关于dfs
    奇怪的电梯(题解)
    信息解码(Message Decoding ACM/ICPC 1991)
    查找最大元素
    8皇后问题(dfs)
    如何利用dfs遍历树
    dfs(计算细胞数量)
    决策树减支问题(优化)dfs减支问题
    组合数问题
  • 原文地址:https://www.cnblogs.com/kekec/p/7350857.html
Copyright © 2011-2022 走看看