导语:本篇文章中,我将尝试揭秘越狱的主要步骤,本文并不包含各种iOS版本中所使用的各种越狱工具的不同的补丁和技术,但读完后,你将大有收获。

对普通用户以及很多技术人员来说,越狱过程一直都是一个神秘过程,对他们来说,所知道的就是在运行越狱程序几秒钟后iOS系统突然脱离苹果的操作环境。而至于越狱程序在此期间发生了什么事,一般人是不知道的。所以在这篇文章中,我将尝试揭秘越狱的主要步骤,本文内容并不包含各种iOS版本中所使用的各种越狱工具的不同的补丁和技术,但读完后,你将大有收获。

为什么要越狱?

该过程的命名可能来自Apple的“Jailed”方法,在越狱前,应用程序和用户只能使用Apple提供的功能,这其实这只是Apple设备功能中的一小部分。因此为了打破原有设备的功能限制,就必须越狱。

不过在此要强调一下,装了Cydia并不算越狱。它只是iPhone、iPod touch、iPad等设备上的一种破解软件,类似苹果在线软件商店iTunes Store的软件平台的客户端,在越狱的过程中被装入到系统中的,其中多数为iPhone、iPod Touch、ipad的第三方软件和补丁,主要都是弥补系统不足用。

Cydia本质是一个GUI(图形用户界面)应用程序,它在后台使用dpkg和apt等来安装.deb(Debian)软件包。这些软件包遵循非常严格的格式,具体的我将在稍后讨论。但其实,你不需要Cydia就能安装软件包。由于Cydia依赖于apt和dpkg等,因此你可以通过SSH或通过设备上的移动终端应用程序简单的使用这些二进制文件。

2017年的圣诞节,开发者Jonathan Levin为我们带来了一款名为LiberiOS的越狱工具,支持运行iOS11.0~11.1.2系统的64位设备进行越狱,以此获得读取与修改iOS11的最高权限。这可能是迄今为止最稳定的iOS 11越狱工具,主要是针对研究人员和高级用户,而不是普通民众。

越狱程序是如何运行的?

在打开Cydia,Installer 5,Icy Project或SSH之前,必须运行越狱程序。越狱在不同阶段的运行内容取决于iOS版本和设备,过去,它对设备类型的依赖程度较低,但随着iOS 9.0上的KPP(内核补丁保护程序)和iOS 10上的KTRR(据称是内核文本只读区域)的出现,对设备类型的依赖程度越来越高。例如,iPhone 7之前的设备使用KPP,这是在EL3(ARM Exception LEVEL 3)中运行的软件保护,但iPhone 7及其以上版本使用的是基于硬件的KTRR。在这种情况下,包含KPP绕过(如Yalu)的越狱在iPhone 7及其以上版本中是不起作用的,因为KPP本身不在iPhone 7上。对于这些设备,需要进行各种KTRR绕过,因此,越狱工具必须非常了解它所处理的设备类型。

想知道越狱的历史吗?让我以iOS 7.x.x的Pangu为例进行说明

Pangu越狱是中国越狱团队“盘古”开发的一款iOS完美越狱工具,适配机型涵盖全系列运行iOS7.1.1的苹果设备, 采用一键式越狱,具有中英文界面,同时针对中文进行主页面优化,更加贴合中国用户的需求和使用习惯。

在设备运行之前,越狱工具就必须以某种方式将越狱有效载荷部署到设备上。这可能听起来没有什么,因为任何人都可以通过访问免费的Apple Developer帐户来签署IPA文件,然后用Cydia Impactor或类似的工具将其安装到设备上,但它以前并没有这么简单。因为这个自签名配置文件是由从iOS 9.0才有的,而iOS 9.0在越狱历史上所占的地位却很小。

早在出现专门的越狱工具之前,codesign就已经被那些高度熟练的越狱团队以非常有趣的方式绕过了。如果你还用的是iPhone 4,你可能会因为使用的是iOS 7.1 – 7.1.2而被Pangu越狱。不过,现在这种技术已经不适用了。所以我谈论的是iOS 7.1.x,请注意,免费提供配置文件的自签名和部署签名的IPA不是一回事。因为针对 iOS 7.1- 7.1.2的Pangu有自己的Windows和macOS程序,可以为用户自动部署,用的是当时存在的企业证书签署的。

