ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

IPA 包脱壳、Mach-O 解析与 Info.plist 信息提取实战

IPA 包脱壳、Mach-O 解析与 Info.plist 信息提取实战 手上要是拿到一个 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文件夹再往下一层才是真正的可执行文件、Frameworks、PlugIns、各种图片音频资源、Info.plist、以及签名相关的_CodeSignature目录。这个结构之所以重要是因为它决定了你后续所有操作的路径。很多新手上来就问“怎么分析一个 ipa”其实第一步永远是先把外壳剥掉看目录。我自己的习惯是先跑一遍unzip -l xxx.ipa只列目录不解压几秒钟就能判断这个包是不是完整、有没有多 target、Frameworks 里挂了哪些第三方 SDK。这一步不用越狱、不用任何特殊工具普通电脑就能做属于零门槛操作。提示有些 ipa 是经过二次打包或者加固工具处理过的内部目录结构会和标准结构有出入比如多出一层壳目录、可执行文件体积异常小、或者Info.plist里CFBundleExecutable指向的名字和实际文件对不上。这种包要格外小心直接解压分析往往得到的是“外壳”真正的逻辑藏在别处。1.2 脱壳到底脱的是什么壳这里的“壳”和 PC 上那些商业加壳工具比如 UPX、VMProtect完全是两码事。iOS 应用从 App Store 下载安装后可执行文件的__TEXT段代码段是被系统层面加密的依靠的是设备绑定的密钥在运行时解密。这个机制让静态分析拿不到明文机器码你直接用otool、Hopper、IDA 去看代码段是一片乱码或者干脆提示加密。所谓脱壳就是把已经加载到内存里、已经被系统解密好的那份代码段 dump 出来重新写回文件得到一个不加密的可执行文件。判断一个二进制是否被加密看 Mach-O 的 Load Commands 里有没有LC_ENCRYPTION_INFO或LC_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.plist、embedded.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-identifier、get-task-allow、keychain-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 成 ipapython3 dump.py -o demo_decrypted.ipa com.example.demo这行命令做的事就是 attach 到目标进程在内存里定位已经解密的__TEXT段把它连同其他段一起重新组装成一个不加密的 Mach-O再打包成 ipa。整个过程通常几十秒到一两分钟取决于应用体积。如果卡在 attach 阶段多半是 Frida 版本不匹配或者应用做了反调试如果 dump 完成后打不开往往是目标进程在 dump 过程中被系统回收了可以先把应用切到前台再试。第四步验证脱壳结果。把生成的 ipa 解压用otool -l看cryptid是不是变成了 0otool -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 Schemestrings Payload/Demo.app/Demo | grep -iE https?:// | sort -u | head -30这两步能快速给你一张“地图”告诉你应用大概用了哪些框架、和哪些服务器通信。有了地图再去反汇编里定位具体方法比漫无目的地翻快得多。脱壳不是终点是一整套分析流程的起点。4. info.plist 文件深度解析4.1 两种形态二进制和 XML 的转换Info.plist有两种存储形态二进制 plistbplist和 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 是 iPhone2 是 iPadCFBundleURLTypesURL Scheme 注册分析应用间跳转、深层链接LSApplicationQueriesSchemes可查询的 Scheme 白名单判断它能唤起哪些其他应用NSAppTransportSecurity网络传输安全策略是否允许明文 HTTPUIBackgroundModes后台运行能力定位、音频、下载等NSxxxUsageDescription权限用途说明隐私相关逐个权限对应一条这张表里我分析时最看重的是CFBundleURLTypes和LSApplicationQueriesSchemes这两组。它们像是应用的“社交关系网”前者是别人怎么唤起我后者是我能唤起谁。通过 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.appdump 目标frida-ps -U导出头文件class-dump -H App -o headers/最后分享一个我自己常用的组合技把“解压 转 plist 提取关键字段”写成一个 shell 脚本每次拿到新包丢进去跑一遍输出一张标准化的信息卡。这个习惯帮我省掉了大量重复劳动也避免了手抖看错字段。分析这件事能自动化的就别手动把精力留给真正需要判断的部分。
返回列表