OP-TEE-study
OP-TEE
SCTF2023的一道题是关于OP-TEE的,学习一波。
OP-TEE,open source project Trusted Execution Environment (TEE), 开源可信执行环境。Rich Execution Environment (REE)相对应。REE中运行的是non-secure OS,例如安卓,Linux系统等。TEE中运行的是secure OS,需要Arm TrustZone技术的支持,依赖硬件设计。
TrustZone是操作系统间实现了相互独立。可以在同一个CPU上运行两个OS,其中一个OS只负责安全存储或者计算的操作。另一个OS就是平时用的OS,比如安卓系统,对于这个OS来说,安全OS是不可见的,其没有权限获取到安全OS中的隐私数据。 两个OS之间需要交互,也就是CA/TA这种调用机制。
0x00 OP-TEE组件功能
其执行流程如下:
(1)CA(代理应用)发起函数调用。完成一次完整的CA请求时在linux userspace端(图的左上部分)需要执行的操作依次如下 :
a. 调用TEEC_InitializeContext函数打开op-tee驱动文件,获取到操作句柄并存放到TEE_Context类型的变量中。
b. 调用TEEC_OpenSession函数,通过获取到的TEE_Context类型的变量创建一个CA与TA之间通信的通道,如果TA image被存放在file system中,那么OP-TEE OS端还会将TA image从file system中加载到OP-TEE。
c. 初始化TEEC_Operation类型的变量,并根据实际需要借助TEEC_PARAM_TYPES宏来设定TEEC_Operation类型变量中paramTypes成员的值,该值规定传递到OP-TEE中的最多4个变量的作用(作为输入还是输出)。
d. 使用session,TA与CA端规定的command ID以及TEEC_Operation作为参数,调用TEEC_InvokeCommand函数来发起请求。 调用TEEC_InvokeCommand成功之后,剩下的事情就由OP-TEE和TA进行处理并将结果和相关的数据通过TEEC_Operation中的params返回给CA。
e. 调用成功之后,如果不需要调用该TA则需要注销session和context,通过调用TEEC_CloseSession函数和TEEC_FinalizeContext函数来实现。
注:libteec库是OP-TEE提供给用户在linux userspace层面调用的接口实现,上述函数都是libteec库中的函数。
(2)tee_supplicant可以使OP-TEE能够通过它来访问REE端文件系统中的资源,例如加载存放在文件系统中的TA镜像到TEE中。
- tee_supplicant在REE启动的时候会作为一个后台程序被自启动,而且常驻于系统中。
- tee_supplicant的启动代码存放在build/init.d.optee文件中,在编译的时候init.d.optee文件将会被打包到根文件系统(rootfs)中。这些操作是在编译生成rootfs的时候所做的。
- 当tee_supplicant接收到来自TA的请求并解析出对应的请求func ID之后,tee_supplicant将会根据func ID来执行具体的请求操作,例如:
RPC_CMD_LOAD_TA
指tee_supplicant将会到文件系统中将TA镜像的内容读取到共享内存中,RPC_CMD_FS
指TA对常规的文件和目录进行打开、关闭、读取等操作。
(3)OP-TEE驱动主要作用是在REE与TEE端进行数据交互。
tee_supplicant和libteec调用之后都会进入到kernel space,然后kernel根据传递的参数找到OP-TEE驱动,并命中驱动的operation结构体中的具体处理函数来完成实际的操作。对于OP-TEE驱动,会触发SMC调用(非安全模式向安全模式的调用),并带参数进入到ARM cortex的monitor模式,执行normal world和secure world的切换,切换完成之后,会将驱动端带入的参数传递给OP-TEE中的thread进行进一步的处理。
Step1:OP-TEE驱动挂载。
linux kernel中加载驱动的函数有两个:subsys_initcall和module_init。
OP-TEE驱动的加载过程分为两步,第一步是初始化class和设备号(由subsys_initcall完成),第二步是probe过程(由module_init完成)。在OP-TEE驱动源代码中使用subsys_init定义的函数为tee_init,使用module_init定义的函数为optee_driver_init。
OP-TEE驱动会分别针对libteec和tee_supplicant建立不同的设备/dev/tee0和/dev/teepriv0。并且为两个设备建立消息队列,来存放normal world与secure world之间的请求,这样libteec和tee_supplicant使用OP-TEE驱动的时就能做到相对独立。secure world与OP-TEE驱动之间使用共享内存进行数据交互。
Step2:驱动挂载完成之后,CA程序通过调用libteec中的接口调用OP-TEE驱动,来切换到secure world中,从而调用对应的TA程序。其中,有4个比较重要的结构体:
tee_fops。当在userspace层面调用open/release/ioctl进行文件操作时,就会调用到tee_fops中的函数。
optee_ops。存放针对/dev/tee0设备的具体操作函数的指针。当用户调用libteec中的接口时(即操作/dev/tee0设备),首先会调用到tee_fops中的函数,tee_fops中的函数再会调用optee_ops中的函数,来完成对/dev/tee0设备的实际操作。
optee_supp_ops。与optee_ops类似,只不过存放的是针对/dev/teepriv0设备的函数的指针。
tee_shm_dma_buf_ops。略。
libteec提供给上层使用的接口总共有10个,这10个接口通过系统调用最终会调用到驱动中,在接口libteec中调用Open函数的时候,在驱动中就会调用到file_operations结构体变量tee_fops中的Open成员。同理在libteec接口中调用ioctl函数,则在驱动中最终会调用tee_fops中的ioctl函数。tee_supplicant与secure world之间的交互则是分为3步:1. 驱动获取来自TEE侧的请求。2.tee_supplicant从驱动中获取TEE侧的请求。3.驱动返回请求操作结果给TEE侧。
(4)Monitor如何处理SMC请求。
- libteec和tee_supplicant调用接口之后,最终会调用到OP-TEE驱动来触发对应的SMC操作。具体做法是:调用arm_smccc_smc之后,CPU中的cortex就会切换到monitor模式,其后会去获取MVBAR寄存器中存放的monitor模式的中断向量表地址,然后查找smc的处理函数。进入到处理函数之后,再根据从REE侧带入的参数判定是进行快速smc处理还是标准的smc处理。
- fast smc一般会在驱动挂载过程中被调用。fast smc的特点就是在OP-TEE不会使用一个thread来对fast smc进行处理,而是在OP-TEE的kernel space层面直接对smc进行请求,并返回处理结果。
0x01 demo
下面是一个optee的官方示例hello_world。
1 | hello_world |
上面的结构中,ta负责可信应用TA部分,而host(代理应用CA)则是调用TA的提供的功能。
以此为基础,写一个数据的加密保存和读取恢复的功能。
Step1:修改主目录下Cmakelist的文件名,修改host文件夹下Makefile的二进制文件名。
Step2:修改host文件夹下的main.c:
1 |
|
Step3:清空ta文件夹下Android.mk的内容,修改sub.mk中的文件名,修改Makefile中的uuid值(任意更改都可)。
Step4:修改user-ta-header-defines.h为:
1 |
Step5:修改include/hello_world_change_ta.h,hello_world_change_ta.c的内容,具体看链接。
0x02 运行hello_world以及0x01中的demo
没看出来。不知道咋运行啊?
To be continue..
留言
- 文章链接: https://wd-2711.tech/
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明出处!