Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

debug/pe - PE 文件格式

概述

debug/pe 包提供了对 PE(Portable Executable)文件格式的读取支持。

PE 是什么

  • 📋 Windows 标准格式:Windows 系统的可执行文件格式
  • 🔧 多种文件类型:可执行文件(.exe)、动态库(.dll)、目标文件(.obj)、驱动程序(.sys)
  • 📦 包含多个节:代码节、数据节、资源节、符号表等
  • 🛠️ Windows 平台专用:用于 Windows NT 及后续版本

主要用途

  • 🔍 分析 Windows 可执行文件:读取节、符号、导入导出表
  • 🛠️ 链接器开发:处理目标文件和符号解析
  • 📊 二进制分析:提取程序结构信息
  • 🔐 安全工具:检查二进制文件完整性、恶意软件分析
  • 🐛 调试工具:配合调试器使用

重要说明

  • ⚠️ 只读访问:仅用于读取 PE 文件
  • ⚠️ 底层格式:需要了解 PE 规范
  • ⚠️ Windows 平台:主要用于 Windows 系统
  • 标准库支持:Go 标准库提供完整支持

PE 文件结构

PE 文件布局

+------------------+
| DOS Header       |  <- DOS 文件头(64 字节)
+------------------+
| DOS Stub         |  <- DOS 存根程序(可选)
+------------------+
| PE Signature     |  <- PE 签名("PE\0\0")
+------------------+
| COFF File Header |  <- COFF 文件头
+------------------+
| Optional Header  |  <- 可选头(PE32 或 PE32+)
+------------------+
| Section Table    |  <- 节表
+------------------+
| Section 1        |  <- 各个节(.text、.data 等)
| Section 2        |
| ...              |
+------------------+
| Symbol Table     |  <- 符号表(可选)
| String Table     |  <- 字符串表(可选)
+------------------+

DOS Header 结构

e_magic: 2 bytes    - 魔术数字(0x5A4D = "MZ")
e_cblp: 2 bytes     - 最后页大小
e_cp: 2 bytes       - 页数
e_crlc: 2 bytes     - 重定位数量
...
e_lfanew: 4 bytes   - PE 头偏移量

PE Header 结构

Signature: 4 bytes      - PE 签名(0x00004550 = "PE\0\0")
Machine: 2 bytes        - 目标机器类型
NumberOfSections: 2 bytes - 节数量
TimeDateStamp: 4 bytes  - 时间戳
PointerToSymbolTable: 4 bytes - 符号表偏移
NumberOfSymbols: 4 bytes - 符号数量
SizeOfOptionalHeader: 2 bytes - 可选头大小
Characteristics: 2 bytes - 文件标志

Optional Header 结构(PE32)

Magic: 2 bytes          - 魔术数字(0x10b = PE32, 0x20b = PE32+)
...
AddressOfEntryPoint: 4 bytes - 入口点 RVA
ImageBase: 4 bytes      - 首选加载地址
...
DataDirectories: 16 entries - 数据目录

核心类型

1. File - PE 文件

type File struct {
    FileHeader
    OptionalHeader interface{}  // *OptionalHeader32 或 *OptionalHeader64
    Sections []*Section
    Symbols []Symbol
    COFFSymbols []COFFSymbol
    StringTable []byte
    
    // 包含过滤或未导出的字段
}

功能:表示打开的 PE 文件。

字段说明

  • FileHeader:COFF 文件头
  • OptionalHeader:可选头(32 位或 64 位)
  • Sections:节列表
  • Symbols:符号列表
  • COFFSymbols:COFF 符号列表
  • StringTable:字符串表

主要方法

// 打开文件
func Open(name string) (*File, error)
func NewFile(r io.ReaderAt) (*File, error)

// 关闭文件
func (f *File) Close() error

// 获取节
func (f *File) Section(name string) *Section

// 获取节(通过索引)
func (f *File) SectionByIndex(index int) (*Section, error)

// 获取符号
func (f *File) Symbols() ([]Symbol, error)

// 获取导入的库
func (f *File) ImportedLibraries() ([]string, error)
func (f *File) ImportedSymbols() ([]string, error)

// 获取 DWARF 数据
func (f *File) DWARF() (*dwarf.Data, error)

注意事项

  • ⚠️ PE 文件可能有多个节(最多 65535 个)
  • ⚠️ 需要区分 PE32 和 PE32+ 格式
  • ✅ 提供完整的符号表和导入导出表解析

2. FileHeader - 文件头

type FileHeader struct {
    Machine              uint16
    NumberOfSections     uint16
    TimeDateStamp        uint32
    PointerToSymbolTable uint32
    NumberOfSymbols      uint32
    SizeOfOptionalHeader uint16
    Characteristics      uint16
}

字段说明

  • Machine:目标机器类型(如 x86、x64、ARM)
  • NumberOfSections:节数量
  • TimeDateStamp:编译时间戳(Unix 时间)
  • PointerToSymbolTable:符号表文件偏移
  • NumberOfSymbols:符号数量
  • SizeOfOptionalHeader:可选头大小
  • Characteristics:文件标志(可重定位、可执行等)

3. OptionalHeader32 - 可选头(32 位)

type OptionalHeader32 struct {
    Magic                       uint16
    MajorLinkerVersion          uint8
    MinorLinkerVersion          uint8
    SizeOfCode                  uint32
    SizeOfInitializedData       uint32
    SizeOfUninitializedData     uint32
    AddressOfEntryPoint         uint32
    BaseOfCode                  uint32
    BaseOfData                  uint32
    ImageBase                   uint32
    SectionAlignment            uint32
    FileAlignment               uint32
    MajorOperatingSystemVersion uint16
    MinorOperatingSystemVersion uint16
    MajorImageVersion           uint16
    MinorImageVersion           uint16
    MajorSubsystemVersion       uint16
    MinorSubsystemVersion       uint16
    Win32VersionValue           uint32
    SizeOfImage                 uint32
    SizeOfHeaders               uint32
    CheckSum                    uint32
    Subsystem                   uint16
    DllCharacteristics          uint16
    SizeOfStackReserve          uint32
    SizeOfStackCommit           uint32
    SizeOfHeapReserve           uint32
    SizeOfHeapCommit            uint32
    LoaderFlags                 uint32
    NumberOfRvaAndSizes         uint32
    DataDirectory               [16]DataDirectory
}

