From fe3cac8aa856435770738bb55720aed4190eae3d Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Thu, 8 Oct 2026 18:19:37 +0800 Subject: [PATCH] fix(apple): normalize simulator cache platforms without losing raw fields --- docs/product-catalog.json | 4 +- .../managed-conformance-manifest.json | 4 +- skills/reverse-engineer-anything/SKILL.md | 2 +- src/artifacts/apple/DyldSharedCacheReader.ts | 29 +++++++++++++- src/domain/apple/dyldSharedCache.ts | 4 ++ .../filesystem/dyldSharedCache.test.ts | 40 +++++++++++++++++++ 6 files changed, 76 insertions(+), 7 deletions(-) diff --git a/docs/product-catalog.json b/docs/product-catalog.json index 774650e1e..c17bf12c9 100644 --- a/docs/product-catalog.json +++ b/docs/product-catalog.json @@ -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" } } diff --git a/docs/verification/managed-conformance-manifest.json b/docs/verification/managed-conformance-manifest.json index 596d875fa..9420b1e40 100644 --- a/docs/verification/managed-conformance-manifest.json +++ b/docs/verification/managed-conformance-manifest.json @@ -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": [ diff --git a/skills/reverse-engineer-anything/SKILL.md b/skills/reverse-engineer-anything/SKILL.md index 99052bef1..d4f5d72df 100644 --- a/skills/reverse-engineer-anything/SKILL.md +++ b/skills/reverse-engineer-anything/SKILL.md @@ -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 diff --git a/src/artifacts/apple/DyldSharedCacheReader.ts b/src/artifacts/apple/DyldSharedCacheReader.ts index cce259ad0..195bf6354 100644 --- a/src/artifacts/apple/DyldSharedCacheReader.ts +++ b/src/artifacts/apple/DyldSharedCacheReader.ts @@ -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 = 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, diff --git a/src/domain/apple/dyldSharedCache.ts b/src/domain/apple/dyldSharedCache.ts index 13cfb70fd..0970a5b4d 100644 --- a/src/domain/apple/dyldSharedCache.ts +++ b/src/domain/apple/dyldSharedCache.ts @@ -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(), diff --git a/tests/boundary/filesystem/dyldSharedCache.test.ts b/tests/boundary/filesystem/dyldSharedCache.test.ts index 8a17e77bc..88a629660 100644 --- a/tests/boundary/filesystem/dyldSharedCache.test.ts +++ b/tests/boundary/filesystem/dyldSharedCache.test.ts @@ -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"); + }, +);