最近安全研究员 Zerotistic 发布了一项有意思的逆向工程研究,他把一台普通 Linux 机器伪装注册进苹果 Find My People 网络,成功获取并解密好友已经授权共享的实时位置数据。
这里需要划清重点,这并不是可以随意追踪陌生人的安全漏洞,整个实验建立在对方已经主动向自己 Apple 账号开放位置共享的前提下,但整套技术实现已经把苹果 Find My 底层私有协议完整展现在大众面前。
相关阅读:物理隔离也能传数据?揭秘苹果Find My网络的隐蔽数据通道
项目的起源非常简单,研究员只是想做一套小自动化脚本,当朋友抵达或者离开某些地点的时候,在 Discord 自动推送提醒,朋友知情并且同意这次测试,研究员最初以为这只是调用一组简单 iCloud 接口拿到 JSON 数据,一个晚上就可以搞定,实际上手之后才发现,Find My People 的位置共享完全没有向网页端开放,公开 iCloud 接口只能查看自己设备的定位,拿不到好友共享过来的人员位置信息。
没有现成公开完整实现案例,手头也没有 Mac 用来调试官方 FindMy.app,研究员只能一边参考 FindMy.py、pypush 这类开源项目,一边反编译 fmfd、findmylocated、searchpartyd 这些苹果系统后台守护进程,一点点试错,不断调整请求字段、编码格式,观察苹果服务器返回的状态码来摸索协议细节。
研究范围说明:这套自研客户端仅可以读取账号下已经被接受的位置共享关系,不能发送共享邀请,不能修改任何人的共享权限,无法新增家庭成员,也不能远程操控设备,地理围栏的判断逻辑全部是在 Linux 本地解密完成之后实现,不在苹果服务端执行。
最开始研究员盯上 fmfd 遗留的initClient接口
这个接口源自旧版 Find My Friends 服务,即便应用改名 Find My 之后接口依旧保留在 MobileMe 命名空间下,接口需要传入账号 DSID(目录服务数字 ID)、设备标识等参数。研究员拿到 iCloud 登录返回的一批 MobileMe 令牌之后,先后测试 mmeFMFAppToken、mmeAuthToken、searchPartyToken,全部返回 401 鉴权失败。
这背后的机制是苹果登录相当于令牌代理,每一项 iCloud 私有服务都需要专属凭证,不是拿到任意和 Find My 相关名字的令牌就可以完成调用。
就算拿到正确令牌,请求依旧不能完成,initClient还需要传入 Find My Friends 主机地址、APNs(Apple Push Notification service,苹果推送服务)的 courier token、完整 clientContext,还有一组叫做 anisette 的 HTTP 请求头。anisette 相当于身份证明材料,用来向苹果服务器证明请求来自一台合规的苹果客户端,一部分数值绑定模拟设备身份,另一部分是短期一次性校验码,缺少这组头部,请求会被直接拒绝。

组装完符合原生守护进程规范的请求体之后,服务器返回了已经接受共享关系列表,研究员拿到好友对应的 fmId,但是响应里没有位置坐标,也没有解密密钥,响应中的 secureLocationsCapable 字段为 true,fallbackToLegacyAllowed 为 false,代表这份位置共享走全新加密链路,旧的兼容通路已经关闭。此时仅仅完成识别共享关系,离拿到真实位置还差很远。

加密位置共享的核心通路跑在 IDS(Apple Identity Service,苹果私有设备身份与加密消息层)之上,iMessage、Find My 设备间消息都依托这套体系,消息数据包经由 APNs 推送分发。浏览器 iCloud 登录只能证明账号身份,没办法把一台 Linux 机器变成 IDS 体系内的合法设备,这是最大的一道门槛。
旧版本 pypush 的登录逻辑已经失效,直接提交密码和二次验证会返回 5000、5068 错误码,研究员改用苹果内部 GrandSlam 账号登录协议,走完 SRP 安全远程密码交换流程,完成 2FA 双重认证,拿到 ADSID 账号标识以及短期等效密码的 PET 令牌,接着向setup/signin/v2/login发起请求,申请com.apple.private.ids服务的委托授权,请求除了带上 anisette 全套头部,还需要提交一份短期校验数据 NAC,用来匹配模拟硬件配置。
https://theapplewiki.com/wiki/Grand_Slam_Authentication

拿到 IDS 委托令牌之后下一步调用 authenticateDS 接口申请 IDS 身份证书,提交 CSR 证书签名请求。这个接口总是返回 HTTP200,真实结果藏在返回的 plist 报文内部,研究员反复踩坑,RSA 不同哈希算法、XML 和二进制 plist、是否开启 gzip 压缩都会直接决定成败。
最终可行方案要求 CSR 使用 PKCS#10 标准格式,2048 位 RSA 密钥搭配 SHA‑1 签名,通用名字段填写 IDS profileID 大写 SHA‑1 哈希,报文封装成 XML 格式 plist 再做 gzip 压缩。


另外一个容易踩坑的细节是 Linux 系统内置根证书包 certifi 缺少部分苹果根 CA,研究员手动导入 Apple Root CA 和 Apple Root CA G3 指纹,保证 TLS 证书校验全程开启,避免中间人风险。
拿到 IDS 身份证书不等于注册完成,调用 id‑register 做设备注册的时候,直接注册 Find My 相关服务会返回 6001 错误。
查阅 rustpush 开源代码之后才理清规则,不能直接注册单项 FMF 服务,必须注册 com.apple.private.alloy.multiplex1 多路复用服务,再把 com.apple.private.alloy.fmf、com.apple.private.alloy.fmd 等六项 Find My 相关子服务挂载在它下面。

