optee UTA加载

流程

在这里插入图片描述

动态TA按照存储位置的不同分为REE filesystem TA:存放在REE侧文件系统里的TA;
Early TA:被嵌入到optee os里的在supplicant启动之前就可用了。
这里我们讲的是常规的存放在REE侧文件系统里的TA。

通过GP标准调用的与TA通信的命令(opensession\invoke\closession)其实都是std smc call,该smc调用后,会进入到TEE中的tee_entry_std中。
在__tee_entry_std函数中调用就是opensession\invoke\closession的接口,调用到entry_open_session接口

  1. 动态TA最终会在tee_ta_init_user_ta_session函数里加载。
    tee_ta_init_user_ta_session里调用ldelf_load_ldelf函数,其实现大致为:
  2. 在代码编译的时候,optee-os下的ldelf目录下编译出ldelf.elf二进制文件,通过gen_ldelf_hex.py脚本将ldelf.elf二进制文件生成ldelf_hex.c文件,其二进制数据会被写入数组ldelf_data,还有代码段,数据段的大小
  3. ldelf_load_ldelf函数中将ldelf_data加载到内存中
    再通过ldelf_init_with_ldelf函数调用thread_enter_user_mode函数切换到用户模式el0,运行ldelf的代码
  • PTA最终tee_ta_init_pseudo_ta_session函数里加载

ldelf

elf格式的文件是一种可执行文件,ldelf就是加载elf文件的加载器。
而TA文件就是加了头的elf文件,所以ldelf其作用为load需要加载的TA。
调用ldelf → ta_elf_load_main → load_main加载TA的二进制文件。

optee_os\ldelf\start_a64.S

https://blog.csdn.net/qq_42878531/article/details/121783732

init_elf

验签、解密、加载TA二进制文件,并判断其是否是ELF格式。
sys_open_ta_bin
系统调用 ldelf_syscall_open_bin 函数,其功能是调用systemPTA,找到并打开TA文件,并返回句柄。
此函数会遍历注册的TA_STORE,调用open等操作函数。注册的是下面这个。

TEE_TA_REGISTER_TA_STORE(9) = {
	.description = "REE",
	.open = ree_fs_ta_open,
	.get_size = ree_fs_ta_get_size,
	.get_tag = ree_fs_ta_get_tag,
	.read = ree_fs_ta_read,
	.close = ree_fs_ta_close,
};

ree_fs_ta_open

  1. 先调用rpc_load:通过rpc调用ree侧的tee_supplicant ,传入uuid,ree侧将uuid对应的TA二进制文件加载到共享内存中,在rpc_load中包含两次TA加载的rpc请求,第一次参数里只有UUID,因为此时optee-os不知道加载的TA的image大小,tee supplicant接收到TA加载请求后由于解析出参数的image size为0,所以其通过UUID读取TA文件后计算出ta_size,发现传入大小不足,所以此时没有加载TA 并返回需要传入的size给到optee-os的rpc_load()
  2. 其得到size后通过rpc请求size大小的共享内存并得到地址,此时将uuid、共享内存地址、size传入作为第二次rpc加载TA请求,tee-suplicant收到后加载TA到对应的共享内存中。
/* Request TA from tee-supplicant */
res = rpc_load(uuid, &ta, &ta_size, &mobj);
shdr_alloc_and_copy

通过rpc_load()之后得到的TA加载的共享内存地址,因为TA镜像在签名时增加签名相关的头部文件shdr,在此函数中计算shdr的大小,struct shdr + hash_size + sig_size。然后申请此大小的内存并将TA镜像的shdr拷贝到申请的内存里。用于后面的验签流程。

/* Make secure copy of signed header */
shdr = shdr_alloc_and_copy(ta, ta_size);
shdr_verify_signature

执行验签工作。传入镜像头部文件shdr指针。shdr包括了magic、img_type、img_size、algo等参数。先检验代码中的magic与shdr中的是否一致,从前面提到的ta_pub_key.c中读取ta_pub_key_modulus,提取出pub_key,从shdr中提取hash和sig,使用rsa算法验签此shdr。

/* Validate header signature */
res = shdr_verify_signature(shdr);

然后在ree_fs_ta_open()里还要取出TA镜像里的uuid与传入的uuid比较,并初始化hash_ctx。

sys_map_ta_bin

