rosetta-frida¶
Translate real (unobfuscated) Java class, method, and field names to the per-version obfuscated names your target app actually uses — at Frida-attach time.
Write one hook. Ship a map per release. Stop rewriting Frida scripts every time the obfuscator rotates.
-
Write once
Hooks reference real names. The library translates to obfuscated names through a per-version map loaded at attach time. The same source compiles unchanged across every app version that has a matching map.
-
Per-version maps
On-disk strict JSON — human-readable and machine- round-trippable. Comment-bearing YAML and TypeScript-module authoring inputs convert to canonical JSON via the CLI.
-
Health-checked at attach
Before your hooks run, rosetta verifies the loaded map matches the running app (package, version, class lookups, AIDL descriptors, anchor strings). Wrong-map disasters surface as a single readable error, not a cascade of nulls.
-
CLI included
rosetta init,pull,validate(+--deep),convert,patch,extract,inspect,diff,merge,types— scaffold maps, pull verified maps from the community repo at build time, swap them into compiled bundles, diff/merge/deep-validate maps, generate real-name type stubs, and audit what every bundle actually targets.
The 30-second pitch¶
import { rosetta, type RosettaMap } from 'rosetta-frida';
import sampleMap from './maps/com.example.app/30405.json' with { type: 'json' };
const map = sampleMap as unknown as RosettaMap;
Java.perform(() => {
// Open a session. Auto-detects the running app + version,
// validates them against the loaded map, runs a health check.
rosetta.session({ map });
// Hook by real name. rosetta translates to the obfuscated name
// (e.g. `aaaa.c`) under the hood.
rosetta.hook(
'com.example.app.IRemoteService$Stub.requestTicket',
function (bundle, callback) {
send({ stage: 'requestTicket', keys: bundle.keySet() });
return rosetta.proceed(bundle, callback);
},
);
});
When the obfuscator rotates aaaa → aaab in the next release, you do
not change a single line of the hook. You ship a new map for 3.5.0
and the same script targets the new version.
Why this exists¶
Large commercial Android apps rotate obfuscation on every minor
release. The class anchors that worked for 1.2.x do not survive
1.3.x. The method letters inside often stay stable — but the class
names at the left side of the table rotate, breaking every hook in
the field.
Today the recovery loop is:
- Static-analysis pass (jadx + diff against the previous version) to discover the new obfuscated names.
- Patch every Frida script that hard-codes those names.
- Re-compile every bundle.
- Re-test on a device.
rosetta-frida decouples what we want to hook (real name) from how it is spelled today (obfuscated name) by introducing per-version mapping files and a lookup layer that consults them at attach time.
See Concepts for the full motivating story.
What's in V1.0¶
- Three-tier API.
Tier 1 is the declarative
rosetta.hook(...)androsetta.field(...)shorthand for the common 90% of hooks. Tier 2 is theJava.use-shaped surface for users who want the familiarKlass.method.overload(...).implementation = fnshape with name translation. Tier 3 is the low-level escape hatch:rosetta.map.resolveClass(...),rosetta.events.on(...), runtime overrides. - Session lifecycle. In-process auto-detect of app + version (via
PackageManager.getPackageInfo, no ADB required), registry-bundle picking with optional fuzzy fallback, attach-time health check (class resolution, AIDL descriptors, anchor strings; fails fast instrictmode). - Strict validation. A Zod validator rejects malformed maps with
structured
{path, message}error reports — no silent corruption when a map drifts. The validator tracks the canonical map schema owned byrosetta-maps. - Comment-bearing authoring input. YAML supported via
rosetta convert. Maps are pure data; TS/JS inputs are not supported. - PEM-style marker block. Maps embed into the compiled bundle
between
-----BEGIN ROSETTA MAP-----/-----END ROSETTA MAP-----comments. Swap maps without recompiling viarosetta patch. - CLI tooling.
init,pull,validate(+--deep),convert,patch,extract,inspect,diff,merge,types.pullfetches the verified map for an(app, version_code)from the communityrosetta-mapsrepo at build time (never on the device). - Sample map and sample hook. A 15-class anonymized example map covering AIDL stubs, callbacks, overloads, fields, enums, anonymous inner classes.
- 100% coverage. Every line of the runtime exercised (see the repository's CI for the current test count).
What is not in V1.0:
- Runtime self-healing / discovery (deferred to V2).
- A populated public maps corpus (the
rosetta-mapsrepo is scaffolded androsetta pullalready fetches from it at build time; V1 still ships the example map in-repo as the worked reference). rosetta diff,merge,types,migrateCLI commands and thevalidate --deepsemantic checks (deferred to V1.5;migratestill pending). The build-time community-registry fetch shipped in V1.0 asrosetta pull.- Native (JNI / ELF symbol) mapping (deferred to V2+).
See Changelog for the full V1.0 changelog and the Roadmap for what's next (purpose + benefit of each upcoming item).
Getting started¶
Install rosetta-frida Read the quick start
Where this fits¶
flowchart LR
A[sigmatcher] -->|emit| M[rosetta map (JSON)]
B[jadx + hand-author] -->|emit| M
M -->|imported at build time| C[frida-compile]
C --> D[hook.bundle.js
marker block embedded]
D --> F[Frida runtime]
F --> G[(running Android app)]
rosetta-frida is the consumer of maps. It does not analyze APKs
(sigmatcher does that). It does not replace Frida (your existing
controller — Python frida, frida CLI, frida-node — loads the
compiled bundle unchanged).
What rosetta-frida is not¶
- Not a deobfuscator. It consumes maps. Use jadx / sigmatcher / hand-authoring to produce them.
- Not a hook framework. It doesn't define what a "hook" is — Frida
does. rosetta-frida just makes
Java.usesmarter. - Not a host-side orchestrator. It's a library that runs inside the Frida JS script. Your Python / Node / CLI controller stays unchanged.
- Not iOS / desktop / non-Android Frida (V1.0). Future versions may add native-side mapping and non-Android targets.
- Not an auto-discoverer of obfuscated names (V1.0). Maps are the only source of translation. Self-healing runtime discovery is on the V2+ roadmap.
Status¶
V1.0 is functionally complete and exercised by a full test suite at
100% line/branch/function/statement coverage. Today, install it by
cloning and building from source; once the first release is tagged it
will also be available from npm (npm install rosetta-frida) — see
Installation. Publishing is
tag-driven: a v* version tag triggers a release that rebuilds,
re-runs the coverage gate, and publishes with npm provenance. For the
milestone overview, see the Roadmap.