tee漏洞学习-翻译-2:探索 Qualcomm TrustZone的实现

原文:http://bits-please.blogspot.com/2015/08/exploring-qualcomms-trustzone.html

获取 TrustZone image

从两个不同的位置提取image

  • 从手机设备本身
  • 从google factory image

已经root的Nexus 5设备,image存储在eMMC芯片上,并且eMMC芯片的分区在/dev/block/platform/msm_sdcc.1下,可以通过dd命令进行复制 。

此外,在/dev/block/platform/msm_sdcc.1/by-name分区下,包含tztzb这些有意义的名称:
在这里插入图片描述

tz(TrustZone 的缩写),另一个名为tzb,作为tz映像的备份映像,并且与tz映像相同。

直接从手机内部提取,可能存在两个问题:

  • 尽管 TrustZone 映像存储在 eMMC 芯片上,但“正常世界”很容易无法访问它(通过要求设置系统总线上的 AxPROT 位),或者它的多个部分可能会丢失。
  • 拉取整个分区的数据不会显示有关image真实(逻辑)边界的信息,因此需要一些额外的工作来确定image实际结束的位置。 (实际上,由于“tz”image是 ELF 二进制文件,因此它的大小包含在 ELF 标头中)。

因此,从设备中提取了一个image后,让我们看一下google factory image。

Nexus 5 的出厂镜像均可从 Google 下载。出厂映像包含一个包含所有默认映像的 ZIP,另外还包含引导加载程序映像。KTU84P

下载工厂映像并查找与 TrustZone 相关的字符串后,很快就发现bootloader包含所需的代码。

然而,这里仍然有一个小问题需要解决 - 引导加载程序image的格式未知。无论如何,使用十六进制编辑器打开该文件并猜测其结构实际上非常简单:
在这里插入图片描述

引导加载程序文件具有以下结构:

  • magic值(“BOOTLDR!”)- 8 个字节
  • image数量 - 4 字节
  • 从文件开头到image数据开头的偏移量 - 4 个字节
  • image中包含的数据的总大小 - 4 字节
  • 一个数组,其中包含与上面的“image数量”字段匹配的多个条目。数组中的每个条目都有两个字段:
    • image名称 - 64 字节(零填充)
    • image长度 - 4 字节

正如您在上图中看到的,引导加载程序映像包含一个名为“tz”的映像,这就是我们要查找的映像。为了解压该文件,我编写了一个小型 python 脚本(可在此处获取),该脚本接收引导加载程序映像并解压其中包含的所有文件。

提取图像并将其与之前从设备中提取的image进行比较后,我验证它们确实是相同的。所以我想这意味着我们现在可以继续检查 TrustZone image。

import sys, struct, osdef main():#Reading the commandline argumentsif len(sys.argv) != 3:print "USAGE: %s <BOOTLOADER_IMAGE> <OUTPUT_DIR>" % sys.argv[0]returnbootloader_path = sys.argv[1]output_path = sys.argv[2]#Verifying the magicbootloader_file = open(bootloader_path, 'rb')magic = bootloader_file.read(8)if magic != "BOOTLDR!":print "[-] Read incorrect magic: %s" % magic.encode("hex")returnprint "[+] Read correct magic"#Reading in the metadata blockimage_count,data_start_addr,total_size = struct.unpack("<III", bootloader_file.read(12))print "[+] Found %d images, starting at %08X, total size: %08X" % (image_count, data_start_addr, total_size)image_metadata = []for i in range(0, image_count):image_name = bootloader_file.read(64).strip('\x00')image_len = struct.unpack("<I", bootloader_file.read(4))[0]image_metadata.append((image_name, image_len))print "[+] Images: %s" % str(image_metadata)#Dumping each imagebootloader_file.seek(data_start_addr, 0)for image_name, image_len in image_metadata:print "[+] Dumping %s" % image_namedata = bootloader_file.read(image_len)open(os.path.join(output_path, image_name), 'wb').write(data)print "[+] Done"if __name__ == "__main__":main()