ldelf_syscall_map_bin,其功能是调用systemPTA,映射TA二进制文件的各种segment到安全内存中。
res = sys_map_ta_bin(&va, SMALL_PAGE_SIZE, flags, elf->handle, 0, 0, 0);
在init_elf中,此处是第一次调用,传入的长度是0x1000,最小页大小。目的为先将TA的从0到0x1000的数据映射到安全内存。主要作用先申请块安全内存,然后映射其地址

struct fobj *f = fobj_ta_mem_alloc(num_pages);
mobj = mobj_with_fobj_alloc(f, file);
res = vm_map_pad(uctx, va, num_rounded_bytes,
                 TEE_MATTR_PRW, vm_flags, mobj, 0,
                 pad_begin, pad_end, 0);

经过这几个步骤,其就会申请了大小为num_pages个pages的空间,并映射到va_range的地址空间里,调试出的信息就是申请的是phys安全内存物理地址,变为了va的虚拟地址(fobj_ta_mem_alloc)。
在这里插入图片描述

optee启动时已划分好
在这里插入图片描述

binh_copy_to

然后调用binh_copy_to函数,此函数调用注册的ree_fs_ta_read,将共享内存中的TA镜像加载到安全内存中。

/*此函数是根据binh句柄,其中记录了当前读取ELF文件的偏移,文件总大小的信息,
  来从offs_bytes在文件中的偏移,读取num_bytes长度的数据到va这个地址,其
  调用注册的read接口,也就是ree_fs_ta_read
  */
static TEE_Result binh_copy_to(struct bin_handle *binh, vaddr_t va,
			       size_t offs_bytes, size_t num_bytes)
{
	TEE_Result res = TEE_SUCCESS;
	size_t next_offs = 0;

	if (offs_bytes < binh->offs_bytes)
		return TEE_ERROR_BAD_STATE;

	if (ADD_OVERFLOW(offs_bytes, num_bytes, &next_offs))
		return TEE_ERROR_BAD_PARAMETERS;

	if (offs_bytes > binh->offs_bytes) {
		/*此处需要读取文件的偏移已经大于了文件所处的偏移,因为ELF文件中的segment
          之间存在空的部分,如果不读取中见的信息,那么ree_fs_ta_read里更新
          hash_ctx就会出问题,导致校验hash不过,所以传入空的地址给read函数,
          只是为了读取空的部分更新hash_ctx,并不加载*/
		res = binh->op->read(binh->h, NULL,
				     offs_bytes - binh->offs_bytes);
		if (res)
			return res;
		binh->offs_bytes = offs_bytes;
	}

	if (next_offs > binh->size_bytes) {
		/*这种情况就是需要读取的部分已经超过了文件的范围,所以此时直接将文件
          剩下的全部读取*/
		size_t rb = binh->size_bytes - binh->offs_bytes;

		res = binh->op->read(binh->h, (void *)va, rb);
		if (res)
			return res;
		memset((uint8_t *)va + rb, 0, num_bytes - rb);
		binh->offs_bytes = binh->size_bytes;
	} else {
		res = binh->op->read(binh->h, (void *)va, num_bytes);
		if (res)
			return res;
		binh->offs_bytes = next_offs;
	}

	return TEE_SUCCESS;
}
ree_fs_ta_read
static TEE_Result ree_fs_ta_read(struct user_ta_store_handle *h, void *data,
				 size_t len)
{
	struct ree_fs_ta_handle *handle = (struct ree_fs_ta_handle *)h;
	/*获取TA镜像除去头之后的elf格式所在位置*/
	uint8_t *src = (uint8_t *)handle->nw_ta + handle->offs;
	size_t next_offs = 0;
	uint8_t *dst = src;
	TEE_Result res = TEE_SUCCESS;

	if (ADD_OVERFLOW(handle->offs, len, &next_offs) ||
	    next_offs > handle->nw_ta_size)
		return TEE_ERROR_BAD_PARAMETERS;
	/*判断TA类型*/
	if (handle->shdr->img_type == SHDR_ENCRYPTED_TA) {
		if (data) {
			dst = data; /* Hash secure buffer */
			/*解密len长度的数据到安全内存中,加载多长的数据就解密多长的数据*/
			res = tee_ta_decrypt_update(handle->enc_ctx, dst, src,
						    len);
			if (res != TEE_SUCCESS)
				return TEE_ERROR_SECURITY;
		} else {
			size_t num_bytes = 0;
			size_t b_size = MIN(1024U, len);
			uint8_t *b = malloc(b_size);

			if (!b)
				return TEE_ERROR_OUT_OF_MEMORY;

			dst = NULL;
			while (num_bytes < len) {
				size_t n = MIN(b_size, len - num_bytes);

				res = tee_ta_decrypt_update(handle->enc_ctx, b,
							    src + num_bytes, n);
				if (res)
					break;
				num_bytes += n;

				res = crypto_hash_update(handle->hash_ctx, b,
							 n);
				if (res)
					break;
			}

			free(b);
			if (res != TEE_SUCCESS)
				return TEE_ERROR_SECURITY;
		}
	} else if (data) { /*不是加密的TA则直接拷贝*/
		dst = data; /* Hash secure buffer (shm might be modified) */
		memcpy(dst, src, len);
	}

	if (dst) {
		/*更新hash_ctx,以便后面计算出TA的hash值使用*/
		res = crypto_hash_update(handle->hash_ctx, dst, len);
		if (res != TEE_SUCCESS)
			return TEE_ERROR_SECURITY;
	}

	handle->offs = next_offs;/*将句柄的偏移更新*/
    /*判断当前是否已经加载了所有的segment*/
	if (handle->offs == handle->nw_ta_size) {
		if (handle->shdr->img_type == SHDR_ENCRYPTED_TA) {
			/*
			 * Last read: time to finalize authenticated
			 * decryption.
			 */
			res = tee_ta_decrypt_final(handle->enc_ctx,
						   handle->ehdr, NULL, NULL, 0);
			if (res != TEE_SUCCESS)
				return TEE_ERROR_SECURITY;
		}
		/*
		 * Last read: time to check if our digest matches the expected
		 * one (from the signed header)
		 */
		 /*加载所有segment之后进行hash校验与shdr里的比较*/
		res = check_digest(handle);
		if (res != TEE_SUCCESS)
			return res;

		if (handle->bs_hdr)
			res = check_update_version(handle->bs_hdr);
	}
	return res;
}

