iOS 应用包里到底藏了什么?不越狱、不运行,给 IPA 做三道静态体检
【免费下载链接】reaReverse engineer anything with agents, from app behavior down to native binaries.项目地址: https://gitcode.com/GitHub_Trending/rea2/rea
拿到一份来路不明的 IPA:装不上机,也越不了狱。用 REA(面向 AI Agent 的逆向工程工具)做 iOS 逆向工程的静态部分:只读包、不执行任何代码,把 IPA 解析、应用结构分析、Mach-O(Apple 平台的原生二进制格式)动态库加载链一次看全。
📦 拿到一份装不了的 IPA,先问什么?
渠道存疑、没开发签名,跑起来又怕带毒——先问三个问题:包有没有被人改过?启动时到底会拉起哪些组件?每个动态库最终落到哪个文件? 越狱和动态调试都用不上时,答案只能从"完全不动它"的静态检查里找。 把整份 IPA 当成一次体检:先出整包底账,再核启动阵容,最后追每条动态库的去向。 三道检查全程只读,做完你手里有一份可回溯的书面结论。
🧾 它可信吗:给 IPA 做一份完整性认证清单
结论先行:第一道体检是整包认证——对 IPA、DMG、ZIP 或 macOS.app目录执行库存操作,产出一条inventory_artifact(带哈希指纹的整包库存证据)记录,后面每句结论都能回到这张底账上核对。
| 字段 | 记录什么 | 体检意义 |
|---|---|---|
nodes | 每个文件的格式、架构、可执行位、内容状态 | 分出谁是真二进制、谁只是资源 |
occurrences | 每个逻辑路径的声明大小与哈希校验状态(verified等) | 同一份内容出现在几个位置,逐个有数 |
root_sha256/root_format | 根文件的哈希指纹与格式(ipa、dmg等) | 拿它和官方渠道的包对指纹,先确认"是不是同一个包" |
limitations | 无法核实的项目,如实列出 | 查不到的地方不装懂,静默忽略等于造假 |
清单只记观察事实:不解析 plist,不判签名语义。 这是刻意划出的边界——底账管"包里有什么",语义问题留给下一节和专门的工具。
🧭 它启动时会带谁上场:一条命令画出 bundle 结构图谱
结论:把上一节的 inventory_artifact 交给一条命令,整包里每个应用根和它名下的嵌套bundle(Apple 平台上独立成组的容器目录)全部摆出来,还按路径习惯自动分好了角色。
rea project-apple-application-graph '{"inventory_evidence":[<inventory_artifact Evidence>]}'应用根不是猜的,按库存根的类型定:
| 库存根长什么样 | 应用根落在哪 | 备注 |
|---|---|---|
| IPA | 每个Payload/<Name>.app | 标准 iOS 安装包布局 |
.app目录 | 目录本身 | 目录型库存一律当独立根,哪怕缺Contents/ |
| 含应用的目录 / ZIP / DMG | 每个最外层<Name>.app(带Contents/) | DMG 形如<image>.dmg/<Volume>/<Name>.app |
只有 DMG 根身份、缺子清单时,覆盖度如实标记为partial,缺失即"未知"而不是"没有";APK、ASAR 这类库存直接按输入错误拒绝,避免张冠李戴。
三种 bundle 布局,对应三种读法
bundles 列表给出每个应用根及其全部嵌套 bundle:info_plist_path、executable_candidates(可执行候选)、signing_paths(签名路径)。布局分三类:
macos-deep:含Contents/,可执行文件位于Contents/MacOS/versioned-framework:含Versions/,逐个真实版本目录分析shallow:两者皆无;带Info.plist就判为ios平台,典型 iOS 应用形态
角色按路径惯例自动归类
先看高危位置:Contents/Library/LoginItems/下的登录项、Contents/Library/LaunchServices/下的特权助手单列;再按惯例归类,*.appex(应用扩展)、*.xpc、*.framework、*.systemextension各归其位。 这批角色正是隐藏启动项和提权点的高发位置,审查时优先过一遍。 AppleDouble 侧车文件(._*、__MACOSX/)算进清单事实,但排除在 bundle 角色与解剖组件之外。 实现见 AppleApplicationService.ts,工具契约定义在同目录的 applicationToolContracts.ts 中。
🩺 它的动态库到底加载到哪:Mach-O 加载链静态解析
结论:前两道回答"包里有什么、谁会上场",这一道回答"每条 dylib 声明最终命中哪个文件"——纯头文件级解析,一个字节都不执行。
rea trace-dylib-resolution MyApp.app --json解析用纯 TypeScript 完成 Mach-O / FAT 头与加载命令,不依赖 Apple 工具,非 macOS 机器上也能跑。 它按 dyld 的规则展开搜索链:@executable_path指向进程根可执行文件所在目录,@loader_path指向持有该加载命令的镜像目录,@rpath先试加载方自己的LC_RPATH,再沿加载链逐级上溯到进程根;符号链接在根内跟随,x86_64h进程还能兼容加载x86_64切片。
每条依赖边都列出试过的候选及结果(resolved、absent、architecture-missing、escapes-target等),整条边再汇总成四级判定:
| 判定 | 什么情况出现 | 审查时怎么读 |
|---|---|---|
resolved | 根内的镜像能加载,且没有更靠前的候选悬而未决 | 加载去向确定 |
conditional | 根内能解析,但根外的更早候选(如/usr/lib/swift)在真实系统上存在时会抢先 | 按"可能换镜像"复核 |
unresolved | 所有候选在根内明确缺失或不可用 | 该依赖加载会失败 |
undetermined | 无根内命中,且存在根外或依赖环境的候选 | 取决于运行环境,如实标注 |
想核对实际汇编,可以把清单预测的加载顺序交给 Hopper 等反汇编引擎做交叉验证:
它在原生调查流程中的位置见 native-investigation.md。
🧰 边界与换工具时机:体检单故意不写的两件事
结论:这套体检刻意停在"包内事实",语义和声明类问题要换工具补位,别在底账上找不存在的答案。
- 不解析 plist 语义:要看
CFBundleExecutable、URL Scheme、权限描述,对 bundle 的info_plist_path用inspect_plist - 不核查签名:对可执行候选用
inspect_signature查签名声明 - 桥接假设(JavaScript ↔ 原生配对)最多序列化 2 MiB,超出时结果携带
bridge_candidate_coverage报告省略数量,整体覆盖度降为partial - 目录型
.app一律视为独立根;DMG 子卷清单依赖原生 macOS 挂载适配器,缺适配器时只能拿到根身份
换工具的时机只有一个标准:你问的问题,当前工具明说"不归我管"。静态体检的分工是照实记录,不是全知全能。
字段语义与状态表的完整版见 apple-application-analysis.md;第一次用 REA 先按 installation.md 完成安装与 Provider 配置。三道检查做完,一份 IPA 的完整性、启动阵容、动态库去向全部有据可查——这就是不越狱、不运行也能看清 iOS 应用包的全部底牌。
【免费下载链接】reaReverse engineer anything with agents, from app behavior down to native binaries.项目地址: https://gitcode.com/GitHub_Trending/rea2/rea
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考