github.com/saferwall/pe@v1.5.2/helper.go (about)

     1  // Copyright 2018 Saferwall. All rights reserved.
     2  // Use of this source code is governed by Apache v2 license
     3  // license that can be found in the LICENSE file.
     4  
     5  package pe
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/binary"
    10  	"errors"
    11  	"golang.org/x/text/encoding/unicode"
    12  	"path"
    13  	"path/filepath"
    14  	"runtime"
    15  	"strings"
    16  )
    17  
    18  const (
    19  	// TinyPESize On Windows XP (x32) the smallest PE executable is 97 bytes.
    20  	TinyPESize = 97
    21  
    22  	// FileAlignmentHardcodedValue represents the value which PointerToRawData
    23  	// should be at least equal or bigger to, or it will be rounded to zero.
    24  	// According to http://corkami.blogspot.com/2010/01/parce-que-la-planche-aura-brule.html
    25  	// if PointerToRawData is less that 0x200 it's rounded to zero.
    26  	FileAlignmentHardcodedValue = 0x200
    27  )
    28  
    29  // Errors
    30  var (
    31  
    32  	// ErrInvalidPESize is returned when the file size is less that the smallest
    33  	// PE file size possible.ErrImageOS2SignatureFound
    34  	ErrInvalidPESize = errors.New("not a PE file, smaller than tiny PE")
    35  
    36  	// ErrDOSMagicNotFound is returned when file is potentially a ZM executable.
    37  	ErrDOSMagicNotFound = errors.New("DOS Header magic not found")
    38  
    39  	// ErrInvalidElfanewValue is returned when e_lfanew is larger than file size.
    40  	ErrInvalidElfanewValue = errors.New("invalid e_lfanew value. Probably not a PE file")
    41  
    42  	// ErrInvalidNtHeaderOffset is returned when the NT Header offset is beyond
    43  	// the image file.
    44  	ErrInvalidNtHeaderOffset = errors.New(
    45  		"invalid NT Header Offset. NT Header Signature not found")
    46  
    47  	// ErrImageOS2SignatureFound is returned when signature is for a NE file.
    48  	ErrImageOS2SignatureFound = errors.New(
    49  		"not a valid PE signature. Probably a NE file")
    50  
    51  	// ErrImageOS2LESignatureFound is returned when signature is for a LE file.
    52  	ErrImageOS2LESignatureFound = errors.New(
    53  		"not a valid PE signature. Probably an LE file")
    54  
    55  	// ErrImageVXDSignatureFound is returned when signature is for a LX file.
    56  	ErrImageVXDSignatureFound = errors.New(
    57  		"not a valid PE signature. Probably an LX file")
    58  
    59  	// ErrImageTESignatureFound is returned when signature is for a TE file.
    60  	ErrImageTESignatureFound = errors.New(
    61  		"not a valid PE signature. Probably a TE file")
    62  
    63  	// ErrImageNtSignatureNotFound is returned when PE magic signature is not found.
    64  	ErrImageNtSignatureNotFound = errors.New(
    65  		"not a valid PE signature. Magic not found")
    66  
    67  	// ErrImageNtOptionalHeaderMagicNotFound is returned when optional header
    68  	// magic is different from PE32/PE32+.
    69  	ErrImageNtOptionalHeaderMagicNotFound = errors.New(
    70  		"not a valid PE signature. Optional Header magic not found")
    71  
    72  	// ErrImageBaseNotAligned is reported when the image base is not aligned to 64K.
    73  	ErrImageBaseNotAligned = errors.New(
    74  		"corrupt PE file. Image base not aligned to 64 K")
    75  
    76  	// AnoImageBaseOverflow is reported when the image base + SizeOfImage is
    77  	// larger than 80000000h/FFFF080000000000h in PE32/P32+.
    78  	AnoImageBaseOverflow = "Image base beyond allowed address"
    79  
    80  	// ErrInvalidSectionFileAlignment is reported when section alignment is less than a
    81  	// PAGE_SIZE and section alignment != file alignment.
    82  	ErrInvalidSectionFileAlignment = errors.New("corrupt PE file. Section " +
    83  		"alignment is less than a PAGE_SIZE and section alignment != file alignment")
    84  
    85  	// AnoInvalidSizeOfImage is reported when SizeOfImage is not multiple of
    86  	// SectionAlignment.
    87  	AnoInvalidSizeOfImage = "Invalid SizeOfImage value, should be multiple " +
    88  		"of SectionAlignment"
    89  
    90  	// ErrOutsideBoundary is reported when attempting to read an address beyond
    91  	// file image limits.
    92  	ErrOutsideBoundary = errors.New("reading data outside boundary")
    93  )
    94  
    95  // Max returns the larger of x or y.
    96  func Max(x, y uint32) uint32 {
    97  	if x < y {
    98  		return y
    99  	}
   100  	return x
   101  }
   102  
   103  func min(a, b uint32) uint32 {
   104  	if a < b {
   105  		return a
   106  	}
   107  	return b
   108  }
   109  
   110  // Min returns the min number in a slice.
   111  func Min(values []uint32) uint32 {
   112  	min := values[0]
   113  	for _, v := range values {
   114  		if v < min {
   115  			min = v
   116  		}
   117  	}
   118  	return min
   119  }
   120  
   121  // IsValidDosFilename returns true if the DLL name is likely to be valid.
   122  // Valid FAT32 8.3 short filename characters according to:
   123  // http://en.wikipedia.org/wiki/8.3_filename
   124  // The filename length is not checked because the DLLs filename
   125  // can be longer that the 8.3
   126  func IsValidDosFilename(filename string) bool {
   127  	alphabet := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
   128  	numerals := "0123456789"
   129  	special := "!#$%&'()-@^_`{}~+,.;=[]\\/"
   130  	charset := alphabet + numerals + special
   131  	for _, c := range filename {
   132  		if !strings.Contains(charset, string(c)) {
   133  			return false
   134  		}
   135  	}
   136  	return true
   137  }
   138  
   139  // IsValidFunctionName checks if an imported name uses the valid accepted
   140  // characters expected in mangled function names. If the symbol's characters
   141  // don't fall within this charset we will assume the name is invalid.
   142  func IsValidFunctionName(functionName string) bool {
   143  	alphabet := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
   144  	numerals := "0123456789"
   145  	special := "_?@$()<>"
   146  	charset := alphabet + numerals + special
   147  	for _, c := range charset {
   148  		if !strings.Contains(charset, string(c)) {
   149  			return false
   150  		}
   151  	}
   152  	return true
   153  }
   154  
   155  // IsPrintable checks weather a string is printable.
   156  func IsPrintable(s string) bool {
   157  	alphabet := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
   158  	numerals := "0123456789"
   159  	whitespace := " \t\n\r\v\f"
   160  	special := "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
   161  	charset := alphabet + numerals + special + whitespace
   162  	for _, c := range charset {
   163  		if !strings.Contains(charset, string(c)) {
   164  			return false
   165  		}
   166  	}
   167  	return true
   168  }
   169  
   170  // getSectionByRva returns the section containing the given address.
   171  func (pe *File) getSectionByRva(rva uint32) *Section {
   172  	for _, section := range pe.Sections {
   173  		if section.Contains(rva, pe) {
   174  			return &section
   175  		}
   176  	}
   177  	return nil
   178  }
   179  
   180  // getSectionByRva returns the section name containing the given address.
   181  func (pe *File) getSectionNameByRva(rva uint32) string {
   182  	for _, section := range pe.Sections {
   183  		if section.Contains(rva, pe) {
   184  			return section.String()
   185  		}
   186  	}
   187  	return ""
   188  }
   189  
   190  func (pe *File) getSectionByOffset(offset uint32) *Section {
   191  	for _, section := range pe.Sections {
   192  		if section.Header.PointerToRawData == 0 {
   193  			continue
   194  		}
   195  
   196  		adjustedPointer := pe.adjustFileAlignment(
   197  			section.Header.PointerToRawData)
   198  		if adjustedPointer <= offset &&
   199  			offset < (adjustedPointer+section.Header.SizeOfRawData) {
   200  			return &section
   201  		}
   202  	}
   203  	return nil
   204  }
   205  
   206  // GetOffsetFromRva returns the file offset corresponding to this RVA.
   207  func (pe *File) GetOffsetFromRva(rva uint32) uint32 {
   208  
   209  	// Given a RVA, this method will find the section where the
   210  	// data lies and return the offset within the file.
   211  	section := pe.getSectionByRva(rva)
   212  	if section == nil {
   213  		if rva < uint32(len(pe.data)) {
   214  			return rva
   215  		}
   216  		return ^uint32(0)
   217  	}
   218  	sectionAlignment := pe.adjustSectionAlignment(section.Header.VirtualAddress)
   219  	fileAlignment := pe.adjustFileAlignment(section.Header.PointerToRawData)
   220  	return rva - sectionAlignment + fileAlignment
   221  }
   222  
   223  // GetRVAFromOffset returns an RVA given an offset.
   224  func (pe *File) GetRVAFromOffset(offset uint32) uint32 {
   225  	section := pe.getSectionByOffset(offset)
   226  	minAddr := ^uint32(0)
   227  	if section == nil {
   228  
   229  		if len(pe.Sections) == 0 {
   230  			return offset
   231  		}
   232  
   233  		for _, section := range pe.Sections {
   234  			vaddr := pe.adjustSectionAlignment(section.Header.VirtualAddress)
   235  			if vaddr < minAddr {
   236  				minAddr = vaddr
   237  			}
   238  		}
   239  		// Assume that offset lies within the headers
   240  		// The case illustrating this behavior can be found at:
   241  		// http://corkami.blogspot.com/2010/01/hey-hey-hey-whats-in-your-head.html
   242  		// where the import table is not contained by any section
   243  		// hence the RVA needs to be resolved to a raw offset
   244  		if offset < minAddr {
   245  			return offset
   246  		}
   247  
   248  		pe.logger.Warn("data at Offset can't be fetched. Corrupt header?")
   249  		return ^uint32(0)
   250  	}
   251  	sectionAlignment := pe.adjustSectionAlignment(section.Header.VirtualAddress)
   252  	fileAlignment := pe.adjustFileAlignment(section.Header.PointerToRawData)
   253  	return offset - fileAlignment + sectionAlignment
   254  }
   255  
   256  func (pe *File) getSectionByName(secName string) (section *ImageSectionHeader) {
   257  	for _, section := range pe.Sections {
   258  		if section.String() == secName {
   259  			return &section.Header
   260  		}
   261  
   262  	}
   263  	return nil
   264  }
   265  
   266  // getStringAtRVA returns an ASCII string located at the given address.
   267  func (pe *File) getStringAtRVA(rva, maxLen uint32) string {
   268  	if rva == 0 {
   269  		return ""
   270  	}
   271  
   272  	section := pe.getSectionByRva(rva)
   273  	if section == nil {
   274  		if rva > pe.size {
   275  			return ""
   276  		}
   277  
   278  		end := rva + maxLen
   279  		if end > pe.size {
   280  			end = pe.size
   281  		}
   282  		s := pe.GetStringFromData(0, pe.data[rva:end])
   283  		return string(s)
   284  	}
   285  	s := pe.GetStringFromData(0, section.Data(rva, maxLen, pe))
   286  	return string(s)
   287  }
   288  
   289  func (pe *File) readUnicodeStringAtRVA(rva uint32, maxLength uint32) string {
   290  	str := ""
   291  	offset := pe.GetOffsetFromRva(rva)
   292  	i := uint32(0)
   293  	for i = 0; i < maxLength; i += 2 {
   294  		if offset+i >= pe.size || pe.data[offset+i] == 0 {
   295  			break
   296  		}
   297  
   298  		str += string(pe.data[offset+i])
   299  	}
   300  	return str
   301  }
   302  
   303  func (pe *File) readASCIIStringAtOffset(offset, maxLength uint32) (uint32, string) {
   304  	str := ""
   305  	i := uint32(0)
   306  
   307  	for i = 0; i < maxLength; i++ {
   308  		if offset+i >= pe.size || pe.data[offset+i] == 0 {
   309  			break
   310  		}
   311  
   312  		str += string(pe.data[offset+i])
   313  	}
   314  	return i, str
   315  }
   316  
   317  // GetStringFromData returns ASCII string from within the data.
   318  func (pe *File) GetStringFromData(offset uint32, data []byte) []byte {
   319  
   320  	dataSize := uint32(len(data))
   321  	if dataSize == 0 {
   322  		return nil
   323  	}
   324  
   325  	if offset > dataSize {
   326  		return nil
   327  	}
   328  
   329  	end := offset
   330  	for end < dataSize {
   331  		if data[end] == 0 {
   332  			break
   333  		}
   334  		end++
   335  	}
   336  	return data[offset:end]
   337  }
   338  
   339  // getStringAtOffset returns a string given an offset.
   340  func (pe *File) getStringAtOffset(offset, size uint32) (string, error) {
   341  	if offset+size > pe.size {
   342  		return "", ErrOutsideBoundary
   343  	}
   344  
   345  	str := string(pe.data[offset : offset+size])
   346  	return strings.Replace(str, "\x00", "", -1), nil
   347  }
   348  
   349  // GetData returns the data given an RVA regardless of the section where it
   350  // lies on.
   351  func (pe *File) GetData(rva, length uint32) ([]byte, error) {
   352  
   353  	// Given a RVA and the size of the chunk to retrieve, this method
   354  	// will find the section where the data lies and return the data.
   355  	section := pe.getSectionByRva(rva)
   356  
   357  	var end uint32
   358  	if length > 0 {
   359  		end = rva + length
   360  	} else {
   361  		end = 0
   362  	}
   363  
   364  	if section == nil {
   365  		if rva < uint32(len(pe.Header)) {
   366  			return pe.Header[rva:end], nil
   367  		}
   368  
   369  		// Before we give up we check whether the file might contain the data
   370  		// anyway. There are cases of PE files without sections that rely on
   371  		// windows loading the first 8291 bytes into memory and assume the data
   372  		// will be there. A functional file with these characteristics is:
   373  		// MD5: 0008892cdfbc3bda5ce047c565e52295
   374  		// SHA-1: c7116b9ff950f86af256defb95b5d4859d4752a9
   375  
   376  		if rva < uint32(len(pe.data)) {
   377  			return pe.data[rva:end], nil
   378  		}
   379  
   380  		return nil, errors.New("data at RVA can't be fetched. Corrupt header?")
   381  	}
   382  	return section.Data(rva, length, pe), nil
   383  }
   384  
   385  // The alignment factor (in bytes) that is used to align the raw data of sections
   386  // in the image file. The value should be a power of 2 between 512 and 64 K,
   387  // inclusive. The default is 512. If the SectionAlignment is less than the
   388  // architecture's page size, then FileAlignment must match SectionAlignment.
   389  func (pe *File) adjustFileAlignment(va uint32) uint32 {
   390  
   391  	var fileAlignment uint32
   392  	switch pe.Is64 {
   393  	case true:
   394  		fileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).FileAlignment
   395  	case false:
   396  		fileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).FileAlignment
   397  	}
   398  
   399  	if fileAlignment > FileAlignmentHardcodedValue && fileAlignment%2 != 0 {
   400  		pe.Anomalies = append(pe.Anomalies, ErrInvalidFileAlignment)
   401  	}
   402  
   403  	if fileAlignment < FileAlignmentHardcodedValue {
   404  		return va
   405  	}
   406  
   407  	// round it to 0x200 if not power of 2.
   408  	// According to https://github.com/corkami/docs/blob/master/PE/PE.md
   409  	// if PointerToRawData is less that 0x200 it's rounded to zero. Loading the
   410  	// test file in a debugger it's easy to verify that the PointerToRawData
   411  	// value of 1 is rounded to zero. Hence we reproduce the behavior
   412  	return (va / 0x200) * 0x200
   413  
   414  }
   415  
   416  // The alignment (in bytes) of sections when they are loaded into memory
   417  // It must be greater than or equal to FileAlignment. The default is the
   418  // page size for the architecture.
   419  func (pe *File) adjustSectionAlignment(va uint32) uint32 {
   420  	var fileAlignment, sectionAlignment uint32
   421  
   422  	switch pe.Is64 {
   423  	case true:
   424  		fileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).FileAlignment
   425  		sectionAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).SectionAlignment
   426  	case false:
   427  		fileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).FileAlignment
   428  		sectionAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).SectionAlignment
   429  	}
   430  
   431  	if fileAlignment < FileAlignmentHardcodedValue &&
   432  		fileAlignment != sectionAlignment {
   433  		pe.Anomalies = append(pe.Anomalies, ErrInvalidSectionAlignment)
   434  	}
   435  
   436  	if sectionAlignment < 0x1000 { // page size
   437  		sectionAlignment = fileAlignment
   438  	}
   439  
   440  	// 0x200 is the minimum valid FileAlignment according to the documentation
   441  	// although ntoskrnl.exe has an alignment of 0x80 in some Windows versions
   442  	if sectionAlignment != 0 && va%sectionAlignment != 0 {
   443  		return sectionAlignment * (va / sectionAlignment)
   444  	}
   445  	return va
   446  }
   447  
   448  // alignDword aligns the offset on a 32-bit boundary.
   449  func alignDword(offset, base uint32) uint32 {
   450  	return ((offset + base + 3) & 0xfffffffc) - (base & 0xfffffffc)
   451  }
   452  
   453  // stringInSlice checks weather a string exists in a slice of strings.
   454  func stringInSlice(a string, list []string) bool {
   455  	for _, b := range list {
   456  		if b == a {
   457  			return true
   458  		}
   459  	}
   460  	return false
   461  }
   462  
   463  // intInSlice checks weather a uint32 exists in a slice of uint32.
   464  func intInSlice(a uint32, list []uint32) bool {
   465  	for _, b := range list {
   466  		if b == a {
   467  			return true
   468  		}
   469  	}
   470  	return false
   471  }
   472  
   473  // IsDriver returns true if the PE file is a Windows driver.
   474  func (pe *File) IsDriver() bool {
   475  
   476  	// Checking that the ImageBase field of the OptionalHeader is above or
   477  	// equal to 0x80000000 (that is, whether it lies in the upper 2GB of
   478  	//the address space, normally belonging to the kernel) is not a
   479  	// reliable enough indicator.  For instance, PEs that play the invalid
   480  	// ImageBase trick to get relocated could be incorrectly assumed to be
   481  	// drivers.
   482  
   483  	// Checking if any section characteristics have the IMAGE_SCN_MEM_NOT_PAGED
   484  	// flag set is not reliable either.
   485  
   486  	// If there's still no import directory (the PE doesn't have one or it's
   487  	// malformed), give up.
   488  	if len(pe.Imports) == 0 {
   489  		return false
   490  	}
   491  
   492  	// DIRECTORY_ENTRY_IMPORT will now exist, although it may be empty.
   493  	// If it imports from "ntoskrnl.exe" or other kernel components it should
   494  	// be a driver.
   495  	systemDLLs := []string{"ntoskrnl.exe", "hal.dll", "ndis.sys",
   496  		"bootvid.dll", "kdcom.dll"}
   497  	for _, dll := range pe.Imports {
   498  		if stringInSlice(strings.ToLower(dll.Name), systemDLLs) {
   499  			return true
   500  		}
   501  	}
   502  
   503  	// If still we couldn't tell, check common driver section with combination
   504  	// of IMAGE_SUBSYSTEM_NATIVE or IMAGE_SUBSYSTEM_NATIVE_WINDOWS.
   505  	subsystem := ImageOptionalHeaderSubsystemType(0)
   506  	oh32 := ImageOptionalHeader32{}
   507  	oh64 := ImageOptionalHeader64{}
   508  	switch pe.Is64 {
   509  	case true:
   510  		oh64 = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64)
   511  		subsystem = oh64.Subsystem
   512  	case false:
   513  		oh32 = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32)
   514  		subsystem = oh32.Subsystem
   515  	}
   516  	commonDriverSectionNames := []string{"page", "paged", "nonpage", "init"}
   517  	for _, section := range pe.Sections {
   518  		s := strings.ToLower(section.String())
   519  		if stringInSlice(s, commonDriverSectionNames) &&
   520  			(subsystem&ImageSubsystemNativeWindows != 0 ||
   521  				subsystem&ImageSubsystemNative != 0) {
   522  			return true
   523  		}
   524  
   525  	}
   526  
   527  	return false
   528  }
   529  
   530  // IsDLL returns true if the PE file is a standard DLL.
   531  func (pe *File) IsDLL() bool {
   532  	return pe.NtHeader.FileHeader.Characteristics&ImageFileDLL != 0
   533  }
   534  
   535  // IsEXE returns true if the PE file is a standard executable.
   536  func (pe *File) IsEXE() bool {
   537  
   538  	// Returns true only if the file has the IMAGE_FILE_EXECUTABLE_IMAGE flag set
   539  	// and the IMAGE_FILE_DLL not set and the file does not appear to be a driver either.
   540  	if pe.IsDLL() || pe.IsDriver() {
   541  		return false
   542  	}
   543  
   544  	if pe.NtHeader.FileHeader.Characteristics&ImageFileExecutableImage == 0 {
   545  		return false
   546  	}
   547  
   548  	return true
   549  }
   550  
   551  // Checksum calculates the PE checksum as generated by CheckSumMappedFile().
   552  func (pe *File) Checksum() uint32 {
   553  	var checksum uint64 = 0
   554  	var max uint64 = 0x100000000
   555  	currentDword := uint32(0)
   556  
   557  	// Get the Checksum offset.
   558  	optionalHeaderOffset := pe.DOSHeader.AddressOfNewEXEHeader + 4 +
   559  		uint32(binary.Size(pe.NtHeader.FileHeader))
   560  
   561  	// `CheckSum` field position in optional PE headers is always 64 for PE and PE+.
   562  	checksumOffset := optionalHeaderOffset + 64
   563  
   564  	// Verify the data is DWORD-aligned and add padding if needed
   565  	remainder := pe.size % 4
   566  	dataLen := pe.size
   567  	if remainder > 0 {
   568  		dataLen = pe.size + (4 - remainder)
   569  		paddedBytes := make([]byte, 4-remainder)
   570  		pe.data = append(pe.data, paddedBytes...)
   571  	}
   572  
   573  	for i := uint32(0); i < dataLen; i += 4 {
   574  		// Skip the checksum field.
   575  		if i == checksumOffset {
   576  			continue
   577  		}
   578  
   579  		// Read DWORD from file.
   580  		currentDword = binary.LittleEndian.Uint32(pe.data[i:])
   581  
   582  		// Calculate checksum.
   583  		checksum = (checksum & 0xffffffff) + uint64(currentDword) + (checksum >> 32)
   584  		if checksum > max {
   585  			checksum = (checksum & 0xffffffff) + (checksum >> 32)
   586  		}
   587  	}
   588  
   589  	checksum = (checksum & 0xffff) + (checksum >> 16)
   590  	checksum = checksum + (checksum >> 16)
   591  	checksum = checksum & 0xffff
   592  
   593  	// The length is the one of the original data, not the padded one
   594  	checksum += uint64(pe.size)
   595  
   596  	return uint32(checksum)
   597  }
   598  
   599  // ReadUint64 read a uint64 from a buffer.
   600  func (pe *File) ReadUint64(offset uint32) (uint64, error) {
   601  	if offset+8 > pe.size {
   602  		return 0, ErrOutsideBoundary
   603  	}
   604  
   605  	return binary.LittleEndian.Uint64(pe.data[offset:]), nil
   606  }
   607  
   608  // ReadUint32 read a uint32 from a buffer.
   609  func (pe *File) ReadUint32(offset uint32) (uint32, error) {
   610  	if offset > pe.size-4 {
   611  		return 0, ErrOutsideBoundary
   612  	}
   613  
   614  	return binary.LittleEndian.Uint32(pe.data[offset:]), nil
   615  }
   616  
   617  // ReadUint16 read a uint16 from a buffer.
   618  func (pe *File) ReadUint16(offset uint32) (uint16, error) {
   619  	if offset > pe.size-2 {
   620  		return 0, ErrOutsideBoundary
   621  	}
   622  
   623  	return binary.LittleEndian.Uint16(pe.data[offset:]), nil
   624  }
   625  
   626  // ReadUint8 read a uint8 from a buffer.
   627  func (pe *File) ReadUint8(offset uint32) (uint8, error) {
   628  	if offset+1 > pe.size {
   629  		return 0, ErrOutsideBoundary
   630  	}
   631  
   632  	b := pe.data[offset : offset+1][0]
   633  	return uint8(b), nil
   634  }
   635  
   636  func (pe *File) structUnpack(iface interface{}, offset, size uint32) (err error) {
   637  	// Boundary check
   638  	totalSize := offset + size
   639  
   640  	// Integer overflow
   641  	if (totalSize > offset) != (size > 0) {
   642  		return ErrOutsideBoundary
   643  	}
   644  
   645  	if offset >= pe.size || totalSize > pe.size {
   646  		return ErrOutsideBoundary
   647  	}
   648  
   649  	buf := bytes.NewReader(pe.data[offset : offset+size])
   650  	err = binary.Read(buf, binary.LittleEndian, iface)
   651  	if err != nil {
   652  		return err
   653  	}
   654  	return nil
   655  }
   656  
   657  // ReadBytesAtOffset returns a byte array from offset.
   658  func (pe *File) ReadBytesAtOffset(offset, size uint32) ([]byte, error) {
   659  	// Boundary check
   660  	totalSize := offset + size
   661  
   662  	// Integer overflow
   663  	if (totalSize > offset) != (size > 0) {
   664  		return nil, ErrOutsideBoundary
   665  	}
   666  
   667  	if offset >= pe.size || totalSize > pe.size {
   668  		return nil, ErrOutsideBoundary
   669  	}
   670  
   671  	return pe.data[offset : offset+size], nil
   672  }
   673  
   674  // DecodeUTF16String decodes the UTF16 string from the byte slice.
   675  func DecodeUTF16String(b []byte) (string, error) {
   676  	n := bytes.Index(b, []byte{0, 0})
   677  	if n == 0 {
   678  		return "", nil
   679  	}
   680  	decoder := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()
   681  	s, err := decoder.Bytes(b[0 : n+1])
   682  	if err != nil {
   683  		return "", err
   684  	}
   685  	return string(s), nil
   686  }
   687  
   688  // IsBitSet returns true when a bit on a particular position is set.
   689  func IsBitSet(n uint64, pos int) bool {
   690  	val := n & (1 << pos)
   691  	return (val > 0)
   692  }
   693  
   694  func getAbsoluteFilePath(testfile string) string {
   695  	_, p, _, _ := runtime.Caller(0)
   696  	return path.Join(filepath.Dir(p), testfile)
   697  }