refactor(pe): share optional-header structural facts

Decode optional-header declarations once and pass detached directory words, locations and SizeOfHeaders to native consumers. Preserve executable machine admission, raw GPU/resource machine reporting, RVA mapping and certificate file-offset semantics.

Validation: fresh build/catalog, full static checks, 466 core/provider fixture tests, 122 workflow tests and 14 selected file-backed PE admission tests passed. Public CLI and actual stdio MCP used authored artifacts; installed external providers were not qualified. Added 17 structural cases and two GPU machine-policy controls. Eager directory decoding costs remain unmeasured.
This commit is contained in:
morluto committed 2026-10-25 05:28:31 +08:00
1 parent 9548953b47
commit 599099f656
12 files changed
+394 -119

No files matched your search

+5 -5
View File
@@ -98,7 +98,7 @@ const parsePeExports = (bytes: Buffer, artifact: Artifact): NativeExports => {
architecture: image.architecture,
bits: image.bits,
};
if (directory === undefined || directory.virtual_address === 0)
if (directory === undefined || directory.address === 0)
return {
...result,
export_directory: null,
@@ -129,7 +129,7 @@ const parsePeExports = (bytes: Buffer, artifact: Artifact): NativeExports => {
return value.toString("latin1");
};
const header = map(directory.virtual_address, PE_EXPORT_DIRECTORY_BYTES);
const header = map(directory.address, PE_EXPORT_DIRECTORY_BYTES);
const ordinalBase = reader.u32(header + 16);
const functionCount = reader.u32(header + 20);
const nameCount = reader.u32(header + 24);
@@ -180,7 +180,7 @@ const parsePeExports = (bytes: Buffer, artifact: Artifact): NativeExports => {
}
}
const forwarderEnd = directory.virtual_address + directory.size;
const forwarderEnd = directory.address + directory.size;
const retain = createRetention();
const exports: PeExport[] = [];
for (let index = 0; index < functionCount; index++) {
@@ -189,7 +189,7 @@ const parsePeExports = (bytes: Buffer, artifact: Artifact): NativeExports => {
if (rva === 0) continue;
const ordinal = ordinalBase + index;
let target: Pick<PeExport, "address" | "forwarder">;
if (rva >= directory.virtual_address && rva < forwarderEnd) {
if (rva >= directory.address && rva < forwarderEnd) {
try {
target = { address: null, forwarder: cString(rva) };
} catch (cause) {
@@ -216,7 +216,7 @@ const parsePeExports = (bytes: Buffer, artifact: Artifact): NativeExports => {
return {
...result,
export_directory: {
rva: directory.virtual_address,
rva: directory.address,
size: directory.size,
dll_name: dllName,
ordinal_base: ordinalBase,
+3 -4
View File
@@ -1,3 +1,5 @@
import type { PeDataDirectory } from "../../domain/peOptionalHeader.js";
/** Distinguish corrupt recognized binaries from containers outside parser coverage. */
export class BinaryFormatFailure extends Error {
constructor(
@@ -120,10 +122,7 @@ interface BinaryContainerFacts {
*/
readonly pe_header_mapping?: FileMapping;
/** PE optional-header data directories, indexed as declared. */
readonly pe_data_directories?: readonly {
readonly virtual_address: number;
readonly size: number;
}[];
readonly pe_data_directories?: readonly PeDataDirectory[];
/** Mach-O load-command observation, when available. */
readonly macho_chained_fixups?: boolean;
/** Section-name lookup was incomplete; absence by name is not established. */
+17 -15
View File
@@ -1,4 +1,5 @@
import { readElfTableDeclarations } from "../../domain/elfDeclaredRanges.js";
import { readPeOptionalHeaderStructure } from "../../domain/peOptionalHeader.js";
import {
BinaryFormatFailure,
BinaryReader,
@@ -286,23 +287,24 @@ export function* readPeSectionDirectory(
const coff = new BinaryReader(yield request(fileSize, BigInt(pe), 24n), true);
if (coff.u32(0) !== 0x4550) reject("PE signature is invalid");
const optionalSize = coff.u16(20);
const optional = new BinaryReader(
const optional = readPeOptionalHeaderStructure(
yield request(fileSize, BigInt(pe + 24), BigInt(optionalSize)),
true,
);
const magic = optional.u16(0);
const bits = magic === 0x10b ? 32 : magic === 0x20b ? 64 : null;
if (bits === null)
throw new BinaryFormatFailure(
"unsupported",
"Only PE32/PE32+ images are supported",
);
const minimum = bits === 64 ? 112 : 96;
if (
optionalSize < minimum ||
optional.u32(minimum - 4) * 8 > optionalSize - minimum
)
reject("PE optional header or data directories are truncated");
if (!optional.ok) {
switch (optional.error.reason) {
case "truncated-magic":
return reject("16-bit field range is truncated or outside the file");
case "unsupported-magic":
throw new BinaryFormatFailure(
"unsupported",
"Only PE32/PE32+ images are supported",
);
case "truncated-fixed-fields":
case "truncated-directory-table":
return reject("PE optional header or data directories are truncated");
}
}
const { bits } = optional.value;
const count = coff.u16(6);
const start = pe + 24 + optionalSize;
const table = yield request(fileSize, BigInt(start), BigInt(count) * 40n);
+2 -2
View File
@@ -82,13 +82,13 @@ export const readPeImage = (bytes: Buffer): PeImage => {
);
if (!facts.ok)
throw new BinaryFormatFailure(facts.error.kind, facts.error.message);
const { bits, dataDirectories } = facts.value;
const { bits, dataDirectories, sizeOfHeaders } = facts.value;
const imageBase = reader.word(optional + (bits === 64 ? 24 : 28), bits);
const fileAlignment = reader.u32(optional + 36);
const headerMapping: FileMapping = {
address: imageBase,
offset: 0,
size: Math.min(reader.u32(optional + 60), bytes.length),
size: Math.min(sizeOfHeaders, bytes.length),
};
const count = reader.u16(pe + 6);
const start = reader.table(
+9 -20
View File
@@ -54,27 +54,13 @@ export const parsePeCertificates = (
artifact: PeCertificates["artifact"],
input: InspectPeCertificatesInput,
): PeCertificates => {
const { pe, optional, magic, directoriesAt, directoryCount, headerSize } =
const { machine, format, dataDirectories, headerSize, optionalHeaderOffset } =
readPeHeaders(bytes);
const machine = bytes.readUInt16LE(pe + 4);
const format = magic === 0x20b ? "pe32-plus" : "pe32";
const securityIndex = 4;
if (directoryCount <= securityIndex) {
return {
artifact,
format,
machine,
table: null,
entries: [],
coverage: { status: "complete", examined_entries: 0, certificates: 0 },
limitations: [...ABSENT_LIMITATIONS],
};
}
const directoryAt = optional + directoriesAt + securityIndex * 8;
requireRange(bytes, directoryAt, 8);
const tableOffset = bytes.readUInt32LE(directoryAt);
const tableSize = bytes.readUInt32LE(directoryAt + 4);
if (tableOffset === 0 && tableSize === 0) {
const security = dataDirectories[4];
if (
security === undefined ||
(security.address === 0 && security.size === 0)
) {
return {
artifact,
format,
@@ -85,6 +71,9 @@ export const parsePeCertificates = (
limitations: [...ABSENT_LIMITATIONS],
};
}
const tableOffset = security.address;
const tableSize = security.size;
const directoryAt = optionalHeaderOffset + security.offset;
if (tableSize === 0 || tableOffset < headerSize)
fail("PE certificate table offset overlaps headers or has zero size.");
requireRange(bytes, tableOffset, tableSize);
+7 -7
View File
@@ -389,12 +389,12 @@ export const parsePeImports = async (
const delayDirectory = image.pe_data_directories?.[PE_DELAY_IMPORT_DIRECTORY];
const imports: PeImportDll[] = [];
let importDirectoryResult: PeImports["import_directory"] = null;
if (importDirectory !== undefined && importDirectory.virtual_address !== 0) {
if (importDirectory !== undefined && importDirectory.address !== 0) {
const spans: { offset: number; bytes: number }[] = [];
for (let index = 0; ; index++) {
await checkpoint();
const descriptorRva =
importDirectory.virtual_address + index * IMPORT_DESCRIPTOR_BYTES;
importDirectory.address + index * IMPORT_DESCRIPTOR_BYTES;
const descriptor = mapDescriptor(
descriptorRva,
IMPORT_DESCRIPTOR_BYTES,
@@ -453,7 +453,7 @@ export const parsePeImports = async (
});
}
importDirectoryResult = {
rva: importDirectory.virtual_address,
rva: importDirectory.address,
size: importDirectory.size,
locations: spans,
};
@@ -463,12 +463,12 @@ export const parsePeImports = async (
const delayImports: PeDelayImportDll[] = [];
let delayDirectoryResult: PeImports["delay_import_directory"] = null;
if (delayDirectory !== undefined && delayDirectory.virtual_address !== 0) {
if (delayDirectory !== undefined && delayDirectory.address !== 0) {
const spans: { offset: number; bytes: number }[] = [];
for (let index = 0; ; index++) {
await checkpoint();
const descriptorRva =
delayDirectory.virtual_address + index * DELAY_DESCRIPTOR_BYTES;
delayDirectory.address + index * DELAY_DESCRIPTOR_BYTES;
const descriptor = mapDescriptor(
descriptorRva,
DELAY_DESCRIPTOR_BYTES,
@@ -534,7 +534,7 @@ export const parsePeImports = async (
});
}
delayDirectoryResult = {
rva: delayDirectory.virtual_address,
rva: delayDirectory.address,
size: delayDirectory.size,
locations: spans,
};
@@ -559,7 +559,7 @@ export const parsePeImports = async (
mappingCompatibility: standardMappingCompatibility,
truncatedDescriptor: pefileTruncatedDescriptor,
bits,
directoryRva: importDirectory?.virtual_address ?? 0,
directoryRva: importDirectory?.address ?? 0,
fileBytes: bytes.length,
}),
coverage: coverage(
+1 -1
View File
@@ -164,7 +164,7 @@ export const parsePeLoadConfig = async (
const dataDirectory = (index: number): DirectorySpan | null => {
const entry = image.pe_data_directories[index];
if (entry === undefined) return null;
const { virtual_address: rva, size } = entry;
const { address: rva, size } = entry;
if (rva === 0 && size === 0) return null;
if (rva === 0 || size === 0)
throw new BinaryFormatFailure(
+45 -29
View File
@@ -1,4 +1,18 @@
import { ArtifactReaderFailure } from "../../artifacts/ArtifactReader.js";
import {
readPeOptionalHeaderStructure,
type PeOptionalHeaderFailure,
} from "../../domain/peOptionalHeader.js";
const OPTIONAL_HEADER_FAILURES: Readonly<
Record<PeOptionalHeaderFailure["reason"], string>
> = {
"truncated-magic": "Truncated PE optional header.",
"unsupported-magic": "Unsupported PE optional header magic.",
"truncated-fixed-fields": "Truncated PE data directory count.",
"truncated-directory-table":
"PE directory count exceeds its optional header.",
};
export const peFailure = (message: string): never => {
throw new ArtifactReaderFailure("format", message);
@@ -71,31 +85,31 @@ export const readPeHeaders = (bytes: Buffer, fileSize = bytes.length) => {
if (bytes.readUInt32LE(pe) !== 0x4550) peFailure("Expected a PE signature.");
const sectionCount = bytes.readUInt16LE(pe + 6);
const optionalSize = bytes.readUInt16LE(pe + 20);
const optional = pe + 24;
requirePeRange(bytes, optional, optionalSize);
const optionalHeaderOffset = pe + 24;
requirePeRange(bytes, optionalHeaderOffset, optionalSize);
if (optionalSize < 64) peFailure("Truncated PE optional header.");
const magic = bytes.readUInt16LE(optional);
if (magic !== 0x10b && magic !== 0x20b)
peFailure("Unsupported PE optional header magic.");
const countAt = magic === 0x10b ? 92 : 108;
const directoriesAt = countAt + 4;
if (optionalSize < directoriesAt)
peFailure("Truncated PE data directory count.");
const directoryCount = bytes.readUInt32LE(optional + countAt);
if (directoryCount > Math.floor((optionalSize - directoriesAt) / 8))
peFailure("PE directory count exceeds its optional header.");
const headerSize = bytes.readUInt32LE(optional + 60);
const table = optional + optionalSize;
const parsed = readPeOptionalHeaderStructure(
bytes.subarray(optionalHeaderOffset, optionalHeaderOffset + optionalSize),
);
if (!parsed.ok)
return peFailure(OPTIONAL_HEADER_FAILURES[parsed.error.reason]);
const {
sizeOfHeaders: headerSize,
format,
directoryOffset,
dataDirectories,
} = parsed.value;
const table = optionalHeaderOffset + optionalSize;
requirePeRange(bytes, table, sectionCount * 40);
if (headerSize < table + sectionCount * 40 || headerSize > fileSize)
peFailure("Invalid PE SizeOfHeaders.");
return {
pe,
machine: bytes.readUInt16LE(pe + 4),
format,
sectionCount,
optional,
magic,
directoriesAt,
directoryCount,
optionalHeaderOffset,
directoryOffset,
dataDirectories,
headerSize,
table,
};
@@ -107,12 +121,12 @@ export const readPeResourceLayout = (
fileSize = bytes.length,
) => {
const {
pe,
machine,
format,
sectionCount,
optional,
magic,
directoriesAt,
directoryCount,
optionalHeaderOffset,
directoryOffset,
dataDirectories,
headerSize,
table,
} = readPeHeaders(bytes, fileSize);
@@ -194,14 +208,16 @@ export const readPeResourceLayout = (
requirePeExtent(fileSize, offset, size);
return offset;
};
const directoryAt = optional + directoriesAt + 16;
const rva = directoryCount > 2 ? bytes.readUInt32LE(directoryAt) : 0;
const size = directoryCount > 2 ? bytes.readUInt32LE(directoryAt + 4) : 0;
const resource = dataDirectories[2];
const directoryAt =
optionalHeaderOffset + (resource?.offset ?? directoryOffset + 2 * 8);
const rva = resource?.address ?? 0;
const size = resource?.size ?? 0;
if ((rva === 0) !== (size === 0))
peFailure("Inconsistent PE resource directory RVA/size.");
return {
format: magic === 0x10b ? ("pe32" as const) : ("pe32-plus" as const),
machine: bytes.readUInt16LE(pe + 4),
format,
machine,
directoryAt,
rva,
size,