为了弄清某服务机器人如何获取软件和系统更新,我沿着原客户端的调用链,做了一轮从 APK 静态分析到远程 ADB 取证的排查。

最初的问题很直接:能不能找到软件、固件和机器人 SDK 的下载地址?真正拆开后才发现,这几个目标走的并不是同一条路。接口地址、设备认证、更新响应和最终安装包之间,还隔着几层需要分别核实的机制。

本文于 2026 年 10 月 27 日整理,主要实机取证发生在 10 月 26 日,并结合此前的 APK 分析。到目前为止,已定位 APK 与 Android 系统的更新接口、认证流程和下载地址字段,也读到了设备缓存中的 token;尚未取得真实更新响应、可验证的下载直链或完整开发 SDK 包。

先看整体流程

身份认证取得 token;服务发现与 TLS 配置决定连接方式。客户端结合设备与版本参数分别查询软件和系统更新,再从真实响应中的包信息提取 URL,交给下载器。图中的虚线表示仍需真实响应验证的环节。

机器人 OTA 身份认证、更新查询与下载地址关系图

功能与字段已概念化,不包含厂商标识或真实凭证。点击图片可查看大图。

一、先把“签名”拆成几个问题

逆向过程中,“签名”很容易成为一个笼统的说法。这次涉及的机制至少有四种:

机制它解决的问题本次确认范围
身份认证与加密证明服务端如何识别设备、签发 token从客户端代码追到普通认证与芯片认证两条分支
客户端 TLS 证书HTTPS 连接是否需要客户端提供证书确认特定私有域名选择专门的证书配置
下载链接签名文件 URL 是否自带访问参数和时效目前没有真实 URL,尚不能判断
APK 签名安装包的签署与安装信任关系本轮没有完成重签名、替换安装或验收

后文所说的认证分析,主要指前两项。拿到 token 不等于能重签名安装包,也不代表能够生成服务器的下载链接签名。

二、从原客户端追到两类更新查询

分析对象是设备上的原始客户端。沿更新入口、后台服务与网络请求的调用关系继续追踪,可以看到软件更新和 Android 系统更新使用了不同的查询接口。为避免关联具体厂商,本文省略包名、版本号、内部命名和接口原始路径,以下使用功能描述。

更新类型查询接口(功能描述)响应中的下载地址位置(概念)
应用软件 / APK软件版本查询最新适用版本 → 应用包列表 → 各包 URL
Android 系统 / ROM系统版本查询最新适用版本 → 系统包 → URL

这里确认的是反编译代码中的声明、数据模型与调用关系,还没有真实响应来验证字段值。

软件响应包含一个包列表,因此一次软件更新可能涉及多个 APK。系统响应则有自己的包信息,除 URL 外,还能看到大小、哈希及元数据等字段。

请求也需要设备上下文。两类模型都包含设备身份、机型、硬件版本和软件主版本;软件查询另外包含系统版本。其中机型结构又有自己的型号和主次版本字段,不能与顶层的软件主版本混为一谈。

所以,单独抄出接口路径还不够。服务器需要根据当前设备与版本信息决定返回什么;即使以后拿到了结果,也应理解为该次请求对应的适用版本,不能直接称为所有设备的全局最新版。

三、为什么在 APK 中找到域名,还不能直接拼请求?

网络层使用了服务发现机制。代码里的更新服务标签是内部路由标识,客户端通过配置获取实际服务地址,再由拦截器替换请求主机。

其中一个容易误读的细节是内部主机选择标记:它参与客户端内部的路由选择,随后会被移除,不能照搬成服务端必需的请求头。

这意味着 APK 中的默认网关、引导配置地址与设备运行时实际使用的 OTA 地址,需要分开核对。设备环境、缓存或配置覆盖,都可能影响最终选择。

本次远程采集后来确实读到了 软件与系统更新服务的实际域名。这推进了定位,但仍然不能据此认定请求已经成功:认证、TLS 和设备参数还需要一起成立。

四、身份认证:普通分支与芯片分支

客户端请求使用 Access-Token,token 的选择还与主机配置有关。代码中至少存在两条机器人认证路线。

普通认证分支

普通分支先获取 secret,再使用 secret 与设备身份等参数获取 token。客户端随后缓存并复用 token,并不是每次检查更新都重新申请一次。

这一分支的代码能够说明协议结构,却不能证明某台设备当前一定适用它,也不能证明离线编写的请求已被服务器接受。

芯片认证分支

另一条分支先获取服务器时间,再将设备 MAC 与经过校正的时间组合,交给设备上的加密实现生成证明,随后请求 token。响应中的 token 还需要经对应解密流程,才能成为有效请求头的值。

静态代码显示,这条路线涉及芯片或 TEE 实现,以及根据硬件条件选择的加密路径。知道 SN 和 MAC,并不足以在另一台电脑上完整复现设备侧能力。

代码里还出现了 120 秒的新鲜度比较。它针对证明材料,不能写成“Access-Token 两分钟过期”。本次未确认 token 的准确有效期,也不能仅凭外观将它认定为 JWT。

五、远程 ADB:最先遇到的是目标选择问题

设备连接在远程 Windows 电脑上,命令需要由对方在 PowerShell 中执行。

最初读取 token 得到数量为零,但同一轮 ADB 已经报出:

more than one device/emulator

