ATX tidevice 开启 iOS16 Developer Mode 的功能开发过程

codeskyblue · 2023年01月16日 · 最后由 从容。 回复于 2023年05月19日 · 53025 次阅读
本帖已被设为精华帖!

tidevice 简介

tidevice 是一个专注于与 iOS 设备做交互的测试工具
链接 https://github.com/alibaba/taobao-iphone-device

以前没写过怎么做 tidevice 的开发的文章,不过猜测应该会有不少人喜欢看吧,所以趁着这次给 iOS 加新功能的契机,记录一下开发流程供想提 pr 的人参考一下吧。

背景

从 iOS16 开始,iPhone 的开发者模式就需要手工开启了。
根据苹果官网提供的资料,需要手机连接 Xcode,然后才会出现开发者模式的开关。所以可以猜测,Xcode 肯定是发送了什么数据到手机。下面的步骤就是介绍怎么捕获到这些数据。

捕获和分析数据用到的工具
go-ios、vscode、一台苹果电脑
简单介绍一下go-ios,这个是用 go 语言写的一个同 iPhone 进行数据交互的一个工具,跟 tidevice 的功能比较类似,甚至功能还更多一点。它自带的这个抓包功能非常的好用,所以就直接拿来用了。

git clone https://github.com/danielpaulus/go-ios
cd go-ios
# 没有安装go环境,可以去官网go.dev下载开发环境
go build
# Output binary: ./go-ios

抓包之前提前关闭 Xcode

抓包

  1. 使用 root 权限启动抓包程序 sudo ./go-ios dproxy
  2. 启动 Xcode,随便打开一个项目。这里选中手机

  1. 然后手机 设置->隐私与安全性->屏幕滚动到最后,看到开发者模式,就算是可以了。
  2. Ctrl-C 停掉 go-ios 程序。捕获到数据全部都保存到了 dump-<时间戳> 的文件夹下面.
  3. 修改一下刚捕获数据的权限 sudo chown -R $(whoami) dump-* ## 分析数据 使用 vscode 打开文件夹 dump-<时间戳> 会看到里面有很多 connection-开头的文件夹,每一个文件夹里面的数据都代表一次连接包含的数据。 实在太多了,有没有感觉。100 多个文件夹。我在里也没有什么太好的办法了,一个个的看过去吧。 打开其中一个文件的时候发现应该是的,后来证明确实是的(如果不是的话,就继续看,知道找到为止) 熟练一些的话,可以用一些脚本过滤出来可能得请求。 比如可以用下面的代码过滤出来所有 StartService 的代码 $ find . -name "jsondump.json" | xargs cat | grep -v PairRecordData | grep StartService ... 结果省略 ...

经过仔细的人工排查,发现其中一个 connection 的可能性比较高:启动了 Service "com.apple.amfi.lockdown" 这部分的代码

代码启动了 Service "com.apple.amfi.lockdown",设备返回连接端口号 49304
但是直接全文搜索 49304 却搜索不到,这个其实跟 go-ios 存储的结构有关系。需要做一下转化

49304 = 0xc098

#LittleEndian to BigEndian
0x98c0 = 39104

# Unsigned int to signed int16
2**16 - 39104 = 26432

用 Python 也可以很快的算出来2**16 - socket.htons(49304)

搜索 26432 就可以找到对应的文件了。然后看到 jsondump.json 并没有多少内容,定位到文件所在的目录,发现里面还有两个文件

connection-#107-2023.01.16-03.28.19.046
|-- bindump-hostservice-to-proxy.txt
|-- from-device.bin
|-- jsondump.json
`-- to-device.bin

为了查看 bin 文件的内容,Vscode 安装个插件 Hex Editor
打开 to-device.bin 就可以看到下面的内容

这个结构相当简单,大概猜一下就出来了。
前 4 个字节代表整个文件的长度。后面接下来的 body 一看就是 plist 编码的。可以用代码解析一下里面的内容

import plistlib

raw = open("to-device.bin", "rb").read()
payload = plistlib.loads(raw[4:])
print(payload)
# 输出
# {'action': 0}

然后 device 返回了的内容在 from-device.bin 中可以查到

00 00 00 D9

这几个字节也看不出来啥意思,估计就是成功的 code 吧。

代码实现

流程差不多理清楚了,用代码模拟一下

import tidevice
import plistlib
import struct

d = tidevice.Device()
conn = d.start_service("com.apple.amfi.lockdown")
body = plistlib.dumps({"action": 0})
head = struct.pack(">I", len(body))
conn.psock.sendall(head+body)
conn.psock.recv() # read until connection closed

看起来没问题了,代码封装成函数就行了。
本次 tidevice 的具体更新内容如下
https://github.com/alibaba/taobao-iphone-device/compare/0.9.14...0.10.1

参考链接

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 11 条回复 时间 点赞
陈恒捷 将本帖设为了精华贴 01月16日 22:44

赞,学习了~

另外,用这个方法打开选项后,应该还需要手动在 ios 系统上手动打开开发者模式,并重启系统,是吧?

预研过程很详细,学习了

陈恒捷 回复

如果手机没有设置密码,会自动重启系统的。

👍 学习了

codeskyblue 回复

意思是,tidevice 可以在没有设置密码的情况下,可以自动让设备展示开发者模式的选项,并自动打开开关和重启系统让其生效?

陈恒捷 回复

还不能直接生效,重启后还需要点个弹窗

非常受用,可以研究一下了,感谢~

有一种 WPE 抓游戏封包的回忆😂

大佬专业,又学习了

11楼 已删除
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册