裸机编码:将iPhone用作蓝牙面包板

(为我省去学术界,并向 我展示代码 !)

简介:该理论似乎很容易

作为序言,我应该注意,此过程依赖于其他研究人员的先前和独立工作,这些研究人员能够免费提供S5L8900应用处理器来运行任意的,未装箱的代码。 没有对系统的这种访问级别,根本不可能实现蓝牙解放。 此外,必须有可能针对ARM体系结构的C编译器,但是在那时不那么容易获得,而且绝对不在当今的开发阶段。

在完成对设备的读/写访问的艰巨工作之后,实现合理的蓝牙实现应该是小菜一碟。 好吧,应该已经。 然而; 没有AppleHostsOnly =没有设置,即使存在,也缺少大量的蓝牙堆栈。 因此理论很简单:我将重写与少量开源堆栈(例如lwBT)接口的Apple Bluetooth驱动程序代码。

很快就很明显,需要对从物理HCI查询到最终UI显示的整个事件链有一个完整的了解。

问:当您需要学习有关系统的所有知识时,您会去哪里?

答:硬件。

无论如何,进行逆向工程时最好的方法通常是实际查看正在使用的硬件。 我还没有足够的游戏能力用自己的全新iPhone 3G做到这一点,所以我把它留给了iFixit的硬件专家来解决。 他们很容易在创作共用下许可自己的工作!

因此,在逻辑板的左上方,有一个蓝牙芯片组(银色的IC),结果证明它是Cambridge Silicon Radio(CSR)BlueCore 6芯片,其数据表可以在这里找到。 从数据表中可以明显看出,BC6可以通过多种方式连接到应用处理器。 通常情况下,这些东西都是通过SPI(闪存盘)或UART(单元调制解调器)进行接线的,因此人们有很大的信心相信这种趋势会持续下去。

在软件和硬件之间的接口上,事情变得很有趣。 在/dev/uart.bluetooth处存在一个设备文件,该文件证实了怀疑该芯片是以UART空调制解调器的方式连接的。 而且,由于用户可以访问,它预示了早期iOS驱动程序的非常奇怪的体系结构。

将几个字节扔进/dev/uart.bluetooth的深渊后,奇迹般地,返回了HCI响应数据包。 快乐的时光!

软件界面

令人难以置信的是,似乎iOS在驱动用户空间中的蓝牙部分,而不是在内核模块或操作系统的其他级别上。 通过使用似乎由iOS调用以初始化各种(不同的)蓝牙芯片的两个二进制包可以看出这一点。 他们甚至拥有自己的命令行界面。

进一步的测试和对访问设备文件的二进制文件(BlueTool和csr)的分析(包括有用的带有注释的“启动”脚本),还通过直接发送和接收HCI命令,确认整个芯片在用户空间级别上得到控制。

掌握了有关HCI消息传递整个过程的更多信息,并且在这一点上,源代码可以发送任意HCI命令,逻辑上的下一步是派生源代码以进行HCI查询并观察芯片的响应(如果有)。 。

HCI查询结果:找到0个设备。

什么? 怎么会这样 答案将构成整个应用程序解决方案的基础,但是在一些阴谋论和许多深夜来临之前,还没有出现。 因此,让我们看一下拼图的各个部分。

蓝牙2.0规范概述了远程设备发现的步骤,方法是发出1)查询命令2)侦听查询结果事件(或类似消息),然后发出3)侦听查询完成事件。 但是,发出以下命令之后:

  TX:查询命令+ --------------------------------------------- ---------------- + |  0x01 |  0x01 0x04 0x05 |  0x33 0x8b 0x9e |  0x08 |  0x00 |  |  CMD |  HCI查询|  GIAC | 时间| 极限值|  |  HCI CMD标头|  HCI CMD有效载荷|  + ------------------------------------------------- ------------ + 

