如何用 REA 完成 iOS 应用结构解剖:从哈希清单到 dylib 加载核验的完整实战
【免费下载链接】reaReverse engineer anything with agents, from app behavior down to native binaries.项目地址: https://gitcode.com/GitHub_Trending/rea2/rea
你拿到一个待审核的第三方 IPA(Apple 应用分发包格式),要求在目标应用不启动的前提下回答三个问题:包内到底装了什么组件、哪些会随主程序一起拉起、每条动态库加载最终命中哪个文件。REA(Reverse engineer anything with agents,面向 AI Agent 的逆向工程工具)把这条 iOS 应用结构解剖链路拆成四份静态证据产物,全程不运行目标二进制。
四份证据产物总览
整条链路只产出四类记录,各自独立、可回溯、可单独复核:
| 产物 | 生成入口 | 回答的问题 |
|---|---|---|
哈希指纹清单inventory_artifact | 库存操作 | 每个组件内容是否校验过、什么格式、有无可执行位 |
| bundle 结构图谱 | project-apple-application-graph | 应用根在哪、嵌套了哪些 bundle、各自扮演什么角色 |
| dylib 加载解析图 | trace-dylib-resolution | 每条动态库加载落到磁盘上哪个文件、结果如何 |
| 字段与签名复核记录 | inspect_plist/inspect_signature | plist 字段真实取值、签名声明内容 |
提示:四份产物都只记录观察事实——路径、哈希、状态。语义解释不在其中,任何结论都要能回指到某条具体记录。
下文按"怎么生成、怎么读、怎么核对"逐一展开,从结构图谱开始。
把文件树投影成 bundle 结构图谱
输入是库存操作产出的inventory_artifact证据(本文第三道工序,两者生成上互相咬合)。投影命令读取证据 ID,输出无执行(execution-free)的 bundle(Apple 平台应用容器,把可执行文件、资源、配置打包在一起)结构图谱:
rea project-apple-application-graph '{"inventory_evidence":[<inventory_artifact Evidence>]}'输出里的bundles数组覆盖全部应用根及其名下嵌套 bundle,每条给出三个锚点:plist 位置info_plist_path、可执行候选executable_candidates、签名文件signing_paths。应用根的识别规则:IPA 取每个Payload/<Name>.app;.app目录就是被库存的目录本身;目录、ZIP 或 DMG 里含应用时,每个顶层.app(其后紧跟Contents子目录)算一个根。DMG 的清单路径分三段:镜像文件、卷、.app,例如My.dmg/MyVol/MyApp.app。若清单只有 DMG 自身的根身份而没有子卷内容,结果标记partial,并明说是否存在 bundle 未知。APK、ASAR 这类非 Apple 格式不被接受,命令直接报输入错误。
布局分三种:macos-deep是标准 macOS 布局,可执行文件放在Contents/下的Contents/MacOS/;versioned-framework含Versions/目录,每个真实版本子目录单独分析;shallow两者都没有——根上有Info.plist即判定为ios平台应用。嵌套组件按路径惯例自动归类,不必逐目录翻:
| 路径特征 | 报告的角色 | 排查价值 |
|---|---|---|
*.appex | app-extension | 随主程序拉起的扩展进程 |
*.xpc | xpc-service | 跨进程通信服务(XPC,Apple 平台 IPC 机制) |
*.systemextension | system-extension | 系统级扩展组件 |
Contents/Library/LoginItems/*.app | login-item | 开机自启的高频藏身处 |
Contents/Library/LaunchServices/ | privileged_helpers | 特权助手,SMJobBless(macOS 提权机制)注册位置 |
*.dext、*.plugin、*.qlgenerator | driver-extension/plug-in | 驱动、插件等辅助组件 |
边界提醒:投影不读取 plist 语义。
CFBundleExecutable、URL Scheme、权限描述等字段取值不在此步产出,必须另行用inspect_plist读取。
这一步还有三处限制需要明说:._*与__MACOSX/这类 AppleDouble 元数据侧车文件会被记录为事实,但不计入 bundle 角色与解剖组件;基于路径推断的 JavaScript/原生配对假设,结果序列化上限 2 MiB,超出时bridge_candidate_coverage记录总对数、输出数与省略数,整体覆盖度降为partial;目录型库存只要目录名以.app结尾就按独立根处理,而 DMG 子卷清单依赖原生 macOS 挂载适配器,非 macOS 主机上拿不到子卷。
追踪 dylib 加载命中路径
结构图谱只回答"有什么"。每条 dylib 加载最终命中磁盘上哪个文件、顺序如何,需要按 dyld(系统动态库加载器)的搜索规则做模拟。实现在 TypeScript 里解析 Mach-O(Apple 平台原生二进制格式)与 FAT(多架构容器)头、加载命令,不依赖 Apple 工具链,Linux、Windows 上同样可跑。
rea trace-dylib-resolution MyApp.app --json指定单个进程根与架构时加参数:
rea trace-dylib-resolution MyApp.app --root Contents/MacOS/MyApp --architecture arm64 --json同一个.app内,主程序、XPC 服务、扩展、登录项、助手工具,每个可执行文件各自构成进程根,@executable_path按该可执行文件所在目录单独计算。@loader_path就地展开,@rpath链从加载者逐级向上游查找;符号链接在根内跟随,架构匹配允许兼容(如x86_64h进程使用x86_64镜像)。
每条依赖的报告按顺序列出尝试过的搜索候选,并给出每个候选的结果(absent、architecture-missing、escapes-target等);整条依赖的最终状态分四级:
| 状态 | 判定口径 |
|---|---|
resolved | 根内镜像确认可加载,且没有更早的候选无法判定 |
conditional | 根内镜像能解析,但更早的根外搜索位置或未建模的环境输入可能改变最终加载的镜像 |
unresolved | 根内所有候选均确定缺失或不可用 |
undetermined | 根内没有候选可解析,且部分候选位于根外或依赖运行环境 |
verify:macos-bundle会把解析器输出与otool -l逐条比对,并用DYLD_PRINT_LIBRARIES对照 dyld 实际加载的镜像来核对预测的加载顺序。想交叉核对时,也可以直接用 Hopper 反汇编引擎打开目标读汇编。这一步在原生调查流程中的位置见 docs/native-investigation.md。
边界提醒:加载图是静态模型,不观察目标设备的主机 CPU,也证明不了设备会执行哪个切片;
conditional边上的发现,只在该镜像确实加载时成立。
生成哈希指纹清单
这是前两份产物的地基。对 IPA、DMG(磁盘镜像)、ZIP 或.app目录执行一次库存操作,得到inventory_artifact证据记录,字段分四组:
root_sha256/root_format:根文件的 SHA-256 摘要与容器格式(如ipa、dmg);nodes:逐组件记录格式、架构、可执行位、内容状态;occurrences:逐出现条目报告逻辑路径、声明大小、哈希校验状态(如verified);limitations:核实不了的条目会显式列在这里,清单不悄悄丢弃任何内容。
某文件字节读不出来时(如加密的归档成员),它带着原始路径、加密标志与空内容身份进入unavailable_components,整体覆盖度随之降为partial。因为这份记录只登记观察到的事实,所以后续所有结论都必须能回指到某条具体哈希。
复核 plist 字段与签名声明
前三份产物都建立在"路径 + 哈希"之上。凡涉及字段取值或签名声明,都要用专门工具补一轮核实。字段取值对 bundle 的info_plist_path执行:
rea inspect-plist <path>CFBundleExecutable、标识符、SMPrivilegedExecutables、URL Scheme、权限描述文本都从这里读出。签名声明则用inspect_signature逐条核对executable_candidates。注意加载图给出的发现(如earlier-rpath-candidate-absent)只是指出更早的搜索位置可能存在兼容镜像,不构成 dyld 将加载哪个镜像的证据,签名元数据仍走inspect_signature。
全局边界
- 四份产物都是观察记录。落笔写"应用声明了某权限""签名含某项权限声明"时,出处必须是
inspect_plist/inspect_signature的结果,而不是投影输出。 - 投影是执行无:不读 plist、不解析签名、不主张观察到运行时调用;框架提示只匹配完整路径段(
React.framework、Flutter.framework等),属于库存派生的提示,不是观察到的加载。 - 首次使用,先按 docs/installation.md 完成安装与 Provider 配置;完整语义与状态表在 docs/apple-application-analysis.md。
| 步骤 | 产物 | 核对方式 |
|---|---|---|
| 库存 | inventory_artifact证据 | occurrences哈希状态对照limitations条目 |
| 结构投影 | bundle 结构图谱(bundles+ 角色) | 用inspect_plist反查info_plist_path |
| 加载追踪 | dylib 解析图(依赖边 + 四级状态) | verify:macos-bundle对照otool -l与实测加载 |
| 字段复核 | plist 取值与签名声明记录 | 回扫正文中每条语义结论逐一确认 |
【免费下载链接】reaReverse engineer anything with agents, from app behavior down to native binaries.项目地址: https://gitcode.com/GitHub_Trending/rea2/rea
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考