CTF Wiki
  • 简介
  • 如何使用 CTF Wiki
  • introduction
    • CTF 历史
    • CTF 竞赛模式简介
    • CTF 竞赛内容
    • 线下攻防经验小结
    • CGC 超级挑战赛
    • 学习资源
  • misc
    • 杂项简介
    • 取证隐写前置技术
    • 信息搜集技术
    • encode
      • 通信领域常用编码
      • 计算机相关的编码
      • 现实世界中常用的编码
    • picture
      • 图片分析简介
      • JPG
      • PNG
      • GIF
    • audio
      • 音频隐写
    • archive
      • RAR 格式
      • ZIP 格式
    • traffic
      • 流量包分析简介
      • 协议分析概述
        • Wireshark
        • DNS
        • HTTP
        • HTTPS
        • FTP
        • USB
        • WIFI
      • 数据提取
      • PCAP 文件修复
    • disk-memory
      • 磁盘内存分析
      • 题目
    • shellcode
    • other
      • pyc
  • web
    • Web 简介
    • XSS
    • php
      • PHP 代码审计
    • SQL 注入
      • sqlmap绕过脚本
      • 各版本数据库语句备忘
    • CSRF
    • SSRF
  • reverse
    • 软件逆向工程简介
    • identify-encode-encryption
      • 常见加密算法和编码识别
    • language
      • 简介
      • go
        • Golang 逆向入门
      • python
        • Python 逆向入门
      • rust
        • Rust 逆向入门
    • maze
      • 迷宫问题
    • obfuscate
      • 控制流平坦化
      • 花指令
      • movofuscator
      • Self-Modified Code
    • vm
      • 虚拟机分析
    • platform
      • linux
        • Detecting Breakpoints
        • Detecting debugging
        • False Disassembly
        • LD_PRELOAD
      • windows
        • anti-debug
          • CheckRemoteDebuggerPresent
          • 反调试技术例题
          • Heap Flags
          • Interrupt 3
          • IsDebuggerPresent
          • 花指令
          • NtGlobalFlag
          • NtQueryInformationProcess
          • The Heap
          • Thread Local Storage(TLS)
          • ZwSetInformationThread
        • unpack
          • 一步到达 OEP 法
          • ESP 定律法
          • DUMP 及 IAT 重建
          • 最后一次异常法
          • 手动查找 IAT 并使用 ImportREC 重建
          • 内存镜像法
          • 保护壳简介
          • SFX 法
          • 单步跟踪法
          • DLL 文件脱壳
    • tools
      • constraint
        • z3
      • debug
        • gdb
        • ollydbg
        • windbg
        • x64dbg/x32dbg
      • simulate-execution
        • angr
        • Unicorn Engine
      • static-analyze
        • dnspy
        • Ghidra
        • IDA Pro
        • jadx
  • crypto
    • 密码学简介
    • asymmetric
      • 介绍
      • discrete-log
        • 离散对数
        • ECC
        • ElGamal
      • knapsack
        • 背包加密
      • lattice
        • CVP
        • 基本介绍
        • 格基规约算法
        • 格概述
      • rsa
        • RSA 选择明密文攻击
        • RSA 复杂题目
        • Coppersmith 相关攻击
        • 公钥指数相关攻击
        • 模数相关攻击
        • Bleichenbacher's attack
        • RSA 侧信道攻击
        • RSA 介绍
        • d_attacks
          • 私钥 d 相关攻击
          • 扩展维纳攻击
    • attack-summary
      • 简介
      • 比特攻击
      • 中间相遇攻击 - MITM
    • basic
      • 基础数学知识
    • blockcipher
      • AES
      • ARX: Add-Rotate-Xor
      • DES
      • IDEA
      • 块加密
      • Simon and Speck Block Ciphers
      • mode
        • CBC
        • CFB
        • CTR
        • ECB
        • 分组模式
        • OFB
        • Padding Oracle Attack
        • 填充方式
        • PCBC
    • certificate
      • 证书格式
    • classical
      • 古典密码简介
      • 单表代换加密
      • 其它类型加密
      • 多表代换加密
      • 总结
    • hash
      • Hash Attack
      • 综合题目
      • Fowler–Noll–Vo hash function
      • 哈希函数
      • MD5
      • SHA1
    • signature
      • DSA
      • ElGamal
      • 数字签名
      • RSA 数字签名
    • streamcipher
      • 流密码
      • fsr
        • 反馈移位寄存器
        • 线性反馈移位寄存器 - LFSR
        • 非线性反馈移位寄存器
      • lcg
        • 题目
        • 线性同余生成器
      • prng
        • 密码安全伪随机数生成器
        • 伪随机数生成器介绍
        • 题目
      • special
        • RC4
  • pwn
    • MacOS
    • misc-os
    • 概述
      • stackoverflow
        • 执行 Shellcode
        • 栈介绍
        • 栈溢出原理
    • browser
      • Chrome
      • Firefox
      • Safari
    • hardware
      • 简介
        • side-channel
          • prefetch side-channel attack
      • trusted-computing
        • 可信执行环境
    • linux
      • kernel-mode
        • 基础知识
        • Introduction
          • DoS
          • Information Disclosure
          • Introduction
            • Change Others
            • Change Self
        • Introduction
          • Introduction
            • 信息泄漏
            • Misc
          • Introduction
            • Kernel Stack Canary
          • Introduction
            • inner-kernel
              • 内部隔离
            • Introduction
              • KPTI - Kernel Page Table Isolation
              • 用户代码不可执行
              • 用户数据不可访问
          • Introduction
            • FGKASLR
            • KASLR
        • Introduction
          • 编译内核驱动
          • 内核下载与编译
          • Qemu 模拟环境
          • Real Device
        • exploitation
          • heap
            • 内核堆概述
            • buddy
              • Cross-Cache Overflow & Page-level Heap Fengshui
              • Page-level UAF
            • slub
              • freelist 劫持
              • Heap Spray
              • kernel UAF
          • race
            • Double Fetch
            • userfaultfd 的使用
          • rop
            • bypass-smep
            • ret2dir
            • 利用 pt_regs 构造通用内核 ROP
            • ret2usr(已过时)
            • Kernel ROP
          • tricks
            • 在内存中直接搜索 flag
      • user-mode
        • environment
        • fmtstr
          • 检测
          • 例子
          • 利用
          • 原理介绍
        • integeroverflow
          • 整数溢出
        • io-file
          • glibc 2.24下 IO_FILE 的利用
          • 伪造vtable劫持程序流程
          • FSOP
          • FILE结构
        • mitigation
          • Canary
        • race-condition
          • introduction
          • 题目
        • summary
          • 获取地址
          • shell 获取小结
          • 控制程序执行流
        • Type Confusion
        • Uninitialized Memory
        • heap
          • mallocng
          • ptmalloc2
            • Chunk Extend and Overlapping
            • Fastbin Attack
            • 堆概述
            • 堆相关数据结构
            • 堆溢出
            • House Of Einherjar
            • House Of Force
            • House of Lore
            • House of Orange
            • House of Pig
            • House of Rabbit
            • House of Roman
            • 堆利用
            • Large Bin Attack
            • 通过堆进行信息泄漏
            • 堆中的 Off-By-One
            • 堆中的检查
            • tcache makes heap exploitation easy again
            • Unlink
            • Unsorted Bin Attack
            • Use After Free
            • implementation
              • 基础操作
              • 释放内存块
              • 堆初始化
              • malloc_state 相关函数
              • 申请内存块
              • 测试支持
              • 深入理解堆的实现
              • tcache
        • stackoverflow
          • arm
            • 环境搭建
            • Arm ROP
          • mips
            • mips - ROP
          • RISC-V
          • x86
            • 基本 ROP
            • 花式栈溢出技巧
            • 中级ROP
            • 栈介绍
            • 栈溢出原理
            • advanced-rop
              • 高级 ROP
              • ret2dlresolve
              • ret2VDSO
              • SROP
    • sandbox
      • Chroot
      • Docker
      • Namespace
      • python
        • Python 沙盒
      • seccomp
        • C 沙盒逃逸
      • Shell Sandbox
    • virtualization
      • basic-knowledge
        • 虚拟化技术简介
        • CPU 虚拟化
        • IO 虚拟化
        • 内存虚拟化
      • parallels
        • Parallels
      • VirtualBox
      • VMWare
      • qemu
        • basic-knowledge
          • QEMU 设备模拟
          • QEMU 内存管理
        • environment
          • 编写 QEMU 模拟设备
          • QEMU 下载与编译
        • exploitation
          • QEMU 逃逸入门
          • 越界读写
  • Android 安全
    • basic_develop
      • Android 开发基础
    • Android 应用运行机制简述
      • Android 中 Java 层的运行机制
        • dex
          • DEX文件
          • ODEX文件
        • smali
          • Smali
      • native_layer
        • so 介绍
    • basic_reverse
      • Android 关键代码定位
      • Android 逆向基本介绍
      • dynamic
        • Android 动态调试
        • IDA 动态调试原生层程序
        • IDA 动态调试 smali 代码
      • static
        • 静态分析综合题目
        • 静态分析 java 层例子
        • 静态分析原生层程序
  • blockchain
    • Blockchain Security Challenges
    • Blockchain Security Overview
    • ethereum
      • Ethereum Basics
      • Ethereum Overview
      • Ethereum Opcodes
      • 学习资源
      • Smart Contract Reverse
      • Function Selector and Argument Encoding
      • Ethereum Storage
      • attacks
        • Airdrop Hunting
        • Arbitrary Writing
        • CREATE2
        • Delegatecall
        • Introduction
        • Jump Oriented Programming
        • Integer Overflow and Underflow
        • Randomness
        • Re-Entrancy
        • Short Address Attack
        • Uninitialized Storage Pointer
    • publicblockchain
      • Public Blockchain Security Overview
      • Blockchain Weaknesses
  • assembly
    • ARM
    • MIPS
    • x86_x64
  • executable
    • elf
      • 程序加载
      • 程序执行流程
      • linking
        • 程序链接
        • Symbol Reslove
      • structure
        • ELF 文件
        • Code Section
        • Data Related Sections
        • Dynamic Sections
        • Misc Sections
        • Sections
        • String Sections
        • .symtab: Symbol Table
    • pe
      • PE 文件格式
      • 导出表
      • 导入表
      • 基址重定位表
  • ics
    • ICS_CTF 竞赛
    • ICS_CTF 发现
    • ICS_CTF 利用
    • ICS_CTF 学习资源
  • contribute
    • 贡献之前
    • 基本贡献方式
    • 贡献文档要求
    • 翻译
  • write up
    • 浙江工业大学CTF赛事
      • 2023第四届“安恒杯”CTF新生赛题解
