ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ADB 1.0.39 Windows版:固定版本安全自查与排错

ADB 1.0.39 Windows版:固定版本安全自查与排错

简介:adb 1.0.39 Windows 版是一份面向 Android 应用开发者、测试与安全研究人员的命令行工具包,用于在电脑与真机或模拟器之间建立调试通道,解决设备连接、应用安装卸载、日志抓取与进程控制等日常需求。压缩包共 1716 个文件,主体为 adb、fastboot 等 exe 可执行程序及配套 dll 依赖,另有大量 html 文档、py 与 js 脚本、css 样式与 json 配置,承载 systrace、adb_profile_chrome 等性能采集与可视化组件;md、txt 说明与 license、readme 文件则交代版本与使用约定,整体约 9.53MB。已有 416 人学习下载。读者可直接获得可用的 Windows 平台工具链,并借由附带的 atrace 采样数据、systrace 模板与性能分析脚本,理解无线连接、logcat 实时日志、备份恢复与模拟输入等操作背后的实现细节,适合需要在真实设备上排查问题、做自动化测试或搭建调试环境的中级开发者查阅。

1. adb 1.0.39 Windows 版:一个不肯退休的调试桥

adb 1.0.39 Windows 版这几个字,在很多人的移动硬盘里对应的是 2016 年前后的一份 platform-tools 快照——压缩包解开只有adb.exe、两个AdbWin*.dll、fastboot.exe和一份说明文件。它没有adb pair,不会自己发现网络里的设备,甚至在某些新系统上会提示一堆看不懂的协议错误;但在 USB 直连的老测试机上做 Android 安全自查——确认应用是不是 debuggable、导出组件有没有裸奔、备份策略是不是形同虚设——它依然够用,而且行为稳定到可以写进脚本。某实验室的回归测试机跑的就是这套东西,理由很简单:换版本要重跑一遍全部用例,收益却只是多几个用不上的子命令。这篇笔记按“这份包是什么、怎么在 Windows 上落地、怎么用它读出可核对的安全清单、哪里会翻车”四段推进,适合手上一批不再升级的测试机、又需要把工具版本钉死的从业者。

2. 拆包看结构:三个进程、两个 DLL 和一次版本取舍

2.1 client、server、adbd:一条链路上其实有三个程序

很多人把 adb 当成一个整体,出问题时就会乱试。实际上敲下adb devices之后发生的事是分层的:命令行里跑的是 client,它只在几毫秒内活着;client 发现本地 5037 端口没有服务,就拉起一个常驻的 server;真正跟手机对话的是设备固件里自带的 adbd,PC 端的版本完全管不到它。这个分层决定了一件关键的事——PC 端用 1.0.39 去连一台 2020 年以后出厂的手机,adb devices、adb shell、adb pull这些基础能力大概率仍然可用,因为 client 与 adbd 之间的传输协议多年没动过;缺的只是后来新增的子命令和对新特性的支持。

我习惯在动手前先确认这条链路的三个节点是不是只有一份。Windows 上最常见的翻车不是设备坏了,而是机器里躺着三份不同版本的 adb.exe,谁先被PATH找到就用谁,结果 server 一会儿是这个版本、一会儿是那个版本。

# 确认 server 是否在跑、占的是哪个端口 adb start-server netstat -ano | findstr 5037 # 打印版本,1.0.39 的输出里会明确写 Android Debug Bridge version 1.0.39 adb version

第一行是幂等的,server 已在跑时什么也不做;第二行在 Windows 上查 5037 的监听进程,输出最后一列是 PID,配合tasklist /FI "PID eq <pid>"就能知道是哪个程序在占坑;第三行除了看客户端版本,也顺带确认二进制本身没坏——如果它直接报错而不是打印版本号,问题就落在下一节的 DLL 上。

2.2 AdbWinApi.dll 与 AdbWinUsbApi.dll:缺一个就打不开

这两个文件是 Windows 独有的一层胶水。AdbWinApi.dll负责 USB 设备的枚举和描述符读取,AdbWinUsbApi.dll是 WinUSB 后端,负责真正搬运数据。它们必须和adb.exe待在同一目录,而且是配套的——用新版的 DLL 去配旧版的 exe,运气好只是连不上设备,运气差直接弹0xc000007b。我见过最典型的错误是把adb.exe单独复制进C:\Windows\System32,觉得这样随处可用,结果每次调用都提示“计算机中丢失 AdbWinApi.dll”。

