This commit is contained in:
bluepython508 committed 2026-10-22 12:27:19 +01:00
1 parent 3530afb854
commit 6903fcff27
2146 files changed
+112256 -190703

No files matched your search

+79 -38
View File
@@ -13,6 +13,7 @@ import (
"errors"
"fmt"
"io"
"math"
"math/bits"
"os"
"reflect"
@@ -83,10 +84,14 @@ func (s *SectionHeader) NameString() string {
return string(s.Name[:])
}
type peReader interface {
io.Closer
type peReaderBase interface {
io.ReaderAt
io.ReadSeeker
}
type peReader interface {
peReaderBase
io.Closer
Base() uintptr
Limit() uintptr
}
@@ -136,7 +141,7 @@ func checkMagic(oh OptionalHeader, machine uint16) bool {
case dpe.IMAGE_FILE_MACHINE_AMD64, dpe.IMAGE_FILE_MACHINE_ARM64:
expectedMagic = 0x020B
default:
panic("unsupported machine")
panic(fmt.Sprintf("unsupported machine 0x%04X", machine))
}
return oh.GetMagic() == expectedMagic
@@ -147,22 +152,32 @@ type peBounds struct {
limit uintptr
}
func (peb *peBounds) Base() uintptr {
return peb.base
}
func (peb *peBounds) Limit() uintptr {
return peb.limit
}
type peFile struct {
*os.File
peReaderBase
peBounds
}
func (pef *peFile) Base() uintptr {
return pef.base
}
func (pef *peFile) Close() error {
if pef.peReaderBase == nil {
return nil
}
func (pef *peFile) Limit() uintptr {
if pef.limit == 0 {
if fi, err := pef.Stat(); err == nil {
pef.limit = uintptr(fi.Size())
if cl, ok := pef.peReaderBase.(io.Closer); ok {
if err := cl.Close(); err != nil {
return err
}
}
return pef.limit
pef.peReaderBase = nil
return nil
}
type peModule struct {
@@ -171,14 +186,6 @@ type peModule struct {
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.
@@ -193,8 +200,18 @@ func NewPEFromFileName(filename string) (*PEHeaders, error) {
}
func newPEFromFile(f *os.File) (*PEHeaders, error) {
// peBounds base is 0, limit is loaded lazily
pef := &peFile{File: f}
fi, err := f.Stat()
if err != nil {
f.Close()
return nil, err
}
return newPEFromReaderBase(f, uintptr(fi.Size()))
}
func newPEFromReaderBase(rb peReaderBase, size uintptr) (*PEHeaders, error) {
// peBounds base is 0, limit is size
pef := &peFile{peReaderBase: rb, peBounds: peBounds{limit: size}}
peh, err := loadHeaders(pef)
if err != nil {
pef.Close()
@@ -204,6 +221,15 @@ func newPEFromFile(f *os.File) (*PEHeaders, error) {
return peh, nil
}
// NewPEFromBlob parses the headers from contents of a PE file stored in blob.
// The blob must contain the contents of a file, not a module. 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 NewPEFromBlob(blob []byte) (*PEHeaders, error) {
return newPEFromReaderBase(bytes.NewReader(blob), uintptr(len(blob)))
}
// Close frees any resources that were opened when peh was created.
func (peh *PEHeaders) Close() error {
return peh.r.Close()
@@ -213,17 +239,20 @@ 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 {
// addOffset ensures that off neither overflows nor underflows base.
func addOffset[O rvaType](base uintptr, off O) (addr uintptr, ok bool) {
if off >= 0 {
return base + uintptr(off)
if upoff := uintptr(off); upoff <= (math.MaxUint - base) {
return base + upoff, true
}
return 0, false
}
negation := uintptr(-off)
if negation >= base {
return 0
return 0, false
}
return base - negation
return base - negation, true
}
func binaryRead(r io.Reader, data any) (err error) {
@@ -254,9 +283,12 @@ func readStruct[T any, R rvaType](r peReader, rva R) (*T, error) {
return result, nil
case *peModule:
addr := addOffset(r.Base(), rva)
szT := unsafe.Sizeof(*((*T)(nil)))
if addr+szT >= v.Limit() {
addr, ok := addOffset(r.Base(), rva)
if !ok {
return nil, ErrInvalidBinary
}
szT := uint32(unsafe.Sizeof(*((*T)(nil))))
if addr2, ok := addOffset(addr, szT); !ok || addr2 >= v.Limit() {
return nil, ErrInvalidBinary
}
@@ -285,9 +317,12 @@ func readStructArray[T any, R rvaType](r peReader, rva R, count int) ([]T, error
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() {
addr, ok := addOffset(r.Base(), rva)
if !ok {
return nil, ErrInvalidBinary
}
szT := uint32(reflect.ArrayOf(count, reflect.TypeFor[T]()).Size())
if addr2, ok := addOffset(addr, szT); !ok || addr2 >= v.Limit() {
return nil, ErrInvalidBinary
}
@@ -330,7 +365,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
if e_lfanew <= 0 {
return nil, ErrInvalidBinary
}
if addOffset(r.Base(), e_lfanew) >= r.Limit() {
if addr, ok := addOffset(r.Base(), e_lfanew); !ok || addr >= r.Limit() {
return nil, ErrInvalidBinary
}
@@ -352,7 +387,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
// Read the file header
fileHeaderOffset := uint32(e_lfanew) + uint32(unsafe.Sizeof(pe))
if r.Base()+uintptr(fileHeaderOffset) >= r.Limit() {
if addr, ok := addOffset(r.Base(), fileHeaderOffset); !ok || addr >= r.Limit() {
return nil, ErrInvalidBinary
}
@@ -368,7 +403,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
// Read the optional header
optionalHeaderOffset := uint32(fileHeaderOffset) + uint32(unsafe.Sizeof(*fileHeader))
if r.Base()+uintptr(optionalHeaderOffset) >= r.Limit() {
if addr, ok := addOffset(r.Base(), optionalHeaderOffset); !ok || addr >= r.Limit() {
return nil, ErrInvalidBinary
}
@@ -402,7 +437,7 @@ func loadHeaders(r peReader) (*PEHeaders, error) {
// Read in the section table
sectionTableOffset := optionalHeaderOffset + uint32(fileHeader.SizeOfOptionalHeader)
if r.Base()+uintptr(sectionTableOffset) >= r.Limit() {
if addr, ok := addOffset(r.Base(), sectionTableOffset); !ok || addr >= r.Limit() {
return nil, ErrInvalidBinary
}
@@ -474,6 +509,8 @@ const (
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = DataDirectoryIndex(dpe.IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)
)
const _IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
// 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
@@ -486,9 +523,13 @@ const (
// sophisticated return values, so be careful to structure your type assertions
// accordingly.
func (nfo *PEHeaders) DataDirectoryEntry(idx DataDirectoryIndex) (any, error) {
if ii := int(idx); ii < 0 || ii >= _IMAGE_NUMBEROF_DIRECTORY_ENTRIES {
return nil, ErrIndexOutOfRange
}
dd := nfo.optionalHeader.GetDataDirectory()
if int(idx) >= len(dd) {
return nil, ErrIndexOutOfRange
return nil, ErrNotPresent
}
dde := dd[idx]