Skip to content

Commit 016efac

Browse files
tedsluisclaude
andcommitted
feat: add Lambda Dispatcher Resolver tool and APK-tooling ideas backlog
Resolves R8-merged synthetic lambda-dispatcher classes (the krb/aie/esk/ftw pattern this project has repeatedly had to resolve by hand during APK reverse engineering) to their exact smali+JADX source evidence, via a list/resolve CLI over androguard (DEX structure) + a narrow smali reader (packed-switch/sparse-switch/if-chain resolution) + a JADX case-block correlator. All SPEC.md acceptance criteria pass (12 pytest cases) against the real decompiled APK. Two bugs were caught only by verifying against the raw smali rather than assuming: baksmali's pswitch_N label suffix does not match the case value it's reached by, and a packed-switch's "no case matched" fallthrough sits right after the switch opcode, not after the last case label. Also fixes a design mistake in the tool's own SPEC.md: an earlier draft proposed committing byte-exact smali/JADX excerpts as test fixtures, which is exactly what PROJECT_RULES.md §8 rule 20 bans (decompiled Google code, even as research output). Tests now read from the maintainer's local, already-gitignored reverse-engineering/apk/ tree and skip themselves when it's absent; .gitignore updated to keep this tool's own venv/caches (but never decompiled content) out of history the same way. BACKLOG.md records the broader tooling ideas (UUID/BLE context reconstruction, dataflow, schema batch-extraction, payload decoding, version diffing) raised while scoping this tool, for later triage - none designed or committed to being built yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 32c67cb commit 016efac

14 files changed

Lines changed: 1645 additions & 0 deletions

File tree

.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ local.properties
2323
reverse-engineering/apk/
2424
*.apk
2525

26+
# Lambda Dispatcher Resolver tool (reverse-engineering/tools/lambda_dispatcher_resolver/):
27+
# its own Python source is tracked normally (original tooling code, not derived APK content),
28+
# but its virtualenv/caches are not, and — same rule as above, same reason — it must never gain
29+
# a committed copy of decompiled smali/JADX output under its own tests/ directory either; its
30+
# test suite runs against the maintainer's local reverse-engineering/apk/<version>/ tree instead
31+
# and skips itself when that tree isn't present (see that tool's own SPEC.md §11 and tests/).
32+
reverse-engineering/tools/*/.venv/
33+
reverse-engineering/tools/*/*.egg-info/
34+
**/__pycache__/
35+
.pytest_cache/
36+
2637
# OS/editor cruft
2738
.DS_Store
2839
Thumbs.db

TODO.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,23 @@ lower priority than finishing ANC/Battery/EQ):**
378378

379379
## Phase 2 — APK reverse engineering
380380

