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