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 forwardadb reverse 命令,建立 Host 端口和设备端口之间的隧道。
    • 启动与守护: 通常在你执行第一个 adb 命令时自动启动(如果尚未运行),并持续在后台运行以保持连接。
  • 典型代表: adb 命令加上 start-serverkill-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.securero.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" 这个名称是为了更明确地指代这个特定的守护进程,区别于其他系统守护进程。

工作流程示例

  1. 用户输入: 你在开发电脑的终端输入 adb shell ls
  2. Client 启动: adb 程序作为 Client 启动。
  3. Client -> Server: Client 连接到本地(127.0.0.1)端口 5037 上的 ADB Server(如果 Server 未运行,Client 会先启动它)。
  4. Server 路由: Server 确认目标设备(如果未指定,则为第一个可用设备),并将 shell:ls 命令封装发送给该设备。
  5. Server -> adbd: Server 通过已建立的 USB 或 TCP 连接(通常是设备的 5555 端口),将命令发送给目标设备上运行的 adbd 进程。
  6. adbd 执行: adbd 收到命令后,在设备上启动一个 shell 进程(如 /system/bin/sh),并在该 shell 中执行 ls 命令。
  7. adbd 收集结果: adbd 捕获 shell 执行 ls 后输出的目录列表内容。
  8. adbd -> Server: adbdls 命令的输出数据通过连接发送回 Host 的 ADB Server。
  9. Server -> Client: ADB Server 将接收到的输出数据转发给最初发起请求的 ADB Client。
  10. 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] 执行过程详解:

  1. 解析目标地址 (主机端 Client):

    • adb connect 命令本身由 ADB Client 执行。
    • Client 解析你提供的 <device_ip> 和可选的 [:port] (如果未指定端口,默认使用 5555)。
  2. Client 联系 ADB Server (主机端):

    • ADB Client 通过 localhost (127.0.0.1) 的 TCP 端口 5037 连接到已经在主机后台运行的 ADB Server
    • Client 向 Server 发送一个命令,其本质是:host:connect:<device_ip>[:port]
  3. 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 (设备) 之间的直接通道。
  4. ADB 应用层握手/身份验证 (设备端 adbd <-> 主机端 ADB Server):

    • 仅仅建立 TCP 连接还不够,ADB 需要在应用层进行初始化和授权检查。这不是标准 TCP 握手的一部分,而是 ADB 协议的一部分,可以看作 ADB 层面的"握手"
    • 连接初始化:
      • ADB Server 通过新建立的 TCP 连接发送一个 ADB 协议格式的消息。这个消息通常包含主机信息和一个 CNXN (Connect) 命令的雏形。
    • adbd 响应:
      • 设备上的 adbd 接收到初始化消息。
      • adbd 检查连接请求:
        • 如果这是首次连接 该主机(或者设备的 RSA 密钥已更改),adbd 会触发设备屏幕上的 "允许 USB 调试?" 提示(即使是通过网络连接)。用户必须在设备上点击 "允许"
        • 如果之前已经授权过该主机(主机的公钥存储在设备的 /data/misc/adb/adb_keys 中),则跳过授权提示。
      • adbd 发送一个响应消息回 ADB Server。这个消息包含设备标识符、协议版本、adbd 支持的最大数据包大小等信息,并且也以 CNXN 命令形式确认连接。
    • 连接确认 (CNXN):
      • 当 ADB Server 收到 adbd 正确的 CNXN 响应后,应用层连接正式建立。双方确认了协议版本和通信参数。
  5. Server 更新状态并通知 Client (主机端):

    • ADB Server 将新连接成功的设备添加到其内部设备列表中。
    • Server 通过 5037 端口连接将连接结果(成功或失败)返回给最初发起 adb connect 命令的 ADB Client。
  6. Client 显示结果 (主机端):

    • ADB Client 接收到 Server 的响应。
    • Client 在终端(或 IDE)中打印出结果信息:
      • 成功:connected to <device_ip>:<port>
      • 失败:unable to connect to <device_ip>:<port>, 后面可能跟着具体原因(如超时、连接被拒绝、未授权等)。

USB 调试授权机制(RSA 密钥交换流程)

