1/* 2 * 3 * Copyright (C) 2012 OpenWrt.org 4 * Copyright (C) 2012 Mikko Hissa <mikko.hissa@uta.fi> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 as published 8 * by the Free Software Foundation. 9 * 10 */ 11 12#include <stdio.h> 13#include <stdlib.h> 14#include <string.h> 15#include <stdarg.h> 16#include <libgen.h> 17#include <errno.h> 18#include <arpa/inet.h> 19#include <unistd.h> 20#include "md5.h" 21 22#define HDR_LEN 0x60 23#define BUF_SIZE 0x200 24#define VERSION_SIZE 0x10 25#define MD5_SIZE 0x10 26#define PAD_SIZE 0x20 27#define DEFAULT_MAGIC 305419896 28#define DEFAULT_VERSION "123" 29#define DEFAULT_VENDOR_ID 536 30#define DEFAULT_HEAD_VALUE 0x0 31#define DEFAULT_BLOCK_SIZE 65535 32#define DEFAULT_PRODUCT_ID 48 33#define DEFAULT_FIRMWARE_TYPE 0x03 34 35typedef struct { 36 uint32_t head; 37 uint32_t vendor_id; 38 uint32_t product_id; 39 uint8_t version[VERSION_SIZE]; 40 uint32_t firmware_type; 41 uint32_t filesize; 42 uint32_t zero; 43 uint8_t md5sum[MD5_SIZE]; 44 uint8_t pad[PAD_SIZE]; 45 uint32_t chksum; 46 uint32_t magic; 47} img_header; 48 49typedef struct { 50 uint8_t id; 51 char * name; 52} firmware_type; 53 54typedef enum { 55 NONE, ENCODE, DECODE 56} op_mode; 57 58static firmware_type FIRMWARE_TYPES[] = { 59 { 0x01, "bootloader" }, 60 { 0x02, "kernel" }, 61 { 0x03, "kernelapp" }, 62 { 0x04, "apps" }, 63 { 0x05, "littleapps" }, 64 { 0x06, "sounds" }, 65 { 0x07, "userconfig" }, 66 { 0x0a, "factoryapps" }, 67 { 0x0b, "odmapps" }, 68 { 0x0c, "langpack" } 69}; 70 71static long get_file_size(const char *filename) 72{ 73 FILE *fp_file; 74 long result; 75 76 fp_file = fopen(filename, "r"); 77 if (!fp_file) 78 return -1; 79 fseek(fp_file, 0, SEEK_END); 80 result = ftell(fp_file); 81 fclose(fp_file); 82 return result; 83} 84 85static int header_checksum(void *data, int len) 86{ 87 int i; 88 int sum; 89 90 sum = 0; 91 if (data != NULL && len >= 0) { 92 for (i = 0; i < len; ++i) 93 sum += *(unsigned char *) (data + i); 94 return sum; 95 } 96 97 return -1; 98} 99 100static int md5_file(const char *filename, uint8_t *dst) 101{ 102 FILE *fp_src; 103 MD5_CTX ctx; 104 char buf[BUF_SIZE]; 105 size_t bytes_read; 106 107 MD5_Init(&ctx); 108 109 fp_src = fopen(filename, "r+b"); 110 if (!fp_src) { 111 return -1; 112 } 113 while (!feof(fp_src)) { 114 bytes_read = fread(&buf, 1, BUF_SIZE, fp_src); 115 MD5_Update(&ctx, &buf, bytes_read); 116 } 117 fclose(fp_src); 118 119 MD5_Final(dst, &ctx); 120 121 return 0; 122} 123 124static int encode_image(const char *input_file_name, 125 const char *output_file_name, img_header *header, int block_size) 126{ 127 char buf[BUF_SIZE]; 128 size_t bytes_read; 129 size_t pad_len = 0; 130 size_t bytes_avail; 131 132 FILE *fp_input; 133 FILE *fp_output; 134 135 int i; 136 long magic; 137 138 fp_input = fopen(input_file_name, "r+b"); 139 if (!fp_input) { 140 fprintf(stderr, "Cannot open %s !!\n", input_file_name); 141 return -1; 142 } 143 144 fp_output = fopen(output_file_name, "w+b"); 145 if (!fp_output) { 146 fprintf(stderr, "Cannot open %s !!\n", output_file_name); 147 fclose(fp_input); 148 return -1; 149 } 150 151 header->filesize = get_file_size(input_file_name); 152 if (!header->filesize) { 153 fprintf(stderr, "File %s open/size error!\n", input_file_name); 154 fclose(fp_input); 155 fclose(fp_output); 156 return -1; 157 } 158 /* 159 * Zero padding 160 */ 161 if (block_size > 0) { 162 pad_len = block_size - (header->filesize % block_size); 163 } 164 165 if (md5_file(input_file_name, (uint8_t *) &header->md5sum) < 0) { 166 fprintf(stderr, "Md5 failed on file %s\n", input_file_name); 167 fclose(fp_input); 168 fclose(fp_output); 169 return -1; 170 } 171 header->zero = 0; 172 header->chksum = header_checksum(header, HDR_LEN); 173 header->head = htonl(header->head); 174 header->vendor_id = htonl(header->vendor_id); 175 header->product_id = htonl(header->product_id); 176 header->firmware_type = htonl(header->firmware_type); 177 header->filesize = htonl(header->filesize); 178 header->chksum = htonl(header->chksum); 179 magic = header->magic; 180 header->magic = htonl(header->magic); 181 182 fwrite(header, HDR_LEN, 1, fp_output); 183 184 while (!feof(fp_input) || pad_len > 0) { 185 186 if (!feof(fp_input)) 187 bytes_read = fread(&buf, 1, BUF_SIZE, fp_input); 188 else 189 bytes_read = 0; 190 191 /* 192 * No more bytes read, start padding 193 */ 194 if (bytes_read < BUF_SIZE && pad_len > 0) { 195 bytes_avail = BUF_SIZE - bytes_read; 196 memset( &buf[bytes_read], 0, bytes_avail); 197 bytes_read += bytes_avail < pad_len ? bytes_avail : pad_len; 198 pad_len -= bytes_avail < pad_len ? bytes_avail : pad_len; 199 } 200 201 for (i = 0; i < bytes_read; i++) 202 buf[i] ^= magic >> ((((i >> 60) + i) & 7) - (i >> 60)); 203 fwrite(&buf, bytes_read, 1, fp_output); 204 } 205 206 fclose(fp_input); 207 fclose(fp_output); 208 return 1; 209} 210 211int decode_image(const char *input_file_name, const char *output_file_name) 212{ 213 img_header header; 214 char buf[512]; 215 216 FILE *fp_input; 217 FILE *fp_output; 218 unsigned int i; 219 220 size_t bytes_read; 221 size_t bytes_written; 222 223 fp_input = fopen(input_file_name, "r+b"); 224 if (!fp_input) { 225 fprintf(stderr, "Cannot open %s !!\n", input_file_name); 226 fclose(fp_input); 227 return -1; 228 } 229 230 fp_output = fopen(output_file_name, "w+b"); 231 if (!fp_output) { 232 fprintf(stderr, "Cannot open %s !!\n", output_file_name); 233 fclose(fp_output); 234 return -1; 235 } 236 237 if (fread(&header, 1, HDR_LEN, fp_input) != HDR_LEN) { 238 fprintf(stderr, "Incorrect header size!!"); 239 fclose(fp_input); 240 fclose(fp_output); 241 return -1; 242 } 243 244 header.head = ntohl(header.head); 245 header.vendor_id = ntohl(header.vendor_id); 246 header.product_id = ntohl(header.product_id); 247 header.firmware_type = ntohl(header.firmware_type); 248 header.filesize = ntohl(header.filesize); 249 header.chksum = ntohl(header.chksum); 250 header.magic = ntohl(header.magic); 251 252 bytes_written = 0; 253 while (!feof(fp_input)) { 254 255 bytes_read = fread(&buf, 1, BUF_SIZE, fp_input); 256 for (i = 0; i < bytes_read; i++) 257 buf[i] ^= header.magic >> ((((i >> 60) + i) & 7) - (i >> 60)); 258 259 /* 260 * Handle padded source file 261 */ 262 if (bytes_written + bytes_read > header.filesize) { 263 bytes_read = header.filesize - bytes_written; 264 if (bytes_read > 0) 265 fwrite(&buf, bytes_read, 1, fp_output); 266 break; 267 } 268 269 fwrite(&buf, bytes_read, 1, fp_output); 270 bytes_written += bytes_read; 271 } 272 273 fclose(fp_input); 274 fclose(fp_output); 275 276 return 1; 277} 278 279static void usage(const char *progname, int status) 280{ 281 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; 282 int i; 283 284 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); 285 fprintf(stream, "\n" 286 "Options:\n" 287 " -e <file> encode image file <file>\n" 288 " -d <file> decode image file <file>\n" 289 " -o <file> write output to the file <file>\n" 290 " -t <type> set image type to <type>, defaults to 3\n" 291 " valid image <type> values:\n"); 292 for (i = 0; i < sizeof(FIRMWARE_TYPES) / sizeof(firmware_type); i++) { 293 fprintf(stream, " %-5i= %s\n", FIRMWARE_TYPES[i].id, 294 FIRMWARE_TYPES[i].name); 295 } 296 fprintf(stream, " -v <version> set image version to <version>\n" 297 " -r <vendor> set image vendor id to <vendor>\n" 298 " -p <product> set image product id to <product>\n" 299 " -m <magic> set encoding magic <magic>\n" 300 " -z enable image padding to <blocksize>\n" 301 " -b <blocksize> set image <blocksize>, defaults to 65535\n" 302 " -h show this screen\n"); 303 exit(status); 304} 305 306int main(int argc, char *argv[]) 307{ 308 int opt; 309 char *input_file, *output_file, *progname = NULL; 310 op_mode mode = NONE; 311 int tmp, i, pad = 0; 312 int block_size; 313 img_header header; 314 315 block_size = DEFAULT_BLOCK_SIZE; 316 progname = basename(argv[0]); 317 318 memset(&header, 0, sizeof( img_header )); 319 header.magic = DEFAULT_MAGIC; 320 header.head = DEFAULT_HEAD_VALUE; 321 header.vendor_id = DEFAULT_VENDOR_ID; 322 header.product_id = DEFAULT_PRODUCT_ID; 323 header.firmware_type = DEFAULT_FIRMWARE_TYPE; 324 strncpy( (char*)&header.version, DEFAULT_VERSION, VERSION_SIZE - 1); 325 326 while ((opt = getopt(argc, argv, ":o:e:d:t:v:r:p:m:b:h?z")) != -1) { 327 switch (opt) { 328 case 'e': 329 input_file = optarg; 330 mode = ENCODE; 331 break; 332 case 'd': 333 input_file = optarg; 334 mode = DECODE; 335 break; 336 case 'o': 337 output_file = optarg; 338 break; 339 case 't': 340 tmp = strtol(optarg, 0, 10); 341 for (i = 0; i < sizeof(FIRMWARE_TYPES) / sizeof(firmware_type); 342 i++) { 343 if (FIRMWARE_TYPES[i].id == tmp) { 344 header.firmware_type = FIRMWARE_TYPES[i].id; 345 break; 346 } 347 } 348 if (header.firmware_type == 0) { 349 fprintf(stderr, "Invalid firmware <type>!\n"); 350 usage(progname, EXIT_FAILURE); 351 } 352 break; 353 case 'v': 354 strncpy( (char*)&header.version, optarg, 355 VERSION_SIZE - 1); 356 break; 357 case 'r': 358 header.vendor_id = strtol(optarg, 0, 10); 359 break; 360 case 'p': 361 header.product_id = strtol(optarg, 0, 10); 362 break; 363 case 'm': 364 header.magic = strtoul(optarg, 0, 16); 365 break; 366 case 'z': 367 pad = 1; 368 break; 369 case 'b': 370 block_size = strtol(optarg, 0, 10); 371 break; 372 case 'h': 373 usage(progname, EXIT_SUCCESS); 374 break; 375 case ':': 376 fprintf(stderr, "Option -%c requires an operand\n", optopt); 377 usage(progname, EXIT_FAILURE); 378 break; 379 case '?': 380 fprintf(stderr, "Unrecognized option: -%c\n", optopt); 381 usage(progname, EXIT_FAILURE); 382 break; 383 default: 384 usage(progname, EXIT_FAILURE); 385 break; 386 } 387 } 388 389 if (input_file == NULL || output_file == NULL ) { 390 fprintf(stderr, "Input and output files must be defined\n"); 391 usage(progname, EXIT_FAILURE); 392 } 393 394 switch (mode) { 395 case NONE: 396 usage(progname, EXIT_FAILURE); 397 break; 398 case ENCODE: 399 if (encode_image(input_file, output_file, &header, pad ? block_size : 0) 400 < 0) 401 return EXIT_FAILURE; 402 break; 403 case DECODE: 404 if (decode_image(input_file, output_file) < 0) 405 return EXIT_FAILURE; 406 break; 407 } 408 409 return EXIT_SUCCESS; 410} 411