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
.Printf(wxT("Could not open: %s"),infile
.c_str());
65 ERR_DIALOG(err
, wxT("mkboot"));
70 ERR_DIALOG(wxT("reading header failed"), wxT("mkboot"));
74 /* This is the length of the binary image without the scrambling
75 overhead (but including the ESTFBINR header) */
76 binary_length
= image
[4] + (image
[5] << 8) +
77 (image
[6] << 16) + (image
[7] << 24);
79 /* Read the rest of the binary data, but not the checksum block */
80 len
= binary_length
+0x200-16;
81 i
= f
.Read(image
+16, len
);
83 ERR_DIALOG(wxT("reading firmware failed"),wxT("mkboot"));
88 /* Now, read the boot loader into the image */
89 if(!f
.Open(bootloader
))
91 err
.Printf(wxT("Could not open: %s"),bootloader
.c_str());
92 ERR_DIALOG(err
, wxT("mkboot"));
98 i
= f
.Read(image
+0x220 + origin
, bllen
);
100 ERR_DIALOG(wxT("reading bootloader failed"), wxT("mkboot"));
106 if(!f
.Open(outfile
,wxFile::write
))
108 err
.Printf(wxT("Could not open: %s"),outfile
.c_str());
109 ERR_DIALOG(err
, wxT("mkboot"));
113 /* Patch the reset vector to start the boot loader */
114 image
[0x220 + 4] = image
[origin
+ 0x220 + 4];
115 image
[0x220 + 5] = image
[origin
+ 0x220 + 5];
116 image
[0x220 + 6] = image
[origin
+ 0x220 + 6];
117 image
[0x220 + 7] = image
[origin
+ 0x220 + 7];
119 /* This is the actual length of the binary, excluding all headers */
120 actual_length
= origin
+ bllen
;
122 /* Patch the ESTFBINR header */
123 image
[0x20c] = (actual_length
>> 24) & 0xff;
124 image
[0x20d] = (actual_length
>> 16) & 0xff;
125 image
[0x20e] = (actual_length
>> 8) & 0xff;
126 image
[0x20f] = actual_length
& 0xff;
128 image
[0x21c] = (actual_length
>> 24) & 0xff;
129 image
[0x21d] = (actual_length
>> 16) & 0xff;
130 image
[0x21e] = (actual_length
>> 8) & 0xff;
131 image
[0x21f] = actual_length
& 0xff;
133 /* This is the length of the binary, including the ESTFBINR header and
134 rounded up to the nearest 0x200 boundary */
135 binary_length
= (actual_length
+ 0x20 + 0x1ff) & 0xfffffe00;
137 /* The number of checksums, i.e number of 0x200 byte blocks */
138 num_chksums
= binary_length
/ 0x200;
140 /* The total file length, including all headers and checksums */
141 total_length
= binary_length
+ num_chksums
+ 0x200;
143 /* Patch the scrambler header with the new length info */
144 image
[0] = total_length
& 0xff;
145 image
[1] = (total_length
>> 8) & 0xff;
146 image
[2] = (total_length
>> 16) & 0xff;
147 image
[3] = (total_length
>> 24) & 0xff;
149 image
[4] = binary_length
& 0xff;
150 image
[5] = (binary_length
>> 8) & 0xff;
151 image
[6] = (binary_length
>> 16) & 0xff;
152 image
[7] = (binary_length
>> 24) & 0xff;
154 image
[8] = num_chksums
& 0xff;
155 image
[9] = (num_chksums
>> 8) & 0xff;
156 image
[10] = (num_chksums
>> 16) & 0xff;
157 image
[11] = (num_chksums
>> 24) & 0xff;
159 i
= f
.Write(image
,total_length
);
160 if(i
< total_length
) {
161 ERR_DIALOG(wxT("writing bootloader failed"), wxT("mkboot"));
170 /* end mkboot.c excerpt */
173 int intable(char *md5
, struct sumpairs
*table
, int len
)
176 for (i
= 0; i
< len
; i
++) {
177 if (strncmp(md5
, table
[i
].unpatched
, 32) == 0) {
187 static int testheader( const unsigned char * const data
)
189 const unsigned char * const d
= data
+16;
190 const char * const * m
= models
;
194 if( memcmp( header
[ index
], d
, 16 ) == 0 )
202 static void modifyheader( unsigned char * data
)
204 const unsigned char * h
= header_modify
;
206 for( i
=0; i
<512; i
++ )
214 int iriver_decode(wxString infile_name
, wxString outfile_name
, unsigned int modify
,
215 enum striptype stripmode
)
221 unsigned char headerdata
[512];
222 unsigned long dwLength1
, dwLength2
, dwLength3
, fp
= 0;
223 unsigned char blockdata
[16+16];
224 unsigned char out
[16];
225 unsigned char newmunge
;
228 unsigned char * pChecksums
, * ppChecksums
= 0;
231 if(!infile
.Open(infile_name
))
233 err
.Printf(wxT("Could not open: %s"),infile_name
.c_str());
234 ERR_DIALOG(err
, wxT("iriver_decode"));
237 if(!outfile
.Open(outfile_name
,wxFile::write
))
239 err
.Printf(wxT("Could not open: %s"),outfile_name
.c_str());
240 ERR_DIALOG(err
, wxT("iriver_decode"));
243 lenread
= infile
.Read( headerdata
, 512);
246 ERR_DIALOG(wxT("This doesn't look like a valid encrypted iHP "
247 "firmware - reason: header length\n"),wxT("iriver_decode"));
254 i
= testheader( headerdata
);
257 ERR_DIALOG( wxT( "This firmware is for an unknown model, or is not"
258 " a valid encrypted iHP firmware\n" ),wxT("iriver_decode"));
263 fprintf( stderr
, "Model %s\n", models
[ i
] );
265 dwLength1
= headerdata
[0] | (headerdata
[1]<<8) |
266 (headerdata
[2]<<16) | (headerdata
[3]<<24);
267 dwLength2
= headerdata
[4] | (headerdata
[5]<<8) |
268 (headerdata
[6]<<16) | (headerdata
[7]<<24);
269 dwLength3
= headerdata
[8] | (headerdata
[9]<<8) |
270 (headerdata
[10]<<16) | (headerdata
[11]<<24);
272 if( dwLength1
< firmware_minsize
[ i
] ||
273 dwLength1
> firmware_maxsize
[ i
] ||
274 dwLength2
< firmware_minsize
[ i
] ||
275 dwLength2
> dwLength1
||
276 dwLength3
> dwLength1
||
277 dwLength2
>>9 != dwLength3
||
278 dwLength2
+dwLength3
+512 != dwLength1
)
280 ERR_DIALOG( wxT( "This doesn't look like a valid encrypted "
281 "iHP firmware - reason: file 'length' data\n" ),wxT("iriver_decode"));
287 pChecksums
= ppChecksums
= (unsigned char *)( malloc( dwLength3
) );
291 modifyheader( headerdata
);
294 if( stripmode
== STRIP_NONE
)
295 outfile
.Write( headerdata
, 512);
297 memset( blockdata
, 0, 16 );
300 while( ( fp
< dwLength2
) &&
301 ( lenread
= infile
.Read( blockdata
+16, 16) == 16) )
305 for( i
=0; i
<16; ++i
)
307 newmunge
= blockdata
[16+i
] ^ munge
[i
];
308 out
[i
] = newmunge
^ blockdata
[i
];
309 blockdata
[i
] = newmunge
;
313 if( fp
> ESTF_SIZE
|| stripmode
!= STRIP_HEADER_CHECKSUM_ESTF
)
315 outfile
.Write( out
+4, 12);
316 outfile
.Write( out
, 4);
320 if( ESTF_SIZE
- fp
< 16 )
322 memcpy( out
+4, blockdata
+16, 12 );
323 memcpy( out
, blockdata
+28, 4 );
324 outfile
.Write( blockdata
+16+ESTF_SIZE
-fp
, ESTF_SIZE
-fp
);
332 memset( blockdata
, 0, 16 );
340 if( fp
!= dwLength2
)
342 ERR_DIALOG( wxT( "This doesn't look like a valid encrypted "
343 "iHP firmware - reason: 'length2' mismatch\n" ),wxT("iriver_decode"));
350 ppChecksums
= pChecksums
;
351 while( ( fp
< dwLength3
) &&
352 ( lenread
= infile
.Read( blockdata
, 32 ) ) > 0 )
355 if( stripmode
== STRIP_NONE
)
356 outfile
.Write( blockdata
, lenread
);
357 if( memcmp( ppChecksums
, blockdata
, lenread
) != 0 )
359 ERR_DIALOG( wxT( "This doesn't look like a valid encrypted "
360 "iHP firmware - reason: Checksum mismatch!" ),wxT("iriver_decode"));
365 ppChecksums
+= lenread
;
368 if( fp
!= dwLength3
)
370 ERR_DIALOG(wxT( "This doesn't look like a valid encrypted "
371 "iHP firmware - reason: 'length3' mismatch\n" ),wxT("iriver_decode"));
378 fprintf( stderr
, "File decoded correctly and all checksums matched!\n" );
383 fprintf(stderr
, "Output file contains all headers and "
386 case STRIP_HEADER_CHECKSUM
:
387 fprintf( stderr
, "NB: output file contains only ESTFBINR header"
388 " and decoded firmware code\n" );
390 case STRIP_HEADER_CHECKSUM_ESTF
:
391 fprintf( stderr
, "NB: output file contains only raw decoded "
402 int iriver_encode(wxString infile_name
, wxString outfile_name
, unsigned int modify
)
408 unsigned char headerdata
[512];
409 unsigned long dwLength1
, dwLength2
, dwLength3
, fp
= 0;
410 unsigned char blockdata
[16+16];
411 unsigned char out
[16];
412 unsigned char newmunge
;
415 unsigned char * pChecksums
, * ppChecksums
;
418 if(!infile
.Open(infile_name
,wxFile::read
))
420 err
.Printf(wxT("Could not open: %s"),infile_name
.c_str());
421 ERR_DIALOG(err
, wxT("iriver_decode"));
424 if(!outfile
.Open(outfile_name
,wxFile::write
))
426 err
.Printf(wxT("Could not open: %s"),outfile_name
.c_str());
427 ERR_DIALOG(err
, wxT("iriver_decode"));
431 lenread
= infile
.Read( headerdata
, 512 );
434 ERR_DIALOG(wxT("This doesn't look like a valid decoded "
435 "iHP firmware - reason: header length\n"), wxT("iriver_decode"));
442 modifyheader( headerdata
); /* reversible */
445 i
= testheader( headerdata
);
448 ERR_DIALOG(wxT("This firmware is for an unknown model, or is not"
449 " a valid decoded iHP firmware\n"), wxT("iriver_decode"));
453 fprintf( stderr
, "Model %s\n", models
[ i
] );
455 dwLength1
= headerdata
[0] | (headerdata
[1]<<8) |
456 (headerdata
[2]<<16) | (headerdata
[3]<<24);
457 dwLength2
= headerdata
[4] | (headerdata
[5]<<8) |
458 (headerdata
[6]<<16) | (headerdata
[7]<<24);
459 dwLength3
= headerdata
[8] | (headerdata
[9]<<8) |
460 (headerdata
[10]<<16) | (headerdata
[11]<<24);
462 if( dwLength1
< firmware_minsize
[i
] ||
463 dwLength1
> firmware_maxsize
[i
] ||
464 dwLength2
< firmware_minsize
[i
] ||
465 dwLength2
> dwLength1
||
466 dwLength3
> dwLength1
||
467 dwLength2
+dwLength3
+512 != dwLength1
)
469 ERR_DIALOG(wxT("This doesn't look like a valid decoded iHP"
470 " firmware - reason: file 'length' data\n"), wxT("iriver_decode"));
475 pChecksums
= ppChecksums
= (unsigned char *)( malloc( dwLength3
) );
477 outfile
.Write( headerdata
, 512);
479 memset( blockdata
, 0, 16 );
481 while( ( fp
< dwLength2
) &&
482 ( lenread
= infile
.Read( blockdata
+16, 16) ) == 16 )
485 for( i
=0; i
<16; ++i
)
487 newmunge
= blockdata
[16+((12+i
)&0xf)] ^ blockdata
[i
];
488 out
[i
] = newmunge
^ munge
[i
];
489 ck
+= blockdata
[16+i
];
490 blockdata
[i
] = newmunge
;
492 outfile
.Write( out
, 16);
497 memset( blockdata
, 0, 16 );
505 if( fp
!= dwLength2
)
507 ERR_DIALOG(wxT("This doesn't look like a valid decoded "
508 "iHP firmware - reason: 'length1' mismatch\n"), wxT("iriver_decode"));
513 /* write out remainder w/out applying descrambler */
516 ppChecksums
= pChecksums
;
517 while( ( fp
< dwLength3
) &&
518 ( lenread
= outfile
.Write( ppChecksums
, lenread
) ) > 0 )
521 ppChecksums
+= lenread
;
522 lenread
= dwLength3
- fp
;
525 if( fp
!= dwLength3
)
527 ERR_DIALOG(wxT("This doesn't look like a valid decoded "
528 "iHP firmware - reason: 'length2' mismatch\n"), wxT("iriver_decode"));
533 fprintf( stderr
, "File encoded successfully and checksum table built!\n" );
541 bool PatchFirmware(wxString firmware
,wxString bootloader
,int series
, int table_entry
)
543 wxString name1
, name2
, name3
;
546 struct sumpairs
*sums
;
549 /* get pointer to the correct bootloader.bin */
552 sums
= &h100pairs
[0];
556 sums
= &h120pairs
[0];
560 sums
= &h300pairs
[0];
565 name1
.Printf(wxT("%s" PATH_SEP
"download" PATH_SEP
"firmware.bin"),
566 gv
->stdpaths
->GetUserDataDir().c_str());
567 /* descrambled file */
568 name2
.Printf(wxT("%s" PATH_SEP
"download" PATH_SEP
"new.bin"),
569 gv
->stdpaths
->GetUserDataDir().c_str());
571 name3
.Printf(wxT("%s" PATH_SEP
"download" PATH_SEP
"new.hex"),
572 gv
->stdpaths
->GetUserDataDir().c_str());
573 if (iriver_decode(firmware
, name1
, FALSE
, STRIP_NONE
) == -1) {
574 ERR_DIALOG(wxT("Error in descramble"), wxT("Descramble Firmware"));
580 if (!mkboot(name1
, name2
, bootloader
, origin
)) {
581 ERR_DIALOG(wxT("Error in patching"),wxT("Patching Firmware"));
587 if (iriver_encode(name2
, name3
, FALSE
) == -1) {
588 ERR_DIALOG(wxT("Error in scramble"),wxT("Scramble Firmware"));
595 if (!FileMD5(name3
, md5sum_str
)) {
596 ERR_DIALOG(wxT("Error in checksumming"),wxT("Checksumming Firmware"));
602 if (strncmp(sums
[table_entry
].patched
, md5sum_str
, 32) == 0) {
603 /* delete temp files */