文件作用缺失或错配时的表现
adb.execlient + server 二合一命令不存在,where adb找不到
AdbWinApi.dllUSB 枚举、设备描述符启动即报“丢失 AdbWinApi.dll”
AdbWinUsbApi.dllWinUSB 数据通道能启动,但adb devices永远空
fastboot.exe刷机模式下的另一套协议与 adb 无关,可单独保留

判断方法很朴素:where adb找到路径之后,直接看那个目录里有没有这两个 DLL。没有就是包没解全,或者被安全软件悄悄隔离了——这种情况在离线内网机器上尤其常见,隔离之后没有任何提示,只觉得“adb 装了但不работает”。(实际判断还是看目录,别猜。)

2.3 什么情况下该留 1.0.39,什么情况下必须换掉

把版本选择讲成“新旧之争”没有意义,正确的问法是:你的工作流依赖的是协议还是特性。依赖协议(连接、shell、pull/push、install、logcat)的,1.0.39 足够;依赖特性(无线配对、增量安装、mDNS 发现)的,就必须换。

能力adb 1.0.39后续版本
USB 连接、shell、文件传输支持支持
adb backup整包备份支持,但多数场景产出空包同样受限
无线调试配对(配对码方式)不支持支持
局域网设备自动发现不支持支持
增量安装不支持支持
老设备与旧驱动兼容性好一般
与老脚本的输出格式兼容好有变化风险

所以我的取舍是:做授权范围内的安全自查、跑在固定测试机上、脚本输出要能逐年对比的,留 1.0.39;需要连 Android 11 以后的无线调试、或者 CI 里跑增量安装的,直接换新。这里没有任何“安全性差异”的考量——adb 本身这几年的改动集中在功能,不在于修补漏洞;把老版本说成“不安全”是吓唬人,把新版本说成“一定更好”也不成立,版本选错带来的时间成本远大于收益。

还有一条边界值得提前说清楚:Android 11 之后很多机型默认关闭了“USB 调试(安全设置)”,无线调试必须走配对码流程,而配对是 client 侧的能力,1.0.39 里根本没有这个子命令。遇到这种机器,正确做法是先用 USB 连一次、adb tcpip 5555把 adbd 切到网络模式,再adb connect。这条路在 1.0.39 上是通的,因为它复用的还是老协议。

3. Windows 落地:把 adb devices 从黄色感叹号跑到 device 状态

3.1 目录规划与 PATH:让机器里只剩一个 adb

第一步不是插手机,是清理环境。把包解到一个固定路径,比如D:\tools\platform-tools-1.0.39,路径里不要有中文和特殊符号——空格本身在现代 Windows 上问题不大,但一堆老脚本没给变量加引号,遇到空格就断。然后把这个目录加到用户级PATH的最前面,注意是“最前面”,否则它会输给 IDE 自带的 adb。

# 把 1.0.39 放到 PATH 最前面,只影响当前会话,确认无误再写进用户环境变量 $env:Path = "D:\tools\platform-tools-1.0.39;" + $env:Path # 验证:应当只输出一行,且指向刚才那个目录 where.exe adb # 确认版本与 DLL 都在 adb version Get-ChildItem "D:\tools\platform-tools-1.0.39" -Filter "AdbWin*.dll"

where.exe会列出所有命中项,如果输出两行以上,说明机器里还有别的 adb,要么删、要么改名成adb.bak,别留着当备胎——备胎就是下一次版本错配的源头。第三行的版本输出是后续所有排错的基准,出问题时第一句话应该是“版本是多少”,而不是“连不上”。

持久化写进环境变量时我一般用图形界面,改完必须新开一个终端才生效,很多人改完在旧窗口里反复试,得出“改了没用”的结论。这一步的血泪经验是:改环境变量之后先跑where adb,跑通了再插设备。

3.2 驱动与授权:黄色感叹号、unauthorized 与 adbkey

设备侧先做三件事:打开开发者选项、打开 USB 调试、把 USB 连接模式从“仅充电”改成“传输文件”。第三件最容易被忽略——部分机型在仅充电模式下根本不暴露 adb 接口,插上去只会充电,设备管理器里干干净净,让人以为线坏了。

