1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
9 * File: irivertools.cpp
11 * Copyright (C) 2007 Dominik Wenger
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
21 #include "irivertools.h"
25 const unsigned char munge
[] = {
26 0x7a, 0x36, 0xc4, 0x43, 0x49, 0x6b, 0x35, 0x4e, 0xa3, 0x46, 0x25, 0x84,
27 0x4d, 0x73, 0x74, 0x61
30 const unsigned char header_modify
[] = "* IHPFIRM-DECODED ";
32 const char * const models
[] = { "iHP-100", "iHP-120/iHP-140", "H300 series",
35 /* aligns with models array; expected min firmware size */
36 const unsigned int firmware_minsize
[] = { 0x100000, 0x100000, 0x200000 };
37 /* aligns with models array; expected max firmware size */
38 const unsigned int firmware_maxsize
[] = { 0x200000, 0x200000, 0x400000 };
40 const unsigned char header
[][16] = {
41 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
42 { 0x20, 0x03, 0x08, 0x27, 0x24, 0x00, 0x02, 0x30, 0x19, 0x17, 0x65, 0x73,
43 0x85, 0x32, 0x83, 0x22 },
44 { 0x20, 0x04, 0x03, 0x27, 0x20, 0x50, 0x01, 0x70, 0x80, 0x30, 0x80, 0x06,
45 0x30, 0x19, 0x17, 0x65 }
48 /* begin mkboot.c excerpt */
49 unsigned char image
[0x400000 + 0x220 + 0x400000/0x200];
51 bool mkboot(wxString infile
, wxString outfile
,wxString bootloader
,int origin
)
56 int actual_length
, total_length
, binary_length
, num_chksums
;
58 memset(image
, 0xff, sizeof(image
));
60 /* First, read the iriver original firmware into the image */
64 ERR_DIALOG(wxT("Could not open: ") + infile
, wxT("mkboot"));
69 ERR_DIALOG(wxT("reading header failed"), wxT("mkboot"));
73 /* This is the length of the binary image without the scrambling
74 overhead (but including the ESTFBINR header) */
75 binary_length
= image
[4] + (image
[5] << 8) +
76 (image
[6] << 16) + (image
[7] << 24);
78 /* Read the rest of the binary data, but not the checksum block */
79 len
= binary_length
+0x200-16;
80 i
= f
.Read(image
+16, len
);
82 ERR_DIALOG(wxT("reading firmware failed"),wxT("mkboot"));
87 /* Now, read the boot loader into the image */
88 if(!f
.Open(bootloader
))
90 ERR_DIALOG(wxT("Could not open: ") + bootloader
, wxT("mkboot"));
96 i
= f
.Read(image
+0x220 + origin
, bllen
);
98 ERR_DIALOG(wxT("reading bootloader failed"), wxT("mkboot"));
104 if(!f
.Open(outfile
,wxFile::write
))
106 ERR_DIALOG(wxT("Could not open: ") + outfile
, wxT("mkboot"));
110 /* Patch the reset vector to start the boot loader */
111 image
[0x220 + 4] = image
[origin
+ 0x220 + 4];
112 image
[0x220 + 5] = image
[origin
+ 0x220 + 5];
113 image
[0x220 + 6] = image
[origin
+ 0x220 + 6];
114 image
[0x220 + 7] = image
[origin
+ 0x220 + 7];
116 /* This is the actual length of the binary, excluding all headers */
117 actual_length
= origin
+ bllen
;
119 /* Patch the ESTFBINR header */
120 image
[0x20c] = (actual_length
>> 24) & 0xff;
121 image
[0x20d] = (actual_length
>> 16) & 0xff;
122 image
[0x20e] = (actual_length
>> 8) & 0xff;
123 image
[0x20f] = actual_length
& 0xff;
125 image
[0x21c] = (actual_length
>> 24) & 0xff;
126 image
[0x21d] = (actual_length
>> 16) & 0xff;
127 image
[0x21e] = (actual_length
>> 8) & 0xff;
128 image
[0x21f] = actual_length
& 0xff;
130 /* This is the length of the binary, including the ESTFBINR header and
131 rounded up to the nearest 0x200 boundary */
132 binary_length
= (actual_length
+ 0x20 + 0x1ff) & 0xfffffe00;
134 /* The number of checksums, i.e number of 0x200 byte blocks */
135 num_chksums
= binary_length
/ 0x200;
137 /* The total file length, including all headers and checksums */
138 total_length
= binary_length
+ num_chksums
+ 0x200;
140 /* Patch the scrambler header with the new length info */
141 image
[0] = total_length
& 0xff;
142 image
[1] = (total_length
>> 8) & 0xff;
143 image
[2] = (total_length
>> 16) & 0xff;
144 image
[3] = (total_length
>> 24) & 0xff;
146 image
[4] = binary_length
& 0xff;
147 image
[5] = (binary_length
>> 8) & 0xff;
148 image
[6] = (binary_length
>> 16) & 0xff;
149 image
[7] = (binary_length
>> 24) & 0xff;
151 image
[8] = num_chksums
& 0xff;
152 image
[9] = (num_chksums
>> 8) & 0xff;
153 image
[10] = (num_chksums
>> 16) & 0xff;
154 image
[11] = (num_chksums
>> 24) & 0xff;
156 i
= f
.Write(image
,total_length
);
157 if(i
< total_length
) {
158 ERR_DIALOG(wxT("writing bootloader failed"), wxT("mkboot"));
167 /* end mkboot.c excerpt */
170 int intable(char *md5
, struct sumpairs
*table
, int len
)
173 for (i
= 0; i
< len
; i
++) {
174 if (strncmp(md5
, table
[i
].unpatched
, 32) == 0) {
184 static int testheader( const unsigned char * const data
)
186 const unsigned char * const d
= data
+16;
187 const char * const * m
= models
;
191 if( memcmp( header
[ index
], d
, 16 ) == 0 )
199 static void modifyheader( unsigned char * data
)
201 const unsigned char * h
= header_modify
;
203 for( i
=0; i
<512; i
++ )
211 int iriver_decode(wxString infile_name
, wxString outfile_name
, unsigned int modify
,
212 enum striptype stripmode
)
218 unsigned char headerdata
[512];
219 unsigned long dwLength1
, dwLength2
, dwLength3
, fp
= 0;
220 unsigned char blockdata
[16+16];
221 unsigned char out
[16];
222 unsigned char newmunge
;
225 unsigned char * pChecksums
, * ppChecksums
= 0;
228 if(!infile
.Open(infile_name
))
230 ERR_DIALOG(wxT("Could not open: ") + infile_name
, wxT("iriver_decode"));
233 if(!outfile
.Open(outfile_name
,wxFile::write
))
235 ERR_DIALOG(wxT("Could not open: ") + outfile_name
,
236 wxT("iriver_decode"));
239 lenread
= infile
.Read( headerdata
, 512);
242 ERR_DIALOG(wxT("This doesn't look like a valid encrypted iHP "
243 "firmware - reason: header length\n"),wxT("iriver_decode"));
250 i
= testheader( headerdata
);
253 ERR_DIALOG( wxT( "This firmware is for an unknown model, or is not"
254 " a valid encrypted iHP firmware\n" ),wxT("iriver_decode"));
259 fprintf( stderr
, "Model %s\n", models
[ i
] );
261 dwLength1
= headerdata
[0] | (headerdata
[1]<<8) |
262 (headerdata
[2]<<16) | (headerdata
[3]<<24);
263 dwLength2
= headerdata
[4] | (headerdata
[5]<<8) |
264 (headerdata
[6]<<16) | (headerdata
[7]<<24);
265 dwLength3
= headerdata
[8] | (headerdata
[9]<<8) |
266 (headerdata
[10]<<16) | (headerdata
[11]<<24);
268 if( dwLength1
< firmware_minsize
[ i
] ||
269 dwLength1
> firmware_maxsize
[ i
] ||
270 dwLength2
< firmware_minsize
[ i
] ||
271 dwLength2
> dwLength1
||
272 dwLength3
> dwLength1
||
273 dwLength2
>>9 != dwLength3
||
274 dwLength2
+dwLength3
+512 != dwLength1
)
276 ERR_DIALOG( wxT( "This doesn't look like a valid encrypted "
277 "iHP firmware - reason: file 'length' data\n" ),wxT("iriver_decode"));
283 pChecksums
= ppChecksums
= (unsigned char *)( malloc( dwLength3
) );
287 modifyheader( headerdata
);
290 if( stripmode
== STRIP_NONE
)
291 outfile
.Write( headerdata
, 512);
293 memset( blockdata
, 0, 16 );
296 while( ( fp
< dwLength2
) &&
297 ( lenread
= infile
.Read( blockdata
+16, 16) == 16) )
301 for( i
=0; i
<16; ++i
)
303 newmunge
= blockdata
[16+i
] ^ munge
[i
];
304 out
[i
] = newmunge
^ blockdata
[i
];
305 blockdata
[i
] = newmunge
;
309 if( fp
> ESTF_SIZE
|| stripmode
!= STRIP_HEADER_CHECKSUM_ESTF
)
311 outfile
.Write( out
+4, 12);
312 outfile
.Write( out
, 4);
316 if( ESTF_SIZE
- fp
< 16 )
318 memcpy( out
+4, blockdata
+16, 12 );
319 memcpy( out
, blockdata
+28, 4 );
320 outfile
.Write( blockdata
+16+ESTF_SIZE
-fp
, ESTF_SIZE
-fp
);
328 memset( blockdata
, 0, 16 );
336 if( fp
!= dwLength2
)
338 ERR_DIALOG( wxT( "This doesn't look like a valid encrypted "
339 "iHP firmware - reason: 'length2' mismatch\n" ),wxT("iriver_decode"));
346 ppChecksums
= pChecksums
;
347 while( ( fp
< dwLength3
) &&
348 ( lenread
= infile
.Read( blockdata
, 32 ) ) > 0 )
351 if( stripmode
== STRIP_NONE
)
352 outfile
.Write( blockdata
, lenread
);
353 if( memcmp( ppChecksums
, blockdata
, lenread
) != 0 )
355 ERR_DIALOG( wxT( "This doesn't look like a valid encrypted "
356 "iHP firmware - reason: Checksum mismatch!" ),wxT("iriver_decode"));
361 ppChecksums
+= lenread
;
364 if( fp
!= dwLength3
)
366 ERR_DIALOG(wxT( "This doesn't look like a valid encrypted "
367 "iHP firmware - reason: 'length3' mismatch\n" ),wxT("iriver_decode"));
374 fprintf( stderr
, "File decoded correctly and all checksums matched!\n" );
379 fprintf(stderr
, "Output file contains all headers and "
382 case STRIP_HEADER_CHECKSUM
:
383 fprintf( stderr
, "NB: output file contains only ESTFBINR header"
384 " and decoded firmware code\n" );
386 case STRIP_HEADER_CHECKSUM_ESTF
:
387 fprintf( stderr
, "NB: output file contains only raw decoded "
398 int iriver_encode(wxString infile_name
, wxString outfile_name
, unsigned int modify
)
404 unsigned char headerdata
[512];
405 unsigned long dwLength1
, dwLength2
, dwLength3
, fp
= 0;
406 unsigned char blockdata
[16+16];
407 unsigned char out
[16];
408 unsigned char newmunge
;
411 unsigned char * pChecksums
, * ppChecksums
;
414 if(!infile
.Open(infile_name
,wxFile::read
))
416 ERR_DIALOG(wxT("Could not open: ") + infile_name
, wxT("iriver_decode"));
419 if(!outfile
.Open(outfile_name
,wxFile::write
))
421 ERR_DIALOG(wxT("Could not open: ") + outfile_name
,
422 wxT("iriver_decode"));
426 lenread
= infile
.Read( headerdata
, 512 );
429 ERR_DIALOG(wxT("This doesn't look like a valid decoded "
430 "iHP firmware - reason: header length\n"), wxT("iriver_decode"));
437 modifyheader( headerdata
); /* reversible */
440 i
= testheader( headerdata
);
443 ERR_DIALOG(wxT("This firmware is for an unknown model, or is not"
444 " a valid decoded iHP firmware\n"), wxT("iriver_decode"));
448 fprintf( stderr
, "Model %s\n", models
[ i
] );
450 dwLength1
= headerdata
[0] | (headerdata
[1]<<8) |
451 (headerdata
[2]<<16) | (headerdata
[3]<<24);
452 dwLength2
= headerdata
[4] | (headerdata
[5]<<8) |
453 (headerdata
[6]<<16) | (headerdata
[7]<<24);
454 dwLength3
= headerdata
[8] | (headerdata
[9]<<8) |
455 (headerdata
[10]<<16) | (headerdata
[11]<<24);
457 if( dwLength1
< firmware_minsize
[i
] ||
458 dwLength1
> firmware_maxsize
[i
] ||
459 dwLength2
< firmware_minsize
[i
] ||
460 dwLength2
> dwLength1
||
461 dwLength3
> dwLength1
||
462 dwLength2
+dwLength3
+512 != dwLength1
)
464 ERR_DIALOG(wxT("This doesn't look like a valid decoded iHP"
465 " firmware - reason: file 'length' data\n"), wxT("iriver_decode"));
470 pChecksums
= ppChecksums
= (unsigned char *)( malloc( dwLength3
) );
472 outfile
.Write( headerdata
, 512);
474 memset( blockdata
, 0, 16 );
476 while( ( fp
< dwLength2
) &&
477 ( lenread
= infile
.Read( blockdata
+16, 16) ) == 16 )
480 for( i
=0; i
<16; ++i
)
482 newmunge
= blockdata
[16+((12+i
)&0xf)] ^ blockdata
[i
];
483 out
[i
] = newmunge
^ munge
[i
];
484 ck
+= blockdata
[16+i
];
485 blockdata
[i
] = newmunge
;
487 outfile
.Write( out
, 16);
492 memset( blockdata
, 0, 16 );
500 if( fp
!= dwLength2
)
502 ERR_DIALOG(wxT("This doesn't look like a valid decoded "
503 "iHP firmware - reason: 'length1' mismatch\n"), wxT("iriver_decode"));
508 /* write out remainder w/out applying descrambler */
511 ppChecksums
= pChecksums
;
512 while( ( fp
< dwLength3
) &&
513 ( lenread
= outfile
.Write( ppChecksums
, lenread
) ) > 0 )
516 ppChecksums
+= lenread
;
517 lenread
= dwLength3
- fp
;
520 if( fp
!= dwLength3
)
522 ERR_DIALOG(wxT("This doesn't look like a valid decoded "
523 "iHP firmware - reason: 'length2' mismatch\n"), wxT("iriver_decode"));
528 fprintf( stderr
, "File encoded successfully and checksum table built!\n" );
536 bool PatchFirmware(wxString firmware
,wxString bootloader
,int series
, int table_entry
)
538 wxString name1
, name2
, name3
;
541 struct sumpairs
*sums
;
544 /* get pointer to the correct bootloader.bin */
547 sums
= &h100pairs
[0];
551 sums
= &h120pairs
[0];
555 sums
= &h300pairs
[0];
560 name1
= gv
->stdpaths
->GetUserDataDir()
561 + wxT("" PATH_SEP
"download" PATH_SEP
"firmware.bin"),
562 /* descrambled file */
563 name2
= gv
->stdpaths
->GetUserDataDir()
564 + wxT("" PATH_SEP
"download" PATH_SEP
"new.bin");
566 name3
= gv
->stdpaths
->GetUserDataDir()
567 + wxT("" PATH_SEP
"download" PATH_SEP
"new.hex");
568 if (iriver_decode(firmware
, name1
, FALSE
, STRIP_NONE
) == -1) {
569 ERR_DIALOG(wxT("Error in descramble"), wxT("Descramble Firmware"));
575 if (!mkboot(name1
, name2
, bootloader
, origin
)) {
576 ERR_DIALOG(wxT("Error in patching"),wxT("Patching Firmware"));
582 if (iriver_encode(name2
, name3
, FALSE
) == -1) {
583 ERR_DIALOG(wxT("Error in scramble"),wxT("Scramble Firmware"));
590 if (!FileMD5(name3
, md5sum_str
)) {
591 ERR_DIALOG(wxT("Error in checksumming"),wxT("Checksumming Firmware"));
597 if (strncmp(sums
[table_entry
].patched
, md5sum_str
, 32) == 0) {
598 /* delete temp files */