/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2004 by Dave Hooper * * This particular source code file is licensed under the X11 license. See the * bottom of the COPYING file for details on this license. * * Original code from http://www.beermex.com/@spc/ihpfirm.src.zip * Details at http://www.rockbox.org/wiki/IriverToolsGuide * ****************************************************************************/ #include #include #include #include "iriver.h" const unsigned char munge[] = { 0x7a, 0x36, 0xc4, 0x43, 0x49, 0x6b, 0x35, 0x4e, 0xa3, 0x46, 0x25, 0x84, 0x4d, 0x73, 0x74, 0x61 }; const unsigned char header_modify[] = "* IHPFIRM-DECODED "; const char * const models[] = { "iHP-100", "iHP-120/iHP-140", "H300 series", NULL }; /* aligns with models array; expected min firmware size */ const unsigned int firmware_minsize[] = { 0x100000, 0x100000, 0x200000 }; /* aligns with models array; expected max firmware size */ const unsigned int firmware_maxsize[] = { 0x200000, 0x200000, 0x400000 }; const unsigned char header[][16] = { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0x20, 0x03, 0x08, 0x27, 0x24, 0x00, 0x02, 0x30, 0x19, 0x17, 0x65, 0x73, 0x85, 0x32, 0x83, 0x22 }, { 0x20, 0x04, 0x03, 0x27, 0x20, 0x50, 0x01, 0x70, 0x80, 0x30, 0x80, 0x06, 0x30, 0x19, 0x17, 0x65 } }; static int testheader( const unsigned char * const data ) { const unsigned char * const d = data+16; const char * const * m = models; int index = 0; while( *m ) { if( memcmp( header[ index ], d, 16 ) == 0 ) return index; index++; m++; }; return -1; }; static void modifyheader( unsigned char * data ) { const unsigned char * h = header_modify; int i; for( i=0; i<512; i++ ) { if( *h == '\0' ) h = header_modify; *data++ ^= *h++; }; }; static FILE * openinfile( const TCHAR * filename ) { FILE * F = _tfopen( filename, TEXT("rb") ); if( F == NULL ) { fprintf( stderr, "Couldn't open input file %s\n", filename ); perror( "Error was " ); }; return F; }; static FILE * openoutfile( const TCHAR * filename ) { FILE * F = _tfopen( filename, TEXT("wb") ); if( F == NULL ) { fprintf( stderr, "Couldn't open output file %s\n", filename ); perror( "Error was " ); }; return F; }; int iriver_decode(TCHAR *infile_name, TCHAR *outfile_name, unsigned int modify, enum striptype stripmode ) { FILE * infile = NULL; FILE * outfile = NULL; int i = -1; unsigned char headerdata[512]; unsigned int dwLength1, dwLength2, dwLength3, fp = 0; unsigned int minsize, maxsize, sizes[2]; unsigned char blockdata[16+16]; unsigned char out[16]; unsigned char newmunge; signed long lenread; int s = 0; unsigned char * pChecksums, * ppChecksums = 0; unsigned char ck; infile = openinfile(infile_name); outfile = openoutfile(outfile_name); if (!infile || !outfile) return -1; lenread = fread( headerdata, 1, 512, infile ); if( lenread != 512 ) { fprintf( stderr, "This doesn't look like a valid encrypted iHP " "firmware - reason: header length\n" ); goto error; }; i = testheader( headerdata ); if( i == -1 ) { fprintf( stderr, "This firmware is for an unknown model, or is not" " a valid encrypted iHP firmware\n" ); goto error; }; fprintf( stderr, "Model %s\n", models[ i ] ); dwLength1 = headerdata[0] | (headerdata[1]<<8) | (headerdata[2]<<16) | (headerdata[3]<<24); dwLength2 = headerdata[4] | (headerdata[5]<<8) | (headerdata[6]<<16) | (headerdata[7]<<24); dwLength3 = headerdata[8] | (headerdata[9]<<8) | (headerdata[10]<<16) | (headerdata[11]<<24); if( dwLength2 > dwLength1 || dwLength3 > dwLength1 || dwLength2>>9 != dwLength3 || dwLength2+dwLength3+512 != dwLength1 ) { fprintf( stderr, "This doesn't look like a valid encrypted " "iHP firmware - reason: file 'length' data\n" ); goto error; }; minsize = firmware_minsize[i]; maxsize = firmware_maxsize[i]; sizes[0] = sizes[1] = 0; pChecksums = ppChecksums = (unsigned char *)( malloc( dwLength3 ) ); if( modify ) { modifyheader( headerdata ); }; if( stripmode == STRIP_NONE ) fwrite( headerdata, 512, 1, outfile ); memset( blockdata, 0, 16 ); ck = 0; while( ( fp < dwLength2 ) && ( lenread = fread( blockdata+16, 1, 16, infile ) ) == 16 ) { fp += 16; for( i=0; i<16; ++i ) { newmunge = blockdata[16+i] ^ munge[i]; out[i] = newmunge ^ blockdata[i]; blockdata[i] = newmunge; ck += out[i]; } if (fp <= 32) sizes[fp / 16 - 1] = (out[0] << 24) | (out[1] << 16) | (out[2] << 8) | (out[3] << 0); if( fp > ESTF_SIZE || stripmode != STRIP_HEADER_CHECKSUM_ESTF ) { fwrite( out+4, 1, 12, outfile ); fwrite( out, 1, 4, outfile ); } else { if( ESTF_SIZE - fp < 16 ) { memcpy( out+4, blockdata+16, 12 ); memcpy( out, blockdata+28, 4 ); fwrite( blockdata+16+ESTF_SIZE-fp, 1, ESTF_SIZE-fp, outfile ); } } if( s == 496 ) { s = 0; memset( blockdata, 0, 16 ); *ppChecksums++ = ck; ck = 0; } else s+=16; }; if( sizes[0] < minsize || sizes[1] < minsize || sizes[0] > maxsize || sizes[1] > maxsize ) { fprintf( stderr, "This doesn't look like a valid encrypted " "iHP firmware - reason: ESTFBINR 'length' data\n" ); goto error; }; if( fp != dwLength2 ) { fprintf( stderr, "This doesn't look like a valid encrypted " "iHP firmware - reason: 'length2' mismatch\n" ); goto error; }; fp = 0; ppChecksums = pChecksums; while( ( fp < dwLength3 ) && ( lenread = fread( blockdata, 1, 32, infile ) ) > 0 ) { fp += lenread; if( stripmode == STRIP_NONE ) fwrite( blockdata, 1, lenread, outfile ); if( memcmp( ppChecksums, blockdata, lenread ) != 0 ) { fprintf( stderr, "This doesn't look like a valid encrypted " "iHP firmware - reason: Checksum mismatch!" ); goto error; }; ppChecksums += lenread; }; if( fp != dwLength3 ) { fprintf( stderr, "This doesn't look like a valid encrypted " "iHP firmware - reason: 'length3' mismatch\n" ); goto error; }; fprintf( stderr, "File decoded correctly and all checksums matched!\n" ); switch( stripmode ) { default: case STRIP_NONE: fprintf(stderr, "Output file contains all headers and " "checksums\n"); break; case STRIP_HEADER_CHECKSUM: fprintf( stderr, "NB: output file contains only ESTFBINR header" " and decoded firmware code\n" ); break; case STRIP_HEADER_CHECKSUM_ESTF: fprintf( stderr, "NB: output file contains only raw decoded " "firmware code\n" ); break; }; fclose(infile); fclose(outfile); return 0; error: fclose(infile); fclose(outfile); return -1; }; int iriver_encode(TCHAR *infile_name, TCHAR *outfile_name, unsigned int modify ) { FILE * infile = NULL; FILE * outfile = NULL; int i = -1; unsigned char headerdata[512]; unsigned int dwLength1, dwLength2, dwLength3, fp = 0; unsigned int minsize, maxsize, sizes[2]; unsigned char blockdata[16+16]; unsigned char out[16]; unsigned char newmunge; signed long lenread; int s = 0; unsigned char * pChecksums, * ppChecksums; unsigned char ck; enum striptype stripmode = STRIP_NONE; infile = openinfile(infile_name); outfile = openoutfile(outfile_name); if (!infile || !outfile) return -1; lenread = fread( headerdata, 1, 512, infile ); if( lenread != 512 ) { fprintf( stderr, "This doesn't look like a valid decoded " "iHP firmware - reason: header length\n" ); goto error; }; if( modify ) { modifyheader( headerdata ); /* reversible */ }; i = testheader( headerdata ); if( i == -1 ) { fprintf( stderr, "This firmware is for an unknown model, or is not" " a valid decoded iHP firmware\n" ); goto error; }; fprintf( stderr, "Model %s\n", models[ i ] ); dwLength1 = headerdata[0] | (headerdata[1]<<8) | (headerdata[2]<<16) | (headerdata[3]<<24); dwLength2 = headerdata[4] | (headerdata[5]<<8) | (headerdata[6]<<16) | (headerdata[7]<<24); dwLength3 = headerdata[8] | (headerdata[9]<<8) | (headerdata[10]<<16) | (headerdata[11]<<24); if( dwLength2 > dwLength1 || dwLength3 > dwLength1 || dwLength2+dwLength3+512 != dwLength1 ) { fprintf( stderr, "This doesn't look like a valid decoded iHP" " firmware - reason: file 'length' data\n" ); goto error; }; minsize = firmware_minsize[i]; maxsize = firmware_maxsize[i]; sizes[0] = sizes[1] = 0; pChecksums = ppChecksums = (unsigned char *)( malloc( dwLength3 ) ); fwrite( headerdata, 512, 1, outfile ); memset( blockdata, 0, 16 ); ck = 0; while( ( fp < dwLength2 ) && ( lenread = fread( blockdata+16, 1, 16, infile ) ) == 16 ) { fp += 16; if (fp <= 32) sizes[fp / 16 - 1] = (blockdata[28] << 24) | (blockdata[29] << 16) | (blockdata[30] << 8) | (blockdata[31] << 0); for( i=0; i<16; ++i ) { newmunge = blockdata[16+((12+i)&0xf)] ^ blockdata[i]; out[i] = newmunge ^ munge[i]; ck += blockdata[16+i]; blockdata[i] = newmunge; }; fwrite( out, 1, 16, outfile ); if( s == 496 ) { s = 0; memset( blockdata, 0, 16 ); *ppChecksums++ = ck; ck = 0; } else s+=16; }; if( sizes[0] < minsize || sizes[1] < minsize || sizes[0] > maxsize || sizes[1] > maxsize ) { fprintf( stderr, "This doesn't look like a valid decoded iHP" " firmware - reason: ESTFBINR 'length' data\n" ); goto error; }; if( fp != dwLength2 ) { fprintf( stderr, "This doesn't look like a valid decoded " "iHP firmware - reason: 'length1' mismatch\n" ); goto error; }; /* write out remainder w/out applying descrambler */ fp = 0; lenread = dwLength3; ppChecksums = pChecksums; while( ( fp < dwLength3) && ( lenread = fwrite( ppChecksums, 1, lenread, outfile ) ) > 0 ) { fp += lenread; ppChecksums += lenread; lenread = dwLength3 - fp; }; if( fp != dwLength3 ) { fprintf( stderr, "This doesn't look like a valid decoded " "iHP firmware - reason: 'length2' mismatch\n" ); goto error; }; fprintf( stderr, "File encoded successfully and checksum table built!\n" ); fclose(infile); fclose(outfile); return 0; error: fclose(infile); fclose(outfile); return -1; };