修复 TrustZone 映像

首先,检查该文件发现它实际上是一个 ELF 文件,这是一个好消息!这意味着内存段及其映射地址应该可供我们使用。

用 IDA Pro 打开文件并让自动分析运行一段时间后,我想开始逆向文件。然而,令人惊讶的是,似乎有很多分支指向未映射的地址(或者更确切地说,未包含在“tz”二进制文件中的地址)。

仔细一看,似乎所有指向无效地址的绝对分支都在文件的第一个代码段内,并且它们指向未映射的高地址。此外,第一个代码段的地址没有绝对分支。

这看起来有点可疑…那么我们看一下 ELF 文件的结构怎么样?执行 readelf 会显示以下内容:
在这里插入图片描述

有一个 NULL 段映射到更高的地址,它实际上对应于无效绝对分支指向的地址范围!

不管怎样,我做了一个相当安全的猜测,那就是第一个代码段实际上映射到了错误的地址,实际上应该映射到更高的地址 - 0xFE840000。很自然地,我想使用 IDA 的 rebase 功能对段进行 rebase,但是你瞧!这会导致 IDA 严重崩溃:
在这里插入图片描述

在这里插入图片描述
0xFC58C48地址太低了,不在加载地址范围之内)

我实际上不确定这是否是高通的反逆向功能,或者 NULL 段是否只是其内部构建过程的结果,但这可以通过手动修复 ELF 文件轻松绕过。所需要做的就是将 NULL 段移动到未使用的地址(因为 IDA 无论如何都会忽略它)Type 类型为NULL,没啥用,所以会被忽略,除非专门为tz编写了加载器,并将第一个代码段从错误的地址 (0xFC86000) 移动到正确的地址 (0xFE840000)这个需要自己用IDA打开提取出的tz,稍微看看就能理解,如下所示:
在这里插入图片描述

现在,在 IDA 中加载镜像后,所有绝对分支都有效了!这意味着我们可以继续分析image。

分析 TrustZone image

首先,应该指出的是,TrustZone 映像是一个相当大的 (285.5 KB) 二进制文件,包含相当少量的字符串,并且没有公共文档。此外,TrustZone 系统由完整的内核组成,具有执行应用程序等功能。所以…目前还不清楚我们应该从哪里开始,因为逆向整个二进制文件可能需要太长时间。

由于我们希望从应用程序处理器攻击 TrustZone 内核,因此最大的攻击面可能是安全监视器调用,这些调用使“正常世界”能够与“安全世界”进行交互。

当然,应该指出的是,我们还可以通过其他方式与 TrustZone 进行交互,例如共享内存甚至中断处理,但由于这些攻击面要小得多,因此最好从分析 SMC 调用。

那么我们如何找到 TrustZone 内核处理 SMC 调用的位置呢?首先,我们回想一下,在执行 SMC 调用时,与处理 SVC 调用(即“正常世界”中的常规系统调用)类似,“安全世界”必须注册向量的地址。当遇到这样的指令时,处理器将跳转。

“安全世界”的等效项是MVBAR(监视器向量基地址寄存器),它提供向量的地址,该向量包含“安全世界”中处理器处理的不同事件的处理函数。

正向的MRC/MSR

MRS x0, TTBR0_EL1 // Move TTBR0_EL1 into x0
MSR TTBR0_EL1, x0 // Move x0 into TTBR0_EL1

每个系统寄存器都可看做是一个标号 正向的源码中可以写寄存器名称,编译器认识,但逆向的IDA中只能看到寄存器标号

使用任意一个插件,IDA将会识别系统寄存器
https://github.com/gdelugre/ida-arm-system-highlight
https://github.com/NeatMonster/AMIE

访问 MVBAR 是使用 MRC/MCR 操作码和以下操作数完成的:
在这里插入图片描述

