fix(native): capture Mach-O header metadata alongside load commands (#525)
* fix(native): capture Mach-O header metadata alongside load commands Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com> * style(test): format native Mach-O header regression Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com> * fix(native): drop dead bare otool override in header test --------- Signed-off-by: Rudy Celekli <47457359+rudycelekli@users.noreply.github.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
This commit is contained in:
parent
31ed005183
commit
a8195ba2cc
|
|
@ -35,7 +35,7 @@ interface NativeMachoInspectionContext {
|
|||
const REQUIRED_COMMANDS = [
|
||||
["file", ["-b"]],
|
||||
["lipo", ["-detailed_info"]],
|
||||
["otool", ["-l"]],
|
||||
["otool", ["-h", "-l"]],
|
||||
["nm", ["-gjU"]],
|
||||
["dyld_info", ["-imports"]],
|
||||
["dyld_info", ["-exports"]],
|
||||
|
|
|
|||
|
|
@ -98,7 +98,6 @@ const parseLoadCommand = (block: string, state: ParsedLoadCommands): void => {
|
|||
collectBuild(kind, block, fields, state);
|
||||
collectDependency(kind, fields, state);
|
||||
collectSegment(kind, block, fields, state);
|
||||
collectFlags(fields, state.flags);
|
||||
};
|
||||
|
||||
const collectUuid = (
|
||||
|
|
@ -159,16 +158,6 @@ const collectSegment = (
|
|||
state.segments.push(parseSegment(block, fields));
|
||||
};
|
||||
|
||||
const collectFlags = (
|
||||
fields: Readonly<Record<string, string | number | null>>,
|
||||
flags: Set<string>,
|
||||
): void => {
|
||||
const rawFlags = stringField(fields, "flags");
|
||||
if (rawFlags === null) return;
|
||||
for (const flag of rawFlags.split(/\s+/u))
|
||||
if (flag.length > 0) flags.add(flag);
|
||||
};
|
||||
|
||||
const parseFields = (block: string): Record<string, string | number | null> => {
|
||||
const fields: Record<string, string | number | null> = {};
|
||||
for (const rawLine of block.split(/\r?\n/u).slice(1)) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import { expect, it } from "vitest";
|
||||
|
||||
import { NativeMacOSProvider } from "../../../../src/native/NativeMacOSProvider.js";
|
||||
import { parseOtoolLoadCommands } from "../../../../src/native/parsers/otool.js";
|
||||
import {
|
||||
NativeFixtureRunner,
|
||||
nativeFixture,
|
||||
nativeMachoTarget,
|
||||
} from "../../../fixtures/nativeCommands.js";
|
||||
|
||||
it("collects the actual Mach header alongside the native load commands", async () => {
|
||||
// The provider always invokes `otool -h -l`, which the runner resolves
|
||||
// via the "otool:-h" key; no bare `otool` override is needed.
|
||||
const runner = new NativeFixtureRunner({
|
||||
"otool:-h": await nativeFixture(
|
||||
"native-macho-header/otool-header-load.txt",
|
||||
),
|
||||
});
|
||||
const client = new NativeMacOSProvider(runner, "darwin").createClient(
|
||||
nativeMachoTarget("/owned/fixture"),
|
||||
);
|
||||
const result = await client.execute("inspect_macho", {});
|
||||
expect(result.ok).toBe(true);
|
||||
if (!result.ok) return;
|
||||
expect(result.value.result).toMatchObject({
|
||||
file_type: "2",
|
||||
flags: ["0x00200085"],
|
||||
segments: {
|
||||
items: expect.arrayContaining([
|
||||
expect.objectContaining({ name: "__TEXT" }),
|
||||
]),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("does not reinterpret segment and section flags as Mach header flags", async () => {
|
||||
const load = parseOtoolLoadCommands(
|
||||
await nativeFixture("native-macho-header/otool-header-load.txt"),
|
||||
);
|
||||
expect(load.flags).toEqual(["0x00200085"]);
|
||||
expect(load.commands.some((command) => command.fields.flags === 0)).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Source-built Mach-O header capture
|
||||
|
||||
Captured on macOS arm64 with the host Xcode tools from the adjacent `fixture.c`:
|
||||
|
||||
```sh
|
||||
xcrun clang fixture.c -o fixture
|
||||
xcrun otool -l fixture > otool-load.txt
|
||||
{ xcrun otool -h fixture; xcrun otool -l fixture; } > otool-header-load.txt
|
||||
```
|
||||
|
||||
Only the temporary fixture path was replaced with `/owned/fixture`; tool fields
|
||||
are unchanged. The first output contains load commands without a Mach header.
|
||||
The second contains the real numeric header followed by those same commands.
|
||||
These are format regression captures, not Hopper or Ghidra runtime proof.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
int owned_value(void) { return 7; }
|
||||
int main(void) { return owned_value(); }
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
/owned/fixture:
|
||||
Mach header
|
||||
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
|
||||
0xfeedfacf 16777228 0 0x00 2 16 744 0x00200085
|
||||
|
||||
/owned/fixture:
|
||||
Load command 0
|
||||
cmd LC_SEGMENT_64
|
||||
cmdsize 72
|
||||
segname __PAGEZERO
|
||||
vmaddr 0x0000000000000000
|
||||
vmsize 0x0000000100000000
|
||||
fileoff 0
|
||||
filesize 0
|
||||
maxprot 0x00000000
|
||||
initprot 0x00000000
|
||||
nsects 0
|
||||
flags 0x0
|
||||
Load command 1
|
||||
cmd LC_SEGMENT_64
|
||||
cmdsize 232
|
||||
segname __TEXT
|
||||
vmaddr 0x0000000100000000
|
||||
vmsize 0x0000000000004000
|
||||
fileoff 0
|
||||
filesize 16384
|
||||
maxprot 0x00000005
|
||||
initprot 0x00000005
|
||||
nsects 2
|
||||
flags 0x0
|
||||
Section
|
||||
sectname __text
|
||||
segname __TEXT
|
||||
addr 0x0000000100000328
|
||||
size 0x0000000000000028
|
||||
offset 808
|
||||
align 2^2 (4)
|
||||
reloff 0
|
||||
nreloc 0
|
||||
flags 0x80000400
|
||||
reserved1 0
|
||||
reserved2 0
|
||||
Section
|
||||
sectname __unwind_info
|
||||
segname __TEXT
|
||||
addr 0x0000000100000350
|
||||
size 0x0000000000000060
|
||||
offset 848
|
||||
align 2^2 (4)
|
||||
reloff 0
|
||||
nreloc 0
|
||||
flags 0x00000000
|
||||
reserved1 0
|
||||
reserved2 0
|
||||
Load command 2
|
||||
cmd LC_SEGMENT_64
|
||||
cmdsize 72
|
||||
segname __LINKEDIT
|
||||
vmaddr 0x0000000100004000
|
||||
vmsize 0x0000000000004000
|
||||
fileoff 16384
|
||||
filesize 504
|
||||
maxprot 0x00000001
|
||||
initprot 0x00000001
|
||||
nsects 0
|
||||
flags 0x0
|
||||
Load command 3
|
||||
cmd LC_DYLD_CHAINED_FIXUPS
|
||||
cmdsize 16
|
||||
dataoff 16384
|
||||
datasize 56
|
||||
Load command 4
|
||||
cmd LC_DYLD_EXPORTS_TRIE
|
||||
cmdsize 16
|
||||
dataoff 16440
|
||||
datasize 64
|
||||
Load command 5
|
||||
cmd LC_SYMTAB
|
||||
cmdsize 24
|
||||
symoff 16512
|
||||
nsyms 3
|
||||
stroff 16560
|
||||
strsize 48
|
||||
Load command 6
|
||||
cmd LC_DYSYMTAB
|
||||
cmdsize 80
|
||||
ilocalsym 0
|
||||
nlocalsym 0
|
||||
iextdefsym 0
|
||||
nextdefsym 3
|
||||
iundefsym 3
|
||||
nundefsym 0
|
||||
tocoff 0
|
||||
ntoc 0
|
||||
modtaboff 0
|
||||
nmodtab 0
|
||||
extrefsymoff 0
|
||||
nextrefsyms 0
|
||||
indirectsymoff 0
|
||||
nindirectsyms 0
|
||||
extreloff 0
|
||||
nextrel 0
|
||||
locreloff 0
|
||||
nlocrel 0
|
||||
Load command 7
|
||||
cmd LC_LOAD_DYLINKER
|
||||
cmdsize 32
|
||||
name /usr/lib/dyld (offset 12)
|
||||
Load command 8
|
||||
cmd LC_UUID
|
||||
cmdsize 24
|
||||
uuid 633B21A4-D14A-3312-A5AD-B3CB919DAC72
|
||||
Load command 9
|
||||
cmd LC_BUILD_VERSION
|
||||
cmdsize 32
|
||||
platform 1
|
||||
minos 26.0
|
||||
sdk 26.4
|
||||
ntools 1
|
||||
tool 3
|
||||
version 1266.8
|
||||
Load command 10
|
||||
cmd LC_SOURCE_VERSION
|
||||
cmdsize 16
|
||||
version 0.0
|
||||
Load command 11
|
||||
cmd LC_MAIN
|
||||
cmdsize 24
|
||||
entryoff 816
|
||||
stacksize 0
|
||||
Load command 12
|
||||
cmd LC_LOAD_DYLIB
|
||||
cmdsize 56
|
||||
name /usr/lib/libSystem.B.dylib (offset 24)
|
||||
time stamp 2 Wed Dec 31 19:00:02 1969
|
||||
current version 1356.0.0
|
||||
compatibility version 1.0.0
|
||||
Load command 13
|
||||
cmd LC_FUNCTION_STARTS
|
||||
cmdsize 16
|
||||
dataoff 16504
|
||||
datasize 8
|
||||
Load command 14
|
||||
cmd LC_DATA_IN_CODE
|
||||
cmdsize 16
|
||||
dataoff 16512
|
||||
datasize 0
|
||||
Load command 15
|
||||
cmd LC_CODE_SIGNATURE
|
||||
cmdsize 16
|
||||
dataoff 16608
|
||||
datasize 280
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
/owned/fixture:
|
||||
Load command 0
|
||||
cmd LC_SEGMENT_64
|
||||
cmdsize 72
|
||||
segname __PAGEZERO
|
||||
vmaddr 0x0000000000000000
|
||||
vmsize 0x0000000100000000
|
||||
fileoff 0
|
||||
filesize 0
|
||||
maxprot 0x00000000
|
||||
initprot 0x00000000
|
||||
nsects 0
|
||||
flags 0x0
|
||||
Load command 1
|
||||
cmd LC_SEGMENT_64
|
||||
cmdsize 232
|
||||
segname __TEXT
|
||||
vmaddr 0x0000000100000000
|
||||
vmsize 0x0000000000004000
|
||||
fileoff 0
|
||||
filesize 16384
|
||||
maxprot 0x00000005
|
||||
initprot 0x00000005
|
||||
nsects 2
|
||||
flags 0x0
|
||||
Section
|
||||
sectname __text
|
||||
segname __TEXT
|
||||
addr 0x0000000100000328
|
||||
size 0x0000000000000028
|
||||
offset 808
|
||||
align 2^2 (4)
|
||||
reloff 0
|
||||
nreloc 0
|
||||
flags 0x80000400
|
||||
reserved1 0
|
||||
reserved2 0
|
||||
Section
|
||||
sectname __unwind_info
|
||||
segname __TEXT
|
||||
addr 0x0000000100000350
|
||||
size 0x0000000000000060
|
||||
offset 848
|
||||
align 2^2 (4)
|
||||
reloff 0
|
||||
nreloc 0
|
||||
flags 0x00000000
|
||||
reserved1 0
|
||||
reserved2 0
|
||||
Load command 2
|
||||
cmd LC_SEGMENT_64
|
||||
cmdsize 72
|
||||
segname __LINKEDIT
|
||||
vmaddr 0x0000000100004000
|
||||
vmsize 0x0000000000004000
|
||||
fileoff 16384
|
||||
filesize 504
|
||||
maxprot 0x00000001
|
||||
initprot 0x00000001
|
||||
nsects 0
|
||||
flags 0x0
|
||||
Load command 3
|
||||
cmd LC_DYLD_CHAINED_FIXUPS
|
||||
cmdsize 16
|
||||
dataoff 16384
|
||||
datasize 56
|
||||
Load command 4
|
||||
cmd LC_DYLD_EXPORTS_TRIE
|
||||
cmdsize 16
|
||||
dataoff 16440
|
||||
datasize 64
|
||||
Load command 5
|
||||
cmd LC_SYMTAB
|
||||
cmdsize 24
|
||||
symoff 16512
|
||||
nsyms 3
|
||||
stroff 16560
|
||||
strsize 48
|
||||
Load command 6
|
||||
cmd LC_DYSYMTAB
|
||||
cmdsize 80
|
||||
ilocalsym 0
|
||||
nlocalsym 0
|
||||
iextdefsym 0
|
||||
nextdefsym 3
|
||||
iundefsym 3
|
||||
nundefsym 0
|
||||
tocoff 0
|
||||
ntoc 0
|
||||
modtaboff 0
|
||||
nmodtab 0
|
||||
extrefsymoff 0
|
||||
nextrefsyms 0
|
||||
indirectsymoff 0
|
||||
nindirectsyms 0
|
||||
extreloff 0
|
||||
nextrel 0
|
||||
locreloff 0
|
||||
nlocrel 0
|
||||
Load command 7
|
||||
cmd LC_LOAD_DYLINKER
|
||||
cmdsize 32
|
||||
name /usr/lib/dyld (offset 12)
|
||||
Load command 8
|
||||
cmd LC_UUID
|
||||
cmdsize 24
|
||||
uuid 633B21A4-D14A-3312-A5AD-B3CB919DAC72
|
||||
Load command 9
|
||||
cmd LC_BUILD_VERSION
|
||||
cmdsize 32
|
||||
platform 1
|
||||
minos 26.0
|
||||
sdk 26.4
|
||||
ntools 1
|
||||
tool 3
|
||||
version 1266.8
|
||||
Load command 10
|
||||
cmd LC_SOURCE_VERSION
|
||||
cmdsize 16
|
||||
version 0.0
|
||||
Load command 11
|
||||
cmd LC_MAIN
|
||||
cmdsize 24
|
||||
entryoff 816
|
||||
stacksize 0
|
||||
Load command 12
|
||||
cmd LC_LOAD_DYLIB
|
||||
cmdsize 56
|
||||
name /usr/lib/libSystem.B.dylib (offset 24)
|
||||
time stamp 2 Wed Dec 31 19:00:02 1969
|
||||
current version 1356.0.0
|
||||
compatibility version 1.0.0
|
||||
Load command 13
|
||||
cmd LC_FUNCTION_STARTS
|
||||
cmdsize 16
|
||||
dataoff 16504
|
||||
datasize 8
|
||||
Load command 14
|
||||
cmd LC_DATA_IN_CODE
|
||||
cmdsize 16
|
||||
dataoff 16512
|
||||
datasize 0
|
||||
Load command 15
|
||||
cmd LC_CODE_SIGNATURE
|
||||
cmdsize 16
|
||||
dataoff 16608
|
||||
datasize 280
|
||||
Loading…
Reference in New Issue