Powered by GitBook
On this page
  • PE 简介
  • 常用术语及其含义
  • PE文件头
  • MS-DOS 文件头
  • IMAGE_NT_HEADERS
  • PE 数据主体
  • Section Header
  • Sections
  1. executable
  2. pe

PE 文件格式

PE 简介

PE 文件的全称是 Portable Executable ,意为可移植的可执行的文件,常见的EXE、DLL、OCX、SYS、COM都是PE 文件,PE 文件是微软Windows操作系统上的程序文件,可能是间接被执行,如DLL)。 一个 32-bits 的 PE 文件布局如下图所示:

+-------------------------------+ \
|     MS-DOS MZ header          |  |
+-------------------------------+  |
| MS-DOS Real-Mode Stub program |  |
+-------------------------------+  |
|     PE Signature              |  | -> PE file header
+-------------------------------+  |
|     IMAGE_FILE_HEADER         |  |
+-------------------------------+  |
|     IMAGE_OPTIONAL_HEADER     |  |
+-------------------------------+ /
|     section header #1         | 
+-------------------------------+ 
|     section header #2 
+------------------------- 
: 
: 
 
+------------------------------+ 
|        section #1            | 
+------------------------------+ 
|        section #2 
+-------------------- 
: 
: 

接下来将会以一个 32-bit 的 PE 文件作为标本介绍一下 PE 文件。

