diff options
Diffstat (limited to 'utils/sansapatcher/sansapatcher.c')
-rw-r--r-- | utils/sansapatcher/sansapatcher.c | 975 |
1 files changed, 975 insertions, 0 deletions
diff --git a/utils/sansapatcher/sansapatcher.c b/utils/sansapatcher/sansapatcher.c new file mode 100644 index 0000000000..e3b105dcca --- /dev/null +++ b/utils/sansapatcher/sansapatcher.c | |||
@@ -0,0 +1,975 @@ | |||
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 <sys/types.h> | ||
29 | #include <sys/stat.h> | ||
30 | |||
31 | #include "sansaio.h" | ||
32 | #include "sansapatcher.h" | ||
33 | |||
34 | /* The offset of the MI4 image header in the firmware partition */ | ||
35 | #define PPMI_OFFSET 0x80000 | ||
36 | #define NVPARAMS_OFFSET 0x780000 | ||
37 | #define NVPARAMS_SIZE (0x80000-0x200) | ||
38 | |||
39 | int sansa_verbose = 0; | ||
40 | |||
41 | /* Windows requires the buffer for disk I/O to be aligned in memory on a | ||
42 | multiple of the disk volume size - so we use a single global variable | ||
43 | and initialise it with sansa_alloc_buf() in main(). | ||
44 | */ | ||
45 | |||
46 | static off_t filesize(int fd) { | ||
47 | struct stat buf; | ||
48 | |||
49 | if (fstat(fd,&buf) < 0) { | ||
50 | perror("[ERR] Checking filesize of input file"); | ||
51 | return -1; | ||
52 | } else { | ||
53 | return(buf.st_size); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | /* Partition table parsing code taken from Rockbox */ | ||
58 | |||
59 | #define MAX_SECTOR_SIZE 2048 | ||
60 | #define SECTOR_SIZE 512 | ||
61 | |||
62 | static inline int32_t le2int(const unsigned char* buf) | ||
63 | { | ||
64 | int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | ||
65 | |||
66 | return res; | ||
67 | } | ||
68 | |||
69 | static inline uint32_t le2uint(const unsigned char* buf) | ||
70 | { | ||
71 | uint32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | ||
72 | |||
73 | return res; | ||
74 | } | ||
75 | |||
76 | static inline void int2le(unsigned int val, unsigned char* addr) | ||
77 | { | ||
78 | addr[0] = val & 0xFF; | ||
79 | addr[1] = (val >> 8) & 0xff; | ||
80 | addr[2] = (val >> 16) & 0xff; | ||
81 | addr[3] = (val >> 24) & 0xff; | ||
82 | } | ||
83 | |||
84 | #define BYTES2INT32(array,pos)\ | ||
85 | ((long)array[pos] | ((long)array[pos+1] << 8 ) |\ | ||
86 | ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) | ||
87 | |||
88 | int sansa_read_partinfo(struct sansa_t* sansa, int silent) | ||
89 | { | ||
90 | int i; | ||
91 | unsigned long count; | ||
92 | |||
93 | count = sansa_read(sansa,sansa->sectorbuf, sansa->sector_size); | ||
94 | |||
95 | if (count <= 0) { | ||
96 | sansa_print_error(" Error reading from disk: "); | ||
97 | return -1; | ||
98 | } | ||
99 | |||
100 | if ((sansa->sectorbuf[510] == 0x55) && (sansa->sectorbuf[511] == 0xaa)) { | ||
101 | /* parse partitions */ | ||
102 | for ( i = 0; i < 4; i++ ) { | ||
103 | unsigned char* ptr = sansa->sectorbuf + 0x1be + 16*i; | ||
104 | sansa->pinfo[i].type = ptr[4]; | ||
105 | sansa->pinfo[i].start = BYTES2INT32(ptr, 8); | ||
106 | sansa->pinfo[i].size = BYTES2INT32(ptr, 12); | ||
107 | |||
108 | /* extended? */ | ||
109 | if ( sansa->pinfo[i].type == 5 ) { | ||
110 | /* not handled yet */ | ||
111 | } | ||
112 | } | ||
113 | } else if ((sansa->sectorbuf[0] == 'E') && (sansa->sectorbuf[1] == 'R')) { | ||
114 | if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n"); | ||
115 | return -1; | ||
116 | } | ||
117 | |||
118 | /* Calculate the starting position of the firmware partition */ | ||
119 | sansa->start = (loff_t)sansa->pinfo[1].start*(loff_t)sansa->sector_size; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | /* NOTE: memmem implementation copied from glibc-2.2.4 - it's a GNU | ||
124 | extension and is not universally. In addition, early versions of | ||
125 | memmem had a serious bug - the meaning of needle and haystack were | ||
126 | reversed. */ | ||
127 | |||
128 | /* Copyright (C) 1991,92,93,94,96,97,98,2000 Free Software Foundation, Inc. | ||
129 | This file is part of the GNU C Library. | ||
130 | |||
131 | The GNU C Library is free software; you can redistribute it and/or | ||
132 | modify it under the terms of the GNU Lesser General Public | ||
133 | License as published by the Free Software Foundation; either | ||
134 | version 2.1 of the License, or (at your option) any later version. | ||
135 | |||
136 | The GNU C Library is distributed in the hope that it will be useful, | ||
137 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
138 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
139 | Lesser General Public License for more details. | ||
140 | |||
141 | You should have received a copy of the GNU Lesser General Public | ||
142 | License along with the GNU C Library; if not, write to the Free | ||
143 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
144 | 02111-1307 USA. */ | ||
145 | |||
146 | /* Return the first occurrence of NEEDLE in HAYSTACK. */ | ||
147 | static void * | ||
148 | sansa_memmem (haystack, haystack_len, needle, needle_len) | ||
149 | const void *haystack; | ||
150 | size_t haystack_len; | ||
151 | const void *needle; | ||
152 | size_t needle_len; | ||
153 | { | ||
154 | const char *begin; | ||
155 | const char *const last_possible | ||
156 | = (const char *) haystack + haystack_len - needle_len; | ||
157 | |||
158 | if (needle_len == 0) | ||
159 | /* The first occurrence of the empty string is deemed to occur at | ||
160 | the beginning of the string. */ | ||
161 | return (void *) haystack; | ||
162 | |||
163 | /* Sanity check, otherwise the loop might search through the whole | ||
164 | memory. */ | ||
165 | if (__builtin_expect (haystack_len < needle_len, 0)) | ||
166 | return NULL; | ||
167 | |||
168 | for (begin = (const char *) haystack; begin <= last_possible; ++begin) | ||
169 | if (begin[0] == ((const char *) needle)[0] && | ||
170 | !memcmp ((const void *) &begin[1], | ||
171 | (const void *) ((const char *) needle + 1), | ||
172 | needle_len - 1)) | ||
173 | return (void *) begin; | ||
174 | |||
175 | return NULL; | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * CRC32 implementation taken from: | ||
180 | * | ||
181 | * efone - Distributed internet phone system. | ||
182 | * | ||
183 | * (c) 1999,2000 Krzysztof Dabrowski | ||
184 | * (c) 1999,2000 ElysiuM deeZine | ||
185 | * | ||
186 | * This program is free software; you can redistribute it and/or | ||
187 | * modify it under the terms of the GNU General Public License | ||
188 | * as published by the Free Software Foundation; either version | ||
189 | * 2 of the License, or (at your option) any later version. | ||
190 | * | ||
191 | */ | ||
192 | |||
193 | /* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab(). | ||
194 | * so make sure, you call it before using the other | ||
195 | * functions! | ||
196 | */ | ||
197 | static unsigned int crc_tab[256]; | ||
198 | |||
199 | /* chksum_crc() -- to a given block, this one calculates the | ||
200 | * crc32-checksum until the length is | ||
201 | * reached. the crc32-checksum will be | ||
202 | * the result. | ||
203 | */ | ||
204 | static unsigned int chksum_crc32 (const unsigned char *block, unsigned int length) | ||
205 | { | ||
206 | register unsigned long crc; | ||
207 | unsigned long i; | ||
208 | |||
209 | crc = 0; | ||
210 | for (i = 0; i < length; i++) | ||
211 | { | ||
212 | crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF]; | ||
213 | } | ||
214 | return (crc); | ||
215 | } | ||
216 | |||
217 | /* chksum_crc32gentab() -- to a global crc_tab[256], this one will | ||
218 | * calculate the crcTable for crc32-checksums. | ||
219 | * it is generated to the polynom [..] | ||
220 | */ | ||
221 | |||
222 | static void chksum_crc32gentab (void) | ||
223 | { | ||
224 | unsigned long crc, poly; | ||
225 | int i, j; | ||
226 | |||
227 | poly = 0xEDB88320L; | ||
228 | for (i = 0; i < 256; i++) | ||
229 | { | ||
230 | crc = i; | ||
231 | for (j = 8; j > 0; j--) | ||
232 | { | ||
233 | if (crc & 1) | ||
234 | { | ||
235 | crc = (crc >> 1) ^ poly; | ||
236 | } | ||
237 | else | ||
238 | { | ||
239 | crc >>= 1; | ||
240 | } | ||
241 | } | ||
242 | crc_tab[i] = crc; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | /* Known keys for Sansa E200 and C200 firmwares: */ | ||
247 | #define NUM_KEYS ((int)(sizeof(keys)/sizeof(keys[0]))) | ||
248 | static const uint32_t keys[][4] = { | ||
249 | { 0xe494e96e, 0x3ee32966, 0x6f48512b, 0xa93fbb42 }, /* "sansa" */ | ||
250 | { 0xd7b10538, 0xc662945b, 0x1b3fce68, 0xf389c0e6 }, /* "sansa_gh" */ | ||
251 | { 0x1d29ddc0, 0x2579c2cd, 0xce339e1a, 0x75465dfe }, /* sansa 103 */ | ||
252 | |||
253 | { 0x2a7968de, 0x15127979, 0x142e60a7, 0xe49c1893 }, /* c200 1.00.03 */ | ||
254 | { 0xbf2d06fa, 0xf0e23d59, 0x29738132, 0xe2d04ca7 }, /* c200 1.00.04 and up*/ | ||
255 | { 0xa913d139, 0xf842f398, 0x3e03f1a6, 0x060ee012 }, /* c200 1.01.05 and up*/ | ||
256 | { 0x0fe92902, 0xe8cc0f89, 0x6ff568ba, 0x1eff5161 }, /* c200 1.01.07 */ | ||
257 | }; | ||
258 | |||
259 | /* | ||
260 | |||
261 | tea_decrypt() from http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm | ||
262 | |||
263 | "Following is an adaptation of the reference encryption and decryption | ||
264 | routines in C, released into the public domain by David Wheeler and | ||
265 | Roger Needham:" | ||
266 | |||
267 | */ | ||
268 | |||
269 | /* NOTE: The mi4 version of TEA uses a different initial value to sum compared | ||
270 | to the reference implementation and the main loop is 8 iterations, not | ||
271 | 32. | ||
272 | */ | ||
273 | |||
274 | static void tea_decrypt(uint32_t* v0, uint32_t* v1, const uint32_t* k) { | ||
275 | uint32_t sum=0xF1BBCDC8, i; /* set up */ | ||
276 | uint32_t delta=0x9E3779B9; /* a key schedule constant */ | ||
277 | uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */ | ||
278 | for(i=0; i<8; i++) { /* basic cycle start */ | ||
279 | *v1 -= ((*v0<<4) + k2) ^ (*v0 + sum) ^ ((*v0>>5) + k3); | ||
280 | *v0 -= ((*v1<<4) + k0) ^ (*v1 + sum) ^ ((*v1>>5) + k1); | ||
281 | sum -= delta; /* end cycle */ | ||
282 | } | ||
283 | } | ||
284 | |||
285 | /* mi4 files are encrypted in 64-bit blocks (two little-endian 32-bit | ||
286 | integers) and the key is incremented after each block | ||
287 | */ | ||
288 | |||
289 | static void tea_decrypt_buf(const unsigned char* src, unsigned char* dest, | ||
290 | size_t n, const uint32_t * initial_key) | ||
291 | { | ||
292 | uint32_t v0, v1; | ||
293 | unsigned int i; | ||
294 | uint32_t key[4]; | ||
295 | |||
296 | memcpy(key, initial_key, sizeof(key)); | ||
297 | for (i = 0; i < (n / 8); i++) { | ||
298 | v0 = le2int(src); | ||
299 | v1 = le2int(src+4); | ||
300 | |||
301 | tea_decrypt(&v0, &v1, key); | ||
302 | |||
303 | int2le(v0, dest); | ||
304 | int2le(v1, dest+4); | ||
305 | |||
306 | src += 8; | ||
307 | dest += 8; | ||
308 | |||
309 | /* Now increment the key */ | ||
310 | key[0]++; | ||
311 | if (key[0]==0) { | ||
312 | key[1]++; | ||
313 | if (key[1]==0) { | ||
314 | key[2]++; | ||
315 | if (key[2]==0) { | ||
316 | key[3]++; | ||
317 | } | ||
318 | } | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | |||
323 | static int get_mi4header(const unsigned char* buf,struct mi4header_t* mi4header) | ||
324 | { | ||
325 | if (memcmp(buf,"PPOS",4)!=0) | ||
326 | return -1; | ||
327 | |||
328 | mi4header->version = le2int(buf+0x04); | ||
329 | mi4header->length = le2int(buf+0x08); | ||
330 | mi4header->crc32 = le2int(buf+0x0c); | ||
331 | mi4header->enctype = le2int(buf+0x10); | ||
332 | mi4header->mi4size = le2int(buf+0x14); | ||
333 | mi4header->plaintext = le2int(buf+0x18); | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static int set_mi4header(unsigned char* buf,const struct mi4header_t* mi4header) | ||
339 | { | ||
340 | if (memcmp(buf,"PPOS",4)!=0) | ||
341 | return -1; | ||
342 | |||
343 | int2le(mi4header->version ,buf+0x04); | ||
344 | int2le(mi4header->length ,buf+0x08); | ||
345 | int2le(mi4header->crc32 ,buf+0x0c); | ||
346 | int2le(mi4header->enctype ,buf+0x10); | ||
347 | int2le(mi4header->mi4size ,buf+0x14); | ||
348 | int2le(mi4header->plaintext ,buf+0x18); | ||
349 | |||
350 | /* Add a dummy DSA signature */ | ||
351 | memset(buf+0x1c,0,40); | ||
352 | buf[0x2f] = 1; | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | static int sansa_seek_and_read(struct sansa_t* sansa, loff_t pos, unsigned char* buf, int nbytes) | ||
358 | { | ||
359 | int n; | ||
360 | |||
361 | if (sansa_seek(sansa, pos) < 0) { | ||
362 | return -1; | ||
363 | } | ||
364 | |||
365 | if ((n = sansa_read(sansa,buf,nbytes)) < 0) { | ||
366 | return -1; | ||
367 | } | ||
368 | |||
369 | if (n < nbytes) { | ||
370 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", | ||
371 | nbytes,n); | ||
372 | return -1; | ||
373 | } | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | |||
379 | /* We identify an E200 based on the following criteria: | ||
380 | |||
381 | 1) Exactly two partitions; | ||
382 | 2) First partition is type "W95 FAT32" (0x0b or 0x0c); | ||
383 | 3) Second partition is type "OS/2 hidden C: drive" (0x84); | ||
384 | 4) The "PPBL" string appears at offset 0 in the 2nd partition; | ||
385 | 5) The "PPMI" string appears at offset PPMI_OFFSET in the 2nd partition. | ||
386 | */ | ||
387 | |||
388 | int is_sansa(struct sansa_t* sansa) | ||
389 | { | ||
390 | struct mi4header_t mi4header; | ||
391 | int ppmi_length; | ||
392 | int ppbl_length; | ||
393 | |||
394 | /* Check partition layout */ | ||
395 | if (((sansa->pinfo[0].type != 0x06) && | ||
396 | (sansa->pinfo[0].type != 0x0b) && | ||
397 | (sansa->pinfo[0].type != 0x0c) && | ||
398 | (sansa->pinfo[0].type != 0x0e)) || | ||
399 | (sansa->pinfo[1].type != 0x84) || | ||
400 | (sansa->pinfo[2].type != 0x00) || | ||
401 | (sansa->pinfo[3].type != 0x00)) { | ||
402 | /* Bad partition layout, abort */ | ||
403 | return -1; | ||
404 | } | ||
405 | |||
406 | /* Check Bootloader header */ | ||
407 | if (sansa_seek_and_read(sansa, sansa->start, sansa->sectorbuf, 0x200) < 0) { | ||
408 | return -2; | ||
409 | } | ||
410 | if (memcmp(sansa->sectorbuf,"PPBL",4)!=0) { | ||
411 | /* No bootloader header, abort */ | ||
412 | return -4; | ||
413 | } | ||
414 | ppbl_length = (le2int(sansa->sectorbuf+4) + 0x1ff) & ~0x1ff; | ||
415 | |||
416 | /* Sanity/safety check - the bootloader can't be larger than PPMI_OFFSET */ | ||
417 | if (ppbl_length > PPMI_OFFSET) | ||
418 | { | ||
419 | return -5; | ||
420 | } | ||
421 | |||
422 | /* Load Sansa bootloader and check for "Sansa C200" magic string */ | ||
423 | if (sansa_seek_and_read(sansa, sansa->start + 0x200, sansa->sectorbuf, ppbl_length) < 0) { | ||
424 | fprintf(stderr,"[ERR] Seek and read to 0x%08"PRIx64" in is_sansa failed.\n", | ||
425 | sansa->start+0x200); | ||
426 | return -6; | ||
427 | } | ||
428 | if (sansa_memmem(sansa->sectorbuf, ppbl_length, "Sansa C200", 10) != NULL) { | ||
429 | /* C200 */ | ||
430 | sansa->targetname="c200"; | ||
431 | } else { | ||
432 | /* E200 */ | ||
433 | sansa->targetname="e200"; | ||
434 | } | ||
435 | |||
436 | /* Check Main firmware header */ | ||
437 | if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sansa->sectorbuf, 0x200) < 0) { | ||
438 | fprintf(stderr,"[ERR] Seek to 0x%"PRIx64" in is_sansa failed.\n", | ||
439 | sansa->start+PPMI_OFFSET); | ||
440 | return -5; | ||
441 | } | ||
442 | if (memcmp(sansa->sectorbuf,"PPMI",4)!=0) { | ||
443 | /* No bootloader header, abort */ | ||
444 | return -7; | ||
445 | } | ||
446 | ppmi_length = le2int(sansa->sectorbuf+4); | ||
447 | |||
448 | /* Check main mi4 file header */ | ||
449 | if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200, sansa->sectorbuf, 0x200) < 0) { | ||
450 | fprintf(stderr,"[ERR] Seek to 0x%"PRIx64" in is_sansa failed.\n", | ||
451 | sansa->start+PPMI_OFFSET+0x200); | ||
452 | return -5; | ||
453 | } | ||
454 | |||
455 | if (get_mi4header(sansa->sectorbuf,&mi4header) < 0) { | ||
456 | fprintf(stderr,"[ERR] Invalid mi4header\n"); | ||
457 | return -6; | ||
458 | } | ||
459 | |||
460 | /* Some sanity checks: | ||
461 | |||
462 | 1) Main MI4 image without RBBL and < 100000 bytes -> old install | ||
463 | 2) Main MI4 image with RBBL but no second image -> old install | ||
464 | */ | ||
465 | |||
466 | sansa->hasoldbootloader = 0; | ||
467 | if (memcmp(sansa->sectorbuf+0x1f8,"RBBL",4)==0) { | ||
468 | /* Look for an original firmware after the first image */ | ||
469 | if (sansa_seek_and_read(sansa, | ||
470 | sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, | ||
471 | sansa->sectorbuf, 512) < 0) { | ||
472 | return -7; | ||
473 | } | ||
474 | |||
475 | if (get_mi4header(sansa->sectorbuf,&mi4header)!=0) { | ||
476 | fprintf(stderr,"[ERR] No original firmware found\n"); | ||
477 | sansa->hasoldbootloader = 1; | ||
478 | } | ||
479 | } else if (mi4header.mi4size < 100000) { | ||
480 | fprintf(stderr,"[ERR] Old bootloader found\n"); | ||
481 | sansa->hasoldbootloader = 1; | ||
482 | } | ||
483 | |||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | int sansa_scan(struct sansa_t* sansa) | ||
488 | { | ||
489 | int i; | ||
490 | int n = 0; | ||
491 | char last_disk[4096]; | ||
492 | int denied = 0; | ||
493 | int result; | ||
494 | |||
495 | printf("[INFO] Scanning disk devices...\n"); | ||
496 | |||
497 | for (i = 0; i <= 25 ; i++) { | ||
498 | #ifdef __WIN32__ | ||
499 | sprintf(sansa->diskname,"\\\\.\\PhysicalDrive%d",i); | ||
500 | #elif defined(linux) || defined (__linux) | ||
501 | sprintf(sansa->diskname,"/dev/sd%c",'a'+i); | ||
502 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ | ||
503 | || defined(__bsdi__) || defined(__DragonFly__) | ||
504 | sprintf(sansa->diskname,"/dev/da%d",i); | ||
505 | #elif defined(__APPLE__) && defined(__MACH__) | ||
506 | sprintf(sansa->diskname,"/dev/disk%d",i); | ||
507 | #else | ||
508 | #error No disk paths defined for this platform | ||
509 | #endif | ||
510 | if ((result = sansa_open(sansa, 1)) < 0) { | ||
511 | if(result == -2) { | ||
512 | denied++; | ||
513 | } | ||
514 | sansa_close(sansa); | ||
515 | continue; | ||
516 | } | ||
517 | |||
518 | if (sansa_read_partinfo(sansa,1) < 0) { | ||
519 | sansa_close(sansa); | ||
520 | continue; | ||
521 | } | ||
522 | |||
523 | if (is_sansa(sansa) < 0) { | ||
524 | sansa_close(sansa); | ||
525 | continue; | ||
526 | } | ||
527 | |||
528 | #ifdef __WIN32__ | ||
529 | printf("[INFO] %s found - disk device %d\n",sansa->targetname, i); | ||
530 | #else | ||
531 | printf("[INFO] %s found - %s\n",sansa->targetname, sansa->diskname); | ||
532 | #endif | ||
533 | n++; | ||
534 | strcpy(last_disk,sansa->diskname); | ||
535 | sansa_close(sansa); | ||
536 | } | ||
537 | |||
538 | if (n==1) { | ||
539 | /* Remember the disk name */ | ||
540 | strcpy(sansa->diskname,last_disk); | ||
541 | } | ||
542 | else if (n == 0 && denied) { | ||
543 | printf("[ERR] FATAL: Permission denied on %d device(s) and no sansa detected.\n", denied); | ||
544 | #ifdef __WIN32__ | ||
545 | printf("[ERR] You need to run this program with administrator priviledges!\n"); | ||
546 | #else | ||
547 | printf("[ERR] You need permissions for raw disc access for this program to work!\n"); | ||
548 | #endif | ||
549 | } | ||
550 | |||
551 | return (n == 0 && denied) ? -1 : n; | ||
552 | } | ||
553 | |||
554 | /* Prepare original firmware for writing to the firmware partition by decrypting | ||
555 | and updating the header */ | ||
556 | static int prepare_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header) | ||
557 | { | ||
558 | unsigned char* tmpbuf; | ||
559 | int i; | ||
560 | int key_found; | ||
561 | |||
562 | get_mi4header(buf,mi4header); | ||
563 | |||
564 | #if 0 | ||
565 | printf("mi4header->version =0x%08x\n",mi4header->version); | ||
566 | printf("mi4header->length =0x%08x\n",mi4header->length); | ||
567 | printf("mi4header->crc32 =0x%08x\n",mi4header->crc32); | ||
568 | printf("mi4header->enctype =0x%08x\n",mi4header->enctype); | ||
569 | printf("mi4header->mi4size =0x%08x\n",mi4header->mi4size); | ||
570 | printf("mi4header->plaintext =0x%08x\n",mi4header->plaintext); | ||
571 | #endif | ||
572 | |||
573 | /* Decrypt anything that needs decrypting. */ | ||
574 | if (mi4header->plaintext < mi4header->mi4size - 0x200) { | ||
575 | /* TODO: Check different keys */ | ||
576 | tmpbuf=malloc(mi4header->mi4size-(mi4header->plaintext+0x200)); | ||
577 | if (tmpbuf==NULL) { | ||
578 | fprintf(stderr,"[ERR] Can not allocate memory\n"); | ||
579 | return -1; | ||
580 | } | ||
581 | |||
582 | key_found=0; | ||
583 | for (i=0; i < NUM_KEYS && !key_found ; i++) { | ||
584 | tea_decrypt_buf(buf+(mi4header->plaintext+0x200), | ||
585 | tmpbuf, | ||
586 | mi4header->mi4size-(mi4header->plaintext+0x200), | ||
587 | keys[i]); | ||
588 | key_found = (le2uint(tmpbuf+mi4header->length-mi4header->plaintext-4) == 0xaa55aa55); | ||
589 | } | ||
590 | |||
591 | if (key_found) { | ||
592 | memcpy(buf+(mi4header->plaintext+0x200),tmpbuf,mi4header->mi4size-(mi4header->plaintext+0x200)); | ||
593 | free(tmpbuf); | ||
594 | } else { | ||
595 | fprintf(stderr,"[ERR] Failed to decrypt image, aborting\n"); | ||
596 | free(tmpbuf); | ||
597 | return -1; | ||
598 | } | ||
599 | } | ||
600 | |||
601 | /* Increase plaintext value to full file */ | ||
602 | mi4header->plaintext = mi4header->mi4size - 0x200; | ||
603 | |||
604 | /* Update CRC checksum */ | ||
605 | chksum_crc32gentab (); | ||
606 | mi4header->crc32 = chksum_crc32(buf+0x200,mi4header->mi4size-0x200); | ||
607 | |||
608 | set_mi4header(buf,mi4header); | ||
609 | |||
610 | /* Add Rockbox-specific header */ | ||
611 | memcpy(buf+0x1f8,"RBOF",4); | ||
612 | memcpy(buf+0x1fc,sansa->targetname,4); | ||
613 | |||
614 | return 0; | ||
615 | } | ||
616 | |||
617 | static int load_original_firmware(struct sansa_t* sansa, unsigned char* buf, struct mi4header_t* mi4header) | ||
618 | { | ||
619 | int ppmi_length; | ||
620 | int n; | ||
621 | |||
622 | /* Read 512 bytes from PPMI_OFFSET - the PPMI header plus the mi4 header */ | ||
623 | if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET, buf, 512) < 0) { | ||
624 | return -1; | ||
625 | } | ||
626 | |||
627 | /* No need to check PPMI magic - it's done during init to confirm | ||
628 | this is an E200 */ | ||
629 | ppmi_length = le2int(buf+4); | ||
630 | |||
631 | /* Firstly look for an original firmware after the first image */ | ||
632 | if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, buf, 512) < 0) { | ||
633 | return -1; | ||
634 | } | ||
635 | |||
636 | if (get_mi4header(buf,mi4header)==0) { | ||
637 | /* We have a valid MI4 file after a bootloader, so we use this. */ | ||
638 | if ((n = sansa_seek_and_read(sansa, | ||
639 | sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, | ||
640 | buf, mi4header->mi4size)) < 0) { | ||
641 | return -1; | ||
642 | } | ||
643 | } else { | ||
644 | /* No valid MI4 file, so read the first image. */ | ||
645 | if ((n = sansa_seek_and_read(sansa, | ||
646 | sansa->start + PPMI_OFFSET + 0x200, | ||
647 | buf, ppmi_length)) < 0) { | ||
648 | return -1; | ||
649 | } | ||
650 | } | ||
651 | return prepare_original_firmware(sansa, buf, mi4header); | ||
652 | } | ||
653 | |||
654 | int sansa_read_firmware(struct sansa_t* sansa, const char* filename) | ||
655 | { | ||
656 | int res; | ||
657 | int outfile; | ||
658 | struct mi4header_t mi4header; | ||
659 | |||
660 | res = load_original_firmware(sansa,sansa->sectorbuf,&mi4header); | ||
661 | if (res < 0) | ||
662 | return res; | ||
663 | |||
664 | outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); | ||
665 | if (outfile < 0) { | ||
666 | fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); | ||
667 | return -1; | ||
668 | } | ||
669 | |||
670 | res = write(outfile,sansa->sectorbuf,mi4header.mi4size); | ||
671 | if (res != (int)mi4header.mi4size) { | ||
672 | fprintf(stderr,"[ERR] Write error - %d\n", res); | ||
673 | return -1; | ||
674 | } | ||
675 | close(outfile); | ||
676 | |||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | unsigned int sansa_read_bootloader(struct sansa_t* sansa, const char* filename, unsigned char** bl_buffer) | ||
681 | { | ||
682 | /* Step 1 - read bootloader into RAM. */ | ||
683 | int infile; | ||
684 | unsigned int n; | ||
685 | unsigned int len; | ||
686 | infile=open(filename,O_RDONLY|O_BINARY); | ||
687 | if (infile < 0) { | ||
688 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
689 | return 0; | ||
690 | } | ||
691 | |||
692 | len = filesize(infile); | ||
693 | |||
694 | unsigned char* b = malloc(len); | ||
695 | if (b == NULL) { | ||
696 | fprintf(stderr,"[ERR] Could not allocate memory for bootloader\n"); | ||
697 | close(infile); | ||
698 | return 0; | ||
699 | } | ||
700 | |||
701 | n = read(infile,b,len); | ||
702 | close(infile); | ||
703 | if (n < len) { | ||
704 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" | ||
705 | ,len,n); | ||
706 | return 0; | ||
707 | } | ||
708 | |||
709 | if (memcmp(b+0x1f8,"RBBL",4)!=0) { | ||
710 | fprintf(stderr,"[ERR] %s is not a Rockbox bootloader, aborting.\n", | ||
711 | filename); | ||
712 | return 0; | ||
713 | } | ||
714 | if (memcmp(b+0x1fc,sansa->targetname,4)!=0) { | ||
715 | fprintf(stderr,"[ERR] %s is not a Rockbox bootloader for %s, aborting.\n", | ||
716 | filename, sansa->targetname); | ||
717 | return 0; | ||
718 | } | ||
719 | *bl_buffer = b; | ||
720 | return len; | ||
721 | } | ||
722 | |||
723 | int sansa_add_bootloader(struct sansa_t* sansa, const unsigned char* bootloader, const unsigned int bl_length) | ||
724 | { | ||
725 | int res; | ||
726 | struct mi4header_t mi4header; | ||
727 | int length; | ||
728 | int n; | ||
729 | |||
730 | /* Create PPMI header */ | ||
731 | memset(sansa->sectorbuf,0,0x200); | ||
732 | memcpy(sansa->sectorbuf,"PPMI",4); | ||
733 | int2le(bl_length, sansa->sectorbuf+4); | ||
734 | int2le(0x00020000, sansa->sectorbuf+8); | ||
735 | |||
736 | /* copy bootloader to sansa->sectorbuf+0x200 */ | ||
737 | memcpy(sansa->sectorbuf+0x200,bootloader,bl_length); | ||
738 | |||
739 | /* Load original firmware from Sansa to the space after the bootloader */ | ||
740 | res = load_original_firmware(sansa,sansa->sectorbuf+0x200+bl_length,&mi4header); | ||
741 | if (res < 0) | ||
742 | return res; | ||
743 | |||
744 | /* Now write the whole thing back to the Sansa */ | ||
745 | |||
746 | if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) { | ||
747 | fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in add_bootloader failed.\n", | ||
748 | sansa->start+PPMI_OFFSET); | ||
749 | return -5; | ||
750 | } | ||
751 | |||
752 | length = 0x200 + bl_length + mi4header.mi4size; | ||
753 | |||
754 | n=sansa_write(sansa, length); | ||
755 | if (n < length) { | ||
756 | fprintf(stderr,"[ERR] Short write in add_bootloader\n"); | ||
757 | return -6; | ||
758 | } | ||
759 | |||
760 | return 0; | ||
761 | } | ||
762 | |||
763 | int sansa_delete_bootloader(struct sansa_t* sansa) | ||
764 | { | ||
765 | int res; | ||
766 | struct mi4header_t mi4header; | ||
767 | int n; | ||
768 | int length; | ||
769 | |||
770 | /* Load original firmware from Sansa to sansa->sectorbuf+0x200 */ | ||
771 | res = load_original_firmware(sansa,sansa->sectorbuf+0x200,&mi4header); | ||
772 | if (res < 0) | ||
773 | return res; | ||
774 | |||
775 | /* Create PPMI header */ | ||
776 | memset(sansa->sectorbuf,0,0x200); | ||
777 | memcpy(sansa->sectorbuf,"PPMI",4); | ||
778 | int2le(mi4header.mi4size, sansa->sectorbuf+4); | ||
779 | int2le(0x00020000, sansa->sectorbuf+8); | ||
780 | |||
781 | /* Now write the whole thing back to the Sansa */ | ||
782 | |||
783 | if (sansa_seek(sansa, sansa->start+PPMI_OFFSET) < 0) { | ||
784 | fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in add_bootloader failed.\n", | ||
785 | sansa->start+PPMI_OFFSET); | ||
786 | return -5; | ||
787 | } | ||
788 | |||
789 | length = 0x200 + mi4header.mi4size; | ||
790 | |||
791 | n=sansa_write(sansa, length); | ||
792 | if (n < length) { | ||
793 | fprintf(stderr,"[ERR] Short write in delete_bootloader\n"); | ||
794 | return -6; | ||
795 | } | ||
796 | |||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | /** List number of MI4 images on the player, return number. | ||
801 | */ | ||
802 | int sansa_list_images(struct sansa_t* sansa) | ||
803 | { | ||
804 | struct mi4header_t mi4header; | ||
805 | loff_t ppmi_length; | ||
806 | int num = 0; | ||
807 | |||
808 | /* Check Main firmware header */ | ||
809 | if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET, sansa->sectorbuf, 0x200) < 0) { | ||
810 | return 0; | ||
811 | } | ||
812 | |||
813 | ppmi_length = le2int(sansa->sectorbuf+4); | ||
814 | |||
815 | printf("[INFO] Image 1 - %"PRIu64" bytes\n",ppmi_length); | ||
816 | num = 1; | ||
817 | |||
818 | /* Look for an original firmware after the first image */ | ||
819 | if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET + 0x200 + ppmi_length, sansa->sectorbuf, 512) < 0) { | ||
820 | return 0; | ||
821 | } | ||
822 | |||
823 | if (get_mi4header(sansa->sectorbuf,&mi4header)==0) { | ||
824 | printf("[INFO] Image 2 - %d bytes\n",mi4header.mi4size); | ||
825 | num = 2; | ||
826 | } | ||
827 | return num; | ||
828 | } | ||
829 | |||
830 | int sansa_update_of(struct sansa_t* sansa, const char* filename) | ||
831 | { | ||
832 | int n; | ||
833 | int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */ | ||
834 | int of_length = 0; /* Keep gcc happy when building for rbutil */ | ||
835 | int ppmi_length; | ||
836 | struct mi4header_t mi4header; | ||
837 | unsigned char buf[512]; | ||
838 | |||
839 | /* Step 1 - check we have an OF on the Sansa to upgrade. We expect the | ||
840 | Rockbox bootloader to be installed and the OF to be after it on disk. */ | ||
841 | |||
842 | /* Read 512 bytes from PPMI_OFFSET - the PPMI header */ | ||
843 | if (sansa_seek_and_read(sansa, sansa->start + PPMI_OFFSET, | ||
844 | buf, 512) < 0) { | ||
845 | return -1; | ||
846 | } | ||
847 | |||
848 | /* No need to check PPMI magic - it's done during init to confirm | ||
849 | this is an E200 */ | ||
850 | ppmi_length = le2int(buf+4); | ||
851 | |||
852 | /* Look for an original firmware after the first image */ | ||
853 | if (sansa_seek_and_read(sansa, sansa->start+PPMI_OFFSET+0x200+ppmi_length, | ||
854 | buf, 512) < 0) { | ||
855 | return -1; | ||
856 | } | ||
857 | |||
858 | if (get_mi4header(buf,&mi4header)!=0) { | ||
859 | /* We don't have a valid MI4 file after a bootloader, so do nothing. */ | ||
860 | fprintf(stderr,"[ERR] No original firmware found at 0x%08"PRIx64"\n", | ||
861 | sansa->start+PPMI_OFFSET+0x200+ppmi_length); | ||
862 | return -1; | ||
863 | } | ||
864 | |||
865 | /* Step 2 - read OF into RAM. */ | ||
866 | infile=open(filename,O_RDONLY|O_BINARY); | ||
867 | if (infile < 0) { | ||
868 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
869 | return -1; | ||
870 | } | ||
871 | |||
872 | of_length = filesize(infile); | ||
873 | |||
874 | /* Load original firmware from file */ | ||
875 | memset(sansa->sectorbuf,0,0x200); | ||
876 | n = read(infile,sansa->sectorbuf,of_length); | ||
877 | close(infile); | ||
878 | if (n < of_length) { | ||
879 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" | ||
880 | , of_length, n); | ||
881 | return -1; | ||
882 | } | ||
883 | |||
884 | /* Check we have a valid MI4 file. */ | ||
885 | if (get_mi4header(sansa->sectorbuf,&mi4header)!=0) { | ||
886 | fprintf(stderr,"[ERR] %s is not a valid mi4 file\n",filename); | ||
887 | return -1; | ||
888 | } | ||
889 | |||
890 | /* Decrypt and build the header */ | ||
891 | if(prepare_original_firmware(sansa, sansa->sectorbuf, &mi4header)!=0){ | ||
892 | fprintf(stderr,"[ERR] Unable to build decrypted mi4 from %s\n" | ||
893 | ,filename); | ||
894 | return -1; | ||
895 | } | ||
896 | |||
897 | /* Step 3 - write the OF to the Sansa */ | ||
898 | if (sansa_seek(sansa, sansa->start+PPMI_OFFSET+0x200+ppmi_length) < 0) { | ||
899 | fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_of failed.\n", | ||
900 | sansa->start+PPMI_OFFSET+0x200+ppmi_length); | ||
901 | return -1; | ||
902 | } | ||
903 | |||
904 | n=sansa_write(sansa, of_length); | ||
905 | if (n < of_length) { | ||
906 | fprintf(stderr,"[ERR] Short write in sansa_update_of\n"); | ||
907 | return -1; | ||
908 | } | ||
909 | |||
910 | /* Step 4 - zero out the nvparams section - we have to do this or we end up | ||
911 | with multiple copies of the nvparams data and don't know which one to | ||
912 | work with for the database rebuild disabling trick in our bootloader */ | ||
913 | if (strcmp(sansa->targetname,"e200") == 0) { | ||
914 | printf("[INFO] Resetting Original Firmware settings\n"); | ||
915 | if (sansa_seek(sansa, sansa->start+NVPARAMS_OFFSET+0x200) < 0) { | ||
916 | fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_of failed.\n", | ||
917 | sansa->start+NVPARAMS_OFFSET+0x200); | ||
918 | return -1; | ||
919 | } | ||
920 | |||
921 | memset(sansa->sectorbuf,0,NVPARAMS_SIZE); | ||
922 | n=sansa_write(sansa, NVPARAMS_SIZE); | ||
923 | if (n < NVPARAMS_SIZE) { | ||
924 | fprintf(stderr,"[ERR] Short write in sansa_update_of\n"); | ||
925 | return -1; | ||
926 | } | ||
927 | } | ||
928 | |||
929 | return 0; | ||
930 | } | ||
931 | |||
932 | /* Update the PPBL (bootloader) image in the hidden firmware partition */ | ||
933 | int sansa_update_ppbl(struct sansa_t* sansa, const char* filename) | ||
934 | { | ||
935 | int n; | ||
936 | int infile = -1; /* Prevent an erroneous "may be used uninitialised" gcc warning */ | ||
937 | int ppbl_length = 0; /* Keep gcc happy when building for rbutil */ | ||
938 | |||
939 | /* Step 1 - read bootloader into RAM. */ | ||
940 | infile=open(filename,O_RDONLY|O_BINARY); | ||
941 | if (infile < 0) { | ||
942 | fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); | ||
943 | return -1; | ||
944 | } | ||
945 | |||
946 | ppbl_length = filesize(infile); | ||
947 | |||
948 | n = read(infile,sansa->sectorbuf+0x200,ppbl_length); | ||
949 | close(infile); | ||
950 | if (n < ppbl_length) { | ||
951 | fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", ppbl_length, n); | ||
952 | return -1; | ||
953 | } | ||
954 | |||
955 | /* Step 2 - Build the header */ | ||
956 | memset(sansa->sectorbuf,0,0x200); | ||
957 | memcpy(sansa->sectorbuf,"PPBL",4); | ||
958 | int2le(ppbl_length, sansa->sectorbuf+4); | ||
959 | int2le(0x00010000, sansa->sectorbuf+8); | ||
960 | |||
961 | /* Step 3 - write the bootloader to the Sansa */ | ||
962 | if (sansa_seek(sansa, sansa->start) < 0) { | ||
963 | fprintf(stderr,"[ERR] Seek to 0x%08"PRIx64" in sansa_update_ppbl failed.\n", sansa->start); | ||
964 | return -1; | ||
965 | } | ||
966 | |||
967 | n=sansa_write(sansa, ppbl_length + 0x200); | ||
968 | if (n < (ppbl_length+0x200)) { | ||
969 | fprintf(stderr,"[ERR] Short write in sansa_update_ppbl\n"); | ||
970 | return -1; | ||
971 | } | ||
972 | |||
973 | return 0; | ||
974 | } | ||
975 | |||