Suzu macOS Playground

M12 · 階梯 3 · L3

逆向與動態分析工具鏈

從靜態解析(otool / nm / dyld_info / class-dump / 符號 demangle)到動態分析(lldb / dtrace / frida)的工具鏈與心智模型——靜態解你自己擁有或公開 IPSW 的映像、ObjC/Swift runtime 還原、動態分析只在自有/授權裝置上對「自己的程式」做,並認識 anti-debug 的類別與根因。不示範繞過任何保護。

●●● 前置:M01、M02、M00 otool · nm · dyld_info · class-dump · c++filt · swift-demangle · lldb · dtrace · frida

逆向不是「破解」,是「把映像讀回語意」

前面 M01 教了一個映像怎麼被信任鏈驗過、M02 教了 Mach-O 的結構、M00 補了 ARM64 與 ObjC/Swift runtime 的底。這一章把這些讀回語意:給你一個 binary,你要能回答「它連了哪些 framework、匯出/匯入什麼符號、它的 ObjC class 與 method 長怎樣、它在執行期實際走哪條路」。

這是階梯 3「找攻擊面」的核心工具能力——但方向是分析,不是武器化。本章嚴守一條線:

⚠ 本章邊界(對齊 DESIGN.md §10):所有動手操作只對你自己擁有或被授權分析的程式、以及公開可下載的 Apple IPSW / kernelcache。動態分析(lldb / dtrace / frida)只在你自己的或受控授權的裝置上、對你自己的測試程式進行。本章不示範繞過任何保護機制(不繞 anti-debug、不繞簽章、不附 PoC/exploit),只教工具鏈、runtime 還原原理、以及這些技術對應的攻擊面類別、根因與偵測點。

靜態 vs 動態:兩種互補的視角

視角看到什麼看不到什麼風險面
靜態(讀檔案,程式不執行)結構、符號、字串、ObjC metadata、連結關係、所有可能的程式碼路徑執行期才決定的值(解密後字串、動態載入、實際走哪條分支)低——不執行可疑碼,🟢
動態(程式跑著時觀察)實際暫存器/記憶體值、實際呼叫順序、解密後狀態沒被觸發的路徑、被 anti-debug 隱藏的行為較高——要執行目標,需自有/授權環境,🟡

直覺:靜態先建地圖,動態再驗證地圖上的問號。對未知/可疑樣本,先靜態看清楚再決定要不要在隔離環境動態跑——絕不在主力機直接執行可疑碼。

挑一個你想回答的問題,看該用哪個工具(靜態看結構、動態看行為):

逆向工作台:選對工具

你想回答的問題
otool / dyld_info靜態
otool -l app · dyld_info -linked_dylibs app

靜態看「結構」、動態看「行為」。要拆 Mach-O 結構本身可直接用 M02 解剖器。

只在自有/獲授權的程式上分析。`dtrace` 受 SIP 限制(需調整 csr);`lldb`/`frida` attach 受 `get-task-allow` / 簽章與系統保護約束。

🟢 靜態工具鏈:解你自己的(或公開的)映像

這些工具都只讀檔案、不執行目標,是本章的綠色主路徑。

結構與符號

otool -hv  ./yourbinary      # mach_header:magic / cputype / filetype / flags(接 M02)
otool -l   ./yourbinary      # 全部 load commands(含 LC_LOAD_DYLIB 的相依、LC_CODE_SIGNATURE)
otool -L   ./yourbinary      # 連結了哪些 dylib/framework(最快的「它依賴什麼」)
nm -m      ./yourbinary      # 符號表:哪些是 external/undefined、來自哪個 dylib

dyld_info 是現代取代「用 otool 看 bind/rebase」的工具,直接讀 chained fixups 與匯出:

dyld_info -linked_dylibs ./yourbinary   # 相依 dylib 與 link 屬性(weak/reexport/upward…);要看版本用 otool -L
dyld_info -fixups        ./yourbinary   # dyld 會 rebase/bind 的位置(接 M02 chained fixups)
dyld_info -exports       ./yourbinary   # 匯出符號(trie)

⚠ errata(工具歸屬):otool / nm / dyld_info / c++filt 是 Xcode Command Line Tools 內附(透過 xcrun 解析路徑)。class-dump 不是 macOS 內建——它是第三方開源工具,需另外安裝;近年的維護分支較能正確處理 arm64e 與 Swift 混編產物,原始版對新格式可能解析不全。別假設它在乾淨系統上存在。

ObjC / Swift runtime 還原(接 M00)

ObjC 的 class/method/protocol metadata 以結構化資料保留在 binary 的 __objc_* sections 裡,所以即使沒符號也能把介面還原出來:

dyld_info -objc ./yourObjCApp # dump ObjC metadata(classes/methods/protocols);現代、正確處理 arm64e
                              # chained fixups 與 relative method list(首選)
