diff options
Diffstat (limited to 'rbutil/sansapatcher/sansapatcher.c')
-rw-r--r-- | rbutil/sansapatcher/sansapatcher.c | 702 |
1 files changed, 702 insertions, 0 deletions
diff --git a/rbutil/sansapatcher/sansapatcher.c b/rbutil/sansapatcher/sansapatcher.c new file mode 100644 index 0000000000..190cf09452 --- /dev/null +++ b/rbutil/sansapatcher/sansapatcher.c | |||
@@ -0,0 +1,702 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id: ipodpatcher.c 12264 2007-02-10 20:09:23Z dave $ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #include <stdio.h> | ||
21 | #include <unistd.h> | ||
22 | #include <fcntl.h> | ||
23 | #include <string.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <inttypes.h> | ||
26 | #include <sys/types.h> | ||
27 | #include <sys/stat.h> | ||
28 | |||
29 | #include "parttypes.h" | ||
30 | #include "sansaio.h" | ||
31 | #include "sansapatcher.h" | ||
32 | #include "bootimg.h" | ||
33 | |||
34 | /* The offset of the MI4 image header in the firmware partition */ | ||
35 | #define PPMI_OFFSET 0x80000 | ||
36 | |||
37 | extern int verbose; | ||
38 | |||
39 | /* Windows requires the buffer for disk I/O to be aligned in memory on a | ||
40 | multiple of the disk volume size - so we use a single global variable | ||
41 | and initialise it with sansa_alloc_buf() in main(). | ||
42 | */ | ||
43 | |||
44 | unsigned char* sectorbuf; | ||
45 | |||
46 | char* get_parttype(int pt) | ||
47 | { | ||
48 | int i; | ||
49 | static char unknown[]="Unknown"; | ||
50 | |||
51 | if (pt == -1) { | ||
52 | return "HFS/HFS+"; | ||
53 | } | ||
54 | |||
55 | i=0; | ||
56 | while (parttypes[i].name != NULL) { | ||
57 | if (parttypes[i].type == pt) { | ||
58 | return (parttypes[i].name); | ||
59 | } | ||
60 | i++; | ||
61 | } | ||
62 | |||
63 | return unknown; | ||
64 | } | ||
65 | |||
66 | off_t filesize(int fd) { | ||
67 | struct stat buf; | ||
68 | |||
69 | if (fstat(fd,&buf) < 0) { | ||
70 | perror("[ERR] Checking filesize of input file"); | ||
71 | return -1; | ||
72 | } else { | ||
73 | return(buf.st_size); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | /* Partition table parsing code taken from Rockbox */ | ||
78 | |||
79 | #define MAX_SECTOR_SIZE 2048 | ||
80 | #define SECTOR_SIZE 512 | ||
81 | |||
82 | unsigned short static inline le2ushort(unsigned char* buf) | ||
83 | { | ||
84 | unsigned short res = (buf[1] << 8) | buf[0]; | ||
85 | |||
86 | return res; | ||
87 | } | ||
88 | |||
89 | int static inline le2int(unsigned char* buf) | ||
90 | { | ||
91 | int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | ||
92 | |||
93 | return res; | ||
94 | } | ||
95 | |||
96 | int static inline be2int(unsigned char* buf) | ||
97 | { | ||
98 | int32_t res = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; | ||
99 | |||
100 | return res; | ||
101 | } | ||
102 | |||
103 | int static inline getint16le(char* buf) | ||
104 | { | ||
105 | int16_t res = (buf[1] << 8) | buf[0]; | ||
106 | |||
107 | return res; | ||
108 | } | ||
109 | |||
110 | void static inline short2le(unsigned short val, unsigned char* addr) | ||
111 | { | ||
112 | addr[0] = val & 0xFF; | ||
113 | addr[1] = (val >> 8) & 0xff; | ||
114 | } | ||
115 | |||
116 | void static inline int2le(unsigned int val, unsigned char* addr) | ||
117 | { | ||
118 | addr[0] = val & 0xFF; | ||
119 | addr[1] = (val >> 8) & 0xff; | ||
120 | addr[2] = (val >> 16) & 0xff; | ||
121 | addr[3] = (val >> 24) & 0xff; | ||
122 | } | ||
123 | |||
124 | void int2be(unsigned int val, unsigned char* addr) | ||
125 | { | ||
126 | addr[0] = (val >> 24) & 0xff; | ||
127 | addr[1] = (val >> 16) & 0xff; | ||
128 | addr[2] = (val >> 8) & 0xff; | ||
129 | addr[3] = val & 0xFF; | ||
130 | } | ||
131 | |||
132 | |||
133 | #define BYTES2INT32(array,pos)\ | ||
134 | ((long)array[pos] | ((long)array[pos+1] << 8 ) |\ | ||
135 | ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) | ||
136 | |||
137 | int read_partinfo(struct sansa_t* sansa, int silent) | ||
138 | { | ||
139 | int i; | ||
140 | unsigned long count; | ||
141 | |||
142 | count = sansa_read(sansa,sectorbuf, sansa->sector_size); | ||
143 | |||
144 | if (count <= 0) { | ||
145 | print_error(" Error reading from disk: "); | ||
146 | return -1; | ||
147 | } | ||
148 | |||
149 | if ((sectorbuf[510] == 0x55) && (sectorbuf[511] == 0xaa)) { | ||
150 | /* parse partitions */ | ||
151 | for ( i = 0; i < 4; i++ ) { | ||
152 | unsigned char* ptr = sectorbuf + 0x1be + 16*i; | ||
153 | sansa->pinfo[i].type = ptr[4]; | ||
154 | sansa->pinfo[i].start = BYTES2INT32(ptr, 8); | ||
155 | sansa->pinfo[i].size = BYTES2INT32(ptr, 12); | ||
156 | |||
157 | /* extended? */ | ||
158 | if ( sansa->pinfo[i].type == 5 ) { | ||
159 | /* not handled yet */ | ||
160 | } | ||
161 | } | ||
162 | } else if ((sectorbuf[0] == 'E') && (sectorbuf[1] == 'R')) { | ||
163 | if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n"); | ||
164 | return -1; | ||
165 | } | ||
166 | |||
167 | /* Calculate the starting position of the firmware partition */ | ||
168 | sansa->start = (loff_t)sansa->pinfo[1].start*(loff_t)sansa->sector_size; | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | |||
173 | /* | ||
174 | * CRC32 implementation taken from: | ||
175 | * | ||
176 | * efone - Distributed internet phone system. | ||
177 | * | ||
178 | * (c) 1999,2000 Krzysztof Dabrowski | ||
179 | * (c) 1999,2000 ElysiuM deeZine | ||
180 | * | ||
181 | * This program is free software; you can redistribute it and/or | ||
182 | * modify it under the terms of the GNU General Public License | ||
183 | * as published by the Free Software Foundation; either version | ||
184 | * 2 of the License, or (at your option) any later version. | ||
185 | * | ||
186 | */ | ||
187 | |||
188 | /* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab(). | ||
189 | * so make sure, you call it before using the other | ||
190 | * functions! | ||
191 | */ | ||
192 | static unsigned int crc_tab[256]; | ||
193 | |||
194 | /* chksum_crc() -- to a given block, this one calculates the | ||
195 | * crc32-checksum until the length is | ||
196 | * reached. the crc32-checksum will be | ||
197 | * the result. | ||
198 | */ | ||
199 | static unsigned int chksum_crc32 (unsigned char *block, unsigned int length) | ||
200 | { | ||
201 | register unsigned long crc; | ||
202 | unsigned long i; | ||
203 | |||
204 | crc = 0; | ||
205 | for (i = 0; i < length; i++) | ||
206 | { | ||
207 | crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF]; | ||
208 | } | ||
209 | return (crc); | ||
210 | } | ||
211 | |||
212 | /* chksum_crc32gentab() -- to a global crc_tab[256], this one will | ||
213 | * calculate the crcTable for crc32-checksums. | ||
214 | * it is generated to the polynom [..] | ||
215 | */ | ||
216 | |||
217 | static void chksum_crc32gentab (void) | ||
218 | { | ||
219 | unsigned long crc, poly; | ||
220 | int i, j; | ||
221 | |||
222 | poly = 0xEDB88320L; | ||
223 | for (i = 0; i < 256; i++) | ||
224 | { | ||
225 | crc = i; | ||
226 | for (j = 8; j > 0; j--) | ||
227 | { | ||
228 | if (crc & 1) | ||
229 | { | ||
230 | crc = (crc >> 1) ^ poly; | ||
231 | } | ||
232 | else | ||
233 | { | ||
234 | crc >>= 1; | ||
235 | } | ||
236 | } | ||
237 | crc_tab[i] = crc; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | /* Known keys for Sansa E200 firmwares: */ | ||
242 | #define NUM_KEYS 2 | ||
243 | static uint32_t keys[][4] = { | ||
244 | { 0xe494e96e, 0x3ee32966, 0x6f48512b, 0xa93fbb42 }, /* "sansa" */ | ||
245 | { 0xd7b10538, 0xc662945b, 0x1b3fce68, 0xf389c0e6 }, /* "sansa_gh" */ | ||
246 | }; | ||
247 | |||
248 | /* | ||
249 | |||
250 | tea_decrypt() from http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm | ||
251 | |||
252 | "Following is an adaptation of the reference encryption and decryption | ||
253 | routines in C, released into the public domain by David Wheeler and | ||
254 | Roger Needham:" | ||
255 | |||
256 | */ | ||
257 | |||
258 | /* NOTE: The mi4 version of TEA uses a different initial value to sum compared | ||
259 | to the reference implementation and the main loop is 8 iterations, not | ||
260 | 32. | ||
261 | */ | ||
262 | |||
263 | void tea_decrypt(uint32_t* v0, uint32_t* v1, uint32_t* k) { | ||
264 | uint32_t sum=0xF1BBCDC8, i; /* set up */ | ||
265 | uint32_t delta=0x9E3779B9; /* a key schedule constant */ | ||
266 | uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */ | ||
267 | for(i=0; i<8; i++) { /* basic cycle start */ | ||
268 | *v1 -= ((*v0<<4) + k2) ^ (*v0 + sum) ^ ((*v0>>5) + k3); | ||
269 | *v0 -= ((*v1<<4) + k0) ^ (*v1 + sum) ^ ((*v1>>5) + k1); | ||
270 | sum -= delta; /* end cycle */ | ||
271 | } | ||
272 | } | ||
273 | |||
274 | /* mi4 files are encrypted in 64-bit blocks (two little-endian 32-bit | ||
275 | integers) and the key is incremented after each block | ||
276 | */ | ||
277 | |||
278 | void tea_decrypt_buf(unsigned char* src, unsigned char* dest, size_t n, uint32_t * key) | ||
279 | { | ||
280 | uint32_t v0, v1; | ||
281 | int i; | ||
282 | |||
283 | for (i = 0; i < (n / 8); i++) { | ||
284 | v0 = le2int(src); | ||
285 | v1 = le2int(src+4); | ||
286 | |||
287 | tea_decrypt(&v0, &v1, key); | ||
288 | |||
289 | int2le(v0, dest); | ||
290 | int2le(v1, dest+4); | ||
291 | |||
292 | src += 8; | ||
293 | dest += 8; | ||
294 | |||
295 | /* Now increment the key */ | ||
296 | key[0]++; | ||
297 | if (key[0]==0) { | ||
298 | key[1]++; | ||
299 | if (key[1]==0) { | ||
300 | key[2]++; | ||
301 | if (key[2]==0) { | ||
302 | key[3]++; | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | } | ||
307 | } | ||
308 | |||
309 | static int get_mi4header(unsigned char* buf,struct mi4header_t* mi4header) | ||
310 | { | ||
311 | if (memcmp(buf,"PPOS",4)!=0) | ||
312 | return -1; | ||
313 | |||
314 | mi4header->version = le2int(buf+0x04); | ||
315 | mi4header->length = le2int(buf+0x08); | ||
316 | mi4header->crc32 = le2int(buf+0x0c); | ||
317 | mi4header->enctype = le2int(buf+0x10); | ||
318 | mi4header->mi4size = le2int(buf+0x14); | ||
319 | mi4header->plaintext = le2int(buf+0x18); | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int set_mi4header(unsigned char* buf,struct mi4header_t* mi4header) | ||
325 | { | ||
326 | if (memcmp(buf,"PPOS",4)!=0) | ||
327 | return -1; | ||
328 | |||
329 | int2le(mi4header->version ,buf+0x04); | ||
330 | int2le(mi4header->length ,buf+0x08); | ||
331 | int2le(mi4header->crc32 ,buf+0x0c); | ||
332 | int2le(mi4header->enctype ,buf+0x10); | ||
333 | int2le(mi4header->mi4size ,buf+0x14); | ||
334 | int2le(mi4header->plaintext ,buf+0x18); | ||
335 | |||
336 | return 0; | ||
337 | } | ||
338 | |||
339 | int sansa_seek_and_read(struct sansa_t* sansa, loff_t pos, unsigned char* buf, int nbytes) | ||
340 | { | ||
341 | int n; | ||
342 | |||
343 | if (sansa_seek(sansa, pos) < 0) { | ||
344 | return -1; | ||
345 | } | ||
346 | |||
347 | if ((n = sansa_read(sansa,buf,nbytes)) < 0) { | ||
348 | return -1; | ||
349 | } | ||
350 | |||
351 | if (n < nbytes) { | ||
352 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", | ||
353 | nbytes,n); | ||
354 | return -1; | ||
355 | } | ||
356 | |||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | |||
361 | /* We identify an E200 based on the following criteria: | ||
362 | |||
363 | 1) Exactly two partitions; | ||
364 | 2) First partition is type "W95 FAT32" (0x0b); | ||
365 | 3) Second partition is type "OS/2 hidden C: drive" (0x84); | ||
366 | 4) The "PPBL" string appears at offset 0 in the 2nd partition; | ||
367 | 5) The "PPMI" string appears at offset PPMI_OFFSET in the 2nd partition. | ||
368 | */ | ||
369 | |||
370 | int is_e200(struct sansa_t* sansa) | ||
371 | { | ||
372 | struct mi4header_t mi4header; | ||
373 | |||
374 | /* Check partition layout */ | ||
375 | |||
376 | if ((sansa->pinfo[0].type != 0x0b) || (sansa->pinfo[1].type != 0x84) || | ||
377 | (sansa->pinfo[2].type != 0x00) || (sansa->pinfo[3].type != 0x00)) { | ||
378 | /* Bad partition layout, abort */ | ||
379 | return -1; | ||
380 | } | ||
381 | |||
382 | /* Check Bootloader header */ | ||
383 | if (sansa_seek_and_read(sansa, sansa->start, sectorbuf, 0x200) < 0) { | ||
384 | return -2; | ||
385 | } | ||
386 | if (memcmp(sectorbuf,"PPBL",4)!=0) { | ||
387 | /* No bootloader header, abort */ | ||
388 | return -4; | ||
389 | } | ||
390 | |||
391 | /* Check Main firmware header */ | ||
392 | if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sectorbuf, 0x200) < 0) { | ||
393 | fprintf(stderr,"[ERR] Seek to 0x%08x in is_e200 failed.\n", | ||
394 | (unsigned int)(sansa->start+PPMI_OFFSET)); | ||
395 | return -5; | ||
396 | } | ||
397 | if (memcmp(sectorbuf,"PPMI",4)!=0) { | ||
398 | /* No bootloader header, abort */ | ||
399 | return -7; | ||
400 | } | ||
401 | |||
402 | /* Check main mi4 file header */ | ||
403 | if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200, sectorbuf, 0x200) < 0) { | ||
404 | fprintf(stderr,"[ERR] Seek to 0x%08x in is_e200 failed.\n", | ||
405 | (unsigned int)(sansa->start+PPMI_OFFSET+0x200)); | ||
406 | return -5; | ||
407 | } | ||
408 | |||
409 | if (get_mi4header(sectorbuf,&mi4header) < 0) { | ||
410 | fprintf(stderr,"[ERR] Invalid mi4header\n"); | ||
411 | return -6; | ||
412 | } | ||
413 | |||
414 | if ((mi4header.mi4size < 100000) && | ||
415 | (memcmp(sectorbuf+0x1f8,"RBBL",4)!=0)) { | ||
416 | sansa->hasoldbootloader = 1; | ||
417 | } else { | ||
418 | sansa->hasoldbootloader = 0; | ||
419 | } | ||
420 | |||
421 | return 0; | ||
422 | } | ||
423 | |||
424 | int sansa_scan(struct sansa_t* sansa) | ||
425 | { | ||
426 | int i; | ||
427 | int n = 0; | ||
428 | char last_disk[4096]; | ||
429 | |||
430 | printf("[INFO] Scanning disk devices...\n"); | ||
431 | |||
432 | for (i = 0; i <= 25 ; i++) { | ||
433 | #ifdef __WIN32__ | ||
434 | sprintf(sansa->diskname,"\\\\.\\PhysicalDrive%d",i); | ||
435 | #elif defined(linux) || defined (__linux) | ||
436 | sprintf(sansa->diskname,"/dev/sd%c",'a'+i); | ||
437 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ | ||
438 | || defined(__bsdi__) || defined(__DragonFly__) | ||
439 | sprintf(sansa->diskname,"/dev/da%d",i); | ||
440 | #elif defined(__APPLE__) && defined(__MACH__) | ||
441 | sprintf(sansa->diskname,"/dev/disk%d",i); | ||
442 | #else | ||
443 | #error No disk paths defined for this platform | ||
444 | #endif | ||
445 | if (sansa_open(sansa, 1) < 0) { | ||
446 | continue; | ||
447 | } | ||
448 | |||
449 | if (read_partinfo(sansa,1) < 0) { | ||
450 | continue; | ||
451 | } | ||
452 | |||
453 | if (is_e200(sansa) < 0) { | ||
454 | continue; | ||
455 | } | ||
456 | |||
457 | #ifdef __WIN32__ | ||
458 | printf("[INFO] E200 found - disk device %d\n",i); | ||
459 | #else | ||
460 | printf("[INFO] E200 found - %s\n",sansa->diskname); | ||
461 | #endif | ||
462 | n++; | ||
463 | strcpy(last_disk,sansa->diskname); | ||
464 | sansa_close(sansa); | ||
465 | } | ||
466 | |||
467 | if (n==1) { | ||
468 | /* Remember the disk name */ | ||
469 | strcpy(sansa->diskname,last_disk); | ||
470 | } | ||
471 | return n; | ||
472 | } | ||
473 | |||
474 | int load_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header) | ||
475 | { | ||
476 | int ppmi_length; | ||
477 | int n; | ||
478 | unsigned char* tmpbuf; | ||
479 | int i; | ||
480 | int key_found; | ||
481 | |||
482 | /* Read 512 bytes from PPMI_OFFSET - the PPMI header plus the mi4 header */ | ||
483 | if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET, buf, 512) < 0) { | ||
484 | return -1; | ||
485 | } | ||
486 | |||
487 | /* No need to check PPMI magic - it's done during init to confirm | ||
488 | this is an E200 */ | ||
489 | ppmi_length = le2int(buf+4); | ||
490 | |||
491 | /* Firstly look for an original firmware after the first image */ | ||
492 | if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, buf, 512) < 0) { | ||
493 | return -1; | ||
494 | } | ||
495 | |||
496 | if (get_mi4header(buf,mi4header)==0) { | ||
497 | /* We have a valid MI4 file after a bootloader, so we use this. */ | ||
498 | if ((n = sansa_seek_and_read(sansa, | ||
499 | sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, | ||
500 | buf, mi4header->mi4size)) < 0) { | ||
501 | return -1; | ||
502 | } | ||
503 | } else { | ||
504 | /* No valid MI4 file, so read the first image. */ | ||
505 | if ((n = sansa_seek_and_read(sansa, | ||
506 | sansa->start + PPMI_OFFSET + 0x200, | ||
507 | buf, ppmi_length)) < 0) { | ||
508 | return -1; | ||
509 | } | ||
510 | } | ||
511 | |||
512 | get_mi4header(buf,mi4header); | ||
513 | |||
514 | #if 0 | ||
515 | printf("mi4header->version =0x%08x\n",mi4header->version); | ||
516 | printf("mi4header->length =0x%08x\n",mi4header->length); | ||
517 | printf("mi4header->crc32 =0x%08x\n",mi4header->crc32); | ||
518 | printf("mi4header->enctype =0x%08x\n",mi4header->enctype); | ||
519 | printf("mi4header->mi4size =0x%08x\n",mi4header->mi4size); | ||
520 | printf("mi4header->plaintext =0x%08x\n",mi4header->plaintext); | ||
521 | #endif | ||
522 | |||
523 | /* Decrypt anything that needs decrypting. */ | ||
524 | if (mi4header->plaintext < mi4header->mi4size - 0x200) { | ||
525 | /* TODO: Check different keys */ | ||
526 | tmpbuf=malloc(mi4header->mi4size-(mi4header->plaintext+0x200)); | ||
527 | if (tmpbuf==NULL) { | ||
528 | fprintf(stderr,"[ERR] Can not allocate memory\n"); | ||
529 | return -1; | ||
530 | } | ||
531 | |||
532 | key_found=0; | ||
533 | for (i=0; i < NUM_KEYS && !key_found ; i++) { | ||
534 | tea_decrypt_buf(buf+(mi4header->plaintext+0x200), | ||
535 | tmpbuf, | ||
536 | mi4header->mi4size-(mi4header->plaintext+0x200), | ||
537 | keys[i]); | ||
538 | key_found = (le2int(tmpbuf+mi4header->length-mi4header->plaintext-4) == 0xaa55aa55); | ||
539 | } | ||
540 | |||
541 | if (key_found) { | ||
542 | printf("Key found - %d\n",i); | ||
543 | memcpy(buf+(mi4header->plaintext+0x200),tmpbuf,mi4header->mi4size-(mi4header->plaintext+0x200)); | ||
544 | free(tmpbuf); | ||
545 | } else { | ||
546 | fprintf(stderr,"[ERR] Failed to decrypt image, aborting\n"); | ||
547 | free(tmpbuf); | ||
548 | return -1; | ||
549 | } | ||
550 | } | ||
551 | |||
552 | /* Increase plaintext value to full file */ | ||
553 | mi4header->plaintext = mi4header->mi4size - 0x200; | ||
554 | |||
555 | /* Update CRC checksum */ | ||
556 | chksum_crc32gentab (); | ||
557 | mi4header->crc32 = chksum_crc32(buf+0x200,mi4header->mi4size-0x200); | ||
558 | |||
559 | set_mi4header(buf,mi4header); | ||
560 | |||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | int read_firmware(struct sansa_t* sansa, char* filename) | ||
565 | { | ||
566 | int res; | ||
567 | int outfile; | ||
568 | struct mi4header_t mi4header; | ||
569 | |||
570 | res = load_original_firmware(sansa,sectorbuf,&mi4header); | ||
571 | if (res < 0) | ||
572 | return res; | ||
573 | |||
574 | outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); | ||
575 | if (outfile < 0) { | ||
576 | fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); | ||
577 | return -1; | ||
578 | } | ||
579 | |||
580 | write(outfile,sectorbuf,mi4header.mi4size); | ||
581 | close(outfile); | ||
582 | |||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | |||
587 | int add_bootloader(struct sansa_t* sansa, char* filename, int type) | ||
588 | { | ||
589 | int res; | ||
590 | int infile; | ||
591 | int bl_length; | ||
592 | struct mi4header_t mi4header; | ||
593 | int n; | ||
594 | int length; | ||
595 | |||
596 | /* Step 1 - read bootloader into RAM. */ | ||
597 | infile=open(filename,O_RDONLY|O_BINARY); | ||
598 | if (infile < 0) { | ||
599 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
600 | return -1; | ||
601 | } | ||
602 | |||
603 | bl_length = filesize(infile); | ||
604 | |||
605 | /* Create PPMI header */ | ||
606 | memset(sectorbuf,0,0x200); | ||
607 | memcpy(sectorbuf,"PPMI",4); | ||
608 | int2le(bl_length, sectorbuf+4); | ||
609 | int2le(0x00020000, sectorbuf+8); | ||
610 | |||
611 | /* Read bootloader into sectorbuf+0x200 */ | ||
612 | n = read(infile,sectorbuf+0x200,bl_length); | ||
613 | if (n < bl_length) { | ||
614 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" | ||
615 | ,bl_length,n); | ||
616 | return -1; | ||
617 | } | ||
618 | |||
619 | /* Load original firmware from Sansa to the space after the bootloader */ | ||
620 | res = load_original_firmware(sansa,sectorbuf+0x200+bl_length,&mi4header); | ||
621 | if (res < 0) | ||
622 | return res; | ||
623 | |||
624 | /* Now write the whole thing back to the Sansa */ | ||
625 | |||
626 | if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) { | ||
627 | fprintf(stderr,"[ERR] Seek to 0x%08x in add_bootloader failed.\n", | ||
628 | (unsigned int)(sansa->start+PPMI_OFFSET)); | ||
629 | return -5; | ||
630 | } | ||
631 | |||
632 | length = 0x200 + bl_length + mi4header.mi4size; | ||
633 | |||
634 | n=sansa_write(sansa, sectorbuf, length); | ||
635 | if (n < length) { | ||
636 | fprintf(stderr,"[ERR] Short write in add_bootloader\n"); | ||
637 | return -6; | ||
638 | } | ||
639 | |||
640 | return 0; | ||
641 | } | ||
642 | |||
643 | int delete_bootloader(struct sansa_t* sansa) | ||
644 | { | ||
645 | int res; | ||
646 | struct mi4header_t mi4header; | ||
647 | int n; | ||
648 | int length; | ||
649 | |||
650 | /* Load original firmware from Sansa to sectorbuf+0x200 */ | ||
651 | res = load_original_firmware(sansa,sectorbuf+0x200,&mi4header); | ||
652 | if (res < 0) | ||
653 | return res; | ||
654 | |||
655 | /* Create PPMI header */ | ||
656 | memset(sectorbuf,0,0x200); | ||
657 | memcpy(sectorbuf,"PPMI",4); | ||
658 | int2le(mi4header.mi4size, sectorbuf+4); | ||
659 | int2le(0x00020000, sectorbuf+8); | ||
660 | |||
661 | /* Now write the whole thing back to the Sansa */ | ||
662 | |||
663 | if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) { | ||
664 | fprintf(stderr,"[ERR] Seek to 0x%08x in add_bootloader failed.\n", | ||
665 | (unsigned int)(sansa->start+PPMI_OFFSET)); | ||
666 | return -5; | ||
667 | } | ||
668 | |||
669 | length = 0x200 + mi4header.mi4size; | ||
670 | |||
671 | n=sansa_write(sansa, sectorbuf, length); | ||
672 | if (n < length) { | ||
673 | fprintf(stderr,"[ERR] Short write in delete_bootloader\n"); | ||
674 | return -6; | ||
675 | } | ||
676 | |||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | void list_images(struct sansa_t* sansa) | ||
681 | { | ||
682 | struct mi4header_t mi4header; | ||
683 | loff_t ppmi_length; | ||
684 | |||
685 | /* Check Main firmware header */ | ||
686 | if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sectorbuf, 0x200) < 0) { | ||
687 | return; | ||
688 | } | ||
689 | |||
690 | ppmi_length = le2int(sectorbuf+4); | ||
691 | |||
692 | printf("[INFO] Image 1 - %llu bytes\n",ppmi_length); | ||
693 | |||
694 | /* Look for an original firmware after the first image */ | ||
695 | if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, sectorbuf, 512) < 0) { | ||
696 | return; | ||
697 | } | ||
698 | |||
699 | if (get_mi4header(sectorbuf,&mi4header)==0) { | ||
700 | printf("[INFO] Image 2 - %d bytes\n",mi4header.mi4size); | ||
701 | } | ||
702 | } | ||