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
框架验证
⤇ TrollStore 攻击点

AMFI
Apple Mobile File Integrity
内核级完整性守护

应用启动

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-allowno-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"

正常情况下的验证流程:

匹配

CoreTrust 收到二进制

读取 CMS 签名

验证证书链 → Apple Root CA

读取 CodeDirectory 哈希

对比实际代码哈希

允许执行

5.2 漏洞本质:SHA1 vs SHA256 的不一致检查

CoreTrust 漏洞的核心是一个检查时序不一致的问题:

CoreTrust 验证阶段查看的是 SHA1 CodeDirectory(看起来来自 App Store),但执行阶段使用的是 SHA256 CodeDirectory(可以包含任意篡改)。

用一句大白话说:保安查你的 SHA1 身份证觉得你没问题,但你实际用的是 SHA256 身份证大摇大摆进去了。

攻击者构造的二进制文件结构如下:

组件 内容 作用
主 CodeDirectory (SHA256) get-task-allowno-sandboxplatform-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 架构:三层协作的"伪系统"签名工厂

Root 权限层

系统应用层

用户层

spawnRoot

spawnRoot

安装到

TrollStore App
前端界面


IPA 拖入/导入
应用管理/卸载
设置/更新 ldid

Persistence Helper
注入到 Tips/时钟等


uicache 重建后恢复
重注册系统应用标记
紧急恢复 TrollStore

Root Helper (trollstorehelper)
以 root 权限执行


① 解压 IPA
② ldid 伪签名
③ 复制到 /var/
④ 注册为系统应用

/private/.../Bundle/
/var/mobile/.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 的流程是:

  1. 启动 → 检测 TrollStore 状态 → 完成注册 → 把控制权交还给原始 Tips
  2. 整个过程对用户来说,就是 Tips 正常打开了,看不出任何异常
  3. 但也因此有一个副作用:被注入的系统 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 变得极其困难:

  1. CoreTrust 的新缓解措施:iOS 17.6+ 禁止非 root 进程以 root 身份 spawn 子进程。Root Helper 是整个架构的基石,如果 Helper 跑不起来,上面的一切都没有意义。

  2. Apple 安全团队的反应速度:CoreTrust 已经不是秘密,Apple 内部一定对这个模块做了全面审计。

  3. 新漏洞的稀缺性:能在不越狱的前提下获得 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 检测、重打包检测
设备指纹 概念、稳定性实现、抹机不变、篡改三方指纹

整套课程攻防双视角,从工具使用讲到工程落地。

Logo

开放原子旋武开源社区(简称“旋武社区”)是由开放原子开源基金会孵化及运营的技术社区,致力于在中国推广和发展Rust编程语言生态,推动Rust在操作系统、终端设备、安全技术、基础软件等关键领域的产业落地,构建安全、可靠、高效的软件基础设施。

更多推荐