Update dependencies
This commit is contained in:
262
vendor/github.com/dblohm7/wingoes/pe/oh.go
generated
vendored
Normal file
262
vendor/github.com/dblohm7/wingoes/pe/oh.go
generated
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package pe
|
||||
|
||||
import (
|
||||
dpe "debug/pe"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// OptionalHeader provides the fields of a PE/COFF optional header. Since the
|
||||
// underlying format differs depending on whether the PE binary is 32-bit or
|
||||
// 64-bit, this type provides a unified interface.
|
||||
type OptionalHeader interface {
|
||||
GetMagic() uint16
|
||||
GetLinkerVersion() (major, minor uint8)
|
||||
GetSizeOfCode() uint32
|
||||
GetSizeOfInitializedData() uint32
|
||||
GetSizeOfUninitializedData() uint32
|
||||
GetAddressOfEntryPoint() uint32
|
||||
GetBaseOfCode() uint32
|
||||
GetImageBase() uint64
|
||||
GetSectionAlignment() uint32
|
||||
GetFileAlignment() uint32
|
||||
GetOperatingSystemVersion() (major, minor uint16)
|
||||
GetImageVersion() (major, minor uint16)
|
||||
GetSubsystemVersion() (major, minor uint16)
|
||||
GetWin32Version() uint32
|
||||
GetSizeOfImage() uint32
|
||||
GetSizeOfHeaders() uint32
|
||||
GetCheckSum() uint32
|
||||
GetSubsystem() uint16
|
||||
GetDllCharacteristics() uint16
|
||||
GetSizeOfStackReserve() uint64
|
||||
GetSizeOfStackCommit() uint64
|
||||
GetSizeOfHeapReserve() uint64
|
||||
GetSizeOfHeapCommit() uint64
|
||||
GetLoaderFlags() uint32
|
||||
GetDataDirectory() []DataDirectoryEntry
|
||||
|
||||
SizeOf() uint16 // Size of the underlying struct, in bytes
|
||||
}
|
||||
|
||||
type optionalHeader32 dpe.OptionalHeader32
|
||||
|
||||
func (oh *optionalHeader32) GetMagic() uint16 {
|
||||
return oh.Magic
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetLinkerVersion() (major, minor uint8) {
|
||||
return oh.MajorLinkerVersion, oh.MinorLinkerVersion
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSizeOfCode() uint32 {
|
||||
return oh.SizeOfCode
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSizeOfInitializedData() uint32 {
|
||||
return oh.SizeOfInitializedData
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSizeOfUninitializedData() uint32 {
|
||||
return oh.SizeOfUninitializedData
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetAddressOfEntryPoint() uint32 {
|
||||
return oh.AddressOfEntryPoint
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetBaseOfCode() uint32 {
|
||||
return oh.BaseOfCode
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetImageBase() uint64 {
|
||||
return uint64(oh.ImageBase)
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSectionAlignment() uint32 {
|
||||
return oh.SectionAlignment
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetFileAlignment() uint32 {
|
||||
return oh.FileAlignment
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetOperatingSystemVersion() (major, minor uint16) {
|
||||
return oh.MajorOperatingSystemVersion, oh.MinorOperatingSystemVersion
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetImageVersion() (major, minor uint16) {
|
||||
return oh.MajorImageVersion, oh.MinorImageVersion
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSubsystemVersion() (major, minor uint16) {
|
||||
return oh.MajorSubsystemVersion, oh.MinorSubsystemVersion
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetWin32Version() uint32 {
|
||||
return oh.Win32VersionValue
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSizeOfImage() uint32 {
|
||||
return oh.SizeOfImage
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSizeOfHeaders() uint32 {
|
||||
return oh.SizeOfHeaders
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetCheckSum() uint32 {
|
||||
return oh.CheckSum
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSubsystem() uint16 {
|
||||
return oh.Subsystem
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetDllCharacteristics() uint16 {
|
||||
return oh.DllCharacteristics
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSizeOfStackReserve() uint64 {
|
||||
return uint64(oh.SizeOfStackReserve)
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSizeOfStackCommit() uint64 {
|
||||
return uint64(oh.SizeOfStackCommit)
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSizeOfHeapReserve() uint64 {
|
||||
return uint64(oh.SizeOfHeapReserve)
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetSizeOfHeapCommit() uint64 {
|
||||
return uint64(oh.SizeOfHeapCommit)
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetLoaderFlags() uint32 {
|
||||
return oh.LoaderFlags
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) GetDataDirectory() []DataDirectoryEntry {
|
||||
cnt := oh.NumberOfRvaAndSizes
|
||||
if maxCnt := uint32(len(oh.DataDirectory)); cnt > maxCnt {
|
||||
cnt = maxCnt
|
||||
}
|
||||
return oh.DataDirectory[:cnt]
|
||||
}
|
||||
|
||||
func (oh *optionalHeader32) SizeOf() uint16 {
|
||||
return uint16(unsafe.Sizeof(*oh))
|
||||
}
|
||||
|
||||
type optionalHeader64 dpe.OptionalHeader64
|
||||
|
||||
func (oh *optionalHeader64) GetMagic() uint16 {
|
||||
return oh.Magic
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetLinkerVersion() (major, minor uint8) {
|
||||
return oh.MajorLinkerVersion, oh.MinorLinkerVersion
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSizeOfCode() uint32 {
|
||||
return oh.SizeOfCode
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSizeOfInitializedData() uint32 {
|
||||
return oh.SizeOfInitializedData
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSizeOfUninitializedData() uint32 {
|
||||
return oh.SizeOfUninitializedData
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetAddressOfEntryPoint() uint32 {
|
||||
return oh.AddressOfEntryPoint
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetBaseOfCode() uint32 {
|
||||
return oh.BaseOfCode
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetImageBase() uint64 {
|
||||
return oh.ImageBase
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSectionAlignment() uint32 {
|
||||
return oh.SectionAlignment
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetFileAlignment() uint32 {
|
||||
return oh.FileAlignment
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetOperatingSystemVersion() (major, minor uint16) {
|
||||
return oh.MajorOperatingSystemVersion, oh.MinorOperatingSystemVersion
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetImageVersion() (major, minor uint16) {
|
||||
return oh.MajorImageVersion, oh.MinorImageVersion
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSubsystemVersion() (major, minor uint16) {
|
||||
return oh.MajorSubsystemVersion, oh.MinorSubsystemVersion
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetWin32Version() uint32 {
|
||||
return oh.Win32VersionValue
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSizeOfImage() uint32 {
|
||||
return oh.SizeOfImage
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSizeOfHeaders() uint32 {
|
||||
return oh.SizeOfHeaders
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetCheckSum() uint32 {
|
||||
return oh.CheckSum
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSubsystem() uint16 {
|
||||
return oh.Subsystem
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetDllCharacteristics() uint16 {
|
||||
return oh.DllCharacteristics
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSizeOfStackReserve() uint64 {
|
||||
return oh.SizeOfStackReserve
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSizeOfStackCommit() uint64 {
|
||||
return oh.SizeOfStackCommit
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSizeOfHeapReserve() uint64 {
|
||||
return oh.SizeOfHeapReserve
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetSizeOfHeapCommit() uint64 {
|
||||
return oh.SizeOfHeapCommit
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetLoaderFlags() uint32 {
|
||||
return oh.LoaderFlags
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) GetDataDirectory() []DataDirectoryEntry {
|
||||
cnt := oh.NumberOfRvaAndSizes
|
||||
if maxCnt := uint32(len(oh.DataDirectory)); cnt > maxCnt {
|
||||
cnt = maxCnt
|
||||
}
|
||||
return oh.DataDirectory[:cnt]
|
||||
}
|
||||
|
||||
func (oh *optionalHeader64) SizeOf() uint16 {
|
||||
return uint16(unsafe.Sizeof(*oh))
|
||||
}
|
||||
746
vendor/github.com/dblohm7/wingoes/pe/pe.go
generated
vendored
Normal file
746
vendor/github.com/dblohm7/wingoes/pe/pe.go
generated
vendored
Normal file
@@ -0,0 +1,746 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package pe provides facilities for extracting information from PE binaries.
|
||||
// It only supports the same CPU architectures as the rest of wingoes.
|
||||
package pe
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
dpe "debug/pe"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/bits"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/dblohm7/wingoes"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// The following constants are from the PE spec
|
||||
const (
|
||||
maxNumSections = 96
|
||||
mzSignature = uint16(0x5A4D) // little-endian
|
||||
offsetIMAGE_DOS_HEADERe_lfanew = 0x3C
|
||||
peSignature = uint32(0x00004550) // little-endian
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrBadLength is returned when the actual length of a data field in the
|
||||
// binary is shorter than the expected length of that field.
|
||||
ErrBadLength = errors.New("effective length did not match expected length")
|
||||
// ErrBadCodeView is returned by (*PEHeaders).ExtractCodeViewInfo if the data
|
||||
// at the requested address does not appear to contain valid CodeView information.
|
||||
ErrBadCodeView = errors.New("invalid CodeView debug info")
|
||||
// ErrIndexOutOfRange is returned by (*PEHeaders).DataDirectoryEntry if the
|
||||
// specified index is greater than the maximum allowable index.
|
||||
ErrIndexOutOfRange = errors.New("index out of range")
|
||||
// ErrInvalidBinary is returned whenever the headers do not parse as expected,
|
||||
// or reference locations outside the bounds of the PE file or module.
|
||||
// The headers might be corrupt, malicious, or have been tampered with.
|
||||
ErrInvalidBinary = errors.New("invalid PE binary")
|
||||
// ErrBadCodeView is returned by (*PEHeaders).ExtractCodeViewInfo if the data
|
||||
// at the requested address contains a non-CodeView debug info format.
|
||||
ErrNotCodeView = errors.New("debug info is not CodeView")
|
||||
// ErrIndexOutOfRange is returned by (*PEHeaders).DataDirectoryEntry if the
|
||||
// corresponding entry is not populated in the PE image.
|
||||
ErrNotPresent = errors.New("not present in this PE image")
|
||||
// ErrResolvingFileRVA is returned when the result of arithmetic on a relative
|
||||
// virtual address did not resolve to a valid RVA.
|
||||
ErrResolvingFileRVA = errors.New("could not resolve file RVA")
|
||||
// ErrUnavailableInModule is returned when requesting data from the binary
|
||||
// that is not mapped into memory when loaded. The information must be
|
||||
// loaded from a file-based PEHeaders.
|
||||
ErrUnavailableInModule = errors.New("this information is unavailable from loaded modules; the PE file itself must be examined")
|
||||
// ErrUnsupportedMachine is returned if the binary's CPU architecture is
|
||||
// unsupported. This package currently implements support for x86, amd64,
|
||||
// and arm64.
|
||||
ErrUnsupportedMachine = errors.New("unsupported machine")
|
||||
)
|
||||
|
||||
// FileHeader is the PE/COFF IMAGE_FILE_HEADER structure.
|
||||
type FileHeader dpe.FileHeader
|
||||
|
||||
// SectionHeader is the PE/COFF IMAGE_SECTION_HEADER structure.
|
||||
type SectionHeader dpe.SectionHeader32
|
||||
|
||||
// NameString returns the name of s as a Go string.
|
||||
func (s *SectionHeader) NameString() string {
|
||||
// s.Name is UTF-8. When the string's length is < len(s.Name), the remaining
|
||||
// bytes are padded with zeros.
|
||||
for i, c := range s.Name {
|
||||
if c == 0 {
|
||||
return string(s.Name[:i])
|
||||
}
|
||||
}
|
||||
|
||||
return string(s.Name[:])
|
||||
}
|
||||
|
||||
type peReader interface {
|
||||
io.Closer
|
||||
io.ReaderAt
|
||||
io.ReadSeeker
|
||||
Base() uintptr
|
||||
Limit() uintptr
|
||||
}
|
||||
|
||||
// PEHeaders represents the partially-parsed headers from a PE binary.
|
||||
type PEHeaders struct {
|
||||
r peReader
|
||||
fileHeader *FileHeader
|
||||
optionalHeader OptionalHeader
|
||||
sections []SectionHeader
|
||||
}
|
||||
|
||||
// FileHeader returns the FileHeader that was parsed from peh.
|
||||
func (peh *PEHeaders) FileHeader() *FileHeader {
|
||||
return peh.fileHeader
|
||||
}
|
||||
|
||||
// FileHeader returns the OptionalHeader that was parsed from peh.
|
||||
func (peh *PEHeaders) OptionalHeader() OptionalHeader {
|
||||
return peh.optionalHeader
|
||||
}
|
||||
|
||||
// Sections returns a slice containing all section headers parsed from peh.
|
||||
func (peh *PEHeaders) Sections() []SectionHeader {
|
||||
return peh.sections
|
||||
}
|
||||
|
||||
// DataDirectoryEntry is a PE/COFF IMAGE_DATA_DIRECTORY structure.
|
||||
type DataDirectoryEntry = dpe.DataDirectory
|
||||
|
||||
func resolveOptionalHeader(machine uint16, r peReader, offset uint32) (OptionalHeader, error) {
|
||||
switch machine {
|
||||
case dpe.IMAGE_FILE_MACHINE_I386:
|
||||
return readStruct[optionalHeader32](r, offset)
|
||||
case dpe.IMAGE_FILE_MACHINE_AMD64, dpe.IMAGE_FILE_MACHINE_ARM64:
|
||||
return readStruct[optionalHeader64](r, offset)
|
||||
default:
|
||||
return nil, ErrUnsupportedMachine
|
||||
}
|
||||
}
|
||||
|
||||
func checkMagic(oh OptionalHeader, machine uint16) bool {
|
||||
var expectedMagic uint16
|
||||
switch machine {
|
||||
case dpe.IMAGE_FILE_MACHINE_I386:
|
||||
expectedMagic = 0x010B
|
||||
case dpe.IMAGE_FILE_MACHINE_AMD64, dpe.IMAGE_FILE_MACHINE_ARM64:
|
||||
expectedMagic = 0x020B
|
||||
default:
|
||||
panic("unsupported machine")
|
||||
}
|
||||
|
||||
return oh.GetMagic() == expectedMagic
|
||||
}
|
||||
|
||||
type peBounds struct {
|
||||
base uintptr
|
||||
limit uintptr
|
||||
}
|
||||
|
||||
type peFile struct {
|
||||
*os.File
|
||||
peBounds
|
||||
}
|
||||
|
||||
func (pef *peFile) Base() uintptr {
|
||||
return pef.base
|
||||
}
|
||||
|
||||
func (pef *peFile) Limit() uintptr {
|
||||
if pef.limit == 0 {
|
||||
if fi, err := pef.Stat(); err == nil {
|
||||
pef.limit = uintptr(fi.Size())
|
||||
}
|
||||
}
|
||||
return pef.limit
|
||||
}
|
||||
|
||||
type peModule struct {
|
||||
*bytes.Reader
|
||||
peBounds
|
||||
modLock uintptr
|
||||
}
|
||||
|
||||
func (pei *peModule) Base() uintptr {
|
||||
return pei.base
|
||||
}
|
||||
|
||||
func (pei *peModule) Limit() uintptr {
|
||||
return pei.limit
|
||||
}
|
||||
|
||||
// NewPEFromFileName opens a PE binary located at filename and parses its PE
|
||||
// headers. Upon success it returns a non-nil *PEHeaders, otherwise it returns a
|
||||
// nil *PEHeaders and a non-nil error.
|
||||
// Call Close() on the returned *PEHeaders when it is no longer needed.
|
||||
func NewPEFromFileName(filename string) (*PEHeaders, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newPEFromFile(f)
|
||||
}
|
||||
|
||||
func newPEFromFile(f *os.File) (*PEHeaders, error) {
|
||||
// peBounds base is 0, limit is loaded lazily
|
||||
pef := &peFile{File: f}
|
||||
peh, err := loadHeaders(pef)
|
||||
if err != nil {
|
||||
pef.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return peh, nil
|
||||
}
|
||||
|
||||
// Close frees any resources that were opened when peh was created.
|
||||
func (peh *PEHeaders) Close() error {
|
||||
return peh.r.Close()
|
||||
}
|
||||
|
||||
type rvaType interface {
|
||||
~int8 | ~int16 | ~int32 | ~uint8 | ~uint16 | ~uint32
|
||||
}
|
||||
|
||||
// addOffset ensures that, if off is negative, it does not underflow base.
|
||||
func addOffset[O rvaType](base uintptr, off O) uintptr {
|
||||
if off >= 0 {
|
||||
return base + uintptr(off)
|
||||
}
|
||||
|
||||
negation := uintptr(-off)
|
||||
if negation >= base {
|
||||
return 0
|
||||
}
|
||||
return base - negation
|
||||
}
|
||||
|
||||
func binaryRead(r io.Reader, data any) (err error) {
|
||||
// Supported Windows archs are now always LittleEndian
|
||||
err = binary.Read(r, binary.LittleEndian, data)
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
err = ErrBadLength
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// readStruct reads a T from offset rva. If r is a *peModule, the returned *T
|
||||
// points to the data in-place.
|
||||
// Note that currently this function will fail if rva references memory beyond
|
||||
// the bounds of the binary; in the case of modules, this may need to be relaxed
|
||||
// in some cases due to tampering by third-party crapware.
|
||||
func readStruct[T any, R rvaType](r peReader, rva R) (*T, error) {
|
||||
switch v := r.(type) {
|
||||
case *peFile:
|
||||
if _, err := r.Seek(int64(rva), io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := new(T)
|
||||
if err := binaryRead(r, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
case *peModule:
|
||||
addr := addOffset(r.Base(), rva)
|
||||
szT := unsafe.Sizeof(*((*T)(nil)))
|
||||
if addr+szT >= v.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
return (*T)(unsafe.Pointer(addr)), nil
|
||||
default:
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
}
|
||||
|
||||
// readStructArray reads a []T with length count from offset rva. If r is a
|
||||
// *peModule, the returned []T references the data in-place.
|
||||
// Note that currently this function will fail if rva references memory beyond
|
||||
// the bounds of the binary; in the case of modules, this may need to be relaxed
|
||||
// in some cases due to tampering by third-party crapware.
|
||||
func readStructArray[T any, R rvaType](r peReader, rva R, count int) ([]T, error) {
|
||||
switch v := r.(type) {
|
||||
case *peFile:
|
||||
if _, err := r.Seek(int64(rva), io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]T, count)
|
||||
if err := binaryRead(r, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
case *peModule:
|
||||
addr := addOffset(r.Base(), rva)
|
||||
szT := reflect.ArrayOf(count, reflect.TypeOf((*T)(nil)).Elem()).Size()
|
||||
if addr+szT >= v.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
return unsafe.Slice((*T)(unsafe.Pointer(addr)), count), nil
|
||||
default:
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
}
|
||||
|
||||
func loadHeaders(r peReader) (*PEHeaders, error) {
|
||||
// Check the signature of the DOS stub header
|
||||
if _, err := r.Seek(0, io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var mz uint16
|
||||
if err := binaryRead(r, &mz); err != nil {
|
||||
if err == ErrBadLength {
|
||||
err = ErrInvalidBinary
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if mz != mzSignature {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
// Seek to the offset of the value that points to the beginning of the PE headers
|
||||
if _, err := r.Seek(offsetIMAGE_DOS_HEADERe_lfanew, io.SeekStart); err != nil {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
// Load the offset to the beginning of the PE headers
|
||||
var e_lfanew int32
|
||||
if err := binaryRead(r, &e_lfanew); err != nil {
|
||||
if err == ErrBadLength {
|
||||
err = ErrInvalidBinary
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if e_lfanew <= 0 {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
if addOffset(r.Base(), e_lfanew) >= r.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
// Check the PE signature
|
||||
if _, err := r.Seek(int64(e_lfanew), io.SeekStart); err != nil {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
var pe uint32
|
||||
if err := binaryRead(r, &pe); err != nil {
|
||||
if err == ErrBadLength {
|
||||
err = ErrInvalidBinary
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if pe != peSignature {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
// Read the file header
|
||||
fileHeaderOffset := uint32(e_lfanew) + uint32(unsafe.Sizeof(pe))
|
||||
if r.Base()+uintptr(fileHeaderOffset) >= r.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
fileHeader, err := readStruct[FileHeader](r, fileHeaderOffset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
machine := fileHeader.Machine
|
||||
if !checkMachine(r, machine) {
|
||||
return nil, ErrUnsupportedMachine
|
||||
}
|
||||
|
||||
// Read the optional header
|
||||
optionalHeaderOffset := uint32(fileHeaderOffset) + uint32(unsafe.Sizeof(*fileHeader))
|
||||
if r.Base()+uintptr(optionalHeaderOffset) >= r.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
optionalHeader, err := resolveOptionalHeader(machine, r, optionalHeaderOffset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !checkMagic(optionalHeader, machine) {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
if fileHeader.SizeOfOptionalHeader < optionalHeader.SizeOf() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
// Coarse-grained check that header sizes make sense
|
||||
totalEssentialHeaderLen := uint32(offsetIMAGE_DOS_HEADERe_lfanew) +
|
||||
uint32(unsafe.Sizeof(e_lfanew)) +
|
||||
uint32(unsafe.Sizeof(*fileHeader)) +
|
||||
uint32(fileHeader.SizeOfOptionalHeader)
|
||||
if optionalHeader.GetSizeOfImage() < totalEssentialHeaderLen {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
numSections := fileHeader.NumberOfSections
|
||||
if numSections > maxNumSections {
|
||||
// More than 96 sections?! Really?!
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
// Read in the section table
|
||||
sectionTableOffset := optionalHeaderOffset + uint32(fileHeader.SizeOfOptionalHeader)
|
||||
if r.Base()+uintptr(sectionTableOffset) >= r.Limit() {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
sections, err := readStructArray[SectionHeader](r, sectionTableOffset, int(numSections))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &PEHeaders{r: r, fileHeader: fileHeader, optionalHeader: optionalHeader, sections: sections}, nil
|
||||
}
|
||||
|
||||
type rva32 interface {
|
||||
~int32 | ~uint32
|
||||
}
|
||||
|
||||
// resolveRVA resolves rva, or returns 0 if unavailable.
|
||||
func resolveRVA[R rva32](nfo *PEHeaders, rva R) R {
|
||||
if _, ok := nfo.r.(*peFile); !ok {
|
||||
// Just the identity function in this case.
|
||||
return rva
|
||||
}
|
||||
|
||||
if rva <= 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// We walk the section table, locating the section that would contain rva if
|
||||
// we were mapped into memory. We then calculate the offset of rva from the
|
||||
// starting virtual address of the section, and then add that offset to the
|
||||
// section's starting file pointer.
|
||||
urva := uint32(rva)
|
||||
for _, s := range nfo.sections {
|
||||
if urva < s.VirtualAddress {
|
||||
continue
|
||||
}
|
||||
if urva >= (s.VirtualAddress + s.VirtualSize) {
|
||||
continue
|
||||
}
|
||||
voff := urva - s.VirtualAddress
|
||||
foff := s.PointerToRawData + voff
|
||||
if foff >= s.PointerToRawData+s.SizeOfRawData {
|
||||
return 0
|
||||
}
|
||||
return R(foff)
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// DataDirectoryIndex is an enumeration specifying a particular entry in the
|
||||
// data directory.
|
||||
type DataDirectoryIndex int
|
||||
|
||||
const (
|
||||
IMAGE_DIRECTORY_ENTRY_EXPORT = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||
IMAGE_DIRECTORY_ENTRY_IMPORT = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_IMPORT)
|
||||
IMAGE_DIRECTORY_ENTRY_RESOURCE = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_RESOURCE)
|
||||
IMAGE_DIRECTORY_ENTRY_EXCEPTION = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_EXCEPTION)
|
||||
IMAGE_DIRECTORY_ENTRY_SECURITY = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_SECURITY)
|
||||
IMAGE_DIRECTORY_ENTRY_BASERELOC = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_BASERELOC)
|
||||
IMAGE_DIRECTORY_ENTRY_DEBUG = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_DEBUG)
|
||||
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_ARCHITECTURE)
|
||||
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_GLOBALPTR)
|
||||
IMAGE_DIRECTORY_ENTRY_TLS = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_TLS)
|
||||
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG)
|
||||
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT)
|
||||
IMAGE_DIRECTORY_ENTRY_IAT = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_IAT)
|
||||
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT)
|
||||
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
|
||||
)
|
||||
|
||||
// DataDirectoryEntry returns information from nfo's data directory at index idx.
|
||||
// The type of the return value depends on the value of idx. Most values for idx
|
||||
// currently return the DataDirectoryEntry itself, however it will return more
|
||||
// sophisticated information for the following values of idx:
|
||||
//
|
||||
// * IMAGE_DIRECTORY_ENTRY_SECURITY returns []AuthenticodeCert
|
||||
// * IMAGE_DIRECTORY_ENTRY_DEBUG returns []IMAGE_DEBUG_DIRECTORY
|
||||
//
|
||||
// Note that other idx values _will_ be modified in the future to support more
|
||||
// sophisticated return values, so be careful to structure your type assertions
|
||||
// accordingly.
|
||||
func (nfo *PEHeaders) DataDirectoryEntry(idx DataDirectoryIndex) (any, error) {
|
||||
dd := nfo.optionalHeader.GetDataDirectory()
|
||||
if int(idx) >= len(dd) {
|
||||
return nil, ErrIndexOutOfRange
|
||||
}
|
||||
|
||||
dde := dd[idx]
|
||||
if dde.VirtualAddress == 0 || dde.Size == 0 {
|
||||
return nil, ErrNotPresent
|
||||
}
|
||||
|
||||
switch idx {
|
||||
/* TODO(aaron): (don't forget to sync tests!)
|
||||
case IMAGE_DIRECTORY_ENTRY_EXPORT:
|
||||
case IMAGE_DIRECTORY_ENTRY_IMPORT:
|
||||
case IMAGE_DIRECTORY_ENTRY_RESOURCE:
|
||||
*/
|
||||
case IMAGE_DIRECTORY_ENTRY_SECURITY:
|
||||
return nfo.extractAuthenticode(dde)
|
||||
case IMAGE_DIRECTORY_ENTRY_DEBUG:
|
||||
return nfo.extractDebugInfo(dde)
|
||||
// case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:
|
||||
default:
|
||||
return dde, nil
|
||||
}
|
||||
}
|
||||
|
||||
// WIN_CERT_REVISION is an enumeration from the Windows SDK.
|
||||
type WIN_CERT_REVISION uint16
|
||||
|
||||
const (
|
||||
WIN_CERT_REVISION_1_0 WIN_CERT_REVISION = 0x0100
|
||||
WIN_CERT_REVISION_2_0 WIN_CERT_REVISION = 0x0200
|
||||
)
|
||||
|
||||
// WIN_CERT_TYPE is an enumeration from the Windows SDK.
|
||||
type WIN_CERT_TYPE uint16
|
||||
|
||||
const (
|
||||
WIN_CERT_TYPE_X509 WIN_CERT_TYPE = 0x0001
|
||||
WIN_CERT_TYPE_PKCS_SIGNED_DATA WIN_CERT_TYPE = 0x0002
|
||||
WIN_CERT_TYPE_TS_STACK_SIGNED WIN_CERT_TYPE = 0x0004
|
||||
)
|
||||
|
||||
type _WIN_CERTIFICATE_HEADER struct {
|
||||
Length uint32
|
||||
Revision WIN_CERT_REVISION
|
||||
CertificateType WIN_CERT_TYPE
|
||||
}
|
||||
|
||||
// AuthenticodeCert represents an authenticode signature that has been extracted
|
||||
// from a signed PE binary but not fully parsed.
|
||||
type AuthenticodeCert struct {
|
||||
header _WIN_CERTIFICATE_HEADER
|
||||
data []byte
|
||||
}
|
||||
|
||||
// Revision returns the revision of ac.
|
||||
func (ac *AuthenticodeCert) Revision() WIN_CERT_REVISION {
|
||||
return ac.header.Revision
|
||||
}
|
||||
|
||||
// Type returns the type of ac.
|
||||
func (ac *AuthenticodeCert) Type() WIN_CERT_TYPE {
|
||||
return ac.header.CertificateType
|
||||
}
|
||||
|
||||
// Data returns the raw bytes of ac's cert.
|
||||
func (ac *AuthenticodeCert) Data() []byte {
|
||||
return ac.data
|
||||
}
|
||||
|
||||
func alignUp[V constraints.Integer](v V, powerOfTwo uint8) V {
|
||||
if bits.OnesCount8(powerOfTwo) != 1 {
|
||||
panic("invalid powerOfTwo argument to alignUp")
|
||||
}
|
||||
return v + ((-v) & (V(powerOfTwo) - 1))
|
||||
}
|
||||
|
||||
// IMAGE_DEBUG_TYPE is an enumeration for indicating the type of debug
|
||||
// information referenced by a particular [IMAGE_DEBUG_DIRECTORY].
|
||||
type IMAGE_DEBUG_TYPE uint32
|
||||
|
||||
const (
|
||||
IMAGE_DEBUG_TYPE_UNKNOWN IMAGE_DEBUG_TYPE = 0
|
||||
IMAGE_DEBUG_TYPE_COFF IMAGE_DEBUG_TYPE = 1
|
||||
IMAGE_DEBUG_TYPE_CODEVIEW IMAGE_DEBUG_TYPE = 2
|
||||
IMAGE_DEBUG_TYPE_FPO IMAGE_DEBUG_TYPE = 3
|
||||
IMAGE_DEBUG_TYPE_MISC IMAGE_DEBUG_TYPE = 4
|
||||
IMAGE_DEBUG_TYPE_EXCEPTION IMAGE_DEBUG_TYPE = 5
|
||||
IMAGE_DEBUG_TYPE_FIXUP IMAGE_DEBUG_TYPE = 6
|
||||
IMAGE_DEBUG_TYPE_OMAP_TO_SRC IMAGE_DEBUG_TYPE = 7
|
||||
IMAGE_DEBUG_TYPE_OMAP_FROM_SRC IMAGE_DEBUG_TYPE = 8
|
||||
IMAGE_DEBUG_TYPE_BORLAND IMAGE_DEBUG_TYPE = 9
|
||||
IMAGE_DEBUG_TYPE_RESERVED10 IMAGE_DEBUG_TYPE = 10
|
||||
IMAGE_DEBUG_TYPE_BBT IMAGE_DEBUG_TYPE = IMAGE_DEBUG_TYPE_RESERVED10
|
||||
IMAGE_DEBUG_TYPE_CLSID IMAGE_DEBUG_TYPE = 11
|
||||
IMAGE_DEBUG_TYPE_VC_FEATURE IMAGE_DEBUG_TYPE = 12
|
||||
IMAGE_DEBUG_TYPE_POGO IMAGE_DEBUG_TYPE = 13
|
||||
IMAGE_DEBUG_TYPE_ILTCG IMAGE_DEBUG_TYPE = 14
|
||||
IMAGE_DEBUG_TYPE_MPX IMAGE_DEBUG_TYPE = 15
|
||||
IMAGE_DEBUG_TYPE_REPRO IMAGE_DEBUG_TYPE = 16
|
||||
IMAGE_DEBUG_TYPE_SPGO IMAGE_DEBUG_TYPE = 18
|
||||
IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS IMAGE_DEBUG_TYPE = 20
|
||||
)
|
||||
|
||||
// IMAGE_DEBUG_DIRECTORY describes debug information embedded in the binary.
|
||||
type IMAGE_DEBUG_DIRECTORY struct {
|
||||
Characteristics uint32
|
||||
TimeDateStamp uint32
|
||||
MajorVersion uint16
|
||||
MinorVersion uint16
|
||||
Type IMAGE_DEBUG_TYPE
|
||||
SizeOfData uint32
|
||||
AddressOfRawData uint32
|
||||
PointerToRawData uint32
|
||||
}
|
||||
|
||||
func (nfo *PEHeaders) extractDebugInfo(dde DataDirectoryEntry) (any, error) {
|
||||
rva := resolveRVA(nfo, dde.VirtualAddress)
|
||||
if rva == 0 {
|
||||
return nil, ErrResolvingFileRVA
|
||||
}
|
||||
|
||||
count := dde.Size / uint32(unsafe.Sizeof(IMAGE_DEBUG_DIRECTORY{}))
|
||||
return readStructArray[IMAGE_DEBUG_DIRECTORY](nfo.r, rva, int(count))
|
||||
}
|
||||
|
||||
// IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED contains CodeView debug information
|
||||
// embedded in the PE file. Note that this structure's ABI does not match its C
|
||||
// counterpart because the former uses a Go string and the latter is packed and
|
||||
// also includes a signature field.
|
||||
type IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED struct {
|
||||
GUID wingoes.GUID
|
||||
Age uint32
|
||||
PDBPath string
|
||||
}
|
||||
|
||||
// String returns the data from u formatted in the same way that Microsoft
|
||||
// debugging tools and symbol servers use to identify PDB files corresponding
|
||||
// to a specific binary.
|
||||
func (u *IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED) String() string {
|
||||
var b strings.Builder
|
||||
fmt.Fprintf(&b, "%08X%04X%04X", u.GUID.Data1, u.GUID.Data2, u.GUID.Data3)
|
||||
for _, v := range u.GUID.Data4 {
|
||||
fmt.Fprintf(&b, "%02X", v)
|
||||
}
|
||||
fmt.Fprintf(&b, "%X", u.Age)
|
||||
return b.String()
|
||||
}
|
||||
|
||||
const codeViewSignature = 0x53445352
|
||||
|
||||
func (u *IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED) unpack(r *bufio.Reader) error {
|
||||
var signature uint32
|
||||
if err := binaryRead(r, &signature); err != nil {
|
||||
return err
|
||||
}
|
||||
if signature != codeViewSignature {
|
||||
return ErrBadCodeView
|
||||
}
|
||||
|
||||
if err := binaryRead(r, &u.GUID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := binaryRead(r, &u.Age); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var storage [16]byte
|
||||
pdbBytes := storage[:0]
|
||||
for b, err := r.ReadByte(); err == nil && b != 0; b, err = r.ReadByte() {
|
||||
pdbBytes = append(pdbBytes, b)
|
||||
}
|
||||
|
||||
u.PDBPath = string(pdbBytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtractCodeViewInfo obtains CodeView debug information from de, assuming that
|
||||
// de represents CodeView debug info.
|
||||
func (nfo *PEHeaders) ExtractCodeViewInfo(de IMAGE_DEBUG_DIRECTORY) (*IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED, error) {
|
||||
if de.Type != IMAGE_DEBUG_TYPE_CODEVIEW {
|
||||
return nil, ErrNotCodeView
|
||||
}
|
||||
|
||||
var sr *io.SectionReader
|
||||
switch v := nfo.r.(type) {
|
||||
case *peFile:
|
||||
sr = io.NewSectionReader(v, int64(de.PointerToRawData), int64(de.SizeOfData))
|
||||
case *peModule:
|
||||
sr = io.NewSectionReader(v, int64(de.AddressOfRawData), int64(de.SizeOfData))
|
||||
default:
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
cv := new(IMAGE_DEBUG_INFO_CODEVIEW_UNPACKED)
|
||||
if err := cv.unpack(bufio.NewReader(sr)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cv, nil
|
||||
}
|
||||
|
||||
func readFull(r io.Reader, buf []byte) (n int, err error) {
|
||||
n, err = io.ReadFull(r, buf)
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
err = ErrBadLength
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (nfo *PEHeaders) extractAuthenticode(dde DataDirectoryEntry) (any, error) {
|
||||
if _, ok := nfo.r.(*peFile); !ok {
|
||||
// Authenticode; only available in file, not loaded at runtime.
|
||||
return nil, ErrUnavailableInModule
|
||||
}
|
||||
|
||||
var result []AuthenticodeCert
|
||||
// The VirtualAddress is a file offset.
|
||||
sr := io.NewSectionReader(nfo.r, int64(dde.VirtualAddress), int64(dde.Size))
|
||||
var curOffset int64
|
||||
szEntry := unsafe.Sizeof(_WIN_CERTIFICATE_HEADER{})
|
||||
|
||||
for {
|
||||
var entry AuthenticodeCert
|
||||
if err := binaryRead(sr, &entry.header); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
curOffset += int64(szEntry)
|
||||
|
||||
if uintptr(entry.header.Length) < szEntry {
|
||||
return nil, ErrInvalidBinary
|
||||
}
|
||||
|
||||
entry.data = make([]byte, uintptr(entry.header.Length)-szEntry)
|
||||
n, err := readFull(sr, entry.data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
curOffset += int64(n)
|
||||
|
||||
result = append(result, entry)
|
||||
|
||||
curOffset = alignUp(curOffset, 8)
|
||||
if _, err := sr.Seek(curOffset, io.SeekStart); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
16
vendor/github.com/dblohm7/wingoes/pe/pe_386.go
generated
vendored
Normal file
16
vendor/github.com/dblohm7/wingoes/pe/pe_386.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build windows
|
||||
|
||||
package pe
|
||||
|
||||
import (
|
||||
dpe "debug/pe"
|
||||
)
|
||||
|
||||
type optionalHeaderForGOARCH = optionalHeader32
|
||||
|
||||
const (
|
||||
expectedMachineForGOARCH = dpe.IMAGE_FILE_MACHINE_I386
|
||||
)
|
||||
16
vendor/github.com/dblohm7/wingoes/pe/pe_amd64.go
generated
vendored
Normal file
16
vendor/github.com/dblohm7/wingoes/pe/pe_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build windows
|
||||
|
||||
package pe
|
||||
|
||||
import (
|
||||
dpe "debug/pe"
|
||||
)
|
||||
|
||||
type optionalHeaderForGOARCH = optionalHeader64
|
||||
|
||||
const (
|
||||
expectedMachineForGOARCH = dpe.IMAGE_FILE_MACHINE_AMD64
|
||||
)
|
||||
16
vendor/github.com/dblohm7/wingoes/pe/pe_arm64.go
generated
vendored
Normal file
16
vendor/github.com/dblohm7/wingoes/pe/pe_arm64.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build windows
|
||||
|
||||
package pe
|
||||
|
||||
import (
|
||||
dpe "debug/pe"
|
||||
)
|
||||
|
||||
type optionalHeaderForGOARCH = optionalHeader64
|
||||
|
||||
const (
|
||||
expectedMachineForGOARCH = dpe.IMAGE_FILE_MACHINE_ARM64
|
||||
)
|
||||
14
vendor/github.com/dblohm7/wingoes/pe/pe_notwindows.go
generated
vendored
Normal file
14
vendor/github.com/dblohm7/wingoes/pe/pe_notwindows.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build !windows
|
||||
|
||||
package pe
|
||||
|
||||
func (pei *peModule) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkMachine(pe peReader, machine uint16) bool {
|
||||
return true
|
||||
}
|
||||
144
vendor/github.com/dblohm7/wingoes/pe/pe_windows.go
generated
vendored
Normal file
144
vendor/github.com/dblohm7/wingoes/pe/pe_windows.go
generated
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package pe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func (pei *peModule) Close() error {
|
||||
return windows.FreeLibrary(windows.Handle(pei.modLock))
|
||||
}
|
||||
|
||||
// NewPEFromBaseAddressAndSize parses the headers in a PE binary loaded
|
||||
// into the current process's address space at address baseAddr with known
|
||||
// size. If you do not have the size, use NewPEFromBaseAddress instead.
|
||||
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a nil
|
||||
// *PEHeaders and a non-nil error.
|
||||
func NewPEFromBaseAddressAndSize(baseAddr uintptr, size uint32) (*PEHeaders, error) {
|
||||
// Grab a strong reference to the module until we're done with it.
|
||||
var modLock windows.Handle
|
||||
if err := windows.GetModuleHandleEx(
|
||||
windows.GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
|
||||
(*uint16)(unsafe.Pointer(baseAddr)),
|
||||
&modLock,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
slc := unsafe.Slice((*byte)(unsafe.Pointer(baseAddr)), size)
|
||||
r := bytes.NewReader(slc)
|
||||
peMod := &peModule{
|
||||
Reader: r,
|
||||
peBounds: peBounds{
|
||||
base: baseAddr,
|
||||
limit: baseAddr + uintptr(size),
|
||||
},
|
||||
modLock: uintptr(modLock),
|
||||
}
|
||||
|
||||
peh, err := loadHeaders(peMod)
|
||||
if err != nil {
|
||||
peMod.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return peh, nil
|
||||
}
|
||||
|
||||
// NewPEFromBaseAddress parses the headers in a PE binary loaded into the
|
||||
// current process's address space at address baseAddr.
|
||||
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a nil
|
||||
// *PEHeaders and a non-nil error.
|
||||
func NewPEFromBaseAddress(baseAddr uintptr) (*PEHeaders, error) {
|
||||
var modInfo windows.ModuleInfo
|
||||
if err := windows.GetModuleInformation(
|
||||
windows.CurrentProcess(),
|
||||
windows.Handle(baseAddr),
|
||||
&modInfo,
|
||||
uint32(unsafe.Sizeof(modInfo)),
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("querying module handle: %w", err)
|
||||
}
|
||||
|
||||
return NewPEFromBaseAddressAndSize(baseAddr, modInfo.SizeOfImage)
|
||||
}
|
||||
|
||||
// NewPEFromHMODULE parses the headers in a PE binary identified by hmodule that
|
||||
// is currently loaded into the current process's address space.
|
||||
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a nil
|
||||
// *PEHeaders and a non-nil error.
|
||||
func NewPEFromHMODULE(hmodule windows.Handle) (*PEHeaders, error) {
|
||||
// HMODULEs are just a loaded module's base address with the lowest two
|
||||
// bits used for flags (see docs for LoadLibraryExW).
|
||||
return NewPEFromBaseAddress(uintptr(hmodule) & ^uintptr(3))
|
||||
}
|
||||
|
||||
// NewPEFromDLL parses the headers in a PE binary identified by dll that
|
||||
// is currently loaded into the current process's address space.
|
||||
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a nil
|
||||
// *PEHeaders and a non-nil error.
|
||||
func NewPEFromDLL(dll *windows.DLL) (*PEHeaders, error) {
|
||||
if dll == nil || dll.Handle == 0 {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
return NewPEFromHMODULE(dll.Handle)
|
||||
}
|
||||
|
||||
// NewPEFromLazyDLL parses the headers in a PE binary identified by ldll that
|
||||
// is currently loaded into the current process's address space.
|
||||
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a nil
|
||||
// *PEHeaders and a non-nil error.
|
||||
func NewPEFromLazyDLL(ldll *windows.LazyDLL) (*PEHeaders, error) {
|
||||
if ldll == nil {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
if err := ldll.Load(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewPEFromHMODULE(windows.Handle(ldll.Handle()))
|
||||
}
|
||||
|
||||
// NewPEFromFileHandle parses the PE headers from hfile, an open Win32 file handle.
|
||||
// It does *not* consume hfile.
|
||||
// Upon success it returns a non-nil *PEHeaders, otherwise it returns a
|
||||
// nil *PEHeaders and a non-nil error.
|
||||
// Call Close() on the returned *PEHeaders when it is no longer needed.
|
||||
func NewPEFromFileHandle(hfile windows.Handle) (*PEHeaders, error) {
|
||||
if hfile == 0 || hfile == windows.InvalidHandle {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
// Duplicate hfile so that we don't consume it.
|
||||
var hfileDup windows.Handle
|
||||
cp := windows.CurrentProcess()
|
||||
if err := windows.DuplicateHandle(
|
||||
cp,
|
||||
hfile,
|
||||
cp,
|
||||
&hfileDup,
|
||||
0,
|
||||
false,
|
||||
windows.DUPLICATE_SAME_ACCESS,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newPEFromFile(os.NewFile(uintptr(hfileDup), "PEFromFileHandle"))
|
||||
}
|
||||
|
||||
func checkMachine(r peReader, machine uint16) bool {
|
||||
// In-memory modules should always have a machine type that matches our own.
|
||||
// (okay, so that's kinda sorta untrue with respect to WOW64, but that's
|
||||
// a _very_ obscure use case).
|
||||
_, isModule := r.(*peModule)
|
||||
return !isModule || machine == expectedMachineForGOARCH
|
||||
}
|
||||
168
vendor/github.com/dblohm7/wingoes/pe/version.go
generated
vendored
Normal file
168
vendor/github.com/dblohm7/wingoes/pe/version.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
//go:build windows
|
||||
|
||||
package pe
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
errFixedFileInfoBadSig = errors.New("bad VS_FIXEDFILEINFO signature")
|
||||
errFixedFileInfoTooShort = errors.New("buffer smaller than VS_FIXEDFILEINFO")
|
||||
)
|
||||
|
||||
// VersionNumber encapsulates a four-component version number that is stored
|
||||
// in Windows VERSIONINFO resources.
|
||||
type VersionNumber struct {
|
||||
Major uint16
|
||||
Minor uint16
|
||||
Patch uint16
|
||||
Build uint16
|
||||
}
|
||||
|
||||
func (vn VersionNumber) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d.%d", vn.Major, vn.Minor, vn.Patch, vn.Build)
|
||||
}
|
||||
|
||||
type langAndCodePage struct {
|
||||
language uint16
|
||||
codePage uint16
|
||||
}
|
||||
|
||||
// VersionInfo encapsulates a buffer containing the VERSIONINFO resources that
|
||||
// have been successfully extracted from a PE binary.
|
||||
type VersionInfo struct {
|
||||
buf []byte
|
||||
fixed *windows.VS_FIXEDFILEINFO
|
||||
translationIDs []langAndCodePage
|
||||
}
|
||||
|
||||
const (
|
||||
langEnUS = 0x0409
|
||||
codePageUTF16LE = 0x04B0
|
||||
langNeutral = 0
|
||||
codePageNeutral = 0
|
||||
)
|
||||
|
||||
// NewVersionInfo extracts any VERSIONINFO resource from filepath, parses its
|
||||
// fixed-size information, and returns a *VersionInfo for further querying.
|
||||
// It returns ErrNotPresent if no VERSIONINFO resources are found.
|
||||
func NewVersionInfo(filepath string) (*VersionInfo, error) {
|
||||
bufSize, err := windows.GetFileVersionInfoSize(filepath, nil)
|
||||
if err != nil {
|
||||
if errors.Is(err, windows.ERROR_RESOURCE_TYPE_NOT_FOUND) {
|
||||
err = ErrNotPresent
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := make([]byte, bufSize)
|
||||
if err := windows.GetFileVersionInfo(filepath, 0, bufSize, unsafe.Pointer(unsafe.SliceData(buf))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var fixed *windows.VS_FIXEDFILEINFO
|
||||
var fixedLen uint32
|
||||
if err := windows.VerQueryValue(unsafe.Pointer(unsafe.SliceData(buf)), `\`, unsafe.Pointer(&fixed), &fixedLen); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fixedLen < uint32(unsafe.Sizeof(windows.VS_FIXEDFILEINFO{})) {
|
||||
return nil, errFixedFileInfoTooShort
|
||||
}
|
||||
if fixed.Signature != 0xFEEF04BD {
|
||||
return nil, errFixedFileInfoBadSig
|
||||
}
|
||||
|
||||
return &VersionInfo{
|
||||
buf: buf,
|
||||
fixed: fixed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (vi *VersionInfo) VersionNumber() VersionNumber {
|
||||
f := vi.fixed
|
||||
|
||||
return VersionNumber{
|
||||
Major: uint16(f.FileVersionMS >> 16),
|
||||
Minor: uint16(f.FileVersionMS & 0xFFFF),
|
||||
Patch: uint16(f.FileVersionLS >> 16),
|
||||
Build: uint16(f.FileVersionLS & 0xFFFF),
|
||||
}
|
||||
}
|
||||
|
||||
func (vi *VersionInfo) maybeLoadTranslationIDs() {
|
||||
if vi.translationIDs != nil {
|
||||
// Already loaded
|
||||
return
|
||||
}
|
||||
|
||||
// Preferred translations, in order of preference.
|
||||
preferredTranslationIDs := []langAndCodePage{
|
||||
langAndCodePage{
|
||||
language: langEnUS,
|
||||
codePage: codePageUTF16LE,
|
||||
},
|
||||
langAndCodePage{
|
||||
language: langNeutral,
|
||||
codePage: codePageNeutral,
|
||||
},
|
||||
}
|
||||
|
||||
var ids *langAndCodePage
|
||||
var idsNumBytes uint32
|
||||
if err := windows.VerQueryValue(
|
||||
unsafe.Pointer(unsafe.SliceData(vi.buf)),
|
||||
`\VarFileInfo\Translation`,
|
||||
unsafe.Pointer(&ids),
|
||||
&idsNumBytes,
|
||||
); err != nil {
|
||||
// If nothing is listed, then just try to use our preferred translation IDs.
|
||||
vi.translationIDs = preferredTranslationIDs
|
||||
return
|
||||
}
|
||||
|
||||
idsSlice := unsafe.Slice(ids, idsNumBytes/uint32(unsafe.Sizeof(*ids)))
|
||||
vi.translationIDs = append(preferredTranslationIDs, idsSlice...)
|
||||
}
|
||||
|
||||
func (vi *VersionInfo) queryWithLangAndCodePage(key string, lcp langAndCodePage) (string, error) {
|
||||
fq := fmt.Sprintf("\\StringFileInfo\\%04x%04x\\%s", lcp.language, lcp.codePage, key)
|
||||
|
||||
var value *uint16
|
||||
var valueLen uint32
|
||||
if err := windows.VerQueryValue(unsafe.Pointer(unsafe.SliceData(vi.buf)), fq, unsafe.Pointer(&value), &valueLen); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return windows.UTF16ToString(unsafe.Slice(value, valueLen)), nil
|
||||
}
|
||||
|
||||
// Field queries the version information for a field named key and either
|
||||
// returns the field's value, or an error. It attempts to resolve strings using
|
||||
// the following order of language preference: en-US, language-neutral, followed
|
||||
// by the first entry in version info's list of supported languages that
|
||||
// successfully resolves the key.
|
||||
// If the key cannot be resolved, it returns ErrNotPresent.
|
||||
func (vi *VersionInfo) Field(key string) (string, error) {
|
||||
vi.maybeLoadTranslationIDs()
|
||||
|
||||
for _, lcp := range vi.translationIDs {
|
||||
value, err := vi.queryWithLangAndCodePage(key, lcp)
|
||||
if err == nil {
|
||||
return value, nil
|
||||
}
|
||||
if !errors.Is(err, windows.ERROR_RESOURCE_TYPE_NOT_FOUND) {
|
||||
return "", err
|
||||
}
|
||||
// Otherwise we continue looping and try the next language
|
||||
}
|
||||
|
||||
return "", ErrNotPresent
|
||||
}
|
||||
Reference in New Issue
Block a user