-
Notifications
You must be signed in to change notification settings - Fork 0
Change Report Schema
The stable, machine-consumable output of twinflame … -f json -o OUT (JSON is the
default format). Downstream tooling (decompiler alignment, diff viewers) should
consume this, not the human stdout summary. The shape is versioned by the
top-level schema_version (twinflame.changes.CHANGES_SCHEMA_VERSION); a breaking
change bumps it. -f csv / -f xml carry the same per-class fields in a flat form.
v2 adds lhs_super / rhs_super, lhs_interfaces / rhs_interfaces, and
components to every entry (type-graph context for malware triage). v3 adds
library — the dictionary-evidence provenance label (<coord>@<version-range>) when the
class matched the known-library signature dictionary (--libsigs).
{
"schema_version": 3,
"summary": { "modified": 384, "added": 282, "removed": 135, "cosmetic": 414, "unchanged": 10436 },
"changes": [ /* one object per class, ranked most-review-worthy first */ ]
}| Field | Type | Meaning |
|---|---|---|
schema_version |
int | Format version. Consumers should check this. |
summary |
object | Count per kind (keys absent when zero). |
changes |
array | One entry per class, pre-sorted (see Ordering). |
{
"kind": "modified",
"lhs": "Lorg/telegram/ui/ArticleViewer;",
"rhs": "Lorg/telegram/ui/ArticleViewer;",
"source_file": "SourceFile",
"origin": "app",
"confidence": "high",
"magnitude": 95,
"match_distance": 0.87,
"anchored": true,
"lhs_super": "Landroid/app/Service;",
"rhs_super": "Landroid/app/Service;",
"lhs_interfaces": [],
"rhs_interfaces": [],
"components": ["BAS"],
"library": null,
"delta": { /* only for kind=modified; see below */ },
"methods": [ /* only for kind=modified when localizable; see below */ ]
}| Field | Type | Meaning |
|---|---|---|
kind |
enum |
modified | added | removed | cosmetic | unchanged (see Kinds). |
lhs |
string | null | Old-build class descriptor (L…;). null when added. |
rhs |
string | null | New-build class descriptor. null when removed. |
source_file |
string | null |
SourceFile attribute if retained (R8 often rewrites it to the literal "SourceFile" or strips it). |
origin |
enum |
app | library | unknown — provenance (see Origin). |
confidence |
enum |
high | low (see Confidence). Non-modified is always high. |
magnitude |
int | Review-worthiness. For modified: feature-delta count + methods added/deleted. For added/removed: class size in instructions. Else 0. |
match_distance |
float | Structural similarity of the underlying match in [0,1] (1.0 = identical). 0.0 for added/removed. |
anchored |
bool | Match rests on a rename-invariant string / framework-call anchor. |
lhs_super / rhs_super
|
string | null | Superclass descriptor per side. Framework supers (Landroid/…;) survive R8, so they carry across the rename. |
lhs_interfaces / rhs_interfaces
|
array | Declared interface descriptors per side. |
components |
array | Sensitive-component short-codes the class implements (transitive superclass/interface resolution): BNL BindNotificationListener, BAS BindAccessibilityService, BDA BindDeviceAdmin, BIM BindInputMethod, BAF BindAutofill, plus generic SVC/RCV/PRV/APP. Empty for ordinary classes. |
library |
string | null |
(v3) Dictionary-evidence provenance: <coord>@<version-range> (e.g. com.squareup.okhttp3:okhttp@[4.9.0,4.12.0]) when the class matched the known-library signature dictionary (--libsigs). Set even for renamed classes that name/prefix heuristics can't reach; null when no dictionary was loaded or the class didn't match. |
delta |
object | absent | Present only for modified. |
methods |
array | absent | Per-method localization; present for modified when method-level changes are localizable. |
All arrays are sorted; framework calls/strings/types are full descriptors. app_types_*
are in new-build naming (translated through the class-match map).
"delta": {
"calls_added": ["Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri;"],
"calls_removed": [],
"strings_added": ["v2/pay"],
"strings_removed":["v1/pay"],
"types_added": ["Landroidx/appcompat/app/AppCompatActivity;"],
"types_removed": [],
"app_types_added": ["Lorg/telegram/ui/Foo;"],
"app_types_removed": []
}calls_* = framework/library call targets; strings_* = string constants;
types_* = framework type references (super/interface/field/param); app_types_* =
app type dependencies (only populated when a class-match map was available).
"methods": [
{ "status": "modified", "name": "onCreate", "descriptor": "(Landroid/os/Bundle;)V",
"instr_count": 214, "calls_added": ["…Uri;->parse…"], "calls_removed": ["…Log;->d…"] },
{ "status": "added", "name": "helper", "descriptor": "()V", "instr_count": 12,
"calls_added": ["…"], "calls_removed": [] }
]status ∈ modified | added | deleted. name/descriptor are obfuscated when the
build renames them — resolve via the --deobfuscation-map. modified methods appear only
when their framework-call set changed (a body-only re-optimization isn't localized).
| Kind | Meaning |
delta/methods
|
|---|---|---|
modified |
Matched; semantic features or method structure changed — a real edit. | yes |
added |
Only in the new build. | no |
removed |
Only in the old build. | no |
cosmetic |
Matched, structurally different, but no semantic-feature change → re-obfuscation/re-optimization noise. Emitted, demoted. | no |
unchanged |
Matched and structurally identical. | no |
-
app— under the developer package (--app-package/--package, else the manifest). Highest priority. -
library— bundled third party: known prefix, a maven-coordinateSourceFile, or (v3) signature-dictionary evidence (thelibraryfield is set). Dictionary evidence demotes a class tolibraryunless the dev prefix claims it first — a library vendored under the app's own package still reviews as app code. Demoted. -
unknown— can't tell (e.g. R8 repackaged it to a short name). Ranked between.
-
high— trustworthy change. -
low— amodifiedresting only on framework-call churn in non-app code. Different R8 versions relocate calls between classes via inlining, so this is the cross-toolchain-noise signature (measured 95% of false-modifiedon a same-source cross-R8 rebuild). Filter these out with--min-confidence high.
changes[] is pre-sorted most-review-worthy first: kind (modified → added → removed
→ cosmetic → unchanged), then origin (app → unknown → library), then confidence
(high → low), then descending magnitude. Consumers may re-sort; the origin/confidence/
magnitude fields carry everything needed to filter or re-rank.
"Demote, never drop": by default every matched/added/removed class is emitted with a score and
category — the report is complete and the consumer thresholds. --min-confidence high is the
one built-in filter (drops low-confidence modified).
User guide
Internals
Evaluation
Repo