Files
reverse-engineering-anything/src/native/AppleDispatchMetadata.test.ts
T
morluto 4fbc501121 feat(native): add inspection primitives, dispatch traces and approved UI observation (#500)
* fix(hopper): normalize string-backed addresses and update verification

* feat(native): add inspection primitives, dispatch traces and approved UI observation

* test: prioritize real artifact E2E and captured golden regressions

* fix(process): queue observed output before reactive rendering actions

* test: prune redundant cases and strengthen boundary assertions

* fix(cli): route native value tracing through shared workflows

* test: replace synthetic success with real workflows and captured data

* fix(catalog): separate portable metadata from native host availability
2026-10-04 16:02:36 +08:00

120 lines
3.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { decodeAppleDispatchMetadata } from "./AppleDispatchMetadata.js";
const base = 0x100000000n;
const fixture = (relative = false) => {
const bytes = Buffer.alloc(2048);
const u32 = (offset: number, value: number) =>
bytes.writeUInt32LE(value, offset);
const ptr = (offset: number, target: number) =>
bytes.writeBigUInt64LE(base + BigInt(target), offset);
u32(0, 0xfeedfacf);
u32(4, 0x0100000c);
u32(16, 1);
u32(20, 152);
u32(32, 0x19);
u32(36, 152);
bytes.write("__DATA", 40);
ptr(56, 0);
bytes.writeBigUInt64LE(2048n, 64);
bytes.writeBigUInt64LE(2048n, 80);
u32(92, 5);
u32(96, 1);
bytes.write("__objc_classlist", 104);
bytes.write("__DATA", 120);
ptr(136, 0x180);
bytes.writeBigUInt64LE(8n, 144);
u32(152, 0x180);
ptr(0x180, 0x200);
ptr(0x200, 0x280);
ptr(0x220, 0x300);
ptr(0x2a0, 0x380);
u32(0x308, 16);
ptr(0x318, 0x450);
ptr(0x320, 0x480);
ptr(0x330, 0x500);
u32(0x380, 1);
ptr(0x398, 0x450);
bytes.write("Fixture\0", 0x450);
bytes.write("performAction:\0", 0x460);
u32(0x480, relative ? 0xc000000c : 24);
u32(0x484, 1);
if (relative) {
bytes.writeInt32LE(0x460 - 0x488, 0x488);
bytes.writeInt32LE(0x5c0 - 0x48c, 0x48c);
bytes.writeInt32LE(0x700 - 0x490, 0x490);
} else {
ptr(0x488, 0x460);
ptr(0x490, 0x5c0);
ptr(0x498, 0x700);
}
u32(0x500, 32);
u32(0x504, 1);
ptr(0x508, 0x580);
ptr(0x510, 0x5a0);
ptr(0x518, 0x5c0);
u32(0x580, 8);
bytes.write("state\0", 0x5a0);
bytes.write("i\0", 0x5c0);
return bytes;
};
const provenance = { path: "/fixture/app", sha256: "a".repeat(64) };
describe("Apple dispatch binary metadata", () => {
it.each([false, true])(
"decodes stripped absolute/relative method lists (relative=%s) with exact offsets",
(relative) => {
const result = decodeAppleDispatchMetadata(
fixture(relative),
100,
provenance,
);
expect(result.objc_classes).toMatchObject([
{
name: "Fixture",
instance_size: 16,
location: { address: "0x100000200", file_offset: 512 },
},
{ name: "Fixture", is_meta_class: true },
]);
expect(result.objc_dispatch_implementations).toMatchObject([
{
selector: "performAction:",
implementation_address: "0x100000700",
location: { file_offset: 0x488 },
decode: { status: "decoded" },
},
]);
expect(result.objc_ivars).toMatchObject([
{
name: "state",
type_encoding: "i",
offset: 8,
location: { file_offset: 0x508 },
},
]);
},
);
it("keeps unsupported pointers and record truncation explicit", () => {
const bytes = fixture();
bytes.writeBigUInt64LE(0xffffffffffffffffn, 0x498);
const result = decodeAppleDispatchMetadata(bytes, 100, provenance);
expect(result.objc_dispatch_implementations[0]).toMatchObject({
implementation_address: null,
decode: { status: "partial" },
});
expect(
decodeAppleDispatchMetadata(fixture(), 1, provenance).coverage[0]?.reason,
).toContain("max_records_reached");
});
it("rejects malformed command and section boundaries", () => {
const bytes = fixture();
bytes.writeUInt32LE(7, 36);
expect(() => decodeAppleDispatchMetadata(bytes, 100, provenance)).toThrow(
"command size",
);
expect(() =>
decodeAppleDispatchMetadata(Buffer.alloc(10), 100, provenance),
).toThrow("Truncated");
});
});