PE结构笔记

提示:前面加*为必须背下来的


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;                    //* NT头指针
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

NT头:

typedef struct _IMAGE_NT_HEADERS {
  DWORD                 Signature;       //*pe签名
  IMAGE_FILE_HEADER     FileHeader;      //*PE标准
  IMAGE_OPTIONAL_HEADER OptionalHeader;  //*PE可选头
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;        

NT::标准PE头:

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;                     //*cpu识别
    WORD    NumberOfSections;            //*文件的节数目 (节表与节的数目一样)
    DWORD   TimeDateStamp;               //*文件创建日期和时间
    DWORD   PointerToSymbolTable;        //用于调试
    DWORD   NumberOfSymbols;             //用于调试
    WORD    SizeOfOptionalHeader;        //*PE可选头的大小,32位PE文件默认为E0h,64位PE文件默认大小为F0h 大小可以自定
    WORD    Characteristics;             //*关于文件信息的标记,比如文件是exe还是dll
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

NT::可选PE头(32位下大小为E0 64位的为F0)

typedef struct _IMAGE_OPTIONAL_HEADER

{
        WORD Magic;                       //*说明文件类型:10B 32位下的PE文件        20B 64位下的PE文件
        BYTE MajorLinkerVersion;          //链接程序的主版本号
        BYTE MinorLinkerVersion;          //链接程序的次版本号
        DWORD SizeOfCode;                 //*所有含代码的节的总大小,必须是FileAlignment的整数倍 编译器填的 没用
        DWORD SizeOfInitializedData;      //*已初始化数据的大小 必须是FileAlignment的整数倍 编译器填的 没用
        DWORD SizeOfUninitializedData;    //*未初始化数据的大小 必须是FileAlignment的整数倍 编译器填的 没用
        DWORD AddressOfEntryPoint;        //*****程序执行入口RVA
        DWORD BaseOfCode;                 //*代码开始的基址, 编译器填的 没用
        DWORD BaseOfData;                 //*数据开始的基址, 编译器填的 没用
        DWORD ImageBase;                  //*****内存镜像基址
        DWORD SectionAlignment;           //*内存中的区块的对齐大小
        DWORD FileAlignment;              //*文件中的区块的对齐大小
        WORD MajorOperatingSystemVersion; //要求操作系统最低版本号的主版本号
        WORD MinorOperatingSystemVersion; //要求操作系统最低版本号的副版本号
        WORD MajorImageVersion;           //可运行于操作系统的主版本号
        WORD MinorImageVersion;           //可运行于操作系统的次版本号
        WORD MajorSubsystemVersion;       //要求最低子系统版本的主版本号,
        WORD MinorSubsystemVersion;       //要求最低子系统版本的次版本号
        DWORD Win32VersionValue;          //莫须有字段,不被病毒利用的话一般为0
        DWORD SizeOfImage;                //*内存中整个PE文件的映射尺寸,可以比实际值大,但必须是SectionAlignment的整数倍
        DWORD SizeOfHeaders;              //*所有头+节表按照文件对齐后的大小,否则加载会出错
        DWORD CheckSum;                   //*校验和,一些系统文件有要求,用来判断文件是否被修改
        WORD Subsystem;                   //可执行文件期望的子系统
        WORD DllCharacteristics;          //DllMain()函数何时被调用,默认为 0
        DWORD SizeOfStackReserve;         //*初始化时的栈大小
        DWORD SizeOfStackCommit;          //*初始化时实际提交的栈大小
        DWORD SizeOfHeapReserve;          //*初始化时保留的堆大小
        DWORD SizeOfHeapCommit;           //*初始化时实际提交的堆大小
        DWORD LoaderFlags;                //与调试有关,默认为 0
        DWORD NumberOfRvaAndSizes;        //下边数据目录的项数,这个字段自Windows NT 发布以来 一直是16
        IMAGE_DATA_DIRECTORY DataDirectory
                    [IMAGE_NUMBEROF_DIRECTORY_ENTRIES];// 数据目录表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

节表:
IMAGE_SIZEOF_SHORT_NAME组成,每个结构体代表一个节区

#define IMAGE_SIZEOF_SHORT_NAME              
 
typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];   //8个字节 一般情况下是以“”结尾的ASCII码字符串来标识的名称 内容可以自定义 可能由于字母太多编译器不添加""需要注意
    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;

注意:
            VirtualAddress 和 PointerToRawData 不带有任何值,分别由_IMAGE_OPTIONAL_HEADER中的SectionAlignment和FileAlignment确定
            VirtualSize和SizeOfRawData一般具有不同的值,即磁盘节区的大小与加载到内存的大小是不一样的。
            
导入表(IAT)/ 导出表():

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    _ANONYMOUS_UNION union {
        DWORD Characteristics;

        DWORD OriginalFirstThunk;            // INT的地址(Import Name Table)(RVA)
    } DUMMYUNIONNAME;                        //
    DWORD TimeDateStamp;                     //
    DWORD ForwarderChain;                    //
    DWORD Name;                              //库名称字符串的地址    (RVA)
    DWORD FirstThunk;                        // IAT的地址(Import Address Table)(RVA)
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp; // 时间戳
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name; // *指向该导出表文件名字符串
    DWORD   Base; // *导出函数起始序号
    DWORD   NumberOfFunctions; // *所有导出函数的个数
    DWORD   NumberOfNames; // *以函数名字导出的函数个数
    DWORD   AddressOfFunctions;     // *导出函数地址表RVA
    DWORD   AddressOfNames;         // *导出函数名称表RVA
    DWORD   AddressOfNameOrdinals;  // *导出涵数序号表RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

 
 
注意: INT与IAT是长整型(4个字节数据类型)数组,以NULL结束 INT中各元素的值为IMAGE_IMPORT_BY_NAME结构体指针(有时IAT也拥有相同的值) INT与IAT的大小应相同 INT输入顺序: 1.读取IID的Name成员,获取库名称字符串("kernel32.dll") 2.装载相应库——————> LoadLibrary("kernel32.dll") 3.读取IID的OriginalFirstThunk成员,获取INT地址 4.逐一读取INT中数组的值,获取相应IMAGE_IMPORT_BY_NAME地址(RVA) 5.使用IMAGE_IMPORT_BY_NAME的Hint(ordinal) 或Name项,获取相应函数的起始地址。 GetProcAddress("GetCurrentThreadId") 6.读取IID的FirstThunk(IAT)成员,获得IAT地址 7.将上面获得的函数地址输入相应IAT数组值。 8.重复以上补助4~7,直到INT结束(遇到NULL时) RVA与RAW转换:     RAW = RAV - VirtualAddress + PointerToRawData     物理地址 = 内存相对地址 - 内存节区的起始地址 + 物理节区的起始位置
原文地址:https://www.cnblogs.com/zheh/p/4143889.html