Files
reverse-engineering-anything/src/application/android/AndroidAnalysisService.ts
T
gensokyo-dev 570abe95f2 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.
2026-10-27 18:53:22 +08:00

91 lines
3.1 KiB
TypeScript

import { analysisOperationInputErrorFromIssues } from "../../domain/inputIssueProjection.js";
import type { EvidenceWriter } from "../investigation/InvestigationRecordPort.js";
import { summarizeRetainedAndroidSearch } from "./AndroidSearchEvidenceSummary.js";
import type { AndroidAnalysisPort } from "./AndroidAnalysisPort.js";
import type { ExecutionOptions } from "../AnalysisProvider.js";
import { parseBinaryTarget } from "../BinaryTargetResolver.js";
import {
androidRequestSchema,
type AndroidOperation,
} from "../../domain/android/androidAnalysis.js";
import { AnalysisCancelledError } from "../../domain/analysisErrorCore.js";
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
import { createEvidence, type Evidence } from "../../domain/evidence.js";
import { err, type Result } from "../../domain/result.js";
import { completeProviderEvidence } from "../ProviderOperationExecution.js";
/** Shared CLI/MCP admission, execution and Evidence composition for Android. */
export class AndroidAnalysisService {
constructor(
readonly provider: AndroidAnalysisPort,
readonly recordEvidence?: EvidenceWriter["recordEvidence"],
) {}
/** Inspect a caller-selected APK and retain producing-provider provenance inline. */
async execute(
operation: AndroidOperation,
input: unknown,
options?: ExecutionOptions,
): Promise<Result<Evidence, AnalysisError>> {
if (options?.signal?.aborted === true)
return err(
new AnalysisCancelledError(operation, { cause: options.signal.reason }),
);
const request = androidRequestSchema.safeParse({ operation, input });
if (!request.success)
return err(
analysisOperationInputErrorFromIssues(
operation,
request.error.issues,
input,
request.error,
),
);
const target = await parseBinaryTarget(
request.data.input.path,
options?.signal === undefined ? {} : { signal: options.signal },
);
if (!target.ok) return target;
const executed = await this.provider.execute(
target.value,
request.data,
options,
);
if (!executed.ok) return executed;
const execution = executed.value;
const completed = completeProviderEvidence(
createEvidence(
execution.subject === undefined
? target.value
: (execution.subject ?? undefined),
execution.provider,
{
operation,
parameters: request.data.input,
result: execution.result,
rawResult: execution.rawResult,
limitations: execution.limitations,
locations: execution.locations,
confidence:
operation === "inspect_android_method" ||
operation === "inspect_android_jni_bindings"
? "derived"
: "observed",
},
),
options?.signal,
);
if (
!completed.ok ||
operation !== "search_android_classes" ||
execution.searchRawSummary === undefined
)
return completed;
return summarizeRetainedAndroidSearch(
completed.value,
execution.searchRawSummary,
this.recordEvidence,
);
}
}