华为 P9 Lite Trust Core TA 解密
source link: https://xuanxuanblingbling.github.io/trustzone/2023/05/29/p9lite/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
华为 P9 Lite Trust Core TA 解密
更新中…是议题 Unearthing the TrustedCore: A Critical Review on Huawei’s Trusted Execution Environment的子部分,解密TA的大概流程主要有三步:(1)模拟运行TEE中的白盒密码算法解出RSA私钥prikeyx。(2)使用RSA的私钥prikeyx解密TA头部的manifest。(3)使用解密后manifest中的AES key 解密 TA 正文。但作者没有给出解密过程中的一些细节,例如RSA和AES密钥的组织方法。所以我尝试复现了这个解密,并给出解密过程中的所有细节。
目标固件VNS-L31C432B160是2016年的版本,也是作者开源在github上的相关工具tckit的示例固件。固件解压后的UPDATE.APP可以使用在之前CVE-2021-39994:HUAWEI SMC SE Factory Check OOB Access中的提到过的Android Image Tools的emui_extractor进行提取,此工具在我本机的mac环境下编译此工具需要添加制定C++11的编译选项-std=c++11
:
emui_extractor: image.h image.cc emui_extractor.cc
g++ -std=c++11 -o emui_extractor image.cc emui_extractor.cc
.PHONY: clean
clean:
rm emui_extractor
然后即可使用其list子功能查看UPDATE.APP中的不同镜像:
➜ ./emui_extractor ./UPDATE.APP list
=====================================================================
Sequence File.img Size Type Device
=====================================================================
fe000000 SHA256RSA.img 256.00 B SHA256RSA HW7x27
fe000000 CRC.img 197.10 KB CRC HW7x27
fffffff0 CURVER.img 15.00 B CURVER HW7x27
fffffff1 VERLIST.img 3.14 KB VERLIST HW7x27
00000000 EFI.img 17.00 KB EFI HW7x27
00000018 XLOADER.img 69.25 KB XLOADER HW7x27
00000017 FW_LPM3.img 164.44 KB FW_LPM3 HW7x27
00000013 FASTBOOT.img 2.53 MB FASTBOOT HW7x27
00000016 MODEMNVM_UPDATE.img 19.97 MB MODEMNVM_UPDATE HW7x27
0000001a TEEOS.img 2.30 MB TEEOS HW7x27
00000012 TRUSTFIRMWARE.img 94.56 KB TRUSTFIRMWARE HW7x27
00000019 SENSORHUB.img 416.44 KB SENSORHUB HW7x27
00000014 FW_HIFI.img 2.51 MB FW_HIFI HW7x27
0000000c BOOT.img 13.67 MB BOOT HW7x27
0000000a RECOVERY.img 32.34 MB RECOVERY HW7x27
0000000a RECOVERY2.img 32.34 MB RECOVERY2 HW7x27
00000008 DTS.img 18.12 MB DTS HW7x27
00000011 MODEM_FW.img 96.00 MB MODEM_FW HW7x27
0000000e CACHE.img 6.10 MB CACHE HW7x27
0000000d SYSTEM.img 2.38 GB SYSTEM HW7x27
0000000f CUST.img 418.13 MB CUST HW7x27
00000010 USERDATA.img 67.33 MB USERDATA HW7x27
=====================================================================
主要关注两个镜像:
- TEEOS.img:TEEOS和静态TA,其中包含对动态TA解密的关键代码和数据
- SYSTEM.img:Android侧文件系统,其中包含加密后的动态TA
将二者提取出来:
➜ ./emui_extractor ./UPDATE.APP dump TEEOS.img TEEOS.img
➜ ./emui_extractor ./UPDATE.APP dump SYSTEM.img SYSTEM.img
SYSTEM.img是android文件系统的主要部分,加密后的TA就在其中,但其格式是 Android sparse image,还需要再次进行处理:
➜ file SYSTEM.img
SYSTEM.img: Android sparse image, version: 1.0, Total of 655320 4096-byte output blocks in 4845 input chunks.
使用https://github.com/anestisb/android-simg2img,将其转化为正常的ext4文件系统格式:
➜ ./simg2img ./SYSTEM.img ./system.ext4
➜ file ./system.ext4
./system.ext4: Linux rev 1.0 ext4 filesystem data, volume name "system" (extents) (large files)
然后在linux中挂载此文件系统即可:
➜ mkdir rootfs
➜ sudo mount ./system.ext4 ./rootfs
➜ ls ./rootfs
app build.prop emui fonts global lib64 phone.prop themes vendor
asr cdrom etc fpgaice40 hw_oem lost+found priv-app tts watermark
bin delapp extras framework lib media screenlock usr xbin
加密的TA就在bin目录下,是以UUID格式和.sec后缀为名字的文件:
➜ ls -al ./rootfs/bin | grep sec
-rw-r-----. 1 root xuan 12756 11月 9 2016 6c8cf255-ca98-439e-a98e-ade64022ecb6.sec
-rw-r-----. 1 root xuan 26696 11月 9 2016 79b77788-9789-4a7a-a2be-b60155eef5f4.sec
-rw-r-----. 1 root xuan 9012 11月 9 2016 868ccafb-794b-46c6-b5c4-9f1462de4e02.sec
-rw-r-----. 1 root xuan 417224 11月 9 2016 883890ba-3ef8-4f0b-9c02-f5874acbf2ff.sec
-rw-r-----. 1 root xuan 23940 11月 9 2016 9b17660b-8968-4eed-917e-dd32379bd548.sec
-rw-r-----. 1 root xuan 41500 11月 9 2016 b4b71581-add2-e89f-d536-f35436dc7973.sec
-rw-r-----. 1 root 1004 12776 11月 9 2016 fd1bbfb2-9a62-4b27-8fdb-a503529076af.sec
-rw-r-----. 1 root 1004 1326040 11月 9 2016 fpc_1021_ta.sec
-rw-r-----. 1 root 1004 1329000 11月 9 2016 fpc_1021_ta_venus.sec
-rw-r-----. 1 root 1004 1208040 11月 9 2016 fpc_1022_ta.sec
-rwxr-x---. 1 root root 10096 11月 9 2016 secure_storage
-rw-r-----. 1 root 1004 1711896 11月 9 2016 syna_109A0_ta.sec
给出示例,之后以868ccafb-794b-46c6-b5c4-9f1462de4e02.sec为例,目标就是解密这个TA,使用binwalk可以看出这个目标确实是加密的:
➜ binwalk -E ./868ccafb-794b-46c6-b5c4-9f1462de4e02.sec
DECIMAL HEXADECIMAL ENTROPY
--------------------------------------------------------------------------------
0 0x0 Rising entropy edge (0.968520)
接下来就用TEEOS.img来解密868ccafb-794b-46c6-b5c4-9f1462de4e02.sec:
拆分TEEOS.img
使用https://github.com/teesec-research/tckit(python2),即可从TEEOS.img拆出RSA的私钥,首先使用splitteeos.py从TEEOS.img拆分出globaltask这个PTA:
➜ python2 tckit/splitteeos/splitteeos.py ./TEEOS.img
➜ ls -al ./tas_out
drwxrwxr-x 2 xuan xuan 4096 6月 4 15:12 .
drwxrwxr-x 4 xuan xuan 4096 6月 4 15:12 ..
-rw-rw-r-- 1 xuan xuan 1642624 6月 4 15:12 globaltask
-rw-rw-r-- 1 xuan xuan 18212 6月 4 15:12 task_gatekeeper
-rw-rw-r-- 1 xuan xuan 75216 6月 4 15:12 task_keyboard
-rw-rw-r-- 1 xuan xuan 107132 6月 4 15:12 task_keymaster
-rw-rw-r-- 1 xuan xuan 14792 6月 4 15:12 task_reet
-rw-rw-r-- 1 xuan xuan 10232 6月 4 15:12 task_secboot
-rw-rw-r-- 1 xuan xuan 15540 6月 4 15:12 task_storage
然后使用globaltaskgencode.py从拆分出的globaltask中,提取白盒密码算法代码,并封装为C代码:
➜ python2 tckit/globaltaskgencode.py ./tas_out/globaltask
➜ file tas_out/globaltask_extract_keys.c
tas_out/globaltask_extract_keys.c: ASCII text
编译并使用QEMU模拟运行此白盒密码算法:
➜ arm-linux-gnueabihf-gcc tas_out/globaltask_extract_keys.c -o test
➜ qemu-arm -L /usr/arm-linux-gnueabihf ./test
运行后即可打印RSA私钥:
private_key = '\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'
public_key = '\xc4\x11\xcc\x98\xce\x92\x0d\x78\x7e\xbb\x7a\xa4\xff\x5f\x60\x82\xa9\x4d\xb5\xe7\x75\x9d\xfd\x7d\xa7\xcf\xa3\xbb\x25\x83\xf5\x24\xf9\x31\x65\x5a\x5c\xea\xab\x88\xb9\x1b\x94\xc8\x5a\x75\x44\x4e\x17\x50\x76\xa5\xa4\x39\xdd\x79\x5b\xf4\xcc\xd0\x11\xb8\x52\xe0\x8d\x4f\x49\xd4\x6b\xb8\x5b\x4a\xdf\x51\x53\xef\x61\x75\xc4\x43\xe1\xfd\x8c\x18\x9a\x3f\x45\x11\x69\x31\xb4\x9c\x2c\x2f\xdb\x5a\xe9\x09\x4d\x99\xf5\xdc\x95\x34\xde\x1a\xc6\x34\xd7\xbf\x86\x27\xce\x94\x4f\xc8\x03\xd1\x47\x24\x02\x8e\x49\x0b\x22\xe6\x82\x42\xf9\xa7\x1b\x85\x29\xb1\x90\x4e\x22\xd0\x48\x4a\x56\x63\xee\x93\x75\x9d\x25\xbc\x02\xa0\x23\x55\xe6\xd4\x67\xa0\x76\x22\x23\x8b\x5a\x7b\x4d\x24\x7a\x28\x71\x83\x4c\xc0\xa1\x28\x9c\x14\x45\x47\x75\xdb\x12\x42\xfd\x94\x05\xc6\xa3\xb9\xcc\xf7\x48\x8c\xe9\x55\xac\x1f\x01\xca\x6b\xc5\xe5\x1c\xa8\xf4\xc7\x09\x7d\x5b\x4b\x2c\x1d\xcb\xc5\x4e\x12\xfb\x46\x76\x23\xb2\x58\x94\x4b\x7b\x66\xbb\xbb\x18\x8b\x7e\x10\x3c\xbc\x2d\xd8\x1d\x0d\xa2\x7b\x60\xa6\xb9\x1a\x20\x40\x96\xa2\x01\xc9\x09\x06\xe6\xb6\xb0\x7f\x44\xa3\xa9\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
私钥的解析以及后续的解密TA,需要逆向globaltask以及TEEOS本身,可以使用globaltask2elf.py恢复globaltask的ELF结构:
➜ python2 tckit/globaltask2elf.py ./tas_out/globaltask
➜ file ./tas_out/globaltask.elf
globaltask.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped
以及使用tos2elf.py恢复TEEOS的ELF结构:
➜ python2 tckit/tos2elf.py ./TEEOS.img
➜ file Rtosck
Rtosck: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), no program header, not stripped
接下来就对这两个ELF进行逆向,主要是globaltask.elf:
RSA私钥解析
根据作者PPT,可以确定QEMU运行打印出的两个密钥中第一个私钥(x)为解密TA所需要的关键密钥:
但对于这个RSA的私钥解析,可以看出来这里的打印的公私钥没有什么openssl的格式,也就不是标准格式公私钥数据,所以猜测其打印的数据就是p、q、n、e、d等RSA相关数据,因此需要逆向分析解析私钥的代码。
但这里的密钥数据其实也直接可以通过公私钥的特性猜出来(就当CTF做),首先可以将私钥数据保存成二进制文件方便查看:
private_key = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'
open('private_key.bin','wb').write(private_key)
使用010editor对私钥数据相面,可以看出来其长度总共0x140字节:
对于RSA的私钥,在理论上一般表达为以下两种:
- p、q、dp、dq、qinv (中国剩余定理优化的RSA计算 RSA-CRT)
关于RSA的中国剩余定理:
但在实际中,RSA私钥一般以PKCS#1和PKCS#8格式存储(ASN.1),其中不仅包括理论上的私钥数据,还包含公钥(n、e),例如使用openssl生成私钥包括(n、e、d、p、q、dp、dq、qinv),对应关系如下:
RSAPrivateKey ::= SEQUENCE {
version Version,
modulus INTEGER, -- n
publicExponent INTEGER, -- e
privateExponent INTEGER, -- d
prime1 INTEGER, -- p
prime2 INTEGER, -- q
exponent1 INTEGER, -- dp: d mod (p-1)
exponent2 INTEGER, -- dq: d mod (q-1)
coefficient INTEGER, -- qinv: (inverse of q) mod p
otherPrimeInfos OtherPrimeInfos OPTIONAL
}
可以自己使用openssl生成一个1024bit的RSA私钥并查看:
➜ openssl genrsa -out rsa_private_key.pem 1024
Generating RSA private key, 1024 bit long modulus
.........++++++
......................++++++
e is 65537 (0x10001)
➜ openssl rsa -in ./rsa_private_key.pem -text
Private-Key: (1024 bit)
modulus:
00:d7:1f:f9:0f:d4:f8:00:91:fd:9e:d9:66:b1:12:
d4:73:20:ee:50:06:8a:e9:f7:28:d6:e0:76:78:60:
e6:96:cc:a0:4b:db:78:bb:56:8d:ae:0f:d9:33:d2:
82:92:11:49:25:83:67:58:77:93:b5:68:4c:ec:7c:
d6:4c:b1:9f:18:ce:93:c7:3e:d4:b4:cc:26:ec:59:
1f:8c:aa:52:b8:9b:9f:e0:86:8a:53:65:6c:47:0f:
de:40:bb:8a:28:53:df:81:da:61:97:32:56:3a:80:
40:8c:27:38:58:3a:97:09:f7:8b:d7:59:37:c2:2c:
39:8e:2d:c5:cd:30:b8:35:01
publicExponent: 65537 (0x10001)
privateExponent:
26:b7:8f:68:c5:08:99:79:ac:ee:b0:eb:e5:84:a1:
0d:d3:68:70:a8:ac:c9:ac:fd:01:a7:46:4b:26:0d:
7a:28:7b:d5:0b:3b:f0:63:84:7e:46:45:ee:28:bd:
ed:32:05:3b:26:2a:2c:66:e1:03:ae:30:e2:03:19:
c2:95:d9:2f:16:2e:1e:b1:34:11:8b:22:02:e5:1e:
ef:0e:21:59:fc:c1:6b:9a:da:24:eb:98:2d:fd:a0:
19:2a:e1:1d:3e:a6:2b:af:03:48:14:f9:69:b0:73:
ba:f7:c6:05:db:f7:ad:5a:b2:3e:80:3e:9f:2d:54:
5a:2c:33:81:20:58:a5:11
prime1:
00:f7:c1:40:a3:b5:20:a0:7d:f9:e7:38:df:ec:4d:
2d:f1:17:00:23:d9:fa:ff:03:c1:e8:78:01:36:2c:
a9:55:3f:67:d6:89:93:ca:b7:bf:b1:a1:2b:aa:90:
1a:1c:a6:5a:df:2b:b4:4f:6d:60:04:45:30:b4:cc:
3b:3f:07:5c:95
prime2:
00:de:48:ba:aa:c6:f7:65:ba:65:84:6b:1c:04:92:
a8:c4:76:55:57:d2:0e:86:60:53:31:fd:34:68:b0:
1e:f5:85:b9:51:b8:0a:84:a5:ff:23:d8:73:19:1f:
51:c2:66:fc:70:c8:fc:32:f0:5b:41:56:3d:61:c2:
2c:91:43:af:bd
exponent1:
1b:9c:a6:1f:98:a8:32:3a:d8:07:35:07:7f:c6:7a:
40:4c:57:ef:a6:f3:9a:48:48:ec:27:b3:ba:dd:ef:
61:58:d7:b1:c9:53:77:5c:53:38:f0:c5:75:14:ea:
54:17:16:39:99:1d:57:5c:d1:3e:a8:97:6d:0e:f5:
eb:68:5e:a1
exponent2:
00:93:de:93:f6:f9:87:28:70:38:0a:3f:ea:92:8c:
31:a3:08:09:3b:f3:ab:df:ee:82:49:b5:e4:40:64:
31:24:29:82:1f:7f:ab:d7:94:49:c7:41:bd:47:90:
13:26:9c:b6:00:1d:63:d0:4b:1e:99:b7:51:fc:0f:
5c:f0:81:b3:8d
coefficient:
00:e8:5b:63:26:f9:a2:e7:d2:08:c4:36:25:8c:64:
8f:69:e6:6c:94:54:dd:8f:ff:d3:e4:56:cb:dd:b1:
f5:4b:d1:a4:6e:44:9c:7f:77:ae:61:4f:49:e2:a0:
96:ec:72:05:6b:3d:1a:22:13:a9:49:a1:f1:4b:dc:
fe:5c:90:66:f0
writing RSA key
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDXH/kP1PgAkf2e2WaxEtRzIO5QBorp9yjW4HZ4YOaWzKBL23i7
Vo2uD9kz0oKSEUklg2dYd5O1aEzsfNZMsZ8YzpPHPtS0zCbsWR+MqlK4m5/ghopT
ZWxHD95Au4ooU9+B2mGXMlY6gECMJzhYOpcJ94vXWTfCLDmOLcXNMLg1AQIDAQAB
AoGAJrePaMUImXms7rDr5YShDdNocKisyaz9AadGSyYNeih71Qs78GOEfkZF7ii9
7TIFOyYqLGbhA64w4gMZwpXZLxYuHrE0EYsiAuUe7w4hWfzBa5raJOuYLf2gGSrh
HT6mK68DSBT5abBzuvfGBdv3rVqyPoA+ny1UWiwzgSBYpRECQQD3wUCjtSCgffnn
ON/sTS3xFwAj2fr/A8HoeAE2LKlVP2fWiZPKt7+xoSuqkBocplrfK7RPbWAERTC0
zDs/B1yVAkEA3ki6qsb3ZbplhGscBJKoxHZVV9IOhmBTMf00aLAe9YW5UbgKhKX/
I9hzGR9Rwmb8cMj8MvBbQVY9YcIskUOvvQJAG5ymH5ioMjrYBzUHf8Z6QExX76bz
mkhI7Cezut3vYVjXsclTd1xTOPDFdRTqVBcWOZkdV1zRPqiXbQ7162heoQJBAJPe
k/b5hyhwOAo/6pKMMaMICTvzq9/ugkm15EBkMSQpgh9/q9eUScdBvUeQEyactgAd
Y9BLHpm3UfwPXPCBs40CQQDoW2Mm+aLn0gjENiWMZI9p5myUVN2P/9PkVsvdsfVL
0aRuRJx/d65hT0nioJbscgVrPRoiE6lJofFL3P5ckGbw
-----END RSA PRIVATE KEY-----
那以上导出的这0x140字节的RSA私钥应该包含什么了呢?
- 如果是n、d,那么n、d各0xa0字节,换算为bit为1280,并非512、1024,不太合理
- 如果是p、q、dp、dq、qinv,则其各64字节,换算为bit为512,可以为RSA 1024,较为合理
那是不是我们猜的p、q、dp、dq、qinv呢?如果是,这里为什么不包含e?因为按照RSA原始的计算方法:
e * d ≡ 1 (mod (p-1)*(q-1))
没有e也就不知道d,也就不能按照原始的办法解密。但其实中国剩余定理优化出的RSA计算方法中多出来的3个数据,即通过dp、dq、qinv,可以和p、q结合直接解密密文,而不需要e的直接参与。并且dp、dq、qinv是和e相关的,可以用过p和dp计算出e。其中dp 不是d*p
,而是定义为 d mod(p-1)
,并且dp、e、p满足一个关系,dp与e在mod(p-1)下在互为逆元
,即:
dp * e ≡ 1 (mod (p-1))
自己证明了一下(我也不知道证的对不对):
那我们就可以假设0x140字节的私钥数据是p、q、dp、dq、qinv,然后计算一下e:
from Crypto.Util.number import bytes_to_long
import gmpy2
private_key = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'
p = bytes_to_long(private_key[:64])
dp = bytes_to_long(private_key[128:192])
e = gmpy2.invert(dp,(p-1))
print(hex(e))
结果为0x10001,就是常用的e,因此:
- 以上导出的这0x140字节的RSA私钥,确实是p、q、dp、dq、qinv各64字节
- dp与e在mod(p-1)下在确实互为逆元
在CTF中也有在不给p、q的情况下,仅通过n、e、dp来破解RSA的题目,大概原理就是利用dp与e在mod(p-1)下在互为逆元
的性质,将其变化为等式:
【 dp * e ≡ 1 (mod (p-1)) 】 -> 【 dp * e = 1 + k * (p-1) 】(k为整数)
因为dp定义为d mod (p-1),所以dp小于p-1,因此k = [dp/(p-1)] * e - 1
的取值范围为[-1,e-1)
。题目一般给出e为0x10001,因此可以爆破k,计算对应的p,当p可以被n整除时,成功拆分n为p和q,即可破解RSA:
但现在我们这里不用这么麻烦,因为已经有p、q、e了,所以我们继续可以算出d,然后就可以对密文进行解密了(当然纯用p、q、dp、dq、qinv也可以解密):
from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes
import gmpy2
private_key = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'
p = bytes_to_long(private_key[:64])
q = bytes_to_long(private_key[64:128])
dp = bytes_to_long(private_key[128:192])
e = gmpy2.invert(dp,(p-1))
d = gmpy2.invert(e,(p-1)*(q-1))
print(len(long_to_bytes(d)))
其他参考:
当然也可以通过逆向找到这个RSA私钥的格式,从作者给出的封装代码中可以看出这个RSA私钥是通过白盒密码算法对ciphertext1(还是带符号的变量)解密得到的:
https://github.com/teesec-research/tckit/blob/master/globaltaskgencode.py
char *ciphertext1 = data + """ + hex(sym_map['ciphertext1']) + """;
char *ciphertext2 = data + """ + hex(sym_map['ciphertext2']) + """;
所以可以从globaltask.elf中对ciphertext1变量的使用出发进行逆向:
➜ strings globaltask.elf | grep ciphertext1
ciphertext1
可以通过IDA的Names或者Exports窗口搜索此符号,找到ciphertext1变量:
然后交叉引用可以跟到get_dx_private_key函数,通过对其中的_CC_CRYS_RSA_Build_PrivKeyCRT函数名字(CRT:中国剩余定理)以及参数,也可以确定RSA私钥的划分,其中的65字节是因为解密后的第一组数据是拷贝的起始地址是buf(v17)第二个字节,原因可能是:Leading 00 in RSA public/private key file
所以通过这个参数也能看出来私钥的0x140字节应该是被划成了五份,所以应该是p、q、dp、dq、qinv。如果想继续跟进_CC_CRYS_RSA_Build_PrivKeyCRT分析会发现进了系统调用,所以需要继续逆向TEEOS,即转换成名为Rtosck的ELF文件,在其中可以找到CRYS_RSA_Build_PrivKeyCRT函数,虽然逆向这个函数一眼看不太出来什么:
但CRYS_RSA_Build_PrivKeyCRT这个函数可搜到,是mbed-os的库函数,通过函数定义中的参数可以彻底确定私钥私钥的组成确实和猜测一致:
https://os.mbed.com/docs/mbed-os/v6.15/mbed-os-api-doxy/group__crys__rsa__build.html
CRYSError_t CRYS_RSA_Build_PrivKeyCRT ( CRYS_RSAUserPrivKey_t * UserPrivKey_ptr,
uint8_t * P_ptr,
uint16_t PSize,
uint8_t * Q_ptr,
uint16_t QSize,
uint8_t * dP_ptr,
uint16_t dPSize,
uint8_t * dQ_ptr,
uint16_t dQSize,
uint8_t * qInv_ptr,
uint16_t qInvSize
)
RSA解密manifest
现在即可使用RSA来解密TA头部的manifest,不过在解密之前我们还是通过010editor看一看TA,可以发现开头的0x18字节比较干净,应该不是加密的数据。从0x98字节开始是个字符串,所以也肯定没加密。因此 0x18 到 0x98偏移这0x80字节的数据比较乱,应该就是加密的manifest,也正好是128字节,满足RSA 1024的密文情况:
通过之前逆向globaltask.elf中的get_dx_private_key函数往上交叉引用 ,可以找到load_secure_app_image函数,其中调用parse_manifest函数的参数中有一个立即数0x18,这个偏移和我们推测的TA中加密的manifest的偏移一致:
跟入parse_manifest函数,可以看到立即数128,和我们推测的manifest密文长度也一致:
所以直接尝试使用RSA解密TA开头偏移0x18,长度为0x80字节的数据:
from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes
import gmpy2
private_key = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'
p = bytes_to_long(private_key[:64])
q = bytes_to_long(private_key[64:128])
dp = bytes_to_long(private_key[128:192])
e = gmpy2.invert(dp,(p-1))
d = gmpy2.invert(e,(p-1)*(q-1))
n = p * q
c = bytes_to_long(open('868ccafb-794b-46c6-b5c4-9f1462de4e02.sec','rb').read()[0x18:0x18+0x80])
m = long_to_bytes(pow(c,d,n))
print(len(m))
print(m.hex())
解密后的数据如下,通过其中连片的00字节,就可以判断出解密成功了:
127
02f7c9f892ef5dbb32fe00fbca8c864b79c646b5c49f1462de4e0201000000000000000000000000400100002000000000000020000000200000001900000031a4679966df8337fb391a9b4bcf1695dbba3037ce89b876165298ac9ab783c813b20f304fb0d2d0e61e37a2fdd6835756803d7ae2367b27ff17dab70f422daf
但需要注意,解密后的数据的开头是02,这是PKCS#1 v1.5 的 padding,也叫 RSA_PKCS1_PADDING,是在数据头部填充padding:
所以需要把解密数据开头的0x02 到 0x00 的数据去掉:
from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes
import gmpy2
private_key = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'
p = bytes_to_long(private_key[:64])
q = bytes_to_long(private_key[64:128])
dp = bytes_to_long(private_key[128:192])
e = gmpy2.invert(dp,(p-1))
d = gmpy2.invert(e,(p-1)*(q-1))
n = p * q
c = bytes_to_long(open('868ccafb-794b-46c6-b5c4-9f1462de4e02.sec','rb').read()[0x18:0x18+128])
m = long_to_bytes(pow(c,d,n))
i = m.find(b'\x00') + 1
open('manifest.bin','wb').write(m[i:])
最终得到的明文manifest,长度0x74字节,具体如下:
接下来就是从这0x74字节找出AES加密的KEY,另外其中也许还包含AES加密的IV。
AES密钥解析逆向
具体来说就是逆向globaltask.elf中的load_secure_app_image函数,以及其调用的两个关键函数:
- parse_manifest:解密manifest并解析
- elf_decry:对加密后的TA进行解密
确定加密TA的偏移
首先结合868ccafb-794b-46c6-b5c4-9f1462de4e02.sec分析load_secure_app_image,可以确定其调用elf_decry的第一个参数,就是加密TA文件中AES加密后TA_ELF的偏移,本例中偏移为0x1b4具体计算如下:
v15 = en_TA + 0x18; // 0x18
v20 = v15 + (0x99 & 0xFFFFFFFC) + 4; // 0x9c
v19 = *(_DWORD *)(en_TA + 0x14); // 0x100
hex(0x18 + 0x9c + 0x100) == 0x1b4
因此load_secure_app_image函数与868ccafb-794b-46c6-b5c4-9f1462de4e02.sec的对应分析的方法如下:
- *.sec文件头的0x18字节对应load_secure_app_image中解析出的一些全局变量
- MANIFEST部分交给parse_manifest函数处理,并在其中也会解析出一些全局变量
- 解密TA_ELF交给elf_decry函数,但其会使用parse_manifest解析出的一些全局变量
确定AES加密模式
跟入elf_decry函数,可以看到其使用的密码函数是符合GP标准的TEE密码函数接口:
GlobalPlatform Technology TEE Internal Core API Specification Version 1.3.1
- TEE_AllocateOperation
- TEE_SetOperationKey
- TEE_CipherInit
elf_decry调用TEE_AllocateOperation的第二个参数为0x10000110,通过TEE_AllocateOperation函数的定义:
TEE_Result TEE_AllocateOperation(
TEE_OperationHandle* operation,
uint32_t algorithm,
uint32_t mode,
uint32_t maxKeySize );
可以识别出其使用的加密算法为AES CBC (0x10000110):
#define TEE_ALG_AES_CBC_NOPAD 0x10000110
确定AES的KEY
v12 = *(_DWORD *)(v20[67] + 4);
if ( v12 )
{
memcpy(v12, dword_156764, dword_156758);
v13 = TEE_SetOperationKey(v18, (int)v20);
if ( v13 )
- dword_156764
- dword_156758
确定AES的IV
TEE_CipherInit(v18, dword_156764, dword_156758 / 2);
from Crypto.Cipher import AES
iv = open('manifest.bin','rb').read()[84:84+16]
key = open('manifest.bin','rb').read()[84:84+32]
cipher = open('868ccafb-794b-46c6-b5c4-9f1462de4e02.sec','rb').read()[0x1b4:0x1b4+32]
a = AES.new(key, AES.MODE_CBC,iv)
msg = a.decrypt(cipher)
print(msg)
➜ python3 exp.py
b'\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00'
完整解密脚本
from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES
import gmpy2
private_key = b'\xcc\x29\xd6\x21\xb9\x86\xab\xa7\x13\xa7\xa2\x61\x06\x32\x1b\x33\x8d\xd1\x12\xd8'
private_key += b'\x6f\x36\x14\xaa\x39\xcd\x1c\xd5\x9b\x1d\xf1\xfd\x5a\x17\x58\xea\x64\xc5\x3d\x76'
private_key += b'\xcb\xce\x2a\x12\x04\x23\xf7\x78\x89\xbe\x63\x5b\xa1\xd4\x0b\x22\xb8\x78\x2a\x9c'
private_key += b'\xc3\xdd\xbf\xeb\xc2\xd1\x59\x53\x2b\x07\xaf\x45\x54\x90\x37\xae\xe9\x7b\x24\x57'
private_key += b'\x42\x68\x44\x59\xce\x72\xe7\x68\xfc\x07\xae\xa7\xcd\xdb\x87\x9b\x4f\x3b\x8c\x49'
private_key += b'\xfe\xe2\x66\xbd\xc8\x77\x89\x0d\xc6\xba\x07\xac\x7a\x9f\xc0\x84\x25\xa8\x62\x66'
private_key += b'\x55\xf7\xae\x43\x68\x15\xe1\xcd\x66\x7f\x62\x77\x8f\xf2\xe2\x5e\x80\xe9\x9a\x05'
private_key += b'\xe7\xdc\x63\xf7\x9f\xed\x24\xee\xef\xf6\x50\xad\x9d\x53\x32\x74\xb2\xe9\x77\xc1'
private_key += b'\xdf\xe6\xf4\xc6\xc8\x4c\x95\xac\xfc\x68\xc6\x8a\x40\xf5\xe5\x99\xe8\x5d\x62\xf8'
private_key += b'\x6f\xe8\x4a\xa6\xe5\xc1\xbe\x72\xf1\x8a\x74\x7d\x76\x3b\xd3\xb8\x53\xdf\x20\x12'
private_key += b'\x35\x96\x29\x15\x30\x82\x19\xb6\x13\x89\x70\x22\x08\xd7\x57\x76\x31\xae\xff\xe2'
private_key += b'\xbb\x5e\xc6\x58\x0d\xa8\x18\x26\x38\x58\x72\xfe\x2f\x11\xcc\xcd\xdd\x93\xbd\x60'
private_key += b'\x82\x33\x3e\x05\x75\x4d\x52\x1a\xc5\x85\xc1\xef\x0a\xd6\x6c\xe9\x22\x41\x21\xbc'
private_key += b'\xa3\x79\xea\x2e\xd1\x40\xd3\xcc\xd2\x75\xbb\xb4\x05\x86\x91\x7a\x17\xf9\xc2\xd5'
private_key += b'\x40\x63\xbb\xe0\x60\xb8\xaa\x85\xc9\x3e\x83\x19\xca\xfe\x1c\xd9\x17\x3c\x4c\x51'
private_key += b'\xc1\xa0\xa0\xd3\xbd\x7f\xa5\xd1\x91\xec\x6d\x03\x8c\x80\x8d\xe6\x7f\xf5\x7f\xba'
p = bytes_to_long(private_key[:64])
q = bytes_to_long(private_key[64:128])
dp = bytes_to_long(private_key[128:192])
e = gmpy2.invert(dp,(p-1))
d = gmpy2.invert(e,(p-1)*(q-1))
n = p * q
c = bytes_to_long(open('868ccafb-794b-46c6-b5c4-9f1462de4e02.sec','rb').read()[0x18:0x18+128])
m = long_to_bytes(pow(c,d,n))
i = m.find(b'\x00') + 1
f = open('manifest.bin','wb')
f.write(m[i:])
f.close()
iv = open('manifest.bin','rb').read()[84:84+16]
key = open('manifest.bin','rb').read()[84:84+32]
cipher = open('868ccafb-794b-46c6-b5c4-9f1462de4e02.sec','rb').read()[0x1b4:]
a = AES.new(key, AES.MODE_CBC,iv)
msg = a.decrypt(cipher)
open('868ccafb-794b-46c6-b5c4-9f1462de4e02.elf','wb').write(msg)
可以发现解出来的TA是甚至是带符号的:
➜ file 868ccafb-794b-46c6-b5c4-9f1462de4e02.elf
868ccafb-794b-46c6-b5c4-9f1462de4e02.elf: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), not stripped
使用IDA逆向此TA可以发现确实有符号,但这个TA居然是可以调用外部函数的,例如SLog。这和OP-TEE的TA不同,OP-TEE的TA相当于静态链接,所有的函数都在静态链接到TA中,TA ELF 外部的功能只有通过svc系统调用进TEE OS完成:
回到华为 Trust Core 方案中的TA,找到SLog函数的本体实现在globaltask中,所以此方案中globaltask和TA运行时应该在同一个内存环境下:
另外 P9 Lite 上这套 Trust Core 方案的 TEE还是值得继续研究的,因为2016年极棒国外黑客nick用鼻子解锁的正是这款P9 Lite,固件版本也和目前的示例相近:
P9 Lite是在国外上市的手机,不过闲鱼上也可以买到,我买了一个,但是系统版本在2020年,尝试使用以上的私钥无法解密其中的TA。应该可以刷低版本复现2016年极棒的漏洞,之后有缘再玩:
其他相关:
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK