• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/firmware-utils/src/mktplinkfw.c

  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 &regions[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