Hotdry.
systems-engineering

macOS 应用 Bundle 剖析:Mach-O、代码签名、沙盒与 XPC 服务集成

深入剖析 macOS .app 包结构,解析 Mach-O 可执行体布局、代码签名验证链、entitlements 权限解析、沙盒配置文件及 launchd/XPC 服务安全集成要点。

macOS 应用以 .app Bundle 形式封装,确保安全执行是其核心设计。每个 Bundle 不仅是资源容器,更是经代码签名与沙盒强化后的安全单元。本文聚焦单一技术点:从目录结构到内核加载的安全链路,结合实用命令与参数,提供可落地审计清单。

Bundle 目录结构剖析

macOS 应用 Bundle 是一个目录,遵循严格约定。核心路径为 Contents/ 下:

  • Info.plist:元数据枢纽,包含 CFBundleExecutable(指向 MacOS/ 可执行体)、CFBundleIdentifier(唯一 ID)、LSMinimumSystemVersion 等。缺失或篡改将触发 Gatekeeper 阻挡。
  • MacOS/:存放主 Mach-O 可执行体,如 MyApp,常为 fat binary 支持 arm64/x86_64。
  • Resources/:图像、nib、lproj 本地化资源。
  • Frameworks/:嵌入动态库,已签名。
  • _CodeSignature/:签名 blob 目录,CodeResources 文件哈希资源树。
  • embedded.provisioning.profile:可选 Provisioning Profile,绑定 Team ID 与 entitlements(macOS 非强制)。

使用 find MyApp.app -type f | head -20 快速树状扫描;plutil -p MyApp.app/Contents/Info.plist 解析 plist,验证 Bundle ID 与签名匹配。

证据:标准 Bundle 规范要求所有嵌套代码(如 Frameworks/*.framework)递归签名,否则 codesign --verify --deep 失败。

Mach-O 可执行体布局

主可执行体为 Mach-O 格式(非 ELF)。用 file MyApp.app/Contents/MacOS/MyApp 确认:"Mach-O universal binary with 2 architectures"。

关键布局:

  • Header:magic=0xFEEDFACF (universal),ncmds 负载命令数。
  • Load Commands:otool -l 显示 LC_SEGMENT_64 (__TEXT, __DATA)、LC_UUID、LC_CODE_SIGNATURE(偏移签名 blob)。
  • Sections:__text(代码)、__cstring(字符串)、__entitlements(嵌入权限 XML,签名后)。

参数:otool -h MyApp 查看 header;otool -l | grep -A5 CODE_SIGNATURE 定位签名负载,offset/size 指向 blob。

落地:开发时用 ld -rpath @executable_path/../Frameworks 嵌入相对路径,避免 DYLD_LIBRARY_PATH 绕过。

代码签名链验证

签名是 CMS/PKCS#7 结构,嵌入 Mach-O blob,经 dyld 加载时验证。链路:Developer ID → Apple WWDR → Root CA。

命令审计:

codesign -dvvv --verbose=4 MyApp.app

输出:Format=bundle with Mach-O,Identifier、TeamIdentifier、Authority=Developer ID Application: XXX (TEAMID),Sealed Resources rules=13 files=XXX。

深验:codesign --verify --deep --strict MyApp.app 检查嵌套;失败常见:资源哈希不匹配或未签名 dylib。

风险:Hardened Runtime (flags=0x10000 (runtime)) 启用 PAC 等防护,codesign -d --entitlements :- MyApp.app dump XML。

Entitlements 解析与沙盒

Entitlements 是签名嵌入的 plist,授予权限如网络 / 文件访问。核心:<key>com.apple.security.app-sandbox</key><true/> 启用沙盒。

Dump:codesign -d --entitlements xml1 MyApp.app/Contents/MacOS/MyApp > ents.xml,查看 com.apple.security.* 键。

沙盒 profile:运行时 sandbox-exec 生成,限制文件到~/Library/Containers/com.bundleid/Data,网络 out-only(无 server)。

监控:log stream --predicate 'subsystem == "com.apple.sandbox"' 捕获 violations,如 denied {file-read-data} /private/var。

参数清单:

  • com.apple.security.network.client: true(出站)
  • com.apple.security.files.user-selected.read-write: true(NSOpenPanel)
  • com.apple.security.temporary-exception.*: 慎用,仅调试。

Launchd/XPC 服务集成

安全执行常涉后台服务。XPC 服务在 Contents/XPCServices/*.xpcservice,子 Bundle 继承父签名。

launchd plist:~/Library/LaunchAgents/com.bundle.service.plist,指定 ProgramArguments、MachServices com.bundle.xpc。

集成:主 app 用 xpc_connection_create 连接,系统校验签名 / 权限。参数:KeepAlive true,RunAtLoad false,避免 DoS。

审计:launchctl print gui/$(id -u)/com.bundle.service 查看状态;服务 Mach-O 同上验证。

安全审计清单(可落地)

  1. 结构完整pkgutil --check-signature MyApp.app 无 warning。
  2. 签名链spctl --assess --verbose MyApp.app → accepted source=Developer ID。
  3. 嵌套代码:逐 Frameworks/Plugins/ 运行 codesign -dvvv。
  4. Entitlements 最小:grep sandbox.xml 无 all-files/unrestricted。
  5. 沙盒测试:sandbox-exec -f ents.sb MyApp.app/Contents/MacOS/MyApp --test,模拟 violations。
  6. 公证xcrun notarytool submit MyApp.dmg --keychain-profile staple ticket。
  7. 回滚:签名失效 → codesign --remove-signature 重签;沙盒违规 → entitlements 精简 + TCC 授权。

参数阈值:签名过期 <90 天警报;violation>5 / 分钟 → 进程 kill。

此链路确保 tamper-proof 执行,适用于自建 / 分发 app。实际如 Safari.app,层层嵌套却无缝。

资料来源

(正文约 1250 字)

查看归档