Android 的 USB 调试授权机制(也适用于网络 ADB 连接)核心在于建立主机(开发电脑)和设备之间的双向信任 ,防止中间人攻击。其核心是 RSA 密钥对的交换和验证

核心角色:

  1. 主机 (Host): 开发电脑,运行 ADB Client 和 ADB Server。
  2. 设备 (Device): Android 设备或模拟器,运行 adbd 守护进程。
  3. 用户 (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 是主流)。

首次连接授权流程 (用户交互触发):

  1. 主机准备密钥对 (如果不存在):

    • 当用户第一次 在主机上执行任何需要与设备通信的 adb 命令(如 adb devices, adb shell),且主机上 ~/.android/ 目录下没有 adbkeyadbkey.pub 文件时:
    • ADB Server (或 Client) 会自动生成 一个新的 2048 位的 RSA 密钥对 (adbkey 私钥, adbkey.pub 公钥)。
    • 这个密钥对与这台主机绑定重要:私钥 adbkey 是这台主机身份的证明,必须保密!
  2. 连接请求与设备端检测:

    • 用户通过 USB 连接设备(或执行 adb connect <ip> 进行网络连接)并启用 USB 调试。
    • adbd 检测到来自新主机(其公钥不在设备的 /data/misc/adb/adb_keys 文件中)的连接请求。
  3. 设备端生成随机令牌 (Challenge):

    • adbd 生成一个随机的、加密安全的、一次性的令牌(Challenge)。这个令牌的目的是让主机证明它拥有对应公钥的私钥。
  4. 显示授权请求对话框:

    • adbd 将主机的 RSA 公钥的指纹(Fingerprint) (通常是 SHA-256 或 MD5 哈希值的可读形式)和这个随机令牌 一起发送给 Android 系统 UI
    • Android 系统在设备屏幕上弹出一个对话框,显示类似以下信息:
      • "允许 USB 调试吗?"
      • "计算机的 RSA 密钥指纹:" (例如 SHA256:47:DE:...MD5:4E:AD:...)
      • "一律允许使用此计算机进行调试" (复选框)
      • "确定" / "取消" 按钮
    • 这个指纹让用户视觉上确认他们正在连接的预期主机。 (理想情况下,用户应在主机上运行 adb keygen <path> 或查看 adbkey.pub 内容来预先知晓并对比这个指纹)。
  5. 用户决策与确认:

    • 用户必须仔细核对 设备上显示的指纹是否与预期主机的公钥指纹一致 。这是防止中间人攻击的关键一步
    • 如果一致,用户勾选"一律允许" (可选),并点击 "确定"
    • 如果指纹不一致或用户不确定来源,应点击 "取消" 拒绝连接。
  6. 设备端发送令牌给主机 (如果用户允许):

    • 当用户点击"确定"后,设备系统 UI 通知 adbd 用户已授权。
    • adbd 将之前生成的 随机令牌 (Challenge) 通过已建立的(但尚未授权的)ADB 连接发送给主机端的 ADB Server。
  7. 主机端签名令牌 (Proof of Ownership):

    • 主机端的 ADB Server 接收到设备发来的随机令牌 (Challenge)。
    • ADB Server 使用主机的私钥 (adbkey) 对这个令牌进行 RSA 签名 。生成一个签名结果 (Signature)
    • 这个签名操作证明了主机确实拥有与之前发送给设备的公钥相对应的私钥
  8. 主机发送签名回设备:

    • 主机 ADB Server 将签名结果 (Signature) 发送回设备的 adbd
  9. 设备端验证签名:

    • 设备的 adbd 接收到主机发回的签名。
    • adbd 使用之前收到的、显示在对话框中的主机公钥 (adbkey.pub)验证 这个签名是否有效:
      • 验证签名是否是用该公钥对应的私钥生成的。
      • 验证签名是否针对它之前发送的那个随机令牌 (Challenge)。
    • 如果验证通过:
      • 证明主机确实拥有正确的私钥,且响应是针对本次连接请求的(防止重放攻击)。
      • 设备信任这台主机。
      • 如果用户勾选了"一律允许",adbd 将主机的公钥 (adbkey.pub 的内容) 永久添加到设备的信任列表文件 /data/misc/adb/adb_keys(需要相应权限)。
      • adbd 向主机发送最终的连接确认 (CNXN 命令响应)。
      • 授权完成,连接建立。 主机现在可以执行各种调试命令。
    • 如果验证失败:
      • 连接被拒绝 (adbd 可能发送错误信息或直接断开)。
      • 设备屏幕上可能会显示授权失败的消息。
      • 主机的 adb 命令会报错(如 device unauthorized)。

