1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> 4 * 5 * This tool was based on: 6 * TP-Link WR941 V2 firmware checksum fixing tool. 7 * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn> 8 */ 9 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <stdint.h> 13 #include <string.h> 14 #include <byteswap.h> 15 #include <unistd.h> /* for unlink() */ 16 #include <libgen.h> 17 #include <getopt.h> /* for getopt() */ 18 #include <stdarg.h> 19 #include <errno.h> 20 #include <stdbool.h> 21 #include <endian.h> 22 #include <sys/stat.h> 23 24 #include <arpa/inet.h> 25 #include <netinet/in.h> 26 27 #include "md5.h" 28 #include "mktplinkfw-lib.h" 29 30 struct fw_header { 31 uint32_t version; /* 0x00: header version */ 32 char fw_version[48]; /* 0x04: fw version string */ 33 uint32_t hw_id; /* 0x34: hardware id */ 34 uint32_t hw_rev; /* 0x38: FIXME: hardware revision? */ 35 uint32_t hw_ver_add; /* 0x3c: additional hardware version */ 36 uint8_t md5sum1[MD5SUM_LEN]; /* 0x40 */ 37 uint32_t unk2; /* 0x50: 0x00000000 */ 38 uint8_t md5sum2[MD5SUM_LEN]; /* 0x54 */ 39 uint32_t unk3; /* 0x64: 0xffffffff */ 40 41 uint32_t kernel_la; /* 0x68: kernel load address */ 42 uint32_t kernel_ep; /* 0x6c: kernel entry point */ 43 uint32_t fw_length; /* 0x70: total length of the image */ 44 uint32_t kernel_ofs; /* 0x74: kernel data offset */ 45 uint32_t kernel_len; /* 0x78: kernel data length */ 46 uint32_t rootfs_ofs; /* 0x7c: rootfs data offset */ 47 uint32_t rootfs_len; /* 0x80: rootfs data length */ 48 uint32_t boot_ofs; /* 0x84: bootloader offset */ 49 uint32_t boot_len; /* 0x88: bootloader length */ 50 uint16_t unk4; /* 0x8c: 0x55aa */ 51 uint8_t sver_hi; /* 0x8e */ 52 uint8_t sver_lo; /* 0x8f */ 53 uint8_t unk5; /* 0x90: magic: 0xa5 */ 54 uint8_t ver_hi; /* 0x91 */ 55 uint8_t ver_mid; /* 0x92 */ 56 uint8_t ver_lo; /* 0x93 */ 57 uint8_t pad[364]; 58 } __attribute__ ((packed)); 59 60 #define FLAG_LE_KERNEL_LA_EP 0x00000001 /* Little-endian used for kernel load address & entry point */ 61 62 struct board_info { 63 char *id; 64 uint32_t hw_id; 65 uint32_t hw_rev; 66 uint32_t hw_ver_add; 67 char *layout_id; 68 uint32_t hdr_ver; 69 uint32_t flags; 70 }; 71 72 /* 73 * Globals 74 */ 75 char *ofname; 76 char *progname; 77 static char *vendor = "TP-LINK Technologies"; 78 static char *version = "ver. 1.0"; 79 static char *fw_ver = "0.0.0"; 80 static char *sver = "1.0"; 81 static uint32_t hdr_ver = 2; 82 83 static struct board_info custom_board; 84 85 static struct board_info *board; 86 static char *layout_id; 87 struct flash_layout *layout; 88 static char *opt_hw_id; 89 static char *opt_hw_rev; 90 static char *opt_hw_ver_add; 91 static int fw_ver_lo; 92 static int fw_ver_mid; 93 static int fw_ver_hi; 94 static int sver_lo; 95 static int sver_hi; 96 struct file_info kernel_info; 97 static uint32_t kernel_la = 0; 98 static uint32_t kernel_ep = 0; 99 uint32_t kernel_len = 0; 100 struct file_info rootfs_info; 101 uint32_t rootfs_ofs = 0; 102 uint32_t rootfs_align; 103 static struct file_info boot_info; 104 int combined; 105 int strip_padding; 106 int add_jffs2_eof; 107 108 static struct file_info inspect_info; 109 static int extract = 0; 110 111 char md5salt_normal[MD5SUM_LEN] = { 112 0xdc, 0xd7, 0x3a, 0xa5, 0xc3, 0x95, 0x98, 0xfb, 113 0xdc, 0xf9, 0xe7, 0xf4, 0x0e, 0xae, 0x47, 0x37, 114 }; 115 116 char md5salt_boot[MD5SUM_LEN] = { 117 0x8c, 0xef, 0x33, 0x5f, 0xd5, 0xc5, 0xce, 0xfa, 118 0xac, 0x9c, 0x28, 0xda, 0xb2, 0xe9, 0x0f, 0x42, 119 }; 120 121 static struct flash_layout layouts[] = { 122 { 123 .id = "4Mmtk", 124 .fw_max_len = 0x3c0000, 125 .kernel_la = 0x80000000, 126 .kernel_ep = 0x80000000, 127 .rootfs_ofs = 0x140000, 128 }, { 129 .id = "4MLmtk", 130 .fw_max_len = 0x3d0000, 131 .kernel_la = 0x80000000, 132 .kernel_ep = 0x80000000, 133 .rootfs_ofs = 0x140000, 134 }, { 135 .id = "8Mltq", 136 .fw_max_len = 0x7a0000, 137 .kernel_la = 0x80002000, 138 .kernel_ep = 0x80002000, 139 .rootfs_ofs = 0x140000, 140 }, { 141 .id = "16Mltq", 142 .fw_max_len = 0xf90000, 143 .kernel_la = 0x80002000, 144 .kernel_ep = 0x800061b0, 145 .rootfs_ofs = 0x140000, 146 }, { 147 .id = "8Mmtk", 148 .fw_max_len = 0x7a0000, 149 .kernel_la = 0x80000000, 150 .kernel_ep = 0x80000000, 151 .rootfs_ofs = 0x140000, 152 }, { 153 .id = "8MSUmtk", /* Split U-Boot OS */ 154 .fw_max_len = 0x770000, 155 .kernel_la = 0x80000000, 156 .kernel_ep = 0x80000000, 157 .rootfs_ofs = 0x140000, 158 }, { 159 .id = "8MLmtk", 160 .fw_max_len = 0x7b0000, 161 .kernel_la = 0x80000000, 162 .kernel_ep = 0x80000000, 163 .rootfs_ofs = 0x140000, 164 }, { 165 .id = "8Mqca", 166 .fw_max_len = 0x7a0000, 167 .kernel_la = 0x80060000, 168 .kernel_ep = 0x80060000, 169 .rootfs_ofs = 0x140000, 170 }, { 171 .id = "16Mqca", 172 .fw_max_len = 0xf90000, 173 .kernel_la = 0x80060000, 174 .kernel_ep = 0x80060000, 175 .rootfs_ofs = 0x140000, 176 }, { 177 /* terminating entry */ 178 } 179 }; 180 181 static void usage(int status) 182 { 183 FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; 184 185 fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); 186 fprintf(stream, 187 "\n" 188 "Options:\n" 189 " -c use combined kernel image\n" 190 " -e swap endianness in kernel load address and entry point\n" 191 " -E <ep> overwrite kernel entry point with <ep> (hexval prefixed with 0x)\n" 192 " -L <la> overwrite kernel load address with <la> (hexval prefixed with 0x)\n" 193 " -H <hwid> use hardware id specified with <hwid>\n" 194 " -W <hwrev> use hardware revision specified with <hwrev>\n" 195 " -w <hwveradd> use additional hardware version specified with <hwveradd>\n" 196 " -F <id> use flash layout specified with <id>\n" 197 " -k <file> read kernel image from the file <file>\n" 198 " -r <file> read rootfs image from the file <file>\n" 199 " -a <align> align the rootfs start on an <align> bytes boundary\n" 200 " -R <offset> overwrite rootfs offset with <offset> (hexval prefixed with 0x)\n" 201 " -o <file> write output to the file <file>\n" 202 " -s strip padding from the end of the image\n" 203 " -j add jffs2 end-of-filesystem markers\n" 204 " -N <vendor> set image vendor to <vendor>\n" 205 " -T <version> set header version to <version>\n" 206 " -V <version> set image version to <version>\n" 207 " -v <version> set firmware version to <version>\n" 208 " -y <version> set secondary version to <version>\n" 209 " -i <file> inspect given firmware file <file>\n" 210 " -x extract kernel and rootfs while inspecting (requires -i)\n" 211 " -h show this screen\n" 212 ); 213 214 exit(status); 215 } 216 217 static int check_options(void) 218 { 219 int ret; 220 221 if (inspect_info.file_name) { 222 ret = get_file_stat(&inspect_info); 223 if (ret) 224 return ret; 225 226 return 0; 227 } else if (extract) { 228 ERR("no firmware for inspection specified"); 229 return -1; 230 } 231 232 if (opt_hw_id == NULL) { 233 ERR("hardware id must be specified"); 234 return -1; 235 } 236 237 board = &custom_board; 238 239 if (layout_id == NULL) { 240 ERR("flash layout is not specified"); 241 return -1; 242 } 243 244 board->hw_id = strtoul(opt_hw_id, NULL, 0); 245 246 board->hw_rev = 1; 247 board->hw_ver_add = 0; 248 249 if (opt_hw_rev) 250 board->hw_rev = strtoul(opt_hw_rev, NULL, 0); 251 if (opt_hw_ver_add) 252 board->hw_ver_add = strtoul(opt_hw_ver_add, NULL, 0); 253 254 layout = find_layout(layouts, layout_id); 255 if (layout == NULL) { 256 ERR("unknown flash layout \"%s\"", layout_id); 257 return -1; 258 } 259 260 if (!kernel_la) 261 kernel_la = layout->kernel_la; 262 if (!kernel_ep) 263 kernel_ep = layout->kernel_ep; 264 if (!rootfs_ofs) 265 rootfs_ofs = layout->rootfs_ofs; 266 267 if (kernel_info.file_name == NULL) { 268 ERR("no kernel image specified"); 269 return -1; 270 } 271 272 ret = get_file_stat(&kernel_info); 273 if (ret) 274 return ret; 275 276 kernel_len = kernel_info.file_size; 277 278 if (combined) { 279 if (kernel_info.file_size > 280 layout->fw_max_len - sizeof(struct fw_header)) { 281 ERR("kernel image is too big"); 282 return -1; 283 } 284 } else { 285 if (rootfs_info.file_name == NULL) { 286 ERR("no rootfs image specified"); 287 return -1; 288 } 289 290 ret = get_file_stat(&rootfs_info); 291 if (ret) 292 return ret; 293 294 if (rootfs_align) { 295 kernel_len += sizeof(struct fw_header); 296 rootfs_ofs = ALIGN(kernel_len, rootfs_align); 297 kernel_len -= sizeof(struct fw_header); 298 299 DBG("rootfs offset aligned to 0x%u", rootfs_ofs); 300 301 if (kernel_len + rootfs_info.file_size > 302 layout->fw_max_len - sizeof(struct fw_header)) { 303 ERR("images are too big"); 304 return -1; 305 } 306 } else { 307 if (kernel_info.file_size > 308 rootfs_ofs - sizeof(struct fw_header)) { 309 ERR("kernel image is too big"); 310 return -1; 311 } 312 313 if (rootfs_info.file_size > 314 (layout->fw_max_len - rootfs_ofs)) { 315 ERR("rootfs image is too big"); 316 return -1; 317 } 318 } 319 } 320 321 if (ofname == NULL) { 322 ERR("no output file specified"); 323 return -1; 324 } 325 326 ret = sscanf(fw_ver, "%d.%d.%d", &fw_ver_hi, &fw_ver_mid, &fw_ver_lo); 327 if (ret != 3) { 328 ERR("invalid firmware version '%s'", fw_ver); 329 return -1; 330 } 331 332 ret = sscanf(sver, "%d.%d", &sver_hi, &sver_lo); 333 if (ret != 2) { 334 ERR("invalid secondary version '%s'", sver); 335 return -1; 336 } 337 338 return 0; 339 } 340 341 void fill_header(char *buf, int len) 342 { 343 struct fw_header *hdr = (struct fw_header *)buf; 344 unsigned ver_len; 345 346 memset(hdr, '\xff', sizeof(struct fw_header)); 347 348 hdr->version = htonl(bswap_32(hdr_ver)); 349 ver_len = strlen(version); 350 if (ver_len > (sizeof(hdr->fw_version) - 1)) 351 ver_len = sizeof(hdr->fw_version) - 1; 352 353 memcpy(hdr->fw_version, version, ver_len); 354 hdr->fw_version[ver_len] = 0; 355 356 hdr->hw_id = htonl(board->hw_id); 357 hdr->hw_rev = htonl(board->hw_rev); 358 hdr->hw_ver_add = htonl(board->hw_ver_add); 359 360 if (boot_info.file_size == 0) { 361 memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1)); 362 hdr->boot_ofs = htonl(0); 363 hdr->boot_len = htonl(0); 364 } else { 365 memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1)); 366 hdr->boot_ofs = htonl(rootfs_ofs + rootfs_info.file_size); 367 hdr->boot_len = htonl(rootfs_info.file_size); 368 } 369 370 hdr->kernel_la = htonl(kernel_la); 371 hdr->kernel_ep = htonl(kernel_ep); 372 hdr->fw_length = htonl(layout->fw_max_len); 373 hdr->kernel_ofs = htonl(sizeof(struct fw_header)); 374 hdr->kernel_len = htonl(kernel_len); 375 if (!combined) { 376 hdr->rootfs_ofs = htonl(rootfs_ofs); 377 hdr->rootfs_len = htonl(rootfs_info.file_size); 378 } 379 380 hdr->boot_ofs = htonl(0); 381 hdr->boot_len = htonl(boot_info.file_size); 382 383 hdr->unk2 = htonl(0); 384 hdr->unk3 = htonl(0xffffffff); 385 hdr->unk4 = htons(0x55aa); 386 hdr->unk5 = 0xa5; 387 388 hdr->sver_hi = sver_hi; 389 hdr->sver_lo = sver_lo; 390 391 hdr->ver_hi = fw_ver_hi; 392 hdr->ver_mid = fw_ver_mid; 393 hdr->ver_lo = fw_ver_lo; 394 395 if (board->flags & FLAG_LE_KERNEL_LA_EP) { 396 hdr->kernel_la = bswap_32(hdr->kernel_la); 397 hdr->kernel_ep = bswap_32(hdr->kernel_ep); 398 } 399 400 get_md5(buf, len, hdr->md5sum1); 401 } 402 403 static int inspect_fw(void) 404 { 405 char *buf; 406 struct fw_header *hdr; 407 uint8_t md5sum[MD5SUM_LEN]; 408 struct board_info *board; 409 int ret = EXIT_FAILURE; 410 411 buf = malloc(inspect_info.file_size); 412 if (!buf) { 413 ERR("no memory for buffer!\n"); 414 goto out; 415 } 416 417 ret = read_to_buf(&inspect_info, buf); 418 if (ret) 419 goto out_free_buf; 420 hdr = (struct fw_header *)buf; 421 422 board = &custom_board; 423 424 if (board->flags & FLAG_LE_KERNEL_LA_EP) { 425 hdr->kernel_la = bswap_32(hdr->kernel_la); 426 hdr->kernel_ep = bswap_32(hdr->kernel_ep); 427 } 428 429 inspect_fw_pstr("File name", inspect_info.file_name); 430 inspect_fw_phexdec("File size", inspect_info.file_size); 431 432 switch(bswap_32(ntohl(hdr->version))) { 433 case 2: 434 case 3: 435 break; 436 default: 437 ERR("file does not seem to have V2/V3 header!\n"); 438 goto out_free_buf; 439 } 440 441 inspect_fw_phexdec("Version 2 Header size", sizeof(struct fw_header)); 442 443 memcpy(md5sum, hdr->md5sum1, sizeof(md5sum)); 444 if (ntohl(hdr->boot_len) == 0) 445 memcpy(hdr->md5sum1, md5salt_normal, sizeof(md5sum)); 446 else 447 memcpy(hdr->md5sum1, md5salt_boot, sizeof(md5sum)); 448 get_md5(buf, inspect_info.file_size, hdr->md5sum1); 449 450 if (memcmp(md5sum, hdr->md5sum1, sizeof(md5sum))) { 451 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(*ERROR*)"); 452 inspect_fw_pmd5sum(" --> expected", hdr->md5sum1, ""); 453 } else { 454 inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(ok)"); 455 } 456 if (ntohl(hdr->unk2) != 0) 457 inspect_fw_phexdec("Unknown value 2", hdr->unk2); 458 inspect_fw_pmd5sum("Header MD5Sum2", hdr->md5sum2, 459 "(purpose yet unknown, unchecked here)"); 460 461 if (ntohl(hdr->unk3) != 0xffffffff) 462 inspect_fw_phexdec("Unknown value 3", hdr->unk3); 463 464 if (ntohs(hdr->unk4) != 0x55aa) 465 inspect_fw_phexdec("Unknown value 4", hdr->unk4); 466 467 if (hdr->unk5 != 0xa5) 468 inspect_fw_phexdec("Unknown value 5", hdr->unk5); 469 470 printf("\n"); 471 472 inspect_fw_pstr("Firmware version", hdr->fw_version); 473 inspect_fw_phex("Hardware ID", ntohl(hdr->hw_id)); 474 inspect_fw_phex("Hardware Revision", 475 ntohl(hdr->hw_rev)); 476 inspect_fw_phex("Additional HW Version", 477 ntohl(hdr->hw_ver_add)); 478 479 printf("%-23s: %d.%d.%d-%d.%d\n", "Software version", 480 hdr->ver_hi, hdr->ver_mid, hdr->ver_lo, 481 hdr->sver_hi, hdr->sver_lo); 482 483 printf("\n"); 484 485 inspect_fw_phexdec("Kernel data offset", 486 ntohl(hdr->kernel_ofs)); 487 inspect_fw_phexdec("Kernel data length", 488 ntohl(hdr->kernel_len)); 489 inspect_fw_phex("Kernel load address", 490 ntohl(hdr->kernel_la)); 491 inspect_fw_phex("Kernel entry point", 492 ntohl(hdr->kernel_ep)); 493 inspect_fw_phexdec("Rootfs data offset", 494 ntohl(hdr->rootfs_ofs)); 495 inspect_fw_phexdec("Rootfs data length", 496 ntohl(hdr->rootfs_len)); 497 inspect_fw_phexdec("Boot loader data offset", 498 ntohl(hdr->boot_ofs)); 499 inspect_fw_phexdec("Boot loader data length", 500 ntohl(hdr->boot_len)); 501 inspect_fw_phexdec("Total firmware length", 502 ntohl(hdr->fw_length)); 503 504 if (extract) { 505 FILE *fp; 506 char *filename; 507 508 printf("\n"); 509 510 filename = malloc(strlen(inspect_info.file_name) + 8); 511 sprintf(filename, "%s-kernel", inspect_info.file_name); 512 printf("Extracting kernel to \"%s\"...\n", filename); 513 fp = fopen(filename, "w"); 514 if (fp) { 515 if (!fwrite(buf + ntohl(hdr->kernel_ofs), 516 ntohl(hdr->kernel_len), 1, fp)) { 517 ERR("error in fwrite(): %s", strerror(errno)); 518 } 519 fclose(fp); 520 } else { 521 ERR("error in fopen(): %s", strerror(errno)); 522 } 523 free(filename); 524 525 filename = malloc(strlen(inspect_info.file_name) + 8); 526 sprintf(filename, "%s-rootfs", inspect_info.file_name); 527 printf("Extracting rootfs to \"%s\"...\n", filename); 528 fp = fopen(filename, "w"); 529 if (fp) { 530 if (!fwrite(buf + ntohl(hdr->rootfs_ofs), 531 ntohl(hdr->rootfs_len), 1, fp)) { 532 ERR("error in fwrite(): %s", strerror(errno)); 533 } 534 fclose(fp); 535 } else { 536 ERR("error in fopen(): %s", strerror(errno)); 537 } 538 free(filename); 539 } 540 541 out_free_buf: 542 free(buf); 543 out: 544 return ret; 545 } 546 547 int main(int argc, char *argv[]) 548 { 549 int ret = EXIT_FAILURE; 550 551 progname = basename(argv[0]); 552 553 while ( 1 ) { 554 int c; 555 556 c = getopt(argc, argv, "a:H:E:F:L:V:N:W:w:ci:k:r:R:o:xhsjv:y:T:e"); 557 if (c == -1) 558 break; 559 560 switch (c) { 561 case 'a': 562 sscanf(optarg, "0x%x", &rootfs_align); 563 break; 564 case 'H': 565 opt_hw_id = optarg; 566 break; 567 case 'E': 568 sscanf(optarg, "0x%x", &kernel_ep); 569 break; 570 case 'F': 571 layout_id = optarg; 572 break; 573 case 'W': 574 opt_hw_rev = optarg; 575 break; 576 case 'w': 577 opt_hw_ver_add = optarg; 578 break; 579 case 'L': 580 sscanf(optarg, "0x%x", &kernel_la); 581 break; 582 case 'V': 583 version = optarg; 584 break; 585 case 'v': 586 fw_ver = optarg; 587 break; 588 case 'y': 589 sver = optarg; 590 break; 591 case 'N': 592 vendor = optarg; 593 break; 594 case 'c': 595 combined++; 596 break; 597 case 'k': 598 kernel_info.file_name = optarg; 599 break; 600 case 'r': 601 rootfs_info.file_name = optarg; 602 break; 603 case 'R': 604 sscanf(optarg, "0x%x", &rootfs_ofs); 605 break; 606 case 'o': 607 ofname = optarg; 608 break; 609 case 's': 610 strip_padding = 1; 611 break; 612 case 'i': 613 inspect_info.file_name = optarg; 614 break; 615 case 'j': 616 add_jffs2_eof = 1; 617 break; 618 case 'x': 619 extract = 1; 620 break; 621 case 'T': 622 hdr_ver = atoi(optarg); 623 break; 624 case 'e': 625 custom_board.flags = FLAG_LE_KERNEL_LA_EP; 626 break; 627 case 'h': 628 usage(EXIT_SUCCESS); 629 break; 630 default: 631 usage(EXIT_FAILURE); 632 break; 633 } 634 } 635 636 ret = check_options(); 637 if (ret) 638 goto out; 639 640 if (!inspect_info.file_name) 641 ret = build_fw(sizeof(struct fw_header)); 642 else 643 ret = inspect_fw(); 644 645 out: 646 return ret; 647 } 648 649
This page was automatically generated by LXR 0.3.1. • OpenWrt