// 示例代码 test.c
#include <stdio.h>

int main(){
  printf("Hello, PE!\n");

  return 0;
}

通过 Devcpp 软件的 TDM-GCC 4.9.2 32-bit Release 方式编译文件生成 test.exe,作为示例文件。

常用术语及其含义

  • 映像文件 因为 PE 文件通常需要加载到内存中才能执行,相当于内存中的映像,所以 PE 文件也叫做映像文件。

  • RVA 相对虚拟地址,映像文件在虚拟内存中相对于加载基址的偏移。

  • VA 虚拟地址,映像文件在虚拟内存中的地址。

  • FOA 文件偏移地址,映像文件在磁盘文件中相对于文件开头的偏移。

因为不论是在磁盘文件上,或是在虚拟内存中,数据相对于其所在节的相对偏移是固定的,据此可以实现 RVA 与 FOA 之间的转换,即RVA - 节区RVA = FOA - 节区FOA。

假设某一个属于 .data 节的数据的 RVA 是 0x3100,.data 节的 节区RVA 为 0x3000,那么该数据相对于 .data 节的相对偏移就是 0x100。而 .data 节在的 节区FOA 为 0x1C00,那么该数据在磁盘文件中的 FOA 就是 0x1D00。完整的计算公式是:FOA = 节区FOA + (RVA - 节区RVA)。如果该映像文件的加载基址为0x40000000,那么该数据的 VA 就是 0x40003100。

PE文件头

PE 文件的最开始便是 PE 文件头,它由 MS-DOS 文件头 和 IMAGE_NT_HEADERS 结构体组成。

MS-DOS 文件头

MS-DOS 文件头 包含 IMAGE_DOS_HEADER 和 DOS Stub 两个部分。

IMAGE_DOS_HEADER 结构体的定义如下:

typedef struct _IMAGE_DOS_HEADER
{
     WORD e_magic;              // "MZ"
     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];
     LONG e_lfanew;             // NT 头相对于文件起始处的偏移
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

IMAGE_DOS_HEADER 结构体中有 2 个重要成员:

  • e_magic 单字。DOS 签名 "4D5A",即 ASCII 值 "MZ"。所有 PE 文件的开头都有 DOS 签名。

  • e_lfanew 单字。IMAGE_NT_HEADER相对于文件起始处的偏移。

