diff --git a/docs/native-investigation.md b/docs/native-investigation.md index 4dad23b3b1..b3051cc647 100644 --- a/docs/native-investigation.md +++ b/docs/native-investigation.md @@ -379,7 +379,7 @@ the tool fails with a `debugger-attach-denied` reason. Install a Ghidra 12.1.x release and the 64-bit full JDK it declares, then configure REA to use them. Ghidra analysis supports Linux x64/arm64 and macOS x64/arm64; each installation requires the matching native decompiler. Experimental Windows x64 P0 admits native -x86-64 PE applications on local NTFS using bundled Job Object ownership, +x86 and x86-64 PE applications and DLLs on local NTFS using bundled Job Object ownership, protected runtime DACLs, and handle-based path admission. See [Windows Ghidra P0](windows-ghidra-p0.md) and [issue #527](https://github.com/morluto/rea/issues/527). On Linux and macOS, `annotate_native_function` atomically edits a function name diff --git a/scripts/verify-packaged-ghidra-windows.mjs b/scripts/verify-packaged-ghidra-windows.mjs index 268d949d9e..be21fde120 100644 --- a/scripts/verify-packaged-ghidra-windows.mjs +++ b/scripts/verify-packaged-ghidra-windows.mjs @@ -26,12 +26,21 @@ if (process.platform !== "win32" || process.arch !== "x64") ); const exec = promisify(execFile); const arguments_ = process.argv.slice(2); +const internalWorkspace = arguments_[0] === "--internal-workspace"; +const fixtureArguments = internalWorkspace ? arguments_.slice(2) : arguments_; +const x86Fixture = fixtureArguments.includes("--x86"); +const dllFixture = fixtureArguments.includes("--dll"); +const [packageRootArgument, targetArgument, reportArgument] = + fixtureArguments.filter( + (argument) => argument !== "--x86" && argument !== "--dll", + ); // Windows locks the loaded package-owned addon until the verifier exits. // Keep installation cleanup in a parent that never loads that addon. -if (arguments_[0] !== "--internal-workspace") { +if (!internalWorkspace) { const ownedWorkspace = await mkdtemp(join(tmpdir(), "rea-packaged-ghidra-")); + let result; try { - const result = await exec( + result = await exec( process.execPath, [ fileURLToPath(import.meta.url), @@ -41,8 +50,6 @@ if (arguments_[0] !== "--internal-workspace") { ], { timeout: 900_000, maxBuffer: 8 * 1024 * 1024 }, ); - process.stdout.write(result.stdout); - process.stderr.write(result.stderr); } catch (cause) { if (typeof cause.stdout === "string") process.stdout.write(cause.stdout); if (typeof cause.stderr === "string") process.stderr.write(cause.stderr); @@ -50,17 +57,17 @@ if (arguments_[0] !== "--internal-workspace") { } finally { await rm(ownedWorkspace, { recursive: true, force: true }); } + const report = JSON.parse(result.stdout); + report.workspaceCleanup = true; + report.ok = true; + if (reportArgument !== undefined) + await writeFile(reportArgument, `${JSON.stringify(report)}\n`); + process.stdout.write(`${JSON.stringify(report)}\n`); + process.stderr.write(result.stderr); process.exit(0); } assert.ok(arguments_[1] !== undefined, "Missing verifier workspace."); const workspace = resolve(arguments_[1]); -const fixtureArguments = arguments_.slice(2); -const x86Fixture = fixtureArguments.includes("--x86"); -const dllFixture = fixtureArguments.includes("--dll"); -const [packageRootArgument, targetArgument, reportArgument] = - fixtureArguments.filter( - (argument) => argument !== "--x86" && argument !== "--dll", - ); let packageRoot = resolve(packageRootArgument ?? "."); // The default lane verifies the npm artifact in an isolated prefix. Explicit // package roots support an already-installed artifact without a second install. @@ -402,7 +409,7 @@ try { assert.deepEqual(await readdir(join(callerDirectory, name)), []); report.callerScriptCollisionsPreserved = true; report.runtimeCleanup = true; - report.ok = true; + // Only the parent can attest success after removing the installation. if (reportArgument !== undefined) await writeFile(reportArgument, `${JSON.stringify(report)}\n`); process.stdout.write(`${JSON.stringify(report)}\n`); diff --git a/src/ghidra/GhidraProvider.behavior.test.ts b/src/ghidra/GhidraProvider.behavior.test.ts index 88e1eb07fe..c607b99508 100644 --- a/src/ghidra/GhidraProvider.behavior.test.ts +++ b/src/ghidra/GhidraProvider.behavior.test.ts @@ -198,6 +198,32 @@ describe("Ghidra Mach-O slice support", () => { }); describe("Ghidra platform support", () => { + it("commits the PE role and managed classification to profile identity", async () => { + const ghidra = provider({ ...installationHost(), platform: "win32" }); + const application = peTarget("x86_64"); + const library = { + ...application, + executableRole: "shared-library", + } as const; + const digests = new Set(); + for (const target of [ + application, + library, + { ...library, managed: true }, + ]) { + const resolved = await ghidra.resolveAnalysisProfile(target); + if (!resolved.ok) throw resolved.error; + const profile = resolved.value.profile; + if (profile === null) throw new Error("Expected a PE analysis profile"); + expect(profile.parameters).toMatchObject({ + executable_role: target.executableRole, + managed: target.managed, + }); + digests.add(profile.digest); + } + expect(digests.size).toBe(3); + }); + it("keeps Windows annotation mutation unavailable independently of native controls", () => { const ghidra = provider({ ...installationHost(), platform: "win32" }); expect(