libnjb: fwupgrade.c

libnjb 2.2.7

fwupgrade.c
#include "common.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
/* To handle .exe files, this must exist */
#if HAVE_ZLIB_H
#include <zlib.h>
#endif
/* You need this 10 MB buffer for this operation... */
#define BUFFERSIZE 10*1024*1024
static int progress (u_int64_t sent, u_int64_t total, const char* buf, unsigned len, void *data)
{
int percent = (sent*100)/total;
#ifdef __WIN32__
printf("Progress: %I64u of %I64u (%d%%)\r", sent, total, percent);
#else
printf("Progress: %llu of %llu (%d%%)\r", sent, total, percent);
#endif
fflush(stdout);
return 0;
}
static void usage (void)
{
fprintf(stderr, "usage: fwupgrade [ -D<debuglvl> ] <path>\n");
exit(1);
}
static int read_in_fw(char *path, unsigned char *buffer) {
/* File descriptor and pointer */
int fd;
size_t bread;
/* Read in the firmware file */
#ifdef __WIN32__
if ( (fd = open(path, O_RDONLY|O_BINARY)) == -1 ) {
#else
if ( (fd = open(path, O_RDONLY)) == -1 ) {
#endif
printf("Could not open firmware file descriptor.\n");
return -1;
}
bread = read(fd, buffer, BUFFERSIZE);
if (bread < 0) {
printf("Error while reading firmware file.\n");
close(fd);
return -1;
}
if (bread == BUFFERSIZE) {
printf("Warning: this firmware file is very large.\n");
printf("It probably cannot be properly decoded.\n");
}
close(fd);
printf("Read in a firmware file of size 0x%x.\n", bread);
if (bread < 1024) {
printf("Ridiculously small firmware file. Aborting.\n");
return -1;
}
return bread;
}
static void ucs2_printf(unsigned char *str) {
while (!str[0] == 0x00 || !str[1] == 0x00) {
printf("%c", str[1]);
str += 2;
}
}
static void analyze_firmware(unsigned char *buffer) {
if (buffer[0] == 'C' && buffer[1] == 'I' &&
buffer[2] == 'F' && buffer[3] == 'F') {
u_int32_t cifflen;
u_int32_t offset = 0;
cifflen = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
printf("Firmware CIFF image, %08x bytes:\n", cifflen);
/* Wind past header */
offset = 8;
printf(" Offset: Type: Size:\n");
while (offset < cifflen) {
char secname[5];
u_int32_t seclen;
secname[0] = buffer[offset];
secname[1] = buffer[offset+1];
secname[2] = buffer[offset+2];
secname[3] = buffer[offset+3];
secname[4] = '\0';
seclen = (buffer[offset+4] << 24) | (buffer[offset+5] << 16) |
(buffer[offset+6] << 8) | buffer[offset+7];
printf(" %08x %s %08x bytes", offset, secname, seclen);
if (!strcmp(secname, "CINF") || !strcmp(secname, "DATA")) {
printf(" \"");
ucs2_printf(&buffer[offset+8]);
printf("\"");
}
printf("\n");
offset += 8;
offset += seclen;
}
} else {
printf("Unknown firmware image type.\n");
}
}
#if HAVE_ZLIB_H
static void dexor_fw_image(unsigned char *buffer,
size_t zimglen,
unsigned char *key,
u_int8_t keylen) {
register u_int8_t j = 0;
register u_int32_t i;
register unsigned char c;
printf("Dexor with key: ");
for (i = 0; i < keylen; i++) {
printf("%02x ", key[i]);
}
printf("\n");
for (i = 0; i < zimglen; i++) {
c = key[j] | 0x80;
buffer[i] = buffer[i] ^ c;
j = (i+1) % keylen;
}
}
static void decompress_fw_image(unsigned char *compressed, size_t compressed_len,
unsigned char **decompressed, size_t *rawlen) {
/* Allocate another big buffer */
*decompressed = (unsigned char *) malloc(BUFFERSIZE);
if (*decompressed == NULL) {
*rawlen = 0;
return;
}
*rawlen = BUFFERSIZE;
if (uncompress(*decompressed, (uLongf*) rawlen, compressed, compressed_len) != Z_OK) {
*rawlen = 0;
return;
}
}
static int write_fw_file(char *path,
unsigned char *decompressed,
size_t rawlen) {
int fd = -1;
#ifdef __WIN32__
if ( (fd = open(path, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0664)) == -1 ) {
#else
if ( (fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0664)) == -1 ) {
#endif
printf("Could not open uncompressed file.\n");
return -1;
}
if (write(fd, decompressed, rawlen) == -1) {
printf("Error while writing uncompressed file.\n");
close(fd);
unlink(path);
return -1;
}
close(fd);
return 0;
}
#endif
static int prompt()
{
char buff[2];
while (1) {
fprintf(stdout, "> ");
if ( fgets(buff, sizeof(buff), stdin) == NULL ) {
if (ferror(stdin)) {
fprintf(stderr, "File error on stdin\n");
} else {
fprintf(stderr, "EOF on stdin\n");
}
return 1;
}
if (buff[0] == 'y') {
return 0;
} else if (buff[0] == 'n') {
return 1;
}
}
}
int main(int argc, char **argv)
{
njb_t njbs[NJB_MAX_DEVICES], *njb;
int n, opt, debug;
extern int optind;
extern char *optarg;
char *path;
char *sendpath;
char *lang;
unsigned char *buffer;
size_t bread;
debug = 0;
while ( (opt = getopt(argc, argv, "D:")) != -1 ) {
switch (opt) {
case 'D':
debug = atoi(optarg);
break;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if ( argc != 1 ) usage();
if ( debug ) NJB_Set_Debug(debug);
/*
* Check environment variables $LANG and $LC_CTYPE
* to see if we want to support UTF-8 unicode
* $LANG = "xx_XX.UTF-8" or $LC_CTYPE = "?"
* trigger unicode support.
*/
lang = getenv("LANG");
if (lang != NULL) {
if (strlen(lang) > 5) {
if (!strcmp(&lang[strlen(lang)-5], "UTF-8")) {
NJB_Set_Unicode(NJB_UC_UTF8);
}
}
}
path = argv[0];
printf("Analyzing firmware file %s...\n", path);
/* Allocate a buffer */
buffer = (unsigned char *) malloc(BUFFERSIZE);
if (buffer == NULL) {
printf("Could not allocate a firmware scanning buffer.\n");
return 1;
}
/* Read in to buffer */
bread = read_in_fw(path, buffer);
if (bread == -1) {
return 1;
}
/*
* See if this is a RAW (dexored) firmware image, all
* firmware images starts with the string "CIFF"
*/
if (buffer[0] == 'C' && buffer[1] == 'I' &&
buffer[2] == 'F' && buffer[3] == 'F') {
printf("This seems to be a raw (dexored) firmware image.\n");
sendpath = path;
} else {
#if HAVE_ZLIB_H
char known_key[] = "SamBanDam";
char uncompressed_file[] = "tempimage.bin";
/* Length of the zlib compressed image */
unsigned int zimglen;
/* Pointer into file buffer */
unsigned int offset = 0;
unsigned char *decompressed;
size_t rawlen;
size_t i;
printf("This could be a zlib compressed windows executable.\n");
/* Scanning for zlib header */
printf("Scanning for zlib header...\n");
for (i = 0; i < bread-3; i++) {
if (
// Old Key
(buffer[i] == 0xAB && buffer[i+1] == 0x3B && buffer[i+2] == 0x01) ||
// New Key
(buffer[i] == 0xCA && buffer[i+1] == 0x69 && buffer[i+2] == 0x0F)
)
{
offset = i-4;
break;
}
}
if (offset == 0) {
printf("Could not locate a zlib header in this firmware file.\n");
return 1;
} else {
printf("Found zlib header at file position 0x%x.\n", offset);
}
zimglen = buffer[offset+3] << 24 | buffer[offset+2] << 16
| buffer[offset+1] << 8 | buffer[offset];
printf("Zlib compressed image length: 0x%x\n", zimglen);
if (zimglen > bread-offset) {
printf("Inconsistent length of zlib compressed image, aborting.\n");
return 1;
}
/* Decompress zlib image using Zlib */
printf("Calling zlib uncompress() on raw chunks...\n");
decompress_fw_image(&buffer[offset+4], zimglen, &decompressed, &rawlen);
if (rawlen == 0) {
printf("Failed.\n");
/* "Dexor" the firmware image */
printf("\"Dexoring\" firmware zlib image with a known key...\n");
dexor_fw_image(&buffer[offset+4], zimglen, known_key, strlen(known_key));
/* Decompress zlib image using Zlib */
printf("Calling zlib uncompress()...\n");
decompress_fw_image(&buffer[offset+4], zimglen, &decompressed, &rawlen);
if (rawlen == 0) {
printf("Could not dexor the firmware :(\nAborting.\n");
exit(1);
}
}
printf("Decompressed image size: 0x%x\n", rawlen);
if (decompressed[0] == 'C' && decompressed[1] == 'I' &&
decompressed[2] == 'F' && decompressed[3] == 'F') {
printf("The extracted image looks like a firmware file.\n");
} else {
printf("The extracted image does not look like a firmware file.\n");
printf("Aborting.\n");
return 1;
}
/* Write out decompressed file */
printf("Writing out the extracted image to disk as \"%s\".\n", uncompressed_file);
if (write_fw_file(uncompressed_file, decompressed, rawlen) == -1) {
printf("Failed to write uncompressed file. Aborting.\n");
return 1;
}
sendpath = uncompressed_file;
/* Exchange buffer for the decompressed image */
free(buffer);
buffer = decompressed;
#else
printf("This may be an zlib compressed .exe file firmware.\n");
printf("You must compile the \"fwupgrade\" program on a system\n");
printf("which has the zlib library and headers properly installed\n");
printf("to enable the compression of zlib compressed firmware images.\n");
#endif
}
/* Make some more information available on this firmware */
analyze_firmware(buffer);
/* Free firmware read buffer */
free(buffer);
printf("Sending firmware file to jukebox\n");
if (NJB_Discover(njbs, 0, &n) == -1) {
fprintf(stderr, "could not locate any jukeboxes\n");
return 1;
}
if ( n == 0 ) {
fprintf(stderr, "no NJB devices found\n");
return 0;
}
njb = njbs;
if ( NJB_Open(njb) == -1 ) {
NJB_Error_Dump(njb,stderr);
return 1;
}
NJB_Capture(njb);
printf("I will now send the firmware to your device.\n");
printf("Continue? (y/n)\n");
if (prompt() == 0) {
if ( NJB_Send_Firmware(njb, sendpath, progress, NULL) == -1 ) {
NJB_Error_Dump(njb,stderr);
} else {
printf("\nFirmware upload complete.");
}
printf("\n");
} else {
printf("Aborted.\n");
}
NJB_Release(njb);
NJB_Close(njb);
return 0;
}