计算iOS固件img3文件key的方法

概述描述iOS固件中的img3格式文件在解密中用到的key和iv是如何计算的?
以及在什么情况下可以进行计算。
基础首先描述加密内核的文件结构以及解密过程,以iPhone4S iOS 8.3b2为例
http://theiphonewiki.com/wiki/StoweVail_12F5037c_%28iPhone4,1%29
**文件结构:**加密内核文件kernelcache.release.n94是一个img3格式文件。由img3文件头和数据内容组成。文件头包含12个字节,结构如下。
typedefstruct 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的文件头结构如下:
typedefstruct 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 (itseems only 128 is supported in current iBoot)
}AppleImg3KBAGHeader;
解析出来的内容:
offset[008b963c]magic [4741424b][KBAG] size 76]dataSize 56]
kbagoffset[008b9648] key_modifier[1] key_bits[256]
[85b1 c8 ac e8 98 b9 18 00 e8 c7 eb 11 44 37 b0 e7 71 30 28 0d f7 d6 bb e2 f0 c497 dd 2b d2 c1 89 59 1e a1 6c 82 10 c1 67 f1 fe 39 e4 9e 30 52 00 00 00 00 0000 00 00]
offset[008b9688]magic [4741424b][KBAG] size 132]dataSize 56]
kbagoffset[008b9694] key_modifier[2] key_bits[256]
[db3b c7 45 38 99 b8 91 86 8e 76 ae 13 e2 09 70 db db b8 f9 da 77 12 d4 bc 34 b918 32 3e a3 2b 1b bd b7 16 10 bb 76 1f 24 7d e3 5b 8e 31 ea 69 00 00 00 00 0000 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);

标准的解密命令:
exportIV=9b2e935f8460d2ecf83ba3700f4e0d6a 16bytes128bits
exportKEY=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位:
typedefstruct Unparsed_KBAG_256 {
uint32_t magic; // string with bytes flipped(“KBAG” in little endian)
uint32_t fullSize; // size of KBAG from beyond that point tothe end of it
uint32_t tagDataSize; // size of KBAGwithout this 0xC header
uint32_t cryptState; // 1 if the key and IV in the KBAG areencrypted with the GID Key
// 2 is used with asecond 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 withthe GID Key
uint8_t encKey[32]; // Key for the firmware file, encrypted withthe GID Key
}UparsedKbagAes256_t;
密钥长度根据aesType可以是128bits,192bits和256bits,对应过来encKey包含16Bytes,24Bytes或32Bytes。
这里的encIV和encKey的意思是经过GIDKey加密后的IV和Key。
GID Keyhttp://theiphonewiki.com/wiki/GID_Key
GIDKey是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:9FF197C0image3_load_decrypt_payload
调用了aes_crypto_cmd,来使用AES硬件加速器。
函数原型:
uint32_taes_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文件。

signedint __fastcall image3_load_decrypt_payload(int a1, int a2, unsigned int a3, inta4, 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,虽然可以patchiRecovery command “clearenv”的处理函数,跳转到指定的地址直接运行,例如上面链接给出的0x9000000
ROM:180074A0 LDR R3, =0x9000000
ROM:180074A2 BX R3
但通过mw命令写内存,放置payload和kbag数据;调用clearenv执行payload;调用命令mdb显示执行结果的mw和mdb已经不存在。
iPhone4SiOS 8.3b2版本的iBoot里就只有下面7条命令:
RAM:9FF41584off_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:9FF41594off_9FF41594 DCD aReset ; DATA XREF: RAM:9FF44834o
RAM:9FF41594 ;“reset”
RAM:9FF41598 DCD cli_reset+1
RAM:9FF415D4off_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 overusb”
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 toflash”
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 toaddress",他也是要解析img3文件的。
这也是为什么上面要patch “clearenv”(现在没有clearenv,可以换个命令来patch)。

再说Patched的iBEC要运行起来需要bootrom漏洞。
Patched的iBoot要怎么运行?从LLB看iBoot保存在NANDFlash的boot扇区(25C3 “Hackingthe 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引擎有关的代码。
Patchkernel,把整个硬件调用的代码流程加进去?这是个大工程,还是找简单的办法来验证。

还是用bootrom漏洞吧,选择itouch4g,利用limera1n。
所以最开始的命题变成:
当可以访问硬件AES引擎(GIDKey)时,求证6.0版本itouch4g内核文件的密钥为:
IVc856cb32d49cf677b1031582560c07f6
Key53503a0603a5e4192df5985ac5817eb1001abaa23e9fc6735dee392a4c063934
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]
dataoffset[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]

kbagoffset[006f5038] key_modifier[1] key_bits[256]
kbag
[b87f 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 fcba 00 0e e6 78 c6 65 82 f0 00 00 00 kbagoffset[006f5084] key_modifier[2] key_bits[256]
kbag
[f8ff 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 0559 90 ca 40 12 94 0f f7 c8 ba a8 c8 aa 8b 00 00 00

使用
[url=https://github.com/Chronic-Dev/Bootrom-Dumper]GitHub - Chronic-Dev/Bootrom-Dumper: Utility to Dump iPhone Bootrom
替换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
@maincode -----------------------------------
.code16
_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。

3 个赞

写的很好,就是基础太差,看不懂。