zoukankan      html  css  js  c++  java
  • PE格式全分析

    我们知道,很多PE分析工具都可以查看一个EXE文件的引用DLL文件函数表,其实,这个本身就是存储在EXE头部的一个重要信息,今天,我们就来研究一下:

    我们借用一张PE结构图来分析:

    一个EXE完整的PE结构分五大部分。见上图.

    最开头的是部分是DOS部首,DOS部首由两部分组成:DOS的MZ文件标志和DOS stub(DOS存根程序)。之所以设置DOS部首是微软为了兼容原有的DOS系统下的程序而设立的。紧接着的是真正的PE文件头。值得注意的是PE文件头中的IMAGE_OPTIONAL_HEADER32是一个非常重要的结构,PE文件中的导入表、导出表、资源、重定位表等数据的位置和长度都保存在这个结构里。

    我们按照顺序,先说说IMAGE_FILE_HEADER。

    IMAGE_FILE_HEADER这个结构的定义如下:

    01.typedef struct _IMAGE_FILE_HEADER {
    02. 00h   WORD    Machine;                   //运行平台
    03. 02h   WORD    NumberOfSections;          //区块数目
    04. 06h   DWORD   TimeDateStamp;            //文件日期时间戳
    05. 0Ah   DWORD   PointerToSymbolTable;      //指向符号表
    06. 0Eh   DWORD   NumberOfSymbols;          //符号表中的符号数量
    07. 12h   WORD    SizeOfOptionalHeader;      //映像可选头结构的大小
    08. 14h   WORD    Characteristics;          //文件特征值
    09.} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

    这个结构体表明一个PE文件的基本特征属性,也是一个PE文件的入口

    Machine域说明这个pe文件在什么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_POWERPC     0x01F0  // IBM PowerPC Little-Endian
        #define IMAGE_FILE_MACHINE_SH3         0x01a2  // SH3 little-endian
        #define IMAGE_FILE_MACHINE_SH3E        0x01a4  // SH3E little-endian
        #define IMAGE_FILE_MACHINE_SH4         0x01a6  // SH4 little-endian
        #define IMAGE_FILE_MACHINE_ARM         0x01c0  // ARM Little-Endian
        #define IMAGE_FILE_MACHINE_THUMB       0x01c2
        #define IMAGE_FILE_MACHINE_IA64        0x0200  // Intel 64
        #define IMAGE_FILE_MACHINE_MIPS16      0x0266  // MIPS
        #define IMAGE_FILE_MACHINE_MIPSFPU     0x0366  // MIPS
        #define IMAGE_FILE_MACHINE_MIPSFPU16   0x0466  // MIPS
        #define IMAGE_FILE_MACHINE_ALPHA64     0x0284  // ALPHA64
        #define IMAGE_FILE_MACHINE_AXP64       IMAGE_FILE_MACHINE_ALPHA64
      

    NumberOfSections
        pe文件中区块的数量.
      
    TimeDateStamp
        文件日期时间戳,指这个pe文件生成的时间,它的值是从1969年12月31日16:00:00以来的秒数.
      
    PointerToSymbolTable
        Coff调试符号表的偏移地址.
      
    NumberOfSymbols
        Coff符号表中符号的个数. 这个域和前个域在release版本的程序里是0.
      
    SizeOfOptionalHeader
        IMAGE_OPTIONAL_HEADER32结构的大小(即多少字节).我们接着就要提到这个结构了.事实上,pe文件的大部分重要的域都在IMAGE_OPTIONAL_HEADER结构里.
      
    Characteristics
        这个域描述pe文件的一些属性信息,比如是否可执行,是否是一个动态连接库等.具体定义如下:

    #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_AGGRESIVE_WS_TRIM         0x0010  // Agressively trim working set
    #define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020  // 程序能处理大于2G的地址
    #define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.
    #define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32位机器
    #define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // .dbg文件的调试信息被移除
    #define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400  // 如果在移动介质中,拷到交换文件中运行
    #define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800  // 如果在网络中,拷到交换文件中运行
    #define IMAGE_FILE_SYSTEM                    0x1000  // 系统文件
    #define IMAGE_FILE_DLL                       0x2000  // 文件是一个dll
    #define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000  // 文件只能运行在单处理器上
    #define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.
     

    所以,根据这个结构体的信息,我们就可以判断一个文件究竟是不是一个真正的PE文件,该PE文件的类型是可执行的还是可调用的(DLL)

    我们可以写个简单的小程序来读取这个信息:

    01.#include "stdafx.h"
    02.#include "windows.h"
    03.#include "stdio.h"
    04.#include "conio.h"
    05.  
    06.int main(int argc, char* argv[])
    07.{
    08.        FILE *p;
    09.        LONG e_lfanew;  //指向IMAGE_NT_HEADERS32结构在文件中的偏移
    10.        IMAGE_FILE_HEADER myfileheader;
    11.         
    12.        p = fopen("test1.exe","r+b");//自定义读取的exe文件
    13.        if(p == NULL)return -1;//如果打开失败就返回
    14.  
    15.        fseek(p,0x3c,SEEK_SET);//注意这里是指针偏移,也就是绕过开头的DOS区块
    16.        fread(&e_lfanew,4,1,p);
    17.        fseek(p,e_lfanew+4,SEEK_SET);  //指向IMAGE_FILE_HEADER结构的偏移
    18.        fread(&myfileheader,sizeof(myfileheader),1,p);
    19.  
    20.        printf("IMAGE_FILE_HEADER结构:\n");
    21.        printf("Machine              : %04X\n",myfileheader.Machine);
    22.        printf("NumberOfSections     : %04X\n",myfileheader.NumberOfSections);
    23.        printf("TimeDateStamp        : %08X\n",myfileheader.TimeDateStamp);
    24.        printf("PointerToSymbolTable : %08X\n",myfileheader.PointerToSymbolTable);
    25.        printf("NumberOfSymbols      : %08X\n",myfileheader.NumberOfSymbols);
    26.        printf("SizeOfOptionalHeader : %04X\n",myfileheader.SizeOfOptionalHeader);
    27.        printf("Characteristics      : %04X\n",myfileheader.Characteristics);
    28.        getch();
    29.        return 0;
    30.}

    注释比较详细了,大家根据这个就可以读取一个PE文件的基本特征信息了.以上代码VC6编译通过

    紧接着上一节,我们来研究下IMAGE_OPTIONAL_HEADER32,这个属于PE中附加结构信息,同样是很重要的。

    我们先来看看它的结构:

    01.typedef struct _IMAGE_OPTIONAL_HEADER {
    02.    //
    03.    // Standard fields.
    04.    //
    05.  
    06.    00h WORD    Magic;                   //幻数,32位pe文件总为010bh
    07.    02h BYTE    MajorLinkerVersion;      //连接器主版本号
    08.    03h BYTE    MinorLinkerVersion;      //连接器副版本号
    09.    04h DWORD   SizeOfCode;              //代码段总大小
    10.    08h DWORD   SizeOfInitializedData;   //已初始化数据段总大小
    11.    0ch DWORD   SizeOfUninitializedData; //未初始化数据段总大小
    12.    10h DWORD   AddressOfEntryPoint;     //程序执行入口地址(RVA)
    13.    14h DWORD   BaseOfCode;              //代码段起始地址(RVA)
    14.    18h DWORD   BaseOfData;              //数据段起始地址(RVA)
    15.  
    16.    //
    17.    // NT additional fields.
    18.    //
    19.  
    20.    1ch DWORD   ImageBase;               //程序默认的装入起始地址
    21.    20h DWORD   SectionAlignment;        //内存中区块的对齐单位
    22.    24h DWORD   FileAlignment;           //文件中区块的对齐单位
    23.    28h WORD    MajorOperatingSystemVersion; //所需操作系统主版本号
    24.    2ah WORD    MinorOperatingSystemVersion; //所需操作系统副版本号
    25.    2ch WORD    MajorImageVersion;       //自定义主版本号
    26.    2eh WORD    MinorImageVersion;       //自定义副版本号
    27.    30h WORD    MajorSubsystemVersion;   //所需子系统主版本号
    28.    32h WORD    MinorSubsystemVersion;   //所需子系统副版本号
    29.    34h DWORD   Win32VersionValue;       //总是0
    30.    38h DWORD   SizeOfImage;             //pe文件在内存中的映像总大小
    31.    3ch DWORD   SizeOfHeaders;           //从pe文件开始到节表(包含节表)的总大小
    32.    40h DWORD   CheckSum;                //pe文件CRC校验和
    33.    44h WORD    Subsystem;               //用户界面使用的子系统类型
    34.    46h WORD    DllCharacteristics;      //为0
    35.    48h DWORD   SizeOfStackReserve;      //为线程的栈初始保留的虚拟内存的默认值
    36.    4ch DWORD   SizeOfStackCommit;       //为线程的栈初始提交的虚拟内存的大小
    37.    50h DWORD   SizeOfHeapReserve;       //为进程的堆保留的虚拟内存的大小
    38.    54h DWORD   SizeOfHeapCommit;        //为进程的堆初始提交的虚拟内存的大小
    39.    58h DWORD   LoaderFlags;             //为0
    40.    5ch DWORD   NumberOfRvaAndSizes;     //数据目录结构数组的项数,总为 00000010h
    41.    60h IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];  //数据目录结构数组
    42.} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

    这个结构体非常的庞大,大家通过注释可以看出,这个结构体保存了相当全面的PE附件信息。

    下面针对重要内容进行一个解释:

    Magic 幻数,32位pe文件总为010bh
    这个常数的定义如下:


      #define IMAGE_NT_OPTIONAL_HDR32_MAGIC      0x10b
      #define IMAGE_NT_OPTIONAL_HDR64_MAGIC      0x20b
      #define IMAGE_ROM_OPTIONAL_HDR_MAGIC       0x107
     

    MajorLinkerVersion 连接程序的主版本号 如vc6.0的为06h

    MinorLinkerVersion 连接程序的次版本号 如vc6.0的为00h

    SizeOfCode pe文件代码段的大小.是FileAlignment的整数倍.

    SizeOfInitializedData 所有含已初始化数据的块的大小,一般在.data段中.

    SizeOfUninitializedData 所有含未初始化数据的块的大小,一般在.bss段中.

    AddressOfEntryPoint 程序开始执行的地址,这是一个RVA(相对虚拟地址).对于exe文件,这里是启动代码;对于dll文件,这里是libMain()的地址.
         在脱壳时第一件事就是找入口点,指的就是这个值.
        
    BaseOfCode 代码段基地址,微软的连接程序生成的程序一般把这个值置为1000h,  
        
    BaseOfData 数据段基地址

    ImageBase pe文件默认的装入地址.windows9x中exe文件为400000h,dll文件为10000000h.

    SectionAlignment 内存中区块的对齐单位.区块总是对齐到这个值的整数倍.x86的32位系统上默认值位1000h

    FileAlignment pe文件中区块的对齐单位.pe文件中默认值为 200h.

    MajorOperatingSystemVersion
    MinorOperatingSystemVersion

        上面两个域是指运行这个pe文件所需的操作系统的最低版本号.windows95/98和windows nt 4.0 的内部版本号都是 4.0 ,而windows2000的内部版本号是5.0
      
    MajorImageVersion
    MinorImageVersion

        上面两个域是指用户自定义的pe文件的版本号.可以通过连接程序来设置,如: LINK /VERSION:2.0 MyApp.obj一般在升级时使用.
      
    MajorSubsystemVersion  
    MinorSubsystemVersion

       上面两个域是指运行这个pe文件所要求的子系统的版本号. 
      
    Win32VersionValue 总是0

    SizeOfImage pe文件装入内存后映像的总大小.如果SectionAlignment域和FileAlignment域相等,那么这个值也是pe文件在硬盘上的大小.
      
    SizeOfHeaders 从文件开始到节表(包含节表)的总大小.其后是各个区段的数据.              

    CheckSum pe文件的CRC校验和.

    Subsystem pe文件的用户界面使用的子系统类型.定义如下:

    #define IMAGE_SUBSYSTEM_UNKNOWN              0   // Unknown subsystem.
    #define IMAGE_SUBSYSTEM_NATIVE               1   // Image doesn't require a subsystem.
    #define IMAGE_SUBSYSTEM_WINDOWS_GUI          2   // Image runs in the Windows GUI subsystem.
    #define IMAGE_SUBSYSTEM_WINDOWS_CUI          3   // Image runs in the Windows character subsystem.
    #define IMAGE_SUBSYSTEM_OS2_CUI              5   // image runs in the OS/2 character subsystem.
    #define IMAGE_SUBSYSTEM_POSIX_CUI            7   // image runs in the Posix character subsystem.
    #define IMAGE_SUBSYSTEM_NATIVE_WINDOWS       8   // image is a native Win9x driver.
    #define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       9   // Image runs in the Windows CE subsystem.


    DllCharacteristics 总为0

    SizeOfStackReserve 为线程的栈初始保留的虚拟内存的大小,默认为00100000h.如果在调用CreateThread函数时指定堆栈的大小为0,被创建的线程的堆栈的初始大小就与这个值相同.

    SizeOfStackCommit 为线程的栈初始提交的虚拟内存的大小.微软的连接程序把这个值置为 1000h.
      

    SizeOfHeapReserve 为进程的堆保留的虚拟内存的大小.默认值为 00100000h.

    SizeOfHeapCommit  为进程的堆初始提交的虚拟内存的大小.微软的连接程序把这个值置为1000h.
      
    LoaderFlags 通常为0

    NumberOfRvaAndSizes 数据目录结构数组的项数,总为 00000010h
       这个值定义如下:
       #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16
      
    IMAGE_DATA_DIRECTORY DataDirectory[0x10] 数据目录结构数组
       IMAGE_DATA_DIRECTORY结构定义如下:

    1.typedef struct _IMAGE_DATA_DIRECTORY {
    2.    DWORD   VirtualAddress;// 相对虚拟地址
    3.    DWORD   Size;           //大小
    4.} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;


    这个结构包含了pe文件中重要部分的RVA地址和大小.这个数组使操作系统的加载程序能够快速定位特定的区段.具体定义如下:

    #define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
    #define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
    #define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
    #define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
    #define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
    #define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
    #define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
    //      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
    #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
    #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
    #define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
    #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
    #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
    #define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
    #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
    #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor

     通过上一节的例子。我们也依旧可以从这个结构体中读出需要的信息.

  • 相关阅读:
    美国大学排名之本科中最用功的学校top15
    PhpStorm (强大的PHP开发环境)2017.3.2 附注册方法
    获取地址栏的URL: PHP JS
    怎么给php下拉框默认选中
    在JS中使用全局变量
    原生和jQuery的ajax用法
    XAMPP重要文件目录及配置
    select获取下拉框的值 下拉框默认选中
    h5 时间控件问题,怎么设置type =datetime-local 的值
    JS截取字符串常用方法详细整理
  • 原文地址:https://www.cnblogs.com/lzjsky/p/2184942.html
Copyright © 2011-2022 走看看