这时命令没有成功读到目标,零条结果不能证明设备不存在 token。后续连接清单中又出现多条在线记录,可能对应同一设备的不同连接方式;仅凭记录数量也不能断言有几台物理机器人。

解决办法是明确选择目标 transport,再执行读取。transport 编号属于当前连接状态,重连后需要重新确认,不能长期写死。

目标选择正确后,回传结果确认了 root 读取权限;采集程序也成功解码两项 token 缓存,每项为 32 字符。报告只保留“读取成功”和长度,未回传 token 明文。

这里的进展是读到了缓存,token 当前是否有效仍未验证。 十六进制形式也只是该缓存的表示方式;把它还原为文本,不等于破解了设备认证算法。

六、有了 token,为什么普通 curl 还不能直接用?

实际发现的服务域名落在原客户端的私有证书分支中。网络代码会对匹配的域名选择带客户端证书材料的 TLS 配置,其余主机则走另一套连接配置。

因此,请求能否成功至少取决于服务路由、TLS 配置、token 和设备参数。只把路径与 token 填入普通 curl 模板,仍可能在 TLS 阶段就失败,根本没有走到业务认证。

本次没有公开证书私钥,也没有将不完整的模板描述为“已验证可查询”。后续应优先复用原客户端已配置好的连接与身份环境,观察它实际收到的内容。

七、检查本地更新缓存:数据库正常,任务表为空

另一条路线是直接检查已保存的 OTA 任务。

远程采集复制了 更新配置文件,以及 SQLite 的 更新任务主库、-wal 和 -shm 文件。之所以连 WAL 一起取,是因为较新的事务可能尚未写回主库,只拿主文件容易遗漏内容。

这次检查得到:

项目结果
SQLite 快速完整性检查ok
更新任务表0 条记录
主库和 WAL 中的 URL 候选0 个
OTA 参数取得主版本与机型参数

附件来自运行中数据库文件的顺序复制,并非事务一致的 SQLite 备份。因此,这次能说明的是:所取得的缓存中没有恢复出下载地址。空任务表不能证明服务器没有更新,也不能证明更新查询一定失败。

八、415 字节的 二进制日志:乱码最终被确认是压缩流

缓存没有结果,下一步转向检查更新时的日志。

普通日志采集只得到一条文件位置记录,指向一个 二进制日志 文件。最初将它作为文本读取,得到不可读字符;这种处理方式可能改变二进制数据,于是改成原样复制后再分析。

原始文件只有 415 字节。结合 原生日志库中的压缩流与解码相关代码,最终确认这份样本采用 带两字节小端属性头的 PPMd8 压缩流。

解码结果如下:

检查项结果
原始压缩文件大小415 字节
解码文本大小730 字节
压缩流结束标记正常到达
解码后内容日志头、文件轮换记录
更新请求、响应或下载 URL未出现

这一步解决了“文件是什么格式”,却没有取得更新响应。解码成功与找到目标内容是两件事,也不能将这个样本的格式推广为所有 二进制日志 文件都相同。

操作方反馈,点击检查更新后页面提示“已经是最新版本”。但还没有原始 JSON,因此无法判断响应是否带有包信息或 URL,也无法独立核对页面结论。

九、下载地址到底从哪里来?

沿现有更新下载代码继续追踪,能看到更新响应中的包 URL 被复制到下载任务,再交给 Android DownloadManager。

在已恢复的这条调用路径中,没有看到额外添加 Access-Token 或 Cookie,也没有看到下载前再请求一次链接签名接口。

这是一个有用的静态观察:最终 URL 来自更新响应中的包字段。 但它仍不能证明 URL 永久有效或公开可用。服务器可能直接返回带签名参数的 URL;由于本次没有真实地址,其访问策略、有效期和是否可复用都还是未知。

也不能根据 APK 版本名、包名或某次时间接口请求,推算出一个可靠的下载直链。

十、SDK 版本号与 SDK 下载包,要单独处理

排查中还出现了两个容易混淆的版本概念:

  • 采集报告里的 Android SDK 等级指系统 API 等级。
  • 设备页面还显示了程序引用的机器人 SDK 版本;具体版本字符串在此省略。

它们都不能直接提供完整开发 SDK 的下载地址。设备应用更新、系统固件更新与开发者 SDK 分发是不同目标,后者仍需要另查发布渠道。

十一、下一步:观察原 OTA 进程实际收到的响应

到这里,继续重复查空缓存或同一份日志,能够增加的信息已经很有限。

下一条更直接的路线,是在原 OTA 进程的 HTTP / Retrofit 响应边界观察已解密内容,让原程序继续处理服务发现、客户端证书和 token。可以考虑使用 Frida,但本轮还没有完成设备兼容性验证或实际响应采集。

另一条路线是在电脑上适配完整客户端 TLS 配置,使用已授权的设备参数与有效 token 发起只读查询。目前也没有完成这条路线的服务端验证。

设备调试页的开关则不能随意当作网络日志开关使用:已检查的处理代码涉及 USB 调试、安装模式等系统行为,并未确认它们能打开 HTTP 响应日志。

目前,这轮排查把“找下载地址”缩小成了一个更具体的问题:如何取得原客户端的一次完整更新响应。接口、认证路径和 URL 字段已有证据,最后的响应与下载验证仍需补齐。后续有了新证据,再继续更新这篇记录。

最后修改:2026 年 10 月 27 日
如果觉得我的文章对你有用,请随意赞赏