github.com/saferwall/pe@v1.5.2/imports.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  	"crypto/md5"
     9  	"encoding/binary"
    10  	"encoding/hex"
    11  	"errors"
    12  	"fmt"
    13  	"strconv"
    14  	"strings"
    15  )
    16  
    17  const (
    18  	imageOrdinalFlag32   = uint32(0x80000000)
    19  	imageOrdinalFlag64   = uint64(0x8000000000000000)
    20  	maxRepeatedAddresses = uint32(0xF)
    21  	maxAddressSpread     = uint32(0x8000000)
    22  	addressMask32        = uint32(0x7fffffff)
    23  	addressMask64        = uint64(0x7fffffffffffffff)
    24  	maxDllLength         = 0x200
    25  	maxImportNameLength  = 0x200
    26  )
    27  
    28  var (
    29  	// AnoInvalidThunkAddressOfData is reported when thunk address is too spread out.
    30  	AnoInvalidThunkAddressOfData = "Thunk Address Of Data too spread out"
    31  
    32  	// AnoManyRepeatedEntries is reported when import directory contains many
    33  	// entries have the same RVA.
    34  	AnoManyRepeatedEntries = "Import directory contains many repeated entries"
    35  
    36  	// AnoAddressOfDataBeyondLimits is reported when Thunk AddressOfData goes
    37  	// beyond limits.
    38  	AnoAddressOfDataBeyondLimits = "Thunk AddressOfData beyond limits"
    39  
    40  	// AnoImportNoNameNoOrdinal is reported when an import entry does not have
    41  	// a name neither an ordinal, most probably malformed data.
    42  	AnoImportNoNameNoOrdinal = "Must have either an ordinal or a name in an import"
    43  
    44  	// ErrDamagedImportTable is reported when the IAT and ILT table length is 0.
    45  	ErrDamagedImportTable = errors.New(
    46  		"damaged Import Table information. ILT and/or IAT appear to be broken")
    47  )
    48  
    49  // ImageImportDescriptor describes the remainder of the import information.
    50  // The import directory table contains address information that is used to
    51  // resolve fixup references to the entry points within a DLL image.
    52  // It consists of an array of import directory entries, one entry for each DLL
    53  // to which the image refers. The last directory entry is empty (filled with
    54  // null values), which indicates the end of the directory table.
    55  type ImageImportDescriptor struct {
    56  	// The RVA of the import lookup/name table (INT). This table contains a name
    57  	// or ordinal for each import. The INT is an array of IMAGE_THUNK_DATA structs.
    58  	OriginalFirstThunk uint32 `json:"original_first_thunk"`
    59  
    60  	// The stamp that is set to zero until the image is bound. After the image
    61  	// is bound, this field is set to the time/data stamp of the DLL.
    62  	TimeDateStamp uint32 `json:"time_date_stamp"`
    63  
    64  	// The index of the first forwarder reference (-1 if no forwarders).
    65  	ForwarderChain uint32 `json:"forwarder_chain"`
    66  
    67  	// The address of an ASCII string that contains the name of the DLL.
    68  	// This address is relative to the image base.
    69  	Name uint32 `json:"name"`
    70  
    71  	// The RVA of the import address table (IAT). The contents of this table are
    72  	// identical to the contents of the import lookup table until the image is bound.
    73  	FirstThunk uint32 `json:"first_thunk"`
    74  }
    75  
    76  // ImageThunkData32 corresponds to one imported function from the executable.
    77  // The entries are an array of 32-bit numbers for PE32 or an array of 64-bit
    78  // numbers for PE32+. The ends of both arrays are indicated by an
    79  // IMAGE_THUNK_DATA element with a value of zero.
    80  // The IMAGE_THUNK_DATA union is a DWORD with these interpretations:
    81  // DWORD Function;       // Memory address of the imported function
    82  // DWORD Ordinal;        // Ordinal value of imported API
    83  // DWORD AddressOfData;  // RVA to an IMAGE_IMPORT_BY_NAME with the imported API name
    84  // DWORD ForwarderString;// RVA to a forwarder string
    85  type ImageThunkData32 struct {
    86  	AddressOfData uint32
    87  }
    88  
    89  // ImageThunkData64 is the PE32+ version of IMAGE_THUNK_DATA.
    90  type ImageThunkData64 struct {
    91  	AddressOfData uint64
    92  }
    93  
    94  type ThunkData32 struct {
    95  	ImageThunkData ImageThunkData32
    96  	Offset         uint32
    97  }
    98  
    99  type ThunkData64 struct {
   100  	ImageThunkData ImageThunkData64
   101  	Offset         uint32
   102  }
   103  
   104  // ImportFunction represents an imported function in the import table.
   105  type ImportFunction struct {
   106  	// An ASCII string that contains the name to import. This is the string that
   107  	// must be matched to the public name in the DLL. This string is case
   108  	// sensitive and terminated by a null byte.
   109  	Name string `json:"name"`
   110  
   111  	// An index into the export name pointer table. A match is attempted first
   112  	// with this value. If it fails, a binary search is performed on the DLL's
   113  	// export name pointer table.
   114  	Hint uint16 `json:"hint"`
   115  
   116  	// If this is true, import by ordinal. Otherwise, import by name.
   117  	ByOrdinal bool `json:"by_ordinal"`
   118  
   119  	// A 16-bit ordinal number. This field is used only if the Ordinal/Name Flag
   120  	// bit field is 1 (import by ordinal). Bits 30-15 or 62-15 must be 0.
   121  	Ordinal uint32 `json:"ordinal"`
   122  
   123  	// Name Thunk Value (OFT)
   124  	OriginalThunkValue uint64 `json:"original_thunk_value"`
   125  
   126  	// Address Thunk Value (FT)
   127  	ThunkValue uint64 `json:"thunk_value"`
   128  
   129  	// Address Thunk RVA.
   130  	ThunkRVA uint32 `json:"thunk_rva"`
   131  
   132  	// Name Thunk RVA.
   133  	OriginalThunkRVA uint32 `json:"original_thunk_rva"`
   134  }
   135  
   136  // Import represents an empty entry in the import table.
   137  type Import struct {
   138  	Offset     uint32                `json:"offset"`
   139  	Name       string                `json:"name"`
   140  	Functions  []ImportFunction      `json:"functions"`
   141  	Descriptor ImageImportDescriptor `json:"descriptor"`
   142  }
   143  
   144  func (pe *File) parseImportDirectory(rva, size uint32) (err error) {
   145  
   146  	for {
   147  		importDesc := ImageImportDescriptor{}
   148  		fileOffset := pe.GetOffsetFromRva(rva)
   149  		importDescSize := uint32(binary.Size(importDesc))
   150  		err := pe.structUnpack(&importDesc, fileOffset, importDescSize)
   151  
   152  		// If the RVA is invalid all would blow up. Some EXEs seem to be
   153  		// specially nasty and have an invalid RVA.
   154  		if err != nil {
   155  			return err
   156  		}
   157  
   158  		// If the structure is all zeros, we reached the end of the list.
   159  		if importDesc == (ImageImportDescriptor{}) {
   160  			break
   161  		}
   162  
   163  		rva += importDescSize
   164  
   165  		// If the array of thunks is somewhere earlier than the import
   166  		// descriptor we can set a maximum length for the array. Otherwise
   167  		// just set a maximum length of the size of the file
   168  		maxLen := uint32(len(pe.data)) - fileOffset
   169  		if rva > importDesc.OriginalFirstThunk || rva > importDesc.FirstThunk {
   170  			if rva < importDesc.OriginalFirstThunk {
   171  				maxLen = rva - importDesc.FirstThunk
   172  			} else if rva < importDesc.FirstThunk {
   173  				maxLen = rva - importDesc.OriginalFirstThunk
   174  			} else {
   175  				maxLen = Max(rva-importDesc.OriginalFirstThunk,
   176  					rva-importDesc.FirstThunk)
   177  			}
   178  		}
   179  
   180  		var importedFunctions []ImportFunction
   181  		if pe.Is64 {
   182  			importedFunctions, err = pe.parseImports64(&importDesc, maxLen)
   183  		} else {
   184  			importedFunctions, err = pe.parseImports32(&importDesc, maxLen)
   185  		}
   186  		if err != nil {
   187  			return err
   188  		}
   189  
   190  		dllName := pe.getStringAtRVA(importDesc.Name, maxDllLength)
   191  		if !IsValidDosFilename(dllName) {
   192  			dllName = "*invalid*"
   193  			continue
   194  		}
   195  
   196  		pe.Imports = append(pe.Imports, Import{
   197  			Offset:     fileOffset,
   198  			Name:       string(dllName),
   199  			Functions:  importedFunctions,
   200  			Descriptor: importDesc,
   201  		})
   202  	}
   203  
   204  	if len(pe.Imports) > 0 {
   205  		pe.HasImport = true
   206  	}
   207  
   208  	return nil
   209  }
   210  
   211  func (pe *File) getImportTable32(rva uint32, maxLen uint32,
   212  	isOldDelayImport bool) ([]ThunkData32, error) {
   213  
   214  	// Setup variables
   215  	thunkTable := make(map[uint32]*ImageThunkData32)
   216  	retVal := []ThunkData32{}
   217  	minAddressOfData := ^uint32(0)
   218  	maxAddressOfData := uint32(0)
   219  	repeatedAddress := uint32(0)
   220  	var size uint32 = 4
   221  	addressesOfData := make(map[uint32]bool)
   222  
   223  	startRVA := rva
   224  
   225  	if rva == 0 {
   226  		return nil, nil
   227  	}
   228  
   229  	for {
   230  		if rva >= startRVA+maxLen {
   231  			pe.logger.Warnf("Error parsing the import table. Entries go beyond bounds.")
   232  			break
   233  		}
   234  
   235  		// if we see too many times the same entry we assume it could be
   236  		// a table containing bogus data (with malicious intent or otherwise)
   237  		if repeatedAddress >= maxRepeatedAddresses {
   238  			if !stringInSlice(AnoManyRepeatedEntries, pe.Anomalies) {
   239  				pe.Anomalies = append(pe.Anomalies, AnoManyRepeatedEntries)
   240  			}
   241  		}
   242  
   243  		// if the addresses point somewhere but the difference between the
   244  		// highest and lowest address is larger than maxAddressSpread we assume
   245  		// a bogus table as the addresses should be contained within a module
   246  		if maxAddressOfData-minAddressOfData > maxAddressSpread {
   247  			if !stringInSlice(AnoInvalidThunkAddressOfData, pe.Anomalies) {
   248  				pe.Anomalies = append(pe.Anomalies, AnoInvalidThunkAddressOfData)
   249  			}
   250  		}
   251  
   252  		// In its original incarnation in Visual C++ 6.0, all ImgDelayDescr
   253  		// fields containing addresses used virtual addresses, rather than RVAs.
   254  		// That is, they contained actual addresses where the delayload data
   255  		// could be found. These fields are DWORDs, the size of a pointer on the x86.
   256  		// Now fast-forward to IA-64 support. All of a sudden, 4 bytes isn't
   257  		// enough to hold a complete address. At this point, Microsoft did the
   258  		// correct thing and changed the fields containing addresses to RVAs.
   259  		offset := uint32(0)
   260  		if isOldDelayImport {
   261  			oh32 := pe.NtHeader.OptionalHeader.(ImageOptionalHeader32)
   262  			newRVA := rva - oh32.ImageBase
   263  			offset = pe.GetOffsetFromRva(newRVA)
   264  			if offset == ^uint32(0) {
   265  				return nil, nil
   266  			}
   267  		} else {
   268  			offset = pe.GetOffsetFromRva(rva)
   269  			if offset == ^uint32(0) {
   270  				return nil, nil
   271  			}
   272  		}
   273  
   274  		// Read the image thunk data.
   275  		thunk := ImageThunkData32{}
   276  		err := pe.structUnpack(&thunk, offset, size)
   277  		if err != nil {
   278  			// pe.logger.Warnf("Error parsing the import table. " +
   279  			// 	"Invalid data at RVA: 0x%x", rva)
   280  			return nil, nil
   281  		}
   282  
   283  		if thunk == (ImageThunkData32{}) {
   284  			break
   285  		}
   286  
   287  		// Check if the AddressOfData lies within the range of RVAs that it's
   288  		// being scanned, abort if that is the case, as it is very unlikely
   289  		// to be legitimate data.
   290  		// Seen in PE with SHA256:
   291  		// 5945bb6f0ac879ddf61b1c284f3b8d20c06b228e75ae4f571fa87f5b9512902c
   292  		if thunk.AddressOfData >= startRVA && thunk.AddressOfData <= rva {
   293  			pe.logger.Warnf("Error parsing the import table. "+
   294  				"AddressOfData overlaps with THUNK_DATA for THUNK at: "+
   295  				"RVA 0x%x", rva)
   296  			break
   297  		}
   298  
   299  		if thunk.AddressOfData&imageOrdinalFlag32 > 0 {
   300  			// If the entry looks like could be an ordinal.
   301  			if thunk.AddressOfData&0x7fffffff > 0xffff {
   302  				// but its value is beyond 2^16, we will assume it's a
   303  				// corrupted and ignore it altogether
   304  				if !stringInSlice(AnoAddressOfDataBeyondLimits, pe.Anomalies) {
   305  					pe.Anomalies = append(pe.Anomalies, AnoAddressOfDataBeyondLimits)
   306  				}
   307  			}
   308  		} else {
   309  			// and if it looks like it should be an RVA keep track of the RVAs seen
   310  			// and store them to study their  properties. When certain non-standard
   311  			// features are detected the parsing will be aborted
   312  			_, ok := addressesOfData[thunk.AddressOfData]
   313  			if ok {
   314  				repeatedAddress++
   315  			} else {
   316  				addressesOfData[thunk.AddressOfData] = true
   317  			}
   318  
   319  			if thunk.AddressOfData > maxAddressOfData {
   320  				maxAddressOfData = thunk.AddressOfData
   321  			}
   322  
   323  			if thunk.AddressOfData < minAddressOfData {
   324  				minAddressOfData = thunk.AddressOfData
   325  			}
   326  		}
   327  
   328  		thunkTable[rva] = &thunk
   329  		thunkData := ThunkData32{ImageThunkData: thunk, Offset: rva}
   330  		retVal = append(retVal, thunkData)
   331  		rva += size
   332  	}
   333  	return retVal, nil
   334  }
   335  
   336  func (pe *File) getImportTable64(rva uint32, maxLen uint32,
   337  	isOldDelayImport bool) ([]ThunkData64, error) {
   338  
   339  	// Setup variables
   340  	thunkTable := make(map[uint32]*ImageThunkData64)
   341  	retVal := []ThunkData64{}
   342  	minAddressOfData := ^uint64(0)
   343  	maxAddressOfData := uint64(0)
   344  	repeatedAddress := uint64(0)
   345  	var size uint32 = 8
   346  	addressesOfData := make(map[uint64]bool)
   347  
   348  	startRVA := rva
   349  
   350  	if rva == 0 {
   351  		return nil, nil
   352  	}
   353  
   354  	for {
   355  		if rva >= startRVA+maxLen {
   356  			pe.logger.Warnf("Error parsing the import table. Entries go beyond bounds.")
   357  			break
   358  		}
   359  
   360  		// if we see too many times the same entry we assume it could be
   361  		// a table containing bogus data (with malicious intent or otherwise)
   362  		if repeatedAddress >= uint64(maxRepeatedAddresses) {
   363  			if !stringInSlice(AnoManyRepeatedEntries, pe.Anomalies) {
   364  				pe.Anomalies = append(pe.Anomalies, AnoManyRepeatedEntries)
   365  			}
   366  		}
   367  
   368  		// if the addresses point somewhere but the difference between the highest
   369  		// and lowest address is larger than maxAddressSpread we assume a bogus
   370  		// table as the addresses should be contained within a module
   371  		if maxAddressOfData-minAddressOfData > uint64(maxAddressSpread) {
   372  			if !stringInSlice(AnoInvalidThunkAddressOfData, pe.Anomalies) {
   373  				pe.Anomalies = append(pe.Anomalies, AnoInvalidThunkAddressOfData)
   374  			}
   375  		}
   376  
   377  		// In its original incarnation in Visual C++ 6.0, all ImgDelayDescr
   378  		// fields containing addresses used virtual addresses, rather than RVAs.
   379  		// That is, they contained actual addresses where the delayload data
   380  		// could be found. These fields are DWORDs, the size of a pointer on the x86.
   381  		// Now fast-forward to IA-64 support. All of a sudden, 4 bytes isn't
   382  		// enough to hold a complete address. At this point, Microsoft did the
   383  		// correct thing and changed the fields containing addresses to RVAs.
   384  		offset := uint32(0)
   385  		if isOldDelayImport {
   386  			oh64 := pe.NtHeader.OptionalHeader.(ImageOptionalHeader64)
   387  			newRVA := rva - uint32(oh64.ImageBase)
   388  			offset = pe.GetOffsetFromRva(newRVA)
   389  			if offset == ^uint32(0) {
   390  				return nil, nil
   391  			}
   392  		} else {
   393  			offset = pe.GetOffsetFromRva(rva)
   394  			if offset == ^uint32(0) {
   395  				return nil, nil
   396  			}
   397  		}
   398  
   399  		// Read the image thunk data.
   400  		thunk := ImageThunkData64{}
   401  		err := pe.structUnpack(&thunk, offset, size)
   402  		if err != nil {
   403  			// pe.logger.Warnf("Error parsing the import table. " +
   404  			// 	"Invalid data at RVA: 0x%x", rva)
   405  			return nil, nil
   406  		}
   407  
   408  		if thunk == (ImageThunkData64{}) {
   409  			break
   410  		}
   411  
   412  		// Check if the AddressOfData lies within the range of RVAs that it's
   413  		// being scanned, abort if that is the case, as it is very unlikely
   414  		// to be legitimate data.
   415  		// Seen in PE with SHA256:
   416  		// 5945bb6f0ac879ddf61b1c284f3b8d20c06b228e75ae4f571fa87f5b9512902c
   417  		if thunk.AddressOfData >= uint64(startRVA) &&
   418  			thunk.AddressOfData <= uint64(rva) {
   419  			pe.logger.Warnf("Error parsing the import table. "+
   420  				"AddressOfData overlaps with THUNK_DATA for THUNK at: "+
   421  				"RVA 0x%x", rva)
   422  			break
   423  		}
   424  
   425  		// If the entry looks like could be an ordinal
   426  		if thunk.AddressOfData&imageOrdinalFlag64 > 0 {
   427  			// but its value is beyond 2^16, we will assume it's a
   428  			// corrupted and ignore it altogether
   429  			if thunk.AddressOfData&0x7fffffff > 0xffff {
   430  				if !stringInSlice(AnoAddressOfDataBeyondLimits, pe.Anomalies) {
   431  					pe.Anomalies = append(pe.Anomalies, AnoAddressOfDataBeyondLimits)
   432  				}
   433  			}
   434  			// and if it looks like it should be an RVA
   435  		} else {
   436  			// keep track of the RVAs seen and store them to study their
   437  			// properties. When certain non-standard features are detected
   438  			// the parsing will be aborted
   439  			_, ok := addressesOfData[thunk.AddressOfData]
   440  			if ok {
   441  				repeatedAddress++
   442  			} else {
   443  				addressesOfData[thunk.AddressOfData] = true
   444  			}
   445  
   446  			if thunk.AddressOfData > maxAddressOfData {
   447  				maxAddressOfData = thunk.AddressOfData
   448  			}
   449  
   450  			if thunk.AddressOfData < minAddressOfData {
   451  				minAddressOfData = thunk.AddressOfData
   452  			}
   453  		}
   454  
   455  		thunkTable[rva] = &thunk
   456  		thunkData := ThunkData64{ImageThunkData: thunk, Offset: rva}
   457  		retVal = append(retVal, thunkData)
   458  		rva += size
   459  	}
   460  	return retVal, nil
   461  }
   462  
   463  func (pe *File) parseImports32(importDesc interface{}, maxLen uint32) (
   464  	[]ImportFunction, error) {
   465  
   466  	var OriginalFirstThunk uint32
   467  	var FirstThunk uint32
   468  	var isOldDelayImport bool
   469  
   470  	switch desc := importDesc.(type) {
   471  	case *ImageImportDescriptor:
   472  		OriginalFirstThunk = desc.OriginalFirstThunk
   473  		FirstThunk = desc.FirstThunk
   474  	case *ImageDelayImportDescriptor:
   475  		OriginalFirstThunk = desc.ImportNameTableRVA
   476  		FirstThunk = desc.ImportAddressTableRVA
   477  		if desc.Attributes == 0 {
   478  			isOldDelayImport = true
   479  		}
   480  	}
   481  
   482  	// Import Lookup Table (OFT). Contains ordinals or pointers to strings.
   483  	ilt, err := pe.getImportTable32(OriginalFirstThunk, maxLen, isOldDelayImport)
   484  	if err != nil {
   485  		return nil, err
   486  	}
   487  
   488  	// Import Address Table (FT). May have identical content to ILT if PE file is
   489  	// not bound. It will contain the address of the imported symbols once
   490  	// the binary is loaded or if it is already bound.
   491  	iat, err := pe.getImportTable32(FirstThunk, maxLen, isOldDelayImport)
   492  	if err != nil {
   493  		return nil, err
   494  	}
   495  
   496  	// Some DLLs has IAT or ILT with nil type.
   497  	if len(iat) == 0 && len(ilt) == 0 {
   498  		return nil, ErrDamagedImportTable
   499  	}
   500  
   501  	var table []ThunkData32
   502  	if len(ilt) > 0 {
   503  		table = ilt
   504  	} else if len(iat) > 0 {
   505  		table = iat
   506  	} else {
   507  		return nil, err
   508  	}
   509  
   510  	importedFunctions := []ImportFunction{}
   511  	numInvalid := uint32(0)
   512  	for idx := uint32(0); idx < uint32(len(table)); idx++ {
   513  		imp := ImportFunction{}
   514  		if table[idx].ImageThunkData.AddressOfData > 0 {
   515  			// If imported by ordinal, we will append the ordinal number
   516  			if table[idx].ImageThunkData.AddressOfData&imageOrdinalFlag32 > 0 {
   517  				imp.ByOrdinal = true
   518  				imp.Ordinal = table[idx].ImageThunkData.AddressOfData & uint32(0xffff)
   519  
   520  				// Original Thunk
   521  				if uint32(len(ilt)) > idx {
   522  					imp.OriginalThunkValue = uint64(ilt[idx].ImageThunkData.AddressOfData)
   523  					imp.OriginalThunkRVA = ilt[idx].Offset
   524  				}
   525  
   526  				// Thunk
   527  				if uint32(len(iat)) > idx {
   528  					imp.ThunkValue = uint64(iat[idx].ImageThunkData.AddressOfData)
   529  					imp.ThunkRVA = iat[idx].Offset
   530  				}
   531  
   532  				imp.Name = "#" + strconv.Itoa(int(imp.Ordinal))
   533  			} else {
   534  				imp.ByOrdinal = false
   535  				if isOldDelayImport {
   536  					table[idx].ImageThunkData.AddressOfData -=
   537  						pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).ImageBase
   538  				}
   539  
   540  				// Original Thunk
   541  				if uint32(len(ilt)) > idx {
   542  					imp.OriginalThunkValue = uint64(ilt[idx].ImageThunkData.AddressOfData & addressMask32)
   543  					imp.OriginalThunkRVA = ilt[idx].Offset
   544  				}
   545  
   546  				// Thunk
   547  				if uint32(len(iat)) > idx {
   548  					imp.ThunkValue = uint64(iat[idx].ImageThunkData.AddressOfData & addressMask32)
   549  					imp.ThunkRVA = iat[idx].Offset
   550  				}
   551  
   552  				// Thunk
   553  				hintNameTableRva := table[idx].ImageThunkData.AddressOfData & addressMask32
   554  				off := pe.GetOffsetFromRva(hintNameTableRva)
   555  				imp.Hint, err = pe.ReadUint16(off)
   556  				if err != nil {
   557  					imp.Hint = ^uint16(0)
   558  				}
   559  				imp.Name = pe.getStringAtRVA(table[idx].ImageThunkData.AddressOfData+2,
   560  					maxImportNameLength)
   561  				if !IsValidFunctionName(imp.Name) {
   562  					imp.Name = "*invalid*"
   563  				}
   564  			}
   565  		}
   566  
   567  		// This file bfe97192e8107d52dd7b4010d12b2924 has an invalid table built
   568  		// in a way that it's parsable but contains invalid entries that lead
   569  		// pefile to take extremely long amounts of time to parse. It also leads
   570  		// to extreme memory consumption. To prevent similar cases, if invalid
   571  		// entries are found in the middle of a table the parsing will be aborted.
   572  		hasName := len(imp.Name) > 0
   573  		if imp.Ordinal == 0 && !hasName {
   574  			if !stringInSlice(AnoImportNoNameNoOrdinal, pe.Anomalies) {
   575  				pe.Anomalies = append(pe.Anomalies, AnoImportNoNameNoOrdinal)
   576  			}
   577  		}
   578  
   579  		// Some PEs appear to interleave valid and invalid imports. Instead of
   580  		// aborting the parsing altogether we will simply skip the invalid entries.
   581  		// Although if we see 1000 invalid entries and no legit ones, we abort.
   582  		if imp.Name == "*invalid*" {
   583  			if numInvalid > 1000 && numInvalid == idx {
   584  				return nil, errors.New(
   585  					`too many invalid names, aborting parsing`)
   586  			}
   587  			numInvalid++
   588  			continue
   589  		}
   590  
   591  		importedFunctions = append(importedFunctions, imp)
   592  	}
   593  
   594  	return importedFunctions, nil
   595  }
   596  
   597  func (pe *File) parseImports64(importDesc interface{}, maxLen uint32) ([]ImportFunction, error) {
   598  
   599  	var OriginalFirstThunk uint32
   600  	var FirstThunk uint32
   601  	var isOldDelayImport bool
   602  
   603  	switch desc := importDesc.(type) {
   604  	case *ImageImportDescriptor:
   605  		OriginalFirstThunk = desc.OriginalFirstThunk
   606  		FirstThunk = desc.FirstThunk
   607  	case *ImageDelayImportDescriptor:
   608  		OriginalFirstThunk = desc.ImportNameTableRVA
   609  		FirstThunk = desc.ImportAddressTableRVA
   610  		if desc.Attributes == 0 {
   611  			isOldDelayImport = true
   612  		}
   613  	}
   614  
   615  	// Import Lookup Table. Contains ordinals or pointers to strings.
   616  	ilt, err := pe.getImportTable64(OriginalFirstThunk, maxLen, isOldDelayImport)
   617  	if err != nil {
   618  		return nil, err
   619  	}
   620  
   621  	// Import Address Table. May have identical content to ILT if PE file is
   622  	// not bound. It will contain the address of the imported symbols once
   623  	// the binary is loaded or if it is already bound.
   624  	iat, err := pe.getImportTable64(FirstThunk, maxLen, isOldDelayImport)
   625  	if err != nil {
   626  		return nil, err
   627  	}
   628  
   629  	// Would crash if IAT or ILT had nil type
   630  	if len(iat) == 0 && len(ilt) == 0 {
   631  		return nil, ErrDamagedImportTable
   632  	}
   633  
   634  	var table []ThunkData64
   635  	if len(ilt) > 0 {
   636  		table = ilt
   637  	} else if len(iat) > 0 {
   638  		table = iat
   639  	} else {
   640  		return nil, err
   641  	}
   642  
   643  	importedFunctions := []ImportFunction{}
   644  	numInvalid := uint32(0)
   645  	for idx := uint32(0); idx < uint32(len(table)); idx++ {
   646  		imp := ImportFunction{}
   647  		if table[idx].ImageThunkData.AddressOfData > 0 {
   648  
   649  			// If imported by ordinal, we will append the ordinal number
   650  			if table[idx].ImageThunkData.AddressOfData&imageOrdinalFlag64 > 0 {
   651  				imp.ByOrdinal = true
   652  				imp.Ordinal = uint32(table[idx].ImageThunkData.AddressOfData) & uint32(0xffff)
   653  
   654  				// Original Thunk
   655  				if uint32(len(ilt)) > idx {
   656  					imp.OriginalThunkValue =
   657  						ilt[idx].ImageThunkData.AddressOfData
   658  					imp.OriginalThunkRVA = ilt[idx].Offset
   659  				}
   660  
   661  				// Thunk
   662  				if uint32(len(iat)) > idx {
   663  					imp.ThunkValue = iat[idx].ImageThunkData.AddressOfData
   664  					imp.ThunkRVA = iat[idx].Offset
   665  				}
   666  
   667  				imp.Name = "#" + strconv.Itoa(int(imp.Ordinal))
   668  
   669  			} else {
   670  
   671  				imp.ByOrdinal = false
   672  
   673  				if isOldDelayImport {
   674  					table[idx].ImageThunkData.AddressOfData -=
   675  						pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).ImageBase
   676  				}
   677  
   678  				// Original Thunk
   679  				if uint32(len(ilt)) > idx {
   680  					imp.OriginalThunkValue =
   681  						ilt[idx].ImageThunkData.AddressOfData & addressMask64
   682  					imp.OriginalThunkRVA = ilt[idx].Offset
   683  				}
   684  
   685  				// Thunk
   686  				if uint32(len(iat)) > idx {
   687  					imp.ThunkValue = iat[idx].ImageThunkData.AddressOfData & addressMask64
   688  					imp.ThunkRVA = iat[idx].Offset
   689  				}
   690  
   691  				hintNameTableRva := table[idx].ImageThunkData.AddressOfData & addressMask64
   692  				off := pe.GetOffsetFromRva(uint32(hintNameTableRva))
   693  				imp.Hint = binary.LittleEndian.Uint16(pe.data[off:])
   694  				imp.Name = pe.getStringAtRVA(uint32(table[idx].ImageThunkData.AddressOfData+2),
   695  					maxImportNameLength)
   696  				if !IsValidFunctionName(imp.Name) {
   697  					imp.Name = "*invalid*"
   698  				}
   699  			}
   700  		}
   701  
   702  		// This file bfe97192e8107d52dd7b4010d12b2924 has an invalid table built
   703  		// in a way that it's parsable but contains invalid entries that lead
   704  		// pefile to take extremely long amounts of time to parse. It also leads
   705  		// to extreme memory consumption. To prevent similar cases, if invalid
   706  		// entries are found in the middle of a table the parsing will be aborted.
   707  		hasName := len(imp.Name) > 0
   708  		if imp.Ordinal == 0 && !hasName {
   709  			if !stringInSlice(AnoImportNoNameNoOrdinal, pe.Anomalies) {
   710  				pe.Anomalies = append(pe.Anomalies, AnoImportNoNameNoOrdinal)
   711  			}
   712  		}
   713  		// Some PEs appear to interleave valid and invalid imports. Instead of
   714  		// aborting the parsing altogether we will simply skip the invalid entries.
   715  		// Although if we see 1000 invalid entries and no legit ones, we abort.
   716  		if imp.Name == "*invalid*" {
   717  			if numInvalid > 1000 && numInvalid == idx {
   718  				return nil, errors.New(
   719  					`too many invalid names, aborting parsing`)
   720  			}
   721  			numInvalid++
   722  			continue
   723  		}
   724  
   725  		importedFunctions = append(importedFunctions, imp)
   726  	}
   727  
   728  	return importedFunctions, nil
   729  }
   730  
   731  // GetImportEntryInfoByRVA return an import function + index of the entry given
   732  // an RVA.
   733  func (pe *File) GetImportEntryInfoByRVA(rva uint32) (Import, int) {
   734  	for _, imp := range pe.Imports {
   735  		for i, entry := range imp.Functions {
   736  			if entry.ThunkRVA == rva {
   737  				return imp, i
   738  			}
   739  		}
   740  	}
   741  
   742  	return Import{}, 0
   743  }
   744  
   745  // md5hash hashes using md5 algorithm.
   746  func md5hash(text string) string {
   747  	h := md5.New()
   748  	h.Write([]byte(text))
   749  	return hex.EncodeToString(h.Sum(nil))
   750  }
   751  
   752  // ImpHash calculates the import hash.
   753  // Algorithm:
   754  // Resolving ordinals to function names when they appear
   755  // Converting both DLL names and function names to all lowercase
   756  // Removing the file extensions from imported module names
   757  // Building and storing the lowercased string . in an ordered list
   758  // Generating the MD5 hash of the ordered list
   759  func (pe *File) ImpHash() (string, error) {
   760  	if len(pe.Imports) == 0 {
   761  		return "", errors.New("no imports found")
   762  	}
   763  
   764  	extensions := []string{"ocx", "sys", "dll"}
   765  	var impStrs []string
   766  
   767  	for _, imp := range pe.Imports {
   768  		var libName string
   769  		parts := strings.Split(imp.Name, ".")
   770  		if len(parts) == 2 && stringInSlice(strings.ToLower(parts[1]), extensions) {
   771  			libName = parts[0]
   772  		} else {
   773  			libName = imp.Name
   774  		}
   775  
   776  		libName = strings.ToLower(libName)
   777  
   778  		for _, function := range imp.Functions {
   779  			var funcName string
   780  			if function.ByOrdinal {
   781  				funcName = OrdLookup(imp.Name, uint64(function.Ordinal), true)
   782  			} else {
   783  				funcName = function.Name
   784  			}
   785  
   786  			if funcName == "" {
   787  				continue
   788  			}
   789  
   790  			impStr := fmt.Sprintf("%s.%s", libName, strings.ToLower(funcName))
   791  			impStrs = append(impStrs, impStr)
   792  		}
   793  	}
   794  
   795  	hash := md5hash(strings.Join(impStrs, ","))
   796  	return hash, nil
   797  }