go hack(十八)go 对PE文件解析

go网络安全代码地址

  1. 使用debug/pe标准库进行解析
  2. 使用Reader对象对PE文件内容进行解析

PE文件结构

  1. DOSheader 包含签名(0x5a4d)peheader(0x3c指向0x50 0x45 0x00 0x00)
  2. dos stub
  3. coff file header
  4. optional header
  5. section table
// pe头
type FileHeader struct {
    Machine              uint16
    NumberOfSections     uint16  // 分区数
    TimeDateStamp        uint32
    PointerToSymbolTable uint32
    NumberOfSymbols      uint32
    SizeOfOptionalHeader uint16
    Characteristics      uint16
}
  1. 如果需要增加新分区,插入后门代码,需要修改这里的分区数属性
  2. 增加新的Sections
  3. 或者在imagebase中增加shellcode
package main

import (
    "debug/pe"
    "encoding/binary"
    "fmt"
    "io"
    "log"
    "os"
)

func main() {
    f, err := os.Open("G:\\Windows\\WinSxS\\amd64_microsoft-windows-calc_31bf3856ad364e35_10.0.18362.1_none_7c1b713697f466dd\\calc.exe") // Modify for binary or change to accept args
    check(err)
    pefile, err := pe.NewFile(f) // 创建pe文件对象
    check(err)
    defer f.Close()
    defer pefile.Close()

    dosHeader := make([]byte, 96) // 读取前96个字节
    sizeOffset := make([]byte, 4)

    // Dec to Ascii (searching for MZ)
    _, err = f.Read(dosHeader)
    check(err)
    fmt.Println("[-----DOS Header / Stub----- header和stub解析]")
    fmt.Printf("[+] Magic Value: %s%s\n", string(dosHeader[0]), string(dosHeader[1]))

    // Validate PE+0+0 (Valid PE format)
    pe_sig_offset := int64(binary.LittleEndian.Uint32(dosHeader[0x3c:])) // 从0x3c后开始读取
    f.ReadAt(sizeOffset, pe_sig_offset)                                  // sizeoffset为buffer,pe_sig_offset是读取的位置  这里不加:可以吗?? 读取0x50 0x45 0x00 0x00

    fmt.Println("[-----Signature Header-----]")
    fmt.Printf("[+] LFANEW Value: %s\n", string(sizeOffset))

    // Create the reader and read COFF Header
    sr := io.NewSectionReader(f, 0, 1<<63-1)       // 读取到2^64次方-1
    _, err = sr.Seek(pe_sig_offset+4, os.SEEK_SET) // 重置指针
    check(err)
    binary.Read(sr, binary.LittleEndian, &pefile.FileHeader) // 二进制读取

    // Get size of OptionalHeader
    // 可选头解析  向加载程序提供重要数据,加载程序将可执行文件加载到虚拟内存
    var sizeofOptionalHeader32 = uint16(binary.Size(pe.OptionalHeader32{}))
    var sizeofOptionalHeader64 = uint16(binary.Size(pe.OptionalHeader64{}))
    var oh32 pe.OptionalHeader32
    var oh64 pe.OptionalHeader64

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

    // Read OptionalHeader
    switch pefile.FileHeader.SizeOfOptionalHeader {
    case sizeofOptionalHeader32:
        binary.Read(sr, binary.LittleEndian, &oh32)
    case sizeofOptionalHeader64:
        binary.Read(sr, binary.LittleEndian, &oh64)
    }
    // Print File Header
    fmt.Println("[-----COFF File Header-----]")
    fmt.Printf("[+] Machine Architecture: %#x\n", pefile.FileHeader.Machine)
    fmt.Printf("[+] Number of Sections: %#x\n", pefile.FileHeader.NumberOfSections)
    fmt.Printf("[+] Size of Optional Header: %#x\n", pefile.FileHeader.SizeOfOptionalHeader)
    // Print section names
    fmt.Println("[-----Section Offsets-----]")
    fmt.Printf("[+] Number of Sections Field Offset: %#x\n", pe_sig_offset+6)
    // this is the end of the Signature header (0x7c) + coff (20bytes) + oh32 (224bytes)
    fmt.Printf("[+] Section Table Offset: %#x\n", pe_sig_offset+0xF8)
    // Print Optional Header
    fmt.Println("[-----Optional Header-----]")

    // 如果对pe文件加入后门,需要对下面两项有所了解
    fmt.Printf("[+] Entry Point: %#x\n", oh32.AddressOfEntryPoint) // 相对于imageBase的可执行代码位置
    fmt.Printf("[+] ImageBase: %#x\n", oh32.ImageBase)             // 将图像加载到内存时第一个字节位置

    fmt.Printf("[+] Size of Image: %#x\n", oh32.SizeOfImage) // 图像的实际大小
    fmt.Printf("[+] Sections Alignment: %#x\n", oh32.SectionAlignment)
    fmt.Printf("[+] File Alignment: %#x\n", oh32.FileAlignment)
    fmt.Printf("[+] Characteristics: %#x\n", pefile.FileHeader.Characteristics)
    fmt.Printf("[+] Size of Headers: %#x\n", oh32.SizeOfHeaders)
    fmt.Printf("[+] Checksum: %#x\n", oh32.CheckSum)
    fmt.Printf("[+] Machine: %#x\n", pefile.FileHeader.Machine)
    fmt.Printf("[+] Subsystem: %#x\n", oh32.Subsystem)
    fmt.Printf("[+] DLLCharacteristics: %#x\n", oh32.DllCharacteristics)

    // Print Data Directory
    fmt.Println("[-----Data Directory----- 数据目录解析,可选头的最后128字节]")
    var winnt_datadirs = []string{
        "IMAGE_DIRECTORY_ENTRY_EXPORT",
        "IMAGE_DIRECTORY_ENTRY_IMPORT",
        "IMAGE_DIRECTORY_ENTRY_RESOURCE",
        "IMAGE_DIRECTORY_ENTRY_EXCEPTION",
        "IMAGE_DIRECTORY_ENTRY_SECURITY",
        "IMAGE_DIRECTORY_ENTRY_BASERELOC",
        "IMAGE_DIRECTORY_ENTRY_DEBUG",
        "IMAGE_DIRECTORY_ENTRY_COPYRIGHT",
        "IMAGE_DIRECTORY_ENTRY_GLOBALPTR",
        "IMAGE_DIRECTORY_ENTRY_TLS",
        "IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG",
        "IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT",
        "IMAGE_DIRECTORY_ENTRY_IAT",
        "IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT",
        "IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR",
        "IMAGE_NUMBEROF_DIRECTORY_ENTRIES",
    }
    for idx, directory := range oh32.DataDirectory {
        fmt.Printf("[!] Data Directory: %s\n", winnt_datadirs[idx])
        fmt.Printf("[+] Image Virtual Address: %#x\n", directory.VirtualAddress)
        fmt.Printf("[+] Image Size: %#x\n", directory.Size)
    }

    fmt.Println("[-----Section Table----- 解析分区表]")
    // 包含了相关分区的详细信息,与coff file header中numberofsections 匹配
    for _, section := range pefile.Sections {
        fmt.Println("[+] --------------------")
        fmt.Printf("[+] Section Name: %s\n", section.Name)
        fmt.Printf("[+] Section Characteristics: %#x\n", section.Characteristics)
        fmt.Printf("[+] Section Virtual Size: %#x\n", section.VirtualSize)
        fmt.Printf("[+] Section Virtual Offset: %#x\n", section.VirtualAddress)
        fmt.Printf("[+] Section Raw Size: %#x\n", section.Size)
        fmt.Printf("[+] Section Raw Offset to Data: %#x\n", section.Offset)
        fmt.Printf("[+] Section Append Offset (Next Section): %#x\n", section.Offset+section.Size)
    }

    // s := pefile.Section(".text")
    // fmt.Printf("%v", *s)

    // "Section Table Offset" + (40bytes * number of sections)

}

func check(e error) {
    if e != nil {
        log.Fatal(e)
    }
}

你可能感兴趣的:(go hack(十八)go 对PE文件解析)