diff options
Diffstat (limited to 'utils/ipodpatcher/ipodpatcher.c')
-rw-r--r-- | utils/ipodpatcher/ipodpatcher.c | 2350 |
1 files changed, 2350 insertions, 0 deletions
diff --git a/utils/ipodpatcher/ipodpatcher.c b/utils/ipodpatcher/ipodpatcher.c new file mode 100644 index 0000000000..e047e52abe --- /dev/null +++ b/utils/ipodpatcher/ipodpatcher.c | |||
@@ -0,0 +1,2350 @@ | |||
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 "parttypes.h" | ||
33 | #include "ipodio.h" | ||
34 | #include "ipodpatcher.h" | ||
35 | |||
36 | #ifdef WITH_BOOTOBJS | ||
37 | #include "ipod1g2g.h" | ||
38 | #include "ipod3g.h" | ||
39 | #include "ipod4g.h" | ||
40 | #include "ipodmini1g.h" | ||
41 | #include "ipodmini2g.h" | ||
42 | #include "ipodcolor.h" | ||
43 | #include "ipodnano1g.h" | ||
44 | #include "ipodvideo.h" | ||
45 | #include "ipodnano2g.h" | ||
46 | #endif | ||
47 | |||
48 | #ifndef RBUTIL | ||
49 | #include "arc4.h" | ||
50 | #endif | ||
51 | |||
52 | int ipod_verbose = 0; | ||
53 | |||
54 | |||
55 | /* The following string appears at the start of the firmware partition */ | ||
56 | static const char apple_stop_sign[] = "{{~~ /-----\\ "\ | ||
57 | "{{~~ / \\ "\ | ||
58 | "{{~~| | "\ | ||
59 | "{{~~| S T O P | "\ | ||
60 | "{{~~| | "\ | ||
61 | "{{~~ \\ / "\ | ||
62 | "{{~~ \\-----/ "\ | ||
63 | "Copyright(C) 200"\ | ||
64 | "1 Apple Computer"\ | ||
65 | ", Inc.----------"\ | ||
66 | "----------------"\ | ||
67 | "----------------"\ | ||
68 | "----------------"\ | ||
69 | "----------------"\ | ||
70 | "----------------"\ | ||
71 | "---------------"; | ||
72 | |||
73 | /* Windows requires the buffer for disk I/O to be aligned in memory on a | ||
74 | multiple of the disk volume size - so we use a single global variable | ||
75 | and initialise it with ipod_alloc_buf() | ||
76 | */ | ||
77 | |||
78 | char* get_parttype(unsigned int pt) | ||
79 | { | ||
80 | int i; | ||
81 | static char unknown[]="Unknown"; | ||
82 | |||
83 | if (pt == PARTTYPE_HFS) { | ||
84 | return "HFS/HFS+"; | ||
85 | } | ||
86 | |||
87 | i=0; | ||
88 | while (parttypes[i].name != NULL) { | ||
89 | if (parttypes[i].type == pt) { | ||
90 | return (parttypes[i].name); | ||
91 | } | ||
92 | i++; | ||
93 | } | ||
94 | |||
95 | return unknown; | ||
96 | } | ||
97 | |||
98 | off_t filesize(int fd) { | ||
99 | struct stat buf; | ||
100 | |||
101 | if (fstat(fd,&buf) < 0) { | ||
102 | perror("[ERR] Checking filesize of input file"); | ||
103 | return -1; | ||
104 | } else { | ||
105 | return(buf.st_size); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | /* Partition table parsing code taken from Rockbox */ | ||
110 | |||
111 | #define MAX_SECTOR_SIZE 2048 | ||
112 | #define SECTOR_SIZE 512 | ||
113 | |||
114 | static inline unsigned short le2ushort(unsigned char* buf) | ||
115 | { | ||
116 | unsigned short res = (buf[1] << 8) | buf[0]; | ||
117 | |||
118 | return res; | ||
119 | } | ||
120 | |||
121 | static inline int le2int(unsigned char* buf) | ||
122 | { | ||
123 | int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | ||
124 | |||
125 | return res; | ||
126 | } | ||
127 | |||
128 | static inline int be2int(unsigned char* buf) | ||
129 | { | ||
130 | int32_t res = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; | ||
131 | |||
132 | return res; | ||
133 | } | ||
134 | |||
135 | static inline int getint16le(char* buf) | ||
136 | { | ||
137 | int16_t res = (buf[1] << 8) | buf[0]; | ||
138 | |||
139 | return res; | ||
140 | } | ||
141 | |||
142 | static inline void short2le(unsigned short val, unsigned char* addr) | ||
143 | { | ||
144 | addr[0] = val & 0xFF; | ||
145 | addr[1] = (val >> 8) & 0xff; | ||
146 | } | ||
147 | |||
148 | static inline void int2le(unsigned int val, unsigned char* addr) | ||
149 | { | ||
150 | addr[0] = val & 0xFF; | ||
151 | addr[1] = (val >> 8) & 0xff; | ||
152 | addr[2] = (val >> 16) & 0xff; | ||
153 | addr[3] = (val >> 24) & 0xff; | ||
154 | } | ||
155 | |||
156 | static inline void int2be(unsigned int val, unsigned char* addr) | ||
157 | { | ||
158 | addr[0] = (val >> 24) & 0xff; | ||
159 | addr[1] = (val >> 16) & 0xff; | ||
160 | addr[2] = (val >> 8) & 0xff; | ||
161 | addr[3] = val & 0xFF; | ||
162 | } | ||
163 | |||
164 | |||
165 | #define BYTES2INT32(array,pos)\ | ||
166 | ((long)array[pos] | ((long)array[pos+1] << 8 ) |\ | ||
167 | ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) | ||
168 | |||
169 | int read_partinfo(struct ipod_t* ipod, int silent) | ||
170 | { | ||
171 | int i; | ||
172 | unsigned long count; | ||
173 | |||
174 | if(ipod->sectorbuf == NULL) { | ||
175 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
176 | return -1; | ||
177 | } | ||
178 | |||
179 | count = ipod_read(ipod,ipod->sector_size); | ||
180 | |||
181 | if (count <= 0) { | ||
182 | ipod_print_error(" Error reading from disk: "); | ||
183 | return -1; | ||
184 | } | ||
185 | |||
186 | memset(ipod->pinfo, 0, sizeof(ipod->pinfo)); | ||
187 | |||
188 | if ((ipod->sectorbuf[510] == 0x55) && (ipod->sectorbuf[511] == 0xaa)) { | ||
189 | /* DOS partition table */ | ||
190 | ipod->macpod = 0; | ||
191 | /* parse partitions */ | ||
192 | for ( i = 0; i < 4; i++ ) { | ||
193 | unsigned char* ptr = ipod->sectorbuf + 0x1be + 16*i; | ||
194 | ipod->pinfo[i].type = ptr[4]; | ||
195 | ipod->pinfo[i].start = BYTES2INT32(ptr, 8); | ||
196 | ipod->pinfo[i].size = BYTES2INT32(ptr, 12); | ||
197 | |||
198 | /* extended? */ | ||
199 | if ( ipod->pinfo[i].type == 5 ) { | ||
200 | /* not handled yet */ | ||
201 | } | ||
202 | } | ||
203 | } else if ((ipod->sectorbuf[0] == 'E') && (ipod->sectorbuf[1] == 'R')) { | ||
204 | /* Apple Partition Map */ | ||
205 | |||
206 | /* APM parsing code based on the check_mac_partitions() function in | ||
207 | ipodloader2 - written by Thomas Tempelmann and released | ||
208 | under the GPL. */ | ||
209 | |||
210 | int blkNo = 1; | ||
211 | int partBlkCount = 1; | ||
212 | int partBlkSizMul = ipod->sectorbuf[2] / 2; | ||
213 | |||
214 | int pmMapBlkCnt; /* # of blks in partition map */ | ||
215 | int pmPyPartStart; /* physical start blk of partition */ | ||
216 | int pmPartBlkCnt; /* # of blks in this partition */ | ||
217 | int i = 0; | ||
218 | |||
219 | ipod->macpod = 1; | ||
220 | |||
221 | memset(ipod->pinfo,0,sizeof(ipod->pinfo)); | ||
222 | |||
223 | while (blkNo <= partBlkCount) { | ||
224 | if (ipod_seek(ipod, blkNo * partBlkSizMul * 512) < 0) { | ||
225 | fprintf(stderr,"[ERR] Seek failed whilst reading APM\n"); | ||
226 | return -1; | ||
227 | } | ||
228 | |||
229 | count = ipod_read(ipod, ipod->sector_size); | ||
230 | |||
231 | if (count <= 0) { | ||
232 | ipod_print_error(" Error reading from disk: "); | ||
233 | return -1; | ||
234 | } | ||
235 | |||
236 | /* see if it's a partition entry */ | ||
237 | if ((ipod->sectorbuf[0] != 'P') || (ipod->sectorbuf[1] != 'M')) { | ||
238 | /* end of partition table -> leave the loop */ | ||
239 | break; | ||
240 | } | ||
241 | |||
242 | /* Extract the interesting entries */ | ||
243 | pmMapBlkCnt = be2int(ipod->sectorbuf + 4); | ||
244 | pmPyPartStart = be2int(ipod->sectorbuf + 8); | ||
245 | pmPartBlkCnt = be2int(ipod->sectorbuf + 12); | ||
246 | |||
247 | /* update the number of part map blocks */ | ||
248 | partBlkCount = pmMapBlkCnt; | ||
249 | |||
250 | if (strncmp((char*)(ipod->sectorbuf + 48), "Apple_MDFW", 32)==0) { | ||
251 | /* A Firmware partition */ | ||
252 | ipod->pinfo[i].start = pmPyPartStart; | ||
253 | ipod->pinfo[i].size = pmPartBlkCnt; | ||
254 | ipod->pinfo[i].type = 0; | ||
255 | i++; | ||
256 | } else if (strncmp((char*)(ipod->sectorbuf + 48), "Apple_HFS", 32)==0) { | ||
257 | /* A HFS partition */ | ||
258 | ipod->pinfo[i].start = pmPyPartStart; | ||
259 | ipod->pinfo[i].size = pmPartBlkCnt; | ||
260 | ipod->pinfo[i].type = PARTTYPE_HFS; | ||
261 | i++; | ||
262 | } | ||
263 | |||
264 | blkNo++; /* read next partition map entry */ | ||
265 | } | ||
266 | } else { | ||
267 | if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n"); | ||
268 | return -1; | ||
269 | } | ||
270 | |||
271 | /* Check that the partition table looks like an ipod: | ||
272 | 1) Partition 1 is of type 0 (Empty) but isn't empty. | ||
273 | 2) Partition 2 is of type 0xb or 0xc (winpod) or -1 (macpod) | ||
274 | */ | ||
275 | if ((ipod->pinfo[0].type != 0) || (ipod->pinfo[0].size == 0) || | ||
276 | ((ipod->pinfo[1].type != 0xb) && (ipod->pinfo[1].type != 0xc) && | ||
277 | (ipod->pinfo[1].type != PARTTYPE_HFS))) { | ||
278 | if (!silent) fprintf(stderr,"[ERR] Partition layout is not an ipod\n"); | ||
279 | return -1; | ||
280 | } | ||
281 | |||
282 | ipod->start = ipod->pinfo[0].start*ipod->sector_size; | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | int read_partition(struct ipod_t* ipod, int outfile) | ||
287 | { | ||
288 | int res; | ||
289 | ssize_t n; | ||
290 | int bytesleft; | ||
291 | int chunksize; | ||
292 | int count = ipod->pinfo[0].size; | ||
293 | |||
294 | if (ipod_seek(ipod, ipod->start) < 0) { | ||
295 | return -1; | ||
296 | } | ||
297 | if(ipod->sectorbuf == NULL) { | ||
298 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
299 | return -1; | ||
300 | } | ||
301 | |||
302 | fprintf(stderr,"[INFO] Writing %d sectors to output file\n",count); | ||
303 | |||
304 | bytesleft = count * ipod->sector_size; | ||
305 | while (bytesleft > 0) { | ||
306 | if (bytesleft > BUFFER_SIZE) { | ||
307 | chunksize = BUFFER_SIZE; | ||
308 | } else { | ||
309 | chunksize = bytesleft; | ||
310 | } | ||
311 | |||
312 | n = ipod_read(ipod, chunksize); | ||
313 | |||
314 | if (n < 0) { | ||
315 | return -1; | ||
316 | } | ||
317 | |||
318 | if (n < chunksize) { | ||
319 | fprintf(stderr, | ||
320 | "[ERR] Short read in disk_read() - requested %d, got %d\n", | ||
321 | chunksize,(int)n); | ||
322 | return -1; | ||
323 | } | ||
324 | |||
325 | bytesleft -= n; | ||
326 | |||
327 | res = write(outfile,ipod->sectorbuf,n); | ||
328 | |||
329 | if (res < 0) { | ||
330 | perror("[ERR] write in disk_read"); | ||
331 | return -1; | ||
332 | } | ||
333 | |||
334 | if (res != n) { | ||
335 | fprintf(stderr, | ||
336 | "Short write - requested %d, received %d - aborting.\n",(int)n,res); | ||
337 | return -1; | ||
338 | } | ||
339 | } | ||
340 | |||
341 | fprintf(stderr,"[INFO] Done.\n"); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | int write_partition(struct ipod_t* ipod, int infile) | ||
346 | { | ||
347 | ssize_t res; | ||
348 | int n; | ||
349 | int bytesread; | ||
350 | int byteswritten = 0; | ||
351 | int eof; | ||
352 | int padding = 0; | ||
353 | |||
354 | if (ipod_seek(ipod, ipod->start) < 0) { | ||
355 | return -1; | ||
356 | } | ||
357 | if(ipod->sectorbuf == NULL) { | ||
358 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
359 | return -1; | ||
360 | } | ||
361 | |||
362 | fprintf(stderr,"[INFO] Writing input file to device\n"); | ||
363 | bytesread = 0; | ||
364 | eof = 0; | ||
365 | while (!eof) { | ||
366 | n = read(infile,ipod->sectorbuf,BUFFER_SIZE); | ||
367 | |||
368 | if (n < 0) { | ||
369 | perror("[ERR] read in disk_write"); | ||
370 | return -1; | ||
371 | } | ||
372 | |||
373 | if (n < BUFFER_SIZE) { | ||
374 | eof = 1; | ||
375 | /* We need to pad the last write to a multiple of SECTOR_SIZE */ | ||
376 | if ((n % ipod->sector_size) != 0) { | ||
377 | padding = (ipod->sector_size-(n % ipod->sector_size)); | ||
378 | n += padding; | ||
379 | } | ||
380 | } | ||
381 | |||
382 | bytesread += n; | ||
383 | |||
384 | res = ipod_write(ipod, n); | ||
385 | |||
386 | if (res < 0) { | ||
387 | ipod_print_error(" Error writing to disk: "); | ||
388 | fprintf(stderr,"Bytes written: %d\n",byteswritten); | ||
389 | return -1; | ||
390 | } | ||
391 | |||
392 | if (res != n) { | ||
393 | fprintf(stderr,"[ERR] Short write - requested %d, received %d - aborting.\n",n,(int)res); | ||
394 | return -1; | ||
395 | } | ||
396 | |||
397 | byteswritten += res; | ||
398 | } | ||
399 | |||
400 | fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n", | ||
401 | byteswritten-padding,padding); | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | char* ftypename[] = { "OSOS", "RSRC", "AUPD", "HIBE", "OSBK" }; | ||
406 | |||
407 | int diskmove(struct ipod_t* ipod, int delta) | ||
408 | { | ||
409 | int src_start; | ||
410 | int src_end; | ||
411 | int bytesleft; | ||
412 | int chunksize; | ||
413 | int n; | ||
414 | |||
415 | src_start = ipod->ipod_directory[1].devOffset; | ||
416 | src_end = (ipod->ipod_directory[ipod->nimages-1].devOffset + ipod->sector_size + | ||
417 | ipod->ipod_directory[ipod->nimages-1].len + | ||
418 | (ipod->sector_size-1)) & ~(ipod->sector_size-1); | ||
419 | bytesleft = src_end - src_start; | ||
420 | |||
421 | if (ipod_verbose) { | ||
422 | fprintf(stderr,"[INFO] Need to move images 2-%d forward %08x bytes\n", ipod->nimages,delta); | ||
423 | fprintf(stderr,"[VERB] src_start = %08x\n",src_start); | ||
424 | fprintf(stderr,"[VERB] src_end = %08x\n",src_end); | ||
425 | fprintf(stderr,"[VERB] dest_start = %08x\n",src_start+delta); | ||
426 | fprintf(stderr,"[VERB] dest_end = %08x\n",src_end+delta); | ||
427 | fprintf(stderr,"[VERB] bytes to copy = %08x\n",bytesleft); | ||
428 | } | ||
429 | |||
430 | while (bytesleft > 0) { | ||
431 | if (bytesleft <= BUFFER_SIZE) { | ||
432 | chunksize = bytesleft; | ||
433 | } else { | ||
434 | chunksize = BUFFER_SIZE; | ||
435 | } | ||
436 | |||
437 | if (ipod_verbose) { | ||
438 | fprintf(stderr,"[VERB] Copying %08x bytes from %08x to %08x (absolute %08x to %08x)\n", | ||
439 | chunksize, | ||
440 | src_end-chunksize, | ||
441 | src_end-chunksize+delta, | ||
442 | (unsigned int)(ipod->start+src_end-chunksize), | ||
443 | (unsigned int)(ipod->start+src_end-chunksize+delta)); | ||
444 | } | ||
445 | |||
446 | |||
447 | if (ipod_seek(ipod, ipod->start+src_end-chunksize) < 0) { | ||
448 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
449 | return -1; | ||
450 | } | ||
451 | |||
452 | if ((n = ipod_read(ipod,chunksize)) < 0) { | ||
453 | perror("[ERR] Write failed\n"); | ||
454 | return -1; | ||
455 | } | ||
456 | |||
457 | if (n < chunksize) { | ||
458 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", | ||
459 | chunksize,n); | ||
460 | return -1; | ||
461 | } | ||
462 | |||
463 | if (ipod_seek(ipod, ipod->start+src_end-chunksize+delta) < 0) { | ||
464 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
465 | return -1; | ||
466 | } | ||
467 | |||
468 | if ((n = ipod_write(ipod,chunksize)) < 0) { | ||
469 | perror("[ERR] Write failed\n"); | ||
470 | return -1; | ||
471 | } | ||
472 | |||
473 | if (n < chunksize) { | ||
474 | fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" | ||
475 | ,chunksize,n); | ||
476 | return -1; | ||
477 | } | ||
478 | |||
479 | src_end -= chunksize; | ||
480 | bytesleft -= chunksize; | ||
481 | } | ||
482 | |||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | static int rename_image(struct ipod_t* ipod, char* from, char* to) | ||
487 | { | ||
488 | int n; | ||
489 | int x; | ||
490 | int found; | ||
491 | int i; | ||
492 | unsigned char* p; | ||
493 | |||
494 | /* diroffset may not be sector-aligned */ | ||
495 | x = ipod->diroffset % ipod->sector_size; | ||
496 | |||
497 | if(ipod->sectorbuf == NULL) { | ||
498 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
499 | return -1; | ||
500 | } | ||
501 | /* Read directory */ | ||
502 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
503 | fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); | ||
504 | return -1; | ||
505 | } | ||
506 | |||
507 | n=ipod_read(ipod, ipod->sector_size); | ||
508 | if (n < 0) { | ||
509 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
510 | return -1; | ||
511 | } | ||
512 | |||
513 | p = ipod->sectorbuf + x; | ||
514 | |||
515 | /* A hack to detect 2nd gen Nanos - maybe there is a better way? */ | ||
516 | if (p[0] == 0) | ||
517 | { | ||
518 | /* Adjust diroffset */ | ||
519 | ipod->diroffset += ipod->sector_size - x; | ||
520 | |||
521 | n=ipod_read(ipod, ipod->sector_size); | ||
522 | if (n < 0) { | ||
523 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
524 | return -1; | ||
525 | } | ||
526 | p = ipod->sectorbuf; | ||
527 | } | ||
528 | |||
529 | found = 0; | ||
530 | for (i=0 ; !found && i < MAX_IMAGES; i++) { | ||
531 | if (memcmp(p + 4, from, 4) == 0) { | ||
532 | memcpy(p + 4, to, 4); | ||
533 | |||
534 | found = 1; | ||
535 | } | ||
536 | p += 40; | ||
537 | } | ||
538 | |||
539 | if (!found) { | ||
540 | fprintf(stderr,"[ERR] Unexpected error - no \"%s\" image!\n", from); | ||
541 | return -1; | ||
542 | } | ||
543 | |||
544 | /* Write directory back to disk */ | ||
545 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
546 | fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); | ||
547 | return -1; | ||
548 | } | ||
549 | |||
550 | n=ipod_write(ipod, ipod->sector_size); | ||
551 | if (n < 0) { | ||
552 | fprintf(stderr,"[ERR] Write of directory failed in rename_image.\n"); | ||
553 | return -1; | ||
554 | } | ||
555 | |||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | static int delete_image(struct ipod_t* ipod, char* name) | ||
560 | { | ||
561 | int n; | ||
562 | int x; | ||
563 | int found; | ||
564 | int i; | ||
565 | unsigned char* p; | ||
566 | |||
567 | /* diroffset may not be sector-aligned */ | ||
568 | x = ipod->diroffset % ipod->sector_size; | ||
569 | |||
570 | /* Read directory */ | ||
571 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
572 | fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); | ||
573 | return -1; | ||
574 | } | ||
575 | |||
576 | n=ipod_read(ipod, ipod->sector_size); | ||
577 | if (n < 0) { | ||
578 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
579 | return -1; | ||
580 | } | ||
581 | |||
582 | p = ipod->sectorbuf + x; | ||
583 | |||
584 | /* A hack to detect 2nd gen Nanos - maybe there is a better way? */ | ||
585 | if (p[0] == 0) | ||
586 | { | ||
587 | /* Adjust diroffset */ | ||
588 | ipod->diroffset += ipod->sector_size - x; | ||
589 | |||
590 | n=ipod_read(ipod, ipod->sector_size); | ||
591 | if (n < 0) { | ||
592 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
593 | return -1; | ||
594 | } | ||
595 | p = ipod->sectorbuf; | ||
596 | } | ||
597 | |||
598 | found = 0; | ||
599 | for (i=0 ; !found && i < MAX_IMAGES; i++) { | ||
600 | if (memcmp(p + 4, name, 4) == 0) { | ||
601 | memset(p, 0, 40); /* Delete directory entry */ | ||
602 | found = 1; | ||
603 | } | ||
604 | p += 40; | ||
605 | } | ||
606 | |||
607 | if (!found) { | ||
608 | fprintf(stderr,"[ERR] Unexpected error - no \"%s\" image!\n", name); | ||
609 | return -1; | ||
610 | } | ||
611 | |||
612 | /* Write directory back to disk */ | ||
613 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
614 | fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); | ||
615 | return -1; | ||
616 | } | ||
617 | |||
618 | n=ipod_write(ipod, ipod->sector_size); | ||
619 | if (n < 0) { | ||
620 | fprintf(stderr,"[ERR] Write of directory failed in delete_image.\n"); | ||
621 | return -1; | ||
622 | } | ||
623 | |||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | int add_new_image(struct ipod_t* ipod, char* imagename, char* filename, int type) | ||
628 | { | ||
629 | int length; | ||
630 | int found; | ||
631 | int i; | ||
632 | int x; | ||
633 | int n; | ||
634 | int infile; | ||
635 | int newsize; | ||
636 | unsigned long chksum=0; | ||
637 | unsigned long filechksum=0; | ||
638 | unsigned long offset; | ||
639 | unsigned char header[8]; /* Header for .ipod file */ | ||
640 | unsigned char* p; | ||
641 | |||
642 | if(ipod->sectorbuf == NULL) { | ||
643 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
644 | return -1; | ||
645 | } | ||
646 | #ifdef WITH_BOOTOBJS | ||
647 | if (type == FILETYPE_INTERNAL) { | ||
648 | fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len); | ||
649 | length = ipod->bootloader_len; | ||
650 | infile = -1; | ||
651 | } | ||
652 | else | ||
653 | #endif | ||
654 | { | ||
655 | /* First check that the input file is the correct type for this ipod. */ | ||
656 | infile=open(filename,O_RDONLY); | ||
657 | if (infile < 0) { | ||
658 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
659 | return -1; | ||
660 | } | ||
661 | |||
662 | if (type==FILETYPE_DOT_IPOD) { | ||
663 | n = read(infile,header,8); | ||
664 | if (n < 8) { | ||
665 | fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); | ||
666 | close(infile); | ||
667 | return -1; | ||
668 | } | ||
669 | |||
670 | if (memcmp(header+4, ipod->modelname,4)!=0) { | ||
671 | fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", | ||
672 | header[4],header[5],header[6],header[7], ipod->modelname); | ||
673 | close(infile); | ||
674 | return -1; | ||
675 | } | ||
676 | |||
677 | filechksum = be2int(header); | ||
678 | |||
679 | length = filesize(infile)-8; | ||
680 | } else { | ||
681 | length = filesize(infile); | ||
682 | } | ||
683 | } | ||
684 | |||
685 | newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
686 | |||
687 | fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", | ||
688 | length,newsize); | ||
689 | |||
690 | if (newsize > BUFFER_SIZE) { | ||
691 | fprintf(stderr,"[ERR] Input file too big for buffer\n"); | ||
692 | if (infile >= 0) close(infile); | ||
693 | return -1; | ||
694 | } | ||
695 | |||
696 | /* TODO: Check if we have enough space in the partition for the new image */ | ||
697 | |||
698 | #ifdef WITH_BOOTOBJS | ||
699 | if (type == FILETYPE_INTERNAL) { | ||
700 | memcpy(ipod->sectorbuf,ipod->bootloader,ipod->bootloader_len); | ||
701 | } | ||
702 | else | ||
703 | #endif | ||
704 | { | ||
705 | fprintf(stderr,"[INFO] Reading input file...\n"); | ||
706 | |||
707 | n = read(infile,ipod->sectorbuf,length); | ||
708 | if (n < 0) { | ||
709 | fprintf(stderr,"[ERR] Couldn't read input file\n"); | ||
710 | close(infile); | ||
711 | return -1; | ||
712 | } | ||
713 | close(infile); | ||
714 | } | ||
715 | |||
716 | /* Pad the data with zeros */ | ||
717 | memset(ipod->sectorbuf+length,0,newsize-length); | ||
718 | |||
719 | if (type==FILETYPE_DOT_IPOD) { | ||
720 | chksum = ipod->modelnum; | ||
721 | for (i = 0; i < length; i++) { | ||
722 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
723 | chksum += ipod->sectorbuf[i]; | ||
724 | } | ||
725 | |||
726 | if (chksum == filechksum) { | ||
727 | fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); | ||
728 | } else { | ||
729 | fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); | ||
730 | return -1; | ||
731 | } | ||
732 | } | ||
733 | |||
734 | |||
735 | offset = ipod->fwoffset + ipod->ipod_directory[ipod->nimages - 1].devOffset + | ||
736 | ipod->ipod_directory[ipod->nimages - 1].len + ipod->sector_size; | ||
737 | |||
738 | /* 2nd Gen Nano has encrypted firmware, and the sector | ||
739 | preceeding the firmware contains hashes that need to be | ||
740 | preserved. Nano 2G images include these extra 2048 (0x800) | ||
741 | bytes | ||
742 | */ | ||
743 | if (ipod_seek(ipod, offset - (ipod->modelnum == 62 ? 0x800 : 0)) < 0) { | ||
744 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
745 | return -1; | ||
746 | } | ||
747 | |||
748 | if ((n = ipod_write(ipod,newsize)) < 0) { | ||
749 | perror("[ERR] Write failed\n"); | ||
750 | return -1; | ||
751 | } | ||
752 | |||
753 | if (n < newsize) { | ||
754 | fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" | ||
755 | ,newsize,n); | ||
756 | return -1; | ||
757 | } | ||
758 | fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); | ||
759 | |||
760 | /* Now we need to create a new directory entry | ||
761 | |||
762 | NOTE: On the Nano 2G, the checksum is the checksum of the | ||
763 | unencrypted firmware. But this isn't checked by the NOR | ||
764 | bootloader (there are cryptographic hashes in the | ||
765 | firmware itself), so it doesn't matter that this is | ||
766 | wrong. | ||
767 | */ | ||
768 | chksum = 0; | ||
769 | for (i = 0; i < length; i++) { | ||
770 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
771 | chksum += ipod->sectorbuf[i]; | ||
772 | } | ||
773 | |||
774 | x = ipod->diroffset % ipod->sector_size; | ||
775 | |||
776 | /* Read directory */ | ||
777 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
778 | |||
779 | n=ipod_read(ipod, ipod->sector_size); | ||
780 | if (n < 0) { return -1; } | ||
781 | |||
782 | /* Create a new directory entry */ | ||
783 | |||
784 | /* Copy OSOS or OSBK details - we assume one of them exists */ | ||
785 | p = ipod->sectorbuf + x; | ||
786 | found = 0; | ||
787 | for (i = 0; !found && i < ipod->nimages; i++) { | ||
788 | if ((memcmp(p + 4, "soso", 4)==0) || (memcmp(p + 4, "kbso", 4)==0)) { | ||
789 | found = 1; | ||
790 | } else { | ||
791 | p += 40; | ||
792 | } | ||
793 | } | ||
794 | |||
795 | if (!found) { | ||
796 | fprintf(stderr,"[ERR] No OSOS or OSBK image to copy directory from\n"); | ||
797 | return -1; | ||
798 | } | ||
799 | |||
800 | /* Copy directory image */ | ||
801 | memcpy(ipod->sectorbuf + x + (ipod->nimages * 40), p, 40); | ||
802 | p = ipod->sectorbuf + x + (ipod->nimages * 40); | ||
803 | |||
804 | /* Modify directory. */ | ||
805 | memcpy(p + 4, imagename, 4); | ||
806 | int2le(offset - ipod->fwoffset, p + 12); /* devOffset */ | ||
807 | int2le(length - (ipod->modelnum==62 ? 0x800: 0), p + 16); /* len */ | ||
808 | int2le(chksum, p + 28); /* checksum */ | ||
809 | |||
810 | /* Write directory */ | ||
811 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
812 | n=ipod_write(ipod, ipod->sector_size); | ||
813 | if (n < 0) { return -1; } | ||
814 | |||
815 | return 0; | ||
816 | } | ||
817 | |||
818 | |||
819 | int ipod_has_bootloader(struct ipod_t* ipod) | ||
820 | { | ||
821 | /* The 2nd gen Nano is installed differently */ | ||
822 | if (ipod->modelnum == 62) { | ||
823 | int i; | ||
824 | int has_osbk = 0; | ||
825 | /* Check if we have an OSBK image */ | ||
826 | for (i = 0; i < ipod->nimages; i++) { | ||
827 | if (ipod->ipod_directory[i].ftype==FTYPE_OSBK) { | ||
828 | has_osbk = 1; | ||
829 | } | ||
830 | } | ||
831 | return has_osbk; | ||
832 | } | ||
833 | else { | ||
834 | return (ipod->ipod_directory[0].entryOffset != 0); | ||
835 | } | ||
836 | } | ||
837 | |||
838 | |||
839 | /* | ||
840 | Bootloader installation on the Nano2G consists of renaming the | ||
841 | OSOS image to OSBK and then writing the Rockbox bootloader as a | ||
842 | new OSOS image. | ||
843 | |||
844 | Maybe this approach can/should be adapted for other ipods, as it | ||
845 | prevents the Apple bootloader loading the original firmware into | ||
846 | RAM along with the Rockbox bootloader (and hence will give a | ||
847 | faster boot when the user just wants to start Rockbox). | ||
848 | |||
849 | */ | ||
850 | |||
851 | static int add_bootloader_nano2g(struct ipod_t* ipod, char* filename, int type) | ||
852 | { | ||
853 | /* Check if we already have an OSBK image */ | ||
854 | if (ipod_has_bootloader(ipod) == 0) { | ||
855 | /* First-time install - rename OSOS to OSBK and create new OSOS for bootloader */ | ||
856 | fprintf(stderr,"[INFO] Creating OSBK backup image of original firmware\n"); | ||
857 | |||
858 | if (rename_image(ipod, "soso", "kbso") < 0) { | ||
859 | fprintf(stderr,"[ERR] Could not rename OSOS image\n"); | ||
860 | return -1; | ||
861 | } | ||
862 | |||
863 | /* Add our bootloader as a brand new image */ | ||
864 | return add_new_image(ipod, "soso", filename, type); | ||
865 | } else { | ||
866 | /* This is an update, just replace OSOS with our bootloader */ | ||
867 | |||
868 | return write_firmware(ipod, filename, type); | ||
869 | } | ||
870 | } | ||
871 | |||
872 | |||
873 | static int delete_bootloader_nano2g(struct ipod_t* ipod) | ||
874 | { | ||
875 | /* Check if we have an OSBK image */ | ||
876 | if (ipod_has_bootloader(ipod) == 0) { | ||
877 | fprintf(stderr,"[ERR] No OSBK image found - nothing to uninstall\n"); | ||
878 | return -1; | ||
879 | } else { | ||
880 | /* Delete our bootloader image */ | ||
881 | if (delete_image(ipod, "soso") < 0) { | ||
882 | fprintf(stderr,"[WARN] Could not delete OSOS image\n"); | ||
883 | } else { | ||
884 | fprintf(stderr,"[INFO] OSOS image deleted\n"); | ||
885 | } | ||
886 | |||
887 | if (rename_image(ipod, "kbso", "soso") < 0) { | ||
888 | fprintf(stderr,"[ERR] Could not rename OSBK image\n"); | ||
889 | return -1; | ||
890 | } | ||
891 | |||
892 | |||
893 | fprintf(stderr,"[INFO] OSBK image renamed to OSOS - bootloader uninstalled.\n"); | ||
894 | return 0; | ||
895 | } | ||
896 | } | ||
897 | |||
898 | |||
899 | int add_bootloader(struct ipod_t* ipod, char* filename, int type) | ||
900 | { | ||
901 | int length; | ||
902 | int i; | ||
903 | int x; | ||
904 | int n; | ||
905 | int infile; | ||
906 | int paddedlength; | ||
907 | int entryOffset; | ||
908 | int delta = 0; | ||
909 | unsigned long chksum=0; | ||
910 | unsigned long filechksum=0; | ||
911 | unsigned char header[8]; /* Header for .ipod file */ | ||
912 | unsigned char* bootloader_buf; | ||
913 | |||
914 | /* The 2nd gen Nano is installed differently */ | ||
915 | if (ipod->modelnum == 62) { | ||
916 | return add_bootloader_nano2g(ipod, filename, type); | ||
917 | } | ||
918 | if(ipod->sectorbuf == NULL) { | ||
919 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
920 | return -1; | ||
921 | } | ||
922 | |||
923 | /* Calculate the position in the OSOS image where our bootloader will go. */ | ||
924 | if (ipod->ipod_directory[0].entryOffset>0) { | ||
925 | /* Keep the same entryOffset */ | ||
926 | entryOffset = ipod->ipod_directory[0].entryOffset; | ||
927 | } else { | ||
928 | entryOffset = (ipod->ipod_directory[0].len+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
929 | } | ||
930 | |||
931 | #ifdef WITH_BOOTOBJS | ||
932 | if (type == FILETYPE_INTERNAL) { | ||
933 | fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len); | ||
934 | memcpy(ipod->sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len); | ||
935 | length = ipod->bootloader_len; | ||
936 | paddedlength=(ipod->bootloader_len+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
937 | } | ||
938 | else | ||
939 | #endif | ||
940 | { | ||
941 | infile=open(filename,O_RDONLY); | ||
942 | if (infile < 0) { | ||
943 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
944 | return -1; | ||
945 | } | ||
946 | |||
947 | if (type==FILETYPE_DOT_IPOD) { | ||
948 | /* First check that the input file is the correct type for this ipod. */ | ||
949 | n = read(infile,header,8); | ||
950 | if (n < 8) { | ||
951 | fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); | ||
952 | close(infile); | ||
953 | return -1; | ||
954 | } | ||
955 | |||
956 | if (memcmp(header+4, ipod->modelname,4)!=0) { | ||
957 | fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", | ||
958 | header[4],header[5],header[6],header[7], ipod->modelname); | ||
959 | close(infile); | ||
960 | return -1; | ||
961 | } | ||
962 | |||
963 | filechksum = be2int(header); | ||
964 | |||
965 | length=filesize(infile)-8; | ||
966 | } else { | ||
967 | length=filesize(infile); | ||
968 | } | ||
969 | paddedlength=(length+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
970 | |||
971 | bootloader_buf = malloc(length); | ||
972 | if (bootloader_buf == NULL) { | ||
973 | fprintf(stderr,"[ERR] Can not allocate memory for bootloader\n"); | ||
974 | return -1; | ||
975 | } | ||
976 | /* Now read our bootloader - we need to check it before modifying the partition*/ | ||
977 | n = read(infile,bootloader_buf,length); | ||
978 | close(infile); | ||
979 | |||
980 | if (n < 0) { | ||
981 | fprintf(stderr,"[ERR] Couldn't read input file\n"); | ||
982 | return -1; | ||
983 | } | ||
984 | |||
985 | if (type==FILETYPE_DOT_IPOD) { | ||
986 | /* Calculate and confirm bootloader checksum */ | ||
987 | chksum = ipod->modelnum; | ||
988 | for (i = 0; i < length; i++) { | ||
989 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
990 | chksum += bootloader_buf[i]; | ||
991 | } | ||
992 | |||
993 | if (chksum == filechksum) { | ||
994 | fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); | ||
995 | } else { | ||
996 | fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); | ||
997 | return -1; | ||
998 | } | ||
999 | } | ||
1000 | } | ||
1001 | |||
1002 | if (entryOffset+paddedlength > BUFFER_SIZE) { | ||
1003 | fprintf(stderr,"[ERR] Input file too big for buffer\n"); | ||
1004 | return -1; | ||
1005 | } | ||
1006 | |||
1007 | if (ipod_verbose) { | ||
1008 | fprintf(stderr,"[VERB] Original firmware begins at 0x%08x\n", ipod->ipod_directory[0].devOffset + ipod->sector_size); | ||
1009 | fprintf(stderr,"[VERB] New entryOffset will be 0x%08x\n",entryOffset); | ||
1010 | fprintf(stderr,"[VERB] End of bootloader will be at 0x%08x\n",entryOffset+paddedlength); | ||
1011 | } | ||
1012 | |||
1013 | /* Check if we have enough space */ | ||
1014 | /* TODO: Check the size of the partition. */ | ||
1015 | if (ipod->nimages > 1) { | ||
1016 | if ((ipod->ipod_directory[0].devOffset+entryOffset+paddedlength) > | ||
1017 | ipod->ipod_directory[1].devOffset) { | ||
1018 | fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); | ||
1019 | delta = ipod->ipod_directory[0].devOffset + entryOffset+paddedlength | ||
1020 | - ipod->ipod_directory[1].devOffset + ipod->sector_size; | ||
1021 | |||
1022 | if (diskmove(ipod, delta) < 0) { | ||
1023 | fprintf(stderr,"[ERR] Image movement failed.\n"); | ||
1024 | return -1; | ||
1025 | } | ||
1026 | } | ||
1027 | } | ||
1028 | |||
1029 | |||
1030 | /* We have moved the partitions, now we can write our bootloader */ | ||
1031 | |||
1032 | /* Firstly read the original firmware into ipod->sectorbuf */ | ||
1033 | fprintf(stderr,"[INFO] Reading original firmware...\n"); | ||
1034 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { | ||
1035 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
1036 | return -1; | ||
1037 | } | ||
1038 | |||
1039 | if ((n = ipod_read(ipod,entryOffset)) < 0) { | ||
1040 | perror("[ERR] Read failed\n"); | ||
1041 | return -1; | ||
1042 | } | ||
1043 | |||
1044 | if (n < entryOffset) { | ||
1045 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" | ||
1046 | ,entryOffset,n); | ||
1047 | return -1; | ||
1048 | } | ||
1049 | |||
1050 | #ifdef WITH_BOOTOBJS | ||
1051 | if (type == FILETYPE_INTERNAL) { | ||
1052 | memcpy(ipod->sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len); | ||
1053 | } | ||
1054 | else | ||
1055 | #endif | ||
1056 | { | ||
1057 | memcpy(ipod->sectorbuf+entryOffset,bootloader_buf,length); | ||
1058 | free(bootloader_buf); | ||
1059 | } | ||
1060 | |||
1061 | /* Calculate new checksum for combined image */ | ||
1062 | chksum = 0; | ||
1063 | for (i=0;i<entryOffset + length; i++) { | ||
1064 | chksum += ipod->sectorbuf[i]; | ||
1065 | } | ||
1066 | |||
1067 | /* Now write the combined firmware image to the disk */ | ||
1068 | |||
1069 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { | ||
1070 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
1071 | return -1; | ||
1072 | } | ||
1073 | |||
1074 | if ((n = ipod_write(ipod,entryOffset+paddedlength)) < 0) { | ||
1075 | perror("[ERR] Write failed\n"); | ||
1076 | return -1; | ||
1077 | } | ||
1078 | |||
1079 | if (n < (entryOffset+paddedlength)) { | ||
1080 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" | ||
1081 | ,entryOffset+paddedlength,n); | ||
1082 | return -1; | ||
1083 | } | ||
1084 | |||
1085 | fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",entryOffset+paddedlength); | ||
1086 | |||
1087 | x = ipod->diroffset % ipod->sector_size; | ||
1088 | |||
1089 | /* Read directory */ | ||
1090 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
1091 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
1092 | return -1; | ||
1093 | } | ||
1094 | |||
1095 | n=ipod_read(ipod, ipod->sector_size); | ||
1096 | if (n < 0) { | ||
1097 | fprintf(stderr,"[ERR] Directory read failed\n"); | ||
1098 | return -1; | ||
1099 | } | ||
1100 | |||
1101 | /* Update entries for image 0 */ | ||
1102 | int2le(entryOffset+length,ipod->sectorbuf+x+16); | ||
1103 | int2le(entryOffset,ipod->sectorbuf+x+24); | ||
1104 | int2le(chksum,ipod->sectorbuf+x+28); | ||
1105 | int2le(0xffffffff,ipod->sectorbuf+x+36); /* loadAddr */ | ||
1106 | |||
1107 | /* Update devOffset entries for other images, if we have moved them */ | ||
1108 | if (delta > 0) { | ||
1109 | for (i=1;i<ipod->nimages;i++) { | ||
1110 | int2le(le2int(ipod->sectorbuf+x+i*40+12)+delta,ipod->sectorbuf+x+i*40+12); | ||
1111 | } | ||
1112 | } | ||
1113 | |||
1114 | /* Write directory */ | ||
1115 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
1116 | fprintf(stderr,"[ERR] Seek to %d failed\n", (int)(ipod->start+ipod->diroffset-x)); | ||
1117 | return -1; | ||
1118 | } | ||
1119 | n=ipod_write(ipod, ipod->sector_size); | ||
1120 | if (n < 0) { | ||
1121 | fprintf(stderr,"[ERR] Directory write failed\n"); | ||
1122 | return -1; | ||
1123 | } | ||
1124 | |||
1125 | return 0; | ||
1126 | } | ||
1127 | |||
1128 | int delete_bootloader(struct ipod_t* ipod) | ||
1129 | { | ||
1130 | int length; | ||
1131 | int i; | ||
1132 | int x; | ||
1133 | int n; | ||
1134 | unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/ | ||
1135 | |||
1136 | /* The 2nd gen Nano is installed differently */ | ||
1137 | if (ipod->modelnum == 62) { | ||
1138 | return delete_bootloader_nano2g(ipod); | ||
1139 | } | ||
1140 | if(ipod->sectorbuf == NULL) { | ||
1141 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
1142 | return -1; | ||
1143 | } | ||
1144 | |||
1145 | /* Removing the bootloader involves adjusting the "length", | ||
1146 | "chksum" and "entryOffset" values in the osos image's directory | ||
1147 | entry. */ | ||
1148 | |||
1149 | /* Firstly check we have a bootloader... */ | ||
1150 | |||
1151 | if (ipod_has_bootloader(ipod) == 0) { | ||
1152 | fprintf(stderr,"[ERR] No bootloader found.\n"); | ||
1153 | return -1; | ||
1154 | } | ||
1155 | |||
1156 | length = ipod->ipod_directory[0].entryOffset; | ||
1157 | |||
1158 | /* Read the firmware so we can calculate the checksum */ | ||
1159 | fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); | ||
1160 | |||
1161 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { | ||
1162 | return -1; | ||
1163 | } | ||
1164 | |||
1165 | i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); | ||
1166 | fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n", | ||
1167 | length,i); | ||
1168 | |||
1169 | if ((n = ipod_read(ipod,i)) < 0) { | ||
1170 | return -1; | ||
1171 | } | ||
1172 | |||
1173 | if (n < i) { | ||
1174 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", | ||
1175 | i,n); | ||
1176 | return -1; | ||
1177 | } | ||
1178 | |||
1179 | chksum = 0; | ||
1180 | for (i = 0; i < length; i++) { | ||
1181 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
1182 | chksum += ipod->sectorbuf[i]; | ||
1183 | } | ||
1184 | |||
1185 | /* Now write back the updated directory entry */ | ||
1186 | |||
1187 | fprintf(stderr,"[INFO] Updating firmware checksum\n"); | ||
1188 | |||
1189 | x = ipod->diroffset % ipod->sector_size; | ||
1190 | |||
1191 | /* Read directory */ | ||
1192 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
1193 | |||
1194 | n=ipod_read(ipod, ipod->sector_size); | ||
1195 | if (n < 0) { return -1; } | ||
1196 | |||
1197 | /* Update entries for image 0 */ | ||
1198 | int2le(length,ipod->sectorbuf+x+16); | ||
1199 | int2le(0,ipod->sectorbuf+x+24); | ||
1200 | int2le(chksum,ipod->sectorbuf+x+28); | ||
1201 | |||
1202 | /* Write directory */ | ||
1203 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
1204 | n=ipod_write(ipod, ipod->sector_size); | ||
1205 | if (n < 0) { return -1; } | ||
1206 | |||
1207 | return 0; | ||
1208 | } | ||
1209 | |||
1210 | int write_firmware(struct ipod_t* ipod, char* filename, int type) | ||
1211 | { | ||
1212 | int length; | ||
1213 | int i; | ||
1214 | int x; | ||
1215 | int n; | ||
1216 | int infile; | ||
1217 | int newsize; | ||
1218 | int bytesavailable; | ||
1219 | unsigned long chksum=0; | ||
1220 | unsigned long filechksum=0; | ||
1221 | unsigned long offset; | ||
1222 | unsigned char header[8]; /* Header for .ipod file */ | ||
1223 | unsigned char* p; | ||
1224 | |||
1225 | if(ipod->sectorbuf == NULL) { | ||
1226 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
1227 | return -1; | ||
1228 | } | ||
1229 | #ifdef WITH_BOOTOBJS | ||
1230 | if (type == FILETYPE_INTERNAL) { | ||
1231 | fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len); | ||
1232 | length = ipod->bootloader_len; | ||
1233 | infile = -1; | ||
1234 | } | ||
1235 | else | ||
1236 | #endif | ||
1237 | { | ||
1238 | /* First check that the input file is the correct type for this ipod. */ | ||
1239 | infile=open(filename,O_RDONLY); | ||
1240 | if (infile < 0) { | ||
1241 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
1242 | return -1; | ||
1243 | } | ||
1244 | |||
1245 | if (type==FILETYPE_DOT_IPOD) { | ||
1246 | n = read(infile,header,8); | ||
1247 | if (n < 8) { | ||
1248 | fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); | ||
1249 | close(infile); | ||
1250 | return -1; | ||
1251 | } | ||
1252 | |||
1253 | if (memcmp(header+4, ipod->modelname,4)!=0) { | ||
1254 | fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", | ||
1255 | header[4],header[5],header[6],header[7], ipod->modelname); | ||
1256 | close(infile); | ||
1257 | return -1; | ||
1258 | } | ||
1259 | |||
1260 | filechksum = be2int(header); | ||
1261 | |||
1262 | length = filesize(infile)-8; | ||
1263 | } else { | ||
1264 | length = filesize(infile); | ||
1265 | } | ||
1266 | } | ||
1267 | |||
1268 | newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
1269 | |||
1270 | fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", | ||
1271 | length,newsize); | ||
1272 | |||
1273 | if (newsize > BUFFER_SIZE) { | ||
1274 | fprintf(stderr,"[ERR] Input file too big for buffer\n"); | ||
1275 | if (infile >= 0) close(infile); | ||
1276 | return -1; | ||
1277 | } | ||
1278 | |||
1279 | /* Check if we have enough space */ | ||
1280 | /* TODO: Check the size of the partition. */ | ||
1281 | if (ipod->nimages > 1) { | ||
1282 | bytesavailable=ipod->ipod_directory[1].devOffset-ipod->ipod_directory[0].devOffset; | ||
1283 | if (bytesavailable < newsize) { | ||
1284 | fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); | ||
1285 | |||
1286 | /* TODO: Implement image movement */ | ||
1287 | fprintf(stderr,"[ERR] Image movement not yet implemented.\n"); | ||
1288 | close(infile); | ||
1289 | return -1; | ||
1290 | } | ||
1291 | } | ||
1292 | |||
1293 | #ifdef WITH_BOOTOBJS | ||
1294 | if (type == FILETYPE_INTERNAL) { | ||
1295 | memcpy(ipod->sectorbuf,ipod->bootloader,ipod->bootloader_len); | ||
1296 | } | ||
1297 | else | ||
1298 | #endif | ||
1299 | { | ||
1300 | fprintf(stderr,"[INFO] Reading input file...\n"); | ||
1301 | /* We now know we have enough space, so write it. */ | ||
1302 | n = read(infile,ipod->sectorbuf,length); | ||
1303 | if (n < 0) { | ||
1304 | fprintf(stderr,"[ERR] Couldn't read input file\n"); | ||
1305 | close(infile); | ||
1306 | return -1; | ||
1307 | } | ||
1308 | close(infile); | ||
1309 | } | ||
1310 | |||
1311 | /* Pad the data with zeros */ | ||
1312 | memset(ipod->sectorbuf+length,0,newsize-length); | ||
1313 | |||
1314 | if (type==FILETYPE_DOT_IPOD) { | ||
1315 | chksum = ipod->modelnum; | ||
1316 | for (i = 0; i < length; i++) { | ||
1317 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
1318 | chksum += ipod->sectorbuf[i]; | ||
1319 | } | ||
1320 | |||
1321 | if (chksum == filechksum) { | ||
1322 | fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); | ||
1323 | } else { | ||
1324 | fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); | ||
1325 | return -1; | ||
1326 | } | ||
1327 | } | ||
1328 | |||
1329 | |||
1330 | offset = ipod->fwoffset+ipod->ipod_directory[ipod->ososimage].devOffset; | ||
1331 | |||
1332 | if (ipod->modelnum==62) { | ||
1333 | |||
1334 | /* 2nd Gen Nano has encrypted firmware, and the sector | ||
1335 | preceeding the firmware contains hashes that need to be | ||
1336 | preserved. Nano 2G images include these extra 2048 (0x800) | ||
1337 | bytes | ||
1338 | */ | ||
1339 | |||
1340 | offset -= 0x800; | ||
1341 | |||
1342 | /* TODO: The above checks need to take into account this 0x800 bytes */ | ||
1343 | } | ||
1344 | |||
1345 | if (ipod_seek(ipod, offset) < 0) { | ||
1346 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
1347 | return -1; | ||
1348 | } | ||
1349 | |||
1350 | if ((n = ipod_write(ipod,newsize)) < 0) { | ||
1351 | perror("[ERR] Write failed\n"); | ||
1352 | return -1; | ||
1353 | } | ||
1354 | |||
1355 | if (n < newsize) { | ||
1356 | fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" | ||
1357 | ,newsize,n); | ||
1358 | return -1; | ||
1359 | } | ||
1360 | fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); | ||
1361 | |||
1362 | /* Now we need to update the "len", "entryOffset" and "chksum" fields | ||
1363 | |||
1364 | NOTE: On the Nano 2G, the checksum is the checksum of the | ||
1365 | unencrypted firmware. But this isn't checked by the NOR | ||
1366 | bootloader (there are cryptographic hashes in the | ||
1367 | firmware itself), so it doesn't matter that this is | ||
1368 | wrong. | ||
1369 | */ | ||
1370 | chksum = 0; | ||
1371 | for (i = 0; i < length; i++) { | ||
1372 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
1373 | chksum += ipod->sectorbuf[i]; | ||
1374 | } | ||
1375 | |||
1376 | x = ipod->diroffset % ipod->sector_size; | ||
1377 | |||
1378 | /* Read directory */ | ||
1379 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
1380 | |||
1381 | n=ipod_read(ipod, ipod->sector_size); | ||
1382 | if (n < 0) { return -1; } | ||
1383 | |||
1384 | /* Update entries for image */ | ||
1385 | p = ipod->sectorbuf + x + (ipod->ososimage * 40); | ||
1386 | int2le(length - (ipod->modelnum==62 ? 0x800: 0), p + 16); | ||
1387 | int2le(0, p + 24); | ||
1388 | int2le(chksum, p + 28); | ||
1389 | |||
1390 | /* Write directory */ | ||
1391 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
1392 | n=ipod_write(ipod, ipod->sector_size); | ||
1393 | if (n < 0) { return -1; } | ||
1394 | |||
1395 | return 0; | ||
1396 | } | ||
1397 | |||
1398 | int read_firmware(struct ipod_t* ipod, char* filename, int type) | ||
1399 | { | ||
1400 | int length; | ||
1401 | int i; | ||
1402 | int outfile; | ||
1403 | int n; | ||
1404 | unsigned long offset; | ||
1405 | unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/ | ||
1406 | unsigned char header[8]; /* Header for .ipod file */ | ||
1407 | |||
1408 | if(ipod->sectorbuf == NULL) { | ||
1409 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
1410 | return -1; | ||
1411 | } | ||
1412 | if (ipod->ipod_directory[ipod->ososimage].entryOffset != 0) { | ||
1413 | /* We have a bootloader... */ | ||
1414 | length = ipod->ipod_directory[ipod->ososimage].entryOffset; | ||
1415 | } else { | ||
1416 | length = ipod->ipod_directory[ipod->ososimage].len; | ||
1417 | } | ||
1418 | |||
1419 | fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); | ||
1420 | |||
1421 | offset = ipod->fwoffset + ipod->ipod_directory[ipod->ososimage].devOffset; | ||
1422 | i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); | ||
1423 | fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n", | ||
1424 | length,i); | ||
1425 | |||
1426 | if (ipod->modelnum==62) { | ||
1427 | /* 2nd Gen Nano has encrypted firmware, and we need to dump the | ||
1428 | sector preceeding the image - it contains hashes */ | ||
1429 | offset -= 0x800; | ||
1430 | length += 0x800; | ||
1431 | i += 0x800; | ||
1432 | } | ||
1433 | |||
1434 | if (ipod_seek(ipod, offset)) { | ||
1435 | return -1; | ||
1436 | } | ||
1437 | |||
1438 | if ((n = ipod_read(ipod,i)) < 0) { | ||
1439 | return -1; | ||
1440 | } | ||
1441 | |||
1442 | if (n < i) { | ||
1443 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", | ||
1444 | i,n); | ||
1445 | return -1; | ||
1446 | } | ||
1447 | |||
1448 | outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); | ||
1449 | if (outfile < 0) { | ||
1450 | fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); | ||
1451 | return -1; | ||
1452 | } | ||
1453 | |||
1454 | if (type == FILETYPE_DOT_IPOD) { | ||
1455 | chksum = ipod->modelnum; | ||
1456 | for (i = 0; i < length; i++) { | ||
1457 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
1458 | chksum += ipod->sectorbuf[i]; | ||
1459 | } | ||
1460 | |||
1461 | int2be(chksum,header); | ||
1462 | memcpy(header+4, ipod->modelname,4); | ||
1463 | |||
1464 | n = write(outfile,header,8); | ||
1465 | if (n != 8) { | ||
1466 | fprintf(stderr,"[ERR] Write error - %d\n",n); | ||
1467 | } | ||
1468 | } | ||
1469 | |||
1470 | n = write(outfile,ipod->sectorbuf,length); | ||
1471 | if (n != length) { | ||
1472 | fprintf(stderr,"[ERR] Write error - %d\n",n); | ||
1473 | } | ||
1474 | close(outfile); | ||
1475 | |||
1476 | return 0; | ||
1477 | } | ||
1478 | |||
1479 | int read_directory(struct ipod_t* ipod) | ||
1480 | { | ||
1481 | ssize_t n; | ||
1482 | int x; | ||
1483 | unsigned char* p; | ||
1484 | unsigned short version; | ||
1485 | |||
1486 | ipod->nimages=0; | ||
1487 | |||
1488 | /* Read firmware partition header (first 512 bytes of disk - but | ||
1489 | let's read a whole sector) */ | ||
1490 | |||
1491 | if (ipod_seek(ipod, ipod->start) < 0) { | ||
1492 | fprintf(stderr,"[ERR] Seek to 0x%08x in read_directory() failed.\n", | ||
1493 | (unsigned int)(ipod->start)); | ||
1494 | return -1; | ||
1495 | } | ||
1496 | |||
1497 | n=ipod_read(ipod, ipod->sector_size); | ||
1498 | if (n < 0) { | ||
1499 | fprintf(stderr,"[ERR] ipod_read(ipod,0x%08x) failed in read_directory()\n", ipod->sector_size); | ||
1500 | return -1; | ||
1501 | } | ||
1502 | |||
1503 | if (memcmp(ipod->sectorbuf,apple_stop_sign,sizeof(apple_stop_sign))!=0) { | ||
1504 | fprintf(stderr,"[ERR] Firmware partition doesn't contain Apple copyright, aborting.\n"); | ||
1505 | return -1; | ||
1506 | } | ||
1507 | |||
1508 | if (memcmp(ipod->sectorbuf+0x100,"]ih[",4)!=0) { | ||
1509 | fprintf(stderr,"[ERR] Bad firmware directory\n"); | ||
1510 | return -1; | ||
1511 | } | ||
1512 | |||
1513 | version = le2ushort(ipod->sectorbuf+0x10a); | ||
1514 | if ((version != 2) && (version != 3)) { | ||
1515 | fprintf(stderr,"[ERR] Unknown firmware format version %04x\n", | ||
1516 | version); | ||
1517 | } | ||
1518 | ipod->diroffset=le2int(ipod->sectorbuf+0x104) + 0x200; | ||
1519 | |||
1520 | /* diroffset may not be sector-aligned */ | ||
1521 | x = ipod->diroffset % ipod->sector_size; | ||
1522 | |||
1523 | /* Read directory */ | ||
1524 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { | ||
1525 | fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); | ||
1526 | return -1; | ||
1527 | } | ||
1528 | |||
1529 | n=ipod_read(ipod, ipod->sector_size); | ||
1530 | if (n < 0) { | ||
1531 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
1532 | return -1; | ||
1533 | } | ||
1534 | |||
1535 | p = ipod->sectorbuf + x; | ||
1536 | |||
1537 | /* A hack to detect 2nd gen Nanos - maybe there is a better way? */ | ||
1538 | if (p[0] == 0) | ||
1539 | { | ||
1540 | /* Adjust diroffset */ | ||
1541 | ipod->diroffset += ipod->sector_size - x; | ||
1542 | |||
1543 | n=ipod_read(ipod, ipod->sector_size); | ||
1544 | if (n < 0) { | ||
1545 | fprintf(stderr,"[ERR] Read of directory failed.\n"); | ||
1546 | return -1; | ||
1547 | } | ||
1548 | p = ipod->sectorbuf; | ||
1549 | } | ||
1550 | |||
1551 | ipod->ososimage = -1; | ||
1552 | while ((ipod->nimages < MAX_IMAGES) && (p < (ipod->sectorbuf + x + 400)) && | ||
1553 | ((memcmp(p,"!ATA",4)==0) || (memcmp(p,"DNAN",4)==0))) { | ||
1554 | p+=4; | ||
1555 | if (memcmp(p,"soso",4)==0) { | ||
1556 | ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSOS; | ||
1557 | ipod->ososimage = ipod->nimages; | ||
1558 | } else if (memcmp(p,"crsr",4)==0) { | ||
1559 | ipod->ipod_directory[ipod->nimages].ftype=FTYPE_RSRC; | ||
1560 | } else if (memcmp(p,"dpua",4)==0) { | ||
1561 | ipod->ipod_directory[ipod->nimages].ftype=FTYPE_AUPD; | ||
1562 | } else if (memcmp(p,"kbso",4)==0) { | ||
1563 | ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSBK; | ||
1564 | } else if (memcmp(p,"ebih",4)==0) { | ||
1565 | ipod->ipod_directory[ipod->nimages].ftype=FTYPE_HIBE; | ||
1566 | } else { | ||
1567 | fprintf(stderr,"[ERR] Unknown image type %c%c%c%c\n", | ||
1568 | p[0],p[1],p[2],p[3]); | ||
1569 | } | ||
1570 | p+=4; | ||
1571 | ipod->ipod_directory[ipod->nimages].id=le2int(p); | ||
1572 | p+=4; | ||
1573 | ipod->ipod_directory[ipod->nimages].devOffset=le2int(p); | ||
1574 | p+=4; | ||
1575 | ipod->ipod_directory[ipod->nimages].len=le2int(p); | ||
1576 | p+=4; | ||
1577 | ipod->ipod_directory[ipod->nimages].addr=le2int(p); | ||
1578 | p+=4; | ||
1579 | ipod->ipod_directory[ipod->nimages].entryOffset=le2int(p); | ||
1580 | p+=4; | ||
1581 | ipod->ipod_directory[ipod->nimages].chksum=le2int(p); | ||
1582 | p+=4; | ||
1583 | ipod->ipod_directory[ipod->nimages].vers=le2int(p); | ||
1584 | p+=4; | ||
1585 | ipod->ipod_directory[ipod->nimages].loadAddr=le2int(p); | ||
1586 | p+=4; | ||
1587 | ipod->nimages++; | ||
1588 | } | ||
1589 | |||
1590 | if (ipod->ososimage < 0) { | ||
1591 | fprintf(stderr,"[ERR] No OSOS image found.\n"); | ||
1592 | return -1; | ||
1593 | } | ||
1594 | |||
1595 | if ((ipod->nimages > 1) && (version==2)) { | ||
1596 | /* The 3g firmware image doesn't appear to have a version, so | ||
1597 | let's make one up... Note that this is never written back to the | ||
1598 | ipod, so it's OK to do. */ | ||
1599 | |||
1600 | if (ipod->ipod_directory[ipod->ososimage].vers == 0) { ipod->ipod_directory[ipod->ososimage].vers = 3; } | ||
1601 | |||
1602 | ipod->fwoffset = ipod->start; | ||
1603 | } else { | ||
1604 | ipod->fwoffset = ipod->start + ipod->sector_size; | ||
1605 | } | ||
1606 | |||
1607 | return 0; | ||
1608 | } | ||
1609 | |||
1610 | int list_images(struct ipod_t* ipod) | ||
1611 | { | ||
1612 | int i; | ||
1613 | |||
1614 | if (ipod_verbose) { | ||
1615 | printf(" Type id devOffset len addr entryOffset chksum vers loadAddr devOffset+len\n"); | ||
1616 | for (i = 0 ; i < ipod->nimages; i++) { | ||
1617 | printf("%d - %s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i, | ||
1618 | ftypename[ipod->ipod_directory[i].ftype], | ||
1619 | ipod->ipod_directory[i].id, | ||
1620 | ipod->ipod_directory[i].devOffset, | ||
1621 | ipod->ipod_directory[i].len, | ||
1622 | ipod->ipod_directory[i].addr, | ||
1623 | ipod->ipod_directory[i].entryOffset, | ||
1624 | ipod->ipod_directory[i].chksum, | ||
1625 | ipod->ipod_directory[i].vers, | ||
1626 | ipod->ipod_directory[i].loadAddr, | ||
1627 | ipod->ipod_directory[i].devOffset+((ipod->ipod_directory[i].len+ipod->sector_size-1)&~(ipod->sector_size-1))); | ||
1628 | } | ||
1629 | } | ||
1630 | |||
1631 | printf("\n"); | ||
1632 | printf("Listing firmware partition contents:\n"); | ||
1633 | printf("\n"); | ||
1634 | |||
1635 | for (i = 0 ; i < ipod->nimages; i++) { | ||
1636 | printf("Image %d:\n",i+1); | ||
1637 | switch(ipod->ipod_directory[i].ftype) { | ||
1638 | case FTYPE_OSOS: | ||
1639 | if (ipod->ipod_directory[i].entryOffset==0) { | ||
1640 | printf(" Main firmware - %d bytes\n", | ||
1641 | ipod->ipod_directory[i].len); | ||
1642 | } else { | ||
1643 | printf(" Main firmware - %d bytes\n", | ||
1644 | ipod->ipod_directory[i].entryOffset); | ||
1645 | printf(" Third-party bootloader - %d bytes\n", | ||
1646 | ipod->ipod_directory[i].len-ipod->ipod_directory[i].entryOffset); | ||
1647 | } | ||
1648 | break; | ||
1649 | default: | ||
1650 | printf(" %s - %d bytes\n", | ||
1651 | ftypename[ipod->ipod_directory[i].ftype], | ||
1652 | ipod->ipod_directory[i].len); | ||
1653 | } | ||
1654 | } | ||
1655 | printf("\n"); | ||
1656 | |||
1657 | return 0; | ||
1658 | } | ||
1659 | |||
1660 | int getmodel(struct ipod_t* ipod, int ipod_version) | ||
1661 | { | ||
1662 | switch (ipod_version) { | ||
1663 | case 0x01: | ||
1664 | ipod->modelstr="1st or 2nd Generation"; | ||
1665 | ipod->modelnum = 19; | ||
1666 | ipod->modelname = "1g2g"; | ||
1667 | ipod->targetname = "ipod1g2g"; | ||
1668 | #ifdef WITH_BOOTOBJS | ||
1669 | ipod->bootloader = ipod1g2g; | ||
1670 | ipod->bootloader_len = LEN_ipod1g2g; | ||
1671 | #endif | ||
1672 | break; | ||
1673 | case 0x02: | ||
1674 | ipod->modelstr="3rd Generation"; | ||
1675 | ipod->modelnum = 7; | ||
1676 | ipod->modelname = "ip3g"; | ||
1677 | ipod->targetname = "ipod3g"; | ||
1678 | #ifdef WITH_BOOTOBJS | ||
1679 | ipod->bootloader = ipod3g; | ||
1680 | ipod->bootloader_len = LEN_ipod3g; | ||
1681 | #endif | ||
1682 | break; | ||
1683 | case 0x40: | ||
1684 | ipod->modelstr="1st Generation Mini"; | ||
1685 | ipod->modelnum = 9; | ||
1686 | ipod->modelname = "mini"; | ||
1687 | ipod->targetname = "ipodmini1g"; | ||
1688 | #ifdef WITH_BOOTOBJS | ||
1689 | ipod->bootloader = ipodmini1g; | ||
1690 | ipod->bootloader_len = LEN_ipodmini1g; | ||
1691 | #endif | ||
1692 | break; | ||
1693 | case 0x50: | ||
1694 | ipod->modelstr="4th Generation"; | ||
1695 | ipod->modelnum = 8; | ||
1696 | ipod->modelname = "ip4g"; | ||
1697 | ipod->targetname = "ipod4gray"; | ||
1698 | #ifdef WITH_BOOTOBJS | ||
1699 | ipod->bootloader = ipod4g; | ||
1700 | ipod->bootloader_len = LEN_ipod4g; | ||
1701 | #endif | ||
1702 | break; | ||
1703 | case 0x60: | ||
1704 | ipod->modelstr="Photo/Color"; | ||
1705 | ipod->modelnum = 3; | ||
1706 | ipod->modelname = "ipco"; | ||
1707 | ipod->targetname = "ipodcolor"; | ||
1708 | #ifdef WITH_BOOTOBJS | ||
1709 | ipod->bootloader = ipodcolor; | ||
1710 | ipod->bootloader_len = LEN_ipodcolor; | ||
1711 | #endif | ||
1712 | break; | ||
1713 | case 0x70: | ||
1714 | ipod->modelstr="2nd Generation Mini"; | ||
1715 | ipod->modelnum = 11; | ||
1716 | ipod->modelname = "mn2g"; | ||
1717 | ipod->targetname = "ipodmini2g"; | ||
1718 | #ifdef WITH_BOOTOBJS | ||
1719 | ipod->bootloader = ipodmini2g; | ||
1720 | ipod->bootloader_len = LEN_ipodmini2g; | ||
1721 | #endif | ||
1722 | break; | ||
1723 | case 0xc0: | ||
1724 | ipod->modelstr="1st Generation Nano"; | ||
1725 | ipod->modelnum = 4; | ||
1726 | ipod->modelname = "nano"; | ||
1727 | ipod->targetname = "ipodnano1g"; | ||
1728 | #ifdef WITH_BOOTOBJS | ||
1729 | ipod->bootloader = ipodnano1g; | ||
1730 | ipod->bootloader_len = LEN_ipodnano1g; | ||
1731 | #endif | ||
1732 | break; | ||
1733 | case 0xb0: | ||
1734 | ipod->modelstr="Video (aka 5th Generation)"; | ||
1735 | ipod->modelnum = 5; | ||
1736 | ipod->modelname = "ipvd"; | ||
1737 | ipod->targetname = "ipodvideo"; | ||
1738 | #ifdef WITH_BOOTOBJS | ||
1739 | ipod->bootloader = ipodvideo; | ||
1740 | ipod->bootloader_len = LEN_ipodvideo; | ||
1741 | #endif | ||
1742 | break; | ||
1743 | case 0x100: | ||
1744 | ipod->modelstr="2nd Generation Nano"; | ||
1745 | ipod->modelnum = 62; | ||
1746 | ipod->modelname = "nn2x"; | ||
1747 | ipod->targetname = "ipodnano2g"; | ||
1748 | #ifdef WITH_BOOTOBJS | ||
1749 | ipod->bootloader = ipodnano2g; | ||
1750 | ipod->bootloader_len = LEN_ipodnano2g; | ||
1751 | #endif | ||
1752 | break; | ||
1753 | default: | ||
1754 | ipod->modelname = NULL; | ||
1755 | ipod->modelnum = 0; | ||
1756 | ipod->targetname = NULL; | ||
1757 | #ifdef WITH_BOOTOBJS | ||
1758 | ipod->bootloader = NULL; | ||
1759 | ipod->bootloader_len = 0; | ||
1760 | #endif | ||
1761 | return -1; | ||
1762 | } | ||
1763 | return 0; | ||
1764 | } | ||
1765 | |||
1766 | /* returns number of found ipods or -1 if no ipods found and permission | ||
1767 | * for raw disc access was denied. */ | ||
1768 | int ipod_scan(struct ipod_t* ipod) | ||
1769 | { | ||
1770 | int i; | ||
1771 | int n = 0; | ||
1772 | int ipod_version; | ||
1773 | struct ipod_t ipod_found; | ||
1774 | int denied = 0; | ||
1775 | int result; | ||
1776 | |||
1777 | printf("[INFO] Scanning disk devices...\n"); | ||
1778 | |||
1779 | for (i = 0; i <= 25 ; i++) { | ||
1780 | #ifdef __WIN32__ | ||
1781 | sprintf(ipod->diskname,"\\\\.\\PhysicalDrive%d",i); | ||
1782 | #elif defined(linux) || defined (__linux) | ||
1783 | sprintf(ipod->diskname,"/dev/sd%c",'a'+i); | ||
1784 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ | ||
1785 | || defined(__bsdi__) || defined(__DragonFly__) | ||
1786 | sprintf(ipod->diskname,"/dev/da%d",i); | ||
1787 | #elif defined(__APPLE__) && defined(__MACH__) | ||
1788 | sprintf(ipod->diskname,"/dev/disk%d",i); | ||
1789 | #else | ||
1790 | #error No disk paths defined for this platform | ||
1791 | #endif | ||
1792 | if ((result = ipod_open(ipod, 1)) < 0) { | ||
1793 | if(result == -2) { | ||
1794 | denied++; | ||
1795 | } | ||
1796 | ipod_close(ipod); | ||
1797 | continue; | ||
1798 | } | ||
1799 | |||
1800 | if (read_partinfo(ipod,1) < 0) { | ||
1801 | ipod_close(ipod); | ||
1802 | continue; | ||
1803 | } | ||
1804 | |||
1805 | if ((ipod->pinfo[0].start==0) || (ipod->pinfo[0].type != 0)) { | ||
1806 | ipod_close(ipod); | ||
1807 | continue; | ||
1808 | } | ||
1809 | |||
1810 | if (read_directory(ipod) < 0) { | ||
1811 | ipod_close(ipod); | ||
1812 | continue; | ||
1813 | } | ||
1814 | |||
1815 | ipod_version=(ipod->ipod_directory[ipod->ososimage].vers>>8); | ||
1816 | ipod->ramsize = 0; | ||
1817 | #ifdef __WIN32__ | ||
1818 | /* Windows requires the ipod in R/W mode for SCSI Inquiry. | ||
1819 | * ipod_reopen_rw does unmount the player on OS X so do this on | ||
1820 | * W32 only during scanning. */ | ||
1821 | ipod_reopen_rw(ipod); | ||
1822 | #endif | ||
1823 | ipod_get_xmlinfo(ipod); | ||
1824 | ipod_get_ramsize(ipod); | ||
1825 | if (getmodel(ipod,ipod_version) < 0) { | ||
1826 | ipod_close(ipod); | ||
1827 | continue; | ||
1828 | } | ||
1829 | |||
1830 | #ifdef __WIN32__ | ||
1831 | printf("[INFO] Ipod found - %s (\"%s\") - disk device %d\n", | ||
1832 | ipod->modelstr,ipod->macpod ? "macpod" : "winpod",i); | ||
1833 | #else | ||
1834 | printf("[INFO] Ipod found - %s (\"%s\") - %s\n", | ||
1835 | ipod->modelstr,ipod->macpod ? "macpod" : "winpod",ipod->diskname); | ||
1836 | #endif | ||
1837 | n++; | ||
1838 | /* save the complete ipod_t structure for match. The for loop might | ||
1839 | * overwrite it, so we need to restore it later if only one found. */ | ||
1840 | memcpy(&ipod_found, ipod, sizeof(struct ipod_t)); | ||
1841 | ipod_close(ipod); | ||
1842 | } | ||
1843 | |||
1844 | if (n==1) { | ||
1845 | /* restore the ipod_t structure, it might have been overwritten */ | ||
1846 | memcpy(ipod, &ipod_found, sizeof(struct ipod_t)); | ||
1847 | } | ||
1848 | else if(n == 0 && denied) { | ||
1849 | printf("[ERR] FATAL: Permission denied on %d device(s) and no ipod detected.\n", denied); | ||
1850 | #ifdef __WIN32__ | ||
1851 | printf("[ERR] You need to run this program with administrator priviledges!\n"); | ||
1852 | #else | ||
1853 | printf("[ERR] You need permissions for raw disc access for this program to work!\n"); | ||
1854 | #endif | ||
1855 | } | ||
1856 | return (n == 0 && denied) ? -1 : n; | ||
1857 | } | ||
1858 | |||
1859 | static void put_int32le(uint32_t x, unsigned char* p) | ||
1860 | { | ||
1861 | p[0] = x & 0xff; | ||
1862 | p[1] = (x >> 8) & 0xff; | ||
1863 | p[2] = (x >> 16) & 0xff; | ||
1864 | p[3] = (x >> 24) & 0xff; | ||
1865 | } | ||
1866 | |||
1867 | int write_dos_partition_table(struct ipod_t* ipod) | ||
1868 | { | ||
1869 | unsigned char* p; | ||
1870 | int i, n; | ||
1871 | uint32_t type; | ||
1872 | |||
1873 | /* Only support 512-byte sectors at the moment */ | ||
1874 | if ( ipod->sector_size != 512 ) | ||
1875 | { | ||
1876 | fprintf(stderr,"[ERR] Only ipods with 512 bytes per sector are supported.\n"); | ||
1877 | return -1; | ||
1878 | } | ||
1879 | if(ipod->sectorbuf == NULL) { | ||
1880 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
1881 | return -1; | ||
1882 | } | ||
1883 | |||
1884 | /* Firstly zero the entire MBR */ | ||
1885 | memset(ipod->sectorbuf, 0, ipod->sector_size); | ||
1886 | |||
1887 | /* Now add the partition info */ | ||
1888 | for (i=0; i < 4 ; i++) | ||
1889 | { | ||
1890 | p = ipod->sectorbuf + 0x1be + i*16; | ||
1891 | |||
1892 | /* Ensure first partition is type 0, and second is 0xb */ | ||
1893 | if (i==0) { type = 0; } | ||
1894 | else if (i==1) { type = 0xb; } | ||
1895 | else { type = ipod->pinfo[i].type; } | ||
1896 | |||
1897 | put_int32le(type, p + 4); | ||
1898 | put_int32le(ipod->pinfo[i].start, p + 8); | ||
1899 | put_int32le(ipod->pinfo[i].size, p + 12); | ||
1900 | } | ||
1901 | |||
1902 | /* Finally add the magic */ | ||
1903 | ipod->sectorbuf[0x1fe] = 0x55; | ||
1904 | ipod->sectorbuf[0x1ff] = 0xaa; | ||
1905 | |||
1906 | if (ipod_seek(ipod, 0) < 0) { | ||
1907 | fprintf(stderr,"[ERR] Seek failed writing MBR\n"); | ||
1908 | return -1; | ||
1909 | } | ||
1910 | |||
1911 | /* Write MBR */ | ||
1912 | if ((n = ipod_write(ipod, ipod->sector_size)) < 0) { | ||
1913 | perror("[ERR] Write failed\n"); | ||
1914 | return -1; | ||
1915 | } | ||
1916 | |||
1917 | return 0; | ||
1918 | } | ||
1919 | |||
1920 | /* Get the XML Device Information, as documented here: | ||
1921 | |||
1922 | http://www.ipodlinux.org/wiki/Device_Information | ||
1923 | */ | ||
1924 | |||
1925 | int ipod_get_xmlinfo(struct ipod_t* ipod) | ||
1926 | { | ||
1927 | unsigned char hdr[255]; | ||
1928 | unsigned char buf[255]; | ||
1929 | char* p; | ||
1930 | int psize; | ||
1931 | int npages; | ||
1932 | int i; | ||
1933 | |||
1934 | if (ipod_scsi_inquiry(ipod, 0xc0, buf, sizeof(buf)) < 0) | ||
1935 | { | ||
1936 | fprintf(stderr,"[ERR] Sending SCSI Command failed.\n"); | ||
1937 | return -1; | ||
1938 | } | ||
1939 | |||
1940 | /* Reading directly into hdr[] causes problems (for an unknown reason) on | ||
1941 | win32 */ | ||
1942 | memcpy(hdr, buf, sizeof(hdr)); | ||
1943 | |||
1944 | npages = hdr[3]; | ||
1945 | |||
1946 | psize = npages * 0xf8; /* Hopefully this is enough. */ | ||
1947 | |||
1948 | ipod->xmlinfo = malloc(psize); | ||
1949 | ipod->xmlinfo_len = 0; | ||
1950 | |||
1951 | if (ipod->xmlinfo == NULL) { | ||
1952 | fprintf(stderr,"[ERR] Could not allocate RAM for xmlinfo\n"); | ||
1953 | return -1; | ||
1954 | } | ||
1955 | |||
1956 | p = ipod->xmlinfo; | ||
1957 | |||
1958 | for (i=0; i < npages; i++) { | ||
1959 | if (ipod_scsi_inquiry(ipod, hdr[i+4], buf, sizeof(buf)) < 0) { | ||
1960 | fprintf(stderr,"[ERR] Sending SCSI Command failed.\n"); | ||
1961 | return -1; | ||
1962 | } | ||
1963 | |||
1964 | if ((buf[3] + ipod->xmlinfo_len) > psize) { | ||
1965 | fprintf(stderr,"[ERR] Ran out of memory reading xmlinfo\n"); | ||
1966 | free(ipod->xmlinfo); | ||
1967 | ipod->xmlinfo = NULL; | ||
1968 | ipod->xmlinfo_len = 0; | ||
1969 | return -1; | ||
1970 | } | ||
1971 | |||
1972 | memcpy(p, buf + 4, buf[3]); | ||
1973 | p += buf[3]; | ||
1974 | ipod->xmlinfo_len += buf[3]; | ||
1975 | } | ||
1976 | |||
1977 | /* NULL-terminate the XML info */ | ||
1978 | *p = 0; | ||
1979 | |||
1980 | fprintf(stderr,"[INFO] Read XML info (%d bytes)\n",ipod->xmlinfo_len); | ||
1981 | |||
1982 | return 0; | ||
1983 | } | ||
1984 | |||
1985 | void ipod_get_ramsize(struct ipod_t* ipod) | ||
1986 | { | ||
1987 | const char needle[] = "<key>RAM</key>\n<integer>"; | ||
1988 | char* p; | ||
1989 | |||
1990 | if (ipod->xmlinfo == NULL) | ||
1991 | return; | ||
1992 | |||
1993 | p = strstr(ipod->xmlinfo, needle); | ||
1994 | |||
1995 | if (p) { | ||
1996 | ipod->ramsize = atoi(p + sizeof(needle) - 1); | ||
1997 | } | ||
1998 | } | ||
1999 | |||
2000 | #ifndef RBUTIL | ||
2001 | |||
2002 | static inline uint32_t getuint32le(unsigned char* buf) | ||
2003 | { | ||
2004 | int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | ||
2005 | |||
2006 | return res; | ||
2007 | } | ||
2008 | |||
2009 | /* testMarker and GetSecurityBlockKey based on code from BadBlocks and | ||
2010 | Kingstone, posted at http://ipodlinux.org/Flash_Decryption | ||
2011 | |||
2012 | */ | ||
2013 | |||
2014 | static bool testMarker(int marker) | ||
2015 | { | ||
2016 | int mask, decrypt, temp1, temp2; | ||
2017 | |||
2018 | mask = (marker&0xff)|((marker&0xff)<<8)|((marker&0xff)<<16)|((marker&0xff)<<24); | ||
2019 | decrypt = marker ^ mask; | ||
2020 | temp1=(int)((unsigned int)decrypt>>24); | ||
2021 | temp2=decrypt<<8; | ||
2022 | |||
2023 | if (temp1==0) | ||
2024 | return false; | ||
2025 | |||
2026 | temp2=(int)((unsigned int)temp2>>24); | ||
2027 | decrypt=decrypt<<16; | ||
2028 | decrypt=(int)((unsigned int)decrypt>>24); | ||
2029 | |||
2030 | if ((temp1 < temp2) && (temp2 < decrypt)) | ||
2031 | { | ||
2032 | temp1 = temp1 & 0xf; | ||
2033 | temp2 = temp2 & 0xf; | ||
2034 | decrypt = decrypt & 0xf; | ||
2035 | |||
2036 | if ((temp1 > temp2) && (temp2 > decrypt) && (decrypt != 0)) | ||
2037 | { | ||
2038 | return true; | ||
2039 | } | ||
2040 | } | ||
2041 | return false; | ||
2042 | } | ||
2043 | |||
2044 | static int GetSecurityBlockKey(unsigned char *data, unsigned char* this_key) | ||
2045 | { | ||
2046 | int constant = 0x54c3a298; | ||
2047 | int key=0; | ||
2048 | int nkeys = 0; | ||
2049 | int aMarker=0; | ||
2050 | int pos=0; | ||
2051 | int c, count; | ||
2052 | int temp1; | ||
2053 | static const int offset[8]={0x5,0x25,0x6f,0x69,0x15,0x4d,0x40,0x34}; | ||
2054 | |||
2055 | for (c = 0; c < 8; c++) | ||
2056 | { | ||
2057 | pos = offset[c]*4; | ||
2058 | aMarker = getuint32le(data + pos); | ||
2059 | |||
2060 | if (testMarker(aMarker)) | ||
2061 | { | ||
2062 | if (c<7) | ||
2063 | pos =(offset[c+1]*4)+4; | ||
2064 | else | ||
2065 | pos =(offset[0]*4)+4; | ||
2066 | |||
2067 | key=0; | ||
2068 | |||
2069 | temp1=aMarker; | ||
2070 | |||
2071 | for (count=0;count<2;count++){ | ||
2072 | int word = getuint32le(data + pos); | ||
2073 | temp1 = aMarker; | ||
2074 | temp1 = temp1^word; | ||
2075 | temp1 = temp1^constant; | ||
2076 | key = temp1; | ||
2077 | pos = pos+4; | ||
2078 | } | ||
2079 | int r1=0x6f; | ||
2080 | int r2=0; | ||
2081 | int r12; | ||
2082 | int r14; | ||
2083 | unsigned int r_tmp; | ||
2084 | |||
2085 | for (count=2;count<128;count=count+2){ | ||
2086 | r2=getuint32le(data+count*4); | ||
2087 | r12=getuint32le(data+(count*4)+4); | ||
2088 | r_tmp=(unsigned int)r12>>16; | ||
2089 | r14=r2 | ((int)r_tmp); | ||
2090 | r2=r2&0xffff; | ||
2091 | r2=r2 | r12; | ||
2092 | r1=r1^r14; | ||
2093 | r1=r1+r2; | ||
2094 | } | ||
2095 | key=key^r1; | ||
2096 | |||
2097 | // Invert key, little endian | ||
2098 | this_key[0] = key & 0xff; | ||
2099 | this_key[1] = (key >> 8) & 0xff; | ||
2100 | this_key[2] = (key >> 16) & 0xff; | ||
2101 | this_key[3] = (key >> 24) & 0xff; | ||
2102 | nkeys++; | ||
2103 | } | ||
2104 | } | ||
2105 | return nkeys; | ||
2106 | } | ||
2107 | |||
2108 | static int find_key(struct ipod_t* ipod, int aupd, unsigned char* key) | ||
2109 | { | ||
2110 | int n; | ||
2111 | |||
2112 | /* Firstly read the security block and find the RC4 key. This is | ||
2113 | in the sector preceeding the AUPD image. */ | ||
2114 | |||
2115 | if(ipod->sectorbuf == NULL) { | ||
2116 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
2117 | return -1; | ||
2118 | } | ||
2119 | fprintf(stderr, "[INFO] Reading security block at offset 0x%08x\n",ipod->ipod_directory[aupd].devOffset-ipod->sector_size); | ||
2120 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset-ipod->sector_size) < 0) { | ||
2121 | return -1; | ||
2122 | } | ||
2123 | |||
2124 | if ((n = ipod_read(ipod, 512)) < 0) { | ||
2125 | return -1; | ||
2126 | } | ||
2127 | |||
2128 | n = GetSecurityBlockKey(ipod->sectorbuf, key); | ||
2129 | |||
2130 | if (n != 1) | ||
2131 | { | ||
2132 | fprintf(stderr, "[ERR] %d keys found in security block, can not continue\n",n); | ||
2133 | return -1; | ||
2134 | } | ||
2135 | |||
2136 | return 0; | ||
2137 | } | ||
2138 | |||
2139 | int read_aupd(struct ipod_t* ipod, char* filename) | ||
2140 | { | ||
2141 | int length; | ||
2142 | int i; | ||
2143 | int outfile; | ||
2144 | int n; | ||
2145 | int aupd; | ||
2146 | struct rc4_key_t rc4; | ||
2147 | unsigned char key[4]; | ||
2148 | unsigned long chksum=0; | ||
2149 | |||
2150 | if(ipod->sectorbuf == NULL) { | ||
2151 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
2152 | return -1; | ||
2153 | } | ||
2154 | aupd = 0; | ||
2155 | while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD)) | ||
2156 | { | ||
2157 | aupd++; | ||
2158 | } | ||
2159 | |||
2160 | if (aupd == ipod->nimages) | ||
2161 | { | ||
2162 | fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n"); | ||
2163 | return -1; | ||
2164 | } | ||
2165 | |||
2166 | length = ipod->ipod_directory[aupd].len; | ||
2167 | |||
2168 | fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); | ||
2169 | |||
2170 | if (find_key(ipod, aupd, key) < 0) | ||
2171 | { | ||
2172 | return -1; | ||
2173 | } | ||
2174 | |||
2175 | fprintf(stderr, "[INFO] Decrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]); | ||
2176 | |||
2177 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) { | ||
2178 | return -1; | ||
2179 | } | ||
2180 | |||
2181 | i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); | ||
2182 | |||
2183 | if ((n = ipod_read(ipod,i)) < 0) { | ||
2184 | return -1; | ||
2185 | } | ||
2186 | |||
2187 | if (n < i) { | ||
2188 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", | ||
2189 | i,n); | ||
2190 | return -1; | ||
2191 | } | ||
2192 | |||
2193 | /* Perform the decryption - this is standard (A)RC4 */ | ||
2194 | matrixArc4Init(&rc4, key, 4); | ||
2195 | matrixArc4(&rc4, ipod->sectorbuf, ipod->sectorbuf, length); | ||
2196 | |||
2197 | chksum = 0; | ||
2198 | for (i = 0; i < (int)length; i++) { | ||
2199 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
2200 | chksum += ipod->sectorbuf[i]; | ||
2201 | } | ||
2202 | |||
2203 | if (chksum != ipod->ipod_directory[aupd].chksum) | ||
2204 | { | ||
2205 | fprintf(stderr,"[ERR] Decryption failed - checksum error\n"); | ||
2206 | return -1; | ||
2207 | } | ||
2208 | fprintf(stderr,"[INFO] Decrypted OK (checksum matches header)\n"); | ||
2209 | |||
2210 | outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); | ||
2211 | if (outfile < 0) { | ||
2212 | fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); | ||
2213 | return -1; | ||
2214 | } | ||
2215 | |||
2216 | n = write(outfile,ipod->sectorbuf,length); | ||
2217 | if (n != length) { | ||
2218 | fprintf(stderr,"[ERR] Write error - %d\n",n); | ||
2219 | } | ||
2220 | close(outfile); | ||
2221 | |||
2222 | return 0; | ||
2223 | } | ||
2224 | |||
2225 | int write_aupd(struct ipod_t* ipod, char* filename) | ||
2226 | { | ||
2227 | unsigned int length; | ||
2228 | int i; | ||
2229 | int x; | ||
2230 | int n; | ||
2231 | int infile; | ||
2232 | int newsize; | ||
2233 | int aupd; | ||
2234 | unsigned long chksum=0; | ||
2235 | struct rc4_key_t rc4; | ||
2236 | unsigned char key[4]; | ||
2237 | |||
2238 | if(ipod->sectorbuf == NULL) { | ||
2239 | fprintf(stderr,"[ERR] Buffer not initialized."); | ||
2240 | return -1; | ||
2241 | } | ||
2242 | /* First check that the input file is the correct type for this ipod. */ | ||
2243 | infile=open(filename,O_RDONLY); | ||
2244 | if (infile < 0) { | ||
2245 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
2246 | return -1; | ||
2247 | } | ||
2248 | |||
2249 | length = filesize(infile); | ||
2250 | newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1); | ||
2251 | |||
2252 | fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", | ||
2253 | length,newsize); | ||
2254 | |||
2255 | if (newsize > BUFFER_SIZE) { | ||
2256 | fprintf(stderr,"[ERR] Input file too big for buffer\n"); | ||
2257 | if (infile >= 0) close(infile); | ||
2258 | return -1; | ||
2259 | } | ||
2260 | |||
2261 | /* Find aupd image number */ | ||
2262 | aupd = 0; | ||
2263 | while ((aupd < ipod->nimages) && (ipod->ipod_directory[aupd].ftype != FTYPE_AUPD)) | ||
2264 | { | ||
2265 | aupd++; | ||
2266 | } | ||
2267 | |||
2268 | if (aupd == ipod->nimages) | ||
2269 | { | ||
2270 | fprintf(stderr,"[ERR] No AUPD image in firmware partition.\n"); | ||
2271 | return -1; | ||
2272 | } | ||
2273 | |||
2274 | if (length != ipod->ipod_directory[aupd].len) | ||
2275 | { | ||
2276 | fprintf(stderr,"[ERR] AUPD image (%d bytes) differs in size to %s (%d bytes).\n", | ||
2277 | ipod->ipod_directory[aupd].len, filename, length); | ||
2278 | return -1; | ||
2279 | } | ||
2280 | |||
2281 | if (find_key(ipod, aupd, key) < 0) | ||
2282 | { | ||
2283 | return -1; | ||
2284 | } | ||
2285 | |||
2286 | fprintf(stderr, "[INFO] Encrypting AUPD image with key %02x%02x%02x%02x\n",key[0],key[1],key[2],key[3]); | ||
2287 | |||
2288 | /* We now know we have enough space, so write it. */ | ||
2289 | |||
2290 | fprintf(stderr,"[INFO] Reading input file...\n"); | ||
2291 | n = read(infile,ipod->sectorbuf,length); | ||
2292 | if (n < 0) { | ||
2293 | fprintf(stderr,"[ERR] Couldn't read input file\n"); | ||
2294 | close(infile); | ||
2295 | return -1; | ||
2296 | } | ||
2297 | close(infile); | ||
2298 | |||
2299 | /* Pad the data with zeros */ | ||
2300 | memset(ipod->sectorbuf+length,0,newsize-length); | ||
2301 | |||
2302 | /* Calculate the new checksum (before we encrypt) */ | ||
2303 | chksum = 0; | ||
2304 | for (i = 0; i < (int)length; i++) { | ||
2305 | /* add 8 unsigned bits but keep a 32 bit sum */ | ||
2306 | chksum += ipod->sectorbuf[i]; | ||
2307 | } | ||
2308 | |||
2309 | /* Perform the encryption - this is standard (A)RC4 */ | ||
2310 | matrixArc4Init(&rc4, key, 4); | ||
2311 | matrixArc4(&rc4, ipod->sectorbuf, ipod->sectorbuf, length); | ||
2312 | |||
2313 | if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[aupd].devOffset) < 0) { | ||
2314 | fprintf(stderr,"[ERR] Seek failed\n"); | ||
2315 | return -1; | ||
2316 | } | ||
2317 | |||
2318 | if ((n = ipod_write(ipod,newsize)) < 0) { | ||
2319 | perror("[ERR] Write failed\n"); | ||
2320 | return -1; | ||
2321 | } | ||
2322 | |||
2323 | if (n < newsize) { | ||
2324 | fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" | ||
2325 | ,newsize,n); | ||
2326 | return -1; | ||
2327 | } | ||
2328 | fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); | ||
2329 | |||
2330 | x = ipod->diroffset % ipod->sector_size; | ||
2331 | |||
2332 | /* Read directory */ | ||
2333 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
2334 | |||
2335 | n=ipod_read(ipod, ipod->sector_size); | ||
2336 | if (n < 0) { return -1; } | ||
2337 | |||
2338 | /* Update checksum */ | ||
2339 | fprintf(stderr,"[INFO] Updating checksum to 0x%08x (was 0x%08x)\n",(unsigned int)chksum,le2int(ipod->sectorbuf + x + aupd*40 + 28)); | ||
2340 | int2le(chksum,ipod->sectorbuf+x+aupd*40+28); | ||
2341 | |||
2342 | /* Write directory */ | ||
2343 | if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } | ||
2344 | n=ipod_write(ipod, ipod->sector_size); | ||
2345 | if (n < 0) { return -1; } | ||
2346 | |||
2347 | return 0; | ||
2348 | } | ||
2349 | |||
2350 | #endif | ||