...

Source file src/debug/pe/file.go

Documentation: debug/pe

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6  Package pe implements access to PE (Microsoft Windows Portable Executable) files.
     7  
     8  # Security
     9  
    10  This package is not designed to be hardened against adversarial inputs, and is
    11  outside the scope of https://go.dev/security/policy. In particular, only basic
    12  validation is done when parsing object files. As such, care should be taken when
    13  parsing untrusted inputs, as parsing malformed files may consume significant
    14  resources, or cause panics.
    15  */
    16  package pe
    17  
    18  import (
    19  	"bytes"
    20  	"compress/zlib"
    21  	"debug/dwarf"
    22  	"encoding/binary"
    23  	"errors"
    24  	"fmt"
    25  	"internal/saferio"
    26  	"io"
    27  	"os"
    28  	"strings"
    29  )
    30  
    31  // A File represents an open PE file.
    32  type File struct {
    33  	FileHeader
    34  	OptionalHeader any // of type *OptionalHeader32 or *OptionalHeader64
    35  	Sections       []*Section
    36  	Symbols        []*Symbol    // COFF symbols with auxiliary symbol records removed
    37  	COFFSymbols    []COFFSymbol // all COFF symbols (including auxiliary symbol records)
    38  	StringTable    StringTable
    39  
    40  	closer io.Closer
    41  }
    42  
    43  // Open opens the named file using [os.Open] and prepares it for use as a PE binary.
    44  func Open(name string) (*File, error) {
    45  	f, err := os.Open(name)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	ff, err := NewFile(f)
    50  	if err != nil {
    51  		f.Close()
    52  		return nil, err
    53  	}
    54  	ff.closer = f
    55  	return ff, nil
    56  }
    57  
    58  // Close closes the [File].
    59  // If the [File] was created using [NewFile] directly instead of [Open],
    60  // Close has no effect.
    61  func (f *File) Close() error {
    62  	var err error
    63  	if f.closer != nil {
    64  		err = f.closer.Close()
    65  		f.closer = nil
    66  	}
    67  	return err
    68  }
    69  
    70  // TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance)
    71  
    72  // NewFile creates a new [File] for accessing a PE binary in an underlying reader.
    73  func NewFile(r io.ReaderAt) (*File, error) {
    74  	f := new(File)
    75  	sr := io.NewSectionReader(r, 0, 1<<63-1)
    76  
    77  	var dosheader [96]byte
    78  	if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
    79  		return nil, err
    80  	}
    81  	var base int64
    82  	if dosheader[0] == 'M' && dosheader[1] == 'Z' {
    83  		signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
    84  		var sign [4]byte
    85  		if _, err := r.ReadAt(sign[:], signoff); err != nil {
    86  			return nil, err
    87  		}
    88  		if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
    89  			return nil, fmt.Errorf("invalid PE file signature: % x", sign)
    90  		}
    91  		base = signoff + 4
    92  	} else {
    93  		base = int64(0)
    94  	}
    95  	sr.Seek(base, io.SeekStart)
    96  	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
    97  		return nil, err
    98  	}
    99  	switch f.FileHeader.Machine {
   100  	case IMAGE_FILE_MACHINE_AMD64,
   101  		IMAGE_FILE_MACHINE_ARM64,
   102  		IMAGE_FILE_MACHINE_ARMNT,
   103  		IMAGE_FILE_MACHINE_I386,
   104  		IMAGE_FILE_MACHINE_RISCV32,
   105  		IMAGE_FILE_MACHINE_RISCV64,
   106  		IMAGE_FILE_MACHINE_RISCV128,
   107  		IMAGE_FILE_MACHINE_UNKNOWN:
   108  		// ok
   109  	default:
   110  		return nil, fmt.Errorf("unrecognized PE machine: %#x", f.FileHeader.Machine)
   111  	}
   112  
   113  	var err error
   114  
   115  	// Read string table.
   116  	f.StringTable, err = readStringTable(&f.FileHeader, sr)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	// Read symbol table.
   122  	f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr)
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  	f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	// Seek past file header.
   132  	_, err = sr.Seek(base+int64(binary.Size(f.FileHeader)), io.SeekStart)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	// Read optional header.
   138  	f.OptionalHeader, err = readOptionalHeader(sr, f.FileHeader.SizeOfOptionalHeader)
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  
   143  	// Process sections.
   144  	f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
   145  	for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
   146  		sh := new(SectionHeader32)
   147  		if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
   148  			return nil, err
   149  		}
   150  		name, err := sh.fullName(f.StringTable)
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  		s := new(Section)
   155  		s.SectionHeader = SectionHeader{
   156  			Name:                 name,
   157  			VirtualSize:          sh.VirtualSize,
   158  			VirtualAddress:       sh.VirtualAddress,
   159  			Size:                 sh.SizeOfRawData,
   160  			Offset:               sh.PointerToRawData,
   161  			PointerToRelocations: sh.PointerToRelocations,
   162  			PointerToLineNumbers: sh.PointerToLineNumbers,
   163  			NumberOfRelocations:  sh.NumberOfRelocations,
   164  			NumberOfLineNumbers:  sh.NumberOfLineNumbers,
   165  			Characteristics:      sh.Characteristics,
   166  		}
   167  		r2 := r
   168  		if sh.PointerToRawData == 0 { // .bss must have all 0s
   169  			r2 = &nobitsSectionReader{}
   170  		}
   171  		s.sr = io.NewSectionReader(r2, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
   172  		s.ReaderAt = s.sr
   173  		f.Sections[i] = s
   174  	}
   175  	for i := range f.Sections {
   176  		var err error
   177  		f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr)
   178  		if err != nil {
   179  			return nil, err
   180  		}
   181  	}
   182  
   183  	return f, nil
   184  }
   185  
   186  type nobitsSectionReader struct{}
   187  
   188  func (*nobitsSectionReader) ReadAt(p []byte, off int64) (n int, err error) {
   189  	return 0, errors.New("unexpected read from section with uninitialized data")
   190  }
   191  
   192  // getString extracts a string from symbol string table.
   193  func getString(section []byte, start int) (string, bool) {
   194  	if start < 0 || start >= len(section) {
   195  		return "", false
   196  	}
   197  
   198  	for end := start; end < len(section); end++ {
   199  		if section[end] == 0 {
   200  			return string(section[start:end]), true
   201  		}
   202  	}
   203  	return "", false
   204  }
   205  
   206  // Section returns the first section with the given name, or nil if no such
   207  // section exists.
   208  func (f *File) Section(name string) *Section {
   209  	for _, s := range f.Sections {
   210  		if s.Name == name {
   211  			return s
   212  		}
   213  	}
   214  	return nil
   215  }
   216  
   217  func (f *File) DWARF() (*dwarf.Data, error) {
   218  	dwarfSuffix := func(s *Section) string {
   219  		switch {
   220  		case strings.HasPrefix(s.Name, ".debug_"):
   221  			return s.Name[7:]
   222  		case strings.HasPrefix(s.Name, ".zdebug_"):
   223  			return s.Name[8:]
   224  		default:
   225  			return ""
   226  		}
   227  
   228  	}
   229  
   230  	// sectionData gets the data for s and checks its size.
   231  	sectionData := func(s *Section) ([]byte, error) {
   232  		b, err := s.Data()
   233  		if err != nil && uint32(len(b)) < s.Size {
   234  			return nil, err
   235  		}
   236  
   237  		if 0 < s.VirtualSize && s.VirtualSize < s.Size {
   238  			b = b[:s.VirtualSize]
   239  		}
   240  
   241  		if len(b) >= 12 && string(b[:4]) == "ZLIB" {
   242  			dlen := binary.BigEndian.Uint64(b[4:12])
   243  			r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
   244  			if err != nil {
   245  				return nil, err
   246  			}
   247  			dbuf, err := saferio.ReadData(r, dlen)
   248  			if err != nil {
   249  				return nil, err
   250  			}
   251  			if err := r.Close(); err != nil {
   252  				return nil, err
   253  			}
   254  			b = dbuf
   255  		}
   256  		return b, nil
   257  	}
   258  
   259  	// There are many other DWARF sections, but these
   260  	// are the ones the debug/dwarf package uses.
   261  	// Don't bother loading others.
   262  	var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
   263  	for _, s := range f.Sections {
   264  		suffix := dwarfSuffix(s)
   265  		if suffix == "" {
   266  			continue
   267  		}
   268  		if _, ok := dat[suffix]; !ok {
   269  			continue
   270  		}
   271  
   272  		b, err := sectionData(s)
   273  		if err != nil {
   274  			return nil, err
   275  		}
   276  		dat[suffix] = b
   277  	}
   278  
   279  	d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
   280  	if err != nil {
   281  		return nil, err
   282  	}
   283  
   284  	// Look for DWARF4 .debug_types sections and DWARF5 sections.
   285  	for i, s := range f.Sections {
   286  		suffix := dwarfSuffix(s)
   287  		if suffix == "" {
   288  			continue
   289  		}
   290  		if _, ok := dat[suffix]; ok {
   291  			// Already handled.
   292  			continue
   293  		}
   294  
   295  		b, err := sectionData(s)
   296  		if err != nil {
   297  			return nil, err
   298  		}
   299  
   300  		if suffix == "types" {
   301  			err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
   302  		} else {
   303  			err = d.AddSection(".debug_"+suffix, b)
   304  		}
   305  		if err != nil {
   306  			return nil, err
   307  		}
   308  	}
   309  
   310  	return d, nil
   311  }
   312  
   313  // TODO(brainman): document ImportDirectory once we decide what to do with it.
   314  
   315  type ImportDirectory struct {
   316  	OriginalFirstThunk uint32
   317  	TimeDateStamp      uint32
   318  	ForwarderChain     uint32
   319  	Name               uint32
   320  	FirstThunk         uint32
   321  
   322  	dll string
   323  }
   324  
   325  // ImportedSymbols returns the names of all symbols
   326  // referred to by the binary f that are expected to be
   327  // satisfied by other libraries at dynamic load time.
   328  // It does not return weak symbols.
   329  func (f *File) ImportedSymbols() ([]string, error) {
   330  	if f.OptionalHeader == nil {
   331  		return nil, nil
   332  	}
   333  
   334  	_, pe64 := f.OptionalHeader.(*OptionalHeader64)
   335  
   336  	// grab the number of data directory entries
   337  	var dd_length uint32
   338  	if pe64 {
   339  		dd_length = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes
   340  	} else {
   341  		dd_length = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes
   342  	}
   343  
   344  	// check that the length of data directory entries is large
   345  	// enough to include the imports directory.
   346  	if dd_length < IMAGE_DIRECTORY_ENTRY_IMPORT+1 {
   347  		return nil, nil
   348  	}
   349  
   350  	// grab the import data directory entry
   351  	var idd DataDirectory
   352  	if pe64 {
   353  		idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
   354  	} else {
   355  		idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
   356  	}
   357  
   358  	// figure out which section contains the import directory table
   359  	var ds *Section
   360  	ds = nil
   361  	for _, s := range f.Sections {
   362  		if s.Offset == 0 {
   363  			continue
   364  		}
   365  		// We are using distance between s.VirtualAddress and idd.VirtualAddress
   366  		// to avoid potential overflow of uint32 caused by addition of s.VirtualSize
   367  		// to s.VirtualAddress.
   368  		if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress-s.VirtualAddress < s.VirtualSize {
   369  			ds = s
   370  			break
   371  		}
   372  	}
   373  
   374  	// didn't find a section, so no import libraries were found
   375  	if ds == nil {
   376  		return nil, nil
   377  	}
   378  
   379  	d, err := ds.Data()
   380  	if err != nil {
   381  		return nil, err
   382  	}
   383  
   384  	// seek to the virtual address specified in the import data directory
   385  	seek := idd.VirtualAddress - ds.VirtualAddress
   386  	if seek >= uint32(len(d)) {
   387  		return nil, errors.New("optional header data directory virtual size doesn't fit within data seek")
   388  	}
   389  	d = d[seek:]
   390  
   391  	// start decoding the import directory
   392  	var ida []ImportDirectory
   393  	for len(d) >= 20 {
   394  		var dt ImportDirectory
   395  		dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
   396  		dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8])
   397  		dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12])
   398  		dt.Name = binary.LittleEndian.Uint32(d[12:16])
   399  		dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
   400  		d = d[20:]
   401  		if dt.OriginalFirstThunk == 0 {
   402  			break
   403  		}
   404  		ida = append(ida, dt)
   405  	}
   406  	// TODO(brainman): this needs to be rewritten
   407  	//  ds.Data() returns contents of section containing import table. Why store in variable called "names"?
   408  	//  Why we are retrieving it second time? We already have it in "d", and it is not modified anywhere.
   409  	//  getString does not extracts a string from symbol string table (as getString doco says).
   410  	//  Why ds.Data() called again and again in the loop?
   411  	//  Needs test before rewrite.
   412  	names, _ := ds.Data()
   413  	var all []string
   414  	for _, dt := range ida {
   415  		dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
   416  		d, _ = ds.Data()
   417  		// seek to OriginalFirstThunk
   418  		seek := dt.OriginalFirstThunk - ds.VirtualAddress
   419  		if seek >= uint32(len(d)) {
   420  			return nil, errors.New("import directory original first thunk doesn't fit within data seek")
   421  		}
   422  		d = d[seek:]
   423  		for len(d) > 0 {
   424  			if pe64 { // 64bit
   425  				if len(d) < 8 {
   426  					return nil, errors.New("thunk parsing needs at least 8-bytes")
   427  				}
   428  				va := binary.LittleEndian.Uint64(d[0:8])
   429  				d = d[8:]
   430  				if va == 0 {
   431  					break
   432  				}
   433  				if va&0x8000000000000000 > 0 { // is Ordinal
   434  					// TODO add dynimport ordinal support.
   435  				} else {
   436  					fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
   437  					all = append(all, fn+":"+dt.dll)
   438  				}
   439  			} else { // 32bit
   440  				if len(d) <= 4 {
   441  					return nil, errors.New("thunk parsing needs at least 5-bytes")
   442  				}
   443  				va := binary.LittleEndian.Uint32(d[0:4])
   444  				d = d[4:]
   445  				if va == 0 {
   446  					break
   447  				}
   448  				if va&0x80000000 > 0 { // is Ordinal
   449  					// TODO add dynimport ordinal support.
   450  					//ord := va&0x0000FFFF
   451  				} else {
   452  					fn, _ := getString(names, int(va-ds.VirtualAddress+2))
   453  					all = append(all, fn+":"+dt.dll)
   454  				}
   455  			}
   456  		}
   457  	}
   458  
   459  	return all, nil
   460  }
   461  
   462  // ImportedLibraries returns the names of all libraries
   463  // referred to by the binary f that are expected to be
   464  // linked with the binary at dynamic link time.
   465  func (f *File) ImportedLibraries() ([]string, error) {
   466  	// TODO
   467  	// cgo -dynimport don't use this for windows PE, so just return.
   468  	return nil, nil
   469  }
   470  
   471  // FormatError is unused.
   472  // The type is retained for compatibility.
   473  type FormatError struct {
   474  }
   475  
   476  func (e *FormatError) Error() string {
   477  	return "unknown error"
   478  }
   479  
   480  // readOptionalHeader accepts an io.ReadSeeker pointing to optional header in the PE file
   481  // and its size as seen in the file header.
   482  // It parses the given size of bytes and returns optional header. It infers whether the
   483  // bytes being parsed refer to 32 bit or 64 bit version of optional header.
   484  func readOptionalHeader(r io.ReadSeeker, sz uint16) (any, error) {
   485  	// If optional header size is 0, return empty optional header.
   486  	if sz == 0 {
   487  		return nil, nil
   488  	}
   489  
   490  	var (
   491  		// First couple of bytes in option header state its type.
   492  		// We need to read them first to determine the type and
   493  		// validity of optional header.
   494  		ohMagic   uint16
   495  		ohMagicSz = binary.Size(ohMagic)
   496  	)
   497  
   498  	// If optional header size is greater than 0 but less than its magic size, return error.
   499  	if sz < uint16(ohMagicSz) {
   500  		return nil, fmt.Errorf("optional header size is less than optional header magic size")
   501  	}
   502  
   503  	// read reads from io.ReadSeeke, r, into data.
   504  	var err error
   505  	read := func(data any) bool {
   506  		err = binary.Read(r, binary.LittleEndian, data)
   507  		return err == nil
   508  	}
   509  
   510  	if !read(&ohMagic) {
   511  		return nil, fmt.Errorf("failure to read optional header magic: %v", err)
   512  
   513  	}
   514  
   515  	switch ohMagic {
   516  	case 0x10b: // PE32
   517  		var (
   518  			oh32 OptionalHeader32
   519  			// There can be 0 or more data directories. So the minimum size of optional
   520  			// header is calculated by subtracting oh32.DataDirectory size from oh32 size.
   521  			oh32MinSz = binary.Size(oh32) - binary.Size(oh32.DataDirectory)
   522  		)
   523  
   524  		if sz < uint16(oh32MinSz) {
   525  			return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) of PE32 optional header", sz, oh32MinSz)
   526  		}
   527  
   528  		// Init oh32 fields
   529  		oh32.Magic = ohMagic
   530  		if !read(&oh32.MajorLinkerVersion) ||
   531  			!read(&oh32.MinorLinkerVersion) ||
   532  			!read(&oh32.SizeOfCode) ||
   533  			!read(&oh32.SizeOfInitializedData) ||
   534  			!read(&oh32.SizeOfUninitializedData) ||
   535  			!read(&oh32.AddressOfEntryPoint) ||
   536  			!read(&oh32.BaseOfCode) ||
   537  			!read(&oh32.BaseOfData) ||
   538  			!read(&oh32.ImageBase) ||
   539  			!read(&oh32.SectionAlignment) ||
   540  			!read(&oh32.FileAlignment) ||
   541  			!read(&oh32.MajorOperatingSystemVersion) ||
   542  			!read(&oh32.MinorOperatingSystemVersion) ||
   543  			!read(&oh32.MajorImageVersion) ||
   544  			!read(&oh32.MinorImageVersion) ||
   545  			!read(&oh32.MajorSubsystemVersion) ||
   546  			!read(&oh32.MinorSubsystemVersion) ||
   547  			!read(&oh32.Win32VersionValue) ||
   548  			!read(&oh32.SizeOfImage) ||
   549  			!read(&oh32.SizeOfHeaders) ||
   550  			!read(&oh32.CheckSum) ||
   551  			!read(&oh32.Subsystem) ||
   552  			!read(&oh32.DllCharacteristics) ||
   553  			!read(&oh32.SizeOfStackReserve) ||
   554  			!read(&oh32.SizeOfStackCommit) ||
   555  			!read(&oh32.SizeOfHeapReserve) ||
   556  			!read(&oh32.SizeOfHeapCommit) ||
   557  			!read(&oh32.LoaderFlags) ||
   558  			!read(&oh32.NumberOfRvaAndSizes) {
   559  			return nil, fmt.Errorf("failure to read PE32 optional header: %v", err)
   560  		}
   561  
   562  		dd, err := readDataDirectories(r, sz-uint16(oh32MinSz), oh32.NumberOfRvaAndSizes)
   563  		if err != nil {
   564  			return nil, err
   565  		}
   566  
   567  		copy(oh32.DataDirectory[:], dd)
   568  
   569  		return &oh32, nil
   570  	case 0x20b: // PE32+
   571  		var (
   572  			oh64 OptionalHeader64
   573  			// There can be 0 or more data directories. So the minimum size of optional
   574  			// header is calculated by subtracting oh64.DataDirectory size from oh64 size.
   575  			oh64MinSz = binary.Size(oh64) - binary.Size(oh64.DataDirectory)
   576  		)
   577  
   578  		if sz < uint16(oh64MinSz) {
   579  			return nil, fmt.Errorf("optional header size(%d) is less minimum size (%d) for PE32+ optional header", sz, oh64MinSz)
   580  		}
   581  
   582  		// Init oh64 fields
   583  		oh64.Magic = ohMagic
   584  		if !read(&oh64.MajorLinkerVersion) ||
   585  			!read(&oh64.MinorLinkerVersion) ||
   586  			!read(&oh64.SizeOfCode) ||
   587  			!read(&oh64.SizeOfInitializedData) ||
   588  			!read(&oh64.SizeOfUninitializedData) ||
   589  			!read(&oh64.AddressOfEntryPoint) ||
   590  			!read(&oh64.BaseOfCode) ||
   591  			!read(&oh64.ImageBase) ||
   592  			!read(&oh64.SectionAlignment) ||
   593  			!read(&oh64.FileAlignment) ||
   594  			!read(&oh64.MajorOperatingSystemVersion) ||
   595  			!read(&oh64.MinorOperatingSystemVersion) ||
   596  			!read(&oh64.MajorImageVersion) ||
   597  			!read(&oh64.MinorImageVersion) ||
   598  			!read(&oh64.MajorSubsystemVersion) ||
   599  			!read(&oh64.MinorSubsystemVersion) ||
   600  			!read(&oh64.Win32VersionValue) ||
   601  			!read(&oh64.SizeOfImage) ||
   602  			!read(&oh64.SizeOfHeaders) ||
   603  			!read(&oh64.CheckSum) ||
   604  			!read(&oh64.Subsystem) ||
   605  			!read(&oh64.DllCharacteristics) ||
   606  			!read(&oh64.SizeOfStackReserve) ||
   607  			!read(&oh64.SizeOfStackCommit) ||
   608  			!read(&oh64.SizeOfHeapReserve) ||
   609  			!read(&oh64.SizeOfHeapCommit) ||
   610  			!read(&oh64.LoaderFlags) ||
   611  			!read(&oh64.NumberOfRvaAndSizes) {
   612  			return nil, fmt.Errorf("failure to read PE32+ optional header: %v", err)
   613  		}
   614  
   615  		dd, err := readDataDirectories(r, sz-uint16(oh64MinSz), oh64.NumberOfRvaAndSizes)
   616  		if err != nil {
   617  			return nil, err
   618  		}
   619  
   620  		copy(oh64.DataDirectory[:], dd)
   621  
   622  		return &oh64, nil
   623  	default:
   624  		return nil, fmt.Errorf("optional header has unexpected Magic of 0x%x", ohMagic)
   625  	}
   626  }
   627  
   628  // readDataDirectories accepts an io.ReadSeeker pointing to data directories in the PE file,
   629  // its size and number of data directories as seen in optional header.
   630  // It parses the given size of bytes and returns given number of data directories.
   631  func readDataDirectories(r io.ReadSeeker, sz uint16, n uint32) ([]DataDirectory, error) {
   632  	ddSz := uint64(binary.Size(DataDirectory{}))
   633  	if uint64(sz) != uint64(n)*ddSz {
   634  		return nil, fmt.Errorf("size of data directories(%d) is inconsistent with number of data directories(%d)", sz, n)
   635  	}
   636  
   637  	dd := make([]DataDirectory, n)
   638  	if err := binary.Read(r, binary.LittleEndian, dd); err != nil {
   639  		return nil, fmt.Errorf("failure to read data directories: %v", err)
   640  	}
   641  
   642  	return dd, nil
   643  }
   644  

View as plain text