IPA 包脱壳、Mach-O 解析与 Info.plist 信息提取实战
2026/9/16 18:07:16 网站建设 项目流程

手上要是拿到一个 ipa 包,很多人第一反应是双击解压,翻出Payload目录,然后兴冲冲地对着里面的可执行文件跑class-dump,结果要么导出个空目录,要么报一堆错——原因很简单,从 App Store 渠道下来的应用,它的二进制代码段是加密的,你没脱壳,拿到的就是一堆“密文”。围绕 ipa 包脱壳、解析、info.plist 文件基本信息介绍这条线,从前到后其实是三个咬合的环节:脱壳负责把加密的可执行文件还原成明文,解析负责把 Mach-O 结构和资源拆开看清骨架,而 info.plist 则是整个应用最权威的一张“身份卡”,包名、版本、可执行文件名、URL Scheme、权限用途说明全在里面。这套流程我做安全研究和应用审计时反复走,踩过的坑也够写一本小册子。如果你是想入门 iOS 逆向的新手、做竞品合规分析的开发,或者只是纯粹好奇一个 ipa 包内部长什么样,下面这些内容都能直接拿去用。

1. 先把概念捋清:ipa、脱壳、解析到底是什么关系

1.1 ipa 包的本质就是一层压缩外壳

先说最容易被误解的一点:ipa 本身不是一种特殊格式,它就是个换了后缀的 zip。苹果为了分发方便,把整个.app目录打成一个压缩包,改名叫iOS App Store Package,缩写就是 ipa。你用系统自带的解压工具把xxx.ipa拖出来,或者直接改后缀成.zip再解压,看到的结构几乎是固定的:一个Payload目录,里面躺着一个.app文件夹,再往下一层才是真正的可执行文件、FrameworksPlugIns、各种图片音频资源、Info.plist、以及签名相关的_CodeSignature目录。

这个结构之所以重要,是因为它决定了你后续所有操作的路径。很多新手上来就问“怎么分析一个 ipa”,其实第一步永远是先把外壳剥掉看目录。我自己的习惯是先跑一遍unzip -l xxx.ipa,只列目录不解压,几秒钟就能判断这个包是不是完整、有没有多 target、Frameworks 里挂了哪些第三方 SDK。这一步不用越狱、不用任何特殊工具,普通电脑就能做,属于零门槛操作。

提示:有些 ipa 是经过二次打包或者加固工具处理过的,内部目录结构会和标准结构有出入,比如多出一层壳目录、可执行文件体积异常小、或者Info.plistCFBundleExecutable指向的名字和实际文件对不上。这种包要格外小心,直接解压分析往往得到的是“外壳”,真正的逻辑藏在别处。

1.2 脱壳到底脱的是什么壳

这里的“壳”和 PC 上那些商业加壳工具(比如 UPX、VMProtect)完全是两码事。iOS 应用从 App Store 下载安装后,可执行文件的__TEXT段(代码段)是被系统层面加密的,依靠的是设备绑定的密钥在运行时解密。这个机制让静态分析拿不到明文机器码,你直接用otool、Hopper、IDA 去看,代码段是一片乱码或者干脆提示加密。

所谓脱壳,就是把已经加载到内存里、已经被系统解密好的那份代码段 dump 出来,重新写回文件,得到一个不加密的可执行文件。判断一个二进制是否被加密,看 Mach-O 的 Load Commands 里有没有LC_ENCRYPTION_INFOLC_ENCRYPTION_INFO_64,里面的cryptid字段如果是 1 就代表加密,0 就代表已经脱壳。脱壳后cryptid会变成 0,这时候 class-dump、Hopper 这些工具才能正常工作。

我不建议把脱壳理解成“破解”。它是逆向工程里最基础的一步准备工作,就像你要修一台机器,得先把外壳螺丝拧开。真正决定你能不能看懂一个应用逻辑的,是脱壳之后的静态分析和动态调试,壳本身只是道门槛。

1.3 解析和脱壳的分工别搞反

很多人把“解析”和“脱壳”混着说,其实顺序不能乱。脱壳是对可执行文件做的,解析是对整个包做的,解析的对象包括 Mach-O 头、Load Commands、符号表、字符串表、资源文件、以及Info.plist。你没脱壳,解析可执行文件就会缺一大块信息;但解析Info.plist这类纯文本/二进制配置文件,跟脱壳一点关系都没有,甚至不用装任何越狱环境。