然而,由于企业证书已经不再使用。计算机上的盘古程序会要求用户将设备的日期和时间设置回2014年6月2日。

由于这个虚拟应用程序是由盘古程序部署的,它的主要目的是删除这个签名证书。IPA本身实际上是盘古Windows/macOS二进制本身的一部分。通过使用任何反汇编程序(我使用的是Jtool和IDA)可以很容易的发现这一点,由Jonathan Levin开发的Jtool可以生成HTML输出。

以下是macOS上的Pangu二进制文件的样式,你看到多余的部分了吗?

LC 00: LC_SEGMENT_64             Mem: 0x000000000-0x100000000    __PAGEZERO
  LC 01: LC_SEGMENT_64             Mem: 0x100000000-0x101e71000    __TEXT
    Mem: 0x100002370-0x1000292cf        __TEXT.__text    (Normal)
    Mem: 0x1000292d0-0x10002982e        __TEXT.__stubs    (Symbol Stubs)
    Mem: 0x100029830-0x10002a132        __TEXT.__stub_helper    (Normal)
    Mem: 0x10002a140-0x10002a690        __TEXT.__const    
    Mem: 0x10002a690-0x10002b914        __TEXT.__objc_methname    (C-String Literals)
    Mem: 0x10002b914-0x10002b9d5        __TEXT.__objc_classname    (C-String Literals)
    Mem: 0x10002b9d5-0x10002beb6        __TEXT.__objc_methtype    (C-String Literals)
    Mem: 0x10002bec0-0x10002e8d5        __TEXT.__cstring    (C-String Literals)
    Mem: 0x10002e8d6-0x10002e92e        __TEXT.__ustring    
    Mem: 0x10002e92e-0x10003dc04        __TEXT.__objc_cons1    
    Mem: 0x10003dc04-0x10029ed87        __TEXT.__objc_cons2    ; Yeee, see this!
    Mem: 0x10029ed87-0x1002b71a9        __TEXT.__objc_cons3    
    Mem: 0x1002b71a9-0x100f11a36        __TEXT.__objc_cons4    
    Mem: 0x100f11a36-0x10160e0ca        __TEXT.__objc_cons5    
    Mem: 0x10160e0ca-0x101dd6e3f        __TEXT.__objc_cons6    
    Mem: 0x101dd6e3f-0x101dd7152        __TEXT.__objc_cons7    
    Mem: 0x101dd7152-0x101dd7a17        __TEXT.__objc_cons8    
    Mem: 0x101dd7a17-0x101e45a6e        __TEXT.__objc_cons9    
    Mem: 0x101e45a6e-0x101e57e74        __TEXT.__objc_cons10    
    Mem: 0x101e57e74-0x101e69288        __TEXT.__objc_cons11    
    Mem: 0x101e69288-0x101e699e0        __TEXT.__unwind_info    
    Mem: 0x101e699e0-0x101e71000        __TEXT.__eh_frame    
  LC 02: LC_SEGMENT_64            Mem: 0x101e71000-0x101e75000    __DATA
    Mem: 0x101e71000-0x101e71028        __DATA.__program_vars    
    Mem: 0x101e71028-0x101e710b8        __DATA.__got    (Non-Lazy Symbol Ptrs)
    Mem: 0x101e710b8-0x101e710c8        __DATA.__nl_symbol_ptr    (Non-Lazy Symbol Ptrs)
    Mem: 0x101e710c8-0x101e717f0        __DATA.__la_symbol_ptr    (Lazy Symbol Ptrs)
    Mem: 0x101e717f0-0x101e717f8        __DATA.__mod_init_func    (Module Init Function Ptrs)
    Mem: 0x101e717f8-0x101e71800        __DATA.__mod_term_func    (Module Termination Function Ptrs)
    Mem: 0x101e71800-0x101e71b40        __DATA.__const    
    Mem: 0x101e71b40-0x101e71b60        __DATA.__objc_classlist    (Normal)
    Mem: 0x101e71b60-0x101e71b68        __DATA.__objc_nlclslist    (Normal)
    Mem: 0x101e71b68-0x101e71b78        __DATA.__objc_catlist    (Normal)
    Mem: 0x101e71b78-0x101e71ba0        __DATA.__objc_protolist    
    Mem: 0x101e71ba0-0x101e71ba8        __DATA.__objc_imageinfo    
    Mem: 0x101e71ba8-0x101e72f90        __DATA.__objc_const    
    Mem: 0x101e72f90-0x101e73590        __DATA.__objc_selrefs    (Literal Pointers)
    Mem: 0x101e73590-0x101e735a0        __DATA.__objc_protorefs    
    Mem: 0x101e735a0-0x101e736f8        __DATA.__objc_classrefs    (Normal)
    Mem: 0x101e736f8-0x101e73718        __DATA.__objc_superrefs    (Normal)
    Mem: 0x101e73718-0x101e738a8        __DATA.__objc_data    
    Mem: 0x101e738a8-0x101e73930        __DATA.__objc_ivar    
    Mem: 0x101e73930-0x101e74390        __DATA.__cfstring    
    Mem: 0x101e74390-0x101e746b8        __DATA.__data    
    Mem: 0x101e746c0-0x101e74b60        __DATA.__bss    (Zero Fill)
    Mem: 0x101e74b60-0x101e74b90        __DATA.__common    (Zero Fill)LC 03: LC_SEGMENT_64          Mem: 0x101e75000-0x101eba000    __ui0
