mirror of
https://github.com/morluto/rea.git
synced 2026-11-11 01:13:39 +00:00
feat(android): link native declarations to static JNI candidates (#7472)
Link original DEX native declarations to per-ABI function exports and relocated static JNI table candidates through shared CLI/MCP workflows. Preserve global attribution ambiguity, exact provenance, partial coverage and retained Evidence; default MCP delivery to bounded summaries with filtered/batch pages. Use one immutable ELF relocation decoder with streaming IL2CPP materialization, cooperative bounded export/version parsing and bounded JNI state. Reuse validated session inventories without loading target libraries or executing JNI code. Validation: relevant type/lint/build and parser/domain/public CLI/MCP regressions; genuine four-ABI NDK fixtures and independent library/slot oracles; actual JDK/JADX Conscrypt workflows with retained-source removal. Full measurements and limitations are recorded in docs/verification/android-jni-bindings-2026-10-27.md. Closes #6403.
This commit is contained in:
1 parent
740c0f5615
commit
570abe95f2
60 files changed
+8414
-330
No files matched your search
@@ -0,0 +1,99 @@
|
||||
import { expect, it } from "vitest";
|
||||
import { readAndroidNativeInventory } from "./AndroidNativeInventory.js";
|
||||
import type { JadxToolPort } from "./JadxAnalysis.js";
|
||||
import type { JsonValue } from "../domain/jsonValue.js";
|
||||
|
||||
const SNAPSHOT = "/admitted/immutable target.apk";
|
||||
|
||||
const row = {
|
||||
source_index: 0,
|
||||
class_name: "fixture.Native",
|
||||
method_name: "read_原",
|
||||
descriptor: "([B)J",
|
||||
dex_entry: "classes.dex",
|
||||
dex_entry_index: 0,
|
||||
class_index: 2,
|
||||
method_index: 5,
|
||||
};
|
||||
const inventory = () => ({
|
||||
scope: "apk-root-dex-declarations",
|
||||
methods: [row],
|
||||
coverage: {
|
||||
status: "complete",
|
||||
dex_entries: 1,
|
||||
examined_dex_entries: 1,
|
||||
examined_classes: 3,
|
||||
examined_methods: 9,
|
||||
},
|
||||
unknowns: [] as string[],
|
||||
});
|
||||
const tools = (value: unknown): JadxToolPort => ({
|
||||
json: async (name, input) => {
|
||||
expect(name).toBe("rea_native_declarations");
|
||||
expect(input).toEqual({ path: SNAPSHOT });
|
||||
return value as JsonValue;
|
||||
},
|
||||
text: async () => {
|
||||
throw new Error("Native inventory must not request source");
|
||||
},
|
||||
listClasses: async () => {
|
||||
throw new Error("Native inventory must not page display classes");
|
||||
},
|
||||
});
|
||||
|
||||
it("preserves duplicate native identities from distinct original DEX members", async () => {
|
||||
const source = inventory();
|
||||
source.methods.push({
|
||||
...row,
|
||||
source_index: 1,
|
||||
dex_entry: "classes2.dex",
|
||||
dex_entry_index: 2,
|
||||
});
|
||||
source.coverage.dex_entries = 2;
|
||||
source.coverage.examined_dex_entries = 2;
|
||||
const actual = await readAndroidNativeInventory(tools(source), SNAPSHOT);
|
||||
expect(actual.methods).toEqual(source.methods);
|
||||
expect(actual.coverage.status).toBe("complete");
|
||||
});
|
||||
|
||||
it("keeps usable facts and explicit incomplete DEX coverage", async () => {
|
||||
const source = inventory();
|
||||
source.coverage.status = "partial";
|
||||
source.coverage.dex_entries = 2;
|
||||
source.unknowns.push("classes2.dex cannot be read");
|
||||
expect(await readAndroidNativeInventory(tools(source), SNAPSHOT)).toEqual(
|
||||
source,
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
(source: ReturnType<typeof inventory>) => {
|
||||
source.methods[0] = { ...row, descriptor: "not a prototype" };
|
||||
},
|
||||
(source: ReturnType<typeof inventory>) => {
|
||||
source.methods[0] = { ...row, source_index: 7 };
|
||||
},
|
||||
(source: ReturnType<typeof inventory>) => {
|
||||
source.coverage.examined_dex_entries = 2;
|
||||
},
|
||||
(source: ReturnType<typeof inventory>) => {
|
||||
source.coverage.dex_entries = 2;
|
||||
},
|
||||
(source: ReturnType<typeof inventory>) => {
|
||||
source.coverage.examined_methods = 0;
|
||||
},
|
||||
(source: ReturnType<typeof inventory>) => {
|
||||
source.unknowns.push("hidden incomplete input");
|
||||
},
|
||||
(source: ReturnType<typeof inventory>) => {
|
||||
source.methods.push({ ...row, source_index: 1, dex_entry: "other.dex" });
|
||||
},
|
||||
])("rejects malformed or inconsistent producer facts (%#)", async (mutate) => {
|
||||
const source = inventory();
|
||||
mutate(source);
|
||||
await expect(
|
||||
readAndroidNativeInventory(tools(source), SNAPSHOT),
|
||||
).rejects.toMatchObject({
|
||||
_tag: "AnalysisOutputError",
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user