我一般的流程是:解析 Info.plist(了解应用基本信息和能力)→ 解压看目录(了解组成)→ 脱壳(拿明文可执行文件)→ 解析 Mach-O(看类和依赖)。这个顺序的好处是,你在动手脱壳之前,已经通过Info.plist知道包名、可执行文件名、支持的设备类型,脱壳工具挂载目标时能少走很多弯路。反过来说,如果你连CFBundleExecutable是哪个文件都没确认就急着脱壳,很容易 dump 错目标。

2. 动手前的准备:环境、工具与合规边界

2.1 脱壳工具怎么选:一张对比表说清

脱壳工具这几年更新换代很快,老牌的dumpdecrypted在新系统上基本已经跑不动了,现在主流的是基于 Frida 的方案和越狱端 GUI 工具。我把常用的几个列出来,方便你按自己的环境选:

工具运行环境操作难度适用场景说明
CrackerXI+越狱设备(GUI)单包快速脱壳越狱源里安装,界面点选,适合新手
frida-ios-dump电脑 + Frida批量、脚本化跨平台,命令行为主,可定制
bagbak电脑 + Node命令行党基于 Frida,输出规范
Clutch越狱设备老系统新系统兼容性差
dumpdecrypted越狱设备学习原理已被淘汰,了解思路即可

我个人的建议是:新手先用 CrackerXI+ 把“脱壳成功”这个正反馈拿到手,再去折腾 Frida 方案。因为 Frida 方案涉及电脑端和手机端的版本匹配,第一次搞很容易卡在“attach 不上”这一步,容易劝退。等你理解了脱壳的本质,再回头用命令行工具做自动化,效率会高很多。

2.2 设备与系统版本的选择

脱壳必须在一个能拿到足够权限的环境里做,通常就是一台越狱设备。这里有个经验:设备和系统版本的组合,直接决定了你能用哪套越狱工具,也决定了 Frida 能不能稳定跑。老设备(A11 及以前)用 checkra1n 这类方案比较成熟,稳定性好;新一点的设备则要对应各自可用的方案。iOS 版本太高,Frida 的服务端可能还没适配,会出现frida-ps -U能列出进程但 attach 就崩的情况。

我踩过最典型的一个坑:手机系统升到某个版本后,Frida 服务端版本没跟着换,frida-ps -U正常,一到 dump 就卡死。后来把电脑端的frida-tools和服务端版本对齐,问题直接消失。所以动手前先确认三件事:越狱环境是否稳定、Frida 电脑端与设备端版本是否一致、目标应用能否正常启动。这三条任意一条不满足,脱壳都会失败。

2.3 合规边界:这条线必须画清楚

技术本身是中性的,但用在哪里决定了性质。我自己给团队立的规矩很明确:只分析自己拥有版权的应用、公司自己上架的应用、或者明确获得授权的第三方应用,用于安全研究、漏洞自查、兼容性测试、竞品公开信息分析。拿别人的付费应用脱壳后打包分发、去广告、改功能,这条线坚决不碰。

尤其是网上流传的一些“自签包”“多开包”“增强版 ipa”,来源不明,里面可能被塞了额外的代码,你在自己设备上装上跑,风险完全不可控。我在做审计时遇到过被二次打包的应用,Info.plist里多出一堆莫名其妙的权限描述,URL Scheme 里混进了第三方域名,这种包绝对不能拿来当分析样本。干净的样本来源,是分析工作最重要的前提。

注意:脱壳、解析这类操作,请务必限定在你有合法权限的样本上。分析他人受版权保护的应用并用于分发、修改、牟利,属于明确的越界行为,一旦涉及法律风险,技术再熟也救不了你。

3. ipa 包结构拆解与脱壳实操

3.1 先解压看目录,摸清包的骨架

拿到一个 ipa,第一步不是脱壳,是解压。用unzip列个目录,几秒钟就能对包的组成有个大概判断:

unzip -l demo.ipa | head -50

典型的输出里你会看到Payload/Payload/Demo.app/,然后是Demo(可执行文件)、Info.plistembedded.mobileprovision_CodeSignature/CodeResources,再往下是Frameworks/PlugIns/,以及大量.car资源包、.png.json文件。这一步我最关注三样东西:可执行文件体积(判断是否加壳)、Frameworks 目录(第三方 SDK 和动态库)、PlugIns 目录(扩展组件)。

