高通QSEE中Trustlet提取

Trustlet提取

QSEE架构的Trustlet通常放在/firmware目录(/vendor/firmware_mnt的软连接),每个模块被分割为多个文件,以xxxx.b0x形式存在,每个模块对应一个mdt文件,如下图:

upload successful

其中,mdt文件保存了elf文件头以及证书,使用binwalk可以看到包含一个elf头和3个DER证书文件头:

upload successful

利用readelf可以看到elf文件分割后的偏移:
(mdt文件的elf头和xxxx.b00的文件头相同)

upload successful

基于这些信息,编写提取Truset的脚本参考如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import sys, os, struct

def main():

#Reading the arguments
if len(sys.argv) != 4:
print "USAGE: <TRUSTLET_DIR> <TRUSTLET_NAME> <OUTPUT_FILE_PATH>"
return


trustlet_dir = sys.argv[1]
trustlet_name = sys.argv[2]
output_file_path = sys.argv[3]


flag = open(os.path.join(trustlet_dir, "%s.mdt" % trustlet_name), "rb").read(5)[4]
if flag == '\x01':
bitness = 32
if flag == '\x02':
bitness = 64

if (bitness == 64):
ELF_HEADER_SIZE = 0x40
E_PHNUM_OFFSET = 0x38
PHDR_SIZE = 0x38
P_FILESZ_OFFSET = 0x20
P_OFFSET_OFFSET = 0x8
else:
ELF_HEADER_SIZE = 0x34
E_PHNUM_OFFSET = 0x2C
PHDR_SIZE = 0x20
P_FILESZ_OFFSET = 0x10
P_OFFSET_OFFSET = 0x4

#Reading the ELF header from the ".mdt" file
mdt = open(os.path.join(trustlet_dir, "%s.mdt" % trustlet_name), "rb")
elf_header = mdt.read(ELF_HEADER_SIZE)

phnum = struct.unpack("<H", elf_header[E_PHNUM_OFFSET:E_PHNUM_OFFSET+2])[0]
print "[+] Found %d program headers" % phnum

#Reading each of the program headers and copying the relevant chunk
output_file = open(output_file_path, 'wb')
for i in range(0, phnum):

#Reading the PHDR
print "[+] Reading PHDR %d" % i
phdr = mdt.read(PHDR_SIZE)
p_filesz = struct.unpack("<I", phdr[P_FILESZ_OFFSET:P_FILESZ_OFFSET+4])[0]
p_offset= struct.unpack("<I", phdr[P_OFFSET_OFFSET:P_OFFSET_OFFSET+4])[0]
print "[+] Size: 0x%08X, Offset: 0x%08X" % (p_filesz, p_offset)

if p_filesz == 0:
print "[+] Empty block, skipping"
continue #There's no backing block

#Copying out the data in the block
block = open(os.path.join(trustlet_dir, "%s.b%02d" % (trustlet_name, i)), 'rb').read()
output_file.seek(p_offset, 0)
output_file.write(block)

mdt.close()
output_file.close()

def parse_DER():
if len(sys.argv) != 4:
print "USAGE: <TRUSTLET_DIR> <TRUSTLET_NAME> <OUTPUT_FILE_PATH>"
return
trustlet_dir = sys.argv[1]
trustlet_name = sys.argv[2]
mdt = open(os.path.join(trustlet_dir, "%s.mdt" % trustlet_name), "rb")
certs = mdt.read()
certs_index = []

for i in range(0x34,len(certs)-5):
if certs[i] == '\x30' and certs[i+1] == '\x82' and certs[i+4] == '\x30' and certs[i+5] == '\x82':
certs_index.append(i)
certs_index.append(len(certs))

print certs_index
for i in range(len(certs_index)-1):
start = certs_index[i]
end = certs_index[i+1]
certname = "cert_"+str(start)+".der"
print certname
open(certname,'wb').write(bytes(certs[start : end]))


if __name__ == "__main__":
main()
parse_DER()

简单逆向分析

高通的QSEE核心逻辑在libQSEECom.so中,实现Trustlet的加载和解析。

Trustlet模块加载入口在QSEECom_start_app:

upload successful

调用_QSEECom_get_handle完成加载:

upload successful

主要操作有包括打开/dev/qseecom,使用ioctls通信
sub_1B00实现了mdt文件的解析,加载分割后的elf文件,完成加载过程。

upload successful