Files

171 lines
5.2 KiB
Go

package pe
import (
"bytes"
"encoding/binary"
"errors"
"os"
)
func (peFile *File) Bytes() ([]byte, error) {
var bytesWritten uint64
peBuf := bytes.NewBuffer(nil)
// write DOS header and stub
binary.Write(peBuf, binary.LittleEndian, peFile.DosHeader)
bytesWritten += uint64(binary.Size(peFile.DosHeader))
binary.Write(peBuf, binary.LittleEndian, peFile.DosStub)
bytesWritten += uint64(binary.Size(peFile.DosStub))
// write Rich header
if peFile.RichHeader != nil {
binary.Write(peBuf, binary.LittleEndian, peFile.RichHeader)
bytesWritten += uint64(len(peFile.RichHeader))
}
// apply padding before PE header if necessary
if uint32(bytesWritten) != peFile.DosHeader.AddressOfNewExeHeader {
padding := make([]byte, peFile.DosHeader.AddressOfNewExeHeader - uint32(bytesWritten))
binary.Write(peBuf, binary.LittleEndian, padding)
bytesWritten += uint64(len(padding))
}
// write PE header
peMagic := []byte{'P', 'E', 0x00, 0x00}
binary.Write(peBuf, binary.LittleEndian, peMagic)
binary.Write(peBuf, binary.LittleEndian, peFile.FileHeader)
bytesWritten += uint64(binary.Size(peFile.FileHeader) + len(peMagic))
var (
is32bit bool
oldCertTableOffset, oldCertTableSize uint32
)
switch peFile.FileHeader.Machine {
case IMAGE_FILE_MACHINE_I386:
is32bit = true
optionalHeader := peFile.OptionalHeader.(*OptionalHeader32)
binary.Write(peBuf, binary.LittleEndian, peFile.OptionalHeader.(*OptionalHeader32))
bytesWritten += uint64(binary.Size(optionalHeader))
oldCertTableOffset = optionalHeader.DataDirectory[CERTIFICATE_TABLE].VirtualAddress
oldCertTableSize = optionalHeader.DataDirectory[CERTIFICATE_TABLE].Size
case IMAGE_FILE_MACHINE_AMD64:
is32bit = false
optionalHeader := peFile.OptionalHeader.(*OptionalHeader64)
binary.Write(peBuf, binary.LittleEndian, optionalHeader)
bytesWritten += uint64(binary.Size(optionalHeader))
oldCertTableOffset = optionalHeader.DataDirectory[CERTIFICATE_TABLE].VirtualAddress
oldCertTableSize = optionalHeader.DataDirectory[CERTIFICATE_TABLE].Size
default:
return nil, errors.New("architecture not supported")
}
// write section headers
sectionHeaders := make([]SectionHeader32, len(peFile.Sections))
for idx, section := range peFile.Sections {
// write section header
sectionHeader := SectionHeader32 {
Name: section.OriginalName,
VirtualSize: section.VirtualSize,
VirtualAddress: section.VirtualAddress,
SizeOfRawData: section.Size,
PointerToRawData: section.Offset,
PointerToRelocations: section.PointerToRelocations,
PointerToLineNumbers: section.PointerToLineNumbers,
NumberOfRelocations: section.NumberOfRelocations,
NumberOfLineNumbers: section.NumberOfLineNumbers,
Characteristics: section.Characteristics,
}
sectionHeaders[idx] = sectionHeader
binary.Write(peBuf, binary.LittleEndian, sectionHeader)
bytesWritten += uint64(binary.Size(sectionHeader))
}
// write sections' data
for idx, sectionHeader := range sectionHeaders {
sectionData, err := peFile.Sections[idx].Data()
if err != nil {
return nil, err
}
// pad section if there is a gap between PointerToRawData end of last section
if sectionHeader.PointerToRawData != uint32(bytesWritten) {
paddingSize := sectionHeader.PointerToRawData - uint32(bytesWritten)
padding := make([]byte, paddingSize, paddingSize)
sectionData = append(padding, sectionData...)
}
binary.Write(peBuf, binary.LittleEndian, sectionData)
bytesWritten += uint64(len(sectionData))
}
// write symbols
binary.Write(peBuf, binary.LittleEndian, peFile.COFFSymbols)
bytesWritten += uint64(binary.Size(peFile.COFFSymbols))
// write the string table
binary.Write(peBuf, binary.LittleEndian, peFile.StringTable)
bytesWritten += uint64(binary.Size(peFile.StringTable))
var newCertTableOffset, newCertTableSize uint32
// write the certificate table
if peFile.CertificateTable != nil {
newCertTableOffset = uint32(bytesWritten)
newCertTableSize = uint32(len(peFile.CertificateTable))
} else {
newCertTableOffset = 0
newCertTableSize = 0
}
binary.Write(peBuf, binary.LittleEndian, peFile.CertificateTable)
bytesWritten += uint64(len(peFile.CertificateTable))
peData := peBuf.Bytes()
// write the offset and size of the new Certificate Table if it changed
if newCertTableOffset != oldCertTableOffset || newCertTableSize != oldCertTableSize {
certTableInfo := &DataDirectory{
VirtualAddress: newCertTableOffset,
Size: newCertTableSize,
}
var certTableInfoBuf bytes.Buffer
binary.Write(&certTableInfoBuf, binary.LittleEndian, certTableInfo)
var certTableLoc int64
if is32bit {
certTableLoc = int64(peFile.DosHeader.AddressOfNewExeHeader) + 24 + 128
} else {
certTableLoc = int64(peFile.DosHeader.AddressOfNewExeHeader) + 24 + 144
}
peData = append(peData[:certTableLoc], append(certTableInfoBuf.Bytes(), peData[int(certTableLoc) + binary.Size(certTableInfo):]...)...)
}
return peData, nil
}
func (peFile *File) WriteFile(destFile string) error {
f, err := os.Create(destFile)
if err != nil {
return err
}
defer f.Close()
peData, err := peFile.Bytes()
if err != nil {
return err
}
_, err = f.Write(peData)
if err != nil {
return err
}
return nil
}