目录
  1. 1. PE框架基础学习
    1. 1.1. PE结构
      1. 1.1.1. RVA 和文件偏移的转换
    2. 1.2. PE 指纹
    3. 1.3. DOS MZ文件头
    4. 1.4. PE 头
      1. 1.4.1. IMAGE_FILE_HEADER标准PE头
      2. 1.4.2. IMAGE_OPTIONAL_HEADER32扩展PE头
      3. 1.4.3. RVA与块对齐
    5. 1.5. 节表/块表
PE框架基础学习

PE框架基础学习

PE结构

虚拟地址(VA): 在一个程序运行起来的时候,会被加载到内存中,并且每个进程都有自己的4GB,这个4GB当中的某个位置叫做虚拟地址,也就是内存中的地址,由物理地址映射过来的,4GB的空间,并没有全部被用到。

基地址( Imagebase ): 磁盘中的文件加载到内存当中的时候可以加载到任意位置,而这个位置就是程序的基址。EXE默认的加载基址是400000h,DLL文件默认基址是10000000h。需要注意的是基地址不是程序的入口点。

相对虚拟地址(RVA):。RVA是在内存中相对与载入地址(基地址)的偏移量,所以你可以发现前三个概念的关系 : 虚拟地址(VA)= 基地址+ 相对虚拟地址(RVA)
文件偏移地址(FOA):当PE文件储存在某个磁盘当中的时候,某个数据的位置相对于文件头的偏移量。

入口点(OEP):首先明确一个概念就是OEP是一个RVA,,然后使用 OEP + Imagebase == 入口点的VA,通常情况下,OEP指向的不是main函数。

VOffset:节起始地址对于ImageBase的偏移量

ROffset:该节起始地址对于文件的偏移量

现在让你求某个虚拟地址VA,对应的文件偏移地址 fRVA

fRVA-ROffset=RVA-VOffset
fRVA=RVA-VOffset+ROffset
(RVA=VA-ImageBase)

图

图

RVA 和文件偏移的转换

图


PE 指纹

文件开头DOS标志“MZ” -> 在0x3c(e_lfanew)处可找到“PE”头地址

由此可确定该文件为PE文件

图


DOS MZ文件头

IMAGE_DOS_HEADER STRUCT 
{ 
    WORD    e_magic        //   MZ(4Dh 5Ah)     DOS可执行文件标记 
    WORD    e_cblp            
    WORD    e_cp                         
    WORD    e_crlc                      
    WORD    e_cparhdr      
    WORD    e_minalloc       
    WORD    e_maxalloc  
    WORD    e_ss           
    WORD    e_sp       
    WORD    e_csum      
    WORD    e_ip        
    WORD    e_cs        
    WORD    e_lfarlc       
    WORD    e_ovno          
    WORD    e_res[4]        
    WORD    e_oemid         
    WORD    e_oeminfo    
    WORD    e_res2[10]  
    DWORD   e_lfanew     //  RVA     指向PE文件头 
} IMAGE_DOS_HEADER ENDS

IMAGE_DOS_HEADER通常有0x40(64)字节

图

其中重要的成员为第一个成员和最后一个成员,即PE指纹,其他部分修改后不影响程序运行


PE 头

  • x32 :

    typedef struct IMAGE_NT_HEADERS 
    {
        DWORD    Signature   //PE标识
        IMAGE_FILE_HEADER    FileHeader   //标准PE头
        IMAGE_OPTIONAL_HEADER32   OptionalHeader   //扩展PE头
    } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
  • x64:

    typedef struct IMAGE_NT_HEADERS 
    {
        DWORD    Signature   //PE标识
        IMAGE_FILE_HEADER    FileHeader   //标准PE头
        IMAGE_OPTIONAL_HEADER64   OptionalHeader   //扩展PE头
    } IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

IMAGE_FILE_HEADER标准PE头

通常有0x14(20)字节

