go 网络安全代码地址
- 应用 debug/pe 规范库进行解析
- 应用 Reader 对象对 PE 文件内容进行解析
PE 文件构造
- DOSheader 蕴含签名(0x5a4d)peheader(0x3c 指向 0x50 0x45 0x00 0x00)
- dos stub
- coff file header
- optional header
- section table
// pe 头
type FileHeader struct {
Machine uint16
NumberOfSections uint16 // 分区数
TimeDateStamp uint32
PointerToSymbolTable uint32
NumberOfSymbols uint32
SizeOfOptionalHeader uint16
Characteristics uint16
}
- 如果须要减少新分区,插入后门代码,须要批改这里的分区数属性
- 减少新的 Sections
- 或者在 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)
}
}