示例程序的 IMAGE_DOS_HEADER 如图 2 所示:

IMAGE_DOS_HEADER 结构体后紧接着是 DOS Stub,它的作用很简单,当系统为 MS-DOS 环境时,输出 This program cannot be run in DOS mode. 并退出程序,表明该程序不能在 MS-DOS 环境下运行。这使得所有的 PE 文件都对 MS-DOS 环境兼容。利用该特性可以创建出一个在 MS-DOS 和 Windows 环境中都能运行的程序,在 MS-DOS 中执行 16-bit MS-DOS 代码,在 Windows 中执行 32-bit Windows 代码。

示例程序的 DOS Stub 如图 3 所示:

IMAGE_NT_HEADERS

IMAGE_NT_HEADERS 结构体,俗称 NT 头。紧跟在 DOS Stub 之后,其定义如下:

typedef struct _IMAGE_NT_HEADERS {
  DWORD                   Signature;         /* +0000h PE 标识 */
  IMAGE_FILE_HEADER       FileHeader;        /* +0004h PE 标准头 */
  IMAGE_OPTIONAL_HEADER32 OptionalHeader;    /* +0018h PE 可选头  */
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

示例程序的 IMAGE_NT_HEADERS 如图 4 所示:

接下来详细说一下 NT 头。

PE Signature

NT 头的第一个成员是PE Signature,它是一个4字节大小的ASCII码字符串 PE\0\0,用于指明当前文件是一个 PE 格式的映像文件。其位置可以通过 IMAGE_DOS_HEADER 的 e_lfanew 成员的值确定。

IMAGE_FILE_HEADER

PE Signature 后紧跟着是 IMAGE_FILE_HEADER 结构体,又称作 COFF 头(标准通用文件格式头)。其定义如下:

typedef struct _IMAGE_FILE_HEADER {
  WORD  Machine;                    /* +0004h 目标机器类型 */
  WORD  NumberOfSections;           /* +0006h PE 中节的数量 */
  DWORD TimeDateStamp;              /* +0008h 时间戳 */
  DWORD PointerToSymbolTable;       /* +000ch 指向符号表的指针 */
  DWORD NumberOfSymbols;            /* +0010h 符号表中符号数目 */
  WORD  SizeOfOptionalHeader;       /* +0012h 可选头的大小 */
  WORD  Characteristics;            /* +0014h 文件属性标志 */
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

接下来依次对每一个字段做出解释:

  • NumberOfSections 单字。文件中存在的节区数量。PE 文件将代码、数据、资源的依据属性分类到不同节区中存储。

  • TimeDateStamp 双字。低 32 位表示从 1970 年 1 月 1 日 00:00 到文件创建时经过的秒数。

  • PointerToSymbolTable 双字。符号表的文件偏移。如果不存在符号表,其值为 0。

  • NumberOfSymbols 双字。该字段表示符号表中的符号数量。由于字符串表紧跟在符号表之后,所有能通过该值定位字符串表。

  • SizeOfOptionalHeader 单字。表示可选头的大小。在 32-bit 机器上默认是 0x00E0,在 64-bit 机器上默认是 0x00F0。

  • **Characteristics 单字。用于标识文件属性,以 bit OR 方式组合。**下面是一些已定义的文件属性标志:

// 文件属性标志
#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_AGGRESSIVE_WS_TRIM       0x0010    // 已废弃
#define IMAGE_FILE_LARGE_ADDRESS_AWARE      0x0020    // 应用可处理大于 2GB 的地址
#define IMAGE_FILE_BYTES_REVERSED_LO        0x0080    // 小尾存储。已废弃
#define IMAGE_FILE_32BIT_MACHINE            0x0100    // 基于 32-bit 体系结构
#define IMAGE_FILE_DEBUG_STRIPPED           0x0200    // 不存在调试信息
#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    // 映像文件是动态链接库文件
#define IMAGE_FILE_UP_SYSTEM_ONLY           0x4000    // 文件只能在单处理器机器上运行
#define IMAGE_FILE_BYTES_REVERSED_HI        0x8000    // 大尾存储(已废弃)

示例程序的 IMAGE_FILE_HEADER 如下:

// 示例程序 IMAGE_FILE_HEADER
RVA       Value      Description
----------------------------------------------------
00000084  014C       机器类型
00000086  000F       节区数量
00000088  5D88E2A6   时间戳
0000008c  00012C00   符号表偏移
00000090  000004E4   符号数量
00000094  00E0       可选头大小
00000096  0107       文件属性
                     0001  IMAGE_FILE_RELOCS_STRIPPED
                     0002  IMAGE_FILE_EXECUTABLE_IMAGE
                     0004  IMAGE_FILE_LINE_NUMS_STRIPPED
                     0100  IMAGE_FILE_32BIT_MACHINE

IMAGE_OPTIONAL_HEADER

之所以IMAGE_OPTIONAL_HEADER 叫做可选头,是因为对于目标文件,它没有任何作用,只是平白增加了目标文件的大小;但对于映像文件来说,它提供了加载时必需的信息。定义如下:

typedef struct _IMAGE_OPTIONAL_HEADER {
  WORD                 Magic;                            /* +0018h 魔数 */
  BYTE                 MajorLinkerVersion;               /* +001ah 链接器主要版本号 */
  BYTE                 MinorLinkerVersion;               /* +001bh 链接器次要版本号 */
  DWORD                SizeOfCode;                       /* +001ch 所有含代码的节的总大小 */
  DWORD                SizeOfInitializedData;            /* +0020h 所有含已初始化数据的节的总大小 */
  DWORD                SizeOfUninitializedData;          /* +0024h 所有含未初始化数据的节的总大小 */
  DWORD                AddressOfEntryPoint;              /* +0028h 程序入口点RVA */
  DWORD                BaseOfCode;                       /* +002ch 代码节起始RVA */
  DWORD                BaseOfData;                       /* +0030h 数据节起始RVA */
  DWORD                ImageBase;                        /* +0034h 映像文件加载时的首选地址 */
  DWORD                SectionAlignment;                 /* +0038h 内存中节对齐粒度*/
  DWORD                FileAlignment;                    /* +003ch 文件中节对齐粒度 */
  WORD                 MajorOperatingSystemVersion;      /* +0040h 操作系统主要版本号 */
  WORD                 MinorOperatingSystemVersion;      /* +0042h 操作系统次要版本号 */
  WORD                 MajorImageVersion;                /* +0044h 映像文件主要版本号 */
  WORD                 MinorImageVersion;                /* +0046h 映像文件次要版本号 */
  WORD                 MajorSubsystemVersion;            /* +0048h 子系统主要版本号 */
  WORD                 MinorSubsystemVersion;            /* +004ah 子系统次要版本号 */
  DWORD                Win32VersionValue;                /* +004ch 保留。置0 */
  DWORD                SizeOfImage;                      /* +0050h 内存中映像文件的大小 */
  DWORD                SizeOfHeaders;                    /* +0054h 所有头+节表大小 */
  DWORD                CheckSum;                         /* +0058h 映像文件校验和 */
  WORD                 Subsystem;                        /* +005ch 运行映像所需子系统 */
  WORD                 DllCharacteristics;               /* +005eh 映像文件的DLL属性 */
  DWORD                SizeOfStackReserve;               /* +0060h 初始化时的保留的栈大小 */
  DWORD                SizeOfStackCommit;                /* +0064h 初始化时实际提交的栈大小 */
  DWORD                SizeOfHeapReserve;                /* +0068h 初始化时保留的堆大小 */
  DWORD                SizeOfHeapCommit;                 /* +006ch 初始化时实际提交的堆大小 */
  DWORD                LoaderFlags;                      /* +0070h 已废弃 */
  DWORD                NumberOfRvaAndSizes;              /* +0074h 数据目录结构的数量 */
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];     /* +0078h 指向数据目录中第一个 IMAGE_DATA_DIRECTORY 结构体的指针 */
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
  • Magic 单字。指明映像文件的类型。0x0107h 表示 ROM 映像;0x010B 表示 PE32;0x020B 表示 PE32+,即 64-bit 的 PE 文件。

  • MajorLinkerVersion 字节。指定链接器主要版本号。

  • MinorLinkerVersion 字节。指定链接器次要版本号。

  • SizeOfCode 双字。所有包含代码的节的总大小。这里的大小指文件对齐后的大小。判断某个节是否包含代码的方法是根据节属性是否包含 IMAGE_SCN_CNT_CODE 标志。

  • SizeOfInitializedData 双字。所有包含已初始化数据节的总大小。

  • SizeOfUninitializedData 双字。所有包含未初始化数据节的总大小。

  • AddressOfEntryPoint 双字。入口点函数的指针相对于映像文件加载基址的偏移量。对于可执行文件,这是启动地址;对于设备驱动,这是初始化函数的地址;入口点函数对于 DLL 文件是可选的,如果不存在入口点,该成员必须置 0。

  • BaseOfCode 双字。代码节的 RVA,代码节起始处相对于映像文件加载基址的偏移量。通常代码节紧跟在 PE 头 后面,节名为 ".text"。

  • BaseOfData 双字。数据节的 RVA,数据节起始处相对于映像文件加载基址的偏移量。通常数据节位于文件末尾,节名为 ".data"。

  • **ImageBase 双字。映像文件加载时的优先载入地址,值必须是 64KB 的整数倍。**应用程序的默认值是 0x00400000;DLL 的默认值是 0x10000000。当一个程序用到了多个 DLL 文件时,PE 加载器会调整 DLL 的载入地址,使所有 DLL 文件都能够被正确载入。

  • SectionAlignment 双字。内存中的节对齐粒度。该成员的值必须不小于 FileAlignment 成员的值。默认的值与系统的页大小相等。

  • FileAlignment 双字。映像文件中原始数据的对齐粒度。值必须是在 512-64K 范围内的 2 的幂。默认值为512,但如果 SectionAlignment 成员的值小于系统页大小,则 FileAlignment 与 SectionAlignment 两者成员的值必须相同。

  • MajorOperatingSystemVersion 单字。操作系统主要版本号。

  • MinorOperatingSystemVersion 单字。操作系统次要版本号。

  • MajorImageVersion 单字。映像文件主要版本号。

  • MinorImageVersion 单字。映像文件次要版本号。

  • MajorSubsystemVersion 单字。子系统主要版本号。

  • MinorSubsystemVersion 单字。子系统次要版本号。

  • Win32VersionValue 双字。保留。置0。

  • SizeOfImage 双字。映像文件在虚拟内存中所占的大小。值必须为 SectionAlignment 的整数倍。

  • SizeOfHeaders 双字。PE 文件头和所有节表大小的总和按照 FileAlignment 对齐后的大小。第一节区在文件开始偏移为 SizeOfHeaders 处。

  • CheckSum 双字。映像文件的校验值。需要在装载时校验的文件有所有的驱动,任何在启动时装载的 DLL,以及任何加载到关键系统进程中的 DLL。

  • Subsystem 单字。运行映像文件所需的子系统。已定义的子系统标志如下:

// Subsystem 标志
#define IMAGE_SUBSYSTEM_UNKNOWN                      0  // 未知子系统
#define IMAGE_SUBSYSTEM_NATIVE                       1  // 不需要子系统。设备驱动和本机系统进程
#define IMAGE_SUBSYSTEM_WINDOWS_GUI                  2  // Windows 图形用户接口(GUI)子系统
#define IMAGE_SUBSYSTEM_WINDOWS_CUI                  3  // Windows 字符模式用户接口子(CUI)系统
#define IMAGE_SUBSYSTEM_OS2_CUI                      5  //  OS/2 CUI 子系统
#define IMAGE_SUBSYSTEM_POSIX_CUI                    7  // POSIX CUI 子系统
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI               9  // Windows CE 系统
#define IMAGE_SUBSYSTEM_EFI_APPLICATION             10  // 可扩展固件接口(EFI)应用程序
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVEICE_DRIVER    11  // 带引导服务的 EFI 驱动程序
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER          12  // 带运行时服务的 EFI 驱动程序
#define IMAGE_SUBSYSTEM_EFI_ROM                     13  // EFI ROM 映像
#define IMAGE_SUBSYSTEM_XBOX                        14  // XBOX 系统
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION    16  // 引导应用程序
  • DllCharacteristics 单字。映像文件的 DLL 属性,以 bit OR 方式组合。各标志位的含义如下:

// DLL 属性标志
// 0x0001 0x0002 0x0004 0x0008 保留,值必须为 0。
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE             0x0040  // DLL 可以在加载时重定位
#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY          0x0080  // 强制实行代码完整性检验
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT                0x0100  // 映像兼容数据执行保护(DEP)
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION             0x0200  // 映像可以隔离,但不应该被隔离
#define IMAGE_DLLCHARACTERISTICS_NO_SEH                   0x0400  // 映像不使用结构化异常处理(SEH)
#define IMAGE_DLLCHARACTERISTICS_NO_BIND                  0x0800  // 不绑定映像
//#define IMAGE_DLLCHARACTERISTICS_APPCONTAINER           0x1000  // 在 32-bit 保留;64-bit 表示映像必须在 AppContainer 内执行
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER               0x2000  // WDM 驱动
//#define IMAGE_DLLCHARACTERISTICS_GUARD_CF               0x4000  // 在 32-bit 保留;64-bit 表示映像支持控制流保护
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE    0x8000  // 映像可用于终端服务器
  • SizeOfStackReserve 双字。初始化时保留的栈内存大小,默认值是 1MB。具体说是初始化时为栈保留的虚拟内存的大小,但并不是所有保留的虚拟内存都能直接作为栈使用。初始化时实际提交的栈大小由 SizeOfStackCommit 成员指定。

  • SizeOfStackCommit 双字。初始化时实际提交的栈内存大小。

  • SizeOfHeapReserve 双字。初始化时保留的堆内存大小,默认值为 1MB。每一个进程至少为会有一个默认的进程堆,在进程启动的时候被创建,并且在进程的声明周期内不会被删除。

  • SizeOfHeapCommit 双字。初始化时实际提交的堆内存大小,默认大小为 1 页。可以通过链接器的 "-heap" 参数指定起始保留的堆内存大小和实际提交的堆内存大小。

  • LoaderFlags 成员已弃用。

  • NumberOfRvaAndSizes 双字。数据目录结构的数量。通常为 0x00000010,即 16 个。

  • DataDirectory 结构体。由 IMAGE_DATA_DIRECTORY 结构体组成的数组,数组的每项都有被定义的值。结构体定义如下:

typedef struct _IMAGE_DATA_DIRECTORY {
  DWORD VirtualAddress;      /* 数据目录的 RVA */
  DWORD Size;                /* 数据目录的大小 */
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

各数组项如下:

// 数据目录
DataDirectory[0] =   EXPORT Directory           // 导入表 RVA 和大小
DataDirectory[1] =   IMPORT Directory           // 导入表 RVA 和大小
DataDirectory[2] =   RESOURCE Directory         // 资源表 RVA 和大小
DataDirectory[3] =   EXCEPTION Directory        // 异常表 RVA 和大小
DataDirectory[4] =   CERTIFICATE Directory      // 证书表 FOA 和大小
DataDirectory[5] =   BASE RELOCATION Directory  // 基址重定位表 RVA 和大小
DataDirectory[6] =   DEBUG Directory            // 调试信息 RVA 和大小
DataDirectory[7] =   ARCH DATA Directory        // 指定架构信息 RVA 和大小
DataDirectory[8] =   GLOBALPTR Directory        // 全局指针寄存器 RVA
DataDirectory[9] =   TLS Directory              // 线程私有存储表 RVA 和大小
DataDirectory[10] =  LOAD CONFIG Directory      // 加载配置表 RVA 和大小
DataDirectory[11] =  BOUND IMPORT Directory     // 绑定导入表 RVA 和大小
DataDirectory[12] =  `IAT` Directory              // 导入地址表 RVA 和大小
DataDirectory[13] =  DELAY IMPORT Directory     // 延迟导入描述符 RVA 和大小
DataDirectory[14] =  CLR Directory              // CLR数据 RVA 和大小
DataDirectory[15] =  Reserverd                  // 保留

PE 数据主体

PE 数据主体包括 Section Header 和所有的节区。

Section Header

紧跟在可选头后面的是 Section Header,也称作节表。PE 文件种所有节的属性都被定义在节表中。节表由一系列的 IMAGE_SECTION_HEADER 结构体组成,结构体大小均为 40 字节。每一个结构体描述一个节的信息,定义如下:

typedef struct _IMAGE_SECTION_HEADER {
  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];    /* 节区名 */
  union {
    DWORD PhysicalAddress;                /* 物理地址 */
    DWORD VirtualSize;                    /* 虚拟内存中节区大小  */
  } Misc;
  DWORD VirtualAddress;                   /* 虚拟内存中节区 RVA */
  DWORD SizeOfRawData;                    /* 磁盘文件中节区大小 */
  DWORD PointerToRawData;                 /* 磁盘文件中节区 FOA */
  DWORD PointerToRelocations;             /* 指向重定位表的指针 */
  DWORD PointerToLinenumbers;             /* 指向行号表的指针 */
  WORD  NumberOfRelocations;              /* 重定位入口数量 */
  WORD  NumberOfLinenumbers;              /* 行号数量 */
  DWORD Characteristics;                  /* 节区属性 */
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
  • Name 节名称字符串。长度最多 8 个字节。

  • Misc

    • PhysicalAddress 双字。文件地址。

    • VirtualSize 双字。虚拟内存中的节区所占内存大小。

  • VirtualAddress 双字。虚拟内存中节区 RVA。

  • SizeOfRawData 双字。对于映像文件,表示磁盘上初始化数据的大小,值必须为 FileAlignment 的整数倍;对于目标文件,表示节的大小。

  • PointerToRawData 双字。磁盘文件中节区起始处的 FOA。值必须是 FileAlignment 的整数倍。

  • PointerToRelocations 双字。在对象文件中使用,指向重定位表的指针。

  • PointerToLinenumbers 双字。行号信息位置(供调试用)。如果没有行号信息则置 0;同时因为不建议使用 COFF 调试信息,在映像文件中应置 0。

  • NumberOfRelocations 单字。重定位入口的数量,在映像文件中置 0。

  • NumberOfLinenumbers 单字。行号数量(供调试用)。因为不建议使用 COFF 调试信息,所以在映像文件中应置 0。

  • Characteristics 双字。节区属性。,以 bit OR 方式组合。各标志位的含义如下:

// 节区属性
#define IMAGE_SCN_CNT_CODE                0x00000020  // 节区包含代码
#define IMAGE_SCN_CNT_INITIALIZED_DATA    0x00000040  // 节区包含已初始化数据
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA  0x00000080  // 节区包含未初始化数据
#define IMAGE_SCN_ALIGN_1BYTES            0x00100000  // 1-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_2BYTES            0x00200000  // 2-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_4BYTES            0x00300000  // 4-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_8BYTES            0x00400000  // 8-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_16BYTES           0x00500000  // 16-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_32BYTES           0x00600000  // 32-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_64BYTES           0x00700000  // 64-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_128BYTES          0x00800000  // 128-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_256BYTES          0x00900000  // 256-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_512BYTES          0x00A00000  // 512-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_1024BYTES         0x00B00000  // 1024-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_2048BYTES         0x00C00000  // 2048-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_4096BYTES         0x00D00000  // 4096-byte 对齐。仅用于目标文件
#define IMAGE_SCN_ALIGN_8192BYTES         0x00E00000  // 8192-byte 对齐。仅用于目标文件
#define IMAGE_SCN_LNK_NRELOC_OVFL         0x01000000  // 节区包含扩展的重定位项
#define IMAGE_SCN_MEM_DISCARDABLE         0x02000000  // 节区可根据需要丢弃,如 .reloc 在进程开始后被丢弃
#define IMAGE_SCN_MEM_NOT_CACHED          0x04000000  // 节区不会被缓存
#define IMAGE_SCN_MEM_NOT_PAGED           0x08000000  // 节区不可分页
#define IMAGE_SCN_MEM_SHARED              0x10000000  // 节区可共享给不同进程
#define IMAGE_SCN_MEM_EXECUTE             0x20000000  // 节区可作为代码执行
#define IMAGE_SCN_MEM_READ                0x40000000  // 节区可读
#define IMAGE_SCN_MEM_WRITE               0x80000000  // 节区可写

示例文件的节区头如下:

No.  Name    VirtualSize  VirtualOffset  RawSize   RawOffset  Characteristics
--------------------------------------------------------------------------
01   .text   00001670     00001000       00001800  00000400   60500020  R-X  包含可执行代码
02   .data   0000002C     00003000       00000200  00001C00   C0300040  RW-  包含已初始化数据
03   .rdata  00000168     00004000       00000600  00001E00   40300040  R--  包含已初始化数据
04   .bss    00000450     00005000       00000000  00000000   C0700080  RW-  包含未初始化数据
05   .idata  00000564     00006000       00000600  00002400   C0300040  RW-  包含已初始化数据
06   .CRT    00000034     00007000       00000200  00002A00   C0300040  RW-  包含已初始化数据
07   .tls    00000020     00008000       00000200  00002C00   C0300040  RW-  包含已初始化数据
08   /4      000002D8     00009000       00000400  00002E00   42400040  R--  包含已初始化数据
09   /19     0000A6D5     0000A000       0000A800  00003200   42100040  R--  包含已初始化数据
0A   /31     0000199E     00015000       00001A00  0000DA00   42100040  R--  包含已初始化数据
0B   /45     000018F3     00017000       00001A00  0000F400   42100040  R--  包含已初始化数据
0C   /57     00000780     00019000       00000800  00010E00   42300040  R--  包含已初始化数据
0D   /70     000002F2     0001A000       00000400  00011600   42100040  R--  包含已初始化数据
0E   /81     00000D1E     0001B000       00000E00  00012800   42100040  R--  包含已初始化数据
0F   /92     00000230     0001C000       00000400  00012C00   42100040  R--  包含已初始化数据

Sections

紧跟在 Section Header 后面的就是各个 sections,即节区。PE 文件一般至少要求有两个节区,用于存储可执行数据的代码节区 .text,和存储数据的数据节区 .data。通过节区名可以猜测节区的用途,但节区名不是决定节区用途的因素,只作为一种参考。比如也可以将代码节区的节区名修改为 .data,对于程序执行不会有影响。这里讲一下常见节区的用途:

 .text  默认的代码节区。用于保存可执行代码。
 .data  默认的读/写数据节区。用于保存已初始化的全局变量,静态变量。
.rdata  默认的只读数据节区。
.idata  用于保存导入表信息。包含IAT, INT, 导入函数名称以及导入 DLL 名称等。
.edata  用于保存导出表信息。
 .rsrc  用于保存资源表信息。
  .bss  用于保存未初始化数据。
  .tls  用于保存 TLS(线程局部存储)信息。
.reloc  用于保存重定位表信息。

其中有一些 Section 需要重点关注,比如保存着库文件导入相关数据的 .idata 节,或者与线程私有存储相关的 .tls 节等等。对这些重要节进行分析,就是之后学习的主要内容。

PreviouspeNext导出表

Last updated 1 year ago

Machine 单字。用于指明 CPU 类型。详细了解所支持的 CPU 类型请参考 。

示例程序的 IMAGE_OPTIONAL_HEADER 如下图:

微软 PE 格式 COFF 文件头 Machine 类型
IMAGE_DOS_HEADER
DOS Stub
NT 头