ADB 底层原理
AI 整理汇总
ADB 四层架构(Client-Server-Daemon-ADBD)
核心思想: ADB 采用客户端-服务器模型,但在实现上细分为四个关键组件,其中 adbd
就是设备端的 daemon
。
1. ADB Client (客户端)
- 角色: 这是你直接交互的部分。它是一个运行在你开发电脑(Host) 上的程序。
- 功能:
- 接收用户在命令行(如
adb devices
,adb shell
,adb install
,adb logcat
)或 IDE(如 Android Studio)中发出的命令。 - 将这些命令打包并通过本地 TCP 连接发送给 ADB Server。
- 接收 ADB Server 返回的命令执行结果或数据流(如 logcat 输出、shell 输出),并将其呈现给用户(打印到终端或显示在 IDE 窗口中)。
- 接收用户在命令行(如
- 典型代表: 命令行工具
adb.exe
(Windows),adb
(Linux/macOS) 就是最常用的 ADB Client。当你执行adb
命令时,你就是在启动一个 Client 进程。
2. ADB Server (服务器)
- 角色: 这是 ADB 架构的核心管理枢纽。它也是一个运行在开发电脑(Host) 上的后台守护进程。
- 功能:
- 管理连接: 绑定到 Host 上的特定 TCP 端口(默认 5037),监听来自 ADB Clients 的连接请求。
- 设备枚举: 负责发现所有连接到 Host(通过 USB 或网络)的 Android 设备和模拟器。
- 多路复用: 管理多个 ADB Clients 与多个设备之间的连接。一个 Server 实例可以同时为多个 Clients 服务,并管理多个设备的连接。
- 命令路由: 接收来自 Clients 的命令,根据命令指定的目标设备(如果未指定,则处理第一个可用设备),将命令转发给相应设备上的
adbd
。 - 数据传输中继: 在 Client 和设备
adbd
之间中继数据(如文件传输、端口转发数据)。 - 端口转发管理: 处理
adb forward
和adb reverse
命令,建立 Host 端口和设备端口之间的隧道。 - 启动与守护: 通常在你执行第一个
adb
命令时自动启动(如果尚未运行),并持续在后台运行以保持连接。
- 典型代表:
adb
命令加上start-server
或kill-server
参数可以显式控制这个后台进程。
3. ADB Daemon (adbd)
- 角色: 这是运行在目标 Android 设备或模拟器(Target) 上的关键后台守护进程。
adbd
就是这个 Daemon 的进程名称。 这是架构中最容易混淆的点:daemon
层指的就是设备上的adbd
进程。 - 功能:
- 监听连接: 在设备上监听来自 Host ADB Server 的连接。通常通过 USB 或网络(TCP 端口,默认 5555)。
- 命令执行: 接收来自 ADB Server 的命令请求,并在设备上实际执行这些命令(例如启动一个 shell 进程、安装 APK、读取日志、访问文件系统等)。
- 结果/数据返回: 将命令执行的结果或请求的数据(如 shell 输出、logcat 日志、文件内容)发送回 Host 上的 ADB Server。
- 提供访问点: 充当 Host 访问设备内部资源(shell、文件系统、日志、调试接口)的入口点。
- 权限控制: 根据设备的
ro.secure
和ro.debuggable
属性以及 USB 调试授权状态,控制 ADB 连接的权限级别(普通用户或 root)。
- 典型代表: 在 Android 设备上,
adbd
进程通常位于/system/bin/adbd
。设备的init
进程会根据系统属性(如service.adb.tcp.port
,persist.adb.tcp.port
,sys.usb.config
)在启动时或满足条件(如 USB 连接、开发者选项启用)时自动启动adbd
。
4. ADBD
- 如前所述,ADBD 就是第三层
ADB Daemon
的进程名本身。 它不是一个独立于daemon
的第四层组件,而是daemon
层的具体实现实体。在讨论架构时,“Daemon” 层和 “ADBD” 通常指的是同一个东西:运行在设备上的那个核心服务进程。 - 使用 “ADBD” 这个名称是为了更明确地指代这个特定的守护进程,区别于其他系统守护进程。
工作流程示例
- 用户输入: 你在开发电脑的终端输入
adb shell ls
。 - Client 启动:
adb
程序作为 Client 启动。 - Client -> Server: Client 连接到本地(127.0.0.1)端口 5037 上的 ADB Server(如果 Server 未运行,Client 会先启动它)。
- Server 路由: Server 确认目标设备(如果未指定,则为第一个可用设备),并将
shell:ls
命令封装发送给该设备。 - Server -> adbd: Server 通过已建立的 USB 或 TCP 连接(通常是设备的 5555 端口),将命令发送给目标设备上运行的
adbd
进程。 - adbd 执行:
adbd
收到命令后,在设备上启动一个 shell 进程(如/system/bin/sh
),并在该 shell 中执行ls
命令。 - adbd 收集结果:
adbd
捕获 shell 执行ls
后输出的目录列表内容。 - adbd -> Server:
adbd
将ls
命令的输出数据通过连接发送回 Host 的 ADB Server。 - Server -> Client: ADB Server 将接收到的输出数据转发给最初发起请求的 ADB Client。
- Client 输出: ADB Client 接收到数据,将其打印到你的终端屏幕上。你看到了设备上当前目录的文件列表。
总结与关键点:
- Client-Server 分离: 分离了用户界面(Client)和核心连接管理/设备通信(Server),允许多个工具(Clients)同时使用同一个 Server 和设备连接。
- Daemon (adbd) 是关键: 设备端的
adbd
是实际执行命令和提供设备访问能力的核心。没有它,ADB 无法工作。 - 长连接: ADB Server 和
adbd
之间通常保持一个长连接(即使没有活动命令),以实现快速响应和设备状态实时监控(如设备插拔)。 - 多路复用: Server 使得多个 Clients 可以透明地共享对同一设备的访问。
- 跨平台: Client 和 Server 运行在 Host (Windows, macOS, Linux),Daemon (adbd) 运行在 Target (Android OS)。
- 端口: Server 监听 Host 的 5037 端口,
adbd
监听设备的 5555 端口(及其后连续端口用于模拟器)。
adb connect 的 TCP/IP 握手过程
adb connect
命令用于通过 TCP/IP 网络将开发主机连接到运行了 adbd
的 Android 设备或模拟器。其背后的 TCP/IP “握手”过程不仅仅是标准的 TCP 三次握手,还包含了 ADB 特有的应用层协议交互。
核心目标: 在主机上的 ADB Server 和设备上的 adbd
守护进程之间建立一个稳定的 TCP 连接通道。
涉及的主要端口:
- 主机端 (ADB Server): 通常监听
5037
端口 (用于接收来自adb
Client 的命令)。 - 设备端 (
adbd
): 默认监听5555
端口 (用于接受来自 ADB Server 的 TCP 连接)。模拟器通常使用5554
,5556
,5558
等相邻端口。
adb connect <device_ip>[:port]
执行过程详解:
-
解析目标地址 (主机端 Client):
adb connect
命令本身由 ADB Client 执行。- Client 解析你提供的
<device_ip>
和可选的[:port]
(如果未指定端口,默认使用5555
)。
-
Client 联系 ADB Server (主机端):
- ADB Client 通过 localhost (127.0.0.1) 的 TCP 端口
5037
连接到已经在主机后台运行的 ADB Server。 - Client 向 Server 发送一个命令,其本质是:
host:connect:<device_ip>[:port]
。
- ADB Client 通过 localhost (127.0.0.1) 的 TCP 端口
-
ADB Server 发起 TCP 连接 (主机端 -> 设备端):
- ADB Server 收到
connect
指令。 - Server 尝试向指定的
<device_ip>:<port>
(通常是5555
) 发起一个标准的 TCP 三次握手 (SYN -> SYN-ACK -> ACK)。 - 这是真正意义上的 TCP/IP 连接建立:
- SYN: Server 发送 SYN 包到设备的
5555
端口。 - SYN-ACK: 如果设备上的
adbd
正在监听5555
端口且网络可达,adbd
的 TCP 协议栈会回应 SYN-ACK 包。 - ACK: Server 收到 SYN-ACK 后,发送 ACK 包确认。至此,TCP 连接建立成功。这个连接是 ADB Server (主机) 和
adbd
(设备) 之间的直接通道。
- SYN: Server 发送 SYN 包到设备的
- ADB Server 收到
-
ADB 应用层握手/身份验证 (设备端
adbd
<-> 主机端 ADB Server):- 仅仅建立 TCP 连接还不够,ADB 需要在应用层进行初始化和授权检查。这不是标准 TCP 握手的一部分,而是 ADB 协议的一部分,可以看作 ADB 层面的“握手”。
- 连接初始化:
- ADB Server 通过新建立的 TCP 连接发送一个 ADB 协议格式的消息。这个消息通常包含主机信息和一个
CNXN
(Connect) 命令的雏形。
- ADB Server 通过新建立的 TCP 连接发送一个 ADB 协议格式的消息。这个消息通常包含主机信息和一个
adbd
响应:- 设备上的
adbd
接收到初始化消息。 adbd
检查连接请求:- 如果这是首次连接该主机(或者设备的 RSA 密钥已更改),
adbd
会触发设备屏幕上的 “允许 USB 调试?” 提示(即使是通过网络连接)。用户必须在设备上点击 “允许”。 - 如果之前已经授权过该主机(主机的公钥存储在设备的
/data/misc/adb/adb_keys
中),则跳过授权提示。
- 如果这是首次连接该主机(或者设备的 RSA 密钥已更改),
adbd
发送一个响应消息回 ADB Server。这个消息包含设备标识符、协议版本、adbd
支持的最大数据包大小等信息,并且也以CNXN
命令形式确认连接。
- 设备上的
- 连接确认 (
CNXN
):- 当 ADB Server 收到
adbd
正确的CNXN
响应后,应用层连接正式建立。双方确认了协议版本和通信参数。
- 当 ADB Server 收到
-
Server 更新状态并通知 Client (主机端):
- ADB Server 将新连接成功的设备添加到其内部设备列表中。
- Server 通过
5037
端口连接将连接结果(成功或失败)返回给最初发起adb connect
命令的 ADB Client。
-
Client 显示结果 (主机端):
- ADB Client 接收到 Server 的响应。
- Client 在终端(或 IDE)中打印出结果信息:
- 成功:
connected to <device_ip>:<port>
- 失败:
unable to connect to <device_ip>:<port>
, 后面可能跟着具体原因(如超时、连接被拒绝、未授权等)。
- 成功:
USB 调试授权机制(RSA 密钥交换流程)
Android 的 USB 调试授权机制(也适用于网络 ADB 连接)核心在于建立主机(开发电脑)和设备之间的双向信任,防止中间人攻击。其核心是 RSA 密钥对的交换和验证。
核心角色:
- 主机 (Host): 开发电脑,运行 ADB Client 和 ADB Server。
- 设备 (Device): Android 设备或模拟器,运行
adbd
守护进程。 - 用户 (User): 在设备屏幕上操作的人。
核心文件:
- 主机端:
~/.android/adbkey
(Linux/macOS) 或%USERPROFILE%\.android\adbkey
(Windows): 主机的 RSA 私钥。这是 ADB 的核心机密,绝对不可泄露或共享。~/.android/adbkey.pub
(Linux/macOS) 或%USERPROFILE%\.android\adbkey.pub
(Windows): 主机的 RSA 公钥。这个文件需要安全地传输给设备进行验证。
- 设备端:
/data/misc/adb/adb_keys
(现代 Android 版本最常见位置): 存储所有被信任主机的 RSA 公钥列表。每行一个公钥。设备信任的核心依据。- (旧版本可能使用
/adb_keys
或其他位置,但/data/misc/adb/adb_keys
是主流)。
首次连接授权流程 (用户交互触发):
-
主机准备密钥对 (如果不存在):
- 当用户第一次在主机上执行任何需要与设备通信的
adb
命令(如adb devices
,adb shell
),且主机上~/.android/
目录下没有adbkey
和adbkey.pub
文件时: - ADB Server (或 Client) 会自动生成一个新的 2048 位的 RSA 密钥对 (
adbkey
私钥,adbkey.pub
公钥)。 - 这个密钥对与这台主机绑定。重要:私钥
adbkey
是这台主机身份的证明,必须保密!
- 当用户第一次在主机上执行任何需要与设备通信的
-
连接请求与设备端检测:
- 用户通过 USB 连接设备(或执行
adb connect <ip>
进行网络连接)并启用 USB 调试。 adbd
检测到来自新主机(其公钥不在设备的/data/misc/adb/adb_keys
文件中)的连接请求。
- 用户通过 USB 连接设备(或执行
-
设备端生成随机令牌 (Challenge):
adbd
生成一个随机的、加密安全的、一次性的令牌(Challenge)。这个令牌的目的是让主机证明它拥有对应公钥的私钥。
-
显示授权请求对话框:
adbd
将主机的 RSA 公钥的指纹(Fingerprint)(通常是 SHA-256 或 MD5 哈希值的可读形式)和这个随机令牌 一起发送给 Android 系统 UI。- Android 系统在设备屏幕上弹出一个对话框,显示类似以下信息:
- “允许 USB 调试吗?”
- “计算机的 RSA 密钥指纹:” (例如
SHA256:47:DE:...
或MD5:4E:AD:...
) - “一律允许使用此计算机进行调试” (复选框)
- “确定” / “取消” 按钮
- 这个指纹让用户视觉上确认他们正在连接的预期主机。 (理想情况下,用户应在主机上运行
adb keygen <path>
或查看adbkey.pub
内容来预先知晓并对比这个指纹)。
-
用户决策与确认:
- 用户必须仔细核对设备上显示的指纹是否与预期主机的公钥指纹一致。这是防止中间人攻击的关键一步。
- 如果一致,用户勾选“一律允许” (可选),并点击 “确定”。
- 如果指纹不一致或用户不确定来源,应点击 “取消” 拒绝连接。
-
设备端发送令牌给主机 (如果用户允许):
- 当用户点击“确定”后,设备系统 UI 通知
adbd
用户已授权。 adbd
将之前生成的 随机令牌 (Challenge) 通过已建立的(但尚未授权的)ADB 连接发送给主机端的 ADB Server。
- 当用户点击“确定”后,设备系统 UI 通知
-
主机端签名令牌 (Proof of Ownership):
- 主机端的 ADB Server 接收到设备发来的随机令牌 (Challenge)。
- ADB Server 使用主机的私钥 (
adbkey
) 对这个令牌进行 RSA 签名。生成一个签名结果 (Signature)。 - 这个签名操作证明了主机确实拥有与之前发送给设备的公钥相对应的私钥。
-
主机发送签名回设备:
- 主机 ADB Server 将签名结果 (Signature) 发送回设备的
adbd
。
- 主机 ADB Server 将签名结果 (Signature) 发送回设备的
-
设备端验证签名:
- 设备的
adbd
接收到主机发回的签名。 adbd
使用之前收到的、显示在对话框中的主机公钥 (adbkey.pub
) 来 验证 这个签名是否有效:- 验证签名是否是用该公钥对应的私钥生成的。
- 验证签名是否针对它之前发送的那个随机令牌 (Challenge)。
- 如果验证通过:
- 证明主机确实拥有正确的私钥,且响应是针对本次连接请求的(防止重放攻击)。
- 设备信任这台主机。
- 如果用户勾选了“一律允许”,
adbd
将主机的公钥 (adbkey.pub
的内容) 永久添加到设备的信任列表文件/data/misc/adb/adb_keys
中(需要相应权限)。 adbd
向主机发送最终的连接确认 (CNXN
命令响应)。- 授权完成,连接建立。 主机现在可以执行各种调试命令。
- 如果验证失败:
- 连接被拒绝 (
adbd
可能发送错误信息或直接断开)。 - 设备屏幕上可能会显示授权失败的消息。
- 主机的
adb
命令会报错(如device unauthorized
)。
- 连接被拒绝 (
- 设备的
后续连接流程 (已授权主机):
- 主机通过 USB 或网络连接到同一台设备。
adbd
检查收到的连接请求中的主机公钥。- 如果发现该公钥已经存在于设备的
/data/misc/adb/adb_keys
文件中:adbd
跳过授权对话框。- 直接进入类似步骤 3-9 的流程(生成 Challenge -> 主机签名 -> 设备验证签名),但用户无感知。
- 验证成功后立即建立连接。
- 如果公钥不在信任列表中,则触发首次连接授权流程(显示对话框)。
ADB 与 Android 系统交互
ADB 与 Android 系统交互的底层逻辑是一个分层通信过程,涉及 ADB 自身架构、Linux 内核机制和 Android 系统服务的协作。以下是核心逻辑的深度解析:
步骤 1:adbd 的权限提升(Root 访问)
- 关键配置文件:
/default.prop
中设置ro.secure=0
→ 允许adbd
以 root 权限运行
(普通 App 无法直接获取 root,但adbd
作为系统进程可突破限制) - 权限开关:
adb root
命令重启adbd
并获取 root 权限(需 userdebug 版本系统)
步骤 2:通过 Binder IPC 调用系统服务
adbd
通过 Binder 驱动与 Android 系统服务通信,这是最核心的交互机制:
-
Binder 调用示例:
adb shell pm install
→adbd
调用PackageManagerService
的installPackage()
方法adb shell am start
→ 调用ActivityManagerService
的startActivity()
方法
-
服务访问权限:
adbd
拥有android:sharedUserId="android.uid.system"
(系统级 UID)- 可访问签名权限(
signature
)或系统权限(system
)保护的 API
步骤 3:直接访问 Linux 层资源
adbd
作为原生进程,可直接操作 Linux 环境:
- 文件系统:
adb push/pull
→ 直接读写/data
、/system
等分区(需 SELinux 放行) - Shell 命令执行:
adb shell ls /data
→ 调用设备的/system/bin/sh
解释器执行命令 - 内核接口:
通过/proc
和/sys
获取系统信息(如adb shell cat /proc/cpuinfo
)
adb root与手机 root的区别
这是一个非常经典的面试陷阱题!adb root
命令不需要手机已被 root(即未解锁 Bootloader 或刷入 SuperSU/Magisk),但需要满足特定系统条件。以下是深度解析:
一、核心结论
场景 | adb root 能否成功? | 根本原因 |
---|---|---|
User 版本(正式零售版) | ❌ 失败 | 系统限制 ro.secure=1 ,禁止提权 |
Userdebug 版本(调试版) | ✅ 成功 | ro.secure=0 ,允许 adbd 以 root 运行 |
Eng 版本(工程机) | ✅ 成功 | 默认开放 root 权限 |
已 root 的 User 版本 | ⚠️ 不一定成功 | 依赖内核是否开放 adbd 提权 |
二、技术原理解析
1. adb root
的本质
该命令实际是 重启设备端的 adbd
守护进程并切换至 root 权限,而非破解系统。
关键代码逻辑(adb
源码):
// system/core/adb/services.cpp
void restart_root_service() {// 1. 检查当前 adbd 是否已运行在 root 模式if (getuid() == 0) return; // 2. 向 adbd 发送 "root:" 协议指令send_to_service("root:"); // 3. adbd 收到指令后,重启自身并提权property_set("service.adb.root", "1");restart_adbd(); // 重启 adbd 进程
}
2. 权限开关由系统属性控制
- 决定性属性:
ro.secure
ro.secure=0
→adbd
启动时自动拥有 root 权限ro.secure=1
→adbd
以 shell 用户权限运行(普通权限)
- 查看属性值:
adb shell getprop ro.secure # 输出 1 表示禁止提权
3. 系统构建类型决定属性值
系统类型 | ro.secure 值 | adbd 默认权限 | 常见设备 |
---|---|---|---|
User | 1 | shell 用户 | 零售机(如小米稳定版) |
Userdebug | 0 | root | 开发者测试机 |
Eng | 0 | root | 工厂工程机 |
三、User 版本(零售机)的提权方案
若需要在未开放 root 的零售机上使用 adb root
,必须突破系统限制:
1. 临时方案:替换 adbd
二进制文件
# 步骤:
adb push prebuilt_adbd /system/bin/adbd # 替换为可提权的 adbd
adb shell chmod 755 /system/bin/adbd
adb reboot
⚠️ 需解锁 Bootloader 并关闭 SELinux(风险高,仅测试用)
2. 内核层攻破:修改 ro.secure
属性
通过内核漏洞(如 CVE-2015-1805)修改只读属性:
// 漏洞利用伪代码
void exploit() {int fd = open("/dev/block/mmcblk0p1", O_RDWR); lseek(fd, PROPERTY_OFFSET, SEEK_SET);write(fd, "ro.secure=0", 11); // 篡改属性值
}
3. Magisk 方案(需已 root)
安装 Magisk 模块 ADB Root
,强制开启 adbd
提权功能。
四、面试高频问题解析
Q1: “执行 adb root
后提示 adbd cannot run as root in production builds
,为什么?”
答案:
当前设备为 User 版本,系统属性 ro.secure=1
禁止 adbd
提权。
Q2: “未 root 的手机如何让 adb shell
默认获得 root 权限?”
答案:
- 刷入 Userdebug/Eng 版本系统
- 或修改
default.prop
:adb pull /default.prop sed -i 's/ro.secure=1/ro.secure=0/g' default.prop adb remount && adb push default.prop /
Q3: “adb root
和 su
命令的 root 权限有何区别?”
命令 | 权限来源 | 作用范围 | 依赖条件 |
---|---|---|---|
adb root | 系统开放 ro.secure=0 | 仅限 adb 命令 | Userdebug/Eng 版本 |
su | SuperSU/Magisk 授权 | 整个 Shell 会话 | 手机已 root |
五、实战验证技巧
# 1. 检查 adbd 当前权限
adb shell id
# 输出:uid=2000(shell) gid=2000(shell) → 普通权限# 2. 尝试提权
adb root# 3. 再次检查权限
adb shell id
# 成功时输出:uid=0(root) gid=0(root) → root权限
终极结论:
✅ adb root
不需要手机已被 root,但要求设备是 Userdebug 或 Eng 版本系统(开发者常用)。
❌ 零售机(User 版本)因系统锁定 ro.secure=1
,无法直接使用此命令提权,需破解系统或刷机。
掌握这一区别,能避免面试时掉入“root权限”的概念陷阱!