Add deliverable-form discipline, execution identity, and four reusable scripts
Generalized from a long hardened-target engagement. Additions target the failure
modes that cost the most time and are not yet covered; nothing here is sample-specific.
New discipline
- SKILL.md: a third override rule -- deliver in the environment the request requires.
A root-only or live-instrumentation result can look like success while failing the
actual requirement (unrooted, installable, self-contained). Also adds two classify
questions: what form the deliverable must take, and which architecture/library is
actually executing.
- long-task-discipline.md: "the most expensive drift" -- a constraint table (privilege,
modification form, ABI, network, persistence, distribution), the checkpoint question,
and how to report a privileged workaround as a fallback rather than a deliverable.
Plus "bound every wait": timeouts on every command, budgets calibrated from
measurement so slow and hung are distinguishable, and sampling an observable you can
inspect instead of sleeping blind.
- environment.md: look at the screen instead of driving blind; which of the two screen
evidence kinds is trustworthy (an empty accessibility tree is common for
runtime-rendered UI, where the image is primary rather than a fallback); emulator
console control and adb recovery; preflight before every experiment block.
- native-and-so.md: what the device claims vs what the package manager chose vs what is
actually mapped; translation layers and their consequences for hooks and native
checks; fat-APK ABI selection.
- runtime-data.md: when the app rewrites your edit (server refresh vs local
regeneration), why chmod/chown cannot work because the app deletes and recreates the
file, and the immutable-attribute fix; decoding a value that only looks encrypted.
- framework-runtimes.md: string encoding traps (a UTF-8 scan of a UTF-16 snapshot
returns nothing and reads as "the strings were stripped"), and non-ASCII obfuscated
identifiers -- resolve by shape, not by name.
- pitfalls.md: P23 the fix lives in a file the app rewrites; P24 the artifact changed
but the wrong one is executing; P25 "search found nothing" treated as "not there".
New scripts (parameterized, path-agnostic, all with explicit timeouts)
- preflight.py environment check: device, root, ABI/translation, clock skew, leftover
proxy/forwards, dead device server, chosen ABI, free space
- lib_map.py what is actually mapped into a live process: path, base, architecture
from the ELF header, and whether it came from the APK or was
materialized at runtime
- blob_decode.py search (don't guess) the framing of a stored value, then re-encode the
edited payload with the same parameters
- snap.py bounded burst screenshots + control-tree capture, with a stall detector
and an explicit verdict on whether the tree is usable
All four were exercised against a live device while writing: preflight correctly
flags multi-device ambiguity and detected the ARM translation layer; lib_map read real
ELF headers and identified the translation shim; blob_decode round-tripped a
rotation+deflate+base64 blob at realistic size in ~0.2s; snap detected a stalled screen
across three hashes.
This commit is contained in:
@@ -72,11 +72,37 @@ the snapshot rather than by pattern-matching machine code.
|
||||
|
||||
- **Strings first.** User-visible text (dialog bodies, feature labels) is the cheapest anchor in any
|
||||
runtime. Find it, then find what references it.
|
||||
- **Search them in the encoding the runtime actually uses, or you will conclude they do not exist.**
|
||||
This is the single most common false negative in cross-platform work. A runtime snapshot does not
|
||||
necessarily store text as UTF-8; UTF-16 (little-endian on these targets) is common and entirely
|
||||
valid. A UTF-8 search of such a snapshot returns **zero hits**, which reads as "the strings were
|
||||
stripped / encrypted, this route is dead" — and that conclusion is wrong.
|
||||
|
||||
```python
|
||||
blob = open('libapp.so', 'rb').read() # or whichever artifact holds the snapshot
|
||||
for phrase in ('<feature label>', '<dialog title>'):
|
||||
print(phrase, 'utf-8:', blob.find(phrase.encode('utf-8')),
|
||||
'utf-16le:', blob.find(phrase.encode('utf-16-le')))
|
||||
```
|
||||
|
||||
Try both, and try a short distinctive substring rather than a long phrase.
|
||||
- **A phrase that is absent may simply never exist as one literal.** UI text is often assembled from
|
||||
fragments or templates, so "the whole sentence" can be missing while both halves are present.
|
||||
Search the shortest distinctive token, and expect the interesting anchor to be a *label* rather
|
||||
than a sentence.
|
||||
- **Landing on a string tells you where the text lives, not which code decided to show it.** Treat the
|
||||
hit as an anchor for reference-hunting, not as the answer.
|
||||
- **Compare two builds.** The same feature in a slightly different version often reveals the code path.
|
||||
- **Watch the boundary, not the interior.** For cross-platform apps it is usually far cheaper to observe
|
||||
what crosses between layers than to reverse the interior of the runtime.
|
||||
- **Do not assume the obfuscated names are stable or meaningful.** They are not, and treating them as
|
||||
identities leads to conclusions that break on the next build.
|
||||
- **Obfuscated identifiers can be non-ASCII and invisible.** Renaming passes can replace class and
|
||||
member names with characters outside ASCII (combining marks, variant selectors and similar). If a
|
||||
class exists at runtime but "cannot be found" by the name you read from a decompiler, suspect
|
||||
encoding or normalisation in your tooling rather than a missing class. Resolve such members by
|
||||
**shape** — parameter counts, types, interface implementation — instead of by name, and prefer
|
||||
runtime enumeration of loaded classes over guessing identifiers.
|
||||
|
||||
## What this changes about your plan
|
||||
|
||||
|
||||
Reference in New Issue
Block a user