解密 TrollStore:一个 CMS 多重签名漏洞如何击穿 iOS 代码签名体系
目标含义正常情况永久有效App 不会 7 天后过期免费开发者签名 → 7 天过期;企业证书 → 有吊销风险任意权限可以声明任何 entitlements(如no-sandbox只有 App Store 签名的 App 才能获得完整权限重启存活设备重启后 App 不会被 iOS 的图标缓存重建(uicache rebuild)踢掉非系统 App 在 uicache 重建后会丢失"系统"标记这三个目标
1. 一个让你怀疑人生的 Demo
一台从未越狱的 iPhone 13,iOS 15.4.1。将一个从 GitHub 下载的、没有任何开发者证书签名的 IPA 拖进一个叫 TrollStore 的 App 里,3 秒后 App 出现在桌面——点击运行,没有"未受信任的开发者"弹窗,没有 7 天到期倒计时,甚至重启后依然能用。一切像魔法,但魔法的背后只是一个 200 行的 CoreTrust 验证逻辑漏洞。
这就是 TrollStore(巨魔商店)的神奇之处。它不越狱、不需要开发者账号、不依赖企业证书,却能做到永久安装任意 IPA。本文将结合 opa334/TrollStore 的开源源码,从 iOS 代码签名体系讲起,逐层拆解这个漏洞的完整利用链。
2. 背景知识:iOS 怎样判断一个 App 能不能运行
要理解漏洞,先要理解正常流程。iOS 启动任何一个可执行文件前,都必须完成代码签名验证。这套验证链条由三个角色共同完成:
TrustCache(信任缓存):系统内置的一份白名单,包含了所有预装的系统二进制文件哈希。命中 → 直接放行,不走后续流程。
CoreTrust:iOS 11 引入的用户层验证框架。它的职责是验证二进制文件的 CMS(Cryptographic Message Syntax)签名是否来自 Apple 信任的证书链。TrollStore 就卡在了 CoreTrust 验证这一步——不是绕过它,而是骗过它。
AMFI(Apple Mobile File Integrity):内核扩展,负责最终的运行时完整性检查。如果 CoreTrust 说"签名有效",AMFI 就信了。
关键认知:代码签名不是一道墙,而是一条流水线。流水线上任何一个环节的逻辑缺陷,都可能导致整个验证体系被 bypass。
3. 先定义"成功":TrollStore 要达成的三个目标
在分析漏洞之前,先明确攻击者想要什么:
| 目标 | 含义 | 正常情况 |
|---|---|---|
| 永久有效 | App 不会 7 天后过期 | 免费开发者签名 → 7 天过期;企业证书 → 有吊销风险 |
| 任意权限 | 可以声明任何 entitlements(如 get-task-allow、no-sandbox) |
只有 App Store 签名的 App 才能获得完整权限 |
| 重启存活 | 设备重启后 App 不会被 iOS 的图标缓存重建(uicache rebuild)踢掉 | 非系统 App 在 uicache 重建后会丢失"系统"标记 |
这三个目标中,前两个靠 CoreTrust 漏洞实现,第三个靠 Persistence Helper 机制实现。下面逐个拆解。
4. 业界方案横评:绕过代码签名的四种方式
在 TrollStore 出现之前,社区已经尝试过多种绕过签名的方式:
| 方案 | 原理 | 为什么不够 | 是否被 TrollStore 取代 |
|---|---|---|---|
| 免费开发者签名 | 用 Apple ID 签名 → 7 天有效 | 有效期太短,需要频繁重签 | 是——TrollStore 实现永久有效 |
| 企业证书签名 | 用企业证书签名 → 理论永久 | 证书随时被 Apple 吊销,成本高 | 是——TrollStore 不需要证书 |
| 越狱 + AppSync | 内核补丁直接关闭 AMFI 校验 | 需要越狱,系统版本升级即失效 | 否——TrollStore 不需要越狱 |
| AltStore / SideStore | PC 端定期刷新签名 | 依赖电脑或 VPN,7 天后需刷新 | 是——TrollStore 无需外部依赖 |
TrollStore 的独特之处在于:它在不越狱、不依赖证书的前提下,让系统自己相信签名是有效的。
5. 核心漏洞:为什么 “双重身份” 能骗过 CoreTrust
5.1 正常签名 vs “伪签名”
一个标准的 App Store IPA,其二进制文件包含一个 CMS 签名。签名里有两类关键数据:
- CodeDirectory:代码的哈希值集合,SHA1 和 SHA256 各一份
- Signer 证书链:证明"签名人是 Apple"
正常情况下的验证流程:
5.2 漏洞本质:SHA1 vs SHA256 的不一致检查
CoreTrust 漏洞的核心是一个检查时序不一致的问题:
CoreTrust 验证阶段查看的是 SHA1 CodeDirectory(看起来来自 App Store),但执行阶段使用的是 SHA256 CodeDirectory(可以包含任意篡改)。
用一句大白话说:保安查你的 SHA1 身份证觉得你没问题,但你实际用的是 SHA256 身份证大摇大摆进去了。
攻击者构造的二进制文件结构如下:
| 组件 | 内容 | 作用 |
|---|---|---|
| 主 CodeDirectory (SHA256) | get-task-allow、no-sandbox、platform-application 等任意权限 |
← 实际执行时使用的权限 |
| 副 CodeDirectory (SHA1) | 从某个合法 App Store App 抄来的签名 | ← 骗过 CoreTrust 验证 |
| CMS 签名块 | signer 1: Apple(看起来合法) + signer 2: fake(携带任意权限) | ← 多重签名者绕过 |
5.3 用 Apple Wiki 的解释来确认
根据 The Apple Wiki 的记录:
CoreTrust Multiple Signer Validation Vulnerability: iOS incorrectly verifies code signatures when a binary has multiple signers. The system checks the SHA1 CodeDirectory (which looks valid) but actually executes with the SHA256 CodeDirectory (which contains arbitrary entitlements).
而 DeepWiki 的总结更加直白:
iOS does not correctly handle binaries containing multiple signers. It checks the SHA1 CodeDirectory — which looks like it originates from a valid App Store signature — but proceeds to execute using the SHA256 CodeDirectory, where an attacker has injected arbitrary entitlements.
这就是整个 TrollStore 的基石:一个 CMS 签名中塞入多个签名的边界条件处理不当。
6. TrollStore 架构:三层协作的"伪系统"签名工厂
三个组件的分工:
- TrollStore App:用户看到的那个 App,运行在普通权限下。它的所有高权限操作(安装、签名、注册)都通过
spawnRoot()委托给 Root Helper。 - Root Helper(trollstorehelper):一个小型 Mach-O,跑在 root 权限下。它调用
ldid完成伪签名,然后把 App 拷贝到/var/mobile/.TrollStore/并注册为系统应用。 - Persistence Helper:注入到某个系统 App 内(如 Tips.app),负责在 iOS 重建图标缓存后把一切恢复成系统应用的注册状态。
7. 源码深潜:从 IPA 拖入到 App 启动的完整链路
7.1 入口:TSInstallationController
当用户拖入一个 IPA 文件,TrollStore 的 TSInstallationController 开始工作。以下是核心流程(基于源码还原):
// 以下为伪代码,还原自 TrollStore 源码中的 TSInstallationController 核心流程
- (void)installIPA:(NSURL *)ipaURL {
// 1. 解压 IPA,提取 App bundle 和所有二进制文件的路径
NSString *tmpDir = [TSUtil temporaryDirectory];
[TSUtil extractArchiveAtPath:ipaURL.path toPath:tmpDir];
// 2. 遍历 Payload 中的所有 .app bundle
NSString *payloadPath = [tmpDir stringByAppendingPathComponent:@"Payload"];
NSArray *bundles = [[NSFileManager defaultManager]
contentsOfDirectoryAtPath:payloadPath error:nil];
for (NSString *bundleName in bundles) {
if (![bundleName hasSuffix:@".app"]) continue;
NSString *appPath = [payloadPath stringByAppendingPathComponent:bundleName];
// 3. 找到主二进制文件(通常与 .app 同名)
NSString *executableName = [self executableNameForBundle:appPath];
NSString *executablePath = [appPath stringByAppendingPathComponent:executableName];
// 4. 对二进制做 CoreTrust 绕过 —— 这就是魔法发生的地方
[self applyCoreTrustBypass:executablePath];
// 5. 通过 Root Helper 安装到系统目录并注册
[TSUtil spawnRoot:@[executablePath, appPath]];
}
}
7.2 核心:CoreTrust 绕过如何实现
伪签名不是随便改几个字节——它利用了 CMS 签名的结构特性。下面是利用 ldid 实现伪签名的概念流程:
// 以下为伪代码,描述 CoreTrust bypass 的核心逻辑
// 实际在 Root Helper 中通过调用 ldid 完成
- (void)applyCoreTrustBypass:(NSString *)binaryPath {
// Step 1: 准备要注入的权限列表
// TrollStore 安装的 App 需要以下关键权限才能正常运行:
NSDictionary *entitlements = @{
// 允许调试器 attach(高级逆向场景)
@"get-task-allow": @YES,
// 跳出沙盒(部分 App 需要)
@"com.apple.private.security.no-sandbox": @YES,
// 标记为平台应用(绕过某些系统检查)
@"platform-application": @YES,
// 允许运行未签名代码(JIT/动态代码生成)
@"dynamic-codesigning": @YES,
};
// Step 2: 构造 "双 CodeDirectory" 的 CMS 签名
// 原理:ldid 生成两个 CodeDirectory ——
// SHA1 版本:指向来自 App Store 合法签名的内容(用于骗过 CoreTrust 验证)
// SHA256 版本:包含我们自定义的 entitlements(实际运行时被使用)
//
// 关键在于:CoreTrust 验证 SHA1 CD 时发现签名来自 Apple,
// 但 AMFI 执行时却去加载 SHA256 CD 中的权限,
// 这两个 CD 可以不一致 —— 这就是漏洞!
NSTask *ldid = [[NSTask alloc] init];
ldid.launchPath = @"/var/jb/usr/bin/ldid"; // Root Helper 内置的 ldid
// -S 参数:设置 entitlements
// -K 参数:指定用于伪签名的 "签名源"(从某个 App Store 的合法 App 提取的签名)
ldid.arguments = @[
@"-S" + [self entitlementsToString:entitlements],
@"-K" + [self appStoreSignatureTemplate], // 来自合法 App Store App 的签名数据
binaryPath
];
[ldid launch];
[ldid waitUntilExit];
// Step 3: 验证伪签名后的状态
// 此时二进制文件拥有:
// - 一个 "看起来合法" 的 SHA1 CodeDirectory(引用 Apple 签名)
// - 一个 "实际执行" 的 SHA256 CodeDirectory(包含自定义 entitlements)
// - CoreTrust 验证 SHA1 → 通过
// - 系统使用 SHA256 → 获得全部自定义权限
}
为什么必须用 root 权限? ldid 修改的是可执行文件的签名段,这需要改写 Mach-O 的 LC_CODE_SIGNATURE load command,涉及文件元数据操作——普通 App 的沙盒没有权限做这件事。
7.3 持久化:Persistence Helper 的核心逻辑
CoreTrust 绕过能安装"系统应用",但 iOS 会在以下时机重建图标缓存(uicache rebuild),把"系统"标记清除:
- 设备重启后
- SpringBoard 崩溃恢复后
- 系统更新后
一旦标记被清除,App 就无法启动了。Persistence Helper 就是解决这个问题的:
// 以下为伪代码,还原 Persistence Helper 的核心逻辑
@interface PersistenceHelper : NSObject
@end
@implementation PersistenceHelper
// 当用户点击被注入的系统 App(如 Tips)时触发
- (void)applicationDidBecomeActive {
// 1. 检查 TrollStore 的 Root Helper 是否还在
NSString *helperPath = @"/var/containers/Bundle/trollstorehelper/";
if (![[NSFileManager defaultManager] fileExistsAtPath:helperPath]) {
// Root Helper 丢失了(极少见),重新安装 TrollStore
[self reinstallTrollStore];
return;
}
// 2. 遍历所有已安装的 TrollStore 应用
// (列表存储在 /var/mobile/.TrollStore/installed.plist)
NSArray *installedApps = [self loadInstalledAppsList];
for (NSString *appPath in installedApps) {
// 3. 对每个应用重新注册为"系统应用"
// 通过调用 root helper 的 MobileInstallation 接口完成
[TSUtil spawnRoot:@[
@"register",
appPath,
@"--as-system" // 关键参数:以系统应用身份注册
]];
}
// 4. 也重新注册 TrollStore 自身
NSString *tsPath = [[NSBundle mainBundle] bundlePath];
[TSUtil spawnRoot:@[@"register", tsPath, @"--as-system"]];
// 5. 刷新 SpringBoard 图标缓存
[TSUtil spawnRoot:@[@"uicache", @"-a"]];
}
@end
为什么要注入到 Tips(提示)App? 因为这个 App:
- 是系统自带且可移除的(用户可以通过长按删除)
- Apple 允许它被用户通过 App Store 重新下载
- 这样即使在 uicache 重建后,它所在的系统路径依然存在,Helper 代码不会丢
8. 三个让你叹为观止的花活
8.1 "签名源"不是伪造的,是借来的
被问最多的问题:TrollStore 伪造的签名为什么能骗过证书链验证?
真相:它没有伪造证书。ldid -K 参数指定的"签名源",实际上是从设备上某个已经安装的 App Store 合法 App中提取出来的真实 CMS 签名块。这个签名块包含了一条完整的证书链——从 Apple Root CA 到具体的签名证书——这一整个都是真实的。
TrollStore 把这个真实签名块中的 SHA1 CodeDirectory 指向的引用保留了下来,但把 SHA256 CodeDirectory 替换成了自己构造的版本。CoreTrust 验证证书链查的是 SHA1 那条路,发现证书链完全合法→放行。但 AMFI 执行时走的是 SHA256→拿到了自定义权限。
换句话说:TrollStore 借用了一个合法 App 的"身份证明",把自己的代码伪装成了那个 App。
8.2 Persistence Helper 的"变色龙"策略
Persistence Helper 不是偷偷后台运行,而是替换了系统 App 的可执行文件。比如你点了 Tips(提示)App,实际上跑的是 Helper 代码。
Helper 的流程是:
- 启动 → 检测 TrollStore 状态 → 完成注册 → 把控制权交还给原始 Tips
- 整个过程对用户来说,就是 Tips 正常打开了,看不出任何异常
- 但也因此有一个副作用:被注入的系统 App 原本的功能可能会丢失
这是 TrollStore 最巧妙的设计:用 iOS 自身的机制来维护自己——每次用户点开一个系统 App(Helper),系统就自己帮 TrollStore 完成了持久化。
8.3 URL Scheme 替换:用"放大镜"做掩护
TrollStore 1.3 版本做了一个特殊的优化:它替换了系统的 apple-magnifier URL Scheme。
正常情况下:
apple-magnifier:// → 打开系统放大镜
TrollStore 安装后:
apple-magnifier:// → 打开 TrollStore 的 Persistence Helper
为什么要这么做?因为这个 URL Scheme 是系统内置的,永远不会被用户删除,而且大部分 App 的"越狱检测"不会去 Hook 这个看起来与越狱毫不相关的 Scheme。用系统功能做掩护,比直接注册一个 trollstore:// 隐蔽得多。
9. 限制:这张"魔法"的边界在哪里
TrollStore 虽然强大,但并非无所不能:
9.1 版本限制(硬件天花板)
- TrollStore 1.x(首个 CoreTrust bug):支持 iOS 14.0 - 15.4.1
- TrollStore 2.x(多签名者 bug):扩展到 iOS 15.5 - 16.6.1、17.0
- iOS 17.6 / 18.0+:Apple 引入了缓解措施——阻止非 root 进程通过
com.apple.private.persona-mgmt权限生成以 root 运行的进程。这意味着即使有新的 CoreTrust 绕过,Root Helper 也无法启动,整个链条断裂。
9.2 权限限制
| 权限 | 状态 | 原因 |
|---|---|---|
task_for_pid-allow |
✅ 可用 | 允许调试器 attach |
get-task-allow |
✅ 可用 | 允许其他进程获取本进程的 task port |
com.apple.private.security.no-sandbox |
✅ 可用 | 逃出沙盒 |
dynamic-codesigning |
⚠️ iOS 15+ A12+ 不可用 | Apple 在 A12 及以后设备上硬编码禁止了该权限,强行使用会 crash |
com.apple.private.cs.debugger |
⚠️ iOS 15+ A12+ 不可用 | 同上,A12+ 硬编码禁止 |
CS_PLATFORMIZED |
❌ 不可用 | 真正的平台化标志只能由 Apple 签名赋予。TrollStore 无法启动 daemon 或向系统进程注入 |
9.3 架构限制
不是越狱。 TrollStore 应用虽然可以逃出沙盒(有了 no-sandbox),但:
- 无法修改系统文件(没有 rootfs 写权限)
- 无法安装 tweak 注入系统进程(没有
CS_PLATFORMIZED) - 设备重启后如果 Persistence Helper 丢失(比如用户删了被注入的系统 App),需要重新安装
9.4 安全风险
TrollStore 安装的 App 拥有远超普通 App 的权限。如果安装了恶意 IPA,它可以:
- 读取其他 App 的沙盒数据
- 访问设备敏感信息
- 绕过系统的隐私保护
请只安装来源可信的 IPA。
10. 演进时间线:从一个 bug 到一个生态
| 时间 | 事件 | 意义 |
|---|---|---|
| 2022 年初 | Linus Henze 发现首个 CoreTrust 漏洞 (CVE-2022-26766) | 证明了 CoreTrust 存在逻辑缺陷 |
| 2022 年 9 月 | opa334 发布 TrollStore 1.0 | 首个公开可用的永久签名工具 |
| 2022 年底 | Apple 在 iOS 15.5 修复第一个 bug | 验证了漏洞的真实性 |
| 2023 年中 | 第二个 CoreTrust 漏洞被发现 | 多重签名者验证绕过 |
| 2023 年 11 月 | TrollStore 2.0 发布 | 支持 iOS 15.5 - 17.0 |
| 2024 年 | TrollFools 等衍生工具出现 | 生态开始繁荣 |
| 2024 年下半年 | iOS 17.6 / 18.0 引入缓解措施 | TrollStore 3 基本无望 |
11. 未来:TrollStore 3 为什么遥遥无期
三个原因让 TrollStore 3 变得极其困难:
-
CoreTrust 的新缓解措施:iOS 17.6+ 禁止非 root 进程以 root 身份 spawn 子进程。Root Helper 是整个架构的基石,如果 Helper 跑不起来,上面的一切都没有意义。
-
Apple 安全团队的反应速度:CoreTrust 已经不是秘密,Apple 内部一定对这个模块做了全面审计。
-
新漏洞的稀缺性:能在不越狱的前提下获得 root 代码执行的 0day,本身就是安全研究界的顶级成果。同时还需要 CoreTrust 级别的签名绕过——两个漏洞同时存在是可遇不可求的。
但这不代表永久签名这条路被封死了。 Apple 如果开放官方 sideloading(欧盟 DMA 法案已推动了一部分),或者出现新的签名验证绕过路径,类似 TrollStore 的工具仍然可能出现。
12. TrollStore 是安全研究的教科书,不是盗版的通行证
iOS 代码签名体系设计精良,但它和所有安全系统一样——99% 的正确还不够,1% 的边界条件处理失误就足以让整个体系失效。TrollStore 的价值不在于让用户可以免费装 App,而在于它向所有安全研究者展示了一个朴素的真理:检查时序的不一致,比检查逻辑的错误更容易被忽略,也更容易被利用。
参考资料
[1] TrollStore — opa334, https://github.com/opa334/TrollStore
[2] TrollStore — The Apple Wiki, https://theapplewiki.com/wiki/TrollStore
[3] CoreTrust — The Apple Wiki, https://theapplewiki.com/wiki/CoreTrust
[4] ldid — ProcursusTeam, https://github.com/ProcursusTeam/ldid
[5] TrollStore Persistent Helper — DeepWiki, https://deepwiki.com/opa334/TrollStore/3.2-persistence-helper
[6] Getting untethered code execution on iOS 14.8 — Alfie CG, https://alfiecg.uk/2023/02/25/Getting-untethered-code-execution-on-iOS-14.8.html
[7] iOS Security Guide — Apple, https://help.apple.com/pdf/security/en_US/apple-platform-security-guide.pdf
[8] CVE-2022-26766 — NIST National Vulnerability Database, https://nvd.nist.gov/vuln/detail/CVE-2022-26766
[9] AMFI Bypass 原理分析 — 蒸米spark, https://juejin.cn/post/6844904190968332302
[10] iOS Code Signing — Apple Developer Documentation, https://developer.apple.com/documentation/security/code-signing-services
声明:本文仅从技术角度分析 TrollStore 的工作原理,旨在帮助安全研究人员和 iOS 开发者理解代码签名与 CoreTrust 的安全机制。请确保你的行为符合当地法律法规和软件使用条款。
技术交流
对代码签名绕过、CoreTrust 漏洞挖掘或 iOS 逆向安全感兴趣,欢迎一起深入探讨。
读完本文,如果你希望把零散的知识点串成完整的攻防体系,系统学习 《iOS逆向安全从入门到攻防实战》:
| 阶段 | 内容 |
|---|---|
| 入门篇 | 逆向基础、越狱、环境搭建、Hook 入门,动手修改 IDFA / IDFV |
| 基础篇 | MachOView / Hopper / IDA Pro 工具链、Mach-O 格式精讲、脱壳、反编译分析 |
| 中级篇 | Method Swizzling / FishHook / Dobby / Frida 四套 Hook 方案,覆盖 OC 方法、C 函数、符号表、运行时注入 |
| 高级篇 | 非 MonkeyDev 重打包、注入动态库、越狱 Tweak 插件开发、Tweak 与 App 通信 |
| 实战篇 | 虚拟相机(替换摄像头帧 + 预览视频)、虚拟定位(开发者方式 / Hook / 注入三类方案) |
| 防护篇 | Method Swizzling 检测、Got 表 Hook 检测、InlineHook 检测、重打包检测 |
| 设备指纹 | 概念、稳定性实现、抹机不变、篡改三方指纹 |
整套课程攻防双视角,从工具使用讲到工程落地。
开放原子旋武开源社区(简称“旋武社区”)是由开放原子开源基金会孵化及运营的技术社区,致力于在中国推广和发展Rust编程语言生态,推动Rust在操作系统、终端设备、安全技术、基础软件等关键领域的产业落地,构建安全、可靠、高效的软件基础设施。
更多推荐



所有评论(0)