然后是8秒钟充满希望的痛苦……

  RX:查询完成事件+ -------------------------------------------- ----------- + |  0x04 |  0x01 0x01 |  0x00 |  | 大事记 填写完整| 查询已成功完成|  |  HCI事件标头|  HCI事件有效载荷|  + ------------------------------------------------- ------ + 

尽管位于多个远程主机的范围内,已打开电源并且能够被发现(并且当时不尝试发现其他主机),但查询结果事件数据包永远不会到达。 在这一点上,通过HCI访问该设备,人们认真考虑到Apple已在LMP级别上修改了板载固件(请记住我确实承诺了阴谋论)。 但这被排除在以前无法检测到(通过标准UI)“ Apple认可”的设备出现时。

当时有种种猜测,即BC6可能会根据某种未记录的(加密的甚至是密码的)质询-响应身份验证,取决于连接到“谁”的人来返回空结果集。 但是随后,该项目的受好评的启动脚本方面又出现了—该功能是急事。 如果我对此进行编码,则我个人至少会在HCI身份验证例程之前放置一个自包含的二进制包。 顺便说一下,这需要刷新固件……大声笑

实施中显然缺少了一些东西。 为了找到它,分析返回到iOS驱动程序代码,正式研究并记录了BC6引导过程。

早期iOS驱动程序的怪异体系结构(不包括阴谋论)

在启动iPhone和进行HCI查询之间,执行了以下一系列操作:

  1. 在应用处理器启动时,将挂载设备并称为/dev/uart.bluetooth
  2. 当在UI上切换蓝牙电源时,启动了BlueTool,它将与BC6通信以便处理其启动和停止。 BlueTool更像是由Apple(私有)框架(如CoreBluetooth)启动的通用外壳。 在不同的Apple设备和不同的蓝牙芯片上都是相同的,但是加载了脚本并安排用于执行csr或bcm的特定于芯片的二进制文件。
  3. 在iPhone 3G上,BlueTool根据启动脚本启动了带有各种参数的csr,以启动BC6。 在引导过程中发送了很多特定于供应商的命令,这看起来越来越像解决方案。
  4. 引导芯片后,将热复位发送到该行,然后二进制文件(关键地)退出。
  5. 最终,随着芯片的启动和初始化,可以相信CoreBluetooth接管了HCI命令并将其直接发送给它。 以前的初始化二进制文件均不涉及。

至此,显而易见的秘密在于引导脚本(特定于供应商)命令中。 这是因为尽管可以发送任意的HCI命令,但是逐字执行Apple引导脚本之后再也不能发送它们。

经历的行为是两种类型之一; 要么:

  1. 完全不使用启动脚本,解决方案代码将打入/ dev / btreset文件并建立与设备的连接,从而允许发送和接收HCI数据包(但HCI查询失败),或者
  2. 使用经过略微修改的启动脚本来启动芯片,退出BlueTool,然后(热重置,建立新连接等)发送同样得到确认的HCI数据包(但使用HCI查询也会失败)

使用逐字引导脚本时; 不能发送或接收HCI数据包(已发送HCI命令,但从未收到确认)。 逻辑上下一步是查看修改后的启动脚本和标准启动脚本之间的区别。 也许根本拒绝通信的命令也可以解释HCI查询模式的问题……以某种方式……?

EtVoilà!

在那里! 标准启动脚本的关键初始化过程是明确设置波特率。 否则,芯片将在热复位后期望高波特率信号,但是当幼稚的代码尝试以原始的115200速度发送HCI命令时,它会崩溃,并且似乎没有收到响应。

事实证明,在重置设备(打开/ dev / btreset之后)时,BC6以默认的UART波特率115200引导。未经修改的启动脚本发出了供应商特定的命令csr -B来将波特率更改为更高的值。 (大约4MBaud),但是直到热复位后才发生。 因此,尽管二进制工具似乎可以设置波特率或##配置UART的速度,但仍以原始的115200波特率进行通信,但人们认为该命令不是必需的-HCI查询将不依赖于速度,而是实现速度更改(在未知时间)将不必要地使概念证明复杂化。

