summaryrefslogtreecommitdiff
path: root/rbutil/sansapatcher/sansapatcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/sansapatcher/sansapatcher.c')
-rw-r--r--rbutil/sansapatcher/sansapatcher.c702
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
37extern 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
44unsigned char* sectorbuf;
45
46char* 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
66off_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
82unsigned short static inline le2ushort(unsigned char* buf)
83{
84 unsigned short res = (buf[1] << 8) | buf[0];
85
86 return res;
87}
88
89int 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
96int 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
103int static inline getint16le(char* buf)
104{
105 int16_t res = (buf[1] << 8) | buf[0];
106
107 return res;
108}
109
110void static inline short2le(unsigned short val, unsigned char* addr)
111{
112 addr[0] = val & 0xFF;
113 addr[1] = (val >> 8) & 0xff;
114}
115
116void 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
124void 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
137int 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 */
192static 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 */
199static 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
217static 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
243static uint32_t keys[][4] = {
244 { 0xe494e96e, 0x3ee32966, 0x6f48512b, 0xa93fbb42 }, /* "sansa" */
245 { 0xd7b10538, 0xc662945b, 0x1b3fce68, 0xf389c0e6 }, /* "sansa_gh" */
246};
247
248/*
249
250tea_decrypt() from http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
251
252"Following is an adaptation of the reference encryption and decryption
253routines in C, released into the public domain by David Wheeler and
254Roger 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
263void 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
278void 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
309static 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
324static 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
339int 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
370int 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
424int 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
474int 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) {
542printf("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
564int 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
587int 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
643int 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
680void 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}