1/* 2 * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> 3 * 4 * This tool was based on: 5 * TP-Link WR941 V2 firmware checksum fixing tool. 6 * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License version 2 as published 10 * by the Free Software Foundation. 11 * 12 */ 13 14#include <stdio.h> 15#include <stdlib.h> 16#include <stdint.h> 17#include <string.h> 18#include <unistd.h> /* for unlink() */ 19#include <libgen.h> 20#include <getopt.h> /* for getopt() */ 21#include <stdarg.h> 22#include <errno.h> 23#include <stdbool.h> 24#include <endian.h> 25#include <sys/stat.h> 26 27#include <arpa/inet.h> 28#include <netinet/in.h> 29 30#include "md5.h" 31 32#define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); }) 33 34#define MD5SUM_LEN 16 35 36struct file_info { 37 char *file_name; /* name of the file */ 38 uint32_t file_size; /* length of the file */ 39}; 40 41struct fw_header { 42 uint32_t version; /* 0x00: header version */ 43 char fw_version[48]; /* 0x04: fw version string */ 44 uint32_t hw_id; /* 0x34: hardware id */ 45 uint32_t hw_rev; /* 0x38: FIXME: hardware revision? */ 46 uint32_t unk1; /* 0x3c: 0x00000000 */ 47 uint8_t md5sum1[MD5SUM_LEN]; /* 0x40 */ 48 uint32_t unk2; /* 0x50: 0x00000000 */ 49 uint8_t md5sum2[MD5SUM_LEN]; /* 0x54 */ 50 uint32_t unk3; /* 0x64: 0xffffffff */ 51 52 uint32_t kernel_la; /* 0x68: kernel load address */ 53 uint32_t kernel_ep; /* 0x6c: kernel entry point */ 54 uint32_t fw_length; /* 0x70: total length of the image */ 55 uint32_t kernel_ofs; /* 0x74: kernel data offset */ 56 uint32_t kernel_len; /* 0x78: kernel data length */ 57 uint32_t rootfs_ofs; /* 0x7c: rootfs data offset */ 58 uint32_t rootfs_len; /* 0x80: rootfs data length */ 59 uint32_t boot_ofs; /* 0x84: FIXME: seems to be unused */ 60 uint32_t boot_len; /* 0x88: FIXME: seems to be unused */ 61 uint16_t unk4; /* 0x8c: 0x55aa */ 62 uint8_t sver_hi; /* 0x8e */ 63 uint8_t sver_lo; /* 0x8f */ 64 uint8_t unk5; /* 0x90: magic: 0xa5 */ 65 uint8_t ver_hi; /* 0x91 */ 66 uint8_t ver_mid; /* 0x92 */ 67 uint8_t ver_lo; /* 0x93 */ 68 uint8_t pad[364]; 69} __attribute__ ((packed)); 70 71struct flash_layout { 72 char *id; 73 uint32_t fw_max_len; 74 uint32_t kernel_la; 75 uint32_t kernel_ep; 76 uint32_t rootfs_ofs; 77}; 78 79struct board_info { 80 char *id; 81 uint32_t hw_id; 82 uint32_t hw_rev; 83 char *layout_id; 84 uint32_t hdr_ver; 85 bool endian_swap; 86}; 87 88/* 89 * Globals 90 */ 91static char *ofname; 92static char *progname; 93static char *vendor = "TP-LINK Technologies"; 94static char *version = "ver. 1.0"; 95static char *fw_ver = "0.0.0"; 96static char *sver = "1.0"; 97static uint32_t hdr_ver = 2; 98 99static char *board_id; 100static struct board_info *board; 101static char *layout_id; 102static struct flash_layout *layout; 103static char *opt_hw_id; 104static uint32_t hw_id; 105static char *opt_hw_rev; 106static uint32_t hw_rev; 107static int fw_ver_lo; 108static int fw_ver_mid; 109static int fw_ver_hi; 110static int sver_lo; 111static int sver_hi; 112static struct file_info kernel_info; 113static uint32_t kernel_la = 0; 114static uint32_t kernel_ep = 0; 115static uint32_t kernel_len = 0; 116static struct file_info rootfs_info; 117static uint32_t rootfs_ofs = 0; 118static uint32_t rootfs_align; 119static struct file_info boot_info; 120static int combined; 121static int strip_padding; 122static int add_jffs2_eof; 123static unsigned char jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde}; 124 125static struct file_info inspect_info; 126static int extract = 0; 127static bool endian_swap = false; 128 129char md5salt_normal[MD5SUM_LEN] = { 130 0xdc, 0xd7, 0x3a, 0xa5, 0xc3, 0x95, 0x98, 0xfb, 131 0xdc, 0xf9, 0xe7, 0xf4, 0x0e, 0xae, 0x47, 0x37, 132}; 133 134char md5salt_boot[MD5SUM_LEN] = { 135 0x8c, 0xef, 0x33, 0x5b, 0xd5, 0xc5, 0xce, 0xfa, 136 0xa7, 0x9c, 0x28, 0xda, 0xb2, 0xe9, 0x0f, 0x42, 137}; 138 139static struct flash_layout layouts[] = { 140 { 141 .id = "8Mltq", 142 .fw_max_len = 0x7a0000, 143 .kernel_la = 0x80002000, 144 .kernel_ep = 0x80002000, 145 .rootfs_ofs = 0x140000, 146 }, { 147 .id = "16Mltq", 148 .fw_max_len = 0xf90000, 149 .kernel_la = 0x80002000, 150 .kernel_ep = 0x800061b0, 151 .rootfs_ofs = 0x140000, 152 }, { 153 .id = "8Mmtk", 154 .fw_max_len = 0x7a0000, 155 .kernel_la = 0x80000000, 156 .kernel_ep = 0x80000000, 157 .rootfs_ofs = 0x140000, 158 }, { 159 /* terminating entry */ 160 } 161}; 162 163static struct board_info boards[] = { 164 { 165 .id = "TD-W8970v1", 166 .hw_id = 0x89700001, 167 .hw_rev = 1, 168 .layout_id = "8Mltq", 169 }, { 170 .id = "TD-W8980v1", 171 .hw_id = 0x89800001, 172 .hw_rev = 14, 173 .layout_id = "8Mltq", 174 }, { 175 .id = "ArcherC20i", 176 .hw_id = 0xc2000001, 177 .hw_rev = 58, 178 .layout_id = "8Mmtk", 179 .hdr_ver = 3, 180 .endian_swap = true, 181 }, { 182 .id = "ArcherVR200V", 183 .hw_id = 0x73b70801, 184 .hw_rev = 0x2f, 185 .layout_id = "16Mltq", 186 .hdr_ver = 2, 187 }, { 188 .id = "ArcherC50", 189 .hw_id = 0xc7500001, 190 .hw_rev = 69, 191 .layout_id = "8Mmtk", 192 .hdr_ver = 3, 193 .endian_swap = true, 194 }, { 195 /* terminating entry */ 196 } 197}; 198 199/* 200 * Message macros 201 */ 202#define ERR(fmt, ...) do { \ 203 fflush(0); \ 204 fprintf(stderr, "[%s] *** error: " fmt "\n", \ 205 progname, ## __VA_ARGS__ ); \ 206} while (0) 207 208#define ERRS(fmt, ...) do { \ 209 int save = errno; \ 210 fflush(0); \ 211 fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \ 212 progname, ## __VA_ARGS__, strerror(save)); \ 213} while (0) 214 215#define DBG(fmt, ...) do { \ 216 fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ 217} while (0) 218 219static struct board_info *find_board(char *id) 220{ 221 struct board_info *ret; 222 struct board_info *board; 223 224 ret = NULL; 225 for (board = boards; board->id != NULL; board++){ 226 if (strcasecmp(id, board->id) == 0) { 227 ret = board; 228 break; 229 } 230 }; 231 232 return ret; 233} 234 235static struct board_info *find_board_by_hwid(uint32_t hw_id) 236{ 237 struct board_info *board; 238 239 for (board = boards; board->id != NULL; board++) { 240 if (hw_id == board->hw_id) 241 return board; 242 }; 243 244 return NULL; 245} 246 247static struct flash_layout *find_layout(char *id) 248{ 249 struct flash_layout *ret; 250 struct flash_layout *l; 251 252 ret = NULL; 253 for (l = layouts; l->id != NULL; l++){ 254 if (strcasecmp(id, l->id) == 0) { 255 ret = l; 256 break; 257 } 258 }; 259 260 return ret; 261} 262 263static void usage(int status) 264{ 265 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; 266 struct board_info *board; 267 268 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); 269 fprintf(stream, 270"\n" 271"Options:\n" 272" -B <board> create image for the board specified with <board>\n" 273" -c use combined kernel image\n" 274" -E <ep> overwrite kernel entry point with <ep> (hexval prefixed with 0x)\n" 275" -L <la> overwrite kernel load address with <la> (hexval prefixed with 0x)\n" 276" -H <hwid> use hardware id specified with <hwid>\n" 277" -W <hwrev> use hardware revision specified with <hwrev>\n" 278" -F <id> use flash layout specified with <id>\n" 279" -k <file> read kernel image from the file <file>\n" 280" -r <file> read rootfs image from the file <file>\n" 281" -a <align> align the rootfs start on an <align> bytes boundary\n" 282" -R <offset> overwrite rootfs offset with <offset> (hexval prefixed with 0x)\n" 283" -o <file> write output to the file <file>\n" 284" -s strip padding from the end of the image\n" 285" -j add jffs2 end-of-filesystem markers\n" 286" -V <version> set image version to <version>\n" 287" -v <version> set firmware version to <version>\n" 288" -y <version> set secondary version to <version>\n" 289" -i <file> inspect given firmware file <file>\n" 290" -x extract kernel and rootfs while inspecting (requires -i)\n" 291" -h show this screen\n" 292 ); 293 294 exit(status); 295} 296 297static int get_md5(char *data, int size, char *md5) 298{ 299 MD5_CTX ctx; 300 301 MD5_Init(&ctx); 302 MD5_Update(&ctx, data, size); 303 MD5_Final(md5, &ctx); 304} 305 306static int get_file_stat(struct file_info *fdata) 307{ 308 struct stat st; 309 int res; 310 311 if (fdata->file_name == NULL) 312 return 0; 313 314 res = stat(fdata->file_name, &st); 315 if (res){ 316 ERRS("stat failed on %s", fdata->file_name); 317 return res; 318 } 319 320 fdata->file_size = st.st_size; 321 return 0; 322} 323 324static int read_to_buf(struct file_info *fdata, char *buf) 325{ 326 FILE *f; 327 int ret = EXIT_FAILURE; 328 329 f = fopen(fdata->file_name, "r"); 330 if (f == NULL) { 331 ERRS("could not open \"%s\" for reading", fdata->file_name); 332 goto out; 333 } 334 335 errno = 0; 336 fread(buf, fdata->file_size, 1, f); 337 if (errno != 0) { 338 ERRS("unable to read from file \"%s\"", fdata->file_name); 339 goto out_close; 340 } 341 342 ret = EXIT_SUCCESS; 343 344 out_close: 345 fclose(f); 346 out: 347 return ret; 348} 349 350static int check_options(void) 351{ 352 int ret; 353 354 if (inspect_info.file_name) { 355 ret = get_file_stat(&inspect_info); 356 if (ret) 357 return ret; 358 359 return 0; 360 } else if (extract) { 361 ERR("no firmware for inspection specified"); 362 return -1; 363 } 364 365 if (board_id == NULL && opt_hw_id == NULL) { 366 ERR("either board or hardware id must be specified"); 367 return -1; 368 } 369 370 if (board_id) { 371 board = find_board(board_id); 372 if (board == NULL) { 373 ERR("unknown/unsupported board id \"%s\"", board_id); 374 return -1; 375 } 376 if (layout_id == NULL) 377 layout_id = board->layout_id; 378 379 hw_id = board->hw_id; 380 hw_rev = board->hw_rev; 381 if (board->hdr_ver) 382 hdr_ver = board->hdr_ver; 383 endian_swap = board->endian_swap; 384 } else { 385 if (layout_id == NULL) { 386 ERR("flash layout is not specified"); 387 return -1; 388 } 389 hw_id = strtoul(opt_hw_id, NULL, 0); 390 391 if (opt_hw_rev) 392 hw_rev = strtoul(opt_hw_rev, NULL, 0); 393 else 394 hw_rev = 1; 395 } 396 397 layout = find_layout(layout_id); 398 if (layout == NULL) { 399 ERR("unknown flash layout \"%s\"", layout_id); 400 return -1; 401 } 402 403 if (!kernel_la) 404 kernel_la = layout->kernel_la; 405 if (!kernel_ep) 406 kernel_ep = layout->kernel_ep; 407 if (!rootfs_ofs) 408 rootfs_ofs = layout->rootfs_ofs; 409 410 if (kernel_info.file_name == NULL) { 411 ERR("no kernel image specified"); 412 return -1; 413 } 414 415 ret = get_file_stat(&kernel_info); 416 if (ret) 417 return ret; 418 419 kernel_len = kernel_info.file_size; 420 421 if (combined) { 422 if (kernel_info.file_size > 423 layout->fw_max_len - sizeof(struct fw_header)) { 424 ERR("kernel image is too big"); 425 return -1; 426 } 427 } else { 428 if (rootfs_info.file_name == NULL) { 429 ERR("no rootfs image specified"); 430 return -1; 431 } 432 433 ret = get_file_stat(&rootfs_info); 434 if (ret) 435 return ret; 436 437 if (rootfs_align) { 438 kernel_len += sizeof(struct fw_header); 439 kernel_len = ALIGN(kernel_len, rootfs_align); 440 kernel_len -= sizeof(struct fw_header); 441 442 DBG("kernel length aligned to %u", kernel_len); 443 444 if (kernel_len + rootfs_info.file_size > 445 layout->fw_max_len - sizeof(struct fw_header)) { 446 ERR("images are too big"); 447 return -1; 448 } 449 } else { 450 if (kernel_info.file_size > 451 rootfs_ofs - sizeof(struct fw_header)) { 452 ERR("kernel image is too big"); 453 return -1; 454 } 455 456 if (rootfs_info.file_size > 457 (layout->fw_max_len - rootfs_ofs)) { 458 ERR("rootfs image is too big"); 459 return -1; 460 } 461 } 462 } 463 464 if (ofname == NULL) { 465 ERR("no output file specified"); 466 return -1; 467 } 468 469 ret = sscanf(fw_ver, "%d.%d.%d", &fw_ver_hi, &fw_ver_mid, &fw_ver_lo); 470 if (ret != 3) { 471 ERR("invalid firmware version '%s'", fw_ver); 472 return -1; 473 } 474 475 ret = sscanf(sver, "%d.%d", &sver_hi, &sver_lo); 476 if (ret != 2) { 477 ERR("invalid secondary version '%s'", sver); 478 return -1; 479 } 480 481 return 0; 482} 483 484static void fill_header(char *buf, int len) 485{ 486 struct fw_header *hdr = (struct fw_header *)buf; 487 unsigned ver_len; 488 489 memset(hdr, '\xff', sizeof(struct fw_header)); 490 491 hdr->version = htonl(bswap_32(hdr_ver)); 492 ver_len = strlen(version); 493 if (ver_len > (sizeof(hdr->fw_version) - 1)) 494 ver_len = sizeof(hdr->fw_version) - 1; 495 496 memcpy(hdr->fw_version, version, ver_len); 497 hdr->fw_version[ver_len] = 0; 498 499 hdr->hw_id = htonl(hw_id); 500 hdr->hw_rev = htonl(hw_rev); 501 502 if (boot_info.file_size == 0) { 503 memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1)); 504 hdr->boot_ofs = htonl(0); 505 hdr->boot_len = htonl(0); 506 } else { 507 memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1)); 508 hdr->boot_ofs = htonl(rootfs_ofs + rootfs_info.file_size); 509 hdr->boot_len = htonl(rootfs_info.file_size); 510 } 511 512 hdr->kernel_la = htonl(kernel_la); 513 hdr->kernel_ep = htonl(kernel_ep); 514 hdr->fw_length = htonl(layout->fw_max_len); 515 hdr->kernel_ofs = htonl(sizeof(struct fw_header)); 516 hdr->kernel_len = htonl(kernel_len); 517 if (!combined) { 518 hdr->rootfs_ofs = htonl(rootfs_ofs); 519 hdr->rootfs_len = htonl(rootfs_info.file_size); 520 } 521 522 hdr->boot_ofs = htonl(0); 523 hdr->boot_len = htonl(boot_info.file_size); 524 525 hdr->unk1 = htonl(0); 526 hdr->unk2 = htonl(0); 527 hdr->unk3 = htonl(0xffffffff); 528 hdr->unk4 = htons(0x55aa); 529 hdr->unk5 = 0xa5; 530 531 hdr->sver_hi = sver_hi; 532 hdr->sver_lo = sver_lo; 533 534 hdr->ver_hi = fw_ver_hi; 535 hdr->ver_mid = fw_ver_mid; 536 hdr->ver_lo = fw_ver_lo; 537 538 if (endian_swap) { 539 hdr->kernel_la = bswap_32(hdr->kernel_la); 540 hdr->kernel_ep = bswap_32(hdr->kernel_ep); 541 } 542 543 get_md5(buf, len, hdr->md5sum1); 544} 545 546static int pad_jffs2(char *buf, int currlen) 547{ 548 int len; 549 uint32_t pad_mask; 550 551 len = currlen; 552 pad_mask = (64 * 1024); 553 while ((len < layout->fw_max_len) && (pad_mask != 0)) { 554 uint32_t mask; 555 int i; 556 557 for (i = 10; i < 32; i++) { 558 mask = 1 << i; 559 if (pad_mask & mask) 560 break; 561 } 562 563 len = ALIGN(len, mask); 564 565 for (i = 10; i < 32; i++) { 566 mask = 1 << i; 567 if ((len & (mask - 1)) == 0) 568 pad_mask &= ~mask; 569 } 570 571 for (i = 0; i < sizeof(jffs2_eof_mark); i++) 572 buf[len + i] = jffs2_eof_mark[i]; 573 574 len += sizeof(jffs2_eof_mark); 575 } 576 577 return len; 578} 579 580static int write_fw(char *data, int len) 581{ 582 FILE *f; 583 int ret = EXIT_FAILURE; 584 585 f = fopen(ofname, "w"); 586 if (f == NULL) { 587 ERRS("could not open \"%s\" for writing", ofname); 588 goto out; 589 } 590 591 errno = 0; 592 fwrite(data, len, 1, f); 593 if (errno) { 594 ERRS("unable to write output file"); 595 goto out_flush; 596 } 597 598 DBG("firmware file \"%s\" completed", ofname); 599 600 ret = EXIT_SUCCESS; 601 602 out_flush: 603 fflush(f); 604 fclose(f); 605 if (ret != EXIT_SUCCESS) { 606 unlink(ofname); 607 } 608 out: 609 return ret; 610} 611 612static int build_fw(void) 613{ 614 int buflen; 615 char *buf; 616 char *p; 617 int ret = EXIT_FAILURE; 618 int writelen = 0; 619 620 buflen = layout->fw_max_len; 621 622 buf = malloc(buflen); 623 if (!buf) { 624 ERR("no memory for buffer\n"); 625 goto out; 626 } 627 628 memset(buf, 0xff, buflen); 629 p = buf + sizeof(struct fw_header); 630 ret = read_to_buf(&kernel_info, p); 631 if (ret) 632 goto out_free_buf; 633 634 writelen = sizeof(struct fw_header) + kernel_len; 635 636 if (!combined) { 637 if (rootfs_align) 638 p = buf + writelen; 639 else 640 p = buf + rootfs_ofs; 641 642 ret = read_to_buf(&rootfs_info, p); 643 if (ret) 644 goto out_free_buf; 645 646 if (rootfs_align) 647 writelen += rootfs_info.file_size; 648 else 649 writelen = rootfs_ofs + rootfs_info.file_size; 650 651 if (add_jffs2_eof) 652 writelen = pad_jffs2(buf, writelen); 653 } 654 655 if (!strip_padding) 656 writelen = buflen; 657 658 fill_header(buf, writelen); 659 ret = write_fw(buf, writelen); 660 if (ret) 661 goto out_free_buf; 662 663 ret = EXIT_SUCCESS; 664 665 out_free_buf: 666 free(buf); 667 out: 668 return ret; 669} 670 671/* Helper functions to inspect_fw() representing different output formats */ 672static inline void inspect_fw_pstr(char *label, char *str) 673{ 674 printf("%-23s: %s\n", label, str); 675} 676 677static inline void inspect_fw_phex(char *label, uint32_t val) 678{ 679 printf("%-23s: 0x%08x\n", label, val); 680} 681 682static inline void inspect_fw_phexpost(char *label, 683 uint32_t val, char *post) 684{ 685 printf("%-23s: 0x%08x (%s)\n", label, val, post); 686} 687 688static inline void inspect_fw_phexdef(char *label, 689 uint32_t val, uint32_t defval) 690{ 691 printf("%-23s: 0x%08x ", label, val); 692 693 if (val == defval) 694 printf("(== OpenWrt default)\n"); 695 else 696 printf("(OpenWrt default: 0x%08x)\n", defval); 697} 698 699static inline void inspect_fw_phexexp(char *label, 700 uint32_t val, uint32_t expval) 701{ 702 printf("%-23s: 0x%08x ", label, val); 703 704 if (val == expval) 705 printf("(ok)\n"); 706 else 707 printf("(expected: 0x%08x)\n", expval); 708} 709 710static inline void inspect_fw_phexdec(char *label, uint32_t val) 711{ 712 printf("%-23s: 0x%08x / %8u bytes\n", label, val, val); 713} 714 715static inline void inspect_fw_phexdecdef(char *label, 716 uint32_t val, uint32_t defval) 717{ 718 printf("%-23s: 0x%08x / %8u bytes ", label, val, val); 719 720 if (val == defval) 721 printf("(== OpenWrt default)\n"); 722 else 723 printf("(OpenWrt default: 0x%08x)\n", defval); 724} 725 726static inline void inspect_fw_pmd5sum(char *label, uint8_t *val, char *text) 727{ 728 int i; 729 730 printf("%-23s:", label); 731 for (i=0; i<MD5SUM_LEN; i++) 732 printf(" %02x", val[i]); 733 printf(" %s\n", text); 734} 735 736static int inspect_fw(void) 737{ 738 char *buf; 739 struct fw_header *hdr; 740 uint8_t md5sum[MD5SUM_LEN]; 741 struct board_info *board; 742 int ret = EXIT_FAILURE; 743 744 buf = malloc(inspect_info.file_size); 745 if (!buf) { 746 ERR("no memory for buffer!\n"); 747 goto out; 748 } 749 750 ret = read_to_buf(&inspect_info, buf); 751 if (ret) 752 goto out_free_buf; 753 hdr = (struct fw_header *)buf; 754 755 inspect_fw_pstr("File name", inspect_info.file_name); 756 inspect_fw_phexdec("File size", inspect_info.file_size); 757 758 switch(bswap_32(ntohl(hdr->version))) { 759 case 2: 760 case 3: 761 break; 762 default: 763 ERR("file does not seem to have V2/V3 header!\n"); 764 goto out_free_buf; 765 } 766 767 inspect_fw_phexdec("Version 2 Header size", sizeof(struct fw_header)); 768 769 if (ntohl(hdr->unk1) != 0) 770 inspect_fw_phexdec("Unknown value 1", hdr->unk1); 771 772 memcpy(md5sum, hdr->md5sum1, sizeof(md5sum)); 773 if (ntohl(hdr->boot_len) == 0) 774 memcpy(hdr->md5sum1, md5salt_normal, sizeof(md5sum)); 775 else 776 memcpy(hdr->md5sum1, md5salt_boot, sizeof(md5sum)); 777 get_md5(buf, inspect_info.file_size, hdr->md5sum1); 778 779 if (memcmp(md5sum, hdr->md5sum1, sizeof(md5sum))) { 780 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(*ERROR*)"); 781 inspect_fw_pmd5sum(" --> expected", hdr->md5sum1, ""); 782 } else { 783 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(ok)"); 784 } 785 if (ntohl(hdr->unk2) != 0) 786 inspect_fw_phexdec("Unknown value 2", hdr->unk2); 787 inspect_fw_pmd5sum("Header MD5Sum2", hdr->md5sum2, 788 "(purpose yet unknown, unchecked here)"); 789 790 if (ntohl(hdr->unk3) != 0xffffffff) 791 inspect_fw_phexdec("Unknown value 3", hdr->unk3); 792 793 if (ntohs(hdr->unk4) != 0x55aa) 794 inspect_fw_phexdec("Unknown value 4", hdr->unk4); 795 796 if (hdr->unk5 != 0xa5) 797 inspect_fw_phexdec("Unknown value 5", hdr->unk5); 798 799 printf("\n"); 800 801 inspect_fw_pstr("Firmware version", hdr->fw_version); 802 803 board = find_board_by_hwid(ntohl(hdr->hw_id)); 804 if (board) { 805 layout = find_layout(board->layout_id); 806 inspect_fw_phexpost("Hardware ID", 807 ntohl(hdr->hw_id), board->id); 808 inspect_fw_phexexp("Hardware Revision", 809 ntohl(hdr->hw_rev), board->hw_rev); 810 } else { 811 inspect_fw_phexpost("Hardware ID", 812 ntohl(hdr->hw_id), "unknown"); 813 inspect_fw_phex("Hardware Revision", 814 ntohl(hdr->hw_rev)); 815 } 816 817 printf("%-23s: %d.%d.%d-%d.%d\n", "Software version", 818 hdr->ver_hi, hdr->ver_mid, hdr->ver_lo, 819 hdr->sver_hi, hdr->sver_lo); 820 821 printf("\n"); 822 823 inspect_fw_phexdec("Kernel data offset", 824 ntohl(hdr->kernel_ofs)); 825 inspect_fw_phexdec("Kernel data length", 826 ntohl(hdr->kernel_len)); 827 if (board) { 828 inspect_fw_phexdef("Kernel load address", 829 ntohl(hdr->kernel_la), 830 layout ? layout->kernel_la : 0xffffffff); 831 inspect_fw_phexdef("Kernel entry point", 832 ntohl(hdr->kernel_ep), 833 layout ? layout->kernel_ep : 0xffffffff); 834 inspect_fw_phexdecdef("Rootfs data offset", 835 ntohl(hdr->rootfs_ofs), 836 layout ? layout->rootfs_ofs : 0xffffffff); 837 } else { 838 inspect_fw_phex("Kernel load address", 839 ntohl(hdr->kernel_la)); 840 inspect_fw_phex("Kernel entry point", 841 ntohl(hdr->kernel_ep)); 842 inspect_fw_phexdec("Rootfs data offset", 843 ntohl(hdr->rootfs_ofs)); 844 } 845 inspect_fw_phexdec("Rootfs data length", 846 ntohl(hdr->rootfs_len)); 847 inspect_fw_phexdec("Boot loader data offset", 848 ntohl(hdr->boot_ofs)); 849 inspect_fw_phexdec("Boot loader data length", 850 ntohl(hdr->boot_len)); 851 inspect_fw_phexdec("Total firmware length", 852 ntohl(hdr->fw_length)); 853 854 if (extract) { 855 FILE *fp; 856 char *filename; 857 858 printf("\n"); 859 860 filename = malloc(strlen(inspect_info.file_name) + 8); 861 sprintf(filename, "%s-kernel", inspect_info.file_name); 862 printf("Extracting kernel to \"%s\"...\n", filename); 863 fp = fopen(filename, "w"); 864 if (fp) { 865 if (!fwrite(buf + ntohl(hdr->kernel_ofs), 866 ntohl(hdr->kernel_len), 1, fp)) { 867 ERR("error in fwrite(): %s", strerror(errno)); 868 } 869 fclose(fp); 870 } else { 871 ERR("error in fopen(): %s", strerror(errno)); 872 } 873 free(filename); 874 875 filename = malloc(strlen(inspect_info.file_name) + 8); 876 sprintf(filename, "%s-rootfs", inspect_info.file_name); 877 printf("Extracting rootfs to \"%s\"...\n", filename); 878 fp = fopen(filename, "w"); 879 if (fp) { 880 if (!fwrite(buf + ntohl(hdr->rootfs_ofs), 881 ntohl(hdr->rootfs_len), 1, fp)) { 882 ERR("error in fwrite(): %s", strerror(errno)); 883 } 884 fclose(fp); 885 } else { 886 ERR("error in fopen(): %s", strerror(errno)); 887 } 888 free(filename); 889 } 890 891 out_free_buf: 892 free(buf); 893 out: 894 return ret; 895} 896 897int main(int argc, char *argv[]) 898{ 899 int ret = EXIT_FAILURE; 900 int err; 901 902 FILE *outfile; 903 904 progname = basename(argv[0]); 905 906 while ( 1 ) { 907 int c; 908 909 c = getopt(argc, argv, "a:B:H:E:F:L:V:N:W:ci:k:r:R:o:xhsjv:y:T:e"); 910 if (c == -1) 911 break; 912 913 switch (c) { 914 case 'a': 915 sscanf(optarg, "0x%x", &rootfs_align); 916 break; 917 case 'B': 918 board_id = optarg; 919 break; 920 case 'H': 921 opt_hw_id = optarg; 922 break; 923 case 'E': 924 sscanf(optarg, "0x%x", &kernel_ep); 925 break; 926 case 'F': 927 layout_id = optarg; 928 break; 929 case 'W': 930 opt_hw_rev = optarg; 931 break; 932 case 'L': 933 sscanf(optarg, "0x%x", &kernel_la); 934 break; 935 case 'V': 936 version = optarg; 937 break; 938 case 'v': 939 fw_ver = optarg; 940 break; 941 case 'y': 942 sver = optarg; 943 break; 944 case 'N': 945 vendor = optarg; 946 break; 947 case 'c': 948 combined++; 949 break; 950 case 'k': 951 kernel_info.file_name = optarg; 952 break; 953 case 'r': 954 rootfs_info.file_name = optarg; 955 break; 956 case 'R': 957 sscanf(optarg, "0x%x", &rootfs_ofs); 958 break; 959 case 'o': 960 ofname = optarg; 961 break; 962 case 's': 963 strip_padding = 1; 964 break; 965 case 'i': 966 inspect_info.file_name = optarg; 967 break; 968 case 'j': 969 add_jffs2_eof = 1; 970 break; 971 case 'x': 972 extract = 1; 973 break; 974 case 'T': 975 hdr_ver = atoi(optarg); 976 break; 977 case 'e': 978 endian_swap = true; 979 break; 980 case 'h': 981 usage(EXIT_SUCCESS); 982 break; 983 default: 984 usage(EXIT_FAILURE); 985 break; 986 } 987 } 988 989 ret = check_options(); 990 if (ret) 991 goto out; 992 993 if (!inspect_info.file_name) 994 ret = build_fw(); 995 else 996 ret = inspect_fw(); 997 998 out: 999 return ret; 1000} 1001 1002