forked from len0rd/rockbox
Added fw modifications required to unpack real world player dumps. Documented more fwu header fields, magic numbers and finite field arithmetics (extended Euclidean for inverse, long division for reducing modulo field_poly). v3 encryption used is standard RC4 with the key additionally ciphered by the Elliptic Curve Integrated Encryption Scheme. Either sect233k1 (NIST K-233) or sect163r2 (NIST B-163) curves can be used, with the former overwhelmingly prevailing, being hardwired in SDK's maker.exe. Using a private/public key scheme is superfluous because both are stored in the firmware, with the added level of complexity likely serving the purpose of obfuscation. The private key is generated at random with each invokation. None of KDF or MAC from ECIES are used, RC4 key is directly xored with the shared secret. The random number r used to calculate rG isn't stored, but that's unimportant since only krG == rkG is actually used in the encryption. Change-Id: Ieacf8cc744bc90c7c5582dd724b2c10a41bfc191
287 lines
7.7 KiB
C
287 lines
7.7 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2012 Amaury Pouly
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <sys/stat.h>
|
|
#include <zlib.h>
|
|
#include "misc.h"
|
|
#include "fwu.h"
|
|
#include "afi.h"
|
|
#include "fw.h"
|
|
|
|
bool g_debug = false;
|
|
char *g_out_prefix = NULL;
|
|
char *g_in_file = NULL;
|
|
|
|
/* [add]: string to add when there is no extension
|
|
* [replace]: string to replace extension */
|
|
static void build_out_prefix(char *add, char *replace, bool slash)
|
|
{
|
|
if(g_out_prefix)
|
|
return;
|
|
/** copy input filename with extra space */
|
|
g_out_prefix = malloc(strlen(g_in_file) + strlen(add) + 16);
|
|
strcpy(g_out_prefix, g_in_file);
|
|
/** remove extension and add '/' */
|
|
char *filename = strrchr(g_out_prefix, '/');
|
|
// have p points to the beginning or after the last '/'
|
|
filename = (filename == NULL) ? g_out_prefix : filename + 1;
|
|
// extension ?
|
|
char *dot = strrchr(filename, '.');
|
|
if(dot)
|
|
{
|
|
*dot = 0; // cut at the dot
|
|
strcat(dot, replace);
|
|
}
|
|
else
|
|
strcat(filename, add); // add extra string
|
|
|
|
if(slash)
|
|
{
|
|
strcat(filename, "/");
|
|
/** make sure the directory exists */
|
|
mkdir(g_out_prefix, S_IRWXU | S_IRGRP | S_IROTH);
|
|
}
|
|
}
|
|
|
|
static int do_fwu(uint8_t *buf, size_t size, enum fwu_mode_t mode)
|
|
{
|
|
int ret = fwu_decrypt(buf, &size, mode);
|
|
if(ret != 0)
|
|
return ret;
|
|
|
|
build_out_prefix(".afi", ".afi", false);
|
|
cprintf(GREY, "Descrambling to %s... ", g_out_prefix);
|
|
FILE *f = fopen(g_out_prefix, "wb");
|
|
if(f)
|
|
{
|
|
fwrite(buf, size, 1, f);
|
|
fclose(f);
|
|
cprintf(RED, "Ok\n");
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
color(RED);
|
|
perror("Failed");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int unpack_afi_fw_cb(const char *filename, uint8_t *buf, size_t size)
|
|
{
|
|
char *name = malloc(strlen(g_out_prefix) + strlen(filename) + 16);
|
|
sprintf(name, "%s%s", g_out_prefix, filename);
|
|
|
|
cprintf(GREY, "Unpacking to %s... ", name);
|
|
FILE *f = fopen(name, "wb");
|
|
if(f)
|
|
{
|
|
if (0 != memcmp(buf, "\x1f\x8b\x8\0\0\0\0\0\0\xb", 10))
|
|
fwrite(buf, size, 1, f);
|
|
else
|
|
{
|
|
uint8_t buf_out[8192];
|
|
z_stream zs;
|
|
int err = Z_OK;
|
|
cprintf(GREEN, "inflating... ");
|
|
memset(&zs, 0, sizeof(zs));
|
|
zs.next_in = buf + 10;
|
|
zs.avail_in = size - 10;
|
|
inflateInit2(&zs, -MAX_WBITS); /* raw */
|
|
while (err == Z_OK)
|
|
{
|
|
zs.next_out = buf_out;
|
|
zs.avail_out = sizeof(buf_out);
|
|
err = inflate(&zs, Z_NO_FLUSH);
|
|
fwrite(buf_out, 1, sizeof(buf_out) - zs.avail_out, f);
|
|
}
|
|
}
|
|
fclose(f);
|
|
cprintf(RED, "Ok\n");
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
color(RED);
|
|
perror("Failed");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int do_afi(uint8_t *buf, size_t size)
|
|
{
|
|
build_out_prefix(".fw", "", true);
|
|
return afi_unpack(buf, size, &unpack_afi_fw_cb);
|
|
}
|
|
|
|
static int do_fw(uint8_t *buf, size_t size, bool big_endian)
|
|
{
|
|
build_out_prefix(".unpack", "", true);
|
|
return fw_unpack(buf, size, &unpack_afi_fw_cb, big_endian);
|
|
}
|
|
static void usage(void)
|
|
{
|
|
printf("Usage: atjboottool [options] firmware\n");
|
|
printf("Options:\n");
|
|
printf(" -o <path> Set output file or output prefix\n");
|
|
printf(" -h/--help Display this message\n");
|
|
printf(" -d/--debug Display debug messages\n");
|
|
printf(" -c/--no-color Disable color output\n");
|
|
printf(" --fwu Unpack a FWU firmware file\n");
|
|
printf(" --afi Unpack a AFI archive file\n");
|
|
printf(" --fw Unpack a FW archive file\n");
|
|
printf(" --fw251 Big-endian FW archive used on Flip80251\n");
|
|
printf(" --atj2127 Force ATJ2127 decryption mode\n");
|
|
printf("The default is to try to guess the format.\n");
|
|
printf("If several formats are specified, all are tried.\n");
|
|
printf("If no output prefix is specified, a default one is picked.\n");
|
|
exit(1);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
bool try_fwu = false;
|
|
bool try_afi = false;
|
|
bool try_fw = false;
|
|
bool big_endian = false;
|
|
enum fwu_mode_t fwu_mode = FWU_AUTO;
|
|
|
|
while(1)
|
|
{
|
|
static struct option long_options[] =
|
|
{
|
|
{"help", no_argument, 0, 'h'},
|
|
{"debug", no_argument, 0, 'd'},
|
|
{"no-color", no_argument, 0, 'c'},
|
|
{"fwu", no_argument, 0, 'u'},
|
|
{"afi", no_argument, 0, 'a'},
|
|
{"fw", no_argument, 0, 'w'},
|
|
{"fw251", no_argument, 0, 'b'},
|
|
{"atj2127", no_argument, 0, '2'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
int c = getopt_long(argc, argv, "hdco:a2b", long_options, NULL);
|
|
if(c == -1)
|
|
break;
|
|
switch(c)
|
|
{
|
|
case -1:
|
|
break;
|
|
case 'c':
|
|
enable_color(false);
|
|
break;
|
|
case 'd':
|
|
g_debug = true;
|
|
break;
|
|
break;
|
|
case 'h':
|
|
usage();
|
|
break;
|
|
case 'o':
|
|
g_out_prefix = optarg;
|
|
break;
|
|
case 'a':
|
|
try_afi = true;
|
|
break;
|
|
case 'u':
|
|
try_fwu = true;
|
|
break;
|
|
case 'w':
|
|
try_fw = true;
|
|
break;
|
|
case 'b':
|
|
try_fw = true;
|
|
big_endian = true;
|
|
break;
|
|
case '2':
|
|
fwu_mode = FWU_ATJ2127;
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
|
|
if(argc - optind != 1)
|
|
{
|
|
usage();
|
|
return 1;
|
|
}
|
|
|
|
g_in_file = argv[optind];
|
|
FILE *fin = fopen(g_in_file, "r");
|
|
if(fin == NULL)
|
|
{
|
|
perror("Cannot open boot file");
|
|
return 1;
|
|
}
|
|
fseek(fin, 0, SEEK_END);
|
|
long size = ftell(fin);
|
|
fseek(fin, 0, SEEK_SET);
|
|
|
|
void *buf = malloc(size);
|
|
if(buf == NULL)
|
|
{
|
|
perror("Cannot allocate memory");
|
|
return 1;
|
|
}
|
|
|
|
if(fread(buf, size, 1, fin) != 1)
|
|
{
|
|
perror("Cannot read file");
|
|
return 1;
|
|
}
|
|
|
|
fclose(fin);
|
|
|
|
int ret = -99;
|
|
if(try_fwu || fwu_check(buf, size))
|
|
ret = do_fwu(buf, size, fwu_mode);
|
|
else if(try_afi || afi_check(buf, size))
|
|
ret = do_afi(buf, size);
|
|
else if(try_fw || fw_check(buf, size))
|
|
ret = do_fw(buf, size, big_endian);
|
|
else
|
|
{
|
|
cprintf(GREY, "No valid format found\n");
|
|
ret = 1;
|
|
}
|
|
|
|
if(ret != 0)
|
|
{
|
|
cprintf(GREY, "Error: %d", ret);
|
|
printf("\n");
|
|
ret = 2;
|
|
}
|
|
free(buf);
|
|
|
|
color(OFF);
|
|
|
|
return ret;
|
|
}
|
|
|