解压出来之后,可执行文件通常是不带后缀的那个文件,名字和Info.plist里的CFBundleExecutable一致。你可以用file命令确认一下它的类型,正常的 iOS 可执行文件会显示 Mach-O 格式,还能看到架构(arm64、arm64e 之类)。如果file显示的体积小得离谱,或者根本不是 Mach-O,那这个包大概率做了特殊处理,需要换思路。

3.2 Mach-O 结构:可执行文件的“体检报告”

Mach-O 是 iOS 可执行文件的标准格式,理解它的结构是解析的核心。它大致分三块:Header(头部)、Load Commands(加载命令)、Data(数据段)。Header 里记录了 CPU 架构、文件类型、Load Commands 的数量;Load Commands 是一张“目录”,告诉系统怎么加载这个文件;Data 里才是真正的代码和资源。

和脱壳最相关的是 Load Commands 里的LC_ENCRYPTION_INFO_64,它记录了加密信息。用otool可以直接看:

otool -l Demo.app/Demo | grep -A 5 LC_ENCRYPTION_INFO

如果看到cryptid 1,说明代码段加密,需要脱壳;cryptid 0说明已经是明文。这个字段是判断脱壳是否成功最直接的依据,比看文件体积靠谱得多。除了加密信息,你还可以关注LC_LOAD_DYLIB(依赖的动态库)、LC_RPATH(运行时搜索路径)、__TEXT__DATA段的分布,这些信息能帮你判断应用用了哪些框架、有没有做特殊的反调试处理。

我通常会把otool -l的输出存成文本,配合MachOView之类的可视化工具交叉看。命令行适合批量处理,可视化工具适合理解结构,两者结合效率最高。别一上来就上重量级的反汇编工具,先把结构吃透,后面定位具体逻辑会轻松很多。

3.3 签名与描述文件:别忽略的“附件”

每个正规 ipa 里都有签名相关的内容:_CodeSignature/CodeResources记录了包内所有文件的哈希,embedded.mobileprovision记录了签名所用的证书和权限(entitlements)。这些信息对分析很有价值,比如通过描述文件能看出应用申请了哪些特殊权限、是不是企业证书签的、有效期到什么时候。

查看描述文件的内容可以用:

security cms -D -i Demo.app/embedded.mobileprovision

输出的 plist 里能看到application-identifierget-task-allowkeychain-access-groups等字段。get-task-allow如果是true,说明这个包被调试过或者是开发包,这对分析有参考意义。校验整个包的签名的命令是codesign -dvvv Demo.app,不过要注意,脱壳后的包签名会失效,这是正常的,不要误以为脱壳把包弄坏了。

关于签名工具和自签,很多刚入门的人会绕进去。我的建议是:分析阶段不需要关心签名是否有效,只有当你要在设备上重新安装这个包做动态调试时,才需要重新签名。全能签这类工具解决的正是“怎么把 ipa 装进设备”的问题,和脱壳解析是两条线,先把脱壳解析搞明白,再考虑安装的事。

3.4 用 frida-ios-dump 完成一次脱壳

环境齐了之后,脱壳本身其实很快。以 frida-ios-dump 为例,整个过程分四步。第一步,电脑端装好环境:

pip3 install frida-tools

第二步,确认能连上设备,列出进程找到目标:

frida-ps -U | grep -i demo

第三步,用 dump 脚本把目标 dump 成 ipa:

python3 dump.py -o demo_decrypted.ipa com.example.demo

这行命令做的事,就是 attach 到目标进程,在内存里定位已经解密的__TEXT段,把它连同其他段一起重新组装成一个不加密的 Mach-O,再打包成 ipa。整个过程通常几十秒到一两分钟,取决于应用体积。如果卡在 attach 阶段,多半是 Frida 版本不匹配或者应用做了反调试;如果 dump 完成后打不开,往往是目标进程在 dump 过程中被系统回收了,可以先把应用切到前台再试。

第四步,验证脱壳结果。把生成的 ipa 解压,用otool -lcryptid是不是变成了 0:

