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上。對於這些裝置,需要進行各種 ofollow,noindex">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_64Mem: 0x000000000-0x100000000__PAGEZERO LC 01: LC_SEGMENT_64Mem: 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_64Mem: 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_64Mem: 0x101e75000-0x101eba000__ui0 LC 04: LC_SEGMENT_64Mem: 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 drwxrwxrwx0 000 Jun 272014 Payload/ drwxrwxrwx0 000 Jun 272014 Payload/ipa1.app/ drwxrwxrwx0 000 Jun 272014 Payload/ipa1.app/_CodeSignature/-rwxrwxrwx0 003638 Jun 272014 Payload/ipa1.app/_CodeSignature/CodeResources-rwxrwxrwx0 0015112 Jun 272014 Payload/ipa1.app/[email protected] 0020753 Jun 272014 Payload/ipa1.app/AppIcon76x76@2x~ipad.png-rwxrwxrwx0 008017 Jun 272014 Payload/ipa1.app/AppIcon76x76~ipad.png-rwxrwxrwx0 0075320 Jun 272014 Payload/ipa1.app/Assets.car-rwxrwxrwx0 007399 Jun 272014 Payload/ipa1.app/embedded.mobileprovision drwxrwxrwx0 000 Jun 272014 Payload/ipa1.app/en.lproj/-rwxrwxrwx0 0074 Jun 272014 Payload/ipa1.app/en.lproj/InfoPlist.strings-rwxrwxrwx0 001955 Jun 272014 Payload/ipa1.app/Info.plist-rwxrwxrwx0 00312208 Jun 272014 Payload/ipa1.app/ipa1-rwxrwxrwx0 00968 Jun 272014 Payload/ipa1.app/ipa1-Info.plist-rwxrwxrwx0 00235794 Jun 272014 Payload/ipa1.app/[email protected] 00785321 Jun 272014 Payload/ipa1.app/LaunchImage-700-Landscape@2x~ipad.png-rwxrwxrwx0 00261481 Jun 272014 Payload/ipa1.app/LaunchImage-700-Landscape~ipad.png-rwxrwxrwx0 00660541 Jun 272014 Payload/ipa1.app/LaunchImage-700-Portrait@2x~ipad.png-rwxrwxrwx0 00244644 Jun 272014 Payload/ipa1.app/LaunchImage-700-Portrait~ipad.png-rwxrwxrwx0 00216627 Jun 272014 Payload/ipa1.app/[email protected] 008 Jun 272014 Payload/ipa1.app/PkgInfo-rwxrwxrwx0 00150 Jun 272014 Payload/ipa1.app/ResourceRules.plist drwxrwxrwx0 000 Jun 272014 Payload/ipa1.app/zh-Hans.lproj/-rwxrwxrwx0 0073 Jun 272014 Payload/ipa1.app/zh-Hans.lproj/InfoPlist.strings Saigon:~ geosn0w$
執行tar xvf會將內容提取到“Payload”資料夾。因此,在手機上部署的IPA檔案實際上是ipa1。如上所示,有一個名為embedded.mobileprovision的檔案,其中包含企業證書。如果我們右鍵單擊它並選擇“獲取資訊”,Finder可以向我們顯示有關嵌入式證書的一些資訊。如下所示,它屬於“Hefei Bo Fang”。
正如你所看到的,像許多其他越獄方式一樣,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版本的不同而有差異的,並且一些越獄流程可能以完全不同的順序來執行。
如上圖所示,最重要的一步是讓越獄程式載入到裝置中。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 /核心通訊。所以你不能直接與核心通訊。下圖即為整個流程:
當然,作為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知道。以下是沙箱對應用程式的檢測。
避免沙箱檢測的方法可以通過多種方式完成: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 backuint32_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 hashchar 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命令的快照了。