fix(apple): normalize simulator cache platforms without losing raw fields

This commit is contained in:
morluto 2026-10-08 18:19:37 +08:00
parent 17f2bc7b0d
commit fe3cac8aa8
6 changed files with 76 additions and 7 deletions

View File

@ -718,9 +718,9 @@
"mcp_prompts": 6
},
"digests": {
"tools_sha256": "66872b9d57c9972fd5dc83d28d26b37811998abe7d4df267d05fbc6fc8214232",
"tools_sha256": "ef0bc1222f6236c035a576f4ba1317ed3a838d2052f672106cc00c07632298b3",
"prompts_sha256": "ef2de8814390b340623c32001610aa2709d90bed51537b6e562ab3abf144ab4d",
"combined_sha256": "07ea9339b88b47f0fc73a203201bb45c0d2d126e86faf35798f351cb163d91da",
"combined_sha256": "63f9e1c6471f3e88bcf5b0faa66717499e9c2e07d9cef9289f8947c4f2c32e41",
"providers_sha256": "bd826b2f7aa112a8d9ad83fe5c7e3a46534aae56c71399cda72413ce62f5e554"
}
}

View File

@ -1,5 +1,5 @@
{
"manifest_id": "ecm_b4c87944f92abbb0d850842b97be1c6f3bf25cc2eb6c030131bff824a50fec57",
"manifest_id": "ecm_9347855ec2e9c25646961e61d955e25774a2c4142e52dd9fc31b1abd272aa615",
"verifier": {
"id": "rea-managed-conformance",
"version": "1"
@ -13,7 +13,7 @@
"skill_digests": [
{
"skill_id": "reverse-engineer-anything",
"sha256": "c0a42b73238793984d91784d95c0e4ba6b6e47e97978d9429812555bab0a4c3a"
"sha256": "9b9765cc8f5a1d761f9d9fa21f6cbafd5ca1a2e0e7f83f706b5b72c903bf8c3a"
}
],
"claims": [

View File

@ -4,7 +4,7 @@ description: Reverse engineer native, managed, Electron/JavaScript, packaged, fi
metadata:
version: "33"
tool_count: 140
catalog_digest: "07ea9339b88b47f0fc73a203201bb45c0d2d126e86faf35798f351cb163d91da"
catalog_digest: "63f9e1c6471f3e88bcf5b0faa66717499e9c2e07d9cef9289f8947c4f2c32e41"
---
# REA

View File

@ -249,6 +249,22 @@ const version = (value: number): string | null =>
? null
: `${value >>> 16}.${(value >>> 8) & 0xff}.${value & 0xff}`;
/** dyld_cache_header.formatBits simulator bit follows the 8-bit version and disk flag. */
const SIMULATOR_CACHE_FLAG = 1 << 9;
const SIMULATOR_PLATFORMS: ReadonlyMap<number, number> = new Map([
[2, 7],
[3, 8],
[4, 9],
[11, 12],
]);
const cachePlatform = (id: number | null, simulator: boolean | null) =>
id === null
? null
: applePlatform(
simulator === true ? (SIMULATOR_PLATFORMS.get(id) ?? id) : id,
);
/**
* Decode `cacheType` according to header generation. Modern headers
* (those carrying `cacheSubType`) use `0 = development`, `1 = production`;
@ -438,6 +454,9 @@ export class DyldSharedCache {
has(parsed, offset, 8) ? hex(header.readBigUInt64LE(offset)) : null;
const platform = u32(FIELD.platform);
const altPlatform = u32(FIELD.altPlatform);
const formatBits = u32(FIELD.formatBits);
const simulator =
formatBits === null ? null : (formatBits & SIMULATOR_CACHE_FLAG) !== 0;
const cacheType = has(parsed, FIELD.cacheType, 8)
? Number(header.readBigUInt64LE(FIELD.cacheType))
: null;
@ -459,12 +478,18 @@ export class DyldSharedCache {
magic: parsed.magic,
architecture: parsed.magic.slice(MAGIC_PREFIX.length).trim(),
uuid: parsed.uuid,
platform: platform === null ? null : applePlatform(platform),
platform: cachePlatform(platform, simulator),
header_platform: platform === null ? null : applePlatform(platform),
header_alt_platform:
altPlatform === null || altPlatform === 0
? null
: applePlatform(altPlatform),
simulator,
os_version: version(u32(FIELD.osVersion) ?? 0),
alt_platform:
altPlatform === null || altPlatform === 0
? null
: applePlatform(altPlatform),
: cachePlatform(altPlatform, simulator),
alt_os_version: version(u32(FIELD.altOsVersion) ?? 0),
cache_type: decodeCacheType(
cacheType,

View File

@ -37,6 +37,10 @@ const headerSchema = z.object({
architecture: z.string(),
uuid: z.string(),
platform: applePlatformSchema.nullable(),
/** Producer platform fields before simulator-family normalization. */
header_platform: applePlatformSchema.nullable().default(null),
header_alt_platform: applePlatformSchema.nullable().default(null),
simulator: z.boolean().nullable().default(null),
os_version: z.string().nullable(),
alt_platform: applePlatformSchema.nullable(),
alt_os_version: z.string().nullable(),

View File

@ -239,3 +239,43 @@ it("discovers companions beside a symlink target while preserving selected metad
coverage: { status: "complete", unreadable_subcaches: [] },
});
});
it.each([
[2, 7],
[3, 8],
[4, 9],
[11, 12],
])(
"decodes simulator cache family %i into platform %i and retains raw metadata",
async (raw, normalized) => {
const fixture = dyldCacheFixture(IMAGES.slice(0, 1));
const main = Buffer.from(fixture.main);
main.writeUInt32LE(raw, 0xd8);
main.writeUInt32LE(1 << 9, 0xdc);
const path = await writeCache({ ...fixture, main });
const result = await inspectDyldSharedCache({ cache_path: path });
if (!result.ok) throw result.error;
expect(result.value).toMatchObject({
platform: { id: normalized },
header_platform: { id: raw },
simulator: true,
});
const targetPath = join(dirname(path), "simulator-tool");
const bytes = machoImage({
fileType: FILE_TYPE.execute,
commands: [
buildVersionCommand(normalized),
dylibCommand(LC.LOAD_DYLIB, "/usr/lib/libSystem.B.dylib"),
],
});
await writeFile(targetPath, bytes);
const trace = await traceDylibResolution({
rootPath: dirname(path),
targetPath,
targetSha256: createHash("sha256").update(bytes).digest("hex"),
enumerateRoots: false,
parameters: { shared_cache: path },
});
expect(trace.edges[0]?.resolution.status).toBe("shared-cache");
},
);