432 lines
23 KiB
Markdown
432 lines
23 KiB
Markdown
# Extension verification — offline emulation (unidbg) and Frida RPC
|
||
|
||
Covers the 2026-09 extension pass that added `references/emulation-and-rpc.md`,
|
||
`scripts/frida_rpc_serve.py` and `scripts/rpc_template.js`. Strength labels as defined in this
|
||
directory's README: **observed** (exact command + output recorded here), **inferred** (follows from
|
||
observed facts, step not executed), **unverified** (assumed or externally reported, not independently
|
||
confirmed here).
|
||
|
||
Reference environment for this pass: Windows host, Python 3.14.0, host `frida` 16.7.19 +
|
||
`frida-python` 16.7.19 (frida-tools 13.7.1), `adb` 37.0.1, JDK 17.0.4.1 (with `JAVA_HOME`
|
||
**unset** in the shell — see §5). Device `<DEVICE>`: Android 11 / API 30, arm64-v8a, rooted
|
||
(Magisk), on-device frida-server 16.7.19. Workbench artifacts live in `tools/_work/t3-rpc/` (not part
|
||
of the skill).
|
||
|
||
**Device-exclusivity constraint during this pass**: another task was unpacking the hardened Flutter
|
||
sample on the same device. This pass never attached to, spawned or force-stopped that sample's
|
||
process, and never killed or restarted the device-side frida-server. All RPC work targeted unrelated
|
||
processes (system apps and third-party apps). De-identification: the sample's package label, real
|
||
package name and md5 are elided in this document as `com.example.app` / `<label>`; command shapes,
|
||
counts, class names and timings are verbatim.
|
||
|
||
---
|
||
|
||
## 0. Summary of what this pass established
|
||
|
||
| Claim | Label |
|
||
|---|---|
|
||
| `droidasc` 0.1.1.post1 and `ddc` 0.1.8 both run on this host; both read the hardened sample's shell dex | observed |
|
||
| Both tools see only the 4 shell classes in the APK's dex; business classes are absent (shell dex only) | observed |
|
||
| `frida_rpc_serve.py` `--help`, all three modes, reconnect, spawn and attach-by-pid work against a live device | observed |
|
||
| Attach **by package name** fails on this ROM while attach **by pid** succeeds for the same process | observed |
|
||
| The device-side server may be (re)started on a non-default port; `--remote HOST:PORT` recovers, device-serial lookup does not | observed |
|
||
| unidbg 0.9.10-SNAPSHOT builds and boots an Android emulator on this host after a pom fix; one bundled test suite passes | observed |
|
||
| Any target `.so` actually emulated under unidbg | **not done** (unverified) |
|
||
| frida 17 compatibility of `rpc_template.js` export lookup | unverified (host has 16.7.19 only) |
|
||
|
||
---
|
||
|
||
## 1. ASC (`droidasc`) — observed
|
||
|
||
### 1.1 Install and version
|
||
|
||
```
|
||
$ python -m pip show droidasc
|
||
Name: droidasc
|
||
Version: 0.1.1.post1
|
||
Summary: ASC is a super FAST python Android Decompiler for Agents/Mobile Researchers
|
||
License-Expression: Apache-2.0
|
||
Location: C:\Python314\Lib\site-packages
|
||
Requires: androguard
|
||
```
|
||
|
||
Install line used (recorded after the fact; the package was already present on this host):
|
||
`python -m pip install droidasc`. Distribution metadata declares only `androguard` as a dependency —
|
||
**no JVM is required** (unlike the `asc` successor tooling in the upstream MG1937/ASC repository,
|
||
which is a separate project). The console script is named **`droidasc`**, not `asc`: there is no
|
||
`asc.exe` in `C:\Python314\Scripts`, and `asc --version` fails with
|
||
`The term 'asc' is not recognized...`. `python -m droidasc` is the equivalent entry point.
|
||
|
||
### 1.2 Subcommands
|
||
|
||
```
|
||
$ python -m droidasc --help
|
||
usage: python.exe -m droidasc [-h]
|
||
{getclass,listclass,getmanifest,findrefs} ...
|
||
ASC tooling entry.
|
||
positional arguments:
|
||
{getclass,listclass,getmanifest,findrefs}
|
||
getclass Locate the target class in APK, extract one DEX in memory, then decompile.
|
||
listclass List classes across all DEX entries; use --prefix to filter by package/class prefix.
|
||
getmanifest Decode AndroidManifest.xml from APK and print it as XML.
|
||
findrefs Find code references for string/type/method/field across all DEX entries in APK.
|
||
examples:
|
||
droidasc app.apk --gui
|
||
droidasc getclass app.apk Lcom/poc/Main; -o Main.java
|
||
droidasc listclass app.apk --prefix com.poc
|
||
droidasc getmanifest app.apk -o AndroidManifest.xml
|
||
droidasc findrefs app.apk string token -o string_refs.txt
|
||
```
|
||
|
||
`--gui` and the bundled `asc_client` web UI were **not** exercised (unverified, headless pass).
|
||
|
||
### 1.3 Measured against the sample
|
||
|
||
```
|
||
$ droidasc getmanifest <sample>.apk -o asc-manifest-verify.xml # 0.44 s, 32,345 B
|
||
<manifest ... package="com.example.app" platformBuildVersionCode="34" ... android:versionCode="1143"
|
||
android:versionName="0.9.0" android:compileSdkVersion="34" ...>
|
||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28"/>
|
||
<uses-permission android:name="android.permission.INTERNET"/>
|
||
... (permissions list follows; manifest decodes cleanly)
|
||
|
||
$ droidasc findrefs <sample>.apk string jiagu -o asc-findrefs-jiagu.txt # 0.42 s
|
||
classes.dex | Lcom/stub/StubApp;-><clinit> | matched=(libjiagu)
|
||
classes.dex | Lcom/stub/StubApp;->attachBaseContext | matched=(/.jiagu; jiagu; jiagu_x86)
|
||
|
||
$ droidasc getclass <sample>.apk com.stub.StubApp -o asc-StubApp-verify.java # 0.33 s, 22,802 B
|
||
package com.stub;
|
||
public final class StubApp extends android.app.Application { ... }
|
||
|
||
$ droidasc listclass <sample>.apk --prefix com.stub # 0.23 s
|
||
Lcom/stub/StubApp;
|
||
|
||
$ droidasc listclass <sample>.apk -o asc-listclass-all.txt # 0.18 s, 4 lines
|
||
Lcom/stub/StubApp;
|
||
Lcom/tianyu/util/Configuration;
|
||
Lcom/tianyu/util/DtcLoader;
|
||
Lcom/tianyu/util/a;
|
||
```
|
||
|
||
`findrefs` reports the shell's own strings (`libjiagu`, `jiagu_x86`, `/.jiagu`) — i.e. the packer
|
||
signature is visible without unpacking, but nothing of the application is.
|
||
|
||
### 1.4 Limits hit
|
||
|
||
```
|
||
$ droidasc getclass <sample>.apk com.example.app.MainActivity -o asc-mainactivity.java
|
||
Error: Class Lcom/example/app/MainActivity; not found in APK.
|
||
exit = 1
|
||
```
|
||
|
||
**This is the limit that matters for this sample class:** the full class listing is 4 classes, so on
|
||
a packed APK ASC is a manifest + shell-inspector, not an application decompiler. The application code
|
||
lives in that shell's payload (`assets/`, the native unpacker and the AOT/Flutter layer), which ASC
|
||
does not touch. ASC still earns its place in the flow: `getmanifest` in under half a second is the
|
||
fastest manifest pass measured in this repository's tool set, and `findrefs` on shell class names is
|
||
how the packer was identified here.
|
||
|
||
Observed behaviours worth knowing: `droidasc` prints per-command timing nowhere, writes to `-o` only,
|
||
and returns a clean `Error: ...` line plus exit 1 when a class is missing.
|
||
|
||
---
|
||
|
||
## 2. ddc — observed
|
||
|
||
### 2.1 Version and acquisition
|
||
|
||
```
|
||
$ bin/ddc.exe -V
|
||
ddc 0.1.8 — DEX → Java decompiler
|
||
https://github.com/ejfkdev/ddc
|
||
```
|
||
|
||
`ddc` is a Rust binary (upstream: [ejfkdev/ddc](https://github.com/ejfkdev/ddc)); the Windows build
|
||
used here is `tools/_work/t3-rpc/bin/ddc.exe`, 1,028,096 B, **not on `PATH`** and with no version
|
||
resource in the PE header (`VersionInfo` empty), so `ddc -V` is the only way to learn its version.
|
||
No JVM, no Python. Subcommands advertised by `ddc help`: `info`, `listclasses`, `manifest`,
|
||
`mainactivity`, `res`, `strings`, `findrefs`, `callers`, `members`, `hierarchy`, `largest`, `disasm`,
|
||
`getclass`, `getmethod`, plus a full-decompile mode (`ddc <INPUT>... [OUTPUT]`).
|
||
|
||
### 2.2 Measured against the sample
|
||
|
||
```
|
||
$ bin/ddc.exe info <sample>.apk # 0.24 s
|
||
label <label>
|
||
package com.example.app
|
||
version 0.9.0 (1143)
|
||
application com.stub.StubApp
|
||
launcher com.example.app.MainActivity
|
||
sdk 19–28
|
||
size 36.9 MB (38688120 bytes)
|
||
md5 1a037a045205184d4e8272092997334f
|
||
|
||
image dex classes methods fields strings
|
||
<sample>!classes.dex 035 4 266 23 481
|
||
total: 1 image(s), 4 classes
|
||
|
||
$ bin/ddc.exe findrefs <sample>.apk string jiagu # 0.13 s
|
||
dex kind class method refs
|
||
classes.dex const-string com/stub/StubApp <clinit>()V "libjiagu"
|
||
classes.dex const-string com/stub/StubApp attachBaseContext(...)V "jiagu_x86"; "jiagu"; "/.jiagu"
|
||
|
||
$ bin/ddc.exe strings <sample>.apk -f jiagu --with-locations # 0.14 s
|
||
dex string used-by
|
||
classes.dex "/.jiagu" com/stub/StubApp attachBaseContext(Landroid/content/Context;)V
|
||
classes.dex "jiagu" com/stub/StubApp attachBaseContext(Landroid/content/Context;)V
|
||
classes.dex "jiagu_x86" com/stub/StubApp attachBaseContext(Landroid/content/Context;)V
|
||
classes.dex "libjiagu" com/stub/StubApp <clinit>()V
|
||
```
|
||
|
||
`ddc info` reaches the same conclusion as ASC by a different route (class/method/string counts per
|
||
dex image) and adds the resources-derived label and the launcher verification; `findrefs` output is
|
||
tabular and one line per (method, string) pair, which is directly pasteable into a report.
|
||
|
||
### 2.3 Limits hit
|
||
|
||
```
|
||
$ bin/ddc.exe getclass <sample>.apk com.example.app.MainActivity
|
||
ddc: class com.example.app.MainActivity not found in the selected image(s)
|
||
(try `ddc listclasses <input> <pattern>`)
|
||
exit = 2
|
||
```
|
||
|
||
Exit codes measured on this build: `info` → 0, `-V` → 0, `getclass` with a missing class → **2**.
|
||
The missing-class path also reprints the full help text on stdout, so a wrapper that only checks
|
||
"did anything print" will misread it as success — key off the exit code. As with ASC, the packed APK
|
||
exposes only the shell dex; `ddc` cannot see application classes either, which is a property of the
|
||
artifact, not of the tool.
|
||
|
||
---
|
||
|
||
## 3. Prior-attempt outputs under test — and the defects found
|
||
|
||
`skills/apk-reverse/references/emulation-and-rpc.md`, `scripts/frida_rpc_serve.py` and
|
||
`scripts/rpc_template.js` were already on disk from an interrupted attempt of this task. This pass
|
||
read all three, ran the script, and fixed four defects. `rpc_template.js` needed no change.
|
||
|
||
| # | Defect (observed) | Fix |
|
||
|---|---|---|
|
||
| 1 | `RpcBridge._teardown()` never detached the session and used `obj.off(...) if obj is self.session else None` / `obj.unload() if hasattr(obj, "unload")` — a script object has `unload()`, a session has `detach()`, so the expression released only the script. Every `reload()`/reconnect cycle leaked one attached session on the device. | Rewritten to unload the script, remove the `detached` handler and `session.detach()` explicitly. |
|
||
| 2 | `list_exports()` called `Script.list_exports()`, which frida 16.7.19 answers with `DeprecationWarning: Script.list_exports will become asynchronous in the future, use the explicit Script.list_exports_sync instead` (printed on every `list` command). | Tries `list_exports_sync` first, then the old name, then local introspection. |
|
||
| 3 | `--mode call` raised out of `main()`: an unknown export produced `frida`'s own `unable to find method 'x'` and a raw Python traceback; a bad `--args` value dumped a `JSONDecodeError` traceback. Scripted callers got a stack trace instead of a result. | `run_call_mode()` now returns one JSON object (`{"ok": false, "error": ...}`) and exit code 1 for both cases; `call()` consults the advertised export list first so the message is `no such rpc export: nope (available: add ping ...)`. |
|
||
| 4 | `frida.TimedOutError` was absent from the retry whitelist. It is **not** a `TransportError` subclass (`frida.TimedOutError.__mro__` → `(TimedOutError, Exception, BaseException, object)`), so a device that timed out during attach produced a traceback in an otherwise recoverable situation — reproduced live (§4.4). | Retryable types are now collected by name into `RETRYABLE` (TransportError, NotSupportedError, ServerNotRunningError, ProcessNotFoundError, InvalidOperationError, TimedOutError) and used by both `ensure()` and the initial connect in `main()`. |
|
||
|
||
Post-fix evidence:
|
||
|
||
```
|
||
$ python skills/apk-reverse/scripts/frida_rpc_serve.py --remote 127.0.0.1:27043 --pid 19938 \
|
||
--script skills/apk-reverse/scripts/rpc_template.js --mode call --export add --args "[40, 2]"
|
||
{"ok": true, "export": "add", "result": 42} exit = 0
|
||
$ ... --mode call --export nope
|
||
{"ok": false, "export": "nope", "error": "no such rpc export: nope
|
||
(available: add ping getpackagename callnative callnativeaddr)"} exit = 1
|
||
$ ... --mode call --export add --args "not-json"
|
||
{"ok": false, "error": "--args is not valid JSON: Expecting value: line 1 column 1 (char 0)"}
|
||
exit = 1
|
||
```
|
||
|
||
Known cosmetic behaviour left as is: in `--mode call` the bridge connects before `--args` is parsed,
|
||
so a bad-args invocation still performs a device attach.
|
||
|
||
## 4. Frida RPC against the live device — observed
|
||
|
||
### 4.1 Attach path: by name fails, by pid works
|
||
|
||
Attach **by package name** failed for every package tried, with the process demonstrably alive in
|
||
both `frida-ps -Uai` and `ps -A`:
|
||
|
||
```
|
||
$ python ...frida_rpc_serve.py --device-serial <DEVICE> --package com.android.settings ...
|
||
[bridge] connect failed (unable to find process with name 'com.android.settings'); retry 1 in 1.0s
|
||
... (5 attempts) ...
|
||
initial connect failed: could not (re)connect after 5 attempts
|
||
$ python ... --package io.github.vvb2060.magisk ...
|
||
[bridge] connect failed (unable to find process with name 'io.github.vvb2060.magisk'); retry 1 in 0.5s
|
||
initial connect failed: could not (re)connect after 1 attempts
|
||
```
|
||
|
||
The same processes attach normally by pid:
|
||
|
||
```
|
||
$ python ... --pid 19938 --mode call --export getpackagename
|
||
{"ok": true, "export": "getpackagename", "result": "com.android.settings"}
|
||
$ python ... --pid 21493 --mode call --export getpackagename
|
||
{"ok": true, "export": "getpackagename", "result": "com.android.email"}
|
||
```
|
||
|
||
So the gap is in the **name→pid lookup** the server serves, not in injection: `--pid` definitely
|
||
does bypass it. `references/emulation-and-rpc.md` had claimed the opposite ("`--pid` does not bypass
|
||
a gap that hides the process from the server's own table"); that row was corrected in this pass, and
|
||
the corrected wording is what the reference now carries.
|
||
|
||
### 4.2 The transport itself
|
||
|
||
| # | Command (pid 19938 = `com.android.settings`, unrelated to the sample) | Result |
|
||
|---|---|---|
|
||
| 1 | `--mode call --export add --args "[40, 2]"` | `{"ok": true, "result": 42}` |
|
||
| 2 | `--mode call --export ping` | `{"ok": true, "result": "pong"}` |
|
||
| 3 | `--mode call --export getpackagename` | `{"ok": true, "result": "com.android.settings"}` |
|
||
| 4 | `--mode call --export callnative --args '["libc.so","getpid","int",[],[]]'` | `{"ok": true, "result": 19938}` (matches the pid — `NativeFunction` wrapping works) |
|
||
| 5 | `--mode repl`, feeding `list` / `reload` / `list` / `add [40, 2]` / `nope` | `add ping getpackagename callnative callnativeaddr`; `reloaded`; same list; `42`; `error: no such rpc export: nope (available: ...)` |
|
||
| 6 | `--mode http`, `GET /exports` | `{"ok":true,"exports":[...5 names...]}` |
|
||
| 7 | `--mode http`, `POST /call {"export":"add","args":[40,2]}` | `{"ok":true,"result":42}` |
|
||
| 8 | `--mode http`, `POST /call {"export":"getpackagename","args":[]}` | `{"ok":true,"result":"com.android.settings"}` |
|
||
| 9 | `--mode http`, `POST /call` with an unknown export | HTTP 502 + `{"ok":false,"error":"no such rpc export: nope (available: ...)"}` |
|
||
| 10 | `--mode http`, `POST /reload` then `POST /call add [1,2]` | `{"ok":true}` then `{"ok":true,"result":3}` — the re-attach path works, and no `DeprecationWarning` is printed any more |
|
||
| 11 | `--spawn --package io.github.vvb2060.magisk --mode call --export getpackagename` | `{"ok": true, "result": "io.github.vvb2060.magisk"}` (spawn → script load → resume → RPC) |
|
||
|
||
Every script load also delivered the template's own liveness message:
|
||
`[script] {"ev": "RPC-READY", "exports": ["add","ping","getpackagename","callnative","callnativeaddr"]}`.
|
||
That line is the cheapest health check for a session and is worth grepping for in automation.
|
||
|
||
### 4.3 The server's port moved under us
|
||
|
||
Mid-pass, `--device-serial <DEVICE>` began failing on attach while `frida-ps -U` kept working:
|
||
|
||
```
|
||
$ python ... --device-serial <DEVICE> --pid 19938 ...
|
||
[bridge] connect failed (unable to connect to remote frida-server: closed); retry 1 in 1.0s
|
||
... (3 attempts) ...
|
||
|
||
$ python -c "import frida;d=frida.get_device('<DEVICE>');d.attach(19938)"
|
||
attach failed: ServerNotRunningError unable to connect to remote frida-server: closed
|
||
|
||
$ adb -s <DEVICE> shell 'su -c "ps -A"' | Select-String 'svc16|frida'
|
||
root 1876 1 2227684 55288 do_sys_poll S .svc16 # only this instance alive
|
||
$ adb -s <DEVICE> shell 'su -c "netstat -ltnp"' | Select-String 2704
|
||
tcp 0 0 0.0.0.0:27043 0.0.0.0:* LISTEN 1876/.svc16 # nothing on 27042
|
||
```
|
||
|
||
The visible `frida-server` process (pid 23405) was gone; a disguised copy (`.svc16`, pid 1876) was
|
||
listening on **27043 only**. `frida-ps -U` still worked because frida's USB transport probes a range
|
||
of ports, while the scripted device-serial path landed on a dead default port. Recovery, with the
|
||
forward already present (`adb forward --list` shows `tcp:27043 tcp:27043`):
|
||
|
||
```
|
||
$ python ... --remote 127.0.0.1:27043 --pid 19938 --mode call --export getpackagename
|
||
{"ok": true, "result": "com.android.settings"}
|
||
```
|
||
|
||
This pass neither started nor killed any server instance; it only observed that the instance changed
|
||
and switched to the explicit `--remote` form. That form is now the recommended one in the reference's
|
||
failure table.
|
||
|
||
### 4.4 A third-party process that refuses the handshake
|
||
|
||
```
|
||
$ python ... --remote 127.0.0.1:27043 --pid 19984 --mode call --export getpackagename
|
||
frida.TimedOutError: unexpectedly timed out while waiting for signal from process with PID 19984
|
||
```
|
||
|
||
An ordinary third-party app process (`com.xunmeng.pinduoduo`, listed by `frida-ps -Uai`) simply did
|
||
not answer the agent handshake within frida's timeout. Pre-fix this surfaced as a traceback; post-fix
|
||
the bridge treats it as retryable and reports `initial connect failed: ...` with exit 2. This is an
|
||
app-side property, not a bridge property — for connectivity probes prefer a system or stable app
|
||
process.
|
||
|
||
### 4.5 Device state after the pass
|
||
|
||
`frida-ps -U` and `frida-ps -Uai` both still answer; the sample's process was never touched; the
|
||
device-side server was never killed or restarted by this pass.
|
||
|
||
---
|
||
|
||
## 5. unidbg — observed (build/boot) and unverified (target emulation)
|
||
|
||
### 5.1 Build
|
||
|
||
```
|
||
$ git clone --depth 1 https://github.com/zhkl0228/unidbg.git # version 0.9.10-SNAPSHOT
|
||
$ .\mvnw.cmd -B -pl unidbg-android -am -DskipTests compile
|
||
Error: JAVA_HOME not found in your environment. # JAVA_HOME was unset on this host
|
||
...
|
||
[ERROR] Failed to execute goal ...maven-compiler-plugin:3.3:compile on project unidbg-api:
|
||
[ERROR] .../AbstractARMDebugger.java:[79,43] reference to Module is ambiguous
|
||
[ERROR] both com.github.unidbg.Module and java.lang.Module match
|
||
```
|
||
|
||
The shipped pom pins `<source>8</source><target>8</target>` with `maven-compiler-plugin` 3.3. On
|
||
JDK 9+ that pair does **not** select the JDK 8 API, so `java.lang.Module` stays visible and every
|
||
unqualified `Module` in `AbstractARMDebugger` becomes ambiguous — the build dies in `unidbg-api`.
|
||
Two local work-copy changes fix it (recorded in `tools/_work/t3-rpc/unidbg/pom.xml`, not part of the
|
||
skill and not committed):
|
||
|
||
```
|
||
maven-compiler-plugin 3.3 -> 3.8.1 # 3.13 is rejected: the bundled wrapper is Maven 3.5.4,
|
||
# and 3.13 requires Maven >= 3.6.3
|
||
<source>8</source><target>8</target> -> <release>8</release> # hides java.lang.Module
|
||
```
|
||
|
||
Result with `JAVA_HOME=C:\Program Files\Java\jdk-17.0.4.1`:
|
||
|
||
```
|
||
$ .\mvnw.cmd -B -pl unidbg-android -am -DskipTests compile
|
||
[INFO] unidbg-api ......... SUCCESS [ 40.838 s]
|
||
[INFO] unidbg-android ..... SUCCESS [ 4.941 s]
|
||
[INFO] BUILD SUCCESS (total 48.4 s, after a one-off dependency download)
|
||
```
|
||
|
||
### 5.2 Boot check
|
||
|
||
The pom sets `<maven.test.skip>true</maven.test.skip>`, so `-Dmaven.test.skip=false` is required to
|
||
run anything:
|
||
|
||
```
|
||
$ .\mvnw.cmd -B -pl unidbg-android -am -Dmaven.test.skip=false \
|
||
-Dtest=MemoryTrackerTest -DfailIfNoTests=false test
|
||
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
|
||
Backend: DynarmicBackend64
|
||
=== Memory Leak Report === ... Address: 0x12000000, Size: 4096 ...
|
||
[INFO] BUILD SUCCESS
|
||
```
|
||
|
||
This proves the toolchain end to end at the *environment* level: emulator construction, memory
|
||
allocator, backend selection (Dynarmic, with Hypervisor and Unicorn2 factories registered) and
|
||
Android 11 (`AndroidResolver(23)`-style) library resolution all execute on this host.
|
||
|
||
### 5.3 What was **not** done — unverified
|
||
|
||
* Loading and calling **any** target `.so`, including this sample's `libjiagu*.so` or `libapp.so`.
|
||
No `createDalvikVM`, `loadLibrary`, `callJNI_OnLoad` or `AbstractJni` stub was exercised.
|
||
* Consequently every per-library statement in `references/emulation-and-rpc.md` §Part A — which JNI
|
||
callbacks a hardened library demands, how many stub rounds it costs, which `/proc` or signature
|
||
checks it runs — remains **inferred** from the library's shape and community practice. The
|
||
reference's "Measured here" section was rewritten in this pass to say exactly that.
|
||
* Note for anyone porting an older tutorial: `emulator.createCode()` / the `Assembly` helper class
|
||
do **not** exist in 0.9.10-SNAPSHOT (no `Assembly*.java` under the tree, no `createCode` symbol in
|
||
any module). Recipes that assemble raw ARM machine code through that API need the current
|
||
backend/JNI entry points instead.
|
||
* `unidbg-android`'s bundled demos (`TTEncrypt`, `SignUtil`, `QDReaderJni`, …) each expect their own
|
||
`.so`/APK to be placed first; none were run, so the demo names in the reference are pointers, not
|
||
verified commands.
|
||
|
||
---
|
||
|
||
## 6. Consolidated list of things that did **not** work (for the pass summary)
|
||
|
||
1. Attach by package name on this ROM (both `com.android.settings` and `io.github.vvb2060.magisk`
|
||
failed while listed by `frida-ps -Uai`). Workaround: pid, from `frida-ps -U` / `pidof`.
|
||
2. Device-serial attach once the device's server came back on 27043 — `ServerNotRunningError:
|
||
unable to connect to remote frida-server: closed`. Workaround: `adb forward` + `--remote`.
|
||
3. Attach by pid to `com.xunmeng.pinduoduo` — `frida.TimedOutError`; the process never answered the
|
||
handshake.
|
||
4. `asc` as a command name (the console script is `droidasc`), and `mvnw` without `JAVA_HOME`.
|
||
5. unidbg out of the box on JDK 17 (`Module` ambiguity) and unidbg tests out of the box
|
||
(`maven.test.skip=true`).
|
||
6. Application-class decompilation of a packed APK by either ASC or ddc (nothing to fix — the dex
|
||
simply is not there; this is the case that justifies the whole emulation/RPC route).
|
||
|
||
## 7. Artifacts produced by this pass
|
||
|
||
`tools/_work/t3-rpc/` (not committed): `asc-manifest-verify.xml`, `asc-StubApp-verify.java`,
|
||
`asc-listclass-all.txt`, `asc-listclass-stub.log`, `asc-getmanifest.log`, `asc-findrefs.log`,
|
||
`asc-findrefs-jiagu.txt`, `asc-getclass.log`, `ddc-info-verify.txt`, `ddc-findrefs-jiagu.txt`,
|
||
`ddc-strings-jiagu.txt`, `rpc-*.log` (one per command in §4), `bin/ddc.exe`, `unidbg/` (patched work
|
||
copy).
|
||
|
||
Files changed in the repository by this pass: `references/emulation-and-rpc.md` (corrected failure
|
||
table, corrected unidbg build recipe, corrected "Measured here" labelling, and a stale section
|
||
reference repointed to `SKILL.md` §Stop conditions) and `scripts/frida_rpc_serve.py` (the four fixes
|
||
in §3).
|