因此,这意味着我们可以简单地在 TrustZone 映像中搜索具有以下操作数的 MCR 操作码,并且我们应该能够找到“监视器向量”。事实上,在 IDA 中搜索操作码会返回以下匹配项:
在这里插入图片描述

正如您所看到的,“开始”符号的地址(顺便说一下,这是唯一导出的符号)被加载到 MVBAR 中
根据ARM文档,Monitor Vector具有以下结构:
在这里插入图片描述

这意味着,如果我们查看前面提到的“开始”符号,我们可以将以下名称分配给该表中的地址:
下图中解析的有问题,Monitor Vector的首地址是0xFE810000
在这里插入图片描述
现在,我们可以分析SMC_VECTOR_HANDLER函数。
实际上,这个函数负责很多任务;

  • 首先,它将所有状态寄存器和返回地址保存在预定义的地址中(在“安全世界”中),
  • 然后,它将堆栈切换到预分配区域(也在“安全世界”中)。
  • 最后,在进行必要的准备之后,它会继续分析用户请求的操作并据此进行操作。

由于发出 SMC 的代码存在于 Linux 内核的高通 MSM 分支中,因此我们可以看一下“正常世界”可以向“安全世界”发出的命令格式。

SMC and SCM(SCM没啥意义,就是高通自己给自己的SMC调用取了个名字)

令人困惑的是,高通选择将“正常世界”通过 SMC 操作码与“安全世界”交互的通道命名为 SCM(安全通道管理器)

无论如何,正如我在上一篇博客文章中提到的,“qseecom”驱动程序用于通过 SCM 与“安全世界”进行通信。

Qualcomm在相关源文件中提供的文档相当丰富,足以很好地掌握SCM命令的格式。

简而言之,SCM 命令分为两类:

  • 常规 SCM call - 参数很的调用方式,通过共享内存进行传参
  • Atomic SCM call - 轻量的调用方式,通过寄存器传参

常规 SCM call - 当需要将信息从“正常世界”传递到“安全世界”时使用这些call,这是为 SCM call提供服务所必需的。
内核填充以下结构:
在这里插入图片描述

TrustZone 内核在为 SCM 调用提供服务后,将响应写回“scm_response”结构:
在这里插入图片描述
为了分配和填充这些结构,内核可以调用包装函数“scm_call”,该函数接收

  • 指向内核空间缓冲区的指针,其中包含要发送的数据、数据应返回的位置
  • 以及最重要的服务标识符和命令标识符。

每个 SCM 调用都有一个类别,这意味着哪个 TrustZone 内核子系统负责处理该调用。这由服务标识符表示。命令标识符是指定在给定服务内请求哪个命令的代码。

在“scm_call”函数分配并填充“scm_command”和“scm_response”缓冲区后,它调用内部“__scm_call”函数刷新所有缓存(内部和外部缓存),并调用“smc”函数。

最后一个函数实际上执行 SMC 操作码,将控制权转移到 TrustZone 内核,如下所示:
在这里插入图片描述
请注意

  • R0 设置为 1
  • R1 设置为指向本地内核堆栈地址,该地址用作该调用的“上下文 ID”
  • R2 设置为指向分配的“scm_command”结构的物理地址。

R0 中设置的这个“神奇”值表明这是一个常规的 SCM 调用,使用“scm_command”结构。然而,对于某些需要较少数据的命令,无缘无故地分配所有这些数据结构将是相当浪费的。为了解决这个问题,引入了另一种形式的 SCM 调用。

Atomic SCM call - 对于参数数量相当低(最多四个参数)的调用,存在另一种请求 SCM 调用的方法。

有四个包装函数“scm_call_atomic_[1-4]”,它们对应于请求的参数数量。可以调用这些函数,以便使用给定的服务和命令 ID 以及给定的参数直接发出 SCM 调用的 SMC。

这是“scm_call_atomic1”函数的代码:
在这里插入图片描述

