TEESimulator工作原理总览:拦截器如何注入keystore守护进程接管KeyMint流量
【免费下载链接】TEESimulatorSoftware simulation for Android hardware-backed key pairs with key attestation项目地址: https://gitcode.com/gh_mirrors/te/TEESimulator
TEESimulator是一个针对 Android 硬件密钥证明(Key Attestation)的软件模拟模块:它把 AOSP 官方的参考版 KeyMint 可信应用(kmr-ta)注入到系统的 keystore 守护进程中,让指定 App 的密钥走"软件 TEE",而其余所有密钥仍由真实硬件处理。本文将用通俗的方式,完整讲清 TEESimulator 拦截器注入 keystore 守护进程、接管 KeyMint 流量的工作原理。
整体架构:一个大脑 + 一个注射器 + 一个拦截器 + 一个加密引擎
先记住四个角色,后面的内容都围绕它们展开 🧠:
| 角色 | 所在目录 | 职责 |
|---|---|---|
| 控制守护进程 | app/ | "大脑":开机收割真实设备参数、解析配置、驱动注入、通过控制 socket 推送配置 |
注入器inject | injector/ | 独立二进制,用 ptrace 把拦截器 .so 装进正在运行的 keystore 进程 |
| 拦截器(hook 库) | keymint/、keystore/ | 注入后常驻的"流量开关",决定每个请求是模拟还是转发 |
| 软件 TEE(Rust TA) | rust/teesim-km/ | 真正的加密引擎:生成密钥、执行运算、用 keybox 签署证明链 |
一句话概括分工:守护进程负责"配置与调度",拦截器负责"路由",Rust TA 负责"算"。三者之间通过一个 root 专属的 Unix socket 通信(见 common/control.h),拦截器本身从不读任何文件——它是纯粹的"密码与路由引擎"。
启动时序:收割 → 注入 → 推送配置
开机后,模块的 service.sh 会拉起 Kotlin 编写的控制守护进程,然后执行三步流水线:
第 1 步:收割(Harvest)设备真实身份
守护进程先生成一个一次性"牺牲"硬件密钥,从它的证明证书中读出设备的真实参数——verified-boot 状态、补丁级别、OS 版本、安全等级(源码:Harvester.kt)。其中 verified-boot 值会被"冻结":因为它们是 KeyMint 派生密钥加密密钥(KEK)的输入,一旦变化,之前存的密钥将永远无法解密。
第 2 步:注入(Inject)拦截器
Injector.kt 找到守护进程的 PID(Android 12+ 是keystore2,10/11 是keystore),运行打包好的inject二进制执行inject <pid> <lib.so> entry。注入器循环监视 PID——keystore 守护进程会不定期重启,每次重启都会丢掉我们的库,注入器就重新打一针。
第 3 步:推送(Push)配置
控制通道(common/control.cpp)把解析好的"配置文件集合"推给拦截器:调用teesim_cfg_begin→ 逐条teesim_cfg_add_profile→teesim_cfg_commit,commit 时原子切换路由表,切换瞬间之前的配置仍在服务。改配置即时生效,无需重启 🚀。
注入器如何把 .so 装进系统进程
这是整个项目最"硬核"的部分(详见 injector/README.md)。两个难点,两种解法:
- 在别人的进程里调函数:
inject用ptrace附加到 keystore 进程,改写寄存器让目标进程替我们执行函数调用;返回地址被刻意指向一块"可读不可执行"的内存页——函数一返回就触发SIGSEGV,这个"受控的崩溃"正是把控制权交还给我们的信号。 - 绕过受限的加载器命名空间:系统进程拒绝
dlopen任何/data下的路径。inject的解法是让目标进程自己创建一个匿名 Unix socket,再通过SCM_RIGHTS把文件描述符传过去,最后用android_dlopen_ext(fd)从 fd 直接加载 ELF——完全跳过路径检查。
加载成功后,注入器在目标进程中调用库的entry()并退出,守护进程继续"若无其事"地跑着,只是身上多了我们的钩子。
Android 12+:PLT 钩住 AIBinder_transact
Android 12 起,keystore2通过 Binder 与 KeyMint HAL 通信。最直觉的做法是"在 keystore2 解析 KeyMint 服务时换掉 binder"——但来不及了:keystore2在开机时就缓存了 KeyMint 代理,而那时我们还没被注入。
所以 TEESimulator 选择拦截流量本身(keymint/README.md、keymint_hook.cpp):
- 用 LSPlt 对
AIBinder_transact做 PLT 钩子——所有出站的 Binder 调用都从这一个 NDK 函数经过; - 钩子检查两件事:目标 binder 的类描述符是不是
IKeyMintDevice(身份验证),以及事务码是否在模拟清单里(generateKey、begin、deleteKey等); - 命中后,把原请求的参数区"搬"到一个指向本地 binder 的新 parcel 上,重新派发——本地设备是一个
TeesimKeyMintDevice,每个方法自己决定模拟还是转发; - 没命中的调用直接走保存的原始函数
real_transact,对调用方完全透明。
一个巧妙的细节:转发到真实 HAL 时会再次经过AIBinder_transact,若不处理就是无限递归。代码用一个线程局部标志tls_forwarding打断循环——正在转发的线程直接放行,其他线程的 KeyMint 流量照常拦截。
Android 10/11:钩住 libbinder 的 ioctl
旧版keystore守护进程暴露的是IKeystoreService接口(详见 keystore/README.md)。拦截点选在服务端:
- 对
libbinder.so的ioctl做 PLT 钩子,监视驱动送来的每一个BR_TRANSACTION——不管对方调用的是哪个服务方法,我们都能看到; - 属于
keystore目标服务的交易,其目标句柄被改写成指向本地桩(事务码0xdeadbeef); - 桩把手递给进程内处理器(keystore_router.cpp):目标 App 的请求由软件 TA 回答,非目标请求报"不是我的",原样重放到真实服务。
对目标 App,密钥根本不接触真实 Keymaster:处理器自己生成密钥对,attestKey时把密钥连同 App 的 challenge 导入参考 TA,由 TA 发出keybox 签名的证明链——和真实 TEE 的生成方式一致,因此证书在构造上就是自洽的。
模拟还是转发:Profile 与两种模式
配置被组织成配置档(Profile):一命名 bundle,包含 keybox、运行模式、补丁/OS 级别、可选设备身份值,以及一组目标 App(格式见 README.md 与 module/config.default.json)。每个目标 App 只属于一个配置档:
- Generation 模式:密钥完全在软件 TA 中铸造,keybox 直接签署全新证明。TA 发出的密钥 blob 带有路由标记(
TEESIMkm前缀,见 rust/teesim-km/src/ops.rs),后续对它的begin/deleteKey等操作因此能"认主",无需跨调用跟踪句柄。 - Patch 模式(默认):
generateKey先转发给真实 HAL——密钥确实是硬件的——然后只重签证明叶子:把 root of trust 修补为 locked/Verified,用 keybox 重新签名(rust/teesim-km/src/resign.rs)。密钥 blob 原封不动,检测点更少。 - 未列入任何配置档的 App 对模块完全透明,密钥直达真实硬件;没加载 keybox 时拦截器是纯粹的 no-op——配置错误只会让模块"不动作",而不是制造危险。
关键文件索引
| 想深入了解 | 去看 |
|---|---|
| 控制守护进程全流程 | app/README.md |
| 注入器 ptrace 细节 | injector/README.md、injector/utils.cpp |
| Android 12+ 拦截器 | keymint/README.md、keymint/keymint_router.cpp |
| Android 10/11 拦截器 | keystore/README.md、keystore/binder_interceptor.cpp |
| 软件 TA 与 keybox 签名 | rust/teesim-km/README.md、rust/teesim-km/src/attest.rs |
| 控制通道协议 | common/control.h |
小结
TEESimulator 的核心思路可以浓缩为一句话:不换服务,只改流量。守护进程在 keystore 进程内装下一个"路由开关",开关把目标 App 的 KeyMint 请求送进进程内的参考 TA,其余请求原路放行。理解这条主线——收割真值、ptrace 注入、PLT 钩子、按 App 路由、keybox 签名——你就掌握了这个项目的全部工作原理。
【免费下载链接】TEESimulatorSoftware simulation for Android hardware-backed key pairs with key attestation项目地址: https://gitcode.com/gh_mirrors/te/TEESimulator
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考