When to use / when not to use
- Use: dynamic debugging of macOS/iOS targets (attach/launch/breakpoints/expression evaluation/symbol lookup)
- Use: debugging debug builds with DWARF on Linux (lldb shares the same origin as the clang toolchain, mature DWARF handling)
- Don't use: Linux targets ([[re-gdb]]), Windows targets ([[re-x64dbg]])
- Don't use: iOS jailbreak/certificate environment issues (belongs to the [[re-mobile]] domain); read-only memory ([[re-memdump]] default dumping takes priority)
- Don't use: scripted large-batch memory dumping/forensics ([[re-memdump]] / [[re-mem-forensics]] are more suitable)
Tool preparation
Refer to the macOS branch of [[re-analyze/platform-tips]]—SIP and TCC restrict attach, Developer Tools authorization required.
lldb
- macOS: built-in (Xcode Command Line Tools:
xcode-select --install) - Linux:
apt install lldb/dnf install lldb/pacman -S lldb - More complete on macOS:
brew install llvm(includes the new lldb) - Verify:
lldb --version - Version differences: Xcode's built-in lldb, llvm.org's lldb, and the Swift toolchain lldb are similar in functionality but have different version numbers (confirm with
lldb --version); when scripts depend on thelldb.SBDebuggerAPI, cross-version compatibility needs to be verified (see [[gotchas]])
SIP disabled or authorized (prerequisite for attach)
- System Settings > Privacy & Security > Developer Tools > authorize the terminal using lldb (TCC authorization)
- Older systems:
sudo DevToolsSecurity -enable - Note: generally do not disable SIP (you can handle it temporarily only for debugging needs); iOS kernel-level debugging requires KDK/development kernel and belongs to the [[re-mobile]] domain
Operating steps
-
Developer Tools permission confirmation:
lldb -p <测试进程pid> # 若报 "please check the developer mode" / 权限拒绝 → 去系统设置授权终端Restart the terminal after authorization; if it still fails, check SIP status:
csrutil status. Note: the TCC authorization popup must be manually confirmed in System Settings and cannot be bypassed from the command line. -
attach/launch:
(lldb) process attach --pid <pid> # 附加 (lldb) process attach --name Safari # 按名字附加(需唯一) (lldb) target create ./sample # 或直接启动 (lldb) run --args arg1 (lldb) continue / ciOS device:
platform select remote-ios+process connect connect://<device>(requires a debugging service; for jailbreak/certificate requirements see [[re-mobile]])- Attach timing note: the target has already run its initialization logic, so pre-entry behavior cannot be seen—if you need to see from the entry point, use
target create+runlaunch mode - Launch and stop at entry:
process launch --stop-at-entry(equivalent to-s)
- Attach timing note: the target has already run its initialization logic, so pre-entry behavior cannot be seen—if you need to see from the entry point, use
-
image lookupsymbol:(lldb) image lookup -n main # 按名字查符号 (lldb) image lookup -a $pc # 按地址查当前所在函数/源行 (lldb) image list # 已加载镜像(dylib 基址表) (lldb) image lookup -rn dispatch # 正则匹配符号For stripped binaries use
image lookup -ato trace back the module an address belongs to, together withimage dump symtab- Module base address:
image list -oadd offset to list—under ASLR the base address differs on each run, so in scripts get the base address first before computing absolute addresses (see [[gotchas]])
- Module base address:
-
Memory expression
expr:(lldb) expr $rax (lldb) expr $rax = 0 # 写寄存器 (lldb) expr (char*)0x100000000 # 类型化读地址 (lldb) expr -O -- @"hello" # Objective-C 求值(objc 运行时) (lldb) memory read -c 20 -s 4 -f x $rsp # 读内存(缺 -f x 会报格式冲突错误) (lldb) memory write -s 1 0x100000000 0x90 # 写字节(绕过校验)- Expression evaluation has side effects (it executes code)—don't evaluate carelessly on sensitive paths, see [[gotchas]]
-
Breakpoints and scripts:
(lldb) breakpoint set -n main (lldb) breakpoint set -a 0x100004000 -c '*(int*)($rsp) == 0x1234' # 条件断点 (lldb) breakpoint command add 1 > frame variable > expr $rax > continue > DONEPython automation: lldb has built-in Python—
script import lldb, or uselldb -s script.lldbbatch processing.- Symbolic breakpoint:
breakpoint set -n objc_msgSend(by symbol name, takes effect automatically after the module loads); regex:-r '^check_' - Breakpoint management:
breakpoint list/breakpoint delete/breakpoint disable 1
- Symbolic breakpoint:
-
Step/stack/thread:
(lldb) thread step-over / step-into / step-out # 步过/步入/出函数(别名 next/step/finish) (lldb) thread backtrace # 当前线程栈(别名 bt) (lldb) thread list # 全部线程 (lldb) frame select 1 # 切栈帧 (lldb) frame variable # 当前帧局部变量 (lldb) register read # 全部寄存器- After stepping over a call, use
step-outtogether with the return register to see the result; this is the fastest path to "verify a function's return"
- After stepping over a call, use
-
Memory search and regions:
(lldb) memory find 0x100000000 0x101000000 -e 0xdeadbeef # 区间内搜值/字节串 (lldb) memory find -s "password" 0x100000000 0x101000000 # 搜字符串 (lldb) memory region 0x100004000 # 该地址所属区域权限/边界- Finding keys/constants: first
memory findLocate the breakpoint, then find the write point; check abnormal segments in region permissions (RWX) first (packer/decryption segment)
- Finding keys/constants: first
-
Evidence verification (wrap-up): breakpoint hit records,
frame variable/exproutput,memory readdump (memory read --force -o 文件export) cross-check against [[re-triage]] initial survey values and file them, write conclusions in [[re-analyze/analysis-contract]]
Cross-domain joint
- [[re-binary-core]]: Workflow step 6 (macOS/iOS debugger)
- [[re-mobile]]: underlying tools for iOS App/jailbreak dynamic debugging
- [[re-format-macho]]: parse structure before debugging (entry/LC_MAIN and signature status)
- When attach fails, follow [[re-analyze/platform-tips]] and switch to [[re-memdump]]
Common pitfalls and traps
- SIP/TCC blocks attach: attach from an unauthorized terminal is denied (
permission deniedor silently fails) — authorize developer tools in System Settings; disable SIP only in extreme cases - Permission dialog requires manual confirmation: TCC pops up a dialog for each new debug target, and scripted attach gets stuck — pre-authorize the target program
- Kernel debugging requires extra configuration: macOS kernel debugging requires a matching-version KDK + booting a development kernel; ordinary reverse engineering does not need it, so do not waste time on user-mode debugging
- Signature status: a modified Mach-O cannot run without re-signing (see [[re-format-macho]]) — first
codesign -f -s -re-sign, then debug - Three-layer restriction of task_for_pid entitlement/SIP/Hardened Runtime: symptom — Developer Tools is authorized but attach still fails or the target crashes immediately on launch; cause — besides TCC there are three more layers: the debugger needs the task_for_pid entitlement, debug APIs are restricted when the target enables Hardened Runtime, and SIP restricts attach to system processes; countermeasure — troubleshoot layer by layer (
csrutil status, check the target signature and entitlements); test targets can first be de-signed/re-signed ([[re-format-macho]]) and then debugged; refer to the macOS branch of [[re-analyze/platform-tips]] - ASLR base address drift: the dylib base address differs on each launch — hard-coded addresses in scripts will become invalid; use
image list -oto get the base address and convert it, and set breakpoints on symbol names as much as possible (-n/-s) - Expression evaluation has side effects:
exprwill actually execute code (call functions/modify memory) — evaluating at a hook point may change the target behavior; for verification scenarios useframe variableread-only first - For version differences, debug server, and boundaries see [[gotchas]]