其中 SCM_ATOMIC 定义为:
在这里插入图片描述
请注意,服务 ID 和命令 ID 以及调用中的参数数量(在本例中为 1)都被编码到 R0 中。这取代了之前用于常规 SCM 调用的“神奇”值 1。
R0 中的这个不同值向 TrustZone 内核表明以下 SCM 调用是原子调用,这意味着参数将使用 R2-R5 传递(而不使用 R2 指向的结构)。

分析 SCM 调用

现在我们了解了 SCM 调用的工作原理,并且已经在 TrustZone 内核中找到了用于处理这些 SCM 调用的处理函数,我们可以开始反汇编 SCM 调用以尝试查找其中之一的漏洞。

我将跳过对 SCM 处理函数的大部分分析,因为其中大部分是用户输入的样板处理等。但是,在将堆栈切换到 TrustZone 区域并保存执行调用的原始寄存器之后,处理函数继续处理服务ID和命令ID,以便查看应该调用哪个内部处理函数。

为了轻松映射服务和命令 ID 以及相关处理函数,静态列表被编译到 TrustZone 映像的数据段中,并由 SCM 处理函数引用。以下是列表中的一小段内容:
在这里插入图片描述
如您所见,该列表具有以下结构:

  • 指向包含 SCM 函数名称的字符串的指针
  • call 类型
  • 指向处理函数的指针
  • 参数数量
  • 每个参数的大小(每个参数一个 DWORD)
  • 服务 ID 和命令 ID 连接成一个 DWORD - 例如,上面的“tz_blow_sw_fuse”函数的类型为 0x2002,这意味着它属于服务 ID 0x20,其命令 ID 为 0x02。

现在剩下的就是开始反汇编这些函数,并希望找到可利用的错误。

The Bug!

因此,在研究了所有上述 SMC 调用(全部 69 个)之后,我终于得到了以下函数:
在这里插入图片描述
通常,当使用常规 SCM 调用机制调用 SCM 命令时,R0 将包含结果地址,该地址指向由内核分配的“scm_response”缓冲区,但也由 TrustZone 内核验证以确保它实际上是“允许”范围内的物理地址 - 即对应于 Linux 内核内存的物理地址,而不是 TrustZone 二进制文件中的内存位置。

此检查是使用内部函数执行的,我将在下一篇博客文章中更详细地介绍该函数。


但是如果我们使用原子 SCM 调用来执行函数会发生什么?在这种情况下,使用的结果地址是原子调用传递的第一个参数。
在这里插入图片描述

现在 - 你能看到上面函数中的错误吗?