重要字段

  • Magic:PE32(0x10b)或 PE32+(0x20b)
  • AddressOfEntryPoint:程序入口点 RVA
  • ImageBase:首选加载地址
  • SectionAlignment:节对齐大小
  • FileAlignment:文件对齐大小
  • Subsystem:子系统类型(GUI、Console 等)
  • DataDirectory:数据目录(导入表、导出表等)

4. OptionalHeader64 - 可选头(64 位)

type OptionalHeader64 struct {
    Magic                       uint16
    MajorLinkerVersion          uint8
    MinorLinkerVersion          uint8
    SizeOfCode                  uint32
    SizeOfInitializedData       uint32
    SizeOfUninitializedData     uint32
    AddressOfEntryPoint         uint32
    BaseOfCode                  uint32
    ImageBase                   uint64  // 64 位地址
    SectionAlignment            uint32
    FileAlignment               uint32
    MajorOperatingSystemVersion uint16
    MinorOperatingSystemVersion uint16
    MajorImageVersion           uint16
    MinorImageVersion           uint16
    MajorSubsystemVersion       uint16
    MinorSubsystemVersion       uint16
    Win32VersionValue           uint32
    SizeOfImage                 uint32
    SizeOfHeaders               uint32
    CheckSum                    uint32
    Subsystem                   uint16
    DllCharacteristics          uint16
    SizeOfStackReserve          uint64  // 64 位大小
    SizeOfStackCommit           uint64
    SizeOfHeapReserve           uint64
    SizeOfHeapCommit            uint64
    LoaderFlags                 uint32
    NumberOfRvaAndSizes         uint32
    DataDirectory               [16]DataDirectory
}

与 32 位的区别

  • ImageBase:64 位地址
  • SizeOfStackReserve/Commit:64 位大小
  • SizeOfHeapReserve/Commit:64 位大小
  • 没有 BaseOfData 字段

5. Section - 节

type Section struct {
    SectionHeader
    io.ReaderAt
}

功能:表示 PE 文件中的一个节。

字段说明

  • SectionHeader:节头信息
  • io.ReaderAt:用于读取节内容

主要方法

// 读取节数据
func (s *Section) Data() ([]byte, error)

// 读取重定位
func (s *Section) Relocs() ([]Reloc, error)

// 读取符号
func (s *Section) Symbols() ([]Symbol, error)

6. SectionHeader - 节头

type SectionHeader struct {
    Name                 [8]byte
    VirtualSize          uint32
    VirtualAddress       uint32
    SizeOfRawData        uint32
    PointerToRawData     uint32
    PointerToRelocations uint32
    PointerToLinenumbers uint32
    NumberOfRelocations  uint16
    NumberOfLinenumbers  uint16
    Characteristics      uint32
}

