Skip to content

fix(findrefs): resolve a string match through its own string_data_item - #56

Merged
MG1937 merged 7 commits into
MG1937:dev-0.1.0from
Henglie:fix/string-locator-order
Oct 29, 2026
Merged

MG1937 merged 7 commits into
MG1937:dev-0.1.0from
Henglie:fix/string-locator-order

Conversation

@Henglie

@Henglie Henglie commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #54.

Root cause

StringLocator assumed two things that the DEX format does not require:

  1. the string_data_items are written back to back, so the first and last string_ids entry bound the region;
  2. they are written in string_ids order, so the item behind a match carries index + 1 — which let the locator map a match through the following offset and subtract one.

d8/R8 satisfy both, but a layout pass such as dexlayout permutes the items, and vendor tools repackage system APKs that way. A synthetic DEX whose string_data is permuted (offsets, signature and checksum rewritten, code unchanged) shows both failure modes from the report:

layout before after
index order correct correct
full reversal region collapses to start > end, every query returns nothing correct
middle permutation first → {1} instead of {3} correct

Fix

  • The region is taken from the smallest and largest offset instead of the first and last entry.
  • A match ends at the first terminator behind its start, since a MUTF-8 payload never holds 0x00 on its own. A match that reaches past that terminator runs into the following item and names no string.
  • When the offsets are already in index order, the old single dict lookup still resolves the match, so d8/R8 output keeps its cost. A permuted layout searches the ascending offsets with bisect instead, and the layout is decided once while building the map.
  • Building the map unpacks the offset array once instead of 68k unpack_from calls, and fills the lookup with update(zip(...)).

Tests

tests/test_string_locator_order.py covers the ordered, reversed and middle-permutation layouts, a substring that must map to the item that holds it, a match that spans two items, and the findrefs output for the reversed layout. tests/dex_fixture.py gains reorder_string_data(), which moves the items inside their region and recomputes the SHA-1 signature and Adler-32 checksum, so the result stays a valid DEX — the test asserts that every index still resolves to the same string before using it.

Verification

  • Reverting string_locator.py to 9216fe2 makes the new file fail 13 subtests; with the fix all 7 pass.
  • tests/run_tests.py --suite unit: 72 tests pass (65 existing + 7 new).
  • tests/run_tests.py --suite integration: 5 tests pass.
  • tests/benchmark_compare.py --baseline <base> --samples 15 --cases unit: no gated regression. unit/string_locator −35%, unit/type_locator −4%, unit/method_locator +2.5% (p=0.30), unit/field_locator +2.0% (p=0.30).
  • tests/benchmark_reference.py --samples 3: all 16 count metrics match reference-baseline.json.

Not verified here: the GUI suite (this machine is Windows with no Tk display), and benchmark_reference.py's 0.0880 s decompilation budget, which the base commit misses on this machine as well (0.28 s vs 0.33 s) — the recorded baseline is a Linux CI number and the decompiler path is untouched.

Note

The end of an item is still taken as the start of the next one: string_data_item has no alignment requirement and both d8 and dexlayout write the items back to back. A layout that pads between items falls back to the search path, which bounds the item by the following offset.

MG1937 and others added 7 commits October 11, 2026 04:29
fix(findrefs,listclass): bound exact-member scan, decode names as MUTF-8
1. Refactor the asc client to enhance code robustness and break down the god class.
2. Add support for finding extends and implements to findrefs.
3. Update the benchmark.
Rust version: This project has been rewritten 1:1 in Rust as rust_ASC. The Rust version is 10× faster and supports full APK decompilation: with 16 threads, it decompiles a 383 MB APK in just 4 seconds. ASC and rust_ASC will be updated in sync going forward.

If your workflow is written in Python, you can continue integrating with this project; it remains the fastest Python Android decompiler available. If you only need a binary, please use rust_ASC.
perf: prefix-only DEX inflate for faster class lookup
StringLocator assumed the string_data_items are written in string_ids order
and back to back: it took the region bounds from the first and last entry,
mapped a match to the offset of the item that follows it, and subtracted one
from that index. The format does not require that layout, and dexlayout (or
the vendor tools that repackage system APKs) permutes the items, so findrefs
returned the wrong methods or nothing at all: a reversed layout collapses the
region to start > end, a partial permutation returns neighbouring indexes.
Method and field queries that resolve names through the locator are affected
the same way.

Take the region from the smallest and largest offset, and end a match at the
first terminator behind its start: a match that reaches past it runs through
the terminator into the following item and names no string. When the offsets
are in index order, the item behind a match still carries the next index and a
single dict lookup resolves it, so d8/r8 output keeps the old cost; a permuted
layout searches the ascending offsets instead. Building the map also stops
unpacking every entry by hand, which leaves the string locator faster than
before (-35% on the supplied DEX workload, no gated regression).

tests/dex_fixture.py gains reorder_string_data(), which rewrites a fixture in
a given order and recomputes the signature and checksum, so the new tests
cover the ordered layout, a full reversal, and a middle permutation, plus the
findrefs output for each.
@MG1937
MG1937 changed the base branch from main to dev-0.1.0 October 25, 2026 16:05
@MG1937

MG1937 commented Oct 26, 2026

Copy link
Copy Markdown
Owner

貌似有性能劣化,两个测试集没过,要不优化一下?

@MG1937
MG1937 merged commit a02ae9f into MG1937:dev-0.1.0 Oct 29, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

findrefs: wrong or missing results when string_data items are not stored in string_ids order

2 participants