Add signature-derived-key coverage, PID attach, MMKV; fix stale question count

New: references/signature-derived-keys.md (apps that use their own signing certificate as crypto key material: detection greps, why offline extraction is unreliable -- the runtime value is a certificate DER from inside the PKCS#7 chain, not the .RSA container -- plus the hardcode-then-verify procedure); scripts/sig_probe.py (offline candidates via --apk, authoritative read via --live, both exercised); pitfalls.md P27 (rebuilt APK installs and renders but every signed request returns empty/-1 signing params).

Updated: dynamic-frida.md (attach by PID when enumerate_processes omits the target; verify runtime/Java-bridge availability before blaming the script); runtime-data.md (MMKV format, parse/write snippet, crc32 and in-memory-overwrite traps); server-api.md (route the signature-derived-key case to the new reference); SKILL.md and README.md (new classification question, corrected question count, index entries).
This commit is contained in:
inliver233
2026-09-19 22:31:32 +08:00
parent cf636c4263
commit 21686e84f1
9 changed files with 594 additions and 4 deletions
+63
View File
@@ -18,6 +18,7 @@ Sometimes the cheapest fix is not code at all, but a value in app data. And some
/data/data/<pkg>/ (== /data/user/0/<pkg>)
├── files/
│ ├── datastore/*.preferences_pb AndroidX DataStore (protobuf)
│ ├── mmkv/<name> MMKV (Tencent) — one file per named store
│ └── ...
├── shared_prefs/*.xml SharedPreferences
├── databases/*.db SQLite (Room, etc.)
@@ -67,6 +68,68 @@ Verify by reading the file back with `xxd` before launching.
Simple XML. Edit with the app stopped. Types are explicit (`<boolean>`, `<int>`, `<long>`, `<string>`). Same ownership rules.
## MMKV (Tencent)
Common in apps whose SDK stack is Chinese-market. One file per named store under `files/mmkv/`,
with a `.crc` companion. It is **not** a database and **not** XML — it is a small custom binary
format:
```
offset 0 uint32 actual_size size of the data region
offset 4 uint32 crc32 over the data region
offset 8 data region repeated: varint key_len, key, varint val_len, value
```
Files are preallocated, so trailing zeroes are normal; `actual_size` is what matters.
Parsing is mechanical, and no library is needed:
```python
import struct
def parse(blob):
size = struct.unpack_from('<I', blob, 0)[0]
pos, end, out = 8, 8 + size, []
def varint(p):
v = s = 0
while True:
b = blob[p]; p += 1
v |= (b & 0x7F) << s
if not b & 0x80:
return v, p
s += 7
while pos < end:
klen, pos = varint(pos); key = blob[pos:pos + klen]; pos += klen
vlen, pos = varint(pos); val = blob[pos:pos + vlen]; pos += vlen
out.append((key, val))
return out
```
Writing a change back means rebuilding the region and **recomputing both header fields**:
```python
import struct, zlib
body = new_region_bytes
blob = bytearray(capacity) # keep the original file length
struct.pack_into('<I', blob, 0, len(body))
struct.pack_into('<I', blob, 4, zlib.crc32(body) & 0xFFFFFFFF)
blob[8:8 + len(body)] = body
```
Cautions:
- **A wrong crc32 makes the library throw the whole store away.** The visible symptom is "my
edit did nothing" or "the app reset every setting", never an error.
- **Values are raw bytes**, not always text. JSON is common; a serialized protobuf or a small
encrypted payload is also common (`scripts/blob_decode.py` searches the framing).
- **The in-memory copy wins until the process restarts.** `am force-stop` before editing and
re-launch after, or the running app overwrites your change on exit.
- **Do not delete the `.crc` companion.** Leave it in place when hand-editing the store; it is
validated and regenerated by the library.
## SQLite
```bash