跳转至

ADB 设备检查与获取

REA 检查已连接的 Android 设备、读取设备状态,并通过调用方的 adb 二进制文件传输文件和包。模拟器、USB 设备和网络设备都以相同方式参与:REA 既不启动也不管理它们,且从不安装平台工具。每个操作都构成一个固定的 adb 参数向量——调用方的值仅占据位置参数,并且在 adb 将其转发到设备 shell 时会加引号。

adb 客户端可能在首次设备查询时启动其本地服务器;这是 adb 客户端行为,在观察到时会记录在工具结果中。inspect_adb_client 仅探测二进制文件,从不与服务器通信。

供给工具

使用任意 Android platform-tools adb(已验证 34.0.5)。通过环境选择它:

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

REA 从不安装 Android SDK、模拟器或系统镜像。就绪性检查通过 rea inspect-adb-client 完成:它运行 adb version 并报告解析出的二进制文件、其版本及选择来源。缺少二进制文件时,会报告 capability_unavailable 并提供 REA_ADB_PATH 补救措施,而不是尝试安装。 binary_session 清单使用相同的受限客户端版本探测;它不查询设备、不启动 adb 服务器,也不确认请求的序列号是否可达。设备状态由每个设备操作进行检查。

回答分析师问题

# 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

等效的 MCP 方法使用 snake_case 名称(list_adb_devices、pull_adb_package、resolve_adb_packages 等)。设备操作接受 serial,与 adb devices 报告的完全一致。拉取的 APK 集直接供现有分析系列使用,例如 rea inspect-android-package /tmp/acq/<package>/base.apk。

操作

Tool Kind What it does
inspect_adb_client 读取 解析出的二进制文件、版本、安装路径;从不与服务器通信
list_adb_devices 读取 序列号、状态、传输方式、型号元数据、推断的模拟器/物理设备类型及其依据
inspect_adb_device 读取 固定的 getprop 白名单:build、SDK、补丁、指纹、产品、ABI、可调试、ro.kernel.qemu
inspect_adb_display 读取 声明的 wm size 和 wm density
list_adb_features 读取 pm list features(硬件/软件特性,十六进制 GL 版本)
list_adb_services 读取 service list binder 服务与接口
list_adb_processes 读取 ps -A:用户、PID、PPID、RSS、状态、名称
inspect_adb_window 读取 dumpsys window 焦点声明(屏幕上显示的内容)
read_adb_setting 读取 一个 settings get 值;设备端的 null 会照实报告
read_adb_logcat 读取 对单个缓冲区进行受限的 logcat -d -t 转储,可选的 pid 过滤器
list_adb_packages 读取 带作用域的 pm list packages -f;基础 APK 设备路径
resolve_adb_packages 读取 不区分大小写的包名字面子串搜索,带精确匹配标志
inspect_adb_package 读取 dumpsys package 投影:版本、安装时间、安装器、标志,以及列出的请求权限行数(包括 Unicode 名称)
list_adb_directory 读取 NUL 分隔的直接子项,包括隐藏条目:类型、权限、所有者、组、大小、符号链接目标
pull_adb_package 读取 来自 pm path 的完整拆分 APK 集合,每个文件均计算摘要
pull_adb_file 读取 拉取一个绝对设备路径并计算摘要
capture_adb_screen 读取 一帧 screencap -p 图像(exec-out,不产生设备端文件),包含尺寸
collect_adb_bugreport 读取 设备生成的 bugreport zip 文件,已计算摘要
push_adb_file 修改设备 将一个本地文件推送到设备路径(例如将工具推送到 /data/local/tmp);检查已存在的目标文件,若无明确覆盖则拒绝写入(该检查并非原子操作);当设备可用 sha256sum 时比较摘要
install_adb_package 修改设备 通过 adb install [-r] 安装本地 APK;保留其成功/失败判定,记录 replace_requested,并将实际替换结果保留为 null,因为未观察先前的包状态
uninstall_adb_package 修改设备 通过 adb shell pm uninstall --user 0 请求移除用户 0 的包,不保留数据
start_adb_app 修改设备 解析包的启动器 Activity(cmd package resolve-activity --brief)并启动它(am start -n)
start_adb_activity 修改设备 启动一个 intent(am start -a,可带 URI、组件和最多 16 个类型化 extras);保留 activity manager 的判定
stop_adb_app 修改设备 强制停止某个包的所有进程,从而结束一次动态分析运行

当 push_adb_file 收到一个已存在的目录时,adb 会将本地文件放入该目录下,并使用本地 basename;Evidence 会报告该实际文件路径,同时在输入参数中保留调用方的目录路径。私有只读暂存副本会保留源文件的可执行位。

对于 inspect_adb_window,focus_contexts 会保留每个显示标头、在可识别时的精确显示 ID,以及每个焦点声明及其原始行。没有显示标头的声明会被明确标记为 unscoped。缺失的声明不同于 value 为 JSON null 的声明。source_section 标识所选的 dumpsys 分区;observed_lines 按源顺序保留该分区中的焦点声明和显示标头。