后续连接流程 (已授权主机):

  1. 主机通过 USB 或网络连接到同一台设备。
  2. adbd 检查收到的连接请求中的主机公钥。
  3. 如果发现该公钥已经存在 于设备的 /data/misc/adb/adb_keys 文件中:
    • adbd 跳过授权对话框
    • 直接进入类似步骤 3-9 的流程(生成 Challenge -> 主机签名 -> 设备验证签名),但用户无感知
    • 验证成功后立即建立连接。
  4. 如果公钥不在信任列表中,则触发首次连接授权流程(显示对话框)。

ADB 与 Android 系统交互

ADB 与 Android 系统交互的底层逻辑是一个分层通信过程 ,涉及 ADB 自身架构Linux 内核机制Android 系统服务的协作。以下是核心逻辑的深度解析:

步骤 1:adbd 的权限提升(Root 访问)

  • 关键配置文件
    /default.prop 中设置 ro.secure=0 → 允许 adbdroot 权限 运行
    (普通 App 无法直接获取 root,但 adbd 作为系统进程可突破限制)
  • 权限开关
    adb root 命令重启 adbd 并获取 root 权限(需 userdebug 版本系统)

步骤 2:通过 Binder IPC 调用系统服务

adbd 通过 Binder 驱动与 Android 系统服务通信,这是最核心的交互机制:
adbd Binder Driver SystemServer 发送 Binder 事务请求 路由请求到目标服务 返回执行结果 传递响应数据 adbd Binder Driver SystemServer

  1. Binder 调用示例

    • adb shell pm installadbd 调用 PackageManagerServiceinstallPackage() 方法
    • adb shell am start → 调用 ActivityManagerServicestartActivity() 方法
  2. 服务访问权限

    • 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 源码):

c 复制代码
// 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=0adbd 启动时自动拥有 root 权限
    • ro.secure=1adbdshell 用户权限运行(普通权限)
  • 查看属性值

    bash 复制代码
    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 二进制文件
bash 复制代码
# 步骤:
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)修改只读属性:

c 复制代码
// 漏洞利用伪代码
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 权限?"

答案

  1. 刷入 Userdebug/Eng 版本系统

  2. 或修改 default.prop

    bash 复制代码
    adb pull /default.prop
    sed -i 's/ro.secure=1/ro.secure=0/g' default.prop
    adb remount && adb push default.prop /
Q3: "adb rootsu 命令的 root 权限有何区别?"
命令 权限来源 作用范围 依赖条件
adb root 系统开放 ro.secure=0 仅限 adb 命令 Userdebug/Eng 版本
su SuperSU/Magisk 授权 整个 Shell 会话 手机已 root

五、实战验证技巧

bash 复制代码
# 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权限"的概念陷阱!

相关推荐
STER labo7 小时前
mysql配置环境变量——(‘mysql‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件解决办法)
数据库·mysql·adb
sjmaysee10 小时前
CentOS7安装Mysql5.7(ARM64架构)
adb·架构
AtOR CUES1 天前
MySQL——表操作及查询
android·mysql·adb
mOok ONSC1 天前
mysql9.0windows安装
windows·adb
xxjj998a1 天前
Laravel8.x核心特性详解
数据库·mysql·adb
TeDi TIVE1 天前
Linux下MySQL的简单使用
linux·mysql·adb
TeDi TIVE1 天前
MySQL四种备份表的方式
mysql·adb·oracle
rleS IONS1 天前
Linux系统离线部署MySQL详细教程(带每步骤图文教程)
linux·mysql·adb
计算机安禾1 天前
【Linux从入门到精通】第40篇:LAMP/LNMP环境一键部署脚本实战
android·linux·adb
xxjj998a1 天前
Laravel7.x核心特性全解析
数据库·mysql·adb