libnjb: fwupgrade.c

- Main Page
- Modules
- Data Structures
- Files
- Directories
- Examples

# fwupgrade.c

    00001
    00013 #include "common.h"
    00014 #include <sys/stat.h>
    00015 #include <sys/types.h>
    00016 #include <fcntl.h>
    00017 #include <string.h>
    00018 /* To handle .exe files, this must exist */
    00019 #if HAVE_ZLIB_H
    00020 #include <zlib.h>
    00021 #endif
    00022 /* You need this 10 MB buffer for this operation... */
    00023 #define BUFFERSIZE 10*1024*1024
    00024
    00025 static int progress (u_int64_t sent, u_int64_t total, const char* buf, unsigned len, void *data)
    00026 {
    00027   int percent = (sent*100)/total;
    00028 #ifdef __WIN32__
    00029   printf("Progress: %I64u of %I64u (%d%%)\r", sent, total, percent);
    00030 #else
    00031   printf("Progress: %llu of %llu (%d%%)\r", sent, total, percent);
    00032 #endif
    00033   fflush(stdout);
    00034   return 0;
    00035 }
    00036
    00037 static void usage (void)
    00038 {
    00039   fprintf(stderr, "usage: fwupgrade [ -D<debuglvl> ] <path>\n");
    00040   exit(1);
    00041 }
    00042
    00046 static int read_in_fw(char *path, unsigned char *buffer) {
    00047   /* File descriptor and pointer */
    00048   int fd;
    00049   size_t bread;
    00050
    00051   /* Read in the firmware file */
    00052 #ifdef __WIN32__
    00053   if ( (fd = open(path, O_RDONLY|O_BINARY)) == -1 ) {
    00054 #else
    00055   if ( (fd = open(path, O_RDONLY)) == -1 ) {
    00056 #endif
    00057     printf("Could not open firmware file descriptor.\n");
    00058     return -1;
    00059   }
    00060   bread = read(fd, buffer, BUFFERSIZE);
    00061   if (bread < 0) {
    00062     printf("Error while reading firmware file.\n");
    00063     close(fd);
    00064     return -1;
    00065   }
    00066
    00067   if (bread == BUFFERSIZE) {
    00068     printf("Warning: this firmware file is very large.\n");
    00069     printf("It probably cannot be properly decoded.\n");
    00070   }
    00071   close(fd);
    00072   printf("Read in a firmware file of size 0x%x.\n", bread);
    00073   if (bread < 1024) {
    00074     printf("Ridiculously small firmware file. Aborting.\n");
    00075     return -1;
    00076   }
    00077   return bread;
    00078 }
    00079
    00083 static void ucs2_printf(unsigned char *str) {
    00084   while (!str[0] == 0x00 || !str[1] == 0x00) {
    00085     printf("%c", str[1]);
    00086     str += 2;
    00087   }
    00088 }
    00089
    00094 static void analyze_firmware(unsigned char *buffer) {
    00095   if (buffer[0] == 'C' && buffer[1] == 'I' &&
    00096       buffer[2] == 'F' && buffer[3] == 'F') {
    00097     u_int32_t cifflen;
    00098     u_int32_t offset = 0;
    00099
    00100     cifflen = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
    00101     printf("Firmware CIFF image, %08x bytes:\n", cifflen);
    00102
    00103     /* Wind past header */
    00104     offset = 8;
    00105
    00106     printf("   Offset:   Type:  Size:\n");
    00107     while (offset < cifflen) {
    00108       char secname[5];
    00109       u_int32_t seclen;
    00110
    00111       secname[0] = buffer[offset];
    00112       secname[1] = buffer[offset+1];
    00113       secname[2] = buffer[offset+2];
    00114       secname[3] = buffer[offset+3];
    00115       secname[4] = '\0';
    00116
    00117       seclen = (buffer[offset+4] << 24) | (buffer[offset+5] << 16) |
    00118         (buffer[offset+6] << 8) | buffer[offset+7];
    00119
    00120       printf("   %08x  %s   %08x bytes", offset, secname, seclen);
    00121
    00122       if (!strcmp(secname, "CINF") || !strcmp(secname, "DATA")) {
    00123         printf(" \"");
    00124         ucs2_printf(&buffer[offset+8]);
    00125         printf("\"");
    00126       }
    00127       printf("\n");
    00128
    00129       offset += 8;
    00130       offset += seclen;
    00131     }
    00132   } else {
    00133     printf("Unknown firmware image type.\n");
    00134   }
    00135 }
    00136
    00137 #if HAVE_ZLIB_H
    00138 static void dexor_fw_image(unsigned char *buffer,
    00139                            size_t zimglen,
    00140                            unsigned char *key,
    00141                            u_int8_t keylen) {
    00142   register u_int8_t j = 0;
    00143   register u_int32_t i;
    00144   register unsigned char c;
    00145
    00146   printf("Dexor with key: ");
    00147   for (i = 0; i < keylen; i++) {
    00148     printf("%02x ", key[i]);
    00149   }
    00150   printf("\n");
    00151   for (i = 0; i < zimglen; i++) {
    00152     c = key[j] | 0x80;
    00153     buffer[i] = buffer[i] ^ c;
    00154     j = (i+1) % keylen;
    00155   }
    00156 }
    00157
    00161 static void decompress_fw_image(unsigned char *compressed, size_t compressed_len,
    00162                          unsigned char **decompressed, size_t *rawlen) {
    00163   /* Allocate another big buffer */
    00164   *decompressed = (unsigned char *) malloc(BUFFERSIZE);
    00165   if (*decompressed == NULL) {
    00166     *rawlen = 0;
    00167     return;
    00168   }
    00169   *rawlen = BUFFERSIZE;
    00170   if (uncompress(*decompressed, (uLongf*) rawlen, compressed, compressed_len) != Z_OK) {
    00171     *rawlen = 0;
    00172     return;
    00173   }
    00174 }
    00175
    00179 static int write_fw_file(char *path,
    00180                   unsigned char *decompressed,
    00181                   size_t rawlen) {
    00182   int fd = -1;
    00183
    00184 #ifdef __WIN32__
    00185    if ( (fd = open(path, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0664)) == -1 ) {
    00186 #else
    00187    if ( (fd = open(path, O_CREAT|O_TRUNC|O_WRONLY, 0664)) == -1 ) {
    00188 #endif
    00189      printf("Could not open uncompressed file.\n");
    00190      return -1;
    00191    }
    00192    if (write(fd, decompressed, rawlen) == -1) {
    00193      printf("Error while writing uncompressed file.\n");
    00194      close(fd);
    00195      unlink(path);
    00196      return -1;
    00197    }
    00198    close(fd);
    00199    return 0;
    00200 }
    00201 #endif
    00202
    00206 static int prompt()
    00207 {
    00208   char buff[2];
    00209
    00210   while (1) {
    00211     fprintf(stdout, "> ");
    00212     if ( fgets(buff, sizeof(buff), stdin) == NULL ) {
    00213       if (ferror(stdin)) {
    00214         fprintf(stderr, "File error on stdin\n");
    00215       } else {
    00216         fprintf(stderr, "EOF on stdin\n");
    00217       }
    00218       return 1;
    00219     }
    00220     if (buff[0] == 'y') {
    00221       return 0;
    00222     } else if (buff[0] == 'n') {
    00223       return 1;
    00224     }
    00225   }
    00226 }
    00227
    00228 int main(int argc, char **argv)
    00229 {
    00230   njb_t njbs[NJB_MAX_DEVICES], *njb;
    00231   int n, opt, debug;
    00232   extern int optind;
    00233   extern char *optarg;
    00234   char *path;
    00235   char *sendpath;
    00236   char *lang;
    00237   unsigned char *buffer;
    00238   size_t bread;
    00239
    00240   debug = 0;
    00241   while ( (opt = getopt(argc, argv, "D:")) != -1 ) {
    00242     switch (opt) {
    00243     case 'D':
    00244       debug = atoi(optarg);
    00245       break;
    00246     default:
    00247       usage();
    00248       break;
    00249     }
    00250   }
    00251   argc -= optind;
    00252   argv += optind;
    00253
    00254   if ( argc != 1 ) usage();
    00255
    00256   if ( debug ) NJB_Set_Debug(debug);
    00257
    00258   /*
    00259    * Check environment variables $LANG and $LC_CTYPE
    00260    * to see if we want to support UTF-8 unicode
    00261    * $LANG = "xx_XX.UTF-8" or $LC_CTYPE = "?"
    00262    * trigger unicode support.
    00263    */
    00264   lang = getenv("LANG");
    00265   if (lang != NULL) {
    00266     if (strlen(lang) > 5) {
    00267       if (!strcmp(&lang[strlen(lang)-5], "UTF-8")) {
    00268         NJB_Set_Unicode(NJB_UC_UTF8);
    00269       }
    00270     }
    00271   }
    00272
    00273   path = argv[0];
    00274
    00275   printf("Analyzing firmware file %s...\n", path);
    00276
    00277   /* Allocate a buffer */
    00278   buffer = (unsigned char *) malloc(BUFFERSIZE);
    00279   if (buffer == NULL) {
    00280     printf("Could not allocate a firmware scanning buffer.\n");
    00281     return 1;
    00282   }
    00283
    00284   /* Read in to buffer */
    00285   bread = read_in_fw(path, buffer);
    00286   if (bread == -1) {
    00287     return 1;
    00288   }
    00289
    00290   /*
    00291    * See if this is a RAW (dexored) firmware image, all
    00292    * firmware images starts with the string "CIFF"
    00293    */
    00294   if (buffer[0] == 'C' && buffer[1] == 'I' &&
    00295       buffer[2] == 'F' && buffer[3] == 'F') {
    00296     printf("This seems to be a raw (dexored) firmware image.\n");
    00297     sendpath = path;
    00298   } else {
    00299 #if HAVE_ZLIB_H
    00300     char known_key[] = "SamBanDam";
    00301     char uncompressed_file[] = "tempimage.bin";
    00302     /* Length of the zlib compressed image */
    00303     unsigned int zimglen;
    00304     /* Pointer into file buffer */
    00305     unsigned int offset = 0;
    00306     unsigned char *decompressed;
    00307     size_t rawlen;
    00308     size_t i;
    00309
    00310     printf("This could be a zlib compressed windows executable.\n");
    00311
    00312     /* Scanning for zlib header */
    00313     printf("Scanning for zlib header...\n");
    00314     for (i = 0; i < bread-3; i++) {
    00315       if (
    00316           // Old Key
    00317           (buffer[i] == 0xAB && buffer[i+1] == 0x3B && buffer[i+2] == 0x01) ||
    00318           // New Key
    00319           (buffer[i] == 0xCA && buffer[i+1] == 0x69 && buffer[i+2] == 0x0F)
    00320           )
    00321         {
    00322           offset = i-4;
    00323           break;
    00324         }
    00325     }
    00326     if (offset == 0) {
    00327       printf("Could not locate a zlib header in this firmware file.\n");
    00328       return 1;
    00329     } else {
    00330       printf("Found zlib header at file position 0x%x.\n", offset);
    00331     }
    00332
    00333     zimglen = buffer[offset+3] << 24 | buffer[offset+2] << 16
    00334       | buffer[offset+1] << 8 | buffer[offset];
    00335     printf("Zlib compressed image length: 0x%x\n", zimglen);
    00336     if (zimglen > bread-offset) {
    00337       printf("Inconsistent length of zlib compressed image, aborting.\n");
    00338       return 1;
    00339     }
    00340
    00341     /* Decompress zlib image using Zlib */
    00342     printf("Calling zlib uncompress() on raw chunks...\n");
    00343     decompress_fw_image(&buffer[offset+4], zimglen, &decompressed, &rawlen);
    00344     if (rawlen == 0) {
    00345       printf("Failed.\n");
    00346
    00347       /* "Dexor" the firmware image */
    00348       printf("\"Dexoring\" firmware zlib image with a known key...\n");
    00349       dexor_fw_image(&buffer[offset+4], zimglen, known_key, strlen(known_key));
    00350
    00351       /* Decompress zlib image using Zlib */
    00352       printf("Calling zlib uncompress()...\n");
    00353       decompress_fw_image(&buffer[offset+4], zimglen, &decompressed, &rawlen);
    00354       if (rawlen == 0) {
    00355         printf("Could not dexor the firmware :(\nAborting.\n");
    00356         exit(1);
    00357       }
    00358     }
    00359     printf("Decompressed image size: 0x%x\n", rawlen);
    00360     if (decompressed[0] == 'C' && decompressed[1] == 'I' &&
    00361         decompressed[2] == 'F' && decompressed[3] == 'F') {
    00362       printf("The extracted image looks like a firmware file.\n");
    00363     } else {
    00364       printf("The extracted image does not look like a firmware file.\n");
    00365       printf("Aborting.\n");
    00366       return 1;
    00367     }
    00368
    00369     /* Write out decompressed file */
    00370     printf("Writing out the extracted image to disk as \"%s\".\n", uncompressed_file);
    00371     if (write_fw_file(uncompressed_file, decompressed, rawlen) == -1) {
    00372       printf("Failed to write uncompressed file. Aborting.\n");
    00373       return 1;
    00374     }
    00375
    00376     sendpath = uncompressed_file;
    00377
    00378     /* Exchange buffer for the decompressed image */
    00379     free(buffer);
    00380     buffer = decompressed;
    00381
    00382 #else
    00383     printf("This may be an zlib compressed .exe file firmware.\n");
    00384     printf("You must compile the \"fwupgrade\" program on a system\n");
    00385     printf("which has the zlib library and headers properly installed\n");
    00386     printf("to enable the compression of zlib compressed firmware images.\n");
    00387 #endif
    00388   }
    00389
    00390   /* Make some more information available on this firmware */
    00391   analyze_firmware(buffer);
    00392
    00393   /* Free firmware read buffer */
    00394   free(buffer);
    00395
    00396   printf("Sending firmware file to jukebox\n");
    00397   if (NJB_Discover(njbs, 0, &n) == -1) {
    00398     fprintf(stderr, "could not locate any jukeboxes\n");
    00399     return 1;
    00400   }
    00401
    00402   if ( n == 0 ) {
    00403     fprintf(stderr, "no NJB devices found\n");
    00404     return 0;
    00405   }
    00406
    00407   njb = njbs;
    00408
    00409   if ( NJB_Open(njb) == -1 ) {
    00410     NJB_Error_Dump(njb,stderr);
    00411     return 1;
    00412   }
    00413
    00414   NJB_Capture(njb);
    00415
    00416   printf("I will now send the firmware to your device.\n");
    00417   printf("Continue? (y/n)\n");
    00418   if (prompt() == 0) {
    00419     if ( NJB_Send_Firmware(njb, sendpath, progress, NULL) == -1 ) {
    00420       NJB_Error_Dump(njb,stderr);
    00421     } else {
    00422       printf("\nFirmware upload complete.");
    00423     }
    00424     printf("\n");
    00425   } else {
    00426     printf("Aborted.\n");
    00427   }
    00428
    00429   NJB_Release(njb);
    00430
    00431   NJB_Close(njb);
    00432
    00433   return 0;
    00434 }