概述
描述iOS固件中的img3格式文件在解密中用到的key和iv是如何计算的?
以及在什么情况下可以进行计算。
基础
首先描述加密内核的文件结构以及解密过程,以iPhone4S iOS 8.3b2为例
http://theiphonewiki.com/wiki/StoweVail_12F5037c_%28iPhone4,1%29
文件结构:
加密内核文件kernelcache.release.n94是一个img3格式文件。由img3文件头和数据内容组成。文件头包含12个字节,结构如下。
typedef struct AppleImg3Header{
u32 magic;
u32 size;
u32 dataSize;
}AppleImg3Header;
$ ./readimg3 ./kernelcache.83b2.n94
offset[00000000] magic [33676d49][Img3] size[ 9148172] dataSize[ 9148152]
从offset=9148172-9148152=20=0x14开始的数据部分,又由一系列的数据块堆积而成,truck、element(xpwntool中的叫法)what ever,反正就是这个意思。
这些数据块仍由文件头和数据组成,而且文件头的结构和img3一致。
offset[00000014] magic [45505954][TYPE] size[ 32] dataSize[ 4]
offset[00000034] magic [41544144][DATA] size[ 9147884] dataSize[ 9147859]
offset[008b9620] magic [4f504553][SEPO] size[ 28] dataSize[ 4]
offset[008b963c] magic [4741424b][KBAG] size[ 76] dataSize[ 56]
offset[008b9688] magic [4741424b][KBAG] size[ 132] dataSize[ 56]
TYPE 里的4个字节,只是说明这是一个内核(krnl)。
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000010 45 50 59 54 20 00 00 00 04 00 00 00 EPYT
00000020 6C 6E 72 6B 00 00 00 00 00 00 00 00 00 00 00 00 lnrk
00000030 00 00 00 00
DATA 是加密后的内核数据。
SEPO 数据内容为0x00000011,含义不明,xpwntool也没有关心这个区域
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
008B9620 4F 50 45 53 1C 00 00 00 04 00 00 00 11 00 00 00 OPES
008B9630 00 00 00 00 00 00 00 00 00 00 00 00
两个KBAG放了两组seed,48个有效数据和8个零组成。KBAG的文件头结构如下:
typedef struct AppleImg3KBAGHeader {
uint32_t key_modifier; // key modifier, can be 0 or 1
uint32_t key_bits; // number of bits in the key, can be 128, 192 or 256 (it seems only 128 is supported in current iBoot)
} AppleImg3KBAGHeader;
解析出来的内容:
offset[008b963c] magic [4741424b][KBAG] size[ 76] dataSize[ 56]
kbag offset[008b9648] key_modifier[1] key_bits[256]
[85 b1 c8 ac e8 98 b9 18 00 e8 c7 eb 11 44 37 b0 e7 71 30 28 0d f7 d6 bb e2 f0 c4 97 dd 2b d2 c1 89 59 1e a1 6c 82 10 c1 67 f1 fe 39 e4 9e 30 52 00 00 00 00 00 00 00 00]
offset[008b9688] magic [4741424b][KBAG] size[ 132] dataSize[ 56]
kbag offset[008b9694] key_modifier[2] key_bits[256]
[db 3b c7 45 38 99 b8 91 86 8e 76 ae 13 e2 09 70 db db b8 f9 da 77 12 d4 bc 34 b9 18 32 3e a3 2b 1b bd b7 16 10 bb 76 1f 24 7d e3 5b 8e 31 ea 69 00 00 00 00 00 00 00 00]
解密过程:
DATA 使用AES算法的分组循环加密模式,每个分组为16个字节,对应的keysize为256bits,因为是循环模式,这里多出来一个初始向量的概念。
关于AES算法参考:
http://www.cnblogs.com/mydomain/archive/2013/05/31/3109590.html
http://blog.csdn.net/yasi_xi/article/details/13997337
主要使用的函数:
//设置加密密钥,使用字符缓冲区
int AES_set_encrypt_key(
const unsigned char *userKey,
const int bits,
AES_KEY *key);
//设置解密密钥,同样适用字符缓冲区
int AES_set_decrypt_key(
const unsigned char *userKey,
const int bits,
AES_KEY *key);
//加解密的接口,通过最后的enc来区分是加密还是解密操作
//每次执行AES_cbc_encrypt后,iv(向量)会被更新,
//所以需要自己保存它。
void AES_cbc_encrypt(
const unsigned char *in,
unsigned char *out,
const unsigned long length,
const AES_KEY *key,
unsigned char *ivec,
const int enc);
标准的解密命令:
export IV=9b2e935f8460d2ecf83ba3700f4e0d6a 16bytes 128bits
export KEY=3c4189279895eda193dd95272616e265786c7da19e32e231df5031c89122a015 32bytes 256bits
xpwntool.exe ./kernelcache.83b2.n94 kernel_decrypt -iv $IV -k $KEY -decrypt
xpwntool估计是考虑的比较多,写的有些复杂,为了便于理解,我进行了一些简化,见readimg3.c
简单说解密只需要调用AES_set_decrypt_key将环境变量里的KEY进行设置,再调用AES_cbc_encrypt对上面数据区的内容进行解密就完事了,这里用到初始向量IV。
是的,很简单的流程!
Note:
对比xpwntool和我写的这个代码内核解密后的结果,xpwntool多出来前0x40的img3文件头、TYPE数据块以及DATA文件头:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 33 67 6D 49 3C 96 8B 00 28 96 8B 00 F8 96 8B 00 3gmI<枊 (枊 鴸?
00000010 6C 6E 72 6B 45 50 59 54 20 00 00 00 04 00 00 00 lnrkEPYT
00000020 6C 6E 72 6B 00 00 00 00 00 00 00 00 00 00 00 00 lnrk
00000030 00 00 00 00 41 54 41 44 EC 95 8B 00 D3 95 8B 00 ATAD鞎?訒?
Xpwntool在尾部上多出来SEPO数据块:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
008B9620 4F 50 45 53 1C 00 00 00 04 00 00 00 11 00 00 00 OPES
008B9630 00 00 00 00 00 00 00 00 00 00 00 00
再有就是数据段中,最后4个字节的内容不一致:
分析
这里可以想象的到,算key的过程就是将kbag里的两组seed带入到iboot的函数中,然后就有了key和iv。
更详细的KBAG结构
http://theiphonewiki.com/wiki/KBAG#KBAG256
针对key_bits为256位:
typedef struct Unparsed_KBAG_256 {
uint32_t magic; // string with bytes flipped ("KBAG" in little endian)
uint32_t fullSize; // size of KBAG from beyond that point to the end of it
uint32_t tagDataSize; // size of KBAG without this 0xC header
uint32_t cryptState; // 1 if the key and IV in the KBAG are encrypted with the GID Key
// 2 is used with a second KBAG for the S5L8920, use is unknown.
uint32_t aesType; // 0x80 = aes128 / 0xc0 = aes192 / 0x100 = aes256
uint8_t encIV[16]; // IV for the firmware file, encrypted with the GID Key
uint8_t encKey[32]; // Key for the firmware file, encrypted with the GID Key
} UparsedKbagAes256_t;
密钥长度根据aesType可以是128bits,192bits和256bits,对应过来encKey包含16Bytes,24Bytes或32Bytes。
这里的encIV和encKey的意思是经过GID Key加密后的IV和Key。
GID Key
http://theiphonewiki.com/wiki/GID_Key
GID Key是Group ID key的缩写,是一个256Bits的密钥,固化在应用处理器(iOS设备的主芯片,俗称A4、A5……,准确的叫法应该称他的型号,例如上面这个S5L8920)中,而且每一款应用处理器(Application Processor)固化的值都不会一样。
下面这个A4的图片,对应型号S5L8930
因为GIDKey被固化在AP中,至于怎么固化的,是烧的熔丝位,还是怎么个方法还不清楚。目前也没看到有直接能读出GIDKey的办法。
而通过AP中的AES硬件加速器可以让GIDKey参与运算,这也是官方使用的方法。
这也说明当我们不知道GIDKey具体值的情况下,不能用PC来算,只能用iOS设备来算key。
下面是iPhone4S iOS 8.3b2版本的iBoot代码,核心内容在image3_load_decrypt_payload函数中
RAM:9FF197C0 image3_load_decrypt_payload
调用了aes_crypto_cmd,来使用AES硬件加速器。
函数原型:
uint32_t aes_crypto_cmd(uint32_t encrypt, void *inBuf, void *outBuf, uint32_t size, uint32_t type, void *key, void *iv);
参数说明:
encrypt : 0x10 加密 0x11 解密
type:
switch(_keyType)
{
case AESCustom:
switch(keyLen)
{
case AES128:
keyType = 0 << 28;
break;
case AES192:
keyType = 1 << 28;
break;
case AES256:
keyType = 2 << 28;
break;
default:
return;
}
break;
case AESGID:
keyType = 512 | (2 << 28);
break;
case AESUID:
keyType = 513 | (2 << 28);
break;
default:
return;
}
0x00000000 -> 128Bits
0x20000000 -> 256Bits
0x10000000 -> 192Bits
0x20000200 -> AESGID
0x20000201 -> AESUID
至于aes_crypto_cmd再向底层,设置了哪些寄存器,怎么做dma操作的,可参考逆向版本:
https://github.com/iDroid-Project/openiBoot
plat-a4\cdma.c文件。
signed int __fastcall image3_load_decrypt_payload(int a1, int a2, unsigned int a3, int a4, int a5, unsigned int *a6)
{
...
keybits = *(_DWORD *)(v90 + 4); //v90指向kbag结构体cryptState
if ( keybits == 128 )
{
v67 = 0;
}
else if ( keybits == 256 )
{
v67 = 0x20000000;
}
else
{
if ( keybits != 192 )
break;
v67 = 0x10000000;
}
type = v67;
sub_9FF3337C((int)&enIV, v90 + 8, 0x10u); //将加密后的IV信息保存在局部变量enIV中,共16字节
keybytes = keybits >> 3;
sub_9FF3337C((int)&enKey, v90 + 24, keybytes); //根据密钥长度,将加密后的Key信息保存在局部变量enKey
if ( !*(_DWORD *)v90 )
goto LABEL_87;
v89 = 0;
if ( !set_AESGID(*(_DWORD *)v90, (int)&v89) ) //设置使用内部GIDKey参与AES运行,v89对应0x20000200
{
if ( aes_crypto_cmd(0x11, (int)&enIV, (int)&enIV, keybytes + 16, v89, 0, 0) ) //对IV和Key同时解密,栈上IV和Key是连续的
break;
LABEL_87:
LOBYTE(size) = v72;
if ( v72 & 0xF )
size = v72 + 16 - (v72 & 0xF);
v64 = aes_crypto_cmd(0x11, inbuf, oubuf, size, type, (int)&enKey, (int)&enIV); //用解密出的IV和Key,做进一步解密
sub_9FF33698(&enIV, 0, 48);
break;
}
...
}
算Key核心代码
aes_crypto_cmd(0x11, (int)&enIV+enKey, (int)&IV+Key, keybytes + 16, 0x20000200, 0, 0) //这里用内部GIDKey,IV和Key就不指定了
验证分析结果,计算IV和Key
这里实际计算一组IV和Key。
如何使用硬件加密引擎,下面介绍了几种方法
http://theiphonewiki.com/wiki/AES_Keys
下面更有较详细的操作步骤。
http://theiphonewiki.com/wiki/Obtaining_IMG3_Keys
里面介绍的方法都已过时,无论是iBEC还是iBoot,虽然可以patch iRecovery command “clearenv”的处理函数,跳转到指定的地址直接运行,例如上面链接给出的0x9000000
ROM:180074A0 LDR R3, =0x9000000
ROM:180074A2 BX R3
但通过mw命令写内存,放置payload和kbag数据;调用clearenv执行payload;调用命令mdb显示执行结果的mw和mdb已经不存在。
iPhone4S iOS 8.3b2版本的iBoot里就只有下面7条命令:
RAM:9FF41584 off_9FF41584 DCD aReboot ; DATA XREF: RAM:9FF44830o
RAM:9FF41584 ; "reboot"
RAM:9FF41588 DCD cli_reset+1
RAM:9FF4158C DCD aRebootTheDevic ; "reboot the device"
RAM:9FF41590 DCD 0
RAM:9FF41594 off_9FF41594 DCD aReset ; DATA XREF: RAM:9FF44834o
RAM:9FF41594 ; "reset"
RAM:9FF41598 DCD cli_reset+1
RAM:9FF415D4 off_9FF415D4 DCD aGo ; DATA XREF: RAM:9FF44838o
RAM:9FF415D4 ; "go"
RAM:9FF415D8 DCD cli_go+1
RAM:9FF415DC DCD aJumpDirectlyTo ; "jump directly to address"
RAM:9FF415E0 DCD 0
RAM:9FF41A58 DCD aGetenv ; "getenv"
RAM:9FF41A5C DCD cli_getenv+1
RAM:9FF41A60 DCD aGetEnvironment ; "get environment variable over usb"
RAM:9FF41A64 DCD 0
RAM:9FF41A68 DCD aSetenv ; "setenv"
RAM:9FF41A6C DCD cli_setenv+1
RAM:9FF41A70 DCD aSetAnEnvironme ; "set an environment variable"
RAM:9FF41A74 DCD 0
RAM:9FF41A78 DCD aSaveenv ; "saveenv"
RAM:9FF41A7C DCD 0x9FF17955
RAM:9FF41A80 DCD aSaveCurrentEnv ; "save current environment to flash"
RAM:9FF41A84 DCD 0
RAM:9FF41A88 DCD aTicket ; "ticket"
RAM:9FF41A8C DCD cli_ticket+1
RAM:9FF41A90 DCD unk_9FF3492D
RAM:9FF41A94 DCD 0
而且这个"go"也不是"jump directly to address",他也是要解析img3文件的。
这也是为什么上面要patch "clearenv"(现在没有clearenv,可以换个命令来patch)。
再说Patched的iBEC要运行起来需要bootrom漏洞。
Patched的iBoot要怎么运行?从LLB看iBoot保存在NAND Flash的boot扇区(25C3 "Hacking the iPhone" 说保存在norflash上,可能是不同的机型?)。
反正正常情况下都是LLB来引导iBoot,那要写NAND,但把他当成iBEC用iBSS来引导理论上估计也能行。但同样要bootrom漏洞。
虽然方法不能用,但核心思想就是想办法调用aes_crypto_cmd。
那我们从内核来调用aes_crypto_cmd可以么?
从内核export的和aes有关的函数符号,例如aes_decrypt_cbc,一路找下来,找到了内核模块com.apple.kec.corecrypto里的函数。例如806EBADD(6.0 n81)
但怎么看都是利用软件算法来计算的……
虽然iOS上应该会用到UIDKey吧,但还没找到和硬件AES引擎有关的代码。
Patch kernel,把整个硬件调用的代码流程加进去?这是个大工程,还是找简单的办法来验证。
还是用bootrom漏洞吧,选择itouch4g,利用limera1n。
所以最开始的命题变成:
当可以访问硬件AES引擎(GIDKey)时,求证6.0版本itouch4g内核文件的密钥为:
<key>IV</key><string>c856cb32d49cf677b1031582560c07f6</string>
<key>Key</key><string>53503a0603a5e4192df5985ac5817eb1001abaa23e9fc6735dee392a4c063934</string>
Key值参考:
http://theiphonewiki.com/wiki/Sundance_10A403_%28iPod4,1%29
因limera1n可以让payload直接运行,构建payload调用bootrom中的aes_crypto_cmd函数,将kbag中的值传入,即可求解。
KeyBag:
$ ./readimg3.exe ./kernelcache.release.n81
offset[00000000] magic [33676d49][Img3] size[ 7297284] dataSize[ 7297264]
offset[00000014] magic [45505954][TYPE] size[ 32] dataSize[ 4]
offset[00000034] magic [41544144][DATA] size[ 7294940] dataSize[ 7294916]
data offset[00000040] data len[7294928]
offset[006f5010] magic [4f504553][SEPO] size[ 28] dataSize[ 4]
offset[006f502c] magic [4741424b][KBAG] size[ 76] dataSize[ 56]
offset[006f5078] magic [4741424b][KBAG] size[ 84] dataSize[ 56]
offset[006f50cc] magic [48534853][SHSH] size[ 140] dataSize[ 128]
offset[006f5158] magic [54524543][CERT] size[ 1964] dataSize[ 1940]
kbag offset[006f5038] key_modifier[1] key_bits[256]
kbag
[b8 7f e7 69 17 3d 1c cc c7 39 aa 59 05 a5 10 be 81 71 fc 33 92 8f a3 98
9a c2 54 0b 12 01 75 76 ae a8 69 3b f7 76 fc ba 00 0e e6 78 c6 65 82 f0 00 00 00
00 00 00 00 00]
kbag offset[006f5084] key_modifier[2] key_bits[256]
kbag
[f8 ff 67 9a f0 40 d6 49 61 ae 7a e9 44 a8 15 fa a3 27 1a 6b a1 28 84 7a
f3 23 3c 37 63 b0 aa 8b de 05 59 90 ca 40 12 94 0f f7 c8 ba a8 c8 aa 8b 00 00 00
00 00 00 00 00]
使用
https://github.com/Chronic-Dev/Bootrom-Dumper
替换payload为:
.pool
@ usb_wait_for_image call offset
.set RET_ADDR, 0x7ef @ A4
.set loadaddr, 0x84000000
.set maxsize, 0x24000
.set a4_aes_crypto_cmd, 0x686d
.set gidtype, 0x20000200
.text
@main code -----------------------------------
.code 16
_start: .global _start
B entry_point
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
@ aes_crypto_cmd(0x11, (int)&iv+key, (int)&iv+key, 256/8 + 16, 0x20000200, 0, 0)
entry_point:
SUB SP, SP, #16
LDR R1, =gidtype
STR R1, [SP]
MOV R1, #0
STR R1, [SP, #4]
STR R1, [SP, #8]
MOV R0, #0x11
ADR R1, KEYBAG
LDR R2, =loadaddr
MOV R3, #48
LDR R4, =a4_aes_crypto_cmd
BLX R4
ADD SP, SP, #16
LDR R0, =loadaddr
LDR R1, =maxsize
MOV R2, #0
LDR R3, =RET_ADDR
BLX R3
KEYBAG:
.align 1
.byte 0xb8, 0x7f, 0xe7, 0x69, 0x17, 0x3d, 0x1c, 0xcc, 0xc7, 0x39, 0xaa, 0x59, 0x05, 0xa5, 0x10, 0xbe
.byte 0x81, 0x71, 0xfc, 0x33, 0x92, 0x8f, 0xa3, 0x98, 0x9a, 0xc2, 0x54, 0x0b, 0x12, 0x01, 0x75, 0x76
.byte 0xae, 0xa8, 0x69, 0x3b, 0xf7, 0x76, 0xfc, 0xba, 0x00, 0x0e, 0xe6, 0x78, 0xc6, 0x65, 0x82, 0xf0
.end
计算结果:
前16字节的IV和后32字节的KEY和网站公布结果一致!
更换第二组KBAG:
KEYBAG1:
.align 1
.byte 0xf8, 0xff, 0x67, 0x9a, 0xf0, 0x40, 0xd6, 0x49, 0x61, 0xae, 0x7a, 0xe9, 0x44, 0xa8, 0x15, 0xfa
.byte 0xa3, 0x27, 0x1a, 0x6b, 0xa1, 0x28, 0x84, 0x7a, 0xf3, 0x23, 0x3c, 0x37, 0x63, 0xb0, 0xaa, 0x8b
.byte 0xde, 0x05, 0x59, 0x90, 0xca, 0x40, 0x12, 0x94, 0x0f, 0xf7, 0xc8, 0xba, 0xa8, 0xc8, 0xaa, 0x8b
.end
计算结果:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 A1 CB 45 F8 21 60 F0 F7 E4 A7 25 B6 B2 F4 98 69 ∷E?`瘅洄%恫魳i
00000010 4D CD 55 B2 9A 88 D3 CB 73 DB DA 9C 00 4E 2E A5 M蚒矚堄藄圳?N.?
00000020 41 68 12 EE 6D 80 47 63 29 90 45 24 53 09 74 34 Ah 頼€Gc)怑$S t4
这个结果又是做什么用处的???
结论
如可访问硬件AES引擎(GIDKey),则可计算使用该AP芯片的iOS设备上的所有固件版本中文件的IV和Key。