focus_summary 仅在只有一个上下文且没有重复的 focus 字段时才为 single_context。单数形式的 focused_window 和 focused_app 概括该上下文;它们并不确立设备范围的焦点。多个上下文或重复字段会产生 ambiguous,此时单数字段为 null,并保留所有声明。如果未声明任何 focus 字段,则状态为 unavailable。只有当 windows 分区未声明任一 focus 字段时,REA 才会读取 displays 分区。

对于 install_adb_package,replace_requested 记录是否发送了 -r。
replaced 结果为 null:成功的安装响应不会说明软件包是全新安装还是替换安装,且 REA 不会事先查询设备。安装判定和主机 APK 摘要仍然可用。

push 和 install 会使用所选本地输入的私有快照。报告的大小和 SHA-256 标识这些被消费的字节;local_path 和 apk_path 保留原始输入路径,其内容之后可能会更改。创建快照需要与输入大小成比例的临时磁盘空间。如果快照清理失败,清理责任仍由 REA 负责,并会在后续操作之前以及关闭期间重试。

解读结果

  • 软件包数字声明保留失败。 inspect_adb_package 将第一个有效的 userId 或 appId 作为 user_id 接受:一个非负安全整数,包括零。空值表示未找到有效声明。有效值之前或之后的格式错误声明会保留在 numeric_issues 中:包含命令、字段、原因和原始 raw_line 的前十个问题,外加确切的 total。原因(reason)为 invalid_format 或 unsafe_integer。任何问题都会使软件包覆盖变为部分覆盖;已有的节缺失也可能使其变为部分覆盖。version_code 保留为精确的源文本。

  • 显示空值保留不确定性。 inspect_adb_display 接受正的安全整数。缺失或格式错误的可选值为空;numeric_issues 区分格式错误的声明与缺失。它保留前十个问题以及确切的总数,包含命令、字段、原始行和原因(invalid_format、unsafe_integer 或 nonpositive)。最后被识别的带标签声明胜出,即使其格式错误。物理密度标签即使格式错误也会抑制裸数字回退;如果没有该标签,则第一个无符号裸数字胜出,包括零或会使值为空的不安全整数。只有在物理密度已知且没有数字问题的情况下,显示覆盖才是完整的。缺失或格式错误的必需物理尺寸会产生协议失败,附带 details.partial_observation:一个来自提供方 adb 的 inspect_adb_display 观察,原因为 physical_size_absent 或 physical_size_malformed,以及一个保留客户端、序列号、可选显示事实、数字问题、空物理尺寸和部分覆盖的 result。

  • 目录名称保留空白字符和换行符。 符号链接目标也是如此,包括结尾换行符。包含无效 UTF-8 的记录在 unparsed_lines 中保留为带标签的十六进制,并使目录覆盖变为部分覆盖。

  • 推断带有其依据。 设备 kind 报告 kind_basis(序列号前缀或 USB 传输);APK role 报告 role_basis: file_name;emulator_observed 派生自构建的 ro.kernel.qemu 属性:仅当属性值为 "1" 时为 true,对于任何其他报告值为 false,属性缺失时为 null。原始属性值仍可在 properties 中获取。

  • 设备路径不是主机路径。 base_apk_device_path 的值位于设备文件系统上。只有 pull 和 capture 操作会生成主机本地路径,每条路径都配有字节大小和 SHA-256 摘要。被 pull 的文件的主机端名称是设备路径的最后一个 POSIX 路径段。REA 保留原始设备路径和文件名;如果缺少最后一段或出现 ./..,则在创建输出之前拒绝。Windows 主机遵循 Windows 文件名规则,而 POSIX 主机保留有效的 POSIX 文件名,例如包含空格或冒号的名称。软件包 pull 在拉取之前会根据目标文件系统检查报告的名称,并拒绝会导致本地路径冲突的名称。

  • 软件包路径拒绝是可诊断的。 来自 pull_adb_package 的拒绝返回 unreadable_output,其中 retryable: false,将 pm path 输出保留在 details.captured_output 中,并在 details.reason 中指明拒绝类型。unsafe_device_path 在 details.diagnostics.rejected_paths 中将拒绝的条目列为 {device_path, reason};duplicate_local_destination 报告共享的 file_name 和两个 device_paths;local_destination_unavailable 报告 device_path、file_name 和文件系统的 error_code。诊断信息保留原始设备字符串,而面向调用方的消息会转义不可见字符和双向字符,使其无法隐藏文本或重排路径。

  • 拆分是完整结果的一部分。 pm path 会枚举基础 APK 和每个已安装的拆分(split);pull 会覆盖整个集合。APK 使用其他名称的系统软件包会报告 unknown 角色,而不是猜测。

  • 部分结果保留事实。 个别 pull 失败时,仍会返回成功的工件并带有 coverage: "partial" 和逐文件的 failures。包含无法识别行的列表和软件包搜索会将前十个报告为 unparsed_lines,当看到更多时在限制中说明确切总数,并标记部分覆盖,而不是丢弃行。

  • 变更操作是显式的。 push_adb_file、install_adb_package 和 uninstall_adb_package 声明 mutatesTarget,并且绝不会隐式运行:push 操作在传输前检查设备上是否已存在文件,并且除非设置了 overwrite,否则拒绝它们。即使设置了 overwrite,失败或意外的存在性探测也会停止传输;只有 test -e 干净地以退出码 1 退出且无输出时,才解释为不存在。该检查不是原子性的,因此另一个进程可能会在 adb push 写入之前创建该路径。当可用时,设备的 sha256sum 会验证传输。缺少命令或输出中没有可识别摘要时,会报告 device_digest_source: "unavailable",并带有一条未验证传输的限制;安装操作会逐字保留设备的 Failure [...] 原因。

  • 生产者身份始终附带。 每个结果都嵌入了产生它的 adb 客户端(binary_path、path_source、version、installed_path)。

