diff options
-rw-r--r-- | rbutil/sansapatcher/Makefile | 44 | ||||
-rw-r--r-- | rbutil/sansapatcher/main.c | 293 | ||||
-rw-r--r-- | rbutil/sansapatcher/sansaio-posix.c | 115 | ||||
-rw-r--r-- | rbutil/sansapatcher/sansaio-win32.c | 196 | ||||
-rw-r--r-- | rbutil/sansapatcher/sansaio.h | 74 | ||||
-rw-r--r-- | rbutil/sansapatcher/sansapatcher.c | 702 | ||||
-rw-r--r-- | rbutil/sansapatcher/sansapatcher.h | 45 |
7 files changed, 1469 insertions, 0 deletions
diff --git a/rbutil/sansapatcher/Makefile b/rbutil/sansapatcher/Makefile new file mode 100644 index 0000000000..17b3fb0047 --- /dev/null +++ b/rbutil/sansapatcher/Makefile | |||
@@ -0,0 +1,44 @@ | |||
1 | CFLAGS=-Wall -D_LARGEFILE64_SOURCE | ||
2 | |||
3 | ifeq ($(findstring CYGWIN,$(shell uname)),CYGWIN) | ||
4 | OUTPUT=sansapatcher.exe | ||
5 | CROSS= | ||
6 | CFLAGS+=-mno-cygwin | ||
7 | else | ||
8 | OUTPUT=sansapatcher | ||
9 | CROSS=i586-mingw32msvc- | ||
10 | endif | ||
11 | |||
12 | NATIVECC = gcc | ||
13 | CC = $(CROSS)gcc | ||
14 | |||
15 | all: $(OUTPUT) | ||
16 | |||
17 | sansapatcher: main.c sansapatcher.c sansaio-posix.c parttypes.h bootimg.c | ||
18 | gcc $(CFLAGS) -o sansapatcher main.c sansapatcher.c sansaio-posix.c bootimg.c | ||
19 | strip sansapatcher | ||
20 | |||
21 | sansapatcher.exe: main.c sansapatcher.c sansaio-win32.c parttypes.h bootimg.c | ||
22 | $(CC) $(CFLAGS) -o sansapatcher.exe main.c sansapatcher.c sansaio-win32.c bootimg.c | ||
23 | $(CROSS)strip sansapatcher.exe | ||
24 | |||
25 | sansapatcher-mac: sansapatcher-i386 sansapatcher-ppc | ||
26 | lipo -create sansapatcher-ppc sansapatcher-i386 -output sansapatcher-mac | ||
27 | |||
28 | sansapatcher-i386: main.c sansapatcher.c sansaio-posix.c parttypes.h bootimg.c | ||
29 | gcc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -o bin/i386/program -arch i386 $(CFLAGS) -o sansapatcher-i386 main.c sansapatcher.c sansaio-posix.c bootimg.c | ||
30 | strip sansapatcher-i386 | ||
31 | |||
32 | sansapatcher-ppc: main.c sansapatcher.c sansaio-posix.c parttypes.h bootimg.c | ||
33 | gcc -arch ppc $(CFLAGS) -o sansapatcher-ppc main.c sansapatcher.c sansaio-posix.c bootimg.c | ||
34 | strip sansapatcher-ppc | ||
35 | |||
36 | #mi42c: mi42c.c | ||
37 | # $(NATIVECC) $(CFLAGS) -o mi42c mi42c.c | ||
38 | |||
39 | #bootimg.c: PP5022.mi4 mi42c | ||
40 | # ./mi42c PP5022.mi4 bootimg | ||
41 | |||
42 | clean: | ||
43 | rm -f sansapatcher.exe sansapatcher-mac sansapatcher-i386 sansapatcher-ppc sansapatcher mi42c *~ | ||
44 | #bootimg.c bootimg.h | ||
diff --git a/rbutil/sansapatcher/main.c b/rbutil/sansapatcher/main.c new file mode 100644 index 0000000000..4e1e2dc120 --- /dev/null +++ b/rbutil/sansapatcher/main.c | |||
@@ -0,0 +1,293 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id: ipodpatcher.c 12237 2007-02-08 21:31:38Z 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 "sansapatcher.h" | ||
30 | #include "sansaio.h" | ||
31 | |||
32 | #define VERSION "0.1" | ||
33 | |||
34 | int verbose = 0; | ||
35 | |||
36 | enum { | ||
37 | NONE, | ||
38 | #ifdef WITH_BOOTOBJS | ||
39 | INSTALL, | ||
40 | #endif | ||
41 | INTERACTIVE, | ||
42 | SHOW_INFO, | ||
43 | LIST_IMAGES, | ||
44 | DELETE_BOOTLOADER, | ||
45 | ADD_BOOTLOADER, | ||
46 | READ_FIRMWARE, | ||
47 | WRITE_FIRMWARE, | ||
48 | READ_PARTITION, | ||
49 | WRITE_PARTITION | ||
50 | }; | ||
51 | |||
52 | void print_usage(void) | ||
53 | { | ||
54 | fprintf(stderr,"Usage: sansapatcher --scan\n"); | ||
55 | #ifdef __WIN32__ | ||
56 | fprintf(stderr," or sansapatcher [DISKNO] [action]\n"); | ||
57 | #else | ||
58 | fprintf(stderr," or sansapatcher [device] [action]\n"); | ||
59 | #endif | ||
60 | fprintf(stderr,"\n"); | ||
61 | fprintf(stderr,"Where [action] is one of the following options:\n"); | ||
62 | #ifdef WITH_BOOTOBJS | ||
63 | fprintf(stderr," --install\n"); | ||
64 | #endif | ||
65 | fprintf(stderr," -l, --list\n"); | ||
66 | fprintf(stderr," -rf, --read-firmware filename.mi4\n"); | ||
67 | fprintf(stderr," -a, --add-bootloader filename.mi4\n"); | ||
68 | fprintf(stderr," -d, --delete-bootloader\n"); | ||
69 | fprintf(stderr,"\n"); | ||
70 | |||
71 | #ifdef __WIN32__ | ||
72 | fprintf(stderr,"DISKNO is the number (e.g. 2) Windows has assigned to your sansa's hard disk.\n"); | ||
73 | fprintf(stderr,"The first hard disk in your computer (i.e. C:\\) will be disk 0, the next disk\n"); | ||
74 | fprintf(stderr,"will be disk 1 etc. sansapatcher will refuse to access a disk unless it\n"); | ||
75 | fprintf(stderr,"can identify it as being an E200.\n"); | ||
76 | fprintf(stderr,"\n"); | ||
77 | #else | ||
78 | #if defined(linux) || defined (__linux) | ||
79 | fprintf(stderr,"\"device\" is the device node (e.g. /dev/sda) assigned to your sansa.\n"); | ||
80 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) | ||
81 | fprintf(stderr,"\"device\" is the device node (e.g. /dev/da1) assigned to your sansa.\n"); | ||
82 | #elif defined(__APPLE__) && defined(__MACH__) | ||
83 | fprintf(stderr,"\"device\" is the device node (e.g. /dev/disk1) assigned to your sansa.\n"); | ||
84 | #endif | ||
85 | fprintf(stderr,"sansapatcher will refuse to access a disk unless it can identify it as being\n"); | ||
86 | fprintf(stderr,"an E200.\n"); | ||
87 | #endif | ||
88 | } | ||
89 | |||
90 | void display_partinfo(struct sansa_t* sansa) | ||
91 | { | ||
92 | int i; | ||
93 | double sectors_per_MB = (1024.0*1024.0)/sansa->sector_size; | ||
94 | |||
95 | printf("[INFO] Part Start Sector End Sector Size (MB) Type\n"); | ||
96 | for ( i = 0; i < 4; i++ ) { | ||
97 | if (sansa->pinfo[i].start != 0) { | ||
98 | printf("[INFO] %d %10ld %10ld %10.1f %s (0x%02x)\n", | ||
99 | i, | ||
100 | sansa->pinfo[i].start, | ||
101 | sansa->pinfo[i].start+sansa->pinfo[i].size-1, | ||
102 | sansa->pinfo[i].size/sectors_per_MB, | ||
103 | get_parttype(sansa->pinfo[i].type), | ||
104 | sansa->pinfo[i].type); | ||
105 | } | ||
106 | } | ||
107 | } | ||
108 | |||
109 | |||
110 | int main(int argc, char* argv[]) | ||
111 | { | ||
112 | #ifdef WITH_BOOTOBJS | ||
113 | char yesno[4]; | ||
114 | #endif | ||
115 | int i; | ||
116 | int n; | ||
117 | char* filename; | ||
118 | int action = SHOW_INFO; | ||
119 | int type; | ||
120 | struct sansa_t sansa; | ||
121 | |||
122 | fprintf(stderr,"sansapatcher v" VERSION " - (C) Dave Chapman 2006-2007\n"); | ||
123 | fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); | ||
124 | fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); | ||
125 | |||
126 | if ((argc > 1) && ((strcmp(argv[1],"-h")==0) || (strcmp(argv[1],"--help")==0))) { | ||
127 | print_usage(); | ||
128 | return 1; | ||
129 | } | ||
130 | |||
131 | if (sansa_alloc_buffer(§orbuf,BUFFER_SIZE) < 0) { | ||
132 | fprintf(stderr,"Failed to allocate memory buffer\n"); | ||
133 | } | ||
134 | |||
135 | if ((argc > 1) && (strcmp(argv[1],"--scan")==0)) { | ||
136 | if (sansa_scan(&sansa) == 0) | ||
137 | fprintf(stderr,"[ERR] No E200s found.\n"); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | /* If the first parameter doesn't start with -, then we interpret it as a device */ | ||
142 | if ((argc > 1) && (argv[1][0] != '-')) { | ||
143 | sansa.diskname[0]=0; | ||
144 | #ifdef __WIN32__ | ||
145 | snprintf(sansa.diskname,sizeof(sansa.diskname),"\\\\.\\PhysicalDrive%s",argv[1]); | ||
146 | #else | ||
147 | strncpy(sansa.diskname,argv[1],sizeof(sansa.diskname)); | ||
148 | #endif | ||
149 | i = 2; | ||
150 | } else { | ||
151 | /* Autoscan for E200s */ | ||
152 | n = sansa_scan(&sansa); | ||
153 | if (n==0) { | ||
154 | fprintf(stderr,"[ERR] No E200s found, aborting\n"); | ||
155 | fprintf(stderr,"[ERR] Please connect your sansa and ensure it is in UMS mode\n"); | ||
156 | #if defined(__APPLE__) && defined(__MACH__) | ||
157 | fprintf(stderr,"[ERR] Also ensure that your E200's main partition is not mounted.\n"); | ||
158 | #elif !defined(__WIN32__) | ||
159 | if (geteuid()!=0) { | ||
160 | fprintf(stderr,"[ERR] You may also need to run sansapatcher as root.\n"); | ||
161 | } | ||
162 | #endif | ||
163 | fprintf(stderr,"[ERR] Please refer to the Rockbox manual if you continue to have problems.\n"); | ||
164 | } else if (n > 1) { | ||
165 | fprintf(stderr,"[ERR] %d E200s found, aborting\n",n); | ||
166 | fprintf(stderr,"[ERR] Please connect only one E200 and re-run sansapatcher.\n"); | ||
167 | } | ||
168 | |||
169 | if (n != 1) { | ||
170 | #ifdef WITH_BOOTOBJS | ||
171 | if (argc==1) { | ||
172 | printf("\nPress ENTER to exit sansapatcher :"); | ||
173 | fgets(yesno,4,stdin); | ||
174 | } | ||
175 | #endif | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | i = 1; | ||
180 | } | ||
181 | |||
182 | #ifdef WITH_BOOTOBJS | ||
183 | action = INTERACTIVE; | ||
184 | #else | ||
185 | action = NONE; | ||
186 | #endif | ||
187 | |||
188 | while (i < argc) { | ||
189 | if ((strcmp(argv[i],"-l")==0) || (strcmp(argv[i],"--list")==0)) { | ||
190 | action = LIST_IMAGES; | ||
191 | i++; | ||
192 | #ifdef WITH_BOOTOBJS | ||
193 | } else if (strcmp(argv[i],"--install")==0) { | ||
194 | action = INSTALL; | ||
195 | i++; | ||
196 | #endif | ||
197 | } else if ((strcmp(argv[i],"-d")==0) || | ||
198 | (strcmp(argv[i],"--delete-bootloader")==0)) { | ||
199 | action = DELETE_BOOTLOADER; | ||
200 | i++; | ||
201 | } else if ((strcmp(argv[i],"-a")==0) || | ||
202 | (strcmp(argv[i],"--add-bootloader")==0)) { | ||
203 | action = ADD_BOOTLOADER; | ||
204 | type = FILETYPE_MI4; | ||
205 | i++; | ||
206 | if (i == argc) { print_usage(); return 1; } | ||
207 | filename=argv[i]; | ||
208 | i++; | ||
209 | } else if ((strcmp(argv[i],"-rf")==0) || | ||
210 | (strcmp(argv[i],"--read-firmware")==0)) { | ||
211 | action = READ_FIRMWARE; | ||
212 | i++; | ||
213 | if (i == argc) { print_usage(); return 1; } | ||
214 | filename=argv[i]; | ||
215 | i++; | ||
216 | } | ||
217 | } | ||
218 | |||
219 | if (sansa.diskname[0]==0) { | ||
220 | print_usage(); | ||
221 | return 1; | ||
222 | } | ||
223 | |||
224 | if (sansa_open(&sansa, 0) < 0) { | ||
225 | return 1; | ||
226 | } | ||
227 | |||
228 | fprintf(stderr,"[INFO] Reading partition table from %s\n",sansa.diskname); | ||
229 | fprintf(stderr,"[INFO] Sector size is %d bytes\n",sansa.sector_size); | ||
230 | |||
231 | if (read_partinfo(&sansa,0) < 0) { | ||
232 | return 2; | ||
233 | } | ||
234 | |||
235 | display_partinfo(&sansa); | ||
236 | |||
237 | i = is_e200(&sansa); | ||
238 | if (i < 0) { | ||
239 | fprintf(stderr,"[ERR] Disk is not an E200 (%d), aborting.\n",i); | ||
240 | return 3; | ||
241 | } | ||
242 | |||
243 | if (sansa.hasoldbootloader) { | ||
244 | printf("[ERR] ************************************************************************\n"); | ||
245 | printf("[ERR] *** OLD ROCKBOX INSTALLATION DETECTED, ABORTING.\n"); | ||
246 | printf("[ERR] *** You must reinstall the original Sansa firmware before running\n"); | ||
247 | printf("[ERR] *** sansapatcher for the first time.\n"); | ||
248 | printf("[ERR] *** See http://www.rockbox.org/twiki/bin/view/Main/SansaE200Install\n"); | ||
249 | printf("[ERR] ************************************************************************\n"); | ||
250 | return 4; | ||
251 | } | ||
252 | |||
253 | if (action==LIST_IMAGES) { | ||
254 | list_images(&sansa); | ||
255 | } else if (action==READ_FIRMWARE) { | ||
256 | if (read_firmware(&sansa, filename)==0) { | ||
257 | fprintf(stderr,"[INFO] Firmware read to file %s.\n",filename); | ||
258 | } else { | ||
259 | fprintf(stderr,"[ERR] --read-firmware failed.\n"); | ||
260 | } | ||
261 | } else if (action==ADD_BOOTLOADER) { | ||
262 | if (sansa_reopen_rw(&sansa) < 0) { | ||
263 | return 5; | ||
264 | } | ||
265 | |||
266 | if (add_bootloader(&sansa, filename, type)==0) { | ||
267 | fprintf(stderr,"[INFO] Bootloader %s written to device.\n",filename); | ||
268 | } else { | ||
269 | fprintf(stderr,"[ERR] --add-bootloader failed.\n"); | ||
270 | } | ||
271 | } else if (action==DELETE_BOOTLOADER) { | ||
272 | if (sansa_reopen_rw(&sansa) < 0) { | ||
273 | return 5; | ||
274 | } | ||
275 | |||
276 | if (delete_bootloader(&sansa)==0) { | ||
277 | fprintf(stderr,"[INFO] Bootloader removed successfully.\n"); | ||
278 | } else { | ||
279 | fprintf(stderr,"[ERR] --delete-bootloader failed.\n"); | ||
280 | } | ||
281 | } | ||
282 | |||
283 | sansa_close(&sansa); | ||
284 | |||
285 | #ifdef WITH_BOOTOBJS | ||
286 | if (action==INTERACTIVE) { | ||
287 | printf("Press ENTER to exit sansapatcher :"); | ||
288 | fgets(yesno,4,stdin); | ||
289 | } | ||
290 | #endif | ||
291 | |||
292 | return 0; | ||
293 | } | ||
diff --git a/rbutil/sansapatcher/sansaio-posix.c b/rbutil/sansapatcher/sansaio-posix.c new file mode 100644 index 0000000000..a8d529c20b --- /dev/null +++ b/rbutil/sansapatcher/sansaio-posix.c | |||
@@ -0,0 +1,115 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id: ipodio-posix.c 12263 2007-02-10 19:49:43Z 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 <sys/types.h> | ||
26 | #include <sys/stat.h> | ||
27 | #include <sys/ioctl.h> | ||
28 | |||
29 | #if defined(linux) || defined (__linux) | ||
30 | #include <sys/mount.h> | ||
31 | #define SANSA_SECTORSIZE_IOCTL BLKSSZGET | ||
32 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ | ||
33 | || defined(__bsdi__) || defined(__DragonFly__) | ||
34 | #include <sys/disk.h> | ||
35 | #define SANSA_SECTORSIZE_IOCTL DIOCGSECTORSIZE | ||
36 | #elif defined(__APPLE__) && defined(__MACH__) | ||
37 | #include <sys/disk.h> | ||
38 | #define SANSA_SECTORSIZE_IOCTL DKIOCGETBLOCKSIZE | ||
39 | #else | ||
40 | #error No sector-size detection implemented for this platform | ||
41 | #endif | ||
42 | |||
43 | #include "sansaio.h" | ||
44 | |||
45 | void print_error(char* msg) | ||
46 | { | ||
47 | perror(msg); | ||
48 | } | ||
49 | |||
50 | int sansa_open(struct sansa_t* sansa, int silent) | ||
51 | { | ||
52 | sansa->dh=open(sansa->diskname,O_RDONLY); | ||
53 | if (sansa->dh < 0) { | ||
54 | if (!silent) perror(sansa->diskname); | ||
55 | return -1; | ||
56 | } | ||
57 | |||
58 | if(ioctl(sansa->dh,SANSA_SECTORSIZE_IOCTL,&sansa->sector_size) < 0) { | ||
59 | sansa->sector_size=512; | ||
60 | if (!silent) { | ||
61 | fprintf(stderr,"[ERR] ioctl() call to get sector size failed, defaulting to %d\n" | ||
62 | ,sansa->sector_size); | ||
63 | } | ||
64 | } | ||
65 | return 0; | ||
66 | } | ||
67 | |||
68 | |||
69 | int sansa_reopen_rw(struct sansa_t* sansa) | ||
70 | { | ||
71 | close(sansa->dh); | ||
72 | sansa->dh=open(sansa->diskname,O_RDWR); | ||
73 | if (sansa->dh < 0) { | ||
74 | perror(sansa->diskname); | ||
75 | return -1; | ||
76 | } | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | int sansa_close(struct sansa_t* sansa) | ||
81 | { | ||
82 | close(sansa->dh); | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | int sansa_alloc_buffer(unsigned char** sectorbuf, int bufsize) | ||
87 | { | ||
88 | *sectorbuf=malloc(bufsize); | ||
89 | if (*sectorbuf == NULL) { | ||
90 | return -1; | ||
91 | } | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | int sansa_seek(struct sansa_t* sansa, loff_t pos) | ||
96 | { | ||
97 | off_t res; | ||
98 | |||
99 | res = lseek64(sansa->dh, pos, SEEK_SET); | ||
100 | |||
101 | if (res == -1) { | ||
102 | return -1; | ||
103 | } | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes) | ||
108 | { | ||
109 | return read(sansa->dh, buf, nbytes); | ||
110 | } | ||
111 | |||
112 | int sansa_write(struct sansa_t* sansa, unsigned char* buf, int nbytes) | ||
113 | { | ||
114 | return write(sansa->dh, buf, nbytes); | ||
115 | } | ||
diff --git a/rbutil/sansapatcher/sansaio-win32.c b/rbutil/sansapatcher/sansaio-win32.c new file mode 100644 index 0000000000..315b379a28 --- /dev/null +++ b/rbutil/sansapatcher/sansaio-win32.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id: ipodio-win32.c 12263 2007-02-10 19:49:43Z dave $ | ||
9 | * | ||
10 | * Copyright (C) 2006-2007 Dave Chapman | ||
11 | * | ||
12 | * error(), lock_volume() and unlock_volume() functions and inspiration taken | ||
13 | * from: | ||
14 | * RawDisk - Direct Disk Read/Write Access for NT/2000/XP | ||
15 | * Copyright (c) 2003 Jan Kiszka | ||
16 | * http://www.stud.uni-hannover.de/user/73174/RawDisk/ | ||
17 | * | ||
18 | * All files in this archive are subject to the GNU General Public License. | ||
19 | * See the file COPYING in the source tree root for full license agreement. | ||
20 | * | ||
21 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
22 | * KIND, either express or implied. | ||
23 | * | ||
24 | ****************************************************************************/ | ||
25 | |||
26 | #include <stdio.h> | ||
27 | #include <unistd.h> | ||
28 | #include <fcntl.h> | ||
29 | #include <string.h> | ||
30 | #include <stdlib.h> | ||
31 | #include <sys/types.h> | ||
32 | #include <sys/stat.h> | ||
33 | #ifdef __WIN32__ | ||
34 | #include <windows.h> | ||
35 | #include <winioctl.h> | ||
36 | #endif | ||
37 | |||
38 | #include "sansaio.h" | ||
39 | |||
40 | static int lock_volume(HANDLE hDisk) | ||
41 | { | ||
42 | DWORD dummy; | ||
43 | |||
44 | return DeviceIoControl(hDisk, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, | ||
45 | &dummy, NULL); | ||
46 | } | ||
47 | |||
48 | static int unlock_volume(HANDLE hDisk) | ||
49 | { | ||
50 | DWORD dummy; | ||
51 | |||
52 | return DeviceIoControl(hDisk, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, | ||
53 | &dummy, NULL); | ||
54 | } | ||
55 | |||
56 | void print_error(char* msg) | ||
57 | { | ||
58 | char* pMsgBuf; | ||
59 | |||
60 | printf(msg); | ||
61 | FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | | ||
62 | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), | ||
63 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pMsgBuf, | ||
64 | 0, NULL); | ||
65 | printf(pMsgBuf); | ||
66 | LocalFree(pMsgBuf); | ||
67 | } | ||
68 | |||
69 | int sansa_open(struct sansa_t* sansa, int silent) | ||
70 | { | ||
71 | DISK_GEOMETRY_EX diskgeometry_ex; | ||
72 | DISK_GEOMETRY diskgeometry; | ||
73 | unsigned long n; | ||
74 | |||
75 | sansa->dh = CreateFile(sansa->diskname, GENERIC_READ, | ||
76 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | ||
77 | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); | ||
78 | |||
79 | if (sansa->dh == INVALID_HANDLE_VALUE) { | ||
80 | if (!silent) print_error(" Error opening disk: "); | ||
81 | return -1; | ||
82 | } | ||
83 | |||
84 | if (!lock_volume(sansa->dh)) { | ||
85 | if (!silent) print_error(" Error locking disk: "); | ||
86 | return -1; | ||
87 | } | ||
88 | |||
89 | if (!DeviceIoControl(sansa->dh, | ||
90 | IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, | ||
91 | NULL, | ||
92 | 0, | ||
93 | &diskgeometry_ex, | ||
94 | sizeof(diskgeometry_ex), | ||
95 | &n, | ||
96 | NULL)) { | ||
97 | if (!DeviceIoControl(sansa->dh, | ||
98 | IOCTL_DISK_GET_DRIVE_GEOMETRY, | ||
99 | NULL, | ||
100 | 0, | ||
101 | &diskgeometry, | ||
102 | sizeof(diskgeometry), | ||
103 | &n, | ||
104 | NULL)) { | ||
105 | if (!silent) print_error(" Error reading disk geometry: "); | ||
106 | return -1; | ||
107 | } else { | ||
108 | sansa->sector_size=diskgeometry.BytesPerSector; | ||
109 | } | ||
110 | } else { | ||
111 | sansa->sector_size=diskgeometry_ex.Geometry.BytesPerSector; | ||
112 | } | ||
113 | |||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | int sansa_reopen_rw(struct sansa_t* sansa) | ||
118 | { | ||
119 | /* Close existing file and re-open for writing */ | ||
120 | unlock_volume(sansa->dh); | ||
121 | CloseHandle(sansa->dh); | ||
122 | |||
123 | sansa->dh = CreateFile(sansa->diskname, GENERIC_READ | GENERIC_WRITE, | ||
124 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, | ||
125 | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); | ||
126 | |||
127 | if (sansa->dh == INVALID_HANDLE_VALUE) { | ||
128 | print_error(" Error opening disk: "); | ||
129 | return -1; | ||
130 | } | ||
131 | |||
132 | if (!lock_volume(sansa->dh)) { | ||
133 | print_error(" Error locking disk: "); | ||
134 | return -1; | ||
135 | } | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | int sansa_close(struct sansa_t* sansa) | ||
141 | { | ||
142 | unlock_volume(sansa->dh); | ||
143 | CloseHandle(sansa->dh); | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | int sansa_alloc_buffer(unsigned char** sectorbuf, int bufsize) | ||
148 | { | ||
149 | /* The ReadFile function requires a memory buffer aligned to a multiple of | ||
150 | the disk sector size. */ | ||
151 | *sectorbuf = (unsigned char*)VirtualAlloc(NULL, bufsize, MEM_COMMIT, PAGE_READWRITE); | ||
152 | if (*sectorbuf == NULL) { | ||
153 | print_error(" Error allocating a buffer: "); | ||
154 | return -1; | ||
155 | } | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | int sansa_seek(struct sansa_t* sansa, loff_t pos) | ||
160 | { | ||
161 | LARGE_INTEGER li; | ||
162 | |||
163 | li.QuadPart = pos; | ||
164 | |||
165 | li.LowPart = SetFilePointer (sansa->dh, li.LowPart, &li.HighPart, FILE_BEGIN); | ||
166 | |||
167 | if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { | ||
168 | print_error(" Seek error "); | ||
169 | return -1; | ||
170 | } | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes) | ||
175 | { | ||
176 | unsigned long count; | ||
177 | |||
178 | if (!ReadFile(sansa->dh, buf, nbytes, &count, NULL)) { | ||
179 | print_error(" Error reading from disk: "); | ||
180 | return -1; | ||
181 | } | ||
182 | |||
183 | return count; | ||
184 | } | ||
185 | |||
186 | int sansa_write(struct sansa_t* sansa, unsigned char* buf, int nbytes) | ||
187 | { | ||
188 | unsigned long count; | ||
189 | |||
190 | if (!WriteFile(sansa->dh, buf, nbytes, &count, NULL)) { | ||
191 | print_error(" Error writing to disk: "); | ||
192 | return -1; | ||
193 | } | ||
194 | |||
195 | return count; | ||
196 | } | ||
diff --git a/rbutil/sansapatcher/sansaio.h b/rbutil/sansapatcher/sansaio.h new file mode 100644 index 0000000000..246400c51c --- /dev/null +++ b/rbutil/sansapatcher/sansaio.h | |||
@@ -0,0 +1,74 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id: ipodio.h 12339 2007-02-16 20:45:00Z 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 | #ifndef __SANSAIO_H | ||
21 | #define __SANSAIO_H | ||
22 | |||
23 | #include <stdint.h> | ||
24 | #include <unistd.h> | ||
25 | |||
26 | #ifdef __WIN32__ | ||
27 | #include <windows.h> | ||
28 | #define loff_t int64_t | ||
29 | #else | ||
30 | #define HANDLE int | ||
31 | #define O_BINARY 0 | ||
32 | |||
33 | /* Only Linux seems to need lseek64 and loff_t */ | ||
34 | #if !defined(linux) && defined (__linux) | ||
35 | #define loff_t off_t | ||
36 | #define lseek64 lseek | ||
37 | #endif | ||
38 | |||
39 | #endif | ||
40 | |||
41 | struct partinfo_t { | ||
42 | unsigned long start; /* first sector (LBA) */ | ||
43 | unsigned long size; /* number of sectors */ | ||
44 | int type; | ||
45 | }; | ||
46 | |||
47 | struct mi4header_t { | ||
48 | uint32_t version; | ||
49 | uint32_t length; | ||
50 | uint32_t crc32; | ||
51 | uint32_t enctype; | ||
52 | uint32_t mi4size; | ||
53 | uint32_t plaintext; | ||
54 | }; | ||
55 | |||
56 | struct sansa_t { | ||
57 | HANDLE dh; | ||
58 | char diskname[4096]; | ||
59 | int sector_size; | ||
60 | struct partinfo_t pinfo[4]; | ||
61 | int hasoldbootloader; | ||
62 | loff_t start; /* Offset in bytes of firmware partition from start of disk */ | ||
63 | }; | ||
64 | |||
65 | void print_error(char* msg); | ||
66 | int sansa_open(struct sansa_t* sansa, int silent); | ||
67 | int sansa_reopen_rw(struct sansa_t* sansa); | ||
68 | int sansa_close(struct sansa_t* sansa); | ||
69 | int sansa_seek(struct sansa_t* sansa, loff_t pos); | ||
70 | int sansa_read(struct sansa_t* sansa, unsigned char* buf, int nbytes); | ||
71 | int sansa_write(struct sansa_t* sansa, unsigned char* buf, int nbytes); | ||
72 | int sansa_alloc_buffer(unsigned char** sectorbuf, int bufsize); | ||
73 | |||
74 | #endif | ||
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 | } | ||
diff --git a/rbutil/sansapatcher/sansapatcher.h b/rbutil/sansapatcher/sansapatcher.h new file mode 100644 index 0000000000..7a2345f34a --- /dev/null +++ b/rbutil/sansapatcher/sansapatcher.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id: ipodpatcher.c 12237 2007-02-08 21:31:38Z 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 | #ifndef _IPODPATCHER_H | ||
21 | #define _IPODPATCHER_H | ||
22 | |||
23 | #include "sansaio.h" | ||
24 | |||
25 | /* Size of buffer for disk I/O - 8MB is large enough for any version | ||
26 | of the Apple firmware, but not the Nano's RSRC image. */ | ||
27 | #define BUFFER_SIZE 8*1024*1024 | ||
28 | extern unsigned char* sectorbuf; | ||
29 | |||
30 | #define FILETYPE_MI4 0 | ||
31 | #ifdef WITH_BOOTOBJS | ||
32 | #define FILETYPE_INTERNAL 1 | ||
33 | #endif | ||
34 | |||
35 | char* get_parttype(int pt); | ||
36 | int read_partinfo(struct sansa_t* sansa, int silent); | ||
37 | off_t filesize(int fd); | ||
38 | int is_e200(struct sansa_t* sansa); | ||
39 | int sansa_scan(struct sansa_t* sansa); | ||
40 | int read_firmware(struct sansa_t* sansa, char* filename); | ||
41 | int add_bootloader(struct sansa_t* sansa, char* filename, int type); | ||
42 | int delete_bootloader(struct sansa_t* sansa); | ||
43 | void list_images(struct sansa_t* sansa); | ||
44 | |||
45 | #endif | ||