otool -oV ./yourObjCApp      # 舊式 dump:x86_64/舊格式可用,但 arm64e 上 otool-classic 對 relative
                              # method list 解析不全(會印 'extends past end of file',是工具限制非檔案損壞)
class-dump ./yourObjCApp     # (第三方)重建 @interface 標頭,看 class 與 method 簽名

Swift 與 C++ 的符號是 mangled(編碼了型別/泛型資訊),要還原成人類可讀:

nm ./yourSwiftApp | xcrun swift-demangle   # Swift 符號還原(swift-demangle 在 Xcode toolchain,用 xcrun 叫)
echo '__Z3foov' | c++filt                  # C++ 符號還原(c++filt 為內建)

⚠ errata(呼叫方式):swift-demangle 不在 /usr/bin 的預設 PATH 上,它住在 Xcode toolchain 裡,正確叫法是 xcrun swift-demangle(或 xcrun --find swift-demangle 找出完整路徑)。直接打 swift-demangle 在未設定的 shell 通常 command not found。

反組譯器(看控制流)

互動式反組譯(Hopper、Ghidra、IDA、radare2/rizin、Binary Ninja)把機器碼還原成組語 + 控制流圖 + 偽 C。它們同樣是靜態讀檔:載入你自己擁有或公開的 binary(或從公開 IPSW 解出的 kernelcache)來看「它怎麼分支、呼叫了什麼」。本課的 Mach-O 解剖器(M02)是它們的教學前置——先在瀏覽器裡看懂 load commands 與 section 佈局,再上真正的反組譯器就不陌生。

⚠ 從公開 IPSW 解 kernelcache、dyld shared cache 做靜態研究屬綠色合法(公開可下載、不執行);但重新散布 Apple 的二進位/還原產物可能涉及授權問題——研究自用與散布是兩回事。

🟡 動態分析:只在自有/授權裝置上,對自己的程式

動態分析要讓目標跑起來並觀察它。風險與門檻都比靜態高,因此整段標黃色,且只對你自己寫的測試程式示範。

lldb:偵錯你自己的程式

lldb 是 macOS 內建偵錯器。對你自己編譯、可被你偵錯的程式,基本流程是 launch 或 attach 後設中斷點、看暫存器/記憶體/呼叫堆疊:

clang -g -O0 -arch arm64 hello.c -o hello   # 先編一個你自己的、帶符號的測試程式
lldb ./hello                                # 載入;(lldb) 提示下 b main / run / register read / bt

⚠ errata(能不能 attach 是有規則的,且不教繞過):

  • lldb 要對一個 process 取得控制,底層需要該 process 的 task port(task_for_pid/get-task-allow)。你自己編譯、未啟用 hardened runtime 的 debug build 預設帶 get-task-allow,可被你偵錯。
  • Apple 平台二進位與啟用 hardened runtime / Library Validation 的程式,在一般系統上不允許被任意附加偵錯——這是 SIP / AMFI / 簽章政策的設計(接 M03/M05)。本章不示範如何取得對它們的 task port,也不示範停用這些保護。
  • 偵錯他人或系統的程式需要降低系統保護(SIP/AMFI),那屬授權研究環境的操作,不在本章範圍;本章只偵錯你自己的程式。

dtrace:受 SIP 限制的全系統動態追蹤

dtrace 能在系統層級對 syscall / 函式進出 / 自訂 probe 做動態追蹤,極適合理解「程式實際呼叫了什麼」。但它受 SIP 限制:

sudo dtrace -l | head        # 列出可用 probe;SIP 全開時會回 "DTrace requires additional privileges"

⚠ errata(SIP 與 dtrace,必記且不教繞過):在 SIP 完整啟用的系統上,dtrace 對系統程序的能力被刻意限制——常見徵狀就是 dtrace: failed to initialize dtrace: DTrace requires additional privileges。要在研究機放寬,需在 recoveryOS 調整 SIP(granular 的「只放開 dtrace」選項或整體 csrutil disable,可用性視 macOS 版本而定,且只能在 recoveryOS 設定),這會降低該機安全(接 M05:csr bitmask 中 dtrace 相關限制位元)。本章不要求你關 SIP:dtrace 段落以對你自己的測試程式、或預錄 asciinema 回放示範即可(對齊 DESIGN.md §3.1 黃色可達性註)。判斷 SIP 一律以 csrutil status 為準。

frida:動態 instrumentation 框架

frida 是第三方動態插樁框架,能在執行期 hook 函式、讀寫記憶體、追 API 呼叫,常用於分析自己 app 的執行期行為。

⚠ errata(frida 的前置與邊界):frida 需另行安裝(非內建),且要對目標注入同樣需要 task port / 偵錯權限——對 hardened runtime / 平台二進位一樣受簽章與 SIP/AMFI 政策約束。本章只在自有測試程式上用 frida 觀察「自己的函式被呼叫的引數」,不示範對第三方 app 注入、繞簽章或繞 anti-debug。

anti-debug:教「類別與根因」,不教繞過

