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 }