Files
reverse-engineering-anything/docs/pe-certificates.md
T
ca4ca65c5e feat(pe): inspect Authenticode certificate tables (#4830)
* feat(pe): inspect Authenticode certificate tables

Add inspect_pe_certificates for IMAGE_DIRECTORY_ENTRY_SECURITY
WIN_CERTIFICATE entries and structurally decoded PKCS#7 signer
and X.509 identity fields. The workflow reads a stable PE image
without executing it or verifying signatures, chains, timestamps,
or Authenticode digest coverage.

* fix(pe): bound and validate certificate DER decoding

- Cap decoded DER values with a shared node budget so sibling fan-out
  cannot exhaust memory before the PKCS#7 check
- Require SignedData version, encapContentInfo and a final signerInfos
  SET in RFC 5652 order, and a signature OCTET STRING in each SignerInfo
- Decode OBJECT IDENTIFIER arcs as unbounded base-128 values, including
  a multi-byte first combined arc
- Decode UniversalString names as UTF-32BE and reject invalid values
- Share PE header admission with the resource layout so a section table
  that does not fit SizeOfHeaders is rejected

* docs: list PE certificates in the translated READMEs

* fix(pe): cap OBJECT IDENTIFIER length and arc size

A crafted type-2 certificate with a 262,144-byte contentType OID stalled
decodeOid for about 20 seconds, because one arc kept growing a BigInt
before the signedData check ran. decodeOid now rejects an OID longer than
256 bytes and an arc longer than 32 bytes before decoding it. Certificate
OIDs, including 2.25 UUID arcs, fit under both caps.

* fix(pe): reject truncated certificate tables and PKCS#7

SignedData now requires one SignerInfo with a non-empty signature and a
complete issuerAndSerialNumber. Certificate names share a 64 KiB decoded-byte
budget and are copied once. A security directory that ends in a short
WIN_CERTIFICATE header fails instead of reporting a complete table. The
certificate CLI forwards the caller abort signal into cleanup.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* chore(ci): keep fork workflow files unchanged

The certificate restack follows current main. Workflow files stay at the
fork tip so this branch update does not rewrite .github/workflows.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(pe): charge serial and validity text to the decoded budget

Certificate and signer serial numbers and validity times now count against
the shared decoded-text budget before they are converted, so a crafted
certificate cannot push megabytes of text into the hashed evidence. The
budget error now names certificate text rather than only names.

Restore the CI workflow to match main: the CI_TIMING_TIER input, the
tier-named timing artifacts and their publication conditions, the Windows
typecheck, and the path and snapshot suites in the curated Windows lane.

* fix(pe): require certificate fields and preserve signer key ids

Reject X.509 records that omit subjectPublicKeyInfo or signatureValue,
return subjectKeyIdentifier bytes as bounded hex, and advertise an absolute
path in the PE certificate input schema.

Co-authored-by: morluto <morluto@users.noreply.github.com>

* fix(pe): reject extra PKCS#7 wrappers and invalid UTF-8 names

ContentInfo and its signedData wrapper no longer ignore trailing values, and
directory strings must round-trip as UTF-8 instead of becoming U+FFFD.

Co-authored-by: morluto <morluto@users.noreply.github.com>

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-22 17:20:07 +08:00

2.3 KiB

PE Authenticode certificate tables

inspect_pe_certificates / inspect-pe-certificates answers whether one selected local PE32/PE32+ image declares an IMAGE_DIRECTORY_ENTRY_SECURITY table, which WIN_CERTIFICATE entries that table stores, and which PKCS#7 signer and X.509 fields those entries carry. It reads the file without executing it and does not require a Windows SDK, catalog file, or disassembler.

rea inspect-pe-certificates /absolute/path/to/application.exe --json

The CLI resolves relative paths against the caller's working directory. MCP and the advertised input schema require an absolute filesystem path and preserve that exact selection:

{
  "name": "inspect_pe_certificates",
  "arguments": { "path": "/artifacts/application.exe" }
}

Observations

The inline Evidence result contains the artifact path, SHA-256, and byte length. table is the file-offset security directory, or null when the directory is absent or zero. Each entries[] item reports WIN_CERTIFICATE length, revision, type, and the payload SHA-256.

For WIN_CERTIFICATE type 2 (PKCS#7 signedData) pkcs7 contains digest algorithm OIDs, embedded X.509 serial/issuer/subject/validity fields, and signer identifier facts. A signer identified by subject key identifier reports that value as subject_key_identifier_hex. Other certificate types keep pkcs7 null.

A missing table is observed absence of that directory. It is not proof the image is unsigned in every catalog or overlay sense.

Limits and failures

The selected file is limited to 64 MiB by default (512 MiB maximum). At most eight WIN_CERTIFICATE entries are examined by default. Malformed PE headers, truncated certificate entries, and PKCS#7 that is not structurally signedData fail without partial success. A security directory that ends in fewer than eight header bytes, including zeros, is a truncated WIN_CERTIFICATE and fails the same way. SignedData must contain at least one SignerInfo, and each SignerInfo must carry a non-empty signature value. Each embedded X.509 certificate must include subjectPublicKeyInfo and signatureValue. Decoded certificate name text, serials, validity spellings, and subject key identifiers share a 64 KiB budget per PKCS#7 payload.

This workflow does not verify signatures, certificate chains, timestamps, or Authenticode digest coverage of image bytes.