5.2 KiB
Stalker exclude — what it fixes, and what it does not
Question. Community practice says a followed thread that runs through libc/libart on arm64
gives deadlocks, watchdog SIGABRTs and zero-event traces, and that Stalker.exclude() is the
remedy. This pass measured which of those two claims survives contact with a device.
Strength summary: the crash half is observed with a clean control; the zero-event half is
observed as a negative — exclusion did not change it. Every command and its output are below.
Design
Three arms, one device, one package, one module, one follow window. Exactly one variable moves
between baseline → control → treatment.
| Arm | What it does | Why it exists |
|---|---|---|
baseline |
spawn, attach, inject the script, resume, and never follow | Without this arm a dead process cannot be attributed: "following killed it" and "the app dies under frida anyway" look identical |
control |
follow the main thread for 6 s with excludeModules: [] |
The shape that produced the earlier zero-event / SIGSEGV results |
treatment |
same follow, with the script's built-in list of 20 system modules excluded | The proposed remedy |
The harness also neutralises one trap in the script itself: CONFIG.autoStart defaults to true, so
loading stalker_trace.js starts a follow with default options before a runtime configuration can
land. Left alone, that run is neither the control nor the treatment and contaminates both. The
harness patches autoStart to false in the injected source and asserts the patch count.
For the same reason the harness resumes explicitly: frida spawns a process suspended, so
without device.resume(pid) the followed thread executes nothing and both arms report a zero-event
trace manufactured by the harness. That failure mode is indistinguishable from the one under
investigation, which is exactly why it has to be engineered out before the measurement means
anything.
Result
baseline alive=True (no follow installed)
control alive=False err=script has been destroyed
EXCL|excluded=0/0 []
treatment alive=True DONE|reason=timeout blocks=0 blk=0 calls=0 truncated=0
EXCL|excluded=20/20 [libm.so,libdl.so,libart.so,libartbase.so,libnativehelper.so,…] not-loaded=3
WARN|zero events 1500ms after follow (blocks=0 blk=0 calls=0) -- the pipeline is NOT proven
What this settles
- A follow with no exclusion killed the target process, and the baseline arm rules out the
alternatives. The process survives an attach plus resume with no follow, and dies only when the
follow is installed. That is
observed, not inferred. - Exclusion kept the process alive. The treatment arm reached its normal
DONE|reason=timeoutwith the process still running. Excluding the system libraries from a follow is not a style preference. - Exclusion did not restore event delivery. The treatment arm still reported
blocks=0 blk=0 calls=0after a full 6-second window. The zero-event trace is therefore a separate defect from the load problem, andStalker.exclude()must not be presented as its fix. This partially contradicts the community framing, which lists "no events" among the symptoms exclusion cures. - The zero-event warning added to
stalker_trace.jsthis pass fires correctly —treatmentemittedWARN|zero events 1500ms after follow, whilecontrolcould not, because its process was already gone. A dead pipeline now announces itself instead of being read as "the code did not run".
What this does not settle
- Why events do not arrive. This measurement says it is not the exclusion list. It does not identify the cause, and it does not distinguish "no events" from "the followed thread genuinely ran nothing in that window" — the per-arm control that would separate those (following a thread executing a known loop) was not run.
- Whether the crash is reachable without a follow. Only one package and one module were used, and the harness kills the process at the end of each arm, so nothing is claimed about a long-lived follow.
- Anything about other ROMs, other frida versions, or armv7. Single-device result.
- The multiplier. Nothing here measures the 20-50x cost figure; this is about survival and delivery, not throughput.
Environment
<DEVICE> (Android 11 / API 30 / arm64-v8a, Magisk root), frida host 16.7.19, on-device
frida-server 16.7.19 reached over adb forward tcp:27099. Target: the MASTG UnCrackable-Level1
sample, package <PKG>, module libc.so followed on the main thread for 6 s per arm. Device
load average was ~30 throughout — high for an 8-core phone, and worth recording because the
earlier incident in native-dbi-and-deobfuscation.md §6 happened on a loaded device.
Reproducing
python tools/_work/b6/stalker_ab.py --package <PKG> --module libc.so \
--mode baseline|control|treatment --seconds 6 --device 127.0.0.1:27099 \
--out tools/_work/b6/r_<mode>.json
The driver is a work artefact under tools/_work/ (git-ignored), not part of the skill. Its three
arms and the two harness traps it neutralises are the reusable part.