PS Jailbreak 原理分析

很久没有写技术文章了,最近PS3破解比较火热,我就分析一下。

首先需要介绍一些破解用到的底层技术:

  • Heap overflow

Heap(堆)是所有编程语言底层内存管理的基础,即便用汇编可以胡乱搞,写大程序也肯定还会用到堆管理器。在cc++里面,当我们用malloc、free、new、delete之类的内存操作函数,我们就和堆管理器打交道了。堆管理器的实现有很多种,算法各有不同,有用红黑树,也有简单的linklist,还有为了提高性能只能分配固定尺寸的内存池。

这些不同的实现大多数有一个典型特征:对齐。由于现代计算机体系结构的缘故,对齐的数据才有较高的访问速度,在某些硬件体系架构上,访问非对齐内存甚至会直接导致machine check。由于对齐,比如我们请求100字节,其实堆管理器会自动选取一个对齐的尺寸,然后分配那么大的一块,比如说128字节,来返回给我们。当然这个描述是不精确的,下面解释一下。

堆管理器需要维护它所管理的每一块内存,也就是说,要维护每块内存的相关信息,比如长度,向前向后指针,状态(未分配,已分配等),那么这个状态怎么维护呢?一种很常见的做法是,放在这个内存块的首部。

假设现在程序员申请100字节,内存块信息本身16字节,那么堆管理器就会找一个128长度的内存块(如果没有这么大的,它会按照一定的算法,比如根据每块内存的信息合并未分配块),把这块内存的首指针+16返回给程序员,于是程序员很高兴,他有了100字节的内存。注意,在古老的系统上其实他可以安全的访问128-16字节。在现代系统上就不行了,堆管理器会在这100字节前后做特殊标记,在硬件页的尺度上也会设定一定的保护(请参考NXDEP),如果你写过了100,当free这块内存的时候,堆管理器会发现标记被破坏,也就是heap overflow了。

如果我们在这100字节的内存上写了256字节的数据呢?一个极大概率的事件是:我们破坏了邻近的内存块的信息。这就为恶意攻击制造了机会。

  • USB

USB是一种不对等总线,也就是有主机和客户机的区别,所有的操作都由Host发出。USB有两个比较重要的概念:地址,端点。

先说地址,Host是没有地址的,只有设备才有。类似的概念是MAC地址,局域网用普通hub大家连在一起,所有的数据包都会经过你的网卡,只有符合你的MAC的数据包网卡才会接受(注意这是最原始的情况,请网络帝指正)。

当一个新的USB设备插入host,比如优盘,由于USB接口上的电平变化,HOST控制器得知有设备插入并且区分出是1.x还是2.0(上拉下拉电阻不同),此时设备(优盘)的USB地址是0,HOST控制器和这个地址通信,并给设备指定一个新的USB地址,范围在1~~127(可以想象是DHCP过程),随后HOST控制器就用新的USB地址来访问设备了,每一个新插入的设备都会这么处理,于是你插上两个一样的优盘,它们也会得到不同的USB地址,于是系统就能区分开两个优盘了。

USB HOST控制器给设备分配了新的USB地址以后,就开始问:你是什么东西啊,你有什么功能阿如此如此,设备会用描述符(descriptor)的方式应答(descriptor格式USB规范里面有):我的VID是xx,我的PID是yy,我的名字叫zz……如此如此。

刚才说到了USB地址,这个数值USB收发器控制电路会保存下来用于今后的通信,但是因为这个东西比较特殊,所以大多数的芯片是不能手工修改自己的USB地址的。

端点是真正执行数据通信的端口,端点0是始终可以用的,被称为控制端点,具体就不细说了。

ok,上面废话这么多,下面开始说主题:PS Jailbreak。

一句话概括:PS Jailbreak通过精心构造的特殊USB描述符,使PS3处理这些描述符的时候Heap overflow,导致代码注入进而获取了GameOS的访问权限。

下面详细介绍PS Jailbreak(以下简称JB)攻击过程

