1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
|
/**
* \file fwupgrade.c
*
* This firmware extraction and flashing program was the result
* of combining finds made by "xchip" from the Nomadness reverse-
* engineering forum with the sniffer logs made for duplicating
* the firmware flashing process.
*
* "xchip" found out how to remove the XOR scrambling and scan for
* the zlib compressed image inside a firmware file from Creative.
*
*/
#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);
}
/**
* This function reads in the firmware to a largeish buffer.
*/
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;
}
/**
* This prints a UCS2 unicode string.
*/
static void ucs2_printf(unsigned char *str) {
while (!str[0] == 0x00 || !str[1] == 0x00) {
printf("%c", str[1]);
str += 2;
}
}
/**
* This function will analyze a firmware image
* and print out some information about it.
*/
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;
}
}
/**
* Here we call zlib to decompress the firmware image.
*/
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;
}
}
/**
* Here we write out the uncompressed file to disk.
*/
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
/**
* Returns 0 if OK (yes), 1 if not OK (no)
*/
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;
}
|