这样就可以了-确定速度更改必要的(特别是因为这是修改后的脚本和标准脚本之间的差异),然后实施它,允许标准引导脚本引导BC6,然后有效地拥有外部代码发送和接收HCI数据包。 进展!

当许多不同的蓝牙远程主机在场时发出HCI查询命令时,关键时刻到来了……

  RX:查询结果事件+ -------------------------------------------- -------------------------------------------------- ----- + |  0x04 |  0x02 0x0f |  0x01      |  | 大事记 查询结果|  |  |  HCI事件标头|  HCI事件有效载荷|  + ------------------------------------------------- -------------------------------------------------- + RX:查询结果事件+ ------------------------------------------- -------------------------------------------------- ------ + |  0x04 |  0x02 0x0f |  0x01      |  | 大事记 查询结果|  |  |  HCI事件标头|  HCI事件有效载荷|  + ------------------------------------------------- -------------------------------------------------- + RX:查询结果事件+ ------------------------------------------- -------------------------------------------------- ------ + |  0x04 |  0x02 0x0f |  0x01      |  | 大事记 查询结果|  |  |  HCI事件标头|  HCI事件有效载荷|  + ------------------------------------------------- -------------------------------------------------- + ... RX:查询完成事件+ ---------------------------------------- --------------- + |  0x04 |  0x01 0x01 |  0x00 |  | 大事记 填写完整| 查询已成功完成|  |  HCI事件标头|  HCI事件有效载荷|  + ------------------------------------------------- ------ + 

但是实际输出是:

 事情:0x04事情:0x02 

这比带注释的数据包魅力十足。 但这没关系。 十六进制二是表示发生查询结果事件所需的全部操作,并且已检测到远程主机! 随后兴高采烈。

漫长的旅程终于完成了。 那时是应用程序编写时间!

但请稍等…

精明的(非电气工程师软件设计师)读者可能会问,如果在热启动之前未设置显式速度,到底发生了什么? UART端口的速度肯定与远程蓝牙通信无关,这是因为BC6无法确定合适的波特率。 但这不可能发生,因为能够在热复位之前和之后发送HCI命令(必要时均为115200波特)证明BC6知道使用哪种速度,并且可以在热复位之前和之后发送和接收HCI命令。 115200波特 ,从而消除了时钟速度/ UART速度/“蓝牙速度”的未知或隐含性质。

但是,这正是软件设计人员会想到的。 我当然做到了 不幸的是,波特率协商的神奇过程是一项艰巨且手动的任务,我们可爱的电气工程师会为我们处理:在冷(/ dev / btreset)重置后,BC6对波特率一无所知,因此应用处理器正在以不合宜的速度发送它,它必须:1)像参加大型聚会一样醒来; 2)了解周围的环境,足以理解有人对它大喊大叫; 3)用一种语言来表达回应其他人可以理解; 本质上是“不,我不知道我的裤子在哪里”。

执行此操作的电路称为PLL或锁相环。 冷重置后,设备处于引导和初始化之间的不确定状态。 它通常与更大的系统时钟(我认为是应用程序进程/ iPhone系统时钟)同步运行,但是在冷重置后,它不知道外部时钟频率,因此以内部时钟运行(慢得多)。 在这种不稳定状态下,要告知时钟速度,它必须以应用处理器给定的速度接收数据包,并且它使用PLL来执行此操作, 副作用是,以这种方式获取时钟时,它将不会初始化无线电电路

标准的引导脚本通过向PSRAM承诺显式的时钟速度来解决此问题,该速度通过热启动而保持不变-正是标准引导脚本结束时的重新引导类型! 啊哈! BC6预热重启,初始化无线电,然后将其打包在一个非常整洁的小包装中。

结束:开始编写应用

完成概念验证代码并进行一些整理之后,将一个库编写并编译为lwBT,然后最终链接到一个(有限的)GUI概念验证应用程序,该应用程序可以在GitHub上找到! (目前,只有概念性的探索性证明是成立的)