summaryrefslogtreecommitdiff
path: root/utils/ipodpatcher/ipodpatcher-aupd.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils/ipodpatcher/ipodpatcher-aupd.c')
-rw-r--r--utils/ipodpatcher/ipodpatcher-aupd.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/utils/ipodpatcher/ipodpatcher-aupd.c b/utils/ipodpatcher/ipodpatcher-aupd.c
new file mode 100644
index 0000000000..69b027284c
--- /dev/null
+++ b/utils/ipodpatcher/ipodpatcher-aupd.c
@@ -0,0 +1,398 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006-2007 Dave Chapman
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdio.h>
23#include <unistd.h>
24#include <fcntl.h>
25#include <string.h>
26#include <stdlib.h>
27#include <inttypes.h>
28#include <stdbool.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31
32#include "ipodpatcher.h"
33
34#include "arc4.h"
35
36static inline int le2int(unsigned char* buf)
37{
38 int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
39
40 return res;
41}
42
43static inline void int2le(unsigned int val, unsigned char* addr)
44{
45 addr[0] = val & 0xFF;
46 addr[1] = (val >> 8) & 0xff;
47 addr[2] = (val >> 16) & 0xff;
48 addr[3] = (val >> 24) & 0xff;
49}
50
51static inline uint32_t getuint32le(unsigned char* buf)
52{
53 int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
54
55 return res;
56}
57
58/* testMarker and GetSecurityBlockKey based on code from BadBlocks and
59 Kingstone, posted at http://ipodlinux.org/Flash_Decryption
60
61*/
62
63static bool testMarker(int marker)
64{
65 int mask, decrypt, temp1, temp2;
66
67 mask = (marker&0xff)|((marker&0xff)<<8)|((marker&0xff)<<16)|((marker&0xff)<<24);
68 decrypt = marker ^ mask;
69 temp1=(int)((unsigned int)decrypt>>24);
70 temp2=decrypt<<8;
71
72 if (temp1==0)
73 return false;
74
75 temp2=(int)((unsigned int)temp2>>24);
76 decrypt=decrypt<<16;
77 decrypt=(int)((unsigned int)decrypt>>24);
78
79 if ((temp1 < temp2) && (temp2 < decrypt))
80 {
81 temp1 = temp1 & 0xf;
82 temp2 = temp2 & 0xf;
83 decrypt = decrypt & 0xf;
84
85 if ((temp1 > temp2) && (temp2 > decrypt) && (decrypt != 0))
86 {
87 return true;
88 }
89 }
90 return false;
91}
92
93static int GetSecurityBlockKey(unsigned char *data, unsigned char* this_key)
94{
95 int constant = 0x54c3a298;
96 int key=0;
97 int nkeys = 0;
98 int aMarker=0;
99 int pos=0;
100 int c, count;
101 int temp1;
102 static const int offset[8]={0x5,0x25,0x6f,0x69,0x15,0x4d,0x40,0x34};
103
104 for (c = 0; c < 8; c++)
105 {
106 pos = offset[c]*4;
107 aMarker = getuint32le(data + pos);
108
109 if (testMarker(aMarker))
110 {
111 if (c<7)
112 pos =(offset[c+1]*4)+4;
113 else
114 pos =(offset[0]*4)+4;
115
116 key=0;
117
118 temp1=aMarker;
119
120 for (count=0;count<2;count++){
121 int word = getuint32le(data + pos);
122 temp1 = aMarker;
123 temp1 = temp1^word;
124 temp1 = temp1^constant;
125 key = temp1;
126 pos = pos+4;
127 }
128 int r1=0x6f;
129 int r2=0;
130 int r12;
131 int r14;
132 unsigned int r_tmp;
133
134 for (count=2;count<128;count=count+2){
135 r2=getuint32le(data+count*4);
136 r12=getuint32le(data+(count*4)+4);
137 r_tmp=(unsigned int)r12>>16;
138 r14=r2 | ((int)r_tmp);
139 r2=r2&0xffff;
140 r2=r2 | r12;
141 r1=r1^r14;
142 r1=r1+r2;
143 }
144 key=key^r1;
145
146 // Invert key, little endian
147 this_key[0] = key & 0xff;
148 this_key[1] = (key >> 8) & 0xff;
149 this_key[2] = (key >> 16) & 0xff;
150 this_key[3] = (key >> 24) & 0xff;
151 nkeys++;
152 }
153 }
154 return nkeys;
155}
156
157static int find_key(struct ipod_t* ipod, int aupd, unsigned char* key)
158{
159 int n;
160
161 /* Firstly read the security block and find the RC4 key. This is
162 in the sector preceeding the AUPD image. */
163
164 if(ipod->sectorbuf == NULL) {
165 fprintf(stderr,"[ERR] Buffer not initialized.");
166 return -1;
167 }
168 fprintf(stderr, "[INFO] Reading security block at offset 0x%08x\n",ipod->ipod_directory[aupd].devOffset-ipod->sector_size);
169 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset-ipod->sector_size) < 0) {
170 return -1;
171 }
172
173 if ((n = ipod_read(ipod, 512)) < 0) {
174 return -1;
175 }
176
177 n = GetSecurityBlockKey(ipod->sectorbuf, key);
178
179 if (n != 1)
180 {
181 fprintf(stderr, "[ERR] %d keys found in security block, can not continue\n",n);
182 return -1;
183 }
184
185 return 0;
186}
187
188int read_aupd(struct ipod_t* ipod, char* filename)
189{
190 int length;
191 int i;
192 int outfile;
193 int n;
194 int aupd;
195 struct rc4_key_t rc4;
196 unsigned char key[4];
197 unsigned long chksum=0;
198
199 if(ipod->sectorbuf == NULL) {
200 fprintf(stderr,"[ERR] Buffer not initialized.");
201 return -1;
202 }
203 aupd = 0;
204 while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD))
205 {
206 aupd++;
207 }
208
209 if (aupd == ipod->nimages)
210 {
211 fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n");
212 return -1;
213 }
214
215 length = ipod->ipod_directory[aupd].len;
216
217 fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length);
218
219 if (find_key(ipod, aupd, key) < 0)
220 {
221 return -1;
222 }
223
224 fprintf(stderr, "[INFO] Decrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]);
225
226 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) {
227 return -1;
228 }
229
230 i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1);
231
232 if ((n = ipod_read(ipod,i)) < 0) {
233 return -1;
234 }
235
236 if (n < i) {
237 fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n",
238 i,n);
239 return -1;
240 }
241
242 /* Perform the decryption - this is standard (A)RC4 */
243 matrixArc4Init(&rc4, key, 4);
244 matrixArc4(&rc4, ipod->sectorbuf, ipod->sectorbuf, length);
245
246 chksum = 0;
247 for (i = 0; i < (int)length; i++) {
248 /* add 8 unsigned bits but keep a 32 bit sum */
249 chksum += ipod->sectorbuf[i];
250 }
251
252 if (chksum != ipod->ipod_directory[aupd].chksum)
253 {
254 fprintf(stderr,"[ERR] Decryption failed - checksum error\n");
255 return -1;
256 }
257 fprintf(stderr,"[INFO] Decrypted OK (checksum matches header)\n");
258
259 outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666);
260 if (outfile < 0) {
261 fprintf(stderr,"[ERR] Couldn't open file %s\n",filename);
262 return -1;
263 }
264
265 n = write(outfile,ipod->sectorbuf,length);
266 if (n != length) {
267 fprintf(stderr,"[ERR] Write error - %d\n",n);
268 }
269 close(outfile);
270
271 return 0;
272}
273
274int write_aupd(struct ipod_t* ipod, char* filename)
275{
276 unsigned int length;
277 int i;
278 int x;
279 int n;
280 int infile;
281 int newsize;
282 int aupd;
283 unsigned long chksum=0;
284 struct rc4_key_t rc4;
285 unsigned char key[4];
286
287 if(ipod->sectorbuf == NULL) {
288 fprintf(stderr,"[ERR] Buffer not initialized.");
289 return -1;
290 }
291 /* First check that the input file is the correct type for this ipod. */
292 infile=open(filename,O_RDONLY);
293 if (infile < 0) {
294 fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename);
295 return -1;
296 }
297
298 length = filesize(infile);
299 newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1);
300
301 fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n",
302 length,newsize);
303
304 if (newsize > BUFFER_SIZE) {
305 fprintf(stderr,"[ERR] Input file too big for buffer\n");
306 if (infile >= 0) close(infile);
307 return -1;
308 }
309
310 /* Find aupd image number */
311 aupd = 0;
312 while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD))
313 {
314 aupd++;
315 }
316
317 if (aupd == ipod->nimages)
318 {
319 fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n");
320 return -1;
321 }
322
323 if (length != ipod->ipod_directory[aupd].len)
324 {
325 fprintf(stderr,"[ERR] AUPD image (%d bytes) differs in size to %s (%d bytes).\n",
326 ipod->ipod_directory[aupd].len, filename, length);
327 return -1;
328 }
329
330 if (find_key(ipod, aupd, key) < 0)
331 {
332 return -1;
333 }
334
335 fprintf(stderr, "[INFO] Encrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]);
336
337 /* We now know we have enough space, so write it. */
338
339 fprintf(stderr,"[INFO] Reading input file...\n");
340 n = read(infile,ipod->sectorbuf,length);
341 if (n < 0) {
342 fprintf(stderr,"[ERR] Couldn't read input file\n");
343 close(infile);
344 return -1;
345 }
346 close(infile);
347
348 /* Pad the data with zeros */
349 memset(ipod->sectorbuf+length,0,newsize-length);
350
351 /* Calculate the new checksum (before we encrypt) */
352 chksum = 0;
353 for (i = 0; i < (int)length; i++) {
354 /* add 8 unsigned bits but keep a 32 bit sum */
355 chksum += ipod->sectorbuf[i];
356 }
357
358 /* Perform the encryption - this is standard (A)RC4 */
359 matrixArc4Init(&rc4, key, 4);
360 matrixArc4(&rc4, ipod->sectorbuf, ipod->sectorbuf, length);
361
362 if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) {
363 fprintf(stderr,"[ERR] Seek failed\n");
364 return -1;
365 }
366
367 if ((n = ipod_write(ipod,newsize)) < 0) {
368 perror("[ERR] Write failed\n");
369 return -1;
370 }
371
372 if (n < newsize) {
373 fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n"
374 ,newsize,n);
375 return -1;
376 }
377 fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n);
378
379 x = ipod->diroffset % ipod->sector_size;
380
381 /* Read directory */
382 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
383
384 n=ipod_read(ipod, ipod->sector_size);
385 if (n < 0) { return -1; }
386
387 /* Update checksum */
388 fprintf(stderr,"[INFO] Updating checksum to 0x%08x (was 0x%08x)\n",(unsigned int)chksum,le2int(ipod->sectorbuf + x + aupd*40 + 28));
389 int2le(chksum,ipod->sectorbuf+x+aupd*40+28);
390
391 /* Write directory */
392 if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; }
393 n=ipod_write(ipod, ipod->sector_size);
394 if (n < 0) { return -1; }
395
396 return 0;
397}
398