381+
- [x] **Added 2026-09-16, implemented same day — Lambda Dispatcher Resolver.**
382+
`reverse-engineering/tools/lambda_dispatcher_resolver/` (`SPEC.md` for the design; `README.md`
383+
for usage) mechanically resolves R8-merged synthetic lambda-dispatcher classes (the
384+
`krb`/`aie`/`esk`/`ftw` pattern this project has repeatedly had to resolve by hand — see
385+
`REVERSE_ENGINEERING.md`'s `qhr`/`fye` entry and `frb`-`gjv` entry) to their exact smali+JADX
386+
source evidence, via a `list`/`resolve` CLI over androguard (DEX structure) + a narrow smali
387+
reader (packed-switch/sparse-switch/if-chain resolution) + a JADX case-block correlator. All of
388+
`SPEC.md` §10's acceptance criteria pass (12 pytest cases, including the adversarial
389+
out-of-range-discriminator fixture) against the real, locally-decompiled APK — no decompiled
390+
content is committed anywhere in the tool's own tree (a design mistake in `SPEC.md`'s original
391+
§11 text, caught and corrected during implementation; see that section's own note). Deliberately
392+
scoped to only this one capability — a possible future broader UUID/BLE-analysis pipeline is
393+
tracked as ideas, not designed, in `reverse-engineering/tools/BACKLOG.md`.
394+
**Not yet done:** wiring this into an actual APK-RE session's workflow (e.g. re-running it
395+
against `MaestroEndpointService`'s own multibinding-assembly search, still open per
396+
`REVERSE_ENGINEERING.md`'s own entry) — the tool exists and is tested, but hasn't yet been used
397+
to advance a real open protocol question.
381398
- [x] **Groundwork/tooling — done 2026-08-30.** Governance, storage, and procedure now in place so
382399
the actual analysis work below can start; none of it constitutes analysis having happened yet:
383400
`DECISIONS.md` ADR-017 (supersedes ADR-003) permits AI mechanical assistance — search, `pbtk`
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# BACKLOG.md — Future APK-analysis tooling ideas
2+
3+
A parking lot for tooling ideas raised while designing and building
4+
[`lambda_dispatcher_resolver/`](lambda_dispatcher_resolver/SPEC.md), so they aren't lost and can be
5+
triaged later. **Nothing here is designed, approved, or committed to being built** — this is
6+
deliberately one level less formal than `lambda_dispatcher_resolver/SPEC.md`. An idea graduates out
7+
of this file by getting its own spec document (following `lambda_dispatcher_resolver/SPEC.md` as
8+
the template) once the maintainer decides to pursue it.
9+
10+
## Governance that applies to every idea below, unconditionally
11+
12+
Any tool built from this list operates inside the exact same boundary
13+
`lambda_dispatcher_resolver/SPEC.md` §8 already established, not a fresh negotiation each time:
14+
15+
- Mechanical assistance only (`DECISIONS.md` ADR-017) — a tool may search, extract, and correlate;
16+
it never decides a finding's relevance to the Bluetooth protocol, and never writes into
17+
`REVERSE_ENGINEERING.md`/`PROTOCOL.md`/`DECISIONS.md`/any `CAP-NNN-FINDINGS.md` itself.
18+
- **No decompiled APK content is ever committed, in any form, including as test fixtures**
19+
`lambda_dispatcher_resolver/SPEC.md` §11 documents a real mistake this project's own first attempt
20+
at this made (committing byte-exact smali/JADX excerpts as "just test fixtures"), caught only
21+
after the fact against this repo's own `.gitignore` comment quoting `PROJECT_RULES.md` §8 rule 20.
22+
Every future tool's tests read from the maintainer's local, gitignored
23+
`reverse-engineering/apk/<version>/` tree and skip themselves when it's absent — never a
24+
committed copy.
25+
- Reuse this project's own FACT/HYPOTHESIS/ASSUMPTION/OPEN QUESTION vocabulary (`PROJECT_RULES.md`
26+
§1) for anything a tool's output implies about protocol meaning — never invent a parallel
27+
confidence taxonomy (an earlier draft proposal for this tooling area used
28+
observed/inferred/confirmed; rejected for exactly this reason).
29+
- Test against already-confirmed findings as regression fixtures before trusting a tool on
30+
something new (`lambda_dispatcher_resolver/SPEC.md` §10's own worked-example discipline,
31+
generalizing `AGENTS.md` §11's "test against real capture fixtures" rule to APK-analysis tooling).
32+
- Decide each tool's own git-tracking boundary explicitly (source code: yes; anything derived from
33+
or copied out of the decompiled APK: no) rather than assuming — per the point above.
34+
35+
## Suggested (not binding) priority order, if any of these are pursued
36+
37+
1. An androguard-based general structural code index (below) — natural next step once
38+
`lambda_dispatcher_resolver` already has androguard wired up; replaces ad hoc
39+
`grep -rl "extends X"`/`grep -rn "LFoo;->bar("` searches with a real query.
40+
2. UUID extraction + BLE/GATT context reconstruction — the original idea's own core ambition.
41+
3. Limited dataflow analysis — highest risk/effort of the group; do this only after (1) and (2)
42+
prove the approach is worth the investment, and scope it to single-basic-block tracking only at
43+
first (per the original proposal's own §6).
44+
4. Schema/payload/version tooling (items below) — independently useful, no dependency on 1-3.
45+
46+
This ordering is a lens, not a commitment — the maintainer may pick any subset in any order.
47+
48+
---
49+
50+
## Idea: general androguard-based structural code index
51+
52+
Generalizes `lambda_dispatcher_resolver`'s Layer 1 beyond just lambda-dispatcher detection into a
53+
standing query tool: "list every class implementing interface X," "find every caller of method
54+
Y" (an XREF search — exactly what a manual `grep -rn "Lgiz;->p("` search stood in for during the
55+
2026-09-15 `gjv.p()` trace, `REVERSE_ENGINEERING.md`'s `frb`-`gjv` entry), "list every subclass of
56+
Z." Cheap once androguard is already a dependency; would remove most remaining need for ad hoc
57+
smali `grep` during a research session.
58+
59+
## Idea: UUID extraction + BLE/GATT context reconstruction
60+
61+
The original, broader ambition this backlog's own tooling effort started from: UUID → usage
62+
location → BLE operation (`BluetoothGattCharacteristic`/`writeCharacteristic`/
63+
`setCharacteristicNotification`/etc.) → possible protocol role, as a relationship graph rather than
64+
a bare list of UUID strings. Large scope — likely its own multi-phase spec document if pursued, and
65+
depends on the structural code index above for the "usage location" half.
66+
67+
## Idea: limited dataflow analysis
68+
69+
Tracking a byte-array's construction (`new-array`/`fill-array-data`/`aput-byte`, or equivalent
70+
protobuf-builder calls) forward to the `writeCharacteristic`/`WriteSetting` call site it ends up
71+
at. The highest-risk item on this list — general dataflow across branches/loops is genuinely hard
72+
to get right and to trust. If ever attempted, scope the first version to straight-line,
73+
single-basic-block flows only, and require the same worked-example test discipline as
74+
`lambda_dispatcher_resolver` before it's used on anything not already manually verified.
75+
76+
## Idea: protobuf/`RawMessageInfo` schema batch-extractor
77+
78+
`scripts/decode_rawmessageinfo.py` already recovers one `GeneratedMessageLite` class's schema at a
79+
time (this is how `qhr`/`qjc`/`qjb`/etc. were originally recovered). A batch mode that scans the
80+
whole decompiled tree for every class matching that same generated-message shape and builds a full
81+
schema register automatically would remove most of the remaining manual work in
82+
`REVERSE_ENGINEERING.md`'s own schema-recovery entries.
83+
84+
## Idea: wire-payload-vs-schema auto-decoder
85+
86+
Given raw hex bytes + a DLCI, attempt to decode against every schema/envelope this project already
87+
knows about (the DLCI 0x08 `[Group][Code][Length][Value]` TLV shape, `qhr`'s protobuf oneof, the
88+
Fast Pair Message Stream shape) and report the best structural match. Generalizes the manual
89+
TLV-walk/protobuf-oneof-decode work redone by hand in most `CAP-NNN-FINDINGS.md` sessions (e.g.
90+
`CAP-050-FINDINGS.md` §3's own `scripts/decode_dlci08_tlv.py` one-off) into one reusable, testable
91+
decoder.
92+
93+
## Idea: APK version-diff tool
94+
95+
A class-level and string-level diff between two decompiled APK versions, keyed off
96+
`reverse-engineering/APK_VERSIONS.md`'s own version index. `APK_REVERSE_ENGINEERING_PROCEDURE.md`
97+
already names "the diff-against-previous-version pass" as a planned step; this would be its actual
98+
implementation, relevant the next time Google ships a companion-app update.
99+
100+
## Idea: tshark/DLCI-reassignment helper library
101+
102+
Every capture-analysis session re-derives, by hand, which session-local DLCI number currently
103+
carries which logical channel (RFCOMM DLCI numbers are session-local, not fixed — `CAP-001-FINDINGS.md`
104+
§2, `DECISIONS.md` ADR-018) by matching content signatures (e.g. `CAP-050-FINDINGS.md` §2's own
105+
`"google-pixel-buds-pro-v1"` re-identification after DLCI 0x08 turned out to carry HFP on one
106+
reconnect). A small Python wrapper standardizing "pull every payload for the channel matching
107+
content signature X, across every reconnect in this log" would remove a real, recurring
108+
misattribution risk from that manual process.
109+
110+
---
111+
112+
---
113+
https://github.com/tedsluis/opencontrolpixelbudspro2/blob/main/reverse-engineering/tools/BACKLOG.md - https://tedsluis.github.io/opencontrolpixelbudspro2/reverse-engineering/tools/BACKLOG
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Lambda Dispatcher Resolver
2+
3+
See [`SPEC.md`](SPEC.md) for the design, the structural detection rules, and the governance
4+
constraints this tool operates under (it never writes to `REVERSE_ENGINEERING.md`/`PROTOCOL.md`/
5+
`DECISIONS.md`, and never judges a branch's relevance to the Bluetooth protocol — SPEC.md §8).
6+
7+
## Setup
8+
9+
Requires the maintainer's own locally-decompiled APK tree to already exist under
10+
`reverse-engineering/apk/<version>/` (see `APK_REVERSE_ENGINEERING_PROCEDURE.md`) — this tool never
11+
ships or commits any decompiled content of its own (SPEC.md §11).
12+
13+
```sh
14+
cd reverse-engineering/tools/lambda_dispatcher_resolver
15+
uv venv .venv --python 3.12 # androguard's own dependency set targets 3.10-3.13
16+
uv pip install --python .venv/bin/python -e ".[dev]"
17+
```
18+
19+
## Usage
20+
21+
```sh
22+
# List every candidate lambda-dispatcher class in the APK (SPEC.md §3's structural shape check):
23+
.venv/bin/python3 -m lambda_dispatcher_resolver.cli list \
24+
--apk-root ../../apk/v1.0.955078536-10253511
25+
26+
# Resolve one (class, discriminator) pair to its exact smali + JADX evidence:
27+
.venv/bin/python3 -m lambda_dispatcher_resolver.cli resolve \
28+
--apk-root ../../apk/v1.0.955078536-10253511 \
29+
--class aie --discriminator 7
30+
```
31+
32+
`--class` accepts the short form (`aie`), the default-package dotted form (`defpackage.aie`), or
33+
the raw smali descriptor (`Laie;`) — all three resolve to the same class.
34+
35+
## Tests
36+
37+
```sh
38+
.venv/bin/python3 -m pytest tests/ -v
39+
```
40+
41+
The suite reads directly from the same local `reverse-engineering/apk/v1.0.955078536-10253511/`
42+
tree used above and is **skipped** (not failed) if that tree isn't present — see `tests/test_resolver.py`'s
43+
own module docstring for why it deliberately does not ship committed fixture copies of decompiled
44+
code.
45+
46+
---
47+
https://github.com/tedsluis/opencontrolpixelbudspro2/blob/main/reverse-engineering/tools/lambda_dispatcher_resolver/README.md - https://tedsluis.github.io/opencontrolpixelbudspro2/reverse-engineering/tools/lambda_dispatcher_resolver/README

0 commit comments

Comments
 (0)