注册报文还需要填写 client‑data,声明同时支持传统 IDS 消息格式以及 NGM v13 新一代设备消息协议,NGM 基于 P‑256 设备密钥与预签名密钥构建,后续位置密钥就封装在 NGM 信封中传输,注册报文同时附带 Key Transparency v5 元数据、Find My 能力标记。
整个请求报文依旧是 XML plist 加上 gzip 压缩,HTTP 头部携带签名,签名规则很特殊,不是标准 HTTP 签名逻辑,需要把 nonce 时间戳、bag 接口标识、查询参数、压缩后报文、APNs 推送 token 做二进制拼接,每个字段前面附加四字节长度前缀,分别用 IDS 密钥、APNs 推送密钥生成两份签名一并提交。

走完这整套流程之后,Linux 机器正式在苹果服务中获得合法身份,拥有 APNs 身份凭证、IDS 认证证书、账号 handle、消息密钥、Find My 服务证书。

接下来就要监听 Find My 的消息,客户端连接私有 APNs 二进制长连接,订阅六项 Find My 子服务主题、SearchParty 容器主题,同时必须订阅父主题 com.apple.private.ids,只订阅子服务会完全收不到任何推送消息,父主题充当消息中转网关。推送包只传递主题 SHA‑1 哈希,本地需要完成哈希到服务名称映射。
每一条推送携带 IDS plist,包含指令、发送方 handle、发送方推送 token、加密模式和负载,不能直接解密消息,同一个账号可以有多台注册设备,每台设备有独立身份,研究员会调用 IDS 目录查询接口,根据推送包里的 sender token 锁定真正发送这条消息的设备身份,只有匹配身份校验通过之后,才继续解析消息负载,防止伪造数据包攻击。

消息信封采用 pair‑ec 也就是 NGM 加密,报文包含密文、临时 P‑256 公钥、ECDSA 签名、六字节 validator 校验片段。解密流程先做 ECDH 密钥协商,校验 validator 字段,再校验发送方设备 ECDSA 签名,使用 HKDF‑SHA256,盐固定为LastPawn‑MessageKeys,衍生出 AES‑CTR 密钥与 IV,解密拿到内层明文。
解密代码:

这里有一个研究员踩坑很久的认知误区,完成共享关系识别之后,密钥并不会自动下发到新注册的 Linux 客户端。我们更换 iPhone 的时候,不需要好友重新开关位置共享,苹果本身就有一套向账号内新增设备分发已有共享密钥的机制,研究员需要模拟这套原生逻辑。
原生系统的调用顺序是 initClient,接着两次 refreshClient,最后发起 SubscribeAndFetch 请求。
关键参数设置 intent 为 distributeKeys,mode 设置 proactive,ids 数组留空,把目标 fmId 填入 fetch 数组,同时完整带上包含 apsToken、clientId 的 SecureLocationsClientContext 上下文对象。参数格式不能错,如果 fetch 写成对象而不是数组、token 用错、传入枚举数字而非字符串,服务器只会返回空 200,不会触发密钥分发。
当这条请求发出,苹果服务会通知正在在线的好友设备,异步分发位置共享密钥,密钥通过刚才的 IDS/APNs 推送链路下发到 Linux 客户端。收到消息之后,解析 plist 得到 entityIdentifier 也就是 fmId、hashedAdvertisement.key.data 位置广播标识,以及一份 85 字节私钥数据包。

这里又是一处关键点,NGM 消息层使用 P‑256 椭圆曲线,但是 Find My People 位置报告加密采用的是 P‑224 曲线,这份 85 字节数据,由 57 字节未压缩公钥点拼接 28 字节私钥标量组成,代码需要校验公私钥匹配关系之后才保存这份密钥,全程不会修改原有位置共享关系,不需要好友关闭再重新开启共享,唯一前提是好友某一台登录账号的设备当时处于在线状态,可以响应密钥分发指令。
拿到 P‑224 私钥和 advertised 位置标识,就可以向 SearchParty 服务发起位置查询,SearchParty 就是苹果存储加密位置报告的后端服务,请求带上 searchPartyToken、anisette 头部,intent 改为 startLocationUpdates,mode 改为 shallow,ids 填入刚刚拿到的位置广播标识。服务器返回的数据结构中 id 字段是明文的 advertised 标识,方便本地匹配对应的解密密钥,location 字段是 base64 编码 ECIES 密文,locationTs 携带时间戳。

解密位置报告时,密文开头 57 字节是服务端生成临时 P‑224 公钥,本地用共享私钥完成 ECDH 协商,执行 X9.63 SHA‑256 密钥派生,以临时公钥作为共享信息,拆分得到 16 字节 AES 密钥与 16 字节 GCM IV,用 AES‑GCM 解密密文剩余部分,最终拿到明文 JSON 或者 plist,里面包含经纬度坐标、定位精度、基于苹果 2001 纪元的时间戳,至此整个链路全部打通,Linux 机器拿到了实时的共享位置。

整套系统是两层加密架构,NGM(pair‑ec)负责设备之间安全传输 P‑224 位置私钥,苹果服务器看不到这一层明文;SearchParty 保存的坐标报告由这份 P‑224 密钥加密,苹果只能拿到密文,没有解密能力,设计上允许单独撤销某一组位置共享,而不用双方设备大规模轮换全部密钥。
这项逆向研究最大价值,就是完整还原 Find My People 整套端到端加密协议的实现细节,让外界看清苹果这套私有加密定位系统完整的运行链路,也提醒每一个普通人,位置共享是极高敏感的权限,授权前需要谨慎评估。
参考来源:zerotistic《Reverse‑engineering Find My People to stalk ~my ex~ a friend, cause I can》
More👇
