• 热门专题

PE文件结构部分解析以及输入的定位

作者:shadow_lei  发布日期:2014-02-18 21:12:57
Tag标签:文件  结构  部分  
  • PE文件定义 

    PE 文件(”Portable executable”, 可移植的可执行文件)文件格式,是微软Windows NT, 中Win32Win32s中的可执行的二进制的文件格式。 包括:.exe, .dll, .sys, .com, .ocs. PE文件最重要的两个因素:

    1.磁盘上的可执行文件和它被映射到windows内存之后的格式非常相像。

    2.对于Win32 来讲, 模块中多使用的所有代码、数据、资源、导入表、和其他需要的模块数据结构都在一个连续的内存块中。因此,只需要知道PE Loader把可执行文件映射到了内存的什么地方(基址),通过作为映像的一部分指针,就可以找到这个模块的所有不同的块。

    PE文件总览: 

    1. DOS Header: (size:64byte)

    _IMAGE_DOS_HEADER结构体:

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

    结构体中有两个重要的数据成员。第一个为e_magic,这个必须为MZ,即0x5A4D。另一个重要的数据成员是最后一个成员e_lfanew,这个成员的值为IMAGE_NT_HEADERS的偏移。其中,*e_lfanew这个字段的值:   PE Header 在磁盘文件中相对于文件开始的偏移地址.

    实例截图:

    2.     PE Header: (size: 248bytes)

    IMAGE_NT_HEADERS 紧接在DOS Stub之后,位置有e_lfanew所指

    1 typedef struct _IMAGE_NT_HEADERS {
    2         DWORD Signature;                        //4 bytes PE文件头标志:(e_lfanew)->‘PE’
    3         IMAGE_FILE_HEADER FileHeader;        //20 bytes PE文件物理分布的信息
    4         IMAGE_OPTIONAL_HEADER32 OptionalHeader;//224bytes PE文件逻辑分布的信息
    5 } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

    PE Header 总览

    IMAGE_NT_HEADERS结构体成员解析:

    2.1.Signature: (4 bytes)

    2.2.IMAGE_FILE_HEADER(20 bytes)

    1 typedef struct _IMAGE_FILE_HEADER {
    2     WORD    Machine;                //运行平台
    3     WORD    NumberOfSections;        //文件区块数目
    4     DWORD   TimeDateStamp;            //文件创建日期和时间
    5     DWORD   PointerToSymbolTable;    //指向符号表(主要用于调试)
    6     DWORD   NumberOfSymbols;        //符号表中符号个数
    7     WORD    SizeOfOptionalHeader;        //IMAGE_OPTIONAL_HEADER32 结构大小
    8     WORD    Characteristics;            //文件属性
    9 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

    IMAGE_FILE_HEADER结构体成员解析:

    1). Machine 代表了CPU的类型  //运行平台

    #define IMAGE_FILE_MACHINE_UNKNOWN           0
    #define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.
    #define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian
    #define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian
    #define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian
    #define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2
    #define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
    #define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian
    #define IMAGE_FILE_MACHINE_SH3DSP            0x01a3
    #define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian
    #define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian
    #define IMAGE_FILE_MACHINE_SH5               0x01a8  // SH5
    #define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian
    ………………….
    #define IMAGE_FILE_MACHINE_CEE               0xC0EE
    View Code

    2)       NumberOfSections: 代表区块的数目,区块表紧跟在IMAGE_NT_HEADERS后面, 区块表大概是一个链表结构,链表长度由NumberOfSection的数值决定.

    3)       TimeDataStamp: 表明文件的创建时间

    4)       SizeOfOptionalHeader: 是IMAGE_NT_HEADERS的另一个子结构IMAGE_OPTIONAL_HEADER的大小

    5)       Characteristics: 代表文件的属性EXE文件一般是0100h DLL文件一般是210Eh,多种属性可以用或运算同时拥有

    #define IMAGE_FILE_RELOCS_STRIPPED   0x0001 // 重定位信息被移除 
    #define IMAGE_FILE_EXECUTABLE_IMAGE   0x0002 // 文件可执行 
    #define IMAGE_FILE_LINE_NUMS_STRIPPED  0x0004 // 行号被移除 
    #define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // 符号被移除 
    ……..
    #define IMAGE_FILE_32BIT_MACHINE  0x0100 // 32位机器 
    #define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // .dbg文件的调试信息被移除 
    ………………….
    #define IMAGE_FILE_SYSTEM       0x1000 // 系统文件 
    #define IMAGE_FILE_DLL         0x2000 // 文件是一个dll 
    #define IMAGE_FILE_UP_SYSTEM_ONLY    0x4000 // 文件只能运行在单处理器上 
    View Code

    实例截图:

    2.3. IMAGE_OPTIONAL_HEADER(224 bytes)

    紧接IMAGE_FILE_HEADER之后,IMAGE_OPTIONAL_HEADER的大小由IMAGE_FILE_HEADER中倒数第二个成员(SizeOfOptionalHeader)指定. IMAGE_OPTIONAL_HEADER结构体如下:

     1 typedef struct _IMAGE_OPTIONAL_HEADER {
     2     WORD    Magic;    //映像文件的状态
     3     BYTE    MajorLinkerVersion;    //连接器的主版本号
     4     BYTE    MinorLinkerVersion;    //连接器的次版本号
     5     DWORD   SizeOfCode;    //代码段的大小,如果有多个代码段则为总和
     6     DWORD   SizeOfInitializedData; //初始化数据段大小.如果多个则为总和
     7     DWORD   SizeOfUninitializedData;//未初始化数据段大小,.如果多个则为总和.bbs
     8     DWORD   AddressOfEntryPoint;    //PE文件入口地址的RAV:OEP = ImageBase + RAV
     9     DWORD   BaseOfCode;    //代码块起始地址的RVA
    10     DWORD   BaseOfData;//数据块的起始地址的RVA
    11     //
    12     // NT additional fields.
    13     //
    14     DWORD   ImageBase;    //可执行文件的基址ImageBase
    15     DWORD   SectionAlignment; //每一个块必须保证始于这个值的整数倍
    16     DWORD   FileAlignment; //对齐映射文件部分原始数据 2 or 512 or 64: 默认为512
    17     WORD    MajorOperatingSystemVersion;//要求的操作系统的主版本号
    18     WORD    MinorOperatingSystemVersion;//要求的操作系统的次版本号
    19     WORD    MajorImageVersion;//映像的主版本号
    20     WORD    MinorImageVersion;//映像的次版本号
    21     WORD    MajorSubsystemVersion;//子系统的主版本号
    22     WORD    MinorSubsystemVersion;//子系统的次版本号
    23     DWORD   Win32VersionValue;//保留值.必须为0
    24     DWORD   SizeOfImage;//映像文件的大小
    25     DWORD   SizeOfHeaders;
    26     DWORD   CheckSum;//映像文件的校验和
    27     WORD    Subsystem;//运行此映像的字系统
    28     WORD    DllCharacteristics;//映像文件的DLL特征
    29     DWORD   SizeOfStackReserve;//堆栈保留字节. 0x100000
    30     DWORD   SizeOfStackCommit;//线程开始提交的初始堆栈大小
    31     DWORD   SizeOfHeapReserve;//为初始进程保留的虚拟内存总数
    32     DWORD   SizeOfHeapCommit;//进程开始提交初始虚拟内存的大小
    33     DWORD   LoaderFlags;
    34     DWORD   NumberOfRvaAndSizes; //0x10
    35 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
    36 //指向第一个IMAGE_DATA_DIRECTORY
    37 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

    _IMAGE_OPTIONAL_HEADER结构体实例截图以及成员解析:

    1) Magic:32位可执行文件来:0x010B

          64位可执行文件来:0x020B

           0x107

    2) SizeOfCode: 代码段的大小,如果有多个代码段则为总和

      RAW:经过文件对齐处理后大小(PE文件中的大小)

    3) SizeOfInitializedData: 初始化数据段大小.如果多个则为总和

    4) ImageBase: 建议的装载地址

    PE建议装载地址:

    实际装载地址:

    5) AddressOfEntryPoint: 程序执行的入口RVA地址

    OEP = ImageBase + (AddressOfEntryPoint)RVA

    6) SectionAlignment:为内存中节的对齐大小,一般为0×00001000

    7) FileAlignment:为PE文件中节的对齐大小

    8)  SizeofImage:映像文件的大小

    9) DataDirectory为数据目录表数组,比较重要:共有16个表项

    Size = sizeof(_IMAGE_DATA_DIRECTORY) * 16

    sizeof(_IMAGE_DATA_DIRECTORY) = 8 bytes

    _IMAGE_DATA_DIRECTORY结构体以及成员定义:

     1 typedef struct _IMAGE_DATA_DIRECTORY {
     2     DWORD   VirtualAddress;
     3     DWORD   Size;
     4 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
     5 #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16
     6 // Directory Entries
     7 #define IMAGE_DIRECTORY_ENTRY_EXPORT            0   // Export Directory
     8 #define IMAGE_DIRECTORY_ENTRY_IMPORT             1   // Import Directory
     9 #define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
    10 #define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
    11 #define IMAGE_DIRECTORY_ENTRY_SECURITY          4   // Security Directory
    12 #define IMAGE_DIRECTORY_ENTRY_BASERELOC        5   // Base Relocation Table
    13 #define IMAGE_DIRECTORY_ENTRY_DEBUG             6   // Debug Directory
    14 //      IMAGE_DIRECTORY_ENTRY_COPYRIGHT        7   // (X86 usage)
    15 #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE  7   // Architecture Specific Data
    16 #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR         8   // RVA of GP
    17 #define IMAGE_DIRECTORY_ENTRY_TLS                 9   // TLS Directory
    18 #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10 // Load Configuration Directory
    19 #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT     11 // Bound Import Directory in headers
    20 #define IMAGE_DIRECTORY_ENTRY_IAT              12   // Import Address Table
    21 #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT    13   // Delay Load Import Descriptors
    22 #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

    Import Table RVA & Size截图:

    3. Section Header:

    Section Header 数量:_IMAGE_FILE_HEADER结构体中NumberOfSections成员。

    Section Header 定位:紧跟在IMAGE_NT_HEADERS后面

    结构体大小:40 bytes

    _IMAGE_SECTION_HEADER 结构体以及重要变量的定义:

     1 #define IMAGE_SIZEOF_SHORT_NAME   8
     2 typedef struct _IMAGE_SECTION_HEADER {
     3     BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];
     4     union {
     5             DWORD   PhysicalAddress;
     6             DWORD   VirtualSize;
     7     } Misc;
     8     DWORD   VirtualAddress;    //内存中偏移地址
     9     DWORD   SizeOfRawData;    //PE文件中对其之后的大小
    10     DWORD   PointerToRawData;//为PE块区在PE文件中偏移
    11     DWORD   PointerToRelocations;
    12     DWORD   PointerToLinenumbers;
    13     WORD    NumberOfRelocations;
    14     WORD    NumberOfLinenumbers;
    15     DWORD   Characteristics;    //块区的属性:可读、可写..
    16 } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
    17 #define IMAGE_SIZEOF_SECTION_HEADER          40

    重要数据成员

    1)      Name[IMAGE_SIZEOF_SHORT_NAME]:

    8字节大小的NAME, 如果节区名称小于8个字节,则多余的用0填充,否则全部填充节名,末尾不保证有1个0,同样会被名字填充

    2)      PointerToRawData:

    为节区在PE文件中的偏移

    3)      Characteristics: 为节区的属性,如可读、可写、可执行等

    1 #define IMAGE_SCN_CNT_CODE       0x00000020  // Section contains code.
    2 #define IMAGE_SCN_LNK_NRELOC_OVFL   0x01000000  // Section contains extended relocations.
    3 #define IMAGE_SCN_MEM_DISCARDABLE   0x02000000  // Section can be discarded.
    4 #define IMAGE_SCN_MEM_NOT_CACHED    0x04000000  // Section is not cachable.
    5 #define IMAGE_SCN_MEM_NOT_PAGED         0x08000000  // Section is not pageable.
    6 #define IMAGE_SCN_MEM_SHARED             0x10000000  // Section is shareable.
    7 #define IMAGE_SCN_MEM_EXECUTE            0x20000000  // Section is executable.
    8 #define IMAGE_SCN_MEM_READ              0x40000000  // Section is readable.
    View Code

    Section Header 实例截图:

    4.  导入表

    _IMAGE_IMPORT_DESCRIPTOR 数据结构:(20 bytes)

    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
        union {
            DWORD   Characteristics;     // 0 for terminating null import descriptor
            DWORD   OriginalFirstThunk; // RVA 指向INT (PIMAGE_THUNK_DATA)
        };
    DWORD   TimeDateStamp;    
        DWORD   ForwarderChain;     // -1 if no forwarders
        DWORD   Name;            //dll 名称
        DWORD   FirstThunk;         //指向引入函数真实地址单元处的RVA  IAT
    } IMAGE_IMPORT_DESCRIPTOR;
    typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

    其中OriginalFirstThunk和FirstThunk非常类似,指向两个本质上相同的数组IMAGE_THUNK_DATA。

    1) 定位查找IMAGE_IMPORT_DESCRIPTO结构

    A 获取引入表的RVA.也就是

     data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress 所指的值

    typedef struct _IMAGE_DATA_DIRECTORY {
        DWORD   VirtualAddress;
        DWORD   Size;
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
    #define IMAGE_DIRECTORY_ENTRY_IMPORT             1   // Import Directory

    查看 IMAGE_DIRECTORY_ENTRY_IMPORT的值

    &data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = 0x0002D51C

    &data directory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualSize= 0xA0

    B. 查找导入表所在的区段:

    if(RVA>=SECTION.VirtualAddress && RVA<SECTION.Misc.VirtualSize)

     {

         //处于该节

     }

     else

     {

         //不在该节中

     }

    C. 找到引入表所在的节后就可以用该节的VirtualAddress和PointerToRawData两个域确定引入表在文件中的偏移量:RVA –△k

    VirtualAddress = 0x00026000

    PointerToRawData = 0x00025000

    △k = VirtualAddress – PointerToRawData = 0x1000H

    Address = 0x0002D51C - △k = 0x0002C51C

    然后定位到文件偏移处:

    阴影部分是IID的内容, IID的大小为20h, 阴影部分存在链各个IID,最后一个为0000000, 说明此PE文件只有8个IID, 对应8个dll。

    0xA0  = 160 bytes = 7 * 20bytes +20bytes(空白)

    截图

    第四个变量的地址RVA:0002DB14, 需要转换成对应的文件偏移

    (0x0002DB14 –△k) = 0x0002cb14,定位到文件偏移为0x0002cb14的地方

    查看内容,里面记录的是IMAGE_IMPORT_DESCRIPTOR的第四个成员变量多对应的dll的名字,mfc90u.dll,到此,我们已经找到了这个输入的dll

    2) 获取dll调用的所有函数

    IMAGE_IMPORT_DESCRIPTOR中的第一个参数和最后一个参数,original_first_thunk 和first_thunk分别指向了INT(输入名称表)IAT(输入地址表)这两个表里面分别记录了指向调用函数名的地址,和此函数在dll中的序号(序号用来快速索引dll中的函数)

    0x0002D85C和0x000262A0是INT 和IAT数组的首地址,下面我们跳到该地址(由于△K=0x1000,故RVA=文件偏移0x0002C85C和0x000252A0)

    _IMAGE_THUNK_DATA32数据结构:

    typedef struct _IMAGE_THUNK_DATA32 {
        union {
            DWORD ForwarderString;      // PBYTE 
            DWORD Function;             // PDWORD
            DWORD Ordinal;               //数据
            DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
        } u1;
    } IMAGE_THUNK_DATA32;
    typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

    a.当一个函数以序号导入.MAGE_THUNK_DATA结构中的AddressOfData最高位被设成1.用16进制表示为(0x80000000),例入一个IMAGE_THUNK_DATA的AddressOfData的值为0x 800010E4在mfc90u.dll数组中.就表示IMAGE_THUNK_DATA将引入mfc90u.dll

    中的第10E4号函数

    INT数组:

    IAT数组:

    b.如果一个函数以名称导入.IMAGE_THUNK_DATA结构中的Ordinal域就包含一个RVA.这个RVA指向一个IMAGE_IMPORT_BY_NAME 结构.该结构保存了一个引入函数的相关信息:例如MSVCR90.dll

    typedef struct _IMAGE_IMPORT_BY_NAME {
        WORD    Hint; //序列号
        BYTE    Name[1];//函数名称
    } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

    有第四个变量Name定位dll名字

    定位INT和IAT:

    INT:

    IAT

    数组里面的值为指向IMAGE_IMPORT_BY_NAME的地址,IMAGE_IMPORT_BY_NAME里面存放的是所调用函数的名字的地址,下来我们选取一个数组里面的值,跳转到相应的IMAGE_IMPORT_BY_NAME

    可以看到,上面的那个图片中显示了所调用的函数的名字,名字前面的两个字节代表的是函数在dll中的序号,方便以后快速索引到

    3. 需要注意的地方

    INT 和IAT数组在一开始的时候,里面存放的地址都是一样的,他们都是指向所调用函数的名字的字符串。而在加载到内存的时候,IAT的值会发生变换,它的值存放的是dll中函数实际被调用的地址,在加载到内存后,就只需要保存IAT就可以了,利用它来调用函数

     

    今天先到这里.有时间的话在继续...

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规