moon-pe-inspect

    Safe PE/COFF inspection, import/export extraction, and security metadata auditing for MoonBit.

    pe
    coff
    binary-analysis
    security
    windows
    Download zip
    Author
    Version
    0.6.3
    License
    Apache-2.0
    Last updated
    10 days ago
    Downloads
    134

    #moon-pe-inspect

    CI

    A pure MoonBit PE/COFF inspector for build artifacts and security metadata.

    moon-pe-inspect parses Windows PE files without loading or executing them. It extracts PE32/PE32+ headers, sections, imports, exports, resources, relocations, delay imports, TLS, Debug/PDB metadata, Load Config, and exception tables. The core is a bounds-checked MoonBit library; the CLI adds text, JSON, and artifact diff output for CI and analysis workflows.

    It also parses standalone COFF object files, including section tables, symbol records, long symbol names, string tables, and auxiliary-symbol skipping.

    #Why this project

    PE files are produced by Windows build pipelines, shipped as DLLs and EXEs, and examined during vulnerability triage. Existing generic binary readers stop at byte access. This project owns the domain logic:

    • validate DOS, PE, COFF, and optional-header structures;
    • map RVAs to section file offsets;
    • walk import descriptors and thunk tables;
    • resolve export names, ordinals, and forwarders;
    • traverse resource, relocation, delay-import, TLS, debug, load-config, and exception directories;
    • detect missing hardening flags and writable-executable sections;
    • compare build artifacts and report metadata changes;
    • return bounded errors instead of panicking on malformed input.

    #Features

    • PE32 and PE32+ support.
    • Machine, subsystem, image base, entry point, checksum, and image-size metadata.
    • Section table parsing with virtual/file ranges and characteristics.
    • Imported DLL and function extraction, including ordinal imports.
    • Export extraction with ordinal and forwarder resolution.
    • Resource trees, base relocations, delay imports, TLS callbacks, CodeView PDB metadata, Load Config, and exception entries.
    • Bound-import descriptors, CLR/.NET headers, Authenticode certificate records, and overlay detection.
    • Standalone COFF object parsing with symbols and string tables.
    • Rich Header decoding and per-section Shannon entropy analysis.
    • Security diagnostics for ASLR, NX, CFG metadata, SafeSEH, security cookies, high-entropy VA, RWX sections, relocations, executable entry points, and certificate-table presence.
    • PE checksum calculation and stored-checksum validation.
    • Artifact diffing for headers, sections, imports, exports, directories, and security findings.
    • CI security gate and SARIF 2.1.0 output.
    • CLI commands with human-readable and JSON output.
    • Reproducible synthetic PE fixtures for tests and demonstrations.

    #Quick start

    Install from mooncakes.io:

    moon add 404-32/moon-pe-inspect@0.6.3

    Package page: https://mooncakes.io/docs/404-32/moon-pe-inspect/0.6.3

    Build and test from source:

    git clone https://github.com/404-32/byc001.git cd byc001 moon test --target wasm

    Inspect the included PE32 fixture:

    moon run cmd/main -- inspect examples/minimal-pe32.exe

    List imports and exports:

    moon run cmd/main -- imports examples/minimal-pe32.exe moon run cmd/main -- exports examples/minimal-pe32.exe

    Produce JSON for CI:

    moon run cmd/main -- inspect examples/minimal-pe32plus.exe --json moon run cmd/main -- security examples/minimal-pe32plus.exe --json

    Inspect extended directories and compare two artifacts:

    moon run cmd/main -- directories examples/minimal-pe32.exe moon run cmd/main -- resources examples/minimal-pe32.exe moon run cmd/main -- delay-imports examples/minimal-pe32.exe moon run cmd/main -- load-config examples/minimal-pe32.exe moon run cmd/main -- bound-imports examples/minimal-pe32.exe moon run cmd/main -- clr examples/minimal-pe32.exe moon run cmd/main -- certificates examples/minimal-pe32.exe moon run cmd/main -- overlay examples/minimal-pe32.exe moon run cmd/main -- checksum examples/minimal-pe32.exe moon run cmd/main -- rich examples/rich-pe32.exe moon run cmd/main -- diff examples/minimal-pe32.exe examples/minimal-pe32plus.exe moon run cmd/main -- coff examples/minimal-coff.obj

    Use the security gate or SARIF output in CI:

    moon run cmd/main -- lint examples/minimal-pe32.exe --json moon run cmd/main -- security examples/minimal-pe32.exe --sarif

    Inspect a real Windows binary:

    moon run cmd/main -- sections C:\Windows\System32\notepad.exe

    #Library API

    ///|
    let image = @pe.PEImage::parse(file_bytes)

    ///|
    let sections = image.sections

    ///|
    let imports = image.imports()

    ///|
    let exports = image.exports()

    ///|
    let resources = image.resources()

    ///|
    let relocations = image.base_relocations()

    ///|
    let delay_imports = image.delay_imports()

    ///|
    let tls = image.tls()

    ///|
    let debug_entries = image.debug_entries()

    ///|
    let load_config = image.load_config()

    ///|
    let exceptions = image.exception_entries()

    ///|
    let bound_imports = image.bound_imports()

    ///|
    let clr_header = image.clr_header()

    ///|
    let certificates = image.certificates()

    ///|
    let overlay = image.overlay()

    ///|
    let checksum = image.checksum_info()

    ///|
    let rich = image.rich_header()

    ///|
    let entropies = image.section_entropies()

    ///|
    let diagnostics = image.security_diagnostics()

    ///|
    let offset = image.rva_to_offset(0x1000UL)

    ///|
    let object = @pe.CoffObject::parse(coff_bytes)

    Parsing is read-only. All binary reads are checked against the available byte range, and malformed files produce a PeError containing a file offset and message.

    #Security checks

    • pe.no-aslr: DYNAMIC_BASE is missing.
    • pe.no-nx: NX_COMPAT is missing.
    • pe.no-cfg: GUARD_CF is missing.
    • pe.cfg-metadata-missing: CFG is advertised without a guard function table.
    • pe.aslr-without-relocations: ASLR is advertised without base relocations.
    • pe.no-safeseh: 32-bit image has no SafeSEH metadata or NO_SEH.
    • pe.no-security-cookie: Load Config does not declare a /GS cookie.
    • pe.no-high-entropy-va: 64-bit image lacks high-entropy VA.
    • pe.rwx-section: a section is writable and executable.
    • pe.entrypoint-not-executable: entry point is outside executable sections.
    • pe.unsigned: no certificate table is present.
    • pe.certificate-table: certificate data exists, but the tool does not verify it.
    • pe.checksum-mismatch: the stored PE checksum does not match the file.
    • pe.high-entropy-section: a section has entropy of at least 7.5.

    The last distinction is intentional: detecting an Authenticode certificate table is not the same as validating a signature.

    #Scope

    This project inspects static PE metadata. It does not load or execute images, perform disassembly, unpack compressed binaries, validate certificate chains, or parse every Windows-specific data directory. New formats and checks can be added without changing the safe binary-reader foundation.

    #MoonBit ecosystem fit

    • Publishable MoonBit module with a stable moon add entry point.
    • Pure MoonBit parsing core with bounded reads and no unsafe native dependency.
    • Wasm-first execution for cross-platform use in CI.
    • Library, CLI, JSON, examples, and generated public interfaces live together.
    • Official moonbitlang/async is used only by the CLI file-I/O layer.

    #References

    • Microsoft PE Format: https://learn.microsoft.com/windows/win32/debug/pe-format
    • Apache License 2.0: https://www.apache.org/licenses/LICENSE-2.0

    The implementation is original code derived from the public format specification. It does not vendor or copy existing parser implementations.

    #License and security

    The project is licensed under Apache-2.0. See SECURITY.md for the trusted-input model and vulnerability reporting guidance, and THIRD_PARTY_NOTICES.md for the runtime dependency notice.

    #Development

    python scripts/generate_fixtures.py moon check --target all --deny-warn moon test --target wasm --deny-warn moon test --target wasm-gc --deny-warn moon test --target js --deny-warn moon fmt --check moon package moon run cmd/main -- inspect examples/minimal-pe32.exe

    License: Apache-2.0.

    PeError

    pub(all) suberror PeError {
    PeError(Int, String)
    }

    BoundForwarder

    pub(all) struct BoundForwarder {
    name : String
    timestamp : Int
    }

    BoundImport

    pub(all) struct BoundImport {
    name : String
    timestamp : Int
    forwarders : Array[BoundForwarder]
    }

    CertificateRecord

    pub(all) struct CertificateRecord {
    length : Int
    revision : Int
    certificate_type : Int
    }

    ChecksumInfo

    pub(all) struct ChecksumInfo {
    stored : Int
    computed : Int
    present : Bool
    valid : Bool
    }

    ClrHeaderInfo

    pub(all) struct ClrHeaderInfo {
    header_size : Int
    major_runtime_version : Int
    minor_runtime_version : Int
    metadata_rva : UInt64
    metadata_size : Int
    flags : Int
    entry_point_token : Int
    resources_rva : UInt64
    resources_size : Int
    strong_name_rva : UInt64
    strong_name_size : Int
    }

    CoffObject

    pub(all) struct CoffObject {
    machine : Int
    section_count : Int
    timestamp : Int
    symbol_table_pointer : Int
    symbol_count : Int
    optional_header_size : Int
    characteristics : Int
    sections : Array[CoffSection]
    symbols : Array[CoffSymbol]
    }

    CoffObject::parse

    fn CoffObject::parse(data : Bytes) -> CoffObject raise PeError

    CoffSection

    pub(all) struct CoffSection {
    name : String
    virtual_size : Int
    virtual_address : UInt64
    raw_size : Int
    raw_offset : Int
    relocation_offset : Int
    line_number_offset : Int
    relocation_count : Int
    line_number_count : Int
    characteristics : UInt64
    }

    CoffSymbol

    pub(all) struct CoffSymbol {
    name : String
    value : UInt64
    section_number : Int
    symbol_type : Int
    storage_class : Int
    }

    DataDirectory

    pub(all) struct DataDirectory {
    index : Int
    name : String
    virtual_address : UInt64
    size : Int
    }

    DebugEntry

    pub(all) struct DebugEntry {
    debug_type : Int
    type_name : String
    timestamp : Int
    size_of_data : Int
    address_of_raw_data : UInt64
    pointer_to_raw_data : Int
    pdb_path : String?
    pdb_age : Int?
    pdb_guid : String?
    }

    DelayImportLibrary

    pub(all) struct DelayImportLibrary {
    name : String
    attributes : Int
    timestamp : Int
    symbols : Array[ImportSymbol]
    }

    Diagnostic

    pub(all) struct Diagnostic {
    severity : Severity
    code : String
    message : String
    }

    DiffItem

    pub(all) struct DiffItem {
    field : String
    before : String
    after : String
    }

    ExceptionEntry

    pub(all) struct ExceptionEntry {
    begin_rva : UInt64
    end_rva : UInt64
    unwind_info_rva : UInt64
    }

    ExportSymbol

    pub(all) struct ExportSymbol {
    name : String
    ordinal : Int
    rva : UInt64
    forwarder : String?
    }

    ImportLibrary

    pub(all) struct ImportLibrary {
    name : String
    symbols : Array[ImportSymbol]
    }

    ImportSymbol

    pub(all) struct ImportSymbol {
    name : String?
    ordinal : Int?
    hint : Int?
    }

    LoadConfigInfo

    pub(all) struct LoadConfigInfo {
    structure_size : Int
    timestamp : Int
    global_flags_clear : Int?
    global_flags_set : Int?
    security_cookie : UInt64?
    se_handler_table : UInt64?
    se_handler_count : Int?
    guard_cf_check_pointer : UInt64?
    guard_cf_dispatch_pointer : UInt64?
    guard_cf_function_table : UInt64?
    guard_cf_function_count : Int?
    guard_flags : Int?
    }

    OverlayInfo

    pub(all) struct OverlayInfo {
    offset : Int
    size : Int
    }

    PEFormat

    pub(all) enum PEFormat {
    PE32
    PE32Plus
    }

    PEImage

    pub(all) struct PEImage {
    data : Bytes
    pe_offset : Int
    format : PEFormat
    machine : Int
    number_of_sections : Int
    timestamp : UInt64
    symbol_table_pointer : UInt64
    number_of_symbols : UInt64
    size_of_optional_header : Int
    characteristics : Int
    entry_point : UInt64
    image_base : UInt64
    section_alignment : Int
    file_alignment : Int
    size_of_image : Int
    size_of_headers : Int
    checksum : UInt64
    subsystem : Int
    dll_characteristics : Int
    sections : Array[Section]
    directories : Array[DataDirectory]
    }

    PEImage::base_relocations

    fn PEImage::base_relocations(self : PEImage) -> Array[RelocationBlock] raise PeError

    PEImage::bound_imports

    fn PEImage::bound_imports(self : PEImage) -> Array[BoundImport] raise PeError

    PEImage::certificates

    fn PEImage::certificates(self : PEImage) -> Array[CertificateRecord] raise PeError

    PEImage::checksum_info

    fn PEImage::checksum_info(self : PEImage) -> ChecksumInfo

    Calculate and compare the PE optional-header checksum.

    PEImage::clr_header

    fn PEImage::clr_header(self : PEImage) -> ClrHeaderInfo? raise PeError

    PEImage::debug_entries

    fn PEImage::debug_entries(self : PEImage) -> Array[DebugEntry] raise PeError

    PEImage::delay_imports

    fn PEImage::delay_imports(self : PEImage) -> Array[DelayImportLibrary] raise PeError

    PEImage::diff

    fn PEImage::diff(self : PEImage, other : PEImage) -> Array[DiffItem]

    PEImage::directory

    fn PEImage::directory(self : PEImage, index : Int) -> DataDirectory?

    PEImage::exception_entries

    fn PEImage::exception_entries(self : PEImage) -> Array[ExceptionEntry] raise PeError

    PEImage::exports

    fn PEImage::exports(self : PEImage) -> Array[ExportSymbol] raise PeError

    PEImage::imports

    fn PEImage::imports(self : PEImage) -> Array[ImportLibrary] raise PeError

    PEImage::is_64_bit

    fn PEImage::is_64_bit(self : PEImage) -> Bool

    PEImage::load_config

    fn PEImage::load_config(self : PEImage) -> LoadConfigInfo? raise PeError

    PEImage::overlay

    fn PEImage::overlay(self : PEImage) -> OverlayInfo?

    PEImage::parse

    fn PEImage::parse(data : Bytes) -> PEImage raise PeError

    PEImage::read_c_string

    fn PEImage::read_c_string(self : PEImage, rva : UInt64, max_length : Int) -> String raise PeError

    PEImage::resources

    fn PEImage::resources(self : PEImage) -> Array[ResourceEntry] raise PeError

    PEImage::rich_header

    fn PEImage::rich_header(self : PEImage) -> RichHeaderInfo?

    Decode the optional Rich Header located between the DOS stub and PE header.

    PEImage::rva_to_offset

    fn PEImage::rva_to_offset(self : PEImage, rva : UInt64) -> Int raise PeError

    PEImage::section_by_name

    fn PEImage::section_by_name(self : PEImage, name : String) -> Section?

    PEImage::section_entropies

    fn PEImage::section_entropies(self : PEImage) -> Array[SectionEntropy] raise PeError

    PEImage::security_diagnostics

    fn PEImage::security_diagnostics(self : PEImage) -> Array[Diagnostic]

    PEImage::tls

    fn PEImage::tls(self : PEImage) -> TlsInfo? raise PeError

    PEImage::va_to_rva

    fn PEImage::va_to_rva(self : PEImage, va : UInt64) -> UInt64?

    RelocationBlock

    pub(all) struct RelocationBlock {
    page_rva : UInt64
    entries : Array[RelocationEntry]
    }

    RelocationEntry

    pub(all) struct RelocationEntry {
    relocation_type : Int
    offset : Int
    target_rva : UInt64
    }

    ResourceEntry

    pub(all) struct ResourceEntry {
    path : String
    type_id : Int?
    name_id : Int?
    language_id : Int?
    data_rva : UInt64
    file_offset : Int?
    size : Int
    code_page : Int
    }

    RichEntry

    pub(all) struct RichEntry {
    product_id : Int
    build_id : Int
    use_count : Int
    }

    RichHeaderInfo

    pub(all) struct RichHeaderInfo {
    xor_key : UInt64
    entries : Array[RichEntry]
    }

    Section

    pub(all) struct Section {
    name : String
    virtual_size : Int
    virtual_address : UInt64
    raw_size : Int
    raw_offset : Int
    characteristics : UInt64
    }

    SectionEntropy

    pub(all) struct SectionEntropy {
    section_name : String
    entropy : Double
    raw_size : Int
    }

    Severity

    pub(all) enum Severity {
    Info
    Warning
    Critical
    }

    TlsInfo

    pub(all) struct TlsInfo {
    raw_data_start : UInt64
    raw_data_end : UInt64
    index_address : UInt64
    callbacks_address : UInt64
    size_of_zero_fill : Int
    characteristics : Int
    callbacks : Array[UInt64]
    }

    architecture_name

    fn architecture_name(machine : Int) -> String

    certificate_type_name

    fn certificate_type_name(certificate_type : Int) -> String

    relocation_type_name

    fn relocation_type_name(relocation_type : Int) -> String

    subsystem_name

    fn subsystem_name(subsystem : Int) -> String