LC 04: LC_SEGMENT_64          Mem: 0x101eba000-0x101ebf000    __LINKEDIT
LC 05: LC_DYLD_INFO          
LC 06: LC_SYMTAB             
    Symbol table is at offset 0x1ebbc48 (32226376), 293 entries
    String table is at offset 0x1ebd610 (32232976), 4776 bytes
....

你看到__TEXT .__ objc_cons2部分了吗?

如果你执行0x10029ed87 – 0x10003dc04 = 2494851字节(十进制)=> 2.494851兆字节,那工作量将会很大,因为它是嵌入式IPA文件。objc_cons1,objc_cons2和objc_cons3都是越狱载荷(untether,plists,libraries等)的嵌入部分。

Jtool是一个非常强大的工具,它能够从二进制文件中提取整个部分。该命令是jtool -e(extract)/ path。如果我们对Pangu二进制文件执行此操作,那将获得一个名为“pangu .__ TEXT .__ objc_cons2”的新文件,这恰好被文件(1)识别为“来自Unix的“gzip压缩数据”,因此tar tvf应该能够很好的列出内容。

Saigon:~ geosn0w$ /Users/geosn0w/Desktop/ToolChain/jtool/jtool -e __TEXT.__objc_cons2 /Users/geosn0w/Desktop/pangu.app/Contents/MacOS/pangu 
Requested section found at Offset 252932
Extracting __TEXT.__objc_cons2 at 252932, 2494851 (261183) bytes into pangu.__TEXT.__objc_cons2
Saigon:~ geosn0w$ file /Users/geosn0w/pangu.__TEXT.__objc_cons2
/Users/geosn0w/pangu.__TEXT.__objc_cons2: gzip compressed data, from Unix
Saigon:~ geosn0w$ tar tvf /Users/geosn0w/pangu.__TEXT.__objc_cons2 
drwxrwxrwx  0 0      0           0 Jun 27  2014 Payload/
drwxrwxrwx  0 0      0           0 Jun 27  2014 Payload/ipa1.app/
drwxrwxrwx  0 0      0           0 Jun 27  2014 Payload/ipa1.app/_CodeSignature/-rwxrwxrwx  0 0      0        3638 Jun 27  2014 Payload/ipa1.app/_CodeSignature/CodeResources-rwxrwxrwx  0 0      0       15112 Jun 27  2014 Payload/ipa1.app/[email protected]  0 0      0       20753 Jun 27  2014 Payload/ipa1.app/[email protected]~ipad.png-rwxrwxrwx  0 0      0        8017 Jun 27  2014 Payload/ipa1.app/AppIcon76x76~ipad.png-rwxrwxrwx  0 0      0       75320 Jun 27  2014 Payload/ipa1.app/Assets.car-rwxrwxrwx  0 0      0        7399 Jun 27  2014 Payload/ipa1.app/embedded.mobileprovision
drwxrwxrwx  0 0      0           0 Jun 27  2014 Payload/ipa1.app/en.lproj/-rwxrwxrwx  0 0      0          74 Jun 27  2014 Payload/ipa1.app/en.lproj/InfoPlist.strings-rwxrwxrwx  0 0      0        1955 Jun 27  2014 Payload/ipa1.app/Info.plist-rwxrwxrwx  0 0      0      312208 Jun 27  2014 Payload/ipa1.app/ipa1-rwxrwxrwx  0 0      0         968 Jun 27  2014 Payload/ipa1.app/ipa1-Info.plist-rwxrwxrwx  0 0      0      235794 Jun 27  2014 Payload/ipa1.app/[email protected]  0 0      0      785321 Jun 27  2014 Payload/ipa1.app/[email protected]~ipad.png-rwxrwxrwx  0 0      0      261481 Jun 27  2014 Payload/ipa1.app/LaunchImage-700-Landscape~ipad.png-rwxrwxrwx  0 0      0      660541 Jun 27  2014 Payload/ipa1.app/[email protected]~ipad.png-rwxrwxrwx  0 0      0      244644 Jun 27  2014 Payload/ipa1.app/LaunchImage-700-Portrait~ipad.png-rwxrwxrwx  0 0      0      216627 Jun 27  2014 Payload/ipa1.app/[email protected]  0 0      0           8 Jun 27  2014 Payload/ipa1.app/PkgInfo-rwxrwxrwx  0 0      0         150 Jun 27  2014 Payload/ipa1.app/ResourceRules.plist
drwxrwxrwx  0 0      0           0 Jun 27  2014 Payload/ipa1.app/zh-Hans.lproj/-rwxrwxrwx  0 0      0          73 Jun 27  2014 Payload/ipa1.app/zh-Hans.lproj/InfoPlist.strings
Saigon:~ geosn0w$