otool -l Payload/Demo.app/Demo | grep -A 4 LC_ENCRYPTION_INFO

看到cryptid 0,这次脱壳就算成功了。接下来可以跑class-dump导出头文件,或者直接用反汇编工具打开。

实操心得:脱壳时尽量保证应用停留在前台且不锁屏,很多 dump 失败都是因为应用被系统挂起。另外 dump 出来的 ipa 体积往往比原包小,这是正常的,因为去掉了部分签名冗余,不用紧张。

3.5 脱壳之后先做什么

脱壳只是拿到了入场券,接下来才是真正花时间的分析。我一般先跑一遍class-dump把类和方法签名导出来:

class-dump -H Payload/Demo.app/Demo -o headers/

然后strings一下可执行文件,找找有没有敏感的域名、密钥、URL Scheme:

strings Payload/Demo.app/Demo | grep -iE "https?://" | sort -u | head -30

这两步能快速给你一张“地图”,告诉你应用大概用了哪些框架、和哪些服务器通信。有了地图再去反汇编里定位具体方法,比漫无目的地翻快得多。脱壳不是终点,是一整套分析流程的起点。

4. info.plist 文件深度解析

4.1 两种形态:二进制和 XML 的转换

Info.plist有两种存储形态:二进制 plist(bplist)和 XML plist。App Store 分发的包里的Info.plist通常是二进制格式,直接用文本编辑器打开会是一堆乱码。这时候需要用plutil转换:

plutil -convert xml1 Payload/Demo.app/Info.plist -o Info.xml

反过来,如果你改完想转回二进制:

plutil -convert binary1 Info.xml -o Info.plist

还有一个更省事的查看方式,plutil -p直接以可读格式打印,不出文件:

plutil -p Payload/Demo.app/Info.plist

我个人习惯是转成 XML 存一份,方便用 diff 对比不同版本。做竞品版本对比分析时,两份 XML 一 diff,新增了哪些权限、改了哪些 Scheme,一目了然。这个技巧在追踪应用功能迭代时特别好用。

4.2 常见 key 逐个拆解

Info.plist里的字段很多,但真正高频用到的就那么十几个。我按重要性整理成一张表,方便你对照查:

Key含义为什么重要
CFBundleIdentifier包唯一标识定位应用、调用 URL Scheme 的核心
CFBundleExecutable可执行文件名脱壳和解析的目标文件就是它
CFBundleName应用短名称显示用,最长 16 字符
CFBundleDisplayName桌面显示名用户实际看到的名字
CFBundleShortVersionString对外版本号用户看到的 1.2.3 这种
CFBundleVersion构建版本号内部迭代用,常是数字递增
MinimumOSVersion最低系统要求判断兼容性
UIDeviceFamily支持的设备类型1 是 iPhone,2 是 iPad
CFBundleURLTypesURL Scheme 注册分析应用间跳转、深层链接
LSApplicationQueriesSchemes可查询的 Scheme 白名单判断它能唤起哪些其他应用
NSAppTransportSecurity网络传输安全策略是否允许明文 HTTP
UIBackgroundModes后台运行能力定位、音频、下载等
NSxxxUsageDescription权限用途说明隐私相关,逐个权限对应一条

这张表里,我分析时最看重的是CFBundleURLTypesLSApplicationQueriesSchemes这两组。它们像是应用的“社交关系网”:前者是别人怎么唤起我,后者是我能唤起谁。通过 Scheme 分析,你能还原出应用之间的跳转链路,这对理解产品生态非常有用。

4.3 用命令行和脚本精准提取字段

手动在 XML 里翻容易看漏,我一般用PlistBuddy精准取值:

/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" Payload/Demo.app/Info.plist /usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" Payload/Demo.app/Info.plist

如果要批量分析很多个包,写个 Python 脚本更高效,plistlib是标准库自带,不用装额外依赖:

import plistlib, sys with open(sys.argv[1], "rb") as f: info = plistlib.load(f) print("包名:", info.get("CFBundleIdentifier")) print("版本:", info.get("CFBundleShortVersionString")) print("构建号:", info.get("CFBundleVersion")) print("可执行文件:", info.get("CFBundleExecutable")) print("最低系统:", info.get("MinimumOSVersion")) print("URL Schemes:", info.get("CFBundleURLTypes", []))

