.pkg文件本质是xar归档,需用xar命令解压而非zip/tar;解析关键为先解xar再处理内部结构,推荐system("xar -x -f package.pkg -c /tmp/pkg-unpack/")并校验路径安全。

macOS的.pkg文件本质是XAR归档,不是标准ZIP或TAR
直接用libzip或tar命令解压.pkg会失败——报错invalid header或Unknown archive format。因为苹果自2007年起把安装包底层换成了xar(eXtensible ARchiver),它用XML描述结构、支持校验和与签名,且可嵌套多层(如Archive.pax.gz或PackageInfo)。
解析关键在于:先解xar,再按内部路径处理子内容。C++没有原生xar支持,必须调用系统工具或集成libxar。
用system()调用xar命令是最简单可靠的方案
macOS系统自带/usr/bin/xar,无需额外依赖,稳定性远高于自己编译libxar或逆向解析二进制格式。尤其对带签名、加密或增量更新的.pkg(如Apple官方更新包),第三方库极易漏掉signature、resource-fork等特殊节点。
实操建议:
- 用
xar -x -f package.pkg -C /tmp/pkg-unpack/解出顶层结构;注意加-C指定输出目录,避免路径遍历风险 - 解压后检查
/tmp/pkg-unpack/PackageInfo(XML描述安装逻辑)和/tmp/pkg-unpack/Scripts(预/后置脚本) - 若需读取Payload(实际文件),它通常是
payload或Resources子目录下的Archive.pax.gz,要用gunzip+pax二次解包 - 不要用
std::system()拼接用户输入的.pkg路径——必须先realpath()并过滤..和空格,否则可能执行任意命令
libxar C API能绕过shell但维护成本高
如果硬性要求纯C++不调外部命令,可链接libxar(macOS SDK自带,头文件在/usr/include/xar.h)。但它暴露的是C接口,无RAII封装,错误处理琐碎,且不处理Payload内部的PAX/GZIP嵌套。
典型坑点:
-
xar_open()返回xar_t指针,但xar_close()不保证释放所有内存,多次调用易泄漏 - 遍历文件用
xar_iter_new()+xar_file_iter_next(),但xar_prop_get()取type属性时,值可能是"file"、"directory"或"symlink",没文档说明全集 - 读取文件内容必须手动调
xar_extract()到临时路径,不能直接获取内存buffer——对大Payload极不友好 - macOS 13+对带公证签名的.pkg,
libxar可能无法验证签名字段,返回XAR_ERR_ARCHIVE却不提示原因
真正需要解析.pkg时,通常只关心PackageInfo和Payload结构
95%的自动化场景(如静默安装检测、内容审计、打包工具链)只需提取两样东西:PackageInfo里的<install-location></install-location>和<payload></payload>的SHA256;以及Payload中./Applications/下是否含可执行文件。没必要完整实现xar协议解析。
更务实的做法:
- 用
xar -t -f pkg.pkg | grep -E "(PackageInfo|payload$)"快速定位关键路径 - 用
xar -x -f pkg.pkg PackageInfo -C /tmp/单独解出XML,再用pugixml或tinyxml2解析 - 对Payload,优先尝试
gunzip -t /tmp/pkg-unpack/payload判断是否gzip,再决定用pax -r -f还是cpio -i - 永远假设.pkg可能被重命名(如
app-installer.zip但实际是xar),先用file pkg.pkg确认魔数0x78617221("xar!")
复杂点不在C++语法,而在macOS安装包本身的设计弹性:一个.pkg可以不含Payload(纯脚本)、可以分卷、可以引用网络资源。别试图“完全解析”,先明确你到底要哪一层信息。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











