项目作者: saferwall

项目描述 :
A :zap: lightweight Go package to parse, analyze and extract metadata from Portable Executable (PE) binaries. Designed for malware analysis tasks and robust against PE malformations.
高级语言: Go
项目地址: git://github.com/saferwall/pe.git
创建时间: 2021-01-30T22:37:51Z
项目社区:https://github.com/saferwall/pe

开源协议:MIT License

下载


Saferwall logo

Portable Executable Parser

GoDoc Go Version Report Card codecov GitHub Workflow Status

pe is a go package for parsing the portable executable file format. This package was designed with malware analysis in mind, and being resistent to PE malformations.

Table of content

Features

  • Works with PE32/PE32+ file format.
  • Supports Intel x86/AMD64/ARM7ARM7 Thumb/ARM8-64/IA64/CHPE architectures.
  • MS DOS header.
  • Rich Header (calculate checksum and hash).
  • NT Header (file header + optional header).
  • COFF symbol table and string table.
  • Sections headers + entropy calculation.
  • Data directories
    • Import Table + ImpHash calculation.
    • Export Table
    • Resource Table
    • Exceptions Table
    • Security Table + Authentihash calculation.
    • Relocations Table
    • Debug Table (CODEVIEW, POGO, VC FEATURE, REPRO, FPO, EXDLL CHARACTERISTICS debug types).
    • TLS Table
    • Load Config Directory (SEH, GFID, GIAT, Guard LongJumps, CHPE, Dynamic Value Reloc Table, Enclave Configuration, Volatile Metadata tables).
    • Bound Import Table
    • Delay Import Table
    • COM Table (CLR Metadata Header, Metadata Table Streams)
  • Report several anomalies

Installing

Using this go package is easy. First, use go get to install the latest version of the library. This command will install the pedumper executable along with the library and its dependencies:

  1. go get -u github.com/saferwall/pe

Next, include pe package in your application:

  1. import "github.com/saferwall/pe"

Using the library

  1. package main
  2. import (
  3. peparser "github.com/saferwall/pe"
  4. )
  5. func main() {
  6. filename := "C:\\Binaries\\notepad.exe"
  7. pe, err := peparser.New(filename, &peparser.Options{})
  8. if err != nil {
  9. log.Fatalf("Error while opening file: %s, reason: %v", filename, err)
  10. }
  11. err = pe.Parse()
  12. if err != nil {
  13. log.Fatalf("Error while parsing file: %s, reason: %v", filename, err)
  14. }
  15. }

Start by instantiating a pe object by called the New() method, which takes the file path to the file to be parsed and some optional options.

Afterwards, a call to the Parse() method will give you access to all the different part of the PE format, directly accessible to be used. Here is the definition of the struct:

  1. type File struct {
  2. DOSHeader ImageDOSHeader `json:"dos_header,omitempty"`
  3. RichHeader RichHeader `json:"rich_header,omitempty"`
  4. NtHeader ImageNtHeader `json:"nt_header,omitempty"`
  5. COFF COFF `json:"coff,omitempty"`
  6. Sections []Section `json:"sections,omitempty"`
  7. Imports []Import `json:"imports,omitempty"`
  8. Export Export `json:"export,omitempty"`
  9. Debugs []DebugEntry `json:"debugs,omitempty"`
  10. Relocations []Relocation `json:"relocations,omitempty"`
  11. Resources ResourceDirectory `json:"resources,omitempty"`
  12. TLS TLSDirectory `json:"tls,omitempty"`
  13. LoadConfig LoadConfig `json:"load_config,omitempty"`
  14. Exceptions []Exception `json:"exceptions,omitempty"`
  15. Certificates CertificateSection `json:"certificates,omitempty"`
  16. DelayImports []DelayImport `json:"delay_imports,omitempty"`
  17. BoundImports []BoundImportDescriptorData `json:"bound_imports,omitempty"`
  18. GlobalPtr uint32 `json:"global_ptr,omitempty"`
  19. CLR CLRData `json:"clr,omitempty"`
  20. IAT []IATEntry `json:"iat,omitempty"`
  21. Anomalies []string `json:"anomalies,omitempty"`
  22. Header []byte
  23. data mmap.MMap
  24. FileInfo
  25. size uint32
  26. OverlayOffset int64
  27. f *os.File
  28. opts *Options
  29. logger *log.Helper
  30. }