这段脚本我几乎每次做批量分析都会用,把一批 ipa 的Info.plist提取出来,导成表格做横向对比,比一个个点开看快太多。尤其是做应用合规检查,需要确认每个应用申请了哪些权限、用了哪些 Scheme,脚本一把梭最省心。

提示:plistlib.load同时支持二进制和 XML 两种格式,不用提前转换。如果读取报错,先确认文件是不是完整的 plist,有些加固包会把Info.plist做特殊处理,这种情况要考虑换样本。

4.4 从 info.plist 里能挖出什么有价值的信息

Info.plist的价值远不止“看看版本号”。它至少能回答这几类问题:应用的身份(包名、版本、构建号)、应用的能力(后台模式、支持的设备、最低系统)、应用的权限诉求(各种 UsageDescription)、应用的生态连接(URL Scheme、查询白名单)。

举个实际场景:做竞品分析时,我想知道某个应用能不能被其他应用直接唤起、唤起时带了哪些参数,答案就在CFBundleURLTypes里。再比如排查一个诡异的权限弹窗,用户说“这应用为什么要定位”,看它NSLocationWhenInUseUsageDescription的文案和是否真的声明了定位后台模式,就能判断是不是过度申请。

还有一个容易被忽略的点:不同版本Info.plist的 diff,能反映产品策略的变化。我见过某个应用在某次更新后悄悄加了几条LSApplicationQueriesSchemes,说明它新增了唤起其他应用的场景;也有应用在某次更新后改了权限文案,这通常和隐私合规整改有关。盯着这张“身份卡”的变化,比读更新日志还有意思。

5. 常见问题与排查技巧实录

5.1 脱壳失败:从现象反推原因

脱壳失败的现象就那么几种,但原因可能完全不同。我把踩过的坑整理成一张排查表:

现象可能原因排查方向
frida-ps 列不出进程版本不匹配 / 服务端没起对齐电脑端与设备端版本
attach 就闪退应用有反调试换时机、加启动参数、换工具
dump 完成但打不开进程被挂起保持前台、关自动锁屏
dump 后 cryptid 仍为 1dump 了错误目标确认 CFBundleExecutable
生成的 ipa 体积异常小只 dump 了部分段检查脚本输出日志
class-dump 导出为空壳没脱干净重新验证 cryptid

这里说一个最隐蔽的坑:有些应用是多个可执行文件(主程序 + 扩展),你 dump 了主程序,但分析的是扩展里的逻辑。扩展的可执行文件在PlugIns目录下,名字和主程序不一样,脱壳时需要单独处理。我一开始在这上面浪费了不少时间,后来养成习惯,先列全所有 Mach-O 文件再动手。

5.2 plist 解析报错与乱码处理

Info.plist解析最常见的问题是“用文本编辑器打开是乱码”。这十有八九是二进制 plist,用plutil -convert xml1转一下就好。如果转的时候报Unexpected character之类的错误,说明文件本身损坏或者不完整,可能是解压过程出了问题,换个解压工具再试。

还有一种情况是中文乱码。XML plist 的编码声明是 UTF-8,但偶尔会遇到编码声明和实际内容不一致的包,这时候用iconv转一下编码,或者用 Python 读取时显式指定编码。如果是用脚本批量处理,建议统一先做编码检测,避免个别文件中断整个批处理流程。

5.3 一张速查表收尾

把日常分析里最高频的命令和对应场景整理出来,需要的时候直接抄:

目的命令
列 ipa 目录unzip -l app.ipa
看架构和类型file App.app/App
查加密状态otool -l App.app/App | grep -A4 LC_ENCRYPTION_INFO
转 XML plistplutil -convert xml1 Info.plist
打印 plistplutil -p Info.plist
取单个字段PlistBuddy -c "Print :Key" Info.plist
看描述文件security cms -D -i embedded.mobileprovision
查签名codesign -dvvv App.app
dump 目标frida-ps -U
导出头文件class-dump -H App -o headers/

最后分享一个我自己常用的组合技:把“解压 + 转 plist + 提取关键字段”写成一个 shell 脚本,每次拿到新包丢进去跑一遍,输出一张标准化的信息卡。这个习惯帮我省掉了大量重复劳动,也避免了手抖看错字段。分析这件事,能自动化的就别手动,把精力留给真正需要判断的部分。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询