执行tar xvf会将内容提取到“Payload”文件夹。因此,在手机上部署的IPA文件实际上是ipa1。如上所示,有一个名为embedded.mobileprovision的文件,其中包含企业证书。如果我们右键单击它并选择“获取信息”,Finder可以向我们显示有关嵌入式证书的一些信息。如下所示,它属于“Hefei Bo Fang”。

3.png

正如你所看到的,像许多其他越狱方式一样,Pangu也依赖于开发人员证书来绕过CodeSign,但是将IPA部署到设备并不像你想象的那么容易。如今,我们可以迅速启动Cydia Impactor,拖入IPA,然后登录就可以了。但在iOS 9.0之前,情况并非如此。

在iTunes 12.x之前,iTunes可以轻松地与苹果设备通信,iTunes也能够处理iOS应用程序。不过,现在iTunes的这些功能都没有,这意味着,一个或多个框架(或Windows民用的DLL)必须能够创建与设备的连接并执行与应用程序相关的任务。当然,这是以谈论AppleMobileDevice(framework/dll)来进行说明的。在iTunes及其驱动程序包中,这个框架以前主要用于越狱,现在仍然被Windows上所有“备份iOS/照片/联系人/其他”程序用于与设备进行可靠的通信。当然,这些API是都是用户越狱后私自安装的,也在libimobiledevice项目中重新被创建。

这样,Pangu就可以与设备通信,并在适当的时候删除有效载荷。接下来,我将在下面讨论一组近乎公式化的规范补丁。

之所以以最新的Pangu为例,是因为与redsn0w等相比,它适用以下大部分下载的补丁,也因为它是我在iPhone 4上经常使用的越狱之一。

我主要以它为例进行说明,以便你可以看到当时绕过CodeSign与现在绕过CodeSign之间的区别。

规范补丁

我创建了下面的越狱流程图表,理论上它应该显示大多数越狱的流程。当然,现实的实现过程还是根据iOS版本的不同而有差异的,并且一些越狱流程可能以完全不同的顺序来执行。

4.png

如上图所示,最重要的一步是让越狱程序加载到设备中。Pangu的这个加载过程可与如今看到的越狱过程不同。如今,大多数越狱程序,包括我开发的Osiris,Coolstar&Co开发的Electra,Jonathan开发的LiberiOS,都会通过临时证书签署的IPA应用程序,并使用Xcode或Cydia Impactor(或签名服务)部署到设备上。之后,用户才能对越狱程序进行操作并触发漏洞,

