mksenaofw.c 9.6 KB

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