Files
rea/docs/adb-device-analysis.md
Dery Ferdikaandmorluto 1d4c4eb030 fix(adb): reject unsafe host file names when pulling one device file (#1962)
pull_adb_file derived its host file name from the last path segment of a
caller-supplied device path and joined it to the output directory without
checking that the name is usable on the host. A device path ending in a
separator, a dot segment, a control character, a Windows reserved device
name, a trailing dot or space, or a ':' alternate-data-stream marker could
resolve to an unexpected host path or fail opaquely after the transfer.

The single-file path now derives the name with POSIX semantics and refuses
any non-portable segment as invalid_request before creating a directory or
running adb pull. This matches the package path's pending host-name policy
and never renames a device-reported file.

Co-authored-by: morluto <[email protected]>
2026-10-12 05:33:40 +08:00

13 KiB

ADB device inspection and acquisition

REA inspects connected Android devices, reads device state, and transfers files and packages through the caller's adb binary. Emulators, USB devices, and network devices participate alike: REA neither starts nor manages them and never installs platform tools. Every operation composes a fixed adb argument vector — caller values occupy positional arguments only and are quoted when adb forwards them to the device shell.

The adb client may start its local server on first device query; that is adb client behavior, recorded in the tool result when observed. inspect_adb_client probes only the binary and never contacts the server.

Supply tools

Use any Android platform-tools adb (verified with 34.0.5). Select it through the environment:

# Optional: absolute path to the adb binary; without it, adb resolves from PATH.
export REA_ADB_PATH=/absolute/path/to/adb

REA never installs the Android SDK, an emulator, or a system image. Readiness is rea inspect-adb-client: it runs adb version and reports the resolved binary, its version, and where it was selected from. A missing binary reports capability_unavailable with the REA_ADB_PATH remediation rather than attempting an installation.

Answer an analyst question

# Device identity
rea inspect-adb-client
rea list-adb-devices
rea inspect-adb-device emulator-5554
rea inspect-adb-display emulator-5554
rea list-adb-features emulator-5554
rea list-adb-services emulator-5554

# Find and acquire an app (the easy-get flow)
rea resolve-adb-packages emulator-5554 whatsapp
rea list-adb-packages emulator-5554 --scope third_party
rea inspect-adb-package emulator-5554 com.whatsapp
rea pull-adb-package emulator-5554 com.whatsapp --output-directory /tmp/acq
rea inspect-android-package /tmp/acq/com.whatsapp/base.apk

# Device state and files
rea list-adb-processes emulator-5554
rea inspect-adb-window emulator-5554
rea read-adb-setting emulator-5554 global adb_enabled
rea read-adb-logcat emulator-5554 --count 200
rea list-adb-directory emulator-5554 /data/data/com.whatsapp
rea pull-adb-file emulator-5554 /data/local/tmp/notes.txt --output-directory /tmp/acq
rea push-adb-file emulator-5554 /tools/frida-server /data/local/tmp/frida-server
rea capture-adb-screen emulator-5554 --output-directory /tmp/shots
rea collect-adb-bugreport emulator-5554 --output-directory /tmp/bugs

# Package lifecycle and dynamic analysis (device-mutating)
rea install-adb-package emulator-5554 /tmp/app.apk --replace
rea start-adb-app emulator-5554 com.example.app
rea read-adb-logcat emulator-5554 --pid 12345
rea start-adb-activity emulator-5554 android.intent.action.VIEW --data-uri https://example.com
rea stop-adb-app emulator-5554 com.example.app
rea uninstall-adb-package emulator-5554 com.example.app

Equivalent MCP methods use the snake_case names (list_adb_devices, pull_adb_package, resolve_adb_packages, …). Device operations take serial exactly as adb devices reports it. The pulled APK set feeds the existing analysis families directly, for example rea inspect-android-package /tmp/acq/<package>/base.apk.

Operations

Tool Kind What it does
inspect_adb_client read Resolved binary, version, install path; never contacts the server
list_adb_devices read Serial, state, transport, model metadata, inferred emulator/physical kind with basis
inspect_adb_device read Fixed getprop whitelist: build, SDK, patch, fingerprint, product, ABIs, debuggable, ro.kernel.qemu
inspect_adb_display read wm size and wm density as declared
list_adb_features read pm list features (hardware/software features, hex GL versions)
list_adb_services read service list binder services and interfaces
list_adb_processes read ps -A: user, pid, ppid, rss, state, name
inspect_adb_window read dumpsys window focus declarations (what is on screen)
read_adb_setting read One settings get value; device-side null is reported as such
read_adb_logcat read Bounded logcat -d -t dump for one buffer, optional pid filter
list_adb_packages read pm list packages -f with scope; base APK device paths
resolve_adb_packages read Case-insensitive package-name substring search with exact-match flag
inspect_adb_package read dumpsys package projection: versions, install times, installer, flags, permission count
list_adb_directory read ls -la: kinds, permissions, owner, group, sizes, symlink targets
pull_adb_package read Complete split APK set from pm path, each file digested
pull_adb_file read One absolute device path pulled and digested
capture_adb_screen read One screencap -p frame (exec-out, no device-side file) with dimensions
collect_adb_bugreport read Device-generated bugreport zip, digested
push_adb_file mutates device One local file to a device path (e.g. pushing tools into /data/local/tmp); refuses existing targets without explicit overwrite; verifies via the device's own sha256sum
install_adb_package mutates device Local APK via adb install [-r]; preserves the device's Success/Failure verdict
uninstall_adb_package mutates device Removes a package and its data for user 0
start_adb_app mutates device Resolves the package's launcher activity (cmd package resolve-activity --brief) and starts it (am start -n)
start_adb_activity mutates device Starts an intent (am start -a with optional URI, component, and up to 16 typed extras); preserves the activity manager's verdict
stop_adb_app mutates device Force-stops every process of a package, ending a dynamic-analysis run

Interpret the result

  • Inferences carry their basis. Device kind reports kind_basis (serial prefix or USB transport); APK role reports role_basis: file_name; emulator_observed is the build's own ro.kernel.qemu value, null when absent.
  • Device paths are not host paths. base_apk_device_path values live on the device filesystem. Only pulls and captures produce host-local paths, each paired with byte size and SHA-256 digest. A pulled file's host name is the device path's last segment, accepted only when every host can store it: a pull_adb_file whose device path ends in a separator, ./.., a control or reserved character, a Windows reserved device name, a trailing dot or space, or a : alternate-data-stream marker is refused as invalid_request before any adb pull runs, and REA never renames a device-reported file.
  • Splits are part of the answer. pm path enumerates the base APK and every installed split; pulls cover the whole set. System packages whose APKs keep other names report the unknown role rather than guessing.
  • Partial results keep facts. Failed individual pulls return successful artifacts with coverage: "partial" and per-file failures. Listings that contain unrecognized lines report them as unparsed_lines and mark partial coverage instead of dropping rows.
  • Mutations are explicit. push_adb_file, install_adb_package, and uninstall_adb_package declare mutatesTarget and never run implicitly: pushes refuse existing device files unless overwrite is set and verify the transfer with the device's own sha256sum; installs preserve the device's Failure [...] reason verbatim.
  • Producer identity stays attached. Every result embeds the producing adb client (binary_path, path_source, version, installed_path).

Resource and lifecycle limits

  • Caller values reach adb only as positional arguments in fixed vectors; no tool accepts an arbitrary shell command line, and UI interaction (input, am, monkey) is deliberately not exposed.
  • Per-invocation budgets: client/devices 10-20 s, shell reads 20-60 s with 64 KiB-8 MiB capture caps (logcat 4 MiB, screen 32 MiB), transfers and bugreports 600 s. Exceeded budgets report resource_constraint; stopped commands report provider_timeout.
  • Pull and capture outputs are additive: the destination must not already exist. Re-running a completed pull refuses the existing directory instead of overwriting evidence.
  • Cancellation during a package pull preserves caller-selected output. The error includes partial observations identifying completed artifacts and the interrupted local path; remaining APKs were not attempted.
  • Failed package acquisition rollback retains its directory identity and retries cleanup before later work and during shutdown. Cleanup refuses a replacement directory. Terminal shutdown stops admission and drains active operations.
  • The provider holds no long-lived processes; every invocation is one bounded adb run. The adb server belongs to the caller.

UI interaction beyond intent starts (tapping, typing, monkey fuzzing, screen recording) remains a separate lane: candidates such as mobile-device-mcp install driver software on the device and pair with the Frida instrumentation lane (#1359) behind explicit mutation-authority contracts. Network transports beyond already-connected devices (adb connect) are not exposed yet.

Real verification

npm run verify:adb exercises every operation against a live device. It requires adb (or REA_ADB_PATH) and any attached emulator or phone; --serial selects one when several are attached, --pull PACKAGE pulls a real package and verifies the returned digests against the files on disk, and --install-apk PATH drives a full install → resolve → uninstall lifecycle with a lane-owned probe.

Record: verified against adb 34.0.5 on Linux against an Android 14 (API 34) x86_64 emulator — all operations above, a 224-package inventory, 287 services, 92 features, a real two-APK split set pulled with byte-exact digests, a verified push/pull roundtrip, a 1.3 MB screen capture, and the complete package lifecycle; see docs/testing.md for the lane summary.