其中比较常见的是触发WebKit漏洞和邮件漏洞。TotallyNotSpyware就是一个适用于从iOS 10.x到10.3.3 64位越狱的很好的例子,如果我们谈论的是Legacy启动模式,则JailbreakMe系列越狱软件可能就是最好的例子,JailbreakMe是一款用于iPhone、iPad、iPod Touch上的一款最简便的iOS越狱软件,是由著名的开发人员Comex所开发的。这些基于WebKit的越狱通常是通过在设备上访问Safari中的网站来部署的,该网站经过精心设计,可以利用webkit漏洞(WebKit是Safari的内核),从而获得任意代码执行。

在接下来的文章中,我所讲解的越狱前提假设都是基于IPA的越狱,如Osiris,LiberiOS或Electra。此外,这篇文章的讨论还假设我们已经有了一个原始内核漏洞利用程序,它为我们提供了TFP0和KPPless方法。

应用程序成功安装并运行后,至少在初始阶段,绕过CodeSign不再是问题。不过我们仍然无法运行未签名或伪造签名的二进制文件,但至少我们可以自己运行(漏洞利用程序)而不会被移动文件完整性(AMFI)安全功能杀死。然而,问题是我们现在仍然受到沙箱的安全限制。因为沙箱使我们无法访问容器外的任何内容,因此我们没有R/W权限。此时,我们只能看到自己的数据,仅此而已。为此,我们必须让Shai Hulud变得更加强大。

沙箱是一个内核扩展(KEXT),它是确保你不会访问超出你应该访问的内容。默认情况下,/var/mobile/Containers中的所有内容都在沙箱中,因为Apple自己的默认应用程序也是被沙箱化了。当你通过Xcode,App Store或Cydia Impactor安装应用程序时,你将自动将应用程序放在/var/mobile/Containers/Bundle/Application/中。由于没有其他方法可以安装应用程序,所以我们安装的越狱应用程序将默认都在沙箱中进行。

那么,越狱应用程序是如何获得运行所需的服务的呢?比如:如何把Deezer(法国在线音乐网)如何连接我的蓝牙耳机?YouTube如何解码通信的帧?Twitter如何向我发送通知?很简单,通过API即可完成。这些API允许你的容器化应用程序以受控方式与核心服务(bluetoothd,wifid,mediaserverd等)进行通信,这些服务也在沙箱中进行,并且通过IOKit与kexts /内核通信。所以你不能直接与内核通信。下图即为整个流程:

5.png

当然,作为iOS上的应用程序,你不仅无法看到文件系统和用户数据,而且你也很大程度上不了解任何其他应用程序的存在。如果其他应用程序被注册为接受处理此类输入的应用程序,那通过一些API,你可以将文件/数据传递给另一个应用程序,但即使这样,你的应用程序也不知道其他应用程序的存在以及通过iOS提供的一系列API,不过你可以传递.PDF文件,例如,在任何应用程序中打开它。

不过有些应用程序还提供了URI方案,供你与它们进行通信。假设你在iOS上使用Chrome浏览器,并且你可以找到要服务公司的电话号码。如果你按下这个号码,系统会询问你是否真的要联系客服,然后直接利用iOS拨打电话应用程序拨打。知道这个过程是如何实现的吗?

这个过程非常简单,由于电话应用程序已注册了一个如下所示的URI方案:tel:// xxxxxxxxxxxxxxxxxx,如果你将tel://5552220001添加到HTML页面并在Safari中单击它,iOS就知道该打开谁来处理它。同样,这个方案也适用于Facebook,Whatsapp,如果它们要使用应用程序中的URI方案,你只需调用正确的UIApplication方法即可。如下所示:

UIApplication.shared.open(url, options: [:], completionHandler: nil)

由于你能够将数据传递到另一个应用程序并打开它,所以你认为现在你已经绕过沙箱了吗?其实你离这个目标还差很远,你以上所做的只是使用了一组控制良好的API,因为你不知道手机应用程序的存在,但iOS知道。以下是沙箱对应用程序的检测。

7.png

避免沙箱检测的方法可以通过多种方式完成:Osiris越狱使用QiLin的内置沙箱进行逃逸,称为“ShaiHulud”。QiLin以及LiberiOS和Osiris越狱,都是通过伪造内核的凭证逃脱了沙箱。如果你拥有内核的凭证,就可以访问任何我们想要的内容,包括像execve()、fork()和posix_spawn()这样的系统调用,Jonathan Levin在这篇文章中,非常清楚地解释了QiLin如何逃离沙箱。

