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