mirror of
https://github.com/morluto/rea.git
synced 2026-11-13 18:22:42 +00:00
175 lines
5.0 KiB
TypeScript
175 lines
5.0 KiB
TypeScript
import type { BinaryTarget } from "../domain/binaryTarget.js";
|
|
import {
|
|
managedMemberInspectionSchema,
|
|
type ManagedMemberInspection,
|
|
type ManagedParseIssue,
|
|
} from "../domain/managed/managedArtifact.js";
|
|
import {
|
|
readManagedPeLayout,
|
|
type ManagedPeLayout,
|
|
} from "./ManagedPeReader.js";
|
|
import {
|
|
type ManagedResourceDirectory,
|
|
managedTableRowCounts,
|
|
readManagedMetadataInventory,
|
|
} from "./ManagedMetadataInventory.js";
|
|
import { readManagedMetadataLayout } from "./ManagedMetadataLayout.js";
|
|
import { ManagedReaderFailure } from "./ManagedReaderFailure.js";
|
|
import { parseTypes, typeRanges } from "./ManagedMemberInspectorCore.js";
|
|
import {
|
|
edges,
|
|
parseFields,
|
|
parseMemberRefs,
|
|
parseMethods,
|
|
} from "./ManagedMemberRows.js";
|
|
|
|
const unavailable = (
|
|
target: BinaryTarget,
|
|
bytes: Buffer,
|
|
issue: ManagedParseIssue | null,
|
|
): ManagedMemberInspection =>
|
|
managedMemberInspectionSchema.parse({
|
|
artifact: {
|
|
path: target.path,
|
|
sha256: target.sha256,
|
|
byte_length: bytes.length,
|
|
format: "pe",
|
|
},
|
|
module: null,
|
|
metadata: {
|
|
status: issue === null ? "absent" : "malformed",
|
|
version: null,
|
|
table_row_counts: {},
|
|
},
|
|
identity_scope: {
|
|
token_identity: "build-local",
|
|
requires_artifact_sha256: target.sha256,
|
|
requires_mvid: null,
|
|
},
|
|
types: [],
|
|
fields: [],
|
|
methods: [],
|
|
member_refs: [],
|
|
call_edges: [],
|
|
field_accesses: [],
|
|
coverage: {
|
|
state: "unavailable",
|
|
issues: issue === null ? [] : [issue],
|
|
},
|
|
limitations: [
|
|
issue === null
|
|
? "The PE has no admitted CLI metadata; managed member inspection is unavailable."
|
|
: "The CLI metadata could not be admitted; managed member inspection is unavailable.",
|
|
],
|
|
});
|
|
|
|
const resourceDirectory = (
|
|
pe: ManagedPeLayout,
|
|
): ManagedResourceDirectory | null => {
|
|
if (
|
|
pe.cli === null ||
|
|
pe.cli.resources.rva === 0 ||
|
|
pe.cli.resources.size === 0
|
|
)
|
|
return null;
|
|
return {
|
|
offset: pe.rvaToOffset(
|
|
pe.cli.resources.rva,
|
|
pe.cli.resources.size,
|
|
"cli.resources",
|
|
),
|
|
size: pe.cli.resources.size,
|
|
};
|
|
};
|
|
|
|
const readMemberInventory = (bytes: Buffer, pe: ManagedPeLayout) => {
|
|
const cli = pe.cli;
|
|
if (cli === null)
|
|
throw new TypeError("Managed inventory requires CLI metadata");
|
|
const rootOffset = pe.rvaToOffset(
|
|
cli.metadata.rva,
|
|
cli.metadata.size,
|
|
"cli.metadata",
|
|
);
|
|
const layout = readManagedMetadataLayout(
|
|
bytes,
|
|
rootOffset,
|
|
cli.metadata.size,
|
|
);
|
|
const inventory = readManagedMetadataInventory(
|
|
bytes,
|
|
layout,
|
|
resourceDirectory(pe),
|
|
);
|
|
return { layout, inventory };
|
|
};
|
|
|
|
/** Inspect metadata members and method bodies without loading target code. */
|
|
export const inspectManagedMembersBytes = (
|
|
bytes: Buffer,
|
|
target: BinaryTarget,
|
|
): ManagedMemberInspection => {
|
|
const pe = readManagedPeLayout(bytes);
|
|
if (pe.cli === null) return unavailable(target, bytes, pe.cliIssue);
|
|
const issues: ManagedParseIssue[] = [];
|
|
try {
|
|
const { layout, inventory } = readMemberInventory(bytes, pe);
|
|
issues.push(...inventory.issues);
|
|
const ranges = typeRanges(bytes, layout);
|
|
const types = parseTypes(bytes, layout, ranges);
|
|
const fields = parseFields(bytes, layout, ranges);
|
|
const memberRefs = parseMemberRefs(bytes, layout);
|
|
const methods = parseMethods({
|
|
bytes,
|
|
layout,
|
|
pe,
|
|
ranges,
|
|
});
|
|
const coverageIssues = issues;
|
|
const related = edges(
|
|
methods.methods,
|
|
methods.core,
|
|
fields.core,
|
|
memberRefs.core,
|
|
);
|
|
return managedMemberInspectionSchema.parse({
|
|
artifact: {
|
|
path: target.path,
|
|
sha256: target.sha256,
|
|
byte_length: bytes.length,
|
|
format: "pe",
|
|
},
|
|
module: inventory.module,
|
|
metadata: {
|
|
status: issues.length === 0 ? "complete" : "partial",
|
|
version: layout.version,
|
|
table_row_counts: managedTableRowCounts(layout),
|
|
},
|
|
identity_scope: {
|
|
token_identity: "build-local",
|
|
requires_artifact_sha256: target.sha256,
|
|
requires_mvid: inventory.module?.mvid ?? null,
|
|
},
|
|
types,
|
|
fields: fields.fields,
|
|
methods: methods.methods,
|
|
member_refs: memberRefs.refs,
|
|
call_edges: related.callEdges,
|
|
field_accesses: related.fieldAccesses,
|
|
coverage: {
|
|
state: coverageIssues.length === 0 ? "complete" : "partial",
|
|
issues: coverageIssues,
|
|
},
|
|
limitations: [
|
|
"Metadata tokens are build-local coordinates and are only meaningful with the reported artifact SHA-256 and MVID.",
|
|
"CIL instruction anchors are decoded from file-backed method bodies only; no target assembly is loaded or executed.",
|
|
"Signatures are decoded for common ECMA-335 primitive, class, valuetype, pointer, byref, array, and generic variable forms; unsupported forms retain raw signature hashes.",
|
|
],
|
|
});
|
|
} catch (cause: unknown) {
|
|
if (cause instanceof ManagedReaderFailure)
|
|
return unavailable(target, bytes, cause.issue);
|
|
throw cause;
|
|
}
|
|
};
|