字段说明

  • Name:节名称(8 字节,如 .text.data
  • VirtualSize:内存中的实际大小
  • VirtualAddress:加载到内存时的 RVA
  • SizeOfRawData:文件中的大小
  • PointerToRawData:文件偏移
  • PointerToRelocations:重定位表偏移
  • NumberOfRelocations:重定位数量
  • Characteristics:节标志(可读、可写、可执行等)

7. Symbol - 符号

type Symbol struct {
    Name          string
    Value         uint32
    SectionNumber int16
    Type          uint16
    StorageClass  uint8
    NumAuxSymbols int
}

字段说明

  • Name:符号名称
  • Value:符号值(地址)
  • SectionNumber:所在节索引
  • Type:符号类型
  • StorageClass:存储类别
  • NumAuxSymbols:辅助符号数量

存储类别常量

IMAGE_SYM_CLASS_END_OF_FUNCTION = 0xFF
IMAGE_SYM_CLASS_NULL          = 0x00
IMAGE_SYM_CLASS_AUTOMATIC     = 0x01
IMAGE_SYM_CLASS_EXTERNAL      = 0x02
IMAGE_SYM_CLASS_STATIC        = 0x03
IMAGE_SYM_CLASS_FUNCTION      = 0x20
IMAGE_SYM_CLASS_FILE          = 0x67
IMAGE_SYM_CLASS_SECTION       = 0x68
IMAGE_SYM_CLASS_WEAK_EXTERNAL = 0x6F

8. COFFSymbol - COFF 符号

type COFFSymbol struct {
    Name          [8]byte
    Value         uint32
    SectionNumber int16
    Type          uint16
    StorageClass  uint8
    NumAuxSymbols int
}

功能:表示 COFF 格式的符号。

辅助方法

// 获取符号名称(支持长名称)
func (s *COFFSymbol) FullName(StringTable []byte) (string, error)

9. DataDirectory - 数据目录

type DataDirectory struct {
    VirtualAddress uint32
    Size           uint32
}

功能:表示数据目录项。

常见数据目录

0:  IMAGE_DIRECTORY_ENTRY_EXPORT         // 导出表
1:  IMAGE_DIRECTORY_ENTRY_IMPORT         // 导入表
2:  IMAGE_DIRECTORY_ENTRY_RESOURCE       // 资源表
3:  IMAGE_DIRECTORY_ENTRY_EXCEPTION      // 异常表
4:  IMAGE_DIRECTORY_ENTRY_SECURITY       // 安全表
5:  IMAGE_DIRECTORY_ENTRY_BASERELOC      // 重定位表
6:  IMAGE_DIRECTORY_ENTRY_DEBUG          // 调试信息
7:  IMAGE_DIRECTORY_ENTRY_ARCHITECTURE   // 架构特定
8:  IMAGE_DIRECTORY_ENTRY_GLOBALPTR      // 全局指针
9:  IMAGE_DIRECTORY_ENTRY_TLS            // TLS 表
10: IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    // 加载配置
11: IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   // 绑定导入
12: IMAGE_DIRECTORY_ENTRY_IAT            // 导入地址表
13: IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   // 延迟导入
14: IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR // COM 描述符

常量定义

Machine - 机器类型

const (
    IMAGE_FILE_MACHINE_UNKNOWN   uint16 = 0x0
    IMAGE_FILE_MACHINE_I386      uint16 = 0x14c  // x86
    IMAGE_FILE_MACHINE_R3000     uint16 = 0x162  // MIPS
    IMAGE_FILE_MACHINE_R4000     uint16 = 0x166  // MIPS
    IMAGE_FILE_MACHINE_R10000    uint16 = 0x168  // MIPS
    IMAGE_FILE_MACHINE_WCEMIPSV2 uint16 = 0x169  // MIPS
    IMAGE_FILE_MACHINE_ALPHA     uint16 = 0x184  // Alpha
    IMAGE_FILE_MACHINE_SH3       uint16 = 0x1a2  // SuperH
    IMAGE_FILE_MACHINE_SH3DSP    uint16 = 0x1a3  // SuperH DSP
    IMAGE_FILE_MACHINE_SH3E      uint16 = 0x1a4  // SuperH 3E
    IMAGE_FILE_MACHINE_SH4       uint16 = 0x1a6  // SuperH 4
    IMAGE_FILE_MACHINE_SH5       uint16 = 0x1a8  // SuperH 5
    IMAGE_FILE_MACHINE_ARM       uint16 = 0x1c0  // ARM
    IMAGE_FILE_MACHINE_THUMB     uint16 = 0x1c2  // ARM Thumb
    IMAGE_FILE_MACHINE_ARMNT     uint16 = 0x1c4  // ARM Thumb-2
    IMAGE_FILE_MACHINE_AM33      uint16 = 0x1d3  // AM33
    IMAGE_FILE_MACHINE_POWERPC   uint16 = 0x1f0  // PowerPC
    IMAGE_FILE_MACHINE_POWERPCFP uint16 = 0x1f1  // PowerPC FP
    IMAGE_FILE_MACHINE_IA64      uint16 = 0x200  // Itanium
    IMAGE_FILE_MACHINE_MIPS16    uint16 = 0x266  // MIPS 16
    IMAGE_FILE_MACHINE_ALPHA64   uint16 = 0x284  // Alpha 64
    IMAGE_FILE_MACHINE_MIPSFPU   uint16 = 0x366  // MIPS FPU
    IMAGE_FILE_MACHINE_MIPSFPU16 uint16 = 0x466  // MIPS FPU 16
    IMAGE_FILE_MACHINE_AXP64     uint16 = 0x284  // AXP 64
    IMAGE_FILE_MACHINE_TRICORE   uint16 = 0x520  // TriCore
    IMAGE_FILE_MACHINE_CEF       uint16 = 0xcef  // CEF
    IMAGE_FILE_MACHINE_EBC       uint16 = 0xebc  // EBC
    IMAGE_FILE_MACHINE_AMD64     uint16 = 0x8664 // x86-64
    IMAGE_FILE_MACHINE_M32R      uint16 = 0x9041 // M32R
    IMAGE_FILE_MACHINE_ARM64     uint16 = 0xaa64 // ARM 64
    IMAGE_FILE_MACHINE_CEE       uint16 = 0xc0ee // CEE
)

Characteristics - 文件标志

const (
    IMAGE_FILE_RELOCS_STRIPPED         uint16 = 0x0001
    IMAGE_FILE_EXECUTABLE_IMAGE        uint16 = 0x0002
    IMAGE_FILE_LINE_NUMS_STRIPPED      uint16 = 0x0004
    IMAGE_FILE_LOCAL_SYMS_STRIPPED     uint16 = 0x0008
    IMAGE_FILE_AGGRESIVE_WS_TRIM       uint16 = 0x0010
    IMAGE_FILE_LARGE_ADDRESS_AWARE     uint16 = 0x0020
    IMAGE_FILE_BYTES_REVERSED_LO       uint16 = 0x0080
    IMAGE_FILE_32BIT_MACHINE           uint16 = 0x0100
    IMAGE_FILE_DEBUG_TRACED            uint16 = 0x0200
    IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP uint16 = 0x0400
    IMAGE_FILE_NET_RUN_FROM_SWAP       uint16 = 0x0800
    IMAGE_FILE_SYSTEM                  uint16 = 0x1000
    IMAGE_FILE_DLL                     uint16 = 0x2000
    IMAGE_FILE_UP_SYSTEM_ONLY          uint16 = 0x4000
    IMAGE_FILE_BYTES_REVERSED_HI       uint16 = 0x8000
)

Section Characteristics - 节标志

const (
    IMAGE_SCN_TYPE_NO_PAD            uint32 = 0x00000008
    IMAGE_SCN_CNT_CODE               uint32 = 0x00000020
    IMAGE_SCN_CNT_INITIALIZED_DATA   uint32 = 0x00000040
    IMAGE_SCN_CNT_UNINITIALIZED_DATA uint32 = 0x00000080
    IMAGE_SCN_LNK_OTHER              uint32 = 0x00000100
    IMAGE_SCN_LNK_INFO               uint32 = 0x00000200
    IMAGE_SCN_LNK_REMOVE             uint32 = 0x00000800
    IMAGE_SCN_LNK_COMDAT             uint32 = 0x00001000
    IMAGE_SCN_NO_DEFER_SPEC_EXC      uint32 = 0x00004000
    IMAGE_SCN_GPREL                  uint32 = 0x00008000
    IMAGE_SCN_MEM_FARDATA            uint32 = 0x00008000
    IMAGE_SCN_MEM_PURGEABLE          uint32 = 0x00020000
    IMAGE_SCN_MEM_16BIT              uint32 = 0x00020000
    IMAGE_SCN_MEM_LOCKED             uint32 = 0x00040000
    IMAGE_SCN_MEM_PRELOAD            uint32 = 0x00080000
    IMAGE_SCN_ALIGN_1BYTES           uint32 = 0x00100000
    IMAGE_SCN_ALIGN_2BYTES           uint32 = 0x00200000
    IMAGE_SCN_ALIGN_4BYTES           uint32 = 0x00300000
    IMAGE_SCN_ALIGN_8BYTES           uint32 = 0x00400000
    IMAGE_SCN_ALIGN_16BYTES          uint32 = 0x00500000
    IMAGE_SCN_ALIGN_32BYTES          uint32 = 0x00600000
    IMAGE_SCN_ALIGN_64BYTES          uint32 = 0x00700000
    IMAGE_SCN_ALIGN_128BYTES         uint32 = 0x00800000
    IMAGE_SCN_ALIGN_256BYTES         uint32 = 0x00900000
    IMAGE_SCN_ALIGN_512BYTES         uint32 = 0x00A00000
    IMAGE_SCN_ALIGN_1024BYTES        uint32 = 0x00B00000
    IMAGE_SCN_ALIGN_2048BYTES        uint32 = 0x00C00000
    IMAGE_SCN_ALIGN_4096BYTES        uint32 = 0x00D00000
    IMAGE_SCN_ALIGN_8192BYTES        uint32 = 0x00E00000
    IMAGE_SCN_ALIGN_MASK             uint32 = 0x00F00000
    IMAGE_SCN_LNK_NRELOC_OVFL        uint32 = 0x01000000
    IMAGE_SCN_MEM_DISCARDABLE        uint32 = 0x02000000
    IMAGE_SCN_MEM_NOT_CACHED         uint32 = 0x04000000
    IMAGE_SCN_MEM_NOT_PAGED          uint32 = 0x08000000
    IMAGE_SCN_MEM_SHARED             uint32 = 0x10000000
    IMAGE_SCN_MEM_EXECUTE            uint32 = 0x20000000
    IMAGE_SCN_MEM_READ               uint32 = 0x40000000
    IMAGE_SCN_MEM_WRITE              uint32 = 0x80000000
)

Subsystem - 子系统类型

const (
    IMAGE_SUBSYSTEM_UNKNOWN              uint16 = 0
    IMAGE_SUBSYSTEM_NATIVE               uint16 = 1
    IMAGE_SUBSYSTEM_WINDOWS_GUI          uint16 = 2
    IMAGE_SUBSYSTEM_WINDOWS_CUI          uint16 = 3
    IMAGE_SUBSYSTEM_OS2_CUI              uint16 = 5
    IMAGE_SUBSYSTEM_POSIX_CUI            uint16 = 7
    IMAGE_SUBSYSTEM_NATIVE_WINDOWS       uint16 = 8
    IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       uint16 = 9
    IMAGE_SUBSYSTEM_EFI_APPLICATION      uint16 = 10
    IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER uint16 = 11
    IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER   uint16 = 12
    IMAGE_SUBSYSTEM_EFI_ROM              uint16 = 13
    IMAGE_SUBSYSTEM_XBOX                 uint16 = 14
    IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION uint16 = 16
)

Magic - 魔术数字

const (
    IMAGE_NT_OPTIONAL_HDR32_MAGIC uint16 = 0x10b  // PE32
    IMAGE_NT_OPTIONAL_HDR64_MAGIC uint16 = 0x20b  // PE32+
    IMAGE_ROM_OPTIONAL_HDR_MAGIC  uint16 = 0x107  // ROM
)

常见节名称

.text          // 代码节
.data          // 已初始化数据节
.bss           // 未初始化数据节
.rdata         // 只读数据节
.rsrc          // 资源节
.reloc         // 重定位节
.idata         // 导入表节
.edata         // 导出表节
.tls           // TLS 节
.pdata         // 异常处理信息
.debug         // 调试信息

完整示例

示例 1:打开和读取 PE 文件

package main

import (
    "debug/pe"
    "fmt"
    "log"
)

func main() {
    // 1. 打开 PE 文件
    f, err := pe.Open("myprogram.exe")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    // 2. 显示文件头信息
    fmt.Printf("机器类型:0x%x\n", f.Machine)
    fmt.Printf("节数量:%d\n", f.NumberOfSections)
    fmt.Printf("时间戳:%d\n", f.TimeDateStamp)
    fmt.Printf("符号表偏移:0x%x\n", f.PointerToSymbolTable)
    fmt.Printf("符号数量:%d\n", f.NumberOfSymbols)
    fmt.Printf("可选头大小:%d\n", f.SizeOfOptionalHeader)
    fmt.Printf("特征:0x%x\n", f.Characteristics)
    
    // 3. 显示可选头信息
    switch oh := f.OptionalHeader.(type) {
    case *pe.OptionalHeader32:
        fmt.Printf("格式:PE32\n")
        fmt.Printf("入口点:0x%x\n", oh.AddressOfEntryPoint)
        fmt.Printf("镜像基址:0x%x\n", oh.ImageBase)
        fmt.Printf("子系统:%d\n", oh.Subsystem)
    case *pe.OptionalHeader64:
        fmt.Printf("格式:PE32+\n")
        fmt.Printf("入口点:0x%x\n", oh.AddressOfEntryPoint)
        fmt.Printf("镜像基址:0x%x\n", oh.ImageBase)
        fmt.Printf("子系统:%d\n", oh.Subsystem)
    }
    
    // 4. 显示节数量
    fmt.Printf("节数量:%d\n", len(f.Sections))
}

示例 2:遍历所有节

package main

import (
    "debug/pe"
    "fmt"
    "log"
    "strings"
)

func main() {
    f, err := pe.Open("myprogram.exe")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    fmt.Println("PE 节信息:")
    fmt.Println("=" + "=" * 79)
    
    for i, section := range f.Sections {
        // 获取节名称(去掉填充的零字节)
        name := strings.TrimRight(string(section.Name[:]), "\x00")
        
        fmt.Printf("%2d. 名称:%s\n", i, name)
        fmt.Printf("    虚拟地址:0x%x\n", section.VirtualAddress)
        fmt.Printf("    虚拟大小:%d 字节\n", section.VirtualSize)
        fmt.Printf("    原始数据大小:%d 字节\n", section.SizeOfRawData)
        fmt.Printf("    原始数据偏移:0x%x\n", section.PointerToRawData)
        fmt.Printf("    重定位数量:%d\n", section.NumberOfRelocations)
        fmt.Printf("    特征:0x%x\n", section.Characteristics)
        
        // 解析特征标志
        fmt.Printf("    标志:")
        if section.Characteristics&0x20000000 != 0 {
            fmt.Printf("可执行 ")
        }
        if section.Characteristics&0x40000000 != 0 {
            fmt.Printf("可读 ")
        }
        if section.Characteristics&0x80000000 != 0 {
            fmt.Printf("可写 ")
        }
        fmt.Println()
        
        fmt.Println()
    }
}

示例 3:读取特定节内容

package main

import (
    "debug/pe"
    "encoding/hex"
    "fmt"
    "log"
    "strings"
)

func main() {
    f, err := pe.Open("myprogram.exe")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    // 1. 读取 .text 节(代码)
    textSection := f.Section(".text")
    if textSection != nil {
        data, err := textSection.Data()
        if err != nil {
            log.Fatal(err)
        }
        
        fmt.Printf(".text 节:\n")
        fmt.Printf("  大小:%d 字节\n", len(data))
        fmt.Printf("  前 64 字节:%x\n", data[:64])
    }
    
    // 2. 读取 .rdata 节(只读数据)
    rdataSection := f.Section(".rdata")
    if rdataSection != nil {
        data, err := rdataSection.Data()
        if err != nil {
            log.Fatal(err)
        }
        
        fmt.Printf("\n.rdata 节:\n")
        fmt.Printf("  大小:%d 字节\n", len(data))
        
        // 尝试提取字符串
        strings := extractStrings(data)
        fmt.Printf("  提取的字符串:%d 个\n", len(strings))
        
        // 显示前 20 个字符串
        count := 0
        for _, str := range strings {
            if count >= 20 {
                break
            }
            if len(str) >= 4 {
                fmt.Printf("    \"%s\"\n", str)
                count++
            }
        }
    }
    
    // 3. 读取 .data 节(已初始化数据)
    dataSection := f.Section(".data")
    if dataSection != nil {
        data, err := dataSection.Data()
        if err != nil {
            log.Fatal(err)
        }
        
        fmt.Printf("\n.data 节:\n")
        fmt.Printf("  大小:%d 字节\n", len(data))
    }
    
    // 4. 读取 .rsrc 节(资源)
    rsrcSection := f.Section(".rsrc")
    if rsrcSection != nil {
        data, err := rsrcSection.Data()
        if err != nil {
            log.Fatal(err)
        }
        
        fmt.Printf("\n.rsrc 节:\n")
        fmt.Printf("  大小:%d 字节\n", len(data))
    }
}

// extractStrings 提取 ASCII 字符串
func extractStrings(data []byte) []string {
    var strings []string
    var current strings.Builder
    
    for _, b := range data {
        if b >= 0x20 && b <= 0x7e {
            // 可打印 ASCII 字符
            current.WriteByte(b)
        } else {
            if current.Len() >= 4 {
                strings = append(strings, current.String())
            }
            current.Reset()
        }
    }
    
    if current.Len() >= 4 {
        strings = append(strings, current.String())
    }
    
    return strings
}

示例 4:读取符号表

package main

import (
    "debug/pe"
    "fmt"
    "log"
)

func main() {
    f, err := pe.Open("myprogram.exe")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    // 1. 读取符号表
    fmt.Println("COFF 符号表:")
    symbols := f.COFFSymbols
    stringTable := f.StringTable
    
    fmt.Printf("符号数量:%d\n", len(symbols))
    fmt.Printf("字符串表大小:%d 字节\n", len(stringTable))
    
    // 显示前 20 个符号
    count := 0
    for _, sym := range symbols {
        if count >= 20 {
            break
        }
        
        // 获取完整名称
        name, err := sym.FullName(stringTable)
        if err != nil {
            name = fmt.Sprintf("<error: %v>", err)
        }
        
        fmt.Printf("  %s\n", name)
        fmt.Printf("    值:0x%x\n", sym.Value)
        fmt.Printf("    节:%d\n", sym.SectionNumber)
        fmt.Printf("    类型:0x%x\n", sym.Type)
        fmt.Printf("    存储类别:0x%x\n", sym.StorageClass)
        fmt.Printf("    辅助符号:%d\n", sym.NumAuxSymbols)
        count++
    }
    
    // 2. 读取 Go 符号表(如果有)
    fmt.Println("\nPE 符号:")
    peSymbols, err := f.Symbols()
    if err != nil {
        log.Printf("读取 PE 符号失败:%v", err)
    } else {
        fmt.Printf("符号数量:%d\n", len(peSymbols))
        
        for i, sym := range peSymbols {
            if i >= 20 {
                break
            }
            fmt.Printf("  %s (0x%x)\n", sym.Name, sym.Value)
        }
    }
}

示例 5:读取导入的库和符号

package main

import (
    "debug/pe"
    "fmt"
    "log"
)

func main() {
    f, err := pe.Open("myprogram.exe")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    // 1. 读取导入的库
    fmt.Println("依赖的 DLL:")
    libs, err := f.ImportedLibraries()
    if err != nil {
        log.Printf("读取库列表失败:%v", err)
    } else {
        for i, lib := range libs {
            fmt.Printf("  %2d. %s\n", i+1, lib)
        }
    }
    
    // 2. 读取导入的符号
    fmt.Println("\n导入的符号:")
    symbols, err := f.ImportedSymbols()
    if err != nil {
        log.Printf("读取导入符号失败:%v", err)
    } else {
        for i, sym := range symbols {
            if i >= 30 {
                break
            }
            fmt.Printf("  %s\n", sym)
        }
    }
    
    // 3. 分析数据目录
    fmt.Println("\n数据目录:")
    switch oh := f.OptionalHeader.(type) {
    case *pe.OptionalHeader32:
        showDataDirectories(oh.DataDirectory[:])
    case *pe.OptionalHeader64:
        showDataDirectories(oh.DataDirectory[:])
    }
}

// showDataDirectories 显示数据目录
func showDataDirectories(dirs []pe.DataDirectory) {
    names := []string{
        "导出表", "导入表", "资源表", "异常表",
        "安全表", "重定位表", "调试信息", "架构特定",
        "全局指针", "TLS 表", "加载配置", "绑定导入",
        "导入地址表", "延迟导入", "COM 描述符",
    }
    
    for i, dir := range dirs {
        if i >= len(names) {
            break
        }
        if dir.Size > 0 {
            fmt.Printf("  %-12s: RVA=0x%x, 大小=%d\n", 
                names[i], dir.VirtualAddress, dir.Size)
        }
    }
}

示例 6:检查 PE 文件特征

package main

import (
    "debug/pe"
    "fmt"
    "log"
    "time"
)

func main() {
    f, err := pe.Open("myprogram.exe")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    fmt.Println("=== PE 文件特征分析 ===\n")
    
    // 1. 机器类型
    fmt.Printf("目标架构:")
    switch f.Machine {
    case pe.IMAGE_FILE_MACHINE_I386:
        fmt.Printf("x86 (32 位)\n")
    case pe.IMAGE_FILE_MACHINE_AMD64:
        fmt.Printf("x86-64 (64 位)\n")
    case pe.IMAGE_FILE_MACHINE_ARM:
        fmt.Printf("ARM\n")
    case pe.IMAGE_FILE_MACHINE_ARMNT:
        fmt.Printf("ARM Thumb-2\n")
    case pe.IMAGE_FILE_MACHINE_ARM64:
        fmt.Printf("ARM 64 位\n")
    case pe.IMAGE_FILE_MACHINE_IA64:
        fmt.Printf("Intel Itanium\n")
    default:
        fmt.Printf("未知 (0x%x)\n", f.Machine)
    }
    
    // 2. 文件类型
    fmt.Printf("文件类型:")
    if f.Characteristics&pe.IMAGE_FILE_DLL != 0 {
        fmt.Printf("动态库 (DLL)\n")
    } else if f.Characteristics&pe.IMAGE_FILE_EXECUTABLE_IMAGE != 0 {
        fmt.Printf("可执行文件 (EXE)\n")
    } else {
        fmt.Printf("目标文件 (OBJ)\n")
    }
    
    // 3. 编译时间
    fmt.Printf("编译时间:%s\n", 
        time.Unix(int64(f.TimeDateStamp), 0).Format("2006-01-02 15:04:05"))
    
    // 4. 特征标志
    fmt.Printf("文件特征:\n")
    flags := []struct {
        mask uint16
        name string
    }{
        {pe.IMAGE_FILE_RELOCS_STRIPPED, "重定位已剥离"},
        {pe.IMAGE_FILE_EXECUTABLE_IMAGE, "可执行镜像"},
        {pe.IMAGE_FILE_LINE_NUMS_STRIPPED, "行号已剥离"},
        {pe.IMAGE_FILE_LOCAL_SYMS_STRIPPED, "本地符号已剥离"},
        {pe.IMAGE_FILE_LARGE_ADDRESS_AWARE, "支持大地址"},
        {pe.IMAGE_FILE_32BIT_MACHINE, "32 位机器"},
        {pe.IMAGE_FILE_DEBUG_TRACED, "调试跟踪"},
        {pe.IMAGE_FILE_SYSTEM, "系统文件"},
        {pe.IMAGE_FILE_DLL, "DLL 文件"},
    }
    
    for _, flag := range flags {
        if f.Characteristics&flag.mask != 0 {
            fmt.Printf("  - %s\n", flag.name)
        }
    }
    
    // 5. 子系统
    fmt.Printf("\n子系统:")
    var subsystem uint16
    switch oh := f.OptionalHeader.(type) {
    case *pe.OptionalHeader32:
        subsystem = oh.Subsystem
    case *pe.OptionalHeader64:
        subsystem = oh.Subsystem
    }
    
    switch subsystem {
    case pe.IMAGE_SUBSYSTEM_WINDOWS_GUI:
        fmt.Printf("Windows GUI 程序\n")
    case pe.IMAGE_SUBSYSTEM_WINDOWS_CUI:
        fmt.Printf("Windows 控制台程序\n")
    case pe.IMAGE_SUBSYSTEM_NATIVE:
        fmt.Printf("原生程序(驱动程序)\n")
    case pe.IMAGE_SUBSYSTEM_EFI_APPLICATION:
        fmt.Printf("EFI 应用程序\n")
    default:
        fmt.Printf("未知 (%d)\n", subsystem)
    }
    
    // 6. 可选头信息
    fmt.Printf("\n可选头信息:\n")
    switch oh := f.OptionalHeader.(type) {
    case *pe.OptionalHeader32:
        fmt.Printf("  格式:PE32 (32 位)\n")
        fmt.Printf("  入口点:0x%x\n", oh.AddressOfEntryPoint)
        fmt.Printf("  镜像基址:0x%x\n", oh.ImageBase)
        fmt.Printf("  栈保留:0x%x\n", oh.SizeOfStackReserve)
        fmt.Printf("  栈提交:0x%x\n", oh.SizeOfStackCommit)
        fmt.Printf("  堆保留:0x%x\n", oh.SizeOfHeapReserve)
        fmt.Printf("  堆提交:0x%x\n", oh.SizeOfHeapCommit)
    case *pe.OptionalHeader64:
        fmt.Printf("  格式:PE32+ (64 位)\n")
        fmt.Printf("  入口点:0x%x\n", oh.AddressOfEntryPoint)
        fmt.Printf("  镜像基址:0x%x\n", oh.ImageBase)
        fmt.Printf("  栈保留:0x%x\n", oh.SizeOfStackReserve)
        fmt.Printf("  栈提交:0x%x\n", oh.SizeOfStackCommit)
        fmt.Printf("  堆保留:0x%x\n", oh.SizeOfHeapReserve)
        fmt.Printf("  堆提交:0x%x\n", oh.SizeOfHeapCommit)
    }
}

示例 7:PE 文件分析工具

package main

import (
    "debug/pe"
    "encoding/hex"
    "fmt"
    "log"
    "os"
    "strings"
    "time"
)

// PEAnalyzer PE 文件分析器
type PEAnalyzer struct {
    file *pe.File
}

// NewPEAnalyzer 创建分析器
func NewPEAnalyzer(filename string) (*PEAnalyzer, error) {
    f, err := pe.Open(filename)
    if err != nil {
        return nil, err
    }
    
    return &PEAnalyzer{file: f}, nil
}

// Close 关闭文件
func (a *PEAnalyzer) Close() error {
    return a.file.Close()
}

// ShowHeader 显示文件头
func (a *PEAnalyzer) ShowHeader() {
    f := a.file
    
    fmt.Println("=== PE 文件头 ===")
    fmt.Printf("机器类型:0x%x\n", f.Machine)
    fmt.Printf("节数量:%d\n", f.NumberOfSections)
    fmt.Printf("编译时间:%s\n", 
        time.Unix(int64(f.TimeDateStamp), 0).Format("2006-01-02 15:04:05"))
    fmt.Printf("符号表偏移:0x%x\n", f.PointerToSymbolTable)
    fmt.Printf("符号数量:%d\n", f.NumberOfSymbols)
    fmt.Printf("可选头大小:%d\n", f.SizeOfOptionalHeader)
    fmt.Printf("特征:0x%x\n", f.Characteristics)
    fmt.Println()
}

// ShowSections 显示节信息
func (a *PEAnalyzer) ShowSections() {
    fmt.Println("=== PE 节 ===")
    
    for i, section := range a.file.Sections {
        name := strings.TrimRight(string(section.Name[:]), "\x00")
        
        fmt.Printf("%2d. %-10s RVA:0x%08x 大小:%6d 字节", 
            i, name, section.VirtualAddress, section.VirtualSize)
        
        // 显示特征
        var flags []string
        if section.Characteristics&0x20000000 != 0 {
            flags = append(flags, "X")
        }
        if section.Characteristics&0x40000000 != 0 {
            flags = append(flags, "R")
        }
        if section.Characteristics&0x80000000 != 0 {
            flags = append(flags, "W")
        }
        if len(flags) > 0 {
            fmt.Printf(" [%s]", strings.Join(flags, ""))
        }
        
        fmt.Println()
    }
    fmt.Println()
}

// ShowSymbols 显示符号
func (a *PEAnalyzer) ShowSymbols() {
    fmt.Println("=== 符号表 ===")
    
    symbols := a.file.COFFSymbols
    stringTable := a.file.StringTable
    
    fmt.Printf("符号数量:%d\n\n", len(symbols))
    
    // 按存储类别分组
    external := make([]pe.COFFSymbol, 0)
    static := make([]pe.COFFSymbol, 0)
    file := make([]pe.COFFSymbol, 0)
    
    for _, sym := range symbols {
        switch sym.StorageClass {
        case 0x02: // IMAGE_SYM_CLASS_EXTERNAL
            external = append(external, sym)
        case 0x03: // IMAGE_SYM_CLASS_STATIC
            static = append(static, sym)
        case 0x67: // IMAGE_SYM_CLASS_FILE
            file = append(file, sym)
        }
    }
    
    fmt.Printf("外部符号:%d\n", len(external))
    fmt.Printf("静态符号:%d\n", len(static))
    fmt.Printf("文件符号:%d\n\n", len(file))
    
    // 显示前 10 个外部符号
    if len(external) > 0 {
        fmt.Println("外部符号示例:")
        for i, sym := range external {
            if i >= 10 {
                break
            }
            name, _ := sym.FullName(stringTable)
            fmt.Printf("  %-40s 0x%x\n", name, sym.Value)
        }
        fmt.Println()
    }
}

// ShowLibraries 显示库依赖
func (a *PEAnalyzer) ShowLibraries() {
    fmt.Println("=== 依赖库 ===")
    
    libs, err := a.file.ImportedLibraries()
    if err != nil {
        fmt.Printf("读取库失败:%v\n", err)
        return
    }
    
    for _, lib := range libs {
        fmt.Printf("  %s\n", lib)
    }
    fmt.Println()
}

// ShowDataDirectories 显示数据目录
func (a *PEAnalyzer) ShowDataDirectories() {
    fmt.Println("=== 数据目录 ===")
    
    var dirs []pe.DataDirectory
    switch oh := a.file.OptionalHeader.(type) {
    case *pe.OptionalHeader32:
        dirs = oh.DataDirectory[:]
    case *pe.OptionalHeader64:
        dirs = oh.DataDirectory[:]
    }
    
    names := []string{
        "导出表", "导入表", "资源表", "异常表",
        "安全表", "重定位表", "调试信息", "架构特定",
        "全局指针", "TLS 表", "加载配置", "绑定导入",
        "导入地址表", "延迟导入", "COM 描述符",
    }
    
    for i, dir := range dirs {
        if i >= len(names) {
            break
        }
        if dir.Size > 0 {
            fmt.Printf("%2d. %-15s RVA:0x%08x 大小:%d\n", 
                i+1, names[i], dir.VirtualAddress, dir.Size)
        }
    }
    fmt.Println()
}

// FindSymbol 查找符号
func (a *PEAnalyzer) FindSymbol(pattern string) error {
    symbols := a.file.COFFSymbols
    stringTable := a.file.StringTable
    
    count := 0
    for _, sym := range symbols {
        name, err := sym.FullName(stringTable)
        if err != nil {
            continue
        }
        
        if strings.Contains(name, pattern) {
            fmt.Printf("找到符号:%s\n", name)
            fmt.Printf("  值:0x%x\n", sym.Value)
            fmt.Printf("  节:%d\n", sym.SectionNumber)
            fmt.Printf("  类型:0x%x\n", sym.Type)
            fmt.Printf("  存储类别:0x%x\n", sym.StorageClass)
            fmt.Println()
            count++
            
            if count >= 20 {
                break
            }
        }
    }
    
    if count == 0 {
        fmt.Printf("未找到匹配的符号\n")
    } else {
        fmt.Printf("找到 %d 个匹配符号\n", count)
    }
    
    return nil
}

// DumpSection 转储节内容
func (a *PEAnalyzer) DumpSection(name string, output string) error {
    section := a.file.Section(name)
    if section == nil {
        return fmt.Errorf("未找到节:%s", name)
    }
    
    data, err := section.Data()
    if err != nil {
        return err
    }
    
    file, err := os.Create(output)
    if err != nil {
        return err
    }
    defer file.Close()
    
    _, err = file.Write(data)
    if err != nil {
        return err
    }
    
    fmt.Printf("已将 %s 节保存到 %s (%d 字节)\n", name, output, len(data))
    return nil
}

func main() {
    if len(os.Args) < 2 {
        log.Fatal("用法:pe-analyzer <pe-file> [command]")
    }
    
    filename := os.Args[1]
    
    analyzer, err := NewPEAnalyzer(filename)
    if err != nil {
        log.Fatal(err)
    }
    defer analyzer.Close()
    
    if len(os.Args) > 2 {
        command := os.Args[2]
        
        switch command {
        case "header":
            analyzer.ShowHeader()
        case "sections":
            analyzer.ShowSections()
        case "symbols":
            analyzer.ShowSymbols()
        case "libs":
            analyzer.ShowLibraries()
        case "dirs":
            analyzer.ShowDataDirectories()
        case "find":
            if len(os.Args) > 3 {
                analyzer.FindSymbol(os.Args[3])
            }
        case "dump":
            if len(os.Args) > 4 {
                err := analyzer.DumpSection(os.Args[3], os.Args[4])
                if err != nil {
                    log.Fatal(err)
                }
            }
        default:
            // 显示所有信息
            analyzer.ShowHeader()
            analyzer.ShowSections()
            analyzer.ShowSymbols()
            analyzer.ShowLibraries()
            analyzer.ShowDataDirectories()
        }
    } else {
        // 默认显示所有信息
        analyzer.ShowHeader()
        analyzer.ShowSections()
        analyzer.ShowSymbols()
        analyzer.ShowLibraries()
        analyzer.ShowDataDirectories()
    }
}

示例 8:读取 DWARF 调试信息

package main

import (
    "debug/pe"
    "debug/dwarf"
    "fmt"
    "io"
    "log"
)

func main() {
    f, err := pe.Open("myprogram.exe")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    // 读取 DWARF 数据
    data, err := f.DWARF()
    if err != nil {
        log.Fatal("无 DWARF 信息:", err)
    }
    
    // 创建读取器
    reader := data.Reader()
    
    // 遍历 DWARF 条目
    fmt.Println("DWARF 调试信息:")
    
    for {
        entry, err := reader.Next()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        
        // 查找函数
        if entry.Tag == dwarf.TagSubprogram {
            name := entry.Val(dwarf.AttrName)
            if name != nil {
                fmt.Printf("函数:%v\n", name)
                
                lowpc := entry.Val(dwarf.AttrLowpc)
                highpc := entry.Val(dwarf.AttrHighpc)
                if lowpc != nil && highpc != nil {
                    fmt.Printf("  地址范围:0x%x - 0x%x\n", lowpc, highpc)
                }
            }
        }
        
        if entry.Children {
            reader.SkipChildren()
        }
    }
}

示例 9:检查 PE 文件是否为有效的 Windows 可执行文件

package main

import (
    "debug/pe"
    "fmt"
    "log"
    "os"
)

// IsValidPE 检查文件是否为有效的 PE 文件
func IsValidPE(filename string) bool {
    f, err := pe.Open(filename)
    if err != nil {
        return false
    }
    defer f.Close()
    
    // 检查是否为可执行文件或 DLL
    if f.Characteristics&pe.IMAGE_FILE_EXECUTABLE_IMAGE == 0 &&
       f.Characteristics&pe.IMAGE_FILE_DLL == 0 {
        return false
    }
    
    return true
}

// GetPEInfo 获取 PE 文件信息
func GetPEInfo(filename string) (map[string]interface{}, error) {
    f, err := pe.Open(filename)
    if err != nil {
        return nil, err
    }
    defer f.Close()
    
    info := make(map[string]interface{})
    
    // 基本信息
    info["machine"] = f.Machine
    info["sections"] = f.NumberOfSections
    info["timestamp"] = f.TimeDateStamp
    info["is_dll"] = f.Characteristics&pe.IMAGE_FILE_DLL != 0
    info["is_exe"] = f.Characteristics&pe.IMAGE_FILE_EXECUTABLE_IMAGE != 0
    
    // 可选头信息
    switch oh := f.OptionalHeader.(type) {
    case *pe.OptionalHeader32:
        info["format"] = "PE32"
        info["entry_point"] = oh.AddressOfEntryPoint
        info["image_base"] = oh.ImageBase
        info["subsystem"] = oh.Subsystem
    case *pe.OptionalHeader64:
        info["format"] = "PE32+"
        info["entry_point"] = oh.AddressOfEntryPoint
        info["image_base"] = oh.ImageBase
        info["subsystem"] = oh.Subsystem
    }
    
    // 导入库
    libs, err := f.ImportedLibraries()
    if err == nil {
        info["imported_libraries"] = libs
    }
    
    return info, nil
}

func main() {
    if len(os.Args) < 2 {
        log.Fatal("用法:pe-check <pe-file>")
    }
    
    filename := os.Args[1]
    
    if !IsValidPE(filename) {
        fmt.Printf("%s 不是有效的 PE 文件\n", filename)
        return
    }
    
    fmt.Printf("%s 是有效的 PE 文件\n", filename)
    
    info, err := GetPEInfo(filename)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("机器类型:0x%x\n", info["machine"])
    fmt.Printf("格式:%v\n", info["format"])
    fmt.Printf("节数量:%v\n", info["sections"])
    fmt.Printf("是否为 DLL: %v\n", info["is_dll"])
    fmt.Printf("是否为 EXE: %v\n", info["is_exe"])
    fmt.Printf("入口点:0x%x\n", info["entry_point"])
    fmt.Printf("镜像基址:0x%x\n", info["image_base"])
    fmt.Printf("子系统:%v\n", info["subsystem"])
    
    if libs, ok := info["imported_libraries"].([]string); ok {
        fmt.Printf("导入库数量:%d\n", len(libs))
    }
}

安全最佳实践

✅ 推荐做法

  1. 始终检查错误

    f, err := pe.Open("file")
    if err != nil {
        return err
    }
    defer f.Close()
    
  2. 检查 nil 指针

    section := f.Section(".text")
    if section != nil {
        data, _ := section.Data()
    }
    
  3. 验证数据大小

    data, err := section.Data()
    if err != nil {
        return err
    }
    
    if len(data) < expectedSize {
        return fmt.Errorf("数据太小")
    }
    
  4. 检查文件格式

    if f.Characteristics&pe.IMAGE_FILE_EXECUTABLE_IMAGE == 0 {
        return fmt.Errorf("不是可执行文件")
    }
    

❌ 不安全做法

  1. 不要忽略错误

    // ❌ 错误
    f, _ := pe.Open("file")
    
    // ✅ 正确
    f, err := pe.Open("file")
    if err != nil {
        // 处理错误
    }
    
  2. 不要忘记关闭文件

    // ❌ 错误
    f, _ := pe.Open("file")
    
    // ✅ 正确
    f, _ := pe.Open("file")
    defer f.Close()
    
  3. 不要假设节一定存在

    // ❌ 错误
    data, _ := f.Section(".text").Data()
    
    // ✅ 正确
    section := f.Section(".text")
    if section == nil {
        return error
    }
    data, err := section.Data()
    

总结

核心类型

File         // PE 文件
FileHeader   // COFF 文件头
OptionalHeader32 // 32 位可选头
OptionalHeader64 // 64 位可选头
Section      // 节
SectionHeader // 节头
Symbol       // 符号
COFFSymbol   // COFF 符号
DataDirectory // 数据目录

使用场景

场景推荐方法说明
打开文件pe.Open()读取 PE 文件
读取节File.Section()获取特定节
读取符号File.Symbols()获取符号表
获取库依赖File.ImportedLibraries()获取依赖库
读取 DWARFFile.DWARF()获取调试信息

Machine 类型

类型常量说明
x86IMAGE_FILE_MACHINE_I38632 位 Intel
x86-64IMAGE_FILE_MACHINE_AMD6464 位 Intel
ARMIMAGE_FILE_MACHINE_ARMARM
ARM Thumb-2IMAGE_FILE_MACHINE_ARMNTARM Thumb-2
ARM64IMAGE_FILE_MACHINE_ARM64ARM 64 位
ItaniumIMAGE_FILE_MACHINE_IA64Intel Itanium

文件类型

类型标志说明
可执行文件IMAGE_FILE_EXECUTABLE_IMAGE.exe 文件
动态库IMAGE_FILE_DLL.dll 文件
目标文件无特殊标志.obj 文件

子系统类型

类型常量说明
GUI 程序IMAGE_SUBSYSTEM_WINDOWS_GUIWindows 图形界面
控制台程序IMAGE_SUBSYSTEM_WINDOWS_CUIWindows 命令行
原生程序IMAGE_SUBSYSTEM_NATIVE驱动程序
EFI 应用IMAGE_SUBSYSTEM_EFI_APPLICATIONEFI 应用程序

常见节

节名用途
.text代码节
.data已初始化数据节
.rdata只读数据节
.bss未初始化数据节
.rsrc资源节
.reloc重定位节
.idata导入表节
.edata导出表节

PE32 vs PE32+

特性PE32PE32+
魔术数字0x10b0x20b
地址大小32 位64 位
ImageBase32 位64 位
栈/堆大小32 位64 位
BaseOfData存在不存在

参考资料


最后更新:2026-04-03
Go 版本:Go 1.23+