继续执行init_elf

由此时传入的参数得知,此次的sys_map_ta_bin只是将TA的elf文件的头映射到了内存中。
有了头就可以根据elf头判断其基本信息了。
完成这些之后,程序会解析判断其elf格式

if (!IS_ELF(*(Elf32_Ehdr *)va))/*根据elf head magic判断*/
		err(TEE_ERROR_BAD_FORMAT, "TA is not an ELF");

	res = e32_parse_ehdr(elf, (void *)va);
	if (res == TEE_ERROR_BAD_FORMAT)
		res = e64_parse_ehdr(elf, (void *)va);/*解析elf head到elf结构体*/
	if (res)
		err(res, "Cannot parse ELF");

	if (MUL_OVERFLOW(elf->e_phnum, elf->e_phentsize, &sz) ||
	    ADD_OVERFLOW(sz, elf->e_phoff, &sz))
		err(TEE_ERROR_BAD_FORMAT, "Program headers size overflow");

	if (sz > SMALL_PAGE_SIZE)
		err(TEE_ERROR_NOT_SUPPORTED, "Cannot read program headers");

	elf->phdr = (void *)(va + elf->e_phoff);

ELF格式的文件加载到安全内存之后,加载器会判断其是否是ELF格式的文件,以及头是否符合elf规范。
ELF格式分两种,32位和64位,基本一样。格式的基本信息包含在ELF的头里,这个是通用的,使用此结构体表示。

/*
 * ELF header.
 */
typedef struct {
	unsigned char	e_ident[EI_NIDENT];	/* File identification. */
	Elf64_Half	e_type;		/* File type. */
	Elf64_Half	e_machine;	/* Machine architecture. */
	Elf64_Word	e_version;	/* ELF format version. */
	Elf64_Addr	e_entry;	/* Entry point. */
	Elf64_Off	e_phoff;	/* Program header file offset. */
	Elf64_Off	e_shoff;	/* Section header file offset. */
	Elf64_Word	e_flags;	/* Architecture-specific flags. */
	Elf64_Half	e_ehsize;	/* Size of ELF header in bytes. */
	Elf64_Half	e_phentsize;	/* Size of program header entry. */
	Elf64_Half	e_phnum;	/* Number of program header entries. */
	Elf64_Half	e_shentsize;	/* Size of section header entry. */
	Elf64_Half	e_shnum;	/* Number of section header entries. */
	Elf64_Half	e_shstrndx;	/* Section name strings section. */
} Elf64_Ehdr;
elf head头后面就是程序头表program segment header,位置就是ELF头里的e_phnum值在的偏移。记录了每个Segment的相关信息,比如类型、对应文件的偏移、大小、属性等。用下面的结构体表示。
typedef struct {
    Elf64_Word  p_type;     /* Entry type. */
    Elf64_Word  p_flags;    /* Access permission flags. */
    Elf64_Off   p_offset;   /* File offset of contents. */
    Elf64_Addr  p_vaddr;    /* Virtual address in memory image. */
    Elf64_Addr  p_paddr;    /* Physical address (not used). */
    Elf64_Xword p_filesz;   /* Size of contents in file. */
    Elf64_Xword p_memsz;    /* Size of contents in memory. */
    Elf64_Xword p_align;    /* Alignment in memory and file. */
} Elf64_Phdr;

