作者:0xcc
原文鏈接:https://mp.weixin.qq.com/s/PGC7LKu-oC5ZaRxLFrhTsg
請注意本文與 kernelcache 沒有任何關系。
只要逆向分析過 iOS 用戶態程序,對 dyld_shared_cache [1] (下文簡稱 dsc)都不會陌生。這個機制將所有系統內置的動態鏈接庫都綁定起來,變成一坨巨大的二進制文件,無疑給反編譯工作帶來了額外的工作量和難以磨滅的心理陰影。而近期轉正的 macOS Big Sur 也用上了 dsc,本來歲月靜好的桌面平臺研究也突然變得麻煩起來。
分析 DSC 通常的做法是使用 dsc_extractor 提取出單獨的庫文件,然后像對待普通 MachO 二進制一樣交給各種反編譯器。這個方法簡單快捷,就是會丟符號。
因為 dsc 在生成的時候會舍棄一些動態綁定功能,例如相當一部分原本使用 got 調用的函數,操作數被直接替換成真正的目標函數地址。即使仍然使用 __stubs 做動態綁定,也會出現鏈接到另一個庫的情況。

如果簡單地分割成單獨的文件,這些函數所在的 segment 屬于另一個庫,就會變成類似 memory[0x10ABCDEF] 的無效地址。
IDA 在 7.2 當中強化了 dscu (dyld_shared_cache utils)的功能 [2] ,可參考官方文檔的 IDA: IDA 7.2 – The Mac Rundown。
簡單說就是采用了一種逐步加載的策略。一開始可以只選擇 single module,然后在缺失的地址上右鍵允許動態載入新的 segment 或者模塊。也可以在 IDAPython 當中使用封裝過的 dscu_load_module 和 dscu_load_region 函數來實現同樣的功能。
在 7.2 的時候這個功能還不夠完善,原本只想還原一個 call stub 的符號名,卻把這個 auth_stubs 所在的整個庫的 text 載入到反編譯進程當中。筆者曾經自作多情寫了一個 IDAPython 插件解決了這個需求,只分析單個模塊,然后修復其中的 Class、selector 等運行時信息和符號的引用。
然而 7.5 之后就優化了載入邏輯,目前不會再出現修復一個符號,分析整個模塊的問題,我的插件就失去了意義。
下面進入硬核模式。
在一開始載入 dsc 的對話框其實有三個選項:
1.單個模塊
2.單個模塊和依賴項
3.整個文件
如果我們在分析代碼的時候其實沒有明確的目的,或者只是想找一些特定函數的全局交叉引用,那么前兩個模式就不太夠用了。
完整分析確實需要耗費大量時間,但經過一些嘗試發現,倒也不是不可能的任務。一些小動作,其實可以提升 IDA Pro 分析大文件的效率。
首先分析大文件自然是掛機操作,硬件方面配置越高越好。記得關閉機器的休眠設置,電源選項設置為最大性能。
在分析大文件時,非常不推薦使用默認的 Qt 圖形界面。如果不小心已經在用這個界面,而且左下角顯示 AU 還一直在分析,舍不得中斷當前進度,那么可以先關閉左側的函數列表窗口。許多人都發現這個窗格一旦打開,IDA 會不斷對這個變化的中的列表做無用的排序,從而拖慢分析速度。
IDA 也提供字符界面,在安裝目錄當中有 idat(.exe) 和 idat64 兩個命令。相對來說,字符界面似乎比圖形界面快上一些。
另外在分析 dsc 格式時常常會遇到一些 IDA 認為損壞的信息。如果沒有使用腳本模式,就會彈出一個模態的消息框一直阻塞,直到用戶關閉并勾選“下次不要再顯示”。
所以最好還是寫一段簡單的 python,分析、保存,一氣呵成。
import idc
# generate an empty idb
idc.auto_mark_range(0, idc.BADADDR, idc.AU_FINAL)
idc.auto_wait()
idc.qexit(0)
然后走命令行模式無人值守:
idat64 -c -A -Sidb.py -Lcache.log -odyld_shared_cache_arm64e dyld_shared_cache_arm64e
靈異的事情是,同樣的一個文件,mac 下的 IDA 要比 Windows 下分析快很多。Linux 暫未實際測試。即使生成好的 idb 重新打開, mac 下速度仍然占優勢。
筆者粗略估計了時間,在 i7 的 mac Mini 上和 i9 的臺式機 Windows 上分析同一個文件,兩邊都是 SSD。mac Mini 僅用了三天左右結束,而 Windows 則要足足掛機一星期。
聽說 M1 芯片暴打老師傅,有興趣的讀者也可以測一測。
文件分析好之后,找一個符號也是一件頭痛的事情。符號名搜索不用說,搜一下卡一下,還是算了。
不過使用內置的跳轉功能,還是能基本完成分析工作的。
g 快捷鍵除了可以輸入地址之外,還支持直接跳轉到符號。而 IDA 為 ObjectiveC 相關的運行時信息生成的符號也是有跡可循的。
例如查找所有調用了 interfaceWithProtocol: 這個方法的交叉引用,可以跳轉到 selRef_interfaceWithProtocol: (注意在 IDA 界面中會把冒號替換成下劃線,請按原樣輸入)
Foundation:__objc_selrefs:00000001C73672F8 selRef_interfaceWithProtocol_ DCQ sel_interfaceWithProtocol_ ; "interfaceWithProtocol:"
接著對右側的 sel_interfaceWithProtocol: 做交叉引用即可。
那么為什么不直接跳轉到 sel_interfaceWithProtocol: 上呢?
IDA Pro 目前有一個 bug,在 dsc 里直接跳轉到 selector 上會顯示成一個單獨的 EXTERN 段,這個 segment 里的鼠標操作不正常,會出現選不中的情況。
回到剛才的交叉引用,除了調用的位置之外,在其中搜索 __objc2_meth 還可以定位到這個 selector 對應的方法的結構體(如果有多個類上的重名方法,都會顯示)

而 __objc2_mth 的第三個成員就是函數的 IMP,雙擊跳轉過去即可。
對于已知的類和方法,也可以直接用 g 快捷鍵跳轉,例如:
+[NSXPCInterface interfaceWithProtocol:]
回車直達。
而要枚舉某個 class 所有的 class methods 和 instance methods,還可以使用如下的符號名:
- _OBJC_CLASS_METHODS_NSXPCInterface
- _OBJC_INSTANCE_METHODS_NSXPCInterface
這樣的導航比關鍵字舒服多了,不過只能支持完全匹配。如果仍然查找子串,還是老老實實地用搜索。又不是不能用。
對于 C 的庫函數符號,例如 getenv,直接跳轉到 _getenv 未必是本尊,也可能是某個 __stub。

在這種庫函數鏈接的時候還發現有一些別名的現象,例如會生成多個 j_getenv_X (x 為數字)的符號。為了對全局的調用做分析,只能是手寫腳本多一層向上的交叉引用,否則會出現較多遺漏。
參考資料
[1]https://iphonedevwiki.net/index.php/Dyld_shared_cache
[2]https://www.hex-rays.com/products/ida/news/7_2/the_mac_rundown/
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1551/
暫無評論