[
  {
    "path": ".editorconfig",
    "content": "; https://editorconfig.org/\n\nroot = true\n\n[*]\ninsert_final_newline = true\ncharset = utf-8\ntrim_trailing_whitespace = true\nindent_style = space\nindent_size = 2\n\n[{Makefile,go.mod,go.sum,*.go,.gitmodules}]\nindent_style = tab\nindent_size = 4\n\n[*.md]\nindent_size = 4\ntrim_trailing_whitespace = false\n\neclint_indent_style = unset\n\n[Dockerfile]\nindent_size = 4"
  },
  {
    "path": ".gitattributes",
    "content": "# Treat all files in the Go repo as binary, with no git magic updating\n# line endings. This produces predictable results in different environments.\n#\n# Windows users contributing to Go will need to use a modern version\n# of git and editors capable of LF line endings.\n#\n# Windows .bat files are known to have multiple bugs when run with LF\n# endings, and so they are checked in with CRLF endings, with a test\n# in test/winbatch.go to catch problems. (See golang.org/issue/37791.)\n#\n# We'll prevent accidental CRLF line endings from entering the repo\n# via the git-codereview gofmt checks and tests.\n#\n# See golang.org/issue/9281.\n\n* -text\n"
  },
  {
    "path": ".github/FUNDING.YML",
    "content": "github: LordNoteworthy"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "content": "name: Build & Test\n\non: [push]\n\njobs:\n  test:\n    name: Build & Test\n    strategy:\n      fail-fast: false\n      matrix:\n        go-version: [1.20.x, 1.21.x, 1.22.x, 1.23.x, 1.24.x, 1.25.x, 1.26.x]\n        os: [ubuntu-latest, macos-latest, windows-latest]\n    runs-on: ${{ matrix.os }}\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n\n      - name: Install Go\n        uses: actions/setup-go@v6\n        with:\n          go-version: ${{ matrix.go-version }}\n\n      - name: Build\n        run: |\n          go env -w GOFLAGS=-mod=mod\n          go build -v ./...\n\n      - name: Extract test data\n        run: |\n          cd test\n          7z x \"*.7z\" -pinfected\n\n      - name: Test With Coverage\n        run: go test -race -coverprofile=coverage -covermode=atomic\n\n      - name: Upload coverage to Codecov\n        uses: codecov/codecov-action@v2\n        with:\n          files: ./coverage\n        if: matrix.os == 'windows-latest' && matrix.go-version == '1.23.x'\n\n      - name: Go vet\n        run: |\n          go vet .\n        if: matrix.os == 'windows-latest' && matrix.go-version == '1.23.x'\n\n      - name: Staticcheck\n        uses: dominikh/staticcheck-action@v1.3.1\n        with:\n          version: \"2024.1\"\n          install-go: false\n          cache-key: ${{ matrix.go }}\n        if: matrix.os == 'windows-latest' && matrix.go-version == '1.23.x'\n"
  },
  {
    "path": ".gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\ncoverage\n\n# Dependency directories (remove the comment below to include it)\nvendor/\n\n# Code editors configs\n.idea/\n.vscode/launch.json\n\n# Go fuzz artefact\ncrashers/\nsuppressions/\n\n# Log files\n*.log\n\ntest/testdata/"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [1.4.0] - Unreleased\n\n### Added\n\n- Permit more granular control over which data directories are parsed by [rabbitstack](https://github.com/rabbitstack) [#72](https://github.com/saferwall/pe/pull/72).\n- Support parsing the different `retpoline` types: Imported Address, Indirect Branch and Switchable retpoline [#70](https://github.com/saferwall/pe/pull/70).\n- Unit tests for load config directory [#70](https://github.com/saferwall/pe/pull/69).\n- Unit tests for TLS directory [#69](https://github.com/saferwall/pe/pull/69).\n- Unit tests for debug directory [#68](https://github.com/saferwall/pe/pull/68).\n- Unit tests for resource directory and add functions to prettify resource (sub)languages [#66](https://github.com/saferwall/pe/pull/66).\n- Annotate PE structures with JSON tags during JSON encoding [#64](https://github.com/saferwall/pe/pull/64), [#65](https://github.com/saferwall/pe/pull/65)  and [#67](https://github.com/saferwall/pe/pull/67).\n- Improve PE dumper to print imports and unit test parsing imports data directory[#63](https://github.com/saferwall/pe/pull/63).\n- Improve PE dumper to print section headers [#62](https://github.com/saferwall/pe/pull/62).\n- Improve PE dumper to print PE headers [#61](https://github.com/saferwall/pe/pull/61).\n- Add `SerialNumber`, `SignatureAlgorithm` and `PubKeyAlgorithm` to the `CertInfo` [#60](https://github.com/saferwall/pe/pull/60).\n- Option to disable certificate validation [#59](https://github.com/saferwall/pe/pull/59).\n- Improve PE dumper to print exceptions [#57](https://github.com/saferwall/pe/pull/57).\n- Unit tests for debug directory [#49](https://github.com/saferwall/pe/pull/49).\n\n### Fixed\n\n- Bug while iterating over VolatileInfoRangeTable entries [#70](https://github.com/saferwall/pe/pull/70).\n- Bug while iterating  (additional padding and loop condition) over DVRT relocation block entries [#70](https://github.com/saferwall/pe/pull/70).\n- Bug while appending (twice) Control Flow Guard IAT entries [#70](https://github.com/saferwall/pe/pull/70).\n- Bug while parsing `POGO` debug entry types [#68](https://github.com/saferwall/pe/pull/68).\n- `Authentihash()` for instances w/o fd thanks to [flanfly](https://github.com/flanfly) [#47](https://github.com/saferwall/pe/pull/47).\n\n### Changed\n\n- Some fields has been renamed for consistency:\n  - `RichHeader.XorKey` -> `RichHeader.XORKey`.\n  - Any `Rva` substring -> `RVA` and any `Iat` substring -> `IAT`.\n  - And many more.\n- Some fields used internally in imports parsing were changed from a slice of pointers to a simple slice.\n- Certificate.Content changed from `*pkcs7.PKCS7` to `pkcs7.PKCS7`.\n- `Section.Entropy` changed from `float64` to `float64*` to distinguish between the case when the section entropy is equal to zero and the case when the entropy is equal to nil - meaning that it was never calculated.\n- Remove `cobra` dependency from `cmd/pedumper` [#56](https://github.com/saferwall/pe/pull/56).\n\n## [1.3.0] - 2022-08-04\n\n## Added\n\n- Authenticode signature validation in Windows [#43](https://github.com/saferwall/pe/pull/43).\n- File information structure that helps to identify what parts of the PE file we have, such as `HasImports()` [#42](https://github.com/saferwall/pe/pull/42)..\n- Calculate Rich header hash thanks to [wanglei-coder](https://github.com/wanglei-coder) [#38](https://github.com/saferwall/pe/pull/38).\n- PE Overlay thanks to [wanglei-coder](https://github.com/wanglei-coder) [#37](https://github.com/saferwall/pe/pull/37).\n- Unit tests for DOS header parsing.\n- Unit tests for CLR directory [#34](https://github.com/saferwall/pe/pull/28).\n- Unit tests for Rich header [#33](https://github.com/saferwall/pe/pull/33).\n\n## Changed\n\n- Do not return an error when parsing a data directory fails [#45](https://github.com/saferwall/pe/pull/45).\n- Remove pointers from fields in the main `File` structure [#44](https://github.com/saferwall/pe/pull/44).\n\n### Fixed\n\n- Fix getting section data repeatedly thanks to [wanglei-coder](https://github.com/wanglei-coder) [#41](https://github.com/saferwall/pe/pull/41).\n- Fix `adjustSectionAlignment()` thanks to [wanglei-coder](https://github.com/wanglei-coder) [#40](https://github.com/saferwall/pe/pull/40).\n- Fix authentihash calculation thanks to [wanglei-coder](https://github.com/wanglei-coder) [#38](https://github.com/saferwall/pe/pull/38).\n- Memory leak in `Close()` function that missed a call to `unmap()` thanks to [Mamba24L8](https://github.com/Mamba24L8).\n\n## [1.2.0] - 2022-06-12\n\n## Added\n\n- Unit tests for export directory [#28](https://github.com/saferwall/pe/pull/28).\n- Add a new option to allow usage of a custom logger [#24](https://github.com/saferwall/pe/pull/24).\n- Unit tests for delay imports directory [#23](https://github.com/saferwall/pe/pull/23).\n- Allow access to the raw certificates content [#22](https://github.com/saferwall/pe/pull/22).\n- Unit tests for security directory [#19](https://github.com/saferwall/pe/pull/19).\n- Unit tests for bound imports directory [#18](https://github.com/saferwall/pe/pull/18).\n\n## Changed\n\n- Make `GetData()` and `GetRVAFromOffset()` and `GetOffsetFromRva()` helper routines public.\n- Keep parsing in exports directories even when anomalies are found [#26](https://github.com/saferwall/pe/pull/26).\n\n## Fixed\n\n- Incorrect check for `skipCertVerification` in security directory.\n- Null pointer dereference in `GetExportFunctionByRVA()` and out of bounds when calculating `symbolAddress` in export directory [#28](https://github.com/saferwall/pe/pull/28).\n- Reading unicode string from resource directory `readUnicodeStringAtRVA()` [#26](https://github.com/saferwall/pe/pull/26).\n- Null pointer dereference in resource directory parsing [#25](https://github.com/saferwall/pe/pull/25).\n- Imphash calculation [#17](https://github.com/saferwall/pe/pull/17) thanks to [@secDre4mer](https://github.com/secDre4mer).\n- Null certificate header in security directory [#19](https://github.com/saferwall/pe/pull/19)\n\n## [1.1.0] - 2021-12-20\n\n### Added\n\n- Add .editorconfig and .vscode config.\n- Add github action CI workflow to test the package.\n- Add few badges for the README.md to track build status, coverage and code quality.\n- Introduce a new API to parse a file from a byte array.\n- Parse .net metadata Module table.\n- Parse .net metadata stream headers and metadata tables stream header.\n- Add cmd/pedumper to illustrate how to use the library.\n- Add unit test for relocation, exception, security, symbol, file, nt header, section and helper files.\n- Add an option `New()` to customize max of relocations entries and COFF symbols to parse.\n\n### Changed\n\n- Remove uneeded break statements & lowercase error messages and anomalies.\n- Make COFF entry in File struct a pointer.\n- Remove unsafe pointer usage from resource directory.\n- Do not return an error when COFF symbol table is not found.\n- License from Apache 2 to MIT.\n\n### Fixed\n\n- Probe for invalid Nt Header offset.\n- Fix authenticode hash calculation.\n- Compile correctly on 32 bit thnkas to @Max Altgelt.\n- COFF symbol table `readASCIIStringAtOffset()` out of bounds exception.\n- Probe for optional header section alignment != 0.\n- Fix infinite loop in exception unwind code parsing.\n- Fix last data directory entry is reserved and must be zero.\n- Safe ready of global pointer register\n\n## [1.0.0] - 2021-03-04 (Initial Release)\n\n- Works with PE32/PE32+ file fomat.\n- Supports Intel x86/AMD64/ARM7ARM7 Thumb/ARM8-64/IA64/CHPE architectures.\n- MS DOS header.\n- Rich Header (calculate checksum).\n- NT Header (file header + optional header).\n- COFF symbol table and string table.\n- Sections headers + entropy calculation.\n- Data directories:\n  - Import Table + ImpHash calculation.\n  - Export Table.\n  - Resource Table.\n  - Exceptions Table.\n  - Security Table + Authentihash calculation.\n  - Relocations Table.\n  - Debug Table (CODEVIEW, POGO, VC FEATURE, REPRO, FPO, EXDLL CHARACTERISTICS debug types).\n  - TLS Table.\n  - Load Config Directory (SEH, GFID, GIAT, Guard LongJumps, CHPE, Dynamic Value Reloc Table, Enclave Configuration, Volatile Metadata tables).\n  - Bound Import Table.\n  - Delay Import Table.\n  - COM Table (CLR Metadata Header, Metadata Table Streams).\n  - Report several anomalies.\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at report@saferwall.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Saferwall\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "<a href=\"https://saferwall.com\" target=\"_blank\" rel=\"noopener noreferrer\"><img align=\"right\" width=\"300\" src=\".github/assets/logo.png\" alt=\"Saferwall logo\"></a>\n\n# Portable Executable Parser\n\n[![GoDoc](http://godoc.org/github.com/saferwall/pe?status.svg)](https://pkg.go.dev/github.com/saferwall/pe) ![Go Version](https://img.shields.io/badge/go%20version-%3E=1.15-61CFDD.svg) [![Report Card](https://goreportcard.com/badge/github.com/saferwall/pe)](https://goreportcard.com/report/github.com/saferwall/pe) [![codecov](https://codecov.io/gh/saferwall/pe/branch/main/graph/badge.svg?token=W7WTOUZLRY)](https://codecov.io/gh/saferwall/pe) ![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/saferwall/pe/ci.yaml?branch=main)\n\n\n**pe** is a go package for parsing the [portable executable](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format) file format. This package was designed with malware analysis in mind, and being resistent to PE malformations.\n\n## Table of content\n\n- [Portable Executable Parser](#portable-executable-parser)\n  - [Table of content](#table-of-content)\n  - [Features](#features)\n  - [Installing](#installing)\n  - [Using the library](#using-the-library)\n    - [PE Header](#pe-header)\n    - [Rich Header](#rich-header)\n    - [Iterating over sections](#iterating-over-sections)\n  - [Roadmap](#roadmap)\n  - [Fuzz Testing](#fuzz-testing)\n  - [Projects Using This Library](#projects-using-this-library)\n  - [References](#references)\n\n## Features\n\n-   Works with PE32/PE32+ file format.\n-   Supports Intel x86/AMD64/ARM7ARM7 Thumb/ARM8-64/IA64/CHPE architectures.\n-   MS DOS header.\n-   Rich Header (calculate checksum and hash).\n-   NT Header (file header + optional header).\n-   COFF symbol table and string table.\n-   Sections headers + entropy calculation.\n-   Data directories\n    -   Import Table + ImpHash calculation.\n    -   Export Table\n    -   Resource Table\n    -   Exceptions Table\n    -   Security Table + Authentihash calculation.\n    -   Relocations Table\n    -   Debug Table (CODEVIEW, POGO, VC FEATURE, REPRO, FPO, EXDLL CHARACTERISTICS debug types).\n    -   TLS Table\n    -   Load Config Directory (SEH, GFID, GIAT, Guard LongJumps, CHPE, Dynamic Value Reloc Table, Enclave Configuration, Volatile Metadata tables).\n    -   Bound Import Table\n    -   Delay Import Table\n    -   COM Table (CLR Metadata Header, Metadata Table Streams)\n-   Report several anomalies\n\n## Installing\n\nUsing 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:\n\n    go get -u github.com/saferwall/pe\n\nNext, include `pe` package in your application:\n\n```go\nimport \"github.com/saferwall/pe\"\n```\n\n## Using the library\n\n```go\npackage main\n\nimport (\n\tpeparser \"github.com/saferwall/pe\"\n)\n\nfunc main() {\n    filename := \"C:\\\\Binaries\\\\notepad.exe\"\n    pe, err := peparser.New(filename, &peparser.Options{})\n\tif err != nil {\n\t\tlog.Fatalf(\"Error while opening file: %s, reason: %v\", filename, err)\n    }\n\n    err = pe.Parse()\n    if err != nil {\n        log.Fatalf(\"Error while parsing file: %s, reason: %v\", filename, err)\n    }\n}\n```\n\nStart 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.\n\nAfterwards, 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:\n\n```go\ntype File struct {\n\tDOSHeader    ImageDOSHeader              `json:\"dos_header,omitempty\"`\n\tRichHeader   RichHeader                  `json:\"rich_header,omitempty\"`\n\tNtHeader     ImageNtHeader               `json:\"nt_header,omitempty\"`\n\tCOFF         COFF                        `json:\"coff,omitempty\"`\n\tSections     []Section                   `json:\"sections,omitempty\"`\n\tImports      []Import                    `json:\"imports,omitempty\"`\n\tExport       Export                      `json:\"export,omitempty\"`\n\tDebugs       []DebugEntry                `json:\"debugs,omitempty\"`\n\tRelocations  []Relocation                `json:\"relocations,omitempty\"`\n\tResources    ResourceDirectory           `json:\"resources,omitempty\"`\n\tTLS          TLSDirectory                `json:\"tls,omitempty\"`\n\tLoadConfig   LoadConfig                  `json:\"load_config,omitempty\"`\n\tExceptions   []Exception                 `json:\"exceptions,omitempty\"`\n\tCertificates CertificateSection          `json:\"certificates,omitempty\"`\n\tDelayImports []DelayImport               `json:\"delay_imports,omitempty\"`\n\tBoundImports []BoundImportDescriptorData `json:\"bound_imports,omitempty\"`\n\tGlobalPtr    uint32                      `json:\"global_ptr,omitempty\"`\n\tCLR          CLRData                     `json:\"clr,omitempty\"`\n\tIAT          []IATEntry                  `json:\"iat,omitempty\"`\n\tAnomalies    []string                    `json:\"anomalies,omitempty\"`\n\tHeader       []byte\n\tdata         mmap.MMap\n\tFileInfo\n\tsize          uint32\n\tOverlayOffset int64\n\tf             *os.File\n\topts          *Options\n\tlogger        *log.Helper\n}\n```\n\n### PE Header\n\nAs 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.\n\n```go\nfmt.Printf(\"Magic is: 0x%x\\n\", pe.DOSHeader.Magic)\nfmt.Printf(\"Signature is: 0x%x\\n\", pe.NtHeader.Signature)\nfmt.Printf(\"Machine is: 0x%x, Meaning: %s\\n\", pe.NtHeader.FileHeader.Machine, pe.NtHeader.FileHeader.Machine.String())\n```\n\nOutput:\n```\nMagic is: 0x5a4d\nSignature is: 0x4550\nMachine is: 0x8664, Meaning: x64\n```\n\n### Rich Header\n\nExample:\n```go\nrichHeader, _ := json.Marshal(pe.RichHeader)\nfmt.Print(prettyPrint(richHeader))\n```\n\nOutput:\n```json\n{\n    \"XorKey\": 2796214951,\n    \"CompIDs\": [\n        {\n            \"MinorCV\": 27412,\n            \"ProdID\": 257,\n            \"Count\": 4,\n            \"Unmasked\": 16870164\n        },\n        {\n            \"MinorCV\": 30729,\n            \"ProdID\": 147,\n            \"Count\": 193,\n            \"Unmasked\": 9664521\n        },\n        {\n            \"MinorCV\": 0,\n            \"ProdID\": 1,\n            \"Count\": 1325,\n            \"Unmasked\": 65536\n        },\n        {\n            \"MinorCV\": 27412,\n            \"ProdID\": 260,\n            \"Count\": 9,\n            \"Unmasked\": 17066772\n        },\n        {\n            \"MinorCV\": 27412,\n            \"ProdID\": 259,\n            \"Count\": 3,\n            \"Unmasked\": 17001236\n        },\n        {\n            \"MinorCV\": 27412,\n            \"ProdID\": 256,\n            \"Count\": 1,\n            \"Unmasked\": 16804628\n        },\n        {\n            \"MinorCV\": 27412,\n            \"ProdID\": 269,\n            \"Count\": 209,\n            \"Unmasked\": 17656596\n        },\n        {\n            \"MinorCV\": 27412,\n            \"ProdID\": 255,\n            \"Count\": 1,\n            \"Unmasked\": 16739092\n        },\n        {\n            \"MinorCV\": 27412,\n            \"ProdID\": 258,\n            \"Count\": 1,\n            \"Unmasked\": 16935700\n        }\n    ],\n    \"DansOffset\": 128,\n    \"Raw\": \"47vE9afaqqan2qqmp9qqprOxq6ej2qqmrqI5pmbaqqan2qumit+qprOxrqeu2qqms7Gpp6TaqqazsaqnptqqprOxp6d22qqms7FVpqbaqqazsainptqqplJpY2in2qqm\"\n}\n\n```\n\n### Iterating over sections\n\n```go\nfor _, sec := range pe.Sections {\n    fmt.Printf(\"Section Name : %s\\n\", sec.NameString())\n    fmt.Printf(\"Section VirtualSize : %x\\n\", sec.Header.VirtualSize)\n    fmt.Printf(\"Section Flags : %x, Meaning: %v\\n\\n\",\n        sec.Header.Characteristics, sec.PrettySectionFlags())\n}\n```\n\nOutput:\n\n```\nSection Name : .text\nSection VirtualSize : 2ea58\nSection Flags : 60500060, Meaning: [Align8Bytes Readable Align16Bytes Executable Contains Code Initialized Data Align1Bytes]\n\nSection Name : .data\nSection VirtualSize : 58\nSection Flags : c0500040, Meaning: [Readable Initialized Data Writable Align1Bytes Align16Bytes Align8Bytes]\n\nSection Name : .rdata\nSection VirtualSize : 18d0\nSection Flags : 40600040, Meaning: [Align2Bytes Align8Bytes Readable Initialized Data Align32Bytes]\n\n...\n```\n\n## Roadmap\n\n- imports MS-styled names demangling\n- PE: VB5 and VB6 typical structures: project info, DLLCall-imports, referenced modules, object table\n\n## Fuzz Testing\n\nTo validate the parser we use the [go-fuzz](https://github.com/dvyukov/go-fuzz) and a corpus of known malformed and tricky PE files from [corkami](https://github.com/corkami/pocs/tree/master/PE).\n\n## Projects Using This Library\n\n  <a href=\"https://www.fibratus.io\" >\n    <img src=\"https://github.com/rabbitstack/fibratus/raw/master/logo.png\" alt=\"Fibratus\" width=\"50px\">\n  </a>\n\n[Fibratus](https://github.com/rabbitstack/fibratus) A modern tool for Windows kernel exploration and tracing with a focus on security.\n\n## References\n\n- [Peering Inside the PE: A Tour of the Win32 Portable Executable File Format by Matt Pietrek](http://bytepointer.com/resources/pietrek_peering_inside_pe.htm)\n- [An In-Depth Look into the Win32 Portable Executable File Format - Part 1 by Matt Pietrek](http://www.delphibasics.info/home/delphibasicsarticles/anin-depthlookintothewin32portableexecutablefileformat-part1)\n- [An In-Depth Look into the Win32 Portable Executable File Format - Part 2 by Matt Pietrek](http://www.delphibasics.info/home/delphibasicsarticles/anin-depthlookintothewin32portableexecutablefileformat-part2)\n- [Portable Executable File Format](https://blog.kowalczyk.info/articles/pefileformat.html)\n- [PE Format MSDN spec](https://docs.microsoft.com/en-us/windows/win32/debug/pe-format)\n- [DotNET format](https://www.ntcore.com/files/dotnetformat.htm)\n- [BlackHat 2011 - CONSTANT INSECURITY: (PECOFF) Portable Executable FIle Format](https://www.youtube.com/watch?v=uoQL3CE24ls)\n"
  },
  {
    "path": "anomaly.go",
    "content": "// Copyright 2021 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n\t\"time\"\n)\n\n// Anomalies found in a PE\nvar (\n\n\t// AnoPEHeaderOverlapDOSHeader is reported when the PE headers overlaps with the DOS header.\n\tAnoPEHeaderOverlapDOSHeader = \"PE header overlaps with DOS header\"\n\n\t// AnoPETimeStampNull is reported when the file header timestamp is 0.\n\tAnoPETimeStampNull = \"file header timestamp set to 0\"\n\n\t// AnoPETimeStampFuture is reported when the file header timestamp is more\n\t// than one day ahead of the current date timestamp.\n\tAnoPETimeStampFuture = \"file header timestamp set to 0\"\n\n\t// NumberOfSections is reported when number of sections is larger or equal than 10.\n\tAnoNumberOfSections10Plus = \"number of sections is 10+\"\n\n\t// AnoNumberOfSectionsNull is reported when sections count's is 0.\n\tAnoNumberOfSectionsNull = \"number of sections is 0\"\n\n\t// AnoSizeOfOptionalHeaderNull is reported when size of optional header is 0.\n\tAnoSizeOfOptionalHeaderNull = \"size of optional header is 0\"\n\n\t// AnoUncommonSizeOfOptionalHeader32 is reported when size of optional\n\t// header for PE32 is larger than 0xE0.\n\tAnoUncommonSizeOfOptionalHeader32 = \"size of optional header is larger than 0xE0 (PE32)\"\n\n\t// AnoUncommonSizeOfOptionalHeader64 is reported when size of optional\n\t// header for PE32+ is larger than 0xF0.\n\tAnoUncommonSizeOfOptionalHeader64 = \"size of optional header is larger than 0xF0 (PE32+)\"\n\n\t// AnoAddressOfEntryPointNull is reported when address of entry point is 0.\n\tAnoAddressOfEntryPointNull = \"address of entry point is 0\"\n\n\t// AnoAddressOfEPLessSizeOfHeaders is reported when address of entry point\n\t// is smaller than size of headers, the file cannot run under Windows.\n\tAnoAddressOfEPLessSizeOfHeaders = \"address of entry point is smaller than size of headers, \" +\n\t\t\"the file cannot run under Windows 8\"\n\n\t// AnoImageBaseNull is reported when the image base is null.\n\tAnoImageBaseNull = \"image base is 0\"\n\n\t// AnoDanSMagicOffset is reported when the `DanS` magic offset is different than 0x80.\n\tAnoDanSMagicOffset = \"`DanS` magic offset is different than 0x80\"\n\n\t// ErrInvalidFileAlignment is reported when file alignment is larger than\n\t//  0x200 and not a power of 2.\n\tErrInvalidFileAlignment = \"FileAlignment larger than 0x200 and not a power of 2\"\n\n\t// ErrInvalidSectionAlignment is reported when file alignment is lesser\n\t// than 0x200 and different from section alignment.\n\tErrInvalidSectionAlignment = \"FileAlignment lesser than 0x200 and different from section alignment\"\n\n\t// AnoMajorSubsystemVersion is reported when MajorSubsystemVersion has a\n\t// value different than the standard 3 --> 6.\n\tAnoMajorSubsystemVersion = \"MajorSubsystemVersion is outside 3<-->6 boundary\"\n\n\t// AnonWin32VersionValue is reported when Win32VersionValue is different than 0\n\tAnonWin32VersionValue = \"Win32VersionValue is a reserved field, must be set to zero\"\n\n\t// AnoInvalidPEChecksum is reported when the optional header checksum field\n\t// is different from what it should normally be.\n\tAnoInvalidPEChecksum = \"optional header checksum is invalid\"\n\n\t// AnoNumberOfRvaAndSizes is reported when NumberOfRvaAndSizes is different than 16.\n\tAnoNumberOfRvaAndSizes = \"optional header NumberOfRvaAndSizes != 16\"\n\n\t// AnoReservedDataDirectoryEntry is reported when the last data directory entry is not zero.\n\tAnoReservedDataDirectoryEntry = \"last data directory entry is a reserved field, must be set to zero\"\n\n\t// AnoCOFFSymbolsCount is reported when the number of COFF symbols is absurdly high.\n\tAnoCOFFSymbolsCount = \"COFF symbols count is absurdly high\"\n\n\t// AnoRelocationEntriesCount is reported when the number of relocation entries is absurdly high.\n\tAnoRelocationEntriesCount = \"relocation entries count is absurdly high\"\n)\n\n// GetAnomalies reportes anomalies found in a PE binary.\n// These nomalies does prevent the Windows loader from loading the files but\n// is an interesting features for malware analysis.\nfunc (pe *File) GetAnomalies() error {\n\n\t// ******************** Anomalies in File header ************************\n\t// An application for Windows NT typically has the nine predefined sections\n\t// named: .text, .bss, .rdata, .data, .rsrc, .edata, .idata, .pdata, and\n\t// .debug. Some applications do not need all of these sections, while\n\t// others may define still more sections to suit their specific needs.\n\t// NumberOfSections can be up to 96 under XP.\n\t// NumberOfSections can be up to 65535 under Vista and later.\n\tif pe.NtHeader.FileHeader.NumberOfSections >= 10 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoNumberOfSections10Plus)\n\t}\n\n\t// File header timestamp set to 0.\n\tif pe.NtHeader.FileHeader.TimeDateStamp == 0 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoPETimeStampNull)\n\t}\n\n\t// File header timestamp set to the future.\n\tnow := time.Now()\n\tfuture := uint32(now.Add(24 * time.Hour).Unix())\n\tif pe.NtHeader.FileHeader.TimeDateStamp > future {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoPETimeStampFuture)\n\t}\n\n\t// NumberOfSections can be null with low alignment PEs\n\t// and in this case, the values are just checked but not really used (under XP)\n\tif pe.NtHeader.FileHeader.NumberOfSections == 0 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoNumberOfSectionsNull)\n\t}\n\n\t// SizeOfOptionalHeader is not the size of the optional header, but the delta\n\t// between the top of the Optional header and the start of the section table.\n\t// Thus, it can be null (the section table will overlap the Optional Header,\n\t// or can be null when no sections are present)\n\tif pe.NtHeader.FileHeader.SizeOfOptionalHeader == 0 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoSizeOfOptionalHeaderNull)\n\t}\n\n\t// SizeOfOptionalHeader can be bigger than the file\n\t// (the section table will be in virtual space, full of zeroes), but can't be negative.\n\t// Do some check here.\n\toh32 := ImageOptionalHeader32{}\n\toh64 := ImageOptionalHeader64{}\n\n\t// SizeOfOptionalHeader standard value is 0xE0 for PE32.\n\tif pe.Is32 &&\n\t\tpe.NtHeader.FileHeader.SizeOfOptionalHeader > uint16(binary.Size(oh32)) {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoUncommonSizeOfOptionalHeader32)\n\t}\n\n\t// SizeOfOptionalHeader standard value is 0xF0 for PE32+.\n\tif pe.Is64 &&\n\t\tpe.NtHeader.FileHeader.SizeOfOptionalHeader > uint16(binary.Size(oh64)) {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoUncommonSizeOfOptionalHeader64)\n\t}\n\n\t// ***************** Anomalies in Optional header *********************\n\t// Under Windows 8, AddressOfEntryPoint is not allowed to be smaller than\n\t// SizeOfHeaders, except if it's null.\n\tswitch pe.Is64 {\n\tcase true:\n\t\toh64 = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\tcase false:\n\t\toh32 = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t}\n\n\t// Use oh for fields which are common for both structures.\n\toh := oh32\n\tif oh.AddressOfEntryPoint != 0 && oh.AddressOfEntryPoint < oh.SizeOfHeaders {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoAddressOfEPLessSizeOfHeaders)\n\t}\n\n\t// AddressOfEntryPoint can be null in DLLs: in this case,\n\t// DllMain is just not called. can be null\n\tif oh.AddressOfEntryPoint == 0 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoAddressOfEntryPointNull)\n\t}\n\n\t// ImageBase can be null, under XP.\n\t// In this case, the binary will be relocated to 10000h\n\tif (pe.Is64 && oh64.ImageBase == 0) ||\n\t\t(pe.Is32 && oh32.ImageBase == 0) {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoImageBaseNull)\n\t}\n\n\t// The msdn states that SizeOfImage must be a multiple of the section\n\t// alignment. This is not a requirement though. Adding it as anomaly.\n\t// Todo: raise an anomaly when SectionAlignment is NULL ?\n\tif oh.SectionAlignment != 0 && oh.SizeOfImage%oh.SectionAlignment != 0 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoInvalidSizeOfImage)\n\t}\n\n\t// For DLLs, MajorSubsystemVersion is ignored until Windows 8. It can have\n\t// any value. Under Windows 8, it needs a standard value (3.10 < 6.30).\n\tif oh.MajorSubsystemVersion < 3 || oh.MajorSubsystemVersion > 6 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoMajorSubsystemVersion)\n\t}\n\n\t// Win32VersionValue officially defined as `reserved` and should be null\n\t// if non null, it overrides MajorVersion/MinorVersion/BuildNumber/PlatformId\n\t// OperatingSystem Versions values located in the PEB, after loading.\n\tif oh.Win32VersionValue != 0 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnonWin32VersionValue)\n\t}\n\n\t// Checksums are required for kernel-mode drivers and some system DLLs.\n\t// Otherwise, this field can be 0.\n\tif pe.Checksum() != oh.CheckSum && oh.CheckSum != 0 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoInvalidPEChecksum)\n\t}\n\n\t// This field contains the number of IMAGE_DATA_DIRECTORY entries.\n\t//  This field has been 16 since the earliest releases of Windows NT.\n\tif (pe.Is64 && oh64.NumberOfRvaAndSizes == 0xA) ||\n\t\t(pe.Is32 && oh32.NumberOfRvaAndSizes == 0xA) {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoNumberOfRvaAndSizes)\n\t}\n\n\treturn nil\n}\n\n// addAnomaly appends the given anomaly to the list of anomalies.\nfunc (pe *File) addAnomaly(anomaly string) {\n\tif !stringInSlice(anomaly, pe.Anomalies) {\n\t\tpe.Anomalies = append(pe.Anomalies, anomaly)\n\t}\n}\n"
  },
  {
    "path": "anomaly_test.go",
    "content": "// Copyright 2021 Saferwall. All rights reserved.\r\n// Use of this source code is governed by Apache v2 license\r\n// license that can be found in the LICENSE file.\r\n\r\npackage pe\r\n\r\nimport (\r\n\t\"testing\"\r\n)\r\n\r\nfunc TestGetAnomalies(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin      string\r\n\t\tout []string\r\n\t}{\r\n\t\t{\r\n\t\t\tgetAbsoluteFilePath(\r\n\t\t\t\"test/050708404553416d103652a7ca1f887ab81f533a019a0eeff0e6bb460a202cde\"),\r\n\t\t\t[]string{AnoReservedDataDirectoryEntry},\r\n\t\t},\r\n\t\t{\r\n\t\t\tgetAbsoluteFilePath(\r\n\t\t\t\"test/0585495341e0ffaae1734acb78708ff55cd3612d844672d37226ef63d12652d0\"),\r\n\t\t\t[]string{AnoAddressOfEntryPointNull, AnoMajorSubsystemVersion},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\t\t\tfile, err := New(tt.in, &Options{})\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.GetAnomalies()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"GetAnomalies(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tfor _, ano := range tt.out {\r\n\t\t\t\tif !stringInSlice(ano, file.Anomalies) {\r\n\t\t\t\t\tt.Errorf(\"anomaly(%s) not found in anomalies, got: %v\", ano, file.Anomalies)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t})\r\n\t}\r\n}\r\n"
  },
  {
    "path": "arch.go",
    "content": "// Copyright 2022 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\n// Architecture-specific data. This data directory is not used\n// (set to all zeros) for I386, IA64, or AMD64 architecture.\nfunc (pe *File) parseArchitectureDirectory(rva, size uint32) error {\n\treturn nil\n}\n"
  },
  {
    "path": "boundimports.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n)\n\nconst (\n\t// MaxStringLength represents the maximum length of a string to be retrieved\n\t// from the file. It's there to prevent loading massive amounts of data from\n\t// memory mapped files. Strings longer than 0x100B should be rather rare.\n\tMaxStringLength = uint32(0x100)\n)\n\n// ImageBoundImportDescriptor represents the IMAGE_BOUND_IMPORT_DESCRIPTOR.\ntype ImageBoundImportDescriptor struct {\n\t// TimeDateStamp is just the value from the Exports information of the DLL\n\t// which is being imported from.\n\tTimeDateStamp uint32 `json:\"time_date_stamp\"`\n\t// Offset of the DLL name counted from the beginning of the BOUND_IMPORT table.\n\tOffsetModuleName uint16 `json:\"offset_module_name\"`\n\t// Number of forwards,\n\tNumberOfModuleForwarderRefs uint16 `json:\"number_of_module_forwarder_refs\"`\n\t// Array of zero or more IMAGE_BOUND_FORWARDER_REF follows.\n}\n\n// ImageBoundForwardedRef represents the IMAGE_BOUND_FORWARDER_REF.\ntype ImageBoundForwardedRef struct {\n\tTimeDateStamp    uint32 `json:\"time_date_stamp\"`\n\tOffsetModuleName uint16 `json:\"offset_module_name\"`\n\tReserved         uint16 `json:\"reserved\"`\n}\n\n// BoundImportDescriptorData represents the descriptor in addition to forwarded refs.\ntype BoundImportDescriptorData struct {\n\tStruct        ImageBoundImportDescriptor `json:\"struct\"`\n\tName          string                     `json:\"name\"`\n\tForwardedRefs []BoundForwardedRefData    `json:\"forwarded_refs\"`\n}\n\n// BoundForwardedRefData represents the struct in addition to the dll name.\ntype BoundForwardedRefData struct {\n\tStruct ImageBoundForwardedRef `json:\"struct\"`\n\tName   string                 `json:\"name\"`\n}\n\n// This table is an array of bound import descriptors, each of which describes\n// a DLL this image was bound up with at the time of the image creation.\n// The descriptors also carry the time stamps of the bindings, and if the\n// bindings are up-to-date, the OS loader uses these bindings as a “shortcut”\n// for API import. Otherwise, the loader ignores the bindings and resolves the\n// imported APIs through the Import tables.\nfunc (pe *File) parseBoundImportDirectory(rva, size uint32) (err error) {\n\tvar sectionsAfterOffset []uint32\n\tvar safetyBoundary uint32\n\tvar start = rva\n\n\tfor {\n\t\tbndDesc := ImageBoundImportDescriptor{}\n\t\tbndDescSize := uint32(binary.Size(bndDesc))\n\t\terr = pe.structUnpack(&bndDesc, rva, bndDescSize)\n\t\t// If the RVA is invalid all would blow up. Some EXEs seem to be\n\t\t// specially nasty and have an invalid RVA.\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// If the structure is all zeros, we reached the end of the list.\n\t\tif bndDesc == (ImageBoundImportDescriptor{}) {\n\t\t\tbreak\n\t\t}\n\n\t\trva += bndDescSize\n\t\tsectionsAfterOffset = nil\n\n\t\tfileOffset := pe.GetOffsetFromRva(rva)\n\t\tsection := pe.getSectionByRva(rva)\n\t\tif section == nil {\n\t\t\tsafetyBoundary = pe.size - fileOffset\n\t\t\tfor _, section := range pe.Sections {\n\t\t\t\tif section.Header.PointerToRawData > fileOffset {\n\t\t\t\t\tsectionsAfterOffset = append(\n\t\t\t\t\t\tsectionsAfterOffset, section.Header.PointerToRawData)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(sectionsAfterOffset) > 0 {\n\t\t\t\t// Find the first section starting at a later offset than that\n\t\t\t\t// specified by 'rva'\n\t\t\t\tfirstSectionAfterOffset := Min(sectionsAfterOffset)\n\t\t\t\tsection = pe.getSectionByOffset(firstSectionAfterOffset)\n\t\t\t\tif section != nil {\n\t\t\t\t\tsafetyBoundary = section.Header.PointerToRawData - fileOffset\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tsectionLen := uint32(len(section.Data(0, 0, pe)))\n\t\t\tsafetyBoundary = (section.Header.PointerToRawData + sectionLen) - fileOffset\n\t\t}\n\n\t\tif section == nil {\n\t\t\tpe.logger.Warnf(\"RVA of IMAGE_BOUND_IMPORT_DESCRIPTOR points to an invalid address: 0x%x\", rva)\n\t\t\treturn nil\n\t\t}\n\n\t\tbndFrwdRef := ImageBoundForwardedRef{}\n\t\tbndFrwdRefSize := uint32(binary.Size(bndFrwdRef))\n\t\tcount := min(uint32(bndDesc.NumberOfModuleForwarderRefs), safetyBoundary/bndFrwdRefSize)\n\n\t\tforwarderRefs := make([]BoundForwardedRefData, 0)\n\t\tfor i := uint32(0); i < count; i++ {\n\t\t\terr = pe.structUnpack(&bndFrwdRef, rva, bndFrwdRefSize)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\trva += bndFrwdRefSize\n\n\t\t\toffset := start + uint32(bndFrwdRef.OffsetModuleName)\n\t\t\tDllNameBuff := string(pe.GetStringFromData(0, pe.data[offset:offset+MaxStringLength]))\n\t\t\tDllName := string(DllNameBuff)\n\n\t\t\t// OffsetModuleName points to a DLL name. These shouldn't be too long.\n\t\t\t// Anything longer than a safety length of 128 will be taken to indicate\n\t\t\t// a corrupt entry and abort the processing of these entries.\n\t\t\t// Names shorter than 4 characters will be taken as invalid as well.\n\t\t\tif DllName != \"\" && (len(DllName) > 256 || !IsPrintable(DllName)) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tforwarderRefs = append(forwarderRefs, BoundForwardedRefData{\n\t\t\t\tStruct: bndFrwdRef, Name: DllName})\n\t\t}\n\n\t\toffset := start + uint32(bndDesc.OffsetModuleName)\n\t\tDllNameBuff := pe.GetStringFromData(0, pe.data[offset:offset+MaxStringLength])\n\t\tDllName := string(DllNameBuff)\n\t\tif DllName != \"\" && (len(DllName) > 256 || !IsPrintable(DllName)) {\n\t\t\tbreak\n\t\t}\n\n\t\tpe.BoundImports = append(pe.BoundImports, BoundImportDescriptorData{\n\t\t\tStruct:        bndDesc,\n\t\t\tName:          DllName,\n\t\t\tForwardedRefs: forwarderRefs})\n\t}\n\n\tif len(pe.BoundImports) > 0 {\n\t\tpe.HasBoundImp = true\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "boundimports_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\r\n// Use of this source code is governed by Apache v2 license\r\n// license that can be found in the LICENSE file.\r\n\r\npackage pe\r\n\r\nimport (\r\n\t\"reflect\"\r\n\t\"testing\"\r\n)\r\n\r\ntype TestBoundImportEntry struct {\r\n\tentryCount     int\r\n\tentryIndex     int\r\n\tentry          BoundImportDescriptorData\r\n\terrOutOfBounds error\r\n}\r\n\r\nfunc TestBoundImportDirectory(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout TestBoundImportEntry\r\n\t}{\r\n\t\t{\r\n\t\t\tgetAbsoluteFilePath(\"test/mfc40u.dll\"),\r\n\t\t\tTestBoundImportEntry{\r\n\t\t\t\tentryCount: 4,\r\n\t\t\t\tentryIndex: 0,\r\n\t\t\t\tentry: BoundImportDescriptorData{\r\n\t\t\t\t\tStruct: ImageBoundImportDescriptor{\r\n\t\t\t\t\t\tTimeDateStamp:               0x31CB50F3,\r\n\t\t\t\t\t\tOffsetModuleName:            0x38,\r\n\t\t\t\t\t\tNumberOfModuleForwarderRefs: 0x1,\r\n\t\t\t\t\t},\r\n\t\t\t\t\tName: \"MSVCRT40.dll\",\r\n\t\t\t\t\tForwardedRefs: []BoundForwardedRefData{\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tStruct: ImageBoundForwardedRef{\r\n\t\t\t\t\t\t\t\tTimeDateStamp:    0x3B7DFE0E,\r\n\t\t\t\t\t\t\t\tOffsetModuleName: 0x45,\r\n\t\t\t\t\t\t\t\tReserved:         0x0,\r\n\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t\tName: \"msvcrt.DLL\",\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t\terrOutOfBounds: nil,\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\t// fake bound imports directory\r\n\t\t\tgetAbsoluteFilePath(\"test/0044e1870806c048a7558082d4482d1650dcd3ea73152ed2218a554983130721\"),\r\n\t\t\tTestBoundImportEntry{\r\n\t\t\t\terrOutOfBounds: ErrOutsideBoundary,\r\n\t\t\t},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryBoundImport]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryBoundImport]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseBoundImportDirectory(va, size)\r\n\t\t\tif err != tt.out.errOutOfBounds {\r\n\t\t\t\tt.Fatalf(\"parseBoundImportDirectory(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\t\t\tgot := file.BoundImports\r\n\t\t\tif len(got) != tt.out.entryCount {\r\n\t\t\t\tt.Errorf(\"bound imports entry count assertion failed, got %v, want %v\", len(got), tt.out.entryCount)\r\n\t\t\t}\r\n\r\n\t\t\tif len(file.BoundImports) > 0 {\r\n\t\t\t\tboundImportEntry := file.BoundImports[tt.out.entryIndex]\r\n\t\t\t\tif !reflect.DeepEqual(boundImportEntry, tt.out.entry) {\r\n\t\t\t\t\tt.Errorf(\"bound import entry assertion failed, got %v, want %v\", boundImportEntry, tt.out.entry)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n"
  },
  {
    "path": "cmd/dump.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"strings\"\n\t\"sync\"\n\t\"text/tabwriter\"\n\t\"time\"\n\t\"unicode\"\n\t\"unsafe\"\n\n\tpeparser \"github.com/saferwall/pe\"\n\t\"github.com/saferwall/pe/log\"\n)\n\nvar (\n\twg   sync.WaitGroup\n\tjobs chan string = make(chan string)\n)\n\nfunc loopFilesWorker(cfg config) error {\n\tfor path := range jobs {\n\t\tfiles, err := os.ReadDir(path)\n\t\tif err != nil {\n\t\t\twg.Done()\n\t\t\treturn err\n\t\t}\n\n\t\tfor _, file := range files {\n\t\t\tif !file.IsDir() {\n\t\t\t\tfullpath := filepath.Join(path, file.Name())\n\t\t\t\tparsePE(fullpath, cfg)\n\t\t\t}\n\t\t}\n\t\twg.Done()\n\t}\n\treturn nil\n}\n\nfunc LoopDirsFiles(path string) error {\n\tfiles, err := os.ReadDir(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tgo func() {\n\t\twg.Add(1)\n\t\tjobs <- path\n\t}()\n\tfor _, file := range files {\n\t\tif file.IsDir() {\n\t\t\tLoopDirsFiles(filepath.Join(path, file.Name()))\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc prettyPrint(iface interface{}) string {\n\tvar prettyJSON bytes.Buffer\n\tbuff, _ := json.Marshal(iface)\n\terr := json.Indent(&prettyJSON, buff, \"\", \"\\t\")\n\tif err != nil {\n\t\tlog.Errorf(\"JSON parse error: %v\", err)\n\t\treturn string(buff)\n\t}\n\n\treturn prettyJSON.String()\n}\n\nfunc humanizeTimestamp(ts uint32) string {\n\tunixTimeUTC := time.Unix(int64(ts), 0)\n\treturn unixTimeUTC.String()\n}\n\nfunc hexDump(b []byte) {\n\tvar a [16]byte\n\tn := (len(b) + 15) &^ 15\n\tfor i := 0; i < n; i++ {\n\t\tif i%16 == 0 {\n\t\t\tfmt.Printf(\"%4d\", i)\n\t\t}\n\t\tif i%8 == 0 {\n\t\t\tfmt.Print(\" \")\n\t\t}\n\t\tif i < len(b) {\n\t\t\tfmt.Printf(\" %02X\", b[i])\n\t\t} else {\n\t\t\tfmt.Print(\"   \")\n\t\t}\n\t\tif i >= len(b) {\n\t\t\ta[i%16] = ' '\n\t\t} else if b[i] < 32 || b[i] > 126 {\n\t\t\ta[i%16] = '.'\n\t\t} else {\n\t\t\ta[i%16] = b[i]\n\t\t}\n\t\tif i%16 == 15 {\n\t\t\tfmt.Printf(\"  %s\\n\", string(a[:]))\n\t\t}\n\t}\n}\n\nfunc hexDumpSize(b []byte, size int) {\n\tvar a [16]byte\n\n\t// Append null bytes when length of the buffer\n\t// is smaller than the requested size.\n\tif len(b) < size {\n\t\ttemp := make([]byte, size)\n\t\tcopy(temp, b)\n\t\tb = temp\n\t}\n\n\tn := (size + 15) &^ 15\n\tfor i := 0; i < n; i++ {\n\t\tif i%16 == 0 {\n\t\t\tfmt.Printf(\"%4d\", i)\n\t\t}\n\t\tif i%8 == 0 {\n\t\t\tfmt.Print(\" \")\n\t\t}\n\t\tif i < len(b) {\n\t\t\tfmt.Printf(\" %02X\", b[i])\n\t\t} else {\n\t\t\tfmt.Print(\"   \")\n\t\t}\n\t\tif i >= len(b) {\n\t\t\ta[i%16] = ' '\n\t\t} else if b[i] < 32 || b[i] > 126 {\n\t\t\ta[i%16] = '.'\n\t\t} else {\n\t\t\ta[i%16] = b[i]\n\t\t}\n\t\tif i%16 == 15 {\n\t\t\tfmt.Printf(\"  %s\\n\", string(a[:]))\n\t\t}\n\t}\n}\n\nfunc IntToByteArray(num uint64) []byte {\n\tsize := int(unsafe.Sizeof(num))\n\tarr := make([]byte, size)\n\tfor i := 0; i < size; i++ {\n\t\tbyt := *(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&num)) + uintptr(i)))\n\t\tarr[i] = byt\n\t}\n\treturn arr\n}\n\nfunc sentenceCase(s string) string {\n\tnewString := string(s[0])\n\tfor i, r := range s[1:] {\n\t\tif unicode.IsLower(r) && unicode.IsLetter(r) {\n\t\t\tnewString += string(r)\n\t\t} else {\n\t\t\tif i < len(s)-2 {\n\t\t\t\tnextChar := rune(s[i+2])\n\t\t\t\tpreviousChar := rune(s[i])\n\t\t\t\tif unicode.IsLower(previousChar) && unicode.IsLetter(previousChar) {\n\t\t\t\t\tnewString += \" \" + string(r)\n\t\t\t\t} else {\n\t\t\t\t\tif unicode.IsLower(nextChar) && unicode.IsLetter(nextChar) {\n\t\t\t\t\t\tnewString += \" \" + string(r)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnewString += string(r)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn newString\n}\n\nfunc isDirectory(path string) bool {\n\tfileInfo, err := os.Stat(path)\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn fileInfo.IsDir()\n}\n\nfunc parse(filePath string, cfg config) {\n\n\t// filePath points to a file.\n\tif !isDirectory(filePath) {\n\t\tparsePE(filePath, cfg)\n\n\t} else {\n\t\t// filePath points to a directory,\n\t\t// walk recursively through all files.\n\t\tfileList := []string{}\n\t\tfilepath.Walk(filePath, func(path string, f os.FileInfo, err error) error {\n\t\t\tif !isDirectory(path) {\n\t\t\t\tfileList = append(fileList, path)\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\n\t\tfor _, file := range fileList {\n\t\t\tparsePE(file, cfg)\n\t\t}\n\t}\n}\n\nfunc parsePE(filename string, cfg config) {\n\n\tlogger := log.NewStdLogger(os.Stdout)\n\tlogger = log.NewFilter(logger, log.FilterLevel(log.LevelInfo))\n\tlog := log.NewHelper(logger)\n\n\tlog.Infof(\"parsing filename %s\", filename)\n\n\tdata, _ := os.ReadFile(filename)\n\tpe, err := peparser.NewBytes(data, &peparser.Options{\n\t\tLogger:                logger,\n\t\tDisableCertValidation: false,\n\t\tFast:                  false,\n\t})\n\n\tif err != nil {\n\t\tlog.Infof(\"Error while opening file: %s, reason: %s\", filename, err)\n\t\treturn\n\t}\n\tdefer pe.Close()\n\n\terr = pe.Parse()\n\tif err != nil {\n\t\tif err != peparser.ErrDOSMagicNotFound {\n\t\t\tlog.Infof(\"Error while parsing file: %s, reason: %s\", filename, err)\n\t\t}\n\t\treturn\n\t}\n\n\t// Dump all results to disk in JSON format.\n\t// f, err := os.Create(\"out.json\")\n\t// if err != nil {\n\t// \treturn\n\t// }\n\t// defer f.Close()\n\t// f.WriteString(prettyPrint(pe))\n\n\tif cfg.wantDOSHeader {\n\t\tDOSHeader := pe.DOSHeader\n\t\tmagic := string(IntToByteArray(uint64(DOSHeader.Magic)))\n\t\tsignature := string(IntToByteArray(uint64(pe.NtHeader.Signature)))\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tfmt.Print(\"\\n\\t------[ DOS Header ]------\\n\\n\")\n\t\tfmt.Fprintf(w, \"Magic:\\t 0x%x (%s)\\n\", DOSHeader.Magic, magic)\n\t\tfmt.Fprintf(w, \"Bytes On Last Page Of File:\\t 0x%x\\n\", DOSHeader.BytesOnLastPageOfFile)\n\t\tfmt.Fprintf(w, \"Pages In File:\\t 0x%x\\n\", DOSHeader.PagesInFile)\n\t\tfmt.Fprintf(w, \"Relocations:\\t 0x%x\\n\", DOSHeader.Relocations)\n\t\tfmt.Fprintf(w, \"Size Of Header:\\t 0x%x\\n\", DOSHeader.SizeOfHeader)\n\t\tfmt.Fprintf(w, \"Min Extra Paragraphs Needed:\\t 0x%x\\n\", DOSHeader.MinExtraParagraphsNeeded)\n\t\tfmt.Fprintf(w, \"Max Extra Paragraphs Needed:\\t 0x%x\\n\", DOSHeader.MaxExtraParagraphsNeeded)\n\t\tfmt.Fprintf(w, \"Initial SS:\\t 0x%x\\n\", DOSHeader.InitialSS)\n\t\tfmt.Fprintf(w, \"Initial SP:\\t 0x%x\\n\", DOSHeader.InitialSP)\n\t\tfmt.Fprintf(w, \"Checksum:\\t 0x%x\\n\", DOSHeader.Checksum)\n\t\tfmt.Fprintf(w, \"Initial IP:\\t 0x%x\\n\", DOSHeader.InitialIP)\n\t\tfmt.Fprintf(w, \"Initial CS:\\t 0x%x\\n\", DOSHeader.InitialCS)\n\t\tfmt.Fprintf(w, \"Address Of Relocation Table:\\t 0x%x\\n\", DOSHeader.AddressOfRelocationTable)\n\t\tfmt.Fprintf(w, \"Overlay Number:\\t 0x%x\\n\", DOSHeader.OverlayNumber)\n\t\tfmt.Fprintf(w, \"OEM Identifier:\\t 0x%x\\n\", DOSHeader.OEMIdentifier)\n\t\tfmt.Fprintf(w, \"OEM Information:\\t 0x%x\\n\", DOSHeader.OEMInformation)\n\t\tfmt.Fprintf(w, \"Address Of New EXE Header:\\t 0x%x (%s)\\n\", DOSHeader.AddressOfNewEXEHeader, signature)\n\t\tw.Flush()\n\t}\n\n\tif cfg.wantRichHeader && pe.FileInfo.HasRichHdr {\n\t\trichHeader := pe.RichHeader\n\t\tfmt.Printf(\"\\nRICH HEADER\\n***********\\n\")\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tfmt.Fprintf(w, \"\\t0x%x\\t XOR Key\\n\", richHeader.XORKey)\n\t\tfmt.Fprintf(w, \"\\t0x%x\\t DanS offset\\n\", richHeader.DansOffset)\n\t\tfmt.Fprintf(w, \"\\t0x%x\\t Checksum\\n\\n\", pe.RichHeaderChecksum())\n\t\tfmt.Fprintln(w, \"ProductID\\tMinorCV\\tCount\\tUnmasked\\tMeaning\\tVSVersion\\t\")\n\t\tfor _, compID := range pe.RichHeader.CompIDs {\n\t\t\tfmt.Fprintf(w, \"0x%x\\t0x%x\\t0x%x\\t0x%x\\t%s\\t%s\\t\\n\",\n\t\t\t\tcompID.ProdID, compID.MinorCV, compID.Count, compID.Unmasked,\n\t\t\t\tpeparser.ProdIDtoStr(compID.ProdID), peparser.ProdIDtoVSversion(compID.ProdID))\n\t\t}\n\t\tw.Flush()\n\t\tfmt.Print(\"\\n   ---Raw header dump---\\n\")\n\t\thexDump(richHeader.Raw)\n\t}\n\n\tif cfg.wantNTHeader {\n\t\tntHeader := pe.NtHeader.FileHeader\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tcharacteristics := strings.Join(ntHeader.Characteristics.String(), \" | \")\n\n\t\tfmt.Print(\"\\n\\t------[ File Header ]------\\n\\n\")\n\t\tfmt.Fprintf(w, \"Machine:\\t 0x%x (%s)\\n\", int(ntHeader.Machine), ntHeader.Machine.String())\n\t\tfmt.Fprintf(w, \"Number Of Sections:\\t 0x%x\\n\", ntHeader.NumberOfSections)\n\t\tfmt.Fprintf(w, \"TimeDateStamp:\\t 0x%x (%s)\\n\", ntHeader.TimeDateStamp, humanizeTimestamp(ntHeader.TimeDateStamp))\n\t\tfmt.Fprintf(w, \"Pointer To Symbol Table:\\t 0x%x\\n\", ntHeader.PointerToSymbolTable)\n\t\tfmt.Fprintf(w, \"Number Of Symbols:\\t 0x%x\\n\", ntHeader.NumberOfSymbols)\n\t\tfmt.Fprintf(w, \"Number Of Symbols:\\t 0x%x\\n\", ntHeader.NumberOfSymbols)\n\t\tfmt.Fprintf(w, \"Size Of Optional Header:\\t 0x%x\\n\", ntHeader.SizeOfOptionalHeader)\n\t\tfmt.Fprintf(w, \"Characteristics:\\t 0x%x (%s)\\n\", ntHeader.Characteristics, characteristics)\n\t\tw.Flush()\n\n\t\tfmt.Print(\"\\n\\t------[ Optional Header ]------\\n\\n\")\n\t\tif pe.Is64 {\n\t\t\toh := pe.NtHeader.OptionalHeader.(peparser.ImageOptionalHeader64)\n\t\t\tdllCharacteristics := strings.Join(oh.DllCharacteristics.String(), \" | \")\n\t\t\tfmt.Fprintf(w, \"Magic:\\t 0x%x (%s)\\n\", oh.Magic, pe.PrettyOptionalHeaderMagic())\n\t\t\tfmt.Fprintf(w, \"Major Linker Version:\\t 0x%x\\n\", oh.MajorLinkerVersion)\n\t\t\tfmt.Fprintf(w, \"Minor Linker Version:\\t 0x%x\\n\", oh.MinorLinkerVersion)\n\t\t\tfmt.Fprintf(w, \"Size Of Code:\\t 0x%x (%s)\\n\", oh.SizeOfCode, BytesSize(float64(oh.SizeOfCode)))\n\t\t\tfmt.Fprintf(w, \"Size Of Initialized Data:\\t 0x%x (%s)\\n\", oh.SizeOfInitializedData,\n\t\t\t\tBytesSize(float64(oh.SizeOfInitializedData)))\n\t\t\tfmt.Fprintf(w, \"Size Of Uninitialized Data:\\t 0x%x (%s)\\n\", oh.SizeOfUninitializedData,\n\t\t\t\tBytesSize(float64(oh.SizeOfUninitializedData)))\n\t\t\tfmt.Fprintf(w, \"Address Of Entry Point:\\t 0x%x\\n\", oh.AddressOfEntryPoint)\n\t\t\tfmt.Fprintf(w, \"Base Of Code:\\t 0x%x\\n\", oh.BaseOfCode)\n\t\t\tfmt.Fprintf(w, \"Image Base:\\t 0x%x\\n\", oh.ImageBase)\n\t\t\tfmt.Fprintf(w, \"Section Alignment:\\t 0x%x (%s)\\n\", oh.SectionAlignment,\n\t\t\t\tBytesSize(float64(oh.SectionAlignment)))\n\t\t\tfmt.Fprintf(w, \"File Alignment:\\t 0x%x (%s)\\n\", oh.FileAlignment,\n\t\t\t\tBytesSize(float64(oh.FileAlignment)))\n\t\t\tfmt.Fprintf(w, \"Major OS Version:\\t 0x%x\\n\", oh.MajorOperatingSystemVersion)\n\t\t\tfmt.Fprintf(w, \"Minor OS Version:\\t 0x%x\\n\", oh.MinorOperatingSystemVersion)\n\t\t\tfmt.Fprintf(w, \"Major Image Version:\\t 0x%x\\n\", oh.MajorImageVersion)\n\t\t\tfmt.Fprintf(w, \"Minor Image Version:\\t 0x%x\\n\", oh.MinorImageVersion)\n\t\t\tfmt.Fprintf(w, \"Major Subsystem Version:\\t 0x%x\\n\", oh.MajorSubsystemVersion)\n\t\t\tfmt.Fprintf(w, \"Minor Subsystem Version:\\t 0x%x\\n\", oh.MinorSubsystemVersion)\n\t\t\tfmt.Fprintf(w, \"Win32 Version Value:\\t 0x%x\\n\", oh.Win32VersionValue)\n\t\t\tfmt.Fprintf(w, \"Size Of Image:\\t 0x%x (%s)\\n\", oh.SizeOfImage, BytesSize(float64(oh.SizeOfImage)))\n\t\t\tfmt.Fprintf(w, \"Size Of Headers:\\t 0x%x (%s)\\n\", oh.SizeOfHeaders, BytesSize(float64(oh.SizeOfHeaders)))\n\t\t\tfmt.Fprintf(w, \"Checksum:\\t 0x%x\\n\", oh.CheckSum)\n\t\t\tfmt.Fprintf(w, \"Subsystem:\\t 0x%x (%s)\\n\", uint16(oh.Subsystem), oh.Subsystem.String())\n\t\t\tfmt.Fprintf(w, \"Dll Characteristics:\\t 0x%x (%s)\\n\", uint16(oh.DllCharacteristics), dllCharacteristics)\n\t\t\tfmt.Fprintf(w, \"Size Of Stack Reserve:\\t 0x%x (%s)\\n\", oh.SizeOfStackReserve, BytesSize(float64(oh.SizeOfStackReserve)))\n\t\t\tfmt.Fprintf(w, \"Size Of Stack Commit:\\t 0x%x (%s)\\n\", oh.SizeOfStackCommit, BytesSize(float64(oh.SizeOfStackCommit)))\n\t\t\tfmt.Fprintf(w, \"Size Of Heap Reserve:\\t 0x%x (%s)\\n\", oh.SizeOfHeapReserve, BytesSize(float64(oh.SizeOfHeapReserve)))\n\t\t\tfmt.Fprintf(w, \"Size Of Heap Commit:\\t 0x%x (%s)\\n\", oh.SizeOfHeapCommit, BytesSize(float64(oh.SizeOfHeapCommit)))\n\t\t\tfmt.Fprintf(w, \"Loader Flags:\\t 0x%x\\n\", oh.LoaderFlags)\n\t\t\tfmt.Fprintf(w, \"Number Of RVA And Sizes:\\t 0x%x\\n\", oh.NumberOfRvaAndSizes)\n\t\t\tfmt.Fprintf(w, \"\\n\")\n\t\t\tfor entry := peparser.ImageDirectoryEntry(0); entry < peparser.ImageNumberOfDirectoryEntries; entry++ {\n\t\t\t\trva := oh.DataDirectory[entry].VirtualAddress\n\t\t\t\tsize := oh.DataDirectory[entry].Size\n\t\t\t\tfmt.Fprintf(w, \"%s Table:\\t RVA: 0x%0.8x\\t Size:0x%0.8x\\t\\n\", entry.String(), rva, size)\n\t\t\t}\n\t\t} else {\n\t\t\toh := pe.NtHeader.OptionalHeader.(peparser.ImageOptionalHeader32)\n\t\t\tdllCharacteristics := strings.Join(oh.DllCharacteristics.String(), \" | \")\n\t\t\tfmt.Fprintf(w, \"Magic:\\t 0x%x (%s)\\n\", oh.Magic, pe.PrettyOptionalHeaderMagic())\n\t\t\tfmt.Fprintf(w, \"Major Linker Version:\\t 0x%x\\n\", oh.MajorLinkerVersion)\n\t\t\tfmt.Fprintf(w, \"Minor Linker Version:\\t 0x%x\\n\", oh.MinorLinkerVersion)\n\t\t\tfmt.Fprintf(w, \"Size Of Code:\\t 0x%x (%s)\\n\", oh.SizeOfCode, BytesSize(float64(oh.SizeOfCode)))\n\t\t\tfmt.Fprintf(w, \"Size Of Initialized Data:\\t 0x%x (%s)\\n\", oh.SizeOfInitializedData,\n\t\t\t\tBytesSize(float64(oh.SizeOfInitializedData)))\n\t\t\tfmt.Fprintf(w, \"Size Of Uninitialized Data:\\t 0x%x (%s)\\n\", oh.SizeOfUninitializedData,\n\t\t\t\tBytesSize(float64(oh.SizeOfUninitializedData)))\n\t\t\tfmt.Fprintf(w, \"Address Of Entry Point:\\t 0x%x\\n\", oh.AddressOfEntryPoint)\n\t\t\tfmt.Fprintf(w, \"Base Of Code:\\t 0x%x\\n\", oh.BaseOfCode)\n\t\t\tfmt.Fprintf(w, \"Image Base:\\t 0x%x\\n\", oh.ImageBase)\n\t\t\tfmt.Fprintf(w, \"Section Alignment:\\t 0x%x (%s)\\n\", oh.SectionAlignment,\n\t\t\t\tBytesSize(float64(oh.SectionAlignment)))\n\t\t\tfmt.Fprintf(w, \"File Alignment:\\t 0x%x (%s)\\n\", oh.FileAlignment,\n\t\t\t\tBytesSize(float64(oh.FileAlignment)))\n\t\t\tfmt.Fprintf(w, \"Major OS Version:\\t 0x%x\\n\", oh.MajorOperatingSystemVersion)\n\t\t\tfmt.Fprintf(w, \"Minor OS Version:\\t 0x%x\\n\", oh.MinorOperatingSystemVersion)\n\t\t\tfmt.Fprintf(w, \"Major Image Version:\\t 0x%x\\n\", oh.MajorImageVersion)\n\t\t\tfmt.Fprintf(w, \"Minor Image Version:\\t 0x%x\\n\", oh.MinorImageVersion)\n\t\t\tfmt.Fprintf(w, \"Major Subsystem Version:\\t 0x%x\\n\", oh.MajorSubsystemVersion)\n\t\t\tfmt.Fprintf(w, \"Minor Subsystem Version:\\t 0x%x\\n\", oh.MinorSubsystemVersion)\n\t\t\tfmt.Fprintf(w, \"Win32 Version Value:\\t 0x%x\\n\", oh.Win32VersionValue)\n\t\t\tfmt.Fprintf(w, \"Size Of Image:\\t 0x%x (%s)\\n\", oh.SizeOfImage, BytesSize(float64(oh.SizeOfImage)))\n\t\t\tfmt.Fprintf(w, \"Size Of Headers:\\t 0x%x (%s)\\n\", oh.SizeOfHeaders, BytesSize(float64(oh.SizeOfHeaders)))\n\t\t\tfmt.Fprintf(w, \"Checksum:\\t 0x%x\\n\", oh.CheckSum)\n\t\t\tfmt.Fprintf(w, \"Subsystem:\\t 0x%x (%s)\\n\", uint16(oh.Subsystem), oh.Subsystem.String())\n\t\t\tfmt.Fprintf(w, \"Dll Characteristics:\\t 0x%x (%s)\\n\", uint16(oh.DllCharacteristics), dllCharacteristics)\n\t\t\tfmt.Fprintf(w, \"Size Of Stack Reserve:\\t 0x%x (%s)\\n\", oh.SizeOfStackReserve, BytesSize(float64(oh.SizeOfStackReserve)))\n\t\t\tfmt.Fprintf(w, \"Size Of Stack Commit:\\t 0x%x (%s)\\n\", oh.SizeOfStackCommit, BytesSize(float64(oh.SizeOfStackCommit)))\n\t\t\tfmt.Fprintf(w, \"Size Of Heap Reserve:\\t 0x%x (%s)\\n\", oh.SizeOfHeapReserve, BytesSize(float64(oh.SizeOfHeapReserve)))\n\t\t\tfmt.Fprintf(w, \"Size Of Heap Commit:\\t 0x%x (%s)\\n\", oh.SizeOfHeapCommit, BytesSize(float64(oh.SizeOfHeapCommit)))\n\t\t\tfmt.Fprintf(w, \"Loader Flags:\\t 0x%x\\n\", oh.LoaderFlags)\n\t\t\tfmt.Fprintf(w, \"Number Of RVA And Sizes:\\t 0x%x\\n\", oh.NumberOfRvaAndSizes)\n\t\t\tfmt.Fprintf(w, \"\\n\")\n\t\t\tfor entry := peparser.ImageDirectoryEntry(0); entry < peparser.ImageNumberOfDirectoryEntries; entry++ {\n\t\t\t\trva := oh.DataDirectory[entry].VirtualAddress\n\t\t\t\tsize := oh.DataDirectory[entry].Size\n\t\t\t\tfmt.Fprintf(w, \"%s Table:\\t RVA: 0x%0.8x\\t Size:0x%0.8x\\t\\n\", entry.String(), rva, size)\n\t\t\t}\n\t\t}\n\t\tw.Flush()\n\t}\n\n\tif cfg.wantCOFF && pe.FileInfo.HasCOFF {\n\t\tfmt.Printf(\"\\nCOFF\\n****\\n\")\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tfmt.Fprintln(w, \"Name\\tValue\\tSectionNumber\\tType\\tStorageClass\\tNumberOfAuxSymbols\\t\")\n\t\tfor _, sym := range pe.COFF.SymbolTable {\n\t\t\tsymName, _ := sym.String(pe)\n\t\t\tfmt.Fprintf(w, \"%s\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t\\n\",\n\t\t\t\tsymName, sym.Value, sym.SectionNumber,\n\t\t\t\tsym.Type, sym.StorageClass, sym.NumberOfAuxSymbols)\n\t\t}\n\t\tw.Flush()\n\t}\n\n\tif cfg.wantSections && pe.FileInfo.HasSections {\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tfor i, sec := range pe.Sections {\n\t\t\thdr := sec.Header\n\t\t\tfmt.Printf(\"\\n\\t------[ Section Header #%d ]------\\n\\n\", i)\n\t\t\tfmt.Fprintf(w, \"Name:\\t %v (%s)\\n\", hdr.Name, sec.String())\n\t\t\tfmt.Fprintf(w, \"Virtual Size:\\t 0x%x (%s)\\n\", hdr.VirtualSize,\n\t\t\t\tBytesSize(float64(hdr.VirtualSize)))\n\t\t\tfmt.Fprintf(w, \"Virtual Address:\\t 0x%x\\n\", hdr.VirtualAddress)\n\t\t\tfmt.Fprintf(w, \"Size Of Raw Data Size:\\t 0x%x (%s)\\n\", hdr.SizeOfRawData,\n\t\t\t\tBytesSize(float64(hdr.SizeOfRawData)))\n\t\t\tfmt.Fprintf(w, \"Pointer To Raw Data:\\t 0x%x\\n\", hdr.PointerToRawData)\n\t\t\tfmt.Fprintf(w, \"Pointer To Relocations:\\t 0x%x\\n\", hdr.PointerToRelocations)\n\t\t\tfmt.Fprintf(w, \"Pointer To Line Numbers:\\t 0x%x\\n\", hdr.PointerToLineNumbers)\n\t\t\tfmt.Fprintf(w, \"Number Of Relocations:\\t 0x%x\\n\", hdr.NumberOfRelocations)\n\t\t\tfmt.Fprintf(w, \"Number Of Line Numbers:\\t 0x%x\\n\", hdr.NumberOfLineNumbers)\n\t\t\tfmt.Fprintf(w, \"Characteristics:\\t 0x%x (%s)\\n\", hdr.Characteristics,\n\t\t\t\tstrings.Join(sec.PrettySectionFlags(), \" | \"))\n\t\t\tfmt.Fprintf(w, \"Entropy:\\t %f\\n\", sec.CalculateEntropy(pe))\n\t\t\tw.Flush()\n\n\t\t\tfmt.Fprintf(w, \"\\n\")\n\t\t\thexDumpSize(sec.Data(0, hdr.PointerToRawData, pe), 128)\n\t\t}\n\t}\n\n\tif cfg.wantImport && pe.FileInfo.HasImport {\n\t\tfmt.Printf(\"\\nIMPORTS\\n********\\n\")\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tfor _, imp := range pe.Imports {\n\t\t\tdesc := imp.Descriptor\n\t\t\tfmt.Printf(\"\\n\\t------[ %s ]------\\n\\n\", imp.Name)\n\t\t\tfmt.Fprintf(w, \"Name:\\t 0x%x\\n\", desc.Name)\n\t\t\tfmt.Fprintf(w, \"Original First Thunk:\\t 0x%x\\n\", desc.OriginalFirstThunk)\n\t\t\tfmt.Fprintf(w, \"First Thunk:\\t 0x%x\\n\", desc.FirstThunk)\n\t\t\tfmt.Fprintf(w, \"TimeDateStamp:\\t 0x%x (%s\\n\", desc.TimeDateStamp,\n\t\t\t\thumanizeTimestamp(desc.TimeDateStamp))\n\t\t\tfmt.Fprintf(w, \"Forwarder Chain:\\t 0x%x\\n\", desc.ForwarderChain)\n\t\t\tfmt.Fprintf(w, \"\\n\")\n\t\t\tfmt.Fprintln(w, \"Name\\tThunkRVA\\tThunkValue\\tOriginalThunkRVA\\tOriginalThunkValue\\tHint\\t\")\n\t\t\tfor _, impFunc := range imp.Functions {\n\t\t\t\tfmt.Fprintf(w, \"%s\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t\\n\",\n\t\t\t\t\timpFunc.Name, impFunc.ThunkRVA, impFunc.ThunkValue,\n\t\t\t\t\timpFunc.OriginalThunkRVA, impFunc.OriginalThunkValue, impFunc.Hint)\n\t\t\t}\n\t\t\tw.Flush()\n\n\t\t}\n\t}\n\n\tif cfg.wantExport && pe.FileInfo.HasExport {\n\t\tfmt.Printf(\"\\nEXPORTS\\n********\\n\")\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\n\t\texpDir := pe.Export.Struct\n\t\tfmt.Printf(\"\\n\\t------[ %s ]------\\n\\n\", pe.Export.Name)\n\t\tfmt.Fprintf(w, \"Characteristics:\\t 0x%x\\n\", expDir.Characteristics)\n\t\tfmt.Fprintf(w, \"TimeDateStamp:\\t 0x%x (%s\\n\", expDir.TimeDateStamp,\n\t\t\thumanizeTimestamp(expDir.TimeDateStamp))\n\t\tfmt.Fprintf(w, \"Major Version:\\t 0x%x\\n\", expDir.MajorVersion)\n\t\tfmt.Fprintf(w, \"Minor Version:\\t 0x%x\\n\", expDir.MinorVersion)\n\t\tfmt.Fprintf(w, \"Name:\\t 0x%x\\n\", expDir.Name)\n\t\tfmt.Fprintf(w, \"Base:\\t 0x%x\\n\", expDir.Base)\n\t\tfmt.Fprintf(w, \"Number Of Functions:\\t 0x%x\\n\", expDir.NumberOfFunctions)\n\t\tfmt.Fprintf(w, \"Number Of Names:\\t 0x%x\\n\", expDir.NumberOfNames)\n\t\tfmt.Fprintf(w, \"Address Of Functions:\\t 0x%x\\n\", expDir.AddressOfFunctions)\n\t\tfmt.Fprintf(w, \"Address Of Names:\\t 0x%x\\n\", expDir.AddressOfNames)\n\t\tfmt.Fprintf(w, \"Address Of Name Ordinals:\\t 0x%x\\n\", expDir.AddressOfNameOrdinals)\n\t\tfmt.Fprintf(w, \"\\n\")\n\n\t\tfmt.Fprintln(w, \"Name\\tOrdinal\\tNameRVA\\tFunctionRVA\\tForwardedTo\\t\")\n\t\tfor _, exp := range pe.Export.Functions {\n\t\t\tfmt.Fprintf(w, \"%s\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t%s\\t\\n\",\n\t\t\t\texp.Name, exp.Ordinal, exp.NameRVA, exp.FunctionRVA, exp.ForwarderRVA, exp.Forwarder)\n\t\t}\n\t\tw.Flush()\n\t}\n\n\tif cfg.wantResource && pe.FileInfo.HasResource {\n\t\tvar printRsrcDir func(rsrcDir peparser.ResourceDirectory)\n\t\tpadding := 0\n\n\t\tprintRsrcDataEntry := func(entry peparser.ResourceDataEntry) {\n\t\t\tpadding++\n\t\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, padding, ' ', 0)\n\t\t\timgRsrcDataEntry := entry.Struct\n\t\t\tfmt.Fprintf(w, \"\\n\\t\\u27A1 Resource Data Entry\\n\\t\")\n\t\t\tfmt.Fprintf(w, \"|- Offset To Data: 0x%x\\n\\t\", imgRsrcDataEntry.OffsetToData)\n\t\t\tfmt.Fprintf(w, \"|- Size: 0x%x\\n\\t\", imgRsrcDataEntry.Size)\n\t\t\tfmt.Fprintf(w, \"|- Code Page: 0x%x\\n\\t\", imgRsrcDataEntry.CodePage)\n\t\t\tfmt.Fprintf(w, \"|- Reserved: 0x%x\\n\\t\", imgRsrcDataEntry.Reserved)\n\t\t\tfmt.Fprintf(w, \"|- Language: %d (%s)\\n\\t\", entry.Lang, entry.Lang.String())\n\t\t\tfmt.Fprintf(w, \"|- Sub-language: %s\\n\\t\", peparser.PrettyResourceLang(entry.Lang, int(entry.SubLang)))\n\t\t\tw.Flush()\n\t\t\tpadding--\n\t\t}\n\n\t\tprintRsrcDir = func(rsrcDir peparser.ResourceDirectory) {\n\t\t\tpadding++\n\t\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, padding, ' ', 0)\n\t\t\timgRsrcDir := rsrcDir.Struct\n\t\t\tfmt.Fprintf(w, \"\\n\\t\\u27A1 Resource Directory\\n\\t\")\n\t\t\tfmt.Fprintf(w, \"|- Characteristics: 0x%x\\n\\t\", imgRsrcDir.Characteristics)\n\t\t\tfmt.Fprintf(w, \"|- TimeDateStamp: 0x%x\\n\\t\", imgRsrcDir.TimeDateStamp)\n\t\t\tfmt.Fprintf(w, \"|- Major Version: 0x%x\\n\\t\", imgRsrcDir.MajorVersion)\n\t\t\tfmt.Fprintf(w, \"|- Minor Version: 0x%x\\n\\t\", imgRsrcDir.MinorVersion)\n\t\t\tfmt.Fprintf(w, \"|- Number Of Named Entries: 0x%x\\n\\t\", imgRsrcDir.NumberOfNamedEntries)\n\t\t\tfmt.Fprintf(w, \"|- Number Of ID Entries: 0x%x\\n\\t\", imgRsrcDir.NumberOfIDEntries)\n\t\t\tfmt.Fprintf(w, \"|----------------------------------\\n\\t\")\n\t\t\tpadding++\n\t\t\tw.Flush()\n\t\t\tw = tabwriter.NewWriter(os.Stdout, 1, 1, padding, ' ', 0)\n\t\t\tfor i, entry := range rsrcDir.Entries {\n\t\t\t\tfmt.Fprintf(w, \"\\t|- \\u27A1 Resource Directory Entry %d, ID: %d\", i+1, entry.ID)\n\n\t\t\t\t// Print the interpretation of a resource ID only in root node.\n\t\t\t\tif padding == 2 {\n\t\t\t\t\tif entry.ID <= peparser.RTManifest {\n\t\t\t\t\t\tfmt.Fprintf(w, \" (%s)\", peparser.ResourceType(entry.ID).String())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfmt.Fprintf(w, \"\\n\\t|- Name: 0x%x\\n\\t\", entry.Struct.Name)\n\t\t\t\tif entry.Name != \"\" {\n\t\t\t\t\tfmt.Fprintf(w, \" (%s)\", entry.Name)\n\t\t\t\t}\n\t\t\t\tfmt.Fprintf(w, \"|- Offset To Data: 0x%x\\t\", entry.Struct.OffsetToData)\n\t\t\t\tfmt.Fprintf(w, \"\\n\\t|----------------------------------\\t\")\n\t\t\t\tw.Flush()\n\t\t\t\tif entry.IsResourceDir {\n\t\t\t\t\tprintRsrcDir(entry.Directory)\n\t\t\t\t} else {\n\t\t\t\t\tprintRsrcDataEntry(entry.Data)\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tpadding -= 2\n\n\t\t}\n\n\t\tfmt.Printf(\"\\nRESOURCES\\n**********\\n\")\n\t\tprintRsrcDir(pe.Resources)\n\n\t\tversionInfo, err := pe.ParseVersionResources()\n\t\tif err != nil {\n\t\t\tlog.Errorf(\"failed to parse version resources: %v\", err)\n\t\t} else {\n\t\t\tfmt.Printf(\"\\nVersion Info: %v\", prettyPrint(versionInfo))\n\t\t}\n\t}\n\n\tif cfg.wantException && pe.FileInfo.HasException {\n\t\tfmt.Printf(\"\\nEXCEPTIONS\\n***********\\n\")\n\t\tfor _, exception := range pe.Exceptions {\n\t\t\tentry := exception.RuntimeFunction\n\t\t\tfmt.Printf(\"\\n\\u27A1 BeginAddress: 0x%x EndAddress:0x%x UnwindInfoAddress:0x%x\\t\\n\",\n\t\t\t\tentry.BeginAddress, entry.EndAddress, entry.UnwindInfoAddress)\n\n\t\t\tui := exception.UnwindInfo\n\t\t\thandlerFlags := peparser.PrettyUnwindInfoHandlerFlags(ui.Flags)\n\t\t\tprettyFlags := strings.Join(handlerFlags, \",\")\n\t\t\tfmt.Printf(\"|- Version: 0x%x\\n\", ui.Version)\n\t\t\tfmt.Printf(\"|- Flags: 0x%x\", ui.Flags)\n\t\t\tif ui.Flags == 0 {\n\t\t\t\tfmt.Print(\" (None)\\n\")\n\t\t\t} else {\n\t\t\t\tfmt.Printf(\" (%s)\\n\", prettyFlags)\n\t\t\t}\n\n\t\t\tfmt.Printf(\"|- Size Of Prolog: 0x%x\\n\", ui.SizeOfProlog)\n\t\t\tfmt.Printf(\"|- Count Of Codes: 0x%x\\n\", ui.CountOfCodes)\n\t\t\tfmt.Printf(\"|- Exception Handler: 0x%x\\n\", ui.ExceptionHandler)\n\t\t\tfmt.Print(\"|- Unwind codes:\\n\")\n\t\t\tfor _, uc := range ui.UnwindCodes {\n\t\t\t\tfmt.Printf(\"|-  * %.2x: %s, %s\\n\", uc.CodeOffset,\n\t\t\t\t\tuc.UnwindOp.String(), uc.Operand)\n\t\t\t}\n\t\t}\n\t}\n\n\tif cfg.wantCertificate && pe.FileInfo.HasCertificate {\n\t\tfmt.Printf(\"\\nSECURITY\\n*********\\n\")\n\n\t\tcert := pe.Certificates\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tfmt.Fprintln(w, \"Length\\tRevision\\tCertificateType\\t\")\n\t\tfmt.Fprintf(w, \"0x%x\\t0x%x\\t0x%x\\t\\n\", cert.Header.Length, cert.Header.Revision,\n\t\t\tcert.Header.CertificateType)\n\t\tw.Flush()\n\t\tfmt.Print(\"\\n   ---Raw Certificate dump---\\n\")\n\t\thexDump(cert.Raw)\n\t\tfor _, cert := range cert.Certificates {\n\t\t\tfmt.Print(\"\\n---Certificate ---\\n\\n\")\n\t\t\tfmt.Fprintf(w, \"Issuer Name:\\t %s\\n\", cert.Info.Issuer)\n\t\t\tfmt.Fprintf(w, \"Subject Name:\\t %s\\n\", cert.Info.Subject)\n\t\t\tfmt.Fprintf(w, \"Serial Number:\\t %x\\n\", cert.Info.SerialNumber)\n\t\t\tfmt.Fprintf(w, \"Validity From:\\t %s to %s\\n\", cert.Info.NotBefore.String(), cert.Info.NotAfter.String())\n\t\t\tfmt.Fprintf(w, \"Signature Algorithm:\\t %s\\n\", cert.Info.SignatureAlgorithm.String())\n\t\t\tfmt.Fprintf(w, \"PublicKey Algorithm:\\t %s\\n\", cert.Info.PublicKeyAlgorithm.String())\n\t\t\tfmt.Fprintf(w, \"Certificate valid:\\t %v\\n\", cert.Verified)\n\t\t\tfmt.Fprintf(w, \"Signature valid:\\t %v\\n\", cert.SignatureValid)\n\t\t\tw.Flush()\n\t\t}\n\n\t\t// Calculate the PE authentihash.\n\t\tpe.Authentihash()\n\t}\n\n\tif cfg.wantReloc && pe.FileInfo.HasReloc {\n\t\tfmt.Printf(\"\\nRELOCATIONS\\n***********\\n\")\n\t\tfor _, reloc := range pe.Relocations {\n\t\t\tfmt.Printf(\"\\n\\u27A1 Virtual Address: 0x%x | Size Of Block:0x%x | Entries Count:0x%x\\t\\n\",\n\t\t\t\treloc.Data.VirtualAddress, reloc.Data.SizeOfBlock, len(reloc.Entries))\n\t\t\tfmt.Print(\"|- Entries:\\n\")\n\t\t\tfor _, relocEntry := range reloc.Entries {\n\t\t\t\tfmt.Printf(\"|-  Data: 0x%x |  Offset: 0x%x | Type:0x%x (%s)\\n\", relocEntry.Data,\n\t\t\t\t\trelocEntry.Offset, relocEntry.Type, relocEntry.Type.String(pe))\n\t\t\t}\n\t\t}\n\t}\n\n\tif cfg.wantDebug && pe.FileInfo.HasDebug {\n\t\tfmt.Printf(\"\\nDEBUGS\\n*******\\n\")\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tfor _, debug := range pe.Debugs {\n\t\t\timgDbgDir := debug.Struct\n\t\t\tfmt.Fprintf(w, \"\\n\\t------[ %s ]------\\n\", debug.Type)\n\t\t\tfmt.Fprintf(w, \"Characteristics:\\t 0x%x\\n\", imgDbgDir.Characteristics)\n\t\t\tfmt.Fprintf(w, \"TimeDateStamp:\\t 0x%x (%s)\\n\", imgDbgDir.TimeDateStamp,\n\t\t\t\thumanizeTimestamp(imgDbgDir.TimeDateStamp))\n\t\t\tfmt.Fprintf(w, \"Major Version:\\t 0x%x\\n\", imgDbgDir.MajorVersion)\n\t\t\tfmt.Fprintf(w, \"Minor Version:\\t 0x%x\\n\", imgDbgDir.MinorVersion)\n\t\t\tfmt.Fprintf(w, \"Type:\\t 0x%x\\n\", imgDbgDir.Type)\n\t\t\tfmt.Fprintf(w, \"Size Of Data:\\t 0x%x (%s)\\n\", imgDbgDir.SizeOfData,\n\t\t\t\tBytesSize(float64(imgDbgDir.SizeOfData)))\n\t\t\tfmt.Fprintf(w, \"Address Of Raw Data:\\t 0x%x\\n\", imgDbgDir.AddressOfRawData)\n\t\t\tfmt.Fprintf(w, \"Pointer To Raw Data:\\t 0x%x\\n\", imgDbgDir.PointerToRawData)\n\t\t\tfmt.Fprintf(w, \"\\n\")\n\t\t\tswitch imgDbgDir.Type {\n\t\t\tcase peparser.ImageDebugTypeCodeView:\n\t\t\t\tdebugSignature, err := pe.ReadUint32(imgDbgDir.PointerToRawData)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tswitch debugSignature {\n\t\t\t\tcase peparser.CVSignatureRSDS:\n\t\t\t\t\tpdb := debug.Info.(peparser.CVInfoPDB70)\n\t\t\t\t\tfmt.Fprintf(w, \"CV Signature:\\t 0x%x (%s)\\n\", pdb.CVSignature,\n\t\t\t\t\t\tpdb.CVSignature.String())\n\t\t\t\t\tfmt.Fprintf(w, \"Signature:\\t %s\\n\", pdb.Signature.String())\n\t\t\t\t\tfmt.Fprintf(w, \"Age:\\t 0x%x\\n\", pdb.Age)\n\t\t\t\t\tfmt.Fprintf(w, \"PDB FileName:\\t %s\\n\", pdb.PDBFileName)\n\t\t\t\tcase peparser.CVSignatureNB10:\n\t\t\t\t\tpdb := debug.Info.(peparser.CVInfoPDB20)\n\t\t\t\t\tfmt.Fprintf(w, \"CV Header Signature:\\t 0x%x (%s)\\n\",\n\t\t\t\t\t\tpdb.CVHeader.Signature, pdb.CVHeader.Signature.String())\n\t\t\t\t\tfmt.Fprintf(w, \"CV Header Offset:\\t 0x%x\\n\", pdb.CVHeader.Offset)\n\t\t\t\t\tfmt.Fprintf(w, \"Signature:\\t 0x%x (%s)\\n\", pdb.Signature,\n\t\t\t\t\t\thumanizeTimestamp(pdb.Signature))\n\t\t\t\t\tfmt.Fprintf(w, \"Age:\\t 0x%x\\n\", pdb.Age)\n\t\t\t\t\tfmt.Fprintf(w, \"PDBFileName:\\t %s\\n\", pdb.PDBFileName)\n\t\t\t\t}\n\t\t\tcase peparser.ImageDebugTypePOGO:\n\t\t\t\tpogo := debug.Info.(peparser.POGO)\n\t\t\t\tif len(pogo.Entries) > 0 {\n\t\t\t\t\tfmt.Fprintf(w, \"Signature:\\t 0x%x (%s)\\n\\n\", pogo.Signature,\n\t\t\t\t\t\tpogo.Signature.String())\n\t\t\t\t\tfmt.Fprintln(w, \"RVA\\tSize\\tName\\tDescription\\t\")\n\t\t\t\t\tfmt.Fprintln(w, \"---\\t----\\t----\\t-----------\\t\")\n\t\t\t\t\tfor _, pogoEntry := range pogo.Entries {\n\t\t\t\t\t\tfmt.Fprintf(w, \"0x%x\\t0x%x\\t%s\\t%s\\t\\n\", pogoEntry.RVA,\n\t\t\t\t\t\t\tpogoEntry.Size, pogoEntry.Name,\n\t\t\t\t\t\t\tpeparser.SectionAttributeDescription(pogoEntry.Name))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase peparser.ImageDebugTypeRepro:\n\t\t\t\trepro := debug.Info.(peparser.REPRO)\n\t\t\t\tfmt.Fprintf(w, \"Hash:\\t %x\\n\", repro.Hash)\n\t\t\t\tfmt.Fprintf(w, \"Size:\\t 0x%x (%s)\\n\", repro.Size, BytesSize(float64(repro.Size)))\n\t\t\tcase peparser.ImageDebugTypeExDllCharacteristics:\n\t\t\t\texDllCharacteristics := debug.Info.(peparser.DllCharacteristicsExType)\n\t\t\t\tfmt.Fprintf(w, \"Value:\\t %d (%s)\\n\", exDllCharacteristics,\n\t\t\t\t\texDllCharacteristics.String())\n\t\t\tcase peparser.ImageDebugTypeVCFeature:\n\t\t\t\tVCFeature := debug.Info.(peparser.VCFeature)\n\t\t\t\tfmt.Fprintf(w, \"Pre VC11:\\t 0x%x\\n\", VCFeature.PreVC11)\n\t\t\t\tfmt.Fprintf(w, \"C/C++:\\t 0x%x\\n\", VCFeature.CCpp)\n\t\t\t\tfmt.Fprintf(w, \"/GS:\\t 0x%x\\n\", VCFeature.Gs)\n\t\t\t\tfmt.Fprintf(w, \"/sdl:\\t 0x%x\\n\", VCFeature.Sdl)\n\t\t\t\tfmt.Fprintf(w, \"GuardN:\\t 0x%x\\n\", VCFeature.GuardN)\n\t\t\tcase peparser.ImageDebugTypeFPO:\n\t\t\t\tfpo := debug.Info.([]peparser.FPOData)\n\t\t\t\tif len(fpo) > 0 {\n\t\t\t\t\tfmt.Fprintln(w, \"OffsetStart\\tProcSize\\tNumLocals\\tParamsSize\\tPrologLength\\tSavedRegsCount\\tHasSEH\\tUseBP\\tReserved\\tFrameType\\t\")\n\t\t\t\t\tfmt.Fprintln(w, \"------\\t------\\t------\\t------\\t------\\t------\\t------\\t------\\t------\\t------\\t\")\n\t\t\t\t\tfor _, fpoData := range fpo {\n\t\t\t\t\t\tfmt.Fprintf(w, \"0x%x\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t%d (%s)\\t\\n\",\n\t\t\t\t\t\t\tfpoData.OffsetStart, fpoData.ProcSize, fpoData.NumLocals,\n\t\t\t\t\t\t\tfpoData.ParamsSize, fpoData.PrologLength,\n\t\t\t\t\t\t\tfpoData.SavedRegsCount, fpoData.HasSEH, fpoData.UseBP,\n\t\t\t\t\t\t\tfpoData.Reserved, fpoData.FrameType, fpoData.FrameType.String())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tw.Flush()\n\t}\n\n\tif cfg.wantBoundImp && pe.FileInfo.HasBoundImp {\n\t\tfmt.Printf(\"\\nBOUND IMPORTS\\n************\\n\")\n\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tfor _, bndImp := range pe.BoundImports {\n\t\t\tfmt.Printf(\"\\n\\t------[ %s ]------\\n\\n\", bndImp.Name)\n\t\t\tfmt.Fprintf(w, \"TimeDateStamp:\\t 0x%x (%s)\\n\", bndImp.Struct.TimeDateStamp,\n\t\t\t\thumanizeTimestamp(bndImp.Struct.TimeDateStamp))\n\t\t\tfmt.Fprintf(w, \"Offset Module Name:\\t 0x%x\\n\", bndImp.Struct.OffsetModuleName)\n\t\t\tfmt.Fprintf(w, \"# Module Forwarder Refs:\\t 0x%x\\n\", bndImp.Struct.NumberOfModuleForwarderRefs)\n\t\t\tfmt.Fprintf(w, \"\\n\")\n\t\t\tif len(bndImp.ForwardedRefs) > 0 {\n\t\t\t\tfmt.Fprintln(w, \"Name\\tTimeDateStamp\\tOffsetModuleName\\tReserved\\t\")\n\t\t\t\tfor _, fr := range bndImp.ForwardedRefs {\n\t\t\t\t\tfmt.Fprintf(w, \"%s\\t0x%x\\t0x%x\\t0x%x\\t\\n\", fr.Name,\n\t\t\t\t\t\tfr.Struct.TimeDateStamp, fr.Struct.OffsetModuleName,\n\t\t\t\t\t\tfr.Struct.Reserved)\n\t\t\t\t}\n\t\t\t}\n\t\t\tw.Flush()\n\t\t}\n\t}\n\n\tif cfg.wantTLS && pe.FileInfo.HasTLS {\n\t\tfmt.Printf(\"\\nTLS\\n*****\\n\\n\")\n\n\t\ttls := pe.TLS\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tif pe.Is64 {\n\t\t\timgTLSDirectory64 := tls.Struct.(peparser.ImageTLSDirectory64)\n\t\t\tfmt.Fprintf(w, \"Start Address Of Raw Data:\\t 0x%x\\n\", imgTLSDirectory64.StartAddressOfRawData)\n\t\t\tfmt.Fprintf(w, \"End Address Of Raw Data:\\t 0x%x\\n\", imgTLSDirectory64.EndAddressOfRawData)\n\t\t\tfmt.Fprintf(w, \"Address Of Index:\\t %x\\n\", imgTLSDirectory64.AddressOfIndex)\n\t\t\tfmt.Fprintf(w, \"Address Of CallBacks:\\t 0x%x\\n\", imgTLSDirectory64.AddressOfCallBacks)\n\t\t\tfmt.Fprintf(w, \"Size Of Zero Fill:\\t 0x%x\\n\", imgTLSDirectory64.SizeOfZeroFill)\n\t\t\tfmt.Fprintf(w, \"Characteristics:\\t 0x%x (%s)\\n\", imgTLSDirectory64.Characteristics,\n\t\t\t\timgTLSDirectory64.Characteristics.String())\n\t\t\tfmt.Fprintf(w, \"Callbacks:\\n\")\n\t\t\tif tls.Callbacks != nil && len(tls.Callbacks.([]uint64)) > 0 {\n\t\t\t\tfor _, callback := range tls.Callbacks.([]uint64) {\n\t\t\t\t\tfmt.Fprintf(w, \"0x%x\\t\\n\", callback)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\timgTLSDirectory32 := tls.Struct.(peparser.ImageTLSDirectory32)\n\t\t\tfmt.Fprintf(w, \"Start Address Of Raw Data:\\t 0x%x\\n\", imgTLSDirectory32.StartAddressOfRawData)\n\t\t\tfmt.Fprintf(w, \"End Address Of Raw Data:\\t 0x%x\\n\", imgTLSDirectory32.EndAddressOfRawData)\n\t\t\tfmt.Fprintf(w, \"Address Of Index:\\t %x\\n\", imgTLSDirectory32.AddressOfIndex)\n\t\t\tfmt.Fprintf(w, \"Address Of CallBacks:\\t 0x%x\\n\", imgTLSDirectory32.AddressOfCallBacks)\n\t\t\tfmt.Fprintf(w, \"Size Of Zero Fill:\\t 0x%x\\n\", imgTLSDirectory32.SizeOfZeroFill)\n\t\t\tfmt.Fprintf(w, \"Characteristics:\\t 0x%x (%s)\\n\", imgTLSDirectory32.Characteristics,\n\t\t\t\timgTLSDirectory32.Characteristics.String())\n\t\t\tfmt.Fprintf(w, \"Callbacks:\\n\")\n\t\t\tif tls.Callbacks != nil && len(tls.Callbacks.([]uint32)) > 0 {\n\t\t\t\tfor _, callback := range tls.Callbacks.([]uint32) {\n\t\t\t\t\tfmt.Fprintf(w, \"0x%x\\t\\n\", callback)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tw.Flush()\n\t}\n\n\tif cfg.wantLoadCfg && pe.FileInfo.HasLoadCFG {\n\t\tfmt.Printf(\"\\nLOAD CONFIG\\n************\\n\\n\")\n\n\t\tloadConfig := pe.LoadConfig\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.TabIndent)\n\t\tv := reflect.ValueOf(loadConfig.Struct)\n\t\ttypeOfS := v.Type()\n\t\timgLoadConfigDirectorySize := v.Field(0).Interface().(uint32)\n\t\ttmp := uint32(0)\n\t\tfor i := 0; i < v.NumField(); i++ {\n\t\t\t// Do not print the fields of the image load config directory structure\n\t\t\t// that does not belong to it.\n\t\t\ttmp += uint32(binary.Size((v.Field(i).Interface())))\n\t\t\tif tmp > imgLoadConfigDirectorySize {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tfmt.Fprintf(w, \"  %s\\t : 0x%v\\n\", sentenceCase(typeOfS.Field(i).Name),\n\t\t\t\tv.Field(i).Interface())\n\t\t}\n\t\tw.Flush()\n\t}\n\n\tif cfg.wantDelayImp && pe.FileInfo.HasDelayImp {\n\t\tfmt.Printf(\"\\nDELAY IMPORTS\\n**************\\n\")\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tfor _, imp := range pe.DelayImports {\n\t\t\tdesc := imp.Descriptor\n\t\t\tfmt.Printf(\"\\n\\t------[ %s ]------\\n\\n\", imp.Name)\n\t\t\tfmt.Fprintf(w, \"Attributes:\\t 0x%x\\n\", desc.Attributes)\n\t\t\tfmt.Fprintf(w, \"Name:\\t 0x%x\\n\", desc.Name)\n\t\t\tfmt.Fprintf(w, \"Module Handle RVA:\\t 0x%x\\n\", desc.ModuleHandleRVA)\n\t\t\tfmt.Fprintf(w, \"Import Address Table RVA:\\t 0x%x\\n\", desc.ImportAddressTableRVA)\n\t\t\tfmt.Fprintf(w, \"Import Name Table RVA:\\t 0x%x\\n\", desc.ImportNameTableRVA)\n\t\t\tfmt.Fprintf(w, \"Bound Import Address Table RVA:\\t 0x%x\\n\", desc.BoundImportAddressTableRVA)\n\t\t\tfmt.Fprintf(w, \"Unload Information Table RVA:\\t 0x%x\\n\", desc.UnloadInformationTableRVA)\n\t\t\tfmt.Fprintf(w, \"TimeDateStamp:\\t 0x%x (%s)\\n\", desc.TimeDateStamp,\n\t\t\t\thumanizeTimestamp(desc.TimeDateStamp))\n\t\t\tfmt.Fprintf(w, \"\\n\")\n\t\t\tfmt.Fprintln(w, \"Name\\tThunkRVA\\tThunkValue\\tOriginalThunkRVA\\tOriginalThunkValue\\tHint\\t\")\n\t\t\tfor _, fn := range imp.Functions {\n\t\t\t\tfmt.Fprintf(w, \"%s\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t0x%x\\t\\n\",\n\t\t\t\t\tfn.Name, fn.ThunkRVA, fn.ThunkValue,\n\t\t\t\t\tfn.OriginalThunkRVA, fn.OriginalThunkValue, fn.Hint)\n\t\t\t}\n\t\t\tw.Flush()\n\t\t}\n\t}\n\n\tif cfg.wantIAT && pe.FileInfo.HasIAT {\n\t\tfmt.Printf(\"\\nIAT\\n****\\n\\n\")\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\t\tfmt.Fprintln(w, \"Index\\tRVA\\tValue\\tMeaning\\t\")\n\t\tfor _, entry := range pe.IAT {\n\t\t\tfmt.Fprintf(w, \"0x%x\\t0x%x\\t%v\\t%s\\t\\n\",\n\t\t\t\tentry.Index, entry.Rva, entry.Value, entry.Meaning)\n\t\t}\n\t\tw.Flush()\n\t}\n\n\tif cfg.wantCLR && pe.FileInfo.HasCLR {\n\t\tfmt.Printf(\"\\nCLR\\n****\\n\")\n\n\t\tfmt.Print(\"\\n\\t------[ CLR Header ]------\\n\\n\")\n\t\tclr := pe.CLR\n\t\tw := tabwriter.NewWriter(os.Stdout, 1, 1, 3, ' ', tabwriter.AlignRight)\n\n\t\tclrHdr := clr.CLRHeader\n\t\tflags := strings.Join(clrHdr.Flags.String(), \" | \")\n\t\tfmt.Fprintf(w, \"Size Of Header:\\t 0x%x\\n\", clrHdr.Cb)\n\t\tfmt.Fprintf(w, \"Major Runtime Version:\\t 0x%x\\n\", clrHdr.MajorRuntimeVersion)\n\t\tfmt.Fprintf(w, \"Minor Runtime Version:\\t 0x%x\\n\", clrHdr.MinorRuntimeVersion)\n\t\tfmt.Fprintf(w, \"MetaData RVA:\\t 0x%x\\n\", clrHdr.MetaData.VirtualAddress)\n\t\tfmt.Fprintf(w, \"MetaData Size:\\t 0x%x\\n\", clrHdr.MetaData.Size)\n\t\tfmt.Fprintf(w, \"Flags:\\t 0x%x (%v)\\n\", clrHdr.Flags, flags)\n\t\tfmt.Fprintf(w, \"EntryPoint RVA or Token:\\t 0x%x\\n\", clrHdr.EntryPointRVAorToken)\n\t\tfmt.Fprintf(w, \"Resources RVA:\\t 0x%x\\n\", clrHdr.Resources.VirtualAddress)\n\t\tfmt.Fprintf(w, \"Resources Size:\\t 0x%x (%s)\\n\", clrHdr.Resources.Size, BytesSize(float64(clrHdr.Resources.Size)))\n\t\tfmt.Fprintf(w, \"Strong Name Signature RVA:\\t 0x%x\\n\", clrHdr.StrongNameSignature.VirtualAddress)\n\t\tfmt.Fprintf(w, \"Strong Name Signature Size:\\t 0x%x (%s)\\n\", clrHdr.StrongNameSignature.Size, BytesSize(float64(clrHdr.StrongNameSignature.Size)))\n\t\tfmt.Fprintf(w, \"Code Manager Table RVA:\\t 0x%x\\n\", clrHdr.CodeManagerTable.VirtualAddress)\n\t\tfmt.Fprintf(w, \"Code Manager Table Size:\\t 0x%x (%s)\\n\", clrHdr.CodeManagerTable.Size, BytesSize(float64(clrHdr.CodeManagerTable.Size)))\n\t\tfmt.Fprintf(w, \"VTable Fixups RVA:\\t 0x%x\\n\", clrHdr.VTableFixups.VirtualAddress)\n\t\tfmt.Fprintf(w, \"VTable Fixups Size:\\t 0x%x (%s)\\n\", clrHdr.VTableFixups.Size, BytesSize(float64(clrHdr.VTableFixups.Size)))\n\t\tfmt.Fprintf(w, \"Export Address Table Jumps RVA:\\t 0x%x\\n\", clrHdr.ExportAddressTableJumps.VirtualAddress)\n\t\tfmt.Fprintf(w, \"Export Address Table Jumps Size:\\t 0x%x (%s)\\n\", clrHdr.ExportAddressTableJumps.Size, BytesSize(float64(clrHdr.ExportAddressTableJumps.Size)))\n\t\tfmt.Fprintf(w, \"Managed Native Header RVA:\\t 0x%x\\n\", clrHdr.ManagedNativeHeader.VirtualAddress)\n\t\tfmt.Fprintf(w, \"Managed Native Header Size:\\t 0x%x (%s)\\n\", clrHdr.ManagedNativeHeader.Size, BytesSize(float64(clrHdr.ManagedNativeHeader.Size)))\n\t\tw.Flush()\n\n\t\tfmt.Print(\"\\n\\t------[ MetaData Header ]------\\n\\n\")\n\t\tmdHdr := clr.MetadataHeader\n\t\tfmt.Fprintf(w, \"Signature:\\t 0x%x (%s)\\n\", mdHdr.Signature,\n\t\t\tstring(IntToByteArray(uint64(mdHdr.Signature))))\n\t\tfmt.Fprintf(w, \"Major Version:\\t 0x%x\\n\", mdHdr.MajorVersion)\n\t\tfmt.Fprintf(w, \"Minor Version:\\t 0x%x\\n\", mdHdr.MinorVersion)\n\t\tfmt.Fprintf(w, \"Extra Data:\\t 0x%x\\n\", mdHdr.ExtraData)\n\t\tfmt.Fprintf(w, \"Version String Length:\\t 0x%x\\n\", mdHdr.VersionString)\n\t\tfmt.Fprintf(w, \"Version String:\\t %s\\n\", mdHdr.Version)\n\t\tfmt.Fprintf(w, \"Flags:\\t 0x%x\\n\", mdHdr.Flags)\n\t\tfmt.Fprintf(w, \"Streams Count:\\t 0x%x\\n\", mdHdr.Streams)\n\t\tw.Flush()\n\n\t\tfmt.Print(\"\\n\\t------[ MetaData Streams ]------\\n\\n\")\n\t\tfor _, sh := range clr.MetadataStreamHeaders {\n\t\t\tfmt.Fprintf(w, \"Stream Name:\\t %s\\n\", sh.Name)\n\t\t\tfmt.Fprintf(w, \"Offset:\\t 0x%x\\n\", sh.Offset)\n\t\t\tfmt.Fprintf(w, \"Size:\\t 0x%x (%s)\\n\", sh.Size, BytesSize(float64(sh.Size)))\n\t\t\tw.Flush()\n\t\t\tfmt.Print(\"\\n   ---Stream Content---\\n\")\n\t\t\thexDumpSize(clr.MetadataStreams[sh.Name], 128)\n\t\t\tfmt.Print(\"\\n\")\n\t\t}\n\n\t\tfmt.Print(\"\\n\\t------[ MetaData Tables Stream Header ]------\\n\\n\")\n\t\tmdTablesStreamHdr := clr.MetadataTablesStreamHeader\n\t\tfmt.Fprintf(w, \"Reserved:\\t 0x%x\\n\", mdTablesStreamHdr.Reserved)\n\t\tfmt.Fprintf(w, \"Major Version:\\t 0x%x\\n\", mdTablesStreamHdr.MajorVersion)\n\t\tfmt.Fprintf(w, \"Minor Version:\\t 0x%x\\n\", mdTablesStreamHdr.MinorVersion)\n\t\tfmt.Fprintf(w, \"Heaps:\\t 0x%x\\n\", mdTablesStreamHdr.Heaps)\n\t\tfmt.Fprintf(w, \"RID:\\t 0x%x\\n\", mdTablesStreamHdr.RID)\n\t\tfmt.Fprintf(w, \"MaskValid:\\t 0x%x\\n\", mdTablesStreamHdr.MaskValid)\n\t\tfmt.Fprintf(w, \"Sorted:\\t 0x%x\\n\", mdTablesStreamHdr.Sorted)\n\t\tw.Flush()\n\n\t\tfmt.Print(\"\\n\\t------[ MetaData Tables ]------\\n\\n\")\n\t\tmdTables := clr.MetadataTables\n\t\tfor _, mdTable := range mdTables {\n\t\t\tfmt.Fprintf(w, \"Name:\\t %s | Items Count:\\t 0x%x\\n\", mdTable.Name, mdTable.CountCols)\n\t\t}\n\t\tw.Flush()\n\n\t\tfor table, modTable := range pe.CLR.MetadataTables {\n\t\t\tswitch table {\n\t\t\tcase peparser.Module:\n\t\t\t\tfmt.Print(\"\\n\\t[Modules]\\n\\t---------\\n\")\n\t\t\t\tmodTableRows := modTable.Content.([]peparser.ModuleTableRow)\n\t\t\t\tfor _, modTableRow := range modTableRows {\n\t\t\t\t\tmodName := pe.GetStringFromData(modTableRow.Name, pe.CLR.MetadataStreams[\"#Strings\"])\n\t\t\t\t\tMvid := pe.GetStringFromData(modTableRow.Mvid, pe.CLR.MetadataStreams[\"#GUID\"])\n\t\t\t\t\tMvidStr := hex.EncodeToString(Mvid)\n\t\t\t\t\tfmt.Fprintf(w, \"Generation:\\t 0x%x\\n\", modTableRow.Generation)\n\t\t\t\t\tfmt.Fprintf(w, \"Name:\\t 0x%x (%s)\\n\", modTableRow.Name, string(modName))\n\t\t\t\t\tfmt.Fprintf(w, \"Mvid:\\t 0x%x (%s)\\n\", modTableRow.Mvid, MvidStr)\n\t\t\t\t\tfmt.Fprintf(w, \"EncID:\\t 0x%x\\n\", modTableRow.EncID)\n\t\t\t\t\tfmt.Fprintf(w, \"EncBaseID:\\t 0x%x\\n\", modTableRow.EncBaseID)\n\t\t\t\t}\n\t\t\t\tw.Flush()\n\n\t\t\t}\n\t\t}\n\t}\n\n\t// Get file type.\n\tif pe.IsEXE() {\n\t\tlog.Debug(\"File is Exe\")\n\t}\n\tif pe.IsDLL() {\n\t\tlog.Debug(\"File is DLL\")\n\t}\n\tif pe.IsDriver() {\n\t\tlog.Debug(\"File is Driver\")\n\t}\n\n\t// Calculate the PE checksum.\n\tpe.Checksum()\n\n\tfmt.Print(\"\\n\")\n}\n"
  },
  {
    "path": "cmd/main.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n)\n\ntype config struct {\n\twantDOSHeader   bool\n\twantRichHeader  bool\n\twantNTHeader    bool\n\twantCOFF        bool\n\twantDataDirs    bool\n\twantSections    bool\n\twantExport      bool\n\twantImport      bool\n\twantResource    bool\n\twantException   bool\n\twantCertificate bool\n\twantReloc       bool\n\twantDebug       bool\n\twantTLS         bool\n\twantLoadCfg     bool\n\twantBoundImp    bool\n\twantIAT         bool\n\twantDelayImp    bool\n\twantCLR         bool\n}\n\nfunc main() {\n\n\tdumpCmd := flag.NewFlagSet(\"dump\", flag.ExitOnError)\n\tdumpDOSHdr := dumpCmd.Bool(\"dosheader\", false, \"Dump DOS header\")\n\tdumpRichHdr := dumpCmd.Bool(\"richheader\", false, \"Dump Rich header\")\n\tdumpNTHdr := dumpCmd.Bool(\"ntheader\", false, \"Dump NT header\")\n\tdumpCOFF := dumpCmd.Bool(\"coff\", false, \"Dump COFF symbols\")\n\tdumpDirs := dumpCmd.Bool(\"directories\", false, \"Dump data directories\")\n\tdumpSections := dumpCmd.Bool(\"sections\", false, \"Dump sections\")\n\tdumpExport := dumpCmd.Bool(\"export\", false, \"Dump export table\")\n\tdumpImport := dumpCmd.Bool(\"import\", false, \"Dump import table\")\n\tdumpResource := dumpCmd.Bool(\"resource\", false, \"Dump resource table\")\n\tdumpException := dumpCmd.Bool(\"exception\", false, \"Dump exception table\")\n\tdumpCertificate := dumpCmd.Bool(\"cert\", false, \"Dump certificate directory\")\n\tdumpReloc := dumpCmd.Bool(\"reloc\", false, \"Dump relocation table\")\n\tdumpDebug := dumpCmd.Bool(\"debug\", false, \"Dump debug infos\")\n\tdumpTLS := dumpCmd.Bool(\"tls\", false, \"Dump TLS\")\n\tdumpLoadCfg := dumpCmd.Bool(\"loadconfig\", false, \"Dump load configuration table\")\n\tdumpBoundImport := dumpCmd.Bool(\"bound\", false, \"Dump bound import table\")\n\tdumpIAT := dumpCmd.Bool(\"iat\", false, \"Dump IAT\")\n\tdumpDelayedImport := dumpCmd.Bool(\"delay\", false, \"Dump delay import descriptor\")\n\tdumpCLR := dumpCmd.Bool(\"clr\", false, \"Dump CLR\")\n\n\tdumpCmd.Usage = func() {\n\t\tfmt.Fprintf(os.Stderr, \"Usage: pedumper dump [flags] <file-or-directory>\\n\\nFlags:\\n\")\n\t\tdumpCmd.PrintDefaults()\n\t}\n\n\tif len(os.Args) < 2 {\n\t\tshowHelp()\n\t}\n\n\tswitch os.Args[1] {\n\n\tcase \"dump\":\n\t\tdumpCmd.Parse(os.Args[2:])\n\n\t\targs := dumpCmd.Args()\n\t\tif len(args) == 0 {\n\t\t\tfmt.Fprintf(os.Stderr, \"Error: missing file or directory path\\n\\n\")\n\t\t\tdumpCmd.Usage()\n\t\t\tos.Exit(1)\n\t\t}\n\t\tfilePath := args[0]\n\n\t\t// If no flags are specified, dump everything.\n\t\tnoFlagsSet := true\n\t\tdumpCmd.Visit(func(f *flag.Flag) { noFlagsSet = false })\n\n\t\tcfg := config{\n\t\t\twantDOSHeader:   *dumpDOSHdr || noFlagsSet,\n\t\t\twantRichHeader:  *dumpRichHdr || noFlagsSet,\n\t\t\twantNTHeader:    *dumpNTHdr || noFlagsSet,\n\t\t\twantCOFF:        *dumpCOFF || noFlagsSet,\n\t\t\twantDataDirs:    *dumpDirs || noFlagsSet,\n\t\t\twantSections:    *dumpSections || noFlagsSet,\n\t\t\twantExport:      *dumpExport || noFlagsSet,\n\t\t\twantImport:      *dumpImport || noFlagsSet,\n\t\t\twantResource:    *dumpResource || noFlagsSet,\n\t\t\twantException:   *dumpException || noFlagsSet,\n\t\t\twantCertificate: *dumpCertificate || noFlagsSet,\n\t\t\twantReloc:       *dumpReloc || noFlagsSet,\n\t\t\twantDebug:       *dumpDebug || noFlagsSet,\n\t\t\twantTLS:         *dumpTLS || noFlagsSet,\n\t\t\twantLoadCfg:     *dumpLoadCfg || noFlagsSet,\n\t\t\twantBoundImp:    *dumpBoundImport || noFlagsSet,\n\t\t\twantIAT:         *dumpIAT || noFlagsSet,\n\t\t\twantDelayImp:    *dumpDelayedImport || noFlagsSet,\n\t\t\twantCLR:         *dumpCLR || noFlagsSet,\n\t\t}\n\n\t\t// Start as many workers you want, default to cpu count -1.\n\t\tnumWorkers := runtime.GOMAXPROCS(runtime.NumCPU() - 1)\n\t\tfor w := 1; w <= numWorkers; w++ {\n\t\t\tgo loopFilesWorker(cfg)\n\t\t}\n\n\t\tif !isDirectory(filePath) {\n\t\t\t// Input path in a single file.\n\t\t\tparsePE(filePath, cfg)\n\t\t} else {\n\t\t\t// Input path in a directory.\n\t\t\tLoopDirsFiles(filePath)\n\t\t\twg.Wait()\n\t\t}\n\n\tcase \"version\":\n\t\tfmt.Println(\"You are using version 1.6.0\")\n\tdefault:\n\t\tshowHelp()\n\t}\n}\n\nfunc showHelp() {\n\tfmt.Print(\n\t\t`\n╔═╗╔═╗  ┌─┐┌─┐┬─┐┌─┐┌─┐┬─┐\n╠═╝║╣   ├─┘├─┤├┬┘└─┐├┤ ├┬┘\n╩  ╚═╝  ┴  ┴ ┴┴└─└─┘└─┘┴└─\n\nA PE-Parser built for speed and malware-analysis in mind.\nBrought to you by Saferwall (c) 2018 MIT\n\nUsage: pedumper <command> [options]\n\nCommands:\n  dump [flags] <file-or-directory>    Parse and dump PE file information\n  version                             Show version information\n\nRun 'pedumper dump -help' for dump flags.\n`)\n\n\tos.Exit(1)\n}\n"
  },
  {
    "path": "cmd/size.go",
    "content": "package main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"strconv\"\r\n\t\"strings\"\r\n)\r\n\r\n// See: http://en.wikipedia.org/wiki/Binary_prefix\r\nconst (\r\n\t// Decimal\r\n\r\n\tKB = 1000\r\n\tMB = 1000 * KB\r\n\tGB = 1000 * MB\r\n\tTB = 1000 * GB\r\n\tPB = 1000 * TB\r\n\r\n\t// Binary\r\n\r\n\tKiB = 1024\r\n\tMiB = 1024 * KiB\r\n\tGiB = 1024 * MiB\r\n\tTiB = 1024 * GiB\r\n\tPiB = 1024 * TiB\r\n)\r\n\r\ntype unitMap map[byte]int64\r\n\r\nvar (\r\n\tdecimalMap = unitMap{'k': KB, 'm': MB, 'g': GB, 't': TB, 'p': PB}\r\n\tbinaryMap  = unitMap{'k': KiB, 'm': MiB, 'g': GiB, 't': TiB, 'p': PiB}\r\n)\r\n\r\nvar (\r\n\tdecimapAbbrs = []string{\"B\", \"kB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"}\r\n\tbinaryAbbrs  = []string{\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\", \"PiB\", \"EiB\", \"ZiB\", \"YiB\"}\r\n)\r\n\r\nfunc getSizeAndUnit(size float64, base float64, _map []string) (float64, string) {\r\n\ti := 0\r\n\tunitsLimit := len(_map) - 1\r\n\tfor size >= base && i < unitsLimit {\r\n\t\tsize = size / base\r\n\t\ti++\r\n\t}\r\n\treturn size, _map[i]\r\n}\r\n\r\n// CustomSize returns a human-readable approximation of a size\r\n// using custom format.\r\nfunc CustomSize(format string, size float64, base float64, _map []string) string {\r\n\tsize, unit := getSizeAndUnit(size, base, _map)\r\n\treturn fmt.Sprintf(format, size, unit)\r\n}\r\n\r\n// HumanSizeWithPrecision allows the size to be in any precision,\r\n// instead of 4 digit precision used in units.HumanSize.\r\nfunc HumanSizeWithPrecision(size float64, precision int) string {\r\n\tsize, unit := getSizeAndUnit(size, 1000.0, decimapAbbrs)\r\n\treturn fmt.Sprintf(\"%.*g%s\", precision, size, unit)\r\n}\r\n\r\n// HumanSize returns a human-readable approximation of a size\r\n// capped at 4 valid numbers (eg. \"2.746 MB\", \"796 KB\").\r\nfunc HumanSize(size float64) string {\r\n\treturn HumanSizeWithPrecision(size, 4)\r\n}\r\n\r\n// BytesSize returns a human-readable size in bytes, kibibytes,\r\n// mebibytes, gibibytes, or tebibytes (eg. \"44kiB\", \"17MiB\").\r\nfunc BytesSize(size float64) string {\r\n\treturn CustomSize(\"%.4g%s\", size, 1024.0, binaryAbbrs)\r\n}\r\n\r\n// FromHumanSize returns an integer from a human-readable specification of a\r\n// size using SI standard (eg. \"44kB\", \"17MB\").\r\nfunc FromHumanSize(size string) (int64, error) {\r\n\treturn parseSize(size, decimalMap)\r\n}\r\n\r\n// RAMInBytes parses a human-readable string representing an amount of RAM\r\n// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and\r\n// returns the number of bytes, or -1 if the string is unparseable.\r\n// Units are case-insensitive, and the 'b' suffix is optional.\r\nfunc RAMInBytes(size string) (int64, error) {\r\n\treturn parseSize(size, binaryMap)\r\n}\r\n\r\n// Parses the human-readable size string into the amount it represents.\r\nfunc parseSize(sizeStr string, uMap unitMap) (int64, error) {\r\n\t// TODO: rewrite to use strings.Cut if there's a space\r\n\t// once Go < 1.18 is deprecated.\r\n\tsep := strings.LastIndexAny(sizeStr, \"01234567890. \")\r\n\tif sep == -1 {\r\n\t\t// There should be at least a digit.\r\n\t\treturn -1, fmt.Errorf(\"invalid size: '%s'\", sizeStr)\r\n\t}\r\n\tvar num, sfx string\r\n\tif sizeStr[sep] != ' ' {\r\n\t\tnum = sizeStr[:sep+1]\r\n\t\tsfx = sizeStr[sep+1:]\r\n\t} else {\r\n\t\t// Omit the space separator.\r\n\t\tnum = sizeStr[:sep]\r\n\t\tsfx = sizeStr[sep+1:]\r\n\t}\r\n\r\n\tsize, err := strconv.ParseFloat(num, 64)\r\n\tif err != nil {\r\n\t\treturn -1, err\r\n\t}\r\n\t// Backward compatibility: reject negative sizes.\r\n\tif size < 0 {\r\n\t\treturn -1, fmt.Errorf(\"invalid size: '%s'\", sizeStr)\r\n\t}\r\n\r\n\tif len(sfx) == 0 {\r\n\t\treturn int64(size), nil\r\n\t}\r\n\r\n\t// Process the suffix.\r\n\r\n\tif len(sfx) > 3 { // Too long.\r\n\t\tgoto badSuffix\r\n\t}\r\n\tsfx = strings.ToLower(sfx)\r\n\t// Trivial case: b suffix.\r\n\tif sfx[0] == 'b' {\r\n\t\tif len(sfx) > 1 { // no extra characters allowed after b.\r\n\t\t\tgoto badSuffix\r\n\t\t}\r\n\t\treturn int64(size), nil\r\n\t}\r\n\t// A suffix from the map.\r\n\tif mul, ok := uMap[sfx[0]]; ok {\r\n\t\tsize *= float64(mul)\r\n\t} else {\r\n\t\tgoto badSuffix\r\n\t}\r\n\r\n\t// The suffix may have extra \"b\" or \"ib\" (e.g. KiB or MB).\r\n\tswitch {\r\n\tcase len(sfx) == 2 && sfx[1] != 'b':\r\n\t\tgoto badSuffix\r\n\tcase len(sfx) == 3 && sfx[1:] != \"ib\":\r\n\t\tgoto badSuffix\r\n\t}\r\n\r\n\treturn int64(size), nil\r\n\r\nbadSuffix:\r\n\treturn -1, fmt.Errorf(\"invalid suffix: '%s'\", sfx)\r\n}\r\n"
  },
  {
    "path": "cmd/size_test.go",
    "content": "package main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"reflect\"\r\n\t\"runtime\"\r\n\t\"strings\"\r\n\t\"testing\"\r\n)\r\n\r\nfunc ExampleBytesSize() {\r\n\tfmt.Println(BytesSize(1024))\r\n\tfmt.Println(BytesSize(1024 * 1024))\r\n\tfmt.Println(BytesSize(1048576))\r\n\tfmt.Println(BytesSize(2 * MiB))\r\n\tfmt.Println(BytesSize(3.42 * GiB))\r\n\tfmt.Println(BytesSize(5.372 * TiB))\r\n\tfmt.Println(BytesSize(2.22 * PiB))\r\n}\r\n\r\nfunc ExampleHumanSize() {\r\n\tfmt.Println(HumanSize(1000))\r\n\tfmt.Println(HumanSize(1024))\r\n\tfmt.Println(HumanSize(1000000))\r\n\tfmt.Println(HumanSize(1048576))\r\n\tfmt.Println(HumanSize(2 * MB))\r\n\tfmt.Println(HumanSize(float64(3.42 * GB)))\r\n\tfmt.Println(HumanSize(float64(5.372 * TB)))\r\n\tfmt.Println(HumanSize(float64(2.22 * PB)))\r\n}\r\n\r\nfunc ExampleFromHumanSize() {\r\n\tfmt.Println(FromHumanSize(\"32\"))\r\n\tfmt.Println(FromHumanSize(\"32b\"))\r\n\tfmt.Println(FromHumanSize(\"32B\"))\r\n\tfmt.Println(FromHumanSize(\"32k\"))\r\n\tfmt.Println(FromHumanSize(\"32K\"))\r\n\tfmt.Println(FromHumanSize(\"32kb\"))\r\n\tfmt.Println(FromHumanSize(\"32Kb\"))\r\n\tfmt.Println(FromHumanSize(\"32Mb\"))\r\n\tfmt.Println(FromHumanSize(\"32Gb\"))\r\n\tfmt.Println(FromHumanSize(\"32Tb\"))\r\n\tfmt.Println(FromHumanSize(\"32Pb\"))\r\n}\r\n\r\nfunc ExampleRAMInBytes() {\r\n\tfmt.Println(RAMInBytes(\"32\"))\r\n\tfmt.Println(RAMInBytes(\"32b\"))\r\n\tfmt.Println(RAMInBytes(\"32B\"))\r\n\tfmt.Println(RAMInBytes(\"32k\"))\r\n\tfmt.Println(RAMInBytes(\"32K\"))\r\n\tfmt.Println(RAMInBytes(\"32kb\"))\r\n\tfmt.Println(RAMInBytes(\"32Kb\"))\r\n\tfmt.Println(RAMInBytes(\"32Mb\"))\r\n\tfmt.Println(RAMInBytes(\"32Gb\"))\r\n\tfmt.Println(RAMInBytes(\"32Tb\"))\r\n\tfmt.Println(RAMInBytes(\"32Pb\"))\r\n\tfmt.Println(RAMInBytes(\"32PB\"))\r\n\tfmt.Println(RAMInBytes(\"32P\"))\r\n}\r\n\r\nfunc TestBytesSize(t *testing.T) {\r\n\tassertEquals(t, \"1KiB\", BytesSize(1024))\r\n\tassertEquals(t, \"1MiB\", BytesSize(1024*1024))\r\n\tassertEquals(t, \"1MiB\", BytesSize(1048576))\r\n\tassertEquals(t, \"2MiB\", BytesSize(2*MiB))\r\n\tassertEquals(t, \"3.42GiB\", BytesSize(3.42*GiB))\r\n\tassertEquals(t, \"5.372TiB\", BytesSize(5.372*TiB))\r\n\tassertEquals(t, \"2.22PiB\", BytesSize(2.22*PiB))\r\n\tassertEquals(t, \"1.049e+06YiB\", BytesSize(KiB*KiB*KiB*KiB*KiB*PiB))\r\n}\r\n\r\nfunc TestHumanSize(t *testing.T) {\r\n\tassertEquals(t, \"1kB\", HumanSize(1000))\r\n\tassertEquals(t, \"1.024kB\", HumanSize(1024))\r\n\tassertEquals(t, \"1MB\", HumanSize(1000000))\r\n\tassertEquals(t, \"1.049MB\", HumanSize(1048576))\r\n\tassertEquals(t, \"2MB\", HumanSize(2*MB))\r\n\tassertEquals(t, \"3.42GB\", HumanSize(float64(3.42*GB)))\r\n\tassertEquals(t, \"5.372TB\", HumanSize(float64(5.372*TB)))\r\n\tassertEquals(t, \"2.22PB\", HumanSize(float64(2.22*PB)))\r\n\tassertEquals(t, \"1e+04YB\", HumanSize(float64(10000000000000*PB)))\r\n}\r\n\r\nfunc TestFromHumanSize(t *testing.T) {\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"0\")\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"0b\")\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"0B\")\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"0 B\")\r\n\tassertSuccessEquals(t, 32, FromHumanSize, \"32\")\r\n\tassertSuccessEquals(t, 32, FromHumanSize, \"32b\")\r\n\tassertSuccessEquals(t, 32, FromHumanSize, \"32B\")\r\n\tassertSuccessEquals(t, 32*KB, FromHumanSize, \"32k\")\r\n\tassertSuccessEquals(t, 32*KB, FromHumanSize, \"32K\")\r\n\tassertSuccessEquals(t, 32*KB, FromHumanSize, \"32kb\")\r\n\tassertSuccessEquals(t, 32*KB, FromHumanSize, \"32Kb\")\r\n\tassertSuccessEquals(t, 32*MB, FromHumanSize, \"32Mb\")\r\n\tassertSuccessEquals(t, 32*GB, FromHumanSize, \"32Gb\")\r\n\tassertSuccessEquals(t, 32*TB, FromHumanSize, \"32Tb\")\r\n\tassertSuccessEquals(t, 32*PB, FromHumanSize, \"32Pb\")\r\n\r\n\tassertSuccessEquals(t, 32.5*KB, FromHumanSize, \"32.5kB\")\r\n\tassertSuccessEquals(t, 32.5*KB, FromHumanSize, \"32.5 kB\")\r\n\tassertSuccessEquals(t, 32, FromHumanSize, \"32.5 B\")\r\n\tassertSuccessEquals(t, 300, FromHumanSize, \"0.3 K\")\r\n\tassertSuccessEquals(t, 300, FromHumanSize, \".3kB\")\r\n\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"0.\")\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"0. \")\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"0.b\")\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"0.B\")\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"-0\")\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"-0b\")\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"-0B\")\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"-0 b\")\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"-0 B\")\r\n\tassertSuccessEquals(t, 32, FromHumanSize, \"32.\")\r\n\tassertSuccessEquals(t, 32, FromHumanSize, \"32.b\")\r\n\tassertSuccessEquals(t, 32, FromHumanSize, \"32.B\")\r\n\tassertSuccessEquals(t, 32, FromHumanSize, \"32. b\")\r\n\tassertSuccessEquals(t, 32, FromHumanSize, \"32. B\")\r\n\r\n\t// We do not tolerate extra leading or trailing spaces\r\n\t// (except for a space after the number and a missing suffix).\r\n\tassertSuccessEquals(t, 0, FromHumanSize, \"0 \")\r\n\r\n\tassertError(t, FromHumanSize, \" 0\")\r\n\tassertError(t, FromHumanSize, \" 0b\")\r\n\tassertError(t, FromHumanSize, \" 0B\")\r\n\tassertError(t, FromHumanSize, \" 0 B\")\r\n\tassertError(t, FromHumanSize, \"0b \")\r\n\tassertError(t, FromHumanSize, \"0B \")\r\n\tassertError(t, FromHumanSize, \"0 B \")\r\n\r\n\tassertError(t, FromHumanSize, \"\")\r\n\tassertError(t, FromHumanSize, \"hello\")\r\n\tassertError(t, FromHumanSize, \".\")\r\n\tassertError(t, FromHumanSize, \". \")\r\n\tassertError(t, FromHumanSize, \" \")\r\n\tassertError(t, FromHumanSize, \"  \")\r\n\tassertError(t, FromHumanSize, \" .\")\r\n\tassertError(t, FromHumanSize, \" . \")\r\n\tassertError(t, FromHumanSize, \"-32\")\r\n\tassertError(t, FromHumanSize, \"-32b\")\r\n\tassertError(t, FromHumanSize, \"-32B\")\r\n\tassertError(t, FromHumanSize, \"-32 b\")\r\n\tassertError(t, FromHumanSize, \"-32 B\")\r\n\tassertError(t, FromHumanSize, \"32b.\")\r\n\tassertError(t, FromHumanSize, \"32B.\")\r\n\tassertError(t, FromHumanSize, \"32 b.\")\r\n\tassertError(t, FromHumanSize, \"32 B.\")\r\n\tassertError(t, FromHumanSize, \"32 bb\")\r\n\tassertError(t, FromHumanSize, \"32 BB\")\r\n\tassertError(t, FromHumanSize, \"32 b b\")\r\n\tassertError(t, FromHumanSize, \"32 B B\")\r\n\tassertError(t, FromHumanSize, \"32  b\")\r\n\tassertError(t, FromHumanSize, \"32  B\")\r\n\tassertError(t, FromHumanSize, \" 32 \")\r\n\tassertError(t, FromHumanSize, \"32m b\")\r\n\tassertError(t, FromHumanSize, \"32bm\")\r\n}\r\n\r\nfunc TestRAMInBytes(t *testing.T) {\r\n\tassertSuccessEquals(t, 32, RAMInBytes, \"32\")\r\n\tassertSuccessEquals(t, 32, RAMInBytes, \"32b\")\r\n\tassertSuccessEquals(t, 32, RAMInBytes, \"32B\")\r\n\tassertSuccessEquals(t, 32*KiB, RAMInBytes, \"32k\")\r\n\tassertSuccessEquals(t, 32*KiB, RAMInBytes, \"32K\")\r\n\tassertSuccessEquals(t, 32*KiB, RAMInBytes, \"32kb\")\r\n\tassertSuccessEquals(t, 32*KiB, RAMInBytes, \"32Kb\")\r\n\tassertSuccessEquals(t, 32*KiB, RAMInBytes, \"32Kib\")\r\n\tassertSuccessEquals(t, 32*KiB, RAMInBytes, \"32KIB\")\r\n\tassertSuccessEquals(t, 32*MiB, RAMInBytes, \"32Mb\")\r\n\tassertSuccessEquals(t, 32*GiB, RAMInBytes, \"32Gb\")\r\n\tassertSuccessEquals(t, 32*TiB, RAMInBytes, \"32Tb\")\r\n\tassertSuccessEquals(t, 32*PiB, RAMInBytes, \"32Pb\")\r\n\tassertSuccessEquals(t, 32*PiB, RAMInBytes, \"32PB\")\r\n\tassertSuccessEquals(t, 32*PiB, RAMInBytes, \"32P\")\r\n\r\n\tassertSuccessEquals(t, 32, RAMInBytes, \"32.3\")\r\n\ttmp := 32.3 * MiB\r\n\tassertSuccessEquals(t, int64(tmp), RAMInBytes, \"32.3 mb\")\r\n\ttmp = 0.3 * MiB\r\n\tassertSuccessEquals(t, int64(tmp), RAMInBytes, \"0.3MB\")\r\n\r\n\tassertError(t, RAMInBytes, \"\")\r\n\tassertError(t, RAMInBytes, \"hello\")\r\n\tassertError(t, RAMInBytes, \"-32\")\r\n\tassertError(t, RAMInBytes, \" 32 \")\r\n\tassertError(t, RAMInBytes, \"32m b\")\r\n\tassertError(t, RAMInBytes, \"32bm\")\r\n}\r\n\r\nfunc BenchmarkParseSize(b *testing.B) {\r\n\tfor i := 0; i < b.N; i++ {\r\n\t\tfor _, s := range []string{\r\n\t\t\t\"\", \"32\", \"32b\", \"32 B\", \"32k\", \"32.5 K\", \"32kb\", \"32 Kb\",\r\n\t\t\t\"32.8Mb\", \"32.9Gb\", \"32.777Tb\", \"32Pb\", \"0.3Mb\", \"-1\",\r\n\t\t} {\r\n\t\t\tFromHumanSize(s)\r\n\t\t\tRAMInBytes(s)\r\n\t\t}\r\n\t}\r\n}\r\n\r\nfunc assertEquals(t *testing.T, expected, actual interface{}) {\r\n\tt.Helper()\r\n\tif expected != actual {\r\n\t\tt.Errorf(\"Expected '%v' but got '%v'\", expected, actual)\r\n\t}\r\n}\r\n\r\n// func that maps to the parse function signatures as testing abstraction\r\ntype parseFn func(string) (int64, error)\r\n\r\n// Define 'String()' for pretty-print\r\nfunc (fn parseFn) String() string {\r\n\tfnName := runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()\r\n\treturn fnName[strings.LastIndex(fnName, \".\")+1:]\r\n}\r\n\r\nfunc assertSuccessEquals(t *testing.T, expected int64, fn parseFn, arg string) {\r\n\tt.Helper()\r\n\tres, err := fn(arg)\r\n\tif err != nil || res != expected {\r\n\t\tt.Errorf(\"%s(\\\"%s\\\") -> expected '%d' but got '%d' with error '%v'\", fn, arg, expected, res, err)\r\n\t}\r\n}\r\n\r\nfunc assertError(t *testing.T, fn parseFn, arg string) {\r\n\tt.Helper()\r\n\tres, err := fn(arg)\r\n\tif err == nil && res != -1 {\r\n\t\tt.Errorf(\"%s(\\\"%s\\\") -> expected error but got '%d'\", fn, arg, res)\r\n\t}\r\n}\r\n"
  },
  {
    "path": "debug.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n)\n\n// The following values are defined for the Type field of the debug directory entry:\nconst (\n\t// An unknown value that is ignored by all tools.\n\tImageDebugTypeUnknown = 0\n\n\t// The COFF debug information (line numbers, symbol table, and string table).\n\t// This type of debug information is also pointed to by fields in the file headers.\n\tImageDebugTypeCOFF = 1\n\n\t// The Visual C++ debug information.\n\tImageDebugTypeCodeView = 2\n\n\t// The frame pointer omission (FPO) information. This information tells the\n\t// debugger how to interpret nonstandard stack frames, which use the EBP\n\t// register for a purpose other than as a frame pointer.\n\tImageDebugTypeFPO = 3\n\n\t// The location of DBG file.\n\tImageDebugTypeMisc = 4\n\n\t// A copy of .pdata section.\n\tImageDebugTypeException = 5\n\n\t// Reserved.\n\tImageDebugTypeFixup = 6\n\n\t// The mapping from an RVA in image to an RVA in source image.\n\tImageDebugTypeOMAPToSrc = 7\n\n\t// The mapping from an RVA in source image to an RVA in image.\n\tImageDebugTypeOMAPFromSrc = 8\n\n\t// Reserved for Borland.\n\tImageDebugTypeBorland = 9\n\n\t// Reserved.\n\tImageDebugTypeReserved = 10\n\n\t// Reserved.\n\tImageDebugTypeCLSID = 11\n\n\t// Visual C++ features (/GS counts /sdl counts and guardN counts).\n\tImageDebugTypeVCFeature = 12\n\n\t// Pogo aka PGO aka Profile Guided Optimization.\n\tImageDebugTypePOGO = 13\n\n\t// Incremental Link Time Code Generation (iLTCG).\n\tImageDebugTypeILTCG = 14\n\n\t// Intel MPX.\n\tImageDebugTypeMPX = 15\n\n\t// PE determinism or reproducibility.\n\tImageDebugTypeRepro = 16\n\n\t// Extended DLL characteristics bits.\n\tImageDebugTypeExDllCharacteristics = 20\n)\n\nconst (\n\t// CVSignatureRSDS represents the CodeView signature 'SDSR'.\n\tCVSignatureRSDS = 0x53445352\n\n\t// CVSignatureNB10 represents the CodeView signature 'NB10'.\n\tCVSignatureNB10 = 0x3031424e\n)\n\nconst (\n\t// FrameFPO indicates a frame of type FPO.\n\tFrameFPO = 0x0\n\n\t// FrameTrap indicates a frame of type Trap.\n\tFrameTrap = 0x1\n\n\t// FrameTSS indicates a frame of type TSS.\n\tFrameTSS = 0x2\n\n\t// FrameNonFPO indicates a frame of type Non-FPO.\n\tFrameNonFPO = 0x3\n)\n\n// DllCharacteristicsExType represents a DLL Characteristics type.\ntype DllCharacteristicsExType uint32\n\nconst (\n\t// ImageDllCharacteristicsExCETCompat indicates that the image is CET\n\t// compatible.\n\tImageDllCharacteristicsExCETCompat = 0x0001\n)\n\nconst (\n\t// POGOTypePGU represents a signature for an undocumented PGO sub type.\n\tPOGOTypePGU = 0x50475500\n\t// POGOTypePGI represents a signature for an undocumented PGO sub type.\n\tPOGOTypePGI = 0x50474900\n\t// POGOTypePGO represents a signature for an undocumented PGO sub type.\n\tPOGOTypePGO = 0x50474F00\n\t// POGOTypeLTCG represents a signature for an undocumented PGO sub type.\n\tPOGOTypeLTCG = 0x4c544347\n)\n\n// ImageDebugDirectoryType represents the type of a debug directory.\ntype ImageDebugDirectoryType uint32\n\n// ImageDebugDirectory represents the IMAGE_DEBUG_DIRECTORY structure.\n// This directory indicates what form of debug information is present\n// and where it is. This directory consists of an array of debug directory\n// entries whose location and size are indicated in the image optional header.\ntype ImageDebugDirectory struct {\n\t// Reserved, must be 0.\n\tCharacteristics uint32 `json:\"characteristics\"`\n\n\t// The time and date that the debug data was created.\n\tTimeDateStamp uint32 `json:\"time_date_stamp\"`\n\n\t// The major version number of the debug data format.\n\tMajorVersion uint16 `json:\"major_version\"`\n\n\t// The minor version number of the debug data format.\n\tMinorVersion uint16 `json:\"minor_version\"`\n\n\t// The format of debugging information. This field enables support of\n\t// multiple debuggers.\n\tType ImageDebugDirectoryType `json:\"type\"`\n\n\t// The size of the debug data (not including the debug directory itself).\n\tSizeOfData uint32 `json:\"size_of_data\"`\n\n\t//The address of the debug data when loaded, relative to the image base.\n\tAddressOfRawData uint32 `json:\"address_of_raw_data\"`\n\n\t// The file pointer to the debug data.\n\tPointerToRawData uint32 `json:\"pointer_to_raw_data\"`\n}\n\n// DebugEntry wraps ImageDebugDirectory to include debug directory type.\ntype DebugEntry struct {\n\t// Points to the image debug entry structure.\n\tStruct ImageDebugDirectory `json:\"struct\"`\n\n\t// Holds specific information about the debug type entry.\n\tInfo interface{} `json:\"info\"`\n\n\t// Type of the debug entry.\n\tType string `json:\"type\"`\n}\n\n// GUID is a 128-bit value consisting of one group of 8 hexadecimal digits,\n// followed by three groups of 4 hexadecimal digits each, followed by one\n// group of 12 hexadecimal digits.\ntype GUID struct {\n\tData1 uint32\n\tData2 uint16\n\tData3 uint16\n\tData4 [8]byte\n}\n\n// CVSignature represents a CodeView signature.\ntype CVSignature uint32\n\n// CVInfoPDB70 represents the the CodeView data block of a PDB 7.0 file.\ntype CVInfoPDB70 struct {\n\t// CodeView signature, equal to `RSDS`.\n\tCVSignature CVSignature `json:\"cv_signature\"`\n\n\t// A unique identifier, which changes with every rebuild of the executable and PDB file.\n\tSignature GUID `json:\"signature\"`\n\n\t// Ever-incrementing value, which is initially set to 1 and incremented every\n\t// time when a part of the PDB file is updated without rewriting the whole file.\n\tAge uint32 `json:\"age\"`\n\n\t// Null-terminated name of the PDB file. It can also contain full or partial\n\t// path to the file.\n\tPDBFileName string `json:\"pdb_file_name\"`\n}\n\n// CVHeader represents the the CodeView header struct to the PDB 2.0 file.\ntype CVHeader struct {\n\t// CodeView signature, equal to `NB10`.\n\tSignature CVSignature `json:\"signature\"`\n\n\t// CodeView offset. Set to 0, because debug information is stored in a\n\t// separate file.\n\tOffset uint32 `json:\"offset\"`\n}\n\n// CVInfoPDB20 represents the the CodeView data block of a PDB 2.0 file.\ntype CVInfoPDB20 struct {\n\t// Points to the CodeView header structure.\n\tCVHeader CVHeader `json:\"cv_header\"`\n\n\t// The time when debug information was created (in seconds since 01.01.1970).\n\tSignature uint32 `json:\"signature\"`\n\n\t// Ever-incrementing value, which is initially set to 1 and incremented every\n\t// time when a part of the PDB file is updated without rewriting the whole file.\n\tAge uint32 `json:\"age\"`\n\n\t// Null-terminated name of the PDB file. It can also contain full or partial\n\t// path to the file.\n\tPDBFileName string `json:\"pdb_file_name\"`\n}\n\n// FPOFrameType represents the type of a FPO frame.\ntype FPOFrameType uint8\n\n// FPOData represents the stack frame layout for a function on an x86 computer when\n// frame pointer omission (FPO) optimization is used. The structure is used to locate\n// the base of the call frame.\ntype FPOData struct {\n\t// The offset of the first byte of the function code.\n\tOffsetStart uint32 `json:\"offset_start\"`\n\n\t// The number of bytes in the function.\n\tProcSize uint32 `json:\"proc_size\"`\n\n\t// The number of local variables.\n\tNumLocals uint32 `json:\"num_locals\"`\n\n\t// The size of the parameters, in DWORDs.\n\tParamsSize uint16 `json:\"params_size\"`\n\n\t// The number of bytes in the function prolog code.\n\tPrologLength uint8 `json:\"prolog_length\"`\n\n\t// The number of registers saved.\n\tSavedRegsCount uint8 `json:\"saved_regs_count\"`\n\n\t// A variable that indicates whether the function uses structured exception handling.\n\tHasSEH uint8 `json:\"has_seh\"`\n\n\t// A variable that indicates whether the EBP register has been allocated.\n\tUseBP uint8 `json:\"use_bp\"`\n\n\t// Reserved for future use.\n\tReserved uint8 `json:\"reserved\"`\n\n\t// A variable that indicates the frame type.\n\tFrameType FPOFrameType `json:\"frame_type\"`\n}\n\n// ImagePGOItem represents the _IMAGE_POGO_INFO structure.\ntype ImagePGOItem struct {\n\tRVA  uint32 `json:\"rva\"`\n\tSize uint32 `json:\"size\"`\n\tName string `json:\"name\"`\n}\n\n// POGOType represents a POGO type.\ntype POGOType uint32\n\n// POGO structure contains information related to the Profile Guided Optimization.\n// PGO is an approach to optimization where the compiler uses profile information\n// to make better optimization decisions for the program.\ntype POGO struct {\n\t// Signature represents the PGO sub type.\n\tSignature POGOType       `json:\"signature\"`\n\tEntries   []ImagePGOItem `json:\"entries\"`\n}\n\ntype VCFeature struct {\n\tPreVC11 uint32 `json:\"pre_vc11\"`\n\tCCpp    uint32 `json:\"C/C++\"`\n\tGs      uint32 `json:\"/GS\"`\n\tSdl     uint32 `json:\"/sdl\"`\n\tGuardN  uint32 `json:\"guardN\"`\n}\n\ntype REPRO struct {\n\tSize uint32 `json:\"size\"`\n\tHash []byte `json:\"hash\"`\n}\n\n// ImageDebugMisc represents the IMAGE_DEBUG_MISC structure.\ntype ImageDebugMisc struct {\n\t// The type of data carried in the `Data` field.\n\tDataType uint32 `json:\"data_type\"`\n\n\t// The length of this structure in bytes, including the entire Data field\n\t// and its NUL terminator (rounded to four byte multiple.)\n\tLength uint32 `json:\"length\"`\n\n\t// The encoding of the Data field. True if data is unicode string.\n\tUnicode bool `json:\"unicode\"`\n\n\t// Reserved.\n\tReserved [3]byte `json:\"reserved\"`\n\n\t// Actual data.\n\tData string `json:\"data\"`\n}\n\n// Image files contain an optional debug directory that indicates what form of\n// debug information is present and where it is. This directory consists of an\n// array of debug directory entries whose location and size are indicated in the\n// image optional header.  The debug directory can be in a discardable .debug\n// section (if one exists), or it can be included in any other section in the\n// image file, or not be in a section at all.\nfunc (pe *File) parseDebugDirectory(rva, size uint32) error {\n\n\tdebugEntry := DebugEntry{}\n\tdebugDir := ImageDebugDirectory{}\n\terrorMsg := fmt.Sprintf(\"Invalid debug information. Can't read data at RVA: 0x%x\", rva)\n\tdebugDirSize := uint32(binary.Size(debugDir))\n\tdebugDirsCount := size / debugDirSize\n\n\tfor i := uint32(0); i < debugDirsCount; i++ {\n\t\toffset := pe.GetOffsetFromRva(rva + debugDirSize*i)\n\t\terr := pe.structUnpack(&debugDir, offset, debugDirSize)\n\t\tif err != nil {\n\t\t\treturn errors.New(errorMsg)\n\t\t}\n\n\t\tswitch debugDir.Type {\n\t\tcase ImageDebugTypeCodeView:\n\t\t\tdebugSignature, err := pe.ReadUint32(debugDir.PointerToRawData)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif debugSignature == CVSignatureRSDS {\n\t\t\t\t// PDB 7.0\n\t\t\t\tpdb := CVInfoPDB70{CVSignature: CVSignatureRSDS}\n\n\t\t\t\t// Extract the GUID.\n\t\t\t\toffset := debugDir.PointerToRawData + 4\n\t\t\t\tguidSize := uint32(binary.Size(pdb.Signature))\n\t\t\t\terr = pe.structUnpack(&pdb.Signature, offset, guidSize)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Extract the age.\n\t\t\t\toffset += guidSize\n\t\t\t\tpdb.Age, err = pe.ReadUint32(offset)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\toffset += 4\n\n\t\t\t\t// PDB file name.\n\t\t\t\tpdbFilenameSize := debugDir.SizeOfData - 24 - 1\n\n\t\t\t\t// pdbFileName_size can be negative here, as seen in the malware\n\t\t\t\t// sample with MD5 hash: 7c297600870d026c014d42596bb9b5fd\n\t\t\t\t// Checking for positive size here to ensure proper parsing.\n\t\t\t\tif pdbFilenameSize > 0 {\n\t\t\t\t\tpdbFilename := make([]byte, pdbFilenameSize)\n\t\t\t\t\terr = pe.structUnpack(&pdbFilename, offset, pdbFilenameSize)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tpdb.PDBFileName = string(pdbFilename)\n\t\t\t\t}\n\n\t\t\t\t// Include these extra information.\n\t\t\t\tdebugEntry.Info = pdb\n\n\t\t\t} else if debugSignature == CVSignatureNB10 {\n\t\t\t\t// PDB 2.0.\n\t\t\t\tcvHeader := CVHeader{}\n\t\t\t\toffset := debugDir.PointerToRawData\n\t\t\t\terr = pe.structUnpack(&cvHeader, offset, size)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tpdb := CVInfoPDB20{CVHeader: cvHeader}\n\n\t\t\t\t// Extract the signature.\n\t\t\t\tpdb.Signature, err = pe.ReadUint32(offset + 8)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Extract the age.\n\t\t\t\tpdb.Age, err = pe.ReadUint32(offset + 12)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\toffset += 16\n\n\t\t\t\tpdbFilenameSize := debugDir.SizeOfData - 16 - 1\n\t\t\t\tif pdbFilenameSize > 0 {\n\t\t\t\t\tpdbFilename := make([]byte, pdbFilenameSize)\n\t\t\t\t\terr = pe.structUnpack(&pdbFilename, offset, pdbFilenameSize)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tpdb.PDBFileName = string(pdbFilename)\n\t\t\t\t}\n\n\t\t\t\t// Include these extra information.\n\t\t\t\tdebugEntry.Info = pdb\n\t\t\t}\n\t\tcase ImageDebugTypePOGO:\n\t\t\tpogoSignature, err := pe.ReadUint32(debugDir.PointerToRawData)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tpogo := POGO{}\n\n\t\t\tswitch pogoSignature {\n\t\t\tcase 0x0, POGOTypePGU, POGOTypePGI, POGOTypePGO, POGOTypeLTCG:\n\t\t\t\t// TODO: Some files like 00da1a2a9d9ebf447508bf6550f05f466f8eabb4ed6c4f2a524c0769b2d75bc1\n\t\t\t\t// have a POGO signature of 0x0. To be reverse engineered.\n\t\t\t\tpogo.Signature = POGOType(pogoSignature)\n\t\t\t\toffset = debugDir.PointerToRawData + 4\n\t\t\t\tc := uint32(0)\n\t\t\t\tfor c < debugDir.SizeOfData-4 {\n\t\t\t\t\tpogoEntry := ImagePGOItem{}\n\t\t\t\t\tpogoEntry.RVA, err = pe.ReadUint32(offset)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\toffset += 4\n\n\t\t\t\t\tpogoEntry.Size, err = pe.ReadUint32(offset)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\toffset += 4\n\n\t\t\t\t\tpogoEntry.Name = string(pe.GetStringFromData(0, pe.data[offset:offset+64]))\n\n\t\t\t\t\tpogo.Entries = append(pogo.Entries, pogoEntry)\n\t\t\t\t\toffset += uint32(len(pogoEntry.Name))\n\n\t\t\t\t\t// Make sure offset is aligned to 4 bytes.\n\t\t\t\t\tpadding := 4 - (offset % 4)\n\t\t\t\t\tc += 4 + 4 + uint32(len(pogoEntry.Name)) + padding\n\t\t\t\t\toffset += padding\n\t\t\t\t}\n\n\t\t\t\tdebugEntry.Info = pogo\n\t\t\t}\n\t\tcase ImageDebugTypeVCFeature:\n\t\t\tvcf := VCFeature{}\n\t\t\tsize := uint32(binary.Size(vcf))\n\t\t\terr := pe.structUnpack(&vcf, debugDir.PointerToRawData, size)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdebugEntry.Info = vcf\n\t\tcase ImageDebugTypeRepro:\n\t\t\trepro := REPRO{}\n\t\t\toffset := debugDir.PointerToRawData\n\n\t\t\t// Extract the size.\n\t\t\trepro.Size, err = pe.ReadUint32(offset)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Extract the hash.\n\t\t\trepro.Hash, err = pe.ReadBytesAtOffset(offset+4, repro.Size)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdebugEntry.Info = repro\n\t\tcase ImageDebugTypeFPO:\n\t\t\toffset := debugDir.PointerToRawData\n\t\t\tsize := uint32(16)\n\t\t\tfpoEntries := []FPOData{}\n\t\t\tc := uint32(0)\n\t\t\tfor c < debugDir.SizeOfData {\n\t\t\t\tfpo := FPOData{}\n\t\t\t\tfpo.OffsetStart, err = pe.ReadUint32(offset)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tfpo.ProcSize, err = pe.ReadUint32(offset + 4)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tfpo.NumLocals, err = pe.ReadUint32(offset + 8)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tfpo.ParamsSize, err = pe.ReadUint16(offset + 12)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tfpo.PrologLength, err = pe.ReadUint8(offset + 14)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tattributes, err := pe.ReadUint16(offset + 15)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\t//\n\t\t\t\t// UChar  cbRegs :3;  /* # regs saved */\n\t\t\t\t// UChar  fHasSEH:1;  /* Structured Exception Handling */\n\t\t\t\t// UChar  fUseBP :1;  /* EBP has been used */\n\t\t\t\t// UChar  reserved:1;\n\t\t\t\t// UChar  cbFrame:2;  /* frame type */\n\t\t\t\t//\n\n\t\t\t\t// The lowest 3 bits\n\t\t\t\tfpo.SavedRegsCount = uint8(attributes & 0x7)\n\n\t\t\t\t// The next bit.\n\t\t\t\tfpo.HasSEH = uint8(attributes & 0x8 >> 3)\n\n\t\t\t\t// The next bit.\n\t\t\t\tfpo.UseBP = uint8(attributes & 0x10 >> 4)\n\n\t\t\t\t// The next bit.\n\t\t\t\tfpo.Reserved = uint8(attributes & 0x20 >> 5)\n\n\t\t\t\t// The next 2 bits.\n\t\t\t\tfpo.FrameType = FPOFrameType(attributes & 0xC0 >> 6)\n\n\t\t\t\tfpoEntries = append(fpoEntries, fpo)\n\t\t\t\tc += size\n\t\t\t\toffset += 16\n\t\t\t}\n\t\t\tdebugEntry.Info = fpoEntries\n\t\tcase ImageDebugTypeExDllCharacteristics:\n\t\t\texDllChar, err := pe.ReadUint32(debugDir.PointerToRawData)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tdebugEntry.Info = DllCharacteristicsExType(exDllChar)\n\t\t}\n\n\t\tdebugEntry.Struct = debugDir\n\t\tdebugEntry.Type = debugDir.Type.String()\n\t\tpe.Debugs = append(pe.Debugs, debugEntry)\n\t}\n\n\tif len(pe.Debugs) > 0 {\n\t\tpe.HasDebug = true\n\t}\n\n\treturn nil\n}\n\n// SectionAttributeDescription maps a section attribute to a friendly name.\nfunc SectionAttributeDescription(section string) string {\n\tsectionNameMap := map[string]string{\n\t\t\".00cfg\":                               \"CFG Check Functions Pointers\",\n\t\t\".bss$00\":                              \"Uninit.data in phaseN of Pri7\",\n\t\t\".bss$dk00\":                            \"PGI: Uninit.data may be not const\",\n\t\t\".bss$dk01\":                            \"PGI: Uninit.data may be not const\",\n\t\t\".bss$pr00\":                            \"PGI: Uninit.data only for read\",\n\t\t\".bss$pr03\":                            \"PGI: Uninit.data only for read\",\n\t\t\".bss$zz\":                              \"PGO: Dead uninit.data\",\n\t\t\".CRT$XCA\":                             \"First C++ Initializer\",\n\t\t\".CRT$XCZ\":                             \"Last C++ Initializer\",\n\t\t\".xdata$x\":                             \"EH data\",\n\t\t\".gfids$y\":                             \"CFG Functions table\",\n\t\t\".CRT$XCAA\":                            \"Startup C++ Initializer\",\n\t\t\".CRT$XCC\":                             \"Global initializer: init_seg(compiler)\",\n\t\t\".CRT$XCL\":                             \"Global initializer: init_seg(lib)\",\n\t\t\".CRT$XCU\":                             \"Global initializer: init_seg(user)\",\n\t\t\".CRT$XDA\":                             \"First Dynamic TLS Initializer\",\n\t\t\".CRT$XDZ\":                             \"Last Dynamic TLS Initializer\",\n\t\t\".CRT$XIA\":                             \"First C Initializer\",\n\t\t\".CRT$XIAA\":                            \"Startup C Initializer\",\n\t\t\".CRT$XIAB\":                            \"PGO C Initializer\",\n\t\t\".CRT$XIAC\":                            \"Post-PGO C Initializer\",\n\t\t\".CRT$XIC\":                             \"CRT C Initializers\",\n\t\t\".CRT$XIYA\":                            \"VCCorLib Threading Model Initializer\",\n\t\t\".CRT$XIYAA\":                           \"XAML Designer Threading Model Override Initializer\",\n\t\t\".CRT$XIYB\":                            \"VCCorLib Main Initializer\",\n\t\t\".CRT$XIZ\":                             \"Last C Initializer\",\n\t\t\".CRT$XLA\":                             \"First Loader TLS Callback\",\n\t\t\".CRT$XLC\":                             \"CRT TLS Constructor\",\n\t\t\".CRT$XLD\":                             \"CRT TLS Terminator\",\n\t\t\".CRT$XLZ\":                             \"Last Loader TLS Callback\",\n\t\t\".CRT$XPA\":                             \"First Pre-Terminator\",\n\t\t\".CRT$XPB\":                             \"CRT ConcRT Pre-Terminator\",\n\t\t\".CRT$XPX\":                             \"CRT Pre-Terminators\",\n\t\t\".CRT$XPXA\":                            \"CRT stdio Pre-Terminator\",\n\t\t\".CRT$XPZ\":                             \"Last Pre-Terminator\",\n\t\t\".CRT$XTA\":                             \"First Terminator\",\n\t\t\".CRT$XTZ\":                             \"Last Terminator\",\n\t\t\".CRTMA$XCA\":                           \"First Managed C++ Initializer\",\n\t\t\".CRTMA$XCZ\":                           \"Last Managed C++ Initializer\",\n\t\t\".CRTVT$XCA\":                           \"First Managed VTable Initializer\",\n\t\t\".CRTVT$XCZ\":                           \"Last Managed VTable Initializer\",\n\t\t\".data$00\":                             \"Init.data in phaseN of Pri7\",\n\t\t\".data$dk00\":                           \"PGI: Init.data may be not const\",\n\t\t\".data$dk00$brc\":                       \"PGI: Init.data may be not const\",\n\t\t\".data$pr00\":                           \"PGI: Init.data only for read\",\n\t\t\".data$r\":                              \"RTTI Type Descriptors\",\n\t\t\".data$zz\":                             \"PGO: Dead init.data\",\n\t\t\".data$zz$brc\":                         \"PGO: Dead init.data\",\n\t\t\".didat$2\":                             \"Delay Import Descriptors\",\n\t\t\".didat$3\":                             \"Delay Import Final NULL Entry\",\n\t\t\".didat$4\":                             \"Delay Import INT\",\n\t\t\".didat$5\":                             \"Delay Import IAT\",\n\t\t\".didat$6\":                             \"Delay Import Symbol Names\",\n\t\t\".didat$7\":                             \"Delay Import Bound IAT\",\n\t\t\".edata\":                               \"Export Table\",\n\t\t\".gehcont\":                             \"CFG EHCont Table\",\n\t\t\".gfids\":                               \"CFG Functions Table\",\n\t\t\".giats\":                               \"CFG IAT Table\",\n\t\t\".idata$2\":                             \"Import Descriptors\",\n\t\t\".idata$3\":                             \"Import Final NULL Entry\",\n\t\t\".idata$4\":                             \"Import Names Table\",\n\t\t\".idata$5\":                             \"Import Addresses Table\",\n\t\t\".idata$6\":                             \"Import Symbol and DLL Names\",\n\t\t\".pdata\":                               \"Procedure data\",\n\t\t\".rdata$00\":                            \"Readonly data in phaseN of Pri7\",\n\t\t\".rdata$00$brc\":                        \"Readonly data in phaseN of Pri7\",\n\t\t\".rdata$09\":                            \"Readonly data in phaseN of Pri7\",\n\t\t\".rdata$brc\":                           \"BaseRelocation Clustering\",\n\t\t\".rdata$r\":                             \"RTTI Data\",\n\t\t\".rdata$sxdata\":                        \"Safe SEH\",\n\t\t\".rdata$T\":                             \"TLS Header\",\n\t\t\".rdata$zETW0\":                         \"ETW Metadata Header\",\n\t\t\".rdata$zETW1\":                         \"ETW Events Metadata\",\n\t\t\".rdata$zETW2\":                         \"ETW Providers Metadata\",\n\t\t\".rdata$zETW9\":                         \"ETW Metadata Footer\",\n\t\t\".rdata$zz\":                            \"PGO: Dead Readonly Data\",\n\t\t\".rdata$zz$brc\":                        \"PGO: Dead Readonly Data\",\n\t\t\".rdata$zzzdbg\":                        \"Debug directory data\",\n\t\t\".rsrc$01\":                             \"Resources Header\",\n\t\t\".rsrc$02\":                             \"Resources Data\",\n\t\t\".rtc$IAA\":                             \"First RTC Initializer\",\n\t\t\".rtc$IZZ\":                             \"Last RTC Initializer\",\n\t\t\".rtc$TAA\":                             \"First RTC Terminator\",\n\t\t\".rtc$TZZ\":                             \"Last RTC Terminator\",\n\t\t\".text$di\":                             \"MSVC Dynamic Initializers\",\n\t\t\".text$lp00kernel32.dll!20_pri7\":       \"PGO: LoaderPhaseN warm-to-hot code\",\n\t\t\".text$lp01kernel32.dll!20_pri7\":       \"PGO: LoaderPhaseN warm-to-hot code\",\n\t\t\".text$lp03kernel32.dll!30_clientonly\": \"PGO: LoaderPhaseN warm-to-hot code\",\n\t\t\".text$lp04kernel32.dll!30_clientonly\": \"PGO: LoaderPhaseN warm-to-hot code\",\n\t\t\".text$lp08kernel32.dll!40_serveronly\": \"PGO: LoaderPhaseN warm-to-hot code\",\n\t\t\".text$lp09kernel32.dll!40_serveronly\": \"PGO: LoaderPhaseN warm-to-hot code\",\n\t\t\".text$lp10kernel32.dll!40_serveronly\": \"PGO: LoaderPhaseN warm-to-hot code\",\n\t\t\".text$mn\":                             \"Contains EP\",\n\t\t\".text$mn$00\":                          \"CFG Dispatching\",\n\t\t\".text$np\":                             \"PGO: __asm or disabled via pragma\",\n\t\t\".text$x\":                              \"EH Filters\",\n\t\t\".text$yd\":                             \"MSVC Destructors\",\n\t\t\".text$zy\":                             \"PGO: Dead Code Blocks\",\n\t\t\".text$zz\":                             \"PGO: Dead Whole Functions\",\n\t\t\".xdata\":                               \"Unwind data\",\n\t}\n\n\tif val, ok := sectionNameMap[section]; ok {\n\t\treturn val\n\t}\n\n\treturn \"\"\n}\n\n// String returns a string interpretation of the FPO frame type.\nfunc (ft FPOFrameType) String() string {\n\tframeTypeMap := map[FPOFrameType]string{\n\t\tFrameFPO:    \"FPO\",\n\t\tFrameTrap:   \"Trap\",\n\t\tFrameTSS:    \"TSS\",\n\t\tFrameNonFPO: \"Non FPO\",\n\t}\n\n\tv, ok := frameTypeMap[ft]\n\tif ok {\n\t\treturn v\n\t}\n\n\treturn \"?\"\n}\n\n// String returns the string representation of a GUID.\nfunc (g GUID) String() string {\n\treturn fmt.Sprintf(\"{%06X-%04X-%04X-%04X-%X}\", g.Data1, g.Data2, g.Data3, g.Data4[0:2], g.Data4[2:])\n}\n\n// String returns the string representation of a debug entry type.\nfunc (t ImageDebugDirectoryType) String() string {\n\n\tdebugTypeMap := map[ImageDebugDirectoryType]string{\n\t\tImageDebugTypeUnknown:              \"Unknown\",\n\t\tImageDebugTypeCOFF:                 \"COFF\",\n\t\tImageDebugTypeCodeView:             \"CodeView\",\n\t\tImageDebugTypeFPO:                  \"FPO\",\n\t\tImageDebugTypeMisc:                 \"Misc\",\n\t\tImageDebugTypeException:            \"Exception\",\n\t\tImageDebugTypeFixup:                \"Fixup\",\n\t\tImageDebugTypeOMAPToSrc:            \"OMAP To Src\",\n\t\tImageDebugTypeOMAPFromSrc:          \"OMAP From Src\",\n\t\tImageDebugTypeBorland:              \"Borland\",\n\t\tImageDebugTypeReserved:             \"Reserved\",\n\t\tImageDebugTypeVCFeature:            \"VC Feature\",\n\t\tImageDebugTypePOGO:                 \"POGO\",\n\t\tImageDebugTypeILTCG:                \"iLTCG\",\n\t\tImageDebugTypeMPX:                  \"MPX\",\n\t\tImageDebugTypeRepro:                \"REPRO\",\n\t\tImageDebugTypeExDllCharacteristics: \"Ex.DLL Characteristics\",\n\t}\n\n\tv, ok := debugTypeMap[t]\n\tif ok {\n\t\treturn v\n\t}\n\n\treturn \"?\"\n}\n\n// String returns a string interpretation of a POGO type.\nfunc (p POGOType) String() string {\n\tpogoTypeMap := map[POGOType]string{\n\t\tPOGOTypePGU:  \"PGU\",\n\t\tPOGOTypePGI:  \"PGI\",\n\t\tPOGOTypePGO:  \"PGO\",\n\t\tPOGOTypeLTCG: \"LTCG\",\n\t}\n\n\tv, ok := pogoTypeMap[p]\n\tif ok {\n\t\treturn v\n\t}\n\n\treturn \"?\"\n}\n\n// String returns a string interpretation of a CodeView signature.\nfunc (s CVSignature) String() string {\n\tcvSignatureMap := map[CVSignature]string{\n\t\tCVSignatureRSDS: \"RSDS\",\n\t\tCVSignatureNB10: \"NB10\",\n\t}\n\n\tv, ok := cvSignatureMap[s]\n\tif ok {\n\t\treturn v\n\t}\n\n\treturn \"?\"\n}\n\n// String returns a string interpretation of Dll Characteristics Ex.\nfunc (flag DllCharacteristicsExType) String() string {\n\tdllCharacteristicsExTypeMap := map[DllCharacteristicsExType]string{\n\t\tImageDllCharacteristicsExCETCompat: \"CET Compatible\",\n\t}\n\n\tv, ok := dllCharacteristicsExTypeMap[flag]\n\tif ok {\n\t\treturn v\n\t}\n\n\treturn \"?\"\n}\n"
  },
  {
    "path": "debug_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype TestDebugIn struct {\n\tfilepath string\n\tindex    int // debug entry index\n}\n\nfunc TestDebugDirectoryCodeView(t *testing.T) {\n\n\ttype TestCodeView struct {\n\t\tdebugEntry DebugEntry\n\t\tsignature  string\n\t}\n\n\ttests := []struct {\n\t\tin  TestDebugIn\n\t\tout TestCodeView\n\t}{\n\t\t{\n\t\t\tTestDebugIn{\n\t\t\t\tindex:    0,\n\t\t\t\tfilepath: getAbsoluteFilePath(\"test/kernel32.dll\"),\n\t\t\t},\n\t\t\tTestCodeView{\n\t\t\t\tdebugEntry: DebugEntry{\n\t\t\t\t\tStruct: ImageDebugDirectory{\n\t\t\t\t\t\tCharacteristics:  0x0,\n\t\t\t\t\t\tTimeDateStamp:    0x38b369c4,\n\t\t\t\t\t\tMajorVersion:     0x0,\n\t\t\t\t\t\tMinorVersion:     0x0,\n\t\t\t\t\t\tType:             0x2,\n\t\t\t\t\t\tSizeOfData:       0x25,\n\t\t\t\t\t\tAddressOfRawData: 0x932f0,\n\t\t\t\t\t\tPointerToRawData: 0x91cf0,\n\t\t\t\t\t},\n\t\t\t\t\tInfo: CVInfoPDB70{\n\t\t\t\t\t\tCVSignature: 0x53445352,\n\t\t\t\t\t\tSignature: GUID{\n\t\t\t\t\t\t\tData1: 0xdbe09e71,\n\t\t\t\t\t\t\tData2: 0xb370,\n\t\t\t\t\t\t\tData3: 0x9cb7,\n\t\t\t\t\t\t\tData4: [8]byte{34, 197, 94, 85, 115, 250, 123, 225},\n\t\t\t\t\t\t},\n\t\t\t\t\t\tAge:         0x1,\n\t\t\t\t\t\tPDBFileName: \"kernel32.pdb\",\n\t\t\t\t\t},\n\t\t\t\t\tType: \"CodeView\",\n\t\t\t\t},\n\t\t\t\tsignature: \"RSDS\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tTestDebugIn{\n\t\t\t\tindex:    0,\n\t\t\t\tfilepath: getAbsoluteFilePath(\"test/01008963d32f5cc17b64c31446386ee5b36a7eab6761df87a2989ba9394d8f3d\"),\n\t\t\t},\n\t\t\tTestCodeView{\n\t\t\t\tdebugEntry: DebugEntry{\n\t\t\t\t\tStruct: ImageDebugDirectory{\n\t\t\t\t\t\tCharacteristics:  0x0,\n\t\t\t\t\t\tTimeDateStamp:    0x3b7d84d4,\n\t\t\t\t\t\tMajorVersion:     0x0,\n\t\t\t\t\t\tMinorVersion:     0x0,\n\t\t\t\t\t\tType:             0x2,\n\t\t\t\t\t\tSizeOfData:       0x1d,\n\t\t\t\t\t\tAddressOfRawData: 0x1cf4,\n\t\t\t\t\t\tPointerToRawData: 0x10f4,\n\t\t\t\t\t},\n\t\t\t\t\tInfo: CVInfoPDB20{\n\t\t\t\t\t\tCVHeader: CVHeader{\n\t\t\t\t\t\t\tSignature: 0x3031424e,\n\t\t\t\t\t\t\tOffset:    0x0,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tSignature:   0x3b7d84d4,\n\t\t\t\t\t\tAge:         0x1,\n\t\t\t\t\t\tPDBFileName: \"routemon.pdb\",\n\t\t\t\t\t},\n\t\t\t\t\tType: \"CodeView\",\n\t\t\t\t},\n\t\t\t\tsignature: \"NB10\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in.filepath, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in.filepath, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\n\t\t\tif file.Is64 {\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t} else {\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseDebugDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseExportDirectory(%s) failed, reason: %v\",\n\t\t\t\t\ttt.in.filepath, err)\n\t\t\t}\n\n\t\t\tdebugEntry := file.Debugs[tt.in.index]\n\t\t\tif !reflect.DeepEqual(debugEntry, tt.out.debugEntry) {\n\t\t\t\tt.Fatalf(\"debug entry assertion failed, got %v, want %v\",\n\t\t\t\t\tdebugEntry, tt.out.debugEntry)\n\t\t\t}\n\n\t\t\tcvSignature := \"\"\n\t\t\tswitch debugEntry.Info.(type) {\n\t\t\tcase CVInfoPDB70:\n\t\t\t\tcvSignature = debugEntry.Info.(CVInfoPDB70).CVSignature.String()\n\t\t\tcase CVInfoPDB20:\n\t\t\t\tcvSignature = debugEntry.Info.(CVInfoPDB20).CVHeader.Signature.String()\n\t\t\t}\n\t\t\tif cvSignature != tt.out.signature {\n\t\t\t\tt.Fatalf(\"debug CV signature assertion failed, got %v, want %v\",\n\t\t\t\t\tcvSignature, tt.out.signature)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDebugDirectoryPOGO(t *testing.T) {\n\n\ttype TestPOGO struct {\n\t\timgDebugEntry ImageDebugDirectory\n\t\tentriesCount  int\n\t\tdebugType     string\n\t\tPOGOItemIndex int\n\t\tPOGOItem      ImagePGOItem\n\t\tPOGOSignature string\n\t}\n\n\ttests := []struct {\n\t\tin  TestDebugIn\n\t\tout TestPOGO\n\t}{\n\t\t{\n\t\t\tTestDebugIn{\n\t\t\t\tindex:    1,\n\t\t\t\tfilepath: getAbsoluteFilePath(\"test/kernel32.dll\"),\n\t\t\t},\n\t\t\tTestPOGO{\n\t\t\t\timgDebugEntry: ImageDebugDirectory{\n\t\t\t\t\tCharacteristics:  0x0,\n\t\t\t\t\tTimeDateStamp:    0x38b369c4,\n\t\t\t\t\tMajorVersion:     0x0,\n\t\t\t\t\tMinorVersion:     0x0,\n\t\t\t\t\tType:             0xd,\n\t\t\t\t\tSizeOfData:       0x574,\n\t\t\t\t\tAddressOfRawData: 0x93318,\n\t\t\t\t\tPointerToRawData: 0x91d18,\n\t\t\t\t},\n\t\t\t\tdebugType:     \"POGO\",\n\t\t\t\tentriesCount:  60,\n\t\t\t\tPOGOItemIndex: 0,\n\t\t\t\tPOGOItem: ImagePGOItem{\n\t\t\t\t\tRVA:  0x1000,\n\t\t\t\t\tSize: 0x280,\n\t\t\t\t\tName: \".text$lp00kernel32.dll!20_pri7\",\n\t\t\t\t},\n\t\t\t\tPOGOSignature: \"PGU\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in.filepath, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in.filepath, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\n\t\t\tif file.Is64 {\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t} else {\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseDebugDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseExportDirectory(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\timgDebugEntry := file.Debugs[tt.in.index].Struct\n\t\t\tif !reflect.DeepEqual(imgDebugEntry, tt.out.imgDebugEntry) {\n\t\t\t\tt.Fatalf(\"debug entry assertion failed, got %v, want %v\",\n\t\t\t\t\timgDebugEntry, tt.out.imgDebugEntry)\n\t\t\t}\n\n\t\t\tdebugTypeString := file.Debugs[tt.in.index].Type\n\t\t\tif debugTypeString != tt.out.debugType {\n\t\t\t\tt.Fatalf(\"debug type assertion failed, got %v, want %v\",\n\t\t\t\t\tdebugTypeString, tt.out.debugType)\n\t\t\t}\n\n\t\t\tpogo := file.Debugs[tt.in.index].Info.(POGO)\n\t\t\tentriesCount := len(pogo.Entries)\n\t\t\tif entriesCount != tt.out.entriesCount {\n\t\t\t\tt.Fatalf(\"debug entry count failed, got %v, want %v\",\n\t\t\t\t\tentriesCount, tt.out.entriesCount)\n\t\t\t}\n\n\t\t\tpogoItem := pogo.Entries[tt.out.POGOItemIndex]\n\t\t\tif !reflect.DeepEqual(pogoItem, tt.out.POGOItem) {\n\t\t\t\tt.Fatalf(\"debug pogo entry assertion failed, got %v, want %v\",\n\t\t\t\t\tpogoItem, tt.out.POGOItemIndex)\n\t\t\t}\n\n\t\t\tpogoItemSignature := pogo.Signature.String()\n\t\t\tif pogoItemSignature != tt.out.POGOSignature {\n\t\t\t\tt.Fatalf(\"debug pogo signature string assertion failed, got %v, want %v\",\n\t\t\t\t\tpogoItemSignature, tt.out.POGOSignature)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDebugDirectoryREPRO(t *testing.T) {\n\n\ttype TestREPRO struct {\n\t\tdebugType  string\n\t\tdebugEntry DebugEntry\n\t}\n\n\ttests := []struct {\n\t\tin  TestDebugIn\n\t\tout TestREPRO\n\t}{\n\n\t\t{\n\t\t\tTestDebugIn{\n\t\t\t\tindex:    2,\n\t\t\t\tfilepath: getAbsoluteFilePath(\"test/kernel32.dll\"),\n\t\t\t},\n\t\t\tTestREPRO{\n\t\t\t\tdebugEntry: DebugEntry{\n\t\t\t\t\tStruct: ImageDebugDirectory{\n\t\t\t\t\t\tCharacteristics:  0x0,\n\t\t\t\t\t\tTimeDateStamp:    0x38b369c4,\n\t\t\t\t\t\tMajorVersion:     0x0,\n\t\t\t\t\t\tMinorVersion:     0x0,\n\t\t\t\t\t\tType:             0x10,\n\t\t\t\t\t\tSizeOfData:       0x24,\n\t\t\t\t\t\tAddressOfRawData: 0x9388c,\n\t\t\t\t\t\tPointerToRawData: 0x9228c,\n\t\t\t\t\t},\n\t\t\t\t\tInfo: REPRO{\n\t\t\t\t\t\tSize: 0x20,\n\t\t\t\t\t\tHash: []byte{113, 158, 224, 219, 112, 179, 183, 156, 34, 197, 94, 85, 115, 250, 123, 225, 130,\n\t\t\t\t\t\t\t247, 187, 89, 220, 154, 207, 99, 80, 113, 179, 171, 196, 105, 179, 56},\n\t\t\t\t\t},\n\n\t\t\t\t\tType: \"REPRO\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in.filepath, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in.filepath, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\n\t\t\tif file.Is64 {\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t} else {\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseDebugDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseExportDirectory(%s) failed, reason: %v\",\n\t\t\t\t\ttt.in.filepath, err)\n\t\t\t}\n\n\t\t\tdebugEntry := file.Debugs[tt.in.index]\n\t\t\tif !reflect.DeepEqual(debugEntry, tt.out.debugEntry) {\n\t\t\t\tt.Fatalf(\"debug entry assertion failed, got %v, want %v\",\n\t\t\t\t\tdebugEntry, tt.out.debugEntry)\n\t\t\t}\n\n\t\t})\n\t}\n}\n\nfunc TestDebugDirectoryExDLLCharacteristics(t *testing.T) {\n\n\ttype TestExDLLCharacteristics struct {\n\t\tdebugEntry           DebugEntry\n\t\texDLLCharacteristics string\n\t}\n\n\ttests := []struct {\n\t\tin  TestDebugIn\n\t\tout TestExDLLCharacteristics\n\t}{\n\t\t{\n\t\t\tTestDebugIn{\n\t\t\t\tindex:    3,\n\t\t\t\tfilepath: getAbsoluteFilePath(\"test/kernel32.dll\"),\n\t\t\t},\n\t\t\tTestExDLLCharacteristics{\n\t\t\t\tdebugEntry: DebugEntry{\n\t\t\t\t\tStruct: ImageDebugDirectory{\n\t\t\t\t\t\tCharacteristics:  0x0,\n\t\t\t\t\t\tTimeDateStamp:    0x38b369c4,\n\t\t\t\t\t\tMajorVersion:     0x0,\n\t\t\t\t\t\tMinorVersion:     0x0,\n\t\t\t\t\t\tType:             0x14,\n\t\t\t\t\t\tSizeOfData:       0x4,\n\t\t\t\t\t\tAddressOfRawData: 0x938b0,\n\t\t\t\t\t\tPointerToRawData: 0x922b0,\n\t\t\t\t\t},\n\t\t\t\t\tInfo: DllCharacteristicsExType(0x1),\n\t\t\t\t\tType: \"Ex.DLL Characteristics\",\n\t\t\t\t},\n\t\t\t\texDLLCharacteristics: \"CET Compatible\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in.filepath, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in.filepath, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\n\t\t\tif file.Is64 {\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t} else {\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseDebugDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseExportDirectory(%s) failed, reason: %v\",\n\t\t\t\t\ttt.in.filepath, err)\n\t\t\t}\n\n\t\t\tdebugEntry := file.Debugs[tt.in.index]\n\t\t\tif !reflect.DeepEqual(debugEntry, tt.out.debugEntry) {\n\t\t\t\tt.Fatalf(\"debug entry assertion failed, got %v, want %v\",\n\t\t\t\t\tdebugEntry, tt.out.debugEntry)\n\t\t\t}\n\n\t\t\tdllCharacteristicsExString := debugEntry.Info.(DllCharacteristicsExType).String()\n\t\t\tif dllCharacteristicsExString != tt.out.exDLLCharacteristics {\n\t\t\t\tt.Fatalf(\"debug entry DllCharacteristicsEx string assertion failed, got %v, want %v\",\n\t\t\t\t\tdllCharacteristicsExString, tt.out.exDLLCharacteristics)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDebugDirectoryVCFeature(t *testing.T) {\n\n\ttype TestVCFeature struct {\n\t\tdebugEntry DebugEntry\n\t}\n\n\ttests := []struct {\n\t\tin  TestDebugIn\n\t\tout TestVCFeature\n\t}{\n\t\t{\n\t\t\tTestDebugIn{\n\t\t\t\tindex:    1,\n\t\t\t\tfilepath: getAbsoluteFilePath(\"test/00da1a2a9d9ebf447508bf6550f05f466f8eabb4ed6c4f2a524c0769b2d75bc1\"),\n\t\t\t},\n\t\t\tTestVCFeature{\n\t\t\t\tdebugEntry: DebugEntry{\n\t\t\t\t\tStruct: ImageDebugDirectory{\n\t\t\t\t\t\tCharacteristics:  0x0,\n\t\t\t\t\t\tTimeDateStamp:    0x5ef47ea0,\n\t\t\t\t\t\tMajorVersion:     0x0,\n\t\t\t\t\t\tMinorVersion:     0x0,\n\t\t\t\t\t\tType:             0xc,\n\t\t\t\t\t\tSizeOfData:       0x14,\n\t\t\t\t\t\tAddressOfRawData: 0x39d58,\n\t\t\t\t\t\tPointerToRawData: 0x39158,\n\t\t\t\t\t},\n\t\t\t\t\tInfo: VCFeature{\n\t\t\t\t\t\tPreVC11: 0xa,\n\t\t\t\t\t\tCCpp:    0x115,\n\t\t\t\t\t\tGs:      0xe4,\n\t\t\t\t\t\tSdl:     0x0,\n\t\t\t\t\t\tGuardN:  0x115,\n\t\t\t\t\t},\n\t\t\t\t\tType: \"VC Feature\",\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in.filepath, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in.filepath, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\n\t\t\tif file.Is64 {\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t} else {\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseDebugDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseExportDirectory(%s) failed, reason: %v\",\n\t\t\t\t\ttt.in.filepath, err)\n\t\t\t}\n\n\t\t\tdebugEntry := file.Debugs[tt.in.index]\n\t\t\tif !reflect.DeepEqual(debugEntry, tt.out.debugEntry) {\n\t\t\t\tt.Fatalf(\"debug entry assertion failed, got %+v, want %+v\",\n\t\t\t\t\tdebugEntry, tt.out.debugEntry)\n\t\t\t}\n\n\t\t})\n\t}\n}\n\nfunc TestDebugDirectoryFPO(t *testing.T) {\n\n\ttype TestFPO struct {\n\t\timgDebugEntry ImageDebugDirectory\n\t\tentriesCount  int\n\t\tdebugType     string\n\t\tFPODataIndex  int\n\t\tFPOData       FPOData\n\t\tFPOFrameType  string\n\t}\n\n\ttests := []struct {\n\t\tin  TestDebugIn\n\t\tout TestFPO\n\t}{\n\t\t{\n\t\t\tTestDebugIn{\n\t\t\t\tindex:    1,\n\t\t\t\tfilepath: getAbsoluteFilePath(\"test/jobexec.dll\"),\n\t\t\t},\n\t\t\tTestFPO{\n\t\t\t\timgDebugEntry: ImageDebugDirectory{\n\t\t\t\t\tCharacteristics:  0x0,\n\t\t\t\t\tTimeDateStamp:    0x355b8e5f,\n\t\t\t\t\tMajorVersion:     0x0,\n\t\t\t\t\tMinorVersion:     0x0,\n\t\t\t\t\tType:             0x3,\n\t\t\t\t\tSizeOfData:       0x840,\n\t\t\t\t\tAddressOfRawData: 0x0,\n\t\t\t\t\tPointerToRawData: 0xb310,\n\t\t\t\t},\n\t\t\t\tdebugType:    \"FPO\",\n\t\t\t\tentriesCount: 131,\n\t\t\t\tFPODataIndex: 0,\n\t\t\t\tFPOData: FPOData{\n\t\t\t\t\tOffsetStart: 0x1bc0,\n\t\t\t\t\tProcSize:    0x22,\n\t\t\t\t},\n\t\t\t\tFPOFrameType: \"FPO\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tTestDebugIn{\n\t\t\t\tindex:    1,\n\t\t\t\tfilepath: getAbsoluteFilePath(\"test/jobexec.dll\"),\n\t\t\t},\n\t\t\tTestFPO{\n\t\t\t\timgDebugEntry: ImageDebugDirectory{\n\t\t\t\t\tCharacteristics:  0x0,\n\t\t\t\t\tTimeDateStamp:    0x355b8e5f,\n\t\t\t\t\tMajorVersion:     0x0,\n\t\t\t\t\tMinorVersion:     0x0,\n\t\t\t\t\tType:             0x3,\n\t\t\t\t\tSizeOfData:       0x840,\n\t\t\t\t\tAddressOfRawData: 0x0,\n\t\t\t\t\tPointerToRawData: 0xb310,\n\t\t\t\t},\n\t\t\t\tdebugType:    \"FPO\",\n\t\t\t\tentriesCount: 131,\n\t\t\t\tFPODataIndex: 2,\n\t\t\t\tFPOData: FPOData{\n\t\t\t\t\tOffsetStart:    0x1c26,\n\t\t\t\t\tProcSize:       0x267,\n\t\t\t\t\tNumLocals:      0x104,\n\t\t\t\t\tParamsSize:     0x1,\n\t\t\t\t\tPrologLength:   0x16,\n\t\t\t\t\tSavedRegsCount: 0x3,\n\t\t\t\t\tHasSEH:         0x0,\n\t\t\t\t\tUseBP:          0x1,\n\t\t\t\t\tReserved:       0x0,\n\t\t\t\t\tFrameType:      0x3,\n\t\t\t\t},\n\t\t\t\tFPOFrameType: \"Non FPO\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in.filepath, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in.filepath, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\n\t\t\tif file.Is64 {\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t} else {\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryDebug]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseDebugDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseExportDirectory(%s) failed, reason: %v\", tt.in.filepath, err)\n\t\t\t}\n\n\t\t\timgDebugEntry := file.Debugs[tt.in.index].Struct\n\t\t\tif !reflect.DeepEqual(imgDebugEntry, tt.out.imgDebugEntry) {\n\t\t\t\tt.Fatalf(\"debug entry assertion failed, got %v, want %v\",\n\t\t\t\t\timgDebugEntry, tt.out.imgDebugEntry)\n\t\t\t}\n\n\t\t\tdebugTypeString := file.Debugs[tt.in.index].Type\n\t\t\tif debugTypeString != tt.out.debugType {\n\t\t\t\tt.Fatalf(\"debug type assertion failed, got %v, want %v\",\n\t\t\t\t\tdebugTypeString, tt.out.debugType)\n\t\t\t}\n\n\t\t\tfpo := file.Debugs[tt.in.index].Info.([]FPOData)\n\t\t\tentriesCount := len(fpo)\n\t\t\tif entriesCount != tt.out.entriesCount {\n\t\t\t\tt.Fatalf(\"debug entry count failed, got %v, want %v\",\n\t\t\t\t\tentriesCount, tt.out.entriesCount)\n\t\t\t}\n\n\t\t\tfpoData := fpo[tt.out.FPODataIndex]\n\t\t\tif !reflect.DeepEqual(fpoData, tt.out.FPOData) {\n\t\t\t\tt.Fatalf(\"debug FPO data entry assertion failed, got %v, want %v\",\n\t\t\t\t\tfpoData, tt.out.FPOData)\n\t\t\t}\n\n\t\t\tframeType := fpoData.FrameType.String()\n\t\t\tif frameType != tt.out.FPOFrameType {\n\t\t\t\tt.Fatalf(\"debug FPO frame type string assertion failed, got %v, want %v\",\n\t\t\t\t\tframeType, tt.out.FPOFrameType)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDebugSectionAttributes(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  string\n\t\tout string\n\t}{\n\t\t{\n\n\t\t\t\".00cfg\",\n\t\t\t\"CFG Check Functions Pointers\",\n\t\t},\n\t\t{\n\t\t\t\"__undefined__\",\n\t\t\t\"\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.out, func(t *testing.T) {\n\n\t\t\tsecAttrString := SectionAttributeDescription(tt.in)\n\t\t\tif secAttrString != tt.out {\n\t\t\t\tt.Fatalf(\"debug section attributes description failed, got %v, want %v\",\n\t\t\t\t\tsecAttrString, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "delayimports.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n)\n\n// ImageDelayImportDescriptor represents the _IMAGE_DELAYLOAD_DESCRIPTOR structure.\ntype ImageDelayImportDescriptor struct {\n\t// As yet, no attribute flags are defined. The linker sets this field to zero\n\t// in the image. This field can be used to extend the record by indicating\n\t// the presence of new fields, or it can be used to indicate behaviors to\n\t// the delay or unload helper functions.\n\tAttributes uint32 `json:\"attributes\"`\n\n\t// The name of the DLL to be delay-loaded resides in the read-only data\n\t// section of the image. It is referenced through the szName field.\n\tName uint32 `json:\"name\"`\n\n\t// The handle of the DLL to be delay-loaded is in the data section of the\n\t// image. The phmod field points to the handle. The supplied delay-load\n\t// helper uses this location to store the handle to the loaded DLL.\n\tModuleHandleRVA uint32 `json:\"module_handle_rva\"`\n\n\t// The delay import address table (IAT) is referenced by the delay import\n\t// descriptor through the pIAT field. The delay-load helper updates these\n\t// pointers with the real entry points so that the thunks are no longer in\n\t// the calling loop\n\tImportAddressTableRVA uint32 `json:\"import_address_table_rva\"`\n\n\t// The delay import name table (INT) contains the names of the imports that\n\t// might require loading. They are ordered in the same fashion as the\n\t// function pointers in the IAT.\n\tImportNameTableRVA uint32 `json:\"import_name_table_rva\"`\n\n\t// The delay bound import address table (BIAT) is an optional table of\n\t// IMAGE_THUNK_DATA items that is used along with the timestamp field\n\t// of the delay-load directory table by a post-process binding phase.\n\tBoundImportAddressTableRVA uint32 `json:\"bound_import_address_table_rva\"`\n\n\t// The delay unload import address table (UIAT) is an optional table of\n\t// IMAGE_THUNK_DATA items that the unload code uses to handle an explicit\n\t// unload request. It consists of initialized data in the read-only section\n\t// that is an exact copy of the original IAT that referred the code to the\n\t// delay-load thunks. On the unload request, the library can be freed,\n\t// the *phmod cleared, and the UIAT written over the IAT to restore\n\t// everything to its preload state.\n\tUnloadInformationTableRVA uint32 `json:\"unload_information_table_rva\"`\n\n\t// 0 if not bound, otherwise, date/time of the target DLL.\n\tTimeDateStamp uint32 `json:\"time_date_stamp\"`\n}\n\n// DelayImport represents an entry in the delay import table.\ntype DelayImport struct {\n\tOffset     uint32                     `json:\"offset\"`\n\tName       string                     `json:\"name\"`\n\tFunctions  []ImportFunction           `json:\"functions\"`\n\tDescriptor ImageDelayImportDescriptor `json:\"descriptor\"`\n}\n\n// Delay-Load Import Tables tables were added to the image to support a uniform\n// mechanism for applications to delay the loading of a DLL until the first call\n// into that DLL. The delay-load directory table is the counterpart to the\n// import directory table.\nfunc (pe *File) parseDelayImportDirectory(rva, size uint32) error {\n\tfor {\n\t\timportDelayDesc := ImageDelayImportDescriptor{}\n\t\tfileOffset := pe.GetOffsetFromRva(rva)\n\t\timportDescSize := uint32(binary.Size(importDelayDesc))\n\t\terr := pe.structUnpack(&importDelayDesc, fileOffset, importDescSize)\n\n\t\t// If the RVA is invalid all would blow up. Some EXEs seem to be\n\t\t// specially nasty and have an invalid RVA.\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// If the structure is all zeros, we reached the end of the list.\n\t\tif importDelayDesc == (ImageDelayImportDescriptor{}) {\n\t\t\tbreak\n\t\t}\n\n\t\trva += importDescSize\n\n\t\t// If the array of thunks is somewhere earlier than the import\n\t\t// descriptor we can set a maximum length for the array. Otherwise\n\t\t// just set a maximum length of the size of the file\n\t\tmaxLen := uint32(len(pe.data)) - fileOffset\n\t\tif rva > importDelayDesc.ImportNameTableRVA ||\n\t\t\trva > importDelayDesc.ImportAddressTableRVA {\n\t\t\tif rva < importDelayDesc.ImportNameTableRVA {\n\t\t\t\tmaxLen = rva - importDelayDesc.ImportAddressTableRVA\n\t\t\t} else if rva < importDelayDesc.ImportAddressTableRVA {\n\t\t\t\tmaxLen = rva - importDelayDesc.ImportNameTableRVA\n\t\t\t} else {\n\t\t\t\tmaxLen = Max(rva-importDelayDesc.ImportNameTableRVA,\n\t\t\t\t\trva-importDelayDesc.ImportAddressTableRVA)\n\t\t\t}\n\t\t}\n\n\t\tvar importedFunctions []ImportFunction\n\t\tif pe.Is64 {\n\t\t\timportedFunctions, err = pe.parseImports64(&importDelayDesc, maxLen)\n\t\t} else {\n\t\t\timportedFunctions, err = pe.parseImports32(&importDelayDesc, maxLen)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tnameRVA := uint32(0)\n\t\tif importDelayDesc.Attributes == 0 {\n\t\t\tnameRVA = importDelayDesc.Name -\n\t\t\t\tpe.NtHeader.OptionalHeader.(ImageOptionalHeader32).ImageBase\n\t\t} else {\n\t\t\tnameRVA = importDelayDesc.Name\n\t\t}\n\t\tdllName := pe.getStringAtRVA(nameRVA, maxLen)\n\t\tif !IsValidDosFilename(dllName) {\n\t\t\tdllName = \"*invalid*\"\n\t\t\tcontinue\n\t\t}\n\n\t\tpe.DelayImports = append(pe.DelayImports, DelayImport{\n\t\t\tOffset:     fileOffset,\n\t\t\tName:       string(dllName),\n\t\t\tFunctions:  importedFunctions,\n\t\t\tDescriptor: importDelayDesc,\n\t\t})\n\t}\n\n\tif len(pe.DelayImports) > 0 {\n\t\tpe.HasDelayImp = true\n\t}\n\n\treturn nil\n}\n\n// GetDelayImportEntryInfoByRVA return an import function + index of the entry given\n// an RVA.\nfunc (pe *File) GetDelayImportEntryInfoByRVA(rva uint32) (DelayImport, int) {\n\tfor _, imp := range pe.DelayImports {\n\t\tfor i, entry := range imp.Functions {\n\t\t\tif entry.ThunkRVA == rva {\n\t\t\t\treturn imp, i\n\t\t\t}\n\t\t}\n\t}\n\n\treturn DelayImport{}, 0\n}\n"
  },
  {
    "path": "delayimports_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\r\n// Use of this source code is governed by Apache v2 license\r\n// license that can be found in the LICENSE file.\r\n\r\npackage pe\r\n\r\nimport (\r\n\t\"reflect\"\r\n\t\"testing\"\r\n)\r\n\r\ntype TestDelayImportEntry struct {\r\n\tentryCount int\r\n\tentryIndex int\r\n\tentry      DelayImport\r\n}\r\n\r\nfunc TestDelayImportDirectory(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout TestDelayImportEntry\r\n\t}{\r\n\t\t{\r\n\t\t\tgetAbsoluteFilePath(\"test/000049925c578e5a0883e7d1a8257c1a44feab8f7d9972ace8d0e3fb96612a4c\"),\r\n\t\t\tTestDelayImportEntry{\r\n\t\t\t\tentryCount: 4,\r\n\t\t\t\tentryIndex: 0,\r\n\t\t\t\tentry: DelayImport{\r\n\t\t\t\t\tOffset: 0x5F7C00,\r\n\t\t\t\t\tName:   \"kernel32.dll\",\r\n\t\t\t\t\tFunctions: []ImportFunction{\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tName:               \"GetLogicalProcessorInformation\",\r\n\t\t\t\t\t\t\tHint:               0x0,\r\n\t\t\t\t\t\t\tByOrdinal:          false,\r\n\t\t\t\t\t\t\tOriginalThunkValue: 0x601192,\r\n\t\t\t\t\t\t\tThunkValue:         0xF04E60,\r\n\t\t\t\t\t\t\tThunkRVA:           0x6010B4,\r\n\t\t\t\t\t\t\tOriginalThunkRVA:   0x6010F0,\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t},\r\n\t\t\t\t\tDescriptor: ImageDelayImportDescriptor{\r\n\t\t\t\t\t\tAttributes:                 0x1,\r\n\t\t\t\t\t\tName:                       0x601184,\r\n\t\t\t\t\t\tModuleHandleRVA:            0x6010A0,\r\n\t\t\t\t\t\tImportAddressTableRVA:      0x6010B4,\r\n\t\t\t\t\t\tImportNameTableRVA:         0x6010F0,\r\n\t\t\t\t\t\tBoundImportAddressTableRVA: 0x60112C,\r\n\t\t\t\t\t\tUnloadInformationTableRVA:  0x601158,\r\n\t\t\t\t\t\tTimeDateStamp:              0x0,\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryDelayImport]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryDelayImport]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseDelayImportDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseDelayImportDirectory(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\t\t\tgot := file.DelayImports\r\n\t\t\tif len(got) != tt.out.entryCount {\r\n\t\t\t\tt.Errorf(\"delay imports entry count assertion failed, got %v, want %v\",\r\n\t\t\t\t\tlen(got), tt.out.entryCount)\r\n\t\t\t}\r\n\r\n\t\t\tif len(file.DelayImports) > 0 {\r\n\t\t\t\tdelayImportEntry := file.DelayImports[tt.out.entryIndex]\r\n\t\t\t\tif !reflect.DeepEqual(delayImportEntry, tt.out.entry) {\r\n\t\t\t\t\tt.Errorf(\"delay import entry assertion failed, got %v, want %v\",\r\n\t\t\t\t\t\tdelayImportEntry, tt.out.entry)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t})\r\n\t}\r\n}\r\n"
  },
  {
    "path": "dosheader.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n)\n\n// ImageDOSHeader represents the DOS stub of a PE.\ntype ImageDOSHeader struct {\n\t// Magic number.\n\tMagic uint16 `json:\"magic\"`\n\n\t// Bytes on last page of file.\n\tBytesOnLastPageOfFile uint16 `json:\"bytes_on_last_page_of_file\"`\n\n\t// Pages in file.\n\tPagesInFile uint16 `json:\"pages_in_file\"`\n\n\t// Relocations.\n\tRelocations uint16 `json:\"relocations\"`\n\n\t// Size of header in paragraphs.\n\tSizeOfHeader uint16 `json:\"size_of_header\"`\n\n\t// Minimum extra paragraphs needed.\n\tMinExtraParagraphsNeeded uint16 `json:\"min_extra_paragraphs_needed\"`\n\n\t// Maximum extra paragraphs needed.\n\tMaxExtraParagraphsNeeded uint16 `json:\"max_extra_paragraphs_needed\"`\n\n\t// Initial (relative) SS value.\n\tInitialSS uint16 `json:\"initial_ss\"`\n\n\t// Initial SP value.\n\tInitialSP uint16 `json:\"initial_sp\"`\n\n\t// Checksum.\n\tChecksum uint16 `json:\"checksum\"`\n\n\t// Initial IP value.\n\tInitialIP uint16 `json:\"initial_ip\"`\n\n\t// Initial (relative) CS value.\n\tInitialCS uint16 `json:\"initial_cs\"`\n\n\t// File address of relocation table.\n\tAddressOfRelocationTable uint16 `json:\"address_of_relocation_table\"`\n\n\t// Overlay number.\n\tOverlayNumber uint16 `json:\"overlay_number\"`\n\n\t// Reserved words.\n\tReservedWords1 [4]uint16 `json:\"reserved_words_1\"`\n\n\t// OEM identifier.\n\tOEMIdentifier uint16 `json:\"oem_identifier\"`\n\n\t// OEM information.\n\tOEMInformation uint16 `json:\"oem_information\"`\n\n\t// Reserved words.\n\tReservedWords2 [10]uint16 `json:\"reserved_words_2\"`\n\n\t// File address of new exe header (Elfanew).\n\tAddressOfNewEXEHeader uint32 `json:\"address_of_new_exe_header\"`\n}\n\n// ParseDOSHeader parses the DOS header stub. Every PE file begins with a small\n// MS-DOS stub. The need for this arose in the early days of Windows, before a\n// significant number of consumers were running it. When executed on a machine\n// without Windows, the program could at least print out a message saying that\n// Windows was required to run the executable.\nfunc (pe *File) ParseDOSHeader() (err error) {\n\toffset := uint32(0)\n\tsize := uint32(binary.Size(pe.DOSHeader))\n\terr = pe.structUnpack(&pe.DOSHeader, offset, size)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// It can be ZM on an (non-PE) EXE.\n\t// These executables still work under XP via ntvdm.\n\tif pe.DOSHeader.Magic != ImageDOSSignature &&\n\t\tpe.DOSHeader.Magic != ImageDOSZMSignature {\n\t\treturn ErrDOSMagicNotFound\n\t}\n\n\t// `e_lfanew` is the only required element (besides the signature) of the\n\t// DOS header to turn the EXE into a PE. It is is a relative offset to the\n\t// NT Headers. It can't be null (signatures would overlap).\n\t// Can be 4 at minimum.\n\tif pe.DOSHeader.AddressOfNewEXEHeader < 4 ||\n\t\tpe.DOSHeader.AddressOfNewEXEHeader > pe.size {\n\t\treturn ErrInvalidElfanewValue\n\t}\n\n\t// tiny pe has a e_lfanew of 4, which means the NT Headers is overlapping\n\t// the DOS Header.\n\tif pe.DOSHeader.AddressOfNewEXEHeader <= 0x3c {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoPEHeaderOverlapDOSHeader)\n\t}\n\n\tpe.HasDOSHdr = true\n\treturn nil\n}\n"
  },
  {
    "path": "dosheader_test.go",
    "content": "// Copyright 2022 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"testing\"\n)\n\ntype TestDOSHeader struct {\n\timageDOSHeader ImageDOSHeader\n}\n\nfunc TestParseDOSHeader(t *testing.T) {\n\ttests := []struct {\n\t\tin  string\n\t\tout TestDOSHeader\n\t}{\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/putty.exe\"),\n\t\t\tTestDOSHeader{\n\t\t\t\timageDOSHeader: ImageDOSHeader{\n\t\t\t\t\tMagic:                    0x5a4d,\n\t\t\t\t\tBytesOnLastPageOfFile:    0x78,\n\t\t\t\t\tPagesInFile:              0x1,\n\t\t\t\t\tRelocations:              0x0,\n\t\t\t\t\tSizeOfHeader:             0x4,\n\t\t\t\t\tMinExtraParagraphsNeeded: 0x0,\n\t\t\t\t\tMaxExtraParagraphsNeeded: 0x0,\n\t\t\t\t\tInitialSS:                0x0,\n\t\t\t\t\tInitialSP:                0x0,\n\t\t\t\t\tChecksum:                 0x0,\n\t\t\t\t\tInitialIP:                0x0,\n\t\t\t\t\tInitialCS:                0x0,\n\t\t\t\t\tAddressOfRelocationTable: 0x40,\n\t\t\t\t\tOverlayNumber:            0x0,\n\t\t\t\t\tReservedWords1:           [4]uint16{},\n\t\t\t\t\tOEMIdentifier:            0x0,\n\t\t\t\t\tOEMInformation:           0x0,\n\t\t\t\t\tReservedWords2:           [10]uint16{},\n\t\t\t\t\tAddressOfNewEXEHeader:    0x78,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\terr = file.ParseDOSHeader()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tgot := file.DOSHeader\n\t\t\tif got != tt.out.imageDOSHeader {\n\t\t\t\tt.Errorf(\"parse DOS header assertion failed, got %v, want %v\", got,\n\t\t\t\t\ttt.out.imageDOSHeader)\n\t\t\t}\n\n\t\t})\n\t}\n}\n\nfunc TestParseDOSHeaderNonMZ(t *testing.T) {\n\ttests := []struct {\n\t\tin  string\n\t\tout error\n\t}{\n\t\t{\n\t\t\t// This is an ELF file.\n\t\t\tgetAbsoluteFilePath(\"test/look\"),\n\t\t\tErrDOSMagicNotFound,\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\terr = file.ParseDOSHeader()\n\t\t\tif err != tt.out {\n\t\t\t\tt.Fatalf(\"parsing DOS header failed, got %v, want %v\", err, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "dotnet.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n)\n\n// References\n// https://www.ntcore.com/files/dotnetformat.htm\n\n// COMImageFlagsType represents a COM+ header entry point flag type.\ntype COMImageFlagsType uint32\n\n// COM+ Header entry point flags.\nconst (\n\t// The image file contains IL code only, with no embedded native unmanaged\n\t// code except the start-up stub (which simply executes an indirect jump to\n\t// the CLR entry point).\n\tCOMImageFlagsILOnly = 0x00000001\n\n\t// The image file can be loaded only into a 32-bit process.\n\tCOMImageFlags32BitRequired = 0x00000002\n\n\t// This flag is obsolete and should not be set. Setting it—as the IL\n\t// assembler allows, using the .corflags directive—will render your module\n\t// un-loadable.\n\tCOMImageFlagILLibrary = 0x00000004\n\n\t// The image file is protected with a strong name signature.\n\tCOMImageFlagsStrongNameSigned = 0x00000008\n\n\t// The executable’s entry point is an unmanaged method. The EntryPointToken/\n\t// EntryPointRVA field of the CLR header contains the RVA of this native\n\t// method. This flag was introduced in version 2.0 of the CLR.\n\tCOMImageFlagsNativeEntrypoint = 0x00000010\n\n\t// The CLR loader and the JIT compiler are required to track debug\n\t// information about the methods. This flag is not used.\n\tCOMImageFlagsTrackDebugData = 0x00010000\n\n\t// The image file can be loaded into any process, but preferably into a\n\t// 32-bit process. This flag can be only set together with flag\n\t// COMIMAGE_FLAGS_32BITREQUIRED. When set, these two flags mean the image\n\t// is platformneutral, but prefers to be loaded as 32-bit when possible.\n\t// This flag was introduced in CLR v4.0\n\tCOMImageFlags32BitPreferred = 0x00020000\n)\n\n// V-table constants.\nconst (\n\t// V-table slots are 32-bits in size.\n\tCORVTable32Bit = 0x01\n\n\t// V-table slots are 64-bits in size.\n\tCORVTable64Bit = 0x02\n\n\t//  The thunk created by the common language runtime must provide data\n\t// marshaling between managed and unmanaged code.\n\tCORVTableFromUnmanaged = 0x04\n\n\t// The thunk created by the common language runtime must provide data\n\t// marshaling between managed and unmanaged code. Current appdomain should\n\t// be selected to dispatch the call.\n\tCORVTableFromUnmanagedRetainAppDomain = 0x08\n\n\t// Call most derived method described by\n\tCORVTableCallMostDerived = 0x10\n)\n\n// Metadata Tables constants.\nconst (\n\t// The current module descriptor.\n\tModule = 0\n\t// Class reference descriptors.\n\tTypeRef = 1\n\t// Class or interface definition descriptors.\n\tTypeDef = 2\n\t// A class-to-fields lookup table, which does not exist in optimized\n\t// metadata (#~ stream).\n\tFieldPtr = 3\n\t// Field definition descriptors.\n\tField = 4\n\t// A class-to-methods lookup table, which does not exist in\n\t// optimized metadata (#~ stream).\n\tMethodPtr = 5\n\t// Method definition descriptors.\n\tMethodDef = 6\n\t// A method-to-parameters lookup table, which does not exist in optimized\n\t// metadata (#~ stream).\n\tParamPtr = 7\n\t// Parameter definition descriptors.\n\tParam = 8\n\t// Interface implementation descriptors.\n\tInterfaceImpl = 9\n\t// Member (field or method) reference descriptors.\n\tMemberRef = 10\n\t// Constant value descriptors that map the default values stored in the\n\t// #Blob stream to respective fields, parameters, and properties.\n\tConstant = 11\n\t// Custom attribute descriptors.\n\tCustomAttribute = 12\n\t// Field or parameter marshaling descriptors for managed/unmanaged\n\t// inter-operations.\n\tFieldMarshal = 13\n\t// Security descriptors.\n\tDeclSecurity = 14\n\t// Class layout descriptors that hold information about how the loader\n\t// should lay out respective classes.\n\tClassLayout = 15\n\t// Field layout descriptors that specify the offset or ordinal of\n\t// individual fields.\n\tFieldLayout = 16\n\t// Stand-alone signature descriptors. Signatures per se are used in two\n\t// capacities: as composite signatures of local variables of methods and as\n\t// parameters of the call indirect (calli) IL instruction.\n\tStandAloneSig = 17\n\t// A class-to-events mapping table. This is not an intermediate lookup\n\t// table, and it does exist in optimized metadata.\n\tEventMap = 18\n\t// An event map–to–events lookup table, which does not exist in optimized\n\t// metadata (#~ stream).\n\tEventPtr = 19\n\t// Event descriptors.\n\tEvent = 20\n\t// A class-to-properties mapping table. This is not an intermediate lookup\n\t// table, and it does exist in optimized metadata.\n\tPropertyMap = 21\n\t// A property map–to–properties lookup table, which does not exist in\n\t// optimized metadata (#~ stream).\n\tPropertyPtr = 22\n\t// Property descriptors.\n\tProperty = 23\n\t// Method semantics descriptors that hold information about which method is\n\t// associated with a specific property or event and in what capacity.\n\tMethodSemantics = 24\n\t// Method implementation descriptors.\n\tMethodImpl = 25\n\t// Module reference descriptors.\n\tModuleRef = 26\n\t// Type specification descriptors.\n\tTypeSpec = 27\n\t// Implementation map descriptors used for the platform invocation\n\t// (P/Invoke) type of managed/unmanaged code inter-operation.\n\tImplMap = 28\n\t// Field-to-data mapping descriptors.\n\tFieldRVA = 29\n\t// Edit-and-continue log descriptors that hold information about what\n\t// changes have been made to specific metadata items during in-memory\n\t// editing. This table does not exist in optimized metadata (#~ stream)\n\tENCLog = 30\n\t// Edit-and-continue mapping descriptors. This table does not exist in\n\t// optimized metadata (#~ stream).\n\tENCMap = 31\n\t// The current assembly descriptor, which should appear only in the prime\n\t// module metadata.\n\tAssembly = 32\n\t// This table is unused.\n\tAssemblyProcessor = 33\n\t// This table is unused.\n\tAssemblyOS = 34\n\t// Assembly reference descriptors.\n\tAssemblyRef = 35\n\t// This table is unused.\n\tAssemblyRefProcessor = 36\n\t// This table is unused.\n\tAssemblyRefOS = 37\n\t// File descriptors that contain information about other files in the\n\t// current assembly.\n\tFileMD = 38\n\t// Exported type descriptors that contain information about public classes\n\t// exported by the current assembly, which are declared in other modules of\n\t// the assembly. Only the prime module of the assembly should carry this\n\t// table.\n\tExportedType = 39\n\t// Managed resource descriptors.\n\tManifestResource = 40\n\t// Nested class descriptors that provide mapping of nested classes to their\n\t// respective enclosing classes.\n\tNestedClass = 41\n\t//  Type parameter descriptors for generic (parameterized) classes and\n\t// methods.\n\tGenericParam = 42\n\t// Generic method instantiation descriptors.\n\tMethodSpec = 43\n\t// Descriptors of constraints specified for type parameters of generic\n\t// classes and methods\n\tGenericParamConstraint = 44\n)\n\n// Heaps Streams Bit Positions.\nconst (\n\tStringStream = 0\n\tGUIDStream   = 1\n\tBlobStream   = 2\n)\n\n// MetadataTableIndexToString returns the string representation of the metadata\n// table index.\nfunc MetadataTableIndexToString(k int) string {\n\tmetadataTablesMap := map[int]string{\n\t\tModule:                 \"Module\",\n\t\tTypeRef:                \"TypeRef\",\n\t\tTypeDef:                \"TypeDef\",\n\t\tFieldPtr:               \"FieldPtr\",\n\t\tField:                  \"Field\",\n\t\tMethodPtr:              \"MethodPtr\",\n\t\tMethodDef:              \"MethodDef\",\n\t\tParamPtr:               \"ParamPtr\",\n\t\tParam:                  \"Param\",\n\t\tInterfaceImpl:          \"InterfaceImpl\",\n\t\tMemberRef:              \"MemberRef\",\n\t\tConstant:               \"Constant\",\n\t\tCustomAttribute:        \"CustomAttribute\",\n\t\tFieldMarshal:           \"FieldMarshal\",\n\t\tDeclSecurity:           \"DeclSecurity\",\n\t\tClassLayout:            \"ClassLayout\",\n\t\tFieldLayout:            \"FieldLayout\",\n\t\tStandAloneSig:          \"StandAloneSig\",\n\t\tEventMap:               \"EventMap\",\n\t\tEventPtr:               \"EventPtr\",\n\t\tEvent:                  \"Event\",\n\t\tPropertyMap:            \"PropertyMap\",\n\t\tPropertyPtr:            \"PropertyPtr\",\n\t\tProperty:               \"Property\",\n\t\tMethodSemantics:        \"MethodSemantics\",\n\t\tMethodImpl:             \"MethodImpl\",\n\t\tModuleRef:              \"ModuleRef\",\n\t\tTypeSpec:               \"TypeSpec\",\n\t\tImplMap:                \"ImplMap\",\n\t\tFieldRVA:               \"FieldRVA\",\n\t\tENCLog:                 \"ENCLog\",\n\t\tENCMap:                 \"ENCMap\",\n\t\tAssembly:               \"Assembly\",\n\t\tAssemblyProcessor:      \"AssemblyProcessor\",\n\t\tAssemblyOS:             \"AssemblyOS\",\n\t\tAssemblyRef:            \"AssemblyRef\",\n\t\tAssemblyRefProcessor:   \"AssemblyRefProcessor\",\n\t\tAssemblyRefOS:          \"AssemblyRefOS\",\n\t\tFileMD:                 \"File\",\n\t\tExportedType:           \"ExportedType\",\n\t\tManifestResource:       \"ManifestResource\",\n\t\tNestedClass:            \"NestedClass\",\n\t\tGenericParam:           \"GenericParam\",\n\t\tMethodSpec:             \"MethodSpec\",\n\t\tGenericParamConstraint: \"GenericParamConstraint\",\n\t}\n\n\tif value, ok := metadataTablesMap[k]; ok {\n\t\treturn value\n\t}\n\treturn \"\"\n}\n\n// GetMetadataStreamIndexSize returns the size of indexes to read into a\n// particular heap.\nfunc (pe *File) GetMetadataStreamIndexSize(BitPosition int) int {\n\t// The `Heaps` field is a bit vector that encodes how wide indexes into the\n\t// various heaps are:\n\t// - If bit 0 is set, indexes into the \"#String\" heap are 4 bytes wide;\n\t// - if bit 1 is set, indexes into the \"#GUID\" heap are 4 bytes wide;\n\t// - if bit 2 is set, indexes into the \"#Blob\" heap are 4 bytes wide.\n\theaps := pe.CLR.MetadataTablesStreamHeader.Heaps\n\tif IsBitSet(uint64(heaps), BitPosition) {\n\t\treturn 4\n\t}\n\t// Conversely, if the HeapSizes bit for a particular heap is not set,\n\t// indexes into that heap are 2 bytes wide.\n\treturn 2\n}\n\n// ImageDataDirectory represents the  directory format.\ntype ImageDataDirectory struct {\n\n\t// The relative virtual address of the table.\n\tVirtualAddress uint32 `json:\"virtual_address\"`\n\n\t// The size of the table, in bytes.\n\tSize uint32 `json:\"size\"`\n}\n\n// ImageCOR20Header represents the CLR 2.0 header structure.\ntype ImageCOR20Header struct {\n\n\t// Size of the header in bytes.\n\tCb uint32 `json:\"cb\"`\n\n\t// Major number of the minimum version of the runtime required to run the\n\t// program.\n\tMajorRuntimeVersion uint16 `json:\"major_runtime_version\"`\n\n\t// Minor number of the version of the runtime required to run the program.\n\tMinorRuntimeVersion uint16 `json:\"minor_runtime_version\"`\n\n\t// RVA and size of the metadata.\n\tMetaData ImageDataDirectory `json:\"meta_data\"`\n\n\t// Bitwise flags indicating attributes of this executable.\n\tFlags COMImageFlagsType `json:\"flags\"`\n\n\t// Metadata identifier (token) of the entry point for the image file; can\n\t// be 0 for DLL images. This field identifies a method belonging to this\n\t// module or a module containing the entry point method.\n\t// In images of version 2.0 and newer, this field may contain RVA of the\n\t// embedded native entry point method.\n\t// union {\n\t//\n\t// If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set,\n\t// EntryPointToken represents a managed entrypoint.\n\t//\tDWORD               EntryPointToken;\n\t//\n\t// If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set,\n\t// EntryPointRVA represents an RVA to a native entrypoint\n\t//\tDWORD               EntryPointRVA;\n\t//};\n\tEntryPointRVAorToken uint32 `json:\"entry_point_rva_or_token\"`\n\n\t// This is the blob of managed resources. Fetched using\n\t// code:AssemblyNative.GetResource and code:PEFile.GetResource and accessible\n\t// from managed code from System.Assembly.GetManifestResourceStream. The\n\t// metadata has a table that maps names to offsets into this blob, so\n\t// logically the blob is a set of resources.\n\tResources ImageDataDirectory `json:\"resources\"`\n\n\t// RVA and size of the hash data for this PE file, used by the loader for\n\t// binding and versioning. IL assemblies can be signed with a public-private\n\t// key to validate who created it. The signature goes here if this feature\n\t// is used.\n\tStrongNameSignature ImageDataDirectory `json:\"strong_name_signature\"`\n\n\t// RVA and size of the Code Manager table. In the existing releases of the\n\t// runtime, this field is reserved and must be set to 0.\n\tCodeManagerTable ImageDataDirectory `json:\"code_manager_table\"`\n\n\t// RVA and size in bytes of an array of virtual table (v-table) fixups.\n\t// Among current managed compilers, only the VC++ linker and the IL\n\t// assembler can produce this array.\n\tVTableFixups ImageDataDirectory `json:\"vtable_fixups\"`\n\n\t// RVA and size of an array of addresses of jump thunks. Among managed\n\t// compilers, only the VC++ of versions pre-8.0 could produce this table,\n\t// which allows the export of unmanaged native methods embedded in the\n\t// managed PE file. In v2.0+ of CLR this entry is obsolete and must be set\n\t// to 0.\n\tExportAddressTableJumps ImageDataDirectory `json:\"export_address_table_jumps\"`\n\n\t// Reserved for precompiled images; set to 0\n\t// NGEN images it points at a code:CORCOMPILE_HEADER structure\n\tManagedNativeHeader ImageDataDirectory `json:\"managed_native_header\"`\n}\n\n// ImageCORVTableFixup defines the v-table fixups that contains the\n// initializing information necessary for the runtime to create the thunks.\n// Non VOS v-table entries.  Define an array of these pointed to by\n// IMAGE_COR20_HEADER.VTableFixups.  Each entry describes a contiguous array of\n// v-table slots.  The slots start out initialized to the meta data token value\n// for the method they need to call.  At image load time, the CLR Loader will\n// turn each entry into a pointer to machine code for the CPU and can be\n// called directly.\ntype ImageCORVTableFixup struct {\n\tRVA   uint32 `json:\"rva\"`   // Offset of v-table array in image.\n\tCount uint16 `json:\"count\"` // How many entries at location.\n\tType  uint16 `json:\"type\"`  // COR_VTABLE_xxx type of entries.\n}\n\n// MetadataHeader consists of a storage signature and a storage header.\ntype MetadataHeader struct {\n\t// The storage signature, which must be 4-byte aligned:\n\t// ”Magic” signature for physical metadata, currently 0x424A5342, or, read\n\t// as characters, BSJB—the initials of four “founding fathers” Brian Harry,\n\t// Susa Radke-Sproull, Jason Zander, and Bill Evans, who started the\n\t// runtime development in 1998.\n\tSignature uint32 `json:\"signature\"`\n\n\t// Major version.\n\tMajorVersion uint16 `json:\"major_version\"`\n\n\t// Minor version.\n\tMinorVersion uint16 `json:\"minor_version\"`\n\n\t// Reserved; set to 0.\n\tExtraData uint32 `json:\"extra_data\"`\n\n\t// Length of the version string.\n\tVersionString uint32 `json:\"version_string\"`\n\n\t// Version string.\n\tVersion string `json:\"version\"`\n\n\t// The storage header follows the storage signature, aligned on a 4-byte\n\t// boundary.\n\t//\n\n\t// Reserved; set to 0.\n\tFlags uint8 `json:\"flags\"`\n\n\t// Another byte used for [padding]\n\n\t// Number of streams.\n\tStreams uint16 `json:\"streams\"`\n}\n\n// MetadataStreamHeader represents a Metadata Stream Header Structure.\ntype MetadataStreamHeader struct {\n\t// Offset in the file for this stream.\n\tOffset uint32 `json:\"offset\"`\n\n\t// Size of the stream in bytes.\n\tSize uint32 `json:\"size\"`\n\n\t// Name of the stream; a zero-terminated ASCII string no longer than 31\n\t// characters (plus zero terminator). The name might be shorter, in which\n\t// case the size of the stream header is correspondingly reduced, padded to\n\t// the 4-byte boundary.\n\tName string `json:\"name\"`\n}\n\n// MetadataTableStreamHeader represents the Metadata Table Stream Header Structure.\ntype MetadataTableStreamHeader struct {\n\t// Reserved; set to 0.\n\tReserved uint32 `json:\"reserved\"`\n\n\t// Major version of the table schema (1 for v1.0 and v1.1; 2 for v2.0 or later).\n\tMajorVersion uint8 `json:\"major_version\"`\n\n\t// Minor version of the table schema (0 for all versions).\n\tMinorVersion uint8 `json:\"minor_version\"`\n\n\t// Binary flags indicate the offset sizes to be used within the heaps.\n\t// 4-byte unsigned integer offset is indicated by:\n\t// - 0x01 for a string heap, 0x02 for a GUID heap, and 0x04 for a blob heap.\n\t// If a flag is not set, the respective heap offset is a 2-byte unsigned integer.\n\t// A #- stream can also have special flags set:\n\t// - flag 0x20, indicating that the stream contains only changes made\n\t// during an edit-and-continue session, and;\n\t// - flag 0x80, indicating that the  metadata might contain items marked as\n\t// deleted.\n\tHeaps uint8 `json:\"heaps\"`\n\n\t// Bit width of the maximal record index to all tables of the metadata;\n\t// calculated at run time (during the metadata stream initialization).\n\tRID uint8 `json:\"rid\"`\n\n\t// Bit vector of present tables, each bit representing one table (1 if\n\t// present).\n\tMaskValid uint64 `json:\"mask_valid\"`\n\n\t// Bit vector of sorted tables, each bit representing a respective table (1\n\t// if sorted)\n\tSorted uint64 `json:\"sorted\"`\n}\n\n// MetadataTable represents the content of a particular table in the metadata.\n// The metadata schema defines 45 tables.\ntype MetadataTable struct {\n\t// The name of the table.\n\tName string `json:\"name\"`\n\n\t// Number of columns in the table.\n\tCountCols uint32 `json:\"count_cols\"`\n\n\t// Every table has a different layout, defined in the ECMA-335 spec.\n\t// Content abstract the type each table is pointing to.\n\tContent interface{} `json:\"content\"`\n}\n\n// CLRData embeds the Common Language Runtime Header structure as well as the\n// Metadata header structure.\ntype CLRData struct {\n\tCLRHeader                  ImageCOR20Header          `json:\"clr_header\"`\n\tMetadataHeader             MetadataHeader            `json:\"metadata_header\"`\n\tMetadataStreamHeaders      []MetadataStreamHeader    `json:\"metadata_stream_headers\"`\n\tMetadataStreams            map[string][]byte         `json:\"-\"`\n\tMetadataTablesStreamHeader MetadataTableStreamHeader `json:\"metadata_tables_stream_header\"`\n\tMetadataTables             map[int]*MetadataTable    `json:\"metadata_tables\"`\n\tStringStreamIndexSize      int                       `json:\"-\"`\n\tGUIDStreamIndexSize        int                       `json:\"-\"`\n\tBlobStreamIndexSize        int                       `json:\"-\"`\n}\n\nfunc (pe *File) parseMetadataStream(off, size uint32) (MetadataTableStreamHeader, error) {\n\tmdTableStreamHdr := MetadataTableStreamHeader{}\n\tif size == 0 {\n\t\treturn mdTableStreamHdr, nil\n\t}\n\n\tmdTableStreamHdrSize := uint32(binary.Size(mdTableStreamHdr))\n\terr := pe.structUnpack(&mdTableStreamHdr, off, mdTableStreamHdrSize)\n\tif err != nil {\n\t\treturn mdTableStreamHdr, err\n\t}\n\n\treturn mdTableStreamHdr, nil\n}\n\nfunc (pe *File) parseMetadataHeader(offset, size uint32) (MetadataHeader, error) {\n\tvar err error\n\tmh := MetadataHeader{}\n\n\tif mh.Signature, err = pe.ReadUint32(offset); err != nil {\n\t\treturn mh, err\n\t}\n\tif mh.MajorVersion, err = pe.ReadUint16(offset + 4); err != nil {\n\t\treturn mh, err\n\t}\n\tif mh.MinorVersion, err = pe.ReadUint16(offset + 6); err != nil {\n\t\treturn mh, err\n\t}\n\tif mh.ExtraData, err = pe.ReadUint32(offset + 8); err != nil {\n\t\treturn mh, err\n\t}\n\tif mh.VersionString, err = pe.ReadUint32(offset + 12); err != nil {\n\t\treturn mh, err\n\t}\n\tmh.Version, err = pe.getStringAtOffset(offset+16, mh.VersionString)\n\tif err != nil {\n\t\treturn mh, err\n\t}\n\n\toffset += 16 + mh.VersionString\n\tif mh.Flags, err = pe.ReadUint8(offset); err != nil {\n\t\treturn mh, err\n\t}\n\n\tif mh.Streams, err = pe.ReadUint16(offset + 2); err != nil {\n\t\treturn mh, err\n\t}\n\n\treturn mh, err\n}\n\n// The 15th directory entry of the PE header contains the RVA and size of the\n// runtime header in the image file. The runtime header, which contains all of\n// the runtime-specific data entries and other information, should reside in a\n// read-only section of the image file. The IL assembler puts the common\n// language runtime header in the .text section.\nfunc (pe *File) parseCLRHeaderDirectory(rva, size uint32) error {\n\n\tclrHeader := ImageCOR20Header{}\n\toffset := pe.GetOffsetFromRva(rva)\n\terr := pe.structUnpack(&clrHeader, offset, size)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpe.CLR.CLRHeader = clrHeader\n\tif clrHeader.MetaData.VirtualAddress == 0 || clrHeader.MetaData.Size == 0 {\n\t\treturn nil\n\t}\n\n\t// If we get a CLR header, we assume that this is enough\n\t// to say we have a CLR data to show even if parsing\n\t// other structures fails later.\n\tpe.HasCLR = true\n\n\tif pe.opts.OmitCLRMetadata {\n\t\treturn nil\n\t}\n\n\toffset = pe.GetOffsetFromRva(clrHeader.MetaData.VirtualAddress)\n\tmh, err := pe.parseMetadataHeader(offset, clrHeader.MetaData.Size)\n\tif err != nil {\n\t\treturn err\n\t}\n\tpe.CLR.MetadataHeader = mh\n\tpe.CLR.MetadataStreams = make(map[string][]byte)\n\toffset += 16 + mh.VersionString + 4\n\n\t// Immediately following the MetadataHeader is a series of Stream Headers.\n\t// A “stream” is to the metadata what a “section” is to the assembly. The\n\t// NumberOfStreams property indicates how many StreamHeaders to read.\n\tmdStreamHdrOff := uint32(0)\n\tmdStreamHdrSize := uint32(0)\n\tfor i := uint16(0); i < mh.Streams; i++ {\n\t\tsh := MetadataStreamHeader{}\n\t\tif sh.Offset, err = pe.ReadUint32(offset); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif sh.Size, err = pe.ReadUint32(offset + 4); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Name requires a special treatment.\n\t\toffset += 8\n\t\tfor j := uint32(0); j <= 32; j++ {\n\t\t\tvar c uint8\n\t\t\tif c, err = pe.ReadUint8(offset); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\toffset++\n\t\t\tif c == 0 && (j+1)%4 == 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif c != 0 {\n\t\t\t\tsh.Name += string(c)\n\t\t\t}\n\t\t}\n\n\t\t// The streams #~ and #- are mutually exclusive; that is, the metadata\n\t\t// structure of the module is either optimized or un-optimized; it\n\t\t// cannot be both at the same time or be something in between.\n\t\tif sh.Name == \"#~\" || sh.Name == \"#-\" {\n\t\t\tmdStreamHdrOff = sh.Offset\n\t\t\tmdStreamHdrSize = sh.Size\n\t\t}\n\n\t\trva = clrHeader.MetaData.VirtualAddress + sh.Offset\n\t\tstart := pe.GetOffsetFromRva(rva)\n\n\t\t// Some malformed/Corrupt PEs has invalid sizes on sh.\n\t\tmdStreamBytes := make([]byte, 0)\n\t\tif start+sh.Size <= uint32(len(pe.data)) {\n\t\t\tmdStreamBytes = pe.data[start : start+sh.Size]\n\t\t}\n\n\t\t// Save the stream into a map <string> []byte.\n\t\tpe.CLR.MetadataStreams[sh.Name] = mdStreamBytes\n\t\tpe.CLR.MetadataStreamHeaders = append(pe.CLR.MetadataStreamHeaders, sh)\n\t}\n\n\t// Get the Metadata Table Stream.\n\tif mdStreamHdrSize == 0 {\n\t\treturn nil\n\t}\n\t// The .Offset indicated by the stream header is an RVA relative to the\n\t// metadataDirectoryAddress in the CLRHeader.\n\trva = clrHeader.MetaData.VirtualAddress + mdStreamHdrOff\n\toffset = pe.GetOffsetFromRva(rva)\n\tmdTableStreamHdr, err := pe.parseMetadataStream(offset, mdStreamHdrSize)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tpe.CLR.MetadataTablesStreamHeader = mdTableStreamHdr\n\n\t// Get the size of indexes of #String\", \"#GUID\" and \"#Blob\" streams.\n\tpe.CLR.StringStreamIndexSize = pe.GetMetadataStreamIndexSize(StringStream)\n\tpe.CLR.GUIDStreamIndexSize = pe.GetMetadataStreamIndexSize(GUIDStream)\n\tpe.CLR.BlobStreamIndexSize = pe.GetMetadataStreamIndexSize(BlobStream)\n\n\t// This header is followed by a sequence of 4-byte unsigned integers\n\t// indicating the number of records in each table marked 1 in the MaskValid\n\t// bit vector.\n\toffset += uint32(binary.Size(mdTableStreamHdr))\n\tpe.CLR.MetadataTables = make(map[int]*MetadataTable)\n\tfor i := 0; i <= GenericParamConstraint; i++ {\n\t\tif IsBitSet(mdTableStreamHdr.MaskValid, i) {\n\t\t\tmdTable := MetadataTable{}\n\t\t\tmdTable.Name = MetadataTableIndexToString(i)\n\t\t\tmdTable.CountCols, err = pe.ReadUint32(offset)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\toffset += 4\n\t\t\tpe.CLR.MetadataTables[i] = &mdTable\n\t\t}\n\t}\n\n\t// Parse the metadata tables.\n\tfor tableIndex := 0; tableIndex <= GenericParamConstraint; tableIndex++ {\n\t\ttable, ok := pe.CLR.MetadataTables[tableIndex]\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tn := uint32(0)\n\t\tswitch tableIndex {\n\t\tcase Module: // 0x00\n\t\t\ttable.Content, n, err = pe.parseMetadataModuleTable(offset)\n\t\tcase TypeRef: // 0x01\n\t\t\ttable.Content, n, err = pe.parseMetadataTypeRefTable(offset)\n\t\tcase TypeDef: // 0x02\n\t\t\ttable.Content, n, err = pe.parseMetadataTypeDefTable(offset)\n\t\tcase Field: // 0x04\n\t\t\ttable.Content, n, err = pe.parseMetadataFieldTable(offset)\n\t\tcase MethodDef: // 0x06\n\t\t\ttable.Content, n, err = pe.parseMetadataMethodDefTable(offset)\n\t\tcase Param: // 0x08\n\t\t\ttable.Content, n, err = pe.parseMetadataParamTable(offset)\n\t\tcase InterfaceImpl: // 0x09\n\t\t\ttable.Content, n, err = pe.parseMetadataInterfaceImplTable(offset)\n\t\tcase MemberRef: // 0x0a\n\t\t\ttable.Content, n, err = pe.parseMetadataMemberRefTable(offset)\n\t\tcase Constant: // 0x0b\n\t\t\ttable.Content, n, err = pe.parseMetadataConstantTable(offset)\n\t\tcase CustomAttribute: // 0x0c\n\t\t\ttable.Content, n, err = pe.parseMetadataCustomAttributeTable(offset)\n\t\tcase FieldMarshal: // 0x0d\n\t\t\ttable.Content, n, err = pe.parseMetadataFieldMarshalTable(offset)\n\t\tcase DeclSecurity: // 0x0e\n\t\t\ttable.Content, n, err = pe.parseMetadataDeclSecurityTable(offset)\n\t\tcase ClassLayout: // 0x0f\n\t\t\ttable.Content, n, err = pe.parseMetadataClassLayoutTable(offset)\n\t\tcase FieldLayout: // 0x10\n\t\t\ttable.Content, n, err = pe.parseMetadataFieldLayoutTable(offset)\n\t\tcase StandAloneSig: // 0x11\n\t\t\ttable.Content, n, err = pe.parseMetadataStandAloneSignTable(offset)\n\t\tcase EventMap: // 0x12\n\t\t\ttable.Content, n, err = pe.parseMetadataEventMapTable(offset)\n\t\tcase Event: // 0x14\n\t\t\ttable.Content, n, err = pe.parseMetadataEventTable(offset)\n\t\tcase PropertyMap: // 0x15\n\t\t\ttable.Content, n, err = pe.parseMetadataPropertyMapTable(offset)\n\t\tcase Property: // 0x17\n\t\t\ttable.Content, n, err = pe.parseMetadataPropertyTable(offset)\n\t\tcase MethodSemantics: // 0x18\n\t\t\ttable.Content, n, err = pe.parseMetadataMethodSemanticsTable(offset)\n\t\tcase MethodImpl: // 0x19\n\t\t\ttable.Content, n, err = pe.parseMetadataMethodImplTable(offset)\n\t\tcase ModuleRef: // 0x1a\n\t\t\ttable.Content, n, err = pe.parseMetadataModuleRefTable(offset)\n\t\tcase TypeSpec: // 0x1b\n\t\t\ttable.Content, n, err = pe.parseMetadataTypeSpecTable(offset)\n\t\tcase ImplMap: // 0x1c\n\t\t\ttable.Content, n, err = pe.parseMetadataImplMapTable(offset)\n\t\tcase FieldRVA: // 0x1d\n\t\t\ttable.Content, n, err = pe.parseMetadataFieldRVATable(offset)\n\t\tcase Assembly: // 0x20\n\t\t\ttable.Content, n, err = pe.parseMetadataAssemblyTable(offset)\n\t\tcase AssemblyRef: // 0x23\n\t\t\ttable.Content, n, err = pe.parseMetadataAssemblyRefTable(offset)\n\t\tcase ExportedType: // 0x27\n\t\t\ttable.Content, n, err = pe.parseMetadataExportedTypeTable(offset)\n\t\tcase ManifestResource: // 0x28\n\t\t\ttable.Content, n, err = pe.parseMetadataManifestResourceTable(offset)\n\t\tcase NestedClass: // 0x29\n\t\t\ttable.Content, n, err = pe.parseMetadataNestedClassTable(offset)\n\t\tcase GenericParam: // 0x2a\n\t\t\ttable.Content, n, err = pe.parseMetadataGenericParamTable(offset)\n\t\tcase MethodSpec: // 0x2b\n\t\t\ttable.Content, n, err = pe.parseMetadataMethodSpecTable(offset)\n\t\tcase GenericParamConstraint: // 0x2c\n\t\t\ttable.Content, n, err = pe.parseMetadataGenericParamConstraintTable(offset)\n\t\tcase FileMD: // 0x26\n\t\t\ttable.Content, n, err = pe.parseMetadataFileTable(offset)\n\t\tdefault:\n\t\t\tpe.logger.Warnf(\"unhandled metadata table %d %s offset 0x%x cols %d\",\n\t\t\t\ttableIndex, MetadataTableIndexToString(tableIndex), offset, table.CountCols)\n\t\t}\n\t\tif err != nil {\n\t\t\tpe.logger.Warnf(\"parsing metadata table %s failed with %v\",\n\t\t\t\tMetadataTableIndexToString(tableIndex), err)\n\t\t}\n\t\toffset += n\n\n\t}\n\n\treturn nil\n}\n\n// String returns a string interpretation of a COMImageFlags type.\nfunc (flags COMImageFlagsType) String() []string {\n\tCOMImageFlags := map[COMImageFlagsType]string{\n\t\tCOMImageFlagsILOnly:           \"IL Only\",\n\t\tCOMImageFlags32BitRequired:    \"32-Bit Required\",\n\t\tCOMImageFlagILLibrary:         \"IL Library\",\n\t\tCOMImageFlagsStrongNameSigned: \"Strong Name Signed\",\n\t\tCOMImageFlagsNativeEntrypoint: \"Native Entrypoint\",\n\t\tCOMImageFlagsTrackDebugData:   \"Track Debug Data\",\n\t\tCOMImageFlags32BitPreferred:   \"32-Bit Preferred\",\n\t}\n\n\tvar values []string\n\tfor k, v := range COMImageFlags {\n\t\tif (k & flags) == k {\n\t\t\tvalues = append(values, v)\n\t\t}\n\t}\n\n\treturn values\n}\n"
  },
  {
    "path": "dotnet_helper.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nconst (\n\t// these are intentionally made so they do not collide with StringStream, GUIDStream, and BlobStream\n\t// they are used only for the getCodedIndexSize function\n\tidxStringStream = iota + 100\n\tidxGUIDStream\n\tidxBlobStream\n)\n\ntype codedidx struct {\n\ttagbits uint8\n\tidx     []int\n}\n\nvar (\n\tidxTypeDefOrRef        = codedidx{tagbits: 2, idx: []int{TypeDef, TypeRef, TypeSpec}}\n\tidxResolutionScope     = codedidx{tagbits: 2, idx: []int{Module, ModuleRef, AssemblyRef, TypeRef}}\n\tidxMemberRefParent     = codedidx{tagbits: 3, idx: []int{TypeDef, TypeRef, ModuleRef, MethodDef, TypeSpec}}\n\tidxHasConstant         = codedidx{tagbits: 2, idx: []int{Field, Param, Property}}\n\tidxHasCustomAttributes = codedidx{tagbits: 5, idx: []int{MethodDef, Field, TypeRef, TypeDef, Param, InterfaceImpl, MemberRef, Module, DeclSecurity, Property, Event, StandAloneSig, ModuleRef, TypeSpec, Assembly, AssemblyRef, FileMD, ExportedType, ManifestResource, GenericParam, GenericParamConstraint, MethodSpec}}\n\tidxCustomAttributeType = codedidx{tagbits: 3, idx: []int{MethodDef, MemberRef}}\n\tidxHasFieldMarshall    = codedidx{tagbits: 1, idx: []int{Field, Param}}\n\tidxHasDeclSecurity     = codedidx{tagbits: 2, idx: []int{TypeDef, MethodDef, Assembly}}\n\tidxHasSemantics        = codedidx{tagbits: 1, idx: []int{Event, Property}}\n\tidxMethodDefOrRef      = codedidx{tagbits: 1, idx: []int{MethodDef, MemberRef}}\n\tidxMemberForwarded     = codedidx{tagbits: 1, idx: []int{Field, MethodDef}}\n\tidxImplementation      = codedidx{tagbits: 2, idx: []int{FileMD, AssemblyRef, ExportedType}}\n\tidxTypeOrMethodDef     = codedidx{tagbits: 1, idx: []int{TypeDef, MethodDef}}\n\n\tidxField        = codedidx{tagbits: 0, idx: []int{Field}}\n\tidxMethodDef    = codedidx{tagbits: 0, idx: []int{MethodDef}}\n\tidxParam        = codedidx{tagbits: 0, idx: []int{Param}}\n\tidxTypeDef      = codedidx{tagbits: 0, idx: []int{TypeDef}}\n\tidxEvent        = codedidx{tagbits: 0, idx: []int{Event}}\n\tidxProperty     = codedidx{tagbits: 0, idx: []int{Property}}\n\tidxModuleRef    = codedidx{tagbits: 0, idx: []int{ModuleRef}}\n\tidxGenericParam = codedidx{tagbits: 0, idx: []int{GenericParam}}\n\n\tidxString = codedidx{tagbits: 0, idx: []int{idxStringStream}}\n\tidxBlob   = codedidx{tagbits: 0, idx: []int{idxBlobStream}}\n\tidxGUID   = codedidx{tagbits: 0, idx: []int{idxGUIDStream}}\n)\n\nfunc (pe *File) getCodedIndexSize(tagbits uint32, idx ...int) uint32 {\n\t// special case String/GUID/Blob streams\n\tswitch idx[0] {\n\tcase int(idxStringStream):\n\t\treturn uint32(pe.GetMetadataStreamIndexSize(StringStream))\n\tcase int(idxGUIDStream):\n\t\treturn uint32(pe.GetMetadataStreamIndexSize(GUIDStream))\n\tcase int(idxBlobStream):\n\t\treturn uint32(pe.GetMetadataStreamIndexSize(BlobStream))\n\t}\n\n\t// now deal with coded indices or single table\n\tvar maxIndex16 uint32 = 1 << (16 - tagbits)\n\tvar maxColumnCount uint32\n\tfor _, tblidx := range idx {\n\t\ttbl, ok := pe.CLR.MetadataTables[tblidx]\n\t\tif ok {\n\t\t\tif tbl.CountCols > maxColumnCount {\n\t\t\t\tmaxColumnCount = tbl.CountCols\n\t\t\t}\n\t\t}\n\t}\n\tif maxColumnCount >= maxIndex16 {\n\t\treturn 4\n\t}\n\treturn 2\n}\n\nfunc (pe *File) readFromMetadataStream(cidx codedidx, off uint32, out *uint32) (uint32, error) {\n\tindexSize := pe.getCodedIndexSize(uint32(cidx.tagbits), cidx.idx...)\n\tvar data uint32\n\tvar err error\n\tswitch indexSize {\n\tcase 2:\n\t\td, err := pe.ReadUint16(off)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tdata = uint32(d)\n\tcase 4:\n\t\tdata, err = pe.ReadUint32(off)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\n\t*out = data\n\treturn uint32(indexSize), nil\n}\n"
  },
  {
    "path": "dotnet_metadata_tables.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\n// the struct definition and comments are from the ECMA-335 spec 6th edition\n// https://www.ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf\n\n// Module 0x00\ntype ModuleTableRow struct {\n\t// a 2-byte value, reserved, shall be zero\n\tGeneration uint16 `json:\"generation\"`\n\t// an index into the String heap\n\tName uint32 `json:\"name\"`\n\t// an index into the Guid heap; simply a Guid used to distinguish between\n\t// two versions of the same module\n\tMvid uint32 `json:\"mvid\"`\n\t// an index into the Guid heap; reserved, shall be zero\n\tEncID uint32 `json:\"enc_id\"`\n\t// an index into the Guid heap; reserved, shall be zero\n\tEncBaseID uint32 `json:\"enc_base_id\"`\n}\n\n// Module 0x00\nfunc (pe *File) parseMetadataModuleTable(off uint32) ([]ModuleTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[Module].CountCols)\n\trows := make([]ModuleTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Generation, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxGUID, off, &rows[i].Mvid); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxGUID, off, &rows[i].EncID); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxGUID, off, &rows[i].EncBaseID); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// TypeRef 0x01\ntype TypeRefTableRow struct {\n\t// an index into a Module, ModuleRef, AssemblyRef or TypeRef table, or null;\n\t// more precisely, a ResolutionScope (§II.24.2.6) coded index.\n\tResolutionScope uint32 `json:\"resolution_scope\"`\n\t// an index into the String heap\n\tTypeName uint32 `json:\"type_name\"`\n\t// an index into the String heap\n\tTypeNamespace uint32 `json:\"type_namespace\"`\n}\n\n// TypeRef 0x01\nfunc (pe *File) parseMetadataTypeRefTable(off uint32) ([]TypeRefTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[TypeRef].CountCols)\n\trows := make([]TypeRefTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxResolutionScope, off, &rows[i].ResolutionScope); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].TypeName); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].TypeNamespace); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// TypeDef 0x02\ntype TypeDefTableRow struct {\n\t// a 4-byte bitmask of type TypeAttributes, §II.23.1.15\n\tFlags uint32 `json:\"flags\"`\n\t// an index into the String heap\n\tTypeName uint32 `json:\"type_name\"`\n\t// an index into the String heap\n\tTypeNamespace uint32 `json:\"type_namespace\"`\n\t// an index into the TypeDef, TypeRef, or TypeSpec table; more precisely,\n\t// a TypeDefOrRef (§II.24.2.6) coded index\n\tExtends uint32 `json:\"extends\"`\n\t// an index into the Field table; it marks the first of a contiguous run\n\t// of Fields owned by this Type\n\tFieldList uint32 `json:\"field_list\"`\n\t// an index into the MethodDef table; it marks the first of a contiguous\n\t// run of Methods owned by this Type\n\tMethodList uint32 `json:\"method_list\"`\n}\n\n// TypeDef 0x02\nfunc (pe *File) parseMetadataTypeDefTable(off uint32) ([]TypeDefTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[TypeDef].CountCols)\n\trows := make([]TypeDefTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Flags, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].TypeName); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].TypeNamespace); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeDefOrRef, off, &rows[i].Extends); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxField, off, &rows[i].FieldList); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxMethodDef, off, &rows[i].MethodList); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// Field 0x04\ntype FieldTableRow struct {\n\t// a 2-byte bitmask of type FieldAttributes, §II.23.1.5\n\tFlags uint16 `json:\"flags\"`\n\t// an index into the String heap\n\tName uint32 `json:\"name\"`\n\t// an index into the Blob heap\n\tSignature uint32 `json:\"signature\"`\n}\n\n// Field 0x04\nfunc (pe *File) parseMetadataFieldTable(off uint32) ([]FieldTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[Field].CountCols)\n\trows := make([]FieldTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Flags, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].Signature); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// MethodDef 0x06\ntype MethodDefTableRow struct {\n\t// a 4-byte constant\n\tRVA uint32 `json:\"rva\"`\n\t// a 2-byte bitmask of type MethodImplAttributes, §II.23.1.10\n\tImplFlags uint16 `json:\"impl_flags\"`\n\t// a 2-byte bitmask of type MethodAttributes, §II.23.1.10\n\tFlags uint16 `json:\"flags\"`\n\t// an index into the String heap\n\tName uint32 `json:\"name\"`\n\t// an index into the Blob heap\n\tSignature uint32 `json:\"signature\"`\n\t// an index into the Param table\n\tParamList uint32 `json:\"param_list\"`\n}\n\n// MethodDef 0x06\nfunc (pe *File) parseMetadataMethodDefTable(off uint32) ([]MethodDefTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[MethodDef].CountCols)\n\trows := make([]MethodDefTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].RVA, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif rows[i].ImplFlags, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif rows[i].Flags, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].Signature); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxParam, off, &rows[i].ParamList); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// Param 0x08\ntype ParamTableRow struct {\n\t// a 2-byte bitmask of type ParamAttributes, §II.23.1.13\n\tFlags uint16 `json:\"flags\"`\n\t// a 2-byte constant\n\tSequence uint16 `json:\"sequence\"`\n\t// an index into the String heap\n\tName uint32 `json:\"name\"`\n}\n\n// Param 0x08\nfunc (pe *File) parseMetadataParamTable(off uint32) ([]ParamTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[Param].CountCols)\n\trows := make([]ParamTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Flags, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif rows[i].Sequence, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// InterfaceImpl 0x09\ntype InterfaceImplTableRow struct {\n\t// an index into the TypeDef table\n\tClass uint32 `json:\"class\"`\n\t// an index into the TypeDef, TypeRef, or TypeSpec table; more precisely,\n\t// a TypeDefOrRef (§II.24.2.6) coded index\n\tInterface uint32 `json:\"interface\"`\n}\n\n// InterfaceImpl 0x09\nfunc (pe *File) parseMetadataInterfaceImplTable(off uint32) ([]InterfaceImplTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[InterfaceImpl].CountCols)\n\trows := make([]InterfaceImplTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeDef, off, &rows[i].Class); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeDefOrRef, off, &rows[i].Interface); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// MembersRef 0x0a\ntype MemberRefTableRow struct {\n\t// an index into the MethodDef, ModuleRef,TypeDef, TypeRef, or TypeSpec\n\t// tables; more precisely, a MemberRefParent (§II.24.2.6) coded index\n\tClass uint32 `json:\"class\"`\n\t// // an index into the String heap\n\tName uint32 `json:\"name\"`\n\t// an index into the Blob heap\n\tSignature uint32 `json:\"signature\"`\n}\n\n// MembersRef 0x0a\nfunc (pe *File) parseMetadataMemberRefTable(off uint32) ([]MemberRefTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[MemberRef].CountCols)\n\trows := make([]MemberRefTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxMemberRefParent, off, &rows[i].Class); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].Signature); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t}\n\treturn rows, n, nil\n}\n\n// Constant 0x0b\ntype ConstantTableRow struct {\n\t// a 1-byte constant, followed by a 1-byte padding zero\n\tType uint8 `json:\"type\"`\n\t// padding zero\n\tPadding uint8 `json:\"padding\"`\n\t// padding zero\n\t// an index into the Param, Field, or Property table; more precisely,\n\t// a HasConstant (§II.24.2.6) coded index\n\tParent uint32 `json:\"parent\"`\n\t// an index into the Blob heap\n\tValue uint32 `json:\"value\"`\n}\n\n// Constant 0x0b\nfunc (pe *File) parseMetadataConstantTable(off uint32) ([]ConstantTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[Constant].CountCols)\n\trows := make([]ConstantTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Type, err = pe.ReadUint8(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 1\n\t\tn += 1\n\n\t\tif rows[i].Padding, err = pe.ReadUint8(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 1\n\t\tn += 1\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxHasConstant, off, &rows[i].Parent); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].Value); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// CustomAttribute 0x0c\ntype CustomAttributeTableRow struct {\n\t// an index into a metadata table that has an associated HasCustomAttribute\n\t// (§II.24.2.6) coded index\n\tParent uint32 `json:\"parent\"`\n\t// an index into the MethodDef or MemberRef table; more precisely,\n\t// a CustomAttributeType (§II.24.2.6) coded index\n\tType uint32 `json:\"type\"`\n\t// an index into the Blob heap\n\tValue uint32 `json:\"value\"`\n}\n\n// CustomAttribute 0x0c\nfunc (pe *File) parseMetadataCustomAttributeTable(off uint32) ([]CustomAttributeTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[CustomAttribute].CountCols)\n\trows := make([]CustomAttributeTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxHasCustomAttributes, off, &rows[i].Parent); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxCustomAttributeType, off, &rows[i].Type); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].Value); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// FieldMarshal 0x0d\ntype FieldMarshalTableRow struct {\n\t// an index into Field or Param table; more precisely,\n\t// a HasFieldMarshal (§II.24.2.6) coded index\n\tParent uint32 `json:\"parent\"`\n\t// an index into the Blob heap\n\tNativeType uint32 `json:\"native_type\"`\n}\n\n// FieldMarshal 0x0d\nfunc (pe *File) parseMetadataFieldMarshalTable(off uint32) ([]FieldMarshalTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[FieldMarshal].CountCols)\n\trows := make([]FieldMarshalTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxHasFieldMarshall, off, &rows[i].Parent); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].NativeType); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// DeclSecurity 0x0e\ntype DeclSecurityTableRow struct {\n\t// a 2-byte value\n\tAction uint16 `json:\"action\"`\n\t// an index into the TypeDef, MethodDef, or Assembly table;\n\t// more precisely, a HasDeclSecurity (§II.24.2.6) coded index\n\tParent uint32 `json:\"parent\"`\n\t// // an index into the Blob heap\n\tPermissionSet uint32 `json:\"permission_set\"`\n}\n\n// DeclSecurity 0x0e\nfunc (pe *File) parseMetadataDeclSecurityTable(off uint32) ([]DeclSecurityTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[DeclSecurity].CountCols)\n\trows := make([]DeclSecurityTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Action, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxHasDeclSecurity, off, &rows[i].Parent); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].PermissionSet); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// ClassLayout 0x0f\ntype ClassLayoutTableRow struct {\n\t// a 2-byte constant\n\tPackingSize uint16 `json:\"packing_size\"`\n\t// a 4-byte constant\n\tClassSize uint32 `json:\"class_size\"`\n\t// an index into the TypeDef table\n\tParent uint32 `json:\"parent\"`\n}\n\n// ClassLayout 0x0f\nfunc (pe *File) parseMetadataClassLayoutTable(off uint32) ([]ClassLayoutTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[ClassLayout].CountCols)\n\trows := make([]ClassLayoutTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].PackingSize, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif rows[i].ClassSize, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeDef, off, &rows[i].Parent); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// FieldLayout 0x10\ntype FieldLayoutTableRow struct {\n\tOffset uint32 `json:\"offset\"` // a 4-byte constant\n\tField  uint32 `json:\"field\"`  // an index into the Field table\n}\n\n// FieldLayout 0x10\nfunc (pe *File) parseMetadataFieldLayoutTable(off uint32) ([]FieldLayoutTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[FieldLayout].CountCols)\n\trows := make([]FieldLayoutTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Offset, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxField, off, &rows[i].Field); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// StandAloneSig 0x11\ntype StandAloneSigTableRow struct {\n\tSignature uint32 `json:\"signature\"` // an index into the Blob heap\n}\n\n// StandAloneSig 0x11\nfunc (pe *File) parseMetadataStandAloneSignTable(off uint32) ([]StandAloneSigTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[StandAloneSig].CountCols)\n\trows := make([]StandAloneSigTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].Signature); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// EventMap 0x12\ntype EventMapTableRow struct {\n\t// an index into the TypeDef table\n\tParent uint32 `json:\"parent\"`\n\t// an index into the Event table\n\tEventList uint32 `json:\"event_list\"`\n}\n\n// EventMap 0x12\nfunc (pe *File) parseMetadataEventMapTable(off uint32) ([]EventMapTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[EventMap].CountCols)\n\trows := make([]EventMapTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeDef, off, &rows[i].Parent); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxEvent, off, &rows[i].EventList); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t}\n\treturn rows, n, nil\n}\n\n// Event 0x14\ntype EventTableRow struct {\n\t// a 2-byte bitmask of type EventAttributes, §II.23.1.4\n\tEventFlags uint16 `json:\"event_flags\"`\n\t// an index into the String heap\n\tName uint32 `json:\"name\"`\n\t// an index into a TypeDef, a TypeRef, or TypeSpec table; more precisely,\n\t// a TypeDefOrRef (§II.24.2.6) coded index)\n\tEventType uint32 `json:\"event_type\"`\n}\n\n// Event 0x14\nfunc (pe *File) parseMetadataEventTable(off uint32) ([]EventTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[Event].CountCols)\n\trows := make([]EventTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].EventFlags, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeDefOrRef, off, &rows[i].EventType); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// PropertyMap 0x15\ntype PropertyMapTableRow struct {\n\t// an index\tinto the TypeDef table\n\tParent uint32 `json:\"parent\"`\n\t// an index into the Property table\n\tPropertyList uint32 `json:\"property_list\"`\n}\n\n// PropertyMap 0x15\nfunc (pe *File) parseMetadataPropertyMapTable(off uint32) ([]PropertyMapTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[PropertyMap].CountCols)\n\trows := make([]PropertyMapTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeDef, off, &rows[i].Parent); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxProperty, off, &rows[i].PropertyList); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// Property 0x17\ntype PropertyTableRow struct {\n\t// a 2-byte bitmask of type PropertyAttributes, §II.23.1.14\n\tFlags uint16 `json:\"flags\"`\n\t// an index into the String heap\n\tName uint32 `json:\"name\"`\n\t// an index into the Blob heap\n\tType uint32 `json:\"type\"`\n}\n\n// Property 0x17\nfunc (pe *File) parseMetadataPropertyTable(off uint32) ([]PropertyTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[Property].CountCols)\n\trows := make([]PropertyTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Flags, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].Type); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// MethodSemantics 0x18\ntype MethodSemanticsTableRow struct {\n\t// a 2-byte bitmask of type MethodSemanticsAttributes, §II.23.1.12\n\tSemantics uint16 `json:\"semantics\"`\n\t// an index into the MethodDef table\n\tMethod uint32 `json:\"method\"`\n\t// an index into the Event or Property table; more precisely,\n\t// a HasSemantics (§II.24.2.6) coded index\n\tAssociation uint32 `json:\"association\"`\n}\n\n// MethodSemantics 0x18\nfunc (pe *File) parseMetadataMethodSemanticsTable(off uint32) ([]MethodSemanticsTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[MethodSemantics].CountCols)\n\trows := make([]MethodSemanticsTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Semantics, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxMethodDef, off, &rows[i].Method); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxHasSemantics, off, &rows[i].Association); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// MethodImpl 0x19\ntype MethodImplTableRow struct {\n\t// an index into the TypeDef table\n\tClass uint32 `json:\"class\"`\n\t// an index into the MethodDef or MemberRef table; more precisely, a\n\t// MethodDefOrRef (§II.24.2.6) coded index\n\tMethodBody uint32 `json:\"method_body\"`\n\t// // an index into the MethodDef or MemberRef table; more precisely, a\n\t// MethodDefOrRef (§II.24.2.6) coded index\n\tMethodDeclaration uint32 `json:\"method_declaration\"`\n}\n\n// MethodImpl 0x19\nfunc (pe *File) parseMetadataMethodImplTable(off uint32) ([]MethodImplTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[MethodImpl].CountCols)\n\trows := make([]MethodImplTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeDef, off, &rows[i].Class); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxMethodDefOrRef, off, &rows[i].MethodBody); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxMethodDefOrRef, off, &rows[i].MethodDeclaration); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// ModuleRef 0x1a\ntype ModuleRefTableRow struct {\n\t// an index into the String heap\n\tName uint32 `json:\"name\"`\n}\n\n// ModuleRef 0x1a\nfunc (pe *File) parseMetadataModuleRefTable(off uint32) ([]ModuleRefTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[ModuleRef].CountCols)\n\trows := make([]ModuleRefTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// TypeSpec 0x1b\ntype TypeSpecTableRow struct {\n\t// an index into the Blob heap\n\tSignature uint32 `json:\"signature\"`\n}\n\n// TypeSpec 0x1b\nfunc (pe *File) parseMetadataTypeSpecTable(off uint32) ([]TypeSpecTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[TypeSpec].CountCols)\n\trows := make([]TypeSpecTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].Signature); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// ImplMap 0x1c\ntype ImplMapTableRow struct {\n\t// a 2-byte bitmask of type PInvokeAttributes, §23.1.8\n\tMappingFlags uint16 `json:\"mapping_flags\"`\n\t// an index into the Field or MethodDef table; more precisely,\n\t// a MemberForwarded (§II.24.2.6) coded index)\n\tMemberForwarded uint32 `json:\"member_forwarded\"`\n\t// an index into the String heap\n\tImportName uint32 `json:\"import_name\"`\n\t// an index into the ModuleRef table\n\tImportScope uint32 `json:\"import_scope\"`\n}\n\n// ImplMap 0x1c\nfunc (pe *File) parseMetadataImplMapTable(off uint32) ([]ImplMapTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[ImplMap].CountCols)\n\trows := make([]ImplMapTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].MappingFlags, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxMemberForwarded, off, &rows[i].MemberForwarded); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].ImportName); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxModuleRef, off, &rows[i].ImportScope); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// FieldRVA 0x1d\ntype FieldRVATableRow struct {\n\t// 4-byte constant\n\tRVA uint32 `json:\"rva\"`\n\t// an index into Field table\n\tField uint32 `json:\"field\"`\n}\n\n// FieldRVA 0x1d\nfunc (pe *File) parseMetadataFieldRVATable(off uint32) ([]FieldRVATableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[FieldRVA].CountCols)\n\trows := make([]FieldRVATableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].RVA, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxField, off, &rows[i].Field); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// Assembly 0x20\ntype AssemblyTableRow struct {\n\t// a 4-byte constant of type AssemblyHashAlgorithm, §II.23.1.1\n\tHashAlgId uint32 `json:\"hash_alg_id\"`\n\t// a 2-byte constant\n\tMajorVersion uint16 `json:\"major_version\"`\n\t// a 2-byte constant\n\tMinorVersion uint16 `json:\"minor_version\"`\n\t// a 2-byte constant\n\tBuildNumber uint16 `json:\"build_number\"`\n\t// a 2-byte constant\n\tRevisionNumber uint16 `json:\"revision_number\"`\n\t// a 4-byte bitmask of type AssemblyFlags, §II.23.1.2\n\tFlags uint32 `json:\"flags\"`\n\t// an index into the Blob heap\n\tPublicKey uint32 `json:\"public_key\"`\n\t// an index into the String heap\n\tName uint32 `json:\"name\"`\n\t// an index into the String heap\n\tCulture uint32 `json:\"culture\"`\n}\n\n// Assembly 0x20\nfunc (pe *File) parseMetadataAssemblyTable(off uint32) ([]AssemblyTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[Assembly].CountCols)\n\trows := make([]AssemblyTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].HashAlgId, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif rows[i].MajorVersion, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif rows[i].MinorVersion, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif rows[i].BuildNumber, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif rows[i].RevisionNumber, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif rows[i].Flags, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].PublicKey); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Culture); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// AssemblyProcessor 0x21\ntype AssemblyProcessorTableRow struct {\n\tProcessor uint32 `json:\"processor\"` // a 4-byte constant\n}\n\n// AssemblyOS 0x22\ntype AssemblyOSTableRow struct {\n\tOSPlatformID   uint32 `json:\"os_platform_id\"`   // a 4-byte constant\n\tOSMajorVersion uint32 `json:\"os_major_version\"` // a 4-byte constant\n\tOSMinorVersion uint32 `json:\"os_minor_version\"` // a 4-byte constant\n}\n\n// AssemblyRef 0x23\ntype AssemblyRefTableRow struct {\n\tMajorVersion     uint16 `json:\"major_version\"`       // a 2-byte constant\n\tMinorVersion     uint16 `json:\"minor_version\"`       // a 2-byte constant\n\tBuildNumber      uint16 `json:\"build_number\"`        // a 2-byte constant\n\tRevisionNumber   uint16 `json:\"revision_number\"`     // a 2-byte constant\n\tFlags            uint32 `json:\"flags\"`               // a 4-byte bitmask of type AssemblyFlags, §II.23.1.2\n\tPublicKeyOrToken uint32 `json:\"public_key_or_token\"` // an index into the Blob heap, indicating the public key or token that identifies the author of this Assembly\n\tName             uint32 `json:\"name\"`                // an index into the String heap\n\tCulture          uint32 `json:\"culture\"`             // an index into the String heap\n\tHashValue        uint32 `json:\"hash_value\"`          // an index into the Blob heap\n}\n\n// AssemblyRef 0x23\nfunc (pe *File) parseMetadataAssemblyRefTable(off uint32) ([]AssemblyRefTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[AssemblyRef].CountCols)\n\trows := make([]AssemblyRefTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].MajorVersion, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif rows[i].MinorVersion, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif rows[i].BuildNumber, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif rows[i].RevisionNumber, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif rows[i].Flags, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].PublicKeyOrToken); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Culture); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].HashValue); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// AssemblyRefProcessor 0x24\ntype AssemblyRefProcessorTableRow struct {\n\tProcessor   uint32 `json:\"processor\"`    // a 4-byte constant\n\tAssemblyRef uint32 `json:\"assembly_ref\"` // an index into the AssemblyRef table\n}\n\n// AssemblyRefOS 0x25\ntype AssemblyRefOSTableRow struct {\n\tOSPlatformID   uint32 `json:\"os_platform_id\"`   // a 4-byte constant\n\tOSMajorVersion uint32 `json:\"os_major_version\"` // a 4-byte constant\n\tOSMinorVersion uint32 `json:\"os_minor_version\"` // a 4-byte constan)\n\tAssemblyRef    uint32 `json:\"assembly_ref\"`     // an index into the AssemblyRef table\n}\n\n// ExportedType 0x27\ntype ExportedTypeTableRow struct {\n\tFlags          uint32 `json:\"flags\"`          // a 4-byte bitmask of type TypeAttributes, §II.23.1.15\n\tTypeDefId      uint32 `json:\"type_def_id\"`    // a 4-byte index into a TypeDef table of another module in this Assembly\n\tTypeName       uint32 `json:\"type_name\"`      // an index into the String heap\n\tTypeNamespace  uint32 `json:\"type_namespace\"` // an index into the String heap\n\tImplementation uint32 `json:\"implementation\"` // an index (more precisely, an Implementation (§II.24.2.6) coded index\n}\n\n// ExportedType 0x27\nfunc (pe *File) parseMetadataExportedTypeTable(off uint32) ([]ExportedTypeTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[ExportedType].CountCols)\n\trows := make([]ExportedTypeTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Flags, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif rows[i].TypeDefId, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].TypeName); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].TypeNamespace); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxImplementation, off, &rows[i].Implementation); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// ManifestResource 0x28\ntype ManifestResourceTableRow struct {\n\tOffset         uint32 `json:\"offset\"`         // a 4-byte constant\n\tFlags          uint32 `json:\"flags\"`          // a 4-byte bitmask of type ManifestResourceAttributes, §II.23.1.9\n\tName           uint32 `json:\"name\"`           // an index into the String heap\n\tImplementation uint32 `json:\"implementation\"` // an index into a File table, a AssemblyRef table, or null; more precisely, an Implementation (§II.24.2.6) coded index\n}\n\n// ManifestResource 0x28\nfunc (pe *File) parseMetadataManifestResourceTable(off uint32) ([]ManifestResourceTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[ManifestResource].CountCols)\n\trows := make([]ManifestResourceTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Offset, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif rows[i].Flags, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxImplementation, off, &rows[i].Implementation); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// NestedClass 0x29\ntype NestedClassTableRow struct {\n\tNestedClass    uint32 `json:\"nested_class\"`    // an index into the TypeDef table\n\tEnclosingClass uint32 `json:\"enclosing_class\"` // an index into the TypeDef table\n}\n\n// NestedClass 0x29\nfunc (pe *File) parseMetadataNestedClassTable(off uint32) ([]NestedClassTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[NestedClass].CountCols)\n\trows := make([]NestedClassTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeDef, off, &rows[i].NestedClass); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeDef, off, &rows[i].EnclosingClass); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// GenericParam 0x2a\ntype GenericParamTableRow struct {\n\tNumber uint16 `json:\"number\"` // the 2-byte index of the generic parameter, numbered left-to-right, from zero\n\tFlags  uint16 `json:\"flags\"`  // a 2-byte bitmask of type GenericParamAttributes, §II.23.1.7\n\tOwner  uint32 `json:\"owner\"`  // an index into the TypeDef or MethodDef table, specifying the Type or Method to which this generic parameter applies; more precisely, a TypeOrMethodDef (§II.24.2.6) coded index\n\tName   uint32 `json:\"name\"`   // a non-null index into the String heap, giving the name for the generic parameter\n}\n\n// GenericParam 0x2a\nfunc (pe *File) parseMetadataGenericParamTable(off uint32) ([]GenericParamTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[GenericParam].CountCols)\n\trows := make([]GenericParamTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Number, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\t\tif rows[i].Flags, err = pe.ReadUint16(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 2\n\t\tn += 2\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeOrMethodDef, off, &rows[i].Owner); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// MethodSpec 0x2b\ntype MethodSpecTableRow struct {\n\tMethod        uint32 `json:\"method\"`        // an index into the MethodDef or MemberRef table, specifying to which generic method this row refers; that is, which generic method this row is an instantiation of; more precisely, a MethodDefOrRef (§II.24.2.6) coded index\n\tInstantiation uint32 `json:\"instantiation\"` // an index into the Blob heap\n}\n\n// MethodSpec 0x2b\nfunc (pe *File) parseMetadataMethodSpecTable(off uint32) ([]MethodSpecTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[MethodSpec].CountCols)\n\trows := make([]MethodSpecTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxMethodDefOrRef, off, &rows[i].Method); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].Instantiation); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// GenericParamConstraint 0x2c\ntype GenericParamConstraintTableRow struct {\n\tOwner      uint32 `json:\"owner\"`      // an index into the GenericParam table, specifying to which generic parameter this row refers\n\tConstraint uint32 `json:\"constraint\"` // an index into the TypeDef, TypeRef, or TypeSpec tables, specifying from which class this generic parameter is constrained to derive; or which interface this generic parameter is constrained to implement; more precisely, a TypeDefOrRef (§II.24.2.6) coded index\n}\n\n// GenericParamConstraint 0x2c\nfunc (pe *File) parseMetadataGenericParamConstraintTable(off uint32) ([]GenericParamConstraintTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[GenericParamConstraint].CountCols)\n\trows := make([]GenericParamConstraintTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif indexSize, err = pe.readFromMetadataStream(idxGenericParam, off, &rows[i].Owner); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxTypeDefOrRef, off, &rows[i].Constraint); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n\n// File 0x26\ntype FileTableRow struct {\n\tFlags     uint32 `json:\"flags\"`      // a 4-byte bitmask of type FileAttributes, §II.23.1.6\n\tName      uint32 `json:\"name\"`       // an index into the String heap\n\tHashValue uint32 `json:\"hash_value\"` // an index into the Blob heap\n}\n\n// File 0x26\nfunc (pe *File) parseMetadataFileTable(off uint32) ([]FileTableRow, uint32, error) {\n\tvar err error\n\tvar indexSize uint32\n\tvar n uint32\n\n\trowCount := int(pe.CLR.MetadataTables[FileMD].CountCols)\n\trows := make([]FileTableRow, rowCount)\n\tfor i := 0; i < rowCount; i++ {\n\t\tif rows[i].Flags, err = pe.ReadUint32(off); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += 4\n\t\tn += 4\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxString, off, &rows[i].Name); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\n\t\tif indexSize, err = pe.readFromMetadataStream(idxBlob, off, &rows[i].HashValue); err != nil {\n\t\t\treturn rows, n, err\n\t\t}\n\t\toff += indexSize\n\t\tn += indexSize\n\t}\n\treturn rows, n, nil\n}\n"
  },
  {
    "path": "dotnet_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"reflect\"\n\t\"sort\"\n\t\"strconv\"\n\t\"testing\"\n)\n\nfunc TestClrDirectoryHeaders(t *testing.T) {\n\ttype TestClrHeaders struct {\n\t\tclrHeader            ImageCOR20Header\n\t\tmdHeader             MetadataHeader\n\t\tmdStreamHeaders      []MetadataStreamHeader\n\t\tmdTablesStreamHeader MetadataTableStreamHeader\n\t}\n\n\ttests := []struct {\n\t\tin  string\n\t\tout TestClrHeaders\n\t}{\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/mscorlib.dll\"),\n\t\t\tTestClrHeaders{\n\t\t\t\tclrHeader: ImageCOR20Header{\n\t\t\t\t\tCb:                  0x48,\n\t\t\t\t\tMajorRuntimeVersion: 0x2,\n\t\t\t\t\tMinorRuntimeVersion: 0x5,\n\t\t\t\t\tMetaData: ImageDataDirectory{\n\t\t\t\t\t\tVirtualAddress: 0x2050,\n\t\t\t\t\t\tSize:           0xae34,\n\t\t\t\t\t},\n\t\t\t\t\tFlags:                0x9,\n\t\t\t\t\tEntryPointRVAorToken: 0x0,\n\t\t\t\t\tStrongNameSignature: ImageDataDirectory{\n\t\t\t\t\t\tVirtualAddress: 0xce84,\n\t\t\t\t\t\tSize:           0x80,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tmdHeader: MetadataHeader{\n\t\t\t\t\tSignature:     0x424a5342,\n\t\t\t\t\tMajorVersion:  0x1,\n\t\t\t\t\tMinorVersion:  0x1,\n\t\t\t\t\tExtraData:     0x0,\n\t\t\t\t\tVersionString: 0xc,\n\t\t\t\t\tVersion:       \"v4.0.30319\",\n\t\t\t\t\tFlags:         0x0,\n\t\t\t\t\tStreams:       0x5,\n\t\t\t\t},\n\t\t\t\tmdStreamHeaders: []MetadataStreamHeader{\n\t\t\t\t\t{\n\t\t\t\t\t\tOffset: 0x6c,\n\t\t\t\t\t\tSize:   0x4c38,\n\t\t\t\t\t\tName:   \"#~\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tOffset: 0x4ca4,\n\t\t\t\t\t\tSize:   0x5ed4,\n\t\t\t\t\t\tName:   \"#Strings\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tOffset: 0xab78,\n\t\t\t\t\t\tSize:   0x4,\n\t\t\t\t\t\tName:   \"#US\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tOffset: 0xab7c,\n\t\t\t\t\t\tSize:   0x10,\n\t\t\t\t\t\tName:   \"#GUID\",\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tOffset: 0xab8c,\n\t\t\t\t\t\tSize:   0x2a8,\n\t\t\t\t\t\tName:   \"#Blob\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tmdTablesStreamHeader: MetadataTableStreamHeader{\n\t\t\t\t\tReserved:     0x0,\n\t\t\t\t\tMajorVersion: 0x2,\n\t\t\t\t\tMinorVersion: 0x0,\n\t\t\t\t\tHeaps:        0x0,\n\t\t\t\t\tRID:          0x1,\n\t\t\t\t\tMaskValid:    0x8900005407,\n\t\t\t\t\tSorted:       0x16003301fa00,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\t\t\tswitch file.Is64 {\n\t\t\tcase true:\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryCLR]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\tcase false:\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryCLR]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseCLRHeaderDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseCLRHeaderDirectory(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\tclr := file.CLR\n\t\t\tif clr.CLRHeader != tt.out.clrHeader {\n\t\t\t\tt.Errorf(\"CLR header assertion failed, got %v, want %v\",\n\t\t\t\t\tclr.CLRHeader, tt.out.clrHeader)\n\t\t\t}\n\n\t\t\tif clr.MetadataHeader != tt.out.mdHeader {\n\t\t\t\tt.Errorf(\"CLR metadata header assertion failed, got %v, want %v\",\n\t\t\t\t\tclr.MetadataHeader, tt.out.mdHeader)\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(clr.MetadataStreamHeaders, tt.out.mdStreamHeaders) {\n\t\t\t\tt.Errorf(\"CLR metadata stream headers assertion failed, got %v, want %v\",\n\t\t\t\t\tclr.MetadataStreamHeaders, tt.out.mdStreamHeaders)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClrDirectoryMetadataTables(t *testing.T) {\n\ttype TestClrMetadataTable struct {\n\t\ttableKind int\n\t\ttable     MetadataTable\n\t}\n\n\ttests := []struct {\n\t\tin  string\n\t\tout []TestClrMetadataTable\n\t}{\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/mscorlib.dll\"),\n\t\t\t[]TestClrMetadataTable{\n\t\t\t\t{\n\t\t\t\t\ttableKind: Module,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"Module\",\n\t\t\t\t\t\tCountCols: 0x1,\n\t\t\t\t\t\tContent: []ModuleTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tGeneration: 0x0,\n\t\t\t\t\t\t\t\tName:       0x2cd7,\n\t\t\t\t\t\t\t\tMvid:       0x1,\n\t\t\t\t\t\t\t\tEncID:      0x0,\n\t\t\t\t\t\t\t\tEncBaseID:  0x0,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: TypeRef,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"TypeRef\",\n\t\t\t\t\t\tCountCols: 19,\n\t\t\t\t\t\tContent: []TypeRefTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tResolutionScope: 0x6,\n\t\t\t\t\t\t\t\tTypeName:        0x22bd,\n\t\t\t\t\t\t\t\tTypeNamespace:   0x4d80,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: MemberRef,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"MemberRef\",\n\t\t\t\t\t\tCountCols: 17,\n\t\t\t\t\t\tContent: []MemberRefTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tClass:     0x9,\n\t\t\t\t\t\t\t\tName:      0x4c76,\n\t\t\t\t\t\t\t\tSignature: 0x1,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: CustomAttribute,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"CustomAttribute\",\n\t\t\t\t\t\tCountCols: 19,\n\t\t\t\t\t\tContent: []CustomAttributeTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tParent: 0x27,\n\t\t\t\t\t\t\t\tType:   0x83,\n\t\t\t\t\t\t\t\tValue:  0x2a1,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: DeclSecurity,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"DeclSecurity\",\n\t\t\t\t\t\tCountCols: 1,\n\t\t\t\t\t\tContent: []DeclSecurityTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tAction:        0x8,\n\t\t\t\t\t\t\t\tParent:        0x6,\n\t\t\t\t\t\t\t\tPermissionSet: 0x52,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: Assembly,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"Assembly\",\n\t\t\t\t\t\tCountCols: 1,\n\t\t\t\t\t\tContent: []AssemblyTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tHashAlgId:      0x8004,\n\t\t\t\t\t\t\t\tMajorVersion:   0x4,\n\t\t\t\t\t\t\t\tMinorVersion:   0x0,\n\t\t\t\t\t\t\t\tBuildNumber:    0x0,\n\t\t\t\t\t\t\t\tRevisionNumber: 0x0,\n\t\t\t\t\t\t\t\tFlags:          0x1,\n\t\t\t\t\t\t\t\tPublicKey:      0x41,\n\t\t\t\t\t\t\t\tName:           0x704,\n\t\t\t\t\t\t\t\tCulture:        0x0,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: AssemblyRef,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"AssemblyRef\",\n\t\t\t\t\t\tCountCols: 30,\n\t\t\t\t\t\tContent: []AssemblyRefTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tMajorVersion:     0x0,\n\t\t\t\t\t\t\t\tMinorVersion:     0x0,\n\t\t\t\t\t\t\t\tBuildNumber:      0x0,\n\t\t\t\t\t\t\t\tRevisionNumber:   0x0,\n\t\t\t\t\t\t\t\tFlags:            0x0,\n\t\t\t\t\t\t\t\tPublicKeyOrToken: 0x26,\n\t\t\t\t\t\t\t\tName:             0x6ed,\n\t\t\t\t\t\t\t\tCulture:          0x0,\n\t\t\t\t\t\t\t\tHashValue:        0x0,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: ExportedType,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"ExportedType\",\n\t\t\t\t\t\tCountCols: 1319,\n\t\t\t\t\t\tContent: []ExportedTypeTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tFlags:          0x200000,\n\t\t\t\t\t\t\t\tTypeDefId:      0x0,\n\t\t\t\t\t\t\t\tTypeName:       0x5d85,\n\t\t\t\t\t\t\t\tTypeNamespace:  0x316,\n\t\t\t\t\t\t\t\tImplementation: 0x9,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/pspluginwkr.dll\"),\n\t\t\t[]TestClrMetadataTable{\n\t\t\t\t{\n\t\t\t\t\ttableKind: Module,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"Module\",\n\t\t\t\t\t\tCountCols: 0x1,\n\t\t\t\t\t\tContent: []ModuleTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tGeneration: 0x0,\n\t\t\t\t\t\t\t\tName:       0x8bdf,\n\t\t\t\t\t\t\t\tMvid:       0x1,\n\t\t\t\t\t\t\t\tEncID:      0x0,\n\t\t\t\t\t\t\t\tEncBaseID:  0x0,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: TypeRef,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"TypeRef\",\n\t\t\t\t\t\tCountCols: 140,\n\t\t\t\t\t\tContent: []TypeRefTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tResolutionScope: 0x6,\n\t\t\t\t\t\t\t\tTypeName:        0x1103,\n\t\t\t\t\t\t\t\tTypeNamespace:   0x1113,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: TypeDef,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"TypeDef\",\n\t\t\t\t\t\tCountCols: 169,\n\t\t\t\t\t\tContent: []TypeDefTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tFlags:         0x0,\n\t\t\t\t\t\t\t\tTypeName:      0x1,\n\t\t\t\t\t\t\t\tTypeNamespace: 0x0,\n\t\t\t\t\t\t\t\tExtends:       0x0,\n\t\t\t\t\t\t\t\tFieldList:     0x1,\n\t\t\t\t\t\t\t\tMethodList:    0x1,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: Field,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"Field\",\n\t\t\t\t\t\tCountCols: 325,\n\t\t\t\t\t\tContent: []FieldTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tFlags:     0x113,\n\t\t\t\t\t\t\t\tName:      0x4af1,\n\t\t\t\t\t\t\t\tSignature: 0xea9,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: MethodDef,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"MethodDef\",\n\t\t\t\t\t\tCountCols: 434,\n\t\t\t\t\t\tContent: []MethodDefTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tRVA:       0x1d414,\n\t\t\t\t\t\t\t\tImplFlags: 0x0,\n\t\t\t\t\t\t\t\tFlags:     0x13,\n\t\t\t\t\t\t\t\tName:      0x1b7f,\n\t\t\t\t\t\t\t\tSignature: 0x125,\n\t\t\t\t\t\t\t\tParamList: 0x1,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: Param,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"Param\",\n\t\t\t\t\t\tCountCols: 679,\n\t\t\t\t\t\tContent: []ParamTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tFlags:    0x2000,\n\t\t\t\t\t\t\t\tSequence: 0x0,\n\t\t\t\t\t\t\t\tName:     0x0,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: InterfaceImpl,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"InterfaceImpl\",\n\t\t\t\t\t\tCountCols: 3,\n\t\t\t\t\t\tContent: []InterfaceImplTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tClass:     0x6c,\n\t\t\t\t\t\t\t\tInterface: 0xa9,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: MemberRef,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"MemberRef\",\n\t\t\t\t\t\tCountCols: 256,\n\t\t\t\t\t\tContent: []MemberRefTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tClass:     0x29,\n\t\t\t\t\t\t\t\tName:      0x79f8,\n\t\t\t\t\t\t\t\tSignature: 0x11e2,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: Constant,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"Constant\",\n\t\t\t\t\t\tCountCols: 2,\n\t\t\t\t\t\tContent: []ConstantTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tType:   0xe,\n\t\t\t\t\t\t\t\tParent: 0x464,\n\t\t\t\t\t\t\t\tValue:  0x1aa8,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: CustomAttribute,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"CustomAttribute\",\n\t\t\t\t\t\tCountCols: 622,\n\t\t\t\t\t\tContent: []CustomAttributeTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tParent: 0x2e,\n\t\t\t\t\t\t\t\tType:   0x7db,\n\t\t\t\t\t\t\t\tValue:  0x2c02,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: FieldMarshal,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"FieldMarshal\",\n\t\t\t\t\t\tCountCols: 33,\n\t\t\t\t\t\tContent: []FieldMarshalTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tParent:     0x3,\n\t\t\t\t\t\t\t\tNativeType: 0x1ca6,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: DeclSecurity,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"DeclSecurity\",\n\t\t\t\t\t\tCountCols: 4,\n\t\t\t\t\t\tContent: []DeclSecurityTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tAction:        0x8,\n\t\t\t\t\t\t\t\tParent:        0x6,\n\t\t\t\t\t\t\t\tPermissionSet: 0x2d81,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: ClassLayout,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"ClassLayout\",\n\t\t\t\t\t\tCountCols: 144,\n\t\t\t\t\t\tContent: []ClassLayoutTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tPackingSize: 0x0,\n\t\t\t\t\t\t\t\tClassSize:   0x10,\n\t\t\t\t\t\t\t\tParent:      0x2,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: StandAloneSig,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"StandAloneSig\",\n\t\t\t\t\t\tCountCols: 358,\n\t\t\t\t\t\tContent: []StandAloneSigTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSignature: 0x1caa,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: EventMap,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"EventMap\",\n\t\t\t\t\t\tCountCols: 2,\n\t\t\t\t\t\tContent: []EventMapTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tParent:    0x7f,\n\t\t\t\t\t\t\t\tEventList: 0x1,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: Event,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"Event\",\n\t\t\t\t\t\tCountCols: 2,\n\t\t\t\t\t\tContent: []EventTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tEventFlags: 0x200,\n\t\t\t\t\t\t\t\tName:       0x7eeb,\n\t\t\t\t\t\t\t\tEventType:  0x16,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: PropertyMap,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"PropertyMap\",\n\t\t\t\t\t\tCountCols: 2,\n\t\t\t\t\t\tContent: []PropertyMapTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tParent:       0x49,\n\t\t\t\t\t\t\t\tPropertyList: 0x1,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: Property,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"Property\",\n\t\t\t\t\t\tCountCols: 2,\n\t\t\t\t\t\tContent: []PropertyTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tFlags: 0x0,\n\t\t\t\t\t\t\t\tName:  0x7a8a,\n\t\t\t\t\t\t\t\tType:  0x11d7,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: MethodSemantics,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"MethodSemantics\",\n\t\t\t\t\t\tCountCols: 9,\n\t\t\t\t\t\tContent: []MethodSemanticsTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSemantics:   0x10,\n\t\t\t\t\t\t\t\tMethod:      0x153,\n\t\t\t\t\t\t\t\tAssociation: 0x2,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: ModuleRef,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"ModuleRef\",\n\t\t\t\t\t\tCountCols: 1,\n\t\t\t\t\t\tContent: []ModuleRefTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tName: 0x0,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: TypeSpec,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"TypeSpec\",\n\t\t\t\t\t\tCountCols: 17,\n\t\t\t\t\t\tContent: []TypeSpecTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tSignature: 0x85,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: ImplMap,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"ImplMap\",\n\t\t\t\t\t\tCountCols: 51,\n\t\t\t\t\t\tContent: []ImplMapTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tMappingFlags:    0x240,\n\t\t\t\t\t\t\t\tMemberForwarded: 0x1cb,\n\t\t\t\t\t\t\t\tImportName:      0x0,\n\t\t\t\t\t\t\t\tImportScope:     0x1,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: FieldRVA,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"FieldRVA\",\n\t\t\t\t\t\tCountCols: 265,\n\t\t\t\t\t\tContent: []FieldRVATableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tRVA:   0x11e4,\n\t\t\t\t\t\t\t\tField: 0x1,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: Assembly,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"Assembly\",\n\t\t\t\t\t\tCountCols: 1,\n\t\t\t\t\t\tContent: []AssemblyTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tHashAlgId:      0x8004,\n\t\t\t\t\t\t\t\tMajorVersion:   0x1,\n\t\t\t\t\t\t\t\tMinorVersion:   0x0,\n\t\t\t\t\t\t\t\tBuildNumber:    0x0,\n\t\t\t\t\t\t\t\tRevisionNumber: 0x0,\n\t\t\t\t\t\t\t\tFlags:          0x1,\n\t\t\t\t\t\t\t\tPublicKey:      0x2b03,\n\t\t\t\t\t\t\t\tName:           0x8bd3,\n\t\t\t\t\t\t\t\tCulture:        0x0,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: AssemblyRef,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"AssemblyRef\",\n\t\t\t\t\t\tCountCols: 5,\n\t\t\t\t\t\tContent: []AssemblyRefTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tMajorVersion:     0x2,\n\t\t\t\t\t\t\t\tMinorVersion:     0x0,\n\t\t\t\t\t\t\t\tBuildNumber:      0x0,\n\t\t\t\t\t\t\t\tRevisionNumber:   0x0,\n\t\t\t\t\t\t\t\tFlags:            0x0,\n\t\t\t\t\t\t\t\tPublicKeyOrToken: 0x1,\n\t\t\t\t\t\t\t\tName:             0x10b9,\n\t\t\t\t\t\t\t\tCulture:          0x0,\n\t\t\t\t\t\t\t\tHashValue:        0xa,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\n\t\t\t\t{\n\t\t\t\t\ttableKind: NestedClass,\n\t\t\t\t\ttable: MetadataTable{\n\t\t\t\t\t\tName:      \"NestedClass\",\n\t\t\t\t\t\tCountCols: 7,\n\t\t\t\t\t\tContent: []NestedClassTableRow{\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tNestedClass:    0x7,\n\t\t\t\t\t\t\t\tEnclosingClass: 0x6,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\t\t\tswitch file.Is64 {\n\t\t\tcase true:\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryCLR]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\tcase false:\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryCLR]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseCLRHeaderDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseCLRHeaderDirectory(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tclr := file.CLR\n\t\t\tfor _, tbl := range tt.out {\n\t\t\t\tmdTable := clr.MetadataTables[tbl.tableKind]\n\t\t\t\tif mdTable.CountCols != tbl.table.CountCols {\n\t\t\t\t\tt.Errorf(\"CLR metadata tables assertion failed on %s table, got %v, want %v\",\n\t\t\t\t\t\ttbl.table.Name, mdTable.CountCols, tbl.table.CountCols)\n\t\t\t\t}\n\t\t\t\tif mdTable.Name != tbl.table.Name {\n\t\t\t\t\tt.Errorf(\"CLR metadata tables assertion failed on %s table, got %v, want %v\",\n\t\t\t\t\t\ttbl.table.Name, mdTable.Name, tbl.table)\n\t\t\t\t}\n\n\t\t\t\tvar got, want interface{}\n\t\t\t\tswitch mdTable.Content.(type) {\n\t\t\t\tcase []ModuleTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]ModuleTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]ModuleTableRow)[0]\n\t\t\t\tcase []TypeRefTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]TypeRefTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]TypeRefTableRow)[0]\n\t\t\t\tcase []TypeDefTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]TypeDefTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]TypeDefTableRow)[0]\n\t\t\t\tcase []MemberRefTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]MemberRefTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]MemberRefTableRow)[0]\n\t\t\t\tcase []CustomAttributeTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]CustomAttributeTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]CustomAttributeTableRow)[0]\n\t\t\t\tcase []DeclSecurityTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]DeclSecurityTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]DeclSecurityTableRow)[0]\n\t\t\t\tcase []AssemblyTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]AssemblyTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]AssemblyTableRow)[0]\n\t\t\t\tcase []AssemblyRefTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]AssemblyRefTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]AssemblyRefTableRow)[0]\n\t\t\t\tcase []ExportedTypeTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]ExportedTypeTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]ExportedTypeTableRow)[0]\n\t\t\t\tcase []FieldTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]FieldTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]FieldTableRow)[0]\n\t\t\t\tcase []MethodDefTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]MethodDefTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]MethodDefTableRow)[0]\n\t\t\t\tcase []ParamTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]ParamTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]ParamTableRow)[0]\n\t\t\t\tcase []InterfaceImplTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]InterfaceImplTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]InterfaceImplTableRow)[0]\n\t\t\t\tcase []ConstantTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]ConstantTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]ConstantTableRow)[0]\n\t\t\t\tcase []FieldMarshalTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]FieldMarshalTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]FieldMarshalTableRow)[0]\n\t\t\t\tcase []ClassLayoutTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]ClassLayoutTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]ClassLayoutTableRow)[0]\n\t\t\t\tcase []StandAloneSigTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]StandAloneSigTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]StandAloneSigTableRow)[0]\n\t\t\t\tcase []EventMapTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]EventMapTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]EventMapTableRow)[0]\n\t\t\t\tcase []EventTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]EventTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]EventTableRow)[0]\n\t\t\t\tcase []PropertyMapTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]PropertyMapTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]PropertyMapTableRow)[0]\n\t\t\t\tcase []PropertyTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]PropertyTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]PropertyTableRow)[0]\n\t\t\t\tcase []MethodSemanticsTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]MethodSemanticsTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]MethodSemanticsTableRow)[0]\n\t\t\t\tcase []ModuleRefTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]ModuleRefTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]ModuleRefTableRow)[0]\n\t\t\t\tcase []TypeSpecTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]TypeSpecTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]TypeSpecTableRow)[0]\n\t\t\t\tcase []ImplMapTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]ImplMapTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]ImplMapTableRow)[0]\n\t\t\t\tcase []FieldRVATableRow:\n\t\t\t\t\tgot = mdTable.Content.([]FieldRVATableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]FieldRVATableRow)[0]\n\t\t\t\tcase []NestedClassTableRow:\n\t\t\t\t\tgot = mdTable.Content.([]NestedClassTableRow)[0]\n\t\t\t\t\twant = tbl.table.Content.([]NestedClassTableRow)[0]\n\t\t\t\tdefault:\n\t\t\t\t\tgot = \"bad type\"\n\t\t\t\t\twant = \"good type\"\n\t\t\t\t}\n\t\t\t\tif !reflect.DeepEqual(got, want) {\n\t\t\t\t\tt.Errorf(\"CLR metadata tables assertion failed on %s table, got %v, want %v\",\n\t\t\t\t\t\ttbl.table.Name, got, want)\n\t\t\t\t}\n\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClrDirectorCOMImageFlagsType(t *testing.T) {\n\ttests := []struct {\n\t\tin  int\n\t\tout []string\n\t}{\n\t\t{\n\t\t\t0x9,\n\t\t\t[]string{\"IL Only\", \"Strong Name Signed\"},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(\"CaseFlagsEqualTo_\"+strconv.Itoa(tt.in), func(t *testing.T) {\n\t\t\tgot := COMImageFlagsType(tt.in).String()\n\t\t\tsort.Strings(got)\n\t\t\tsort.Strings(tt.out)\n\t\t\tif !reflect.DeepEqual(got, tt.out) {\n\t\t\t\tt.Errorf(\"CLR header flags assertion failed, got %v, want %v\",\n\t\t\t\t\tgot, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestClrDirectoryMalformed(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tin   string\n\t}{\n\t\t{\n\t\t\tname: \"malformed_dotnet_binary\",\n\t\t\tin:   getAbsoluteFilePath(\"test/04521ac8297cabd58e62e86039f6874a06753362dac4edc56bbf6d4655bb67bd\"),\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\t// Should not panic, and should continue parsing past the\n\t\t\t// malformed metadata stream.\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\t// Verify that streams with out-of-bounds sizes get\n\t\t\t// an empty byte slice instead of causing a panic.\n\t\t\tfor _, name := range []string{\"#Strings\", \"#US\", \"#GUID\", \"#Blob\"} {\n\t\t\t\tdata, ok := file.CLR.MetadataStreams[name]\n\t\t\t\tif !ok {\n\t\t\t\t\tt.Errorf(\"expected stream %q to be present\", name)\n\t\t\t\t}\n\t\t\t\tif len(data) != 0 {\n\t\t\t\t\tt.Errorf(\"expected stream %q to be empty, got len=%d\", name, len(data))\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "exception.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n\t\"strconv\"\n)\n\nconst (\n\t// Unwind information flags.\n\n\t// UnwFlagNHandler - The function has no handler.\n\tUnwFlagNHandler = uint8(0x0)\n\n\t// UnwFlagEHandler - The function has an exception handler that should\n\t// be called when looking for functions that need to examine exceptions.\n\tUnwFlagEHandler = uint8(0x1)\n\n\t// UnwFlagUHandler - The function has a termination handler that should\n\t// be called when unwinding an exception.\n\tUnwFlagUHandler = uint8(0x2)\n\n\t// UnwFlagChainInfo - This unwind info structure is not the primary one\n\t// for the procedure. Instead, the chained unwind info entry is the contents\n\t// of a previous RUNTIME_FUNCTION entry. For information, see Chained unwind\n\t// info structures. If this flag is set, then the UNW_FLAG_EHANDLER and\n\t// UNW_FLAG_UHANDLER flags must be cleared. Also, the frame register and\n\t// fixed-stack allocation field must have the same values as in the primary\n\t// unwind info.\n\tUnwFlagChainInfo = uint8(0x4)\n)\n\n// The meaning of the operation info bits depends upon the operation code.\n// To encode a general-purpose (integer) register, this mapping is used:\nconst (\n\trax = iota\n\trcx\n\trdx\n\trbx\n\trsp\n\trbp\n\trsi\n\trdi\n\tr8\n\tr9\n\tr10\n\tr11\n\tr12\n\tr13\n\tr14\n\tr15\n)\n\n// OpInfoRegisters maps registers to string.\nvar OpInfoRegisters = map[uint8]string{\n\trax: \"RAX\",\n\trcx: \"RCX\",\n\trdx: \"RDX\",\n\trbx: \"RBX\",\n\trsp: \"RSP\",\n\trbp: \"RBP\",\n\trsi: \"RSI\",\n\trdi: \"RDI\",\n\tr8:  \"R8\",\n\tr9:  \"R9\",\n\tr10: \"R10\",\n\tr11: \"R11\",\n\tr12: \"R12\",\n\tr13: \"R13\",\n\tr14: \"R14\",\n\tr15: \"R15\",\n}\n\n// UnwindOpType represents the type of an unwind opcode.\ntype UnwindOpType uint8\n\n// _UNWIND_OP_CODES\nconst (\n\t// Push a nonvolatile integer register, decrementing RSP by 8. The\n\t// operation info is the number of the register. Because of the constraints\n\t// on epilogs, UWOP_PUSH_NONVOL unwind codes must appear first in the\n\t// prolog and correspondingly, last in the unwind code array. This relative\n\t// ordering applies to all other unwind codes except UWOP_PUSH_MACHFRAME.\n\tUwOpPushNonVol = UnwindOpType(0)\n\n\t// Allocate a large-sized area on the stack. There are two forms. If the\n\t// operation info equals 0, then the size of the allocation divided by 8 is\n\t// recorded in the next slot, allowing an allocation up to 512K - 8. If the\n\t// operation info equals 1, then the unscaled size of the allocation is\n\t// recorded in the next two slots in little-endian format, allowing\n\t// allocations up to 4GB - 8.\n\tUwOpAllocLarge = UnwindOpType(1)\n\n\t// Allocate a small-sized area on the stack. The size of the allocation is\n\t// the operation info field * 8 + 8, allowing allocations from 8 to 128\n\t// bytes.\n\tUwOpAllocSmall = UnwindOpType(2)\n\n\t// Establish the frame pointer register by setting the register to some\n\t// offset of the current RSP. The offset is equal to the Frame Register\n\t// offset (scaled) field in the UNWIND_INFO * 16, allowing offsets from 0\n\t// to 240. The use of an offset permits establishing a frame pointer that\n\t// points to the middle of the fixed stack allocation, helping code density\n\t// by allowing more accesses to use short instruction forms. The operation\n\t// info field is reserved and shouldn't be used.\n\tUwOpSetFpReg = UnwindOpType(3)\n\n\t// Save a nonvolatile integer register on the stack using a MOV instead of\n\t// a PUSH. This code is primarily used for shrink-wrapping, where a\n\t// nonvolatile register is saved to the stack in a position that was\n\t// previously allocated. The operation info is the number of the register.\n\t// The scaled-by-8 stack offset is recorded in the next unwind operation\n\t// code slot, as described in the note above.\n\tUwOpSaveNonVol = UnwindOpType(4)\n\n\t// Save a nonvolatile integer register on the stack with a long offset,\n\t// using a MOV instead of a PUSH. This code is primarily used for\n\t// shrink-wrapping, where a nonvolatile register is saved to the stack in a\n\t// position that was previously allocated. The operation info is the number\n\t// of the register. The unscaled stack offset is recorded in the next two\n\t// unwind operation code slots, as described in the note above.\n\tUwOpSaveNonVolFar = UnwindOpType(5)\n\n\t// For version 1 of the UNWIND_INFO structure, this code was called\n\t// UWOP_SAVE_XMM and occupied 2 records, it retained the lower 64 bits of\n\t// the XMM register, but was later removed and is now skipped. In practice,\n\t// this code has never been used.\n\t// For version 2 of the UNWIND_INFO structure, this code is called\n\t// UWOP_EPILOG, takes 2 entries, and describes the function epilogue.\n\tUwOpEpilog = UnwindOpType(6)\n\n\t// For version 1 of the UNWIND_INFO structure, this code was called\n\t// UWOP_SAVE_XMM_FAR and occupied 3 records, it saved the lower 64 bits of\n\t// the XMM register, but was later removed and is now skipped. In practice,\n\t// this code has never been used.\n\t// For version 2 of the UNWIND_INFO structure, this code is called\n\t// UWOP_SPARE_CODE, takes 3 entries, and makes no sense.\n\tUwOpSpareCode = UnwindOpType(7)\n\n\t// Save all 128 bits of a nonvolatile XMM register on the stack. The\n\t// operation info is the number of the register. The scaled-by-16 stack\n\t// offset is recorded in the next slot.\n\tUwOpSaveXmm128 = UnwindOpType(8)\n\n\t// Save all 128 bits of a nonvolatile XMM register on the stack with a long\n\t// offset. The operation info is the number of the register. The unscaled\n\t// stack offset is recorded in the next two slots.\n\tUwOpSaveXmm128Far = UnwindOpType(9)\n\n\t// Push a machine frame. This unwind code is used to record the effect of a\n\t// hardware interrupt or exception.\n\tUwOpPushMachFrame = UnwindOpType(10)\n\n\t// UWOP_SET_FPREG_LARGE is a CLR Unix-only extension to the Windows AMD64\n\t// unwind codes. It is not part of the standard Windows AMD64 unwind codes\n\t// specification. UWOP_SET_FPREG allows for a maximum of a 240 byte offset\n\t// between RSP and the frame pointer, when the frame pointer is\n\t// established. UWOP_SET_FPREG_LARGE has a 32-bit range scaled by 16. When\n\t// UWOP_SET_FPREG_LARGE is used, UNWIND_INFO.FrameRegister must be set to\n\t// the frame pointer register, and UNWIND_INFO.FrameOffset must be set to\n\t// 15 (its maximum value). UWOP_SET_FPREG_LARGE is followed by two\n\t// UNWIND_CODEs that are combined to form a 32-bit offset (the same as\n\t// UWOP_SAVE_NONVOL_FAR). This offset is then scaled by 16. The result must\n\t// be less than 2^32 (that is, the top 4 bits of the unscaled 32-bit number\n\t// must be zero). This result is used as the frame pointer register offset\n\t// from RSP at the time the frame pointer is established. Either\n\t// UWOP_SET_FPREG or UWOP_SET_FPREG_LARGE can be used, but not both.\n\tUwOpSetFpRegLarge = UnwindOpType(11)\n)\n\n// ImageRuntimeFunctionEntry represents an entry in the function table on 64-bit\n// Windows (IMAGE_RUNTIME_FUNCTION_ENTRY). Table-based exception handling request\n// a table entry for all functions that allocate stack space or call another\n// function (for example, non-leaf functions).\ntype ImageRuntimeFunctionEntry struct {\n\t// The address of the start of the function.\n\tBeginAddress uint32 `json:\"begin_address\"`\n\n\t// The address of the end of the function.\n\tEndAddress uint32 `json:\"end_address\"`\n\n\t// The unwind data info structure is used to record the effects a function\n\t// has on the stack pointer, and where the nonvolatile registers are saved\n\t// on the stack.\n\tUnwindInfoAddress uint32 `json:\"unwind_info_address\"`\n}\n\n// ImageARMRuntimeFunctionEntry represents the function table entry for the ARM\n// platform.\ntype ImageARMRuntimeFunctionEntry struct {\n\t// Function Start RVA is the 32-bit RVA of the start of the function. If\n\t// the function contains thumb code, the low bit of this address must be set.\n\tBeginAddress uint32 `bitfield:\",functionstart\" json:\"begin_address\"`\n\n\t// Flag is a 2-bit field that indicates how to interpret the remaining\n\t// 30 bits of the second .pdata word. If Flag is 0, then the remaining bits\n\t// form an Exception Information RVA (with the low two bits implicitly 0).\n\t// If Flag is non-zero, then the remaining bits form a Packed Unwind Data\n\t// structure.\n\tFlag uint8 `json:\"flag\"`\n\n\t/* Exception Information RVA or Packed Unwind Data.\n\n\tException Information RVA is the address of the variable-length exception\n\tinformation structure, stored in the .xdata section.\n\tThis data must be 4-byte aligned.\n\n\tPacked Unwind Data is a compressed description of the operations required\n\tto unwind from a function, assuming a canonical form. In this case, no\n\t.xdata record is required. */\n\tExceptionFlag uint32 `json:\"exception_flag\"`\n}\n\n// UnwindCode is used to record the sequence of operations in the prolog that\n// affect the nonvolatile registers and RSP. Each code item has this format:\n/* typedef union _UNWIND_CODE {\n    struct {\n        UCHAR CodeOffset;\n        UCHAR UnwindOp : 4;\n        UCHAR OpInfo : 4;\n    } DUMMYUNIONNAME;\n\n    struct {\n        UCHAR OffsetLow;\n        UCHAR UnwindOp : 4;\n        UCHAR OffsetHigh : 4;\n    } EpilogueCode;\n\n    USHORT FrameOffset;\n} UNWIND_CODE, *PUNWIND_CODE;*/\n//\n// It provides information about the amount of stack space allocated, the location\n// of saved non-volatile registers, and whether or not a frame register is used\n// and what relation it has to the rest of the stack.\ntype UnwindCode struct {\n\t// Offset (from the beginning of the prolog) of the end of the instruction\n\t// that performs is operation, plus 1 (that is, the offset of the start of\n\t// the next instruction).\n\tCodeOffset uint8 `json:\"code_offset\"`\n\n\t// The unwind operation code.\n\tUnwindOp UnwindOpType `json:\"unwind_op\"`\n\n\t// Operation info.\n\tOpInfo uint8 `json:\"op_info\"`\n\n\t// Allocation size.\n\tOperand     string `json:\"operand\"`\n\tFrameOffset uint16 `json:\"frame_offset\"`\n}\n\n// UnwindInfo represents the _UNWIND_INFO structure. It is used to record the\n// effects a function has on the stack pointer, and where the nonvolatile\n// registers are saved on the stack.\ntype UnwindInfo struct {\n\t// (3 bits) Version number of the unwind data, currently 1 and 2.\n\tVersion uint8 `json:\"version\"`\n\n\t// (5 bits) Three flags are currently defined above.\n\tFlags uint8 `json:\"flags\"`\n\n\t// Length of the function prolog in bytes.\n\tSizeOfProlog uint8 `json:\"size_of_prolog\"`\n\n\t// The number of slots in the unwind codes array. Some unwind codes,\n\t// for example, UWOP_SAVE_NONVOL, require more than one slot in the array.\n\tCountOfCodes uint8 `json:\"count_of_codes\"`\n\n\t// If nonzero, then the function uses a frame pointer (FP), and this field\n\t// is the number of the nonvolatile register used as the frame pointer,\n\t// using the same encoding for the operation info field of UNWIND_CODE nodes.\n\tFrameRegister uint8 `json:\"frame_register\"`\n\n\t// If the frame register field is nonzero, this field is the scaled offset\n\t// from RSP that is applied to the FP register when it's established. The\n\t// actual FP register is set to RSP + 16 * this number, allowing offsets\n\t// from 0 to 240. This offset permits pointing the FP register into the\n\t// middle of the local stack allocation for dynamic stack frames, allowing\n\t// better code density through shorter instructions. (That is, more\n\t// instructions can use the 8-bit signed offset form.)\n\tFrameOffset uint8 `json:\"frame_offset\"`\n\n\t// An array of items that explains the effect of the prolog on the\n\t// nonvolatile registers and RSP. See the section on UNWIND_CODE for the\n\t// meanings of individual items. For alignment purposes, this array always\n\t// has an even number of entries, and the final entry is potentially\n\t// unused. In that case, the array is one longer than indicated by the\n\t// count of unwind codes field.\n\tUnwindCodes []UnwindCode `json:\"unwind_codes\"`\n\n\t// Address of exception handler when UNW_FLAG_EHANDLER is set.\n\tExceptionHandler uint32 `json:\"exception_handler\"`\n\n\t// If flag UNW_FLAG_CHAININFO is set, then the UNWIND_INFO structure ends\n\t// with three UWORDs. These UWORDs represent the RUNTIME_FUNCTION\n\t// information for the function of the chained unwind.\n\tFunctionEntry ImageRuntimeFunctionEntry `json:\"function_entry\"`\n}\n\n//\n// The unwind codes are followed by an optional DWORD aligned field that\n// contains the exception handler address or the address of chained unwind\n// information. If an exception handler address is specified, then it is\n// followed by the language specified exception handler data.\n//\n//  union {\n//      ULONG ExceptionHandler;\n//      ULONG FunctionEntry;\n//  };\n//\n//  ULONG ExceptionData[];\n//\n\ntype ScopeRecord struct {\n\t// This value indicates the offset of the first instruction within a __try\n\t// block located in the function.\n\tBeginAddress uint32 `json:\"begin_address\"`\n\n\t// This value indicates the offset to the instruction after the last\n\t// instruction within the __try block (conceptually the __except statement).\n\tEndAddress uint32 `json:\"end_address\"`\n\n\t// This value indicates the offset to the function located within the\n\t// parentheses of the __except() statement. In the documentation you'll\n\t// find this routine called the \"exception handler\" or \"exception filter\".\n\tHandlerAddress uint32 `json:\"handler_address\"`\n\n\t// This value indicates the offset to the first instruction in the __except\n\t// block associated with the __try block.\n\tJumpTarget uint32 `json:\"jump_target\"`\n}\n\n// ScopeTable represents a variable length structure containing a count followed\n// by Count \"scope records\". While the RUNTIME_FUNCTION describes the entire range\n// of a function that contains SEH, the SCOPE_TABLE describes each of the individual\n// __try/__except blocks within the function.\ntype ScopeTable struct {\n\t// The count of scope records.\n\tCount uint32 `json:\"count\"`\n\n\t// A array of scope record.\n\tScopeRecords []ScopeRecord `json:\"scope_records\"`\n}\n\n//  typedef struct _SCOPE_TABLE {\n// \t\tULONG Count;\n// \t\tstruct\n// \t\t{\n// \t\t\tULONG BeginAddress;\n// \t\t\tULONG EndAddress;\n// \t\t\tULONG HandlerAddress;\n// \t\t\tULONG JumpTarget;\n// \t\t} ScopeRecord[1];\n//  } SCOPE_TABLE, *PSCOPE_TABLE;\n\n// Exception represent an entry in the function table.\ntype Exception struct {\n\tRuntimeFunction ImageRuntimeFunctionEntry `json:\"runtime_function\"`\n\tUnwindInfo      UnwindInfo                `json:\"unwind_info\"`\n}\n\nfunc (pe *File) parseUnwindCode(offset uint32, version uint8) (UnwindCode, int) {\n\n\tunwindCode := UnwindCode{}\n\tadvanceBy := 0\n\n\t// Read the unwind code at offset (2 bytes)\n\tuc, err := pe.ReadUint16(offset)\n\tif err != nil {\n\t\treturn unwindCode, advanceBy\n\t}\n\n\tunwindCode.CodeOffset = uint8(uc & 0xff)\n\tunwindCode.UnwindOp = UnwindOpType(uc & 0xf00 >> 8)\n\tunwindCode.OpInfo = uint8(uc & 0xf000 >> 12)\n\n\tswitch unwindCode.UnwindOp {\n\tcase UwOpAllocSmall:\n\t\tsize := int(unwindCode.OpInfo*8 + 8)\n\t\tunwindCode.Operand = \"Size=\" + strconv.Itoa(size)\n\t\tadvanceBy++\n\tcase UwOpAllocLarge:\n\t\tif unwindCode.OpInfo == 0 {\n\t\t\tsize := int(binary.LittleEndian.Uint16(pe.data[offset+2:]) * 8)\n\t\t\tunwindCode.Operand = \"Size=\" + strconv.Itoa(size)\n\t\t\tadvanceBy += 2\n\t\t} else {\n\t\t\tsize := int(binary.LittleEndian.Uint32(pe.data[offset+2:]) << 16)\n\t\t\tunwindCode.Operand = \"Size=\" + strconv.Itoa(size)\n\t\t\tadvanceBy += 3\n\t\t}\n\tcase UwOpSetFpReg:\n\t\tunwindCode.Operand = \"Register=\" + OpInfoRegisters[unwindCode.OpInfo]\n\t\tadvanceBy++\n\tcase UwOpPushNonVol:\n\t\tunwindCode.Operand = \"Register=\" + OpInfoRegisters[unwindCode.OpInfo]\n\t\tadvanceBy++\n\tcase UwOpSaveNonVol:\n\t\tfo := binary.LittleEndian.Uint16(pe.data[offset+2:])\n\t\tunwindCode.FrameOffset = fo * 8\n\t\tunwindCode.Operand = \"Register=\" + OpInfoRegisters[unwindCode.OpInfo] +\n\t\t\t\", Offset=\" + strconv.Itoa(int(unwindCode.FrameOffset))\n\t\tadvanceBy += 2\n\tcase UwOpSaveNonVolFar:\n\t\tfo := binary.LittleEndian.Uint32(pe.data[offset+2:])\n\t\tunwindCode.FrameOffset = uint16(fo * 8)\n\t\tunwindCode.Operand = \"Register=\" + OpInfoRegisters[unwindCode.OpInfo] +\n\t\t\t\", Offset=\" + strconv.Itoa(int(unwindCode.FrameOffset))\n\t\tadvanceBy += 3\n\tcase UwOpSaveXmm128:\n\t\tfo := binary.LittleEndian.Uint16(pe.data[offset+2:])\n\t\tunwindCode.FrameOffset = fo * 16\n\t\tunwindCode.Operand = \"Register=XMM\" + strconv.Itoa(int(unwindCode.OpInfo)) +\n\t\t\t\", Offset=\" + strconv.Itoa(int(unwindCode.FrameOffset))\n\t\tadvanceBy += 2\n\tcase UwOpSaveXmm128Far:\n\t\tfo := binary.LittleEndian.Uint32(pe.data[offset+2:])\n\t\tunwindCode.FrameOffset = uint16(fo)\n\t\tunwindCode.Operand = \"Register=XMM\" + strconv.Itoa(int(unwindCode.OpInfo)) +\n\t\t\t\", Offset=\" + strconv.Itoa(int(unwindCode.FrameOffset))\n\t\tadvanceBy += 3\n\tcase UwOpSetFpRegLarge:\n\t\tunwindCode.Operand = \"Register=\" + OpInfoRegisters[unwindCode.OpInfo]\n\t\tadvanceBy += 2\n\tcase UwOpPushMachFrame:\n\t\tadvanceBy++\n\tcase UwOpEpilog:\n\t\tif version == 2 {\n\t\t\tunwindCode.Operand = \"Flags=\" + strconv.Itoa(int(unwindCode.OpInfo)) + \", Size=\" + strconv.Itoa(int(unwindCode.CodeOffset))\n\t\t}\n\t\tadvanceBy += 2\n\tcase UwOpSpareCode:\n\t\tadvanceBy += 3\n\tdefault:\n\t\tadvanceBy++ // so we can get out of the loop\n\t\tpe.logger.Warnf(\"Wrong unwind opcode %d\", unwindCode.UnwindOp)\n\t}\n\n\treturn unwindCode, advanceBy\n}\n\nfunc (pe *File) parseUnwindInfo(unwindInfo uint32) UnwindInfo {\n\n\tui := UnwindInfo{}\n\n\toffset := pe.GetOffsetFromRva(unwindInfo)\n\tv, err := pe.ReadUint32(offset)\n\tif err != nil {\n\t\treturn ui\n\t}\n\n\t// The lowest 3 bits\n\tui.Version = uint8(v & 0x7)\n\n\t// The next 5 bits.\n\tui.Flags = uint8(v & 0xf8 >> 3)\n\n\t// The next byte\n\tui.SizeOfProlog = uint8(v & 0xff00 >> 8)\n\n\t// The next byte\n\tui.CountOfCodes = uint8(v & 0xff0000 >> 16)\n\n\t// The next 4 bits\n\tui.FrameRegister = uint8(v & 0xf00000 >> 24)\n\n\t// The next 4 bits.\n\tui.FrameOffset = uint8(v&0xf0000000>>28) * 6\n\n\t// Each unwind code struct is 2 bytes wide.\n\toffset += 4\n\ti := 0\n\tfor i < int(ui.CountOfCodes) {\n\t\tucOffset := offset + 2*uint32(i)\n\t\tunwindCode, advanceBy := pe.parseUnwindCode(ucOffset, ui.Version)\n\t\tif advanceBy == 0 {\n\t\t\treturn ui\n\t\t}\n\t\tui.UnwindCodes = append(ui.UnwindCodes, unwindCode)\n\t\ti += advanceBy\n\t}\n\n\tif ui.CountOfCodes&1 == 1 {\n\t\toffset += 2\n\t}\n\n\t// An image-relative pointer to either the function's language-specific\n\t// exception or termination handler, if flag UNW_FLAG_CHAININFO is clear\n\t// and one of the flags UNW_FLAG_EHADLER or UNW_FLAG_UHANDLER is set.\n\tif ui.Flags&UnwFlagEHandler != 0 || ui.Flags&UnwFlagUHandler != 0 {\n\t\tif ui.Flags&UnwFlagChainInfo == 0 {\n\t\t\thandlerOffset := offset + 2*uint32(i)\n\t\t\tui.ExceptionHandler = binary.LittleEndian.Uint32(pe.data[handlerOffset:])\n\t\t}\n\t}\n\n\t// If the UNW_FLAG_CHAININFO flag is set, then an unwind info structure\n\t// is a secondary one, and the shared exception-handler/chained-info\n\t// address field contains the primary unwind information. This sample\n\t// code retrieves the primary unwind information, assuming that unwindInfo\n\t// is the structure that has the UNW_FLAG_CHAININFO flag set.\n\tif ui.Flags&UnwFlagChainInfo != 0 {\n\t\tchainOffset := offset + 2*uint32(i)\n\t\trf := ImageRuntimeFunctionEntry{}\n\t\tsize := uint32(binary.Size(ImageRuntimeFunctionEntry{}))\n\t\terr := pe.structUnpack(&rf, chainOffset, size)\n\t\tif err != nil {\n\t\t\treturn ui\n\t\t}\n\t\tui.FunctionEntry = rf\n\t}\n\n\treturn ui\n}\n\n// Exception directory contains an array of function table entries that are used\n// for exception handling.\nfunc (pe *File) parseExceptionDirectory(rva, size uint32) error {\n\n\t// The target platform determines which format of the function table entry\n\t// to use.\n\tvar exceptions []Exception\n\tfileOffset := pe.GetOffsetFromRva(rva)\n\n\tentrySize := uint32(binary.Size(ImageRuntimeFunctionEntry{}))\n\tentriesCount := size / entrySize\n\n\tfor i := uint32(0); i < entriesCount; i++ {\n\t\tfunctionEntry := ImageRuntimeFunctionEntry{}\n\t\toffset := fileOffset + (entrySize * i)\n\t\terr := pe.structUnpack(&functionEntry, offset, entrySize)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\texception := Exception{RuntimeFunction: functionEntry}\n\n\t\tif pe.Is64 {\n\t\t\texception.UnwindInfo = pe.parseUnwindInfo(functionEntry.UnwindInfoAddress)\n\t\t}\n\n\t\texceptions = append(exceptions, exception)\n\t}\n\n\tpe.Exceptions = exceptions\n\tif len(exceptions) > 0 {\n\t\tpe.HasException = true\n\t}\n\treturn nil\n}\n\n// PrettyUnwindInfoHandlerFlags returns the string representation of the\n// `flags` field of the unwind info structure.\nfunc PrettyUnwindInfoHandlerFlags(flags uint8) []string {\n\tvar values []string\n\n\tunwFlagHandlerMap := map[uint8]string{\n\t\tUnwFlagNHandler:  \"No Handler\",\n\t\tUnwFlagEHandler:  \"Exception\",\n\t\tUnwFlagUHandler:  \"Termination\",\n\t\tUnwFlagChainInfo: \"Chain\",\n\t}\n\n\tfor k, s := range unwFlagHandlerMap {\n\t\tif k&flags != 0 {\n\t\t\tvalues = append(values, s)\n\t\t}\n\t}\n\treturn values\n}\n\n// String returns the string representation of the an unwind opcode.\nfunc (uo UnwindOpType) String() string {\n\n\tunOpToString := map[UnwindOpType]string{\n\t\tUwOpPushNonVol:    \"UWOP_PUSH_NONVOL\",\n\t\tUwOpAllocLarge:    \"UWOP_ALLOC_LARE\",\n\t\tUwOpAllocSmall:    \"UWOP_ALLOC_SMALL\",\n\t\tUwOpSetFpReg:      \"UWOP_SET_FPREG\",\n\t\tUwOpSaveNonVol:    \"UWOP_SAVE_NONVOL\",\n\t\tUwOpSaveNonVolFar: \"UWOP_SAVE_NONVOL_FAR\",\n\t\tUwOpEpilog:        \"UWOP_EPILOG\",\n\t\tUwOpSpareCode:     \"UWOP_SPARE_CODE\",\n\t\tUwOpSaveXmm128:    \"UWOP_SAVE_XMM128\",\n\t\tUwOpSaveXmm128Far: \"UWOP_SAVE_XMM128_FAR\",\n\t\tUwOpPushMachFrame: \"UWOP_PUSH_MACHFRAME\",\n\t\tUwOpSetFpRegLarge: \"UWOP_SET_FPREG_LARGE\",\n\t}\n\n\tif val, ok := unOpToString[uo]; ok {\n\t\treturn val\n\t}\n\n\treturn \"?\"\n}\n"
  },
  {
    "path": "exception_test.go",
    "content": "// Copyright 2021 Saferwall. All rights reserved.\r\n// Use of this source code is governed by Apache v2 license\r\n// license that can be found in the LICENSE file.\r\n\r\npackage pe\r\n\r\nimport (\r\n\t\"reflect\"\r\n\t\"strconv\"\r\n\t\"testing\"\r\n)\r\n\r\ntype TestExceptionEntry struct {\r\n\tentryCount  int\r\n\tentryIndex  int\r\n\truntimeFunc ImageRuntimeFunctionEntry\r\n\tunwindInfo  UnwindInfo\r\n}\r\n\r\nfunc TestParseExceptionDirectory(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout TestExceptionEntry\r\n\t}{\r\n\t\t{\r\n\t\t\tgetAbsoluteFilePath(\"test/kernel32.dll\"),\r\n\t\t\tTestExceptionEntry{\r\n\t\t\t\tentryCount: 1835,\r\n\t\t\t\tentryIndex: 0,\r\n\t\t\t\truntimeFunc: ImageRuntimeFunctionEntry{\r\n\t\t\t\t\tBeginAddress:      0x1010,\r\n\t\t\t\t\tEndAddress:        0x1053,\r\n\t\t\t\t\tUnwindInfoAddress: 0x938b8,\r\n\t\t\t\t},\r\n\t\t\t\tunwindInfo: UnwindInfo{\r\n\t\t\t\t\tVersion:       0x1,\r\n\t\t\t\t\tFlags:         0x0,\r\n\t\t\t\t\tSizeOfProlog:  0x7,\r\n\t\t\t\t\tCountOfCodes:  0x1,\r\n\t\t\t\t\tFrameRegister: 0x0,\r\n\t\t\t\t\tFrameOffset:   0x0,\r\n\t\t\t\t\tUnwindCodes: []UnwindCode{\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tCodeOffset:  0x07,\r\n\t\t\t\t\t\t\tUnwindOp:    0x2,\r\n\t\t\t\t\t\t\tOpInfo:      0x8,\r\n\t\t\t\t\t\t\tOperand:     \"Size=72\",\r\n\t\t\t\t\t\t\tFrameOffset: 0x0,\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\t// fake exception directory\r\n\t\t\tgetAbsoluteFilePath(\"test/0585495341e0ffaae1734acb78708ff55cd3612d844672d37226ef63d12652d0\"),\r\n\t\t\tTestExceptionEntry{\r\n\t\t\t\tentryCount: 3349,\r\n\t\t\t\tentryIndex: 0,\r\n\t\t\t\truntimeFunc: ImageRuntimeFunctionEntry{\r\n\t\t\t\t\tBeginAddress:      0xf860617,\r\n\t\t\t\t\tEndAddress:        0x205fef60,\r\n\t\t\t\t\tUnwindInfoAddress: 0x2c0365b4,\r\n\t\t\t\t},\r\n\t\t\t\tunwindInfo: UnwindInfo{},\r\n\t\t\t},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\t\t\tswitch file.Is64 {\r\n\t\t\tcase true:\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryException]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\tcase false:\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryException]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseExceptionDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseExceptionDirectory(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\t\t\tgot := file.Exceptions\r\n\t\t\tif len(got) != tt.out.entryCount {\r\n\t\t\t\tt.Errorf(\"Exception entry count assertion failed, got %v, want %v\", len(got), tt.out.entryCount)\r\n\t\t\t}\r\n\r\n\t\t\truntimeFunc := file.Exceptions[tt.out.entryIndex].RuntimeFunction\r\n\t\t\tif runtimeFunc != tt.out.runtimeFunc {\r\n\t\t\t\tt.Errorf(\"RuntimeFunction assertion failed, got %v, want %v\", len(got), tt.out.entryCount)\r\n\t\t\t}\r\n\r\n\t\t\tunwindInfo := file.Exceptions[tt.out.entryIndex].UnwindInfo\r\n\t\t\tif !reflect.DeepEqual(unwindInfo, tt.out.unwindInfo) {\r\n\t\t\t\tt.Errorf(\"UnwindInfo assertion failed, got %v, want %v\", unwindInfo, tt.out.unwindInfo)\r\n\t\t\t}\r\n\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestExceptionDirectoryUnwindOpcode(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  UnwindOpType\r\n\t\tout string\r\n\t}{\r\n\t\t{\r\n\t\t\tUwOpPushNonVol,\r\n\t\t\t\"UWOP_PUSH_NONVOL\",\r\n\t\t},\r\n\t\t{\r\n\t\t\tUnwindOpType(0xff),\r\n\t\t\t\"?\",\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tname := \"CaseUnwindOpcodeEqualTo_\" + strconv.Itoa(int(tt.in))\r\n\t\tt.Run(name, func(t *testing.T) {\r\n\t\t\tgot := tt.in.String()\r\n\t\t\tif got != tt.out {\r\n\t\t\t\tt.Errorf(\"unwind opcode string interpretation, got %v, want %v\",\r\n\t\t\t\t\tgot, tt.out)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n"
  },
  {
    "path": "exports.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n)\n\nconst (\n\tmaxExportedSymbols = 0x2000\n)\n\nvar (\n\tErrExportMaxOrdEntries       = \"Export directory contains more than max ordinal entries\"\n\tErrExportManyRepeatedEntries = \"Export directory contains many repeated entries\"\n\tAnoNullNumberOfFunctions     = \"Export directory contains zero number of functions\"\n\tAnoNullAddressOfFunctions    = \"Export directory contains zero address of functions\"\n)\n\n// ImageExportDirectory represents the IMAGE_EXPORT_DIRECTORY structure.\n// The export directory table contains address information that is used\n// to resolve imports to the entry points within this image.\ntype ImageExportDirectory struct {\n\t// Reserved, must be 0.\n\tCharacteristics uint32 `json:\"characteristics\"`\n\n\t// The time and date that the export data was created.\n\tTimeDateStamp uint32 `json:\"time_date_stamp\"`\n\n\t// The major version number.\n\t//The major and minor version numbers can be set by the user.\n\tMajorVersion uint16 `json:\"major_version\"`\n\n\t// The minor version number.\n\tMinorVersion uint16 `json:\"minor_version\"`\n\n\t// The address of the ASCII string that contains the name of the DLL.\n\t// This address is relative to the image base.\n\tName uint32 `json:\"name\"`\n\n\t// The starting ordinal number for exports in this image. This field\n\t// specifies the starting ordinal number for the export address table.\n\t// It is usually set to 1.\n\tBase uint32 `json:\"base\"`\n\n\t// The number of entries in the export address table.\n\tNumberOfFunctions uint32 `json:\"number_of_functions\"`\n\n\t// The number of entries in the name pointer table. This is also the number\n\t// of entries in the ordinal table.\n\tNumberOfNames uint32 `json:\"number_of_names\"`\n\n\t// The address of the export address table, relative to the image base.\n\tAddressOfFunctions uint32 `json:\"address_of_functions\"`\n\n\t// The address of the export name pointer table, relative to the image base.\n\t// The table size is given by the Number of Name Pointers field.\n\tAddressOfNames uint32 `json:\"address_of_names\"`\n\n\t// The address of the ordinal table, relative to the image base.\n\tAddressOfNameOrdinals uint32 `json:\"address_of_name_ordinals\"`\n}\n\n// ExportFunction represents an imported function in the export table.\ntype ExportFunction struct {\n\tOrdinal      uint32 `json:\"ordinal\"`\n\tFunctionRVA  uint32 `json:\"function_rva\"`\n\tNameOrdinal  uint32 `json:\"name_ordinal\"`\n\tNameRVA      uint32 `json:\"name_rva\"`\n\tName         string `json:\"name\"`\n\tForwarder    string `json:\"forwarder\"`\n\tForwarderRVA uint32 `json:\"forwarder_rva\"`\n}\n\n// Export represent the export table.\ntype Export struct {\n\tFunctions []ExportFunction     `json:\"functions\"`\n\tStruct    ImageExportDirectory `json:\"struct\"`\n\tName      string               `json:\"name\"`\n}\n\n/*\nA few notes learned from `Corkami` about parsing export directory:\n  - like many data directories, Exports' size are not necessary, except for forwarding.\n  - Characteristics, TimeDateStamp, MajorVersion and MinorVersion are not necessary.\n  - the export name is not necessary, and can be anything.\n  - AddressOfNames is lexicographically-ordered.\n  - export names can have any value (even null or more than 65536 characters long,\n    with unprintable characters), just null terminated.\n  - an EXE can have exports (no need of relocation nor DLL flag), and can use\n\nthem normally\n- exports can be not used for execution, but for documenting the internal code\n- numbers of functions will be different from number of names when the file\nis exporting some functions by ordinal.\n*/\nfunc (pe *File) parseExportDirectory(rva, size uint32) error {\n\n\t// Define some vars.\n\texp := Export{}\n\texportDir := ImageExportDirectory{}\n\terrorMsg := fmt.Sprintf(\"Error parsing export directory at RVA: 0x%x\", rva)\n\n\tfileOffset := pe.GetOffsetFromRva(rva)\n\texportDirSize := uint32(binary.Size(exportDir))\n\terr := pe.structUnpack(&exportDir, fileOffset, exportDirSize)\n\tif err != nil {\n\t\treturn errors.New(errorMsg)\n\t}\n\texp.Struct = exportDir\n\n\t// We keep track of the bytes left in the file and use it to set a upper\n\t// bound in the number of items that can be read from the different arrays.\n\tlengthUntilEOF := func(rva uint32) uint32 {\n\t\treturn pe.size - pe.GetOffsetFromRva(rva)\n\t}\n\tvar length uint32\n\tvar addressOfNames []byte\n\n\t// Some DLLs have null number of functions.\n\tif exportDir.NumberOfFunctions == 0 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoNullNumberOfFunctions)\n\t}\n\n\t// Some DLLs have null address of functions.\n\tif exportDir.AddressOfFunctions == 0 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoNullAddressOfFunctions)\n\t}\n\n\tlength = min(lengthUntilEOF(exportDir.AddressOfNames),\n\t\texportDir.NumberOfNames*4)\n\taddressOfNames, err = pe.GetData(exportDir.AddressOfNames, length)\n\tif err != nil {\n\t\treturn errors.New(errorMsg)\n\t}\n\n\tlength = min(lengthUntilEOF(exportDir.AddressOfNameOrdinals),\n\t\texportDir.NumberOfNames*4)\n\taddressOfNameOrdinals, err := pe.GetData(exportDir.AddressOfNameOrdinals, length)\n\tif err != nil {\n\t\treturn errors.New(errorMsg)\n\t}\n\n\tlength = min(lengthUntilEOF(exportDir.AddressOfFunctions),\n\t\texportDir.NumberOfFunctions*4)\n\taddressOfFunctions, err := pe.GetData(exportDir.AddressOfFunctions, length)\n\tif err != nil {\n\t\treturn errors.New(errorMsg)\n\t}\n\n\texp.Name = pe.getStringAtRVA(exportDir.Name, 0x100000)\n\n\tmaxFailedEntries := 10\n\tvar forwarderStr string\n\tvar forwarderOffset uint32\n\tsafetyBoundary := pe.size // overly generous upper bound\n\tsymbolCounts := make(map[uint32]int)\n\tparsingFailed := false\n\n\t// read the image export directory\n\tsection := pe.getSectionByRva(exportDir.AddressOfNames)\n\tif section != nil {\n\t\tsafetyBoundary = (section.Header.VirtualAddress +\n\t\t\tuint32(len(section.Data(0, 0, pe)))) - exportDir.AddressOfNames\n\t}\n\n\tnumNames := min(exportDir.NumberOfNames, safetyBoundary/4)\n\tvar symbolAddress uint32\n\tfor i := uint32(0); i < numNames; i++ {\n\n\t\tdefer func() {\n\t\t\t// recover from panic if one occured. Set err to nil otherwise.\n\t\t\tif recover() != nil {\n\t\t\t\terr = errors.New(\"array index out of bounds\")\n\t\t\t}\n\t\t}()\n\n\t\tsymbolOrdinal := binary.LittleEndian.Uint16(addressOfNameOrdinals[i*2:])\n\t\tsymbolAddress = binary.LittleEndian.Uint32(addressOfFunctions[symbolOrdinal*4:])\n\t\tif symbolAddress == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\t// If the function's RVA points within the export directory\n\t\t// it will point to a string with the forwarded symbol's string\n\t\t// instead of pointing the the function start address.\n\t\tif symbolAddress >= rva && symbolAddress < rva+size {\n\t\t\tforwarderStr = pe.getStringAtRVA(symbolAddress, 0x100000)\n\t\t\tforwarderOffset = pe.GetOffsetFromRva(symbolAddress)\n\t\t} else {\n\t\t\tforwarderStr = \"\"\n\t\t\tfileOffset = 0\n\t\t}\n\n\t\tsymbolNameAddress := binary.LittleEndian.Uint32(addressOfNames[i*4:])\n\t\tif symbolNameAddress == 0 {\n\t\t\tmaxFailedEntries--\n\t\t\tif maxFailedEntries <= 0 {\n\t\t\t\tparsingFailed = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tsymbolName := pe.getStringAtRVA(symbolNameAddress, 0x100000)\n\t\tif !IsValidFunctionName(symbolName) {\n\t\t\tparsingFailed = true\n\t\t\tbreak\n\t\t}\n\n\t\tsymbolNameOffset := pe.GetOffsetFromRva(symbolNameAddress)\n\t\tif symbolNameOffset == 0 {\n\t\t\tmaxFailedEntries--\n\t\t\tif maxFailedEntries <= 0 {\n\t\t\t\tparsingFailed = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\t// File 0b1d3d3664915577ab9a32188d29bbf3542b86c7b9ce333e245496c3018819f1\n\t\t// was being parsed as potentially containing millions of exports.\n\t\t// Checking for duplicates addresses the issue.\n\t\tsymbolCounts[symbolAddress]++\n\t\tif symbolCounts[symbolAddress] > 10 {\n\t\t\tif !stringInSlice(ErrExportManyRepeatedEntries, pe.Anomalies) {\n\t\t\t\tpe.Anomalies = append(pe.Anomalies, ErrExportManyRepeatedEntries)\n\t\t\t}\n\t\t}\n\t\tif len(symbolCounts) > maxExportedSymbols {\n\t\t\tif !stringInSlice(ErrExportMaxOrdEntries, pe.Anomalies) {\n\t\t\t\tpe.Anomalies = append(pe.Anomalies, ErrExportMaxOrdEntries)\n\t\t\t}\n\t\t}\n\t\tnewExport := ExportFunction{\n\t\t\tName:         symbolName,\n\t\t\tNameRVA:      symbolNameAddress,\n\t\t\tNameOrdinal:  uint32(symbolOrdinal),\n\t\t\tOrdinal:      exportDir.Base + uint32(symbolOrdinal),\n\t\t\tFunctionRVA:  symbolAddress,\n\t\t\tForwarder:    forwarderStr,\n\t\t\tForwarderRVA: forwarderOffset,\n\t\t}\n\n\t\texp.Functions = append(exp.Functions, newExport)\n\t}\n\n\tif parsingFailed {\n\t\tfmt.Printf(\"RVA AddressOfNames in the export directory points to an \"+\n\t\t\t\"invalid address: 0x%x\\n\", exportDir.AddressOfNames)\n\t}\n\n\tmaxFailedEntries = 10\n\tsection = pe.getSectionByRva(exportDir.AddressOfFunctions)\n\n\t// Overly generous upper bound\n\tsafetyBoundary = pe.size\n\tif section != nil {\n\t\tsafetyBoundary = section.Header.VirtualAddress +\n\t\t\tuint32(len(section.Data(0, 0, pe))) - exportDir.AddressOfNames\n\t}\n\tparsingFailed = false\n\tordinals := make(map[uint32]bool)\n\tfor _, export := range exp.Functions {\n\t\tordinals[export.Ordinal] = true\n\t}\n\tnumNames = min(exportDir.NumberOfFunctions, safetyBoundary/4)\n\tfor i := uint32(0); i < numNames; i++ {\n\t\tvalue := i + exportDir.Base\n\t\tif ordinals[value] {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(addressOfFunctions) >= int(i*4)+4 {\n\t\t\tsymbolAddress = binary.LittleEndian.Uint32(addressOfFunctions[i*4:])\n\t\t}\n\t\tif symbolAddress == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Checking for forwarder again.\n\t\tif symbolAddress >= rva && symbolAddress < rva+size {\n\t\t\tforwarderStr = pe.getStringAtRVA(symbolAddress, 0x100000)\n\t\t\tforwarderOffset = pe.GetOffsetFromRva(symbolAddress)\n\t\t} else {\n\t\t\tforwarderStr = \"\"\n\t\t\tfileOffset = 0\n\t\t}\n\n\t\t// File 0b1d3d3664915577ab9a32188d29bbf3542b86c7b9ce333e245496c3018819f1\n\t\t// was being parsed as potentially containing millions of exports.\n\t\t// Checking for duplicates addresses the issue.\n\t\tsymbolCounts[symbolAddress]++\n\t\tif symbolCounts[symbolAddress] > 10 {\n\t\t\tif !stringInSlice(ErrExportManyRepeatedEntries, pe.Anomalies) {\n\t\t\t\tpe.Anomalies = append(pe.Anomalies, ErrExportManyRepeatedEntries)\n\t\t\t}\n\t\t}\n\t\tif len(symbolCounts) > maxExportedSymbols {\n\t\t\tif !stringInSlice(ErrExportMaxOrdEntries, pe.Anomalies) {\n\n\t\t\t\tpe.Anomalies = append(pe.Anomalies, ErrExportMaxOrdEntries)\n\t\t\t}\n\t\t}\n\t\tnewExport := ExportFunction{\n\t\t\tOrdinal:      exportDir.Base + i,\n\t\t\tFunctionRVA:  symbolAddress,\n\t\t\tForwarder:    forwarderStr,\n\t\t\tForwarderRVA: forwarderOffset,\n\t\t}\n\n\t\texp.Functions = append(exp.Functions, newExport)\n\t}\n\n\tpe.Export = exp\n\tpe.HasExport = true\n\treturn nil\n}\n\n// GetExportFunctionByRVA return an export function given an RVA.\nfunc (pe *File) GetExportFunctionByRVA(rva uint32) ExportFunction {\n\tfor _, exp := range pe.Export.Functions {\n\t\tif exp.FunctionRVA == rva {\n\t\t\treturn exp\n\t\t}\n\t}\n\n\treturn ExportFunction{}\n}\n"
  },
  {
    "path": "exports_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\r\n// Use of this source code is governed by Apache v2 license\r\n// license that can be found in the LICENSE file.\r\n\r\npackage pe\r\n\r\nimport (\r\n\t\"testing\"\r\n)\r\n\r\ntype TestExport struct {\r\n\tentryCount int\r\n\tentryIndex int\r\n\tname       string\r\n\timgExpDir  ImageExportDirectory\r\n\texpFunc    ExportFunction\r\n}\r\n\r\nfunc TestExportDirectory(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout TestExport\r\n\t}{\r\n\t\t{\r\n\t\t\tgetAbsoluteFilePath(\"test/kernel32.dll\"),\r\n\t\t\tTestExport{\r\n\t\t\t\tentryCount: 1633,\r\n\t\t\t\tentryIndex: 0,\r\n\t\t\t\tname:       \"KERNEL32.dll\",\r\n\t\t\t\timgExpDir: ImageExportDirectory{\r\n\t\t\t\t\tTimeDateStamp:         0x38B369C4,\r\n\t\t\t\t\tName:                  0x0009E1D2,\r\n\t\t\t\t\tBase:                  0x1,\r\n\t\t\t\t\tNumberOfFunctions:     0x661,\r\n\t\t\t\t\tNumberOfNames:         0x661,\r\n\t\t\t\t\tAddressOfFunctions:    0x0009A208,\r\n\t\t\t\t\tAddressOfNames:        0x0009BB8C,\r\n\t\t\t\t\tAddressOfNameOrdinals: 0x0009D510,\r\n\t\t\t\t},\r\n\t\t\t\texpFunc: ExportFunction{\r\n\t\t\t\t\tOrdinal:      0x1,\r\n\t\t\t\t\tFunctionRVA:  0x0009E1F7,\r\n\t\t\t\t\tNameRVA:      0x0009E1DF,\r\n\t\t\t\t\tName:         \"AcquireSRWLockExclusive\",\r\n\t\t\t\t\tForwarder:    \"NTDLL.RtlAcquireSRWLockExclusive\",\r\n\t\t\t\t\tForwarderRVA: 0x9CBF7,\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tgetAbsoluteFilePath(\"test/mfc140u.dll\"),\r\n\t\t\tTestExport{\r\n\t\t\t\tentryCount: 14103,\r\n\t\t\t\tentryIndex: 0,\r\n\t\t\t\tname:       \"KERNEL32.dll\",\r\n\t\t\t\timgExpDir: ImageExportDirectory{\r\n\t\t\t\t\tTimeDateStamp:      0x5b8f7bca,\r\n\t\t\t\t\tName:               0x3e2e0c,\r\n\t\t\t\t\tBase:               0x100,\r\n\t\t\t\t\tNumberOfFunctions:  0x371d,\r\n\t\t\t\t\tAddressOfFunctions: 0x3d5198,\r\n\t\t\t\t},\r\n\t\t\t\texpFunc: ExportFunction{\r\n\t\t\t\t\tOrdinal:     0x100,\r\n\t\t\t\t\tFunctionRVA: 0x275fa0,\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t\t// {\r\n\t\t// \t// TODO: ThreadSanitizer failed to allocate 0x000048000000 (1207959552) in Github CI\r\n\t\t// \tgetAbsoluteFilePath(\"test/0b1d3d3664915577ab9a32188d29bbf3542b86c7b9ce333e245496c3018819f1\"),\r\n\t\t// \tTestExport{\r\n\t\t// \t\tentryCount: 7728638,\r\n\t\t// \t\tentryIndex: 0,\r\n\t\t// \t\tname:       \"\",\r\n\t\t// \t\timgExpDir: ImageExportDirectory{\r\n\t\t// \t\t\tCharacteristics:       0xac0000,\r\n\t\t// \t\t\tTimeDateStamp:         0xac0000,\r\n\t\t// \t\t\tMinorVersion:          0xac,\r\n\t\t// \t\t\tName:                  0xac0000,\r\n\t\t// \t\t\tBase:                  0xac0000,\r\n\t\t// \t\t\tNumberOfFunctions:     0xac0000,\r\n\t\t// \t\t\tNumberOfNames:         0xac0000,\r\n\t\t// \t\t\tAddressOfFunctions:    0xac0000,\r\n\t\t// \t\t\tAddressOfNames:        0xac0000,\r\n\t\t// \t\t\tAddressOfNameOrdinals: 0xac0000,\r\n\t\t// \t\t},\r\n\t\t// \t\texpFunc: ExportFunction{\r\n\t\t// \t\t\tOrdinal:     0xac0000,\r\n\t\t// \t\t\tFunctionRVA: 0xac0000,\r\n\t\t// \t\t\tNameRVA:     0xac0000,\r\n\t\t// \t\t},\r\n\t\t// \t},\r\n\t\t// },\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryExport]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryExport]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseExportDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseExportDirectory(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\texport := file.Export\r\n\t\t\tif len(export.Functions) != tt.out.entryCount {\r\n\t\t\t\tt.Fatalf(\"export functions count assertion failed, got %v, want %v\",\r\n\t\t\t\t\tlen(export.Functions), tt.out.entryCount)\r\n\t\t\t}\r\n\r\n\t\t\timgExpDir := export.Struct\r\n\t\t\tif imgExpDir != tt.out.imgExpDir {\r\n\t\t\t\tt.Fatalf(\"image export directory assertion failed, got %v, want %v\",\r\n\t\t\t\t\timgExpDir, tt.out.imgExpDir)\r\n\t\t\t}\r\n\r\n\t\t\tif len(export.Functions) > 0 {\r\n\t\t\t\texpFunc := export.Functions[tt.out.entryIndex]\r\n\t\t\t\tif expFunc != tt.out.expFunc {\r\n\t\t\t\t\tt.Fatalf(\"export entry assertion failed, got %v, want %v\", expFunc, tt.out.expFunc)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n"
  },
  {
    "path": "file.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"errors\"\n\t\"os\"\n\n\t\"github.com/edsrzf/mmap-go\"\n\n\t\"github.com/saferwall/pe/log\"\n)\n\n// A File represents an open PE file.\ntype File struct {\n\tDOSHeader    ImageDOSHeader              `json:\"dos_header,omitempty\"`\n\tRichHeader   RichHeader                  `json:\"rich_header,omitempty\"`\n\tNtHeader     ImageNtHeader               `json:\"nt_header,omitempty\"`\n\tCOFF         COFF                        `json:\"coff,omitempty\"`\n\tSections     []Section                   `json:\"sections,omitempty\"`\n\tImports      []Import                    `json:\"imports,omitempty\"`\n\tExport       Export                      `json:\"export,omitempty\"`\n\tDebugs       []DebugEntry                `json:\"debugs,omitempty\"`\n\tRelocations  []Relocation                `json:\"relocations,omitempty\"`\n\tResources    ResourceDirectory           `json:\"resources,omitempty\"`\n\tTLS          TLSDirectory                `json:\"tls,omitempty\"`\n\tLoadConfig   LoadConfig                  `json:\"load_config,omitempty\"`\n\tExceptions   []Exception                 `json:\"exceptions,omitempty\"`\n\tCertificates CertificateSection          `json:\"certificates,omitempty\"`\n\tDelayImports []DelayImport               `json:\"delay_imports,omitempty\"`\n\tBoundImports []BoundImportDescriptorData `json:\"bound_imports,omitempty\"`\n\tGlobalPtr    uint32                      `json:\"global_ptr,omitempty\"`\n\tCLR          CLRData                     `json:\"clr,omitempty\"`\n\tIAT          []IATEntry                  `json:\"iat,omitempty\"`\n\tAnomalies    []string                    `json:\"anomalies,omitempty\"`\n\tHeader       []byte\n\tdata         mmap.MMap\n\tFileInfo\n\tsize          uint32\n\tOverlayOffset int64\n\tf             *os.File\n\topts          *Options\n\tlogger        *log.Helper\n}\n\n// Options that influence the PE parsing behaviour.\ntype Options struct {\n\t// Parse only the PE header and do not parse data directories, by default (false).\n\tFast bool\n\n\t// Includes section entropy, by default (false).\n\tSectionEntropy bool\n\n\t// Maximum COFF symbols to parse, by default (MaxDefaultCOFFSymbolsCount).\n\tMaxCOFFSymbolsCount uint32\n\n\t// Maximum relocations to parse, by default (MaxDefaultRelocEntriesCount).\n\tMaxRelocEntriesCount uint32\n\n\t// Disable certificate validation, by default (false).\n\tDisableCertValidation bool\n\n\t// Disable signature validation, by default (false).\n\tDisableSignatureValidation bool\n\n\t// A custom logger.\n\tLogger log.Logger\n\n\t// OmitExportDirectory determines if export directory parsing is skipped, by default (false).\n\tOmitExportDirectory bool\n\n\t// OmitImportDirectory determines if import directory parsing is skipped, by default (false).\n\tOmitImportDirectory bool\n\n\t// OmitExceptionDirectory determines if exception directory parsing is skipped, by default (false).\n\tOmitExceptionDirectory bool\n\n\t// OmitResourceDirectory determines if resource directory parsing is skipped, by default (false).\n\tOmitResourceDirectory bool\n\n\t// OmitSecurityDirectory determines if security directory parsing is skipped, by default (false).\n\tOmitSecurityDirectory bool\n\n\t// OmitRelocDirectory determines if relocation directory parsing is skipped, by default (false).\n\tOmitRelocDirectory bool\n\n\t// OmitDebugDirectory determines if debug directory parsing is skipped, by default (false).\n\tOmitDebugDirectory bool\n\n\t// OmitArchitectureDirectory determines if architecture directory parsing is skipped, by default (false).\n\tOmitArchitectureDirectory bool\n\n\t// OmitGlobalPtrDirectory determines if global pointer directory parsing is skipped, by default (false).\n\tOmitGlobalPtrDirectory bool\n\n\t// OmitTLSDirectory determines if TLS directory parsing is skipped, by default (false).\n\tOmitTLSDirectory bool\n\n\t// OmitLoadConfigDirectory determines if load config directory parsing is skipped, by default (false).\n\tOmitLoadConfigDirectory bool\n\n\t// OmitBoundImportDirectory determines if bound import directory parsing is skipped, by default (false).\n\tOmitBoundImportDirectory bool\n\n\t// OmitIATDirectory determines if IAT directory parsing is skipped, by default (false).\n\tOmitIATDirectory bool\n\n\t// OmitDelayImportDirectory determines if delay import directory parsing is skipped, by default (false).\n\tOmitDelayImportDirectory bool\n\n\t// OmitCLRHeaderDirectory determines if CLR header directory parsing is skipped, by default (false).\n\tOmitCLRHeaderDirectory bool\n\n\t// OmitCLRMetadata determines if CLR metadata parsing is skipped, by default (false).\n\tOmitCLRMetadata bool\n}\n\n// New instantiates a file instance with options given a file name.\nfunc New(name string, opts *Options) (*File, error) {\n\tf, err := os.Open(name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewFile(f, opts)\n}\n\n// NewFile instantiates a file instance with options given a file handle.\nfunc NewFile(f *os.File, opts *Options) (*File, error) {\n\t// Memory map the file instead of using read/write.\n\tdata, err := mmap.Map(f, mmap.RDONLY, 0)\n\tif err != nil {\n\t\tf.Close()\n\t\treturn nil, err\n\t}\n\n\tfile := File{}\n\tif opts != nil {\n\t\tfile.opts = opts\n\t} else {\n\t\tfile.opts = &Options{}\n\t}\n\n\tif file.opts.MaxCOFFSymbolsCount == 0 {\n\t\tfile.opts.MaxCOFFSymbolsCount = MaxDefaultCOFFSymbolsCount\n\t}\n\tif file.opts.MaxRelocEntriesCount == 0 {\n\t\tfile.opts.MaxRelocEntriesCount = MaxDefaultRelocEntriesCount\n\t}\n\n\tvar logger log.Logger\n\tif file.opts.Logger == nil {\n\t\tlogger = log.NewStdLogger(os.Stdout)\n\t\tfile.logger = log.NewHelper(log.NewFilter(logger,\n\t\t\tlog.FilterLevel(log.LevelError)))\n\t} else {\n\t\tfile.logger = log.NewHelper(file.opts.Logger)\n\t}\n\n\tfile.data = data\n\tfile.size = uint32(len(file.data))\n\tfile.f = f\n\treturn &file, nil\n}\n\n// NewBytes instantiates a file instance with options given a memory buffer.\nfunc NewBytes(data []byte, opts *Options) (*File, error) {\n\n\tfile := File{}\n\tif opts != nil {\n\t\tfile.opts = opts\n\t} else {\n\t\tfile.opts = &Options{}\n\t}\n\n\tif file.opts.MaxCOFFSymbolsCount == 0 {\n\t\tfile.opts.MaxCOFFSymbolsCount = MaxDefaultCOFFSymbolsCount\n\t}\n\tif file.opts.MaxRelocEntriesCount == 0 {\n\t\tfile.opts.MaxRelocEntriesCount = MaxDefaultRelocEntriesCount\n\t}\n\n\tvar logger log.Logger\n\tif file.opts.Logger == nil {\n\t\tlogger = log.NewStdLogger(os.Stdout)\n\t\tfile.logger = log.NewHelper(log.NewFilter(logger,\n\t\t\tlog.FilterLevel(log.LevelError)))\n\t} else {\n\t\tfile.logger = log.NewHelper(opts.Logger)\n\t}\n\n\tfile.data = data\n\tfile.size = uint32(len(file.data))\n\treturn &file, nil\n}\n\nfunc (pe *File) Close() error {\n\t_ = pe.Unmap()\n\n\tif pe.f != nil {\n\t\treturn pe.f.Close()\n\t}\n\treturn nil\n}\n\n// Close memory mapped file\nfunc (pe *File) Unmap() error {\n\tif pe.data != nil {\n\t\treturn pe.data.Unmap()\n\t}\n\n\treturn nil\n}\n\n// Parse performs the file parsing for a PE binary.\nfunc (pe *File) Parse() error {\n\n\t// check for the smallest PE size.\n\tif len(pe.data) < TinyPESize {\n\t\treturn ErrInvalidPESize\n\t}\n\n\t// Parse the DOS header.\n\terr := pe.ParseDOSHeader()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Parse the Rich header.\n\terr = pe.ParseRichHeader()\n\tif err != nil {\n\t\tpe.logger.Errorf(\"rich header parsing failed: %v\", err)\n\t}\n\n\t// Parse the NT header.\n\terr = pe.ParseNTHeader()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Parse COFF symbol table.\n\terr = pe.ParseCOFFSymbolTable()\n\tif err != nil {\n\t\tpe.logger.Debugf(\"coff symbols parsing failed: %v\", err)\n\t}\n\n\t// Parse the Section Header.\n\terr = pe.ParseSectionHeader()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// In fast mode, do not parse data directories.\n\tif pe.opts.Fast {\n\t\treturn nil\n\t}\n\n\t// Parse the Data Directory entries.\n\treturn pe.ParseDataDirectories()\n}\n\n// String stringify the data directory entry.\nfunc (entry ImageDirectoryEntry) String() string {\n\tdataDirMap := map[ImageDirectoryEntry]string{\n\t\tImageDirectoryEntryExport:       \"Export\",\n\t\tImageDirectoryEntryImport:       \"Import\",\n\t\tImageDirectoryEntryResource:     \"Resource\",\n\t\tImageDirectoryEntryException:    \"Exception\",\n\t\tImageDirectoryEntryCertificate:  \"Security\",\n\t\tImageDirectoryEntryBaseReloc:    \"Relocation\",\n\t\tImageDirectoryEntryDebug:        \"Debug\",\n\t\tImageDirectoryEntryArchitecture: \"Architecture\",\n\t\tImageDirectoryEntryGlobalPtr:    \"GlobalPtr\",\n\t\tImageDirectoryEntryTLS:          \"TLS\",\n\t\tImageDirectoryEntryLoadConfig:   \"LoadConfig\",\n\t\tImageDirectoryEntryBoundImport:  \"BoundImport\",\n\t\tImageDirectoryEntryIAT:          \"IAT\",\n\t\tImageDirectoryEntryDelayImport:  \"DelayImport\",\n\t\tImageDirectoryEntryCLR:          \"CLR\",\n\t\tImageDirectoryEntryReserved:     \"Reserved\",\n\t}\n\n\treturn dataDirMap[entry]\n}\n\n// ParseDataDirectories parses the data directories. The DataDirectory is an\n// array of 16 structures. Each array entry has a predefined meaning for what\n// it refers to.\nfunc (pe *File) ParseDataDirectories() error {\n\n\tfoundErr := false\n\toh32 := ImageOptionalHeader32{}\n\toh64 := ImageOptionalHeader64{}\n\n\tswitch pe.Is64 {\n\tcase true:\n\t\toh64 = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\tcase false:\n\t\toh32 = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t}\n\n\t// Maps data directory index to function which parses that directory.\n\tfuncMaps := make(map[ImageDirectoryEntry]func(uint32, uint32) error)\n\tif !pe.opts.OmitExportDirectory {\n\t\tfuncMaps[ImageDirectoryEntryExport] = pe.parseExportDirectory\n\t}\n\tif !pe.opts.OmitImportDirectory {\n\t\tfuncMaps[ImageDirectoryEntryImport] = pe.parseImportDirectory\n\t}\n\tif !pe.opts.OmitExceptionDirectory {\n\t\tfuncMaps[ImageDirectoryEntryException] = pe.parseExceptionDirectory\n\t}\n\tif !pe.opts.OmitResourceDirectory {\n\t\tfuncMaps[ImageDirectoryEntryResource] = pe.parseResourceDirectory\n\t}\n\tif !pe.opts.OmitSecurityDirectory {\n\t\tfuncMaps[ImageDirectoryEntryCertificate] = pe.parseSecurityDirectory\n\t}\n\tif !pe.opts.OmitRelocDirectory {\n\t\tfuncMaps[ImageDirectoryEntryBaseReloc] = pe.parseRelocDirectory\n\t}\n\tif !pe.opts.OmitDebugDirectory {\n\t\tfuncMaps[ImageDirectoryEntryDebug] = pe.parseDebugDirectory\n\t}\n\tif !pe.opts.OmitArchitectureDirectory {\n\t\tfuncMaps[ImageDirectoryEntryArchitecture] = pe.parseArchitectureDirectory\n\t}\n\tif !pe.opts.OmitGlobalPtrDirectory {\n\t\tfuncMaps[ImageDirectoryEntryGlobalPtr] = pe.parseGlobalPtrDirectory\n\t}\n\tif !pe.opts.OmitTLSDirectory {\n\t\tfuncMaps[ImageDirectoryEntryTLS] = pe.parseTLSDirectory\n\t}\n\tif !pe.opts.OmitLoadConfigDirectory {\n\t\tfuncMaps[ImageDirectoryEntryLoadConfig] = pe.parseLoadConfigDirectory\n\t}\n\tif !pe.opts.OmitBoundImportDirectory {\n\t\tfuncMaps[ImageDirectoryEntryBoundImport] = pe.parseBoundImportDirectory\n\t}\n\tif !pe.opts.OmitIATDirectory {\n\t\tfuncMaps[ImageDirectoryEntryIAT] = pe.parseIATDirectory\n\t}\n\tif !pe.opts.OmitDelayImportDirectory {\n\t\tfuncMaps[ImageDirectoryEntryDelayImport] = pe.parseDelayImportDirectory\n\t}\n\tif !pe.opts.OmitCLRHeaderDirectory {\n\t\tfuncMaps[ImageDirectoryEntryCLR] = pe.parseCLRHeaderDirectory\n\t}\n\n\t// Iterate over data directories and call the appropriate function.\n\tfor entryIndex := ImageDirectoryEntry(0); entryIndex < ImageNumberOfDirectoryEntries; entryIndex++ {\n\n\t\tvar va, size uint32\n\t\tswitch pe.Is64 {\n\t\tcase true:\n\t\t\tdirEntry := oh64.DataDirectory[entryIndex]\n\t\t\tva = dirEntry.VirtualAddress\n\t\t\tsize = dirEntry.Size\n\t\tcase false:\n\t\t\tdirEntry := oh32.DataDirectory[entryIndex]\n\t\t\tva = dirEntry.VirtualAddress\n\t\t\tsize = dirEntry.Size\n\t\t}\n\n\t\tif va != 0 {\n\t\t\tfunc() {\n\t\t\t\t// keep parsing data directories even though some entries fails.\n\t\t\t\tdefer func() {\n\t\t\t\t\tif e := recover(); e != nil {\n\t\t\t\t\t\tpe.logger.Errorf(\"unhandled exception when parsing data directory %s, reason: %v\",\n\t\t\t\t\t\t\tentryIndex.String(), e)\n\t\t\t\t\t\tfoundErr = true\n\t\t\t\t\t}\n\t\t\t\t}()\n\n\t\t\t\t// the last entry in the data directories is reserved and must be zero.\n\t\t\t\tif entryIndex == ImageDirectoryEntryReserved {\n\t\t\t\t\tpe.Anomalies = append(pe.Anomalies, AnoReservedDataDirectoryEntry)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tparseDirectory, ok := funcMaps[entryIndex]\n\t\t\t\tif !ok {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\terr := parseDirectory(va, size)\n\t\t\t\tif err != nil {\n\t\t\t\t\tpe.logger.Warnf(\"failed to parse data directory %s, reason: %v\",\n\t\t\t\t\t\tentryIndex.String(), err)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t}\n\n\tif foundErr {\n\t\treturn errors.New(\"Data directory parsing failed\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "file_test.go",
    "content": "// Copyright 2021 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"testing\"\n)\n\nvar peTests = []struct {\n\tin  string\n\tout error\n}{\n\t{getAbsoluteFilePath(\"test/putty.exe\"), nil},\n}\n\nfunc TestParse(t *testing.T) {\n\tfor _, tt := range peTests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tgot := file.Parse()\n\t\t\tif got != nil {\n\t\t\t\tt.Errorf(\"Parse(%s) got %v, want %v\", tt.in, got, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseOmitDirectories(t *testing.T) {\n\tfor _, tt := range peTests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{OmitSecurityDirectory: true})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tgot := file.Parse()\n\t\t\tif got != nil {\n\t\t\t\tt.Errorf(\"Parse(%s) got %v, want %v\", tt.in, got, tt.out)\n\t\t\t}\n\t\t\t// Should expect an empty certificate\n\t\t\tif file.Certificates.Raw != nil {\n\t\t\t\tt.Errorf(\"Parse(%s) expected empty certificate\", tt.in)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNewBytes(t *testing.T) {\n\tfor _, tt := range peTests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tdata, _ := ioutil.ReadFile(tt.in)\n\t\t\tfile, err := NewBytes(data, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"NewBytes(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tgot := file.Parse()\n\t\t\tif got != nil {\n\t\t\t\tt.Errorf(\"Parse(%s) got %v, want %v\", tt.in, got, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestChecksum(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  string\n\t\tout uint32\n\t}{\n\t\t// file is DWORD aligned.\n\t\t{getAbsoluteFilePath(\"test/putty.exe\"),\n\t\t\t0x00122C22},\n\t\t// file is not DWORD aligned and needs paddings.\n\t\t{getAbsoluteFilePath(\"test/010001e68577ef704792448ff474d22c6545167231982447c568e55041169ef0\"),\n\t\t\t0x0006D558},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tgot := file.Checksum()\n\t\t\tif got != tt.out {\n\t\t\t\tt.Errorf(\"Checksum(%s) got %v, want %v\", tt.in, got, tt.out)\n\t\t\t}\n\n\t\t})\n\t}\n}\n\nfunc TestCanParseWithHandleAndClose(t *testing.T) {\n\tfor _, tt := range peTests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := os.Open(tt.in)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Open file(%s) failed\", tt.in)\n\t\t\t}\n\t\t\tpefile, err := NewFile(file, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"NewFile (%s) failed\", tt.in)\n\t\t\t}\n\t\t\terr = pefile.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse (%s) failed\", tt.in)\n\t\t\t}\n\t\t\terr = pefile.Unmap()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Unmap (%s) failed\", tt.in)\n\t\t\t}\n\t\t\tconst len = 2\n\t\t\theader := [len]byte{}\n\t\t\tn, err := file.ReadAt(header[:], 0)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Failed to read after unmap (%s)\", tt.in)\n\t\t\t}\n\t\t\tif n != len {\n\t\t\t\tt.Fatalf(\"Failed to read data (%s)\", tt.in)\n\t\t\t}\n\t\t\terr = file.Close()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Failed to close file (%s)\", tt.in)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "globalptr.go",
    "content": "// Copyright 2022 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nconst (\n\t// AnoInvalidGlobalPtrReg is reported when the global pointer register offset is outide the image.\n\tAnoInvalidGlobalPtrReg = \"Global pointer register offset outside of PE image\"\n)\n\n// RVA of the value to be stored in the global pointer register. The size must\n// be set to 0. This data directory is set to all zeros if the target\n// architecture (for example, I386 or AMD64) does not use the concept of a\n// global pointer.\nfunc (pe *File) parseGlobalPtrDirectory(rva, size uint32) error {\n\n\tvar err error\n\n\t// RVA of the value to be stored in the global pointer register.\n\toffset := pe.GetOffsetFromRva(rva)\n\tif offset == ^uint32(0) {\n\t\t// Fake global pointer data directory\n\t\t// sample: 0101f36de484fbc7bfbe6cb942a1ecf6fac0c3acd9f65b88b19400582d7e7007\n\t\tpe.Anomalies = append(pe.Anomalies, AnoInvalidGlobalPtrReg)\n\t\treturn nil\n\t}\n\n\tpe.GlobalPtr, err = pe.ReadUint32(offset)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpe.HasGlobalPtr = true\n\treturn nil\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/saferwall/pe\n\ngo 1.15\n\nrequire (\n\tgithub.com/ayoubfaouzi/pkcs7 v0.2.3\n\tgithub.com/edsrzf/mmap-go v1.1.0\n\tgolang.org/x/text v0.22.0\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/ayoubfaouzi/pkcs7 v0.2.3 h1:XGCYHteXgclHnNlPdCF8aFyoUKwP9VhLQp+VX+hBZ3U=\ngithub.com/ayoubfaouzi/pkcs7 v0.2.3/go.mod h1:u1EPWZOeIdVRo6C0/FVjB91Nsletw+8vZeAaAmeyJvQ=\ngithub.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=\ngithub.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=\ngolang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=\ngolang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=\ngolang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=\ngolang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=\ngolang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=\ngolang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=\ngolang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=\ngolang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=\ngolang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=\ngolang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=\ngolang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=\ngolang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=\ngolang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=\ngolang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\n"
  },
  {
    "path": "helper.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"golang.org/x/text/encoding/unicode\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nconst (\n\t// TinyPESize On Windows XP (x32) the smallest PE executable is 97 bytes.\n\tTinyPESize = 97\n\n\t// FileAlignmentHardcodedValue represents the value which PointerToRawData\n\t// should be at least equal or bigger to, or it will be rounded to zero.\n\t// According to http://corkami.blogspot.com/2010/01/parce-que-la-planche-aura-brule.html\n\t// if PointerToRawData is less that 0x200 it's rounded to zero.\n\tFileAlignmentHardcodedValue = 0x200\n)\n\n// Errors\nvar (\n\n\t// ErrInvalidPESize is returned when the file size is less that the smallest\n\t// PE file size possible.ErrImageOS2SignatureFound\n\tErrInvalidPESize = errors.New(\"not a PE file, smaller than tiny PE\")\n\n\t// ErrDOSMagicNotFound is returned when file is potentially a ZM executable.\n\tErrDOSMagicNotFound = errors.New(\"DOS Header magic not found\")\n\n\t// ErrInvalidElfanewValue is returned when e_lfanew is larger than file size.\n\tErrInvalidElfanewValue = errors.New(\"invalid e_lfanew value. Probably not a PE file\")\n\n\t// ErrInvalidNtHeaderOffset is returned when the NT Header offset is beyond\n\t// the image file.\n\tErrInvalidNtHeaderOffset = errors.New(\n\t\t\"invalid NT Header Offset. NT Header Signature not found\")\n\n\t// ErrImageOS2SignatureFound is returned when signature is for a NE file.\n\tErrImageOS2SignatureFound = errors.New(\n\t\t\"not a valid PE signature. Probably a NE file\")\n\n\t// ErrImageOS2LESignatureFound is returned when signature is for a LE file.\n\tErrImageOS2LESignatureFound = errors.New(\n\t\t\"not a valid PE signature. Probably an LE file\")\n\n\t// ErrImageVXDSignatureFound is returned when signature is for a LX file.\n\tErrImageVXDSignatureFound = errors.New(\n\t\t\"not a valid PE signature. Probably an LX file\")\n\n\t// ErrImageTESignatureFound is returned when signature is for a TE file.\n\tErrImageTESignatureFound = errors.New(\n\t\t\"not a valid PE signature. Probably a TE file\")\n\n\t// ErrImageNtSignatureNotFound is returned when PE magic signature is not found.\n\tErrImageNtSignatureNotFound = errors.New(\n\t\t\"not a valid PE signature. Magic not found\")\n\n\t// ErrImageNtOptionalHeaderMagicNotFound is returned when optional header\n\t// magic is different from PE32/PE32+.\n\tErrImageNtOptionalHeaderMagicNotFound = errors.New(\n\t\t\"not a valid PE signature. Optional Header magic not found\")\n\n\t// ErrImageBaseNotAligned is reported when the image base is not aligned to 64K.\n\tErrImageBaseNotAligned = errors.New(\n\t\t\"corrupt PE file. Image base not aligned to 64 K\")\n\n\t// AnoImageBaseOverflow is reported when the image base + SizeOfImage is\n\t// larger than 80000000h/FFFF080000000000h in PE32/P32+.\n\tAnoImageBaseOverflow = \"Image base beyond allowed address\"\n\n\t// ErrInvalidSectionFileAlignment is reported when section alignment is less than a\n\t// PAGE_SIZE and section alignment != file alignment.\n\tErrInvalidSectionFileAlignment = errors.New(\"corrupt PE file. Section \" +\n\t\t\"alignment is less than a PAGE_SIZE and section alignment != file alignment\")\n\n\t// AnoInvalidSizeOfImage is reported when SizeOfImage is not multiple of\n\t// SectionAlignment.\n\tAnoInvalidSizeOfImage = \"Invalid SizeOfImage value, should be multiple \" +\n\t\t\"of SectionAlignment\"\n\n\t// ErrOutsideBoundary is reported when attempting to read an address beyond\n\t// file image limits.\n\tErrOutsideBoundary = errors.New(\"reading data outside boundary\")\n)\n\n// Max returns the larger of x or y.\nfunc Max(x, y uint32) uint32 {\n\tif x < y {\n\t\treturn y\n\t}\n\treturn x\n}\n\nfunc min(a, b uint32) uint32 {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n\n// Min returns the min number in a slice.\nfunc Min(values []uint32) uint32 {\n\tmin := values[0]\n\tfor _, v := range values {\n\t\tif v < min {\n\t\t\tmin = v\n\t\t}\n\t}\n\treturn min\n}\n\n// IsValidDosFilename returns true if the DLL name is likely to be valid.\n// Valid FAT32 8.3 short filename characters according to:\n// http://en.wikipedia.org/wiki/8.3_filename\n// The filename length is not checked because the DLLs filename\n// can be longer that the 8.3\nfunc IsValidDosFilename(filename string) bool {\n\talphabet := \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\tnumerals := \"0123456789\"\n\tspecial := \"!#$%&'()-@^_`{}~+,.;=[]\\\\/\"\n\tcharset := alphabet + numerals + special\n\tfor _, c := range filename {\n\t\tif !strings.Contains(charset, string(c)) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// IsValidFunctionName checks if an imported name uses the valid accepted\n// characters expected in mangled function names. If the symbol's characters\n// don't fall within this charset we will assume the name is invalid.\nfunc IsValidFunctionName(functionName string) bool {\n\talphabet := \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\tnumerals := \"0123456789\"\n\tspecial := \"_?@$()<>\"\n\tcharset := alphabet + numerals + special\n\tfor _, c := range functionName {\n\t\tif !strings.Contains(charset, string(c)) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// IsPrintable checks weather a string is printable.\nfunc IsPrintable(s string) bool {\n\talphabet := \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\tnumerals := \"0123456789\"\n\twhitespace := \" \\t\\n\\r\\v\\f\"\n\tspecial := \"!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~\"\n\tcharset := alphabet + numerals + special + whitespace\n\tfor _, c := range s {\n\t\tif !strings.Contains(charset, string(c)) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// getSectionByRva returns the section containing the given address.\nfunc (pe *File) getSectionByRva(rva uint32) *Section {\n\tfor _, section := range pe.Sections {\n\t\tif section.Contains(rva, pe) {\n\t\t\treturn &section\n\t\t}\n\t}\n\treturn nil\n}\n\n// getSectionByRva returns the section name containing the given address.\nfunc (pe *File) getSectionNameByRva(rva uint32) string {\n\tfor _, section := range pe.Sections {\n\t\tif section.Contains(rva, pe) {\n\t\t\treturn section.String()\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc (pe *File) getSectionByOffset(offset uint32) *Section {\n\tfor _, section := range pe.Sections {\n\t\tif section.Header.PointerToRawData == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tadjustedPointer := pe.adjustFileAlignment(\n\t\t\tsection.Header.PointerToRawData)\n\t\tif adjustedPointer <= offset &&\n\t\t\toffset < (adjustedPointer+section.Header.SizeOfRawData) {\n\t\t\treturn &section\n\t\t}\n\t}\n\treturn nil\n}\n\n// GetOffsetFromRva returns the file offset corresponding to this RVA.\nfunc (pe *File) GetOffsetFromRva(rva uint32) uint32 {\n\n\t// Given a RVA, this method will find the section where the\n\t// data lies and return the offset within the file.\n\tsection := pe.getSectionByRva(rva)\n\tif section == nil {\n\t\tif rva < uint32(len(pe.data)) {\n\t\t\treturn rva\n\t\t}\n\t\treturn ^uint32(0)\n\t}\n\tsectionAlignment := pe.adjustSectionAlignment(section.Header.VirtualAddress)\n\tfileAlignment := pe.adjustFileAlignment(section.Header.PointerToRawData)\n\treturn rva - sectionAlignment + fileAlignment\n}\n\n// GetRVAFromOffset returns an RVA given an offset.\nfunc (pe *File) GetRVAFromOffset(offset uint32) uint32 {\n\tsection := pe.getSectionByOffset(offset)\n\tminAddr := ^uint32(0)\n\tif section == nil {\n\n\t\tif len(pe.Sections) == 0 {\n\t\t\treturn offset\n\t\t}\n\n\t\tfor _, section := range pe.Sections {\n\t\t\tvaddr := pe.adjustSectionAlignment(section.Header.VirtualAddress)\n\t\t\tif vaddr < minAddr {\n\t\t\t\tminAddr = vaddr\n\t\t\t}\n\t\t}\n\t\t// Assume that offset lies within the headers\n\t\t// The case illustrating this behavior can be found at:\n\t\t// http://corkami.blogspot.com/2010/01/hey-hey-hey-whats-in-your-head.html\n\t\t// where the import table is not contained by any section\n\t\t// hence the RVA needs to be resolved to a raw offset\n\t\tif offset < minAddr {\n\t\t\treturn offset\n\t\t}\n\n\t\tpe.logger.Warn(\"data at Offset can't be fetched. Corrupt header?\")\n\t\treturn ^uint32(0)\n\t}\n\tsectionAlignment := pe.adjustSectionAlignment(section.Header.VirtualAddress)\n\tfileAlignment := pe.adjustFileAlignment(section.Header.PointerToRawData)\n\treturn offset - fileAlignment + sectionAlignment\n}\n\nfunc (pe *File) getSectionByName(secName string) (section *ImageSectionHeader) {\n\tfor _, section := range pe.Sections {\n\t\tif section.String() == secName {\n\t\t\treturn &section.Header\n\t\t}\n\n\t}\n\treturn nil\n}\n\n// getStringAtRVA returns an ASCII string located at the given address.\nfunc (pe *File) getStringAtRVA(rva, maxLen uint32) string {\n\tif rva == 0 {\n\t\treturn \"\"\n\t}\n\n\tsection := pe.getSectionByRva(rva)\n\tif section == nil {\n\t\tif rva > pe.size {\n\t\t\treturn \"\"\n\t\t}\n\n\t\tend := rva + maxLen\n\t\tif end > pe.size {\n\t\t\tend = pe.size\n\t\t}\n\t\ts := pe.GetStringFromData(0, pe.data[rva:end])\n\t\treturn string(s)\n\t}\n\ts := pe.GetStringFromData(0, section.Data(rva, maxLen, pe))\n\treturn string(s)\n}\n\nfunc (pe *File) readUnicodeStringAtRVA(rva uint32, maxLength uint32) string {\n\tstr := \"\"\n\toffset := pe.GetOffsetFromRva(rva)\n\ti := uint32(0)\n\tfor i = 0; i < maxLength; i += 2 {\n\t\tif offset+i >= pe.size || pe.data[offset+i] == 0 {\n\t\t\tbreak\n\t\t}\n\n\t\tstr += string(pe.data[offset+i])\n\t}\n\treturn str\n}\n\nfunc (pe *File) readASCIIStringAtOffset(offset, maxLength uint32) (uint32, string) {\n\tstr := \"\"\n\ti := uint32(0)\n\n\tfor i = 0; i < maxLength; i++ {\n\t\tif offset+i >= pe.size || pe.data[offset+i] == 0 {\n\t\t\tbreak\n\t\t}\n\n\t\tstr += string(pe.data[offset+i])\n\t}\n\treturn i, str\n}\n\n// GetStringFromData returns ASCII string from within the data.\nfunc (pe *File) GetStringFromData(offset uint32, data []byte) []byte {\n\n\tdataSize := uint32(len(data))\n\tif dataSize == 0 {\n\t\treturn nil\n\t}\n\n\tif offset > dataSize {\n\t\treturn nil\n\t}\n\n\tend := offset\n\tfor end < dataSize {\n\t\tif data[end] == 0 {\n\t\t\tbreak\n\t\t}\n\t\tend++\n\t}\n\treturn data[offset:end]\n}\n\n// getStringAtOffset returns a string given an offset.\nfunc (pe *File) getStringAtOffset(offset, size uint32) (string, error) {\n\tif offset+size > pe.size {\n\t\treturn \"\", ErrOutsideBoundary\n\t}\n\n\tstr := string(pe.data[offset : offset+size])\n\treturn strings.Replace(str, \"\\x00\", \"\", -1), nil\n}\n\n// GetData returns the data given an RVA regardless of the section where it\n// lies on.\nfunc (pe *File) GetData(rva, length uint32) ([]byte, error) {\n\n\t// Given a RVA and the size of the chunk to retrieve, this method\n\t// will find the section where the data lies and return the data.\n\tsection := pe.getSectionByRva(rva)\n\n\tvar end uint32\n\tif length > 0 {\n\t\tend = rva + length\n\t} else {\n\t\tend = 0\n\t}\n\n\tif section == nil {\n\t\tif rva < uint32(len(pe.Header)) {\n\t\t\treturn pe.Header[rva:end], nil\n\t\t}\n\n\t\t// Before we give up we check whether the file might contain the data\n\t\t// anyway. There are cases of PE files without sections that rely on\n\t\t// windows loading the first 8291 bytes into memory and assume the data\n\t\t// will be there. A functional file with these characteristics is:\n\t\t// MD5: 0008892cdfbc3bda5ce047c565e52295\n\t\t// SHA-1: c7116b9ff950f86af256defb95b5d4859d4752a9\n\n\t\tif rva < uint32(len(pe.data)) {\n\t\t\treturn pe.data[rva:end], nil\n\t\t}\n\n\t\treturn nil, errors.New(\"data at RVA can't be fetched. Corrupt header?\")\n\t}\n\treturn section.Data(rva, length, pe), nil\n}\n\n// The alignment factor (in bytes) that is used to align the raw data of sections\n// in the image file. The value should be a power of 2 between 512 and 64 K,\n// inclusive. The default is 512. If the SectionAlignment is less than the\n// architecture's page size, then FileAlignment must match SectionAlignment.\nfunc (pe *File) adjustFileAlignment(va uint32) uint32 {\n\n\tvar fileAlignment uint32\n\tswitch pe.Is64 {\n\tcase true:\n\t\tfileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).FileAlignment\n\tcase false:\n\t\tfileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).FileAlignment\n\t}\n\n\tif fileAlignment > FileAlignmentHardcodedValue && fileAlignment%2 != 0 {\n\t\tpe.Anomalies = append(pe.Anomalies, ErrInvalidFileAlignment)\n\t}\n\n\tif fileAlignment < FileAlignmentHardcodedValue {\n\t\treturn va\n\t}\n\n\t// round it to 0x200 if not power of 2.\n\t// According to https://github.com/corkami/docs/blob/master/PE/PE.md\n\t// if PointerToRawData is less that 0x200 it's rounded to zero. Loading the\n\t// test file in a debugger it's easy to verify that the PointerToRawData\n\t// value of 1 is rounded to zero. Hence we reproduce the behavior\n\treturn (va / 0x200) * 0x200\n\n}\n\n// The alignment (in bytes) of sections when they are loaded into memory\n// It must be greater than or equal to FileAlignment. The default is the\n// page size for the architecture.\nfunc (pe *File) adjustSectionAlignment(va uint32) uint32 {\n\tvar fileAlignment, sectionAlignment uint32\n\n\tswitch pe.Is64 {\n\tcase true:\n\t\tfileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).FileAlignment\n\t\tsectionAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).SectionAlignment\n\tcase false:\n\t\tfileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).FileAlignment\n\t\tsectionAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).SectionAlignment\n\t}\n\n\tif fileAlignment < FileAlignmentHardcodedValue &&\n\t\tfileAlignment != sectionAlignment {\n\t\tpe.Anomalies = append(pe.Anomalies, ErrInvalidSectionAlignment)\n\t}\n\n\tif sectionAlignment < 0x1000 { // page size\n\t\tsectionAlignment = fileAlignment\n\t}\n\n\t// 0x200 is the minimum valid FileAlignment according to the documentation\n\t// although ntoskrnl.exe has an alignment of 0x80 in some Windows versions\n\tif sectionAlignment != 0 && va%sectionAlignment != 0 {\n\t\treturn sectionAlignment * (va / sectionAlignment)\n\t}\n\treturn va\n}\n\n// alignDword aligns the offset on a 32-bit boundary.\nfunc alignDword(offset, base uint32) uint32 {\n\treturn ((offset + base + 3) & 0xfffffffc) - (base & 0xfffffffc)\n}\n\n// stringInSlice checks weather a string exists in a slice of strings.\nfunc stringInSlice(a string, list []string) bool {\n\tfor _, b := range list {\n\t\tif b == a {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// intInSlice checks weather a uint32 exists in a slice of uint32.\nfunc intInSlice(a uint32, list []uint32) bool {\n\tfor _, b := range list {\n\t\tif b == a {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// IsDriver returns true if the PE file is a Windows driver.\nfunc (pe *File) IsDriver() bool {\n\n\t// Checking that the ImageBase field of the OptionalHeader is above or\n\t// equal to 0x80000000 (that is, whether it lies in the upper 2GB of\n\t//the address space, normally belonging to the kernel) is not a\n\t// reliable enough indicator.  For instance, PEs that play the invalid\n\t// ImageBase trick to get relocated could be incorrectly assumed to be\n\t// drivers.\n\n\t// Checking if any section characteristics have the IMAGE_SCN_MEM_NOT_PAGED\n\t// flag set is not reliable either.\n\n\t// If there's still no import directory (the PE doesn't have one or it's\n\t// malformed), give up.\n\tif len(pe.Imports) == 0 {\n\t\treturn false\n\t}\n\n\t// DIRECTORY_ENTRY_IMPORT will now exist, although it may be empty.\n\t// If it imports from \"ntoskrnl.exe\" or other kernel components it should\n\t// be a driver.\n\tsystemDLLs := []string{\"ntoskrnl.exe\", \"hal.dll\", \"ndis.sys\",\n\t\t\"bootvid.dll\", \"kdcom.dll\"}\n\tfor _, dll := range pe.Imports {\n\t\tif stringInSlice(strings.ToLower(dll.Name), systemDLLs) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\t// If still we couldn't tell, check common driver section with combination\n\t// of IMAGE_SUBSYSTEM_NATIVE or IMAGE_SUBSYSTEM_NATIVE_WINDOWS.\n\tsubsystem := ImageOptionalHeaderSubsystemType(0)\n\toh32 := ImageOptionalHeader32{}\n\toh64 := ImageOptionalHeader64{}\n\tswitch pe.Is64 {\n\tcase true:\n\t\toh64 = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\tsubsystem = oh64.Subsystem\n\tcase false:\n\t\toh32 = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\tsubsystem = oh32.Subsystem\n\t}\n\tcommonDriverSectionNames := []string{\"page\", \"paged\", \"nonpage\", \"init\"}\n\tfor _, section := range pe.Sections {\n\t\ts := strings.ToLower(section.String())\n\t\tif stringInSlice(s, commonDriverSectionNames) &&\n\t\t\t(subsystem&ImageSubsystemNativeWindows != 0 ||\n\t\t\t\tsubsystem&ImageSubsystemNative != 0) {\n\t\t\treturn true\n\t\t}\n\n\t}\n\n\treturn false\n}\n\n// IsDLL returns true if the PE file is a standard DLL.\nfunc (pe *File) IsDLL() bool {\n\treturn pe.NtHeader.FileHeader.Characteristics&ImageFileDLL != 0\n}\n\n// IsEXE returns true if the PE file is a standard executable.\nfunc (pe *File) IsEXE() bool {\n\n\t// Returns true only if the file has the IMAGE_FILE_EXECUTABLE_IMAGE flag set\n\t// and the IMAGE_FILE_DLL not set and the file does not appear to be a driver either.\n\tif pe.IsDLL() || pe.IsDriver() {\n\t\treturn false\n\t}\n\n\tif pe.NtHeader.FileHeader.Characteristics&ImageFileExecutableImage == 0 {\n\t\treturn false\n\t}\n\n\treturn true\n}\n\n// Checksum calculates the PE checksum as generated by CheckSumMappedFile().\nfunc (pe *File) Checksum() uint32 {\n\tvar checksum uint64 = 0\n\tvar max uint64 = 0x100000000\n\tcurrentDword := uint32(0)\n\n\t// Get the Checksum offset.\n\toptionalHeaderOffset := pe.DOSHeader.AddressOfNewEXEHeader + 4 +\n\t\tuint32(binary.Size(pe.NtHeader.FileHeader))\n\n\t// `CheckSum` field position in optional PE headers is always 64 for PE and PE+.\n\tchecksumOffset := optionalHeaderOffset + 64\n\n\t// Verify the data is DWORD-aligned and add padding if needed\n\tremainder := pe.size % 4\n\tdataLen := pe.size\n\tif remainder > 0 {\n\t\tdataLen = pe.size + (4 - remainder)\n\t\tpaddedBytes := make([]byte, 4-remainder)\n\t\tpe.data = append(pe.data, paddedBytes...)\n\t}\n\n\tfor i := uint32(0); i < dataLen; i += 4 {\n\t\t// Skip the checksum field.\n\t\tif i == checksumOffset {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Read DWORD from file.\n\t\tcurrentDword = binary.LittleEndian.Uint32(pe.data[i:])\n\n\t\t// Calculate checksum.\n\t\tchecksum = (checksum & 0xffffffff) + uint64(currentDword) + (checksum >> 32)\n\t\tif checksum > max {\n\t\t\tchecksum = (checksum & 0xffffffff) + (checksum >> 32)\n\t\t}\n\t}\n\n\tchecksum = (checksum & 0xffff) + (checksum >> 16)\n\tchecksum = checksum + (checksum >> 16)\n\tchecksum = checksum & 0xffff\n\n\t// The length is the one of the original data, not the padded one\n\tchecksum += uint64(pe.size)\n\n\treturn uint32(checksum)\n}\n\n// ReadUint64 read a uint64 from a buffer.\nfunc (pe *File) ReadUint64(offset uint32) (uint64, error) {\n\tif offset+8 > pe.size {\n\t\treturn 0, ErrOutsideBoundary\n\t}\n\n\treturn binary.LittleEndian.Uint64(pe.data[offset:]), nil\n}\n\n// ReadUint32 read a uint32 from a buffer.\nfunc (pe *File) ReadUint32(offset uint32) (uint32, error) {\n\tif offset > pe.size-4 {\n\t\treturn 0, ErrOutsideBoundary\n\t}\n\n\treturn binary.LittleEndian.Uint32(pe.data[offset:]), nil\n}\n\n// ReadUint16 read a uint16 from a buffer.\nfunc (pe *File) ReadUint16(offset uint32) (uint16, error) {\n\tif offset > pe.size-2 {\n\t\treturn 0, ErrOutsideBoundary\n\t}\n\n\treturn binary.LittleEndian.Uint16(pe.data[offset:]), nil\n}\n\n// ReadUint8 read a uint8 from a buffer.\nfunc (pe *File) ReadUint8(offset uint32) (uint8, error) {\n\tif offset+1 > pe.size {\n\t\treturn 0, ErrOutsideBoundary\n\t}\n\n\tb := pe.data[offset : offset+1][0]\n\treturn uint8(b), nil\n}\n\nfunc (pe *File) structUnpack(iface interface{}, offset, size uint32) (err error) {\n\t// Boundary check\n\ttotalSize := offset + size\n\n\t// Integer overflow\n\tif (totalSize > offset) != (size > 0) {\n\t\treturn ErrOutsideBoundary\n\t}\n\n\tif offset >= pe.size || totalSize > pe.size {\n\t\treturn ErrOutsideBoundary\n\t}\n\n\tbuf := bytes.NewReader(pe.data[offset : offset+size])\n\terr = binary.Read(buf, binary.LittleEndian, iface)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// ReadBytesAtOffset returns a byte array from offset.\nfunc (pe *File) ReadBytesAtOffset(offset, size uint32) ([]byte, error) {\n\t// Boundary check\n\ttotalSize := offset + size\n\n\t// Integer overflow\n\tif (totalSize > offset) != (size > 0) {\n\t\treturn nil, ErrOutsideBoundary\n\t}\n\n\tif offset >= pe.size || totalSize > pe.size {\n\t\treturn nil, ErrOutsideBoundary\n\t}\n\n\treturn pe.data[offset : offset+size], nil\n}\n\n// DecodeUTF16String decodes the UTF16 string from the byte slice.\nfunc DecodeUTF16String(b []byte) (string, error) {\n\tn := bytes.Index(b, []byte{0, 0})\n\tif n == 0 {\n\t\treturn \"\", nil\n\t}\n\tdecoder := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder()\n\ts, err := decoder.Bytes(b[0 : n+1])\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(s), nil\n}\n\n// IsBitSet returns true when a bit on a particular position is set.\nfunc IsBitSet(n uint64, pos int) bool {\n\tval := n & (1 << pos)\n\treturn (val > 0)\n}\n\nfunc getAbsoluteFilePath(testfile string) string {\n\t_, p, _, _ := runtime.Caller(0)\n\treturn path.Join(filepath.Dir(p), testfile)\n}\n"
  },
  {
    "path": "helper_test.go",
    "content": "// Copyright 2021 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"testing\"\n)\n\nfunc TestIsEXE(t *testing.T) {\n\ttests := []struct {\n\t\tin  string\n\t\tout bool\n\t}{\n\t\t{getAbsoluteFilePath(\"test/liblzo2-2.dll\"), false},\n\t\t{getAbsoluteFilePath(\"test/putty.exe\"), true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tgot := file.IsEXE()\n\t\t\tif got != tt.out {\n\t\t\t\tt.Errorf(\"IsEXE(%s) got %v, want %v\", tt.in, got, tt.out)\n\t\t\t}\n\n\t\t})\n\t}\n}\n\nfunc TestIsDLL(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  string\n\t\tout bool\n\t}{\n\t\t{getAbsoluteFilePath(\"test/liblzo2-2.dll\"), true},\n\t\t{getAbsoluteFilePath(\"test/putty.exe\"), false},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tgot := file.IsDLL()\n\t\t\tif got != tt.out {\n\t\t\t\tt.Errorf(\"IsDLL(%s) got %v, want %v\", tt.in, got, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIsDriver(t *testing.T) {\n\ttests := []struct {\n\t\tin  string\n\t\tout bool\n\t}{\n\t\t{getAbsoluteFilePath(\"test/liblzo2-2.dll\"), false},\n\t\t{getAbsoluteFilePath(\"test/WdBoot.sys\"), true},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\tgot := file.IsDriver()\n\t\t\tif got != tt.out {\n\t\t\t\tt.Errorf(\"IsDriver(%s) got %v, want %v\", tt.in, got, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "iat.go",
    "content": "// Copyright 2022 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\n// IATEntry represents an entry inside the IAT.\ntype IATEntry struct {\n\tIndex   uint32      `json:\"index\"`\n\tRva     uint32      `json:\"rva\"`\n\tValue   interface{} `json:\"value,omitempty\"`\n\tMeaning string      `json:\"meaning\"`\n}\n\n// The structure and content of the import address table are identical to those\n// of the import lookup table, until the file is bound. During binding, the\n// entries in the import address table are overwritten with the 32-bit (for\n// PE32) or 64-bit (for PE32+) addresses of the symbols that are being imported.\n// These addresses are the actual memory addresses of the symbols, although\n// technically they are still called “virtual addresses.” The loader typically\n// processes the binding.\n//\n// The Import Address Table is there to to only trigger Copy On Write for as\n// few pages as possible (those being the actual Import Address Table pages\n// themselves).\n// This is, partially the reason there's that extra level of indirection in the\n// PE to begin with.\nfunc (pe *File) parseIATDirectory(rva, size uint32) error {\n\n\tvar entries []IATEntry\n\tvar index uint32\n\tvar err error\n\n\tstartRva := rva\n\n\tfor startRva+size > rva {\n\t\tie := IATEntry{}\n\t\toffset := pe.GetOffsetFromRva(rva)\n\t\tif pe.Is64 {\n\t\t\tie.Value, err = pe.ReadUint64(offset)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tie.Rva = rva\n\t\t\trva += 8\n\t\t} else {\n\t\t\tie.Value, err = pe.ReadUint32(offset)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tie.Rva = rva\n\n\t\t\trva += 4\n\t\t}\n\t\tie.Index = index\n\t\timp, i := pe.GetImportEntryInfoByRVA(rva)\n\t\tif len(imp.Functions) != 0 {\n\t\t\tie.Meaning = imp.Name + \"!\" + imp.Functions[i].Name\n\t\t}\n\t\tentries = append(entries, ie)\n\t\tindex++\n\t}\n\n\tpe.IAT = entries\n\tpe.HasIAT = true\n\treturn nil\n}\n"
  },
  {
    "path": "imports.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nconst (\n\timageOrdinalFlag32   = uint32(0x80000000)\n\timageOrdinalFlag64   = uint64(0x8000000000000000)\n\tmaxRepeatedAddresses = uint32(0xF)\n\tmaxAddressSpread     = uint32(0x8000000)\n\taddressMask32        = uint32(0x7fffffff)\n\taddressMask64        = uint64(0x7fffffffffffffff)\n\tmaxDllLength         = 0x200\n\tmaxImportNameLength  = 0x200\n)\n\nvar (\n\t// AnoInvalidThunkAddressOfData is reported when thunk address is too spread out.\n\tAnoInvalidThunkAddressOfData = \"Thunk Address Of Data too spread out\"\n\n\t// AnoManyRepeatedEntries is reported when import directory contains many\n\t// entries have the same RVA.\n\tAnoManyRepeatedEntries = \"Import directory contains many repeated entries\"\n\n\t// AnoAddressOfDataBeyondLimits is reported when Thunk AddressOfData goes\n\t// beyond limits.\n\tAnoAddressOfDataBeyondLimits = \"Thunk AddressOfData beyond limits\"\n\n\t// AnoImportNoNameNoOrdinal is reported when an import entry does not have\n\t// a name neither an ordinal, most probably malformed data.\n\tAnoImportNoNameNoOrdinal = \"Must have either an ordinal or a name in an import\"\n\n\t// ErrDamagedImportTable is reported when the IAT and ILT table length is 0.\n\tErrDamagedImportTable = errors.New(\n\t\t\"damaged Import Table information. ILT and/or IAT appear to be broken\")\n)\n\n// ImageImportDescriptor describes the remainder of the import information.\n// The import directory table contains address information that is used to\n// resolve fixup references to the entry points within a DLL image.\n// It consists of an array of import directory entries, one entry for each DLL\n// to which the image refers. The last directory entry is empty (filled with\n// null values), which indicates the end of the directory table.\ntype ImageImportDescriptor struct {\n\t// The RVA of the import lookup/name table (INT). This table contains a name\n\t// or ordinal for each import. The INT is an array of IMAGE_THUNK_DATA structs.\n\tOriginalFirstThunk uint32 `json:\"original_first_thunk\"`\n\n\t// The stamp that is set to zero until the image is bound. After the image\n\t// is bound, this field is set to the time/data stamp of the DLL.\n\tTimeDateStamp uint32 `json:\"time_date_stamp\"`\n\n\t// The index of the first forwarder reference (-1 if no forwarders).\n\tForwarderChain uint32 `json:\"forwarder_chain\"`\n\n\t// The address of an ASCII string that contains the name of the DLL.\n\t// This address is relative to the image base.\n\tName uint32 `json:\"name\"`\n\n\t// The RVA of the import address table (IAT). The contents of this table are\n\t// identical to the contents of the import lookup table until the image is bound.\n\tFirstThunk uint32 `json:\"first_thunk\"`\n}\n\n// ImageThunkData32 corresponds to one imported function from the executable.\n// The entries are an array of 32-bit numbers for PE32 or an array of 64-bit\n// numbers for PE32+. The ends of both arrays are indicated by an\n// IMAGE_THUNK_DATA element with a value of zero.\n// The IMAGE_THUNK_DATA union is a DWORD with these interpretations:\n// DWORD Function;       // Memory address of the imported function\n// DWORD Ordinal;        // Ordinal value of imported API\n// DWORD AddressOfData;  // RVA to an IMAGE_IMPORT_BY_NAME with the imported API name\n// DWORD ForwarderString;// RVA to a forwarder string\ntype ImageThunkData32 struct {\n\tAddressOfData uint32\n}\n\n// ImageThunkData64 is the PE32+ version of IMAGE_THUNK_DATA.\ntype ImageThunkData64 struct {\n\tAddressOfData uint64\n}\n\ntype ThunkData32 struct {\n\tImageThunkData ImageThunkData32\n\tOffset         uint32\n}\n\ntype ThunkData64 struct {\n\tImageThunkData ImageThunkData64\n\tOffset         uint32\n}\n\n// ImportFunction represents an imported function in the import table.\ntype ImportFunction struct {\n\t// An ASCII string that contains the name to import. This is the string that\n\t// must be matched to the public name in the DLL. This string is case\n\t// sensitive and terminated by a null byte.\n\tName string `json:\"name\"`\n\n\t// An index into the export name pointer table. A match is attempted first\n\t// with this value. If it fails, a binary search is performed on the DLL's\n\t// export name pointer table.\n\tHint uint16 `json:\"hint\"`\n\n\t// If this is true, import by ordinal. Otherwise, import by name.\n\tByOrdinal bool `json:\"by_ordinal\"`\n\n\t// A 16-bit ordinal number. This field is used only if the Ordinal/Name Flag\n\t// bit field is 1 (import by ordinal). Bits 30-15 or 62-15 must be 0.\n\tOrdinal uint32 `json:\"ordinal\"`\n\n\t// Name Thunk Value (OFT)\n\tOriginalThunkValue uint64 `json:\"original_thunk_value\"`\n\n\t// Address Thunk Value (FT)\n\tThunkValue uint64 `json:\"thunk_value\"`\n\n\t// Address Thunk RVA.\n\tThunkRVA uint32 `json:\"thunk_rva\"`\n\n\t// Name Thunk RVA.\n\tOriginalThunkRVA uint32 `json:\"original_thunk_rva\"`\n}\n\n// Import represents an empty entry in the import table.\ntype Import struct {\n\tOffset     uint32                `json:\"offset\"`\n\tName       string                `json:\"name\"`\n\tFunctions  []ImportFunction      `json:\"functions\"`\n\tDescriptor ImageImportDescriptor `json:\"descriptor\"`\n}\n\nfunc (pe *File) parseImportDirectory(rva, size uint32) (err error) {\n\n\tfor {\n\t\timportDesc := ImageImportDescriptor{}\n\t\tfileOffset := pe.GetOffsetFromRva(rva)\n\t\timportDescSize := uint32(binary.Size(importDesc))\n\t\terr := pe.structUnpack(&importDesc, fileOffset, importDescSize)\n\n\t\t// If the RVA is invalid all would blow up. Some EXEs seem to be\n\t\t// specially nasty and have an invalid RVA.\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// If the structure is all zeros, we reached the end of the list.\n\t\tif importDesc == (ImageImportDescriptor{}) {\n\t\t\tbreak\n\t\t}\n\n\t\trva += importDescSize\n\n\t\t// If the array of thunks is somewhere earlier than the import\n\t\t// descriptor we can set a maximum length for the array. Otherwise\n\t\t// just set a maximum length of the size of the file\n\t\tmaxLen := uint32(len(pe.data)) - fileOffset\n\t\tif rva > importDesc.OriginalFirstThunk || rva > importDesc.FirstThunk {\n\t\t\tif rva < importDesc.OriginalFirstThunk {\n\t\t\t\tmaxLen = rva - importDesc.FirstThunk\n\t\t\t} else if rva < importDesc.FirstThunk {\n\t\t\t\tmaxLen = rva - importDesc.OriginalFirstThunk\n\t\t\t} else {\n\t\t\t\tmaxLen = Max(rva-importDesc.OriginalFirstThunk,\n\t\t\t\t\trva-importDesc.FirstThunk)\n\t\t\t}\n\t\t}\n\n\t\tvar importedFunctions []ImportFunction\n\t\tif pe.Is64 {\n\t\t\timportedFunctions, err = pe.parseImports64(&importDesc, maxLen)\n\t\t} else {\n\t\t\timportedFunctions, err = pe.parseImports32(&importDesc, maxLen)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tdllName := pe.getStringAtRVA(importDesc.Name, maxDllLength)\n\t\tif !IsValidDosFilename(dllName) {\n\t\t\tdllName = \"*invalid*\"\n\t\t\tcontinue\n\t\t}\n\n\t\tpe.Imports = append(pe.Imports, Import{\n\t\t\tOffset:     fileOffset,\n\t\t\tName:       string(dllName),\n\t\t\tFunctions:  importedFunctions,\n\t\t\tDescriptor: importDesc,\n\t\t})\n\t}\n\n\tif len(pe.Imports) > 0 {\n\t\tpe.HasImport = true\n\t}\n\n\treturn nil\n}\n\nfunc (pe *File) getImportTable32(rva uint32, maxLen uint32,\n\tisOldDelayImport bool) ([]ThunkData32, error) {\n\n\t// Setup variables\n\tthunkTable := make(map[uint32]*ImageThunkData32)\n\tretVal := []ThunkData32{}\n\tminAddressOfData := ^uint32(0)\n\tmaxAddressOfData := uint32(0)\n\trepeatedAddress := uint32(0)\n\tvar size uint32 = 4\n\taddressesOfData := make(map[uint32]bool)\n\n\tstartRVA := rva\n\n\tif rva == 0 {\n\t\treturn nil, nil\n\t}\n\n\tfor {\n\t\tif rva >= startRVA+maxLen {\n\t\t\tpe.logger.Warnf(\"Error parsing the import table. Entries go beyond bounds.\")\n\t\t\tbreak\n\t\t}\n\n\t\t// if we see too many times the same entry we assume it could be\n\t\t// a table containing bogus data (with malicious intent or otherwise)\n\t\tif repeatedAddress >= maxRepeatedAddresses {\n\t\t\tif !stringInSlice(AnoManyRepeatedEntries, pe.Anomalies) {\n\t\t\t\tpe.Anomalies = append(pe.Anomalies, AnoManyRepeatedEntries)\n\t\t\t}\n\t\t}\n\n\t\t// if the addresses point somewhere but the difference between the\n\t\t// highest and lowest address is larger than maxAddressSpread we assume\n\t\t// a bogus table as the addresses should be contained within a module\n\t\tif maxAddressOfData-minAddressOfData > maxAddressSpread {\n\t\t\tif !stringInSlice(AnoInvalidThunkAddressOfData, pe.Anomalies) {\n\t\t\t\tpe.Anomalies = append(pe.Anomalies, AnoInvalidThunkAddressOfData)\n\t\t\t}\n\t\t}\n\n\t\t// In its original incarnation in Visual C++ 6.0, all ImgDelayDescr\n\t\t// fields containing addresses used virtual addresses, rather than RVAs.\n\t\t// That is, they contained actual addresses where the delayload data\n\t\t// could be found. These fields are DWORDs, the size of a pointer on the x86.\n\t\t// Now fast-forward to IA-64 support. All of a sudden, 4 bytes isn't\n\t\t// enough to hold a complete address. At this point, Microsoft did the\n\t\t// correct thing and changed the fields containing addresses to RVAs.\n\t\toffset := uint32(0)\n\t\tif isOldDelayImport {\n\t\t\toh32 := pe.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\tnewRVA := rva - oh32.ImageBase\n\t\t\toffset = pe.GetOffsetFromRva(newRVA)\n\t\t\tif offset == ^uint32(0) {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t} else {\n\t\t\toffset = pe.GetOffsetFromRva(rva)\n\t\t\tif offset == ^uint32(0) {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t}\n\n\t\t// Read the image thunk data.\n\t\tthunk := ImageThunkData32{}\n\t\terr := pe.structUnpack(&thunk, offset, size)\n\t\tif err != nil {\n\t\t\t// pe.logger.Warnf(\"Error parsing the import table. \" +\n\t\t\t// \t\"Invalid data at RVA: 0x%x\", rva)\n\t\t\treturn nil, nil\n\t\t}\n\n\t\tif thunk == (ImageThunkData32{}) {\n\t\t\tbreak\n\t\t}\n\n\t\t// Check if the AddressOfData lies within the range of RVAs that it's\n\t\t// being scanned, abort if that is the case, as it is very unlikely\n\t\t// to be legitimate data.\n\t\t// Seen in PE with SHA256:\n\t\t// 5945bb6f0ac879ddf61b1c284f3b8d20c06b228e75ae4f571fa87f5b9512902c\n\t\tif thunk.AddressOfData >= startRVA && thunk.AddressOfData <= rva {\n\t\t\tpe.logger.Warnf(\"Error parsing the import table. \"+\n\t\t\t\t\"AddressOfData overlaps with THUNK_DATA for THUNK at: \"+\n\t\t\t\t\"RVA 0x%x\", rva)\n\t\t\tbreak\n\t\t}\n\n\t\tif thunk.AddressOfData&imageOrdinalFlag32 > 0 {\n\t\t\t// If the entry looks like could be an ordinal.\n\t\t\tif thunk.AddressOfData&0x7fffffff > 0xffff {\n\t\t\t\t// but its value is beyond 2^16, we will assume it's a\n\t\t\t\t// corrupted and ignore it altogether\n\t\t\t\tif !stringInSlice(AnoAddressOfDataBeyondLimits, pe.Anomalies) {\n\t\t\t\t\tpe.Anomalies = append(pe.Anomalies, AnoAddressOfDataBeyondLimits)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\t// and if it looks like it should be an RVA keep track of the RVAs seen\n\t\t\t// and store them to study their  properties. When certain non-standard\n\t\t\t// features are detected the parsing will be aborted\n\t\t\t_, ok := addressesOfData[thunk.AddressOfData]\n\t\t\tif ok {\n\t\t\t\trepeatedAddress++\n\t\t\t} else {\n\t\t\t\taddressesOfData[thunk.AddressOfData] = true\n\t\t\t}\n\n\t\t\tif thunk.AddressOfData > maxAddressOfData {\n\t\t\t\tmaxAddressOfData = thunk.AddressOfData\n\t\t\t}\n\n\t\t\tif thunk.AddressOfData < minAddressOfData {\n\t\t\t\tminAddressOfData = thunk.AddressOfData\n\t\t\t}\n\t\t}\n\n\t\tthunkTable[rva] = &thunk\n\t\tthunkData := ThunkData32{ImageThunkData: thunk, Offset: rva}\n\t\tretVal = append(retVal, thunkData)\n\t\trva += size\n\t}\n\treturn retVal, nil\n}\n\nfunc (pe *File) getImportTable64(rva uint32, maxLen uint32,\n\tisOldDelayImport bool) ([]ThunkData64, error) {\n\n\t// Setup variables\n\tthunkTable := make(map[uint32]*ImageThunkData64)\n\tretVal := []ThunkData64{}\n\tminAddressOfData := ^uint64(0)\n\tmaxAddressOfData := uint64(0)\n\trepeatedAddress := uint64(0)\n\tvar size uint32 = 8\n\taddressesOfData := make(map[uint64]bool)\n\n\tstartRVA := rva\n\n\tif rva == 0 {\n\t\treturn nil, nil\n\t}\n\n\tfor {\n\t\tif rva >= startRVA+maxLen {\n\t\t\tpe.logger.Warnf(\"Error parsing the import table. Entries go beyond bounds.\")\n\t\t\tbreak\n\t\t}\n\n\t\t// if we see too many times the same entry we assume it could be\n\t\t// a table containing bogus data (with malicious intent or otherwise)\n\t\tif repeatedAddress >= uint64(maxRepeatedAddresses) {\n\t\t\tif !stringInSlice(AnoManyRepeatedEntries, pe.Anomalies) {\n\t\t\t\tpe.Anomalies = append(pe.Anomalies, AnoManyRepeatedEntries)\n\t\t\t}\n\t\t}\n\n\t\t// if the addresses point somewhere but the difference between the highest\n\t\t// and lowest address is larger than maxAddressSpread we assume a bogus\n\t\t// table as the addresses should be contained within a module\n\t\tif maxAddressOfData-minAddressOfData > uint64(maxAddressSpread) {\n\t\t\tif !stringInSlice(AnoInvalidThunkAddressOfData, pe.Anomalies) {\n\t\t\t\tpe.Anomalies = append(pe.Anomalies, AnoInvalidThunkAddressOfData)\n\t\t\t}\n\t\t}\n\n\t\t// In its original incarnation in Visual C++ 6.0, all ImgDelayDescr\n\t\t// fields containing addresses used virtual addresses, rather than RVAs.\n\t\t// That is, they contained actual addresses where the delayload data\n\t\t// could be found. These fields are DWORDs, the size of a pointer on the x86.\n\t\t// Now fast-forward to IA-64 support. All of a sudden, 4 bytes isn't\n\t\t// enough to hold a complete address. At this point, Microsoft did the\n\t\t// correct thing and changed the fields containing addresses to RVAs.\n\t\toffset := uint32(0)\n\t\tif isOldDelayImport {\n\t\t\toh64 := pe.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\tnewRVA := rva - uint32(oh64.ImageBase)\n\t\t\toffset = pe.GetOffsetFromRva(newRVA)\n\t\t\tif offset == ^uint32(0) {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t} else {\n\t\t\toffset = pe.GetOffsetFromRva(rva)\n\t\t\tif offset == ^uint32(0) {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t}\n\n\t\t// Read the image thunk data.\n\t\tthunk := ImageThunkData64{}\n\t\terr := pe.structUnpack(&thunk, offset, size)\n\t\tif err != nil {\n\t\t\t// pe.logger.Warnf(\"Error parsing the import table. \" +\n\t\t\t// \t\"Invalid data at RVA: 0x%x\", rva)\n\t\t\treturn nil, nil\n\t\t}\n\n\t\tif thunk == (ImageThunkData64{}) {\n\t\t\tbreak\n\t\t}\n\n\t\t// Check if the AddressOfData lies within the range of RVAs that it's\n\t\t// being scanned, abort if that is the case, as it is very unlikely\n\t\t// to be legitimate data.\n\t\t// Seen in PE with SHA256:\n\t\t// 5945bb6f0ac879ddf61b1c284f3b8d20c06b228e75ae4f571fa87f5b9512902c\n\t\tif thunk.AddressOfData >= uint64(startRVA) &&\n\t\t\tthunk.AddressOfData <= uint64(rva) {\n\t\t\tpe.logger.Warnf(\"Error parsing the import table. \"+\n\t\t\t\t\"AddressOfData overlaps with THUNK_DATA for THUNK at: \"+\n\t\t\t\t\"RVA 0x%x\", rva)\n\t\t\tbreak\n\t\t}\n\n\t\t// If the entry looks like could be an ordinal\n\t\tif thunk.AddressOfData&imageOrdinalFlag64 > 0 {\n\t\t\t// but its value is beyond 2^16, we will assume it's a\n\t\t\t// corrupted and ignore it altogether\n\t\t\tif thunk.AddressOfData&0x7fffffff > 0xffff {\n\t\t\t\tif !stringInSlice(AnoAddressOfDataBeyondLimits, pe.Anomalies) {\n\t\t\t\t\tpe.Anomalies = append(pe.Anomalies, AnoAddressOfDataBeyondLimits)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// and if it looks like it should be an RVA\n\t\t} else {\n\t\t\t// keep track of the RVAs seen and store them to study their\n\t\t\t// properties. When certain non-standard features are detected\n\t\t\t// the parsing will be aborted\n\t\t\t_, ok := addressesOfData[thunk.AddressOfData]\n\t\t\tif ok {\n\t\t\t\trepeatedAddress++\n\t\t\t} else {\n\t\t\t\taddressesOfData[thunk.AddressOfData] = true\n\t\t\t}\n\n\t\t\tif thunk.AddressOfData > maxAddressOfData {\n\t\t\t\tmaxAddressOfData = thunk.AddressOfData\n\t\t\t}\n\n\t\t\tif thunk.AddressOfData < minAddressOfData {\n\t\t\t\tminAddressOfData = thunk.AddressOfData\n\t\t\t}\n\t\t}\n\n\t\tthunkTable[rva] = &thunk\n\t\tthunkData := ThunkData64{ImageThunkData: thunk, Offset: rva}\n\t\tretVal = append(retVal, thunkData)\n\t\trva += size\n\t}\n\treturn retVal, nil\n}\n\nfunc (pe *File) parseImports32(importDesc interface{}, maxLen uint32) (\n\t[]ImportFunction, error) {\n\n\tvar OriginalFirstThunk uint32\n\tvar FirstThunk uint32\n\tvar isOldDelayImport bool\n\n\tswitch desc := importDesc.(type) {\n\tcase *ImageImportDescriptor:\n\t\tOriginalFirstThunk = desc.OriginalFirstThunk\n\t\tFirstThunk = desc.FirstThunk\n\tcase *ImageDelayImportDescriptor:\n\t\tOriginalFirstThunk = desc.ImportNameTableRVA\n\t\tFirstThunk = desc.ImportAddressTableRVA\n\t\tif desc.Attributes == 0 {\n\t\t\tisOldDelayImport = true\n\t\t}\n\t}\n\n\t// Import Lookup Table (OFT). Contains ordinals or pointers to strings.\n\tilt, err := pe.getImportTable32(OriginalFirstThunk, maxLen, isOldDelayImport)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Import Address Table (FT). May have identical content to ILT if PE file is\n\t// not bound. It will contain the address of the imported symbols once\n\t// the binary is loaded or if it is already bound.\n\tiat, err := pe.getImportTable32(FirstThunk, maxLen, isOldDelayImport)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Some DLLs has IAT or ILT with nil type.\n\tif len(iat) == 0 && len(ilt) == 0 {\n\t\treturn nil, ErrDamagedImportTable\n\t}\n\n\tvar table []ThunkData32\n\tif len(ilt) > 0 {\n\t\ttable = ilt\n\t} else if len(iat) > 0 {\n\t\ttable = iat\n\t} else {\n\t\treturn nil, err\n\t}\n\n\timportedFunctions := []ImportFunction{}\n\tnumInvalid := uint32(0)\n\tfor idx := uint32(0); idx < uint32(len(table)); idx++ {\n\t\timp := ImportFunction{}\n\t\tif table[idx].ImageThunkData.AddressOfData > 0 {\n\t\t\t// If imported by ordinal, we will append the ordinal number\n\t\t\tif table[idx].ImageThunkData.AddressOfData&imageOrdinalFlag32 > 0 {\n\t\t\t\timp.ByOrdinal = true\n\t\t\t\timp.Ordinal = table[idx].ImageThunkData.AddressOfData & uint32(0xffff)\n\n\t\t\t\t// Original Thunk\n\t\t\t\tif uint32(len(ilt)) > idx {\n\t\t\t\t\timp.OriginalThunkValue = uint64(ilt[idx].ImageThunkData.AddressOfData)\n\t\t\t\t\timp.OriginalThunkRVA = ilt[idx].Offset\n\t\t\t\t}\n\n\t\t\t\t// Thunk\n\t\t\t\tif uint32(len(iat)) > idx {\n\t\t\t\t\timp.ThunkValue = uint64(iat[idx].ImageThunkData.AddressOfData)\n\t\t\t\t\timp.ThunkRVA = iat[idx].Offset\n\t\t\t\t}\n\n\t\t\t\timp.Name = \"#\" + strconv.Itoa(int(imp.Ordinal))\n\t\t\t} else {\n\t\t\t\timp.ByOrdinal = false\n\t\t\t\tif isOldDelayImport {\n\t\t\t\t\ttable[idx].ImageThunkData.AddressOfData -=\n\t\t\t\t\t\tpe.NtHeader.OptionalHeader.(ImageOptionalHeader32).ImageBase\n\t\t\t\t}\n\n\t\t\t\t// Original Thunk\n\t\t\t\tif uint32(len(ilt)) > idx {\n\t\t\t\t\timp.OriginalThunkValue = uint64(ilt[idx].ImageThunkData.AddressOfData & addressMask32)\n\t\t\t\t\timp.OriginalThunkRVA = ilt[idx].Offset\n\t\t\t\t}\n\n\t\t\t\t// Thunk\n\t\t\t\tif uint32(len(iat)) > idx {\n\t\t\t\t\timp.ThunkValue = uint64(iat[idx].ImageThunkData.AddressOfData & addressMask32)\n\t\t\t\t\timp.ThunkRVA = iat[idx].Offset\n\t\t\t\t}\n\n\t\t\t\t// Thunk\n\t\t\t\thintNameTableRva := table[idx].ImageThunkData.AddressOfData & addressMask32\n\t\t\t\toff := pe.GetOffsetFromRva(hintNameTableRva)\n\t\t\t\timp.Hint, err = pe.ReadUint16(off)\n\t\t\t\tif err != nil {\n\t\t\t\t\timp.Hint = ^uint16(0)\n\t\t\t\t}\n\t\t\t\timp.Name = pe.getStringAtRVA(table[idx].ImageThunkData.AddressOfData+2,\n\t\t\t\t\tmaxImportNameLength)\n\t\t\t\tif !IsValidFunctionName(imp.Name) {\n\t\t\t\t\timp.Name = \"*invalid*\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// This file bfe97192e8107d52dd7b4010d12b2924 has an invalid table built\n\t\t// in a way that it's parsable but contains invalid entries that lead\n\t\t// pefile to take extremely long amounts of time to parse. It also leads\n\t\t// to extreme memory consumption. To prevent similar cases, if invalid\n\t\t// entries are found in the middle of a table the parsing will be aborted.\n\t\thasName := len(imp.Name) > 0\n\t\tif imp.Ordinal == 0 && !hasName {\n\t\t\tif !stringInSlice(AnoImportNoNameNoOrdinal, pe.Anomalies) {\n\t\t\t\tpe.Anomalies = append(pe.Anomalies, AnoImportNoNameNoOrdinal)\n\t\t\t}\n\t\t}\n\n\t\t// Some PEs appear to interleave valid and invalid imports. Instead of\n\t\t// aborting the parsing altogether we will simply skip the invalid entries.\n\t\t// Although if we see 1000 invalid entries and no legit ones, we abort.\n\t\tif imp.Name == \"*invalid*\" {\n\t\t\tif numInvalid > 1000 && numInvalid == idx {\n\t\t\t\treturn nil, errors.New(\n\t\t\t\t\t`too many invalid names, aborting parsing`)\n\t\t\t}\n\t\t\tnumInvalid++\n\t\t\tcontinue\n\t\t}\n\n\t\timportedFunctions = append(importedFunctions, imp)\n\t}\n\n\treturn importedFunctions, nil\n}\n\nfunc (pe *File) parseImports64(importDesc interface{}, maxLen uint32) ([]ImportFunction, error) {\n\n\tvar OriginalFirstThunk uint32\n\tvar FirstThunk uint32\n\tvar isOldDelayImport bool\n\n\tswitch desc := importDesc.(type) {\n\tcase *ImageImportDescriptor:\n\t\tOriginalFirstThunk = desc.OriginalFirstThunk\n\t\tFirstThunk = desc.FirstThunk\n\tcase *ImageDelayImportDescriptor:\n\t\tOriginalFirstThunk = desc.ImportNameTableRVA\n\t\tFirstThunk = desc.ImportAddressTableRVA\n\t\tif desc.Attributes == 0 {\n\t\t\tisOldDelayImport = true\n\t\t}\n\t}\n\n\t// Import Lookup Table. Contains ordinals or pointers to strings.\n\tilt, err := pe.getImportTable64(OriginalFirstThunk, maxLen, isOldDelayImport)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Import Address Table. May have identical content to ILT if PE file is\n\t// not bound. It will contain the address of the imported symbols once\n\t// the binary is loaded or if it is already bound.\n\tiat, err := pe.getImportTable64(FirstThunk, maxLen, isOldDelayImport)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Would crash if IAT or ILT had nil type\n\tif len(iat) == 0 && len(ilt) == 0 {\n\t\treturn nil, ErrDamagedImportTable\n\t}\n\n\tvar table []ThunkData64\n\tif len(ilt) > 0 {\n\t\ttable = ilt\n\t} else if len(iat) > 0 {\n\t\ttable = iat\n\t} else {\n\t\treturn nil, err\n\t}\n\n\timportedFunctions := []ImportFunction{}\n\tnumInvalid := uint32(0)\n\tfor idx := uint32(0); idx < uint32(len(table)); idx++ {\n\t\timp := ImportFunction{}\n\t\tif table[idx].ImageThunkData.AddressOfData > 0 {\n\n\t\t\t// If imported by ordinal, we will append the ordinal number\n\t\t\tif table[idx].ImageThunkData.AddressOfData&imageOrdinalFlag64 > 0 {\n\t\t\t\timp.ByOrdinal = true\n\t\t\t\timp.Ordinal = uint32(table[idx].ImageThunkData.AddressOfData) & uint32(0xffff)\n\n\t\t\t\t// Original Thunk\n\t\t\t\tif uint32(len(ilt)) > idx {\n\t\t\t\t\timp.OriginalThunkValue =\n\t\t\t\t\t\tilt[idx].ImageThunkData.AddressOfData\n\t\t\t\t\timp.OriginalThunkRVA = ilt[idx].Offset\n\t\t\t\t}\n\n\t\t\t\t// Thunk\n\t\t\t\tif uint32(len(iat)) > idx {\n\t\t\t\t\timp.ThunkValue = iat[idx].ImageThunkData.AddressOfData\n\t\t\t\t\timp.ThunkRVA = iat[idx].Offset\n\t\t\t\t}\n\n\t\t\t\timp.Name = \"#\" + strconv.Itoa(int(imp.Ordinal))\n\n\t\t\t} else {\n\n\t\t\t\timp.ByOrdinal = false\n\n\t\t\t\tif isOldDelayImport {\n\t\t\t\t\ttable[idx].ImageThunkData.AddressOfData -=\n\t\t\t\t\t\tpe.NtHeader.OptionalHeader.(ImageOptionalHeader64).ImageBase\n\t\t\t\t}\n\n\t\t\t\t// Original Thunk\n\t\t\t\tif uint32(len(ilt)) > idx {\n\t\t\t\t\timp.OriginalThunkValue =\n\t\t\t\t\t\tilt[idx].ImageThunkData.AddressOfData & addressMask64\n\t\t\t\t\timp.OriginalThunkRVA = ilt[idx].Offset\n\t\t\t\t}\n\n\t\t\t\t// Thunk\n\t\t\t\tif uint32(len(iat)) > idx {\n\t\t\t\t\timp.ThunkValue = iat[idx].ImageThunkData.AddressOfData & addressMask64\n\t\t\t\t\timp.ThunkRVA = iat[idx].Offset\n\t\t\t\t}\n\n\t\t\t\thintNameTableRva := table[idx].ImageThunkData.AddressOfData & addressMask64\n\t\t\t\toff := pe.GetOffsetFromRva(uint32(hintNameTableRva))\n\t\t\t\timp.Hint = binary.LittleEndian.Uint16(pe.data[off:])\n\t\t\t\timp.Name = pe.getStringAtRVA(uint32(table[idx].ImageThunkData.AddressOfData+2),\n\t\t\t\t\tmaxImportNameLength)\n\t\t\t\tif !IsValidFunctionName(imp.Name) {\n\t\t\t\t\timp.Name = \"*invalid*\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// This file bfe97192e8107d52dd7b4010d12b2924 has an invalid table built\n\t\t// in a way that it's parsable but contains invalid entries that lead\n\t\t// pefile to take extremely long amounts of time to parse. It also leads\n\t\t// to extreme memory consumption. To prevent similar cases, if invalid\n\t\t// entries are found in the middle of a table the parsing will be aborted.\n\t\thasName := len(imp.Name) > 0\n\t\tif imp.Ordinal == 0 && !hasName {\n\t\t\tif !stringInSlice(AnoImportNoNameNoOrdinal, pe.Anomalies) {\n\t\t\t\tpe.Anomalies = append(pe.Anomalies, AnoImportNoNameNoOrdinal)\n\t\t\t}\n\t\t}\n\t\t// Some PEs appear to interleave valid and invalid imports. Instead of\n\t\t// aborting the parsing altogether we will simply skip the invalid entries.\n\t\t// Although if we see 1000 invalid entries and no legit ones, we abort.\n\t\tif imp.Name == \"*invalid*\" {\n\t\t\tif numInvalid > 1000 && numInvalid == idx {\n\t\t\t\treturn nil, errors.New(\n\t\t\t\t\t`too many invalid names, aborting parsing`)\n\t\t\t}\n\t\t\tnumInvalid++\n\t\t\tcontinue\n\t\t}\n\n\t\timportedFunctions = append(importedFunctions, imp)\n\t}\n\n\treturn importedFunctions, nil\n}\n\n// GetImportEntryInfoByRVA return an import function + index of the entry given\n// an RVA.\nfunc (pe *File) GetImportEntryInfoByRVA(rva uint32) (Import, int) {\n\tfor _, imp := range pe.Imports {\n\t\tfor i, entry := range imp.Functions {\n\t\t\tif entry.ThunkRVA == rva {\n\t\t\t\treturn imp, i\n\t\t\t}\n\t\t}\n\t}\n\n\treturn Import{}, 0\n}\n\n// md5hash hashes using md5 algorithm.\nfunc md5hash(text string) string {\n\th := md5.New()\n\th.Write([]byte(text))\n\treturn hex.EncodeToString(h.Sum(nil))\n}\n\n// ImpHash calculates the import hash.\n// Algorithm:\n// Resolving ordinals to function names when they appear\n// Converting both DLL names and function names to all lowercase\n// Removing the file extensions from imported module names\n// Building and storing the lowercased string . in an ordered list\n// Generating the MD5 hash of the ordered list\nfunc (pe *File) ImpHash() (string, error) {\n\tif len(pe.Imports) == 0 {\n\t\treturn \"\", errors.New(\"no imports found\")\n\t}\n\n\textensions := []string{\"ocx\", \"sys\", \"dll\"}\n\tvar impStrs []string\n\n\tfor _, imp := range pe.Imports {\n\t\tvar libName string\n\t\tparts := strings.Split(imp.Name, \".\")\n\t\tif len(parts) == 2 && stringInSlice(strings.ToLower(parts[1]), extensions) {\n\t\t\tlibName = parts[0]\n\t\t} else {\n\t\t\tlibName = imp.Name\n\t\t}\n\n\t\tlibName = strings.ToLower(libName)\n\n\t\tfor _, function := range imp.Functions {\n\t\t\tvar funcName string\n\t\t\tif function.ByOrdinal {\n\t\t\t\tfuncName = OrdLookup(imp.Name, uint64(function.Ordinal), true)\n\t\t\t} else {\n\t\t\t\tfuncName = function.Name\n\t\t\t}\n\n\t\t\tif funcName == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\timpStr := fmt.Sprintf(\"%s.%s\", libName, strings.ToLower(funcName))\n\t\t\timpStrs = append(impStrs, impStr)\n\t\t}\n\t}\n\n\thash := md5hash(strings.Join(impStrs, \",\"))\n\treturn hash, nil\n}\n"
  },
  {
    "path": "imports_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype TestImportEntry struct {\n\tentryCount int\n\tentryIndex int\n\tentry      Import\n}\n\nfunc TestImportDirectory(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  string\n\t\tout TestImportEntry\n\t}{\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/kernel32.dll\"),\n\t\t\tTestImportEntry{\n\t\t\t\tentryCount: 96,\n\t\t\t\tentryIndex: 34,\n\t\t\t\tentry: Import{\n\t\t\t\t\tOffset: 0xa6d94,\n\t\t\t\t\tName:   \"api-ms-win-core-namedpipe-l1-2-1.dll\",\n\t\t\t\t\tDescriptor: ImageImportDescriptor{\n\t\t\t\t\t\tOriginalFirstThunk: 0xa9a38,\n\t\t\t\t\t\tTimeDateStamp:      0x0,\n\t\t\t\t\t\tForwarderChain:     0x0,\n\t\t\t\t\t\tName:               0xaeeb8,\n\t\t\t\t\t\tFirstThunk:         0x82978,\n\t\t\t\t\t},\n\t\t\t\t\tFunctions: []ImportFunction{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:               \"GetNamedPipeHandleStateW\",\n\t\t\t\t\t\t\tHint:               0x6,\n\t\t\t\t\t\t\tByOrdinal:          false,\n\t\t\t\t\t\t\tOrdinal:            0x0,\n\t\t\t\t\t\t\tOriginalThunkValue: 0xaee00,\n\t\t\t\t\t\t\tThunkValue:         0xaee00,\n\t\t\t\t\t\t\tThunkRVA:           0x82978,\n\t\t\t\t\t\t\tOriginalThunkRVA:   0xa9a38,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/impbyord.exe\"),\n\t\t\tTestImportEntry{\n\t\t\t\tentryCount: 2,\n\t\t\t\tentryIndex: 1,\n\t\t\t\tentry: Import{\n\t\t\t\t\tOffset: 0x284,\n\t\t\t\t\tName:   \"impbyord.exe\",\n\t\t\t\t\tDescriptor: ImageImportDescriptor{\n\t\t\t\t\t\tOriginalFirstThunk: 0x10b4,\n\t\t\t\t\t\tTimeDateStamp:      0x0,\n\t\t\t\t\t\tForwarderChain:     0x0,\n\t\t\t\t\t\tName:               0x10d0,\n\t\t\t\t\t\tFirstThunk:         0x1058,\n\t\t\t\t\t},\n\t\t\t\t\tFunctions: []ImportFunction{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tName:               \"#35\",\n\t\t\t\t\t\t\tHint:               0x0,\n\t\t\t\t\t\t\tByOrdinal:          true,\n\t\t\t\t\t\t\tOrdinal:            0x23,\n\t\t\t\t\t\t\tOriginalThunkValue: 0x80000023,\n\t\t\t\t\t\t\tThunkValue:         0x10b4,\n\t\t\t\t\t\t\tThunkRVA:           0x1058,\n\t\t\t\t\t\t\tOriginalThunkRVA:   0x10b4,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\n\t\t\tif file.Is64 {\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryImport]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t} else {\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryImport]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseImportDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseImportDirectory(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\tgot := file.Imports\n\t\t\tif len(got) != tt.out.entryCount {\n\t\t\t\tt.Errorf(\"imports entry count assertion failed, got %v, want %v\", len(got), tt.out.entryCount)\n\t\t\t}\n\n\t\t\timpFunc := file.Imports[tt.out.entryIndex]\n\t\t\tif !reflect.DeepEqual(impFunc, tt.out.entry) {\n\t\t\t\tt.Errorf(\"import function entry assertion failed, got %v, want %v\", impFunc, tt.out.entry)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestImpHash(t *testing.T) {\n\tfor _, tt := range []struct {\n\t\tin  string\n\t\tout string\n\t}{\n\t\t{getAbsoluteFilePath(\"test/putty.exe\"), \"2e3215acc61253e5fa73a840384e9720\"},\n\t\t{getAbsoluteFilePath(\"test/01008963d32f5cc17b64c31446386ee5b36a7eab6761df87a2989ba9394d8f3d\"), \"431cb9bbc479c64cb0d873043f4de547\"},\n\t\t{getAbsoluteFilePath(\"test/0103daa751660333b7ae5f098795df58f07e3031563e042d2eb415bffa71fe7a\"), \"8b58a51c1fff9c4a944265c1fe0fab74\"},\n\t\t{getAbsoluteFilePath(\"test/0585495341e0ffaae1734acb78708ff55cd3612d844672d37226ef63d12652d0\"), \"e4290fa6afc89d56616f34ebbd0b1f2c\"},\n\t} {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\tif err := file.Parse(); err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\timpHash, err := file.ImpHash()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"ImpHash(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\tif impHash != tt.out {\n\t\t\t\tt.Errorf(\"ImpHash(%s) got %v, want %v\", tt.in, impHash, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "loadconfig.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\n// References:\n// https://www.virtualbox.org/svn/vbox/trunk/include/iprt/formats/pecoff.h\n// https://github.com/hdoc/llvm-project/blob/release/15.x/llvm/include/llvm/Object/COFF.h\n// https://ffri.github.io/ProjectChameleon/new_reloc_chpev2/\n// https://blogs.blackberry.com/en/2019/09/teardown-windows-10-on-arm-x86-emulation\n// DVRT: https://www.alex-ionescu.com/?p=323\n// https://xlab.tencent.com/en/2016/11/02/return-flow-guard/\n// https://denuvosoftwaresolutions.github.io/DVRT/dvrt.html\n// BlueHat v18 || Retpoline: The Anti sectre type 2 mitigation in windows: https://www.youtube.com/watch?v=ZfxXjDQRpsU\n\npackage pe\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"reflect\"\n)\n\n// ImageGuardFlagType represents the type for load configuration image guard flags.\ntype ImageGuardFlagType uint8\n\n// GFIDS table entry flags.\nconst (\n\t// ImageGuardFlagFIDSuppressed indicates that the call target is explicitly\n\t// suppressed (do not treat it as valid for purposes of CFG).\n\tImageGuardFlagFIDSuppressed = 0x1\n\n\t// ImageGuardFlagExportSuppressed indicates that the call target is export\n\t// suppressed. See Export suppression for more details.\n\tImageGuardFlagExportSuppressed = 0x2\n)\n\n// The GuardFlags field contains a combination of one or more of the\n// following flags and subfields:\nconst (\n\t// ImageGuardCfInstrumented indicates that the module performs control flow\n\t// integrity checks using system-supplied support.\n\tImageGuardCfInstrumented = 0x00000100\n\n\t// ImageGuardCfWInstrumented indicates that the module performs control\n\t// flow and write integrity checks.\n\tImageGuardCfWInstrumented = 0x00000200\n\n\t// ImageGuardCfFunctionTablePresent indicates that the module contains\n\t// valid control flow target metadata.\n\tImageGuardCfFunctionTablePresent = 0x00000400\n\n\t// ImageGuardSecurityCookieUnused indicates that the module does not make\n\t// use of the /GS security cookie.\n\tImageGuardSecurityCookieUnused = 0x00000800\n\n\t// ImageGuardProtectDelayLoadIAT indicates that the module supports read\n\t// only delay load IAT.\n\tImageGuardProtectDelayLoadIAT = 0x00001000\n\n\t// ImageGuardDelayLoadIATInItsOwnSection indicates that the Delayload\n\t// import table in its own .didat section (with nothing else in it) that\n\t// can be freely reprotected.\n\tImageGuardDelayLoadIATInItsOwnSection = 0x00002000\n\n\t// ImageGuardCfExportSuppressionInfoPresent indicates that the module\n\t// contains suppressed export information. This also infers that the\n\t// address taken IAT table is also present in the load config.\n\tImageGuardCfExportSuppressionInfoPresent = 0x00004000\n\n\t// ImageGuardCfEnableExportSuppression indicates that the module enables\n\t// suppression of exports.\n\tImageGuardCfEnableExportSuppression = 0x00008000\n\n\t// ImageGuardCfLongJumpTablePresent indicates that the module contains\n\t// long jmp target information.\n\tImageGuardCfLongJumpTablePresent = 0x00010000\n)\n\nconst (\n\t// ImageGuardCfFunctionTableSizeMask indicates that the mask for the\n\t// subfield that contains the stride of Control Flow Guard function table\n\t// entries (that is, the additional count of bytes per table entry).\n\tImageGuardCfFunctionTableSizeMask = 0xF0000000\n\n\t// ImageGuardCfFunctionTableSizeShift indicates the shift to right-justify\n\t// Guard CF function table stride.\n\tImageGuardCfFunctionTableSizeShift = 28\n)\n\nconst (\n\tImageDynamicRelocationGuardRfPrologue = 0x00000001\n\tImageDynamicRelocationGuardREpilogue  = 0x00000002\n)\n\n// Software enclave information.\nconst (\n\tImageEnclaveLongIDLength  = 32\n\tImageEnclaveShortIDLength = 16\n)\n\nconst (\n\t// ImageEnclaveImportMatchNone indicates that none of the identifiers of the\n\t// image need to match the value in the import record.\n\tImageEnclaveImportMatchNone = 0x00000000\n\n\t// ImageEnclaveImportMatchUniqueId indicates that the value of the enclave\n\t// unique identifier of the image must match the value in the import record.\n\t// Otherwise, loading of the image fails.\n\tImageEnclaveImportMatchUniqueID = 0x00000001\n\n\t// ImageEnclaveImportMatchAuthorId indicates that the value of the enclave\n\t// author identifier of the image must match the value in the import record.\n\t// Otherwise, loading of the image fails. If this flag is set and the import\n\t// record indicates an author identifier of all zeros, the imported image\n\t// must be part of the Windows installation.\n\tImageEnclaveImportMatchAuthorID = 0x00000002\n\n\t// ImageEnclaveImportMatchFamilyId indicates that the value of the enclave\n\t// family identifier of the image must match the value in the import record.\n\t// Otherwise, loading of the image fails.\n\tImageEnclaveImportMatchFamilyID = 0x00000003\n\n\t// ImageEnclaveImportMatchImageId indicates that the value of the enclave\n\t// image identifier must match the value in the import record. Otherwise,\n\t// loading of the image fails.\n\tImageEnclaveImportMatchImageID = 0x00000004\n)\n\n// ImageLoadConfigDirectory32 Contains the load configuration data of an image for x86 binaries.\ntype ImageLoadConfigDirectory32 struct {\n\t// The actual size of the structure inclusive. May differ from the size\n\t// given in the data directory for Windows XP and earlier compatibility.\n\tSize uint32 `json:\"size\"`\n\n\t// Date and time stamp value.\n\tTimeDateStamp uint32 `json:\"time_date_stamp\"`\n\n\t// Major version number.\n\tMajorVersion uint16 `json:\"major_version\"`\n\n\t// Minor version number.\n\tMinorVersion uint16 `json:\"minor_version\"`\n\n\t// The global loader flags to clear for this process as the loader starts\n\t// the process.\n\tGlobalFlagsClear uint32 `json:\"global_flags_clear\"`\n\n\t// The global loader flags to set for this process as the loader starts the\n\t// process.\n\tGlobalFlagsSet uint32 `json:\"global_flags_set\"`\n\n\t// The default timeout value to use for this process's critical sections\n\t// that are abandoned.\n\tCriticalSectionDefaultTimeout uint32 `json:\"critical_section_default_timeout\"`\n\n\t// Memory that must be freed before it is returned to the system, in bytes.\n\tDeCommitFreeBlockThreshold uint32 `json:\"de_commit_free_block_threshold\"`\n\n\t// Total amount of free memory, in bytes.\n\tDeCommitTotalFreeThreshold uint32 `json:\"de_commit_total_free_threshold\"`\n\n\t// [x86 only] The VA of a list of addresses where the LOCK prefix is used so\n\t// that they can be replaced with NOP on single processor machines.\n\tLockPrefixTable uint32 `json:\"lock_prefix_table\"`\n\n\t// Maximum allocation size, in bytes.\n\tMaximumAllocationSize uint32 `json:\"maximum_allocation_size\"`\n\n\t// Maximum virtual memory size, in bytes.\n\tVirtualMemoryThreshold uint32 `json:\"virtual_memory_threshold\"`\n\n\t// Process heap flags that correspond to the first argument of the HeapCreate\n\t// function. These flags apply to the process heap that is created during\n\t// process startup.\n\tProcessHeapFlags uint32 `json:\"process_heap_flags\"`\n\n\t// Setting this field to a non-zero value is equivalent to calling\n\t// SetProcessAffinityMask with this value during process startup (.exe only)\n\tProcessAffinityMask uint32 `json:\"process_affinity_mask\"`\n\n\t// The service pack version identifier.\n\tCSDVersion uint16 `json:\"csd_version\"`\n\n\t// Must be zero.\n\tDependentLoadFlags uint16 `json:\"dependent_load_flags\"`\n\n\t// Reserved for use by the system.\n\tEditList uint32 `json:\"edit_list\"`\n\n\t// A pointer to a cookie that is used by Visual C++ or GS implementation.\n\tSecurityCookie uint32 `json:\"security_cookie\"`\n\n\t// [x86 only] The VA of the sorted table of RVAs of each valid, unique SE\n\t// handler in the image.\n\tSEHandlerTable uint32 `json:\"se_handler_table\"`\n\n\t// [x86 only] The count of unique handlers in the table.\n\tSEHandlerCount uint32 `json:\"se_handler_count\"`\n\n\t// The VA where Control Flow Guard check-function pointer is stored.\n\tGuardCFCheckFunctionPointer uint32 `json:\"guard_cf_check_function_pointer\"`\n\n\t// The VA where Control Flow Guard dispatch-function pointer is stored.\n\tGuardCFDispatchFunctionPointer uint32 `json:\"guard_cf_dispatch_function_pointer\"`\n\n\t// The VA of the sorted table of RVAs of each Control Flow Guard function in\n\t// the image.\n\tGuardCFFunctionTable uint32 `json:\"guard_cf_function_table\"`\n\n\t// The count of unique RVAs in the above table.\n\tGuardCFFunctionCount uint32 `json:\"guard_cf_function_count\"`\n\n\t// Control Flow Guard related flags.\n\tGuardFlags uint32 `json:\"guard_flags\"`\n\n\t// Code integrity information.\n\tCodeIntegrity ImageLoadConfigCodeIntegrity `json:\"code_integrity\"`\n\n\t// The VA where Control Flow Guard address taken IAT table is stored.\n\tGuardAddressTakenIATEntryTable uint32 `json:\"guard_address_taken_iat_entry_table\"`\n\n\t// The count of unique RVAs in the above table.\n\tGuardAddressTakenIATEntryCount uint32 `json:\"guard_address_taken_iat_entry_count\"`\n\n\t// The VA where Control Flow Guard long jump target table is stored.\n\tGuardLongJumpTargetTable uint32 `json:\"guard_long_jump_target_table\"`\n\n\t// The count of unique RVAs in the above table.\n\tGuardLongJumpTargetCount uint32 `json:\"guard_long_jump_target_count\"`\n\n\tDynamicValueRelocTable uint32 `json:\"dynamic_value_reloc_table\"`\n\n\t// Not sure when this was renamed from HybridMetadataPointer.\n\tCHPEMetadataPointer uint32 `json:\"chpe_metadata_pointer\"`\n\n\tGuardRFFailureRoutine                    uint32 `json:\"guard_rf_failure_routine\"`\n\tGuardRFFailureRoutineFunctionPointer     uint32 `json:\"guard_rf_failure_routine_function_pointer\"`\n\tDynamicValueRelocTableOffset             uint32 `json:\"dynamic_value_reloc_table_offset\"`\n\tDynamicValueRelocTableSection            uint16 `json:\"dynamic_value_reloc_table_section\"`\n\tReserved2                                uint16 `json:\"reserved_2\"`\n\tGuardRFVerifyStackPointerFunctionPointer uint32 `json:\"guard_rf_verify_stack_pointer_function_pointer\"`\n\tHotPatchTableOffset                      uint32 `json:\"hot_patch_table_offset\"`\n\tReserved3                                uint32 `json:\"reserved_3\"`\n\tEnclaveConfigurationPointer              uint32 `json:\"enclave_configuration_pointer\"`\n\tVolatileMetadataPointer                  uint32 `json:\"volatile_metadata_pointer\"`\n\tGuardEHContinuationTable                 uint32 `json:\"guard_eh_continuation_table\"`\n\tGuardEHContinuationCount                 uint32 `json:\"guard_eh_continuation_count\"`\n\tGuardXFGCheckFunctionPointer             uint32 `json:\"guard_xfg_check_function_pointer\"`\n\tGuardXFGDispatchFunctionPointer          uint32 `json:\"guard_xfg_dispatch_function_pointer\"`\n\tGuardXFGTableDispatchFunctionPointer     uint32 `json:\"guard_xfg_table_dispatch_function_pointer\"`\n\tCastGuardOSDeterminedFailureMode         uint32 `json:\"cast_guard_os_determined_failure_mode\"`\n\tGuardMemcpyFunctionPointer               uint32 `json:\"guard_memcpy_function_pointer\"`\n}\n\n// ImageLoadConfigDirectory64 Contains the load configuration data of an image for x64 binaries.\ntype ImageLoadConfigDirectory64 struct {\n\t// The actual size of the structure inclusive. May differ from the size\n\t// given in the data directory for Windows XP and earlier compatibility.\n\tSize uint32 `json:\"size\"`\n\n\t// Date and time stamp value.\n\tTimeDateStamp uint32 `json:\"time_date_stamp\"`\n\n\t// Major version number.\n\tMajorVersion uint16 `json:\"major_version\"`\n\n\t// Minor version number.\n\tMinorVersion uint16 `json:\"minor_version\"`\n\n\t// The global loader flags to clear for this process as the loader starts\n\t// the process.\n\tGlobalFlagsClear uint32 `json:\"global_flags_clear\"`\n\n\t// The global loader flags to set for this process as the loader starts the\n\t// process.\n\tGlobalFlagsSet uint32 `json:\"global_flags_set\"`\n\n\t// The default timeout value to use for this process's critical sections\n\t// that are abandoned.\n\tCriticalSectionDefaultTimeout uint32 `json:\"critical_section_default_timeout\"`\n\n\t// Memory that must be freed before it is returned to the system, in bytes.\n\tDeCommitFreeBlockThreshold uint64 `json:\"de_commit_free_block_threshold\"`\n\n\t// Total amount of free memory, in bytes.\n\tDeCommitTotalFreeThreshold uint64 `json:\"de_commit_total_free_threshold\"`\n\n\t// [x86 only] The VA of a list of addresses where the LOCK prefix is used so\n\t// that they can be replaced with NOP on single processor machines.\n\tLockPrefixTable uint64 `json:\"lock_prefix_table\"`\n\n\t// Maximum allocation size, in bytes.\n\tMaximumAllocationSize uint64 `json:\"maximum_allocation_size\"`\n\n\t// Maximum virtual memory size, in bytes.\n\tVirtualMemoryThreshold uint64 `json:\"virtual_memory_threshold\"`\n\n\t// Setting this field to a non-zero value is equivalent to calling\n\t// SetProcessAffinityMask with this value during process startup (.exe only)\n\tProcessAffinityMask uint64 `json:\"process_affinity_mask\"`\n\n\t// Process heap flags that correspond to the first argument of the HeapCreate\n\t// function. These flags apply to the process heap that is created during\n\t// process startup.\n\tProcessHeapFlags uint32 `json:\"process_heap_flags\"`\n\n\t// The service pack version identifier.\n\tCSDVersion uint16 `json:\"csd_version\"`\n\n\t// Must be zero.\n\tDependentLoadFlags uint16 `json:\"dependent_load_flags\"`\n\n\t// Reserved for use by the system.\n\tEditList uint64 `json:\"edit_list\"`\n\n\t// A pointer to a cookie that is used by Visual C++ or GS implementation.\n\tSecurityCookie uint64 `json:\"security_cookie\"`\n\n\t// [x86 only] The VA of the sorted table of RVAs of each valid, unique SE\n\t// handler in the image.\n\tSEHandlerTable uint64 `json:\"se_handler_table\"`\n\n\t// [x86 only] The count of unique handlers in the table.\n\tSEHandlerCount uint64 `json:\"se_handler_count\"`\n\n\t// The VA where Control Flow Guard check-function pointer is stored.\n\tGuardCFCheckFunctionPointer uint64 `json:\"guard_cf_check_function_pointer\"`\n\n\t// The VA where Control Flow Guard dispatch-function pointer is stored.\n\tGuardCFDispatchFunctionPointer uint64 `json:\"guard_cf_dispatch_function_pointer\"`\n\n\t// The VA of the sorted table of RVAs of each Control Flow Guard function in\n\t// the image.\n\tGuardCFFunctionTable uint64 `json:\"guard_cf_function_table\"`\n\n\t// The count of unique RVAs in the above table.\n\tGuardCFFunctionCount uint64 `json:\"guard_cf_function_count\"`\n\n\t// Control Flow Guard related flags.\n\tGuardFlags uint32 `json:\"guard_flags\"`\n\n\t// Code integrity information.\n\tCodeIntegrity ImageLoadConfigCodeIntegrity `json:\"code_integrity\"`\n\n\t// The VA where Control Flow Guard address taken IAT table is stored.\n\tGuardAddressTakenIATEntryTable uint64 `json:\"guard_address_taken_iat_entry_table\"`\n\n\t// The count of unique RVAs in the above table.\n\tGuardAddressTakenIATEntryCount uint64 `json:\"guard_address_taken_iat_entry_count\"`\n\n\t// The VA where Control Flow Guard long jump target table is stored.\n\tGuardLongJumpTargetTable uint64 `json:\"guard_long_jump_target_table\"`\n\n\t// The count of unique RVAs in the above table.\n\tGuardLongJumpTargetCount uint64 `json:\"guard_long_jump_target_count\"`\n\n\tDynamicValueRelocTable uint64 `json:\"dynamic_value_reloc_table\"`\n\n\t// Not sure when this was renamed from HybridMetadataPointer.\n\tCHPEMetadataPointer uint64 `json:\"chpe_metadata_pointer\"`\n\n\tGuardRFFailureRoutine                    uint64 `json:\"guard_rf_failure_routine\"`\n\tGuardRFFailureRoutineFunctionPointer     uint64 `json:\"guard_rf_failure_routine_function_pointer\"`\n\tDynamicValueRelocTableOffset             uint32 `json:\"dynamic_value_reloc_table_offset\"`\n\tDynamicValueRelocTableSection            uint16 `json:\"dynamic_value_reloc_table_section\"`\n\tReserved2                                uint16 `json:\"reserved_2\"`\n\tGuardRFVerifyStackPointerFunctionPointer uint64 `json:\"guard_rf_verify_stack_pointer_function_pointer\"`\n\tHotPatchTableOffset                      uint32 `json:\"hot_patch_table_offset\"`\n\tReserved3                                uint32 `json:\"reserved_3\"`\n\tEnclaveConfigurationPointer              uint64 `json:\"enclave_configuration_pointer\"`\n\tVolatileMetadataPointer                  uint64 `json:\"volatile_metadata_pointer\"`\n\tGuardEHContinuationTable                 uint64 `json:\"guard_eh_continuation_table\"`\n\tGuardEHContinuationCount                 uint64 `json:\"guard_eh_continuation_count\"`\n\tGuardXFGCheckFunctionPointer             uint64 `json:\"guard_xfg_check_function_pointer\"`\n\tGuardXFGDispatchFunctionPointer          uint64 `json:\"guard_xfg_dispatch_function_pointer\"`\n\tGuardXFGTableDispatchFunctionPointer     uint64 `json:\"guard_xfg_table_dispatch_function_pointer\"`\n\tCastGuardOSDeterminedFailureMode         uint64 `json:\"cast_guard_os_determined_failure_mode\"`\n\tGuardMemcpyFunctionPointer               uint64 `json:\"guard_memcpy_function_pointer\"`\n}\n\n// ImageCHPEMetadataX86 represents the X86_IMAGE_CHPE_METADATA_X86.\ntype ImageCHPEMetadataX86 struct {\n\tVersion                                  uint32 `json:\"version\"`\n\tCHPECodeAddressRangeOffset               uint32 `json:\"chpe_code_address_range_offset\"`\n\tCHPECodeAddressRangeCount                uint32 `json:\"chpe_code_address_range_count\"`\n\tWoWA64ExceptionHandlerFunctionPtr        uint32 `json:\"wow_a64_exception_handler_function_ptr\"`\n\tWoWA64DispatchCallFunctionPtr            uint32 `json:\"wow_a64_dispatch_call_function_ptr\"`\n\tWoWA64DispatchIndirectCallFunctionPtr    uint32 `json:\"wow_a64_dispatch_indirect_call_function_ptr\"`\n\tWoWA64DispatchIndirectCallCfgFunctionPtr uint32 `json:\"wow_a64_dispatch_indirect_call_cfg_function_ptr\"`\n\tWoWA64DispatchRetFunctionPtr             uint32 `json:\"wow_a64_dispatch_ret_function_ptr\"`\n\tWoWA64DispatchRetLeafFunctionPtr         uint32 `json:\"wow_a64_dispatch_ret_leaf_function_ptr\"`\n\tWoWA64DispatchJumpFunctionPtr            uint32 `json:\"wow_a64_dispatch_jump_function_ptr\"`\n\tCompilerIATPointer                       uint32 `json:\"compiler_iat_pointer\"`       // Present if Version >= 2\n\tWoWA64RDTSCFunctionPtr                   uint32 `json:\"wow_a64_rdtsc_function_ptr\"` // Present if Version >= 3\n}\n\ntype CodeRange struct {\n\tBegin   uint32 `json:\"begin\"`\n\tLength  uint32 `json:\"length\"`\n\tMachine uint8  `json:\"machine\"`\n}\n\ntype CompilerIAT struct {\n\tRVA         uint32 `json:\"rva\"`\n\tValue       uint32 `json:\"value\"`\n\tDescription string `json:\"description\"`\n}\n\ntype HybridPE struct {\n\tCHPEMetadata interface{}   `json:\"chpe_metadata\"`\n\tCodeRanges   []CodeRange   `json:\"code_ranges\"`\n\tCompilerIAT  []CompilerIAT `json:\"compiler_iat\"`\n}\n\n// ImageDynamicRelocationTable represents the DVRT header.\ntype ImageDynamicRelocationTable struct {\n\t// Until now, there is only one version of the DVRT header (1)..\n\tVersion uint32 `json:\"version\"`\n\t// Size represents the number of bytes after the header that contains\n\t// retpoline information.\n\tSize uint32 `json:\"size\"`\n\t//  IMAGE_DYNAMIC_RELOCATION DynamicRelocations[0];\n}\n\n// Dynamic value relocation entries following IMAGE_DYNAMIC_RELOCATION_TABLE.\n// Each block starts with the header.\n\n// ImageDynamicRelocation32 represents the 32-bit version of a reloc entry.\ntype ImageDynamicRelocation32 struct {\n\t// Symbol field identifies one of the existing types of dynamic relocations\n\t// so far (values 3, 4 and 5).\n\tSymbol uint32 `json:\"symbol\"`\n\n\t// Then, for each page, there is a block that starts with a relocation entry.\n\t// BaseRelocSize represents the size of the block.\n\tBaseRelocSize uint32 `json:\"base_reloc_size\"`\n\t//  IMAGE_BASE_RELOCATION BaseRelocations[0];\n}\n\n// ImageDynamicRelocation64 represents the 64-bit version of a reloc entry.\ntype ImageDynamicRelocation64 struct {\n\t// Symbol field identifies one of the existing types of dynamic relocations\n\t// so far (values 3, 4 and 5).\n\tSymbol uint64 `json:\"symbol\"`\n\n\t// Then, for each page, there is a block that starts with a relocation entry.\n\t// BaseRelocSize represents the size of the block.\n\tBaseRelocSize uint32 `json:\"base_reloc_size\"`\n\t//  IMAGE_BASE_RELOCATION BaseRelocations[0];\n}\n\ntype ImageDynamicRelocation32v2 struct {\n\tHeaderSize    uint32 `json:\"header_size\"`\n\tFixupInfoSize uint32 `json:\"fixup_info_size\"`\n\tSymbol        uint32 `json:\"symbol\"`\n\tSymbolGroup   uint32 `json:\"symbol_group\"`\n\tFlags         uint32 `json:\"flags\"`\n\t// ...     variable length header fields\n\t// UCHAR   FixupInfo[FixupInfoSize]\n}\n\ntype ImageDynamicRelocation64v2 struct {\n\tHeaderSize    uint32 `json:\"header_size\"`\n\tFixupInfoSize uint32 `json:\"fixup_info_size\"`\n\tSymbol        uint64 `json:\"symbol\"`\n\tSymbolGroup   uint32 `json:\"symbol_group\"`\n\tFlags         uint32 `json:\"flags\"`\n\t// ...     variable length header fields\n\t// UCHAR   FixupInfo[FixupInfoSize]\n}\n\ntype ImagePrologueDynamicRelocationHeader struct {\n\tPrologueByteCount uint8 `json:\"prologue_byte_count\"`\n\t// UCHAR   PrologueBytes[PrologueByteCount];\n}\n\ntype ImageEpilogueDynamicRelocationHeader struct {\n\tEpilogueCount               uint32 `json:\"epilogue_count\"`\n\tEpilogueByteCount           uint8  `json:\"epilogue_byte_count\"`\n\tBranchDescriptorElementSize uint8  `json:\"branch_descriptor_element_size\"`\n\tBranchDescriptorCount       uint8  `json:\"branch_descriptor_count\"`\n\t// UCHAR   BranchDescriptors[...];\n\t// UCHAR   BranchDescriptorBitMap[...];\n}\n\ntype CFGFunction struct {\n\t// RVA of the target CFG call.\n\tRVA uint32 `json:\"rva\"`\n\n\t// Flags attached to each GFIDS entry if any call targets have metadata.\n\tFlags       ImageGuardFlagType `json:\"flags\"`\n\tDescription string             `json:\"description\"`\n}\n\ntype CFGIATEntry struct {\n\tRVA         uint32 `json:\"rva\"`\n\tIATValue    uint32 `json:\"iat_value\"`\n\tINTValue    uint32 `json:\"int_value\"`\n\tDescription string `json:\"description\"`\n}\n\ntype RelocBlock struct {\n\tImgBaseReloc ImageBaseRelocation `json:\"img_base_reloc\"`\n\tTypeOffsets  []interface{}       `json:\"type_offsets\"`\n}\ntype RelocEntry struct {\n\t// Could be ImageDynamicRelocation32{} or ImageDynamicRelocation64{}\n\tImageDynamicRelocation interface{}  `json:\"image_dynamic_relocation\"`\n\tRelocBlocks            []RelocBlock `json:\"reloc_blocks\"`\n}\n\n// ImageImportControlTransferDynamicRelocation represents the Imported Address\n// Retpoline (type 3), size = 4 bytes.\ntype ImageImportControlTransferDynamicRelocation struct {\n\tPageRelativeOffset uint16 `json:\"page_relative_offset\"` // (12 bits)\n\t// 1 - the opcode is a CALL\n\t// 0 - the opcode is a JMP.\n\tIndirectCall uint16 `json:\"indirect_call\"` // (1 bit)\n\tIATIndex     uint32 `json:\"iat_index\"`     // (19 bits)\n}\n\n// ImageIndirectControlTransferDynamicRelocation represents the Indirect Branch\n// Retpoline (type 4), size = 2 bytes.\ntype ImageIndirectControlTransferDynamicRelocation struct {\n\tPageRelativeOffset uint16 `json:\"page_relative_offset\"` // (12 bits)\n\tIndirectCall       uint8  `json:\"indirect_call\"`        // (1 bit)\n\tRexWPrefix         uint8  `json:\"rex_w_prefix\"`         // (1 bit)\n\tCfgCheck           uint8  `json:\"cfg_check\"`            // (1 bit)\n\tReserved           uint8  `json:\"reserved\"`             // (1 bit)\n}\n\n// ImageSwitchableBranchDynamicRelocation represents the Switchable Retpoline\n// (type 5), size = 2 bytes.\ntype ImageSwitchableBranchDynamicRelocation struct {\n\tPageRelativeOffset uint16 `json:\"page_relative_offset\"` // (12 bits)\n\tRegisterNumber     uint16 `json:\"register_number\"`      // (4 bits)\n}\n\n// DVRT represents the Dynamic Value Relocation Table.\n// The DVRT was originally introduced back in the Windows 10 Creators Update to\n// improve kernel address space layout randomization (KASLR). It allowed the\n// memory manager’s page frame number (PFN) database and page table self-map to\n// be assigned dynamic addresses at runtime. The DVRT is stored directly in the\n// binary and contains a series of relocation entries for each symbol (i.e.\n// address) that is to be relocated. The relocation entries are themselves\n// arranged in a hierarchical fashion grouped first by symbol and then by\n// containing page to allow for a compact description of all locations in the\n// binary that reference a relocatable symbol.\n// Reference: https://techcommunity.microsoft.com/t5/windows-os-platform-blog/mitigating-spectre-variant-2-with-retpoline-on-windows/ba-p/295618\ntype DVRT struct {\n\tImageDynamicRelocationTable `json:\"image_dynamic_relocation_table\"`\n\tEntries                     []RelocEntry `json:\"entries\"`\n}\n\ntype Enclave struct {\n\n\t// Points to either ImageEnclaveConfig32{} or ImageEnclaveConfig64{}.\n\tConfig interface{} `json:\"config\"`\n\n\tImports []ImageEnclaveImport `json:\"imports\"`\n}\n\ntype RangeTableEntry struct {\n\tRVA  uint32 `json:\"rva\"`\n\tSize uint32 `json:\"size\"`\n}\n\ntype VolatileMetadata struct {\n\tStruct         ImageVolatileMetadata `json:\"struct\"`\n\tAccessRVATable []uint32              `json:\"access_rva_table\"`\n\tInfoRangeTable []RangeTableEntry     `json:\"info_range_table\"`\n}\ntype LoadConfig struct {\n\tStruct           interface{}       `json:\"struct\"`\n\tSEH              []uint32          `json:\"seh\"`\n\tGFIDS            []CFGFunction     `json:\"gfids\"`\n\tCFGIAT           []CFGIATEntry     `json:\"cfgiat\"`\n\tCFGLongJump      []uint32          `json:\"cfg_long_jump\"`\n\tCHPE             *HybridPE         `json:\"chpe\"`\n\tDVRT             *DVRT             `json:\"dvrt\"`\n\tEnclave          *Enclave          `json:\"enclave\"`\n\tVolatileMetadata *VolatileMetadata `json:\"volatile_metadata\"`\n}\n\n// ImageLoadConfigCodeIntegrity Code Integrity in load config (CI).\ntype ImageLoadConfigCodeIntegrity struct {\n\t// Flags to indicate if CI information is available, etc.\n\tFlags uint16 `json:\"flags\"`\n\t// 0xFFFF means not available\n\tCatalog       uint16 `json:\"catalog\"`\n\tCatalogOffset uint32 `json:\"catalog_offset\"`\n\t// Additional bitmask to be defined later\n\tReserved uint32 `json:\"reserved\"`\n}\n\ntype ImageEnclaveConfig32 struct {\n\n\t// The size of the IMAGE_ENCLAVE_CONFIG32 structure, in bytes.\n\tSize uint32 `json:\"size\"`\n\n\t// The minimum size of the IMAGE_ENCLAVE_CONFIG32 structure that the image\n\t// loader must be able to process in order for the enclave to be usable.\n\t// This member allows an enclave to inform an earlier version of the image\n\t// loader that the image loader can safely load the enclave and ignore optional\n\t// members added to IMAGE_ENCLAVE_CONFIG32 for later versions of the enclave.\n\n\t// If the size of IMAGE_ENCLAVE_CONFIG32 that the image loader can process is\n\t// less than MinimumRequiredConfigSize, the enclave cannot be run securely.\n\t// If MinimumRequiredConfigSize is zero, the minimum size of the\n\t// IMAGE_ENCLAVE_CONFIG32 structure that the image loader must be able to\n\t// process in order for the enclave to be usable is assumed to be the size\n\t// of the structure through and including the MinimumRequiredConfigSize member.\n\tMinimumRequiredConfigSize uint32 `json:\"minimum_required_config_size\"`\n\n\t// A flag that indicates whether the enclave permits debugging.\n\tPolicyFlags uint32 `json:\"policy_flags\"`\n\n\t// The number of images in the array of images that the ImportList member\n\t// points to.\n\tNumberOfImports uint32 `json:\"number_of_imports\"`\n\n\t// The relative virtual address of the array of images that the enclave\n\t// image may import, with identity information for each image.\n\tImportList uint32 `json:\"import_list\"`\n\n\t// The size of each image in the array of images that the ImportList member\n\t// points to.\n\tImportEntrySize uint32 `json:\"import_entry_size\"`\n\n\t// The family identifier that the author of the enclave assigned to the enclave.\n\tFamilyID [ImageEnclaveShortIDLength]uint8 `json:\"family_id\"`\n\n\t// The image identifier that the author of the enclave assigned to the enclave.\n\tImageID [ImageEnclaveShortIDLength]uint8 `json:\"image_id\"`\n\n\t// The version number that the author of the enclave assigned to the enclave.\n\tImageVersion uint32 `json:\"image_version\"`\n\n\t// The security version number that the author of the enclave assigned to\n\t// the enclave.\n\tSecurityVersion uint32 `json:\"security_version\"`\n\n\t// The expected virtual size of the private address range for the enclave,\n\t// in bytes.\n\tEnclaveSize uint32 `json:\"enclave_size\"`\n\n\t// The maximum number of threads that can be created within the enclave.\n\tNumberOfThreads uint32 `json:\"number_of_threads\"`\n\n\t// A flag that indicates whether the image is suitable for use as the\n\t// primary image in the enclave.\n\tEnclaveFlags uint32 `json:\"enclave_flags\"`\n}\n\ntype ImageEnclaveConfig64 struct {\n\n\t// The size of the IMAGE_ENCLAVE_CONFIG32 structure, in bytes.\n\tSize uint32 `json:\"size\"`\n\n\t// The minimum size of the IMAGE_ENCLAVE_CONFIG32 structure that the image\n\t// loader must be able to process in order for the enclave to be usable.\n\t// This member allows an enclave to inform an earlier version of the image\n\t// loader that the image loader can safely load the enclave and ignore\n\t// optional members added to IMAGE_ENCLAVE_CONFIG32 for later versions of\n\t// the enclave.\n\n\t// If the size of IMAGE_ENCLAVE_CONFIG32 that the image loader can process\n\t// is less than MinimumRequiredConfigSize, the enclave cannot be run securely.\n\t// If MinimumRequiredConfigSize is zero, the minimum size of the\n\t// IMAGE_ENCLAVE_CONFIG32 structure that the image loader must be able to\n\t// process in order for the enclave to be usable is assumed to be the size\n\t// of the structure through and including the MinimumRequiredConfigSize member.\n\tMinimumRequiredConfigSize uint32 `json:\"minimum_required_config_size\"`\n\n\t// A flag that indicates whether the enclave permits debugging.\n\tPolicyFlags uint32 `json:\"policy_flags\"`\n\n\t// The number of images in the array of images that the ImportList member\n\t// points to.\n\tNumberOfImports uint32 `json:\"number_of_imports\"`\n\n\t// The relative virtual address of the array of images that the enclave\n\t// image may import, with identity information for each image.\n\tImportList uint32 `json:\"import_list\"`\n\n\t// The size of each image in the array of images that the ImportList member\n\t// points to.\n\tImportEntrySize uint32 `json:\"import_entry_size\"`\n\n\t// The family identifier that the author of the enclave assigned to the enclave.\n\tFamilyID [ImageEnclaveShortIDLength]uint8 `json:\"family_id\"`\n\n\t// The image identifier that the author of the enclave assigned to the enclave.\n\tImageID [ImageEnclaveShortIDLength]uint8 `json:\"image_id\"`\n\n\t// The version number that the author of the enclave assigned to the enclave.\n\tImageVersion uint32 `json:\"image_version\"`\n\n\t// The security version number that the author of the enclave assigned to the enclave.\n\tSecurityVersion uint32 `json:\"security_version\"`\n\n\t// The expected virtual size of the private address range for the enclave,in bytes.\n\tEnclaveSize uint64 `json:\"enclave_size\"`\n\n\t// The maximum number of threads that can be created within the enclave.\n\tNumberOfThreads uint32 `json:\"number_of_threads\"`\n\n\t// A flag that indicates whether the image is suitable for use as the primary\n\t// image in the enclave.\n\tEnclaveFlags uint32 `json:\"enclave_flags\"`\n}\n\n// ImageEnclaveImport defines a entry in the array of images that an enclave can import.\ntype ImageEnclaveImport struct {\n\n\t// The type of identifier of the image that must match the value in the import record.\n\tMatchType uint32 `json:\"match_type\"`\n\n\t// The minimum enclave security version that each image must have for the\n\t// image to be imported successfully. The image is rejected unless its\n\t// enclave security version is equal to or greater than the minimum value in\n\t// the import record. Set the value in the import record to zero to turn off\n\t// the security version check.\n\tMinimumSecurityVersion uint32 `json:\"minimum_security_version\"`\n\n\t// The unique identifier of the primary module for the enclave, if the\n\t// MatchType member is IMAGE_ENCLAVE_IMPORT_MATCH_UNIQUE_ID. Otherwise,\n\t// the author identifier of the primary module for the enclave..\n\tUniqueOrAuthorID [ImageEnclaveLongIDLength]uint8 `json:\"unique_or_author_id\"`\n\n\t// The family identifier of the primary module for the enclave.\n\tFamilyID [ImageEnclaveShortIDLength]uint8 `json:\"family_id\"`\n\n\t// The image identifier of the primary module for the enclave.\n\tImageID [ImageEnclaveShortIDLength]uint8 `json:\"image_id\"`\n\n\t// The relative virtual address of a NULL-terminated string that contains\n\t// the same value found in the import directory for the image.\n\tImportName uint32 `json:\"import_name\"`\n\n\t// Reserved.\n\tReserved uint32 `json:\"reserved\"`\n}\n\ntype ImageVolatileMetadata struct {\n\tSize                       uint32 `json:\"size\"`\n\tVersion                    uint32 `json:\"version\"`\n\tVolatileAccessTable        uint32 `json:\"volatile_access_table\"`\n\tVolatileAccessTableSize    uint32 `json:\"volatile_access_table_size\"`\n\tVolatileInfoRangeTable     uint32 `json:\"volatile_info_range_table\"`\n\tVolatileInfoRangeTableSize uint32 `json:\"volatile_info_range_table_size\"`\n}\n\n// The load configuration structure (IMAGE_LOAD_CONFIG_DIRECTORY) was formerly\n// used in very limited cases in the Windows NT operating system itself to\n// describe various features too difficult or too large to describe in the file\n\n// header or optional header of the image. Current versions of the Microsoft\n// linker and Windows XP and later versions of Windows use a new version of this\n// structure for 32-bit x86-based systems that include reserved SEH technology.\n// The data directory entry for a pre-reserved SEH load configuration structure\n// must specify a particular size of the load configuration structure because\n// the operating system loader always expects it to be a certain value. In that\n// regard, the size is really only a version check. For compatibility with\n// Windows XP and earlier versions of Windows, the size must be 64 for x86 images.\nfunc (pe *File) parseLoadConfigDirectory(rva, size uint32) error {\n\n\t// As the load config structure changes over time,\n\t// we first read it size to figure out which one we have to cast against.\n\tfileOffset := pe.GetOffsetFromRva(rva)\n\tstructSize, err := pe.ReadUint32(fileOffset)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Use this helper function to print struct size.\n\t// PrintLoadConfigStruct()\n\tvar loadCfg interface{}\n\n\t// Boundary check\n\ttotalSize := fileOffset + size\n\n\t// Integer overflow\n\tif (totalSize > fileOffset) != (size > 0) {\n\t\treturn ErrOutsideBoundary\n\t}\n\n\tif fileOffset >= pe.size || totalSize > pe.size {\n\t\treturn ErrOutsideBoundary\n\t}\n\n\tif pe.Is32 {\n\t\tmaxSize := uint32(binary.Size(ImageLoadConfigDirectory32{}))\n\t\tif structSize > maxSize {\n\t\t\treturn ErrOutsideBoundary\n\t\t}\n\t\tloadCfg32 := ImageLoadConfigDirectory32{}\n\t\timgLoadConfigDirectory := make([]byte, binary.Size(loadCfg32))\n\t\tcopy(imgLoadConfigDirectory, pe.data[fileOffset:fileOffset+structSize])\n\t\tbuf := bytes.NewReader(imgLoadConfigDirectory)\n\t\terr = binary.Read(buf, binary.LittleEndian, &loadCfg32)\n\t\tloadCfg = loadCfg32\n\t} else {\n\t\tmaxSize := uint32(binary.Size(ImageLoadConfigDirectory64{}))\n\t\tif structSize > maxSize {\n\t\t\treturn ErrOutsideBoundary\n\t\t}\n\t\tloadCfg64 := ImageLoadConfigDirectory64{}\n\t\timgLoadConfigDirectory := make([]byte, binary.Size(loadCfg64))\n\t\tcopy(imgLoadConfigDirectory, pe.data[fileOffset:fileOffset+structSize])\n\t\tbuf := bytes.NewReader(imgLoadConfigDirectory)\n\t\terr = binary.Read(buf, binary.LittleEndian, &loadCfg64)\n\t\tloadCfg = loadCfg64\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Save the load config struct.\n\tpe.HasLoadCFG = true\n\tpe.LoadConfig.Struct = loadCfg\n\n\t// Retrieve SEH handlers if there are any..\n\tif pe.Is32 {\n\t\thandlers := pe.getSEHHandlers()\n\t\tpe.LoadConfig.SEH = handlers\n\t}\n\n\t// Retrieve Control Flow Guard Function Targets if there are any.\n\tpe.LoadConfig.GFIDS = pe.getControlFlowGuardFunctions()\n\n\t// Retrieve Control Flow Guard IAT entries if there are any.\n\tpe.LoadConfig.CFGIAT = pe.getControlFlowGuardIAT()\n\n\t// Retrieve Long jump target functions if there are any.\n\tpe.LoadConfig.CFGLongJump = pe.getLongJumpTargetTable()\n\n\t// Retrieve compiled hybrid PE metadata if there are any.\n\tpe.LoadConfig.CHPE = pe.getHybridPE()\n\n\t// Retrieve dynamic value relocation table if there are any.\n\tpe.LoadConfig.DVRT = pe.getDynamicValueRelocTable()\n\n\t// Retrieve enclave configuration if there are any.\n\tpe.LoadConfig.Enclave = pe.getEnclaveConfiguration()\n\n\t// Retrieve volatile metadata table if there are any.\n\tpe.LoadConfig.VolatileMetadata = pe.getVolatileMetadata()\n\n\treturn nil\n}\n\n// StringifyGuardFlags returns list of strings which describes the GuardFlags.\nfunc StringifyGuardFlags(flags uint32) []string {\n\tvar values []string\n\tguardFlagMap := map[uint32]string{\n\t\tImageGuardCfInstrumented:                 \"Instrumented\",\n\t\tImageGuardCfWInstrumented:                \"WriteInstrumented\",\n\t\tImageGuardCfFunctionTablePresent:         \"TargetMetadata\",\n\t\tImageGuardSecurityCookieUnused:           \"SecurityCookieUnused\",\n\t\tImageGuardProtectDelayLoadIAT:            \"DelayLoadIAT\",\n\t\tImageGuardDelayLoadIATInItsOwnSection:    \"DelayLoadIATInItsOwnSection\",\n\t\tImageGuardCfExportSuppressionInfoPresent: \"ExportSuppressionInfoPresent\",\n\t\tImageGuardCfEnableExportSuppression:      \"EnableExportSuppression\",\n\t\tImageGuardCfLongJumpTablePresent:         \"LongJumpTablePresent\",\n\t}\n\n\tfor k, s := range guardFlagMap {\n\t\tif k&flags != 0 {\n\t\t\tvalues = append(values, s)\n\t\t}\n\t}\n\treturn values\n}\n\nfunc (pe *File) getSEHHandlers() []uint32 {\n\n\tvar handlers []uint32\n\tv := reflect.ValueOf(pe.LoadConfig.Struct)\n\n\t// SEHandlerCount is found in index 19 of the struct.\n\tSEHandlerCount := uint32(v.Field(19).Uint())\n\tif SEHandlerCount > 0 {\n\t\tSEHandlerTable := uint32(v.Field(18).Uint())\n\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).ImageBase\n\t\trva := SEHandlerTable - imageBase\n\t\tfor i := uint32(0); i < SEHandlerCount; i++ {\n\t\t\toffset := pe.GetOffsetFromRva(rva + i*4)\n\t\t\thandler, err := pe.ReadUint32(offset)\n\t\t\tif err != nil {\n\t\t\t\treturn handlers\n\t\t\t}\n\n\t\t\thandlers = append(handlers, handler)\n\t\t}\n\t}\n\n\treturn handlers\n}\n\nfunc (pe *File) getControlFlowGuardFunctions() []CFGFunction {\n\n\tv := reflect.ValueOf(pe.LoadConfig.Struct)\n\tvar GFIDS []CFGFunction\n\tvar err error\n\n\t// The GFIDS table is an array of 4 + n bytes, where n is given by :\n\t// ((GuardFlags & IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK) >>\n\t// IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT).\n\n\t// This allows for extra metadata to be attached to CFG call targets in\n\t// the future. The only currently defined metadata is an optional 1-byte\n\t// extra flags field (“GFIDS flags”) that is attached to each GFIDS\n\t// entry if any call targets have metadata.\n\tGuardFlags := v.Field(24).Uint()\n\tn := (GuardFlags & ImageGuardCfFunctionTableSizeMask) >>\n\t\tImageGuardCfFunctionTableSizeShift\n\tGuardCFFunctionCount := v.Field(23).Uint()\n\tif GuardCFFunctionCount > 0 {\n\t\tif pe.Is32 {\n\t\t\tGuardCFFunctionTable := uint32(v.Field(22).Uint())\n\t\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).ImageBase\n\t\t\trva := GuardCFFunctionTable - imageBase\n\t\t\toffset := pe.GetOffsetFromRva(rva)\n\t\t\tfor i := uint32(1); i <= uint32(GuardCFFunctionCount); i++ {\n\t\t\t\tcfgFunction := CFGFunction{}\n\t\t\t\tvar cfgFlags uint8\n\t\t\t\tcfgFunction.RVA, err = pe.ReadUint32(offset)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn GFIDS\n\t\t\t\t}\n\t\t\t\tif n > 0 {\n\t\t\t\t\terr = pe.structUnpack(&cfgFlags, offset+4, uint32(n))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn GFIDS\n\t\t\t\t\t}\n\t\t\t\t\tcfgFunction.Flags = ImageGuardFlagType(cfgFlags)\n\t\t\t\t\tif cfgFlags == ImageGuardFlagFIDSuppressed ||\n\t\t\t\t\t\tcfgFlags == ImageGuardFlagExportSuppressed {\n\t\t\t\t\t\texportName := pe.GetExportFunctionByRVA(cfgFunction.RVA)\n\t\t\t\t\t\tcfgFunction.Description = exportName.Name\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tGFIDS = append(GFIDS, cfgFunction)\n\t\t\t\toffset += 4 + uint32(n)\n\t\t\t}\n\t\t} else {\n\t\t\tGuardCFFunctionTable := v.Field(22).Uint()\n\t\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).ImageBase\n\t\t\trva := uint32(GuardCFFunctionTable - imageBase)\n\t\t\toffset := pe.GetOffsetFromRva(rva)\n\t\t\tfor i := uint64(1); i <= GuardCFFunctionCount; i++ {\n\t\t\t\tvar cfgFlags uint8\n\t\t\t\tcfgFunction := CFGFunction{}\n\t\t\t\tcfgFunction.RVA, err = pe.ReadUint32(offset)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn GFIDS\n\t\t\t\t}\n\t\t\t\tif n > 0 {\n\t\t\t\t\tpe.structUnpack(&cfgFlags, offset+4, uint32(n))\n\t\t\t\t\tcfgFunction.Flags = ImageGuardFlagType(cfgFlags)\n\t\t\t\t\tif cfgFlags == ImageGuardFlagFIDSuppressed ||\n\t\t\t\t\t\tcfgFlags == ImageGuardFlagExportSuppressed {\n\t\t\t\t\t\texportName := pe.GetExportFunctionByRVA(cfgFunction.RVA)\n\t\t\t\t\t\tcfgFunction.Description = exportName.Name\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tGFIDS = append(GFIDS, cfgFunction)\n\t\t\t\toffset += 4 + uint32(n)\n\t\t\t}\n\t\t}\n\t}\n\treturn GFIDS\n}\n\nfunc (pe *File) getControlFlowGuardIAT() []CFGIATEntry {\n\n\tv := reflect.ValueOf(pe.LoadConfig.Struct)\n\tvar GFGIAT []CFGIATEntry\n\tvar err error\n\n\t// GuardAddressTakenIatEntryCount is found in index 27 of the struct.\n\t// An image that supports CFG ES includes a GuardAddressTakenIatEntryTable\n\t// whose count is provided by the GuardAddressTakenIatEntryCount as part\n\t// of its load configuration directory. This table is structurally\n\t// formatted the same as the GFIDS table. It uses the same GuardFlags\n\t// IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK mechanism to encode extra\n\t// optional metadata bytes in the address taken IAT table, though all\n\t// metadata bytes must be zero for the address taken IAT table and are\n\t// reserved.\n\tGuardFlags := v.Field(24).Uint()\n\tn := (GuardFlags & ImageGuardCfFunctionTableSizeMask) >>\n\t\tImageGuardCfFunctionTableSizeShift\n\tGuardAddressTakenIatEntryCount := v.Field(27).Uint()\n\tif GuardAddressTakenIatEntryCount > 0 {\n\t\tif pe.Is32 {\n\t\t\tGuardAddressTakenIatEntryTable := uint32(v.Field(26).Uint())\n\t\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).ImageBase\n\t\t\trva := GuardAddressTakenIatEntryTable - imageBase\n\t\t\toffset := pe.GetOffsetFromRva(rva)\n\t\t\tfor i := uint32(1); i <= uint32(GuardAddressTakenIatEntryCount); i++ {\n\t\t\t\tcfgIATEntry := CFGIATEntry{}\n\t\t\t\tcfgIATEntry.RVA, err = pe.ReadUint32(offset)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn GFGIAT\n\t\t\t\t}\n\t\t\t\timp, index := pe.GetImportEntryInfoByRVA(cfgIATEntry.RVA)\n\t\t\t\tif len(imp.Functions) != 0 {\n\t\t\t\t\tcfgIATEntry.INTValue = uint32(imp.Functions[index].OriginalThunkValue)\n\t\t\t\t\tcfgIATEntry.IATValue = uint32(imp.Functions[index].ThunkValue)\n\t\t\t\t\tcfgIATEntry.Description = imp.Name + \"!\" + imp.Functions[index].Name\n\t\t\t\t}\n\t\t\t\tGFGIAT = append(GFGIAT, cfgIATEntry)\n\t\t\t\toffset += 4 + uint32(n)\n\t\t\t}\n\t\t} else {\n\t\t\tGuardAddressTakenIatEntryTable := v.Field(26).Uint()\n\t\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).ImageBase\n\t\t\trva := uint32(GuardAddressTakenIatEntryTable - imageBase)\n\t\t\toffset := pe.GetOffsetFromRva(rva)\n\t\t\tfor i := uint64(1); i <= GuardAddressTakenIatEntryCount; i++ {\n\t\t\t\tcfgIATEntry := CFGIATEntry{}\n\t\t\t\tcfgIATEntry.RVA, err = pe.ReadUint32(offset)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn GFGIAT\n\t\t\t\t}\n\t\t\t\timp, index := pe.GetImportEntryInfoByRVA(cfgIATEntry.RVA)\n\t\t\t\tif len(imp.Functions) != 0 {\n\t\t\t\t\tcfgIATEntry.INTValue = uint32(imp.Functions[index].OriginalThunkValue)\n\t\t\t\t\tcfgIATEntry.IATValue = uint32(imp.Functions[index].ThunkValue)\n\t\t\t\t\tcfgIATEntry.Description = imp.Name + \"!\" + imp.Functions[index].Name\n\t\t\t\t}\n\n\t\t\t\tGFGIAT = append(GFGIAT, cfgIATEntry)\n\t\t\t\toffset += 4 + uint32(n)\n\t\t\t}\n\t\t}\n\n\t}\n\treturn GFGIAT\n}\n\nfunc (pe *File) getLongJumpTargetTable() []uint32 {\n\n\tv := reflect.ValueOf(pe.LoadConfig.Struct)\n\tvar longJumpTargets []uint32\n\n\t// The long jump table represents a sorted array of RVAs that are valid\n\t// long jump targets. If a long jump target module sets\n\t// IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT in its GuardFlags field, then\n\t// all long jump targets must be enumerated in the LongJumpTargetTable.\n\tGuardFlags := v.Field(24).Uint()\n\tn := (GuardFlags & ImageGuardCfFunctionTableSizeMask) >>\n\t\tImageGuardCfFunctionTableSizeShift\n\n\t// GuardLongJumpTargetCount is found in index 29 of the struct.\n\tGuardLongJumpTargetCount := v.Field(29).Uint()\n\tif GuardLongJumpTargetCount > 0 {\n\t\tif pe.Is32 {\n\t\t\tGuardLongJumpTargetTable := uint32(v.Field(28).Uint())\n\t\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).ImageBase\n\t\t\trva := GuardLongJumpTargetTable - imageBase\n\t\t\toffset := pe.GetOffsetFromRva(rva)\n\t\t\tfor i := uint32(1); i <= uint32(GuardLongJumpTargetCount); i++ {\n\t\t\t\ttarget, err := pe.ReadUint32(offset)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn longJumpTargets\n\t\t\t\t}\n\t\t\t\tlongJumpTargets = append(longJumpTargets, target)\n\t\t\t\toffset += 4 + uint32(n)\n\t\t\t}\n\t\t} else {\n\t\t\tGuardLongJumpTargetTable := v.Field(28).Uint()\n\t\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).ImageBase\n\t\t\trva := uint32(GuardLongJumpTargetTable - imageBase)\n\t\t\toffset := pe.GetOffsetFromRva(rva)\n\t\t\tfor i := uint64(1); i <= GuardLongJumpTargetCount; i++ {\n\t\t\t\ttarget, err := pe.ReadUint32(offset)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn longJumpTargets\n\t\t\t\t}\n\t\t\t\tlongJumpTargets = append(longJumpTargets, target)\n\t\t\t\toffset += 4 + uint32(n)\n\t\t\t}\n\t\t}\n\n\t}\n\treturn longJumpTargets\n}\n\nfunc (pe *File) getHybridPE() *HybridPE {\n\tv := reflect.ValueOf(pe.LoadConfig.Struct)\n\n\t// CHPEMetadataPointer is found in index 31 of the struct.\n\tCHPEMetadataPointer := v.Field(31).Uint()\n\tif CHPEMetadataPointer == 0 {\n\t\treturn nil\n\t}\n\tvar rva uint32\n\tif pe.Is32 {\n\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).ImageBase\n\t\trva = uint32(CHPEMetadataPointer) - imageBase\n\t} else {\n\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).ImageBase\n\t\trva = uint32(CHPEMetadataPointer - imageBase)\n\t}\n\n\t// As the image CHPE metadata structure changes over time,\n\t// we first read its version to figure out which one we have to\n\t// cast against.\n\tfileOffset := pe.GetOffsetFromRva(rva)\n\tversion, err := pe.ReadUint32(fileOffset)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tstructSize := uint32(0)\n\timgCHPEMetaX86 := ImageCHPEMetadataX86{}\n\n\tswitch version {\n\tcase 0x1:\n\t\tstructSize = uint32(binary.Size(imgCHPEMetaX86) - 8)\n\tcase 0x2:\n\t\tstructSize = uint32(binary.Size(imgCHPEMetaX86) - 4)\n\tcase 0x3:\n\t\tstructSize = uint32(binary.Size(imgCHPEMetaX86))\n\tdefault:\n\t\t// This should be a newer version, default to the latest CHPE version.\n\t\tstructSize = uint32(binary.Size(imgCHPEMetaX86))\n\t}\n\n\t// Boundary check\n\ttotalSize := fileOffset + structSize\n\n\t// Integer overflow\n\tif (totalSize > fileOffset) != (structSize > 0) {\n\t\tpe.logger.Debug(\"encountered an outside read boundary when reading CHPE structure\")\n\t\treturn nil\n\t}\n\n\tif fileOffset >= pe.size || totalSize > pe.size {\n\t\tpe.logger.Debug(\"encountered an outside read boundary when reading CHPE structure\")\n\t\treturn nil\n\t}\n\n\timgCHPEMeta := make([]byte, binary.Size(imgCHPEMetaX86))\n\tcopy(imgCHPEMeta, pe.data[fileOffset:fileOffset+structSize])\n\tbuf := bytes.NewReader(imgCHPEMeta)\n\terr = binary.Read(buf, binary.LittleEndian, &imgCHPEMetaX86)\n\tif err != nil {\n\t\tpe.logger.Debug(\"encountered an error while unpacking image CHPE Meta\")\n\t\treturn nil\n\t}\n\n\thybridPE := HybridPE{}\n\thybridPE.CHPEMetadata = imgCHPEMetaX86\n\n\t// Code Ranges\n\n\t/*\n\t\ttypedef struct _IMAGE_CHPE_RANGE_ENTRY {\n\t\t\tunion {\n\t\t\t\tULONG StartOffset;\n\t\t\t\tstruct {\n\t\t\t\t\tULONG NativeCode : 1;\n\t\t\t\t\tULONG AddressBits : 31;\n\t\t\t\t} DUMMYSTRUCTNAME;\n\t\t\t} DUMMYUNIONNAME;\n\n\t\t\tULONG Length;\n\t\t} IMAGE_CHPE_RANGE_ENTRY, *PIMAGE_CHPE_RANGE_ENTRY;\n\t*/\n\n\trva = imgCHPEMetaX86.CHPECodeAddressRangeOffset\n\tfor i := 0; i < int(imgCHPEMetaX86.CHPECodeAddressRangeCount); i++ {\n\n\t\tcodeRange := CodeRange{}\n\t\tfileOffset := pe.GetOffsetFromRva(rva)\n\t\tbegin, err := pe.ReadUint32(fileOffset)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\n\t\tif begin&1 == 1 {\n\t\t\tcodeRange.Machine = 1\n\t\t\tbegin = uint32(int(begin) & ^1)\n\t\t}\n\t\tcodeRange.Begin = begin\n\n\t\tfileOffset += 4\n\t\tsize, err := pe.ReadUint32(fileOffset)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tcodeRange.Length = size\n\n\t\thybridPE.CodeRanges = append(hybridPE.CodeRanges, codeRange)\n\t\trva += 8\n\t}\n\n\t// Compiler IAT\n\tif imgCHPEMetaX86.CompilerIATPointer != 0 {\n\t\trva := imgCHPEMetaX86.CompilerIATPointer\n\t\tfor i := 0; i < 1024; i++ {\n\t\t\tcompilerIAT := CompilerIAT{}\n\t\t\tcompilerIAT.RVA = rva\n\t\t\tfileOffset = pe.GetOffsetFromRva(rva)\n\t\t\tcompilerIAT.Value, err = pe.ReadUint32(fileOffset)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\timpFunc, _ := pe.GetImportEntryInfoByRVA(compilerIAT.RVA)\n\t\t\tcompilerIAT.Description = impFunc.Name\n\t\t\thybridPE.CompilerIAT = append(\n\t\t\t\thybridPE.CompilerIAT, compilerIAT)\n\t\t\trva += 4\n\t\t}\n\t}\n\treturn &hybridPE\n}\n\nfunc (pe *File) getDynamicValueRelocTable() *DVRT {\n\n\tvar structSize uint32\n\tvar imgDynRelocSize uint32\n\tvar retpolineType uint8\n\tdvrt := DVRT{}\n\timgDynRelocTable := ImageDynamicRelocationTable{}\n\n\tv := reflect.ValueOf(pe.LoadConfig.Struct)\n\tDynamicValueRelocTableOffset := v.Field(34).Uint()\n\tDynamicValueRelocTableSection := v.Field(35).Uint()\n\tif DynamicValueRelocTableOffset == 0 || DynamicValueRelocTableSection == 0 {\n\t\treturn nil\n\t}\n\n\tsection := pe.getSectionByName(\".reloc\")\n\tif section == nil {\n\t\treturn nil\n\t}\n\n\t// Get the dynamic value relocation table header.\n\trva := section.VirtualAddress + uint32(DynamicValueRelocTableOffset)\n\toffset := pe.GetOffsetFromRva(rva)\n\tstructSize = uint32(binary.Size(imgDynRelocTable))\n\terr := pe.structUnpack(&imgDynRelocTable, offset, structSize)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tdvrt.ImageDynamicRelocationTable = imgDynRelocTable\n\toffset += structSize\n\n\t// Get dynamic relocation entries according to version.\n\tswitch imgDynRelocTable.Version {\n\tcase 1:\n\t\trelocTableIt := uint32(0)\n\t\tbaseBlockSize := uint32(0)\n\n\t\t// Iterate over our dynamic reloc table entries.\n\t\tfor relocTableIt < imgDynRelocTable.Size {\n\n\t\t\trelocEntry := RelocEntry{}\n\n\t\t\t// Each block starts with the header.\n\t\t\tif pe.Is32 {\n\t\t\t\timgDynReloc := ImageDynamicRelocation32{}\n\t\t\t\timgDynRelocSize = uint32(binary.Size(imgDynReloc))\n\t\t\t\terr = pe.structUnpack(&imgDynReloc, offset, imgDynRelocSize)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\trelocEntry.ImageDynamicRelocation = imgDynReloc\n\t\t\t\tbaseBlockSize = imgDynReloc.BaseRelocSize\n\t\t\t\tretpolineType = uint8(imgDynReloc.Symbol)\n\t\t\t} else {\n\t\t\t\timgDynReloc := ImageDynamicRelocation64{}\n\t\t\t\timgDynRelocSize = uint32(binary.Size(imgDynReloc))\n\t\t\t\terr = pe.structUnpack(&imgDynReloc, offset, imgDynRelocSize)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\trelocEntry.ImageDynamicRelocation = imgDynReloc\n\t\t\t\tbaseBlockSize = imgDynReloc.BaseRelocSize\n\t\t\t\tretpolineType = uint8(imgDynReloc.Symbol)\n\t\t\t}\n\t\t\toffset += imgDynRelocSize\n\t\t\trelocTableIt += imgDynRelocSize\n\n\t\t\t// Then, for each page, there is a block that starts with a relocation entry:\n\t\t\tblockIt := uint32(0)\n\t\t\tfor blockIt <= baseBlockSize-imgDynRelocSize {\n\t\t\t\trelocBlock := RelocBlock{}\n\n\t\t\t\tbaseReloc := ImageBaseRelocation{}\n\t\t\t\tstructSize = uint32(binary.Size(baseReloc))\n\t\t\t\terr = pe.structUnpack(&baseReloc, offset, structSize)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\trelocBlock.ImgBaseReloc = baseReloc\n\t\t\t\toffset += structSize\n\n\t\t\t\t// After that there are entries for all of the places which need\n\t\t\t\t// to be overwritten by the retpoline jump. The structure used\n\t\t\t\t// for those entries depends on the type (symbol) that was used\n\t\t\t\t// above. There are three types of retpoline so far. Entry for\n\t\t\t\t//each of them will contain pageRelativeOffset. The kernel uses\n\t\t\t\t// that entry to apply the proper replacement under\n\t\t\t\t// virtualAddress + pageRelativeOffset address.\n\t\t\t\tbranchIt := uint32(0)\n\t\t\t\tswitch retpolineType {\n\t\t\t\tcase 3:\n\t\t\t\t\tfor branchIt < (baseReloc.SizeOfBlock-structSize)/4 {\n\t\t\t\t\t\timgImpCtrlTransDynReloc := ImageImportControlTransferDynamicRelocation{}\n\n\t\t\t\t\t\tdword, err := pe.ReadUint32(offset)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\timgImpCtrlTransDynReloc.PageRelativeOffset = uint16(dword) & 0xfff\n\t\t\t\t\t\timgImpCtrlTransDynReloc.IndirectCall = uint16(dword) & 0x1000 >> 12\n\t\t\t\t\t\timgImpCtrlTransDynReloc.IATIndex = dword & 0xFFFFE000 >> 13\n\n\t\t\t\t\t\toffset += 4\n\t\t\t\t\t\tbranchIt += 1\n\t\t\t\t\t\trelocBlock.TypeOffsets = append(relocBlock.TypeOffsets, imgImpCtrlTransDynReloc)\n\t\t\t\t\t}\n\t\t\t\tcase 4:\n\t\t\t\t\tfor branchIt < (baseReloc.SizeOfBlock-structSize)/2 {\n\t\t\t\t\t\timgIndirCtrlTransDynReloc := ImageIndirectControlTransferDynamicRelocation{}\n\n\t\t\t\t\t\tword, err := pe.ReadUint16(offset)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}\n\t\t\t\t\t\timgIndirCtrlTransDynReloc.PageRelativeOffset = word & 0xfff\n\t\t\t\t\t\timgIndirCtrlTransDynReloc.IndirectCall = uint8(word & 0x1000 >> 12)\n\t\t\t\t\t\timgIndirCtrlTransDynReloc.RexWPrefix = uint8(word & 0x2000 >> 13)\n\t\t\t\t\t\timgIndirCtrlTransDynReloc.CfgCheck = uint8(word & 0x4000 >> 14)\n\t\t\t\t\t\timgIndirCtrlTransDynReloc.Reserved = uint8(word & 0x8000 >> 15)\n\n\t\t\t\t\t\tbranchIt += 1\n\t\t\t\t\t\toffset += 2\n\n\t\t\t\t\t\t// Padding might be added at the end of the block.\n\t\t\t\t\t\tif (ImageIndirectControlTransferDynamicRelocation{}) == imgIndirCtrlTransDynReloc {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\trelocBlock.TypeOffsets = append(relocBlock.TypeOffsets, imgIndirCtrlTransDynReloc)\n\t\t\t\t\t}\n\t\t\t\tcase 5:\n\t\t\t\t\tfor branchIt < (baseReloc.SizeOfBlock-structSize)/2 {\n\t\t\t\t\t\timgSwitchBranchDynReloc := ImageSwitchableBranchDynamicRelocation{}\n\n\t\t\t\t\t\tword, err := pe.ReadUint16(offset)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn nil\n\t\t\t\t\t\t}\n\t\t\t\t\t\timgSwitchBranchDynReloc.PageRelativeOffset = word & 0xfff\n\t\t\t\t\t\timgSwitchBranchDynReloc.RegisterNumber = word & 0xf000 >> 12\n\n\t\t\t\t\t\toffset += 2\n\t\t\t\t\t\tbranchIt += 1\n\n\t\t\t\t\t\t// Padding might be added at the end of the block.\n\t\t\t\t\t\tif (ImageSwitchableBranchDynamicRelocation{}) == imgSwitchBranchDynReloc {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\t\t\t\t\t\trelocBlock.TypeOffsets = append(relocBlock.TypeOffsets, imgSwitchBranchDynReloc)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tblockIt += baseReloc.SizeOfBlock\n\t\t\t\trelocEntry.RelocBlocks = append(relocEntry.RelocBlocks, relocBlock)\n\t\t\t}\n\n\t\t\tdvrt.Entries = append(dvrt.Entries, relocEntry)\n\t\t\trelocTableIt += baseBlockSize\n\t\t}\n\tcase 2:\n\t\tfmt.Print(\"Got version 2 !\")\n\t}\n\n\treturn &dvrt\n}\n\nfunc (pe *File) getEnclaveConfiguration() *Enclave {\n\n\tenclave := Enclave{}\n\n\tv := reflect.ValueOf(pe.LoadConfig.Struct)\n\tEnclaveConfigurationPointer := v.Field(40).Uint()\n\tif EnclaveConfigurationPointer == 0 {\n\t\treturn nil\n\t}\n\n\tif pe.Is32 {\n\t\timgEnclaveCfg := ImageEnclaveConfig32{}\n\t\timgEnclaveCfgSize := uint32(binary.Size(imgEnclaveCfg))\n\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).ImageBase\n\t\trva := uint32(EnclaveConfigurationPointer) - imageBase\n\t\toffset := pe.GetOffsetFromRva(rva)\n\t\terr := pe.structUnpack(&imgEnclaveCfg, offset, imgEnclaveCfgSize)\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\t\tenclave.Config = imgEnclaveCfg\n\t} else {\n\t\timgEnclaveCfg := ImageEnclaveConfig64{}\n\t\timgEnclaveCfgSize := uint32(binary.Size(imgEnclaveCfg))\n\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).ImageBase\n\t\trva := uint32(EnclaveConfigurationPointer - imageBase)\n\t\toffset := pe.GetOffsetFromRva(rva)\n\t\terr := pe.structUnpack(&imgEnclaveCfg, offset, imgEnclaveCfgSize)\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\t\tenclave.Config = imgEnclaveCfg\n\t}\n\n\t// Get the array of images that an enclave can import.\n\tval := reflect.ValueOf(enclave.Config)\n\tImportListRVA := val.FieldByName(\"ImportList\").Interface().(uint32)\n\tNumberOfImports := val.FieldByName(\"NumberOfImports\").Interface().(uint32)\n\tImportEntrySize := val.FieldByName(\"ImportEntrySize\").Interface().(uint32)\n\n\toffset := pe.GetOffsetFromRva(ImportListRVA)\n\tfor i := uint32(0); i < NumberOfImports; i++ {\n\t\timgEncImp := ImageEnclaveImport{}\n\t\timgEncImpSize := uint32(binary.Size(imgEncImp))\n\t\terr := pe.structUnpack(&imgEncImp, offset, imgEncImpSize)\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\n\t\toffset += ImportEntrySize\n\t\tenclave.Imports = append(enclave.Imports, imgEncImp)\n\t}\n\n\treturn &enclave\n}\n\nfunc (pe *File) getVolatileMetadata() *VolatileMetadata {\n\n\tvolatileMeta := VolatileMetadata{}\n\timgVolatileMeta := ImageVolatileMetadata{}\n\trva := uint32(0)\n\n\tv := reflect.ValueOf(pe.LoadConfig.Struct)\n\tif v.NumField() <= 41 {\n\t\treturn nil\n\t}\n\n\tVolatileMetadataPointer := v.Field(41).Uint()\n\tif VolatileMetadataPointer == 0 {\n\t\treturn nil\n\t}\n\n\tif pe.Is32 {\n\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).ImageBase\n\t\trva = uint32(VolatileMetadataPointer) - imageBase\n\t} else {\n\t\timageBase := pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).ImageBase\n\t\trva = uint32(VolatileMetadataPointer - imageBase)\n\t}\n\n\toffset := pe.GetOffsetFromRva(rva)\n\timgVolatileMetaSize := uint32(binary.Size(imgVolatileMeta))\n\terr := pe.structUnpack(&imgVolatileMeta, offset, imgVolatileMetaSize)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tvolatileMeta.Struct = imgVolatileMeta\n\n\tif imgVolatileMeta.VolatileAccessTable != 0 &&\n\t\timgVolatileMeta.VolatileAccessTableSize != 0 {\n\t\toffset := pe.GetOffsetFromRva(imgVolatileMeta.VolatileAccessTable)\n\t\tfor i := uint32(0); i < imgVolatileMeta.VolatileAccessTableSize/4; i++ {\n\t\t\taccessRVA, err := pe.ReadUint32(offset)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tvolatileMeta.AccessRVATable = append(volatileMeta.AccessRVATable, accessRVA)\n\t\t\toffset += 4\n\t\t}\n\t}\n\n\tif imgVolatileMeta.VolatileInfoRangeTable != 0 && imgVolatileMeta.VolatileInfoRangeTableSize != 0 {\n\t\toffset := pe.GetOffsetFromRva(imgVolatileMeta.VolatileInfoRangeTable)\n\t\trangeEntrySize := uint32(binary.Size(RangeTableEntry{}))\n\t\tfor i := uint32(0); i < imgVolatileMeta.VolatileInfoRangeTableSize/rangeEntrySize; i++ {\n\t\t\tentry := RangeTableEntry{}\n\t\t\terr := pe.structUnpack(&entry, offset, rangeEntrySize)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tvolatileMeta.InfoRangeTable = append(volatileMeta.InfoRangeTable, entry)\n\t\t\toffset += rangeEntrySize\n\t\t}\n\t}\n\n\treturn &volatileMeta\n}\n\n// String returns a string interpretation of the load config directory image\n// guard flag.\nfunc (flag ImageGuardFlagType) String() string {\n\timageGuardFlagTypeMap := map[ImageGuardFlagType]string{\n\t\tImageGuardFlagFIDSuppressed:    \"FID Suppressed\",\n\t\tImageGuardFlagExportSuppressed: \"Export Suppressed\",\n\t}\n\n\tv, ok := imageGuardFlagTypeMap[flag]\n\tif ok {\n\t\treturn v\n\t}\n\n\treturn \"?\"\n}\n"
  },
  {
    "path": "loadconfig_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\r\n// Use of this source code is governed by Apache v2 license\r\n// license that can be found in the LICENSE file.\r\n\r\npackage pe\r\n\r\nimport (\r\n\t\"reflect\"\r\n\t\"testing\"\r\n)\r\n\r\nfunc TestLoadConfigDirectory(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout interface{}\r\n\t}{\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/pspluginwkr.dll\"),\r\n\t\t\tout: ImageLoadConfigDirectory32{\r\n\t\t\t\tSize:           0x48,\r\n\t\t\t\tSecurityCookie: 0x45e44220,\r\n\t\t\t\tSEHandlerTable: 0x45e382e0,\r\n\t\t\t\tSEHandlerCount: 0x1,\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/00da1a2a9d9ebf447508bf6550f05f466f8eabb4ed6c4f2a524c0769b2d75bc1\"),\r\n\t\t\tout: ImageLoadConfigDirectory32{\r\n\t\t\t\tSize:                        0x5c,\r\n\t\t\t\tSecurityCookie:              0x43D668,\r\n\t\t\t\tSEHandlerTable:              0x439C70,\r\n\t\t\t\tSEHandlerCount:              0x25,\r\n\t\t\t\tGuardCFCheckFunctionPointer: 0x432260,\r\n\t\t\t\tGuardCFFunctionTable:        0x4322D4,\r\n\t\t\t\tGuardCFFunctionCount:        0x90,\r\n\t\t\t\tGuardFlags:                  0x10013500,\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/3a081c7fe475ec68ed155c76d30cfddc4d41f7a09169810682d1c75421e98eaa\"),\r\n\t\t\tout: ImageLoadConfigDirectory32{\r\n\t\t\t\tSize:                        0xa0,\r\n\t\t\t\tSecurityCookie:              0x417008,\r\n\t\t\t\tSEHandlerTable:              0x415410,\r\n\t\t\t\tSEHandlerCount:              0x2,\r\n\t\t\t\tGuardCFCheckFunctionPointer: 0x40e384,\r\n\t\t\t\tGuardFlags:                  0x100,\r\n\t\t\t},\r\n\t\t},\r\n\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/IEAdvpack.dll\"),\r\n\t\t\tout: ImageLoadConfigDirectory32{\r\n\t\t\t\tSize:                           0xa4,\r\n\t\t\t\tSecurityCookie:                 0x6501b074,\r\n\t\t\t\tSEHandlerTable:                 0x650046d0,\r\n\t\t\t\tSEHandlerCount:                 0x1,\r\n\t\t\t\tGuardCFCheckFunctionPointer:    0x6502937c,\r\n\t\t\t\tGuardCFFunctionTable:           0x650010f0,\r\n\t\t\t\tGuardCFFunctionCount:           0x55,\r\n\t\t\t\tGuardFlags:                     0x10017500,\r\n\t\t\t\tGuardAddressTakenIATEntryTable: 0x6500129c,\r\n\t\t\t\tGuardAddressTakenIATEntryCount: 0x1,\r\n\t\t\t\tGuardLongJumpTargetTable:       0x650012a4,\r\n\t\t\t\tGuardLongJumpTargetCount:       0x2,\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/KernelBase.dll\"),\r\n\t\t\tout: ImageLoadConfigDirectory32{\r\n\t\t\t\tSize:                           0xb8,\r\n\t\t\t\tDependentLoadFlags:             0x800,\r\n\t\t\t\tSecurityCookie:                 0x101f3b50,\r\n\t\t\t\tSEHandlerTable:                 0x10090c40,\r\n\t\t\t\tSEHandlerCount:                 0x3,\r\n\t\t\t\tGuardCFCheckFunctionPointer:    0x101f7b08,\r\n\t\t\t\tGuardCFFunctionTable:           0x1005ab70,\r\n\t\t\t\tGuardCFFunctionCount:           0xc4a,\r\n\t\t\t\tGuardFlags:                     0x10017500,\r\n\t\t\t\tGuardAddressTakenIATEntryTable: 0x1005e8e4,\r\n\t\t\t\tGuardAddressTakenIATEntryCount: 0xa,\r\n\t\t\t\tVolatileMetadataPointer:        0x10090c4c,\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/WdfCoInstaller01011.dll\"),\r\n\t\t\tout: ImageLoadConfigDirectory64{\r\n\t\t\t\tSize:           0x70,\r\n\t\t\t\tSecurityCookie: 0x18000f108,\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/D2D1Debug2.dll\"),\r\n\t\t\tout: ImageLoadConfigDirectory64{\r\n\t\t\t\tSize:                        0x94,\r\n\t\t\t\tSecurityCookie:              0x180061008,\r\n\t\t\t\tGuardCFCheckFunctionPointer: 0x180001000,\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/amdxata.sys\"),\r\n\t\t\tout: ImageLoadConfigDirectory64{\r\n\t\t\t\tSize:                           0xa0,\r\n\t\t\t\tSecurityCookie:                 0x1c00030b0,\r\n\t\t\t\tGuardCFCheckFunctionPointer:    0x1c0005160,\r\n\t\t\t\tGuardCFDispatchFunctionPointer: 0x1c0005168,\r\n\t\t\t\tGuardCFFunctionTable:           0x1c0009000,\r\n\t\t\t\tGuardCFFunctionCount:           0x17,\r\n\t\t\t\tGuardFlags:                     0x500,\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/amdi2c.sys\"),\r\n\t\t\tout: ImageLoadConfigDirectory64{\r\n\t\t\t\tSize:                           0xd0,\r\n\t\t\t\tSecurityCookie:                 0x140009090,\r\n\t\t\t\tGuardCFCheckFunctionPointer:    0x140008100,\r\n\t\t\t\tGuardCFDispatchFunctionPointer: 0x140008108,\r\n\t\t\t\tGuardFlags:                     0x100,\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/brave.exe\"),\r\n\t\t\tout: ImageLoadConfigDirectory64{\r\n\t\t\t\tSize:                           0x100,\r\n\t\t\t\tSecurityCookie:                 0x14017b648,\r\n\t\t\t\tGuardCFCheckFunctionPointer:    0x140191000,\r\n\t\t\t\tGuardCFDispatchFunctionPointer: 0x140191008,\r\n\t\t\t\tGuardCFFunctionTable:           0x14016b627,\r\n\t\t\t\tGuardCFFunctionCount:           0x561,\r\n\t\t\t\tGuardFlags:                     0x500,\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/shimeng.dll\"),\r\n\t\t\tout: ImageLoadConfigDirectory64{\r\n\t\t\t\tSize:                           0x108,\r\n\t\t\t\tSecurityCookie:                 0x180003000,\r\n\t\t\t\tGuardCFCheckFunctionPointer:    0x180002188,\r\n\t\t\t\tGuardCFDispatchFunctionPointer: 0x180002190,\r\n\t\t\t\tGuardCFFunctionTable:           0x180002198,\r\n\t\t\t\tGuardCFFunctionCount:           0x3,\r\n\t\t\t\tGuardFlags:                     0x17500,\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/kernel32.dll\"),\r\n\t\t\tout: ImageLoadConfigDirectory64{\r\n\t\t\t\tSize:                           0x118,\r\n\t\t\t\tSecurityCookie:                 0x1800b3220,\r\n\t\t\t\tGuardCFCheckFunctionPointer:    0x180084218,\r\n\t\t\t\tGuardCFDispatchFunctionPointer: 0x180084220,\r\n\t\t\t\tGuardCFFunctionTable:           0x180084388,\r\n\t\t\t\tGuardCFFunctionCount:           0x5e6,\r\n\t\t\t\tGuardFlags:                     0x10417500,\r\n\t\t\t\tGuardAddressTakenIATEntryTable: 0x180086108,\r\n\t\t\t\tGuardAddressTakenIATEntryCount: 0x3,\r\n\t\t\t\tGuardEHContinuationTable:       0x180084228,\r\n\t\t\t\tGuardEHContinuationCount:       0x46,\r\n\t\t\t},\r\n\t\t},\r\n\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseLoadConfigDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseLoadConfigDirectory(%s) failed, reason: %v\",\r\n\t\t\t\t\ttt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\timgLoadCfgDirectory := file.LoadConfig.Struct\r\n\t\t\tif imgLoadCfgDirectory != tt.out {\r\n\t\t\t\tt.Fatalf(\"load config directory structure assertion failed, got %v, want %v\",\r\n\t\t\t\t\timgLoadCfgDirectory, tt.out)\r\n\t\t\t}\r\n\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestLoadConfigDirectorySEHHandlers(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout []uint32\r\n\t}{\r\n\t\t{\r\n\t\t\tin:  getAbsoluteFilePath(\"test/KernelBase.dll\"),\r\n\t\t\tout: []uint32{0x14ad30, 0x14af40, 0x14b0d0},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseLoadConfigDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseLoadConfigDirectory(%s) failed, reason: %v\",\r\n\t\t\t\t\ttt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tsehHandlers := file.LoadConfig.SEH\r\n\t\t\tif !reflect.DeepEqual(sehHandlers, tt.out) {\r\n\t\t\t\tt.Fatalf(\"load config SEH handlers assertion failed, got %v, want %v\",\r\n\t\t\t\t\tsehHandlers, tt.out)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestLoadConfigDirectoryControlFlowGuardFunctions(t *testing.T) {\r\n\r\n\ttype TestGFIDSEntry struct {\r\n\t\tentriesCount int\r\n\t\tentryIndex   int\r\n\t\tCFGFunction  CFGFunction\r\n\t}\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout TestGFIDSEntry\r\n\t}{\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/KernelBase.dll\"),\r\n\t\t\tout: TestGFIDSEntry{\r\n\t\t\t\tentriesCount: 0xc4a,\r\n\t\t\t\tentryIndex:   0x1,\r\n\t\t\t\tCFGFunction: CFGFunction{\r\n\t\t\t\t\tRVA:         0xfe2a0,\r\n\t\t\t\t\tFlags:       ImageGuardFlagExportSuppressed,\r\n\t\t\t\t\tDescription: \"GetCalendarInfoEx\",\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/kernel32.dll\"),\r\n\t\t\tout: TestGFIDSEntry{\r\n\t\t\t\tentriesCount: 0x5e6,\r\n\t\t\t\tentryIndex:   0x5d3,\r\n\t\t\t\tCFGFunction: CFGFunction{\r\n\t\t\t\t\tRVA:         0x71390,\r\n\t\t\t\t\tFlags:       ImageGuardFlagExportSuppressed,\r\n\t\t\t\t\tDescription: \"QuirkIsEnabledForPackage2Worker\",\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\r\n\t\t\tops := Options{Fast: false}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseLoadConfigDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseLoadConfigDirectory(%s) failed, reason: %v\",\r\n\t\t\t\t\ttt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tgfids := file.LoadConfig.GFIDS\r\n\t\t\tif len(gfids) != tt.out.entriesCount {\r\n\t\t\t\tt.Fatalf(\"load config GFIDS entries count assert failed, got %v, want %v\",\r\n\t\t\t\t\tlen(gfids), tt.out.entriesCount)\r\n\t\t\t}\r\n\r\n\t\t\tguardedFunction := gfids[tt.out.entryIndex]\r\n\t\t\tif !reflect.DeepEqual(guardedFunction, tt.out.CFGFunction) {\r\n\t\t\t\tt.Fatalf(\"load config GFIDS entry assertion failed, got %v, want %v\",\r\n\t\t\t\t\tguardedFunction, tt.out.CFGFunction)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestLoadConfigDirectoryControlFlowGuardIAT(t *testing.T) {\r\n\r\n\ttype TestGFIDSEntry struct {\r\n\t\tentriesCount int\r\n\t\tentryIndex   int\r\n\t\tCFGFunction  CFGIATEntry\r\n\t}\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout TestGFIDSEntry\r\n\t}{\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/KernelBase.dll\"),\r\n\t\t\tout: TestGFIDSEntry{\r\n\t\t\t\tentriesCount: 0xa,\r\n\t\t\t\tentryIndex:   0x9,\r\n\t\t\t\tCFGFunction: CFGIATEntry{\r\n\t\t\t\t\tRVA:         0x1f7924,\r\n\t\t\t\t\tIATValue:    0x80000008,\r\n\t\t\t\t\tINTValue:    0x80000008,\r\n\t\t\t\t\tDescription: \"ntdll.dll!#8\",\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/kernel32.dll\"),\r\n\t\t\tout: TestGFIDSEntry{\r\n\t\t\t\tentriesCount: 0x3,\r\n\t\t\t\tentryIndex:   0x2,\r\n\t\t\t\tCFGFunction: CFGIATEntry{\r\n\t\t\t\t\tRVA:         0x83838,\r\n\t\t\t\t\tIATValue:    0xac0e0,\r\n\t\t\t\t\tINTValue:    0xac0e0,\r\n\t\t\t\t\tDescription: \"ntdll.dll!RtlGetLengthWithoutLastFullDosOrNtPathElement\",\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\r\n\t\t\tops := Options{Fast: false}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseLoadConfigDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseLoadConfigDirectory(%s) failed, reason: %v\",\r\n\t\t\t\t\ttt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tcfgIAT := file.LoadConfig.CFGIAT\r\n\t\t\tif len(cfgIAT) != tt.out.entriesCount {\r\n\t\t\t\tt.Fatalf(\"load config CFG IAT entries count assert failed, got %v, want %v\",\r\n\t\t\t\t\tlen(cfgIAT), tt.out.entriesCount)\r\n\t\t\t}\r\n\r\n\t\t\tcfgIATEntry := cfgIAT[tt.out.entryIndex]\r\n\t\t\tif !reflect.DeepEqual(cfgIATEntry, tt.out.CFGFunction) {\r\n\t\t\t\tt.Fatalf(\"load config CFG IAT entry assertion failed, got %v, want %v\",\r\n\t\t\t\t\tcfgIATEntry, tt.out.CFGFunction)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestLoadConfigDirectoryControlFlowGuardLongJump(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout []uint32\r\n\t}{\r\n\t\t{\r\n\t\t\tin:  getAbsoluteFilePath(\"test/IEAdvpack.dll\"),\r\n\t\t\tout: []uint32{0x13EDD, 0x1434F},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin:  getAbsoluteFilePath(\"test/PSCRIPT5.DLL\"),\r\n\t\t\tout: []uint32{0x3FE11, 0x401F8, 0x4077D, 0x40B53, 0x40DFD, 0x40FB3},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseLoadConfigDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseLoadConfigDirectory(%s) failed, reason: %v\",\r\n\t\t\t\t\ttt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tcfgLongJumpTargetTable := file.LoadConfig.CFGLongJump\r\n\t\t\tif !reflect.DeepEqual(cfgLongJumpTargetTable, tt.out) {\r\n\t\t\t\tt.Fatalf(\"load config CFG long jump target table assertion failed, got %v, want %v\",\r\n\t\t\t\t\tcfgLongJumpTargetTable, tt.out)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestLoadConfigDirectoryHybridPE(t *testing.T) {\r\n\r\n\ttype TestCHPE struct {\r\n\t\timgCHPEMetadata ImageCHPEMetadataX86\r\n\t\tcodeRanges      []CodeRange\r\n\t\tcompilerIAT     CompilerIAT\r\n\t}\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout TestCHPE\r\n\t}{\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/msyuv.dll\"),\r\n\t\t\tout: TestCHPE{\r\n\t\t\t\timgCHPEMetadata: ImageCHPEMetadataX86{\r\n\t\t\t\t\tVersion:                                  0x4,\r\n\t\t\t\t\tCHPECodeAddressRangeOffset:               0x26f8,\r\n\t\t\t\t\tCHPECodeAddressRangeCount:                0x4,\r\n\t\t\t\t\tWoWA64ExceptionHandlerFunctionPtr:        0x1000c,\r\n\t\t\t\t\tWoWA64DispatchCallFunctionPtr:            0x10000,\r\n\t\t\t\t\tWoWA64DispatchIndirectCallFunctionPtr:    0x10004,\r\n\t\t\t\t\tWoWA64DispatchIndirectCallCfgFunctionPtr: 0x10008,\r\n\t\t\t\t\tWoWA64DispatchRetFunctionPtr:             0x10010,\r\n\t\t\t\t\tWoWA64DispatchRetLeafFunctionPtr:         0x10014,\r\n\t\t\t\t\tWoWA64DispatchJumpFunctionPtr:            0x10018,\r\n\t\t\t\t\tCompilerIATPointer:                       0x11000,\r\n\t\t\t\t\tWoWA64RDTSCFunctionPtr:                   0x1001c,\r\n\t\t\t\t},\r\n\t\t\t\tcodeRanges: []CodeRange{\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tBegin:   0x1000,\r\n\t\t\t\t\t\tLength:  0x10,\r\n\t\t\t\t\t\tMachine: 0x0,\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tBegin:   0x2a00,\r\n\t\t\t\t\t\tLength:  0x4e28,\r\n\t\t\t\t\t\tMachine: 0x1,\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tBegin:   0x8000,\r\n\t\t\t\t\t\tLength:  0x4b1,\r\n\t\t\t\t\t\tMachine: 0x0,\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tBegin:   0x9000,\r\n\t\t\t\t\t\tLength:  0x2090,\r\n\t\t\t\t\t\tMachine: 0x1,\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\r\n\t\t\tops := Options{Fast: false}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseLoadConfigDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseLoadConfigDirectory(%s) failed, reason: %v\",\r\n\t\t\t\t\ttt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tchpe := file.LoadConfig.CHPE\r\n\t\t\tif chpe.CHPEMetadata != tt.out.imgCHPEMetadata {\r\n\t\t\t\tt.Fatalf(\"load config CHPE metadata assertion failed, got %v, want %v\",\r\n\t\t\t\t\tchpe.CHPEMetadata, tt.out.imgCHPEMetadata)\r\n\t\t\t}\r\n\r\n\t\t\tif !reflect.DeepEqual(chpe.CodeRanges, tt.out.codeRanges) {\r\n\t\t\t\tt.Fatalf(\"load config CHPE code ranges assertion failed, got %v, want %v\",\r\n\t\t\t\t\tchpe.CodeRanges, tt.out.codeRanges)\r\n\t\t\t}\r\n\r\n\t\t\t// TODO: test compiler IAT.\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestLoadConfigDirectoryDVRT(t *testing.T) {\r\n\r\n\ttype TestDVRT struct {\r\n\t\timgDynRelocTable  ImageDynamicRelocationTable\r\n\t\trelocEntriesCount int\r\n\t}\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout TestDVRT\r\n\t}{\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/WdBoot.sys\"),\r\n\t\t\tout: TestDVRT{\r\n\t\t\t\timgDynRelocTable: ImageDynamicRelocationTable{\r\n\t\t\t\t\tVersion: 0x1,\r\n\t\t\t\t\tSize:    0x2dc,\r\n\t\t\t\t},\r\n\t\t\t\trelocEntriesCount: 0x2,\r\n\t\t\t},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseLoadConfigDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseLoadConfigDirectory(%s) failed, reason: %v\",\r\n\t\t\t\t\ttt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tDVRT := file.LoadConfig.DVRT\r\n\t\t\tif DVRT.ImageDynamicRelocationTable != tt.out.imgDynRelocTable {\r\n\t\t\t\tt.Fatalf(\"load config DVRT header assertion failed, got %v, want %v\",\r\n\t\t\t\t\tDVRT.ImageDynamicRelocationTable, tt.out.imgDynRelocTable)\r\n\t\t\t}\r\n\r\n\t\t\tif len(DVRT.Entries) != tt.out.relocEntriesCount {\r\n\t\t\t\tt.Fatalf(\"load config DVRT entries count  assertion failed, got %v, want %v\",\r\n\t\t\t\t\tlen(DVRT.Entries), tt.out.relocEntriesCount)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestLoadConfigDirectoryDVRTRetpolineType(t *testing.T) {\r\n\r\n\ttype DVRTRetpolineType struct {\r\n\t\trelocEntryIdx   int\r\n\t\timgDynReloc     interface{}\r\n\t\tRelocBlockCount int\r\n\t\trelocBlockIdx   int\r\n\t\trelocBlock      RelocBlock\r\n\t}\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout DVRTRetpolineType\r\n\t}{\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/WdBoot.sys\"),\r\n\t\t\tout: DVRTRetpolineType{\r\n\t\t\t\trelocEntryIdx: 0x0,\r\n\t\t\t\timgDynReloc: ImageDynamicRelocation64{\r\n\t\t\t\t\tSymbol:        0x3,\r\n\t\t\t\t\tBaseRelocSize: 0x278,\r\n\t\t\t\t},\r\n\t\t\t\tRelocBlockCount: 0x7,\r\n\t\t\t\trelocBlockIdx:   0x0,\r\n\t\t\t\trelocBlock: RelocBlock{\r\n\t\t\t\t\tImgBaseReloc: ImageBaseRelocation{\r\n\t\t\t\t\t\tVirtualAddress: 0x2000,\r\n\t\t\t\t\t\tSizeOfBlock:    0xc,\r\n\t\t\t\t\t},\r\n\t\t\t\t\tTypeOffsets: []interface{}{\r\n\t\t\t\t\t\tImageImportControlTransferDynamicRelocation{\r\n\t\t\t\t\t\t\tPageRelativeOffset: 0x611,\r\n\t\t\t\t\t\t\tIndirectCall:       0x0,\r\n\t\t\t\t\t\t\tIATIndex:           0x28,\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/WdBoot.sys\"),\r\n\t\t\tout: DVRTRetpolineType{\r\n\t\t\t\trelocEntryIdx: 0x1,\r\n\t\t\t\timgDynReloc: ImageDynamicRelocation64{\r\n\t\t\t\t\tSymbol:        0x4,\r\n\t\t\t\t\tBaseRelocSize: 0x4c,\r\n\t\t\t\t},\r\n\t\t\t\tRelocBlockCount: 0x5,\r\n\t\t\t\trelocBlockIdx:   0x4,\r\n\t\t\t\trelocBlock: RelocBlock{\r\n\t\t\t\t\tImgBaseReloc: ImageBaseRelocation{\r\n\t\t\t\t\t\tVirtualAddress: 0xb000,\r\n\t\t\t\t\t\tSizeOfBlock:    0xc,\r\n\t\t\t\t\t},\r\n\t\t\t\t\tTypeOffsets: []interface{}{\r\n\t\t\t\t\t\tImageIndirectControlTransferDynamicRelocation{\r\n\t\t\t\t\t\t\tPageRelativeOffset: 0x58e,\r\n\t\t\t\t\t\t\tIndirectCall:       0x1,\r\n\t\t\t\t\t\t\tCfgCheck:           0x1,\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/acpi.sys\"),\r\n\t\t\tout: DVRTRetpolineType{\r\n\t\t\t\trelocEntryIdx: 0x2,\r\n\t\t\t\timgDynReloc: ImageDynamicRelocation64{\r\n\t\t\t\t\tSymbol:        0x5,\r\n\t\t\t\t\tBaseRelocSize: 0x4c,\r\n\t\t\t\t},\r\n\t\t\t\tRelocBlockCount: 0x6,\r\n\t\t\t\trelocBlockIdx:   0x5,\r\n\t\t\t\trelocBlock: RelocBlock{\r\n\t\t\t\t\tImgBaseReloc: ImageBaseRelocation{\r\n\t\t\t\t\t\tVirtualAddress: 0x43000,\r\n\t\t\t\t\t\tSizeOfBlock:    0xc,\r\n\t\t\t\t\t},\r\n\t\t\t\t\tTypeOffsets: []interface{}{\r\n\t\t\t\t\t\tImageSwitchableBranchDynamicRelocation{\r\n\t\t\t\t\t\t\tPageRelativeOffset: 0xd1,\r\n\t\t\t\t\t\t\tRegisterNumber:     0x1,\r\n\t\t\t\t\t\t},\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseLoadConfigDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseLoadConfigDirectory(%s) failed, reason: %v\",\r\n\t\t\t\t\ttt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tDVRT := file.LoadConfig.DVRT\r\n\t\t\trelocEntry := DVRT.Entries[tt.out.relocEntryIdx]\r\n\t\t\tif relocEntry.ImageDynamicRelocation != tt.out.imgDynReloc {\r\n\t\t\t\tt.Fatalf(\"load config DVRT reloc entry imaged dynamic relocation assertion failed, got %#v, want %#v\",\r\n\t\t\t\t\trelocEntry.ImageDynamicRelocation, tt.out.imgDynReloc)\r\n\t\t\t}\r\n\r\n\t\t\tif len(relocEntry.RelocBlocks) != tt.out.RelocBlockCount {\r\n\t\t\t\tt.Fatalf(\"load config DVRT reloc block count dynamic relocation assertion failed, got %v, want %v\",\r\n\t\t\t\t\tlen(relocEntry.RelocBlocks), tt.out.RelocBlockCount)\r\n\t\t\t}\r\n\r\n\t\t\trelocBlock := relocEntry.RelocBlocks[tt.out.relocBlockIdx]\r\n\t\t\tif !reflect.DeepEqual(relocBlock, tt.out.relocBlock) {\r\n\t\t\t\tt.Fatalf(\"load config DVRT reloc block assertion failed, got %#v, want %#v\",\r\n\t\t\t\t\trelocBlock, tt.out.relocBlock)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestLoadConfigDirectoryEnclave(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout Enclave\r\n\t}{\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/SgrmEnclave_secure.dll\"),\r\n\t\t\tout: Enclave{\r\n\t\t\t\tConfig: ImageEnclaveConfig64{\r\n\t\t\t\t\tSize:                      0x50,\r\n\t\t\t\t\tMinimumRequiredConfigSize: 0x4c,\r\n\t\t\t\t\tNumberOfImports:           0x4,\r\n\t\t\t\t\tImportList:                0x55224,\r\n\t\t\t\t\tImportEntrySize:           0x50,\r\n\t\t\t\t\tFamilyID:                  [ImageEnclaveShortIDLength]uint8{0xb1, 0x35, 0x7c, 0x2b, 0x69, 0x9f, 0x47, 0xf9, 0xbb, 0xc9, 0x4f, 0x44, 0xf2, 0x54, 0xdb, 0x9d},\r\n\t\t\t\t\tImageID:                   [ImageEnclaveShortIDLength]uint8{0x24, 0x56, 0x46, 0x36, 0xcd, 0x4a, 0x4a, 0xd8, 0x86, 0xa2, 0xf4, 0xec, 0x25, 0xa9, 0x72, 0x2},\r\n\t\t\t\t\tImageVersion:              0x1,\r\n\t\t\t\t\tSecurityVersion:           0x1,\r\n\t\t\t\t\tEnclaveSize:               0x10000000,\r\n\t\t\t\t\tNumberOfThreads:           0x8,\r\n\t\t\t\t\tEnclaveFlags:              0x1,\r\n\t\t\t\t},\r\n\t\t\t\tImports: []ImageEnclaveImport{\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tMatchType:  0x0,\r\n\t\t\t\t\t\tImportName: 0xffff,\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tMatchType: 0x4,\r\n\t\t\t\t\t\tImageID: [ImageEnclaveShortIDLength]uint8{\r\n\t\t\t\t\t\t\t0xf0, 0x3c, 0xcd, 0xa7, 0xe8, 0x7b, 0x46, 0xeb, 0xaa, 0xe7, 0x1f, 0x13, 0xd5, 0xcd, 0xde, 0x5d},\r\n\t\t\t\t\t\tImportName: 0x5b268,\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tMatchType: 0x4,\r\n\t\t\t\t\t\tImageID: [ImageEnclaveShortIDLength]uint8{\r\n\t\t\t\t\t\t\t0x20, 0x27, 0xbd, 0x68, 0x75, 0x59, 0x49, 0xb7, 0xbe, 0x6, 0x34, 0x50, 0xe2, 0x16, 0xd7, 0xed},\r\n\t\t\t\t\t\tImportName: 0x5b428,\r\n\t\t\t\t\t},\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tMatchType: 0x4,\r\n\t\t\t\t\t\tImageID: [ImageEnclaveShortIDLength]uint8{\r\n\t\t\t\t\t\t\t0x72, 0x84, 0x41, 0x72, 0x67, 0xa8, 0x4e, 0x8d, 0xbf, 0x1, 0x28, 0x4b, 0x7, 0x43, 0x2b, 0x1e},\r\n\t\t\t\t\t\tImportName: 0x5b63c,\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseLoadConfigDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseLoadConfigDirectory(%s) failed, reason: %v\",\r\n\t\t\t\t\ttt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tenclave := file.LoadConfig.Enclave\r\n\t\t\tif !reflect.DeepEqual(*enclave, tt.out) {\r\n\t\t\t\tt.Fatalf(\"load config enclave assertion failed, got %#v, want %#v\",\r\n\t\t\t\t\tenclave, tt.out)\r\n\t\t\t}\r\n\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestLoadConfigDirectoryVolatileMetadata(t *testing.T) {\r\n\r\n\ttype TestVolatileMetadata struct {\r\n\t\timgVolatileMetadata ImageVolatileMetadata\r\n\t\taccessRVATableCount int\r\n\t\taccessRVATableIndex int\r\n\t\taccessRVAEntry      uint32\r\n\t\tinfoRangeTableCount int\r\n\t\tinfoRangeTableIndex int\r\n\t\tinfoRangeEntry      RangeTableEntry\r\n\t}\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout TestVolatileMetadata\r\n\t}{\r\n\t\t{\r\n\t\t\tin: getAbsoluteFilePath(\"test/KernelBase.dll\"),\r\n\t\t\tout: TestVolatileMetadata{\r\n\t\t\t\timgVolatileMetadata: ImageVolatileMetadata{\r\n\t\t\t\t\tSize:                       0x18,\r\n\t\t\t\t\tVersion:                    0x1,\r\n\t\t\t\t\tVolatileAccessTable:        0x00090C64,\r\n\t\t\t\t\tVolatileAccessTableSize:    0x00002E48,\r\n\t\t\t\t\tVolatileInfoRangeTable:     0x00093AAC,\r\n\t\t\t\t\tVolatileInfoRangeTableSize: 0x000001D0,\r\n\t\t\t\t},\r\n\t\t\t\taccessRVATableCount: 0xB92,\r\n\t\t\t\taccessRVATableIndex: 0xB91,\r\n\t\t\t\taccessRVAEntry:      0x1DF998,\r\n\t\t\t\tinfoRangeTableCount: 0x3A,\r\n\t\t\t\tinfoRangeTableIndex: 0x39,\r\n\t\t\t\tinfoRangeEntry: RangeTableEntry{\r\n\t\t\t\t\tRVA:  0x16BB10,\r\n\t\t\t\t\tSize: 0x75550,\r\n\t\t\t\t},\r\n\t\t\t},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\r\n\t\t\tif file.Is64 {\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t} else {\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseLoadConfigDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseLoadConfigDirectory(%s) failed, reason: %v\",\r\n\t\t\t\t\ttt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvolatileMetadata := file.LoadConfig.VolatileMetadata\r\n\t\t\tif volatileMetadata.Struct != tt.out.imgVolatileMetadata {\r\n\t\t\t\tt.Fatalf(\"load config image volatile metadata assertion failed, got %v, want %v\",\r\n\t\t\t\t\tvolatileMetadata, tt.out.imgVolatileMetadata)\r\n\t\t\t}\r\n\r\n\t\t\tif len(volatileMetadata.AccessRVATable) != tt.out.accessRVATableCount {\r\n\t\t\t\tt.Fatalf(\"load config access RVA table entries count assert failed, got %v, want %v\",\r\n\t\t\t\t\tlen(volatileMetadata.AccessRVATable), tt.out.accessRVATableCount)\r\n\t\t\t}\r\n\r\n\t\t\taccessRVAEntry := volatileMetadata.AccessRVATable[tt.out.accessRVATableIndex]\r\n\t\t\tif accessRVAEntry != tt.out.accessRVAEntry {\r\n\t\t\t\tt.Fatalf(\"load config access RVA table entry assertion failed, got %v, want %v\",\r\n\t\t\t\t\taccessRVAEntry, tt.out.accessRVAEntry)\r\n\t\t\t}\r\n\r\n\t\t\tif len(volatileMetadata.InfoRangeTable) != tt.out.infoRangeTableCount {\r\n\t\t\t\tt.Fatalf(\"load config info range table entries count assert failed, got %v, want %v\",\r\n\t\t\t\t\tlen(volatileMetadata.InfoRangeTable), tt.out.infoRangeTableCount)\r\n\t\t\t}\r\n\r\n\t\t\tinfoRangeEntry := volatileMetadata.InfoRangeTable[tt.out.infoRangeTableIndex]\r\n\t\t\tif infoRangeEntry != tt.out.infoRangeEntry {\r\n\t\t\t\tt.Fatalf(\"load config info range table entry assertion failed, got %v, want %v\",\r\n\t\t\t\t\tinfoRangeEntry, tt.out.infoRangeEntry)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestLoadConfigDirectoryCorruptSize(t *testing.T) {\r\n\t// This PE has a LoadConfig Size field (0xb50087) that far exceeds the\r\n\t// maximum valid struct size. parseLoadConfigDirectory should return an\r\n\t// error instead of panicking.\r\n\tpath := getAbsoluteFilePath(\"test/03f18017c215ad67f4d043cd733d6f762edbf99f9f7e0ed89166536f80544d96\")\r\n\tops := Options{Fast: true}\r\n\tfile, err := New(path, &ops)\r\n\tif err != nil {\r\n\t\tt.Fatalf(\"New(%s) failed, reason: %v\", path, err)\r\n\t}\r\n\r\n\terr = file.Parse()\r\n\tif err != nil {\r\n\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", path, err)\r\n\t}\r\n\r\n\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryLoadConfig]\r\n\terr = file.parseLoadConfigDirectory(dirEntry.VirtualAddress, dirEntry.Size)\r\n\tif err == nil {\r\n\t\tt.Fatalf(\"parseLoadConfigDirectory should have failed for corrupt LoadConfig size\")\r\n\t}\r\n}\r\n"
  },
  {
    "path": "log/README.md",
    "content": "# Logger\n\nThis code was taken from the go microservice framework [kratos](https://github.com/go-kratos/kratos).\n\n## Usage\n\n### Structured logging\n\n```go\nlogger := log.NewStdLogger(os.Stdout)\n// fields & valuer\nlogger = log.With(logger,\n    \"service.name\", \"hellworld\",\n    \"service.version\", \"v1.0.0\",\n    \"ts\", log.DefaultTimestamp,\n    \"caller\", log.DefaultCaller,\n)\nlogger.Log(log.LevelInfo, \"key\", \"value\")\n\n// helper\nhelper := log.NewHelper(logger)\nhelper.Log(log.LevelInfo, \"key\", \"value\")\nhelper.Info(\"info message\")\nhelper.Infof(\"info %s\", \"message\")\nhelper.Infow(\"key\", \"value\")\n\n// filter\nlog := log.NewHelper(log.NewFilter(logger,\n\tlog.FilterLevel(log.LevelInfo),\n\tlog.FilterKey(\"foo\"),\n\tlog.FilterValue(\"bar\"),\n\tlog.FilterFunc(customFilter),\n))\nlog.Debug(\"debug log\")\nlog.Info(\"info log\")\nlog.Warn(\"warn log\")\nlog.Error(\"warn log\")\n```\n\n## Third party log library\n\nIf you need to implement a third party logging library like `zap`, have a look this [url](https://github.com/go-kratos/kratos/tree/main/contrib/log)."
  },
  {
    "path": "log/filter.go",
    "content": "package log\n\n// FilterOption is filter option.\ntype FilterOption func(*Filter)\n\nconst fuzzyStr = \"***\"\n\n// FilterLevel with filter level.\nfunc FilterLevel(level Level) FilterOption {\n\treturn func(opts *Filter) {\n\t\topts.level = level\n\t}\n}\n\n// FilterKey with filter key.\nfunc FilterKey(key ...string) FilterOption {\n\treturn func(o *Filter) {\n\t\tfor _, v := range key {\n\t\t\to.key[v] = struct{}{}\n\t\t}\n\t}\n}\n\n// FilterValue with filter value.\nfunc FilterValue(value ...string) FilterOption {\n\treturn func(o *Filter) {\n\t\tfor _, v := range value {\n\t\t\to.value[v] = struct{}{}\n\t\t}\n\t}\n}\n\n// FilterFunc with filter func.\nfunc FilterFunc(f func(level Level, keyvals ...interface{}) bool) FilterOption {\n\treturn func(o *Filter) {\n\t\to.filter = f\n\t}\n}\n\n// Filter is a logger filter.\ntype Filter struct {\n\tlogger Logger\n\tlevel  Level\n\tkey    map[interface{}]struct{}\n\tvalue  map[interface{}]struct{}\n\tfilter func(level Level, keyvals ...interface{}) bool\n}\n\n// NewFilter new a logger filter.\nfunc NewFilter(logger Logger, opts ...FilterOption) *Filter {\n\toptions := Filter{\n\t\tlogger: logger,\n\t\tkey:    make(map[interface{}]struct{}),\n\t\tvalue:  make(map[interface{}]struct{}),\n\t}\n\tfor _, o := range opts {\n\t\to(&options)\n\t}\n\treturn &options\n}\n\n// Log Print log by level and keyvals.\nfunc (f *Filter) Log(level Level, keyvals ...interface{}) error {\n\tif level < f.level {\n\t\treturn nil\n\t}\n\t// fkv is used to provide a slice to contains both logger.prefix and keyvals for filter\n\tvar fkv []interface{}\n\tif l, ok := f.logger.(*logger); ok {\n\t\tif len(l.prefix) > 0 {\n\t\t\tfkv = make([]interface{}, 0, len(l.prefix)+len(keyvals))\n\t\t\tfkv = append(fkv, l.prefix...)\n\t\t\tfkv = append(fkv, keyvals...)\n\t\t}\n\t} else {\n\t\tfkv = keyvals\n\t}\n\tif f.filter != nil && f.filter(level, fkv...) {\n\t\treturn nil\n\t}\n\tif len(f.key) > 0 || len(f.value) > 0 {\n\t\tfor i := 0; i < len(keyvals); i += 2 {\n\t\t\tv := i + 1\n\t\t\tif v >= len(keyvals) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, ok := f.key[keyvals[i]]; ok {\n\t\t\t\tkeyvals[v] = fuzzyStr\n\t\t\t}\n\t\t\tif _, ok := f.value[keyvals[v]]; ok {\n\t\t\t\tkeyvals[v] = fuzzyStr\n\t\t\t}\n\t\t}\n\t}\n\treturn f.logger.Log(level, keyvals...)\n}\n"
  },
  {
    "path": "log/filter_test.go",
    "content": "package log\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"testing\"\n)\n\nfunc TestFilterAll(t *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(NewFilter(logger,\n\t\tFilterLevel(LevelDebug),\n\t\tFilterKey(\"username\"),\n\t\tFilterValue(\"hello\"),\n\t\tFilterFunc(testFilterFunc),\n\t))\n\tlog.Log(LevelDebug, \"msg\", \"test debug\")\n\tlog.Info(\"hello\")\n\tlog.Infow(\"password\", \"123456\")\n\tlog.Infow(\"username\", \"kratos\")\n\tlog.Warn(\"warn log\")\n}\n\nfunc TestFilterLevel(t *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(NewFilter(NewFilter(logger, FilterLevel(LevelWarn))))\n\tlog.Log(LevelDebug, \"msg1\", \"te1st debug\")\n\tlog.Debug(\"test debug\")\n\tlog.Debugf(\"test %s\", \"debug\")\n\tlog.Debugw(\"log\", \"test debug\")\n\tlog.Warn(\"warn log\")\n}\n\nfunc TestFilterCaller(t *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewFilter(logger)\n\t_ = log.Log(LevelDebug, \"msg1\", \"te1st debug\")\n\tlogHelper := NewHelper(NewFilter(logger))\n\tlogHelper.Log(LevelDebug, \"msg1\", \"te1st debug\")\n}\n\nfunc TestFilterKey(t *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(NewFilter(logger, FilterKey(\"password\")))\n\tlog.Debugw(\"password\", \"123456\")\n}\n\nfunc TestFilterValue(t *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(NewFilter(logger, FilterValue(\"debug\")))\n\tlog.Debugf(\"test %s\", \"debug\")\n}\n\nfunc TestFilterFunc(t *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(NewFilter(logger, FilterFunc(testFilterFunc)))\n\tlog.Debug(\"debug level\")\n\tlog.Infow(\"password\", \"123456\")\n}\n\nfunc BenchmarkFilterKey(b *testing.B) {\n\tlog := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterKey(\"password\")))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Infow(\"password\", \"123456\")\n\t}\n}\n\nfunc BenchmarkFilterValue(b *testing.B) {\n\tlog := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterValue(\"password\")))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Infow(\"password\")\n\t}\n}\n\nfunc BenchmarkFilterFunc(b *testing.B) {\n\tlog := NewHelper(NewFilter(NewStdLogger(io.Discard), FilterFunc(testFilterFunc)))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Info(\"password\", \"123456\")\n\t}\n}\n\nfunc testFilterFunc(level Level, keyvals ...interface{}) bool {\n\tif level == LevelWarn {\n\t\treturn true\n\t}\n\tfor i := 0; i < len(keyvals); i++ {\n\t\tif keyvals[i] == \"password\" {\n\t\t\tkeyvals[i+1] = fuzzyStr\n\t\t}\n\t}\n\treturn false\n}\n\nfunc TestFilterFuncWitchLoggerPrefix(t *testing.T) {\n\tbuf := new(bytes.Buffer)\n\ttests := []struct {\n\t\tlogger Logger\n\t\twant   string\n\t}{\n\t\t{\n\t\t\tlogger: NewFilter(With(NewStdLogger(buf), \"caller\", \"caller\", \"prefix\", \"whaterver\"), FilterFunc(testFilterFuncWithLoggerPrefix)),\n\t\t\twant:   \"\",\n\t\t},\n\t\t{\n\t\t\tlogger: NewFilter(With(NewStdLogger(buf), \"caller\", \"caller\"), FilterFunc(testFilterFuncWithLoggerPrefix)),\n\t\t\twant:   \"INFO caller=caller msg=msg\\n\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\terr := tt.logger.Log(LevelInfo, \"msg\", \"msg\")\n\t\tif err != nil {\n\t\t\tt.Fatal(\"err should be nil\")\n\t\t}\n\t\tgot := buf.String()\n\t\tif got != tt.want {\n\t\t\tt.Fatalf(\"filter should catch prefix, want %s, got %s.\", tt.want, got)\n\t\t}\n\t\tbuf.Reset()\n\t}\n}\n\nfunc testFilterFuncWithLoggerPrefix(level Level, keyvals ...interface{}) bool {\n\tif level == LevelWarn {\n\t\treturn true\n\t}\n\tfor i := 0; i < len(keyvals); i += 2 {\n\t\tif keyvals[i] == \"prefix\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "log/global.go",
    "content": "package log\n\nimport (\n\t\"sync\"\n)\n\n// globalLogger is designed as a global logger in current process.\nvar global = &loggerAppliance{}\n\n// loggerAppliance is the proxy of `Logger` to\n// make logger change will affect all sub-logger.\ntype loggerAppliance struct {\n\tlock sync.Mutex\n\tLogger\n\thelper *Helper\n}\n\nfunc init() {\n\tglobal.SetLogger(DefaultLogger)\n}\n\nfunc (a *loggerAppliance) SetLogger(in Logger) {\n\ta.lock.Lock()\n\tdefer a.lock.Unlock()\n\ta.Logger = in\n\ta.helper = NewHelper(a.Logger)\n}\n\nfunc (a *loggerAppliance) GetLogger() Logger {\n\treturn a.Logger\n}\n\n// SetLogger should be called before any other log call.\n// And it is NOT THREAD SAFE.\nfunc SetLogger(logger Logger) {\n\tglobal.SetLogger(logger)\n}\n\n// GetLogger returns global logger appliance as logger in current process.\nfunc GetLogger() Logger {\n\treturn global\n}\n\n// Log Print log by level and keyvals.\nfunc Log(level Level, keyvals ...interface{}) {\n\tglobal.helper.Log(level, keyvals...)\n}\n\n// Debug logs a message at debug level.\nfunc Debug(a ...interface{}) {\n\tglobal.helper.Debug(a...)\n}\n\n// Debugf logs a message at debug level.\nfunc Debugf(format string, a ...interface{}) {\n\tglobal.helper.Debugf(format, a...)\n}\n\n// Debugw logs a message at debug level.\nfunc Debugw(keyvals ...interface{}) {\n\tglobal.helper.Debugw(keyvals...)\n}\n\n// Info logs a message at info level.\nfunc Info(a ...interface{}) {\n\tglobal.helper.Info(a...)\n}\n\n// Infof logs a message at info level.\nfunc Infof(format string, a ...interface{}) {\n\tglobal.helper.Infof(format, a...)\n}\n\n// Infow logs a message at info level.\nfunc Infow(keyvals ...interface{}) {\n\tglobal.helper.Infow(keyvals...)\n}\n\n// Warn logs a message at warn level.\nfunc Warn(a ...interface{}) {\n\tglobal.helper.Warn(a...)\n}\n\n// Warnf logs a message at warnf level.\nfunc Warnf(format string, a ...interface{}) {\n\tglobal.helper.Warnf(format, a...)\n}\n\n// Warnw logs a message at warnf level.\nfunc Warnw(keyvals ...interface{}) {\n\tglobal.helper.Warnw(keyvals...)\n}\n\n// Error logs a message at error level.\nfunc Error(a ...interface{}) {\n\tglobal.helper.Error(a...)\n}\n\n// Errorf logs a message at error level.\nfunc Errorf(format string, a ...interface{}) {\n\tglobal.helper.Errorf(format, a...)\n}\n\n// Errorw logs a message at error level.\nfunc Errorw(keyvals ...interface{}) {\n\tglobal.helper.Errorw(keyvals...)\n}\n\n// Fatal logs a message at fatal level.\nfunc Fatal(a ...interface{}) {\n\tglobal.helper.Fatal(a...)\n}\n\n// Fatalf logs a message at fatal level.\nfunc Fatalf(format string, a ...interface{}) {\n\tglobal.helper.Fatalf(format, a...)\n}\n\n// Fatalw logs a message at fatal level.\nfunc Fatalw(keyvals ...interface{}) {\n\tglobal.helper.Fatalw(keyvals...)\n}\n"
  },
  {
    "path": "log/global_test.go",
    "content": "package log\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestGlobalLog(t *testing.T) {\n\tbuffer := &bytes.Buffer{}\n\tSetLogger(NewStdLogger(buffer))\n\n\ttestCases := []struct {\n\t\tlevel   Level\n\t\tcontent []interface{}\n\t}{\n\t\t{\n\t\t\tLevelDebug,\n\t\t\t[]interface{}{\"test debug\"},\n\t\t},\n\t\t{\n\t\t\tLevelInfo,\n\t\t\t[]interface{}{\"test info\"},\n\t\t},\n\t\t{\n\t\t\tLevelInfo,\n\t\t\t[]interface{}{\"test %s\", \"info\"},\n\t\t},\n\t\t{\n\t\t\tLevelWarn,\n\t\t\t[]interface{}{\"test warn\"},\n\t\t},\n\t\t{\n\t\t\tLevelError,\n\t\t\t[]interface{}{\"test error\"},\n\t\t},\n\t\t{\n\t\t\tLevelError,\n\t\t\t[]interface{}{\"test %s\", \"error\"},\n\t\t},\n\t}\n\n\texpected := []string{}\n\tfor _, tc := range testCases {\n\t\tmsg := fmt.Sprintf(tc.content[0].(string), tc.content[1:]...)\n\t\tswitch tc.level {\n\t\tcase LevelDebug:\n\t\t\tDebugf(tc.content[0].(string), tc.content[1:]...)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"DEBUG\", msg))\n\t\tcase LevelInfo:\n\t\t\tInfof(tc.content[0].(string), tc.content[1:]...)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"INFO\", msg))\n\t\tcase LevelWarn:\n\t\t\tWarnf(tc.content[0].(string), tc.content[1:]...)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"WARN\", msg))\n\t\tcase LevelError:\n\t\t\tErrorf(tc.content[0].(string), tc.content[1:]...)\n\t\t\texpected = append(expected, fmt.Sprintf(\"%s msg=%s\", \"ERROR\", msg))\n\t\t}\n\t}\n\texpected = append(expected, \"\")\n\n\tt.Logf(\"Content: %s\", buffer.String())\n\tif buffer.String() != strings.Join(expected, \"\\n\") {\n\t\tt.Errorf(\"Expected: %s, got: %s\", strings.Join(expected, \"\\n\"), buffer.String())\n\t}\n}\n\nfunc TestGlobalLogUpdate(t *testing.T) {\n\tl := &loggerAppliance{}\n\tl.SetLogger(NewStdLogger(os.Stdout))\n\tLOG := NewHelper(l)\n\tLOG.Info(\"Log to stdout\")\n\n\tbuffer := &bytes.Buffer{}\n\tl.SetLogger(NewStdLogger(buffer))\n\tLOG.Info(\"Log to buffer\")\n\n\texpected := \"INFO msg=Log to buffer\\n\"\n\tif buffer.String() != expected {\n\t\tt.Errorf(\"Expected: %s, got: %s\", expected, buffer.String())\n\t}\n}\n"
  },
  {
    "path": "log/helper.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n)\n\n// DefaultMessageKey default message key.\nvar DefaultMessageKey = \"msg\"\n\n// Option is Helper option.\ntype Option func(*Helper)\n\n// Helper is a logger helper.\ntype Helper struct {\n\tlogger Logger\n\tmsgKey string\n}\n\n// WithMessageKey with message key.\nfunc WithMessageKey(k string) Option {\n\treturn func(opts *Helper) {\n\t\topts.msgKey = k\n\t}\n}\n\n// NewHelper new a logger helper.\nfunc NewHelper(logger Logger, opts ...Option) *Helper {\n\toptions := &Helper{\n\t\tmsgKey: DefaultMessageKey, // default message key\n\t\tlogger: logger,\n\t}\n\tfor _, o := range opts {\n\t\to(options)\n\t}\n\treturn options\n}\n\n// WithContext returns a shallow copy of h with its context changed\n// to ctx. The provided ctx must be non-nil.\nfunc (h *Helper) WithContext(ctx context.Context) *Helper {\n\treturn &Helper{\n\t\tmsgKey: h.msgKey,\n\t\tlogger: WithContext(ctx, h.logger),\n\t}\n}\n\n// Log Print log by level and keyvals.\nfunc (h *Helper) Log(level Level, keyvals ...interface{}) {\n\t_ = h.logger.Log(level, keyvals...)\n}\n\n// Debug logs a message at debug level.\nfunc (h *Helper) Debug(a ...interface{}) {\n\t_ = h.logger.Log(LevelDebug, h.msgKey, fmt.Sprint(a...))\n}\n\n// Debugf logs a message at debug level.\nfunc (h *Helper) Debugf(format string, a ...interface{}) {\n\t_ = h.logger.Log(LevelDebug, h.msgKey, fmt.Sprintf(format, a...))\n}\n\n// Debugw logs a message at debug level.\nfunc (h *Helper) Debugw(keyvals ...interface{}) {\n\t_ = h.logger.Log(LevelDebug, keyvals...)\n}\n\n// Info logs a message at info level.\nfunc (h *Helper) Info(a ...interface{}) {\n\t_ = h.logger.Log(LevelInfo, h.msgKey, fmt.Sprint(a...))\n}\n\n// Infof logs a message at info level.\nfunc (h *Helper) Infof(format string, a ...interface{}) {\n\t_ = h.logger.Log(LevelInfo, h.msgKey, fmt.Sprintf(format, a...))\n}\n\n// Infow logs a message at info level.\nfunc (h *Helper) Infow(keyvals ...interface{}) {\n\t_ = h.logger.Log(LevelInfo, keyvals...)\n}\n\n// Warn logs a message at warn level.\nfunc (h *Helper) Warn(a ...interface{}) {\n\t_ = h.logger.Log(LevelWarn, h.msgKey, fmt.Sprint(a...))\n}\n\n// Warnf logs a message at warnf level.\nfunc (h *Helper) Warnf(format string, a ...interface{}) {\n\t_ = h.logger.Log(LevelWarn, h.msgKey, fmt.Sprintf(format, a...))\n}\n\n// Warnw logs a message at warnf level.\nfunc (h *Helper) Warnw(keyvals ...interface{}) {\n\t_ = h.logger.Log(LevelWarn, keyvals...)\n}\n\n// Error logs a message at error level.\nfunc (h *Helper) Error(a ...interface{}) {\n\t_ = h.logger.Log(LevelError, h.msgKey, fmt.Sprint(a...))\n}\n\n// Errorf logs a message at error level.\nfunc (h *Helper) Errorf(format string, a ...interface{}) {\n\t_ = h.logger.Log(LevelError, h.msgKey, fmt.Sprintf(format, a...))\n}\n\n// Errorw logs a message at error level.\nfunc (h *Helper) Errorw(keyvals ...interface{}) {\n\t_ = h.logger.Log(LevelError, keyvals...)\n}\n\n// Fatal logs a message at fatal level.\nfunc (h *Helper) Fatal(a ...interface{}) {\n\t_ = h.logger.Log(LevelFatal, h.msgKey, fmt.Sprint(a...))\n\tos.Exit(1)\n}\n\n// Fatalf logs a message at fatal level.\nfunc (h *Helper) Fatalf(format string, a ...interface{}) {\n\t_ = h.logger.Log(LevelFatal, h.msgKey, fmt.Sprintf(format, a...))\n\tos.Exit(1)\n}\n\n// Fatalw logs a message at fatal level.\nfunc (h *Helper) Fatalw(keyvals ...interface{}) {\n\t_ = h.logger.Log(LevelFatal, keyvals...)\n\tos.Exit(1)\n}\n"
  },
  {
    "path": "log/helper_test.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestHelper(t *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(logger)\n\n\tlog.Log(LevelDebug, \"msg\", \"test debug\")\n\tlog.Debug(\"test debug\")\n\tlog.Debugf(\"test %s\", \"debug\")\n\tlog.Debugw(\"log\", \"test debug\")\n\n\tlog.Warn(\"test warn\")\n\tlog.Warnf(\"test %s\", \"warn\")\n\tlog.Warnw(\"log\", \"test warn\")\n}\n\nfunc TestHelperWithMsgKey(t *testing.T) {\n\tlogger := With(DefaultLogger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\tlog := NewHelper(logger, WithMessageKey(\"message\"))\n\tlog.Debugf(\"test %s\", \"debug\")\n\tlog.Debugw(\"log\", \"test debug\")\n}\n\nfunc TestHelperLevel(t *testing.T) {\n\tlog := NewHelper(DefaultLogger)\n\tlog.Debug(\"test debug\")\n\tlog.Info(\"test info\")\n\tlog.Infof(\"test %s\", \"info\")\n\tlog.Warn(\"test warn\")\n\tlog.Error(\"test error\")\n\tlog.Errorf(\"test %s\", \"error\")\n\tlog.Errorw(\"log\", \"test error\")\n}\n\nfunc BenchmarkHelperPrint(b *testing.B) {\n\tlog := NewHelper(NewStdLogger(io.Discard))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Debug(\"test\")\n\t}\n}\n\nfunc BenchmarkHelperPrintf(b *testing.B) {\n\tlog := NewHelper(NewStdLogger(io.Discard))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Debugf(\"%s\", \"test\")\n\t}\n}\n\nfunc BenchmarkHelperPrintw(b *testing.B) {\n\tlog := NewHelper(NewStdLogger(io.Discard))\n\tfor i := 0; i < b.N; i++ {\n\t\tlog.Debugw(\"key\", \"value\")\n\t}\n}\n\ntype traceKey struct{}\n\nfunc TestContext(t *testing.T) {\n\tlogger := With(NewStdLogger(os.Stdout),\n\t\t\"trace\", Trace(),\n\t)\n\tlog := NewHelper(logger)\n\tctx := context.WithValue(context.Background(), traceKey{}, \"2233\")\n\tlog.WithContext(ctx).Info(\"got trace!\")\n}\n\nfunc Trace() Valuer {\n\treturn func(ctx context.Context) interface{} {\n\t\ts := ctx.Value(traceKey{}).(string)\n\t\treturn s\n\t}\n}\n"
  },
  {
    "path": "log/level.go",
    "content": "package log\n\nimport \"strings\"\n\n// Level is a logger level.\ntype Level int8\n\n// LevelKey is logger level key.\nconst LevelKey = \"level\"\n\nconst (\n\t// LevelDebug is logger debug level.\n\tLevelDebug Level = iota - 1\n\t// LevelInfo is logger info level.\n\tLevelInfo\n\t// LevelWarn is logger warn level.\n\tLevelWarn\n\t// LevelError is logger error level.\n\tLevelError\n\t// LevelFatal is logger fatal level\n\tLevelFatal\n)\n\nfunc (l Level) String() string {\n\tswitch l {\n\tcase LevelDebug:\n\t\treturn \"DEBUG\"\n\tcase LevelInfo:\n\t\treturn \"INFO\"\n\tcase LevelWarn:\n\t\treturn \"WARN\"\n\tcase LevelError:\n\t\treturn \"ERROR\"\n\tcase LevelFatal:\n\t\treturn \"FATAL\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\n// ParseLevel parses a level string into a logger Level value.\nfunc ParseLevel(s string) Level {\n\tswitch strings.ToUpper(s) {\n\tcase \"DEBUG\":\n\t\treturn LevelDebug\n\tcase \"INFO\":\n\t\treturn LevelInfo\n\tcase \"WARN\":\n\t\treturn LevelWarn\n\tcase \"ERROR\":\n\t\treturn LevelError\n\tcase \"FATAL\":\n\t\treturn LevelFatal\n\t}\n\treturn LevelInfo\n}\n"
  },
  {
    "path": "log/level_test.go",
    "content": "package log\n\nimport \"testing\"\n\nfunc TestLevel_String(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tl    Level\n\t\twant string\n\t}{\n\t\t{\n\t\t\tname: \"DEBUG\",\n\t\t\tl:    LevelDebug,\n\t\t\twant: \"DEBUG\",\n\t\t},\n\t\t{\n\t\t\tname: \"INFO\",\n\t\t\tl:    LevelInfo,\n\t\t\twant: \"INFO\",\n\t\t},\n\t\t{\n\t\t\tname: \"WARN\",\n\t\t\tl:    LevelWarn,\n\t\t\twant: \"WARN\",\n\t\t},\n\t\t{\n\t\t\tname: \"ERROR\",\n\t\t\tl:    LevelError,\n\t\t\twant: \"ERROR\",\n\t\t},\n\t\t{\n\t\t\tname: \"FATAL\",\n\t\t\tl:    LevelFatal,\n\t\t\twant: \"FATAL\",\n\t\t},\n\t\t{\n\t\t\tname: \"other\",\n\t\t\tl:    10,\n\t\t\twant: \"\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := tt.l.String(); got != tt.want {\n\t\t\t\tt.Errorf(\"String() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseLevel(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\ts    string\n\t\twant Level\n\t}{\n\t\t{\n\t\t\tname: \"DEBUG\",\n\t\t\twant: LevelDebug,\n\t\t\ts:    \"DEBUG\",\n\t\t},\n\t\t{\n\t\t\tname: \"INFO\",\n\t\t\twant: LevelInfo,\n\t\t\ts:    \"INFO\",\n\t\t},\n\t\t{\n\t\t\tname: \"WARN\",\n\t\t\twant: LevelWarn,\n\t\t\ts:    \"WARN\",\n\t\t},\n\t\t{\n\t\t\tname: \"ERROR\",\n\t\t\twant: LevelError,\n\t\t\ts:    \"ERROR\",\n\t\t},\n\t\t{\n\t\t\tname: \"FATAL\",\n\t\t\twant: LevelFatal,\n\t\t\ts:    \"FATAL\",\n\t\t},\n\t\t{\n\t\t\tname: \"other\",\n\t\t\twant: LevelInfo,\n\t\t\ts:    \"other\",\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := ParseLevel(tt.s); got != tt.want {\n\t\t\t\tt.Errorf(\"ParseLevel() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "log/log.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"log\"\n)\n\n// DefaultLogger is default logger.\nvar DefaultLogger = NewStdLogger(log.Writer())\n\n// Logger is a logger interface.\ntype Logger interface {\n\tLog(level Level, keyvals ...interface{}) error\n}\n\ntype logger struct {\n\tlogs      []Logger\n\tprefix    []interface{}\n\thasValuer bool\n\tctx       context.Context\n}\n\nfunc (c *logger) Log(level Level, keyvals ...interface{}) error {\n\tkvs := make([]interface{}, 0, len(c.prefix)+len(keyvals))\n\tkvs = append(kvs, c.prefix...)\n\tif c.hasValuer {\n\t\tbindValues(c.ctx, kvs)\n\t}\n\tkvs = append(kvs, keyvals...)\n\tfor _, l := range c.logs {\n\t\tif err := l.Log(level, kvs...); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// With with logger fields.\nfunc With(l Logger, kv ...interface{}) Logger {\n\tif c, ok := l.(*logger); ok {\n\t\tkvs := make([]interface{}, 0, len(c.prefix)+len(kv))\n\t\tkvs = append(kvs, kv...)\n\t\tkvs = append(kvs, c.prefix...)\n\t\treturn &logger{\n\t\t\tlogs:      c.logs,\n\t\t\tprefix:    kvs,\n\t\t\thasValuer: containsValuer(kvs),\n\t\t\tctx:       c.ctx,\n\t\t}\n\t}\n\treturn &logger{logs: []Logger{l}, prefix: kv, hasValuer: containsValuer(kv)}\n}\n\n// WithContext returns a shallow copy of l with its context changed\n// to ctx. The provided ctx must be non-nil.\nfunc WithContext(ctx context.Context, l Logger) Logger {\n\tif c, ok := l.(*logger); ok {\n\t\treturn &logger{\n\t\t\tlogs:      c.logs,\n\t\t\tprefix:    c.prefix,\n\t\t\thasValuer: c.hasValuer,\n\t\t\tctx:       ctx,\n\t\t}\n\t}\n\treturn &logger{logs: []Logger{l}, ctx: ctx}\n}\n\n// MultiLogger wraps multi logger.\nfunc MultiLogger(logs ...Logger) Logger {\n\treturn &logger{logs: logs}\n}\n"
  },
  {
    "path": "log/log_test.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestInfo(t *testing.T) {\n\tlogger := DefaultLogger\n\tlogger = With(logger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\t_ = logger.Log(LevelInfo, \"key1\", \"value1\")\n}\n\nfunc TestWrapper(t *testing.T) {\n\tout := NewStdLogger(os.Stdout)\n\terr := NewStdLogger(os.Stderr)\n\n\tl := With(MultiLogger(out, err), \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\t_ = l.Log(LevelInfo, \"msg\", \"test\")\n}\n\nfunc TestWithContext(t *testing.T) {\n\tWithContext(context.Background(), nil)\n}\n"
  },
  {
    "path": "log/std.go",
    "content": "package log\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"sync\"\n)\n\nvar _ Logger = (*stdLogger)(nil)\n\ntype stdLogger struct {\n\tlog  *log.Logger\n\tpool *sync.Pool\n}\n\n// NewStdLogger new a logger with writer.\nfunc NewStdLogger(w io.Writer) Logger {\n\treturn &stdLogger{\n\t\tlog: log.New(w, \"\", 0),\n\t\tpool: &sync.Pool{\n\t\t\tNew: func() interface{} {\n\t\t\t\treturn new(bytes.Buffer)\n\t\t\t},\n\t\t},\n\t}\n}\n\n// Log print the kv pairs log.\nfunc (l *stdLogger) Log(level Level, keyvals ...interface{}) error {\n\tif len(keyvals) == 0 {\n\t\treturn nil\n\t}\n\tif (len(keyvals) & 1) == 1 {\n\t\tkeyvals = append(keyvals, \"KEYVALS UNPAIRED\")\n\t}\n\tbuf := l.pool.Get().(*bytes.Buffer)\n\tbuf.WriteString(level.String())\n\tfor i := 0; i < len(keyvals); i += 2 {\n\t\t_, _ = fmt.Fprintf(buf, \" %s=%v\", keyvals[i], keyvals[i+1])\n\t}\n\t_ = l.log.Output(4, buf.String()) //nolint:gomnd\n\tbuf.Reset()\n\tl.pool.Put(buf)\n\treturn nil\n}\n"
  },
  {
    "path": "log/std_test.go",
    "content": "package log\n\nimport \"testing\"\n\nfunc TestStdLogger(t *testing.T) {\n\tlogger := DefaultLogger\n\tlogger = With(logger, \"caller\", DefaultCaller, \"ts\", DefaultTimestamp)\n\n\t_ = logger.Log(LevelInfo, \"msg\", \"test debug\")\n\t_ = logger.Log(LevelInfo, \"msg\", \"test info\")\n\t_ = logger.Log(LevelInfo, \"msg\", \"test warn\")\n\t_ = logger.Log(LevelInfo, \"msg\", \"test error\")\n\t_ = logger.Log(LevelDebug, \"singular\")\n\n\tlogger2 := DefaultLogger\n\t_ = logger2.Log(LevelDebug)\n}\n"
  },
  {
    "path": "log/value.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nvar (\n\tdefaultDepth = 3\n\t// DefaultCaller is a Valuer that returns the file and line.\n\tDefaultCaller = Caller(defaultDepth)\n\n\t// DefaultTimestamp is a Valuer that returns the current wallclock time.\n\tDefaultTimestamp = Timestamp(time.RFC3339)\n)\n\n// Valuer is returns a log value.\ntype Valuer func(ctx context.Context) interface{}\n\n// Value return the function value.\nfunc Value(ctx context.Context, v interface{}) interface{} {\n\tif v, ok := v.(Valuer); ok {\n\t\treturn v(ctx)\n\t}\n\treturn v\n}\n\n// Caller returns a Valuer that returns a pkg/file:line description of the caller.\nfunc Caller(depth int) Valuer {\n\treturn func(context.Context) interface{} {\n\t\td := depth\n\t\t_, file, line, _ := runtime.Caller(d)\n\t\tif strings.LastIndex(file, \"/log/filter.go\") > 0 {\n\t\t\td++\n\t\t\t_, file, line, _ = runtime.Caller(d)\n\t\t}\n\t\tif strings.LastIndex(file, \"/log/helper.go\") > 0 {\n\t\t\td++\n\t\t\t_, file, line, _ = runtime.Caller(d)\n\t\t}\n\t\tidx := strings.LastIndexByte(file, '/')\n\t\treturn file[idx+1:] + \":\" + strconv.Itoa(line)\n\t}\n}\n\n// Timestamp returns a timestamp Valuer with a custom time format.\nfunc Timestamp(layout string) Valuer {\n\treturn func(context.Context) interface{} {\n\t\treturn time.Now().Format(layout)\n\t}\n}\n\nfunc bindValues(ctx context.Context, keyvals []interface{}) {\n\tfor i := 1; i < len(keyvals); i += 2 {\n\t\tif v, ok := keyvals[i].(Valuer); ok {\n\t\t\tkeyvals[i] = v(ctx)\n\t\t}\n\t}\n}\n\nfunc containsValuer(keyvals []interface{}) bool {\n\tfor i := 1; i < len(keyvals); i += 2 {\n\t\tif _, ok := keyvals[i].(Valuer); ok {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "log/value_test.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"testing\"\n)\n\nfunc TestValue(t *testing.T) {\n\tlogger := DefaultLogger\n\tlogger = With(logger, \"ts\", DefaultTimestamp, \"caller\", DefaultCaller)\n\t_ = logger.Log(LevelInfo, \"msg\", \"helloworld\")\n\n\tlogger = DefaultLogger\n\tlogger = With(logger)\n\t_ = logger.Log(LevelDebug, \"msg\", \"helloworld\")\n\n\tvar v1 interface{}\n\tgot := Value(context.Background(), v1)\n\tif got != v1 {\n\t\tt.Errorf(\"Value() = %v, want %v\", got, v1)\n\t}\n\tvar v2 Valuer = func(ctx context.Context) interface{} {\n\t\treturn 3\n\t}\n\tgot = Value(context.Background(), v2)\n\tres := got.(int)\n\tif res != 3 {\n\t\tt.Errorf(\"Value() = %v, want %v\", res, 3)\n\t}\n}\n"
  },
  {
    "path": "ntheader.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n)\n\n// ImageFileHeaderMachineType represents the type of the image file header `Machine“ field.\ntype ImageFileHeaderMachineType uint16\n\n// ImageFileHeaderCharacteristicsType represents the type of the image file header\n// `Characteristics` field.\ntype ImageFileHeaderCharacteristicsType uint16\n\n// ImageOptionalHeaderSubsystemType represents the type of the optional header `Subsystem field.\ntype ImageOptionalHeaderSubsystemType uint16\n\n// ImageOptionalHeaderDllCharacteristicsType represents the type of the optional header `DllCharacteristics field.\ntype ImageOptionalHeaderDllCharacteristicsType uint16\n\n// ImageNtHeader represents the PE header and is the general term for a structure\n// named IMAGE_NT_HEADERS.\ntype ImageNtHeader struct {\n\t// Signature is a DWORD containing the value 50h, 45h, 00h, 00h.\n\tSignature uint32 `json:\"signature\"`\n\n\t// IMAGE_NT_HEADERS provides a standard COFF header. It is located\n\t// immediately after the PE signature. The COFF header provides the most\n\t// general characteristics of a PE/COFF file, applicable to both object and\n\t// executable files. It is represented with IMAGE_FILE_HEADER structure.\n\tFileHeader ImageFileHeader `json:\"file_header\"`\n\n\t// OptionalHeader is of type ImageOptionalHeader32 or ImageOptionalHeader64.\n\tOptionalHeader interface{} `json:\"optional_header\"`\n}\n\n// ImageFileHeader contains infos about the physical layout and properties of the\n// file.\ntype ImageFileHeader struct {\n\t// The number that identifies the type of target machine.\n\tMachine ImageFileHeaderMachineType `json:\"machine\"`\n\n\t// The number of sections. This indicates the size of the section table,\n\t// which immediately follows the headers.\n\tNumberOfSections uint16 `json:\"number_of_sections\"`\n\n\t// // The low 32 bits of the number of seconds since 00:00 January 1, 1970\n\t// (a C run-time time_t value), that indicates when the file was created.\n\tTimeDateStamp uint32 `json:\"time_date_stamp\"`\n\n\t// // The file offset of the COFF symbol table, or zero if no COFF symbol\n\t// table is present. This value should be zero for an image because COFF\n\t// debugging information is deprecated.\n\tPointerToSymbolTable uint32 `json:\"pointer_to_symbol_table\"`\n\n\t// The number of entries in the symbol table. This data can be used to\n\t// locate the string table, which immediately follows the symbol table.\n\t// This value should be zero for an image because COFF debugging information\n\t// is deprecated.\n\tNumberOfSymbols uint32 `json:\"number_of_symbols\"`\n\n\t// The size of the optional header, which is required for executable files\n\t// but not for object files. This value should be zero for an object file.\n\tSizeOfOptionalHeader uint16 `json:\"size_of_optional_header\"`\n\n\t// The flags that indicate the attributes of the file.\n\tCharacteristics ImageFileHeaderCharacteristicsType `json:\"characteristics\"`\n}\n\n// ImageOptionalHeader32 represents the PE32 format structure of the optional header.\n// PE32 contains this additional field, which is absent in PE32+.\ntype ImageOptionalHeader32 struct {\n\n\t// The unsigned integer that identifies the state of the image file.\n\t// The most common number is 0x10B, which identifies it as a normal\n\t// executable file. 0x107 identifies it as a ROM image, and 0x20B identifies\n\t// it as a PE32+ executable.\n\tMagic uint16 `json:\"magic\"`\n\n\t// Linker major version number. The VC++ linker sets this field to current\n\t// version of Visual Studio.\n\tMajorLinkerVersion uint8 `json:\"major_linker_version\"`\n\n\t// The linker minor version number.\n\tMinorLinkerVersion uint8 `json:\"minor_linker_version\"`\n\n\t// The size of the code (text) section, or the sum of all code sections\n\t// if there are multiple sections.\n\tSizeOfCode uint32 `json:\"size_of_code\"`\n\n\t// The size of the initialized data section (held in the field SizeOfRawData\n\t// of the respective section header), or the sum of all such sections if\n\t// there are multiple data sections.\n\tSizeOfInitializedData uint32 `json:\"size_of_initialized_data\"`\n\n\t// The size of the uninitialized data section (BSS), or the sum of all\n\t// such sections if there are multiple BSS sections. This data is not part\n\t// of the disk file and does not have specific values, but the OS loader\n\t// commits memory space for this data when the file is loaded.\n\tSizeOfUninitializedData uint32 `json:\"size_of_uninitialized_data\"`\n\n\t// The address of the entry point relative to the image base when the\n\t// executable file is loaded into memory. For program images, this is the\n\t// starting address. For device drivers, this is the address of the\n\t// initialization function. An entry point is optional for DLLs. When no\n\t// entry point is present, this field must be zero. For managed PE files,\n\t// this value always points to the common language runtime invocation stub.\n\tAddressOfEntryPoint uint32 `json:\"address_of_entrypoint\"`\n\n\t// The address that is relative to the image base of the beginning-of-code\n\t// section when it is loaded into memory.\n\tBaseOfCode uint32 `json:\"base_of_code\"`\n\n\t// The address that is relative to the image base of the beginning-of-data\n\t// section when it is loaded into memory. This entry doesn’t exist in the\n\t// 64-bit Optional header.\n\tBaseOfData uint32 `json:\"base_of_data\"`\n\n\t// The preferred address of the first byte of image when loaded into memory;\n\t// must be a multiple of 64 K. The default for DLLs is 0x10000000. The\n\t// default for Windows CE EXEs is 0x00010000. The default for Windows NT,\n\t// Windows 2000, Windows XP, Windows 95, Windows 98, and Windows Me is\n\t// 0x00400000.\n\tImageBase uint32 `json:\"image_base\"`\n\n\t// The alignment (in bytes) of sections when they are loaded into memory.\n\t// It must be greater than or equal to FileAlignment. The default is the\n\t// page size for the architecture.\n\tSectionAlignment uint32 `json:\"section_alignment\"`\n\n\t// The alignment factor (in bytes) that is used to align the raw data of\n\t// sections in the image file. The value should be a power of 2 between 512\n\t// and 64 K, inclusive. The default is 512. If the SectionAlignment is less\n\t// than the architecture's page size, then FileAlignment must match\n\t// SectionAlignment.\n\tFileAlignment uint32 `json:\"file_alignment\"`\n\n\t// The major version number of the required operating system.\n\tMajorOperatingSystemVersion uint16 `json:\"major_os_version\"`\n\n\t// The minor version number of the required operating system.\n\tMinorOperatingSystemVersion uint16 `json:\"minor_os_version\"`\n\n\t// The major version number of the image.\n\tMajorImageVersion uint16 `json:\"major_image_version\"`\n\n\t// The minor version number of the image.\n\tMinorImageVersion uint16 `json:\"minor_image_version\"`\n\n\t// The major version number of the subsystem.\n\tMajorSubsystemVersion uint16 `json:\"major_subsystem_version\"`\n\n\t// The minor version number of the subsystem.\n\tMinorSubsystemVersion uint16 `json:\"minor_subsystem_version\"`\n\n\t// Reserved, must be zero.\n\tWin32VersionValue uint32 `json:\"win32_version_value\"`\n\n\t// The size (in bytes) of the image, including all headers, as the image\n\t// is loaded in memory. It must be a multiple of SectionAlignment.\n\tSizeOfImage uint32 `json:\"size_of_image\"`\n\n\t// The combined size of an MS-DOS stub, PE header, and section headers\n\t// rounded up to a multiple of FileAlignment.\n\tSizeOfHeaders uint32 `json:\"size_of_headers\"`\n\n\t// The image file checksum. The algorithm for computing the checksum is\n\t// incorporated into IMAGHELP.DLL. The following are checked for validation\n\t// at load time: all drivers, any DLL loaded at boot time, and any DLL\n\t// that is loaded into a critical Windows process.\n\tCheckSum uint32 `json:\"checksum\"`\n\n\t// The subsystem that is required to run this image.\n\tSubsystem ImageOptionalHeaderSubsystemType `json:\"subsystem\"`\n\n\t// For more information, see DLL Characteristics later in this specification.\n\tDllCharacteristics ImageOptionalHeaderDllCharacteristicsType `json:\"dll_characteristics\"`\n\n\t// Size of virtual memory to reserve for the initial thread’s stack. Only\n\t// the SizeOfStackCommit field is committed; the rest is available in\n\t// one-page increments. The default is 1MB for 32-bit images and 4MB for\n\t// 64-bit images.\n\tSizeOfStackReserve uint32 `json:\"size_of_stack_reserve\"`\n\n\t// Size of virtual memory initially committed for the initial thread’s\n\t// stack. The default is one page (4KB) for 32-bit images and 16KB for\n\t// 64-bit images.\n\tSizeOfStackCommit uint32 `json:\"size_of_stack_commit\"`\n\n\t// size of the local heap space to reserve. Only SizeOfHeapCommit is\n\t// committed; the rest is made available one page at a time until the\n\t// reserve size is reached. The default is 1MB for both 32-bit and 64-bit\n\t// images.\n\tSizeOfHeapReserve uint32 `json:\"size_of_heap_reserve\"`\n\n\t// Size of virtual memory initially committed for the process heap. The\n\t// default is 4KB (one operating system memory page) for 32-bit images and\n\t// 16KB for 64-bit images.\n\tSizeOfHeapCommit uint32 `json:\"size_of_heap_commit\"`\n\n\t// Reserved, must be zero.\n\tLoaderFlags uint32 `json:\"loader_flags\"`\n\n\t// Number of entries in the DataDirectory array; at least 16. Although it\n\t// is theoretically possible to emit more than 16 data directories, all\n\t// existing managed compilers emit exactly 16 data directories, with the\n\t// 16th (last) data directory never used (reserved).\n\tNumberOfRvaAndSizes uint32 `json:\"number_of_rva_and_sizes\"`\n\n\t// An array of 16 IMAGE_DATA_DIRECTORY structures.\n\tDataDirectory [16]DataDirectory `json:\"data_directories\"`\n}\n\n// ImageOptionalHeader64 represents the PE32+ format structure of the optional header.\ntype ImageOptionalHeader64 struct {\n\t// The unsigned integer that identifies the state of the image file.\n\t// The most common number is 0x10B, which identifies it as a normal\n\t// executable file. 0x107 identifies it as a ROM image, and 0x20B identifies\n\t// it as a PE32+ executable.\n\tMagic uint16 `json:\"magic\"`\n\n\t// Linker major version number. The VC++ linker sets this field to current\n\t// version of Visual Studio.\n\tMajorLinkerVersion uint8 `json:\"major_linker_version\"`\n\n\t// The linker minor version number.\n\tMinorLinkerVersion uint8 `json:\"minor_linker_version\"`\n\n\t// The size of the code (text) section, or the sum of all code sections\n\t// if there are multiple sections.\n\tSizeOfCode uint32 `json:\"size_of_code\"`\n\n\t// The size of the initialized data section (held in the field SizeOfRawData\n\t// of the respective section header), or the sum of all such sections if\n\t// there are multiple data sections.\n\tSizeOfInitializedData uint32 `json:\"size_of_initialized_data\"`\n\n\t// The size of the uninitialized data section (BSS), or the sum of all\n\t// such sections if there are multiple BSS sections. This data is not part\n\t// of the disk file and does not have specific values, but the OS loader\n\t// commits memory space for this data when the file is loaded.\n\tSizeOfUninitializedData uint32 `json:\"size_of_uninitialized_data\"`\n\n\t// The address of the entry point relative to the image base when the\n\t// executable file is loaded into memory. For program images, this is the\n\t// starting address. For device drivers, this is the address of the\n\t// initialization function. An entry point is optional for DLLs. When no\n\t// entry point is present, this field must be zero. For managed PE files,\n\t// this value always points to the common language runtime invocation stub.\n\tAddressOfEntryPoint uint32 `json:\"address_of_entrypoint\"`\n\n\t// The address that is relative to the image base of the beginning-of-code\n\t// section when it is loaded into memory.\n\tBaseOfCode uint32 `json:\"base_of_code\"`\n\n\t// In PE+, ImageBase is 8 bytes size.\n\tImageBase uint64 `json:\"image_base\"`\n\n\t// The alignment (in bytes) of sections when they are loaded into memory.\n\t// It must be greater than or equal to FileAlignment. The default is the\n\t// page size for the architecture.\n\tSectionAlignment uint32 `json:\"section_alignment\"`\n\n\t// The alignment factor (in bytes) that is used to align the raw data of\n\t// sections in the image file. The value should be a power of 2 between 512\n\t// and 64 K, inclusive. The default is 512. If the SectionAlignment is less\n\t// than the architecture's page size, then FileAlignment must match SectionAlignment.\n\tFileAlignment uint32 `json:\"file_alignment\"`\n\n\t// The major version number of the required operating system.\n\tMajorOperatingSystemVersion uint16 `json:\"major_os_version\"`\n\n\t// The minor version number of the required operating system.\n\tMinorOperatingSystemVersion uint16 `json:\"minor_os_version\"`\n\n\t// The major version number of the image.\n\tMajorImageVersion uint16 `json:\"major_image_version\"`\n\n\t// The minor version number of the image.\n\tMinorImageVersion uint16 `json:\"minor_image_version\"`\n\n\t// The major version number of the subsystem.\n\tMajorSubsystemVersion uint16 `json:\"major_subsystem_version\"`\n\n\t// The minor version number of the subsystem.\n\tMinorSubsystemVersion uint16 `json:\"minor_subsystem_version\"`\n\n\t// Reserved, must be zero.\n\tWin32VersionValue uint32 `json:\"win32_version_value\"`\n\n\t// The size (in bytes) of the image, including all headers, as the image\n\t// is loaded in memory. It must be a multiple of SectionAlignment.\n\tSizeOfImage uint32 `json:\"size_of_image\"`\n\n\t// The combined size of an MS-DOS stub, PE header, and section headers\n\t// rounded up to a multiple of FileAlignment.\n\tSizeOfHeaders uint32 `json:\"size_of_headers\"`\n\n\t// The image file checksum. The algorithm for computing the checksum is\n\t// incorporated into IMAGHELP.DLL. The following are checked for validation\n\t// at load time: all drivers, any DLL loaded at boot time, and any DLL\n\t// that is loaded into a critical Windows process.\n\tCheckSum uint32 `json:\"checksum\"`\n\n\t// The subsystem that is required to run this image.\n\tSubsystem ImageOptionalHeaderSubsystemType `json:\"subsystem\"`\n\n\t// For more information, see DLL Characteristics later in this specification.\n\tDllCharacteristics ImageOptionalHeaderDllCharacteristicsType `json:\"dll_characteristics\"`\n\n\t// Size of virtual memory to reserve for the initial thread’s stack. Only\n\t// the SizeOfStackCommit field is committed; the rest is available in\n\t// one-page increments. The default is 1MB for 32-bit images and 4MB for\n\t// 64-bit images.\n\tSizeOfStackReserve uint64 `json:\"size_of_stack_reserve\"`\n\n\t// Size of virtual memory initially committed for the initial thread’s\n\t// stack. The default is one page (4KB) for 32-bit images and 16KB for\n\t// 64-bit images.\n\tSizeOfStackCommit uint64 `json:\"size_of_stack_commit\"`\n\n\t// size of the local heap space to reserve. Only SizeOfHeapCommit is\n\t// committed; the rest is made available one page at a time until the\n\t// reserve size is reached. The default is 1MB for both 32-bit and 64-bit\n\t// images.\n\tSizeOfHeapReserve uint64 `json:\"size_of_heap_reserve\"`\n\n\t// Size of virtual memory initially committed for the process heap. The\n\t// default is 4KB (one operating system memory page) for 32-bit images and\n\t// 16KB for 64-bit images.\n\tSizeOfHeapCommit uint64 `json:\"size_of_heap_commit\"`\n\n\t// Reserved, must be zero.\n\tLoaderFlags uint32 `json:\"loader_flags\"`\n\n\t// Number of entries in the DataDirectory array; at least 16. Although it\n\t// is theoretically possible to emit more than 16 data directories, all\n\t// existing managed compilers emit exactly 16 data directories, with the\n\t// 16th (last) data directory never used (reserved).\n\tNumberOfRvaAndSizes uint32 `json:\"number_of_rva_and_sizes\"`\n\n\t// An array of 16 IMAGE_DATA_DIRECTORY structures.\n\tDataDirectory [16]DataDirectory `json:\"data_directories\"`\n}\n\n// DataDirectory represents an array of 16 IMAGE_DATA_DIRECTORY structures,\n// 8 bytes apiece, each relating to an important data structure in the PE file.\n// The data directory table starts at offset 96 in a 32-bit PE header and at\n// offset 112 in a 64-bit PE header. Each entry in the data directory table\n// contains the RVA and size of a table or a string that this particular\n// directory entry describes;this information is used by the operating system.\ntype DataDirectory struct {\n\t// The RVA of the data structure.\n\tVirtualAddress uint32 `json:\"virtual_address\"`\n\t// The size in bytes of the data structure referred to.\n\tSize uint32 `json:\"size\"`\n}\n\n// ParseNTHeader parse the PE NT header structure referred as IMAGE_NT_HEADERS.\n// Its offset is given by the e_lfanew field in the IMAGE_DOS_HEADER at the\n// beginning of the file.\nfunc (pe *File) ParseNTHeader() (err error) {\n\tntHeaderOffset := pe.DOSHeader.AddressOfNewEXEHeader\n\tsignature, err := pe.ReadUint32(ntHeaderOffset)\n\tif err != nil {\n\t\treturn ErrInvalidNtHeaderOffset\n\t}\n\n\t// Probe for PE signature.\n\tif signature&0xFFFF == ImageOS2Signature {\n\t\treturn ErrImageOS2SignatureFound\n\t}\n\tif signature&0xFFFF == ImageOS2LESignature {\n\t\treturn ErrImageOS2LESignatureFound\n\t}\n\tif signature&0xFFFF == ImageVXDSignature {\n\t\treturn ErrImageVXDSignatureFound\n\t}\n\tif signature&0xFFFF == ImageTESignature {\n\t\treturn ErrImageTESignatureFound\n\t}\n\n\t// This is the smallest requirement for a valid PE.\n\tif signature != ImageNTSignature {\n\t\treturn ErrImageNtSignatureNotFound\n\t}\n\tpe.NtHeader.Signature = signature\n\n\t// The file header structure contains some basic information about the file;\n\t// most importantly, a field describing the size of the optional data that\n\t// follows it.\n\tfileHeaderSize := uint32(binary.Size(pe.NtHeader.FileHeader))\n\tfileHeaderOffset := ntHeaderOffset + 4\n\terr = pe.structUnpack(&pe.NtHeader.FileHeader, fileHeaderOffset, fileHeaderSize)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// The PE header which immediately follows the COFF header, provides\n\t// information for the OS loader. Although this header is referred to as\n\t// the optional header, it is optional only in the sense that object files\n\t// usually don’t contain it. For PE files, this header is mandatory.\n\t// The size of the PE header is not fixed. It depends on the number of data\n\t// directories defined in the header and is specified in the\n\t// SizeOfOptionalHeader field of the COFF header.\n\t// The optional header could be either for a PE or PE+ file.\n\toh32 := ImageOptionalHeader32{}\n\toh64 := ImageOptionalHeader64{}\n\n\toptHeaderOffset := ntHeaderOffset + (fileHeaderSize + 4)\n\tmagic, err := pe.ReadUint16(optHeaderOffset)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Probes for PE32/PE32+ optional header magic.\n\tif magic != ImageNtOptionalHeader32Magic &&\n\t\tmagic != ImageNtOptionalHeader64Magic {\n\t\treturn ErrImageNtOptionalHeaderMagicNotFound\n\t}\n\n\t// Are we dealing with a PE64 optional header.\n\tswitch magic {\n\tcase ImageNtOptionalHeader64Magic:\n\t\tsize := uint32(binary.Size(oh64))\n\t\terr = pe.structUnpack(&oh64, optHeaderOffset, size)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpe.Is64 = true\n\t\tpe.NtHeader.OptionalHeader = oh64\n\tcase ImageNtOptionalHeader32Magic:\n\t\tsize := uint32(binary.Size(oh32))\n\t\terr = pe.structUnpack(&oh32, optHeaderOffset, size)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tpe.Is32 = true\n\t\tpe.NtHeader.OptionalHeader = oh32\n\t}\n\n\t// ImageBase should be multiple of 10000h.\n\tif (pe.Is64 && oh64.ImageBase%0x10000 != 0) || (pe.Is32 && oh32.ImageBase%0x10000 != 0) {\n\t\treturn ErrImageBaseNotAligned\n\t}\n\n\t// ImageBase can be any value as long as:\n\t// ImageBase + SizeOfImage < 80000000h for PE32.\n\t// ImageBase + SizeOfImage < 0xffff080000000000 for PE32+.\n\tif (pe.Is32 && oh32.ImageBase+oh32.SizeOfImage >= 0x80000000) || (pe.Is64 && oh64.ImageBase+uint64(oh64.SizeOfImage) >= 0xffff080000000000) {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoImageBaseOverflow)\n\t}\n\n\tpe.HasNTHdr = true\n\treturn nil\n}\n\n// String returns the string representations of the `Machine` field of the IMAGE_FILE_HEADER.\nfunc (t ImageFileHeaderMachineType) String() string {\n\tmachineType := map[ImageFileHeaderMachineType]string{\n\t\tImageFileMachineUnknown:   \"Unknown\",\n\t\tImageFileMachineAM33:      \"Matsushita AM33\",\n\t\tImageFileMachineAMD64:     \"x64\",\n\t\tImageFileMachineARM:       \"ARM little endian\",\n\t\tImageFileMachineARM64:     \"ARM64 little endian\",\n\t\tImageFileMachineARMNT:     \"ARM Thumb-2 little endian\",\n\t\tImageFileMachineEBC:       \"EFI byte code\",\n\t\tImageFileMachineI386:      \"Intel 386 or later / compatible processors\",\n\t\tImageFileMachineIA64:      \"Intel Itanium processor family\",\n\t\tImageFileMachineM32R:      \"Mitsubishi M32R little endian\",\n\t\tImageFileMachineMIPS16:    \"MIPS16\",\n\t\tImageFileMachineMIPSFPU:   \"MIPS with FPU\",\n\t\tImageFileMachineMIPSFPU16: \"MIPS16 with FPU\",\n\t\tImageFileMachinePowerPC:   \"Power PC little endian\",\n\t\tImageFileMachinePowerPCFP: \"Power PC with floating point support\",\n\t\tImageFileMachineR4000:     \"MIPS little endian\",\n\t\tImageFileMachineRISCV32:   \"RISC-V 32-bit address space\",\n\t\tImageFileMachineRISCV64:   \"RISC-V 64-bit address space\",\n\t\tImageFileMachineRISCV128:  \"RISC-V 128-bit address space\",\n\t\tImageFileMachineSH3:       \"Hitachi SH3\",\n\t\tImageFileMachineSH3DSP:    \"Hitachi SH3 DSP\",\n\t\tImageFileMachineSH4:       \"Hitachi SH4\",\n\t\tImageFileMachineSH5:       \"Hitachi SH5\",\n\t\tImageFileMachineTHUMB:     \"Thumb\",\n\t\tImageFileMachineWCEMIPSv2: \"MIPS little-endian WCE v2\",\n\t}\n\n\tif val, ok := machineType[t]; ok {\n\t\treturn val\n\t}\n\treturn \"?\"\n}\n\n// String returns the string representations of the `Characteristics` field of the IMAGE_FILE_HEADER.\nfunc (t ImageFileHeaderCharacteristicsType) String() []string {\n\tvar values []string\n\tfileHeaderCharacteristics := map[ImageFileHeaderCharacteristicsType]string{\n\t\tImageFileRelocsStripped:       \"RelocsStripped\",\n\t\tImageFileExecutableImage:      \"ExecutableImage\",\n\t\tImageFileLineNumsStripped:     \"LineNumsStripped\",\n\t\tImageFileLocalSymsStripped:    \"LocalSymsStripped\",\n\t\tImageFileAggressiveWSTrim:     \"AgressibeWsTrim\",\n\t\tImageFileLargeAddressAware:    \"LargeAddressAware\",\n\t\tImageFileBytesReservedLow:     \"BytesReservedLow\",\n\t\tImageFile32BitMachine:         \"32BitMachine\",\n\t\tImageFileDebugStripped:        \"DebugStripped\",\n\t\tImageFileRemovableRunFromSwap: \"RemovableRunFromSwap\",\n\t\tImageFileSystem:               \"FileSystem\",\n\t\tImageFileDLL:                  \"DLL\",\n\t\tImageFileUpSystemOnly:         \"UpSystemOnly\",\n\t\tImageFileBytesReservedHigh:    \"BytesReservedHigh\",\n\t}\n\n\tfor k, s := range fileHeaderCharacteristics {\n\t\tif k&t != 0 {\n\t\t\tvalues = append(values, s)\n\t\t}\n\t}\n\n\treturn values\n}\n\n// String returns the string representations of the `DllCharacteristics` field of ImageOptionalHeader.\nfunc (t ImageOptionalHeaderDllCharacteristicsType) String() []string {\n\tvar values []string\n\n\timgDllCharacteristics := map[ImageOptionalHeaderDllCharacteristicsType]string{\n\t\tImageDllCharacteristicsHighEntropyVA:        \"HighEntropyVA\",\n\t\tImageDllCharacteristicsDynamicBase:          \"DynamicBase\",\n\t\tImageDllCharacteristicsForceIntegrity:       \"ForceIntegrity\",\n\t\tImageDllCharacteristicsNXCompact:            \"NXCompact\",\n\t\tImageDllCharacteristicsNoIsolation:          \"NoIsolation\",\n\t\tImageDllCharacteristicsNoSEH:                \"NoSEH\",\n\t\tImageDllCharacteristicsNoBind:               \"NoBind\",\n\t\tImageDllCharacteristicsAppContainer:         \"AppContainer\",\n\t\tImageDllCharacteristicsWdmDriver:            \"WdmDriver\",\n\t\tImageDllCharacteristicsGuardCF:              \"GuardCF\",\n\t\tImageDllCharacteristicsTerminalServiceAware: \"TerminalServiceAware\",\n\t}\n\n\tfor k, s := range imgDllCharacteristics {\n\t\tif k&t != 0 {\n\t\t\tvalues = append(values, s)\n\t\t}\n\t}\n\n\treturn values\n}\n\n// String returns the string representations of the `Subsystem` field\n// of ImageOptionalHeader.\nfunc (subsystem ImageOptionalHeaderSubsystemType) String() string {\n\tsubsystemMap := map[ImageOptionalHeaderSubsystemType]string{\n\t\tImageSubsystemUnknown:                \"Unknown\",\n\t\tImageSubsystemNative:                 \"Native\",\n\t\tImageSubsystemWindowsGUI:             \"Windows GUI\",\n\t\tImageSubsystemWindowsCUI:             \"Windows CUI\",\n\t\tImageSubsystemOS2CUI:                 \"OS/2 character\",\n\t\tImageSubsystemPosixCUI:               \"POSIX character\",\n\t\tImageSubsystemNativeWindows:          \"Native Win9x driver\",\n\t\tImageSubsystemWindowsCEGUI:           \"Windows CE GUI\",\n\t\tImageSubsystemEFIApplication:         \"EFI Application\",\n\t\tImageSubsystemEFIBootServiceDriver:   \"EFI Boot Service Driver\",\n\t\tImageSubsystemEFIRuntimeDriver:       \"EFI ROM image\",\n\t\tImageSubsystemEFIRom:                 \"EFI ROM image\",\n\t\tImageSubsystemXBOX:                   \"XBOX\",\n\t\tImageSubsystemWindowsBootApplication: \"Windows boot application\",\n\t}\n\n\tif val, ok := subsystemMap[subsystem]; ok {\n\t\treturn val\n\t}\n\n\treturn \"?\"\n}\n\n// PrettyOptionalHeaderMagic returns the string representations of the\n// `Magic` field of ImageOptionalHeader.\nfunc (pe *File) PrettyOptionalHeaderMagic() string {\n\n\tvar magic uint16\n\n\tif pe.Is64 {\n\t\tmagic =\n\t\t\tpe.NtHeader.OptionalHeader.(ImageOptionalHeader64).Magic\n\t} else {\n\t\tmagic =\n\t\t\tpe.NtHeader.OptionalHeader.(ImageOptionalHeader32).Magic\n\t}\n\n\tswitch magic {\n\tcase ImageNtOptionalHeader32Magic:\n\t\treturn \"PE32\"\n\tcase ImageNtOptionalHeader64Magic:\n\t\treturn \"PE64\"\n\tcase ImageROMOptionalHeaderMagic:\n\t\treturn \"ROM\"\n\tdefault:\n\t\treturn \"?\"\n\t}\n}\n"
  },
  {
    "path": "ntheader_test.go",
    "content": "// Copyright 2021 Saferwall. All rights reserved.\r\n// Use of this source code is governed by Apache v2 license\r\n// license that can be found in the LICENSE file.\r\n\r\npackage pe\r\n\r\nimport (\r\n\t\"reflect\"\r\n\t\"sort\"\r\n\t\"strconv\"\r\n\t\"testing\"\r\n)\r\n\r\nfunc TestParseNtHeaderNE(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout error\r\n\t}{\r\n\t\t{\r\n\t\t\t// This is an NE executable file. Extracted from Windows CE 2.0.\r\n\t\t\tgetAbsoluteFilePath(\"test/_setup.dll\"),\r\n\t\t\tErrImageOS2SignatureFound,\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != tt.out {\r\n\t\t\t\tt.Fatalf(\"parsing nt header failed, got %v, want %v\", err, tt.out)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestNtHeaderMachineType(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  ImageFileHeaderMachineType\r\n\t\tout string\r\n\t}{\r\n\t\t{\r\n\t\t\tImageFileHeaderMachineType(0x8664), \"x64\",\r\n\t\t},\r\n\t\t{\r\n\t\t\tImageFileHeaderMachineType(0xffff), \"?\",\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tname := \"CaseNtHeaderMachineTypeEqualTo_\" + strconv.Itoa(int(tt.in))\r\n\t\tt.Run(name, func(t *testing.T) {\r\n\r\n\t\t\tgot := tt.in.String()\r\n\t\t\tif got != tt.out {\r\n\t\t\t\tt.Errorf(\"nt header machine type assertion failed, got %v, want %v\",\r\n\t\t\t\t\tgot, tt.out)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestNtHeaderCharacteristicsType(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  ImageFileHeaderCharacteristicsType\r\n\t\tout []string\r\n\t}{\r\n\t\t{\r\n\t\t\tImageFileHeaderCharacteristicsType(0x0022), []string{\"ExecutableImage\", \"LargeAddressAware\"},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tname := \"CaseNtHeaderCharacteristicsTypeEqualTo_\" + strconv.Itoa(int(tt.in))\r\n\t\tt.Run(name, func(t *testing.T) {\r\n\t\t\tgot := tt.in.String()\r\n\t\t\tsort.Strings(got)\r\n\t\t\tsort.Strings(tt.out)\r\n\t\t\tif !reflect.DeepEqual(got, tt.out) {\r\n\t\t\t\tt.Errorf(\"nt header Characteristics type assertion failed, got %v, want %v\",\r\n\t\t\t\t\tgot, tt.out)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestOptionalHeaderSubsystemType(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  ImageOptionalHeaderSubsystemType\r\n\t\tout string\r\n\t}{\r\n\t\t{\r\n\t\t\tImageOptionalHeaderSubsystemType(0x2), \"Windows GUI\",\r\n\t\t},\r\n\t\t{\r\n\t\t\tImageOptionalHeaderSubsystemType(0xff), \"?\",\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tname := \"CaseOptionalHeaderSubsystemTypeEqualTo_\" + strconv.Itoa(int(tt.in))\r\n\t\tt.Run(name, func(t *testing.T) {\r\n\t\t\tgot := tt.in.String()\r\n\t\t\tif got != tt.out {\r\n\t\t\t\tt.Errorf(\"optional header subsystem type assertion failed, got %v, want %v\",\r\n\t\t\t\t\tgot, tt.out)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestOptionalHeaderDllCharacteristicsType(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  ImageOptionalHeaderDllCharacteristicsType\r\n\t\tout []string\r\n\t}{\r\n\t\t{\r\n\t\t\tImageOptionalHeaderDllCharacteristicsType(0x8160),\r\n\t\t\t[]string{\"DynamicBase\", \"HighEntropyVA\", \"NXCompact\", \"TerminalServiceAware\"},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tname := \"CaseOptionalHeaderDllCharacteristicsTypeEqualTo_\" + strconv.Itoa(int(tt.in))\r\n\t\tt.Run(name, func(t *testing.T) {\r\n\t\t\tgot := tt.in.String()\r\n\t\t\tsort.Strings(got)\r\n\t\t\tsort.Strings(tt.out)\r\n\t\t\tif !reflect.DeepEqual(got, tt.out) {\r\n\t\t\t\tt.Errorf(\"optional header dll characteristics type assertion failed, got %v, want %v\",\r\n\t\t\t\t\tgot, tt.out)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n"
  },
  {
    "path": "ordlookup.go",
    "content": "// Copyright 2021 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\n// WS232OrdNames maps ordinals to name.\nvar WS232OrdNames = map[uint64]string{\n\t1:   \"accept\",\n\t2:   \"bind\",\n\t3:   \"closesocket\",\n\t4:   \"connect\",\n\t5:   \"getpeername\",\n\t6:   \"getsockname\",\n\t7:   \"getsockopt\",\n\t8:   \"htonl\",\n\t9:   \"htons\",\n\t10:  \"ioctlsocket\",\n\t11:  \"inet_addr\",\n\t12:  \"inet_ntoa\",\n\t13:  \"listen\",\n\t14:  \"ntohl\",\n\t15:  \"ntohs\",\n\t16:  \"recv\",\n\t17:  \"recvfrom\",\n\t18:  \"select\",\n\t19:  \"send\",\n\t20:  \"sendto\",\n\t21:  \"setsockopt\",\n\t22:  \"shutdown\",\n\t23:  \"socket\",\n\t24:  \"GetAddrInfoW\",\n\t25:  \"GetNameInfoW\",\n\t26:  \"WSApSetPostRoutine\",\n\t27:  \"FreeAddrInfoW\",\n\t28:  \"WPUCompleteOverlappedRequest\",\n\t29:  \"WSAAccept\",\n\t30:  \"WSAAddressToStringA\",\n\t31:  \"WSAAddressToStringW\",\n\t32:  \"WSACloseEvent\",\n\t33:  \"WSAConnect\",\n\t34:  \"WSACreateEvent\",\n\t35:  \"WSADuplicateSocketA\",\n\t36:  \"WSADuplicateSocketW\",\n\t37:  \"WSAEnumNameSpaceProvidersA\",\n\t38:  \"WSAEnumNameSpaceProvidersW\",\n\t39:  \"WSAEnumNetworkEvents\",\n\t40:  \"WSAEnumProtocolsA\",\n\t41:  \"WSAEnumProtocolsW\",\n\t42:  \"WSAEventSelect\",\n\t43:  \"WSAGetOverlappedResult\",\n\t44:  \"WSAGetQOSByName\",\n\t45:  \"WSAGetServiceClassInfoA\",\n\t46:  \"WSAGetServiceClassInfoW\",\n\t47:  \"WSAGetServiceClassNameByClassIdA\",\n\t48:  \"WSAGetServiceClassNameByClassIdW\",\n\t49:  \"WSAHtonl\",\n\t50:  \"WSAHtons\",\n\t51:  \"gethostbyaddr\",\n\t52:  \"gethostbyname\",\n\t53:  \"getprotobyname\",\n\t54:  \"getprotobynumber\",\n\t55:  \"getservbyname\",\n\t56:  \"getservbyport\",\n\t57:  \"gethostname\",\n\t58:  \"WSAInstallServiceClassA\",\n\t59:  \"WSAInstallServiceClassW\",\n\t60:  \"WSAIoctl\",\n\t61:  \"WSAJoinLeaf\",\n\t62:  \"WSALookupServiceBeginA\",\n\t63:  \"WSALookupServiceBeginW\",\n\t64:  \"WSALookupServiceEnd\",\n\t65:  \"WSALookupServiceNextA\",\n\t66:  \"WSALookupServiceNextW\",\n\t67:  \"WSANSPIoctl\",\n\t68:  \"WSANtohl\",\n\t69:  \"WSANtohs\",\n\t70:  \"WSAProviderConfigChange\",\n\t71:  \"WSARecv\",\n\t72:  \"WSARecvDisconnect\",\n\t73:  \"WSARecvFrom\",\n\t74:  \"WSARemoveServiceClass\",\n\t75:  \"WSAResetEvent\",\n\t76:  \"WSASend\",\n\t77:  \"WSASendDisconnect\",\n\t78:  \"WSASendTo\",\n\t79:  \"WSASetEvent\",\n\t80:  \"WSASetServiceA\",\n\t81:  \"WSASetServiceW\",\n\t82:  \"WSASocketA\",\n\t83:  \"WSASocketW\",\n\t84:  \"WSAStringToAddressA\",\n\t85:  \"WSAStringToAddressW\",\n\t86:  \"WSAWaitForMultipleEvents\",\n\t87:  \"WSCDeinstallProvider\",\n\t88:  \"WSCEnableNSProvider\",\n\t89:  \"WSCEnumProtocols\",\n\t90:  \"WSCGetProviderPath\",\n\t91:  \"WSCInstallNameSpace\",\n\t92:  \"WSCInstallProvider\",\n\t93:  \"WSCUnInstallNameSpace\",\n\t94:  \"WSCUpdateProvider\",\n\t95:  \"WSCWriteNameSpaceOrder\",\n\t96:  \"WSCWriteProviderOrder\",\n\t97:  \"freeaddrinfo\",\n\t98:  \"getaddrinfo\",\n\t99:  \"getnameinfo\",\n\t101: \"WSAAsyncSelect\",\n\t102: \"WSAAsyncGetHostByAddr\",\n\t103: \"WSAAsyncGetHostByName\",\n\t104: \"WSAAsyncGetProtoByNumber\",\n\t105: \"WSAAsyncGetProtoByName\",\n\t106: \"WSAAsyncGetServByPort\",\n\t107: \"WSAAsyncGetServByName\",\n\t108: \"WSACancelAsyncRequest\",\n\t109: \"WSASetBlockingHook\",\n\t110: \"WSAUnhookBlockingHook\",\n\t111: \"WSAGetLastError\",\n\t112: \"WSASetLastError\",\n\t113: \"WSACancelBlockingCall\",\n\t114: \"WSAIsBlocking\",\n\t115: \"WSAStartup\",\n\t116: \"WSACleanup\",\n\t151: \"__WSAFDIsSet\",\n\t500: \"WEP\",\n}\n\n// OleAut32OrdNames maps ordinals to names.\nvar OleAut32OrdNames = map[uint64]string{\n\t2:   \"SysAllocString\",\n\t3:   \"SysReAllocString\",\n\t4:   \"SysAllocStringLen\",\n\t5:   \"SysReAllocStringLen\",\n\t6:   \"SysFreeString\",\n\t7:   \"SysStringLen\",\n\t8:   \"VariantInit\",\n\t9:   \"VariantClear\",\n\t10:  \"VariantCopy\",\n\t11:  \"VariantCopyInd\",\n\t12:  \"VariantChangeType\",\n\t13:  \"VariantTimeToDosDateTime\",\n\t14:  \"DosDateTimeToVariantTime\",\n\t15:  \"SafeArrayCreate\",\n\t16:  \"SafeArrayDestroy\",\n\t17:  \"SafeArrayGetDim\",\n\t18:  \"SafeArrayGetElemsize\",\n\t19:  \"SafeArrayGetUBound\",\n\t20:  \"SafeArrayGetLBound\",\n\t21:  \"SafeArrayLock\",\n\t22:  \"SafeArrayUnlock\",\n\t23:  \"SafeArrayAccessData\",\n\t24:  \"SafeArrayUnaccessData\",\n\t25:  \"SafeArrayGetElement\",\n\t26:  \"SafeArrayPutElement\",\n\t27:  \"SafeArrayCopy\",\n\t28:  \"DispGetParam\",\n\t29:  \"DispGetIDsOfNames\",\n\t30:  \"DispInvoke\",\n\t31:  \"CreateDispTypeInfo\",\n\t32:  \"CreateStdDispatch\",\n\t33:  \"RegisterActiveObject\",\n\t34:  \"RevokeActiveObject\",\n\t35:  \"GetActiveObject\",\n\t36:  \"SafeArrayAllocDescriptor\",\n\t37:  \"SafeArrayAllocData\",\n\t38:  \"SafeArrayDestroyDescriptor\",\n\t39:  \"SafeArrayDestroyData\",\n\t40:  \"SafeArrayRedim\",\n\t41:  \"SafeArrayAllocDescriptorEx\",\n\t42:  \"SafeArrayCreateEx\",\n\t43:  \"SafeArrayCreateVectorEx\",\n\t44:  \"SafeArraySetRecordInfo\",\n\t45:  \"SafeArrayGetRecordInfo\",\n\t46:  \"VarParseNumFromStr\",\n\t47:  \"VarNumFromParseNum\",\n\t48:  \"VarI2FromUI1\",\n\t49:  \"VarI2FromI4\",\n\t50:  \"VarI2FromR4\",\n\t51:  \"VarI2FromR8\",\n\t52:  \"VarI2FromCy\",\n\t53:  \"VarI2FromDate\",\n\t54:  \"VarI2FromStr\",\n\t55:  \"VarI2FromDisp\",\n\t56:  \"VarI2FromBool\",\n\t57:  \"SafeArraySetIID\",\n\t58:  \"VarI4FromUI1\",\n\t59:  \"VarI4FromI2\",\n\t60:  \"VarI4FromR4\",\n\t61:  \"VarI4FromR8\",\n\t62:  \"VarI4FromCy\",\n\t63:  \"VarI4FromDate\",\n\t64:  \"VarI4FromStr\",\n\t65:  \"VarI4FromDisp\",\n\t66:  \"VarI4FromBool\",\n\t67:  \"SafeArrayGetIID\",\n\t68:  \"VarR4FromUI1\",\n\t69:  \"VarR4FromI2\",\n\t70:  \"VarR4FromI4\",\n\t71:  \"VarR4FromR8\",\n\t72:  \"VarR4FromCy\",\n\t73:  \"VarR4FromDate\",\n\t74:  \"VarR4FromStr\",\n\t75:  \"VarR4FromDisp\",\n\t76:  \"VarR4FromBool\",\n\t77:  \"SafeArrayGetVartype\",\n\t78:  \"VarR8FromUI1\",\n\t79:  \"VarR8FromI2\",\n\t80:  \"VarR8FromI4\",\n\t81:  \"VarR8FromR4\",\n\t82:  \"VarR8FromCy\",\n\t83:  \"VarR8FromDate\",\n\t84:  \"VarR8FromStr\",\n\t85:  \"VarR8FromDisp\",\n\t86:  \"VarR8FromBool\",\n\t87:  \"VarFormat\",\n\t88:  \"VarDateFromUI1\",\n\t89:  \"VarDateFromI2\",\n\t90:  \"VarDateFromI4\",\n\t91:  \"VarDateFromR4\",\n\t92:  \"VarDateFromR8\",\n\t93:  \"VarDateFromCy\",\n\t94:  \"VarDateFromStr\",\n\t95:  \"VarDateFromDisp\",\n\t96:  \"VarDateFromBool\",\n\t97:  \"VarFormatDateTime\",\n\t98:  \"VarCyFromUI1\",\n\t99:  \"VarCyFromI2\",\n\t100: \"VarCyFromI4\",\n\t101: \"VarCyFromR4\",\n\t102: \"VarCyFromR8\",\n\t103: \"VarCyFromDate\",\n\t104: \"VarCyFromStr\",\n\t105: \"VarCyFromDisp\",\n\t106: \"VarCyFromBool\",\n\t107: \"VarFormatNumber\",\n\t108: \"VarBstrFromUI1\",\n\t109: \"VarBstrFromI2\",\n\t110: \"VarBstrFromI4\",\n\t111: \"VarBstrFromR4\",\n\t112: \"VarBstrFromR8\",\n\t113: \"VarBstrFromCy\",\n\t114: \"VarBstrFromDate\",\n\t115: \"VarBstrFromDisp\",\n\t116: \"VarBstrFromBool\",\n\t117: \"VarFormatPercent\",\n\t118: \"VarBoolFromUI1\",\n\t119: \"VarBoolFromI2\",\n\t120: \"VarBoolFromI4\",\n\t121: \"VarBoolFromR4\",\n\t122: \"VarBoolFromR8\",\n\t123: \"VarBoolFromDate\",\n\t124: \"VarBoolFromCy\",\n\t125: \"VarBoolFromStr\",\n\t126: \"VarBoolFromDisp\",\n\t127: \"VarFormatCurrency\",\n\t128: \"VarWeekdayName\",\n\t129: \"VarMonthName\",\n\t130: \"VarUI1FromI2\",\n\t131: \"VarUI1FromI4\",\n\t132: \"VarUI1FromR4\",\n\t133: \"VarUI1FromR8\",\n\t134: \"VarUI1FromCy\",\n\t135: \"VarUI1FromDate\",\n\t136: \"VarUI1FromStr\",\n\t137: \"VarUI1FromDisp\",\n\t138: \"VarUI1FromBool\",\n\t139: \"VarFormatFromTokens\",\n\t140: \"VarTokenizeFormatString\",\n\t141: \"VarAdd\",\n\t142: \"VarAnd\",\n\t143: \"VarDiv\",\n\t144: \"DllCanUnloadNow\",\n\t145: \"DllGetClassObject\",\n\t146: \"DispCallFunc\",\n\t147: \"VariantChangeTypeEx\",\n\t148: \"SafeArrayPtrOfIndex\",\n\t149: \"SysStringByteLen\",\n\t150: \"SysAllocStringByteLen\",\n\t151: \"DllRegisterServer\",\n\t152: \"VarEqv\",\n\t153: \"VarIdiv\",\n\t154: \"VarImp\",\n\t155: \"VarMod\",\n\t156: \"VarMul\",\n\t157: \"VarOr\",\n\t158: \"VarPow\",\n\t159: \"VarSub\",\n\t160: \"CreateTypeLib\",\n\t161: \"LoadTypeLib\",\n\t162: \"LoadRegTypeLib\",\n\t163: \"RegisterTypeLib\",\n\t164: \"QueryPathOfRegTypeLib\",\n\t165: \"LHashValOfNameSys\",\n\t166: \"LHashValOfNameSysA\",\n\t167: \"VarXor\",\n\t168: \"VarAbs\",\n\t169: \"VarFix\",\n\t170: \"OaBuildVersion\",\n\t171: \"ClearCustData\",\n\t172: \"VarInt\",\n\t173: \"VarNeg\",\n\t174: \"VarNot\",\n\t175: \"VarRound\",\n\t176: \"VarCmp\",\n\t177: \"VarDecAdd\",\n\t178: \"VarDecDiv\",\n\t179: \"VarDecMul\",\n\t180: \"CreateTypeLib2\",\n\t181: \"VarDecSub\",\n\t182: \"VarDecAbs\",\n\t183: \"LoadTypeLibEx\",\n\t184: \"SystemTimeToVariantTime\",\n\t185: \"VariantTimeToSystemTime\",\n\t186: \"UnRegisterTypeLib\",\n\t187: \"VarDecFix\",\n\t188: \"VarDecInt\",\n\t189: \"VarDecNeg\",\n\t190: \"VarDecFromUI1\",\n\t191: \"VarDecFromI2\",\n\t192: \"VarDecFromI4\",\n\t193: \"VarDecFromR4\",\n\t194: \"VarDecFromR8\",\n\t195: \"VarDecFromDate\",\n\t196: \"VarDecFromCy\",\n\t197: \"VarDecFromStr\",\n\t198: \"VarDecFromDisp\",\n\t199: \"VarDecFromBool\",\n\t200: \"GetErrorInfo\",\n\t201: \"SetErrorInfo\",\n\t202: \"CreateErrorInfo\",\n\t203: \"VarDecRound\",\n\t204: \"VarDecCmp\",\n\t205: \"VarI2FromI1\",\n\t206: \"VarI2FromUI2\",\n\t207: \"VarI2FromUI4\",\n\t208: \"VarI2FromDec\",\n\t209: \"VarI4FromI1\",\n\t210: \"VarI4FromUI2\",\n\t211: \"VarI4FromUI4\",\n\t212: \"VarI4FromDec\",\n\t213: \"VarR4FromI1\",\n\t214: \"VarR4FromUI2\",\n\t215: \"VarR4FromUI4\",\n\t216: \"VarR4FromDec\",\n\t217: \"VarR8FromI1\",\n\t218: \"VarR8FromUI2\",\n\t219: \"VarR8FromUI4\",\n\t220: \"VarR8FromDec\",\n\t221: \"VarDateFromI1\",\n\t222: \"VarDateFromUI2\",\n\t223: \"VarDateFromUI4\",\n\t224: \"VarDateFromDec\",\n\t225: \"VarCyFromI1\",\n\t226: \"VarCyFromUI2\",\n\t227: \"VarCyFromUI4\",\n\t228: \"VarCyFromDec\",\n\t229: \"VarBstrFromI1\",\n\t230: \"VarBstrFromUI2\",\n\t231: \"VarBstrFromUI4\",\n\t232: \"VarBstrFromDec\",\n\t233: \"VarBoolFromI1\",\n\t234: \"VarBoolFromUI2\",\n\t235: \"VarBoolFromUI4\",\n\t236: \"VarBoolFromDec\",\n\t237: \"VarUI1FromI1\",\n\t238: \"VarUI1FromUI2\",\n\t239: \"VarUI1FromUI4\",\n\t240: \"VarUI1FromDec\",\n\t241: \"VarDecFromI1\",\n\t242: \"VarDecFromUI2\",\n\t243: \"VarDecFromUI4\",\n\t244: \"VarI1FromUI1\",\n\t245: \"VarI1FromI2\",\n\t246: \"VarI1FromI4\",\n\t247: \"VarI1FromR4\",\n\t248: \"VarI1FromR8\",\n\t249: \"VarI1FromDate\",\n\t250: \"VarI1FromCy\",\n\t251: \"VarI1FromStr\",\n\t252: \"VarI1FromDisp\",\n\t253: \"VarI1FromBool\",\n\t254: \"VarI1FromUI2\",\n\t255: \"VarI1FromUI4\",\n\t256: \"VarI1FromDec\",\n\t257: \"VarUI2FromUI1\",\n\t258: \"VarUI2FromI2\",\n\t259: \"VarUI2FromI4\",\n\t260: \"VarUI2FromR4\",\n\t261: \"VarUI2FromR8\",\n\t262: \"VarUI2FromDate\",\n\t263: \"VarUI2FromCy\",\n\t264: \"VarUI2FromStr\",\n\t265: \"VarUI2FromDisp\",\n\t266: \"VarUI2FromBool\",\n\t267: \"VarUI2FromI1\",\n\t268: \"VarUI2FromUI4\",\n\t269: \"VarUI2FromDec\",\n\t270: \"VarUI4FromUI1\",\n\t271: \"VarUI4FromI2\",\n\t272: \"VarUI4FromI4\",\n\t273: \"VarUI4FromR4\",\n\t274: \"VarUI4FromR8\",\n\t275: \"VarUI4FromDate\",\n\t276: \"VarUI4FromCy\",\n\t277: \"VarUI4FromStr\",\n\t278: \"VarUI4FromDisp\",\n\t279: \"VarUI4FromBool\",\n\t280: \"VarUI4FromI1\",\n\t281: \"VarUI4FromUI2\",\n\t282: \"VarUI4FromDec\",\n\t283: \"BSTR_UserSize\",\n\t284: \"BSTR_UserMarshal\",\n\t285: \"BSTR_UserUnmarshal\",\n\t286: \"BSTR_UserFree\",\n\t287: \"VARIANT_UserSize\",\n\t288: \"VARIANT_UserMarshal\",\n\t289: \"VARIANT_UserUnmarshal\",\n\t290: \"VARIANT_UserFree\",\n\t291: \"LPSAFEARRAY_UserSize\",\n\t292: \"LPSAFEARRAY_UserMarshal\",\n\t293: \"LPSAFEARRAY_UserUnmarshal\",\n\t294: \"LPSAFEARRAY_UserFree\",\n\t295: \"LPSAFEARRAY_Size\",\n\t296: \"LPSAFEARRAY_Marshal\",\n\t297: \"LPSAFEARRAY_Unmarshal\",\n\t298: \"VarDecCmpR8\",\n\t299: \"VarCyAdd\",\n\t300: \"DllUnregisterServer\",\n\t301: \"OACreateTypeLib2\",\n\t303: \"VarCyMul\",\n\t304: \"VarCyMulI4\",\n\t305: \"VarCySub\",\n\t306: \"VarCyAbs\",\n\t307: \"VarCyFix\",\n\t308: \"VarCyInt\",\n\t309: \"VarCyNeg\",\n\t310: \"VarCyRound\",\n\t311: \"VarCyCmp\",\n\t312: \"VarCyCmpR8\",\n\t313: \"VarBstrCat\",\n\t314: \"VarBstrCmp\",\n\t315: \"VarR8Pow\",\n\t316: \"VarR4CmpR8\",\n\t317: \"VarR8Round\",\n\t318: \"VarCat\",\n\t319: \"VarDateFromUdateEx\",\n\t322: \"GetRecordInfoFromGuids\",\n\t323: \"GetRecordInfoFromTypeInfo\",\n\t325: \"SetVarConversionLocaleSetting\",\n\t326: \"GetVarConversionLocaleSetting\",\n\t327: \"SetOaNoCache\",\n\t329: \"VarCyMulI8\",\n\t330: \"VarDateFromUdate\",\n\t331: \"VarUdateFromDate\",\n\t332: \"GetAltMonthNames\",\n\t333: \"VarI8FromUI1\",\n\t334: \"VarI8FromI2\",\n\t335: \"VarI8FromR4\",\n\t336: \"VarI8FromR8\",\n\t337: \"VarI8FromCy\",\n\t338: \"VarI8FromDate\",\n\t339: \"VarI8FromStr\",\n\t340: \"VarI8FromDisp\",\n\t341: \"VarI8FromBool\",\n\t342: \"VarI8FromI1\",\n\t343: \"VarI8FromUI2\",\n\t344: \"VarI8FromUI4\",\n\t345: \"VarI8FromDec\",\n\t346: \"VarI2FromI8\",\n\t347: \"VarI2FromUI8\",\n\t348: \"VarI4FromI8\",\n\t349: \"VarI4FromUI8\",\n\t360: \"VarR4FromI8\",\n\t361: \"VarR4FromUI8\",\n\t362: \"VarR8FromI8\",\n\t363: \"VarR8FromUI8\",\n\t364: \"VarDateFromI8\",\n\t365: \"VarDateFromUI8\",\n\t366: \"VarCyFromI8\",\n\t367: \"VarCyFromUI8\",\n\t368: \"VarBstrFromI8\",\n\t369: \"VarBstrFromUI8\",\n\t370: \"VarBoolFromI8\",\n\t371: \"VarBoolFromUI8\",\n\t372: \"VarUI1FromI8\",\n\t373: \"VarUI1FromUI8\",\n\t374: \"VarDecFromI8\",\n\t375: \"VarDecFromUI8\",\n\t376: \"VarI1FromI8\",\n\t377: \"VarI1FromUI8\",\n\t378: \"VarUI2FromI8\",\n\t379: \"VarUI2FromUI8\",\n\t401: \"OleLoadPictureEx\",\n\t402: \"OleLoadPictureFileEx\",\n\t411: \"SafeArrayCreateVector\",\n\t412: \"SafeArrayCopyData\",\n\t413: \"VectorFromBstr\",\n\t414: \"BstrFromVector\",\n\t415: \"OleIconToCursor\",\n\t416: \"OleCreatePropertyFrameIndirect\",\n\t417: \"OleCreatePropertyFrame\",\n\t418: \"OleLoadPicture\",\n\t419: \"OleCreatePictureIndirect\",\n\t420: \"OleCreateFontIndirect\",\n\t421: \"OleTranslateColor\",\n\t422: \"OleLoadPictureFile\",\n\t423: \"OleSavePictureFile\",\n\t424: \"OleLoadPicturePath\",\n\t425: \"VarUI4FromI8\",\n\t426: \"VarUI4FromUI8\",\n\t427: \"VarI8FromUI8\",\n\t428: \"VarUI8FromI8\",\n\t429: \"VarUI8FromUI1\",\n\t430: \"VarUI8FromI2\",\n\t431: \"VarUI8FromR4\",\n\t432: \"VarUI8FromR8\",\n\t433: \"VarUI8FromCy\",\n\t434: \"VarUI8FromDate\",\n\t435: \"VarUI8FromStr\",\n\t436: \"VarUI8FromDisp\",\n\t437: \"VarUI8FromBool\",\n\t438: \"VarUI8FromI1\",\n\t439: \"VarUI8FromUI2\",\n\t440: \"VarUI8FromUI4\",\n\t441: \"VarUI8FromDec\",\n\t442: \"RegisterTypeLibForUser\",\n\t443: \"UnRegisterTypeLibForUser\",\n}\n\n// OrdNames maps the dll names to ordinal names.\nvar OrdNames = map[string]map[uint64]string{\n\t\"ws2_32.dll\":   WS232OrdNames,\n\t\"wsock32.dll\":  WS232OrdNames,\n\t\"oleaut32.dll\": OleAut32OrdNames,\n}\n\n// OrdLookup returns API name given an ordinal.\nfunc OrdLookup(libname string, ord uint64, makeName bool) string {\n\tnames, ok := OrdNames[strings.ToLower(libname)]\n\tif ok {\n\t\tif name, ok := names[ord]; ok {\n\t\t\treturn name\n\t\t}\n\t}\n\tif makeName {\n\t\treturn fmt.Sprintf(\"ord%d\", ord)\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "overlay.go",
    "content": "// Copyright 2022 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"errors\"\n\t\"io\"\n)\n\n// error\nvar (\n\tErrNoOverlayFound = errors.New(\"pe does not have overlay data\")\n)\n\n// NewOverlayReader returns a new ReadSeeker reading the PE overlay data.\nfunc (pe *File) NewOverlayReader() (*io.SectionReader, error) {\n\tif pe.data == nil {\n\t\treturn nil, errors.New(\"pe: file reader is nil\")\n\t}\n\treturn io.NewSectionReader(pe.f, pe.OverlayOffset, 1<<63-1), nil\n}\n\n// Overlay returns the overlay of the PE file.\nfunc (pe *File) Overlay() ([]byte, error) {\n\tsr, err := pe.NewOverlayReader()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\toverlay := make([]byte, int64(pe.size)-pe.OverlayOffset)\n\tn, err := sr.ReadAt(overlay, 0)\n\tif n == len(overlay) {\n\t\tpe.HasOverlay = true\n\t\terr = nil\n\t}\n\n\treturn overlay, err\n}\n\nfunc (pe *File) OverlayLength() int64 {\n\treturn int64(pe.size) - pe.OverlayOffset\n}\n"
  },
  {
    "path": "overlay_test.go",
    "content": "package pe\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"testing\"\n)\n\ntype TestOverlay struct {\n\toverlayOffset int64\n\toverlayLength int64\n\tmd5str        string\n}\n\nvar overlayTests = []struct {\n\tin  string\n\tout TestOverlay\n}{\n\t{getAbsoluteFilePath(\"test/putty.exe\"),\n\t\tTestOverlay{\n\t\t\toverlayOffset: 1163264,\n\t\t\toverlayLength: 15760,\n\t\t\tmd5str:        \"1f46295a513e744895a6acf1029e136f\",\n\t\t}},\n}\n\nfunc TestFile_NewOverlayReader(t *testing.T) {\n\tfor _, tt := range overlayTests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tif err := file.Parse(); err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\tif file.OverlayOffset != tt.out.overlayOffset {\n\t\t\t\tt.Errorf(\"overlayLength failed, got %d, want %d\", file.OverlayOffset, tt.out.overlayOffset)\n\t\t\t}\n\n\t\t\toverlayLength := file.OverlayLength()\n\t\t\tif overlayLength != tt.out.overlayLength {\n\t\t\t\tt.Errorf(\"overlayOffset failed, got %d, want %d\", overlayLength, tt.out.overlayLength)\n\t\t\t}\n\n\t\t\toverlay, _ := file.Overlay()\n\t\t\th := md5.New()\n\t\t\th.Write(overlay)\n\t\t\tmd5str := hex.EncodeToString(h.Sum(nil))\n\t\t\tif md5str != tt.out.md5str {\n\t\t\t\tt.Errorf(\"overlayOffset failed, got %s, want %s\", md5str, tt.out.md5str)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pe.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\n// Image executable types\nconst (\n\n\t// The DOS MZ executable format is the executable file format used\n\t// for .EXE files in DOS.\n\tImageDOSSignature   = 0x5A4D // MZ\n\tImageDOSZMSignature = 0x4D5A // ZM\n\n\t// The New Executable (abbreviated NE or NewEXE) is a 16-bit .exe file\n\t// format, a successor to the DOS MZ executable format. It was used in\n\t// Windows 1.0–3.x, multitasking MS-DOS 4.0, OS/2 1.x, and the OS/2 subset\n\t// of Windows NT up to version 5.0 (Windows 2000). A NE is also called a\n\t// segmented executable.\n\tImageOS2Signature = 0x454E\n\n\t// Linear Executable is an executable file format in the EXE family.\n\t// It was used by 32-bit OS/2, by some DOS extenders, and by Microsoft\n\t// Windows VxD files. It is an extension of MS-DOS EXE, and a successor\n\t// to NE (New Executable).\n\tImageOS2LESignature = 0x454C\n\n\t// There are two main varieties of LE executables:\n\t// LX (32-bit), and LE (mixed 16/32-bit).\n\tImageVXDSignature = 0x584C\n\n\t// Terse Executables have a 'VZ' signature.\n\tImageTESignature = 0x5A56\n\n\t// The Portable Executable (PE) format is a file format for executables,\n\t// object code, DLLs and others used in 32-bit and 64-bit versions of\n\t// Windows operating systems.\n\tImageNTSignature = 0x00004550 // PE00\n)\n\n// Optional Header magic\nconst (\n\tImageNtOptionalHeader32Magic = 0x10b\n\tImageNtOptionalHeader64Magic = 0x20b\n\tImageROMOptionalHeaderMagic  = 0x10\n)\n\n// Image file machine types\nconst (\n\tImageFileMachineUnknown   = ImageFileHeaderMachineType(0x0)    // The contents of this field are assumed to be applicable to any machine type\n\tImageFileMachineAM33      = ImageFileHeaderMachineType(0x1d3)  // Matsushita AM33\n\tImageFileMachineAMD64     = ImageFileHeaderMachineType(0x8664) // x64\n\tImageFileMachineARM       = ImageFileHeaderMachineType(0x1c0)  // ARM little endian\n\tImageFileMachineARM64     = ImageFileHeaderMachineType(0xaa64) // ARM64 little endian\n\tImageFileMachineARMNT     = ImageFileHeaderMachineType(0x1c4)  // ARM Thumb-2 little endian\n\tImageFileMachineEBC       = ImageFileHeaderMachineType(0xebc)  // EFI byte code\n\tImageFileMachineI386      = ImageFileHeaderMachineType(0x14c)  // Intel 386 or later processors and compatible processors\n\tImageFileMachineIA64      = ImageFileHeaderMachineType(0x200)  // Intel Itanium processor family\n\tImageFileMachineM32R      = ImageFileHeaderMachineType(0x9041) // Mitsubishi M32R little endian\n\tImageFileMachineMIPS16    = ImageFileHeaderMachineType(0x266)  // MIPS16\n\tImageFileMachineMIPSFPU   = ImageFileHeaderMachineType(0x366)  // MIPS with FPU\n\tImageFileMachineMIPSFPU16 = ImageFileHeaderMachineType(0x466)  // MIPS16 with FPU\n\tImageFileMachinePowerPC   = ImageFileHeaderMachineType(0x1f0)  // Power PC little endian\n\tImageFileMachinePowerPCFP = ImageFileHeaderMachineType(0x1f1)  // Power PC with floating point support\n\tImageFileMachineR4000     = ImageFileHeaderMachineType(0x166)  // MIPS little endian\n\tImageFileMachineRISCV32   = ImageFileHeaderMachineType(0x5032) // RISC-V 32-bit address space\n\tImageFileMachineRISCV64   = ImageFileHeaderMachineType(0x5064) // RISC-V 64-bit address space\n\tImageFileMachineRISCV128  = ImageFileHeaderMachineType(0x5128) // RISC-V 128-bit address space\n\tImageFileMachineSH3       = ImageFileHeaderMachineType(0x1a2)  // Hitachi SH3\n\tImageFileMachineSH3DSP    = ImageFileHeaderMachineType(0x1a3)  // Hitachi SH3 DSP\n\tImageFileMachineSH4       = ImageFileHeaderMachineType(0x1a6)  // Hitachi SH4\n\tImageFileMachineSH5       = ImageFileHeaderMachineType(0x1a8)  // Hitachi SH5\n\tImageFileMachineTHUMB     = ImageFileHeaderMachineType(0x1c2)  // Thumb\n\tImageFileMachineWCEMIPSv2 = ImageFileHeaderMachineType(0x169)  // MIPS little-endian WCE v2\n)\n\n// The Characteristics field contains flags that indicate attributes of the object or image file.\nconst (\n\t// Image file only. This flag indicates that the file contains no base\n\t// relocations and must be loaded at its preferred base address. In the\n\t// case of base address conflict, the OS loader reports an error. This flag\n\t// should not be set for managed PE files.\n\tImageFileRelocsStripped = 0x0001\n\n\t// Flag indicates that the file is an image file (EXE or DLL). This flag\n\t// should be set for managed PE files. If it is not set, this generally\n\t// indicates a linker error (i.e. no unresolved external references).\n\tImageFileExecutableImage = 0x0002\n\n\t// COFF line numbers have been removed. This flag should be set for managed\n\t// PE files because they do not use the debug information embedded in the\n\t// PE file itself. Instead, the debug information is saved in accompanying\n\t// program database (PDB) files.\n\tImageFileLineNumsStripped = 0x0004\n\n\t// COFF symbol table entries for local symbols have been removed. This flag\n\t// should be set for managed PE files, for the reason given in the preceding\n\t// entry.\n\tImageFileLocalSymsStripped = 0x0008\n\n\t// Aggressively trim the working set.\n\tImageFileAggressiveWSTrim = 0x0010\n\n\t// Application can handle addresses beyond the 2GB range. This flag should\n\t// not be set for pure-IL managed PE files of versions 1.0 and 1.1 but can\n\t// be set for v2.0+ files.\n\tImageFileLargeAddressAware = 0x0020\n\n\t// Little endian.\n\tImageFileBytesReservedLow = 0x0080\n\n\t// Machine is based on 32-bit architecture. This flag is usually set by\n\t// the current versions of code generators producing managed PE files.\n\t// Version 2.0 and newer, however, can produce 64-bit specific images,\n\t// which don’t have this flag set.\n\tImageFile32BitMachine = 0x0100\n\n\t// Debug information has been removed from the image file.\n\tImageFileDebugStripped = 0x0200\n\n\t// If the image file is on removable media, copy and run it from the swap\n\t// file.\n\tImageFileRemovableRunFromSwap = 0x0400\n\n\t// If the image file is on a network, copy and run it from the swap file.\n\tImageFileNetRunFromSwap = 0x0800\n\n\t// The image file is a system file (for example, a device driver). This flag\n\tImageFileSystem = 0x1000\n\n\t// The image file is a DLL rather than an EXE. It cannot be directly run.\n\tImageFileDLL = 0x2000\n\n\t// The image file should be run on a uniprocessor machine only.\n\tImageFileUpSystemOnly = 0x4000\n\n\t// Big endian.\n\tImageFileBytesReservedHigh = 0x8000\n)\n\n// Subsystem values of an OptionalHeader.\nconst (\n\tImageSubsystemUnknown                = 0  // An unknown subsystem.\n\tImageSubsystemNative                 = 1  // Device drivers and native Windows processes\n\tImageSubsystemWindowsGUI             = 2  // The Windows graphical user interface (GUI) subsystem.\n\tImageSubsystemWindowsCUI             = 3  // The Windows character subsystem\n\tImageSubsystemOS2CUI                 = 5  // The OS/2 character subsystem.\n\tImageSubsystemPosixCUI               = 7  // The Posix character subsystem.\n\tImageSubsystemNativeWindows          = 8  // Native Win9x driver\n\tImageSubsystemWindowsCEGUI           = 9  // Windows CE\n\tImageSubsystemEFIApplication         = 10 // An Extensible Firmware Interface (EFI) application\n\tImageSubsystemEFIBootServiceDriver   = 11 // An EFI driver with boot services\n\tImageSubsystemEFIRuntimeDriver       = 12 // An EFI driver with run-time services\n\tImageSubsystemEFIRom                 = 13 // An EFI ROM image .\n\tImageSubsystemXBOX                   = 14 // XBOX.\n\tImageSubsystemWindowsBootApplication = 16 // Windows boot application.\n)\n\n// DllCharacteristics values of an OptionalHeader\nconst (\n\tImageDllCharacteristicsReserved1            = 0x0001 // Reserved, must be zero.\n\tImageDllCharacteristicsReserved2            = 0x0002 // Reserved, must be zero.\n\tImageDllCharacteristicsReserved4            = 0x0004 // Reserved, must be zero.\n\tImageDllCharacteristicsReserved8            = 0x0008 // Reserved, must be zero.\n\tImageDllCharacteristicsHighEntropyVA        = 0x0020 // Image can handle a high entropy 64-bit virtual address space\n\tImageDllCharacteristicsDynamicBase          = 0x0040 // DLL can be relocated at load time.\n\tImageDllCharacteristicsForceIntegrity       = 0x0080 // Code Integrity checks are enforced.\n\tImageDllCharacteristicsNXCompact            = 0x0100 // Image is NX compatible.\n\tImageDllCharacteristicsNoIsolation          = 0x0200 // Isolation aware, but do not isolate the image.\n\tImageDllCharacteristicsNoSEH                = 0x0400 // Does not use structured exception (SE) handling. No SE handler may be called in this image.\n\tImageDllCharacteristicsNoBind               = 0x0800 // Do not bind the image.\n\tImageDllCharacteristicsAppContainer         = 0x1000 // Image must execute in an AppContainer\n\tImageDllCharacteristicsWdmDriver            = 0x2000 // A WDM driver.\n\tImageDllCharacteristicsGuardCF              = 0x4000 // Image supports Control Flow Guard.\n\tImageDllCharacteristicsTerminalServiceAware = 0x8000 // Terminal Server aware.\n\n)\n\n// ImageDirectoryEntry represents an entry inside the data directories.\ntype ImageDirectoryEntry int\n\n// DataDirectory entries of an OptionalHeader\nconst (\n\tImageDirectoryEntryExport       ImageDirectoryEntry = iota // Export Table\n\tImageDirectoryEntryImport                                  // Import Table\n\tImageDirectoryEntryResource                                // Resource Table\n\tImageDirectoryEntryException                               // Exception Table\n\tImageDirectoryEntryCertificate                             // Certificate Directory\n\tImageDirectoryEntryBaseReloc                               // Base Relocation Table\n\tImageDirectoryEntryDebug                                   // Debug\n\tImageDirectoryEntryArchitecture                            // Architecture Specific Data\n\tImageDirectoryEntryGlobalPtr                               // The RVA of the value to be stored in the global pointer register.\n\tImageDirectoryEntryTLS                                     // The thread local storage (TLS) table\n\tImageDirectoryEntryLoadConfig                              // The load configuration table\n\tImageDirectoryEntryBoundImport                             // The bound import table\n\tImageDirectoryEntryIAT                                     // Import Address Table\n\tImageDirectoryEntryDelayImport                             // Delay Import Descriptor\n\tImageDirectoryEntryCLR                                     // CLR Runtime Header\n\tImageDirectoryEntryReserved                                // Must be zero\n\tImageNumberOfDirectoryEntries                              // Tables count.\n)\n\n// FileInfo represents the PE file information struct.\ntype FileInfo struct {\n\tIs32           bool\n\tIs64           bool\n\tHasDOSHdr      bool\n\tHasRichHdr     bool\n\tHasCOFF        bool\n\tHasNTHdr       bool\n\tHasSections    bool\n\tHasExport      bool\n\tHasImport      bool\n\tHasResource    bool\n\tHasException   bool\n\tHasCertificate bool\n\tHasReloc       bool\n\tHasDebug       bool\n\tHasArchitect   bool\n\tHasGlobalPtr   bool\n\tHasTLS         bool\n\tHasLoadCFG     bool\n\tHasBoundImp    bool\n\tHasIAT         bool\n\tHasDelayImp    bool\n\tHasCLR         bool\n\tHasOverlay     bool\n\tIsSigned       bool\n}\n"
  },
  {
    "path": "reloc.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n)\n\nvar (\n\t// ErrInvalidBaseRelocVA is reposed when base reloc lies outside of the image.\n\tErrInvalidBaseRelocVA = errors.New(\"invalid relocation information.\" +\n\t\t\" Base Relocation VirtualAddress is outside of PE Image\")\n\n\t// ErrInvalidBasicRelocSizeOfBloc is reposed when base reloc is too large.\n\tErrInvalidBasicRelocSizeOfBloc = errors.New(\"invalid relocation \" +\n\t\t\"information. Base Relocation SizeOfBlock too large\")\n)\n\n// ImageBaseRelocationEntryType represents the type of an in image base relocation entry.\ntype ImageBaseRelocationEntryType uint8\n\n// The Type field of the relocation record indicates what kind of relocation\n// should be performed. Different relocation types are defined for each type\n// of machine.\nconst (\n\t// The base relocation is skipped. This type can be used to pad a block.\n\tImageRelBasedAbsolute = 0\n\n\t// The base relocation adds the high 16 bits of the difference to the 16-bit\n\t// field at offset. The 16-bit field represents the high value of a 32-bit word.\n\tImageRelBasedHigh = 1\n\n\t// The base relocation adds the low 16 bits of the difference to the 16-bit\n\t// field at offset. The 16-bit field represents the low half of a 32-bit word.\n\tImageRelBasedLow = 2\n\n\t// The base relocation applies all 32 bits of the difference to the 32-bit\n\t// field at offset.\n\tImageRelBasedHighLow = 3\n\n\t// The base relocation adds the high 16 bits of the difference to the 16-bit\n\t// field at offset. The 16-bit field represents the high value of a 32-bit\n\t// word. The low 16 bits of the 32-bit value are stored in the 16-bit word\n\t// that follows this base relocation. This means that this base relocation\n\t// occupies two slots.\n\tImageRelBasedHighAdj = 4\n\n\t// The relocation interpretation is dependent on the machine type.\n\t// When the machine type is MIPS, the base relocation applies to a MIPS jump\n\t// instruction.\n\tImageRelBasedMIPSJmpAddr = 5\n\n\t// This relocation is meaningful only when the machine type is ARM or Thumb.\n\t// The base relocation applies the 32-bit address of a symbol across a\n\t// consecutive MOVW/MOVT instruction pair.\n\tImageRelBasedARMMov32 = 5\n\n\t// This relocation is only meaningful when the machine type is RISC-V. The\n\t// base relocation applies to the high 20 bits of a 32-bit absolute address.\n\tImageRelBasedRISCVHigh20 = 5\n\n\t// Reserved, must be zero.\n\tImageRelReserved = 6\n\n\t// This relocation is meaningful only when the machine type is Thumb.\n\t// The base relocation applies the 32-bit address of a symbol to a\n\t// consecutive MOVW/MOVT instruction pair.\n\tImageRelBasedThumbMov32 = 7\n\n\t// This relocation is only meaningful when the machine type is RISC-V.\n\t// The base relocation applies to the low 12 bits of a 32-bit absolute\n\t// address formed in RISC-V I-type instruction format.\n\tImageRelBasedRISCVLow12i = 7\n\n\t// This relocation is only meaningful when the machine type is RISC-V.\n\t// The base relocation applies to the low 12 bits of a 32-bit absolute\n\t// address formed in RISC-V S-type instruction format.\n\tImageRelBasedRISCVLow12s = 8\n\n\t// The relocation is only meaningful when the machine type is MIPS.\n\t// The base relocation applies to a MIPS16 jump instruction.\n\tImageRelBasedMIPSJmpAddr16 = 9\n\n\t// The base relocation applies the difference to the 64-bit field at offset.\n\tImageRelBasedDir64 = 10\n)\n\nconst (\n\t// MaxDefaultRelocEntriesCount represents the default maximum number of\n\t// relocations entries to parse. Some malware uses a fake huge reloc entries that\n\t// can slow significantly the parser.\n\t// Example:  01008963d32f5cc17b64c31446386ee5b36a7eab6761df87a2989ba9394d8f3d\n\tMaxDefaultRelocEntriesCount = 0x1000\n)\n\n// ImageBaseRelocation represents the IMAGE_BASE_RELOCATION structure.\n// Each chunk of base relocation data begins with an IMAGE_BASE_RELOCATION structure.\ntype ImageBaseRelocation struct {\n\t// The image base plus the page RVA is added to each offset to create the\n\t// VA where the base relocation must be applied.\n\tVirtualAddress uint32 `json:\"virtual_address\"`\n\n\t// The total number of bytes in the base relocation block, including the\n\t// Page RVA and Block Size fields and the Type/Offset fields that follow.\n\tSizeOfBlock uint32 `json:\"size_of_block\"`\n}\n\n// ImageBaseRelocationEntry represents an image base relocation entry.\ntype ImageBaseRelocationEntry struct {\n\t// Locate data that must be reallocated in buffer (data being an address\n\t// we use pointer of pointer).\n\tData uint16 `json:\"data\"`\n\n\t// The offset of the relocation. This value plus the VirtualAddress\n\t// in IMAGE_BASE_RELOCATION is the complete RVA.\n\tOffset uint16 `json:\"offset\"`\n\n\t// A value that indicates the kind of relocation that should be performed.\n\t// Valid relocation types depend on machine type.\n\tType ImageBaseRelocationEntryType `json:\"type\"`\n}\n\n// Relocation represents the relocation table which holds the data that needs to\n// be relocated.\ntype Relocation struct {\n\t// Points to the ImageBaseRelocation structure.\n\tData ImageBaseRelocation `json:\"data\"`\n\n\t// holds the list of entries for each chunk.\n\tEntries []ImageBaseRelocationEntry `json:\"entries\"`\n}\n\nfunc (pe *File) parseRelocations(dataRVA, rva, size uint32) ([]ImageBaseRelocationEntry, error) {\n\tvar relocEntries []ImageBaseRelocationEntry\n\trelocEntriesCount := size / 2\n\tif relocEntriesCount > pe.opts.MaxRelocEntriesCount {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoAddressOfDataBeyondLimits)\n\t\t// Defense-in-depth: cap the iteration. A block with a genuinely huge\n\t\t// (but still smaller than SizeOfImage) SizeOfBlock would otherwise\n\t\t// have us decoding tens of thousands of meaningless WORDs as fake\n\t\t// entries — see MaxDefaultRelocEntriesCount above for the reference\n\t\t// malware sample that motivated this cap.\n\t\trelocEntriesCount = pe.opts.MaxRelocEntriesCount\n\t}\n\n\toffset := pe.GetOffsetFromRva(dataRVA)\n\tvar err error\n\tfor i := uint32(0); i < relocEntriesCount; i++ {\n\t\tentry := ImageBaseRelocationEntry{}\n\t\tentry.Data, err = pe.ReadUint16(offset + (i * 2))\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tentry.Type = ImageBaseRelocationEntryType(entry.Data >> 12)\n\t\tentry.Offset = entry.Data & 0x0fff\n\t\trelocEntries = append(relocEntries, entry)\n\t}\n\n\treturn relocEntries, nil\n}\n\nfunc (pe *File) parseRelocDirectory(rva, size uint32) error {\n\tvar sizeOfImage uint32\n\tswitch pe.Is64 {\n\tcase true:\n\t\tsizeOfImage = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).SizeOfImage\n\tcase false:\n\t\tsizeOfImage = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).SizeOfImage\n\t}\n\n\trelocSize := uint32(binary.Size(ImageBaseRelocation{}))\n\tend := rva + size\n\tfor rva < end {\n\t\tbaseReloc := ImageBaseRelocation{}\n\t\toffset := pe.GetOffsetFromRva(rva)\n\t\terr := pe.structUnpack(&baseReloc, offset, relocSize)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Implicit end-of-table on a {VirtualAddress=0, SizeOfBlock=0} block.\n\t\t//\n\t\t// The PE/COFF spec does not define this sentinel — it states only that\n\t\t// the data-directory Size field bounds the table (see \"The .reloc\n\t\t// Section (Image Only)\" in the PE Format spec). In practice though,\n\t\t// many real binaries declare BaseReloc.Size larger than the actual\n\t\t// reloc data: e.g. when the .reloc section's VirtualSize exceeds its\n\t\t// RawSize, or when the linker rounds the directory to a section/page\n\t\t// boundary, the slack is zero-filled. Walking into that slack reads\n\t\t// {0, 0} — and continuing would (a) loop forever (zero size advances\n\t\t// rva by 0) and (b) make parseRelocations underflow on\n\t\t// SizeOfBlock - relocSize and synthesise millions of phantom entries\n\t\t// from whatever bytes happen to sit past the real reloc data.\n\t\t//\n\t\t// The Windows loader and every major PE parser (pefile, LIEF, ...)\n\t\t// treat {0, 0} as table termination for the same reasons; we follow\n\t\t// suit. The MaxRelocEntriesCount cap in parseRelocations remains as\n\t\t// the spec-strict backstop for blocks with a non-zero but bogus size.\n\t\tif baseReloc.SizeOfBlock == 0 {\n\t\t\tbreak\n\t\t}\n\n\t\t// Per the spec, Block Size is \"the total number of bytes in the base\n\t\t// relocation block, including the Page RVA and Block Size fields\"\n\t\t// — so the minimum legitimate value is the 8-byte header alone (zero\n\t\t// entries). Anything smaller is malformed and would underflow the\n\t\t// SizeOfBlock - relocSize calculation passed to parseRelocations.\n\t\tif baseReloc.SizeOfBlock < relocSize {\n\t\t\treturn ErrInvalidBasicRelocSizeOfBloc\n\t\t}\n\n\t\t// VirtualAddress must lie within the Image.\n\t\tif baseReloc.VirtualAddress > sizeOfImage {\n\t\t\treturn ErrInvalidBaseRelocVA\n\t\t}\n\n\t\t// SizeOfBlock must be less or equal than the size of the image.\n\t\t// It's a rather loose sanity test.\n\t\tif baseReloc.SizeOfBlock > sizeOfImage {\n\t\t\treturn ErrInvalidBasicRelocSizeOfBloc\n\t\t}\n\n\t\trelocEntries, err := pe.parseRelocations(rva+relocSize,\n\t\t\tbaseReloc.VirtualAddress, baseReloc.SizeOfBlock-relocSize)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tpe.Relocations = append(pe.Relocations, Relocation{\n\t\t\tData:    baseReloc,\n\t\t\tEntries: relocEntries,\n\t\t})\n\n\t\trva += baseReloc.SizeOfBlock\n\t}\n\n\tif len(pe.Relocations) > 0 {\n\t\tpe.HasReloc = true\n\t}\n\n\treturn nil\n}\n\n// String returns the string representation of the `Type` field of a base reloc entry.\nfunc (t ImageBaseRelocationEntryType) String(pe *File) string {\n\trelocTypesMap := map[ImageBaseRelocationEntryType]string{\n\t\tImageRelBasedAbsolute:      \"Absolute\",\n\t\tImageRelBasedHigh:          \"High\",\n\t\tImageRelBasedLow:           \"Low\",\n\t\tImageRelBasedHighLow:       \"HighLow\",\n\t\tImageRelBasedHighAdj:       \"HighAdj\",\n\t\tImageRelReserved:           \"Reserved\",\n\t\tImageRelBasedRISCVLow12s:   \"RISC-V Low12s\",\n\t\tImageRelBasedMIPSJmpAddr16: \"MIPS Jmp Addr16\",\n\t\tImageRelBasedDir64:         \"DIR64\",\n\t}\n\n\tif value, ok := relocTypesMap[t]; ok {\n\t\treturn value\n\t}\n\n\tswitch pe.NtHeader.FileHeader.Machine {\n\tcase ImageFileMachineMIPS16, ImageFileMachineMIPSFPU, ImageFileMachineMIPSFPU16, ImageFileMachineWCEMIPSv2:\n\t\tif t == ImageRelBasedMIPSJmpAddr {\n\t\t\treturn \"MIPS JMP Addr\"\n\t\t}\n\n\tcase ImageFileMachineARM, ImageFileMachineARM64, ImageFileMachineARMNT:\n\t\tif t == ImageRelBasedARMMov32 {\n\t\t\treturn \"ARM MOV 32\"\n\t\t}\n\n\t\tif t == ImageRelBasedThumbMov32 {\n\t\t\treturn \"Thumb MOV 32\"\n\t\t}\n\tcase ImageFileMachineRISCV32, ImageFileMachineRISCV64, ImageFileMachineRISCV128:\n\t\tif t == ImageRelBasedRISCVHigh20 {\n\t\t\treturn \"RISC-V High 20\"\n\t\t}\n\n\t\tif t == ImageRelBasedRISCVLow12i {\n\t\t\treturn \"RISC-V Low 12\"\n\t\t}\n\t}\n\n\treturn \"?\"\n}\n"
  },
  {
    "path": "reloc_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"testing\"\n)\n\nfunc TestParseRelocDirectoryData(t *testing.T) {\n\ttype TestRelocData struct {\n\t\timgBaseRelocation ImageBaseRelocation\n\t\trelocEntriesCount int\n\t\trelocDataIndex    int\n\t}\n\n\ttests := []struct {\n\t\tin  string\n\t\tout TestRelocData\n\t}{\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/putty.exe\"),\n\t\t\tTestRelocData{\n\t\t\t\timgBaseRelocation: ImageBaseRelocation{\n\t\t\t\t\tVirtualAddress: 0xd8000, SizeOfBlock: 0xc},\n\t\t\t\trelocEntriesCount: 18,\n\t\t\t\trelocDataIndex:    17,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\t\t\tswitch file.Is64 {\n\t\t\tcase true:\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryBaseReloc]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\tcase false:\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryBaseReloc]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseRelocDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseRelocDirectory(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\trelocs := file.Relocations\n\t\t\tif len(relocs) != tt.out.relocEntriesCount {\n\t\t\t\tt.Errorf(\"relocations entries count assertion failed, got %v, want %v\",\n\t\t\t\t\tlen(relocs), tt.out.relocEntriesCount)\n\t\t\t}\n\n\t\t\timgBaseRelocation := relocs[tt.out.relocDataIndex].Data\n\t\t\tif imgBaseRelocation != tt.out.imgBaseRelocation {\n\t\t\t\tt.Errorf(\"reloc data assertion failed, got %v, want %v\",\n\t\t\t\t\timgBaseRelocation, tt.out.imgBaseRelocation)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// TestParseRelocDirectoryZeroSizeOfBlock exercises the end-of-table sentinel\n// (VirtualAddress=0, SizeOfBlock=0). Before the fix, the sentinel was handed\n// to parseRelocations unchanged; SizeOfBlock - relocSize underflowed (uint32)\n// and the parser synthesised millions of phantom entries from bytes past the\n// real reloc table, ballooning the marshalled output to ~154 MB.\n//\n// The sample below is a real PE with 14 legitimate relocation blocks followed\n// by the {0,0} sentinel. We assert that:\n//   - the sentinel block is NOT appended to pe.Relocations\n//   - the total number of parsed entries stays bounded (3558 real entries)\n//   - the last real block matches the expected header\nfunc TestParseRelocDirectoryZeroSizeOfBlock(t *testing.T) {\n\tin := getAbsoluteFilePath(\n\t\t\"test/05df99cc2e77a59aa3443cae13325af553271bddaeedff3c08bf4f6995bbc62d\")\n\n\tops := Options{Fast: true}\n\tfile, err := New(in, &ops)\n\tif err != nil {\n\t\tt.Fatalf(\"New(%s) failed, reason: %v\", in, err)\n\t}\n\tif err := file.Parse(); err != nil {\n\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", in, err)\n\t}\n\n\tvar va, size uint32\n\tswitch file.Is64 {\n\tcase true:\n\t\tdirEntry := file.NtHeader.OptionalHeader.(ImageOptionalHeader64).\n\t\t\tDataDirectory[ImageDirectoryEntryBaseReloc]\n\t\tva, size = dirEntry.VirtualAddress, dirEntry.Size\n\tcase false:\n\t\tdirEntry := file.NtHeader.OptionalHeader.(ImageOptionalHeader32).\n\t\t\tDataDirectory[ImageDirectoryEntryBaseReloc]\n\t\tva, size = dirEntry.VirtualAddress, dirEntry.Size\n\t}\n\n\tif err := file.parseRelocDirectory(va, size); err != nil {\n\t\tt.Fatalf(\"parseRelocDirectory(%s) failed, reason: %v\", in, err)\n\t}\n\n\t// Exactly 14 real blocks — the {0,0} sentinel must not be appended.\n\tif got, want := len(file.Relocations), 14; got != want {\n\t\tt.Fatalf(\"relocation block count: got %d, want %d\", got, want)\n\t}\n\n\t// No block should carry a zero SizeOfBlock — if one does, the sentinel\n\t// slipped through.\n\tfor i, r := range file.Relocations {\n\t\tif r.Data.SizeOfBlock == 0 {\n\t\t\tt.Errorf(\"block %d has SizeOfBlock=0 (sentinel leaked into result)\", i)\n\t\t}\n\t}\n\n\t// Total entries across all blocks must be bounded (pre-fix: 4,270,384).\n\ttotal := 0\n\tfor _, r := range file.Relocations {\n\t\ttotal += len(r.Entries)\n\t}\n\tif total != 3558 {\n\t\tt.Errorf(\"total relocation entries: got %d, want 3558\", total)\n\t}\n\n\t// The last legitimate block.\n\tlast := file.Relocations[13]\n\twantLast := ImageBaseRelocation{VirtualAddress: 0x466000, SizeOfBlock: 20}\n\tif last.Data != wantLast {\n\t\tt.Errorf(\"last block header: got %+v, want %+v\", last.Data, wantLast)\n\t}\n\tif len(last.Entries) != 6 {\n\t\tt.Errorf(\"last block entry count: got %d, want 6\", len(last.Entries))\n\t}\n}\n\nfunc TestParseRelocDirectoryEntry(t *testing.T) {\n\ttype TestRelocEntry struct {\n\t\timgBaseRelocationEntry ImageBaseRelocationEntry\n\t\trelocEntriesCount      int\n\t\trelocDataIndex         int\n\t\trelocEntryIndex        int\n\t\trelocTypeMeaning       string\n\t}\n\n\ttests := []struct {\n\t\tin  string\n\t\tout TestRelocEntry\n\t}{\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/putty.exe\"),\n\t\t\tTestRelocEntry{\n\t\t\t\timgBaseRelocationEntry: ImageBaseRelocationEntry{\n\t\t\t\t\tData:   0xab00,\n\t\t\t\t\tOffset: 0xb00,\n\t\t\t\t\tType:   0xa,\n\t\t\t\t},\n\t\t\t\trelocDataIndex:    0x1,\n\t\t\t\trelocEntriesCount: 154,\n\t\t\t\trelocEntryIndex:   17,\n\t\t\t\trelocTypeMeaning:  \"DIR64\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/arp.dll\"),\n\t\t\tTestRelocEntry{\n\t\t\t\timgBaseRelocationEntry: ImageBaseRelocationEntry{\n\t\t\t\t\tData:   0x8004,\n\t\t\t\t\tOffset: 0x4,\n\t\t\t\t\tType:   0x8,\n\t\t\t\t},\n\t\t\t\trelocDataIndex:    3,\n\t\t\t\trelocEntriesCount: 204,\n\t\t\t\trelocEntryIndex:   1,\n\t\t\t\trelocTypeMeaning:  \"RISC-V Low12s\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\t\t\tswitch file.Is64 {\n\t\t\tcase true:\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryBaseReloc]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\tcase false:\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryBaseReloc]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseRelocDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseRelocDirectory(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\treloc := file.Relocations[tt.out.relocDataIndex]\n\t\t\tif len(reloc.Entries) != tt.out.relocEntriesCount {\n\t\t\t\tt.Errorf(\"relocations entries count assertion failed, got %v, want %v\",\n\t\t\t\t\tlen(reloc.Entries), tt.out.relocEntriesCount)\n\t\t\t}\n\n\t\t\trelocEntry := reloc.Entries[tt.out.relocEntryIndex]\n\t\t\tif relocEntry != tt.out.imgBaseRelocationEntry {\n\t\t\t\tt.Errorf(\"reloc image base relocation entry assertion failed, got %v, want %v\",\n\t\t\t\t\trelocEntry, tt.out.imgBaseRelocationEntry)\n\t\t\t}\n\n\t\t\trelocType := relocEntry.Type.String(file)\n\t\t\tif relocType != tt.out.relocTypeMeaning {\n\t\t\t\tt.Errorf(\"pretty reloc type assertion failed, got %v, want %v\", relocType,\n\t\t\t\t\ttt.out.relocTypeMeaning)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "resource.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n)\n\n// ResourceType represents a resource type.\ntype ResourceType int\n\n// ResourceLang represents a resource language.\ntype ResourceLang uint32\n\n// ResourceSubLang represents a resource sub language.\ntype ResourceSubLang uint32\n\n// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f\n\n// Special resource (sub)language identifiers.\nconst (\n\tLangNeutral       ResourceLang = 0x00 // Default custom (MUI) locale language\n\tLangUserDefault   ResourceLang = 0x01 // User default locale language\n\tLangSystemDefault ResourceLang = 0x02 // System default locale language\n\tLangInvariant     ResourceLang = 0x7F // Invariant locale language\n\n\tSubLangNeutral           ResourceSubLang = 0x00 // Neutral sub-language\n\tSubLangInvariant         ResourceSubLang = 0x00 // Invariant sub-language\n\tSubLangDefault           ResourceSubLang = 0x01 // User default sub-language\n\tSubLangSysDefault        ResourceSubLang = 0x02 // System default sub-language\n\tSubLangCustomDefault     ResourceSubLang = 0x03 // Default custom sub-language\n\tSubLangCustomUnspecified ResourceSubLang = 0x04 // Unspecified custom sub-language\n\tSubLangMUICustomDefault  ResourceSubLang = 0x05 // Default custom MUI sub-language\n)\n\n// All resource language identifiers.\nconst (\n\t// Afrikaans (af)\n\tLangAfrikaans ResourceLang = 0x0036\n\t// Albanian (sq)\n\tLangAlbanian ResourceLang = 0x001C\n\t// Alsatian (gsw)\n\tLangAlsatian ResourceLang = 0x0084\n\t// Amharic (am)\n\tLangAmharic ResourceLang = 0x005E\n\t// Arabic (ar)\n\tLangArabic ResourceLang = 0x0001\n\t// Armenian (hy)\n\tLangArmenian ResourceLang = 0x002B\n\t// Assamese (as)\n\tLangAssamese ResourceLang = 0x004D\n\t// Azerbaijani (Latin) (az)\n\tLangAzerbaijaniLatin ResourceLang = 0x002C\n\t// Bangla (bn)\n\tLangBangla ResourceLang = 0x0045\n\t// Bashkir (ba)\n\tLangBashkir ResourceLang = 0x006D\n\t// Basque (eu)\n\tLangBasque ResourceLang = 0x002D\n\t// Belarusian (be)\n\tLangBelarusian ResourceLang = 0x0023\n\t// Bosnian (Latin) (bs)\n\tLangBosnianLatin ResourceLang = 0x781A\n\t// Breton (br)\n\tLangBreton ResourceLang = 0x007E\n\t// Bulgarian (bg)\n\tLangBulgarian ResourceLang = 0x0002\n\t// Burmese (my)\n\tLangBurmese ResourceLang = 0x0055\n\t// Catalan (ca)\n\tLangCatalan ResourceLang = 0x0003\n\t// Central Kurdish (ku)\n\tLangCentralKurdish ResourceLang = 0x0092\n\t// Cherokee (chr)\n\tLangCherokee ResourceLang = 0x005C\n\t// Chinese (Simplified) (zh)\n\tLangChineseSimplified ResourceLang = 0x7804\n\t// Corsican (co)\n\tLangCorsican ResourceLang = 0x0083\n\t// Croatian (hr)\n\tLangCroatian ResourceLang = 0x001A\n\t// Czech (cs)\n\tLangCzech ResourceLang = 0x0005\n\t// Danish (da)\n\tLangDanish ResourceLang = 0x0006\n\t// Dari (prs)\n\tLangDari ResourceLang = 0x008C\n\t// Divehi (dv)\n\tLangDivehi ResourceLang = 0x0065\n\t// Dutch (nl)\n\tLangDutch ResourceLang = 0x0013\n\t// English (en)\n\tLangEnglish ResourceLang = 0x0009\n\t// Estonian (et)\n\tLangEstonian ResourceLang = 0x0025\n\t// Faroese (fo)\n\tLangFaroese ResourceLang = 0x0038\n\t// Filipino (fil)\n\tLangFilipino ResourceLang = 0x0064\n\t// Finnish (fi)\n\tLangFinnish ResourceLang = 0x000B\n\t// French (fr)\n\tLangFrench ResourceLang = 0x000C\n\t// Frisian (fy)\n\tLangFrisian ResourceLang = 0x0062\n\t// Fulah (ff)\n\tLangFulah ResourceLang = 0x0067\n\t// Fulah (Latin) (ff-Latn)\n\tLangFulahLatin ResourceLang = 0x7C67\n\t// Galician (gl)\n\tLangGalician ResourceLang = 0x0056\n\t// Georgian (ka)\n\tLangGeorgian ResourceLang = 0x0037\n\t// German (de)\n\tLangGerman ResourceLang = 0x0007\n\t// Greek (el)\n\tLangGreek ResourceLang = 0x0008\n\t// Greenlandic (kl)\n\tLangGreenlandic ResourceLang = 0x006F\n\t// Guarani (gn)\n\tLangGuarani ResourceLang = 0x0074\n\t// Gujarati (gu)\n\tLangGujarati ResourceLang = 0x0047\n\t// Hausa (Latin) (ha)\n\tLangHausaLatin ResourceLang = 0x0068\n\t// Hawaiian (haw)\n\tLangHawaiian ResourceLang = 0x0075\n\t// Hebrew (he)\n\tLangHebrew ResourceLang = 0x000D\n\t// Hindi (hi)\n\tLangHindi ResourceLang = 0x0039\n\t// Hungarian (hu)\n\tLangHungarian ResourceLang = 0x000E\n\t// Icelandic (is)\n\tLangIcelandic ResourceLang = 0x000F\n\t// Igbo (ig)\n\tLangIgbo ResourceLang = 0x0070\n\t// Indonesian (id)\n\tLangIndonesian ResourceLang = 0x0021\n\t// Inuktitut (Latin) (iu)\n\tLangInuktitutLatin ResourceLang = 0x005D\n\t// Irish (ga)\n\tLangIrish ResourceLang = 0x003C\n\t// Italian (it)\n\tLangItalian ResourceLang = 0x0010\n\t// Japanese (ja)\n\tLangJapanese ResourceLang = 0x0011\n\t// Kannada (kn)\n\tLangKannada ResourceLang = 0x004B\n\t// Kashmiri (ks)\n\tLangKashmiri ResourceLang = 0x0060\n\t// Kazakh (kk)\n\tLangKazakh ResourceLang = 0x003F\n\t// Khmer (km)\n\tLangKhmer ResourceLang = 0x0053\n\t// K'iche (quc)\n\tLangKiche ResourceLang = 0x0086\n\t// Kinyarwanda (rw)\n\tLangKinyarwanda ResourceLang = 0x0087\n\t// Kiswahili (sw)\n\tLangKiswahili ResourceLang = 0x0041\n\t// Konkani (kok)\n\tLangKonkani ResourceLang = 0x0057\n\t// Korean (ko)\n\tLangKorean ResourceLang = 0x0012\n\t// Kyrgyz (ky)\n\tLangKyrgyz ResourceLang = 0x0040\n\t// Lao (lo)\n\tLangLao ResourceLang = 0x0054\n\t// Latvian (lv)\n\tLangLatvian ResourceLang = 0x0026\n\t// Lithuanian (lt)\n\tLangLithuanian ResourceLang = 0x0027\n\t// Lower Sorbian (dsb)\n\tLangLowerSorbian ResourceLang = 0x7C2E\n\t// Luxembourgish (lb)\n\tLangLuxembourgish ResourceLang = 0x006E\n\t// Macedonian (mk)\n\tLangMacedonian ResourceLang = 0x002F\n\t// Malay (ms)\n\tLangMalay ResourceLang = 0x003E\n\t// Malayalam (ml)\n\tLangMalayalam ResourceLang = 0x004C\n\t// Maltese (mt)\n\tLangMaltese ResourceLang = 0x003A\n\t// Maori (mi)\n\tLangMaori ResourceLang = 0x0081\n\t// Mapudungun (arn)\n\tLangMapudungun ResourceLang = 0x007A\n\t// Marathi (mr)\n\tLangMarathi ResourceLang = 0x004E\n\t// Mohawk (moh)\n\tLangMohawk ResourceLang = 0x007C\n\t// Mongolian (Cyrillic) (mn)\n\tLangMongolianCyrillic ResourceLang = 0x0050\n\t// Nepali (ne)\n\tLangNepali ResourceLang = 0x0061\n\t// Norwegian (Bokmal) (no)\n\tLangNorwegianBokmalNo ResourceLang = 0x0014\n\t// Norwegian (Bokmal) (nb)\n\tLangNorwegianBokmal ResourceLang = 0x7C14\n\t// Norwegian (Nynorsk) (nn)\n\tLangNorwegianNynorsk ResourceLang = 0x7814\n\t// Occitan (oc)\n\tLangOccitan ResourceLang = 0x0082\n\t// Odia (or)\n\tLangOdia ResourceLang = 0x0048\n\t// Oromo (om)\n\tLangOromo ResourceLang = 0x0072\n\t// Pashto (ps)\n\tLangPashto ResourceLang = 0x0063\n\t// Persian (fa)\n\tLangPersian ResourceLang = 0x0029\n\t// Polish (pl)\n\tLangPolish ResourceLang = 0x0015\n\t// Portuguese (pt)\n\tLangPortuguese ResourceLang = 0x0016\n\t// Punjabi (pa)\n\tLangPunjabi ResourceLang = 0x0046\n\t// Quechua (quz)\n\tLangQuechua ResourceLang = 0x006B\n\t// Romanian (ro)\n\tLangRomanian ResourceLang = 0x0018\n\t// Romansh (rm)\n\tLangRomansh ResourceLang = 0x0017\n\t// Russian (ru)\n\tLangRussian ResourceLang = 0x0019\n\t// Sakha (sah)\n\tLangSakha ResourceLang = 0x0085\n\t// Sami (Inari) (smn)\n\tLangSamiInari ResourceLang = 0x703B\n\t// Sami (Lule) (smj)\n\tLangSamiLule ResourceLang = 0x7C3B\n\t// Sami (Northern) (se)\n\tLangSamiNorthern ResourceLang = 0x003B\n\t// Sami (Skolt) (sms)\n\tLangSamiSkolt ResourceLang = 0x743B\n\t// Sami (Southern) (sma)\n\tLangSamiSouthern ResourceLang = 0x783B\n\t// Sanskrit (sa)\n\tLangSanskrit ResourceLang = 0x004F\n\t// Scottish Gaelic (gd)\n\tLangScottishGaelic ResourceLang = 0x0091\n\t// Serbian (Latin) (sr)\n\tLangSerbianLatin ResourceLang = 0x7C1A\n\t// Sesotho Sa Leboa (nso)\n\tLangSesothoSaLeboa ResourceLang = 0x006C\n\t// Setswana (tn)\n\tLangSetswana ResourceLang = 0x0032\n\t// Sindhi (sd)\n\tLangSindhi ResourceLang = 0x0059\n\t// Sinhala (si)\n\tLangSinhala ResourceLang = 0x005B\n\t// Slovak (sk)\n\tLangSlovak ResourceLang = 0x001B\n\t// Slovenian (sl)\n\tLangSlovenian ResourceLang = 0x0024\n\t// Somali (so)\n\tLangSomali ResourceLang = 0x0077\n\t// Sotho (st)\n\tLangSotho ResourceLang = 0x0030\n\t// Spanish (es)\n\tLangSpanish ResourceLang = 0x000A\n\t// Swedish (sv)\n\tLangSwedish ResourceLang = 0x001D\n\t// Syriac (syr)\n\tLangSyriac ResourceLang = 0x005A\n\t// Tajik (Cyrillic) (tg)\n\tLangTajikCyrillic ResourceLang = 0x0028\n\t// Tamazight (Latin) (tzm)\n\tLangTamazightLatin ResourceLang = 0x005F\n\t// Tamil (ta)\n\tLangTamil ResourceLang = 0x0049\n\t// Tatar (tt)\n\tLangTatar ResourceLang = 0x0044\n\t// Telugu (te)\n\tLangTelugu ResourceLang = 0x004A\n\t// Thai (th)\n\tLangThai ResourceLang = 0x001E\n\t// Tibetan (bo)\n\tLangTibetan ResourceLang = 0x0051\n\t// Tigrinya (ti)\n\tLangTigrinya ResourceLang = 0x0073\n\t// Tsonga (ts)\n\tLangTsonga ResourceLang = 0x0031\n\t// Turkish (tr)\n\tLangTurkish ResourceLang = 0x001F\n\t// Turkmen (tk)\n\tLangTurkmen ResourceLang = 0x0042\n\t// Ukrainian (uk)\n\tLangUkrainian ResourceLang = 0x0022\n\t// Upper Sorbian (hsb)\n\tLangUpperSorbian ResourceLang = 0x002E\n\t// Urdu (ur)\n\tLangUrdu ResourceLang = 0x0020\n\t// Uyghur (ug)\n\tLangUyghur ResourceLang = 0x0080\n\t// Uzbek (Latin) (uz)\n\tLangUzbekLatin ResourceLang = 0x0043\n\t// Venda (ve)\n\tLangVenda ResourceLang = 0x0033\n\t// Vietnamese (vi)\n\tLangVietnamese ResourceLang = 0x002A\n\t// Welsh (cy)\n\tLangWelsh ResourceLang = 0x0052\n\t// Wolof (wo)\n\tLangWolof ResourceLang = 0x0088\n\t// Xhosa (xh)\n\tLangXhosa ResourceLang = 0x0034\n\t// Yi (ii)\n\tLangYi ResourceLang = 0x0078\n\t// Yoruba (yo)\n\tLangYoruba ResourceLang = 0x006A\n\t// Zulu (zu)\n\tLangZulu ResourceLang = 0x0035\n)\n\n// All resource sub-language identifiers.\nconst (\n\t// Afrikaans South Africa (af-ZA)\n\tSubLangAfrikaansSouthAfrica ResourceSubLang = iota\n\t// Albanian Albania (sq-AL)\n\tSubLangAlbanianAlbania\n\t// Alsatian France (gsw-FR)\n\tSubLangAlsatianFrance\n\t// Amharic Ethiopia (am-ET)\n\tSubLangAmharicEthiopia\n\t// Arabic Algeria (ar-DZ)\n\tSubLangArabicAlgeria\n\t// Arabic Bahrain (ar-BH)\n\tSubLangArabicBahrain\n\t// Arabic Egypt (ar-EG)\n\tSubLangArabicEgypt\n\t// Arabic Iraq (ar-IQ)\n\tSubLangArabicIraq\n\t// Arabic Jordan (ar-JO)\n\tSubLangArabicJordan\n\t// Arabic Kuwait (ar-KW)\n\tSubLangArabicKuwait\n\t// Arabic Lebanon (ar-LB)\n\tSubLangArabicLebanon\n\t// Arabic Libya (ar-LY)\n\tSubLangArabicLibya\n\t// Arabic Morocco (ar-MA)\n\tSubLangArabicMorocco\n\t// Arabic Oman (ar-OM)\n\tSubLangArabicOman\n\t// Arabic Qatar (ar-QA)\n\tSubLangArabicQatar\n\t// Arabic Saudi Arabia (ar-SA)\n\tSubLangArabicSaudiArabia\n\t// Arabic Syria (ar-SY)\n\tSubLangArabicSyria\n\t// Arabic Tunisia (ar-TN)\n\tSubLangArabicTunisia\n\t// Arabic U.a.e. (ar-AE)\n\tSubLangArabicUae\n\t// Arabic Yemen (ar-YE)\n\tSubLangArabicYemen\n\t// Armenian Armenia (hy-AM)\n\tSubLangArmenianArmenia\n\t// Assamese India (as-IN)\n\tSubLangAssameseIndia\n\t// Azerbaijani (Cyrillic) (az-Cyrl)\n\tSubLangAzerbaijaniCyrillic\n\t// Azerbaijani (Cyrillic) Azerbaijan (az-Cyrl-AZ)\n\tSubLangAzerbaijaniCyrillicAzerbaijan\n\t// Azerbaijani (Latin) (az-Latn)\n\tSubLangAzerbaijaniLatin\n\t// Azerbaijani (Latin) Azerbaijan (az-Latn-AZ)\n\tSubLangAzerbaijaniLatinAzerbaijan\n\t// Bangla Bangladesh (bn-BD)\n\tSubLangBanglaBangladesh\n\t// Bangla India (bn-IN)\n\tSubLangBanglaIndia\n\t// Bashkir Russia (ba-RU)\n\tSubLangBashkirRussia\n\t// Basque Spain (eu-ES)\n\tSubLangBasqueSpain\n\t// Belarusian Belarus (be-BY)\n\tSubLangBelarusianBelarus\n\t// Bosnian (Cyrillic) (bs-Cyrl)\n\tSubLangBosnianCyrillic\n\t// Bosnian (Cyrillic) Bosnia And Herzegovina (bs-Cyrl-BA)\n\tSubLangBosnianCyrillicBosniaAndHerzegovina\n\t// Bosnian (Latin) (bs-Latn)\n\tSubLangBosnianLatin\n\t// Bosnian (Latin) Bosnia And Herzegovina (bs-Latn-BA)\n\tSubLangBosnianLatinBosniaAndHerzegovina\n\t// Breton France (br-FR)\n\tSubLangBretonFrance\n\t// Bulgarian Bulgaria (bg-BG)\n\tSubLangBulgarianBulgaria\n\t// Burmese Myanmar (my-MM)\n\tSubLangBurmeseMyanmar\n\t// Catalan Spain (ca-ES)\n\tSubLangCatalanSpain\n\t// Central Atlas Tamazight (Arabic) Morocco (tzm-ArabMA)\n\tSubLangCentralAtlasTamazightArabicMorocco\n\t// Central Kurdish (ku-Arab)\n\tSubLangCentralKurdish\n\t// Central Kurdish Iraq (ku-Arab-IQ)\n\tSubLangCentralKurdishIraq\n\t// Cherokee (chr-Cher)\n\tSubLangCherokee\n\t// Cherokee United States (chr-Cher-US)\n\tSubLangCherokeeUnitedStates\n\t// Chinese (Simplified) (zh-Hans)\n\tSubLangChineseSimplified\n\t// Chinese (Simplified) People's Republic Of China (zh-CN)\n\tSubLangChineseSimplifiedPeoplesRepublicOfChina\n\t// Chinese (Simplified) Singapore (zh-SG)\n\tSubLangChineseSimplifiedSingapore\n\t// Chinese (Traditional) (zh-Hant)\n\tSubLangChineseTraditional\n\t// Chinese (Traditional) Hong Kong S.a.r. (zh-HK)\n\tSubLangChineseTraditionalHongKongSar\n\t// Chinese (Traditional) Macao S.a.r. (zh-MO)\n\tSubLangChineseTraditionalMacaoSar\n\t// Chinese (Traditional) Taiwan (zh-TW)\n\tSubLangChineseTraditionalTaiwan\n\t// Corsican France (co-FR)\n\tSubLangCorsicanFrance\n\t// Croatian Croatia (hr-HR)\n\tSubLangCroatianCroatia\n\t// Croatian (Latin) Bosnia And Herzegovina (hr-BA)\n\tSubLangCroatianLatinBosniaAndHerzegovina\n\t// Czech Czech Republic (cs-CZ)\n\tSubLangCzechCzechRepublic\n\t// Danish Denmark (da-DK)\n\tSubLangDanishDenmark\n\t// Dari Afghanistan (prs-AF)\n\tSubLangDariAfghanistan\n\t// Divehi Maldives (dv-MV)\n\tSubLangDivehiMaldives\n\t// Dutch Belgium (nl-BE)\n\tSubLangDutchBelgium\n\t// Dutch Netherlands (nl-NL)\n\tSubLangDutchNetherlands\n\t// Dzongkha Bhutan (dz-BT)\n\tSubLangDzongkhaBhutan\n\t// English Australia (en-AU)\n\tSubLangEnglishAustralia\n\t// English Belize (en-BZ)\n\tSubLangEnglishBelize\n\t// English Canada (en-CA)\n\tSubLangEnglishCanada\n\t// English Caribbean (en-029)\n\tSubLangEnglishCaribbean\n\t// English Hong Kong (en-HK)\n\tSubLangEnglishHongKong\n\t// English India (en-IN)\n\tSubLangEnglishIndia\n\t// English Ireland (en-IE)\n\tSubLangEnglishIreland\n\t// English Jamaica (en-JM)\n\tSubLangEnglishJamaica\n\t// English Malaysia (en-MY)\n\tSubLangEnglishMalaysia\n\t// English New Zealand (en-NZ)\n\tSubLangEnglishNewZealand\n\t// English Republic Of The Philippines (en-PH)\n\tSubLangEnglishRepublicOfThePhilippines\n\t// English Singapore (en-SG)\n\tSubLangEnglishSingapore\n\t// English South Africa (en-ZA)\n\tSubLangEnglishSouthAfrica\n\t// English Trinidad And Tobago (en-TT)\n\tSubLangEnglishTrinidadAndTobago\n\t// English United Arab Emirates (en-AE)\n\tSubLangEnglishUnitedArabEmirates\n\t// English United Kingdom (en-GB)\n\tSubLangEnglishUnitedKingdom\n\t// English United States (en-US)\n\tSubLangEnglishUnitedStates\n\t// English Zimbabwe (en-ZW)\n\tSubLangEnglishZimbabwe\n\t// Estonian Estonia (et-EE)\n\tSubLangEstonianEstonia\n\t// Faroese Faroe Islands (fo-FO)\n\tSubLangFaroeseFaroeIslands\n\t// Filipino Philippines (fil-PH)\n\tSubLangFilipinoPhilippines\n\t// Finnish Finland (fi-FI)\n\tSubLangFinnishFinland\n\t// French Belgium (fr-BE)\n\tSubLangFrenchBelgium\n\t// French Cameroon (fr-CM)\n\tSubLangFrenchCameroon\n\t// French Canada (fr-CA)\n\tSubLangFrenchCanada\n\t// French Caribbean (fr-029)\n\tSubLangFrenchCaribbean\n\t// French Congo, Drc (fr-CD)\n\tSubLangFrenchCongoDrc\n\t// French Côte D'ivoire (fr-CI)\n\tSubLangFrenchCôteDivoire\n\t// French France (fr-FR)\n\tSubLangFrenchFrance\n\t// French Haiti (fr-HT)\n\tSubLangFrenchHaiti\n\t// French Luxembourg (fr-LU)\n\tSubLangFrenchLuxembourg\n\t// French Mali (fr-ML)\n\tSubLangFrenchMali\n\t// French Morocco (fr-MA)\n\tSubLangFrenchMorocco\n\t// French Principality Of Monaco (fr-MC)\n\tSubLangFrenchPrincipalityOfMonaco\n\t// French Reunion (fr-RE)\n\tSubLangFrenchReunion\n\t// French Senegal (fr-SN)\n\tSubLangFrenchSenegal\n\t// French Switzerland (fr-CH)\n\tSubLangFrenchSwitzerland\n\t// Frisian Netherlands (fy-NL)\n\tSubLangFrisianNetherlands\n\t// Fulah Nigeria (ff-NG)\n\tSubLangFulahNigeria\n\t// Fulah (Latin) Nigeria (ff-Latn-NG)\n\tSubLangFulahLatinNigeria\n\t// Fulah Senegal (ff-Latn-SN)\n\tSubLangFulahSenegal\n\t// Galician Spain (gl-ES)\n\tSubLangGalicianSpain\n\t// Georgian Georgia (ka-GE)\n\tSubLangGeorgianGeorgia\n\t// German Austria (de-AT)\n\tSubLangGermanAustria\n\t// German Germany (de-DE)\n\tSubLangGermanGermany\n\t// German Liechtenstein (de-LI)\n\tSubLangGermanLiechtenstein\n\t// German Luxembourg (de-LU)\n\tSubLangGermanLuxembourg\n\t// German Switzerland (de-CH)\n\tSubLangGermanSwitzerland\n\t// Greek Greece (el-GR)\n\tSubLangGreekGreece\n\t// Greenlandic Greenland (kl-GL)\n\tSubLangGreenlandicGreenland\n\t// Guarani Paraguay (gn-PY)\n\tSubLangGuaraniParaguay\n\t// Gujarati India (gu-IN)\n\tSubLangGujaratiIndia\n\t// Hausa (Latin) (ha-Latn)\n\tSubLangHausaLatin\n\t// Hausa (Latin) Nigeria (ha-Latn-NG)\n\tSubLangHausaLatinNigeria\n\t// Hawaiian United States (haw-US)\n\tSubLangHawaiianUnitedStates\n\t// Hebrew Israel (he-IL)\n\tSubLangHebrewIsrael\n\t// Hindi India (hi-IN)\n\tSubLangHindiIndia\n\t// Hungarian Hungary (hu-HU)\n\tSubLangHungarianHungary\n\t// Icelandic Iceland (is-IS)\n\tSubLangIcelandicIceland\n\t// Igbo Nigeria (ig-NG)\n\tSubLangIgboNigeria\n\t// Indonesian Indonesia (id-ID)\n\tSubLangIndonesianIndonesia\n\t// Inuktitut (Latin) (iu-Latn)\n\tSubLangInuktitutLatin\n\t// Inuktitut (Latin) Canada (iu-Latn-CA)\n\tSubLangInuktitutLatinCanada\n\t// Inuktitut (Syllabics) (iu-Cans)\n\tSubLangInuktitutSyllabics\n\t// Inuktitut (Syllabics) Canada (iu-Cans-CA)\n\tSubLangInuktitutSyllabicsCanada\n\t// Irish Ireland (ga-IE)\n\tSubLangIrishIreland\n\t// Italian Italy (it-IT)\n\tSubLangItalianItaly\n\t// Italian Switzerland (it-CH)\n\tSubLangItalianSwitzerland\n\t// Japanese Japan (ja-JP)\n\tSubLangJapaneseJapan\n\t// Kannada India (kn-IN)\n\tSubLangKannadaIndia\n\t// Kanuri (Latin) Nigeria (kr-Latn-NG)\n\tSubLangKanuriLatinNigeria\n\t// Kashmiri Perso-Arabic (ks-Arab)\n\tSubLangKashmiriPersoArabic\n\t// Kashmiri (Devanagari) India (ks-Deva-IN)\n\tSubLangKashmiriDevanagariIndia\n\t// Kazakh Kazakhstan (kk-KZ)\n\tSubLangKazakhKazakhstan\n\t// Khmer Cambodia (km-KH)\n\tSubLangKhmerCambodia\n\t// K'iche Guatemala (quc-Latn-GT)\n\tSubLangKicheGuatemala\n\t// Kinyarwanda Rwanda (rw-RW)\n\tSubLangKinyarwandaRwanda\n\t// Kiswahili Kenya (sw-KE)\n\tSubLangKiswahiliKenya\n\t// Konkani India (kok-IN)\n\tSubLangKonkaniIndia\n\t// Korean Korea (ko-KR)\n\tSubLangKoreanKorea\n\t// Kyrgyz Kyrgyzstan (ky-KG)\n\tSubLangKyrgyzKyrgyzstan\n\t// Lao Lao P.d.r. (lo-LA)\n\tSubLangLaoLaoPdr\n\t// Latin Vatican City (la-VA)\n\tSubLangLatinVaticanCity\n\t// Latvian Latvia (lv-LV)\n\tSubLangLatvianLatvia\n\t// Lithuanian Lithuania (lt-LT)\n\tSubLangLithuanianLithuania\n\t// Lower Sorbian Germany (dsb-DE)\n\tSubLangLowerSorbianGermany\n\t// Luxembourgish Luxembourg (lb-LU)\n\tSubLangLuxembourgishLuxembourg\n\t// Macedonian North Macedonia (mk-MK)\n\tSubLangMacedonianNorthMacedonia\n\t// Malay Brunei Darussalam (ms-BN)\n\tSubLangMalayBruneiDarussalam\n\t// Malay Malaysia (ms-MY)\n\tSubLangMalayMalaysia\n\t// Malayalam India (ml-IN)\n\tSubLangMalayalamIndia\n\t// Maltese Malta (mt-MT)\n\tSubLangMalteseMalta\n\t// Maori New Zealand (mi-NZ)\n\tSubLangMaoriNewZealand\n\t// Mapudungun Chile (arn-CL)\n\tSubLangMapudungunChile\n\t// Marathi India (mr-IN)\n\tSubLangMarathiIndia\n\t// Mohawk Canada (moh-CA)\n\tSubLangMohawkCanada\n\t// Mongolian (Cyrillic) (mn-Cyrl)\n\tSubLangMongolianCyrillic\n\t// Mongolian (Cyrillic) Mongolia (mn-MN)\n\tSubLangMongolianCyrillicMongolia\n\t// Mongolian (Traditional Mongolian) (mn-Mong)\n\tSubLangMongolianTraditionalMongolian\n\t// Mongolian (Traditional Mongolian) People's Republic Of China (mn-MongCN)\n\tSubLangMongolianTraditionalMongolianPeoplesRepublicOfChina\n\t// Mongolian (Traditional Mongolian) Mongolia (mn-MongMN)\n\tSubLangMongolianTraditionalMongolianMongolia\n\t// Nepali India (ne-IN)\n\tSubLangNepaliIndia\n\t// Nepali Nepal (ne-NP)\n\tSubLangNepaliNepal\n\t// Norwegian (Bokmal) Norway (nb-NO)\n\tSubLangNorwegianBokmalNorway\n\t// Norwegian (Nynorsk) Norway (nn-NO)\n\tSubLangNorwegianNynorskNorway\n\t// Occitan France (oc-FR)\n\tSubLangOccitanFrance\n\t// Odia India (or-IN)\n\tSubLangOdiaIndia\n\t// Oromo Ethiopia (om-ET)\n\tSubLangOromoEthiopia\n\t// Pashto Afghanistan (ps-AF)\n\tSubLangPashtoAfghanistan\n\t// Persian Iran (fa-IR)\n\tSubLangPersianIran\n\t// Polish Poland (pl-PL)\n\tSubLangPolishPoland\n\t// Portuguese Brazil (pt-BR)\n\tSubLangPortugueseBrazil\n\t// Portuguese Portugal (pt-PT)\n\tSubLangPortuguesePortugal\n\t// Pseudo Language Pseudo Locale For East Asian/Complex Script Localization Testing (qps-ploca)\n\tSubLangPseudoLanguagePseudoLocaleForEastAsianComplexScriptLocalizationTesting\n\t// Pseudo Language Pseudo Locale Used For Localization Testing (qps-ploc)\n\tSubLangPseudoLanguagePseudoLocaleUsedForLocalizationTesting\n\t// Pseudo Language Pseudo Locale Used For Localization Testing Of Mirrored Locales (qps-plocm)\n\tSubLangPseudoLanguagePseudoLocaleUsedForLocalizationTestingOfMirroredLocales\n\t// Punjabi (pa-Arab)\n\tSubLangPunjabi\n\t// Punjabi India (pa-IN)\n\tSubLangPunjabiIndia\n\t// Punjabi Islamic Republic Of Pakistan (pa-Arab-PK)\n\tSubLangPunjabiIslamicRepublicOfPakistan\n\t// Quechua Bolivia (quz-BO)\n\tSubLangQuechuaBolivia\n\t// Quechua Ecuador (quz-EC)\n\tSubLangQuechuaEcuador\n\t// Quechua Peru (quz-PE)\n\tSubLangQuechuaPeru\n\t// Romanian Moldova (ro-MD)\n\tSubLangRomanianMoldova\n\t// Romanian Romania (ro-RO)\n\tSubLangRomanianRomania\n\t// Romansh Switzerland (rm-CH)\n\tSubLangRomanshSwitzerland\n\t// Russian Moldova (ru-MD)\n\tSubLangRussianMoldova\n\t// Russian Russia (ru-RU)\n\tSubLangRussianRussia\n\t// Sakha Russia (sah-RU)\n\tSubLangSakhaRussia\n\t// Sami (Inari) Finland (smn-FI)\n\tSubLangSamiInariFinland\n\t// Sami (Lule) Norway (smj-NO)\n\tSubLangSamiLuleNorway\n\t// Sami (Lule) Sweden (smj-SE)\n\tSubLangSamiLuleSweden\n\t// Sami (Northern) Finland (se-FI)\n\tSubLangSamiNorthernFinland\n\t// Sami (Northern) Norway (se-NO)\n\tSubLangSamiNorthernNorway\n\t// Sami (Northern) Sweden (se-SE)\n\tSubLangSamiNorthernSweden\n\t// Sami (Skolt) Finland (sms-FI)\n\tSubLangSamiSkoltFinland\n\t// Sami (Southern) Norway (sma-NO)\n\tSubLangSamiSouthernNorway\n\t// Sami (Southern) Sweden (sma-SE)\n\tSubLangSamiSouthernSweden\n\t// Sanskrit India (sa-IN)\n\tSubLangSanskritIndia\n\t// Scottish Gaelic United Kingdom (gd-GB)\n\tSubLangScottishGaelicUnitedKingdom\n\t// Serbian (Cyrillic) (sr-Cyrl)\n\tSubLangSerbianCyrillic\n\t// Serbian (Cyrillic) Bosnia And Herzegovina (sr-Cyrl-BA)\n\tSubLangSerbianCyrillicBosniaAndHerzegovina\n\t// Serbian (Cyrillic) Montenegro (sr-Cyrl-ME)\n\tSubLangSerbianCyrillicMontenegro\n\t// Serbian (Cyrillic) Serbia (sr-Cyrl-RS)\n\tSubLangSerbianCyrillicSerbia\n\t// Serbian (Cyrillic) Serbia And Montenegro (Former) (sr-Cyrl-CS)\n\tSubLangSerbianCyrillicSerbiaAndMontenegroFormer\n\t// Serbian (Latin) (sr-Latn)\n\tSubLangSerbianLatin\n\t// Serbian (Latin) Bosnia And Herzegovina (sr-Latn-BA)\n\tSubLangSerbianLatinBosniaAndHerzegovina\n\t// Serbian (Latin) Montenegro (sr-Latn-ME)\n\tSubLangSerbianLatinMontenegro\n\t// Serbian (Latin) Serbia (sr-Latn-RS)\n\tSubLangSerbianLatinSerbia\n\t// Serbian (Latin) Serbia And Montenegro (Former) (sr-Latn-CS)\n\tSubLangSerbianLatinSerbiaAndMontenegroFormer\n\t// Sesotho Sa Leboa South Africa (nso-ZA)\n\tSubLangSesothoSaLeboaSouthAfrica\n\t// Setswana Botswana (tn-BW)\n\tSubLangSetswanaBotswana\n\t// Setswana South Africa (tn-ZA)\n\tSubLangSetswanaSouthAfrica\n\t// Sindhi (sd-Arab)\n\tSubLangSindhi\n\t// Sindhi Islamic Republic Of Pakistan (sd-Arab-PK)\n\tSubLangSindhiIslamicRepublicOfPakistan\n\t// Sinhala Sri Lanka (si-LK)\n\tSubLangSinhalaSriLanka\n\t// Slovak Slovakia (sk-SK)\n\tSubLangSlovakSlovakia\n\t// Slovenian Slovenia (sl-SI)\n\tSubLangSlovenianSlovenia\n\t// Somali Somalia (so-SO)\n\tSubLangSomaliSomalia\n\t// Sotho South Africa (st-ZA)\n\tSubLangSothoSouthAfrica\n\t// Spanish Argentina (es-AR)\n\tSubLangSpanishArgentina\n\t// Spanish Bolivarian Republic Of Venezuela (es-VE)\n\tSubLangSpanishBolivarianRepublicOfVenezuela\n\t// Spanish Bolivia (es-BO)\n\tSubLangSpanishBolivia\n\t// Spanish Chile (es-CL)\n\tSubLangSpanishChile\n\t// Spanish Colombia (es-CO)\n\tSubLangSpanishColombia\n\t// Spanish Costa Rica (es-CR)\n\tSubLangSpanishCostaRica\n\t// Spanish Cuba (es-CU)\n\tSubLangSpanishCuba\n\t// Spanish Dominican Republic (es-DO)\n\tSubLangSpanishDominicanRepublic\n\t// Spanish Ecuador (es-EC)\n\tSubLangSpanishEcuador\n\t// Spanish El Salvador (es-SV)\n\tSubLangSpanishElSalvador\n\t// Spanish Guatemala (es-GT)\n\tSubLangSpanishGuatemala\n\t// Spanish Honduras (es-HN)\n\tSubLangSpanishHonduras\n\t// Spanish Latin America (es-419)\n\tSubLangSpanishLatinAmerica\n\t// Spanish Mexico (es-MX)\n\tSubLangSpanishMexico\n\t// Spanish Nicaragua (es-NI)\n\tSubLangSpanishNicaragua\n\t// Spanish Panama (es-PA)\n\tSubLangSpanishPanama\n\t// Spanish Paraguay (es-PY)\n\tSubLangSpanishParaguay\n\t// Spanish Peru (es-PE)\n\tSubLangSpanishPeru\n\t// Spanish Puerto Rico (es-PR)\n\tSubLangSpanishPuertoRico\n\t// Spanish Spain (es-ES_tradnl)\n\tSubLangSpanishSpainTraditional\n\t// Spanish Spain (es-ES)\n\tSubLangSpanishSpain\n\t// Spanish United States (es-US)\n\tSubLangSpanishUnitedStates\n\t// Spanish Uruguay (es-UY)\n\tSubLangSpanishUruguay\n\t// Swedish Finland (sv-FI)\n\tSubLangSwedishFinland\n\t// Swedish Sweden (sv-SE)\n\tSubLangSwedishSweden\n\t// Syriac Syria (syr-SY)\n\tSubLangSyriacSyria\n\t// Tajik (Cyrillic) (tg-Cyrl)\n\tSubLangTajikCyrillic\n\t// Tajik (Cyrillic) Tajikistan (tg-Cyrl-TJ)\n\tSubLangTajikCyrillicTajikistan\n\t// Tamazight (Latin) (tzm-Latn)\n\tSubLangTamazightLatin\n\t// Tamazight (Latin) Algeria (tzm-Latn-DZ)\n\tSubLangTamazightLatinAlgeria\n\t// Tamil India (ta-IN)\n\tSubLangTamilIndia\n\t// Tamil Sri Lanka (ta-LK)\n\tSubLangTamilSriLanka\n\t// Tatar Russia (tt-RU)\n\tSubLangTatarRussia\n\t// Telugu India (te-IN)\n\tSubLangTeluguIndia\n\t// Thai Thailand (th-TH)\n\tSubLangThaiThailand\n\t// Tibetan People's Republic Of China (bo-CN)\n\tSubLangTibetanPeoplesRepublicOfChina\n\t// Tigrinya Eritrea (ti-ER)\n\tSubLangTigrinyaEritrea\n\t// Tigrinya Ethiopia (ti-ET)\n\tSubLangTigrinyaEthiopia\n\t// Tsonga South Africa (ts-ZA)\n\tSubLangTsongaSouthAfrica\n\t// Turkish Turkey (tr-TR)\n\tSubLangTurkishTurkey\n\t// Turkmen Turkmenistan (tk-TM)\n\tSubLangTurkmenTurkmenistan\n\t// Ukrainian Ukraine (uk-UA)\n\tSubLangUkrainianUkraine\n\t// Upper Sorbian Germany (hsb-DE)\n\tSubLangUpperSorbianGermany\n\t// Urdu India (ur-IN)\n\tSubLangUrduIndia\n\t// Urdu Islamic Republic Of Pakistan (ur-PK)\n\tSubLangUrduIslamicRepublicOfPakistan\n\t// Uyghur People's Republic Of China (ug-CN)\n\tSubLangUyghurPeoplesRepublicOfChina\n\t// Uzbek (Cyrillic) (uz-Cyrl)\n\tSubLangUzbekCyrillic\n\t// Uzbek (Cyrillic) Uzbekistan (uz-Cyrl-UZ)\n\tSubLangUzbekCyrillicUzbekistan\n\t// Uzbek (Latin) (uz-Latn)\n\tSubLangUzbekLatin\n\t// Uzbek (Latin) Uzbekistan (uz-Latn-UZ)\n\tSubLangUzbekLatinUzbekistan\n\t// Valencian Spain (ca-ESvalencia)\n\tSubLangValencianSpain\n\t// Venda South Africa (ve-ZA)\n\tSubLangVendaSouthAfrica\n\t// Vietnamese Vietnam (vi-VN)\n\tSubLangVietnameseVietnam\n\t// Welsh United Kingdom (cy-GB)\n\tSubLangWelshUnitedKingdom\n\t// Wolof Senegal (wo-SN)\n\tSubLangWolofSenegal\n\t// Xhosa South Africa (xh-ZA)\n\tSubLangXhosaSouthAfrica\n\t// Yi People's Republic Of China (ii-CN)\n\tSubLangYiPeoplesRepublicOfChina\n\t// Yiddish World (yi-001)\n\tSubLangYiddishWorld\n\t// Yoruba Nigeria (yo-NG)\n\tSubLangYorubaNigeria\n\t// Zulu South Africa (zu-ZA)\n\tSubLangZuluSouthAfrica\n)\n\nconst (\n\tmaxAllowedEntries = 0x1000\n)\n\n// Predefined Resource Types.\nconst (\n\tRTCursor       ResourceType = iota + 1      // Hardware-dependent cursor resource.\n\tRTBitmap                    = 2             // Bitmap resource.\n\tRTIcon                      = 3             // Hardware-dependent icon resource.\n\tRTMenu                      = 4             // Menu resource.\n\tRTDialog                    = 5             // Dialog box.\n\tRTString                    = 6             // String-table entry.\n\tRTFontDir                   = 7             // Font directory resource.\n\tRTFont                      = 8             // Font resource.\n\tRTAccelerator               = 9             // Accelerator table.\n\tRTRCdata                    = 10            // Application-defined resource (raw data).\n\tRTMessageTable              = 11            // Message-table entry.\n\tRTGroupCursor               = RTCursor + 11 // Hardware-independent cursor resource.\n\tRTGroupIcon                 = RTIcon + 11   // Hardware-independent icon resource.\n\tRTVersion                   = 16            // Version resource.\n\tRTDlgInclude                = 17            // Dialog include entry.\n\tRTPlugPlay                  = 19            // Plug and Play resource.\n\tRTVxD                       = 20            // VXD.\n\tRTAniCursor                 = 21            // Animated cursor.\n\tRTAniIcon                   = 22            // Animated icon.\n\tRTHtml                      = 23            // HTML resource.\n\tRTManifest                  = 24            // Side-by-Side Assembly Manifest.\n)\n\n// ImageResourceDirectory represents the IMAGE_RESOURCE_DIRECTORY.\n// This data structure should be considered the heading of a table because the\n// table actually consists of directory entries.\ntype ImageResourceDirectory struct {\n\t// Resource flags. This field is reserved for future use. It is currently\n\t// set to zero.\n\tCharacteristics uint32 `json:\"characteristics\"`\n\n\t// The time that the resource data was created by the resource compiler.\n\tTimeDateStamp uint32 `json:\"time_date_stamp\"`\n\n\t// The major version number, set by the user.\n\tMajorVersion uint16 `json:\"major_version\"`\n\n\t// The minor version number, set by the user.\n\tMinorVersion uint16 `json:\"minor_version\"`\n\n\t// The number of directory entries immediately following the table that use\n\t// strings to identify Type, Name, or Language entries (depending on the\n\t// level of the table).\n\tNumberOfNamedEntries uint16 `json:\"number_of_named_entries\"`\n\n\t// The number of directory entries immediately following the Name entries\n\t// that use numeric IDs for Type, Name, or Language entries.\n\tNumberOfIDEntries uint16 `json:\"number_of_id_entries\"`\n}\n\n// ImageResourceDirectoryEntry represents an entry in the resource directory\n// entries.\ntype ImageResourceDirectoryEntry struct {\n\t// Name is used to identify either a type of resource, a resource name, or a\n\t// resource's language ID.\n\tName uint32 `json:\"name\"`\n\n\t// OffsetToData is always used to point to a sibling in the tree, either a\n\t// directory node or a leaf node.\n\tOffsetToData uint32 `json:\"offset_to_data\"`\n}\n\n// ImageResourceDataEntry Each Resource Data entry describes an actual unit of\n// raw data in the Resource Data area.\ntype ImageResourceDataEntry struct {\n\t// The address of a unit of resource data in the Resource Data area.\n\tOffsetToData uint32 `json:\"offset_to_data\"`\n\n\t// The size, in bytes, of the resource data that is pointed to by the Data\n\t// RVA field.\n\tSize uint32 `json:\"size\"`\n\n\t// The code page that is used to decode code point values within the\n\t// resource data. Typically, the code page would be the Unicode code page.\n\tCodePage uint32 `json:\"code_page\"`\n\n\t// Reserved, must be 0.\n\tReserved uint32 `json:\"reserved\"`\n}\n\n// ResourceDirectory represents resource directory information.\ntype ResourceDirectory struct {\n\t// IMAGE_RESOURCE_DIRECTORY structure.\n\tStruct ImageResourceDirectory `json:\"struct\"`\n\n\t// list of entries.\n\tEntries []ResourceDirectoryEntry `json:\"entries\"`\n}\n\n// ResourceDirectoryEntry represents a resource directory entry.\ntype ResourceDirectoryEntry struct {\n\t// IMAGE_RESOURCE_DIRECTORY_ENTRY structure.\n\tStruct ImageResourceDirectoryEntry `json:\"struct\"`\n\n\t// If the resource is identified by name this attribute will contain the\n\t// name string. Empty string otherwise. If identified by id, the id is\n\t// available at `ID` field.\n\tName string `json:\"name\"`\n\n\t// The resource identifier.\n\tID uint32 `json:\"id\"`\n\n\t// IsResourceDir tell us if the entry is pointing to a resource directory or\n\t// a resource data entry.\n\tIsResourceDir bool `json:\"is_resource_dir\"`\n\n\t// If this entry has a lower level directory this attribute will point to\n\t// the ResourceDirData instance representing it.\n\tDirectory ResourceDirectory `json:\"directory\"`\n\n\t// If this entry has no further lower directories and points to the actual\n\t// resource data, this attribute will reference the corresponding\n\t// ResourceDataEntry instance.\n\tData ResourceDataEntry `json:\"data\"`\n}\n\n// ResourceDataEntry represents a resource data entry.\ntype ResourceDataEntry struct {\n\n\t// IMAGE_RESOURCE_DATA_ENTRY structure.\n\tStruct ImageResourceDataEntry `json:\"struct\"`\n\n\t// Primary language ID.\n\tLang ResourceLang `json:\"lang\"`\n\n\t// Sub language ID.\n\tSubLang ResourceSubLang `json:\"sub_lang\"`\n}\n\nfunc (pe *File) parseResourceDataEntry(rva uint32) ImageResourceDataEntry {\n\tdataEntry := ImageResourceDataEntry{}\n\tdataEntrySize := uint32(binary.Size(dataEntry))\n\toffset := pe.GetOffsetFromRva(rva)\n\terr := pe.structUnpack(&dataEntry, offset, dataEntrySize)\n\tif err != nil {\n\t\tpe.logger.Warnf(\"Error parsing a resource directory data entry, the RVA is invalid\")\n\t}\n\treturn dataEntry\n}\n\nfunc (pe *File) parseResourceDirectoryEntry(rva uint32) *ImageResourceDirectoryEntry {\n\tresource := ImageResourceDirectoryEntry{}\n\tresourceSize := uint32(binary.Size(resource))\n\toffset := pe.GetOffsetFromRva(rva)\n\terr := pe.structUnpack(&resource, offset, resourceSize)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tif resource == (ImageResourceDirectoryEntry{}) {\n\t\treturn nil\n\t}\n\n\t// resource.NameOffset = resource.Name & 0x7FFFFFFF\n\n\t// resource.__pad = resource.Name & 0xFFFF0000\n\t// resource.Id = resource.Name & 0x0000FFFF\n\n\t// resource.DataIsDirectory = (resource.OffsetToData & 0x80000000) >> 31\n\t// resource.OffsetToDirectory = resource.OffsetToData & 0x7FFFFFFF\n\n\treturn &resource\n}\n\n// Navigating the resource directory hierarchy is like navigating a hard disk.\n// There's a master directory (the root directory), which has subdirectories.\n// The subdirectories have subdirectories of their own that may point to the\n// raw resource data for things like dialog templates.\nfunc (pe *File) doParseResourceDirectory(rva, size, baseRVA, level uint32,\n\tdirs []uint32) (ResourceDirectory, error) {\n\n\tresourceDir := ImageResourceDirectory{}\n\tresourceDirSize := uint32(binary.Size(resourceDir))\n\toffset := pe.GetOffsetFromRva(rva)\n\terr := pe.structUnpack(&resourceDir, offset, resourceDirSize)\n\tif err != nil {\n\t\treturn ResourceDirectory{}, err\n\t}\n\n\tif baseRVA == 0 {\n\t\tbaseRVA = rva\n\t}\n\n\tif len(dirs) == 0 {\n\t\tdirs = append(dirs, rva)\n\t}\n\n\t// Advance the RVA to the position immediately following the directory\n\t// table header and pointing to the first entry in the table.\n\trva += resourceDirSize\n\n\tnumberOfEntries := int(resourceDir.NumberOfNamedEntries +\n\t\tresourceDir.NumberOfIDEntries)\n\tvar dirEntries []ResourceDirectoryEntry\n\n\t// Set a hard limit on the maximum reasonable number of entries.\n\tif numberOfEntries > maxAllowedEntries {\n\t\tpe.logger.Warnf(`Error parsing the resources directory.\n\t\t The directory contains %d entries`, numberOfEntries)\n\t\treturn ResourceDirectory{}, nil\n\t}\n\n\tfor i := 0; i < numberOfEntries; i++ {\n\t\tres := pe.parseResourceDirectoryEntry(rva)\n\t\tif res == nil {\n\t\t\tpe.logger.Warn(\"Error parsing a resource directory entry, the RVA is invalid\")\n\t\t\tbreak\n\t\t}\n\n\t\tnameIsString := (res.Name & 0x80000000) >> 31\n\t\tentryName := \"\"\n\t\tentryID := uint32(0)\n\t\tif nameIsString == 0 {\n\t\t\tentryID = res.Name\n\t\t} else {\n\t\t\tnameOffset := res.Name & 0x7FFFFFFF\n\t\t\tuStringOffset := pe.GetOffsetFromRva(baseRVA + nameOffset)\n\t\t\tmaxLen, err := pe.ReadUint16(uStringOffset)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tentryName = pe.readUnicodeStringAtRVA(baseRVA+nameOffset+2,\n\t\t\t\tuint32(maxLen*2))\n\t\t}\n\n\t\t// A directory entry points to either another resource directory or to\n\t\t// the data for an individual resource. When the directory entry points\n\t\t// to another resource directory, the high bit of the second DWORD in\n\t\t// the structure is set and the remaining 31 bits are an offset to the\n\t\t// resource directory.\n\t\tdataIsDirectory := (res.OffsetToData & 0x80000000) >> 31\n\n\t\t// The offset is relative to the beginning of the resource section,\n\t\t// not an RVA.\n\t\tOffsetToDirectory := res.OffsetToData & 0x7FFFFFFF\n\t\tif dataIsDirectory > 0 {\n\t\t\t// One trick malware can do is to recursively reference\n\t\t\t// the next directory. This causes hilarity to ensue when\n\t\t\t// trying to parse everything correctly.\n\t\t\t// If the original RVA given to this function is equal to\n\t\t\t// the next one to parse, we assume that it's a trick.\n\t\t\t// Instead of raising a PEFormatError this would skip some\n\t\t\t// reasonable data so we just break.\n\t\t\t// 9ee4d0a0caf095314fd7041a3e4404dc is the offending sample.\n\t\t\tif intInSlice(baseRVA+OffsetToDirectory, dirs) {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tlevel++\n\t\t\tdirs = append(dirs, baseRVA+OffsetToDirectory)\n\t\t\tdirectoryEntry, _ := pe.doParseResourceDirectory(\n\t\t\t\tbaseRVA+OffsetToDirectory,\n\t\t\t\tsize-(rva-baseRVA),\n\t\t\t\tbaseRVA,\n\t\t\t\tlevel,\n\t\t\t\tdirs)\n\n\t\t\tdirEntries = append(dirEntries, ResourceDirectoryEntry{\n\t\t\t\tStruct:        *res,\n\t\t\t\tName:          entryName,\n\t\t\t\tID:            entryID,\n\t\t\t\tIsResourceDir: true,\n\t\t\t\tDirectory:     directoryEntry})\n\t\t} else {\n\t\t\t// data is entry\n\t\t\tdataEntryStruct := pe.parseResourceDataEntry(baseRVA +\n\t\t\t\tOffsetToDirectory)\n\t\t\tentryData := ResourceDataEntry{\n\t\t\t\tStruct:  dataEntryStruct,\n\t\t\t\tLang:    ResourceLang(res.Name & 0x3ff),\n\t\t\t\tSubLang: ResourceSubLang(res.Name >> 10),\n\t\t\t}\n\n\t\t\tdirEntries = append(dirEntries, ResourceDirectoryEntry{\n\t\t\t\tStruct:        *res,\n\t\t\t\tName:          entryName,\n\t\t\t\tID:            entryID,\n\t\t\t\tIsResourceDir: false,\n\t\t\t\tData:          entryData})\n\t\t}\n\n\t\trva += uint32(binary.Size(res))\n\t}\n\n\treturn ResourceDirectory{\n\t\tStruct:  resourceDir,\n\t\tEntries: dirEntries,\n\t}, nil\n}\n\n// The resource directory contains resources like dialog templates, icons,\n// and bitmaps. The resources are found in a section called .rsrc section.\nfunc (pe *File) parseResourceDirectory(rva, size uint32) error {\n\tvar dirs []uint32\n\tResources, err := pe.doParseResourceDirectory(rva, size, 0, 0, dirs)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tpe.Resources = Resources\n\tpe.HasResource = true\n\treturn err\n}\n\n// String stringify the resource type.\nfunc (rt ResourceType) String() string {\n\n\trsrcTypeMap := map[ResourceType]string{\n\t\tRTCursor:       \"Cursor\",\n\t\tRTBitmap:       \"Bitmap\",\n\t\tRTIcon:         \"Icon\",\n\t\tRTMenu:         \"Menu\",\n\t\tRTDialog:       \"Dialog box\",\n\t\tRTString:       \"String\",\n\t\tRTFontDir:      \"Font directory\",\n\t\tRTFont:         \"Font\",\n\t\tRTAccelerator:  \"Accelerator\",\n\t\tRTRCdata:       \"RC Data\",\n\t\tRTMessageTable: \"Message Table\",\n\t\tRTGroupCursor:  \"Group Cursor\",\n\t\tRTGroupIcon:    \"Group Icon\",\n\t\tRTVersion:      \"Version\",\n\t\tRTDlgInclude:   \"Dialog Include\",\n\t\tRTPlugPlay:     \"Plug & Play\",\n\t\tRTVxD:          \"VxD\",\n\t\tRTAniCursor:    \"Animated Cursor\",\n\t\tRTAniIcon:      \"Animated Icon\",\n\t\tRTHtml:         \"HTML\",\n\t\tRTManifest:     \"Manifest\",\n\t}\n\n\tif val, ok := rsrcTypeMap[rt]; ok {\n\t\treturn val\n\t}\n\n\treturn \"?\"\n}\n\n// String stringify the resource language.\nfunc (lang ResourceLang) String() string {\n\n\trsrcLangMap := map[ResourceLang]string{\n\t\tLangAfrikaans:         \"Afrikaans (af)\",\n\t\tLangAlbanian:          \"Albanian (sq)\",\n\t\tLangAlsatian:          \"Alsatian (gsw)\",\n\t\tLangAmharic:           \"Amharic (am)\",\n\t\tLangArabic:            \"Arabic (ar)\",\n\t\tLangArmenian:          \"Armenian (hy)\",\n\t\tLangAssamese:          \"Assamese (as)\",\n\t\tLangAzerbaijaniLatin:  \"Azerbaijani (Latin) (az)\",\n\t\tLangBangla:            \"Bangla (bn)\",\n\t\tLangBashkir:           \"Bashkir (ba)\",\n\t\tLangBasque:            \"Basque (eu)\",\n\t\tLangBelarusian:        \"Belarusian (be)\",\n\t\tLangBosnianLatin:      \"Bosnian (Latin) (bs)\",\n\t\tLangBreton:            \"Breton (br)\",\n\t\tLangBulgarian:         \"Bulgarian (bg)\",\n\t\tLangBurmese:           \"Burmese (my)\",\n\t\tLangCatalan:           \"Catalan (ca)\",\n\t\tLangCentralKurdish:    \"Central Kurdish (ku)\",\n\t\tLangCherokee:          \"Cherokee (chr)\",\n\t\tLangChineseSimplified: \"Chinese (Simplified) (zh)\",\n\t\tLangCorsican:          \"Corsican (co)\",\n\t\tLangCroatian:          \"Croatian (hr)\",\n\t\tLangCzech:             \"Czech (cs)\",\n\t\tLangDanish:            \"Danish (da)\",\n\t\tLangDari:              \"Dari (prs)\",\n\t\tLangDivehi:            \"Divehi (dv)\",\n\t\tLangDutch:             \"Dutch (nl)\",\n\t\tLangEnglish:           \"English (en)\",\n\t\tLangEstonian:          \"Estonian (et)\",\n\t\tLangFaroese:           \"Faroese (fo)\",\n\t\tLangFilipino:          \"Filipino (fil)\",\n\t\tLangFinnish:           \"Finnish (fi)\",\n\t\tLangFrench:            \"French (fr)\",\n\t\tLangFrisian:           \"Frisian (fy)\",\n\t\tLangFulah:             \"Fulah (ff)\",\n\t\tLangFulahLatin:        \"Fulah (Latin) (ff-Latn)\",\n\t\tLangGalician:          \"Galician (gl)\",\n\t\tLangGeorgian:          \"Georgian (ka)\",\n\t\tLangGerman:            \"German (de)\",\n\t\tLangGreek:             \"Greek (el)\",\n\t\tLangGreenlandic:       \"Greenlandic (kl)\",\n\t\tLangGuarani:           \"Guarani (gn)\",\n\t\tLangGujarati:          \"Gujarati (gu)\",\n\t\tLangHausaLatin:        \"Hausa (Latin) (ha)\",\n\t\tLangHawaiian:          \"Hawaiian (haw)\",\n\t\tLangHebrew:            \"Hebrew (he)\",\n\t\tLangHindi:             \"Hindi (hi)\",\n\t\tLangHungarian:         \"Hungarian (hu)\",\n\t\tLangIcelandic:         \"Icelandic (is)\",\n\t\tLangIgbo:              \"Igbo (ig)\",\n\t\tLangIndonesian:        \"Indonesian (id)\",\n\t\tLangInuktitutLatin:    \"Inuktitut (Latin) (iu)\",\n\t\tLangIrish:             \"Irish (ga)\",\n\t\tLangItalian:           \"Italian (it)\",\n\t\tLangJapanese:          \"Japanese (ja)\",\n\t\tLangKannada:           \"Kannada (kn)\",\n\t\tLangKashmiri:          \"Kashmiri (ks)\",\n\t\tLangKazakh:            \"Kazakh (kk)\",\n\t\tLangKhmer:             \"Khmer (km)\",\n\t\tLangKiche:             \"K'iche (quc)\",\n\t\tLangKinyarwanda:       \"Kinyarwanda (rw)\",\n\t\tLangKiswahili:         \"Kiswahili (sw)\",\n\t\tLangKonkani:           \"Konkani (kok)\",\n\t\tLangKorean:            \"Korean (ko)\",\n\t\tLangKyrgyz:            \"Kyrgyz (ky)\",\n\t\tLangLao:               \"Lao (lo)\",\n\t\tLangLatvian:           \"Latvian (lv)\",\n\t\tLangLithuanian:        \"Lithuanian (lt)\",\n\t\tLangLowerSorbian:      \"Lower Sorbian (dsb)\",\n\t\tLangLuxembourgish:     \"Luxembourgish (lb)\",\n\t\tLangMacedonian:        \"Macedonian (mk)\",\n\t\tLangMalay:             \"Malay (ms)\",\n\t\tLangMalayalam:         \"Malayalam (ml)\",\n\t\tLangMaltese:           \"Maltese (mt)\",\n\t\tLangMaori:             \"Maori (mi)\",\n\t\tLangMapudungun:        \"Mapudungun (arn)\",\n\t\tLangMarathi:           \"Marathi (mr)\",\n\t\tLangMohawk:            \"Mohawk (moh)\",\n\t\tLangMongolianCyrillic: \"Mongolian (Cyrillic) (mn)\",\n\t\tLangNepali:            \"Nepali (ne)\",\n\t\tLangNorwegianBokmalNo: \"Norwegian (Bokmal) (no)\",\n\t\tLangNorwegianBokmal:   \"Norwegian (Bokmal) (nb)\",\n\t\tLangNorwegianNynorsk:  \"Norwegian (Nynorsk) (nn)\",\n\t\tLangOccitan:           \"Occitan (oc)\",\n\t\tLangOdia:              \"Odia (or)\",\n\t\tLangOromo:             \"Oromo (om)\",\n\t\tLangPashto:            \"Pashto (ps)\",\n\t\tLangPersian:           \"Persian (fa)\",\n\t\tLangPolish:            \"Polish (pl)\",\n\t\tLangPortuguese:        \"Portuguese (pt)\",\n\t\tLangPunjabi:           \"Punjabi (pa)\",\n\t\tLangQuechua:           \"Quechua (quz)\",\n\t\tLangRomanian:          \"Romanian (ro)\",\n\t\tLangRomansh:           \"Romansh (rm)\",\n\t\tLangRussian:           \"Russian (ru)\",\n\t\tLangSakha:             \"Sakha (sah)\",\n\t\tLangSamiInari:         \"Sami (Inari) (smn)\",\n\t\tLangSamiLule:          \"Sami (Lule) (smj)\",\n\t\tLangSamiNorthern:      \"Sami (Northern) (se)\",\n\t\tLangSamiSkolt:         \"Sami (Skolt) (sms)\",\n\t\tLangSamiSouthern:      \"Sami (Southern) (sma)\",\n\t\tLangSanskrit:          \"Sanskrit (sa)\",\n\t\tLangScottishGaelic:    \"Scottish Gaelic (gd)\",\n\t\tLangSerbianLatin:      \"Serbian (Latin) (sr)\",\n\t\tLangSesothoSaLeboa:    \"Sesotho Sa Leboa (nso)\",\n\t\tLangSetswana:          \"Setswana (tn)\",\n\t\tLangSindhi:            \"Sindhi (sd)\",\n\t\tLangSinhala:           \"Sinhala (si)\",\n\t\tLangSlovak:            \"Slovak (sk)\",\n\t\tLangSlovenian:         \"Slovenian (sl)\",\n\t\tLangSomali:            \"Somali (so)\",\n\t\tLangSotho:             \"Sotho (st)\",\n\t\tLangSpanish:           \"Spanish (es)\",\n\t\tLangSwedish:           \"Swedish (sv)\",\n\t\tLangSyriac:            \"Syriac (syr)\",\n\t\tLangTajikCyrillic:     \"Tajik (Cyrillic) (tg)\",\n\t\tLangTamazightLatin:    \"Tamazight (Latin) (tzm)\",\n\t\tLangTamil:             \"Tamil (ta)\",\n\t\tLangTatar:             \"Tatar (tt)\",\n\t\tLangTelugu:            \"Telugu (te)\",\n\t\tLangThai:              \"Thai (th)\",\n\t\tLangTibetan:           \"Tibetan (bo)\",\n\t\tLangTigrinya:          \"Tigrinya (ti)\",\n\t\tLangTsonga:            \"Tsonga (ts)\",\n\t\tLangTurkish:           \"Turkish (tr)\",\n\t\tLangTurkmen:           \"Turkmen (tk)\",\n\t\tLangUkrainian:         \"Ukrainian (uk)\",\n\t\tLangUpperSorbian:      \"Upper Sorbian (hsb)\",\n\t\tLangUrdu:              \"Urdu (ur)\",\n\t\tLangUyghur:            \"Uyghur (ug)\",\n\t\tLangUzbekLatin:        \"Uzbek (Latin) (uz)\",\n\t\tLangVenda:             \"Venda (ve)\",\n\t\tLangVietnamese:        \"Vietnamese (vi)\",\n\t\tLangWelsh:             \"Welsh (cy)\",\n\t\tLangWolof:             \"Wolof (wo)\",\n\t\tLangXhosa:             \"Xhosa (xh)\",\n\t\tLangYi:                \"Yi (ii)\",\n\t\tLangYoruba:            \"Yoruba (yo)\",\n\t\tLangZulu:              \"Zulu (zu)\",\n\t}\n\n\tif val, ok := rsrcLangMap[lang]; ok {\n\t\treturn val\n\t}\n\n\treturn \"?\"\n}\n\n// String stringify the resource sub language.\nfunc (subLang ResourceSubLang) String() string {\n\n\trsrcSubLangMap := map[ResourceSubLang]string{\n\t\tSubLangAfrikaansSouthAfrica:                    \"Afrikaans South Africa (af-ZA)\",\n\t\tSubLangAlbanianAlbania:                         \"Albanian Albania (sq-AL)\",\n\t\tSubLangAlsatianFrance:                          \"Alsatian France (gsw-FR)\",\n\t\tSubLangAmharicEthiopia:                         \"Amharic Ethiopia (am-ET)\",\n\t\tSubLangArabicAlgeria:                           \"Arabic Algeria (ar-DZ)\",\n\t\tSubLangArabicBahrain:                           \"Arabic Bahrain (ar-BH)\",\n\t\tSubLangArabicEgypt:                             \"Arabic Egypt (ar-EG)\",\n\t\tSubLangArabicIraq:                              \"Arabic Iraq (ar-IQ)\",\n\t\tSubLangArabicJordan:                            \"Arabic Jordan (ar-JO)\",\n\t\tSubLangArabicKuwait:                            \"Arabic Kuwait (ar-KW)\",\n\t\tSubLangArabicLebanon:                           \"Arabic Lebanon (ar-LB)\",\n\t\tSubLangArabicLibya:                             \"Arabic Libya (ar-LY)\",\n\t\tSubLangArabicMorocco:                           \"Arabic Morocco (ar-MA)\",\n\t\tSubLangArabicOman:                              \"Arabic Oman (ar-OM)\",\n\t\tSubLangArabicQatar:                             \"Arabic Qatar (ar-QA)\",\n\t\tSubLangArabicSaudiArabia:                       \"Arabic Saudi Arabia (ar-SA)\",\n\t\tSubLangArabicSyria:                             \"Arabic Syria (ar-SY)\",\n\t\tSubLangArabicTunisia:                           \"Arabic Tunisia (ar-TN)\",\n\t\tSubLangArabicUae:                               \"Arabic U.a.e. (ar-AE)\",\n\t\tSubLangArabicYemen:                             \"Arabic Yemen (ar-YE)\",\n\t\tSubLangArmenianArmenia:                         \"Armenian Armenia (hy-AM)\",\n\t\tSubLangAssameseIndia:                           \"Assamese India (as-IN)\",\n\t\tSubLangAzerbaijaniCyrillic:                     \"Azerbaijani (Cyrillic) (az-Cyrl)\",\n\t\tSubLangAzerbaijaniCyrillicAzerbaijan:           \"Azerbaijani (Cyrillic) Azerbaijan (az-Cyrl-AZ)\",\n\t\tSubLangAzerbaijaniLatin:                        \"Azerbaijani (Latin) (az-Latn)\",\n\t\tSubLangAzerbaijaniLatinAzerbaijan:              \"Azerbaijani (Latin) Azerbaijan (az-Latn-AZ)\",\n\t\tSubLangBanglaBangladesh:                        \"Bangla Bangladesh (bn-BD)\",\n\t\tSubLangBanglaIndia:                             \"Bangla India (bn-IN)\",\n\t\tSubLangBashkirRussia:                           \"Bashkir Russia (ba-RU)\",\n\t\tSubLangBasqueSpain:                             \"Basque Spain (eu-ES)\",\n\t\tSubLangBelarusianBelarus:                       \"Belarusian Belarus (be-BY)\",\n\t\tSubLangBosnianCyrillic:                         \"Bosnian (Cyrillic) (bs-Cyrl)\",\n\t\tSubLangBosnianCyrillicBosniaAndHerzegovina:     \"Bosnian (Cyrillic) Bosnia And Herzegovina (bs-Cyrl-BA)\",\n\t\tSubLangBosnianLatin:                            \"Bosnian (Latin) (bs-Latn)\",\n\t\tSubLangBosnianLatinBosniaAndHerzegovina:        \"Bosnian (Latin) Bosnia And Herzegovina (bs-Latn-BA)\",\n\t\tSubLangBretonFrance:                            \"Breton France (br-FR)\",\n\t\tSubLangBulgarianBulgaria:                       \"Bulgarian Bulgaria (bg-BG)\",\n\t\tSubLangBurmeseMyanmar:                          \"Burmese Myanmar (my-MM)\",\n\t\tSubLangCatalanSpain:                            \"Catalan Spain (ca-ES)\",\n\t\tSubLangCentralAtlasTamazightArabicMorocco:      \"Central Atlas Tamazight (Arabic) Morocco (tzm-ArabMA)\",\n\t\tSubLangCentralKurdish:                          \"Central Kurdish (ku-Arab)\",\n\t\tSubLangCentralKurdishIraq:                      \"Central Kurdish Iraq (ku-Arab-IQ)\",\n\t\tSubLangCherokee:                                \"Cherokee (chr-Cher)\",\n\t\tSubLangCherokeeUnitedStates:                    \"Cherokee United States (chr-Cher-US)\",\n\t\tSubLangChineseSimplified:                       \"Chinese (Simplified) (zh-Hans)\",\n\t\tSubLangChineseSimplifiedPeoplesRepublicOfChina: \"Chinese (Simplified) People's Republic Of China (zh-CN)\",\n\t\tSubLangChineseSimplifiedSingapore:              \"Chinese (Simplified) Singapore (zh-SG)\",\n\t\tSubLangChineseTraditional:                      \"Chinese (Traditional) (zh-Hant)\",\n\t\tSubLangChineseTraditionalHongKongSar:           \"Chinese (Traditional) Hong Kong S.a.r. (zh-HK)\",\n\t\tSubLangChineseTraditionalMacaoSar:              \"Chinese (Traditional) Macao S.a.r. (zh-MO)\",\n\t\tSubLangChineseTraditionalTaiwan:                \"Chinese (Traditional) Taiwan (zh-TW)\",\n\t\tSubLangCorsicanFrance:                          \"Corsican France (co-FR)\",\n\t\tSubLangCroatianCroatia:                         \"Croatian Croatia (hr-HR)\",\n\t\tSubLangCroatianLatinBosniaAndHerzegovina:       \"Croatian (Latin) Bosnia And Herzegovina (hr-BA)\",\n\t\tSubLangCzechCzechRepublic:                      \"Czech Czech Republic (cs-CZ)\",\n\t\tSubLangDanishDenmark:                           \"Danish Denmark (da-DK)\",\n\t\tSubLangDariAfghanistan:                         \"Dari Afghanistan (prs-AF)\",\n\t\tSubLangDivehiMaldives:                          \"Divehi Maldives (dv-MV)\",\n\t\tSubLangDutchBelgium:                            \"Dutch Belgium (nl-BE)\",\n\t\tSubLangDutchNetherlands:                        \"Dutch Netherlands (nl-NL)\",\n\t\tSubLangDzongkhaBhutan:                          \"Dzongkha Bhutan (dz-BT)\",\n\t\tSubLangEnglishAustralia:                        \"English Australia (en-AU)\",\n\t\tSubLangEnglishBelize:                           \"English Belize (en-BZ)\",\n\t\tSubLangEnglishCanada:                           \"English Canada (en-CA)\",\n\t\tSubLangEnglishCaribbean:                        \"English Caribbean (en-029)\",\n\t\tSubLangEnglishHongKong:                         \"English Hong Kong (en-HK)\",\n\t\tSubLangEnglishIndia:                            \"English India (en-IN)\",\n\t\tSubLangEnglishIreland:                          \"English Ireland (en-IE)\",\n\t\tSubLangEnglishJamaica:                          \"English Jamaica (en-JM)\",\n\t\tSubLangEnglishMalaysia:                         \"English Malaysia (en-MY)\",\n\t\tSubLangEnglishNewZealand:                       \"English New Zealand (en-NZ)\",\n\t\tSubLangEnglishRepublicOfThePhilippines:         \"English Republic Of The Philippines (en-PH)\",\n\t\tSubLangEnglishSingapore:                        \"English Singapore (en-SG)\",\n\t\tSubLangEnglishSouthAfrica:                      \"English South Africa (en-ZA)\",\n\t\tSubLangEnglishTrinidadAndTobago:                \"English Trinidad And Tobago (en-TT)\",\n\t\tSubLangEnglishUnitedArabEmirates:               \"English United Arab Emirates (en-AE)\",\n\t\tSubLangEnglishUnitedKingdom:                    \"English United Kingdom (en-GB)\",\n\t\tSubLangEnglishUnitedStates:                     \"English United States (en-US)\",\n\t\tSubLangEnglishZimbabwe:                         \"English Zimbabwe (en-ZW)\",\n\t\tSubLangEstonianEstonia:                         \"Estonian Estonia (et-EE)\",\n\t\tSubLangFaroeseFaroeIslands:                     \"Faroese Faroe Islands (fo-FO)\",\n\t\tSubLangFilipinoPhilippines:                     \"Filipino Philippines (fil-PH)\",\n\t\tSubLangFinnishFinland:                          \"Finnish Finland (fi-FI)\",\n\t\tSubLangFrenchBelgium:                           \"French Belgium (fr-BE)\",\n\t\tSubLangFrenchCameroon:                          \"French Cameroon (fr-CM)\",\n\t\tSubLangFrenchCanada:                            \"French Canada (fr-CA)\",\n\t\tSubLangFrenchCaribbean:                         \"French Caribbean (fr-029)\",\n\t\tSubLangFrenchCongoDrc:                          \"French Congo, Drc (fr-CD)\",\n\t\tSubLangFrenchCôteDivoire:                       \"French Côte D'ivoire (fr-CI)\",\n\t\tSubLangFrenchFrance:                            \"French France (fr-FR)\",\n\t\tSubLangFrenchHaiti:                             \"French Haiti (fr-HT)\",\n\t\tSubLangFrenchLuxembourg:                        \"French Luxembourg (fr-LU)\",\n\t\tSubLangFrenchMali:                              \"French Mali (fr-ML)\",\n\t\tSubLangFrenchMorocco:                           \"French Morocco (fr-MA)\",\n\t\tSubLangFrenchPrincipalityOfMonaco:              \"French Principality Of Monaco (fr-MC)\",\n\t\tSubLangFrenchReunion:                           \"French Reunion (fr-RE)\",\n\t\tSubLangFrenchSenegal:                           \"French Senegal (fr-SN)\",\n\t\tSubLangFrenchSwitzerland:                       \"French Switzerland (fr-CH)\",\n\t\tSubLangFrisianNetherlands:                      \"Frisian Netherlands (fy-NL)\",\n\t\tSubLangFulahNigeria:                            \"Fulah Nigeria (ff-NG)\",\n\t\tSubLangFulahLatinNigeria:                       \"Fulah (Latin) Nigeria (ff-Latn-NG)\",\n\t\tSubLangFulahSenegal:                            \"Fulah Senegal (ff-Latn-SN)\",\n\t\tSubLangGalicianSpain:                           \"Galician Spain (gl-ES)\",\n\t\tSubLangGeorgianGeorgia:                         \"Georgian Georgia (ka-GE)\",\n\t\tSubLangGermanAustria:                           \"German Austria (de-AT)\",\n\t\tSubLangGermanGermany:                           \"German Germany (de-DE)\",\n\t\tSubLangGermanLiechtenstein:                     \"German Liechtenstein (de-LI)\",\n\t\tSubLangGermanLuxembourg:                        \"German Luxembourg (de-LU)\",\n\t\tSubLangGermanSwitzerland:                       \"German Switzerland (de-CH)\",\n\t\tSubLangGreekGreece:                             \"Greek Greece (el-GR)\",\n\t\tSubLangGreenlandicGreenland:                    \"Greenlandic Greenland (kl-GL)\",\n\t\tSubLangGuaraniParaguay:                         \"Guarani Paraguay (gn-PY)\",\n\t\tSubLangGujaratiIndia:                           \"Gujarati India (gu-IN)\",\n\t\tSubLangHausaLatin:                              \"Hausa (Latin) (ha-Latn)\",\n\t\tSubLangHausaLatinNigeria:                       \"Hausa (Latin) Nigeria (ha-Latn-NG)\",\n\t\tSubLangHawaiianUnitedStates:                    \"Hawaiian United States (haw-US)\",\n\t\tSubLangHebrewIsrael:                            \"Hebrew Israel (he-IL)\",\n\t\tSubLangHindiIndia:                              \"Hindi India (hi-IN)\",\n\t\tSubLangHungarianHungary:                        \"Hungarian Hungary (hu-HU)\",\n\t\tSubLangIcelandicIceland:                        \"Icelandic Iceland (is-IS)\",\n\t\tSubLangIgboNigeria:                             \"Igbo Nigeria (ig-NG)\",\n\t\tSubLangIndonesianIndonesia:                     \"Indonesian Indonesia (id-ID)\",\n\t\tSubLangInuktitutLatin:                          \"Inuktitut (Latin) (iu-Latn)\",\n\t\tSubLangInuktitutLatinCanada:                    \"Inuktitut (Latin) Canada (iu-Latn-CA)\",\n\t\tSubLangInuktitutSyllabics:                      \"Inuktitut (Syllabics) (iu-Cans)\",\n\t\tSubLangInuktitutSyllabicsCanada:                \"Inuktitut (Syllabics) Canada (iu-Cans-CA)\",\n\t\tSubLangIrishIreland:                            \"Irish Ireland (ga-IE)\",\n\t\tSubLangItalianItaly:                            \"Italian Italy (it-IT)\",\n\t\tSubLangItalianSwitzerland:                      \"Italian Switzerland (it-CH)\",\n\t\tSubLangJapaneseJapan:                           \"Japanese Japan (ja-JP)\",\n\t\tSubLangKannadaIndia:                            \"Kannada India (kn-IN)\",\n\t\tSubLangKanuriLatinNigeria:                      \"Kanuri (Latin) Nigeria (kr-Latn-NG)\",\n\t\tSubLangKashmiriPersoArabic:                     \"Kashmiri Perso-Arabic (ks-Arab)\",\n\t\tSubLangKashmiriDevanagariIndia:                 \"Kashmiri (Devanagari) India (ks-Deva-IN)\",\n\t\tSubLangKazakhKazakhstan:                        \"Kazakh Kazakhstan (kk-KZ)\",\n\t\tSubLangKhmerCambodia:                           \"Khmer Cambodia (km-KH)\",\n\t\tSubLangKicheGuatemala:                          \"K'iche Guatemala (quc-Latn-GT)\",\n\t\tSubLangKinyarwandaRwanda:                       \"Kinyarwanda Rwanda (rw-RW)\",\n\t\tSubLangKiswahiliKenya:                          \"Kiswahili Kenya (sw-KE)\",\n\t\tSubLangKonkaniIndia:                            \"Konkani India (kok-IN)\",\n\t\tSubLangKoreanKorea:                             \"Korean Korea (ko-KR)\",\n\t\tSubLangKyrgyzKyrgyzstan:                        \"Kyrgyz Kyrgyzstan (ky-KG)\",\n\t\tSubLangLaoLaoPdr:                               \"Lao Lao P.d.r. (lo-LA)\",\n\t\tSubLangLatinVaticanCity:                        \"Latin Vatican City (la-VA)\",\n\t\tSubLangLatvianLatvia:                           \"Latvian Latvia (lv-LV)\",\n\t\tSubLangLithuanianLithuania:                     \"Lithuanian Lithuania (lt-LT)\",\n\t\tSubLangLowerSorbianGermany:                     \"Lower Sorbian Germany (dsb-DE)\",\n\t\tSubLangLuxembourgishLuxembourg:                 \"Luxembourgish Luxembourg (lb-LU)\",\n\t\tSubLangMacedonianNorthMacedonia:                \"Macedonian North Macedonia (mk-MK)\",\n\t\tSubLangMalayBruneiDarussalam:                   \"Malay Brunei Darussalam (ms-BN)\",\n\t\tSubLangMalayMalaysia:                           \"Malay Malaysia (ms-MY)\",\n\t\tSubLangMalayalamIndia:                          \"Malayalam India (ml-IN)\",\n\t\tSubLangMalteseMalta:                            \"Maltese Malta (mt-MT)\",\n\t\tSubLangMaoriNewZealand:                         \"Maori New Zealand (mi-NZ)\",\n\t\tSubLangMapudungunChile:                         \"Mapudungun Chile (arn-CL)\",\n\t\tSubLangMarathiIndia:                            \"Marathi India (mr-IN)\",\n\t\tSubLangMohawkCanada:                            \"Mohawk Canada (moh-CA)\",\n\t\tSubLangMongolianCyrillic:                       \"Mongolian (Cyrillic) (mn-Cyrl)\",\n\t\tSubLangMongolianCyrillicMongolia:               \"Mongolian (Cyrillic) Mongolia (mn-MN)\",\n\t\tSubLangMongolianTraditionalMongolian:           \"Mongolian (Traditional Mongolian) (mn-Mong)\",\n\t\tSubLangMongolianTraditionalMongolianPeoplesRepublicOfChina: \"Mongolian (Traditional Mongolian) People's Republic Of China (mn-MongCN)\",\n\t\tSubLangMongolianTraditionalMongolianMongolia:               \"Mongolian (Traditional Mongolian) Mongolia (mn-MongMN)\",\n\t\tSubLangNepaliIndia:            \"Nepali India (ne-IN)\",\n\t\tSubLangNepaliNepal:            \"Nepali Nepal (ne-NP)\",\n\t\tSubLangNorwegianBokmalNorway:  \"Norwegian (Bokmal) Norway (nb-NO)\",\n\t\tSubLangNorwegianNynorskNorway: \"Norwegian (Nynorsk) Norway (nn-NO)\",\n\t\tSubLangOccitanFrance:          \"Occitan France (oc-FR)\",\n\t\tSubLangOdiaIndia:              \"Odia India (or-IN)\",\n\t\tSubLangOromoEthiopia:          \"Oromo Ethiopia (om-ET)\",\n\t\tSubLangPashtoAfghanistan:      \"Pashto Afghanistan (ps-AF)\",\n\t\tSubLangPersianIran:            \"Persian Iran (fa-IR)\",\n\t\tSubLangPolishPoland:           \"Polish Poland (pl-PL)\",\n\t\tSubLangPortugueseBrazil:       \"Portuguese Brazil (pt-BR)\",\n\t\tSubLangPortuguesePortugal:     \"Portuguese Portugal (pt-PT)\",\n\t\tSubLangPseudoLanguagePseudoLocaleForEastAsianComplexScriptLocalizationTesting: \"Pseudo Language Pseudo Locale For East Asian/Complex Script Localization Testing (qps-ploca)\",\n\t\tSubLangPseudoLanguagePseudoLocaleUsedForLocalizationTesting:                   \"Pseudo Language Pseudo Locale Used For Localization Testing (qps-ploc)\",\n\t\tSubLangPseudoLanguagePseudoLocaleUsedForLocalizationTestingOfMirroredLocales:  \"Pseudo Language Pseudo Locale Used For Localization Testing Of Mirrored Locales (qps-plocm)\",\n\t\tSubLangPunjabi:                                  \"Punjabi (pa-Arab)\",\n\t\tSubLangPunjabiIndia:                             \"Punjabi India (pa-IN)\",\n\t\tSubLangPunjabiIslamicRepublicOfPakistan:         \"Punjabi Islamic Republic Of Pakistan (pa-Arab-PK)\",\n\t\tSubLangQuechuaBolivia:                           \"Quechua Bolivia (quz-BO)\",\n\t\tSubLangQuechuaEcuador:                           \"Quechua Ecuador (quz-EC)\",\n\t\tSubLangQuechuaPeru:                              \"Quechua Peru (quz-PE)\",\n\t\tSubLangRomanianMoldova:                          \"Romanian Moldova (ro-MD)\",\n\t\tSubLangRomanianRomania:                          \"Romanian Romania (ro-RO)\",\n\t\tSubLangRomanshSwitzerland:                       \"Romansh Switzerland (rm-CH)\",\n\t\tSubLangRussianMoldova:                           \"Russian Moldova (ru-MD)\",\n\t\tSubLangRussianRussia:                            \"Russian Russia (ru-RU)\",\n\t\tSubLangSakhaRussia:                              \"Sakha Russia (sah-RU)\",\n\t\tSubLangSamiInariFinland:                         \"Sami (Inari) Finland (smn-FI)\",\n\t\tSubLangSamiLuleNorway:                           \"Sami (Lule) Norway (smj-NO)\",\n\t\tSubLangSamiLuleSweden:                           \"Sami (Lule) Sweden (smj-SE)\",\n\t\tSubLangSamiNorthernFinland:                      \"Sami (Northern) Finland (se-FI)\",\n\t\tSubLangSamiNorthernNorway:                       \"Sami (Northern) Norway (se-NO)\",\n\t\tSubLangSamiNorthernSweden:                       \"Sami (Northern) Sweden (se-SE)\",\n\t\tSubLangSamiSkoltFinland:                         \"Sami (Skolt) Finland (sms-FI)\",\n\t\tSubLangSamiSouthernNorway:                       \"Sami (Southern) Norway (sma-NO)\",\n\t\tSubLangSamiSouthernSweden:                       \"Sami (Southern) Sweden (sma-SE)\",\n\t\tSubLangSanskritIndia:                            \"Sanskrit India (sa-IN)\",\n\t\tSubLangScottishGaelicUnitedKingdom:              \"Scottish Gaelic United Kingdom (gd-GB)\",\n\t\tSubLangSerbianCyrillic:                          \"Serbian (Cyrillic) (sr-Cyrl)\",\n\t\tSubLangSerbianCyrillicBosniaAndHerzegovina:      \"Serbian (Cyrillic) Bosnia And Herzegovina (sr-Cyrl-BA)\",\n\t\tSubLangSerbianCyrillicMontenegro:                \"Serbian (Cyrillic) Montenegro (sr-Cyrl-ME)\",\n\t\tSubLangSerbianCyrillicSerbia:                    \"Serbian (Cyrillic) Serbia (sr-Cyrl-RS)\",\n\t\tSubLangSerbianCyrillicSerbiaAndMontenegroFormer: \"Serbian (Cyrillic) Serbia And Montenegro (Former) (sr-Cyrl-CS)\",\n\t\tSubLangSerbianLatin:                             \"Serbian (Latin) (sr-Latn)\",\n\t\tSubLangSerbianLatinBosniaAndHerzegovina:         \"Serbian (Latin) Bosnia And Herzegovina (sr-Latn-BA)\",\n\t\tSubLangSerbianLatinMontenegro:                   \"Serbian (Latin) Montenegro (sr-Latn-ME)\",\n\t\tSubLangSerbianLatinSerbia:                       \"Serbian (Latin) Serbia (sr-Latn-RS)\",\n\t\tSubLangSerbianLatinSerbiaAndMontenegroFormer:    \"Serbian (Latin) Serbia And Montenegro (Former) (sr-Latn-CS)\",\n\t\tSubLangSesothoSaLeboaSouthAfrica:                \"Sesotho Sa Leboa South Africa (nso-ZA)\",\n\t\tSubLangSetswanaBotswana:                         \"Setswana Botswana (tn-BW)\",\n\t\tSubLangSetswanaSouthAfrica:                      \"Setswana South Africa (tn-ZA)\",\n\t\tSubLangSindhi:                                   \"Sindhi (sd-Arab)\",\n\t\tSubLangSindhiIslamicRepublicOfPakistan:          \"Sindhi Islamic Republic Of Pakistan (sd-Arab-PK)\",\n\t\tSubLangSinhalaSriLanka:                          \"Sinhala Sri Lanka (si-LK)\",\n\t\tSubLangSlovakSlovakia:                           \"Slovak Slovakia (sk-SK)\",\n\t\tSubLangSlovenianSlovenia:                        \"Slovenian Slovenia (sl-SI)\",\n\t\tSubLangSomaliSomalia:                            \"Somali Somalia (so-SO)\",\n\t\tSubLangSothoSouthAfrica:                         \"Sotho South Africa (st-ZA)\",\n\t\tSubLangSpanishArgentina:                         \"Spanish Argentina (es-AR)\",\n\t\tSubLangSpanishBolivarianRepublicOfVenezuela:     \"Spanish Bolivarian Republic Of Venezuela (es-VE)\",\n\t\tSubLangSpanishBolivia:                           \"Spanish Bolivia (es-BO)\",\n\t\tSubLangSpanishChile:                             \"Spanish Chile (es-CL)\",\n\t\tSubLangSpanishColombia:                          \"Spanish Colombia (es-CO)\",\n\t\tSubLangSpanishCostaRica:                         \"Spanish Costa Rica (es-CR)\",\n\t\tSubLangSpanishCuba:                              \"Spanish Cuba (es-CU)\",\n\t\tSubLangSpanishDominicanRepublic:                 \"Spanish Dominican Republic (es-DO)\",\n\t\tSubLangSpanishEcuador:                           \"Spanish Ecuador (es-EC)\",\n\t\tSubLangSpanishElSalvador:                        \"Spanish El Salvador (es-SV)\",\n\t\tSubLangSpanishGuatemala:                         \"Spanish Guatemala (es-GT)\",\n\t\tSubLangSpanishHonduras:                          \"Spanish Honduras (es-HN)\",\n\t\tSubLangSpanishLatinAmerica:                      \"Spanish Latin America (es-419)\",\n\t\tSubLangSpanishMexico:                            \"Spanish Mexico (es-MX)\",\n\t\tSubLangSpanishNicaragua:                         \"Spanish Nicaragua (es-NI)\",\n\t\tSubLangSpanishPanama:                            \"Spanish Panama (es-PA)\",\n\t\tSubLangSpanishParaguay:                          \"Spanish Paraguay (es-PY)\",\n\t\tSubLangSpanishPeru:                              \"Spanish Peru (es-PE)\",\n\t\tSubLangSpanishPuertoRico:                        \"Spanish Puerto Rico (es-PR)\",\n\t\tSubLangSpanishSpainTraditional:                  \"Spanish Spain (es-ES_tradnl)\",\n\t\tSubLangSpanishSpain:                             \"Spanish Spain (es-ES)\",\n\t\tSubLangSpanishUnitedStates:                      \"Spanish United States (es-US)\",\n\t\tSubLangSpanishUruguay:                           \"Spanish Uruguay (es-UY)\",\n\t\tSubLangSwedishFinland:                           \"Swedish Finland (sv-FI)\",\n\t\tSubLangSwedishSweden:                            \"Swedish Sweden (sv-SE)\",\n\t\tSubLangSyriacSyria:                              \"Syriac Syria (syr-SY)\",\n\t\tSubLangTajikCyrillic:                            \"Tajik (Cyrillic) (tg-Cyrl)\",\n\t\tSubLangTajikCyrillicTajikistan:                  \"Tajik (Cyrillic) Tajikistan (tg-Cyrl-TJ)\",\n\t\tSubLangTamazightLatin:                           \"Tamazight (Latin) (tzm-Latn)\",\n\t\tSubLangTamazightLatinAlgeria:                    \"Tamazight (Latin) Algeria (tzm-Latn-DZ)\",\n\t\tSubLangTamilIndia:                               \"Tamil India (ta-IN)\",\n\t\tSubLangTamilSriLanka:                            \"Tamil Sri Lanka (ta-LK)\",\n\t\tSubLangTatarRussia:                              \"Tatar Russia (tt-RU)\",\n\t\tSubLangTeluguIndia:                              \"Telugu India (te-IN)\",\n\t\tSubLangThaiThailand:                             \"Thai Thailand (th-TH)\",\n\t\tSubLangTibetanPeoplesRepublicOfChina:            \"Tibetan People's Republic Of China (bo-CN)\",\n\t\tSubLangTigrinyaEritrea:                          \"Tigrinya Eritrea (ti-ER)\",\n\t\tSubLangTigrinyaEthiopia:                         \"Tigrinya Ethiopia (ti-ET)\",\n\t\tSubLangTsongaSouthAfrica:                        \"Tsonga South Africa (ts-ZA)\",\n\t\tSubLangTurkishTurkey:                            \"Turkish Turkey (tr-TR)\",\n\t\tSubLangTurkmenTurkmenistan:                      \"Turkmen Turkmenistan (tk-TM)\",\n\t\tSubLangUkrainianUkraine:                         \"Ukrainian Ukraine (uk-UA)\",\n\t\tSubLangUpperSorbianGermany:                      \"Upper Sorbian Germany (hsb-DE)\",\n\t\tSubLangUrduIndia:                                \"Urdu India (ur-IN)\",\n\t\tSubLangUrduIslamicRepublicOfPakistan:            \"Urdu Islamic Republic Of Pakistan (ur-PK)\",\n\t\tSubLangUyghurPeoplesRepublicOfChina:             \"Uyghur People's Republic Of China (ug-CN)\",\n\t\tSubLangUzbekCyrillic:                            \"Uzbek (Cyrillic) (uz-Cyrl)\",\n\t\tSubLangUzbekCyrillicUzbekistan:                  \"Uzbek (Cyrillic) Uzbekistan (uz-Cyrl-UZ)\",\n\t\tSubLangUzbekLatin:                               \"Uzbek (Latin) (uz-Latn)\",\n\t\tSubLangUzbekLatinUzbekistan:                     \"Uzbek (Latin) Uzbekistan (uz-Latn-UZ)\",\n\t\tSubLangValencianSpain:                           \"Valencian Spain (ca-ESvalencia)\",\n\t\tSubLangVendaSouthAfrica:                         \"Venda South Africa (ve-ZA)\",\n\t\tSubLangVietnameseVietnam:                        \"Vietnamese Vietnam (vi-VN)\",\n\t\tSubLangWelshUnitedKingdom:                       \"Welsh United Kingdom (cy-GB)\",\n\t\tSubLangWolofSenegal:                             \"Wolof Senegal (wo-SN)\",\n\t\tSubLangXhosaSouthAfrica:                         \"Xhosa South Africa (xh-ZA)\",\n\t\tSubLangYiPeoplesRepublicOfChina:                 \"Yi People's Republic Of China (ii-CN)\",\n\t\tSubLangYiddishWorld:                             \"Yiddish World (yi-001)\",\n\t\tSubLangYorubaNigeria:                            \"Yoruba Nigeria (yo-NG)\",\n\t\tSubLangZuluSouthAfrica:                          \"Zulu South Africa (zu-ZA)\",\n\t}\n\n\tif val, ok := rsrcSubLangMap[subLang]; ok {\n\t\treturn val\n\t}\n\n\treturn \"?\"\n}\n\n// PrettyResourceLang prettifies the resource lang and sub lang.\nfunc PrettyResourceLang(lang ResourceLang, subLang int) string {\n\tm := map[ResourceLang]map[int]ResourceSubLang{\n\t\tLangAfrikaans: {\n\t\t\t0x1: SubLangAfrikaansSouthAfrica,\n\t\t},\n\t\tLangAlbanian: {\n\t\t\t0x1: SubLangAlbanianAlbania,\n\t\t},\n\t\tLangAlsatian: {\n\t\t\t0x1: SubLangAlsatianFrance,\n\t\t},\n\t\tLangAmharic: {\n\t\t\t0x1: SubLangAmharicEthiopia,\n\t\t},\n\t\tLangArabic: {\n\t\t\t0x5:  SubLangArabicAlgeria,\n\t\t\t0xf:  SubLangArabicBahrain,\n\t\t\t0x3:  SubLangArabicEgypt,\n\t\t\t0x2:  SubLangArabicIraq,\n\t\t\t0xb:  SubLangArabicJordan,\n\t\t\t0xd:  SubLangArabicKuwait,\n\t\t\t0xc:  SubLangArabicLebanon,\n\t\t\t0x4:  SubLangArabicLibya,\n\t\t\t0x6:  SubLangArabicMorocco,\n\t\t\t0x8:  SubLangArabicOman,\n\t\t\t0x10: SubLangArabicQatar,\n\t\t\t0x1:  SubLangArabicSaudiArabia,\n\t\t\t0xa:  SubLangArabicSyria,\n\t\t\t0x7:  SubLangArabicTunisia,\n\t\t\t0xe:  SubLangArabicUae,\n\t\t\t0x9:  SubLangArabicYemen,\n\t\t},\n\t\tLangArmenian: {\n\t\t\t0x1: SubLangArmenianArmenia,\n\t\t},\n\t\tLangAssamese: {\n\t\t\t0x1:  SubLangAssameseIndia,\n\t\t\t0x1d: SubLangAzerbaijaniCyrillic,\n\t\t\t0x2:  SubLangAzerbaijaniCyrillicAzerbaijan,\n\t\t},\n\t\tLangAzerbaijaniLatin: {\n\t\t\t0x1e: SubLangAzerbaijaniLatin,\n\t\t\t0x1:  SubLangAzerbaijaniLatinAzerbaijan,\n\t\t},\n\t\tLangBangla: {\n\t\t\t0x2: SubLangBanglaBangladesh,\n\t\t\t0x1: SubLangBanglaIndia,\n\t\t},\n\t\tLangBashkir: {\n\t\t\t0x1: SubLangBashkirRussia,\n\t\t},\n\t\tLangBasque: {\n\t\t\t0x1: SubLangBasqueSpain,\n\t\t},\n\t\tLangBelarusian: {\n\t\t\t0x1:  SubLangBelarusianBelarus,\n\t\t\t0x19: SubLangBosnianCyrillic,\n\t\t\t0x8:  SubLangBosnianCyrillicBosniaAndHerzegovina,\n\t\t\t0x1a: SubLangBosnianLatin,\n\t\t},\n\t\tLangBosnianLatin: {\n\t\t\t0x5: SubLangBosnianLatinBosniaAndHerzegovina,\n\t\t},\n\t\tLangBreton: {\n\t\t\t0x1: SubLangBretonFrance,\n\t\t},\n\t\tLangBulgarian: {\n\t\t\t0x1: SubLangBulgarianBulgaria,\n\t\t},\n\t\tLangBurmese: {\n\t\t\t0x1: SubLangBurmeseMyanmar,\n\t\t},\n\t\tLangCatalan: {\n\t\t\t0x1: SubLangCatalanSpain,\n\t\t},\n\t\tLangCentralKurdish: {\n\t\t\t0x1f: SubLangCentralKurdish,\n\t\t\t0x1:  SubLangCentralKurdishIraq,\n\t\t},\n\t\tLangCherokee: {\n\t\t\t0x1f: SubLangCherokee,\n\t\t\t0x1:  SubLangCherokeeUnitedStates,\n\t\t\t0x0:  SubLangChineseSimplified,\n\t\t},\n\t\tLangChineseSimplified: {\n\t\t\t0x2:  SubLangChineseSimplifiedPeoplesRepublicOfChina,\n\t\t\t0x4:  SubLangChineseSimplifiedSingapore,\n\t\t\t0x1f: SubLangChineseTraditional,\n\t\t\t0x3:  SubLangChineseTraditionalHongKongSar,\n\t\t\t0x5:  SubLangChineseTraditionalMacaoSar,\n\t\t\t0x1:  SubLangChineseTraditionalTaiwan,\n\t\t},\n\t\tLangCorsican: {\n\t\t\t0x1: SubLangCorsicanFrance,\n\t\t},\n\t\tLangCroatian: {\n\t\t\t0x1: SubLangCroatianCroatia,\n\t\t\t0x4: SubLangCroatianLatinBosniaAndHerzegovina,\n\t\t},\n\t\tLangCzech: {\n\t\t\t0x1: SubLangCzechCzechRepublic,\n\t\t},\n\t\tLangDanish: {\n\t\t\t0x1: SubLangDanishDenmark,\n\t\t},\n\t\tLangDari: {\n\t\t\t0x1: SubLangDariAfghanistan,\n\t\t},\n\t\tLangDivehi: {\n\t\t\t0x1: SubLangDivehiMaldives,\n\t\t},\n\t\tLangDutch: {\n\t\t\t0x2: SubLangDutchBelgium,\n\t\t\t0x1: SubLangDutchNetherlands,\n\t\t\t0x3: SubLangDzongkhaBhutan,\n\t\t},\n\t\tLangEnglish: {\n\t\t\t0x3:  SubLangEnglishAustralia,\n\t\t\t0xa:  SubLangEnglishBelize,\n\t\t\t0x4:  SubLangEnglishCanada,\n\t\t\t0x9:  SubLangEnglishCaribbean,\n\t\t\t0xf:  SubLangEnglishHongKong,\n\t\t\t0x10: SubLangEnglishIndia,\n\t\t\t0x6:  SubLangEnglishIreland,\n\t\t\t0x8:  SubLangEnglishJamaica,\n\t\t\t0x11: SubLangEnglishMalaysia,\n\t\t\t0x5:  SubLangEnglishNewZealand,\n\t\t\t0xd:  SubLangEnglishRepublicOfThePhilippines,\n\t\t\t0x12: SubLangEnglishSingapore,\n\t\t\t0x7:  SubLangEnglishSouthAfrica,\n\t\t\t0xb:  SubLangEnglishTrinidadAndTobago,\n\t\t\t0x13: SubLangEnglishUnitedArabEmirates,\n\t\t\t0x2:  SubLangEnglishUnitedKingdom,\n\t\t\t0x1:  SubLangEnglishUnitedStates,\n\t\t\t0xc:  SubLangEnglishZimbabwe,\n\t\t},\n\t\tLangEstonian: {\n\t\t\t0x1: SubLangEstonianEstonia,\n\t\t},\n\t\tLangFaroese: {\n\t\t\t0x1: SubLangFaroeseFaroeIslands,\n\t\t},\n\t\tLangFilipino: {\n\t\t\t0x1: SubLangFilipinoPhilippines,\n\t\t},\n\t\tLangFinnish: {\n\t\t\t0x1: SubLangFinnishFinland,\n\t\t},\n\t\tLangFrench: {\n\t\t\t0x2: SubLangFrenchBelgium,\n\t\t\t0xb: SubLangFrenchCameroon,\n\t\t\t0x3: SubLangFrenchCanada,\n\t\t\t0x7: SubLangFrenchCaribbean,\n\t\t\t0x9: SubLangFrenchCongoDrc,\n\t\t\t0xc: SubLangFrenchCôteDivoire,\n\t\t\t0x1: SubLangFrenchFrance,\n\t\t\t0xf: SubLangFrenchHaiti,\n\t\t\t0x5: SubLangFrenchLuxembourg,\n\t\t\t0xd: SubLangFrenchMali,\n\t\t\t0xe: SubLangFrenchMorocco,\n\t\t\t0x6: SubLangFrenchPrincipalityOfMonaco,\n\t\t\t0x8: SubLangFrenchReunion,\n\t\t\t0xa: SubLangFrenchSenegal,\n\t\t\t0x4: SubLangFrenchSwitzerland,\n\t\t},\n\t\tLangFrisian: {\n\t\t\t0x1: SubLangFrisianNetherlands,\n\t\t},\n\t\tLangFulah: {\n\t\t\t0x1: SubLangFulahNigeria,\n\t\t\t0x2: SubLangFulahSenegal,\n\t\t},\n\t\tLangFulahLatin: {\n\t\t\t0x1: SubLangFulahLatinNigeria,\n\t\t},\n\t\tLangGalician: {\n\t\t\t0x1: SubLangGalicianSpain,\n\t\t},\n\t\tLangGeorgian: {\n\t\t\t0x1: SubLangGeorgianGeorgia,\n\t\t},\n\t\tLangGerman: {\n\t\t\t0x3: SubLangGermanAustria,\n\t\t\t0x1: SubLangGermanGermany,\n\t\t\t0x5: SubLangGermanLiechtenstein,\n\t\t\t0x4: SubLangGermanLuxembourg,\n\t\t\t0x2: SubLangGermanSwitzerland,\n\t\t},\n\t\tLangGreek: {\n\t\t\t0x1: SubLangGreekGreece,\n\t\t},\n\t\tLangGreenlandic: {\n\t\t\t0x1: SubLangGreenlandicGreenland,\n\t\t},\n\t\tLangGuarani: {\n\t\t\t0x1: SubLangGuaraniParaguay,\n\t\t},\n\t\tLangGujarati: {\n\t\t\t0x1: SubLangGujaratiIndia,\n\t\t},\n\t\tLangHausaLatin: {\n\t\t\t0x1f: SubLangHausaLatin,\n\t\t\t0x1:  SubLangHausaLatinNigeria,\n\t\t},\n\t\tLangHawaiian: {\n\t\t\t0x1: SubLangHawaiianUnitedStates,\n\t\t},\n\t\tLangHebrew: {\n\t\t\t0x1: SubLangHebrewIsrael,\n\t\t},\n\t\tLangHindi: {\n\t\t\t0x1: SubLangHindiIndia,\n\t\t},\n\t\tLangHungarian: {\n\t\t\t0x1: SubLangHungarianHungary,\n\t\t},\n\t\tLangIcelandic: {\n\t\t\t0x1: SubLangIcelandicIceland,\n\t\t},\n\t\tLangIgbo: {\n\t\t\t0x1: SubLangIgboNigeria,\n\t\t},\n\t\tLangIndonesian: {\n\t\t\t0x1: SubLangIndonesianIndonesia,\n\t\t},\n\t\tLangInuktitutLatin: {\n\t\t\t0x1f: SubLangInuktitutLatin,\n\t\t\t0x2:  SubLangInuktitutLatinCanada,\n\t\t\t0x1e: SubLangInuktitutSyllabics,\n\t\t\t0x1:  SubLangInuktitutSyllabicsCanada,\n\t\t},\n\t\tLangIrish: {\n\t\t\t0x2: SubLangIrishIreland,\n\t\t},\n\t\tLangItalian: {\n\t\t\t0x1: SubLangItalianItaly,\n\t\t\t0x2: SubLangItalianSwitzerland,\n\t\t},\n\t\tLangJapanese: {\n\t\t\t0x1: SubLangJapaneseJapan,\n\t\t},\n\t\tLangKannada: {\n\t\t\t0x1: SubLangKannadaIndia,\n\t\t},\n\t\tLangKashmiri: {\n\t\t\t0x1: SubLangKashmiriPersoArabic,\n\t\t\t0x2: SubLangKashmiriDevanagariIndia,\n\t\t},\n\t\tLangKazakh: {\n\t\t\t0x1: SubLangKazakhKazakhstan,\n\t\t},\n\t\tLangKhmer: {\n\t\t\t0x1: SubLangKhmerCambodia,\n\t\t},\n\t\tLangKiche: {\n\t\t\t0x1: SubLangKicheGuatemala,\n\t\t},\n\t\tLangKinyarwanda: {\n\t\t\t0x1: SubLangKinyarwandaRwanda,\n\t\t},\n\t\tLangKiswahili: {\n\t\t\t0x1: SubLangKiswahiliKenya,\n\t\t},\n\t\tLangKonkani: {\n\t\t\t0x1: SubLangKonkaniIndia,\n\t\t},\n\t\tLangKorean: {\n\t\t\t0x1: SubLangKoreanKorea,\n\t\t},\n\t\tLangKyrgyz: {\n\t\t\t0x1: SubLangKyrgyzKyrgyzstan,\n\t\t},\n\t\tLangLao: {\n\t\t\t0x1: SubLangLaoLaoPdr,\n\t\t},\n\t\tLangLatvian: {\n\t\t\t0x1: SubLangLatvianLatvia,\n\t\t},\n\t\tLangLithuanian: {\n\t\t\t0x1: SubLangLithuanianLithuania,\n\t\t},\n\t\tLangLowerSorbian: {\n\t\t\t0x2: SubLangLowerSorbianGermany,\n\t\t},\n\t\tLangLuxembourgish: {\n\t\t\t0x1: SubLangLuxembourgishLuxembourg,\n\t\t},\n\t\tLangMacedonian: {\n\t\t\t0x1: SubLangMacedonianNorthMacedonia,\n\t\t},\n\t\tLangMalay: {\n\t\t\t0x2: SubLangMalayBruneiDarussalam,\n\t\t\t0x1: SubLangMalayMalaysia,\n\t\t},\n\t\tLangMalayalam: {\n\t\t\t0x1: SubLangMalayalamIndia,\n\t\t},\n\t\tLangMaltese: {\n\t\t\t0x1: SubLangMalteseMalta,\n\t\t},\n\t\tLangMaori: {\n\t\t\t0x1: SubLangMaoriNewZealand,\n\t\t},\n\t\tLangMapudungun: {\n\t\t\t0x1: SubLangMapudungunChile,\n\t\t},\n\t\tLangMarathi: {\n\t\t\t0x1: SubLangMarathiIndia,\n\t\t},\n\t\tLangMohawk: {\n\t\t\t0x1: SubLangMohawkCanada,\n\t\t},\n\t\tLangMongolianCyrillic: {\n\t\t\t0x1e: SubLangMongolianCyrillic,\n\t\t\t0x1:  SubLangMongolianCyrillicMongolia,\n\t\t\t0x1f: SubLangMongolianTraditionalMongolian,\n\t\t\t0x2:  SubLangMongolianTraditionalMongolianPeoplesRepublicOfChina,\n\t\t\t0x3:  SubLangMongolianTraditionalMongolianMongolia,\n\t\t},\n\t\tLangNepali: {\n\t\t\t0x2: SubLangNepaliIndia,\n\t\t\t0x1: SubLangNepaliNepal,\n\t\t},\n\t\tLangNorwegianBokmalNo: {},\n\t\tLangNorwegianBokmal: {\n\t\t\t0x1: SubLangNorwegianBokmalNorway,\n\t\t},\n\t\tLangNorwegianNynorsk: {\n\t\t\t0x2: SubLangNorwegianNynorskNorway,\n\t\t},\n\t\tLangOccitan: {\n\t\t\t0x1: SubLangOccitanFrance,\n\t\t},\n\t\tLangOdia: {\n\t\t\t0x1: SubLangOdiaIndia,\n\t\t},\n\t\tLangOromo: {\n\t\t\t0x1: SubLangOromoEthiopia,\n\t\t},\n\t\tLangPashto: {\n\t\t\t0x1: SubLangPashtoAfghanistan,\n\t\t},\n\t\tLangPersian: {\n\t\t\t0x1: SubLangPersianIran,\n\t\t},\n\t\tLangPolish: {\n\t\t\t0x1: SubLangPolishPoland,\n\t\t},\n\t\tLangPortuguese: {\n\t\t\t0x1: SubLangPortugueseBrazil,\n\t\t\t0x2: SubLangPortuguesePortugal,\n\t\t},\n\t\tLangPunjabi: {\n\t\t\t0x1f: SubLangPunjabi,\n\t\t\t0x1:  SubLangPunjabiIndia,\n\t\t\t0x2:  SubLangPunjabiIslamicRepublicOfPakistan,\n\t\t},\n\t\tLangQuechua: {\n\t\t\t0x1: SubLangQuechuaBolivia,\n\t\t\t0x2: SubLangQuechuaEcuador,\n\t\t\t0x3: SubLangQuechuaPeru,\n\t\t},\n\t\tLangRomanian: {\n\t\t\t0x2: SubLangRomanianMoldova,\n\t\t\t0x1: SubLangRomanianRomania,\n\t\t},\n\t\tLangRomansh: {\n\t\t\t0x1: SubLangRomanshSwitzerland,\n\t\t},\n\t\tLangRussian: {\n\t\t\t0x2: SubLangRussianMoldova,\n\t\t\t0x1: SubLangRussianRussia,\n\t\t},\n\t\tLangSakha: {\n\t\t\t0x1: SubLangSakhaRussia,\n\t\t},\n\t\tLangSamiInari: {\n\t\t\t0x9: SubLangSamiInariFinland,\n\t\t},\n\t\tLangSamiLule: {\n\t\t\t0x4: SubLangSamiLuleNorway,\n\t\t\t0x5: SubLangSamiLuleSweden,\n\t\t},\n\t\tLangSamiNorthern: {\n\t\t\t0x3: SubLangSamiNorthernFinland,\n\t\t\t0x1: SubLangSamiNorthernNorway,\n\t\t\t0x2: SubLangSamiNorthernSweden,\n\t\t},\n\t\tLangSamiSkolt: {\n\t\t\t0x8: SubLangSamiSkoltFinland,\n\t\t},\n\t\tLangSamiSouthern: {\n\t\t\t0x6: SubLangSamiSouthernNorway,\n\t\t\t0x7: SubLangSamiSouthernSweden,\n\t\t},\n\t\tLangSanskrit: {\n\t\t\t0x1: SubLangSanskritIndia,\n\t\t},\n\t\tLangScottishGaelic: {\n\t\t\t0x1:  SubLangScottishGaelicUnitedKingdom,\n\t\t\t0x1b: SubLangSerbianCyrillic,\n\t\t\t0x7:  SubLangSerbianCyrillicBosniaAndHerzegovina,\n\t\t\t0xc:  SubLangSerbianCyrillicMontenegro,\n\t\t\t0xa:  SubLangSerbianCyrillicSerbia,\n\t\t\t0x3:  SubLangSerbianCyrillicSerbiaAndMontenegroFormer,\n\t\t\t0x1c: SubLangSerbianLatin,\n\t\t},\n\t\tLangSerbianLatin: {\n\t\t\t0x6: SubLangSerbianLatinBosniaAndHerzegovina,\n\t\t\t0xb: SubLangSerbianLatinMontenegro,\n\t\t\t0x9: SubLangSerbianLatinSerbia,\n\t\t\t0x2: SubLangSerbianLatinSerbiaAndMontenegroFormer,\n\t\t},\n\t\tLangSesothoSaLeboa: {\n\t\t\t0x1: SubLangSesothoSaLeboaSouthAfrica,\n\t\t},\n\t\tLangSetswana: {\n\t\t\t0x2: SubLangSetswanaBotswana,\n\t\t\t0x1: SubLangSetswanaSouthAfrica,\n\t\t},\n\t\tLangSindhi: {\n\t\t\t0x1f: SubLangSindhi,\n\t\t\t0x2:  SubLangSindhiIslamicRepublicOfPakistan,\n\t\t},\n\t\tLangSinhala: {\n\t\t\t0x1: SubLangSinhalaSriLanka,\n\t\t},\n\t\tLangSlovak: {\n\t\t\t0x1: SubLangSlovakSlovakia,\n\t\t},\n\t\tLangSlovenian: {\n\t\t\t0x1: SubLangSlovenianSlovenia,\n\t\t},\n\t\tLangSomali: {\n\t\t\t0x1: SubLangSomaliSomalia,\n\t\t},\n\t\tLangSotho: {\n\t\t\t0x1: SubLangSothoSouthAfrica,\n\t\t},\n\t\tLangSpanish: {\n\t\t\t0xb:  SubLangSpanishArgentina,\n\t\t\t0x8:  SubLangSpanishBolivarianRepublicOfVenezuela,\n\t\t\t0x10: SubLangSpanishBolivia,\n\t\t\t0xd:  SubLangSpanishChile,\n\t\t\t0x9:  SubLangSpanishColombia,\n\t\t\t0x5:  SubLangSpanishCostaRica,\n\t\t\t0x17: SubLangSpanishCuba,\n\t\t\t0x7:  SubLangSpanishDominicanRepublic,\n\t\t\t0xc:  SubLangSpanishEcuador,\n\t\t\t0x11: SubLangSpanishElSalvador,\n\t\t\t0x4:  SubLangSpanishGuatemala,\n\t\t\t0x12: SubLangSpanishHonduras,\n\t\t\t0x16: SubLangSpanishLatinAmerica,\n\t\t\t0x2:  SubLangSpanishMexico,\n\t\t\t0x13: SubLangSpanishNicaragua,\n\t\t\t0x6:  SubLangSpanishPanama,\n\t\t\t0xf:  SubLangSpanishParaguay,\n\t\t\t0xa:  SubLangSpanishPeru,\n\t\t\t0x14: SubLangSpanishPuertoRico,\n\t\t\t0x1:  SubLangSpanishSpain,\n\t\t\t0x3:  SubLangSpanishSpain,\n\t\t\t0x15: SubLangSpanishUnitedStates,\n\t\t\t0xe:  SubLangSpanishUruguay,\n\t\t},\n\t\tLangSwedish: {\n\t\t\t0x2: SubLangSwedishFinland,\n\t\t\t0x1: SubLangSwedishSweden,\n\t\t},\n\t\tLangSyriac: {\n\t\t\t0x1: SubLangSyriacSyria,\n\t\t},\n\t\tLangTajikCyrillic: {\n\t\t\t0x1f: SubLangTajikCyrillic,\n\t\t\t0x1:  SubLangTajikCyrillicTajikistan,\n\t\t},\n\t\tLangTamazightLatin: {\n\t\t\t0x1f: SubLangTamazightLatin,\n\t\t\t0x2:  SubLangTamazightLatinAlgeria,\n\t\t},\n\t\tLangTamil: {\n\t\t\t0x1: SubLangTamilIndia,\n\t\t\t0x2: SubLangTamilSriLanka,\n\t\t},\n\t\tLangTatar: {\n\t\t\t0x1: SubLangTatarRussia,\n\t\t},\n\t\tLangTelugu: {\n\t\t\t0x1: SubLangTeluguIndia,\n\t\t},\n\t\tLangThai: {\n\t\t\t0x1: SubLangThaiThailand,\n\t\t},\n\t\tLangTibetan: {\n\t\t\t0x1: SubLangTibetanPeoplesRepublicOfChina,\n\t\t},\n\t\tLangTigrinya: {\n\t\t\t0x2: SubLangTigrinyaEritrea,\n\t\t\t0x1: SubLangTigrinyaEthiopia,\n\t\t},\n\t\tLangTsonga: {\n\t\t\t0x1: SubLangTsongaSouthAfrica,\n\t\t},\n\t\tLangTurkish: {\n\t\t\t0x1: SubLangTurkishTurkey,\n\t\t},\n\t\tLangTurkmen: {\n\t\t\t0x1: SubLangTurkmenTurkmenistan,\n\t\t},\n\t\tLangUkrainian: {\n\t\t\t0x1: SubLangUkrainianUkraine,\n\t\t},\n\t\tLangUpperSorbian: {\n\t\t\t0x1: SubLangUpperSorbianGermany,\n\t\t},\n\t\tLangUrdu: {\n\t\t\t0x2: SubLangUrduIndia,\n\t\t\t0x1: SubLangUrduIslamicRepublicOfPakistan,\n\t\t},\n\t\tLangUyghur: {\n\t\t\t0x1:  SubLangUyghurPeoplesRepublicOfChina,\n\t\t\t0x1e: SubLangUzbekCyrillic,\n\t\t\t0x2:  SubLangUzbekCyrillicUzbekistan,\n\t\t},\n\t\tLangUzbekLatin: {\n\t\t\t0x1f: SubLangUzbekLatin,\n\t\t\t0x1:  SubLangUzbekLatinUzbekistan,\n\t\t\t0x2:  SubLangValencianSpain,\n\t\t},\n\t\tLangVenda: {\n\t\t\t0x1: SubLangVendaSouthAfrica,\n\t\t},\n\t\tLangVietnamese: {\n\t\t\t0x1: SubLangVietnameseVietnam,\n\t\t},\n\t\tLangWelsh: {\n\t\t\t0x1: SubLangWelshUnitedKingdom,\n\t\t},\n\t\tLangWolof: {\n\t\t\t0x1: SubLangWolofSenegal,\n\t\t},\n\t\tLangXhosa: {\n\t\t\t0x1: SubLangXhosaSouthAfrica,\n\t\t},\n\t\tLangYi: {\n\t\t\t0x1: SubLangYiPeoplesRepublicOfChina,\n\t\t},\n\t\tLangYoruba: {\n\t\t\t0x1: SubLangYorubaNigeria,\n\t\t},\n\t\tLangZulu: {\n\t\t\t0x1: SubLangZuluSouthAfrica,\n\t\t},\n\t}\n\n\tif val, ok := m[lang][subLang]; ok {\n\t\treturn val.String()\n\t}\n\n\treturn \"?\"\n}\n"
  },
  {
    "path": "resource_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype TestRsrcDir struct {\n\tLevel1ImgRsrcDir   ImageResourceDirectory\n\tLevel2Index        int\n\tLevel2ImgRsrcDir   ImageResourceDirectory\n\tLevel3Index        int\n\tLevel3ImgRsrcDir   ImageResourceDirectory\n\tLevel3RsrcDirEntry ResourceDirectoryEntry\n\tLevel4Index        int\n\tLevel4RsrcDirEntry ResourceDirectoryEntry\n}\n\nfunc TestParseResourceDirectory(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  string\n\t\tout TestRsrcDir\n\t}{\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/putty.exe\"),\n\t\t\tTestRsrcDir{\n\t\t\t\tLevel1ImgRsrcDir: ImageResourceDirectory{\n\t\t\t\t\tCharacteristics:      0x0,\n\t\t\t\t\tTimeDateStamp:        0x0,\n\t\t\t\t\tMajorVersion:         0x0,\n\t\t\t\t\tMinorVersion:         0x0,\n\t\t\t\t\tNumberOfNamedEntries: 0x0,\n\t\t\t\t\tNumberOfIDEntries:    0x6,\n\t\t\t\t},\n\t\t\t\tLevel2Index: 0x3,\n\t\t\t\tLevel2ImgRsrcDir: ImageResourceDirectory{\n\t\t\t\t\tCharacteristics:      0x0,\n\t\t\t\t\tTimeDateStamp:        0x0,\n\t\t\t\t\tMajorVersion:         0x0,\n\t\t\t\t\tMinorVersion:         0x0,\n\t\t\t\t\tNumberOfNamedEntries: 0x0,\n\t\t\t\t\tNumberOfIDEntries:    0x1,\n\t\t\t\t},\n\t\t\t\tLevel3Index: 0x0,\n\t\t\t\tLevel3ImgRsrcDir: ImageResourceDirectory{\n\t\t\t\t\tCharacteristics:      0x0,\n\t\t\t\t\tTimeDateStamp:        0x0,\n\t\t\t\t\tMajorVersion:         0x0,\n\t\t\t\t\tMinorVersion:         0x0,\n\t\t\t\t\tNumberOfNamedEntries: 0x0,\n\t\t\t\t\tNumberOfIDEntries:    0x1,\n\t\t\t\t},\n\t\t\t\tLevel4Index: 0x0,\n\t\t\t\tLevel4RsrcDirEntry: ResourceDirectoryEntry{\n\t\t\t\t\tStruct: ImageResourceDirectoryEntry{\n\t\t\t\t\t\tName:         0x409,\n\t\t\t\t\t\tOffsetToData: 0x460,\n\t\t\t\t\t},\n\t\t\t\t\tName:          \"\",\n\t\t\t\t\tID:            0x409,\n\t\t\t\t\tIsResourceDir: false,\n\t\t\t\t\tData: ResourceDataEntry{\n\t\t\t\t\t\tLang:    0x9,\n\t\t\t\t\t\tSubLang: 0x1,\n\t\t\t\t\t\tStruct: ImageResourceDataEntry{\n\t\t\t\t\t\t\tOffsetToData: 0x124838,\n\t\t\t\t\t\t\tSize:         0x324,\n\t\t\t\t\t\t\tCodePage:     0x0,\n\t\t\t\t\t\t\tReserved:     0x0,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\t\t\tif file.Is64 {\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryResource]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t} else {\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryResource]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseResourceDirectory(va, size)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"parseResourceDirectory(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\trsrc := file.Resources\n\t\t\tif rsrc.Struct != tt.out.Level1ImgRsrcDir {\n\t\t\t\tt.Fatalf(\"level 1 resource directory assertion failed, got %v, want %v\",\n\t\t\t\t\trsrc.Struct, tt.out.Level1ImgRsrcDir)\n\t\t\t}\n\n\t\t\trsrcDirLevel2 := rsrc.Entries[tt.out.Level2Index].Directory\n\t\t\tif rsrcDirLevel2.Struct != tt.out.Level2ImgRsrcDir {\n\t\t\t\tt.Fatalf(\"level 2 resource directory assertion failed, got %v, want %v\",\n\t\t\t\t\trsrc.Struct, tt.out.Level2ImgRsrcDir)\n\t\t\t}\n\n\t\t\trsrcDirLevel3 := rsrcDirLevel2.Entries[tt.out.Level3Index].Directory\n\t\t\tif rsrcDirLevel3.Struct != tt.out.Level3ImgRsrcDir {\n\t\t\t\tt.Fatalf(\"level 3 resource directory assertion failed, got %v, want %v\",\n\t\t\t\t\trsrc.Struct, tt.out.Level3ImgRsrcDir)\n\t\t\t}\n\n\t\t\trsrcDirEntry := rsrcDirLevel3.Entries[tt.out.Level4Index]\n\t\t\tif !reflect.DeepEqual(rsrcDirEntry, tt.out.Level4RsrcDirEntry) {\n\t\t\t\tt.Fatalf(\"level 3 resource directory entry assertion failed, got %v, want %v\",\n\t\t\t\t\trsrc.Struct, tt.out.Level3ImgRsrcDir)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestResourceTypeString(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  ResourceType\n\t\tout string\n\t}{\n\t\t{\n\t\t\tRTCursor,\n\t\t\t\"Cursor\",\n\t\t},\n\t\t{\n\t\t\tResourceType(0xff),\n\t\t\t\"?\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.out, func(t *testing.T) {\n\n\t\t\trsrcTypeString := tt.in.String()\n\t\t\tif rsrcTypeString != tt.out {\n\t\t\t\tt.Fatalf(\"resource type string conversion failed, got %v, want %v\",\n\t\t\t\t\trsrcTypeString, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestResourceLangString(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  ResourceLang\n\t\tout string\n\t}{\n\t\t{\n\n\t\t\tLangArabic,\n\t\t\t\"Arabic (ar)\",\n\t\t},\n\t\t{\n\t\t\tResourceLang(0xffff),\n\t\t\t\"?\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.out, func(t *testing.T) {\n\n\t\t\trsrcLangString := tt.in.String()\n\t\t\tif rsrcLangString != tt.out {\n\t\t\t\tt.Fatalf(\"resource language string conversion failed, got %v, want %v\",\n\t\t\t\t\trsrcLangString, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestResourceSubLangString(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  ResourceSubLang\n\t\tout string\n\t}{\n\t\t{\n\n\t\t\tSubLangArabicMorocco,\n\t\t\t\"Arabic Morocco (ar-MA)\",\n\t\t},\n\t\t{\n\t\t\tResourceSubLang(0xffff),\n\t\t\t\"?\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.out, func(t *testing.T) {\n\n\t\t\trsrcSubLangString := tt.in.String()\n\t\t\tif rsrcSubLangString != tt.out {\n\t\t\t\tt.Fatalf(\"resource sub-language string conversion failed, got %v, want %v\",\n\t\t\t\t\trsrcSubLangString, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPrettyResourceLang(t *testing.T) {\n\n\ttype resourceLang struct {\n\t\tlang    ResourceLang\n\t\tsubLang int\n\t}\n\n\ttests := []struct {\n\t\tin  resourceLang\n\t\tout string\n\t}{\n\t\t{\n\t\t\tresourceLang{\n\t\t\t\tlang:    LangEnglish,\n\t\t\t\tsubLang: 0x1,\n\t\t\t},\n\t\t\t\"English United States (en-US)\",\n\t\t},\n\t\t{\n\t\t\tresourceLang{\n\t\t\t\tlang:    ResourceLang(0xff),\n\t\t\t\tsubLang: 0x1,\n\t\t\t},\n\t\t\t\"?\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.out, func(t *testing.T) {\n\n\t\t\tprettyRsrcLang := PrettyResourceLang(tt.in.lang, tt.in.subLang)\n\t\t\tif prettyRsrcLang != tt.out {\n\t\t\t\tt.Fatalf(\"pretty resource language failed, got %v, want %v\",\n\t\t\t\t\tprettyRsrcLang, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "richheader.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"bytes\"\n\t\"crypto/md5\"\n\t\"encoding/binary\"\n\t\"fmt\"\n)\n\nconst (\n\t// DansSignature ('DanS' as dword) is where the rich header struct starts.\n\tDansSignature = 0x536E6144\n\n\t// RichSignature ('0x68636952' as dword) is where the rich header struct ends.\n\tRichSignature = \"Rich\"\n\n\t// AnoDansSigNotFound is reported when rich header signature was found, but\n\tAnoDansSigNotFound = \"Rich Header found, but could not locate DanS \" +\n\t\t\"signature\"\n\n\t// AnoPaddingDwordNotZero is reported when rich header signature leading\n\t// padding DWORDs are not equal to 0.\n\tAnoPaddingDwordNotZero = \"Rich header found: 3 leading padding DWORDs \" +\n\t\t\"not found after DanS signature\"\n)\n\n// CompID represents the `@comp.id` structure.\ntype CompID struct {\n\t// The minor version information for the compiler used when building the product.\n\tMinorCV uint16 `json:\"minor_compiler_version\"`\n\n\t// Provides information about the identity or type of the objects used to\n\t// build the PE32.\n\tProdID uint16 `json:\"product_id\"`\n\n\t// Indicates how often the object identified by the former two fields is\n\t// referenced by this PE32 file.\n\tCount uint32 `json:\"count\"`\n\n\t// The raw @comp.id structure (unmasked).\n\tUnmasked uint32 `json:\"unmasked\"`\n}\n\n// RichHeader is a structure that is written right after the MZ DOS header.\n// It consists of pairs of 4-byte integers. And it is also\n// encrypted using a simple XOR operation using the checksum as the key.\n// The data between the magic values encodes the ‘bill of materials’ that were\n// collected by the linker to produce the binary.\ntype RichHeader struct {\n\tXORKey     uint32   `json:\"xor_key\"`\n\tCompIDs    []CompID `json:\"comp_ids\"`\n\tDansOffset int      `json:\"dans_offset\"`\n\tRaw        []byte   `json:\"raw\"`\n}\n\n// ParseRichHeader parses the rich header struct.\nfunc (pe *File) ParseRichHeader() error {\n\n\trh := RichHeader{}\n\tntHeaderOffset := pe.DOSHeader.AddressOfNewEXEHeader\n\trichSigOffset := bytes.Index(pe.data[:ntHeaderOffset], []byte(RichSignature))\n\n\t// For example, .NET executable files do not use the MSVC linker and these\n\t// executables do not contain a detectable Rich Header.\n\tif richSigOffset < 0 {\n\t\treturn nil\n\t}\n\n\t// The DWORD following the \"Rich\" sequence is the XOR key stored by and\n\t// calculated by the linker. It is actually a checksum of the DOS header with\n\t// the e_lfanew zeroed out, and additionally includes the values of the\n\t// unencrypted \"Rich\" array. Using a checksum with encryption will not only\n\t// obfuscate the values, but it also serves as a rudimentary digital\n\t// signature. If the checksum is calculated from scratch once the values\n\t// have been decrypted, but doesn't match the stored key, it can be assumed\n\t// the structure had been tampered with. For those that go the extra step to\n\t// recalculate the checksum/key, this simple protection mechanism can be bypassed.\n\trh.XORKey = binary.LittleEndian.Uint32(pe.data[richSigOffset+4:])\n\n\t// To decrypt the array, start with the DWORD just prior to the `Rich` sequence\n\t// and XOR it with the key. Continue the loop backwards, 4 bytes at a time,\n\t// until the sequence `DanS` is decrypted.\n\tvar decRichHeader []uint32\n\tdansSigOffset := -1\n\testimatedBeginDans := richSigOffset - 4 - binary.Size(ImageDOSHeader{})\n\tfor it := 0; it < estimatedBeginDans; it += 4 {\n\t\tbuff := binary.LittleEndian.Uint32(pe.data[richSigOffset-4-it:])\n\t\tres := buff ^ rh.XORKey\n\t\tif res == DansSignature {\n\t\t\tdansSigOffset = richSigOffset - it - 4\n\t\t\tbreak\n\t\t}\n\n\t\tdecRichHeader = append(decRichHeader, res)\n\t}\n\n\t// Probe we successfuly found the `DanS` magic.\n\tif dansSigOffset == -1 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoDansSigNotFound)\n\t\treturn nil\n\t}\n\n\t// Anomaly check: dansSigOffset is usually found in offset 0x80.\n\tif dansSigOffset != 0x80 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoDanSMagicOffset)\n\t}\n\n\trh.DansOffset = dansSigOffset\n\trh.Raw = pe.data[dansSigOffset : richSigOffset+8]\n\n\t// Reverse the decrypted rich header\n\tfor i, j := 0, len(decRichHeader)-1; i < j; i, j = i+1, j-1 {\n\t\tdecRichHeader[i], decRichHeader[j] = decRichHeader[j], decRichHeader[i]\n\t}\n\n\t// After the `DanS` signature, there are some zero-padded In practice,\n\t// Microsoft seems to have wanted the entries to begin on a 16-byte\n\t// (paragraph) boundary, so the 3 leading padding DWORDs can be safely\n\t// skipped as not belonging to the data.\n\tif decRichHeader[0] != 0 || decRichHeader[1] != 0 || decRichHeader[2] != 0 {\n\t\tpe.Anomalies = append(pe.Anomalies, AnoPaddingDwordNotZero)\n\t}\n\n\t// The array stores entries that are 8-bytes each, broken into 3 members.\n\t// Each entry represents either a tool that was employed as part of building\n\t// the executable or a statistic.\n\t// The @compid struct should be multiple of 8 (bytes), some malformed pe\n\t// files have incorrect number of entries.\n\tvar lenCompIDs int\n\tif (len(decRichHeader)-3)%2 != 0 {\n\t\tlenCompIDs = len(decRichHeader) - 1\n\t} else {\n\t\tlenCompIDs = len(decRichHeader)\n\t}\n\n\tfor i := 3; i < lenCompIDs; i += 2 {\n\t\tcid := CompID{}\n\t\tcompid := make([]byte, binary.Size(cid))\n\t\tbinary.LittleEndian.PutUint32(compid, decRichHeader[i])\n\t\tbinary.LittleEndian.PutUint32(compid[4:], decRichHeader[i+1])\n\t\tbuf := bytes.NewReader(compid)\n\t\terr := binary.Read(buf, binary.LittleEndian, &cid)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcid.Unmasked = binary.LittleEndian.Uint32(compid)\n\t\trh.CompIDs = append(rh.CompIDs, cid)\n\t}\n\n\tpe.RichHeader = rh\n\tpe.HasRichHdr = true\n\n\tchecksum := pe.RichHeaderChecksum()\n\tif checksum != rh.XORKey {\n\t\tpe.Anomalies = append(pe.Anomalies, \"Invalid rich header checksum\")\n\t}\n\n\treturn nil\n}\n\n// RichHeaderChecksum calculate the Rich Header checksum.\nfunc (pe *File) RichHeaderChecksum() uint32 {\n\n\tchecksum := uint32(pe.RichHeader.DansOffset)\n\n\t// First, calculate the sum of the DOS header bytes each rotated left the\n\t// number of times their position relative to the start of the DOS header e.g.\n\t// second byte is rotated left 2x using rol operation.\n\tfor i := 0; i < pe.RichHeader.DansOffset; i++ {\n\t\t// skip over dos e_lfanew field at offset 0x3C\n\t\tif i >= 0x3C && i < 0x40 {\n\t\t\tcontinue\n\t\t}\n\t\tb := uint32(pe.data[i])\n\t\tchecksum += ((b << (i % 32)) | (b>>(32-(i%32)))&0xff)\n\t\tchecksum &= 0xFFFFFFFF\n\t}\n\n\t// Next, take summation of each Rich header entry by combining its ProductId\n\t// and BuildNumber into a single 32 bit number and rotating by its count.\n\tfor _, compid := range pe.RichHeader.CompIDs {\n\t\tchecksum += (compid.Unmasked<<(compid.Count%32) |\n\t\t\tcompid.Unmasked>>(32-(compid.Count%32)))\n\t\tchecksum &= 0xFFFFFFFF\n\t}\n\n\treturn checksum\n}\n\n// RichHeaderHash calculate the Rich Header hash.\nfunc (pe *File) RichHeaderHash() string {\n\tif !pe.HasRichHdr {\n\t\treturn \"\"\n\t}\n\n\trichIndex := bytes.Index(pe.RichHeader.Raw, []byte(RichSignature))\n\tif richIndex == -1 {\n\t\treturn \"\"\n\t}\n\n\tkey := make([]byte, 4)\n\tbinary.LittleEndian.PutUint32(key, pe.RichHeader.XORKey)\n\n\trawData := pe.RichHeader.Raw[:richIndex]\n\tclearData := make([]byte, len(rawData))\n\tfor idx, val := range rawData {\n\t\tclearData[idx] = val ^ key[idx%len(key)]\n\t}\n\treturn fmt.Sprintf(\"%x\", md5.Sum(clearData))\n}\n\n// ProdIDtoStr maps product ids to MS internal names.\n// list from: https://github.com/kirschju/richheader\nfunc ProdIDtoStr(prodID uint16) string {\n\n\tprodIDtoStrMap := map[uint16]string{\n\t\t0x0000: \"Unknown\",\n\t\t0x0001: \"Import0\",\n\t\t0x0002: \"Linker510\",\n\t\t0x0003: \"Cvtomf510\",\n\t\t0x0004: \"Linker600\",\n\t\t0x0005: \"Cvtomf600\",\n\t\t0x0006: \"Cvtres500\",\n\t\t0x0007: \"Utc11_Basic\",\n\t\t0x0008: \"Utc11_C\",\n\t\t0x0009: \"Utc12_Basic\",\n\t\t0x000a: \"Utc12_C\",\n\t\t0x000b: \"Utc12_CPP\",\n\t\t0x000c: \"AliasObj60\",\n\t\t0x000d: \"VisualBasic60\",\n\t\t0x000e: \"Masm613\",\n\t\t0x000f: \"Masm710\",\n\t\t0x0010: \"Linker511\",\n\t\t0x0011: \"Cvtomf511\",\n\t\t0x0012: \"Masm614\",\n\t\t0x0013: \"Linker512\",\n\t\t0x0014: \"Cvtomf512\",\n\t\t0x0015: \"Utc12_C_Std\",\n\t\t0x0016: \"Utc12_CPP_Std\",\n\t\t0x0017: \"Utc12_C_Book\",\n\t\t0x0018: \"Utc12_CPP_Book\",\n\t\t0x0019: \"Implib700\",\n\t\t0x001a: \"Cvtomf700\",\n\t\t0x001b: \"Utc13_Basic\",\n\t\t0x001c: \"Utc13_C\",\n\t\t0x001d: \"Utc13_CPP\",\n\t\t0x001e: \"Linker610\",\n\t\t0x001f: \"Cvtomf610\",\n\t\t0x0020: \"Linker601\",\n\t\t0x0021: \"Cvtomf601\",\n\t\t0x0022: \"Utc12_1_Basic\",\n\t\t0x0023: \"Utc12_1_C\",\n\t\t0x0024: \"Utc12_1_CPP\",\n\t\t0x0025: \"Linker620\",\n\t\t0x0026: \"Cvtomf620\",\n\t\t0x0027: \"AliasObj70\",\n\t\t0x0028: \"Linker621\",\n\t\t0x0029: \"Cvtomf621\",\n\t\t0x002a: \"Masm615\",\n\t\t0x002b: \"Utc13_LTCG_C\",\n\t\t0x002c: \"Utc13_LTCG_CPP\",\n\t\t0x002d: \"Masm620\",\n\t\t0x002e: \"ILAsm100\",\n\t\t0x002f: \"Utc12_2_Basic\",\n\t\t0x0030: \"Utc12_2_C\",\n\t\t0x0031: \"Utc12_2_CPP\",\n\t\t0x0032: \"Utc12_2_C_Std\",\n\t\t0x0033: \"Utc12_2_CPP_Std\",\n\t\t0x0034: \"Utc12_2_C_Book\",\n\t\t0x0035: \"Utc12_2_CPP_Book\",\n\t\t0x0036: \"Implib622\",\n\t\t0x0037: \"Cvtomf622\",\n\t\t0x0038: \"Cvtres501\",\n\t\t0x0039: \"Utc13_C_Std\",\n\t\t0x003a: \"Utc13_CPP_Std\",\n\t\t0x003b: \"Cvtpgd1300\",\n\t\t0x003c: \"Linker622\",\n\t\t0x003d: \"Linker700\",\n\t\t0x003e: \"Export622\",\n\t\t0x003f: \"Export700\",\n\t\t0x0040: \"Masm700\",\n\t\t0x0041: \"Utc13_POGO_I_C\",\n\t\t0x0042: \"Utc13_POGO_I_CPP\",\n\t\t0x0043: \"Utc13_POGO_O_C\",\n\t\t0x0044: \"Utc13_POGO_O_CPP\",\n\t\t0x0045: \"Cvtres700\",\n\t\t0x0046: \"Cvtres710p\",\n\t\t0x0047: \"Linker710p\",\n\t\t0x0048: \"Cvtomf710p\",\n\t\t0x0049: \"Export710p\",\n\t\t0x004a: \"Implib710p\",\n\t\t0x004b: \"Masm710p\",\n\t\t0x004c: \"Utc1310p_C\",\n\t\t0x004d: \"Utc1310p_CPP\",\n\t\t0x004e: \"Utc1310p_C_Std\",\n\t\t0x004f: \"Utc1310p_CPP_Std\",\n\t\t0x0050: \"Utc1310p_LTCG_C\",\n\t\t0x0051: \"Utc1310p_LTCG_CPP\",\n\t\t0x0052: \"Utc1310p_POGO_I_C\",\n\t\t0x0053: \"Utc1310p_POGO_I_CPP\",\n\t\t0x0054: \"Utc1310p_POGO_O_C\",\n\t\t0x0055: \"Utc1310p_POGO_O_CPP\",\n\t\t0x0056: \"Linker624\",\n\t\t0x0057: \"Cvtomf624\",\n\t\t0x0058: \"Export624\",\n\t\t0x0059: \"Implib624\",\n\t\t0x005a: \"Linker710\",\n\t\t0x005b: \"Cvtomf710\",\n\t\t0x005c: \"Export710\",\n\t\t0x005d: \"Implib710\",\n\t\t0x005e: \"Cvtres710\",\n\t\t0x005f: \"Utc1310_C\",\n\t\t0x0060: \"Utc1310_CPP\",\n\t\t0x0061: \"Utc1310_C_Std\",\n\t\t0x0062: \"Utc1310_CPP_Std\",\n\t\t0x0063: \"Utc1310_LTCG_C\",\n\t\t0x0064: \"Utc1310_LTCG_CPP\",\n\t\t0x0065: \"Utc1310_POGO_I_C\",\n\t\t0x0066: \"Utc1310_POGO_I_CPP\",\n\t\t0x0067: \"Utc1310_POGO_O_C\",\n\t\t0x0068: \"Utc1310_POGO_O_CPP\",\n\t\t0x0069: \"AliasObj710\",\n\t\t0x006a: \"AliasObj710p\",\n\t\t0x006b: \"Cvtpgd1310\",\n\t\t0x006c: \"Cvtpgd1310p\",\n\t\t0x006d: \"Utc1400_C\",\n\t\t0x006e: \"Utc1400_CPP\",\n\t\t0x006f: \"Utc1400_C_Std\",\n\t\t0x0070: \"Utc1400_CPP_Std\",\n\t\t0x0071: \"Utc1400_LTCG_C\",\n\t\t0x0072: \"Utc1400_LTCG_CPP\",\n\t\t0x0073: \"Utc1400_POGO_I_C\",\n\t\t0x0074: \"Utc1400_POGO_I_CPP\",\n\t\t0x0075: \"Utc1400_POGO_O_C\",\n\t\t0x0076: \"Utc1400_POGO_O_CPP\",\n\t\t0x0077: \"Cvtpgd1400\",\n\t\t0x0078: \"Linker800\",\n\t\t0x0079: \"Cvtomf800\",\n\t\t0x007a: \"Export800\",\n\t\t0x007b: \"Implib800\",\n\t\t0x007c: \"Cvtres800\",\n\t\t0x007d: \"Masm800\",\n\t\t0x007e: \"AliasObj800\",\n\t\t0x007f: \"PhoenixPrerelease\",\n\t\t0x0080: \"Utc1400_CVTCIL_C\",\n\t\t0x0081: \"Utc1400_CVTCIL_CPP\",\n\t\t0x0082: \"Utc1400_LTCG_MSIL\",\n\t\t0x0083: \"Utc1500_C\",\n\t\t0x0084: \"Utc1500_CPP\",\n\t\t0x0085: \"Utc1500_C_Std\",\n\t\t0x0086: \"Utc1500_CPP_Std\",\n\t\t0x0087: \"Utc1500_CVTCIL_C\",\n\t\t0x0088: \"Utc1500_CVTCIL_CPP\",\n\t\t0x0089: \"Utc1500_LTCG_C\",\n\t\t0x008a: \"Utc1500_LTCG_CPP\",\n\t\t0x008b: \"Utc1500_LTCG_MSIL\",\n\t\t0x008c: \"Utc1500_POGO_I_C\",\n\t\t0x008d: \"Utc1500_POGO_I_CPP\",\n\t\t0x008e: \"Utc1500_POGO_O_C\",\n\t\t0x008f: \"Utc1500_POGO_O_CPP\",\n\t\t0x0090: \"Cvtpgd1500\",\n\t\t0x0091: \"Linker900\",\n\t\t0x0092: \"Export900\",\n\t\t0x0093: \"Implib900\",\n\t\t0x0094: \"Cvtres900\",\n\t\t0x0095: \"Masm900\",\n\t\t0x0096: \"AliasObj900\",\n\t\t0x0097: \"Resource\",\n\t\t0x0098: \"AliasObj1000\",\n\t\t0x0099: \"Cvtpgd1600\",\n\t\t0x009a: \"Cvtres1000\",\n\t\t0x009b: \"Export1000\",\n\t\t0x009c: \"Implib1000\",\n\t\t0x009d: \"Linker1000\",\n\t\t0x009e: \"Masm1000\",\n\t\t0x009f: \"Phx1600_C\",\n\t\t0x00a0: \"Phx1600_CPP\",\n\t\t0x00a1: \"Phx1600_CVTCIL_C\",\n\t\t0x00a2: \"Phx1600_CVTCIL_CPP\",\n\t\t0x00a3: \"Phx1600_LTCG_C\",\n\t\t0x00a4: \"Phx1600_LTCG_CPP\",\n\t\t0x00a5: \"Phx1600_LTCG_MSIL\",\n\t\t0x00a6: \"Phx1600_POGO_I_C\",\n\t\t0x00a7: \"Phx1600_POGO_I_CPP\",\n\t\t0x00a8: \"Phx1600_POGO_O_C\",\n\t\t0x00a9: \"Phx1600_POGO_O_CPP\",\n\t\t0x00aa: \"Utc1600_C\",\n\t\t0x00ab: \"Utc1600_CPP\",\n\t\t0x00ac: \"Utc1600_CVTCIL_C\",\n\t\t0x00ad: \"Utc1600_CVTCIL_CPP\",\n\t\t0x00ae: \"Utc1600_LTCG_C\",\n\t\t0x00af: \"Utc1600_LTCG_CPP\",\n\t\t0x00b0: \"Utc1600_LTCG_MSIL\",\n\t\t0x00b1: \"Utc1600_POGO_I_C\",\n\t\t0x00b2: \"Utc1600_POGO_I_CPP\",\n\t\t0x00b3: \"Utc1600_POGO_O_C\",\n\t\t0x00b4: \"Utc1600_POGO_O_CPP\",\n\t\t0x00b5: \"AliasObj1010\",\n\t\t0x00b6: \"Cvtpgd1610\",\n\t\t0x00b7: \"Cvtres1010\",\n\t\t0x00b8: \"Export1010\",\n\t\t0x00b9: \"Implib1010\",\n\t\t0x00ba: \"Linker1010\",\n\t\t0x00bb: \"Masm1010\",\n\t\t0x00bc: \"Utc1610_C\",\n\t\t0x00bd: \"Utc1610_CPP\",\n\t\t0x00be: \"Utc1610_CVTCIL_C\",\n\t\t0x00bf: \"Utc1610_CVTCIL_CPP\",\n\t\t0x00c0: \"Utc1610_LTCG_C\",\n\t\t0x00c1: \"Utc1610_LTCG_CPP\",\n\t\t0x00c2: \"Utc1610_LTCG_MSIL\",\n\t\t0x00c3: \"Utc1610_POGO_I_C\",\n\t\t0x00c4: \"Utc1610_POGO_I_CPP\",\n\t\t0x00c5: \"Utc1610_POGO_O_C\",\n\t\t0x00c6: \"Utc1610_POGO_O_CPP\",\n\t\t0x00c7: \"AliasObj1100\",\n\t\t0x00c8: \"Cvtpgd1700\",\n\t\t0x00c9: \"Cvtres1100\",\n\t\t0x00ca: \"Export1100\",\n\t\t0x00cb: \"Implib1100\",\n\t\t0x00cc: \"Linker1100\",\n\t\t0x00cd: \"Masm1100\",\n\t\t0x00ce: \"Utc1700_C\",\n\t\t0x00cf: \"Utc1700_CPP\",\n\t\t0x00d0: \"Utc1700_CVTCIL_C\",\n\t\t0x00d1: \"Utc1700_CVTCIL_CPP\",\n\t\t0x00d2: \"Utc1700_LTCG_C\",\n\t\t0x00d3: \"Utc1700_LTCG_CPP\",\n\t\t0x00d4: \"Utc1700_LTCG_MSIL\",\n\t\t0x00d5: \"Utc1700_POGO_I_C\",\n\t\t0x00d6: \"Utc1700_POGO_I_CPP\",\n\t\t0x00d7: \"Utc1700_POGO_O_C\",\n\t\t0x00d8: \"Utc1700_POGO_O_CPP\",\n\t\t0x00d9: \"AliasObj1200\",\n\t\t0x00da: \"Cvtpgd1800\",\n\t\t0x00db: \"Cvtres1200\",\n\t\t0x00dc: \"Export1200\",\n\t\t0x00dd: \"Implib1200\",\n\t\t0x00de: \"Linker1200\",\n\t\t0x00df: \"Masm1200\",\n\t\t0x00e0: \"Utc1800_C\",\n\t\t0x00e1: \"Utc1800_CPP\",\n\t\t0x00e2: \"Utc1800_CVTCIL_C\",\n\t\t0x00e3: \"Utc1800_CVTCIL_CPP\",\n\t\t0x00e4: \"Utc1800_LTCG_C\",\n\t\t0x00e5: \"Utc1800_LTCG_CPP\",\n\t\t0x00e6: \"Utc1800_LTCG_MSIL\",\n\t\t0x00e7: \"Utc1800_POGO_I_C\",\n\t\t0x00e8: \"Utc1800_POGO_I_CPP\",\n\t\t0x00e9: \"Utc1800_POGO_O_C\",\n\t\t0x00ea: \"Utc1800_POGO_O_CPP\",\n\t\t0x00eb: \"AliasObj1210\",\n\t\t0x00ec: \"Cvtpgd1810\",\n\t\t0x00ed: \"Cvtres1210\",\n\t\t0x00ee: \"Export1210\",\n\t\t0x00ef: \"Implib1210\",\n\t\t0x00f0: \"Linker1210\",\n\t\t0x00f1: \"Masm1210\",\n\t\t0x00f2: \"Utc1810_C\",\n\t\t0x00f3: \"Utc1810_CPP\",\n\t\t0x00f4: \"Utc1810_CVTCIL_C\",\n\t\t0x00f5: \"Utc1810_CVTCIL_CPP\",\n\t\t0x00f6: \"Utc1810_LTCG_C\",\n\t\t0x00f7: \"Utc1810_LTCG_CPP\",\n\t\t0x00f8: \"Utc1810_LTCG_MSIL\",\n\t\t0x00f9: \"Utc1810_POGO_I_C\",\n\t\t0x00fa: \"Utc1810_POGO_I_CPP\",\n\t\t0x00fb: \"Utc1810_POGO_O_C\",\n\t\t0x00fc: \"Utc1810_POGO_O_CPP\",\n\t\t0x00fd: \"AliasObj1400\",\n\t\t0x00fe: \"Cvtpgd1900\",\n\t\t0x00ff: \"Cvtres1400\",\n\t\t0x0100: \"Export1400\",\n\t\t0x0101: \"Implib1400\",\n\t\t0x0102: \"Linker1400\",\n\t\t0x0103: \"Masm1400\",\n\t\t0x0104: \"Utc1900_C\",\n\t\t0x0105: \"Utc1900_CPP\",\n\t\t0x0106: \"Utc1900_CVTCIL_C\",\n\t\t0x0107: \"Utc1900_CVTCIL_CPP\",\n\t\t0x0108: \"Utc1900_LTCG_C\",\n\t\t0x0109: \"Utc1900_LTCG_CPP\",\n\t\t0x010a: \"Utc1900_LTCG_MSIL\",\n\t\t0x010b: \"Utc1900_POGO_I_C\",\n\t\t0x010c: \"Utc1900_POGO_I_CPP\",\n\t\t0x010d: \"Utc1900_POGO_O_C\",\n\t\t0x010e: \"Utc1900_POGO_O_CPP\",\n\t}\n\n\tif val, ok := prodIDtoStrMap[prodID]; ok {\n\t\treturn val\n\t}\n\n\treturn \"?\"\n}\n\n// ProdIDtoVSversion retrieves the Visual Studio version from product id.\n// list from: https://github.com/kirschju/richheader\nfunc ProdIDtoVSversion(prodID uint16) string {\n\tif prodID > 0x010e {\n\t\treturn \"\"\n\t} else if prodID >= 0x00fd && prodID < 0x010e+1 {\n\t\treturn \"Visual Studio 2015 14.00\"\n\t} else if prodID >= 0x00eb && prodID < 0x00fd {\n\t\treturn \"Visual Studio 2013 12.10\"\n\t} else if prodID >= 0x00d9 && prodID < 0x00eb {\n\t\treturn \"Visual Studio 2013 12.00\"\n\t} else if prodID >= 0x00c7 && prodID < 0x00d9 {\n\t\treturn \"Visual Studio 2012 11.00\"\n\t} else if prodID >= 0x00b5 && prodID < 0x00c7 {\n\t\treturn \"Visual Studio 2010 10.10\"\n\t} else if prodID >= 0x0098 && prodID < 0x00b5 {\n\t\treturn \"Visual Studio 2010 10.00\"\n\t} else if prodID >= 0x0083 && prodID < 0x0098 {\n\t\treturn \"Visual Studio 2008 09.00\"\n\t} else if prodID >= 0x006d && prodID < 0x0083 {\n\t\treturn \"Visual Studio 2005 08.00\"\n\t} else if prodID >= 0x005a && prodID < 0x006d {\n\t\treturn \"Visual Studio 2003 07.10\"\n\t} else if prodID == 1 {\n\t\treturn \"Visual Studio\"\n\t} else {\n\t\treturn \"<unknown>\"\n\t}\n}\n"
  },
  {
    "path": "richheader_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype TestRichHeader struct {\n\trichHeader   RichHeader\n\tcompIDIndex  uint8\n\tprettyProdID string\n\tVSVersion    string\n\tchecksum     uint32\n}\n\nfunc TestParseRichHeader(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  string\n\t\tout TestRichHeader\n\t}{\n\t\t{getAbsoluteFilePath(\"test/kernel32.dll\"),\n\t\t\tTestRichHeader{\n\t\t\t\trichHeader: RichHeader{\n\t\t\t\t\tXORKey: 2796214951,\n\t\t\t\t\tCompIDs: []CompID{\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMinorCV:  27412,\n\t\t\t\t\t\t\tProdID:   257,\n\t\t\t\t\t\t\tCount:    4,\n\t\t\t\t\t\t\tUnmasked: 16870164,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMinorCV:  30729,\n\t\t\t\t\t\t\tProdID:   147,\n\t\t\t\t\t\t\tCount:    193,\n\t\t\t\t\t\t\tUnmasked: 9664521,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMinorCV:  0,\n\t\t\t\t\t\t\tProdID:   1,\n\t\t\t\t\t\t\tCount:    1325,\n\t\t\t\t\t\t\tUnmasked: 65536,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMinorCV:  27412,\n\t\t\t\t\t\t\tProdID:   260,\n\t\t\t\t\t\t\tCount:    9,\n\t\t\t\t\t\t\tUnmasked: 17066772,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMinorCV:  27412,\n\t\t\t\t\t\t\tProdID:   259,\n\t\t\t\t\t\t\tCount:    3,\n\t\t\t\t\t\t\tUnmasked: 17001236,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMinorCV:  27412,\n\t\t\t\t\t\t\tProdID:   256,\n\t\t\t\t\t\t\tCount:    1,\n\t\t\t\t\t\t\tUnmasked: 16804628,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMinorCV:  27412,\n\t\t\t\t\t\t\tProdID:   269,\n\t\t\t\t\t\t\tCount:    209,\n\t\t\t\t\t\t\tUnmasked: 17656596,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMinorCV:  27412,\n\t\t\t\t\t\t\tProdID:   255,\n\t\t\t\t\t\t\tCount:    1,\n\t\t\t\t\t\t\tUnmasked: 16739092,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMinorCV:  27412,\n\t\t\t\t\t\t\tProdID:   258,\n\t\t\t\t\t\t\tCount:    1,\n\t\t\t\t\t\t\tUnmasked: 16935700,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\tDansOffset: 128,\n\t\t\t\t\tRaw: []byte{\n\t\t\t\t\t\t0xe3, 0xbb, 0xc4, 0xf5, 0xa7, 0xda, 0xaa, 0xa6, 0xa7, 0xda, 0xaa, 0xa6, 0xa7, 0xda, 0xaa,\n\t\t\t\t\t\t0xa6, 0xb3, 0xb1, 0xab, 0xa7, 0xa3, 0xda, 0xaa, 0xa6, 0xae, 0xa2, 0x39, 0xa6, 0x66, 0xda,\n\t\t\t\t\t\t0xaa, 0xa6, 0xa7, 0xda, 0xab, 0xa6, 0x8a, 0xdf, 0xaa, 0xa6, 0xb3, 0xb1, 0xae, 0xa7, 0xae,\n\t\t\t\t\t\t0xda, 0xaa, 0xa6, 0xb3, 0xb1, 0xa9, 0xa7, 0xa4, 0xda, 0xaa, 0xa6, 0xb3, 0xb1, 0xaa, 0xa7,\n\t\t\t\t\t\t0xa6, 0xda, 0xaa, 0xa6, 0xb3, 0xb1, 0xa7, 0xa7, 0x76, 0xda, 0xaa, 0xa6, 0xb3, 0xb1, 0x55,\n\t\t\t\t\t\t0xa6, 0xa6, 0xda, 0xaa, 0xa6, 0xb3, 0xb1, 0xa8, 0xa7, 0xa6, 0xda, 0xaa, 0xa6, 0x52, 0x69,\n\t\t\t\t\t\t0x63, 0x68, 0xa7, 0xda, 0xaa, 0xa6},\n\t\t\t\t},\n\t\t\t\tcompIDIndex:  3,\n\t\t\t\tprettyProdID: \"Utc1900_C\",\n\t\t\t\tVSVersion:    \"Visual Studio 2015 14.00\",\n\t\t\t\tchecksum:     0xa6aadaa7,\n\t\t\t}},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\trichHeader := file.RichHeader\n\t\t\tif !reflect.DeepEqual(richHeader, tt.out.richHeader) {\n\t\t\t\tt.Errorf(\"rich header test failed, got %v, want %v\",\n\t\t\t\trichHeader, tt.out)\n\t\t\t}\n\n\t\t\tprodID := richHeader.CompIDs[tt.out.compIDIndex].ProdID\n\t\t\tprettyProdID := ProdIDtoStr(prodID)\n\t\t\tif prettyProdID != tt.out.prettyProdID {\n\t\t\t\tt.Errorf(\"rich header pretty prod ID failed, got %v, want %v\",\n\t\t\t\t\tprettyProdID, tt.out.prettyProdID)\n\t\t\t}\n\n\t\t\tVSVersion := ProdIDtoVSversion(prodID)\n\t\t\tif VSVersion != tt.out.VSVersion {\n\t\t\t\tt.Errorf(\"rich header VS verion of prod ID failed, got %v, want %v\",\n\t\t\t\t\tVSVersion, tt.out.VSVersion)\n\t\t\t}\n\n\t\t\tchecksum := file.RichHeaderChecksum()\n\t\t\tif checksum != tt.out.checksum {\n\t\t\t\tt.Errorf(\"rich header checksum failed, got %v, want %v\",\n\t\t\t\t\tchecksum, tt.out.checksum)\n\t\t\t}\n\n\t\t})\n\t}\n}\n\nfunc TestRichHeaderHash(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  string\n\t\tout string\n\t}{\n\t\t{getAbsoluteFilePath(\"test/kernel32.dll\"),\n\t\t\t\"4549320af6790d410f09ddc3bab86c86\"},\n\t\t{getAbsoluteFilePath(\"test/WdBoot.sys\"),\n\t\t\t\"3cbccbf62a2a6a8066a5c9d294c90948\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tgot := file.RichHeaderHash()\n\t\t\tif string(got) != tt.out {\n\t\t\t\tt.Errorf(\"Authentihash(%s) got %v, want %v\", tt.in, got, tt.out)\n\t\t\t}\n\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "scripts/extract-rsrc-lang.py",
    "content": "# Text file containing languages and sub-languages extracted from:\r\n# Language Identifier Constants and Strings for Microsoft Windows doc.\r\nspec = \"ms-lcid.txt\"\r\n\r\nclass Language:\r\n    language = \"\"\r\n    originalLanguage = \"\"\r\n    id = 0\r\n    tag = \"\"\r\n    isSubLang = False\r\n\r\n    def __str__(self) -> str:\r\n        return f\"{self.originalLanguage} : {self.id} : {self.tag}\"\r\n\r\ndef sanitize_lang(language):\r\n    language = language.replace(\".\", \"\") # example: U.A.E.\r\n    language = language.replace(\"(\", \"\") # example: (Latin)\r\n    language = language.replace(\")\", \"\") # example: (Latin)\r\n    language = language.replace(\"'\", \"\") # example: People's Republic of China\r\n    language = language.replace(\"[\", \"\") # example: Cocos [Keeling] Islands\r\n    language = language.replace(\"]\", \"\") # example: Cocos [Keeling] Islands\r\n    language = language.replace(\"-\", \"\") # example: Guinea-Bissau\r\n    language = language.replace(\"/\", \"\") # example: # Pseudo locale for east Asian/complex script localization testing\r\n    language = language.replace(\" \", \"\") # example: Congo, DRC\r\n    language = language.replace(\",\", \"\") # example: Congo, DRC\r\n    return language\r\n\r\ndef read_lang_ids(filename):\r\n    lines = []\r\n    with open(filename, 'r', encoding=\"utf-8\") as f:\r\n        lines = f.readlines()\r\n\r\n    lang_ids = []\r\n    for line in lines:\r\n        elements = line.split()\r\n        lang_ids.append(elements[0])\r\n\r\n    return lang_ids\r\n\r\ndef parse_txt_file(filename, lang_ids):\r\n    lines = []\r\n    with open(filename, 'r', encoding=\"utf-8\") as f:\r\n        lines = f.readlines()\r\n\r\n    languages = []\r\n    for line in lines:\r\n        lang  = Language()\r\n        line = line.strip()\r\n        elements = line.split()\r\n        lang.tag = elements[-1]\r\n        lang.id = elements[-2]\r\n        if \"-\" not in lang.tag:\r\n            lang.isSubLang = False\r\n        else:\r\n            if not lang.id in lang_ids:\r\n                lang.isSubLang = True\r\n        i = 0\r\n\r\n        while i < len(elements) - 2:\r\n            for letter in [\"(\", \"[\"]:\r\n                if elements[i].startswith(letter):\r\n                    # Capitalize words so golang is happy.\r\n                    lang.originalLanguage += letter + elements[i][1:].capitalize() + \" \"\r\n                    break\r\n                else:\r\n                    lang.originalLanguage += elements[i].capitalize() + \" \"\r\n                    break\r\n            i += 1\r\n\r\n        begin = lang.originalLanguage.find(\"-\")\r\n        if begin > 0:\r\n            lang.originalLanguage = lang.originalLanguage[:begin+1] + \\\r\n                lang.originalLanguage[begin+1:begin+3].capitalize() + lang.originalLanguage[begin+3:]\r\n\r\n        # Strip the last whitespace.\r\n        lang.originalLanguage =  lang.originalLanguage[:-1]\r\n        lang.language = sanitize_lang(lang.originalLanguage)\r\n\r\n        # Skip unsupported locals.\r\n        if lang.id == \"0x1000\":\r\n            print (f\"skipping {lang}\")\r\n            continue\r\n\r\n        languages.append(lang)\r\n\r\n    return languages\r\n\r\ndef generate_go_code(languages : list[Language]):\r\n    code = \"\"\r\n\r\n    # Generate langs constants\r\n    for lang in languages:\r\n        if lang.isSubLang:\r\n           continue\r\n        else:\r\n            code += f\"// {lang.originalLanguage} ({lang.tag})\\n\"\r\n            code += f\"Lang{lang.language} ResourceLang = {lang.id}\\n\"\r\n\r\n    # Generate sub-langs constants\r\n    i = 0\r\n    for lang in languages:\r\n        if lang.isSubLang:\r\n            code += f\"// {lang.originalLanguage} ({lang.tag})\\n\"\r\n            code += f\"SubLang{lang.language}\\n\"\r\n            i += 1\r\n    return code\r\n\r\ndef generate_lang_string(languages : list[Language]):\r\n    code = \"\"\r\n    for lang in languages:\r\n        if lang.isSubLang:\r\n            continue\r\n        code += f'Lang{lang.language} :  \"{lang.originalLanguage} ({lang.tag})\",\\n'\r\n    return code\r\n\r\ndef generate_sub_lang_string(languages : list[Language]):\r\n    code = \"\"\r\n    for lang in languages:\r\n        if not lang.isSubLang:\r\n            continue\r\n        code += f'SubLang{lang.language} :  \"{lang.originalLanguage} ({lang.tag})\",\\n'\r\n    return code\r\n\r\ndef generate_lang_sub_lang_map_string(languages : list[Language]):\r\n    code = \"\"\r\n    curly_bracket_is_open = False\r\n    # The following tags don't have a location.\r\n    ignore_list = [\"0x0476\", \"0x05FE\", \"0x0501\", \"0x09FF\", \"0x043D\", \"0x0471\", \"0x045F\", \"0x7C67\"]\r\n    for lang in languages:\r\n        if lang.id in ignore_list:\r\n            continue\r\n        if not lang.isSubLang:\r\n            if curly_bracket_is_open:\r\n                code += f\"}},\\n\"\r\n            code += f\"Lang{lang.language} : {{\\n\"\r\n            curly_bracket_is_open = True\r\n        else:\r\n            id = int(lang.id, 0) >> 10\r\n            code += f' 0x{id:x} : SubLang{lang.language}.String(),\\n'\r\n    return code\r\n\r\ndef write_generated_code(code, filename):\r\n    with open(filename, \"w\", encoding=\"utf-8\") as f:\r\n        f.write(code)\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    lang_ids = read_lang_ids(\"lang_ids.txt\")\r\n    languages = parse_txt_file(spec, lang_ids)\r\n\r\n    code = generate_go_code(languages)\r\n    write_generated_code(code, \"out.txt\")\r\n\r\n    code = generate_lang_string(languages)\r\n    langs = write_generated_code(code, \"langs.txt\")\r\n\r\n    code = generate_sub_lang_string(languages)\r\n    langs = write_generated_code(code, \"sub_langs.txt\")\r\n\r\n    code = generate_lang_sub_lang_map_string(languages)\r\n    langs = write_generated_code(code, \"map.txt\")\r\n\r\n"
  },
  {
    "path": "scripts/ms-lcid.txt",
    "content": "Afar 0x1000 aa\r\nAfar Djibouti 0x1000 aa-DJ\r\nAfar Eritrea 0x1000 aa-ER\r\nAfar Ethiopia 0x1000 aa-ET\r\nAfrikaans 0x0036 af\r\nAfrikaans Namibia 0x1000 af-NA\r\nAfrikaans South Africa 0x0436 af-ZA\r\nAghem 0x1000 agq\r\nAghem Cameroon 0x1000 agq-CM\r\nAkan 0x1000 ak\r\nAkan Ghana 0x1000 ak-GH\r\nAlbanian 0x001C sq\r\nAlbanian Albania 0x041C sq-AL\r\nAlbanian North Macedonia 0x1000 sq-MK\r\nAlsatian 0x0084 gsw\r\nAlsatian France 0x0484 gsw-FR\r\nAlsatian Liechtenstein 0x1000 gsw-LI\r\nAlsatian Switzerland 0x1000 gsw-CH\r\nAmharic 0x005E am\r\nAmharic Ethiopia 0x045E am-ET\r\nArabic 0x0001 ar\r\nArabic Algeria 0x1401 ar-DZ\r\nArabic Bahrain 0x3C01 ar-BH\r\nArabic Chad 0x1000 ar-TD\r\nArabic Comoros 0x1000 ar-KM\r\nArabic Djibouti 0x1000 ar-DJ\r\nArabic Egypt 0x0c01 ar-EG\r\nArabic Eritrea 0x1000 ar-ER\r\nArabic Iraq 0x0801 ar-IQ\r\nArabic Israel 0x1000 ar-IL\r\nArabic Jordan 0x2C01 ar-JO\r\nArabic Kuwait 0x3401 ar-KW\r\nArabic Lebanon 0x3001 ar-LB\r\nArabic Libya 0x1001 ar-LY\r\nArabic Mauritania 0x1000 ar-MR\r\nArabic Morocco 0x1801 ar-MA\r\nArabic Oman 0x2001 ar-OM\r\nArabic Palestinian Authority 0x1000 ar-PS\r\nArabic Qatar 0x4001 ar-QA\r\nArabic Saudi Arabia 0x0401 ar-SA\r\nArabic Somalia 0x1000 ar-SO\r\nArabic South Sudan 0x1000 ar-SS\r\nArabic Sudan 0x1000 ar-SD\r\nArabic Syria 0x2801 ar-SY\r\nArabic Tunisia 0x1C01 ar-TN\r\nArabic U.A.E. 0x3801 ar-AE\r\nArabic World 0x1000 ar-001\r\nArabic Yemen 0x2401 ar-YE\r\nArmenian 0x002B hy\r\nArmenian Armenia 0x042B hy-AM\r\nAssamese 0x004D as\r\nAssamese India 0x044D as-IN\r\nAsturian 0x1000 ast\r\nAsturian Spain 0x1000 ast-ES\r\nAsu 0x1000 asa\r\nAsu Tanzania 0x1000 asa-TZ\r\nAzerbaijani (Cyrillic) 0x742C az-Cyrl\r\nAzerbaijani (Cyrillic) Azerbaijan 0x082C az-Cyrl-AZ\r\nAzerbaijani (Latin) 0x002C az\r\nAzerbaijani (Latin) 0x782C az-Latn\r\nAzerbaijani (Latin) Azerbaijan 0x042C az-Latn-AZ\r\nBafia 0x1000 ksf\r\nBafia Cameroon 0x1000 ksf-CM\r\nBamanankan 0x1000 bm\r\nBamanankan (Latin) Mali 0x1000 bm-Latn-ML\r\nBangla 0x0045 bn\r\nBangla Bangladesh 0x0845 bn-BD\r\nBangla India 0x0445 bn-IN\r\nBasaa 0x1000 bas\r\nBasaa Cameroon 0x1000 bas-CM\r\nBashkir 0x006D ba\r\nBashkir Russia 0x046D ba-RU\r\nBasque 0x002D eu\r\nBasque Spain 0x042D eu-ES\r\nBelarusian 0x0023 be\r\nBelarusian Belarus 0x0423 be-BY\r\nBemba 0x1000 bem\r\nBemba Zambia 0x1000 bem-ZM\r\nBena 0x1000 bez\r\nBena Tanzania 0x1000 bez-TZ\r\nBlin 0x1000 byn\r\nBlin Eritrea 0x1000 byn-ER\r\nBodo 0x1000 brx\r\nBodo India 0x1000 brx-IN\r\nBosnian (Cyrillic) 0x641A bs-Cyrl\r\nBosnian (Cyrillic) Bosnia and Herzegovina 0x201A bs-Cyrl-BA\r\nBosnian (Latin) 0x681A bs-Latn\r\nBosnian (Latin) 0x781A bs\r\nBosnian (Latin) Bosnia and Herzegovina 0x141A bs-Latn-BA\r\nBreton 0x007E br\r\nBreton France 0x047E br-FR\r\nBulgarian 0x0002 bg\r\nBulgarian Bulgaria 0x0402 bg-BG\r\nBurmese 0x0055 my\r\nBurmese Myanmar 0x0455 my-MM\r\nCatalan 0x0003 ca\r\nCatalan Andorra 0x1000 ca-AD\r\nCatalan France 0x1000 ca-FR\r\nCatalan Italy 0x1000 ca-IT\r\nCatalan Spain 0x0403 ca-ES\r\nCebuano 0x1000 ceb\r\nCebuan (Latin) 0x1000 ceb-Latn\r\nCebuan (Latin) Philippines 0x1000 ceb-Latn-PH\r\nCentral Atlas Tamazight (Arabic) Morocco 0x045F tzm-ArabMA\r\nCentral Atlas Tamazight (Latin) Morocco 0x1000 tzm-LatnMA\r\nCentral Kurdish 0x0092 ku\r\nCentral Kurdish 0x7c92 ku-Arab\r\nCentral Kurdish Iraq 0x0492 ku-Arab-IQ\r\nChakma 0x1000 ccp\r\nChakma Chakma 0x1000 ccp-Cakm\r\nChakma Bangladesh 0x1000 ccp-CakmBD\r\nChakma India 0x1000 ccp-CakmIN\r\nChechen Russia 0x1000 ce-RU\r\nCherokee 0x005C chr\r\nCherokee 0x7c5C chr-Cher\r\nCherokee United States 0x045C chr-Cher-US\r\nChiga 0x1000 cgg\r\nChiga Uganda 0x1000 cgg-UG\r\nChinese (Simplified) 0x0004 zh-Hans\r\nChinese (Simplified) 0x7804 zh\r\nChinese (Simplified) People's Republic of China 0x0804 zh-CN\r\nChinese (Simplified) Singapore 0x1004 zh-SG\r\nChinese (Traditional) 0x7C04 zh-Hant\r\nChinese (Traditional) Hong Kong S.A.R. 0x0C04 zh-HK\r\nChinese (Traditional) Macao S.A.R. 0x1404 zh-MO\r\nChinese (Traditional) Taiwan 0x0404 zh-TW\r\nChurch Slavic Russia 0x1000 cu-RU\r\nCongo Swahili 0x1000 swc\r\nCongo Swahili Congo DRC 0x1000 swc-CD\r\nCornish 0x1000 kw\r\nCornish United Kingdom 0x1000 kw-GB\r\nCorsican 0x0083 co\r\nCorsican France 0x0483 co-FR\r\nCroatian 0x001A hr\r\nCroatian Croatia 0x041A hr-HR\r\nCroatian (Latin) Bosnia and Herzegovina 0x101A hr-BA\r\nCzech 0x0005 cs\r\nCzech Czech Republic 0x0405 cs-CZ\r\nDanish 0x0006 da\r\nDanish Denmark 0x0406 da-DK\r\nDanish Greenland 0x1000 da-GL\r\nDari 0x008C prs\r\nDari Afghanistan 0x048C prs-AF\r\nDivehi 0x0065 dv\r\nDivehi Maldives 0x0465 dv-MV\r\nDuala 0x1000 dua\r\nDuala Cameroon 0x1000 dua-CM\r\nDutch 0x0013 nl\r\nDutch Aruba 0x1000 nl-AW\r\nDutch Belgium 0x0813 nl-BE\r\nDutch Bonaire, Sint Eustatius and Saba 0x1000 nl-BQ\r\nDutch Curaçao 0x1000 nl-CW\r\nDutch Netherlands 0x0413 nl-NL\r\nDutch Sint Maarten 0x1000 nl-SX\r\nDutch Suriname 0x1000 nl-SR\r\nDzongkha 0x1000 dz\r\nDzongkha Bhutan 0x0C51 dz-BT\r\nEmbu 0x1000 ebu\r\nEmbu Kenya 0x1000 ebu-KE\r\nEnglish 0x0009 en\r\nEnglish American Samoa 0x1000 en-AS\r\nEnglish Anguilla 0x1000 en-AI\r\nEnglish Antigua and Barbuda 0x1000 en-AG\r\nEnglish Australia 0x0C09 en-AU\r\nEnglish Austria 0x1000 en-AT\r\nEnglish Bahamas 0x1000 en-BS\r\nEnglish Barbados 0x1000 en-BB\r\nEnglish Belgium 0x1000 en-BE\r\nEnglish Belize 0x2809 en-BZ\r\nEnglish Bermuda 0x1000 en-BM\r\nEnglish Botswana 0x1000 en-BW\r\nEnglish British Indian Ocean Territory 0x1000 en-IO\r\nEnglish British Virgin Islands 0x1000 en-VG\r\nEnglish Burundi 0x1000 en-BI\r\nEnglish Cameroon 0x1000 en-CM\r\nEnglish Canada 0x1009 en-CA\r\nEnglish Caribbean 0x2409 en-029\r\nEnglish Cayman Islands 0x1000 en-KY\r\nEnglish Christmas Island 0x1000 en-CX\r\nEnglish Cocos [Keeling] Islands 0x1000 en-CC\r\nEnglish Cook Islands 0x1000 en-CK\r\nEnglish Cyprus 0x1000 en-CY\r\nEnglish Denmark 0x1000 en-DK\r\nEnglish Dominica 0x1000 en-DM\r\nEnglish Eritrea 0x1000 en-ER\r\nEnglish Europe 0x1000 en-150\r\nEnglish Falkland Islands 0x1000 en-FK\r\nEnglish Finland 0x1000 en-FI\r\nEnglish Fiji 0x1000 en-FJ\r\nEnglish Gambia 0x1000 en-GM\r\nEnglish Germany 0x1000 en-DE\r\nEnglish Ghana 0x1000 en-GH\r\nEnglish Gibraltar 0x1000 en-GI\r\nEnglish Grenada 0x1000 en-GD\r\nEnglish Guam 0x1000 en-GU\r\nEnglish Guernsey 0x1000 en-GG\r\nEnglish Guyana 0x1000 en-GY\r\nEnglish Hong Kong 0x3C09 en-HK\r\nEnglish India 0x4009 en-IN\r\nEnglish Ireland 0x1809 en-IE\r\nEnglish Isle of Man 0x1000 en-IM\r\nEnglish Israel 0x1000 en-IL\r\nEnglish Jamaica 0x2009 en-JM\r\nEnglish Jersey 0x1000 en-JE\r\nEnglish Kenya 0x1000 en-KE\r\nEnglish Kiribati 0x1000 en-KI\r\nEnglish Lesotho 0x1000 en-LS\r\nEnglish Liberia 0x1000 en-LR\r\nEnglish Macao SAR 0x1000 en-MO\r\nEnglish Madagascar 0x1000 en-MG\r\nEnglish Malawi 0x1000 en-MW\r\nEnglish Malaysia 0x4409 en-MY\r\nEnglish Malta 0x1000 en-MT\r\nEnglish Marshall Islands 0x1000 en-MH\r\nEnglish Mauritius 0x1000 en-MU\r\nEnglish Micronesia 0x1000 en-FM\r\nEnglish Montserrat 0x1000 en-MS\r\nEnglish Namibia 0x1000 en-NA\r\nEnglish Nauru 0x1000 en-NR\r\nEnglish Netherlands 0x1000 en-NL\r\nEnglish New Zealand 0x1409 en-NZ\r\nEnglish Nigeria 0x1000 en-NG\r\nEnglish Niue 0x1000 en-NU\r\nEnglish Norfolk Island 0x1000 en-NF\r\nEnglish Northern Mariana Islands 0x1000 en-MP\r\nEnglish Pakistan 0x1000 en-PK\r\nEnglish Palau 0x1000 en-PW\r\nEnglish Papua New Guinea 0x1000 en-PG\r\nEnglish Pitcairn Islands 0x1000 en-PN\r\nEnglish Puerto Rico 0x1000 en-PR\r\nEnglish Republic of the Philippines 0x3409 en-PH\r\nEnglish Rwanda 0x1000 en-RW\r\nEnglish Saint Kitts and Nevis 0x1000 en-KN\r\nEnglish Saint Lucia 0x1000 en-LC\r\nEnglish Saint Vincent and the Grenadines 0x1000 en-VC\r\nEnglish Samoa 0x1000 en-WS\r\nEnglish Seychelles 0x1000 en-SC\r\nEnglish Sierra Leone 0x1000 en-SL\r\nEnglish Singapore 0x4809 en-SG\r\nEnglish Sint Maarten 0x1000 en-SX\r\nEnglish Slovenia 0x1000 en-SI\r\nEnglish Solomon Islands 0x1000 en-SB\r\nEnglish South Africa 0x1C09 en-ZA\r\nEnglish South Sudan 0x1000 en-SS\r\nEnglish St Helena, Ascension, Tristan da Cunha 0x1000 en-SH\r\nEnglish Sudan 0x1000 en-SD\r\nEnglish Swaziland 0x1000 en-SZ\r\nEnglish Sweden 0x1000 en-SE\r\nEnglish Switzerland 0x1000 en-CH\r\nEnglish Tanzania 0x1000 en-TZ\r\nEnglish Tokelau 0x1000 en-TK\r\nEnglish Tonga 0x1000 en-TO\r\nEnglish Trinidad and Tobago 0x2c09 en-TT\r\nEnglish Turks and Caicos Islands 0x1000 en-TC\r\nEnglish Tuvalu 0x1000 en-TV\r\nEnglish Uganda 0x1000 en-UG\r\nEnglish United Arab Emirates 0x4C09 en-AE\r\nEnglish United Kingdom 0x0809 en-GB\r\nEnglish United States 0x0409 en-US\r\nEnglish US Minor Outlying Islands 0x1000 en-UM\r\nEnglish US Virgin Islands 0x1000 en-VI\r\nEnglish Vanuatu 0x1000 en-VU\r\nEnglish World 0x1000 en-001\r\nEnglish Zambia 0x1000 en-ZM\r\nEnglish Zimbabwe 0x3009 en-ZW\r\nEsperanto 0x1000 eo\r\nEsperanto World 0x1000 eo-001\r\nEstonian 0x0025 et\r\nEstonian Estonia 0x0425 et-EE\r\nEwe 0x1000 ee\r\nEwe Ghana 0x1000 ee-GH\r\nEwe Togo 0x1000 ee-TG\r\nEwondo 0x1000 ewo\r\nEwondo Cameroon 0x1000 ewo-CM\r\nFaroese 0x0038 fo\r\nFaroese Denmark 0x1000 fo-DK\r\nFaroese Faroe Islands 0x0438 fo-FO\r\nFilipino 0x0064 fil\r\nFilipino Philippines 0x0464 fil-PH\r\nFinnish 0x000B fi\r\nFinnish Finland 0x040B fi-FI\r\nFrench 0x000C fr\r\nFrench Algeria 0x1000 fr-DZ\r\nFrench Belgium 0x080C fr-BE\r\nFrench Benin 0x1000 fr-BJ\r\nFrench Burkina Faso 0x1000 fr-BF\r\nFrench Burundi 0x1000 fr-BI\r\nFrench Cameroon 0x2c0C fr-CM\r\nFrench Canada 0x0c0C fr-CA\r\nFrench Caribbean 0x1C0C fr-029\r\nFrench Central African Republic 0x1000 fr-CF\r\nFrench Chad 0x1000 fr-TD\r\nFrench Comoros 0x1000 fr-KM\r\nFrench Congo 0x1000 fr-CG\r\nFrench Congo, DRC 0x240C fr-CD\r\nFrench Côte d'Ivoire 0x300C fr-CI\r\nFrench Djibouti 0x1000 fr-DJ\r\nFrench Equatorial Guinea 0x1000 fr-GQ\r\nFrench France 0x040C fr-FR\r\nFrench French Guiana 0x1000 fr-GF\r\nFrench French Polynesia 0x1000 fr-PF\r\nFrench Gabon 0x1000 fr-GA\r\nFrench Guadeloupe 0x1000 fr-GP\r\nFrench Guinea 0x1000 fr-GN\r\nFrench Haiti 0x3c0C fr-HT\r\nFrench Luxembourg 0x140C fr-LU\r\nFrench Madagascar 0x1000 fr-MG\r\nFrench Mali 0x340C fr-ML\r\nFrench Martinique 0x1000 fr-MQ\r\nFrench Mauritania 0x1000 fr-MR\r\nFrench Mauritius 0x1000 fr-MU\r\nFrench Mayotte 0x1000 fr-YT\r\nFrench Morocco 0x380C fr-MA\r\nFrench New Caledonia 0x1000 fr-NC\r\nFrench Niger 0x1000 fr-NE\r\nFrench Principality of Monaco 0x180C fr-MC\r\nFrench Reunion 0x200C fr-RE\r\nFrench Rwanda 0x1000 fr-RW\r\nFrench Saint Barthélemy 0x1000 fr-BL\r\nFrench Saint Martin 0x1000 fr-MF\r\nFrench Saint Pierre and Miquelon 0x1000 fr-PM\r\nFrench Senegal 0x280C fr-SN\r\nFrench Seychelles 0x1000 fr-SC\r\nFrench Switzerland 0x100C fr-CH\r\nFrench Syria 0x1000 fr-SY\r\nFrench Togo 0x1000 fr-TG\r\nFrench Tunisia 0x1000 fr-TN\r\nFrench Vanuatu 0x1000 fr-VU\r\nFrench Wallis and Futuna 0x1000 fr-WF\r\nFrisian 0x0062 fy\r\nFrisian Netherlands 0x0462 fy-NL\r\nFriulian 0x1000 fur\r\nFriulian Italy 0x1000 fur-IT\r\nFulah 0x0067 ff\r\nFulah (Latin) 0x7C67 ff-Latn\r\nFulah (Latin) Burkina Faso 0x1000 ff-Latn-BF\r\nFulah Cameroon 0x1000 ff-CM\r\nFulah (Latin) Cameroon 0x1000 ff-Latn-CM\r\nFulah (Latin) Gambia 0x1000 ff-Latn-GM\r\nFulah (Latin) Ghana 0x1000 ff-Latn-GH\r\nFulah Guinea 0x1000 ff-GN\r\nFulah (Latin) Guinea 0x1000 ff-Latn-GN\r\nFulah (Latin) Guinea-Bissau 0x1000 ff-Latn-GW\r\nFulah (Latin) Liberia 0x1000 ff-Latn-LR\r\nFulah Mauritania 0x1000 ff-MR\r\nFulah (Latin) Mauritania 0x1000 ff-Latn-MR\r\nFulah (Latin) Niger 0x1000 ff-Latn-NE\r\nFulah Nigeria 0x0467 ff-NG\r\nFulah (Latin) Nigeria 0x0467 ff-Latn-NG\r\nFulah Senegal 0x0867 ff-Latn-SN\r\nFulah (Latin) Sierra Leone 0x1000 ff-Latn-SL\r\nGalician 0x0056 gl\r\nGalician Spain 0x0456 gl-ES\r\nGanda 0x1000 lg\r\nGanda Uganda 0x1000 lg-UG\r\nGeorgian 0x0037 ka\r\nGeorgian Georgia 0x0437 ka-GE\r\nGerman 0x0007 de\r\nGerman Austria 0x0C07 de-AT\r\nGerman Belgium 0x1000 de-BE\r\nGerman Germany 0x0407 de-DE\r\nGerman Italy 0x1000 de-IT\r\nGerman Liechtenstein 0x1407 de-LI\r\nGerman Luxembourg 0x1007 de-LU\r\nGerman Switzerland 0x0807 de-CH\r\nGreek 0x0008 el\r\nGreek Cyprus 0x1000 el-CY\r\nGreek Greece 0x0408 el-GR\r\nGreenlandic 0x006F kl\r\nGreenlandic Greenland 0x046F kl-GL\r\nGuarani 0x0074 gn\r\nGuarani Paraguay 0x0474 gn-PY\r\nGujarati 0x0047 gu\r\nGujarati India 0x0447 gu-IN\r\nGusii 0x1000 guz\r\nGusii Kenya 0x1000 guz-KE\r\nHausa (Latin) 0x0068 ha\r\nHausa (Latin) 0x7C68 ha-Latn\r\nHausa (Latin) Ghana 0x1000 ha-Latn-GH\r\nHausa (Latin) Niger 0x1000 ha-Latn-NE\r\nHausa (Latin) Nigeria 0x0468 ha-Latn-NG\r\nHawaiian 0x0075 haw\r\nHawaiian United States 0x0475 haw-US\r\nHebrew 0x000D he\r\nHebrew Israel 0x040D he-IL\r\nHindi 0x0039 hi\r\nHindi India 0x0439 hi-IN\r\nHungarian 0x000E hu\r\nHungarian Hungary 0x040E hu-HU\r\nIcelandic 0x000F is\r\nIcelandic Iceland 0x040F is-IS\r\nIgbo 0x0070 ig\r\nIgbo Nigeria 0x0470 ig-NG\r\nIndonesian 0x0021 id\r\nIndonesian Indonesia 0x0421 id-ID\r\nInterlingua 0x1000 ia\r\nInterlingua France 0x1000 ia-FR\r\nInterlingua World 0x1000 ia-001\r\nInuktitut (Latin) 0x005D iu\r\nInuktitut (Latin) 0x7C5D iu-Latn\r\nInuktitut (Latin) Canada 0x085D iu-Latn-CA\r\nInuktitut (Syllabics) 0x785D iu-Cans\r\nInuktitut (Syllabics) Canada 0x045d iu-Cans-CA\r\nIrish 0x003C ga\r\nIrish Ireland 0x083C ga-IE\r\nItalian 0x0010 it\r\nItalian Italy 0x0410 it-IT\r\nItalian San Marino 0x1000 it-SM\r\nItalian Switzerland 0x0810 it-CH\r\nItalian Vatican City 0x1000 it-VA\r\nJapanese 0x0011 ja\r\nJapanese Japan 0x0411 ja-JP\r\nJavanese 0x1000 jv\r\nJavanese Latin 0x1000 jv-Latn\r\nJavanese Latin, Indonesia 0x1000 jv-Latn-ID\r\nJola-Fonyi 0x1000 dyo\r\nJola-Fonyi Senegal 0x1000 dyo-SN\r\nKabuverdianu 0x1000 kea\r\nKabuverdianu Cabo Verde 0x1000 kea-CV\r\nKabyle 0x1000 kab\r\nKabyle Algeria 0x1000 kab-DZ\r\nKako 0x1000 kkj\r\nKako Cameroon 0x1000 kkj-CM\r\nKalenjin 0x1000 kln\r\nKalenjin Kenya 0x1000 kln-KE\r\nKamba 0x1000 kam\r\nKamba Kenya 0x1000 kam-KE\r\nKannada 0x004B kn\r\nKannada India 0x044B kn-IN\r\nKanuri (Latin) Nigeria 0x0471 kr-Latn-NG\r\nKashmiri 0x0060 ks\r\nKashmiri Perso-Arabic 0x0460 ks-Arab\r\nKashmiri Perso-Arabic 0x1000 ks-Arab-IN\r\nKashmiri (Devanagari) India 0x0860 ks-Deva-IN\r\nKazakh 0x003F kk\r\nKazakh Kazakhstan 0x043F kk-KZ\r\nKhmer 0x0053 km\r\nKhmer Cambodia 0x0453 km-KH\r\nK'iche 0x0086 quc\r\nK'iche Guatemala 0x0486 quc-Latn-GT\r\nKikuyu 0x1000 ki\r\nKikuyu Kenya 0x1000 ki-KE\r\nKinyarwanda 0x0087 rw\r\nKinyarwanda Rwanda 0x0487 rw-RW\r\nKiswahili 0x0041 sw\r\nKiswahili Kenya 0x0441 sw-KE\r\nKiswahili Tanzania 0x1000 sw-TZ\r\nKiswahili Uganda 0x1000 sw-UG\r\nKonkani 0x0057 kok\r\nKonkani India 0x0457 kok-IN\r\nKorean 0x0012 ko\r\nKorean Korea 0x0412 ko-KR\r\nKorean North Korea 0x1000 ko-KP\r\nKoyra Chiini 0x1000 khq\r\nKoyra Chiini Mali 0x1000 khq-ML\r\nKoyraboro Senni 0x1000 ses\r\nKoyraboro Senni Mali 0x1000 ses-ML\r\nKwasio 0x1000 nmg\r\nKwasio Cameroon 0x1000 nmg-CM\r\nKyrgyz 0x0040 ky\r\nKyrgyz Kyrgyzstan 0x0440 ky-KG\r\nKurdish Perso-Arabic, Iran 0x1000 ku-Arab-IR\r\nLakota 0x1000 lkt\r\nLakota United States 0x1000 lkt-US\r\nLangi 0x1000 lag\r\nLangi Tanzania 0x1000 lag-TZ\r\nLao 0x0054 lo\r\nLao Lao P.D.R. 0x0454 lo-LA\r\nLatin Vatican City 0x0476 la-VA\r\nLatvian 0x0026 lv\r\nLatvian Latvia 0x0426 lv-LV\r\nLingala 0x1000 ln\r\nLingala Angola 0x1000 ln-AO\r\nLingala Central African Republic 0x1000 ln-CF\r\nLingala Congo 0x1000 ln-CG\r\nLingala Congo DRC 0x1000 ln-CD\r\nLithuanian 0x0027 lt\r\nLithuanian Lithuania 0x0427 lt-LT\r\nLow German 0x1000 nds\r\nLow German Germany 0x1000 nds-DE\r\nLow German Netherlands 0x1000 nds-NL\r\nLower Sorbian 0x7C2E dsb\r\nLower Sorbian Germany 0x082E dsb-DE\r\nLuba-Katanga 0x1000 lu\r\nLuba-Katanga Congo DRC 0x1000 lu-CD\r\nLuo 0x1000 luo\r\nLuo Kenya 0x1000 luo-KE\r\nLuxembourgish 0x006E lb\r\nLuxembourgish Luxembourg 0x046E lb-LU\r\nLuyia 0x1000 luy\r\nLuyia Kenya 0x1000 luy-KE\r\nMacedonian 0x002F mk\r\nMacedonian North Macedonia 0x042F mk-MK\r\nMachame 0x1000 jmc\r\nMachame Tanzania 0x1000 jmc-TZ\r\nMakhuwa-Meetto 0x1000 mgh\r\nMakhuwa-Meetto Mozambique 0x1000 mgh-MZ\r\nMakonde 0x1000 kde\r\nMakonde Tanzania 0x1000 kde-TZ\r\nMalagasy 0x1000 mg\r\nMalagasy Madagascar 0x1000 mg-MG\r\nMalay 0x003E ms\r\nMalay Brunei Darussalam 0x083E ms-BN\r\nMalay Malaysia 0x043E ms-MY\r\nMalayalam 0x004C ml\r\nMalayalam India 0x044C ml-IN\r\nMaltese 0x003A mt\r\nMaltese Malta 0x043A mt-MT\r\nManx 0x1000 gv\r\nManx Isle of Man 0x1000 gv-IM\r\nMaori 0x0081 mi\r\nMaori New Zealand 0x0481 mi-NZ\r\nMapudungun 0x007A arn\r\nMapudungun Chile 0x047A arn-CL\r\nMarathi 0x004E mr\r\nMarathi India 0x044E mr-IN\r\nMasai 0x1000 mas\r\nMasai Kenya 0x1000 mas-KE\r\nMasai Tanzania 0x1000 mas-TZ\r\nMazanderani Iran 0x1000 mzn-IR\r\nMeru 0x1000 mer\r\nMeru Kenya 0x1000 mer-KE\r\nMeta' 0x1000 mgo\r\nMeta' Cameroon 0x1000 mgo-CM\r\nMohawk 0x007C moh\r\nMohawk Canada 0x047C moh-CA\r\nMongolian (Cyrillic) 0x0050 mn\r\nMongolian (Cyrillic) 0x7850 mn-Cyrl\r\nMongolian (Cyrillic) Mongolia 0x0450 mn-MN\r\nMongolian (Traditional Mongolian) 0x7C50 mn-Mong\r\nMongolian (Traditional Mongolian) People's Republic of China 0x0850 mn-MongCN\r\nMongolian (Traditional Mongolian) Mongolia 0x0C50 mn-MongMN\r\nMorisyen 0x1000 mfe\r\nMorisyen Mauritius 0x1000 mfe-MU\r\nMundang 0x1000 mua\r\nMundang Cameroon 0x1000 mua-CM\r\nN'ko 0x1000 nqo\r\nN'ko Guinea 0x1000 nqo-GN\r\nNama 0x1000 naq\r\nNama Namibia 0x1000 naq-NA\r\nNepali 0x0061 ne\r\nNepali India 0x0861 ne-IN\r\nNepali Nepal 0x0461 ne-NP\r\nNgiemboon 0x1000 nnh\r\nNgiemboon Cameroon 0x1000 nnh-CM\r\nNgomba 0x1000 jgo\r\nNgomba Cameroon 0x1000 jgo-CM\r\nNorthern Luri Iraq 0x1000 lrc-IQ\r\nNorthern Luri Iran 0x1000 lrc-IR\r\nNorth Ndebele 0x1000 nd\r\nNorth Ndebele Zimbabwe 0x1000 nd-ZW\r\nNorwegian (Bokmal) 0x0014 no\r\nNorwegian (Bokmal) 0x7C14 nb\r\nNorwegian (Bokmal) Norway 0x0414 nb-NO\r\nNorwegian (Nynorsk) 0x7814 nn\r\nNorwegian (Nynorsk) Norway 0x0814 nn-NO\r\nNorwegian Bokmål Svalbard and Jan Mayen 0x1000 nb-SJ\r\nNuer 0x1000 nus\r\nNuer Sudan 0x1000 nus-SD\r\nNuer South Sudan 0x1000 nus-SS\r\nNyankole 0x1000 nyn\r\nNyankole Uganda 0x1000 nyn-UG\r\nOccitan 0x0082 oc\r\nOccitan France 0x0482 oc-FR\r\nOdia 0x0048 or\r\nOdia India 0x0448 or-IN\r\nOromo 0x0072 om\r\nOromo Ethiopia 0x0472 om-ET\r\nOromo Kenya 0x1000 om-KE\r\nOssetian 0x1000 os\r\nOssetian Cyrillic, Georgia 0x1000 os-GE\r\nOssetian Cyrillic, Russia 0x1000 os-RU\r\nPashto 0x0063 ps\r\nPashto Afghanistan 0x0463 ps-AF\r\nPashto Pakistan 0x1000 ps-PK\r\nPersian 0x0029 fa\r\nPersian Afghanistan 0x1000 fa-AF\r\nPersian Iran 0x0429 fa-IR\r\nPolish 0x0015 pl\r\nPolish Poland 0x0415 pl-PL\r\nPortuguese 0x0016 pt\r\nPortuguese Angola 0x1000 pt-AO\r\nPortuguese Brazil 0x0416 pt-BR\r\nPortuguese Cabo Verde 0x1000 pt-CV\r\nPortuguese Equatorial Guinea 0x1000 pt-GQ\r\nPortuguese Guinea-Bissau 0x1000 pt-GW\r\nPortuguese Luxembourg 0x1000 pt-LU\r\nPortuguese Macao SAR 0x1000 pt-MO\r\nPortuguese Mozambique 0x1000 pt-MZ\r\nPortuguese Portugal 0x0816 pt-PT\r\nPortuguese São Tomé and Príncipe 0x1000 pt-ST\r\nPortuguese Switzerland 0x1000 pt-CH\r\nPortuguese Timor-Leste 0x1000 pt-TL\r\nPrussian 0x1000 prg-001\r\nPseudo Language Pseudo locale for east Asian/complex script localization testing 0x05FE qps-ploca\r\nPseudo Language Pseudo locale used for localization testing 0x0501 qps-ploc\r\nPseudo Language Pseudo locale used for localization testing of mirrored locales 0x09FF qps-plocm\r\nPunjabi 0x0046 pa\r\nPunjabi 0x7C46 pa-Arab\r\nPunjabi India 0x0446 pa-IN\r\nPunjabi Islamic Republic of Pakistan 0x0846 pa-Arab-PK\r\nQuechua 0x006B quz\r\nQuechua Bolivia 0x046B quz-BO\r\nQuechua Ecuador 0x086B quz-EC\r\nQuechua Peru 0x0C6B quz-PE\r\nRipuarian 0x1000 ksh\r\nRipuarian Germany 0x1000 ksh-DE\r\nRomanian 0x0018 ro\r\nRomanian Moldova 0x0818 ro-MD\r\nRomanian Romania 0x0418 ro-RO\r\nRomansh 0x0017 rm\r\nRomansh Switzerland 0x0417 rm-CH\r\nRombo 0x1000 rof\r\nRombo Tanzania 0x1000 rof-TZ\r\nRundi 0x1000 rn\r\nRundi Burundi 0x1000 rn-BI\r\nRussian 0x0019 ru\r\nRussian Belarus 0x1000 ru-BY\r\nRussian Kazakhstan 0x1000 ru-KZ\r\nRussian Kyrgyzstan 0x1000 ru-KG\r\nRussian Moldova 0x0819 ru-MD\r\nRussian Russia 0x0419 ru-RU\r\nRussian Ukraine 0x1000 ru-UA\r\nRwa 0x1000 rwk\r\nRwa Tanzania 0x1000 rwk-TZ\r\nSaho 0x1000 ssy\r\nSaho Eritrea 0x1000 ssy-ER\r\nSakha 0x0085 sah\r\nSakha Russia 0x0485 sah-RU\r\nSamburu 0x1000 saq\r\nSamburu Kenya 0x1000 saq-KE\r\nSami (Inari) 0x703B smn\r\nSami (Inari) Finland 0x243B smn-FI\r\nSami (Lule) 0x7C3B smj\r\nSami (Lule) Norway 0x103B smj-NO\r\nSami (Lule) Sweden 0x143B smj-SE\r\nSami (Northern) 0x003B se\r\nSami (Northern) Finland 0x0C3B se-FI\r\nSami (Northern) Norway 0x043B se-NO\r\nSami (Northern) Sweden 0x083B se-SE\r\nSami (Skolt) 0x743B sms\r\nSami (Skolt) Finland 0x203B sms-FI\r\nSami (Southern) 0x783B sma\r\nSami (Southern) Norway 0x183B sma-NO\r\nSami (Southern) Sweden 0x1C3B sma-SE\r\nSango 0x1000 sg\r\nSango Central African Republic 0x1000 sg-CF\r\nSangu 0x1000 sbp\r\nSangu Tanzania 0x1000 sbp-TZ\r\nSanskrit 0x004F sa\r\nSanskrit India 0x044F sa-IN\r\nScottish Gaelic 0x0091 gd\r\nScottish Gaelic United Kingdom 0x0491 gd-GB\r\nSena 0x1000 seh\r\nSena Mozambique 0x1000 seh-MZ\r\nSerbian (Cyrillic) 0x6C1A sr-Cyrl\r\nSerbian (Cyrillic) Bosnia and Herzegovina 0x1C1A sr-Cyrl-BA\r\nSerbian (Cyrillic) Montenegro 0x301A sr-Cyrl-ME\r\nSerbian (Cyrillic) Serbia 0x281A sr-Cyrl-RS\r\nSerbian (Cyrillic) Serbia and Montenegro (Former) 0x0C1A sr-Cyrl-CS\r\nSerbian (Latin) 0x701A sr-Latn\r\nSerbian (Latin) 0x7C1A sr\r\nSerbian (Latin) Bosnia and Herzegovina 0x181A sr-Latn-BA\r\nSerbian (Latin) Montenegro 0x2c1A sr-Latn-ME\r\nSerbian (Latin) Serbia 0x241A sr-Latn-RS\r\nSerbian (Latin) Serbia and Montenegro (Former) 0x081A sr-Latn-CS\r\nSesotho sa Leboa 0x006C nso\r\nSesotho sa Leboa South Africa 0x046C nso-ZA\r\nSetswana 0x0032 tn\r\nSetswana Botswana 0x0832 tn-BW\r\nSetswana South Africa 0x0432 tn-ZA\r\nShambala 0x1000 ksb\r\nShambala Tanzania 0x1000 ksb-TZ\r\nShona 0x1000 sn\r\nShona Latin 0x1000 sn-Latn\r\nShona Zimbabwe 0x1000 sn-Latn-ZW\r\nSindhi 0x0059 sd\r\nSindhi 0x7C59 sd-Arab\r\nSindhi Islamic Republic of Pakistan 0x0859 sd-Arab-PK\r\nSinhala 0x005B si\r\nSinhala Sri Lanka 0x045B si-LK\r\nSlovak 0x001B sk\r\nSlovak Slovakia 0x041B sk-SK\r\nSlovenian 0x0024 sl\r\nSlovenian Slovenia 0x0424 sl-SI\r\nSoga 0x1000 xog\r\nSoga Uganda 0x1000 xog-UG\r\nSomali 0x0077 so\r\nSomali Djibouti 0x1000 so-DJ\r\nSomali Ethiopia 0x1000 so-ET\r\nSomali Kenya 0x1000 so-KE\r\nSomali Somalia 0x0477 so-SO\r\nSotho 0x0030 st\r\nSotho South Africa 0x0430 st-ZA\r\nSouth Ndebele 0x1000 nr\r\nSouth Ndebele South Africa 0x1000 nr-ZA\r\nSouthern Sotho Lesotho 0x1000 st-LS\r\nSpanish 0x000A es\r\nSpanish Argentina 0x2C0A es-AR\r\nSpanish Belize 0x1000 es-BZ\r\nSpanish Bolivarian Republic of Venezuela 0x200A es-VE\r\nSpanish Bolivia 0x400A es-BO\r\nSpanish Brazil 0x1000 es-BR\r\nSpanish Chile 0x340A es-CL\r\nSpanish Colombia 0x240A es-CO\r\nSpanish Costa Rica 0x140A es-CR\r\nSpanish Cuba 0x5c0A es-CU\r\nSpanish Dominican Republic 0x1c0A es-DO\r\nSpanish Ecuador 0x300A es-EC\r\nSpanish El Salvador 0x440A es-SV\r\nSpanish Equatorial Guinea 0x1000 es-GQ\r\nSpanish Guatemala 0x100A es-GT\r\nSpanish Honduras 0x480A es-HN\r\nSpanish Latin America 0x580A es-419\r\nSpanish Mexico 0x080A es-MX\r\nSpanish Nicaragua 0x4C0A es-NI\r\nSpanish Panama 0x180A es-PA\r\nSpanish Paraguay 0x3C0A es-PY\r\nSpanish Peru 0x280A es-PE\r\nSpanish Philippines 0x1000 es-PH\r\nSpanish Puerto Rico 0x500A es-PR\r\nSpanish Spain 0x040A es-ES_tradnl\r\nSpanish Spain 0x0c0A es-ES\r\nSpanish United States 0x540A es-US\r\nSpanish Uruguay 0x380A es-UY\r\nStandard Moroccan Tamazight 0x1000 zgh\r\nStandard Moroccan Tamazight Morocco 0x1000 zgh-Tfng-MA\r\nStandard Moroccan Tamazight Tifinagh 0x1000 zgh-Tfng\r\nSwati 0x1000 ss\r\nSwati South Africa 0x1000 ss-ZA\r\nSwati Swaziland 0x1000 ss-SZ\r\nSwedish 0x001D sv\r\nSwedish Åland Islands 0x1000 sv-AX\r\nSwedish Finland 0x081D sv-FI\r\nSwedish Sweden 0x041D sv-SE\r\nSyriac 0x005A syr\r\nSyriac Syria 0x045A syr-SY\r\nTachelhit 0x1000 shi\r\nTachelhit Tifinagh 0x1000 shi-Tfng\r\nTachelhit Tifinagh, Morocco 0x1000 shi-Tfng-MA\r\nTachelhit (Latin) 0x1000 shi-Latn\r\nTachelhit (Latin) Morocco 0x1000 shi-Latn-MA\r\nTaita 0x1000 dav\r\nTaita Kenya 0x1000 dav-KE\r\nTajik (Cyrillic) 0x0028 tg\r\nTajik (Cyrillic) 0x7C28 tg-Cyrl\r\nTajik (Cyrillic) Tajikistan 0x0428 tg-Cyrl-TJ\r\nTamazight (Latin) 0x005F tzm\r\nTamazight (Latin) 0x7C5F tzm-Latn\r\nTamazight (Latin) Algeria 0x085F tzm-Latn-DZ\r\nTamil 0x0049 ta\r\nTamil India 0x0449 ta-IN\r\nTamil Malaysia 0x1000 ta-MY\r\nTamil Singapore 0x1000 ta-SG\r\nTamil Sri Lanka 0x0849 ta-LK\r\nTasawaq 0x1000 twq\r\nTasawaq Niger 0x1000 twq-NE\r\nTatar 0x0044 tt\r\nTatar Russia 0x0444 tt-RU\r\nTelugu 0x004A te\r\nTelugu India 0x044A te-IN\r\nTeso 0x1000 teo\r\nTeso Kenya 0x1000 teo-KE\r\nTeso Uganda 0x1000 teo-UG\r\nThai 0x001E th\r\nThai Thailand 0x041E th-TH\r\nTibetan 0x0051 bo\r\nTibetan India 0x1000 bo-IN\r\nTibetan People's Republic of China 0x0451 bo-CN\r\nTigre 0x1000 tig\r\nTigre Eritrea 0x1000 tig-ER\r\nTigrinya 0x0073 ti\r\nTigrinya Eritrea 0x0873 ti-ER\r\nTigrinya Ethiopia 0x0473 ti-ET\r\nTongan 0x1000 to\r\nTongan Tonga 0x1000 to-TO\r\nTsonga 0x0031 ts\r\nTsonga South Africa 0x0431 ts-ZA\r\nTurkish 0x001F tr\r\nTurkish Cyprus 0x1000 tr-CY\r\nTurkish Turkey 0x041F tr-TR\r\nTurkmen 0x0042 tk\r\nTurkmen Turkmenistan 0x0442 tk-TM\r\nUkrainian 0x0022 uk\r\nUkrainian Ukraine 0x0422 uk-UA\r\nUpper Sorbian 0x002E hsb\r\nUpper Sorbian Germany 0x042E hsb-DE\r\nUrdu 0x0020 ur\r\nUrdu India 0x0820 ur-IN\r\nUrdu Islamic Republic of Pakistan 0x0420 ur-PK\r\nUyghur 0x0080 ug\r\nUyghur People's Republic of China 0x0480 ug-CN\r\nUzbek Perso-Arabic 0x1000 uz-Arab\r\nUzbek Perso-Arabic, Afghanistan 0x1000 uz-Arab-AF\r\nUzbek (Cyrillic) 0x7843 uz-Cyrl\r\nUzbek (Cyrillic) Uzbekistan 0x0843 uz-Cyrl-UZ\r\nUzbek (Latin) 0x0043 uz\r\nUzbek (Latin) 0x7C43 uz-Latn\r\nUzbek (Latin) Uzbekistan 0x0443 uz-Latn-UZ\r\nVai 0x1000 vai\r\nVai 0x1000 vai-Vaii\r\nVai Liberia 0x1000 vai-Vaii-LR\r\nVai (Latin) Liberia 0x1000 vai-Latn-LR\r\nVai (Latin) 0x1000 vai-Latn\r\nValencian Spain 0x0803 ca-ESvalencia\r\nVenda 0x0033 ve\r\nVenda South Africa 0x0433 ve-ZA\r\nVietnamese 0x002A vi\r\nVietnamese Vietnam 0x042A vi-VN\r\nVolapük 0x1000 vo\r\nVolapük World 0x1000 vo-001\r\nVunjo 0x1000 vun\r\nVunjo Tanzania 0x1000 vun-TZ\r\nWalser 0x1000 wae\r\nWalser Switzerland 0x1000 wae-CH\r\nWelsh 0x0052 cy\r\nWelsh United Kingdom 0x0452 cy-GB\r\nWolaytta 0x1000 wal\r\nWolaytta Ethiopia 0x1000 wal-ET\r\nWolof 0x0088 wo\r\nWolof Senegal 0x0488 wo-SN\r\nXhosa 0x0034 xh\r\nXhosa South Africa 0x0434 xh-ZA\r\nYangben 0x1000 yav\r\nYangben Cameroon 0x1000 yav-CM\r\nYi 0x0078 ii\r\nYi People's Republic of China 0x0478 ii-CN\r\nYiddish World 0x043D yi-001\r\nYoruba 0x006A yo\r\nYoruba Benin 0x1000 yo-BJ\r\nYoruba Nigeria 0x046A yo-NG\r\nZarma 0x1000 dje\r\nZarma Niger 0x1000 dje-NE\r\nZulu 0x0035 zu\r\nZulu South Africa 0x0435 zu-ZA\r\n"
  },
  {
    "path": "section.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n\t\"math\"\n\t\"sort\"\n\t\"strings\"\n)\n\nconst (\n\t// ImageSectionReserved1 for future use.\n\tImageSectionReserved1 = 0x00000000\n\n\t// ImageSectionReserved2 for future use.\n\tImageSectionReserved2 = 0x00000001\n\n\t// ImageSectionReserved3 for future use.\n\tImageSectionReserved3 = 0x00000002\n\n\t// ImageSectionReserved4 for future use.\n\tImageSectionReserved4 = 0x00000004\n\n\t// ImageSectionTypeNoPad indicates the section should not be padded to the next\n\t// boundary. This flag is obsolete and is replaced by ImageSectionAlign1Bytes.\n\t// This is valid only for object files.\n\tImageSectionTypeNoPad = 0x00000008\n\n\t// ImageSectionReserved5 for future use.\n\tImageSectionReserved5 = 0x00000010\n\n\t// ImageSectionCntCode indicates the section contains executable code.\n\tImageSectionCntCode = 0x00000020\n\n\t// ImageSectionCntInitializedData indicates the section contains initialized\n\t// data.\n\tImageSectionCntInitializedData = 0x00000040\n\n\t// ImageSectionCntUninitializedData indicates the section contains uninitialized\n\t// data.\n\tImageSectionCntUninitializedData = 0x00000080\n\n\t// ImageSectionLnkOther is reserved for future use.\n\tImageSectionLnkOther = 0x00000100\n\n\t// ImageSectionLnkInfo indicates the section contains comments or other\n\t// information. The .drectve section has this type. This is valid for\n\t// object files only.\n\tImageSectionLnkInfo = 0x00000200\n\n\t// ImageSectionReserved6 for future use.\n\tImageSectionReserved6 = 0x00000400\n\n\t// ImageSectionLnkRemove indicates the section will not become part of the image\n\t// This is valid only for object files.\n\tImageSectionLnkRemove = 0x00000800\n\n\t// ImageSectionLnkComdat indicates the section contains COMDAT data. For more\n\t// information, see COMDAT Sections (Object Only). This is valid only for\n\t// object files.\n\tImageSectionLnkCOMDAT = 0x00001000\n\n\t// ImageSectionGpRel indicates the section contains data referenced through the\n\t// global pointer (GP).\n\tImageSectionGpRel = 0x00008000\n\n\t// ImageSectionMemPurgeable is reserved for future use.\n\tImageSectionMemPurgeable = 0x00020000\n\n\t// ImageSectionMem16Bit is reserved for future use.\n\tImageSectionMem16Bit = 0x00020000\n\n\t// ImageSectionMemLocked is reserved for future use.\n\tImageSectionMemLocked = 0x00040000\n\n\t// ImageSectionMemPreload is reserved for future use.\n\tImageSectionMemPreload = 0x00080000\n\n\t// ImageSectionAlign1Bytes indicates to align data on a 1-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign1Bytes = 0x00100000\n\n\t// ImageSectionAlign2Bytes indicates to align data on a 2-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign2Bytes = 0x00200000\n\n\t// ImageSectionAlign4Bytes indicates to align data on a 4-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign4Bytes = 0x00300000\n\n\t// ImageSectionAlign8Bytes indicates to align data on a 8-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign8Bytes = 0x00400000\n\n\t// ImageSectionAlign16Bytes indicates to align data on a 16-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign16Bytes = 0x00500000\n\n\t// ImageSectionAlign32Bytes indicates to align data on a 32-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign32Bytes = 0x00600000\n\n\t// ImageSectionAlign64Bytes indicates to align data on a 64-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign64Bytes = 0x00700000\n\n\t// ImageSectionAlign128Bytes indicates to align data on a 128-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign128Bytes = 0x00800000\n\n\t// ImageSectionAlign256Bytes indicates to align data on a 256-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign256Bytes = 0x00900000\n\n\t// ImageSectionAlign512Bytes indicates to align data on a 512-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign512Bytes = 0x00A00000\n\n\t// ImageSectionAlign1024Bytes indicates to align data on a 1024-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign1024Bytes = 0x00B00000\n\n\t// ImageSectionAlign2048Bytes indicates to align data on a 2048-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign2048Bytes = 0x00C00000\n\n\t// ImageSectionAlign4096Bytes indicates to align data on a 4096-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign4096Bytes = 0x00D00000\n\n\t// ImageSectionAlign8192Bytes indicates to align data on a 8192-byte boundary.\n\t// Valid only for object files.\n\tImageSectionAlign8192Bytes = 0x00E00000\n\n\t// ImageSectionLnkNRelocOvfl indicates the section contains extended\n\t// relocations.\n\tImageSectionLnkNRelocOvfl = 0x01000000\n\n\t// ImageSectionMemDiscardable indicates the section can be discarded as needed.\n\tImageSectionMemDiscardable = 0x02000000\n\n\t// ImageSectionMemNotCached indicates the  section cannot be cached.\n\tImageSectionMemNotCached = 0x04000000\n\n\t// ImageSectionMemNotPaged indicates the section is not pageable.\n\tImageSectionMemNotPaged = 0x08000000\n\n\t// ImageSectionMemShared indicates the section can be shared in memory.\n\tImageSectionMemShared = 0x10000000\n\n\t// ImageSectionMemExecute indicates the section can be executed as code.\n\tImageSectionMemExecute = 0x20000000\n\n\t// ImageSectionMemRead indicates the section can be read.\n\tImageSectionMemRead = 0x40000000\n\n\t// ImageSectionMemWrite indicates the section can be written to.\n\tImageSectionMemWrite = 0x80000000\n)\n\n// ImageSectionHeader is part of the section table , in fact section table is an\n// array of Image Section Header each contains information about one section of\n// the whole file such as attribute,virtual offset. the array size is the number\n// of sections in the file.\n// Binary Spec : each struct is 40 byte and there is no padding .\ntype ImageSectionHeader struct {\n\n\t//  An 8-byte, null-padded UTF-8 encoded string. If the string is exactly 8\n\t// characters long, there is no terminating null. For longer names, this\n\t// field contains a slash (/) that is followed by an ASCII representation of\n\t// a decimal number that is an offset into the string table. Executable\n\t// images do not use a string table and do not support section names longer\n\t// than 8 characters. Long names in object files are truncated if they are\n\t// emitted to an executable file.\n\tName [8]uint8 `json:\"name\"`\n\n\t// The total size of the section when loaded into memory. If this value is\n\t// greater than SizeOfRawData, the section is zero-padded. This field is\n\t// valid only for executable images and should be set to zero for object files.\n\tVirtualSize uint32 `json:\"virtual_size\"`\n\n\t// For executable images, the address of the first byte of the section\n\t// relative to the image base when the section is loaded into memory.\n\t// For object files, this field is the address of the first byte before\n\t// relocation is applied; for simplicity, compilers should set this to zero.\n\t// Otherwise, it is an arbitrary value that is subtracted from offsets during\n\t// relocation.\n\tVirtualAddress uint32 `json:\"virtual_address\"`\n\n\t// The size of the section (for object files) or the size of the initialized\n\t// data on disk (for image files). For executable images, this must be a\n\t// multiple of FileAlignment from the optional header. If this is less than\n\t// VirtualSize, the remainder of the section is zero-filled. Because the\n\t// SizeOfRawData field is rounded but the VirtualSize field is not, it is\n\t// possible for SizeOfRawData to be greater than VirtualSize as well. When\n\t// a section contains only uninitialized data, this field should be zero.\n\tSizeOfRawData uint32 `json:\"size_of_raw_data\"`\n\n\t// The file pointer to the first page of the section within the COFF file.\n\t// For executable images, this must be a multiple of FileAlignment from the\n\t// optional header. For object files, the value should be aligned on a\n\t// 4-byte boundary for best performance. When a section contains only\n\t// uninitialized data, this field should be zero.\n\tPointerToRawData uint32 `json:\"pointer_to_raw_data\"`\n\n\t// The file pointer to the beginning of relocation entries for the section.\n\t// This is set to zero for executable images or if there are no relocations.\n\tPointerToRelocations uint32 `json:\"pointer_to_relocations\"`\n\n\t// The file pointer to the beginning of line-number entries for the section.\n\t// This is set to zero if there are no COFF line numbers. This value should\n\t// be zero for an image because COFF debugging information is deprecated.\n\tPointerToLineNumbers uint32 `json:\"pointer_to_line_numbers\"`\n\n\t// The number of relocation entries for the section.\n\t// This is set to zero for executable images.\n\tNumberOfRelocations uint16 `json:\"number_of_relocations\"`\n\n\t// The number of line-number entries for the section. This value should be\n\t// zero for an image because COFF debugging information is deprecated.\n\tNumberOfLineNumbers uint16 `json:\"number_of_line_numbers\"`\n\n\t// The flags that describe the characteristics of the section.\n\tCharacteristics uint32 `json:\"characteristics\"`\n}\n\n// Section represents a PE section header, plus additional data like entropy.\ntype Section struct {\n\tHeader ImageSectionHeader `json:\"header\"`\n\t// Entropy represents the section entropy. This field is not always populated\n\t// depending on weather entropy calculation is enabled. The reason behind\n\t// using a float64 pointer instead of a float64 is to distinguish between\n\t// the case when the section entropy is equal to zero and the case when the\n\t// entropy is equal to nil - meaning that it was never calculated.\n\tEntropy *float64 `json:\"entropy,omitempty\"`\n}\n\n// ParseSectionHeader parses the PE section headers. Each row of the section\n// table is, in effect, a section header. It must immediately follow the PE\n// header.\nfunc (pe *File) ParseSectionHeader() (err error) {\n\n\t// Get the first section offset.\n\toptionalHeaderOffset := pe.DOSHeader.AddressOfNewEXEHeader + 4 +\n\t\tuint32(binary.Size(pe.NtHeader.FileHeader))\n\toffset := optionalHeaderOffset +\n\t\tuint32(pe.NtHeader.FileHeader.SizeOfOptionalHeader)\n\n\t// Track invalid/suspicious values while parsing sections.\n\tmaxErr := 3\n\n\tsecHeader := ImageSectionHeader{}\n\tnumberOfSections := pe.NtHeader.FileHeader.NumberOfSections\n\tsecHeaderSize := uint32(binary.Size(secHeader))\n\n\t// The section header indexing in the table is one-based, with the order of\n\t// the sections defined by the linker. The sections follow one another\n\t// contiguously in the order defined by the section header table, with\n\t// starting RVAs aligned by the value of the SectionAlignment field of the\n\t// PE header.\n\tfor i := uint16(0); i < numberOfSections; i++ {\n\t\terr := pe.structUnpack(&secHeader, offset, secHeaderSize)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif secEnd := int64(secHeader.PointerToRawData) + int64(secHeader.SizeOfRawData); secEnd > pe.OverlayOffset {\n\t\t\tpe.OverlayOffset = secEnd\n\t\t}\n\n\t\tcountErr := 0\n\t\tsec := Section{Header: secHeader}\n\t\tsecName := sec.String()\n\n\t\tif (ImageSectionHeader{}) == secHeader {\n\t\t\tpe.Anomalies = append(pe.Anomalies, \"Section `\"+secName+\"` Contents are null-bytes\")\n\t\t\tcountErr++\n\t\t}\n\n\t\tif secHeader.SizeOfRawData+secHeader.PointerToRawData > pe.size {\n\t\t\tpe.Anomalies = append(pe.Anomalies, \"Section `\"+secName+\n\t\t\t\t\"` SizeOfRawData is larger than file\")\n\t\t\tcountErr++\n\t\t}\n\n\t\tif pe.adjustFileAlignment(secHeader.PointerToRawData) > pe.size {\n\t\t\tpe.Anomalies = append(pe.Anomalies, \"Section `\"+secName+\n\t\t\t\t\"` PointerToRawData points beyond the end of the file\")\n\t\t\tcountErr++\n\t\t}\n\n\t\tif secHeader.VirtualSize > 0x10000000 {\n\t\t\tpe.Anomalies = append(pe.Anomalies, \"Section `\"+secName+\n\t\t\t\t\"` VirtualSize is extremely large > 256MiB\")\n\t\t\tcountErr++\n\t\t}\n\n\t\tif pe.adjustSectionAlignment(secHeader.VirtualAddress) > 0x10000000 {\n\t\t\tpe.Anomalies = append(pe.Anomalies, \"Section `\"+secName+\n\t\t\t\t\"` VirtualAddress is beyond 0x10000000\")\n\t\t\tcountErr++\n\t\t}\n\n\t\tvar fileAlignment uint32\n\t\tswitch pe.Is64 {\n\t\tcase true:\n\t\t\tfileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64).FileAlignment\n\t\tcase false:\n\t\t\tfileAlignment = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32).FileAlignment\n\t\t}\n\t\tif fileAlignment != 0 && secHeader.PointerToRawData%fileAlignment != 0 {\n\t\t\tpe.Anomalies = append(pe.Anomalies, \"Section `\"+secName+\n\t\t\t\t\"` PointerToRawData is not multiple of FileAlignment\")\n\t\t\tcountErr++\n\t\t}\n\n\t\tif countErr >= maxErr {\n\t\t\tbreak\n\t\t}\n\n\t\t// Append to the list of sections.\n\t\tif pe.opts.SectionEntropy {\n\t\t\tentropy := sec.CalculateEntropy(pe)\n\t\t\tsec.Entropy = &entropy\n\t\t}\n\t\tpe.Sections = append(pe.Sections, sec)\n\n\t\toffset += secHeaderSize\n\t}\n\n\t// Sort the sections by their VirtualAddress. This will allow to check\n\t// for potentially overlapping sections in badly constructed PEs.\n\tsort.Sort(byVirtualAddress(pe.Sections))\n\n\tif pe.NtHeader.FileHeader.NumberOfSections > 0 && len(pe.Sections) > 0 {\n\t\toffset += secHeaderSize * uint32(pe.NtHeader.FileHeader.NumberOfSections)\n\t}\n\n\t// There could be a problem if there are no raw data sections\n\t// greater than 0. Example: fc91013eb72529da005110a3403541b6\n\t// Should this throw an exception in the minimum header offset\n\t// can't be found?\n\tvar rawDataPointers []uint32\n\tfor _, sec := range pe.Sections {\n\t\tif sec.Header.PointerToRawData > 0 {\n\t\t\trawDataPointers = append(\n\t\t\t\trawDataPointers, pe.adjustFileAlignment(\n\t\t\t\t\tsec.Header.PointerToRawData))\n\t\t}\n\t}\n\n\tvar lowestSectionOffset uint32\n\tif len(rawDataPointers) > 0 {\n\t\tlowestSectionOffset = Min(rawDataPointers)\n\t} else {\n\t\tlowestSectionOffset = 0\n\t}\n\n\tif lowestSectionOffset == 0 || lowestSectionOffset < offset {\n\t\tif offset <= pe.size {\n\t\t\tpe.Header = pe.data[:offset]\n\t\t}\n\t} else {\n\t\tif lowestSectionOffset <= pe.size {\n\t\t\tpe.Header = pe.data[:lowestSectionOffset]\n\t\t}\n\t}\n\n\tpe.HasSections = true\n\treturn nil\n}\n\n// String stringifies the section name.\nfunc (section *Section) String() string {\n\treturn strings.Replace(string(section.Header.Name[:]), \"\\x00\", \"\", -1)\n}\n\n// NextHeaderAddr returns the VirtualAddress of the next section.\nfunc (section *Section) NextHeaderAddr(pe *File) uint32 {\n\tfor i, currentSection := range pe.Sections {\n\t\tif i == len(pe.Sections)-1 {\n\t\t\treturn 0\n\t\t}\n\n\t\tif section.Header == currentSection.Header {\n\t\t\treturn pe.Sections[i+1].Header.VirtualAddress\n\t\t}\n\t}\n\n\treturn 0\n}\n\n// Contains checks whether the section contains a given RVA.\nfunc (section *Section) Contains(rva uint32, pe *File) bool {\n\n\t// Check if the SizeOfRawData is realistic. If it's bigger than the size of\n\t// the whole PE file minus the start address of the section it could be\n\t// either truncated or the SizeOfRawData contains a misleading value.\n\t// In either of those cases we take the VirtualSize.\n\n\tvar size uint32\n\tadjustedPointer := pe.adjustFileAlignment(section.Header.PointerToRawData)\n\tif uint32(len(pe.data))-adjustedPointer < section.Header.SizeOfRawData {\n\t\tsize = section.Header.VirtualSize\n\t} else {\n\t\tsize = Max(section.Header.SizeOfRawData, section.Header.VirtualSize)\n\t}\n\tvaAdj := pe.adjustSectionAlignment(section.Header.VirtualAddress)\n\n\t// Check whether there's any section after the current one that starts before\n\t// the calculated end for the current one. If so, cut the current section's\n\t// size to fit in the range up to where the next section starts.\n\tif section.NextHeaderAddr(pe) != 0 &&\n\t\tsection.NextHeaderAddr(pe) > section.Header.VirtualAddress &&\n\t\tvaAdj+size > section.NextHeaderAddr(pe) {\n\t\tsize = section.NextHeaderAddr(pe) - vaAdj\n\t}\n\n\treturn vaAdj <= rva && rva < vaAdj+size\n}\n\n// Data returns a data chunk from a section.\nfunc (section *Section) Data(start, length uint32, pe *File) []byte {\n\n\tpointerToRawDataAdj := pe.adjustFileAlignment(\n\t\tsection.Header.PointerToRawData)\n\tvirtualAddressAdj := pe.adjustSectionAlignment(\n\t\tsection.Header.VirtualAddress)\n\n\tvar offset uint32\n\tif start == 0 {\n\t\toffset = pointerToRawDataAdj\n\t} else {\n\t\toffset = (start - virtualAddressAdj) + pointerToRawDataAdj\n\t}\n\n\tif offset > pe.size {\n\t\treturn nil\n\t}\n\n\tvar end uint32\n\tif length != 0 {\n\t\tend = offset + length\n\t} else {\n\t\tend = offset + section.Header.SizeOfRawData\n\t}\n\n\t// PointerToRawData is not adjusted here as we might want to read any possible\n\t// extra bytes that might get cut off by aligning the start (and hence cutting\n\t// something off the end)\n\tif end > section.Header.PointerToRawData+section.Header.SizeOfRawData &&\n\t\tsection.Header.PointerToRawData+section.Header.SizeOfRawData > offset {\n\t\tend = section.Header.PointerToRawData + section.Header.SizeOfRawData\n\t}\n\n\tif end > pe.size {\n\t\tend = pe.size\n\t}\n\n\treturn pe.data[offset:end]\n}\n\n// CalculateEntropy calculates section entropy.\nfunc (section *Section) CalculateEntropy(pe *File) float64 {\n\tsectionData := section.Data(0, 0, pe)\n\tif sectionData == nil {\n\t\treturn 0.0\n\t}\n\n\tsectionSize := float64(len(sectionData))\n\tif sectionSize == 0.0 {\n\t\treturn 0.0\n\t}\n\n\tvar frequencies [256]uint64\n\tfor _, v := range sectionData {\n\t\tfrequencies[v]++\n\t}\n\n\tvar entropy float64\n\tfor _, p := range frequencies {\n\t\tif p > 0 {\n\t\t\tfreq := float64(p) / sectionSize\n\t\t\tentropy += freq * math.Log2(freq)\n\t\t}\n\t}\n\n\treturn -entropy\n}\n\n// byVirtualAddress sorts all sections by Virtual Address.\ntype byVirtualAddress []Section\n\nfunc (s byVirtualAddress) Len() int      { return len(s) }\nfunc (s byVirtualAddress) Swap(i, j int) { s[i], s[j] = s[j], s[i] }\nfunc (s byVirtualAddress) Less(i, j int) bool {\n\treturn s[i].Header.VirtualAddress < s[j].Header.VirtualAddress\n}\n\n// byPointerToRawData sorts all sections by PointerToRawData.\ntype byPointerToRawData []Section\n\nfunc (s byPointerToRawData) Len() int      { return len(s) }\nfunc (s byPointerToRawData) Swap(i, j int) { s[i], s[j] = s[j], s[i] }\nfunc (s byPointerToRawData) Less(i, j int) bool {\n\treturn s[i].Header.PointerToRawData < s[j].Header.PointerToRawData\n}\n\n// PrettySectionFlags returns the string representations of the `Flags` field\n// of section header.\nfunc (section *Section) PrettySectionFlags() []string {\n\tvar values []string\n\n\tsectionFlags := map[uint32]string{\n\t\t//ImageSectionReserved1:            \"Reserved1\",\n\t\tImageSectionReserved2:            \"Reserved2\",\n\t\tImageSectionReserved3:            \"Reserved3\",\n\t\tImageSectionReserved4:            \"Reserved4\",\n\t\tImageSectionTypeNoPad:            \"No Padd\",\n\t\tImageSectionReserved5:            \"Reserved5\",\n\t\tImageSectionCntCode:              \"Contains Code\",\n\t\tImageSectionCntInitializedData:   \"Initialized Data\",\n\t\tImageSectionCntUninitializedData: \"Uninitialized Data\",\n\t\tImageSectionLnkOther:             \"Lnk Other\",\n\t\tImageSectionLnkInfo:              \"Lnk Info\",\n\t\tImageSectionReserved6:            \"Reserved6\",\n\t\tImageSectionLnkRemove:            \"LnkRemove\",\n\t\tImageSectionLnkCOMDAT:            \"LnkCOMDAT\",\n\t\tImageSectionGpRel:                \"GpReferenced\",\n\t\tImageSectionMemPurgeable:         \"Purgeable\",\n\t\tImageSectionMemLocked:            \"Locked\",\n\t\tImageSectionMemPreload:           \"Preload\",\n\t\tImageSectionAlign1Bytes:          \"Align1Bytes\",\n\t\tImageSectionAlign2Bytes:          \"Align2Bytes\",\n\t\tImageSectionAlign4Bytes:          \"Align4Bytes\",\n\t\tImageSectionAlign8Bytes:          \"Align8Bytes\",\n\t\tImageSectionAlign16Bytes:         \"Align16Bytes\",\n\t\tImageSectionAlign32Bytes:         \"Align32Bytes\",\n\t\tImageSectionAlign64Bytes:         \"Align64Bytes\",\n\t\tImageSectionAlign128Bytes:        \"Align128Bytes\",\n\t\tImageSectionAlign256Bytes:        \"Align256Bytes\",\n\t\tImageSectionAlign512Bytes:        \"Align512Bytes\",\n\t\tImageSectionAlign1024Bytes:       \"Align1024Bytes\",\n\t\tImageSectionAlign2048Bytes:       \"Align2048Bytes\",\n\t\tImageSectionAlign4096Bytes:       \"Align4096Bytes\",\n\t\tImageSectionAlign8192Bytes:       \"Align8192Bytes\",\n\t\tImageSectionLnkNRelocOvfl:        \"ExtendedReloc\",\n\t\tImageSectionMemDiscardable:       \"Discardable\",\n\t\tImageSectionMemNotCached:         \"NotCached\",\n\t\tImageSectionMemNotPaged:          \"NotPaged\",\n\t\tImageSectionMemShared:            \"Shared\",\n\t\tImageSectionMemExecute:           \"Executable\",\n\t\tImageSectionMemRead:              \"Readable\",\n\t\tImageSectionMemWrite:             \"Writable\",\n\t}\n\n\tflags := section.Header.Characteristics\n\tfor k, v := range sectionFlags {\n\t\tif (k & flags) == k {\n\t\t\tvalues = append(values, v)\n\t\t}\n\t}\n\n\treturn values\n}\n"
  },
  {
    "path": "section_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"reflect\"\n\t\"sort\"\n\t\"testing\"\n)\n\ntype TestSection struct {\n\tsectionCount int\n\tsectionIndex int\n\tsectionName  string\n\theader       ImageSectionHeader\n\tsectionFlags []string\n\tentropy      float64\n}\n\nfunc TestParseSectionHeaders(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  string\n\t\tout TestSection\n\t}{\n\t\t{getAbsoluteFilePath(\"test/putty.exe\"),\n\t\t\tTestSection{\n\t\t\t\tsectionCount: 8,\n\t\t\t\tsectionIndex: 3,\n\t\t\t\tsectionName:  \".pdata\",\n\t\t\t\theader: ImageSectionHeader{\n\t\t\t\t\tName:             [8]uint8{0x2e, 0x70, 0x64, 0x61, 0x74, 0x61, 0x0, 0x0},\n\t\t\t\t\tVirtualSize:      0x588c,\n\t\t\t\t\tVirtualAddress:   0xd2000,\n\t\t\t\t\tSizeOfRawData:    0x5a00,\n\t\t\t\t\tPointerToRawData: 0xc9c00,\n\t\t\t\t\tCharacteristics:  0x40000040,\n\t\t\t\t},\n\t\t\t\tsectionFlags: []string{\"Initialized Data\", \"Readable\"},\n\t\t\t\tentropy:      5.789589357441211,\n\t\t\t}},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tsections := file.Sections\n\t\t\tif len(sections) != tt.out.sectionCount {\n\t\t\t\tt.Errorf(\"sections count assertion failed, got %v, want %v\",\n\t\t\t\t\tlen(sections), tt.out.sectionCount)\n\t\t\t}\n\n\t\t\tsection := sections[tt.out.sectionIndex]\n\t\t\tif !reflect.DeepEqual(section.Header, tt.out.header) {\n\t\t\t\tt.Errorf(\"section header assertion failed, got %v, want %v\",\n\t\t\t\t\tsection.Header, tt.out.header)\n\t\t\t}\n\n\t\t\tsectionName := sections[tt.out.sectionIndex].String()\n\t\t\tif sectionName != tt.out.sectionName {\n\t\t\t\tt.Errorf(\"section name assertion failed, got %v, want %v\",\n\t\t\t\t\tsectionName, tt.out.sectionName)\n\t\t\t}\n\n\t\t\tprettySectionFlags := section.PrettySectionFlags()\n\t\t\tsort.Strings(prettySectionFlags)\n\t\t\tif !reflect.DeepEqual(prettySectionFlags, tt.out.sectionFlags) {\n\t\t\t\tt.Errorf(\"pretty section flags assertion failed, got %v, want %v\",\n\t\t\t\t\tprettySectionFlags, tt.out.sectionFlags)\n\t\t\t}\n\n\t\t\tentropy := sections[tt.out.sectionIndex].CalculateEntropy(file)\n\t\t\tif entropy != tt.out.entropy {\n\t\t\t\tt.Errorf(\"entropy calculation failed, got %v, want %v\",\n\t\t\t\t\tentropy, tt.out.entropy)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "security.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"bytes\"\n\t\"crypto\"\n\t\"crypto/x509\"\n\t\"crypto/x509/pkix\"\n\t\"encoding/asn1\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"hash\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ayoubfaouzi/pkcs7\"\n)\n\n// The options for the WIN_CERTIFICATE Revision member include\n// (but are not limited to) the following.\nconst (\n\t// WinCertRevision1_0 represents the WIN_CERT_REVISION_1_0 Version 1,\n\t// legacy version of the Win_Certificate structure.\n\t// It is supported only for purposes of verifying legacy Authenticode\n\t// signatures\n\tWinCertRevision1_0 = 0x0100\n\n\t// WinCertRevision2_0 represents the WIN_CERT_REVISION_2_0. Version 2\n\t// is the current version of the Win_Certificate structure.\n\tWinCertRevision2_0 = 0x0200\n)\n\n// The options for the WIN_CERTIFICATE CertificateType member include\n// (but are not limited to) the items in the following table. Note that some\n// values are not currently supported.\nconst (\n\t// Certificate contains an X.509 Certificate (Not Supported)\n\tWinCertTypeX509 = 0x0001\n\n\t// Certificate contains a PKCS#7 SignedData structure.\n\tWinCertTypePKCSSignedData = 0x0002\n\n\t// Reserved.\n\tWinCertTypeReserved1 = 0x0003\n\n\t// Terminal Server Protocol Stack Certificate signing (Not Supported).\n\tWinCertTypeTSStackSigned = 0x0004\n)\n\nvar (\n\t// ErrSecurityDataDirInvalidCertHeader is reported when the certificate\n\t// header in the security directory is invalid.\n\tErrSecurityDataDirInvalid = errors.New(\n\t\t`invalid certificate header in security directory`)\n\n)\n\n// CertificateSection represents the security directory of a PE file, which\n// contains Authenticode signatures used to verify the integrity and origin of\n// the binary. The raw PKCS#7 data is parsed into one or more certificates,\n// including any nested counter-signatures.\ntype CertificateSection struct {\n\t// Header is the WIN_CERTIFICATE structure at the start of the security\n\t// directory, specifying the length, revision, and type of the certificate.\n\tHeader WinCertificate `json:\"header\"`\n\n\t// Raw contains the raw PKCS#7 signed data bytes (excluding the\n\t// WIN_CERTIFICATE header). This field is excluded from JSON output.\n\tRaw []byte `json:\"-\"`\n\n\t// Certificates holds the parsed certificate chain. The first entry is the\n\t// primary Authenticode signature; subsequent entries are nested\n\t// counter-signatures extracted from unsigned PKCS#7 attributes.\n\tCertificates []Certificate `json:\"certificates,omitempty\"`\n}\n\n// Certificate represents a parsed Authenticode signature extracted from the\n// PE security directory. It pairs the full PKCS#7 content with validation\n// results: whether the signature hash matches the PE image (SignatureValid)\n// and whether the signing certificate chains to a trusted root (Verified).\ntype Certificate struct {\n\t// Content is the full parsed PKCS#7 structure. Excluded from JSON output.\n\tContent pkcs7.PKCS7 `json:\"-\"`\n\n\t// SignatureContent holds the parsed Authenticode digest algorithm and hash\n\t// from the SpcIndirectDataContent. Excluded from JSON output.\n\tSignatureContent AuthenticodeContent `json:\"-\"`\n\n\t// SignatureValid is true when the Authenticode hash in the signature\n\t// matches the computed authentihash of the PE image.\n\tSignatureValid bool `json:\"signature_valid\"`\n\n\t// Info contains the human-readable certificate metadata (issuer, subject,\n\t// validity period, serial number, and algorithms).\n\tInfo CertInfo `json:\"info\"`\n\n\t// Verified is true when the signing certificate chains to a trusted root\n\t// in the system certificate store.\n\tVerified bool `json:\"verified\"`\n}\n\n// WinCertificate encapsulates a signature used in verifying executable files.\ntype WinCertificate struct {\n\t// Specifies the length, in bytes, of the signature.\n\tLength uint32 `json:\"length\"`\n\n\t// Specifies the certificate revision.\n\tRevision uint16 `json:\"revision\"`\n\n\t// Specifies the type of certificate.\n\tCertificateType uint16 `json:\"certificate_type\"`\n}\n\n// CertInfo wraps the important fields of the pkcs7 structure.\n// This is what we what keep in JSON marshalling.\ntype CertInfo struct {\n\t// The certificate authority (CA) that charges customers to issue\n\t// certificates for them.\n\tIssuer string `json:\"issuer\"`\n\n\t// The subject of the certificate is the entity its public key is associated\n\t// with (i.e. the \"owner\" of the certificate).\n\tSubject string `json:\"subject\"`\n\n\t// The certificate won't be valid before this timestamp.\n\tNotBefore time.Time `json:\"not_before\"`\n\n\t// The certificate won't be valid after this timestamp.\n\tNotAfter time.Time `json:\"not_after\"`\n\n\t// The serial number MUST be a positive integer assigned by the CA to each\n\t// certificate. It MUST be unique for each certificate issued by a given CA\n\t// (i.e., the issuer name and serial number identify a unique certificate).\n\t// CAs MUST force the serialNumber to be a non-negative integer.\n\t// For convenience, we convert the big int to string.\n\tSerialNumber string `json:\"serial_number\"`\n\n\t// The identifier for the cryptographic algorithm used by the CA to sign\n\t// this certificate.\n\tSignatureAlgorithm x509.SignatureAlgorithm `json:\"signature_algorithm\"`\n\n\t// The Public Key Algorithm refers to the public key inside the certificate.\n\t// This certificate is used together with the matching private key to prove\n\t// the identity of the peer.\n\tPublicKeyAlgorithm x509.PublicKeyAlgorithm `json:\"public_key_algorithm\"`\n}\n\ntype RelRange struct {\n\tStart  uint32\n\tLength uint32\n}\n\ntype byStart []RelRange\n\nfunc (s byStart) Len() int      { return len(s) }\nfunc (s byStart) Swap(i, j int) { s[i], s[j] = s[j], s[i] }\nfunc (s byStart) Less(i, j int) bool {\n\treturn s[i].Start < s[j].Start\n}\n\ntype Range struct {\n\tStart uint32\n\tEnd   uint32\n}\n\nfunc (pe *File) parseLocations() (map[string]*RelRange, error) {\n\tlocation := make(map[string]*RelRange, 3)\n\n\tfileHdrSize := uint32(binary.Size(pe.NtHeader.FileHeader))\n\toptionalHeaderOffset := pe.DOSHeader.AddressOfNewEXEHeader + 4 + fileHdrSize\n\n\tvar (\n\t\toh32 ImageOptionalHeader32\n\t\toh64 ImageOptionalHeader64\n\n\t\toptionalHeaderSize uint32\n\t)\n\n\tswitch pe.Is64 {\n\tcase true:\n\t\toh64 = pe.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\toptionalHeaderSize = oh64.SizeOfHeaders\n\tcase false:\n\t\toh32 = pe.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\toptionalHeaderSize = oh32.SizeOfHeaders\n\t}\n\n\tif optionalHeaderSize > pe.size-optionalHeaderOffset {\n\t\tmsgF := \"the optional header exceeds the file length (%d + %d > %d)\"\n\t\treturn nil, fmt.Errorf(msgF, optionalHeaderSize, optionalHeaderOffset, pe.size)\n\t}\n\n\tif optionalHeaderSize < 68 {\n\t\tmsgF := \"the optional header size is %d < 68, which is insufficient for authenticode\"\n\t\treturn nil, fmt.Errorf(msgF, optionalHeaderSize)\n\t}\n\n\t// The location of the checksum\n\tlocation[\"checksum\"] = &RelRange{optionalHeaderOffset + 64, 4}\n\n\tvar rvaBase, certBase, numberOfRvaAndSizes uint32\n\tswitch pe.Is64 {\n\tcase true:\n\t\trvaBase = optionalHeaderOffset + 108\n\t\tcertBase = optionalHeaderOffset + 144\n\t\tnumberOfRvaAndSizes = oh64.NumberOfRvaAndSizes\n\tcase false:\n\t\trvaBase = optionalHeaderOffset + 92\n\t\tcertBase = optionalHeaderOffset + 128\n\t\tnumberOfRvaAndSizes = oh32.NumberOfRvaAndSizes\n\t}\n\n\tif optionalHeaderOffset+optionalHeaderSize < rvaBase+4 {\n\t\tpe.logger.Debug(\"The PE Optional Header size can not accommodate for the NumberOfRvaAndSizes field\")\n\t\treturn location, nil\n\t}\n\n\tif numberOfRvaAndSizes < uint32(5) {\n\t\tpe.logger.Debugf(\"The PE Optional Header does not have a Certificate Table entry in its \"+\n\t\t\t\"Data Directory; NumberOfRvaAndSizes = %d\", numberOfRvaAndSizes)\n\t\treturn location, nil\n\t}\n\n\tif optionalHeaderOffset+optionalHeaderSize < certBase+8 {\n\t\tpe.logger.Debug(\"The PE Optional Header size can not accommodate for a Certificate Table\" +\n\t\t\t\"entry in its Data Directory\")\n\t\treturn location, nil\n\t}\n\n\t// The location of the entry of the Certificate Table in the Data Directory\n\tlocation[\"datadir_certtable\"] = &RelRange{certBase, 8}\n\n\tvar address, size uint32\n\tswitch pe.Is64 {\n\tcase true:\n\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryCertificate]\n\t\taddress = dirEntry.VirtualAddress\n\t\tsize = dirEntry.Size\n\tcase false:\n\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryCertificate]\n\t\taddress = dirEntry.VirtualAddress\n\t\tsize = dirEntry.Size\n\t}\n\n\tif size == 0 {\n\t\tpe.logger.Debug(\"The Certificate Table is empty\")\n\t\treturn location, nil\n\t}\n\n\tif int64(address) < int64(optionalHeaderSize)+int64(optionalHeaderOffset) ||\n\t\tint64(address)+int64(size) > int64(pe.size) {\n\t\tpe.logger.Debugf(\"The location of the Certificate Table in the binary makes no sense and \"+\n\t\t\t\"is either beyond the boundaries of the file, or in the middle of the PE header; \"+\n\t\t\t\"VirtualAddress: %x, Size: %x\", address, size)\n\t\treturn location, nil\n\t}\n\n\t// The location of the Certificate Table\n\tlocation[\"certtable\"] = &RelRange{address, size}\n\treturn location, nil\n}\n\n// Authentihash generates the SHA256 pe image file hash.\n// The relevant sections to exclude during hashing are:\n//   - The location of the checksum\n//   - The location of the entry of the Certificate Table in the Data Directory\n//   - The location of the Certificate Table.\nfunc (pe *File) Authentihash() []byte {\n\tresults := pe.AuthentihashExt(crypto.SHA256.New())\n\tif len(results) > 0 {\n\t\treturn results[0]\n\t}\n\treturn nil\n}\n\n// AuthentihashExt generates pe image file hashes using the given hashers.\n// The relevant sections to exclude during hashing are:\n//   - The location of the checksum\n//   - The location of the entry of the Certificate Table in the Data Directory\n//   - The location of the Certificate Table.\nfunc (pe *File) AuthentihashExt(hashers ...hash.Hash) [][]byte {\n\n\tlocationMap, err := pe.parseLocations()\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\tlocationSlice := make([]RelRange, 0, len(locationMap))\n\tfor k, v := range locationMap {\n\t\tif stringInSlice(k, []string{\"checksum\", \"datadir_certtable\", \"certtable\"}) {\n\t\t\tlocationSlice = append(locationSlice, *v)\n\t\t}\n\t}\n\tsort.Sort(byStart(locationSlice))\n\n\tranges := make([]*Range, 0, len(locationSlice))\n\tstart := uint32(0)\n\tfor _, r := range locationSlice {\n\t\tranges = append(ranges, &Range{Start: start, End: r.Start})\n\t\tstart = r.Start + r.Length\n\t}\n\tranges = append(ranges, &Range{Start: start, End: pe.size})\n\n\tvar rd io.ReaderAt\n\tif pe.f != nil {\n\t\trd = pe.f\n\t} else {\n\t\trd = bytes.NewReader(pe.data)\n\t}\n\n\tfor _, v := range ranges {\n\t\tfor _, hasher := range hashers {\n\t\t\tsr := io.NewSectionReader(rd, int64(v.Start), int64(v.End)-int64(v.Start))\n\t\t\tio.Copy(hasher, sr)\n\t\t\tsr.Seek(0, io.SeekStart)\n\t\t}\n\t}\n\n\tvar ret [][]byte\n\tfor _, hasher := range hashers {\n\t\tret = append(ret, hasher.Sum(nil))\n\t}\n\n\treturn ret\n}\n\n// The security directory contains the authenticode signature, which is a digital\n// signature format that is used, among other purposes, to determine the origin\n// and integrity of software binaries. Authenticode is based on the Public-Key\n// Cryptography Standards (PKCS) #7 standard and uses X.509 v3 certificates to\n// bind an Authenticode-signed file to the identity of a software publisher.\n// This data are not loaded into memory as part of the image file.\nfunc (pe *File) parseSecurityDirectory(rva, size uint32) error {\n\tvar certHeader WinCertificate\n\tcertSize := uint32(binary.Size(certHeader))\n\tsignatureContent := AuthenticodeContent{}\n\n\t// The virtual address value from the Certificate Table entry in the\n\t// Optional Header Data Directory is a file offset to the first attribute\n\t// certificate entry.\n\tfileOffset := rva\n\n\terr := pe.structUnpack(&certHeader, fileOffset, certSize)\n\tif err != nil {\n\t\treturn ErrOutsideBoundary\n\t}\n\n\tif certHeader.Length > size {\n\t\treturn ErrOutsideBoundary\n\t}\n\n\tif fileOffset+certHeader.Length > pe.size {\n\t\treturn ErrOutsideBoundary\n\t}\n\n\tif certHeader.Length < certSize {\n\t\treturn ErrSecurityDataDirInvalid\n\t}\n\n\tpe.HasCertificate = true\n\tpe.Certificates.Header = certHeader\n\tpe.Certificates.Raw = pe.data[fileOffset+certSize : fileOffset+certHeader.Length]\n\n\tcertContent := pe.Certificates.Raw\n\tfor {\n\t\tpkcs, err := pkcs7.Parse(certContent)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// The pkcs7.PKCS7 structure contains many fields that we are not\n\t\t// interested to, so create another structure, similar to _CERT_INFO\n\t\t// structure which contains only the important information.\n\t\tvar signerCertificate = pkcs.GetOnlySigner()\n\t\tif signerCertificate == nil {\n\t\t\treturn errors.New(\"could not find signer certificate\")\n\t\t}\n\n\t\tvar certInfo CertInfo\n\n\t\tcertInfo.SerialNumber = hex.EncodeToString(signerCertificate.SerialNumber.Bytes())\n\t\tcertInfo.PublicKeyAlgorithm = signerCertificate.PublicKeyAlgorithm\n\n\t\tcertInfo.NotAfter = signerCertificate.NotAfter\n\t\tcertInfo.NotBefore = signerCertificate.NotBefore\n\n\t\t// Issuer infos\n\t\tcertInfo.Issuer = formatPkixName(signerCertificate.Issuer)\n\n\t\t// Subject infos\n\t\tcertInfo.Subject = formatPkixName(signerCertificate.Subject)\n\n\t\t// Let's mark the file as signed, then we verify if the signature is valid.\n\t\tpe.IsSigned = true\n\n\t\tvar certValid bool\n\t\t// Let's load the system root certs.\n\t\tif !pe.opts.DisableCertValidation {\n\t\t\tvar certPool *x509.CertPool\n\t\t\tif runtime.GOOS == \"windows\" {\n\t\t\t\tcertPool, err = loadSystemRoots()\n\t\t\t} else {\n\t\t\t\tcertPool, err = x509.SystemCertPool()\n\t\t\t}\n\n\t\t\t// Verify the signature. This will also verify the chain of trust of the\n\t\t\t// the end-entity signer cert to one of the root in the trust store.\n\t\t\tif err != nil {\n\t\t\t\tpe.logger.Errorf(\"failed to loadSystemRoots: %v\", err)\n\t\t\t} else {\n\t\t\t\terr = pkcs.VerifyWithChain(certPool)\n\t\t\t\tif err == nil {\n\t\t\t\t\tcertValid = true\n\t\t\t\t} else {\n\t\t\t\t\tcertValid = false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tvar signatureValid bool\n\t\tsignatureContent, err = parseAuthenticodeContent(pkcs.Content)\n\t\tif err != nil {\n\t\t\tpe.logger.Errorf(\"could not parse authenticode content: %v\", err)\n\t\t\tsignatureValid = false\n\t\t} else if !pe.opts.DisableSignatureValidation {\n\t\t\tauthentihash := pe.AuthentihashExt(signatureContent.HashFunction.New())\n\t\t\tif len(authentihash) > 0 {\n\t\t\t\tsignatureValid = bytes.Equal(authentihash[0], signatureContent.HashResult) && certValid\n\t\t\t}\n\t\t}\n\n\t\tcertInfo.SignatureAlgorithm = signatureContent.Algorithm\n\t\tpe.Certificates.Certificates = append(pe.Certificates.Certificates, Certificate{\n\t\t\tContent:          *pkcs,\n\t\t\tSignatureContent: signatureContent,\n\t\t\tSignatureValid:   signatureValid,\n\t\t\tInfo:             certInfo,\n\t\t\tVerified:         certValid,\n\t\t})\n\n\t\t// Subsequent certificates are an (unsigned) attribute of the PKCS#7\n\t\tvar newCert asn1.RawValue\n\t\tnestedSignatureOid := asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 4, 1}\n\t\terr = pkcs.UnmarshalUnsignedAttribute(nestedSignatureOid, &newCert)\n\t\tif err != nil {\n\t\t\tvar attributeNotFound pkcs7.AttributeNotFoundError\n\t\t\tif errors.As(err, &attributeNotFound) {\n\t\t\t\tbreak // No further nested certificates\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tcertContent = newCert.FullBytes\n\t}\n\n\treturn nil\n}\n\n// loadSystemsRoots manually downloads all the trusted root certificates\n// in Windows by spawning certutil then adding root certs individually\n// to the cert pool. Initially, when running in windows, go SystemCertPool()\n// used to enumerate all the certificate in the Windows store using\n// (CertEnumCertificatesInStore). Unfortunately, Windows does not ship\n// with all of its root certificates installed. Instead, it downloads them\n// on-demand. As a consequence, this behavior leads to a non-deterministic\n// results. Go team then disabled the loading Windows root certs.\nfunc loadSystemRoots() (*x509.CertPool, error) {\n\n\tneedSync := true\n\troots := x509.NewCertPool()\n\n\t// Create a temporary dir in the OS temp folder\n\t// if it does not exists.\n\tdir := filepath.Join(os.TempDir(), \"certs\")\n\tinfo, err := os.Stat(dir)\n\tif os.IsNotExist(err) {\n\t\tif err = os.Mkdir(dir, 0755); err != nil {\n\t\t\treturn roots, err\n\t\t}\n\t} else {\n\t\tnow := time.Now()\n\t\tmodTime := info.ModTime()\n\t\tdiff := now.Sub(modTime).Hours()\n\t\tif diff < 24 {\n\t\t\tneedSync = false\n\t\t}\n\t}\n\n\t// Use certutil to download all the root certs.\n\tif needSync {\n\t\tcmd := exec.Command(\"certutil\", \"-syncWithWU\", dir)\n\t\thideWindow(cmd)\n\t\terr := cmd.Run()\n\t\tif err != nil {\n\t\t\treturn roots, err\n\t\t}\n\t\tif cmd.ProcessState.ExitCode() != 0 {\n\t\t\treturn roots, err\n\t\t}\n\t}\n\n\tfiles, err := os.ReadDir(dir)\n\tif err != nil {\n\t\treturn roots, err\n\t}\n\n\tfor _, f := range files {\n\t\tif !strings.HasSuffix(f.Name(), \".crt\") {\n\t\t\tcontinue\n\t\t}\n\t\tcertPath := filepath.Join(dir, f.Name())\n\t\tcertData, err := os.ReadFile(certPath)\n\t\tif err != nil {\n\t\t\treturn roots, err\n\t\t}\n\n\t\tif crt, err := x509.ParseCertificate(certData); err == nil {\n\t\t\troots.AddCert(crt)\n\t\t}\n\t}\n\n\treturn roots, nil\n}\n\ntype SpcIndirectDataContent struct {\n\tData          SpcAttributeTypeAndOptionalValue\n\tMessageDigest DigestInfo\n}\n\ntype SpcAttributeTypeAndOptionalValue struct {\n\tType  asn1.ObjectIdentifier\n\tValue SpcPeImageData `asn1:\"optional\"`\n}\n\ntype SpcPeImageData struct {\n\tFlags asn1.BitString\n\tFile  asn1.RawValue\n}\n\ntype DigestInfo struct {\n\tDigestAlgorithm pkix.AlgorithmIdentifier\n\tDigest          []byte\n}\n\n// Translation of algorithm identifier to hash algorithm, copied from pkcs7.getHashForOID\nfunc parseHashAlgorithm(identifier pkix.AlgorithmIdentifier) (crypto.Hash, x509.SignatureAlgorithm, error) {\n\toid := identifier.Algorithm\n\tswitch {\n\tcase oid.Equal(pkcs7.OIDDigestAlgorithmMD5):\n\t\treturn crypto.MD5, x509.MD5WithRSA, nil\n\tcase oid.Equal(pkcs7.OIDDigestAlgorithmSHA1), oid.Equal(pkcs7.OIDEncryptionAlgorithmRSA):\n\t\treturn crypto.SHA1, x509.SHA1WithRSA, nil\n\tcase oid.Equal(pkcs7.OIDDigestAlgorithmECDSASHA1):\n\t\treturn crypto.SHA1, x509.ECDSAWithSHA1, nil\n\tcase oid.Equal(pkcs7.OIDDigestAlgorithmDSA), oid.Equal(pkcs7.OIDDigestAlgorithmDSASHA1):\n\t\treturn crypto.SHA1, x509.DSAWithSHA1, nil\n\tcase oid.Equal(pkcs7.OIDDigestAlgorithmSHA256):\n\t\treturn crypto.SHA256, x509.SHA256WithRSA, nil\n\tcase oid.Equal(pkcs7.OIDDigestAlgorithmECDSASHA256):\n\t\treturn crypto.SHA256, x509.ECDSAWithSHA256, nil\n\tcase oid.Equal(pkcs7.OIDDigestAlgorithmSHA384):\n\t\treturn crypto.SHA384, x509.SHA384WithRSA, nil\n\tcase oid.Equal(pkcs7.OIDDigestAlgorithmECDSASHA384):\n\t\treturn crypto.SHA384, x509.ECDSAWithSHA384, nil\n\tcase oid.Equal(pkcs7.OIDDigestAlgorithmSHA512):\n\t\treturn crypto.SHA512, x509.SHA512WithRSA, nil\n\tcase oid.Equal(pkcs7.OIDDigestAlgorithmECDSASHA512):\n\t\treturn crypto.SHA512, x509.ECDSAWithSHA512, nil\n\t}\n\treturn 0, 0, pkcs7.ErrUnsupportedAlgorithm\n}\n\n// AuthenticodeContent provides a simplified view on SpcIndirectDataContent, which specifies the ASN.1 encoded values of\n// the authenticode signature content.\ntype AuthenticodeContent struct {\n\tAlgorithm    x509.SignatureAlgorithm\n\tHashFunction crypto.Hash\n\tHashResult   []byte\n}\n\nfunc parseAuthenticodeContent(content []byte) (AuthenticodeContent, error) {\n\tvar authenticodeContent SpcIndirectDataContent\n\tcontent, err := asn1.Unmarshal(content, &authenticodeContent.Data)\n\tif err != nil {\n\t\treturn AuthenticodeContent{}, err\n\t}\n\t_, err = asn1.Unmarshal(content, &authenticodeContent.MessageDigest)\n\tif err != nil {\n\t\treturn AuthenticodeContent{}, err\n\t}\n\thashFunction, algorithmID, err := parseHashAlgorithm(authenticodeContent.MessageDigest.DigestAlgorithm)\n\tif err != nil {\n\t\treturn AuthenticodeContent{}, err\n\t}\n\treturn AuthenticodeContent{\n\t\tAlgorithm:    algorithmID,\n\t\tHashFunction: hashFunction,\n\t\tHashResult:   authenticodeContent.MessageDigest.Digest,\n\t}, nil\n}\n\nfunc formatPkixName(name pkix.Name) string {\n\tvar parts []string\n\tif len(name.Country) > 0 {\n\t\tparts = append(parts, name.Country[0])\n\t}\n\tif len(name.Province) > 0 {\n\t\tparts = append(parts, name.Province[0])\n\t}\n\tif len(name.Locality) > 0 {\n\t\tparts = append(parts, name.Locality[0])\n\t}\n\tif len(name.Organization) > 0 {\n\t\tparts = append(parts, name.Organization[0])\n\t}\n\tif name.CommonName != \"\" {\n\t\tparts = append(parts, name.CommonName)\n\t}\n\treturn strings.Join(parts, \", \")\n}\n"
  },
  {
    "path": "security_linux_mac.go",
    "content": "//go:build !windows\r\n// +build !windows\r\n\r\npackage pe\r\n\r\nimport \"os/exec\"\r\n\r\nfunc hideWindow(cmd *exec.Cmd) {\r\n}\r\n"
  },
  {
    "path": "security_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"crypto/x509\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"reflect\"\n\t\"runtime\"\n\t\"testing\"\n\t\"time\"\n)\n\ntype TestSecurityEntry struct {\n\tHeader       WinCertificate\n\tCertificates []Certificate\n\terr          error\n}\n\nfunc TestParseSecurityDirectory(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  string\n\t\tout TestSecurityEntry\n\t}{\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/putty.exe\"),\n\t\t\tTestSecurityEntry{\n\t\t\t\tHeader: WinCertificate{\n\t\t\t\t\tLength:          0x3D90,\n\t\t\t\t\tRevision:        0x200,\n\t\t\t\t\tCertificateType: 0x2,\n\t\t\t\t},\n\t\t\t\tCertificates: []Certificate{\n\t\t\t\t\t{\n\t\t\t\t\t\tInfo: CertInfo{\n\t\t\t\t\t\t\tIssuer:             \"GB, Greater Manchester, Salford, COMODO CA Limited, COMODO RSA Code Signing CA\",\n\t\t\t\t\t\t\tSubject:            \"GB, Cambridgeshire, Cambridge, Simon Tatham, Simon Tatham\",\n\t\t\t\t\t\t\tNotBefore:          time.Date(2018, time.November, 13, 00, 00, 0, 0, time.UTC),\n\t\t\t\t\t\t\tNotAfter:           time.Date(2021, time.November, 8, 23, 59, 59, 0, time.UTC),\n\t\t\t\t\t\t\tSerialNumber:       \"7c1118cbbadc95da3752c46e47a27438\",\n\t\t\t\t\t\t\tPublicKeyAlgorithm: x509.RSA,\n\t\t\t\t\t\t\tSignatureAlgorithm: x509.SHA1WithRSA,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVerified:       true,\n\t\t\t\t\t\tSignatureValid: true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tInfo: CertInfo{\n\t\t\t\t\t\t\tIssuer:             \"GB, Greater Manchester, Salford, COMODO CA Limited, COMODO RSA Code Signing CA\",\n\t\t\t\t\t\t\tSubject:            \"GB, Cambridgeshire, Cambridge, Simon Tatham, Simon Tatham\",\n\t\t\t\t\t\t\tNotBefore:          time.Date(2018, time.November, 13, 00, 00, 0, 0, time.UTC),\n\t\t\t\t\t\t\tNotAfter:           time.Date(2021, time.November, 8, 23, 59, 59, 0, time.UTC),\n\t\t\t\t\t\t\tSerialNumber:       \"7c1118cbbadc95da3752c46e47a27438\",\n\t\t\t\t\t\t\tPublicKeyAlgorithm: x509.RSA,\n\t\t\t\t\t\t\tSignatureAlgorithm: x509.SHA256WithRSA,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVerified:       true,\n\t\t\t\t\t\tSignatureValid: true,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/putty_modified.exe\"),\n\t\t\tTestSecurityEntry{\n\t\t\t\tHeader: WinCertificate{\n\t\t\t\t\tLength:          0x3D90,\n\t\t\t\t\tRevision:        0x200,\n\t\t\t\t\tCertificateType: 0x2,\n\t\t\t\t},\n\t\t\t\tCertificates: []Certificate{\n\t\t\t\t\t{\n\t\t\t\t\t\tInfo: CertInfo{\n\t\t\t\t\t\t\tIssuer:             \"GB, Greater Manchester, Salford, COMODO CA Limited, COMODO RSA Code Signing CA\",\n\t\t\t\t\t\t\tSubject:            \"GB, Cambridgeshire, Cambridge, Simon Tatham, Simon Tatham\",\n\t\t\t\t\t\t\tNotBefore:          time.Date(2018, time.November, 13, 00, 00, 0, 0, time.UTC),\n\t\t\t\t\t\t\tNotAfter:           time.Date(2021, time.November, 8, 23, 59, 59, 0, time.UTC),\n\t\t\t\t\t\t\tSerialNumber:       \"7c1118cbbadc95da3752c46e47a27438\",\n\t\t\t\t\t\t\tPublicKeyAlgorithm: x509.RSA,\n\t\t\t\t\t\t\tSignatureAlgorithm: x509.SHA1WithRSA,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVerified:       true,\n\t\t\t\t\t\tSignatureValid: false,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tInfo: CertInfo{\n\t\t\t\t\t\t\tIssuer:             \"GB, Greater Manchester, Salford, COMODO CA Limited, COMODO RSA Code Signing CA\",\n\t\t\t\t\t\t\tSubject:            \"GB, Cambridgeshire, Cambridge, Simon Tatham, Simon Tatham\",\n\t\t\t\t\t\t\tNotBefore:          time.Date(2018, time.November, 13, 00, 00, 0, 0, time.UTC),\n\t\t\t\t\t\t\tNotAfter:           time.Date(2021, time.November, 8, 23, 59, 59, 0, time.UTC),\n\t\t\t\t\t\t\tSerialNumber:       \"7c1118cbbadc95da3752c46e47a27438\",\n\t\t\t\t\t\t\tPublicKeyAlgorithm: x509.RSA,\n\t\t\t\t\t\t\tSignatureAlgorithm: x509.SHA256WithRSA,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVerified:       true,\n\t\t\t\t\t\tSignatureValid: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/579fd8a0385482fb4c789561a30b09f25671e86422f40ef5cca2036b28f99648\"),\n\t\t\tTestSecurityEntry{\n\t\t\t\tHeader: WinCertificate{\n\t\t\t\t\tLength:          0x3488,\n\t\t\t\t\tRevision:        0x200,\n\t\t\t\t\tCertificateType: 0x2,\n\t\t\t\t},\n\t\t\t\tCertificates: []Certificate{\n\t\t\t\t\t{\n\t\t\t\t\t\tInfo: CertInfo{\n\t\t\t\t\t\t\tIssuer:             \"US, VeriSign, Inc., VeriSign Class 3 Code Signing 2010 CA\",\n\t\t\t\t\t\t\tSubject:            \"US, California, Mountain View, Symantec Corporation, Symantec Corporation\",\n\t\t\t\t\t\t\tNotBefore:          time.Date(2016, time.December, 16, 00, 00, 0, 0, time.UTC),\n\t\t\t\t\t\t\tNotAfter:           time.Date(2017, time.December, 17, 23, 59, 59, 0, time.UTC),\n\t\t\t\t\t\t\tSerialNumber:       \"0ebfea68d677b3e26cab41c33f3e69de\",\n\t\t\t\t\t\t\tPublicKeyAlgorithm: x509.RSA,\n\t\t\t\t\t\t\tSignatureAlgorithm: x509.SHA1WithRSA,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVerified:       false,\n\t\t\t\t\t\tSignatureValid: false,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tInfo: CertInfo{\n\t\t\t\t\t\t\tIssuer:             \"US, Symantec Corporation, Symantec Class 3 SHA256 Code Signing CA - G2\",\n\t\t\t\t\t\t\tSubject:            \"US, California, Mountain View, Symantec Corporation, Symantec Corporation\",\n\t\t\t\t\t\t\tNotBefore:          time.Date(2017, time.March, 15, 00, 00, 0, 0, time.UTC),\n\t\t\t\t\t\t\tNotAfter:           time.Date(2018, time.April, 13, 23, 59, 59, 0, time.UTC),\n\t\t\t\t\t\t\tSerialNumber:       \"2e6be6bd11a8676e6c57909e9b0d5f57\",\n\t\t\t\t\t\t\tPublicKeyAlgorithm: x509.RSA,\n\t\t\t\t\t\t\tSignatureAlgorithm: x509.SHA256WithRSA,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tVerified:       false,\n\t\t\t\t\t\tSignatureValid: false,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\terr: nil,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tgetAbsoluteFilePath(\"test/00121dae38f26a33da2990987db58738c5a5966930126a42f606a3b40e014624\"),\n\t\t\tTestSecurityEntry{\n\t\t\t\terr: ErrSecurityDataDirInvalid,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(filepath.Base(tt.in), func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tvar va, size uint32\n\t\t\tif file.Is64 {\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryCertificate]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t} else {\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryCertificate]\n\t\t\t\tva = dirEntry.VirtualAddress\n\t\t\t\tsize = dirEntry.Size\n\t\t\t}\n\n\t\t\terr = file.parseSecurityDirectory(va, size)\n\t\t\tif err != tt.out.err {\n\t\t\t\tt.Fatalf(\"parseSecurityDirectory(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tgot := file.Certificates\n\t\t\tif tt.out.err == nil {\n\t\t\t\tif !reflect.DeepEqual(got.Header, tt.out.Header) {\n\t\t\t\t\tt.Fatalf(\"certificate header assertion failed, got %v, want %v\", got.Header, tt.out.Header)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif len(got.Certificates) != len(tt.out.Certificates) {\n\t\t\t\tt.Fatalf(\"certificate count assertion failed, got %d, want %d\", len(got.Certificates), len(tt.out.Certificates))\n\t\t\t}\n\t\t\tfor i, cert := range got.Certificates {\n\t\t\t\texpected := tt.out.Certificates[i]\n\t\t\t\tif !reflect.DeepEqual(cert.Info, expected.Info) {\n\t\t\t\t\tt.Fatalf(\"certificate info %d assertion failed, got %v, want %v\", i, cert.Info, expected.Info)\n\t\t\t\t}\n\t\t\t\tif runtime.GOOS == \"linux\" || runtime.GOOS == \"windows\" {\n\t\t\t\t\tif expected.SignatureValid != cert.SignatureValid {\n\t\t\t\t\t\tt.Fatalf(\"signature verification %d failed, cert %v, want %v\", i, cert.SignatureValid, expected.SignatureValid)\n\t\t\t\t\t}\n\t\t\t\t\tif expected.Verified != cert.Verified {\n\t\t\t\t\t\tt.Fatalf(\"certificate verification %d failed, cert %v, want %v\", i, cert.Verified, expected.Verified)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestAuthentihash(t *testing.T) {\n\n\ttests := []struct {\n\t\tin  string\n\t\tout string\n\t}{\n\t\t{getAbsoluteFilePath(\"test/putty.exe\"),\n\t\t\t\"8be7d65593b0fff2e8b29004640261b8a0d4fcc651a14cd0b8b702b7928f8ee0\"},\n\t\t{getAbsoluteFilePath(\"test/mscorlib.dll\"),\n\t\t\t\"a52bd7784efbf206dbda2db058f3928deaf15f6fedf2773affae56023e2f0edb\"},\n\t\t{getAbsoluteFilePath(\"test/liblzo2-2.dll\"),\n\t\t\t\"ae603480b92c7ea3feca164010d2594f9a5282f8b732ecaa0aca29f3225835f6\"},\n\t\t{getAbsoluteFilePath(\"test/kernel32.dll\"),\n\t\t\t\"595e4eb556587a1363ff297df9f354a377963ecac0bed19230992b9601426aae\"},\n\t\t{getAbsoluteFilePath(\"test/mfc40u.dll\"),\n\t\t\t\"5c8acdf9b2c7854c6b8e22e973d2fbae9c68fc22513d24c68c8e8010b1663e67\"},\n\t\t{getAbsoluteFilePath(\"test/000057fd78f66e64e15f5070364c824a8923b6216bd8bcf6368857fb9674c483\"),\n\t\t\t\"\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\thash := file.Authentihash()\n\t\t\tgot := fmt.Sprintf(\"%x\", hash)\n\t\t\tif string(got) != tt.out {\n\t\t\t\tt.Errorf(\"Authentihash(%s) got %v, want %v\", tt.in, got, tt.out)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "security_windows.go",
    "content": "//go:build windows\r\n// +build windows\r\n\r\npackage pe\r\n\r\nimport (\r\n\t\"os/exec\"\r\n\t\"syscall\"\r\n)\r\n\r\nfunc hideWindow(cmd *exec.Cmd) {\r\n\tcmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}\r\n}\r\n"
  },
  {
    "path": "staticcheck.conf",
    "content": "checks = [\"all\", \"-ST1000\", \"-U1000\", \"-SA1019\"]"
  },
  {
    "path": "symbol.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"strings\"\n)\n\nconst (\n\n\t// MaxDefaultSymbolsCount represents the default maximum number of COFF\n\t// symbols to parse. Some malware uses a fake huge NumberOfSymbols that\n\t// can cause an OOM exception.\n\t// Example: 0000e876c5b712b6b7b3ce97f757ddd918fb3dbdc5a3938e850716fbd841309f\n\tMaxDefaultCOFFSymbolsCount = 0x10000\n\n\t// MaxCOFFSymStrLength represents the maximum string length of a COFF symbol\n\t// to read.\n\tMaxCOFFSymStrLength = 0x50\n\n\t//\n\t// Type Representation\n\t//\n\n\t// ImageSymTypeNull indicates no type information or unknown base type.\n\t// Microsoft tools use this setting.\n\tImageSymTypeNull = 0\n\n\t// ImageSymTypeVoid indicates no type no valid type; used with void pointers and functions.\n\tImageSymTypeVoid = 1\n\n\t// ImageSymTypeChar indicates a character (signed byte).\n\tImageSymTypeChar = 2\n\n\t// ImageSymTypeShort indicates a 2-byte signed integer.\n\tImageSymTypeShort = 3\n\n\t// ImageSymTypeInt indicates a natural integer type (normally 4 bytes in\n\t// Windows).\n\tImageSymTypeInt = 4\n\n\t// ImageSymTypeLong indicates a 4-byte signed integer.\n\tImageSymTypeLong = 5\n\n\t// ImageSymTypeFloat indicates a 4-byte floating-point number.\n\tImageSymTypeFloat = 6\n\n\t// ImageSymTypeDouble indicates an 8-byte floating-point number.\n\tImageSymTypeDouble = 7\n\n\t// ImageSymTypeStruct indicates a structure.\n\tImageSymTypeStruct = 8\n\n\t// ImageSymTypeUnion indicates a union.\n\tImageSymTypeUnion = 9\n\n\t// ImageSymTypeEnum indicates an enumerated type.\n\tImageSymTypeEnum = 10\n\n\t// ImageSymTypeMoe A member of enumeration (a specific value).\n\tImageSymTypeMoe = 11\n\n\t// ImageSymTypeByte indicates a byte; unsigned 1-byte integer.\n\tImageSymTypeByte = 12\n\n\t// ImageSymTypeWord indicates a word; unsigned 2-byte integer.\n\tImageSymTypeWord = 13\n\n\t// ImageSymTypeUint indicates an unsigned integer of natural size\n\t// (normally, 4 bytes).\n\tImageSymTypeUint = 14\n\n\t// ImageSymTypeDword indicates an unsigned 4-byte integer.\n\tImageSymTypeDword = 15\n\n\t//\n\t// Storage Class\n\t//\n\n\t// ImageSymClassEndOfFunction indicates a special symbol that represents\n\t// the end of function, for debugging purposes.\n\tImageSymClassEndOfFunction = 0xff\n\n\t// ImageSymClassNull indicates no assigned storage class.\n\tImageSymClassNull = 0\n\n\t// ImageSymClassAutomatic indicates automatic (stack) variable. The Value\n\t// field specifies the stack frame offset.\n\tImageSymClassAutomatic = 1\n\n\t// ImageSymClassExternal indicates a value that Microsoft tools use for\n\t// external symbols. The Value field indicates the size if the section\n\t// number is IMAGE_SYM_UNDEFINED (0). If the section number is not zero,\n\t// then the Value field specifies the offset within the section.\n\tImageSymClassExternal = 2\n\n\t// ImageSymClassStatic indicates the offset of the symbol within the\n\t// section. If the Value field is zero, then the symbol represents a\n\t// section name.\n\tImageSymClassStatic = 3\n\n\t// ImageSymClassRegister indicates a register variable. The Value field\n\t// specifies the register number.\n\tImageSymClassRegister = 4\n\n\t// ImageSymClassExternalDef indicates a symbol that is defined externally.\n\tImageSymClassExternalDef = 5\n\n\t// ImageSymClassLabel indicates a code label that is defined within the\n\t// module. The Value field specifies the offset of the symbol within the\n\t// section.\n\tImageSymClassLabel = 6\n\n\t// ImageSymClassUndefinedLabel indicates a reference to a code label that\n\t// is not defined.\n\tImageSymClassUndefinedLabel = 7\n\n\t// ImageSymClassMemberOfStruct indicates the structure member. The Value\n\t// field specifies the n th member.\n\tImageSymClassMemberOfStruct = 8\n\n\t// ImageSymClassArgument indicates a formal argument (parameter) of a\n\t// function. The Value field specifies the n th argument.\n\tImageSymClassArgument = 9\n\n\t// ImageSymClassStructTag indicates the structure tag-name entry.\n\tImageSymClassStructTag = 10\n\n\t// ImageSymClassMemberOfUnion indicates a union member. The Value field\n\t// specifies the n th member.\n\tImageSymClassMemberOfUnion = 11\n\n\t// ImageSymClassUnionTag indicates the structure tag-name entry.\n\tImageSymClassUnionTag = 12\n\n\t// ImageSymClassTypeDefinition indicates a typedef entry.\n\tImageSymClassTypeDefinition = 13\n\n\t// ImageSymClassUndefinedStatic indicates a static data declaration.\n\tImageSymClassUndefinedStatic = 14\n\n\t// ImageSymClassEnumTag indicates an enumerated type tagname entry.\n\tImageSymClassEnumTag = 15\n\n\t// ImageSymClassMemberOfEnum indicates a member of an enumeration. The\n\t// Value field specifies the n th member.\n\tImageSymClassMemberOfEnum = 16\n\n\t// ImageSymClassRegisterParam indicates a register parameter.\n\tImageSymClassRegisterParam = 17\n\n\t// ImageSymClassBitField indicates a bit-field reference. The Value field\n\t// specifies the n th bit in the bit field.\n\tImageSymClassBitField = 18\n\n\t// ImageSymClassBlock indicates a .bb (beginning of block) or .eb (end of\n\t// block) record. The Value field is the relocatable address of the code\n\t// location.\n\tImageSymClassBlock = 100\n\n\t// ImageSymClassFunction indicates a value that Microsoft tools use for\n\t// symbol records that define the extent of a function: begin function (.bf\n\t// ), end function ( .ef ), and lines in function ( .lf ). For .lf\n\t// records, the Value field gives the number of source lines in the\n\t// function. For .ef records, the Value field gives the size of the\n\t// function code.\n\tImageSymClassFunction = 101\n\n\t// ImageSymClassEndOfStruct indicates an end-of-structure entry.\n\tImageSymClassEndOfStruct = 102\n\n\t// ImageSymClassFile indicates a value that Microsoft tools, as well as\n\t// traditional COFF format, use for the source-file symbol record. The\n\t// symbol is followed by auxiliary records that name the file.\n\tImageSymClassFile = 103\n\n\t// ImageSymClassSsection indicates a definition of a section (Microsoft\n\t// tools use STATIC storage class instead).\n\tImageSymClassSsection = 104\n\n\t// ImageSymClassWeakExternal indicates a weak external. For more\n\t// information, see Auxiliary Format 3: Weak Externals.\n\tImageSymClassWeakExternal = 24\n\n\t// ImageSymClassClrToken indicates a CLR token symbol. The name is an ASCII\n\t// string that consists of the hexadecimal value of the token. For more\n\t// information, see CLR Token Definition (Object Only).\n\tImageSymClassClrToken = 25\n\n\t//\n\t// Section Number Values.\n\t//\n\n\t// ImageSymUndefined indicates that the symbol record is not yet assigned a\n\t// section. A value of zero indicates that a reference to an external\n\t// symbol is defined elsewhere. A value of non-zero is a common symbol with\n\t// a size that is specified by the value.\n\tImageSymUndefined = 0\n\n\t// ImageSymAbsolute indicates that the symbol has an absolute\n\t// (non-relocatable) value and is not an address.\n\tImageSymAbsolute = -1\n\n\t// ImageSymDebug indicates that the symbol provides general type or\n\t// debugging information but does not correspond to a section. Microsoft\n\t// tools use this setting along with .file records (storage class FILE).\n\tImageSymDebug = -2\n)\n\nvar (\n\terrCOFFTableNotPresent = errors.New(\n\t\t\"PE image does not contains a COFF symbol table\")\n\terrNoCOFFStringInTable = errors.New(\n\t\t\"PE image got a PointerToSymbolTable but no string in the COFF string table\")\n\terrCOFFSymbolOutOfBounds = errors.New(\n\t\t\"COFF symbol offset out of bounds\")\n\terrCOFFSymbolsTooHigh = errors.New(\n\t\t\"COFF symbols count is absurdly high\")\n)\n\n// COFFSymbol represents an entry in the COFF symbol table, which it is an\n// array of records, each 18 bytes long. Each record is either a standard or\n// auxiliary symbol-table record. A standard record defines a symbol or name\n// and has the following format.\ntype COFFSymbol struct {\n\t// The name of the symbol, represented by a union of three structures. An\n\t// array of 8 bytes is used if the name is not more than 8 bytes long.\n\t// union {\n\t//    BYTE     ShortName[8];\n\t//    struct {\n\t//        DWORD   Short;     // if 0, use LongName\n\t//        DWORD   Long;      // offset into string table\n\t//    } Name;\n\t//    DWORD   LongName[2];    // PBYTE  [2]\n\t// } N;\n\tName [8]byte `json:\"name\"`\n\n\t// The value that is associated with the symbol. The interpretation of this\n\t// field depends on SectionNumber and StorageClass. A typical meaning is\n\t// the relocatable address.\n\tValue uint32 `json:\"value\"`\n\n\t// The signed integer that identifies the section, using a one-based index\n\t// into the section table. Some values have special meaning.\n\t// See \"Section Number Values.\"\n\tSectionNumber int16 `json:\"section_number\"`\n\n\t// A number that represents type. Microsoft tools set this field to\n\t// 0x20 (function) or 0x0 (not a function). For more information,\n\t// see Type Representation.\n\tType uint16 `json:\"type\"`\n\n\t// An enumerated value that represents storage class.\n\t// For more information, see Storage Class.\n\tStorageClass uint8 `json:\"storage_class\"`\n\n\t// The number of auxiliary symbol table entries that follow this record.\n\tNumberOfAuxSymbols uint8 `json:\"number_of_aux_symbols\"`\n}\n\n// COFF holds properties related to the COFF format.\ntype COFF struct {\n\tSymbolTable       []COFFSymbol `json:\"symbol_table\"`\n\tStringTable       []string     `json:\"string_table\"`\n\tStringTableOffset uint32       `json:\"string_table_offset\"`\n\t// Map the symbol offset => symbol name.\n\tStringTableM map[uint32]string `json:\"-\"`\n}\n\n// ParseCOFFSymbolTable parses the COFF symbol table. The symbol table is\n// inherited from the traditional COFF format. It is distinct from Microsoft\n// Visual C++ debug information. A file can contain both a COFF symbol table\n// and Visual C++ debug information, and the two are kept separate. Some\n// Microsoft tools use the symbol table for limited but important purposes,\n// such as communicating COMDAT information to the linker. Section names and\n// file names, as well as code and data symbols, are listed in the symbol table.\nfunc (pe *File) ParseCOFFSymbolTable() error {\n\tpointerToSymbolTable := pe.NtHeader.FileHeader.PointerToSymbolTable\n\tif pointerToSymbolTable == 0 {\n\t\treturn errCOFFTableNotPresent\n\t}\n\n\tsymCount := pe.NtHeader.FileHeader.NumberOfSymbols\n\tif symCount == 0 {\n\t\treturn nil\n\t}\n\tif symCount > pe.opts.MaxCOFFSymbolsCount {\n\t\tpe.addAnomaly(AnoCOFFSymbolsCount)\n\t\treturn errCOFFSymbolsTooHigh\n\t}\n\n\t// The location of the symbol table is indicated in the COFF header.\n\toffset := pe.NtHeader.FileHeader.PointerToSymbolTable\n\n\t// The symbol table is an array of records, each 18 bytes long.\n\tsize := uint32(binary.Size(COFFSymbol{}))\n\tsymbols := make([]COFFSymbol, symCount)\n\n\t// Each record is either a standard or auxiliary symbol-table record.\n\t// A standard record defines a symbol or name and has the COFFSymbol STRUCT format.\n\tfor i := uint32(0); i < symCount; i++ {\n\t\terr := pe.structUnpack(&symbols[i], offset, size)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\toffset += size\n\t}\n\n\tpe.COFF.SymbolTable = symbols\n\n\t// Get the COFF string table.\n\tpe.COFFStringTable()\n\n\tpe.HasCOFF = true\n\treturn nil\n}\n\n// COFFStringTable retrieves the list of strings in the COFF string table if\n// any.\nfunc (pe *File) COFFStringTable() error {\n\tm := make(map[uint32]string)\n\tpointerToSymbolTable := pe.NtHeader.FileHeader.PointerToSymbolTable\n\tif pointerToSymbolTable == 0 {\n\t\treturn errCOFFTableNotPresent\n\t}\n\n\tsymCount := pe.NtHeader.FileHeader.NumberOfSymbols\n\tif symCount == 0 {\n\t\treturn nil\n\t}\n\tif symCount > pe.opts.MaxCOFFSymbolsCount {\n\t\tpe.addAnomaly(AnoCOFFSymbolsCount)\n\t\treturn errCOFFSymbolsTooHigh\n\t}\n\n\t// COFF String Table immediately following the COFF symbol table. The\n\t// position of this table is found by taking the symbol table address in\n\t// the COFF header and adding the number of symbols multiplied by the size\n\t// of a symbol.\n\tsize := uint32(binary.Size(COFFSymbol{}))\n\toffset := pointerToSymbolTable + (size * symCount)\n\n\t// At the beginning of the COFF string table are 4 bytes that contain the\n\t// total size (in bytes) of the rest of the string table. This size\n\t// includes the size field itself, so that the value in this location would\n\t// be 4 if no strings were present.\n\tpe.COFF.StringTableOffset = offset\n\tstrTableSize, err := pe.ReadUint32(offset)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif strTableSize <= 4 {\n\t\treturn errNoCOFFStringInTable\n\t}\n\toffset += 4\n\n\t// Following the size are null-terminated strings that are pointed to by\n\t// symbols in the COFF symbol table. We create a map to map offset to\n\t// string.\n\tend := offset + strTableSize - 4\n\tfor offset < end {\n\t\tlen, str := pe.readASCIIStringAtOffset(offset, MaxCOFFSymStrLength)\n\t\tif len == 0 {\n\t\t\tbreak\n\t\t}\n\t\tm[offset] = str\n\t\toffset += len + 1\n\t\tpe.COFF.StringTable = append(pe.COFF.StringTable, str)\n\t}\n\n\tpe.COFF.StringTableM = m\n\treturn nil\n}\n\n// String returns the representation of the symbol name.\nfunc (symbol *COFFSymbol) String(pe *File) (string, error) {\n\tvar short, long uint32\n\n\t// The ShortName field in a symbol table consists of 8 bytes\n\t// that contain the name itself, if it is not more than 8\n\t// bytes long, or the ShortName field gives an offset into\n\t// the string table.\n\thighDw := bytes.NewBuffer(symbol.Name[4:])\n\tlowDw := bytes.NewBuffer(symbol.Name[:4])\n\terrl := binary.Read(lowDw, binary.LittleEndian, &short)\n\terrh := binary.Read(highDw, binary.LittleEndian, &long)\n\tif errl != nil || errh != nil {\n\t\treturn \"\", errCOFFSymbolOutOfBounds\n\t}\n\n\t// To determine whether the name itself or an offset is given,\n\t// test the first 4 bytes for equality to zero.\n\tif short != 0 {\n\t\tname := strings.Replace(string(symbol.Name[:]), \"\\x00\", \"\", -1)\n\t\treturn name, nil\n\t}\n\n\t// Long name offset to the string table.\n\tstrOff := pe.COFF.StringTableOffset + long\n\tname := pe.COFF.StringTableM[strOff]\n\treturn name, nil\n}\n\n// SectionNumberName returns the name of the section corresponding to a section\n// symbol number if any.\nfunc (symbol *COFFSymbol) SectionNumberName(pe *File) string {\n\n\t// Normally, the Section Value field in a symbol table entry is a one-based\n\t// index into the section table. However, this field is a signed integer\n\t// and can take negative values. The following values, less than one, have\n\t// special meanings.\n\tif symbol.SectionNumber > 0 && symbol.SectionNumber < int16(len(pe.Sections)) {\n\t\treturn pe.Sections[symbol.SectionNumber-1].String()\n\t}\n\n\tswitch symbol.SectionNumber {\n\tcase ImageSymUndefined:\n\t\treturn \"Undefined\"\n\tcase ImageSymAbsolute:\n\t\treturn \"Absolute\"\n\tcase ImageSymDebug:\n\t\treturn \"Debug\"\n\t}\n\n\treturn \"?\"\n}\n\n// PrettyCOFFTypeRepresentation returns the string representation of the `Type`\n// field of a COFF table entry.\nfunc (pe *File) PrettyCOFFTypeRepresentation(k uint16) string {\n\tcoffSymTypeMap := map[uint16]string{\n\t\tImageSymTypeNull:   \"Null\",\n\t\tImageSymTypeVoid:   \"Void\",\n\t\tImageSymTypeChar:   \"Char\",\n\t\tImageSymTypeShort:  \"Short\",\n\t\tImageSymTypeInt:    \"Int\",\n\t\tImageSymTypeLong:   \"Long\",\n\t\tImageSymTypeFloat:  \"Float\",\n\t\tImageSymTypeDouble: \"Double\",\n\t\tImageSymTypeStruct: \"Struct\",\n\t\tImageSymTypeUnion:  \"Union\",\n\t\tImageSymTypeEnum:   \"Enum\",\n\t\tImageSymTypeMoe:    \"Moe\",\n\t\tImageSymTypeByte:   \"Byte\",\n\t\tImageSymTypeWord:   \"Word\",\n\t\tImageSymTypeUint:   \"Uint\",\n\t\tImageSymTypeDword:  \"Dword\",\n\t}\n\n\tif value, ok := coffSymTypeMap[k]; ok {\n\t\treturn value\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "symbol_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport \"testing\"\n\ntype TestCOFFSymbol struct {\n\terrTooManySymbols error\n\tsymbolsCount      int\n\tsymbolIdx         int\n\tsymbol            COFFSymbol\n\tstringTableOffset uint32\n\tsymbolName        string\n\tsectionNumberName string\n\tsymbolTypeString  string\n}\n\nvar symbolTests = []struct {\n\tin  string\n\tout TestCOFFSymbol\n}{\n\t{\n\t\tgetAbsoluteFilePath(\"test/liblzo2-2.dll\"),\n\t\tTestCOFFSymbol{\n\t\t\terrTooManySymbols: nil,\n\t\t\tsymbolsCount:      50,\n\t\t\tsymbolIdx:         0,\n\t\t\tsymbol: COFFSymbol{\n\t\t\t\tName:               [8]byte{0, 0, 0, 0, 4, 0, 0, 0},\n\t\t\t\tValue:              0x2ac,\n\t\t\t\tSectionNumber:      8,\n\t\t\t\tType:               0x0,\n\t\t\t\tStorageClass:       0x2,\n\t\t\t\tNumberOfAuxSymbols: 0x0,\n\t\t\t},\n\t\t\tstringTableOffset: 0x35184,\n\t\t\tsymbolName:        \"__imp_abort\",\n\t\t\tsectionNumberName: \".idata\",\n\t\t\tsymbolTypeString:  \"Null\",\n\t\t},\n\t},\n\n\t{\n\t\tgetAbsoluteFilePath(\n\t\t\t\"test/0103daa751660333b7ae5f098795df58f07e3031563e042d2eb415bffa71fe7a\",\n\t\t),\n\t\tTestCOFFSymbol{\n\t\t\terrTooManySymbols: nil,\n\t\t\tsymbolsCount:      346,\n\t\t\tsymbolIdx:         3,\n\t\t\tsymbol: COFFSymbol{\n\t\t\t\tName:               [8]byte{0, 0, 0, 0, 4, 0, 0, 0},\n\t\t\t\tValue:              0x2ac,\n\t\t\t\tSectionNumber:      8,\n\t\t\t\tType:               0x0,\n\t\t\t\tStorageClass:       0x2,\n\t\t\t\tNumberOfAuxSymbols: 0x0,\n\t\t\t},\n\t\t\tstringTableOffset: 0x1b054,\n\t\t\tsymbolName:        \"___mingw_CRTStartup\",\n\t\t\tsectionNumberName: \".text\",\n\t\t\tsymbolTypeString:  \"\",\n\t\t},\n\t},\n\n\t{\n\t\tgetAbsoluteFilePath(\n\t\t\t\"test/0000e876c5b712b6b7b3ce97f757ddd918fb3dbdc5a3938e850716fbd841309f\",\n\t\t),\n\t\tTestCOFFSymbol{\n\t\t\terrTooManySymbols: errCOFFSymbolsTooHigh,\n\t\t},\n\t},\n}\n\nfunc TestParseCOFFSymbolTable(t *testing.T) {\n\tfor _, tt := range symbolTests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tops := Options{Fast: true}\n\t\t\tfile, err := New(tt.in, &ops)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\terr = file.Parse()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\terr = file.ParseCOFFSymbolTable()\n\t\t\tif err != tt.out.errTooManySymbols {\n\t\t\t\tt.Errorf(\n\t\t\t\t\t\"errTooManySymbols assertion failed, reason: %v\",\n\t\t\t\t\ttt.out.errTooManySymbols,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\t// exit early when err is errCOFFSymbolsTooHigh.\n\t\t\tif err == errCOFFSymbolsTooHigh {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif len(file.COFF.SymbolTable) != tt.out.symbolsCount {\n\t\t\t\tt.Errorf(\n\t\t\t\t\t\"symbolsCount assertion failed, want: %d, got: %d\",\n\t\t\t\t\ttt.out.symbolsCount,\n\t\t\t\t\tlen(file.COFF.SymbolTable),\n\t\t\t\t)\n\t\t\t}\n\t\t\tif file.COFF.StringTableOffset != tt.out.stringTableOffset {\n\t\t\t\tt.Errorf(\n\t\t\t\t\t\"stringTableOffset assertion failed, want: %d, got: %d\",\n\t\t\t\t\ttt.out.stringTableOffset,\n\t\t\t\t\tfile.COFF.StringTableOffset,\n\t\t\t\t)\n\t\t\t}\n\t\t\tif !stringInSlice(tt.out.symbolName, file.COFF.StringTable) {\n\t\t\t\tt.Errorf(\n\t\t\t\t\t\"symbolName assertion failed, want: %s, got: %v\",\n\t\t\t\t\ttt.out.symbolName,\n\t\t\t\t\tfile.COFF.StringTable,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tcoffSymbol := file.COFF.SymbolTable[tt.out.symbolIdx]\n\t\t\tsymbolNameStr, err := coffSymbol.String(file)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"COFFSymbol.String() failed with: %v\", err)\n\t\t\t}\n\t\t\tif symbolNameStr != tt.out.symbolName {\n\t\t\t\tt.Errorf(\n\t\t\t\t\t\"symbol name to string failed, want: %s, got: %s\",\n\t\t\t\t\ttt.out.symbolName,\n\t\t\t\t\tsymbolNameStr,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tsecNumName := coffSymbol.SectionNumberName(file)\n\t\t\tif secNumName != tt.out.sectionNumberName {\n\t\t\t\tt.Errorf(\n\t\t\t\t\t\"SectionNumberName assertion failed, want: %s, got: %s\",\n\t\t\t\t\ttt.out.sectionNumberName,\n\t\t\t\t\tsecNumName,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\ttypeString := file.PrettyCOFFTypeRepresentation(coffSymbol.Type)\n\t\t\tif typeString != tt.out.symbolTypeString {\n\t\t\t\tt.Errorf(\n\t\t\t\t\t\"PrettyCOFFTypeRepresentation assertion failed, want: %s, got: %s\",\n\t\t\t\t\ttt.out.symbolTypeString,\n\t\t\t\t\ttypeString,\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "tls.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"encoding/binary\"\n)\n\n// TLSDirectoryCharacteristicsType represents the type of a TLS directory\n// Characteristics.\ntype TLSDirectoryCharacteristicsType uint32\n\n// TLSDirectory represents tls directory information with callback entries.\ntype TLSDirectory struct {\n\n\t// of type *IMAGE_TLS_DIRECTORY32 or *IMAGE_TLS_DIRECTORY64 structure.\n\tStruct interface{} `json:\"struct\"`\n\n\t// of type []uint32 or []uint64.\n\tCallbacks interface{} `json:\"callbacks\"`\n}\n\n// ImageTLSDirectory32 represents the IMAGE_TLS_DIRECTORY32 structure.\n// It Points to the Thread Local Storage initialization section.\ntype ImageTLSDirectory32 struct {\n\n\t// The starting address of the TLS template. The template is a block of data\n\t// that is used to initialize TLS data.\n\tStartAddressOfRawData uint32 `json:\"start_address_of_raw_data\"`\n\n\t// The address of the last byte of the TLS, except for the zero fill.\n\t// As with the Raw Data Start VA field, this is a VA, not an RVA.\n\tEndAddressOfRawData uint32 `json:\"end_address_of_raw_data\"`\n\n\t// The location to receive the TLS index, which the loader assigns. This\n\t// location is in an ordinary data section, so it can be given a symbolic\n\t// name that is accessible to the program.\n\tAddressOfIndex uint32 `json:\"address_of_index\"`\n\n\t// The pointer to an array of TLS callback functions. The array is\n\t// null-terminated, so if no callback function is supported, this field\n\t// points to 4 bytes set to zero.\n\tAddressOfCallBacks uint32 `json:\"address_of_callbacks\"`\n\n\t// The size in bytes of the template, beyond the initialized data delimited\n\t// by the Raw Data Start VA and Raw Data End VA fields. The total template\n\t// size should be the same as the total size of TLS data in the image file.\n\t// The zero fill is the amount of data that comes after the initialized\n\t// nonzero data.\n\tSizeOfZeroFill uint32 `json:\"size_of_zero_fill\"`\n\n\t// The four bits [23:20] describe alignment info. Possible values are those\n\t// defined as IMAGE_SCN_ALIGN_*, which are also used to describe alignment\n\t// of section in object files. The other 28 bits are reserved for future use.\n\tCharacteristics TLSDirectoryCharacteristicsType `json:\"characteristics\"`\n}\n\n// ImageTLSDirectory64 represents the IMAGE_TLS_DIRECTORY64 structure.\n// It Points to the Thread Local Storage initialization section.\ntype ImageTLSDirectory64 struct {\n\t// The starting address of the TLS template. The template is a block of data\n\t// that is used to initialize TLS data.\n\tStartAddressOfRawData uint64 `json:\"start_address_of_raw_data\"`\n\n\t// The address of the last byte of the TLS, except for the zero fill. As\n\t// with the Raw Data Start VA field, this is a VA, not an RVA.\n\tEndAddressOfRawData uint64 `json:\"end_address_of_raw_data\"`\n\n\t// The location to receive the TLS index, which the loader assigns. This\n\t// location is in an ordinary data section, so it can be given a symbolic\n\t// name that is accessible to the program.\n\tAddressOfIndex uint64 `json:\"address_of_index\"`\n\n\t// The pointer to an array of TLS callback functions. The array is\n\t// null-terminated, so if no callback function is supported, this field\n\t// points to 4 bytes set to zero.\n\tAddressOfCallBacks uint64 `json:\"address_of_callbacks\"`\n\n\t// The size in bytes of the template, beyond the initialized data delimited\n\t// by the Raw Data Start VA and Raw Data End VA fields. The total template\n\t// size should be the same as the total size of TLS data in the image file.\n\t// The zero fill is the amount of data that comes after the initialized\n\t// nonzero data.\n\tSizeOfZeroFill uint32 `json:\"size_of_zero_fill\"`\n\n\t// The four bits [23:20] describe alignment info. Possible values are those\n\t// defined as IMAGE_SCN_ALIGN_*, which are also used to describe alignment\n\t// of section in object files. The other 28 bits are reserved for future use.\n\tCharacteristics TLSDirectoryCharacteristicsType `json:\"characteristics\"`\n}\n\n// TLS provides direct PE and COFF support for static thread local storage (TLS).\n// TLS is a special storage class that Windows supports in which a data object\n// is not an automatic (stack) variable, yet is local to each individual thread\n// that runs the code. Thus, each thread can maintain a different value for a\n// variable declared by using TLS.\nfunc (pe *File) parseTLSDirectory(rva, size uint32) error {\n\n\ttls := TLSDirectory{}\n\n\tif pe.Is64 {\n\t\ttlsDir := ImageTLSDirectory64{}\n\t\ttlsSize := uint32(binary.Size(tlsDir))\n\t\tfileOffset := pe.GetOffsetFromRva(rva)\n\t\terr := pe.structUnpack(&tlsDir, fileOffset, tlsSize)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttls.Struct = tlsDir\n\n\t\tif tlsDir.AddressOfCallBacks != 0 {\n\t\t\tcallbacks := make([]uint64, 0)\n\t\t\trvaAddressOfCallBacks := uint32(tlsDir.AddressOfCallBacks -\n\t\t\t\tpe.NtHeader.OptionalHeader.(ImageOptionalHeader64).ImageBase)\n\t\t\toffset := pe.GetOffsetFromRva(rvaAddressOfCallBacks)\n\t\t\tfor {\n\t\t\t\tc, err := pe.ReadUint64(offset)\n\t\t\t\tif err != nil || c == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcallbacks = append(callbacks, c)\n\t\t\t\toffset += 8\n\t\t\t}\n\t\t\ttls.Callbacks = callbacks\n\t\t}\n\t} else {\n\t\ttlsDir := ImageTLSDirectory32{}\n\t\ttlsSize := uint32(binary.Size(tlsDir))\n\t\tfileOffset := pe.GetOffsetFromRva(rva)\n\t\terr := pe.structUnpack(&tlsDir, fileOffset, tlsSize)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttls.Struct = tlsDir\n\n\t\t// 94a9dc17d47b03f6fb01cb639e25503b37761b452e7c07ec6b6c2280635f1df9\n\t\t// Callbacks may be empty.\n\t\tif tlsDir.AddressOfCallBacks != 0 {\n\t\t\tcallbacks := make([]uint32, 0)\n\t\t\trvaAddressOfCallBacks := tlsDir.AddressOfCallBacks -\n\t\t\t\tpe.NtHeader.OptionalHeader.(ImageOptionalHeader32).ImageBase\n\t\t\toffset := pe.GetOffsetFromRva(rvaAddressOfCallBacks)\n\t\t\tfor {\n\t\t\t\tc, err := pe.ReadUint32(offset)\n\t\t\t\tif err != nil || c == 0 {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcallbacks = append(callbacks, c)\n\t\t\t\toffset += 4\n\t\t\t}\n\t\t\ttls.Callbacks = callbacks\n\t\t}\n\t}\n\n\tpe.TLS = tls\n\tpe.HasTLS = true\n\treturn nil\n}\n\n// String returns the string representations of the `Characteristics` field of\n// TLS directory.\nfunc (characteristics TLSDirectoryCharacteristicsType) String() string {\n\n\tm := map[TLSDirectoryCharacteristicsType]string{\n\t\tImageSectionAlign1Bytes:    \"Align 1-Byte\",\n\t\tImageSectionAlign2Bytes:    \"Align 2-Bytes\",\n\t\tImageSectionAlign4Bytes:    \"Align 4-Bytes\",\n\t\tImageSectionAlign8Bytes:    \"Align 8-Bytes\",\n\t\tImageSectionAlign16Bytes:   \"Align 16-Bytes\",\n\t\tImageSectionAlign32Bytes:   \"Align 32-Bytes\",\n\t\tImageSectionAlign64Bytes:   \"Align 64-Bytes\",\n\t\tImageSectionAlign128Bytes:  \"Align 128-Bytes\",\n\t\tImageSectionAlign256Bytes:  \"Align 265-Bytes\",\n\t\tImageSectionAlign512Bytes:  \"Align 512-Bytes\",\n\t\tImageSectionAlign1024Bytes: \"Align 1024-Bytes\",\n\t\tImageSectionAlign2048Bytes: \"Align 2048-Bytes\",\n\t\tImageSectionAlign4096Bytes: \"Align 4096-Bytes\",\n\t\tImageSectionAlign8192Bytes: \"Align 8192-Bytes\",\n\t}\n\n\tv, ok := m[characteristics]\n\tif ok {\n\t\treturn v\n\t}\n\n\treturn \"?\"\n}\n"
  },
  {
    "path": "tls_test.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\r\n// Use of this source code is governed by Apache v2 license\r\n// license that can be found in the LICENSE file.\r\n\r\npackage pe\r\n\r\nimport (\r\n\t\"reflect\"\r\n\t\"testing\"\r\n)\r\n\r\nfunc TestParseTLSDirectory(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  string\r\n\t\tout TLSDirectory\r\n\t}{\r\n\t\t{\r\n\t\t\tgetAbsoluteFilePath(\"test/liblzo2-2.dll\"),\r\n\t\t\tTLSDirectory{\r\n\t\t\t\tStruct: ImageTLSDirectory64{\r\n\t\t\t\t\tStartAddressOfRawData: 0x6CBBB000,\r\n\t\t\t\t\tEndAddressOfRawData:   0x6CBBB060,\r\n\t\t\t\t\tAddressOfIndex:        0x6CBB75AC,\r\n\t\t\t\t\tAddressOfCallBacks:    0x6CBBA030,\r\n\t\t\t\t},\r\n\t\t\t\tCallbacks: []uint64{0x6cbae7e0, 0x6cbae7b0},\r\n\t\t\t},\r\n\t\t},\r\n\t\t{\r\n\t\t\tgetAbsoluteFilePath(\"test/3a081c7fe475ec68ed155c76d30cfddc4d41f7a09169810682d1c75421e98eaa\"),\r\n\t\t\tTLSDirectory{\r\n\t\t\t\tStruct: ImageTLSDirectory32{\r\n\t\t\t\t\tStartAddressOfRawData: 0x004157B8,\r\n\t\t\t\t\tEndAddressOfRawData:   0x004157B9,\r\n\t\t\t\t\tAddressOfIndex:        0x0042F8DC,\r\n\t\t\t\t\tAddressOfCallBacks:    0x0040E3AC,\r\n\t\t\t\t\tCharacteristics:       0x00100000,\r\n\t\t\t\t},\r\n\t\t\t\tCallbacks: []uint32{0x40A5A0},\r\n\t\t\t},\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.in, func(t *testing.T) {\r\n\t\t\tops := Options{Fast: true}\r\n\t\t\tfile, err := New(tt.in, &ops)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\terr = file.Parse()\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"Parse(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\r\n\t\t\tvar va, size uint32\r\n\t\t\tswitch file.Is64 {\r\n\t\t\tcase true:\r\n\t\t\t\toh64 := file.NtHeader.OptionalHeader.(ImageOptionalHeader64)\r\n\t\t\t\tdirEntry := oh64.DataDirectory[ImageDirectoryEntryTLS]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\tcase false:\r\n\t\t\t\toh32 := file.NtHeader.OptionalHeader.(ImageOptionalHeader32)\r\n\t\t\t\tdirEntry := oh32.DataDirectory[ImageDirectoryEntryTLS]\r\n\t\t\t\tva = dirEntry.VirtualAddress\r\n\t\t\t\tsize = dirEntry.Size\r\n\t\t\t}\r\n\r\n\t\t\terr = file.parseTLSDirectory(va, size)\r\n\t\t\tif err != nil {\r\n\t\t\t\tt.Fatalf(\"parseRelocDirectory(%s) failed, reason: %v\", tt.in, err)\r\n\t\t\t}\r\n\t\t\ttls := file.TLS\r\n\t\t\tif !reflect.DeepEqual(tls, tt.out) {\r\n\t\t\t\tt.Fatalf(\"TLS directory assertion failed, got %v, want %v\", tls.Struct,\r\n\t\t\t\t\ttt.out)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n\r\nfunc TestTLSDirectoryCharacteristics(t *testing.T) {\r\n\r\n\ttests := []struct {\r\n\t\tin  TLSDirectoryCharacteristicsType\r\n\t\tout string\r\n\t}{\r\n\t\t{\r\n\r\n\t\t\tTLSDirectoryCharacteristicsType(0x00100000),\r\n\t\t\t\"Align 1-Byte\",\r\n\t\t},\r\n\t\t{\r\n\t\t\t0xff,\r\n\t\t\t\"?\",\r\n\t\t},\r\n\t}\r\n\r\n\tfor _, tt := range tests {\r\n\t\tt.Run(tt.out, func(t *testing.T) {\r\n\r\n\t\t\tTLSDirectoryCharacteristics := tt.in.String()\r\n\t\t\tif TLSDirectoryCharacteristics != tt.out {\r\n\t\t\t\tt.Fatalf(\"TLS directory characteristics string assertion failed, got %v, want %v\",\r\n\t\t\t\t\tTLSDirectoryCharacteristics, tt.out)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n}\r\n"
  },
  {
    "path": "version.go",
    "content": "// Copyright 2018 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n)\n\nconst (\n\t// VersionResourceType identifies the version resource type in the resource directory\n\tVersionResourceType = 16\n\n\t// VsVersionInfoString is the UTF16-encoded string that identifies the VS_VERSION_INFO block\n\tVsVersionInfoString = \"VS_VERSION_INFO\"\n\n\t// VsFileInfoSignature is the file info signature\n\tVsFileInfoSignature uint32 = 0xFEEF04BD\n\n\t// StringFileInfoString is the UTF16-encoded string that identifies the StringFileInfo block\n\tStringFileInfoString = \"StringFileInfo\"\n\t// VarFileInfoString is the UTF16-encoded string that identifies the VarFileInfoString block\n\tVarFileInfoString = \"VarFileInfo\"\n\n\t// VsVersionInfoStringLength specifies the length of the VS_VERSION_INFO structure\n\tVsVersionInfoStringLength uint32 = 6\n\t// StringFileInfoLength specifies length of the StringFileInfo structure\n\tStringFileInfoLength uint32 = 6\n\t// StringTableLength specifies the length of the StringTable structure\n\tStringTableLength uint32 = 6\n\t// StringLength specifies the length of the String structure\n\tStringLength uint32 = 6\n\t// LangIDLength specifies the length of the language identifier string.\n\t// It is represented as 8-digit hexadecimal number stored as a Unicode string.\n\tLangIDLength uint32 = 8*2 + 1\n)\n\n// VsVersionInfo represents the organization of data in\n// a file-version resource. It is the root structure that\n// contains all other file-version information structures.\ntype VsVersionInfo struct {\n\t// Length is the length, in bytes, of the VS_VERSIONINFO structure.\n\t// This length does not include any padding that aligns any\n\t// subsequent version resource data on a 32-bit boundary.\n\tLength uint16 `json:\"length\"`\n\t// ValueLength is the length, in bytes, of arbitrary data associated\n\t// with the VS_VERSIONINFO structure.\n\t// This value is zero if there is no any data associated with the\n\t// current version structure.\n\tValueLength uint16 `json:\"value_length\"`\n\t// Type represents as many zero words as necessary to align the StringFileInfo\n\t// and VarFileInfo structures on a 32-bit boundary. These bytes are not included\n\t// in ValueLength.\n\tType uint16 `json:\"type\"`\n}\n\nfunc (pe *File) parseVersionInfo(e ResourceDirectoryEntry) (*VsVersionInfo, error) {\n\toffset := pe.GetOffsetFromRva(e.Data.Struct.OffsetToData)\n\tb, err := pe.ReadBytesAtOffset(offset, e.Data.Struct.Size)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar v VsVersionInfo\n\tif err := binary.Read(bytes.NewBuffer(b), binary.LittleEndian, &v); err != nil {\n\t\treturn nil, err\n\t}\n\tb, err = pe.ReadBytesAtOffset(offset+VsVersionInfoStringLength, uint32(v.ValueLength))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvsVersionString, err := DecodeUTF16String(b)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif vsVersionString != VsVersionInfoString {\n\t\treturn nil, fmt.Errorf(\"invalid VS_VERSION_INFO block. %s\", vsVersionString)\n\t}\n\treturn &v, nil\n}\n\n// VsFixedFileInfo contains version information for a file.\n// This information is language and code page independent.\ntype VsFixedFileInfo struct {\n\t// Signature contains the value 0xFEEF04BD. This is used\n\t// with the `key` member of the VS_VERSIONINFO structure\n\t// when searching a file for the VS_FIXEDFILEINFO structure.\n\tSignature uint32 `json:\"signature\"`\n\t// StructVer is the binary version number of this structure.\n\t// The high-order word of this member contains the major version\n\t// number, and the low-order word contains the minor version number.\n\tStructVer uint32 `json:\"struct_ver\"`\n\t// FileVersionMS denotes the most significant 32 bits of the file's\n\t// binary version number.\n\tFileVersionMS uint32 `json:\"file_version_ms\"`\n\t// FileVersionLS denotes the least significant 32 bits of the file's\n\t// binary version number.\n\tFileVersionLS uint32 `json:\"file_version_ls\"`\n\t// ProductVersionMS represents the most significant 32 bits of the\n\t// binary version number of the product with which this file was distributed.\n\tProductVersionMS uint32 `json:\"product_version_ms\"`\n\t// ProductVersionLS represents the most significant 32 bits of the\n\t// binary version number of the product with which this file was distributed.\n\tProductVersionLS uint32 `json:\"product_version_ls\"`\n\t// FileFlagMask contains a bitmask that specifies the valid bits in FileFlags.\n\t// A bit is valid only if it was defined when the file was created.\n\tFileFlagMask uint32 `json:\"file_flag_mask\"`\n\t// FileFlags contains a bitmask that specifies the Boolean attributes of the file.\n\t// For example, the file contains debugging information or is compiled with debugging\n\t// features enabled if FileFlags is equal to 0x00000001L (VS_FF_DEBUG).\n\tFileFlags uint32 `json:\"file_flags\"`\n\t// FileOS represents the operating system for which this file was designed.\n\tFileOS uint32 `json:\"file_os\"`\n\t// FileType describes the general type of file.\n\tFileType uint32 `json:\"file_type\"`\n\t// FileSubtype specifies the function of the file. The possible values depend on the value of FileType.\n\tFileSubtype uint32 `json:\"file_subtype\"`\n\t// FileDateMS are the most significant 32 bits of the file's 64-bit binary creation date and time stamp.\n\tFileDateMS uint32 `json:\"file_date_ms\"`\n\t// FileDateLS are the least significant 32 bits of the file's 64-bit binary creation date and time stamp.\n\tFileDateLS uint32 `json:\"file_date_ls\"`\n}\n\n// Size returns the size of this structure in bytes.\nfunc (f *VsFixedFileInfo) Size() uint32 { return uint32(binary.Size(f)) }\n\nfunc (f *VsFixedFileInfo) GetStringFileInfoOffset(e ResourceDirectoryEntry) uint32 {\n\treturn alignDword(VsVersionInfoStringLength+uint32(2*len(VsVersionInfoString)+1)+f.Size(), e.Data.Struct.OffsetToData)\n}\n\nfunc (f *VsFixedFileInfo) GetOffset(e ResourceDirectoryEntry, pe *File) uint32 {\n\toffset := pe.GetOffsetFromRva(e.Data.Struct.OffsetToData) + VsVersionInfoStringLength\n\toffset += uint32(2*len(VsVersionInfoString)) + 1\n\treturn alignDword(offset, e.Data.Struct.OffsetToData)\n}\n\nfunc (pe *File) parseFixedFileInfo(e ResourceDirectoryEntry) (*VsFixedFileInfo, error) {\n\tvar f VsFixedFileInfo\n\toffset := f.GetOffset(e, pe)\n\tb, err := pe.ReadBytesAtOffset(offset, f.Size())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := binary.Read(bytes.NewBuffer(b), binary.LittleEndian, &f); err != nil {\n\t\treturn nil, err\n\t}\n\tif f.Signature != VsFileInfoSignature {\n\t\treturn nil, fmt.Errorf(\"invalid file info signature %d\", f.Signature)\n\t}\n\treturn &f, nil\n}\n\n// StringFileInfo represents the organization of data in a\n// file-version resource. It contains version information\n// that can be displayed for a particular language and code page.\ntype StringFileInfo struct {\n\tLength      uint16\n\tValueLength uint16\n\tType        uint16\n}\n\nfunc (s *StringFileInfo) GetStringTableOffset(offset uint32) uint32 {\n\treturn offset + StringFileInfoLength + uint32(2*len(StringFileInfoString)) + 1\n}\n\nfunc (s *StringFileInfo) GetOffset(rva uint32, e ResourceDirectoryEntry, pe *File) uint32 {\n\toffset := pe.GetOffsetFromRva(e.Data.Struct.OffsetToData) + rva\n\treturn alignDword(offset, e.Data.Struct.OffsetToData)\n}\n\nfunc (pe *File) parseStringFileInfo(rva uint32, e ResourceDirectoryEntry) (*StringFileInfo, string, error) {\n\tvar s StringFileInfo\n\toffset := s.GetOffset(rva, e, pe)\n\tb, err := pe.ReadBytesAtOffset(offset, StringFileInfoLength)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tif err := binary.Read(bytes.NewBuffer(b), binary.LittleEndian, &s); err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tb, err = pe.ReadBytesAtOffset(offset+StringFileInfoLength, uint32(len(StringFileInfoString)*2)+1)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tstr, err := DecodeUTF16String(b)\n\treturn &s, str, err\n}\n\n// StringTable represents the organization of data in a\n// file-version resource. It contains language and code\n// page formatting information for the version strings\ntype StringTable struct {\n\tLength      uint16\n\tValueLength uint16\n\tType        uint16\n}\n\nfunc (s *StringTable) GetStringOffset(offset uint32, e ResourceDirectoryEntry) uint32 {\n\treturn alignDword(offset+StringTableLength+LangIDLength, e.Data.Struct.OffsetToData)\n}\n\nfunc (s *StringTable) GetOffset(rva uint32, e ResourceDirectoryEntry, pe *File) uint32 {\n\toffset := pe.GetOffsetFromRva(e.Data.Struct.OffsetToData) + rva\n\treturn alignDword(offset, e.Data.Struct.OffsetToData)\n}\n\nfunc (pe *File) parseStringTable(rva uint32, e ResourceDirectoryEntry) (*StringTable, error) {\n\tvar s StringTable\n\toffset := s.GetOffset(rva, e, pe)\n\tb, err := pe.ReadBytesAtOffset(offset, StringTableLength)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif err := binary.Read(bytes.NewBuffer(b), binary.LittleEndian, &s); err != nil {\n\t\treturn nil, err\n\t}\n\t// Read the 8-digit hexadecimal number stored as a Unicode string.\n\t// The four most significant digits represent the language identifier.\n\t// The four least significant digits represent the code page for which\n\t// the data is formatted.\n\tb, err = pe.ReadBytesAtOffset(offset+StringTableLength, (8*2)+1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlangID, err := DecodeUTF16String(b)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(langID) != int(LangIDLength/2) {\n\t\treturn nil, fmt.Errorf(\"invalid language identifier length. Expected: %d, Got: %d\",\n\t\t\tLangIDLength/2,\n\t\t\tlen(langID))\n\t}\n\treturn &s, nil\n}\n\n// String Represents the organization of data in a\n// file-version resource. It contains a string that\n// describes a specific aspect of a file, for example,\n// a file's version, its copyright notices, or its trademarks.\ntype String struct {\n\tLength      uint16\n\tValueLength uint16\n\tType        uint16\n}\n\nfunc (s *String) GetOffset(rva uint32, e ResourceDirectoryEntry, pe *File) uint32 {\n\toffset := pe.GetOffsetFromRva(e.Data.Struct.OffsetToData) + rva\n\treturn alignDword(offset, e.Data.Struct.OffsetToData)\n}\n\n// variant of GetOffset which also returns the number of bytes which were added\n// to achieve 32-bit alignment. The padding value needs to be added to the\n// string length to figure out the offset of the next string\nfunc (s *String) getOffsetAndPadding(rva uint32, e ResourceDirectoryEntry, pe *File) (uint32, uint16) {\n\tunalignedOffset := pe.GetOffsetFromRva(e.Data.Struct.OffsetToData) + rva\n\talignedOffset := alignDword(unalignedOffset, e.Data.Struct.OffsetToData)\n\treturn alignedOffset, uint16(alignedOffset - unalignedOffset)\n}\n\nfunc (pe *File) parseString(rva uint32, e ResourceDirectoryEntry) (string, string, uint16, error) {\n\tvar s String\n\toffset, padding := s.getOffsetAndPadding(rva, e, pe)\n\tb, err := pe.ReadBytesAtOffset(offset, StringLength)\n\tif err != nil {\n\t\treturn \"\", \"\", 0, err\n\t}\n\tif err := binary.Read(bytes.NewBuffer(b), binary.LittleEndian, &s); err != nil {\n\t\treturn \"\", \"\", 0, err\n\t}\n\tconst maxKeySize = 100\n\tb, err = pe.ReadBytesAtOffset(offset+StringLength, maxKeySize)\n\tif err != nil {\n\t\treturn \"\", \"\", 0, err\n\t}\n\tkey, err := DecodeUTF16String(b)\n\tif err != nil {\n\t\treturn \"\", \"\", 0, err\n\t}\n\tvalueOffset := alignDword(uint32(2*(len(key)+1))+offset+StringLength, e.Data.Struct.OffsetToData)\n\tb, err = pe.ReadBytesAtOffset(valueOffset, uint32(2*(s.ValueLength+1)))\n\tif err != nil {\n\t\treturn \"\", \"\", 0, err\n\t}\n\tvalue, err := DecodeUTF16String(b)\n\tif err != nil {\n\t\treturn \"\", \"\", 0, err\n\t}\n\t// The caller of this function uses the string length as an offset to find\n\t// the next string in the file. We need add the alignment padding here\n\t// since the caller is unaware of the byte alignment, and will add the\n\t// string length to the unaligned offset to get the address of the next\n\t// string.\n\ttotalLength := s.Length + padding\n\treturn key, value, totalLength, nil\n}\n\n// ParseVersionResources parses file version strings from the version resource\n// directory. This directory contains several structures starting with VS_VERSION_INFO\n// with references to children StringFileInfo structures. In addition, StringFileInfo\n// contains the StringTable structure with String entries describing the name and value\n// of each file version strings.\nfunc (pe *File) ParseVersionResources() (map[string]string, error) {\n\tvers := make(map[string]string)\n\tif pe.opts.OmitResourceDirectory {\n\t\treturn vers, nil\n\t}\n\tfor _, e := range pe.Resources.Entries {\n\t\tif e.ID != VersionResourceType {\n\t\t\tcontinue\n\t\t}\n\t\tif len(e.Directory.Entries) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tdirectory := e.Directory.Entries[0].Directory\n\n\t\tfor _, e := range directory.Entries {\n\t\t\tm, err := pe.parseVersionEntry(e, vers)\n\t\t\tif err != nil {\n\t\t\t\treturn m, err\n\t\t\t}\n\t\t}\n\t}\n\treturn vers, nil\n}\n\nfunc (pe *File) parseVersionEntry(e ResourceDirectoryEntry, vers map[string]string) (map[string]string, error) {\n\tver, err := pe.parseVersionInfo(e)\n\tif err != nil {\n\t\treturn vers, err\n\t}\n\tff, err := pe.parseFixedFileInfo(e)\n\tif err != nil {\n\t\treturn vers, err\n\t}\n\n\toffset := ff.GetStringFileInfoOffset(e)\n\n\tfor {\n\t\tf, n, err := pe.parseStringFileInfo(offset, e)\n\t\tif err != nil || f.Length == 0 {\n\t\t\tbreak\n\t\t}\n\n\t\tswitch n {\n\t\tcase StringFileInfoString:\n\t\t\ttableOffset := f.GetStringTableOffset(offset)\n\t\t\tfor {\n\t\t\t\ttable, err := pe.parseStringTable(tableOffset, e)\n\t\t\t\tif err != nil {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tstringOffset := table.GetStringOffset(tableOffset, e)\n\t\t\t\tfor stringOffset < tableOffset+uint32(table.Length) {\n\t\t\t\t\tk, v, l, err := pe.parseString(stringOffset, e)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tvers[k] = v\n\t\t\t\t\tif l == 0 {\n\t\t\t\t\t\tstringOffset = tableOffset + uint32(table.Length)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstringOffset = stringOffset + uint32(l)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// handle potential infinite loops\n\t\t\t\tif uint32(table.Length)+tableOffset > tableOffset {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif tableOffset > uint32(f.Length) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\tcase VarFileInfoString:\n\t\tdefault:\n\t\t}\n\n\t\toffset += uint32(f.Length)\n\n\t\t// StringFileInfo/VarFileinfo structs consumed?\n\t\tif offset >= uint32(ver.Length) {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nil, nil\n}\n\n// ParseVersionResourcesForEntries parses file version strings from the version resource\n// directory. This directory contains several structures starting with VS_VERSION_INFO\n// with references to children StringFileInfo structures. In addition, StringFileInfo\n// contains the StringTable structure with String entries describing the name and value\n// of each file version strings.\nfunc (pe *File) ParseVersionResourcesForEntries() ([]map[string]string, error) {\n\tvar allVersions []map[string]string\n\tif pe.opts.OmitResourceDirectory {\n\t\treturn allVersions, nil\n\t}\n\tfor _, e := range pe.Resources.Entries {\n\t\tif e.ID != VersionResourceType {\n\t\t\tcontinue\n\t\t}\n\n\t\tdirectory := e.Directory.Entries[0].Directory\n\n\t\tfor _, e := range directory.Entries {\n\t\t\tvers := make(map[string]string)\n\t\t\tallVersions = append(allVersions, vers)\n\t\t\t_, err := pe.parseVersionEntry(e, vers)\n\t\t\tif err != nil {\n\t\t\t\treturn allVersions, err\n\t\t\t}\n\t\t}\n\t}\n\treturn allVersions, nil\n}\n"
  },
  {
    "path": "version_test.go",
    "content": "// Copyright 2021 Saferwall. All rights reserved.\n// Use of this source code is governed by Apache v2 license\n// license that can be found in the LICENSE file.\n\npackage pe\n\nimport (\n\t\"testing\"\n)\n\nvar peVersionResourceTests = []struct {\n\tin               string\n\tout              error\n\tversionResources map[string]string\n}{\n\t{\n\t\tgetAbsoluteFilePath(\"test/putty.exe\"),\n\t\tnil,\n\t\tmap[string]string{\"CompanyName\": \"Simon Tatham\", \"FileDescription\": \"SSH, Telnet and Rlogin client\", \"FileVersion\": \"Release 0.73 (with embedded help)\", \"InternalName\": \"PuTTY\", \"OriginalFilename\": \"PuTTY\", \"ProductName\": \"PuTTY suite\", \"ProductVersion\": \"Release 0.73\"},\n\t},\n\t{\n\t\tgetAbsoluteFilePath(\"test/brave.exe\"),\n\t\tnil,\n\t\tmap[string]string{\"CompanyName\": \"Brave Software, Inc.\", \"FileDescription\": \"Brave Browser\", \"FileVersion\": \"80.1.7.92\", \"InternalName\": \"chrome_exe\"},\n\t},\n\t{\n\t\tgetAbsoluteFilePath(\"test/impbyord.exe\"),\n\t\tnil,\n\t\tmap[string]string{},\n\t},\n\t{\n\t\tgetAbsoluteFilePath(\"test/WdBoot.sys\"),\n\t\tnil,\n\t\tmap[string]string{\"CompanyName\": \"Microsoft Corporation\", \"FileDescription\": \"Microsoft antimalware boot driver\", \"FileVersion\": \"4.18.1906.3 (GitEnlistment(winpbld).190621-1227)\", \"InternalName\": \"WdBoot\"},\n\t},\n\t{\n\t\tgetAbsoluteFilePath(\"test/shimeng.dll\"),\n\t\tnil,\n\t\tmap[string]string{\"CompanyName\": \"Microsoft Corporation\", \"FileDescription\": \"Shim Engine DLL\", \"FileVersion\": \"10.0.17763.1 (WinBuild.160101.0800)\", \"OriginalFilename\": \"Shim Engine DLL (IAT)\", \"LegalCopyright\": \"© Microsoft Corporation. All rights reserved.\", \"InternalName\": \"Shim Engine DLL (IAT)\", \"ProductName\": \"Microsoft® Windows® Operating System\", \"ProductVersion\": \"10.0.17763.1\"},\n\t},\n\t{\n\t\tgetAbsoluteFilePath(\"test/pwsh.exe\"),\n\t\tnil,\n\t\tmap[string]string{\"Assembly Version\": \"7.3.4.500\", \"Comments\": \"PowerShell on Windows top-level project\", \"CompanyName\": \"Microsoft Corporation\", \"FileDescription\": \"pwsh\", \"FileVersion\": \"7.3.4.500\", \"InternalName\": \"pwsh.dll\", \"LegalCopyright\": \"(c) Microsoft Corporation.\", \"OriginalFilename\": \"pwsh.dll\", \"ProductName\": \"PowerShell\", \"ProductVersion\": \"7.3.4 SHA: b59f05d5a1b2fceca231f75c53c203a02edf6203\"},\n\t},\n\t{\n\t\tgetAbsoluteFilePath(\"test/YourPhone.Exp.WinRT.dll\"),\n\t\tnil,\n\t\tmap[string]string{\"CompanyName\": \"Microsoft Corporation\", \"FileDescription\": \"\", \"FileVersion\": \"1.24052.124.0\", \"OriginalFilename\": \"YourPhone.Exp.WinRT.dll\", \"LegalCopyright\": \"Â© Microsoft Corporation.  All rights reserved.\", \"InternalName\": \"YourPhone.Exp.WinRT\", \"ProductName\": \"Microsoft Phone Link\", \"ProductVersion\": \"1.24052.124.0\"},\n\t},\n}\n\nfunc TestParseVersionResources(t *testing.T) {\n\tfor _, tt := range peVersionResourceTests {\n\t\tt.Run(tt.in, func(t *testing.T) {\n\t\t\tfile, err := New(tt.in, &Options{})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"New(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\n\t\t\tgot := file.Parse()\n\t\t\tif got != nil {\n\t\t\t\tt.Errorf(\"Parse(%s) got %v, want %v\", tt.in, got, tt.out)\n\t\t\t}\n\t\t\tvers, err := file.ParseVersionResources()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"ParseVersionResurces(%s) failed, reason: %v\", tt.in, err)\n\t\t\t}\n\t\t\tfor k, v := range tt.versionResources {\n\t\t\t\tval, ok := vers[k]\n\t\t\t\tif !ok {\n\t\t\t\t\tt.Errorf(\"%s: should have %s version resource\", tt.in, k)\n\t\t\t\t}\n\t\t\t\tif val != v {\n\t\t\t\t\tt.Errorf(\"%s: expected: %s version resource got: %s. Available resources: %v\", tt.in, v, val, vers)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  }
]