(绝大多数翻译自http://ps3wiki.lan.st/index.php/PSJailbreak_Exploit_Reverse_Engineering,并加上必要的解释说明):

JB设备的外形(注意不是物理外形,是技术上的)是一个“六口 USB Hub”,注意我用了双引号,这东西只是对外宣称自己是Hub,实际上只是为了满足USB协议的需求,并没有完整地实现USB Hub的全部功能。

PS3开机的时候,在特定的情况下会在USB接口上搜索官方的JIG设备(我不知道这玩意的具体功能,操作方法是按POWER后200ms内按Eject),JB利用这个特性在开机检测JIG的时候在其虚拟的六个USB Port上轮番插拔6个设备(…………),由于系统需要为每个设备的处理过程分配内存,通过精心构造的USB描述符,实现了Heap overflow。

Port1:Hub初始化以后,第一个设备插入,pid/vid 0xAAAA/0x5555,有4个配置,每一个长度都是0xf00,由于这个长度没有超过4K的页面,所以推测PS3系统的malloc会为每一个配置分配一个4k的内存页。为什么要4个呢,因为可能已经有空闲内存了,用4个是保证有足够大的概率把页面对齐到4k边界上。然后JB重新报告其配置为18字节。其实在这个比较长的配置里面包含有payload(也就是用于注入攻击的功能代码)。

Port2:PS3读取完成1号设备的描述符以后,JB切换回Hub USB地址,然后谎称第二个设备插入,pid/vid 0xAAAA/0xBBBB,这个设备有一个22字节的描述符,只有前18个字节是有意义的,最后4个意义不明。

Port3:随后这个设备插入,pid/vid 0xAAAA/0x5555,和第一个一样但是描述符不一样,他有两个配置描述符,每一个长度为0xa4d,大部分的数据被认为是垃圾。按照对堆管理器的猜测,这些描述符会被放在一个新的4k页面上,紧随之前的两个设备。

Port2:拔出。这个设备的拔出导致一个显而易见的结果,第一个设备和第三个设备之间分配的内存被释放了。

OK,上面这样的折腾,准备好了真正的攻击环境上下文。

Port4:连接。pid/vid 0xAAAA/0x5555,有三个配置描述符。

配置描述符A,18字节的正常描述符。

配置描述符B,和A一样的描述符,但是当PS3初次读取它之后,它把自己的长度变成了0字节。这是破解的关键之处,但是其具体含义含混不清,它导致了配置描述符C后面的数据覆盖了某一个malloc的边界标志,很可能是属于Port3的。但是这个溢出的详细原因恐怕得看攻击代码本身了。

配置描述符C,这个描述符开始和A是一样的,但是最后多了14个字节。

.. .. 3e 21 00 00 00 00
fa ce b0 03 aa bb cc dd
80 00 00 00 00 46 50 00
80 00 00 00 00 3d ee 70

前六个字节被认为是占位(但是我不这么认为,by hyperiris),接下来是一个magic number,fa ce b0 03 aa bb cc dd,用英语来看就是FACEBOOK AABBCCDD,随后的数据是一个指针,它覆盖了malloc块的边界标记,这会导致malloc在之后处理这个块的时候发生错误,使其按照攻击者的意愿在指定的位置操作内存。(这是两个64位的指针,by hyperiris)

Port5:当Port4完成工作以后,假的JIG被插入到了Port5,它和SONY官方的JIG PID/VID 0x054C/0x02EB æ˜¯ä¸€æ ·çš„ï¼ŒæŽ¨æµ‹å’Œå®˜æ–¹çš„é…ç½ ®å’Œç«¯ç‚¹ä¸€è‡´ã€‚

可以猜测由于这个玩意(JIG)是PS3已知的设备,PS3系统不会为它在堆上分配内存。

随后PS3发送64字节的数据要求JIG进行认证,然后JB返回64字节的应答。PS3将会分配内存来保存这个应答(!!!!),由于之前malloc块的边界标记已经被Port4的插入所修改,所以这次内存分配将会在一个设计好的位置,也就是某一个函数的前面,(某函数24字节偏移之前),然后函数的前面被这64字节覆盖了(!!!!)

由于系统的JIG认证代码没有被patch,所以JB返回的数据被验证无效。

Port3:拔出。JB现在通知PS3,Port3拔出,这导致PS3释放为Port3设备配置描述符分配的内存,也就是被Port4设备描述符覆盖的那个。

于是Shell code此刻被调用,R3寄存器现在指向的是Port3配置描述符的内存边界标记位置。

Shellcode:

ROM:00000018                 ld      %r4, -0x10(%r3)
ROM:0000001C                 ld      %r3, -8(%r3)
ROM:00000020
ROM:00000020 loc_20:                               # CODE XREF: sub_18+14�j
ROM:00000020                 ld      %r5, 0x18(%r3)
ROM:00000024                 addi    %r3, %r3, 0x1000
ROM:00000028                 cmpw    %r4, %r5
ROM:0000002C                 bne     loc_20
ROM:00000030                 addi    %r6, %r3, -0xFE0
ROM:00000034                 mtctr   %r6
ROM:00000038                 bctr

R4保存的就是0xfaceb003aabbccdd,然后R3加载0x8000000000465000,然后shellcode从0x8000000000465000开始搜索每一个4k边界,直到在某一个位置发现0xFACEB003AABBCCDD,发现之后,shellcode跳转到那里,从偏移0x20处开始执行。

清理:现在一切都清静了,Port5,4,1都将被拔出。Payload应该在Port1拔出之前将自己复制到一个不会被释放的内存块里。

Port6:这个设备没有任何的实际意义/功能,vid/pid 0xAAAA/0xDEC0,只响应一个控制传输0xAA,当PS3给这个设备发送这个控制传输,JB就知道自己成功了,并点亮LED。

在原始的JB里面,payload会检测这个设备是不是被拔掉,如果拔掉了,就调用LV1_Panic宕机。PSGroove把这个傻逼功能去掉了。

至于payload代码,和PS3版本有关,具体资料没有,因为需要ps3 main memory dump。

原文地址:https://www.cnblogs.com/skogkatt/p/4163797.html