PC 侧的判断点在设备管理器。插上手机后看有没有出现带黄色感叹号的未知设备,或者一个叫ADB Interface的条目。有感叹号就是驱动问题,装厂商驱动或者手动指定 WinUSB;没有感叹号但adb devices里是空的,多半是线或者口的问题,换 USB 2.0 口试一次往往就好了,这个玄学在 USB 3.x 控制器上至今有效。

# 彻底重启一次服务端,避免旧 server 缓存了错误的设备状态 adb kill-server adb start-server # -l 会带出设备型号与传输方式,老版本若不认这个参数就去掉 adb devices -l # 首次连接会弹 RSA 指纹,勾选一律允许后应看到 device 而不是 unauthorized

kill-server之后再start-server,是为了让 server 重新枚举 USB,很多“明明插着却看不到设备”的情况到这一步就恢复了。adb devices -l的-l是 long 模式,多输出 product、model、device 三列,方便在多台机器里认出目标;如果 1.0.39 对这个参数报错,直接去掉,不影响主流程。

授权状态有四种,含义完全不同:device是正常;unauthorized是设备端没确认指纹;offline是 adbd 起来了但握手失败,通常要重新插拔;no permissions在 Windows 上少见,基本等于驱动没装对。授权记录存在%USERPROFILE%\.android\下的adbkey和adbkey.pub,反复弹窗又确认不掉的时候,把这个目录里的两个文件删掉、kill-server再插一次,比任何教程都好使。注意这会清掉所有已授权设备,共用机器上要提前打招呼。

3.3 多设备与串号:-s 与环境变量

测试架上同时挂三四台机器是常态,这时任何一条 adb 命令都会报more than one device/emulator,然后什么都不做。解决办法是显式指定串号,串号就是adb devices第一列那串字符。

# 指定设备执行,串号从 adb devices 第一列取 adb -s 0123456789ABCDEF shell getprop ro.product.model # 整段脚本只对一台机器生效时,用环境变量更省事 set ANDROID_SERIAL=0123456789ABCDEF adb shell getprop ro.build.version.release

-s是逐条指定,写起来啰嗦但不会误伤;ANDROID_SERIAL是会话级默认值,一次设置后续所有命令都跟着走,适合把一组检查串成批处理。我一般在批处理开头设环境变量,中间需要临时切机时再用-s覆盖,两种方式混用不会有冲突,因为命令行参数的优先级更高。

4. 读设备状态:包、组件、备份、日志四条取证线

这一章是整份包真正的价值所在。adb 1.0.39 的命令集不大,但足够把一台设备上的应用安全姿态读成一份可核对、可存档、可 diff 的清单。前提是知道每条命令读的是哪一层、输出里哪些字段才是结论。

4.1 包清单:四个过滤器把设备分成几堆

pm list packages是最基础的一条,但只用裸命令等于没查。四个过滤器组合起来才能看出问题:-3只列第三方、-s只列系统、-d只列被禁用的、-e只列启用的。做自查时我通常先看“被禁用的系统应用”,因为那是厂商预装被停用的部分,往往还留着组件出口;再看“第三方包里带 test 字样的”,那是历史遗留的测试包,常年没人维护。

# 第三方应用,按包名排序,方便两次结果对比 adb shell pm list packages -3 | sort # 被禁用的包:这里面常有不该存在的预装残留 adb shell pm list packages -d # 单个包的详情,权限、签名摘要、安装来源都在这里 adb shell dumpsys package com.demo.app

pm list packages的输出格式是固定的package:包名,所以sort之后可以直接和上一次的结果做文本比对,新增一个包就是新增一个风险面。dumpsys package的输出很长,直接在终端里看会刷屏,正确姿势是重定向到文件再过滤,下一节展开。

需要提醒的是,-d和-e是互斥状态,同一时刻一个包只会在其中一边;如果某个包两边都出现,说明中间有安装或禁用操作正在发生,重新跑一次即可。这种小概率抖动在频繁装卸的测试机上很常见,别急着下结论。

4.2 组件导出:从 dumpsys 里挑出 exported=true

应用被外部拉起的入口只有那么几类:带 intent-filter 的 Activity、Service、BroadcastReceiver、ContentProvider。它们是否对外可达,取决于android:exported,而这个值在dumpsys package的输出里是明文的。自查的逻辑很直接——导出了、又没有permission保护的组件,就是这个应用暴露给全设备的攻击面。