资源和生命周期限制

  • 调用方的值仅作为固定向量中的位置参数传入 adb;没有任何工具接受任意的 shell 命令行、input 或 monkey。应用/Activity 生命周期工具使用 am start -W 和 am force-stop。
  • 单次调用预算:客户端/设备 10-20 秒,shell 读取 20-60 秒并带有 64 KiB-8 MiB 的捕获上限(logcat 4 MiB,屏幕 32 MiB),传输和 bugreport 为 600 秒。输出捕获溢出会报告 resource_constraint 并指明字节额度。截止时间到期(包括客户端版本探测)会报告 provider_timeout 并指明实际的毫秒数。此版本没有针对这些截止时间或捕获上限的运行时覆盖。命令取消会保留调用方的确切原因;后续中止不会替换已经完成的命令失败。
  • 拉取和捕获输出都是新增式的:目标位置必须尚不存在。重新运行已完成的拉取会拒绝现有目录,而不是覆盖证据。
  • 在包拉取过程中取消会保留调用方选择的输出。错误信息包含部分观测结果,用于标识已完成的工件和被中断的本地路径;其余 APK 未尝试。
  • 屏幕捕获在发布其独占输出之前检查取消状态。如果取消在发布之后到达,错误会保留已提交文件的路径、摘要和尺寸;该文件仍可作为证据使用。
  • Bugreport 会在隔离的暂存区中收集,并以独占创建方式发布;文件名冲突时选择下一个数字后缀。如果发布失败,已完成的归档仍留在暂存区。错误的局部观测结果提供其可恢复的 local_path、原始的 adb_reported_path 以及未知的摘要状态。后续操作和关闭都会保留这份留存证据。
  • 失败的包获取回滚会保留其目录身份,并在后续工作之前以及关闭期间重试清理。清理会拒绝替换目录。最终关闭会停止接收新操作并排空活动操作。
  • 提供方不持有长生命周期进程;每次调用都是一次有界的 adb 运行。adb 服务器属于调用方。

除 intent 启动之外的 UI 交互(点击、输入、monkey 模糊测试、屏幕录制)仍然是一个独立通道:诸如 mobile-device-mcp 之类的候选方案会在设备上安装驱动软件,并与 Frida 插桩通道(#1359)在明确的变更权限契约约束下配对。除已连接设备(adb connect)之外的网络传输尚未暴露。

真实验证

npm run verify:adb 会针对真实设备执行每一项操作。它需要 adb(或 REA_ADB_PATH)以及任何已连接的模拟器或手机;当多个设备已连接时,--serial 选择其中一个,--pull PACKAGE 拉取真实包并对照磁盘上的文件验证返回的摘要,--install-apk PATH 使用该通道自有的探测程序驱动完整的安装 → 解析 → 卸载生命周期。

记录:已在 Linux 上针对 adb 34.0.5 和 Android 14(API 34)x86_64 模拟器验证——上述所有操作、一个含 224 个包的清单、287 个服务、92 个功能特性、一个以字节精确摘要拉取的真实双 APK 拆分集、一次已验证的 push/pull 往返、一次 1.3 MB 屏幕捕获,以及完整的包生命周期;通道摘要见 docs/testing.md。

Intent 整数附加数据使用有符号十进制文本。ADB 参数编码器会为 Android 对基数敏感的 --ei 解析器移除冗余的前导零,而保留的 Evidence 则保持调用方的原始拼写。包检查同时接受 userId 和 Android 16 的 appId 拼写,用于旧的 user_id 结果字段。

一次完成的 push 与设备摘要验证是分开的观测。验证超时、取消或输出预算失败会返回类型化的失败,并带有已完成传输的部分 Evidence。校验和命令不可用或校验和输出无法识别时,验证结果明确为未知。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 rea2/rea