軟體(含惡意樣本)常用反偵錯手法讓動態分析變難。本章把它當成攻擊面類別與偵測點來理解,而不給任何繞過步驟:

anti-debug 類別根因(為何有效)偵測 / 研究對策(防禦視角)
ptrace(PT_DENY_ATTACH)程序主動要求 kernel 拒絕被附加偵錯靜態就能看出它呼叫了 ptrace;行為偵測可記 ES 的相關事件(接 M14)
查 P_TRACED flag / sysctl 自檢程式問 kernel「我正被偵錯嗎」屬「程式自檢」類別;分析時改用靜態或非附加式觀測(如 ES NOTIFY)即可旁路其前提,不需繞它
偵測 frida/常見工具特徵掃描特定 port / 字串 / 載入的庫反映「工具留下指紋」這一通則;DFIR 上反過來可當該樣本在反分析的訊號
時間差檢測(timing)偵錯下執行變慢理解其原理即可,分析改走靜態/取樣

⚠ errata / 防禦框架:anti-debug 的存在本身就是高價值偵測訊號——一個正常 app 通常不需要 PT_DENY_ATTACH 或掃 frida。所以對 DFIR(M14)而言,「偵測到 anti-debug 行為」比「成功繞過它」更有價值。本章刻意只停在「辨識類別 + 根因 + 它在 telemetry 上長怎樣」,把「如何繞過特定保護」歸為 🔴 僅概念、不落地。

🔴 紅線:哪些東西本章只談類別、不落地

對齊 DESIGN.md §3.1 的三色分級,以下屬僅概念:

  • 針對特定保護(anti-debug / 簽章 / SIP / hardened runtime)的可操作繞過步驟 → 只談「這類保護的根因與偵測」。
  • 任何 offset / gadget / payload / exploit primitive 串接 → 留給 M13,且 M13 全程以抽象模型呈現(「mitigation 為何有效、攻擊者被迫轉向哪類技術」),不給可武器化細節。
  • 對他人 app / 系統程序的注入、脫殼、繞驗證 → 不示範;本章的動態分析對象一律是你自己的程式。

逆向能力的正當產出是:攻擊面標註、根因歸類、為某類行為設計一條偵測規則(接 M14)。這才是階梯 3→5 要的東西。

🟢/🟡 真機操作卡(在你自己的 Mac 上,macOS 26 適用)

以下靜態段全是唯讀、對你自己擁有或公開的映像安全執行;動態段(🟡)只對你自己編的測試程式。需要 root 的標 sudo。

# ── 🟢 靜態:結構與符號(只讀檔案,不執行目標)──
otool -hv ./yourbinary          # mach_header(接 M02)
otool -L  ./yourbinary          # 連結了哪些 dylib/framework
nm -m     ./yourbinary          # 符號表:external/undefined 來源
dyld_info -fixups  ./yourbinary # chained fixups(dyld 會改寫的位置)
dyld_info -exports ./yourbinary # 匯出符號 trie

# ── 🟢 靜態:ObjC/Swift runtime 還原 ──
dyld_info -objc ./yourObjCApp                 # ObjC metadata(首選;arm64e 正確)。arm64e 上避免 otool -oV
nm ./yourSwiftApp | xcrun swift-demangle      # Swift 符號還原(swift-demangle 用 xcrun 叫)
echo '__Z3foov' | c++filt                     # C++ 符號還原(內建)
# class-dump ./yourObjCApp                     # 第三方,需自行安裝後才可用

# ── 🟡 動態:只對你自己編譯、帶符號的測試程式 ──
clang -g -O0 -arch arm64 hello.c -o hello     # 先做一個你能合法偵錯的目標
lldb ./hello                                  # (lldb) b main / run / register read / bt
sudo dtrace -l | head                         # 列 probe;SIP 全開會 "requires additional privileges"

⚠ 危險/授權邊界(必讀):

  1. 靜態分析對你自己擁有或公開 IPSW 的映像是綠色合法,但重新散布 Apple 二進位/還原產物涉授權,請勿散布。
  2. 動態分析(lldb / dtrace / frida)只在你自有或受控授權的裝置上、對你自己的測試程式進行。對他人或系統程序的偵錯/注入需降低 SIP/AMFI,屬授權研究環境操作,不在本章範圍。
  3. dtrace 受 SIP 限制:放寬需在 recoveryOS 調整 csrutil(granular 的 dtrace 放行選項,或 csrutil disable),會降低整機安全;本章不要求你關 SIP,可用預錄回放替代。
  4. 本章不示範繞過任何保護機制——anti-debug / 簽章 / SIP / hardened runtime 一律只談類別、根因與偵測(DESIGN.md §10)。

下一步:M13 漏洞模型與緩解 — 把逆向看到的攻擊面收斂成「鏈的結構」,用抽象記憶體模型理解 PAC/KASLR/PPL/SPTM-TXM 等 mitigation 為何有效、攻擊者被迫轉向 data-only 的軍備競賽。全章純概念、不武器化。