optee example的一个TA的elf读出其program header:
aarch64-linux-gnu-readelf -l xxx.elf
在这里插入图片描述

init_elf函数结束

总结:找到TA文件,验签成功则解密0x1000头部数据,加载0x1000字节到内存,判断其是否是elf以及是否合法。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Git】.gitignore 的匹配规则

每行一个规则&#xff1a;每行只能包含一个规则&#xff0c;多个规则需要分别写在不同的行上。 示例&#xff1a; # 忽略日志文件 logs/ # 忽略临时文件 temp.txt种类匹配&#xff1a; 文件&#xff1a;在规则的开头指定文件名或路径&#xff0c;如 file.txt。 示例&#xff1a…

openGauss学习笔记-220 openGauss性能调优-确定性能调优范围-查询最耗性能的SQL

文章目录 openGauss学习笔记-220 openGauss性能调优-确定性能调优范围-查询最耗性能的SQL220.1 操作步骤 openGauss学习笔记-220 openGauss性能调优-确定性能调优范围-查询最耗性能的SQL 系统中有些SQL语句运行了很长时间还没有结束&#xff0c;这些语句会消耗很多的系统性能&…

http“超级应用与理解”

本篇文章来介绍一下http协议和其应用 1.http协议是在OSI模型的哪一层 HTTP&#xff08;超文本传输协议&#xff09;是应用层协议&#xff0c;它是在 OSI 模型的最高层&#xff0c;即第七层——应用层。HTTP 通过互联网来传输数据和信息&#xff0c;主要用于 Web 浏览器和 Web …

CF1845 D. Rating System [思维题+数形结合]

传送门:CF [前题提要]:自己在做这道题的时候思路完全想错方向,导致怎么做都做不出来,看了题解之后感觉数形结合的思考方式挺好的(或者这种做法挺典的),故写篇题解记录一下 题目很简单,不再解释.先不考虑 k k k,想想是一种什么情况?很显然应该是跟下图一样是一个折线图的变化.…

计算机设计大赛 深度学习YOLO安检管制物品识别与检测 - python opencv

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络4 Yolov55 模型训练6 实现效果7 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习YOLO安检管制误判识别与检测 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&…

计算机组成原理(2)-----存储芯片与CPU的连接

目录 一.单块存储芯片与CPU的连接 二.多块存储芯片与CPU的连接 1.位扩展 2.字扩展 &#xff08;1&#xff09;线选法 &#xff08;2&#xff09;译码器片选法 3.字位同时扩展 三.译码器相关 一.单块存储芯片与CPU的连接 如图所示是8*8位的芯片&#xff0c;总共8个存储…

C++ 双向广度搜索,嚯嚯!不就是双指针理念吗

1. 前言 在线性数据结构中搜索时&#xff0c;常使用线性搜索算法&#xff0c;但其性能偏低下&#xff0c;其性能改善方案常有二分搜索和双指针或多指针搜索算法。在复杂的数据结构如树和图中&#xff0c;常规搜索算法是深度和广度搜索。在深度搜索算法过程中常借助剪枝或记忆化…

分布式文件系统 SpringBoot+FastDFS+Vue.js【二】

分布式文件系统 SpringBootFastDFSVue.js【二】 六、实现上传功能并展示数据6.1.创建数据库6.2.创建spring boot项目fastDFS-java6.3.引入依赖6.3.fastdfs-client配置文件6.4.跨域配置GlobalCrosConfig.java6.5.创建模型--实体类6.5.1.FastDfsFile.java6.5.2.FastDfsFileType.j…

Mac M2芯片配置PHP环境

