资讯动态

TEESimulator注入技术深度解析:ptrace远程加载从SCM_RIGHTS传fd到信号返回的完整过程

发布时间:2026/10/2 23:53:06 来源:尧图企业网站定制
TEESimulator注入技术深度解析ptrace远程加载从SCM_RIGHTS传fd到信号返回的完整过程【免费下载链接】TEESimulatorSoftware simulation for Android hardware-backed key pairs with key attestation项目地址: https://gitcode.com/gh_mirrors/te/TEESimulatorTEESimulator 是一款针对 Android 硬件级密钥认证Key Attestation的软件模拟器它通过一个基于 ptrace 的注入工具inject把拦截库远程加载进系统的 keystore 守护进程从而在进程内部接管 KeyMint 请求。本文面向想搞懂 Android 进程注入原理的读者完整拆解这条链路从PTRACE_ATTACH附加进程、用SCM_RIGHTS跨进程传递文件描述符fd到利用受控返回地址触发信号、把远程调用的结果接回手里。一、为什么 TEESimulator 必须做 ptrace 注入TEESimulator 的拦截逻辑必须运行在 keystore 守护进程Android 12 的keystore210/11 的keystore自己的地址空间里才能挂钩它的 Binder 调用。但这类系统服务有两个护身符让常规手段全部失效无法重编译它是系统预置二进制你改不了它的代码。受限的链接器命名空间动态链接器会拒绝dlopen任何/data路径下的库——命名空间允许的搜索路径里根本不含/data检查发生在打开文件之前文件权限再大也没用。所以 TEESimulator 选择最原始的路子用 injector/main.cpp 中的inject工具ptrace附加到正在运行的守护进程替它调用dlopen加载我们的.so再调用库导出的entry()完成挂钩最后脱离现场——守护进程照常运行仿佛什么都没发生。二、注入前的三步准备附加、备份寄存器、让出红区整个注入由 injector/main.cpp 中的inject_library编排开头用 RAII 对象把顺序依赖写成了真正的控制流PTRACE_ATTACH附加目标进程收到SIGSTOP停下注入器用waitpid确认它确实停下了injector/utils.cpp 的wait_for_trace还会处理EINTR重试。备份全部寄存器get_regs拍一张 CPU 寄存器的快照存入backup_regs由RegisterRestorer负责在作用域退出时原样写回——目标进程会在被中断的那条指令上精确续跑。x86_64 额外下探 128 字节跳过被中断函数可能仍在使用的红区red zone避免往栈上写数据时踩坏现场。随后lsplt::MapInfo::Scan解析/proc/pid/maps得到目标进程中libc.so等模块的基址。所有远程函数地址都不直接写死而是用 injector/utils.cpp 的find_func_addr解决在本地dlopen同一个libc.so拿到符号偏移再加到目标进程中的模块基址上——ASLR 随机化的是加载基址不随机化库内部布局所以偏移量在两个进程里完全一致。三、SCM_RIGHTS 传 fd为什么递文件句柄是整个方案的灵魂既然不能把库路径递给keystore2那就换个思路不递路径递一个已经打开的文件描述符。Android 提供了android_dlopen_extANDROID_DLEXT_USE_LIBRARY_FD组合——给链接器一个 fd它就直接从 fd 加载 ELF彻底绕开路径与命名空间检查。坑在于fd 必须有效于目标进程的文件表。这正是SCM_RIGHTS的用武之地它是 Linux 内核原生的 fd 传递机制通过 Unix 域套接字的控制消息cmsg把发送方打开的文件在内核里复制一份到接收方的 fd 表中。injector/main.cpp 中transfer_fd_to_remote的完整舞蹈如下步骤动作关键点①远程调用socket(AF_UNIX, SOCK_DGRAM)套接字由目标进程自己创建活在 keystore 自己的 SELinux 域里传输才合法②生成 16 字符随机 magicbind 到抽象地址抽象 socket 没有文件系统节点不存在目录被 SELinux 拒绝穿越的问题③push_memory把cmsghdr缓冲区和msghdr压到目标栈上栈空间由REG_SP下移腾出16 字节对齐④remote_pre_call发起recvmsg(fd, …, MSG_WAITALL)目标进程阻塞在 recvmsg 内部等数据报到达⑤注入器本地sendmsg附带库文件的 fdSCM_RIGHTS内核在目标进程 fd 表中安装副本⑥remote_post_call收尾read_proc读回控制缓冲区解析 cmsg得到目标进程中那个 fd 的编号最后remote_dlopen拿这个编号配上ANDROID_DLEXT_USE_LIBRARY_FD远程调用android_dlopen_ext完成加载。用完的 fd 由RemoteLibraryHandle析构时远程调用close清理防止在守护进程里泄漏。四、信号返回用一次受控崩溃换回函数结果远程调用remote_callinjector/utils.cpp是整篇文章最值得细品的部分它是所有远程动作的原语在别的进程里调用任意函数并取回返回值。做法是改写目标寄存器参数写入 ABI 约定的参数寄存器x86_64 的rdi/rsi/…arm64 的x0–x7超出部分压栈指令指针REG_IP指向目标函数然后PTRACE_CONT放它跑。最关键的一步是设置一个我们控制的返回地址find_module_return_addr扫描目标 maps返回libc.so中一段可读但不可执行页面的起始地址。x86_64 把它压栈模拟callarm64 则装入链接寄存器x30。当远程函数执行完、跳回这个地址时CPU 试图从不可执行页取指触发SIGSEGV——故障地址恰好等于我们设置的返回地址。这个信号不是错误而是约定的完成信号waitpid因之返回remote_post_call校验REG_IP 期望返回地址任何别的停点都说明函数在真实位置崩了此时取PTRACE_GETSIGINFO记录崩溃细节并放弃最后从返回寄存器REG_RETrax/x0里直接读出函数结果。一句话总结这个技巧页面必须存在但跑不了因为故障才是信号而不是事故。remote_pre_call/remote_post_call拆成两半的用武之地正是上文第④步——让目标停在recvmsg内部注入器从外部完成sendmsg再回来收尾。五、Staging 兜底与 entry() 契约如果 fd 传递失败比如 seccomp 过滤了recvmsginjector/main.cpp 会走inject_via_staging兜底把库拷到/data/local/tmp/lib随机8位.so、chmod 0644、远程调用普通dlopen随后ScopedFileDeleter立即unlink文件——inode 因目标已映射而继续存活文件却从磁盘上消失。注意它只在命名空间宽松的场景有效Android 10/11 的keystore可以keystore2不行后者只能靠 fd 传递。加载成功后remote_find_entry在目标里dlsym出entry符号并远程调用参数就是dlopen句柄。两个值得记住的细节从不dlclose库常驻目标进程钩子才持续有效。注入成功≠钩子生效remote_call_entry只要调用完成就返回 true。所以守护进程会另行验证——等拦截库通过控制 socket/data/misc/keystore/.teesim-ctl藏在 keystore 自己的 0700 数据目录里报到见 Const.kt 与 module/sepolicy.rule 中对应 SELinux 授权长时间不到报就打日志告警提示查avc: denied。六、控制面守护进程如何驱动注入循环Kotlin 侧的 Injector.kt 把一切串起来启动后开线程轮询/proc找 keystore 进程名对应的 pid等 Binder 服务注册完毕避免注入半初始化的进程然后执行inject pid lib.so entry。keystore 守护进程会频繁重启每次重启都会丢掉我们的库所以循环监听 pid 变化并重新注入连续失败 3 次后把退避从 2 秒拉长到 10 秒避免死磕一个卡死的服务。SELinux 层面module/sepolicy.rule 授予crash_dump域对 keystore 的process *权限即 ptrace 附加并允许 keystore 读取/data/adbadb_data_file下的拦截库文件——enforcing 策略的设备上这两条缺一不可。七、关键文件索引路径作用injector/main.cpp注入总编排transfer_fd_to_remote、remote_dlopen、inject_via_staging与 RAII 守卫injector/utils.cppptrace 原语寄存器读写、process_vm_readv内存搬运、remote_call三件套injector/include/utils.hpp各架构寄存器宏REG_SP/REG_IP/REG_RET与 API 声明injector/README.md注入器设计文档fd 传递六步舞、staging 兜底、entry 契约app/src/main/java/org/matrix/teesim/Injector.kt控制面注入循环找 pid、判服务就绪、重注入与确认module/sepolicy.ruleptrace 附加与库文件读取的 SELinux 授权CMakeLists.txtinject目标的构建定义按 ABI 交叉编译八、写在最后TEESimulator 的注入器把操作系统教材里的几块拼图——ptrace寄存器操控、process_vm_readv跨进程内存访问、SCM_RIGHTSfd 传递、不可执行页故障——组装成了一条完整可用的远程dlopen流水线。理解它等于同时理解了 Android 链接器命名空间的限制与绕过方式、以及用故障当信号这一类精巧的系统编程技巧。若想动手复现建议从 injector/README.md 开始再对照 injector/main.cpp 顶部的 ASCII 流程图逐段走读源码。【免费下载链接】TEESimulatorSoftware simulation for Android hardware-backed key pairs with key attestation项目地址: https://gitcode.com/gh_mirrors/te/TEESimulator创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