与其他 SCM 处理函数相反,该函数没有验证 R0(“结果地址”)中的值,因此如果我们传入:

  • R1为非零值(为了通过第一个分支)(原文有问题,是R0为非0
    在这里插入图片描述

  • 第四个参数(在上面的 var_1C 处传入)非零

    • LDR R0,[SP, #x28+var_1C]
    • CBZ R0, loc_FE84B372
    • 进入最左侧的分支
  • R0 为任何物理地址,包括 TrustZone 地址空间范围内的地址

    • MOVS R6, R0
    • MOVS R1, #0
    • STR R1, [R6]

该函数将到达上面函数中最左边的分支,并在 R0 中包含的地址写入一个零 DWORD

What’s next? 下一步是什么?

在下一篇博文中,我将分享针对上述漏洞的详细(而且相当复杂!)利用,该漏洞可以在 TrustZone 内核中实现完整的代码执行。我还将发布完整的漏洞利用代码,敬请期待!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/254731.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探索Spring Validation:优雅实现后端数据验证的艺术

在现代Web应用开发中&#xff0c;数据验证是一项至关重要的任务&#xff0c;确保应用程序接收到的用户输入符合预期规范&#xff0c;不仅能够提高系统的健壮性&#xff0c;也能有效防止潜在的安全漏洞。Spring Framework通过其内置的Spring Validation模块&#xff0c;为我们提…

极值图论基础

目录 一&#xff0c;普通子图禁图 二&#xff0c;Turan问题 三&#xff0c;Turan定理、Turan图 1&#xff0c;Turan定理 2&#xff0c;Turan图 四&#xff0c;以完全二部图为禁图的Turan问题 1&#xff0c;最大边数的上界 2&#xff0c;最大边数的下界 五&#xff0c;…

Vue3大事件项目(ing)

文章目录 核心内容1.大事件项目介绍2.大事件项目创建3.Eslint配置代码风格4.配置代码检查工作流问题: pnpm lint是全量检查,耗时问题,历史问题 5.目录调整6.vue-router4 路由代码解析7.引入 Element Plus 组件库8.Pinia 构建仓库 和 持久化9.Pinia 仓库统一管理 核心内容 Vue3…

【C/C++ 10】扫雷小游戏

一、题目 写一个扫雷小游戏&#xff0c;每次输入一个坐标&#xff0c;若该处是地雷&#xff0c;则游戏失败&#xff0c;若该处不是地雷&#xff0c;则显示周围地雷数量&#xff0c;若扫除全部非地雷区域&#xff0c;则扫雷成功。 二、算法 设置两张地图&#xff08;二维数组&…

04-Java建造者模式 ( Builder Pattern )

建造者模式 摘要实现范例 建造者模式&#xff08;Builder Pattern&#xff09;使用多个简单的对象一步一步构建成一个复杂的对象 一个Builder 类会一步一步构造最终的对象&#xff0c;该 Builder 类是独立于其他对象的 建造者模式属于创建型模式&#xff0c;它提供了一种创建对…

【Java八股面试系列】并发编程-并发关键字,线程池

目录 并发关键字 Synchronized synchronized最主要的三种使用方式&#xff1a; 具体使用&#xff1a;双重校验锁单例模式 synchronized 底层实现原理&#xff1f; synchronized锁的优化 偏向锁 轻量级锁 重量级锁 Mark Word 与 Monitor 之间的关系 总结 偏向锁、轻量…

Aethir和Well-Link Tech携手革新云游戏,释放人工智能(AI)潜力

​Aethir将为Well-Link Tech的2亿用户提供先进的GPU计算能力&#xff0c;大幅提升他们的游戏体验。 新加坡&#xff0c;2024年2月7日 - 先驱性的去中心化GPU网络Aethir与实时云渲染技术领导者Well-Link Tech携手共创云游戏和元宇宙发展的新时代。 借助Well-Link Tech对领先游戏…

2024年第一篇博客

这是2024年的第一篇博客&#xff0c;2023年笔者经历了一连串的生活、工作、学习上的转折和调整&#xff0c;跌跌撞撞时光飞逝&#xff0c;转眼间就踏着元旦的钟声步入了2024年&#xff0c;前思后想、辗转反侧、犹豫再三不知道从哪里开始博客新的篇章&#xff0c;这个问题坦诚说…

.NET高级面试指南专题六【线程安全】5种方法解决线程安全问题

前言 多线程编程相对于单线程会出现一个特有的问题&#xff0c;就是线程安全的问题。所谓的线程安全&#xff0c;就是如果你的代码所在的进程中有多个线程在同时运行&#xff0c;而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的&#xff0c;而且…

ElementUI Data:Table 表格

ElementUI安装与使用指南 Table 表格 点击下载learnelementuispringboot项目源码 效果图 el-table.vue&#xff08;Table表格&#xff09;页面效果图 项目里el-table.vue代码 <script> export default {name: el_table,data() {return {tableData: …

ubuntu22.04@laptop OpenCV Get Started: 000_hello_opencv

ubuntu22.04laptop OpenCV Get Started: 000_hello_opencv 1. 源由2. Hello OpenCV2.1 C应用Demo2.2 Python应用Demo 3. 参考资料 1. 源由 之前&#xff0c;通过敲门砖已经砸开了OpenCV的大门&#xff0c;接下来是体验下“Hello World&#xff01;”程序。 2. Hello OpenCV …

【01】判断素数/质数(C语言)

目录 &#xff08;1&#xff09;素数特点&#xff1a;只能被1和本身整除 &#xff08;2&#xff09;代码如下&#xff1a; &#xff08;3&#xff09;运行结果如下 ​编辑 &#xff08;4&#xff09;函数引申 &#xff08;1&#xff09;素数特点&#xff1a;只能被1和本身…

每日五道java面试题之java基础篇(四)

第一题. 访问修饰符 public、private、protected、以及不写&#xff08;默认&#xff09;时的区别&#xff1f; Java 中&#xff0c;可以使⽤访问控制符来保护对类、变量、⽅法和构造⽅法的访问。Java ⽀持 4 种不同的访问权限。 default (即默认&#xff0c;什么也不写&…

5-3、S曲线生成器【51单片机+L298N步进电机系列教程】

↑↑↑点击上方【目录】&#xff0c;查看本系列全部文章 摘要&#xff1a;本节介绍步进电机S曲线生成器的计算以及使用 一.计算原理 根据上一节内容&#xff0c;已经计算了一条任意S曲线的函数。在步进电机S曲线加减速的控制中&#xff0c;需要的S曲线如图1所示&#xff0c;横…

containerd中文翻译系列(十九)cri插件

cri插件包含的内容比较多&#xff0c;阅读之前请深呼吸三次、三次、三次。 CRI 插件的架构 本小节介绍了 containerd 的 cri 插件的架构。 该插件是 Kubernetes 容器运行时接口&#xff08;CRI&#xff09; 的实现。Containerd与Kubelet在同一个节点上运行。containerd内部的…

【Rust日报】2024-02-08 Loungy:使用 Rust 和 GPUI 开发的 MacOS 启动器

Mira Screenshare&#xff1a;基于 Rust 和 WebRTC 的高性能屏幕分享工具 一群大学生宣布推出了他们的期末项目&#xff1a;Mira Screenshare&#xff0c;一个开源、高性能的屏幕共享工具&#xff0c;由 Rust 和 WebRTC 构建。此项目支持 4k 60 FPS 和 110ms 端到端延迟的屏幕…

pycharm deployment 灰色 一直无法点击

我的development的配置如下&#xff0c;我看了很多教程一直不知道为什么一直是灰色的&#xff0c; 文件夹配置&#xff1a; 如果你这里 Autodect&#xff0c;那么你Mapping 的文件夹应该是应该省略这个前缀的&#xff0c;例如我下面&#xff0c;我应该将本地文件夹映射到/home…

深度学习入门笔记(九)自编码器

自编码器是一个无监督的应用&#xff0c;它使用反向传播来更新参数&#xff0c;它最终的目标是让输出等于输入。数学上的表达为&#xff0c;f(x) x&#xff0c;f 为自编码器&#xff0c;x 为输入数据。 自编码器会先将输入数据压缩到一个较低维度的特征&#xff0c;然后利用这…

天猫平台数据查询(天猫数据分析):床上用品市场规模达335亿,床品消费呈现功能化趋势

作为人们的生活必需品之一&#xff0c;床上用品的市场规模庞大。近年来&#xff0c;越来越多消费者购物习惯发生转变&#xff0c;线上客流大幅提升&#xff0c;面对这一变化&#xff0c;许多家纺企业开始借助线上平台开展销售&#xff0c;不断创新营销模式&#xff0c;通过短视…

【大厂AI课学习笔记】【1.5 AI技术领域】(8)文本分类

8,9,10&#xff0c;将分别讨论自然语言处理领域的3个重要场景。 自然语言处理&#xff0c;Natual Language Processing&#xff0c;NLP&#xff0c;包括自然语言识别和自然语言生成。 用途是从非结构化的文本数据中&#xff0c;发掘洞见&#xff0c;并访问这些信息&#xff0…