当然,QiLin保存了应用程序的凭证,并在退出之前恢复它们,这是为了防止由于内核cred被锁定而造成的恐慌。

适用于iOS 11.2.x的Electra – > iOS 11.3.1也会使用相同的内核凭证方法绕过沙箱和其他权限。

重新对文件系统执行mount命令

mount是Linux下的一个命令,它可以将分区挂接到Linux的一个文件夹下,从而将分区和该目录联系起来,因此我们只要访问这个文件夹,就相当于访问该分区了。目前mount已经不仅仅局限于Linux了。在Windows系统下的应用也越来越广了,多用在虚拟光驱类软件上,比如Clone CD,Daemon tool,WinMount等。

所以,我要利用的就是内核,并获得内核内存的R/W权限,为此我利用了其中一个漏洞,才获得了内核凭证,才逃避了沙箱的检测,现在我想要做的就是删除我的有效载荷,这可以包含Cydia、二进制文件、配置文件以及用于检查是否安装了越狱的虚拟文件等。为了能够做到这一点,就需要我获取文件系统的R/W权限。默认情况下,iOS ROOT FS仅以只读方式执行mount命令,因此我们需要重新执行mount命令,为此我需要的修补程序的名称为Root FS Remount,这个组件是在2018年7月iOS 11.3.1的Electra开发时丢失的。

那么,我们该怎么做?

在QiLin以及在LiberiOS和Osiris越狱上,重新在iOS 11.2.6上执行mount命令的方式是这样的:正如我以上所说,默认情况下ROOT FS是以只读方式执行mount命令。不仅如此,沙箱还有一个挂钩,可以防止你重新执行mount命令而获得R/W权限。挂钩是通过mount_begin_update()和mount_common()中的MACF调用强制执行的。所有挂钩都会检查mount命令中是否存在MNT_ROOTFS标志。如果存在,则操作失败。此时,QiLin会关闭MNT_ROOTFS标志。

以下列表是QiLin Toolkit中的remountRootFS,由Jonathan Levin发布。