# 把整包信息落到文件,避免在终端里翻页 adb shell dumpsys package com.demo.app > dump_demo.txt # Windows 侧过滤,只留导出相关行和上下文 findstr /i /n "exported permission" dump_demo.txt

第一步重定向发生在 PC 端,dumpsys的输出原样落到本地文件,编码不会出问题。第二步用findstr在 PC 侧过滤,/i忽略大小写,/n带行号——行号很关键,因为exported=true和它所属的组件名往往隔着好几行,没有行号就得靠肉眼回溯。看到exported=true之后,往上找最近的组件声明行,往下找有没有permission=,两个都确认了才算判断完整。

这里有个容易误判的点:exported=true但声明了android:permission且该权限是 signature 级的,实际外部应用也进不来,属于“看起来危险、实际安全”。反过来,exported=false的组件如果所在进程被同签名的应用共享,也未必绝对隔离。所以结论要写成“导出且无保护”,不要写成“导出了所以有问题”。

4.3 调试与备份:debuggable、allowBackup 怎么实证

android:debuggable="true"是自查里优先级最高的一个标志,因为一旦为真,任何拿到设备的人都能用run-as直接读应用私有目录。实证方法比读清单更可靠:直接试着进去。

# 能进去并打印 uid,说明 debuggable 为真 adb shell run-as com.demo.app id # 从 dumpsys 里再确认一次标志位,作为交叉验证 adb shell dumpsys package com.demo.app | findstr /i "flags"

run-as在非 debuggable 的应用上会直接返回package not debuggable,这个报错就是结论,不需要再查别的。第二条命令里的flags行会列出DEBUGGABLE、SYSTEM、STOPPED等标志,两个结果对得上才算证据链闭合。

备份这一条要格外小心。adb backup在 1.0.39 上是存在的,但 Android 9 之后绝大多数应用已经无法通过这条路径导出数据,命令会“成功”返回,产出的文件只有一千字节左右,本质是个空壳。

# -f 指定输出文件,-apk 表示连安装包一起打包(多数场景不需要) adb backup -f demo.ab com.demo.app # 看文件大小,1KB 上下基本等于空包,不能据此判断 allowBackup dir demo.ab

所以判断allowBackup,主要依据还是清单和dumpsys里的信息,adb backup只作为一种辅助验证:包体明显大于空壳,才有讨论价值。把“备份失败”直接等同于“配置正确”,是这类自查里最常见的错误结论之一。

4.4 日志与截图:logcat 过滤与二进制重定向的坑

自查过程需要留证,日志和截图是最直观的两类。logcat的关键是先把缓冲区清掉再复现,否则拿到的是一堆历史噪声。

# 清空缓冲区,再按优先级过滤,只留 W 以上 adb logcat -c adb logcat -v threadtime *:W # 截图走“先落盘再拉取”,比直接重定向稳 adb shell screencap -p /sdcard/shot.png adb pull /sdcard/shot.png ./shot.png

-c清空缓冲,-v threadtime换成带线程和时间戳的格式,方便和操作时间对齐;*:W表示所有 tag 只输出 Warning 及以上,噪声会少一个数量级。截图用两步走的原因是 Windows 的 PowerShell 在重定向时会做编码转换,直接adb exec-out screencap -p > shot.png拿到的图大概率打不开——文件头就被改坏了。这个坑我在第一次做取证的时候就踩过,打开是一张全黑图,排查了半天以为是设备问题。

5. 避坑记录:端口冲突、版本错配与二进制重定向的五次翻车

5.1 连接层:列表为空和命令卡死

坑一:adb devices一直空,start-server卡住不返回。现象是命令敲下去光标一直闪,等三十秒也没输出,Ctrl+C 才退出。原因是 5037 端口被别的程序占了,最常见的是各类手机助手、模拟器和某些 IDE 的后台服务,它们内置了自己的 adb server 并且先启动了。解决办法是先定位再处理:

netstat -ano | findstr 5037 tasklist /FI "PID eq 12345"

第一列输出最后一组数字是 PID,第二行拿 PID 换进程名。确认是无关程序就关掉;确实是工作需要的,就改成“先启动 1.0.39 的 server 再启动它”,谁先占坑谁说了算。这类冲突不会自己消失,每台新机器都要查一遍。