Mac M2芯片配置PHP环境 1. XAMPP 1. XAMPP 官网地址 https://www.apachefriends.org/ 安装 安装完成 web server打开后&#xff0c;在打开localhost 成功&#xff01;

(三十九)大数据实战——Prometheus监控平台的部署搭建

前言 Prometheus监控&#xff08;Prometheus Monitoring&#xff09;是一种开源的系统监控和警报工具。它最初由SoundCloud开发并于2012年发布&#xff0c;并在2016年加入了云原生计算基金会&#xff08;CNCF&#xff09;。Prometheus监控旨在收集、存储和查询各种指标数据&am…

Android---DslTabLayout实现底部导航栏

1. 在 Android 项目中引用 JitPack 库 AGP 8. 根目录的 settings.gradle dependencyResolutionManagement {...repositories {...maven { url https://jitpack.io }} } AGP 8. 根目录如果是 settings.gradle.kts 文件 dependencyResolutionManagement {...repositories {...…

A. Desorting

链接 : Problem - A - Codeforces 题意 : 思路 : 先判断序列是否排好序 &#xff0c; 不是排好序的&#xff0c;直接输出0即可&#xff0c;排好序的 : 先求出相邻元素之间的最小间隔&#xff0c;因为&#xff0c;要使有序非递减序列&#xff0c;变得不排序&#xff0c;…

OLMo 以促进语言模型科学之名 —— OLMo Accelerating the Science of Language Models —— 全文翻译

OLMo: Accelerating the Science of Language Models OLMo 以促进语言模型科学之名 摘要 语言模型在自然语言处理的研究中和商业产品中已经变得无所不在。因为其商业上的重要性激增&#xff0c;所以&#xff0c;其中最强大的模型已经闭源&#xff0c;控制在专有接口之中&#…

Leetcode-1572. 矩阵对角线元素的和

题目&#xff1a; 给你一个正方形矩阵 mat&#xff0c;请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&#xff1a; 输入&#xff1a;mat [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;25 解释&#xff1a;对角线…

Apache httpd 换行解析漏洞复现(CVE-2017-15715)

Web页面&#xff1a; 新建一个一句话木马&#xff1a; 0.php <?php system($_GET[0]); ?> 上传木马&#xff0c; burpsuite 抓包。 直接上传是回显 bad file。 我们查看数据包的二进制内容&#xff08;hex&#xff09;&#xff0c;内容是以16进制显示的&#xff0c;…

挑战杯 wifi指纹室内定位系统

简介 今天来介绍一下室内定位相关的原理以及实现方法; WIFI全称WirelessFidelity&#xff0c;在中文里又称作“行动热点”&#xff0c;是Wi-Fi联盟制造商的商标做为产品的品牌认证&#xff0c;是一个创建于IEEE 802.11标准的无线局域网技术。基于两套系统的密切相关&#xff…

html的列表标签

列表标签 列表在html里面经常会用到的&#xff0c;主要使用来布局的&#xff0c;使其整齐好看. 无序列表 无序列表[重要]&#xff1a; ul &#xff0c;li 示例代码1&#xff1a; 对应的效果&#xff1a; 无序列表的属性 属性值描述typedisc&#xff0c;square&#xff0c;…

U盘重装系统

因为系统管理员密码忘记&#xff0c;登录不了window系统&#xff0c;使用老毛桃制作U盘启动盘 1、下载老毛桃 下载地址为http://lmt.psydrj.com/index.html 安装后&#xff0c;桌面上显示为 2、制作U盘启动盘 启动老毛桃U盘启动装机工具&#xff0c;插入U盘&#xff0c;点击一…

[Java][算法 滑动窗口]Day 03---LeetCode 热题 100---08~09

第一题 无重复字符串的最长子串 思路 其实就是在字符串S中 找到没有重复的最长子串的长度 这道题的难点就是在于如何判断最长并且无重复 首先 最长长度 可以使用变量max记录保存 再者 判断有无重复 最简单的方法就是 暴力遍历法 即对于每次找的子串都再次寻找遍历…

【十九】【C++】 priority_queue简单使用和仿函数

priority_queue文档介绍翻译 优先队列是一种容器适配器&#xff0c;专门设计成其中的第一个元素始终是根据某种严格的弱排序准则最大的元素。 这种上下文类似于堆&#xff0c;其中元素可以在任何时刻插入&#xff0c;而只能检索最大堆元素&#xff08;在优先队列中顶部的元素&a…