图

struct IMAGE_ FILE_ HEADER {
    WORD Machine ;
    WORD NumberOfSections ;
    DWORD TineDatestanp;
    DWORD Pointer ToSymbolTable;
    DWORD Number0fSymbols ;
    WORD SizeOfOptionalHeader ; //OptionHeader成员大小
    WORD Characteristics;
} 

IMAGE_OPTIONAL_HEADER32扩展PE头

(其大小通常在标准PE头中指出,即上面结构体中的SizeOfOptionalHeader

在32位系统下通常有0xE0(224)字节

在64位系统下通常为0xF0(240)字节

图

typedef struct _IMAGE_OPTIONAL_HEADER
{
    //
    // Standard fields.  
    //
    WORD    Magic;                   // 标志字, ROM 映像(0107h),普通可执行文件(010Bh)
    BYTE    MajorLinkerVersion;      // 链接程序的主版本号
    BYTE    MinorLinkerVersion;      // 链接程序的次版本号
    DWORD   SizeOfCode;              // 所有含代码的节的总大小
    DWORD   SizeOfInitializedData;   // 所有含已初始化数据的节的总大小
    DWORD   SizeOfUninitializedData; // 所有含未初始化数据的节的大小
    DWORD   AddressOfEntryPoint;     // 程序执行入口RVA
    DWORD   BaseOfCode;              // 代码的区块的起始RVA
    DWORD   BaseOfData;              // 数据的区块的起始RVA
    //
    // NT additional fields.    以下是属于NT结构增加的领域。
    //
    DWORD   ImageBase;               // 程序的首选装载地址
    DWORD   SectionAlignment;        // 内存中的区块的对齐大小
    DWORD   FileAlignment;           // 磁盘中文件中的区块的对齐大小
    WORD    MajorOperatingSystemVersion;  // 要求操作系统最低版本号的主版本号
    WORD    MinorOperatingSystemVersion;  // 要求操作系统最低版本号的副版本号
    WORD    MajorImageVersion;       // 可运行于操作系统的主版本号
    WORD    MinorImageVersion;       // 可运行于操作系统的次版本号
    WORD    MajorSubsystemVersion;   // 要求最低子系统版本的主版本号
    WORD    MinorSubsystemVersion;   // 要求最低子系统版本的次版本号
    DWORD   Win32VersionValue;       // 一般为0
    DWORD   SizeOfImage;             // 映像装入内存后的总尺寸
    DWORD   SizeOfHeaders;           // 所有头 + 区块表的尺寸大小
    DWORD   CheckSum;                // 映像的校检和
    WORD    Subsystem;               // 可执行文件期望的子系统
    WORD    DllCharacteristics;      // DllMain()函数何时被调用,默认为 0
    DWORD   SizeOfStackReserve;      // 初始化时的栈大小
    DWORD   SizeOfStackCommit;       // 初始化时实际提交的栈大小
    DWORD   SizeOfHeapReserve;       // 初始化时保留的堆大小
    DWORD   SizeOfHeapCommit;        // 初始化时实际提交的堆大小
    DWORD   LoaderFlags;             
    DWORD   NumberOfRvaAndSizes;     
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];   
// 数据目录表
} 

RVA与块对齐

其中SizeOfHeaders(位于标准PE头IMAGE_FILE_HEADER后0x20字节后)是FileAlignment的整数倍

图

图片中第一个方块中为内存中的区块的对齐大小0x1000

第二个方块为磁盘文件中的区块的对齐大小0x200

图


节表/块表

IMAGE_SECTION_HEADER 各个块通常有0x28(40)字节

图

文章作者: Sakura式
文章链接: http://yoursite.com/2020/07/28/PE%E6%A1%86%E6%9E%B6%E5%9F%BA%E7%A1%80%E5%AD%A6%E4%B9%A0/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Hexo
打赏
  • 微信
  • 支付寶