坑二:设备管理器里有ADB Interface,但状态是感叹号。现象是驱动装了一半,设备能被系统识别却拿不到数据通道。原因是 Windows 给这个接口绑了错误的驱动,可能是厂商的 MTP 驱动抢先匹配了。解决是在设备管理器里右键 → 更新驱动 → 从列表中选择 → 选 WinUSB 或厂商提供的 ADB 驱动,装完拔插一次。别信那些让你去改注册表adb_usb.ini的老教程,那套机制在 1.0.39 这个年代已经不生效了,改了只会让问题更复杂。

5.2 版本层:server 与 client 对不上

坑三:报adb server version (xx) doesn't match this client (39)。现象是命令能跑但行为诡异,偶尔报错,偶尔又正常。原因是机器里有两份不同版本的 adb,A 启动了 server,B 作为 client 连上去发现协议版本不一致,于是尝试杀掉旧 server 重启自己的——如果旧 server 正被 IDE 用着,IDE 那边就断了。解决顺序是:where adb找出全部实例,把非目标版本改名或删除,adb kill-server,再用目标版本start-server。我现在的习惯是把所有 IDE 里的 adb 路径都显式指向同一个目录,宁可多配一次,也不留给下一次玄学。

5.3 输出与授权:PNG 打不开、unauthorized 反复出现

坑四:截图重定向出来的 PNG 打不开。现象是文件生成了,大小也对,双击提示损坏。原因是 PowerShell 的>会把字节流当文本处理,插入 BOM 并转换换行。解决是改用“设备端落盘 +adb pull”,或者在 cmd 里用>,再或者用adb exec-out(该子命令在老版本上是否可用需实测,不可用就走落盘这条路)。

坑五:授权弹窗点了允许,下次插上还是unauthorized。现象是每次连接都要重新确认,甚至确认了也不生效。原因通常是%USERPROFILE%\.android\下的adbkey权限异常,或者被安全软件拦了写入,也可能是换了 USB 口导致设备指纹变了。解决办法是删掉adbkey与adbkey.pub,adb kill-server,重新插拔并在弹窗里勾选“一律允许”。如果仍然反复,检查该目录是不是只读,或者被同步盘占用。

这五个坑的共同点是:报错信息都不指向真正的原因。所以排错顺序应该固定下来——先where adb确认版本唯一,再netstat确认端口,再adb devices -l确认状态,最后才怀疑设备和线。把顺序固定住,比记住一百条命令有用。

6. 进阶固化:把一次自查写成可 diff 的批处理

单次手工检查的价值有限,真正的价值在于“同一套检查,跑两次能看出差异”。我一般会在测试机上放一个批处理,把四条线的输出各自落到独立文件,命名带日期,跑完直接和上一次比。

@echo off setlocal set ADB=adb set OUT=out_%1 if "%1"=="" set OUT=out_today mkdir %OUT% 2>nul %ADB% devices -l > %OUT%\01_devices.txt %ADB% shell getprop > %OUT%\02_props.txt %ADB% shell pm list packages -3 | sort > %OUT%\03_pkg3.txt %ADB% shell pm list packages -d > %OUT%\04_disabled.txt %ADB% shell dumpsys package > %OUT%\05_dump.txt echo done: %OUT%

第一个参数是批次名,不传就用默认值,这样同一台机器可以保留多份快照。mkdir ... 2>nul是为了让目录已存在时不报错,Windows 批处理里2>nul的作用就是把这类无关错误吞掉。五份输出的用途各不相同:01看设备是否还是同一台,02看系统版本和安全属性有没有被改过,03看有没有新装的第三方包,04看被禁用的组件有没有变化,05是原始底料,留给findstr做二次过滤。

比对用系统自带的fc就够,不用装任何东西:

fc /n out_0101\03_pkg3.txt out_0202\03_pkg3.txt

/n会带行号输出差异,一眼能看出多了哪个包、少了哪个包。我通常固定比对03和04两份,前者对应“新装了什么”,后者对应“什么被停用了”,这两类变化最能说明设备被动过。

有一处细节值得写进脚本注释:out_%1里的日期别用%date%拼,不同区域设置下格式不一样,2024/01/05和01/05/2024会拼出带斜杠的路径直接失败。用批次名传参是最省心的做法,谁跑谁负责命名。

那次把dumpsys package直接甩进终端、滚了三屏才发现关键行在上面、回头又滚不回去之后,我养成了一个习惯:任何输出超过一屏的 adb 命令,一律先重定向到文件,再用带行号的过滤去看。这个习惯救过我很多次,也让每次自查的结论都能被别人复核。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进