PE Header

As mentioned before, all members of the struct are directly (no getters) accessible, additionally, the fields types has been preserved as the spec defines them, that means if you need to show the prettified version of an int type, you have to call the corresponding helper function.

  1. fmt.Printf("Magic is: 0x%x\n", pe.DOSHeader.Magic)
  2. fmt.Printf("Signature is: 0x%x\n", pe.NtHeader.Signature)
  3. fmt.Printf("Machine is: 0x%x, Meaning: %s\n", pe.NtHeader.FileHeader.Machine, pe.NtHeader.FileHeader.Machine.String())

Output:

  1. Magic is: 0x5a4d
  2. Signature is: 0x4550
  3. Machine is: 0x8664, Meaning: x64

Rich Header

Example:

  1. richHeader, _ := json.Marshal(pe.RichHeader)
  2. fmt.Print(prettyPrint(richHeader))

Output:

  1. {
  2. "XorKey": 2796214951,
  3. "CompIDs": [
  4. {
  5. "MinorCV": 27412,
  6. "ProdID": 257,
  7. "Count": 4,
  8. "Unmasked": 16870164
  9. },
  10. {
  11. "MinorCV": 30729,
  12. "ProdID": 147,
  13. "Count": 193,
  14. "Unmasked": 9664521
  15. },
  16. {
  17. "MinorCV": 0,
  18. "ProdID": 1,
  19. "Count": 1325,
  20. "Unmasked": 65536
  21. },
  22. {
  23. "MinorCV": 27412,
  24. "ProdID": 260,
  25. "Count": 9,
  26. "Unmasked": 17066772
  27. },
  28. {
  29. "MinorCV": 27412,
  30. "ProdID": 259,
  31. "Count": 3,
  32. "Unmasked": 17001236
  33. },
  34. {
  35. "MinorCV": 27412,
  36. "ProdID": 256,
  37. "Count": 1,
  38. "Unmasked": 16804628
  39. },
  40. {
  41. "MinorCV": 27412,
  42. "ProdID": 269,
  43. "Count": 209,
  44. "Unmasked": 17656596
  45. },
  46. {
  47. "MinorCV": 27412,
  48. "ProdID": 255,
  49. "Count": 1,
  50. "Unmasked": 16739092
  51. },
  52. {
  53. "MinorCV": 27412,
  54. "ProdID": 258,
  55. "Count": 1,
  56. "Unmasked": 16935700
  57. }
  58. ],
  59. "DansOffset": 128,
  60. "Raw": "47vE9afaqqan2qqmp9qqprOxq6ej2qqmrqI5pmbaqqan2qumit+qprOxrqeu2qqms7Gpp6TaqqazsaqnptqqprOxp6d22qqms7FVpqbaqqazsainptqqplJpY2in2qqm"
  61. }

Iterating over sections

  1. for _, sec := range pe.Sections {
  2. fmt.Printf("Section Name : %s\n", sec.NameString())
  3. fmt.Printf("Section VirtualSize : %x\n", sec.Header.VirtualSize)
  4. fmt.Printf("Section Flags : %x, Meaning: %v\n\n",
  5. sec.Header.Characteristics, sec.PrettySectionFlags())
  6. }

Output:

  1. Section Name : .text
  2. Section VirtualSize : 2ea58
  3. Section Flags : 60500060, Meaning: [Align8Bytes Readable Align16Bytes Executable Contains Code Initialized Data Align1Bytes]
  4. Section Name : .data
  5. Section VirtualSize : 58
  6. Section Flags : c0500040, Meaning: [Readable Initialized Data Writable Align1Bytes Align16Bytes Align8Bytes]
  7. Section Name : .rdata
  8. Section VirtualSize : 18d0
  9. Section Flags : 40600040, Meaning: [Align2Bytes Align8Bytes Readable Initialized Data Align32Bytes]
  10. ...

Roadmap

  • imports MS-styled names demangling
  • PE: VB5 and VB6 typical structures: project info, DLLCall-imports, referenced modules, object table

Fuzz Testing

To validate the parser we use the go-fuzz and a corpus of known malformed and tricky PE files from corkami.

Projects Using This Library


Fibratus

Fibratus A modern tool for Windows kernel exploration and tracing with a focus on security.

References