int remountRootFS (void){
   ...
   uint64_t rootVnodeAddr = findKernelSymbol("_rootvnode");
   uint64_t *actualVnodeAddr;
   struct vnode *rootvnode = 0;
   char *v_mount;
   status("Attempting to remount rootFS...\n");
   readKernelMemory(rootVnodeAddr, sizeof(void *), &actualVnodeAddr);
   readKernelMemory(*actualVnodeAddr, sizeof(struct vnode), &rootvnode);
   readKernelMemory(rootvnode->v_mount, 0x100, &v_mount);
   // Disable MNT_ROOTFS momentarily, remounts , and then flips the flag back   uint32_t mountFlags = (*(uint32_t * )(v_mount + 0x70)) & ~(MNT_ROOTFS | MNT_RDONLY);
   writeKernelMemory(((char *)rootvnode->v_mount) + 0x70 ,sizeof(mountFlags), &mountFlags);
   char *opts = strdup("/dev/disk0s1s1");
   // Not enough to just change the MNT_RDONLY flag - we have to call   // mount(2) again, to refresh the kernel code paths for mounting..   int rc = mount("apfs", "/", MNT_UPDATE, (void *)&opts);
   printf("RC: %d (flags: 0x%x) %s \n", rc, mountFlags, strerror(errno));
   mountFlags |= MNT_ROOTFS;
   writeKernelMemory(((char *)rootvnode->v_mount) + 0x70 ,sizeof(mountFlags), &mountFlags);
   // Quick test:   int fd = open ("/test.txt", O_TRUNC| O_CREAT);
   if (fd < 0) { error ("Failed to remount /"); }
   else {
     status("Mounted / as read write :-)\n");
     unlink("/test.txt"); // clean up   }
 return 0;

Jonathan Levin的代码很简单,逆向MNT_ROOTFS标志,调用mount(2)刷新内核代码路径以执行mount命令、恢复标志并进行测试。此时,你就具有了R/W权限。

我以上所说的,都存在于较旧的越狱补丁LightweightVolumeManager :: _ mapForIO中。

对Electra越狱工具重新执行mount命令

iOS 11.3更进一步,加入了APFS快照。当苹果开始使用快照的时候,APFS已经在iOS中使用了很长一段时间,但是这破坏了我对iOS 11.2.x甚至更早版本的越狱尝试,我必须得重新执行mount命令。要解决这个问题,我就需要找到一个新的漏洞。但问题是iOS会恢复到以只具有只读方式mount命令的快照,这样我以前安装的所有关于微调、二进制等方面的内容都消失了。

此时,只能通过更改整个越狱过程并使用Rootless机制(可以将该机制理解为一个更高等级的系统的内核保护措施,系统默认将会锁定 /system、/sbin、/usr 这三个目录。),或者找到一个方法绕过快照。在此,感谢@ Pwn20wnd和@umanghere已经重新创建的mount命令,Umang发现了pwn20wnd在Electra中利用的一个漏洞。

Pwn20wnd绕过这个快照的方法非常简单,用于iOS 11.3.1的Electra源代码中的函数可能看起来很复杂。那是因为Pwn20wnd花了很多工作来实现这个绕过,但是一旦开始利用这个漏洞,就会出现错误。请注意,该错误非常简单,如果有重启,iOS将在每次重启后恢复为APFS系统快照。此时如果没有捕获机制,iOS就会像在没有快照的iOS 11.2.6上一样,随意的继续启动,而不是以被破坏的方式启动或出错。

至于补丁,可以查找快照并将其重命名为其他内容。至于越狱过程就有点复杂,如果你分析代码,就可以看到所述快照的名称是动态的或至少包含动态部分,因此无法进行硬编码。动态部分恰好是boot-manifest-hash。在做任何事情之前,Pwn20wnd似乎能赋予自己内核凭证。

你可以通过运行以下命令找到它boot-manifest-hash:

ioreg -p IODeviceTree -l | grep boot-manifest-hash

在Electra中,查找boot-manifest-hash的所有逻辑都位于apfs_util.c中,如下所示:

char *copyBootHash(void) {
    unsigned char buf[1024];
    uint32_t length = 1024;
    io_registry_entry_t chosen = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/chosen");
    
    if (!MACH_PORT_VALID(chosen)) {
        printf("Unable to get IODeviceTree:/chosen port\n");
        return NULL;
    }
    
    kern_return_t ret = IORegistryEntryGetProperty(chosen, "boot-manifest-hash", (void*)buf, &length);
    
    IOObjectRelease(chosen);
    
    if (ret != ERR_SUCCESS) {
        printf("Unable to read boot-manifest-hash\n");
        return NULL;
    }
    
    // Make a hex string out of the hash    char manifestHash[length*2+1];
    bzero(manifestHash, sizeof(manifestHash));
    
    int i;
    for (i=0; i<length; i++) {
        sprintf(manifestHash+i*2, "%02X", buf[i]);
    }
    
    printf("Hash: %s\n", manifestHash);
    return strdup(manifestHash);}

这个函数从另一个名为find_system_snapshot的函数内部调用,该函数处理查找快照本身的逻辑。该函数将检索到的manifestHash附加到硬编码部分com.apple.os.update-,以得到当前快照的真实名称,然后它将快照名称返回给调用者。

const char *find_system_snapshot(const char *rootfsmnt) {
    char *bootHash = copyBootHash();
    char *system_snapshot = malloc(sizeof(char *) + (21 + strlen(bootHash)));
    bzero(system_snapshot, sizeof(char *) + (21 + strlen(bootHash)));
    
    if (!bootHash) {
        return NULL;
    }
    sprintf(system_snapshot, "com.apple.os.update-%s", bootHash);
    printf("System snapshot: %s\n", system_snapshot);
    return system_snapshot;}

有了内核的凭证,以及正确的快照名称,Pwn20wnd就将返回rootfs_remount.c,以获取重命名快照。将其重命名为“orig-fs”,然后Pwn20wnd将检查重命名是否成功,此时Pwn20wnd会恢复自己的凭证并删除内核凭证。 最后,Pwn20wnd重新启动设备,这就是为什么第一次在iOS 11.3-11.3.1中使用Electra时,无论你使用的是哪种漏洞,你的设备都会强制重新启动。

重命名了快照后,iOS就没有要执行mount命令的快照了。

源链接

Hacking more

...