Modify

Opened 6 years ago

Closed 6 years ago

Last modified 2 years ago

#7525 closed enhancement (fixed)

Enhance mktplinkfw utility with firmware inspection, kernel/rootfs extract and offset override features

Reported by: Pieter "Fate" Hollants <pieter@…> Owned by: juhosg
Priority: normal Milestone: Barrier Breaker 14.07
Component: toolchain Version: Trunk
Keywords: mktplinkfw inspect extract offset Cc:

Description

Attached patch enhances the mktplinkfw utility:

  1. existing firmware files in TP-Link format can now be inspected, showing eg. the used offsets
  2. optionally the kernel and rootfs parts can be extracted (saves calculations for dd)
  3. when generating firmwares, kernel entry point, load address and rootfs offset can be overwritten (for experienced users)

Attachments (2)

7525-mktplinkfw_enhancements.diff (9.9 KB) - added by Pieter "Fate" Hollants <pieter@…> 6 years ago.
Patch to extend mktplinkfw
7525-mktplinkfw_enhancements_2.diff (11.3 KB) - added by Pieter "Fate" Hollants <pieter@…> 6 years ago.
Revised version of patch adapted to OpenWrt coding style and some smaller changes (fixes, wording)

Download all attachments as: .zip

Change History (6)

Changed 6 years ago by Pieter "Fate" Hollants <pieter@…>

Patch to extend mktplinkfw

comment:1 Changed 6 years ago by juhosg

  • Milestone changed from Backfire 10.03.1 to Kamikaze
  • Owner changed from developers to juhosg
  • Status changed from new to accepted
Index: tools/firmware-utils/src/mktplinkfw.c
===================================================================
--- tools/firmware-utils/src/mktplinkfw.c	(Revision 21987)
+++ tools/firmware-utils/src/mktplinkfw.c	(Arbeitskopie)
@@ -92,11 +92,17 @@
 static char *board_id;
 static struct board_info *board;
 static struct file_info kernel_info;
+static uint32_t kernel_la = 0;
+static uint32_t kernel_ep = 0;
 static struct file_info rootfs_info;
+static uint32_t rootfs_ofs = 0;
 static struct file_info boot_info;
 static int combined;
 static int strip_padding;
 
+static struct file_info inspect_info;
+static int extract;
+
 char md5salt_normal[MD5SUM_LEN] = {
 	0xdc, 0xd7, 0x3a, 0xa5, 0xc3, 0x95, 0x98, 0xfb,
 	0xdd, 0xf9, 0xe7, 0xf4, 0x0e, 0xae, 0x47, 0x38,
@@ -205,6 +211,20 @@
 	return ret;
 }
 
+static struct board_info *find_board_by_hwid(uint32_t hw_id)
+{
+	struct board_info *board;
+
+	for (board = boards; board->id != NULL; board++)
+	{

Please use the existing coding style.

+		if (hw_id == board->hw_id)
+			return board;
+	};
+
+	return NULL;
+}
+
+
 static void usage(int status)
 {
 	FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
@@ -216,12 +236,17 @@
 "Options:\n"
 "  -B <board>      create image for the board specified with <board>\n"
 "  -c              use combined kernel image\n"
+"  -E <ep>         overwrite kernel entry point with <ep> (hexval prefixed with 0x)\n"
+"  -L <la>         overwrite kernel load address with <la> (hexval prefixed with 0x)\n"
 "  -k <file>       read kernel image from the file <file>\n"
 "  -r <file>       read rootfs image from the file <file>\n"
+"  -R <offset>     overwrite rootfs offset with <offset> (hexval prefixed with 0x)\n"
 "  -o <file>       write output to the file <file>\n"
 "  -s              strip padding from the end of the image\n"
 "  -N <vendor>     set image vendor to <vendor>\n"
 "  -V <version>    set image version to <version>\n"
+"  -i <file>       inspect given firmware file <file>\n"
+"  -x              extract kernel and rootfs while inspecting (requires -i)\n"
 "  -h              show this screen\n"
 	);
 
@@ -285,6 +310,18 @@
 {
 	int ret;
 
+	if (inspect_info.file_name) {
+		ret = get_file_stat(&inspect_info);
+		if (ret)
+			return ret;
+
+		return 0;
+	}
+	else if (extract) {

Same here and at some other places in the whole patch.

+		ERR("no firmware for inspection specified");
+		return -1;
+	}
+
 	if (board_id == NULL) {
 		ERR("no board specified");
 		return -1;
@@ -295,6 +332,12 @@
 		ERR("unknown/unsupported board id \"%s\"", board_id);
 		return -1;
 	}
+	if (!kernel_la)
+		kernel_la = board->kernel_la;
+	if (!kernel_ep)
+		kernel_ep = board->kernel_ep;
+	if (!rootfs_ofs)
+		rootfs_ofs = board->rootfs_ofs;
 
 	if (kernel_info.file_name == NULL) {
 		ERR("no kernel image specified");
@@ -313,7 +356,7 @@
 		}
 	} else {
 		if (kernel_info.file_size >
-		    board->rootfs_ofs - sizeof(struct fw_header)) {
+		    rootfs_ofs - sizeof(struct fw_header)) {
 			ERR("kernel image is too big");
 			return -1;
 		}
@@ -327,7 +370,7 @@
 			return ret;
 
 		if (rootfs_info.file_size >
-                    (board->fw_max_len - board->rootfs_ofs)) {
+                    (board->fw_max_len - rootfs_ofs)) {
 			ERR("rootfs image is too big");
 			return -1;
 		}
@@ -358,13 +401,13 @@
 	else
 		memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1));
 
-	hdr->kernel_la = HOST_TO_BE32(board->kernel_la);
-	hdr->kernel_ep = HOST_TO_BE32(board->kernel_ep);
+	hdr->kernel_la = HOST_TO_BE32(kernel_la);
+	hdr->kernel_ep = HOST_TO_BE32(kernel_ep);
 	hdr->fw_length = HOST_TO_BE32(board->fw_max_len);
 	hdr->kernel_ofs = HOST_TO_BE32(sizeof(struct fw_header));
 	hdr->kernel_len = HOST_TO_BE32(kernel_info.file_size);
 	if (!combined) {
-		hdr->rootfs_ofs = HOST_TO_BE32(board->rootfs_ofs);
+		hdr->rootfs_ofs = HOST_TO_BE32(rootfs_ofs);
 		hdr->rootfs_len = HOST_TO_BE32(rootfs_info.file_size);
 	}
 
@@ -428,12 +471,12 @@
 	writelen = kernel_info.file_size;
 
 	if (!combined) {
-		p = buf + board->rootfs_ofs;
+		p = buf + rootfs_ofs;
 		ret = read_to_buf(&rootfs_info, p);
 		if (ret)
 			goto out_free_buf;
 
-		writelen = board->rootfs_ofs + rootfs_info.file_size;
+		writelen = rootfs_ofs + rootfs_info.file_size;
 	}
 
 	if (!strip_padding)
@@ -452,6 +495,127 @@
 	return ret;
 }
 
+static int inspect_fw(void)
+{
+	char *buf;
+	struct fw_header *hdr;
+	uint8_t md5sum[MD5SUM_LEN];
+	struct board_info *board;
+	int i;
+	int ret = EXIT_FAILURE;
+
+	buf = malloc(inspect_info.file_size);
+	if (!buf) {
+		ERR("no memory for buffer!\n");
+		goto out;
+	}
+
+	ret = read_to_buf(&inspect_info, buf);
+	if (ret)
+		goto out_free_buf;
+	hdr = (struct fw_header *)buf;
+
+	printf("File name              : %s\nFile size              : 0x%08x / %8d bytes\n",
+	 inspect_info.file_name, inspect_info.file_size, inspect_info.file_size);

Don't print multiple lines in one statement, and please use a helper function to print the strings and numbers with the same format. That would be easier to read, and you would not have to pass every number twice.

+
+	if (BE32_TO_HOST(hdr->version) != HEADER_VERSION_V1) {
+		ERR("file does not seem to have V1 header!\n");
+		goto out_free_buf;
+	}
+
+	printf("Version 1 Header size  : 0x%08x / %8d bytes\n", sizeof(struct fw_header), sizeof(struct fw_header));
+
+	if (BE32_TO_HOST(hdr->unk1) != 0)
+		printf("Unknown value 1        : 0x%08x / %8d (usually zero)\n", hdr->unk1, hdr->unk1);
+
+	memcpy(md5sum, hdr->md5sum1, sizeof(md5sum));
+
+	if (BE32_TO_HOST(hdr->boot_len) == 0)
+		memcpy(hdr->md5sum1, md5salt_normal, sizeof(md5sum));
+	else
+		memcpy(hdr->md5sum1, md5salt_boot, sizeof(md5sum));
+	get_md5(buf, inspect_info.file_size, hdr->md5sum1);
+
+	printf("Header MD5Sum1         :");
+	for (i=0; i<MD5SUM_LEN; i++)
+		printf(" %02x", md5sum[i]);
+	if (memcmp(md5sum, hdr->md5sum1, sizeof(md5sum))) {
+		printf(" ERROR!\n");
+		printf("          --> expected :");
+		for (i=0; i<MD5SUM_LEN; i++)
+			printf(" %02x", hdr->md5sum1[i]);
+		printf("\n");
+	}
+	else
+		printf(" (ok)\n");
+
+	if (BE32_TO_HOST(hdr->unk2) != 0)
+		printf("Unknown value 2        : 0x%08x / %8d (usually zero)\n", hdr->unk2, hdr->unk2);
+
+	printf("Header MD5Sum2         :");
+	for (i=0; i<MD5SUM_LEN; i++)
+		printf(" %02x", hdr->md5sum2[i]);
+	printf(" (purpose yet unknown, unchecked here)\n");
+
+	printf("\nVendor name            : %s\n", hdr->vendor_name);
+	printf("Firmware version       : %s\n", hdr->fw_version);
+	printf("Hardware ID            : 0x%08x", BE32_TO_HOST(hdr->hw_id));
+	if (board = find_board_by_hwid(BE32_TO_HOST(hdr->hw_id)))
+		printf(" (%s)\n", board->id);
+	else
+		printf(" (unknown)\n");
+	
+	printf("Hardware Revision      : 0x%08x ", BE32_TO_HOST(hdr->hw_rev));
+	board ? (BE32_TO_HOST(hdr->hw_rev) == board->hw_rev ? printf("(ok)\n") : printf("(expected: 0x%08x)\n", board->hw_rev)) : printf("\n");
+
+	printf("\nKernel data offset     : 0x%08x / %8d bytes\n", BE32_TO_HOST(hdr->kernel_ofs), BE32_TO_HOST(hdr->kernel_ofs));
+	printf("Kernel data length     : 0x%08x / %8d bytes\n", BE32_TO_HOST(hdr->kernel_len), BE32_TO_HOST(hdr->kernel_len));
+	printf("Kernel load address    : 0x%08x ", BE32_TO_HOST(hdr->kernel_la));
+	board ? (BE32_TO_HOST(hdr->kernel_la) == board->kernel_la ? printf("(OpenWrt's value)\n") : printf("                 (OpenWrt uses: 0x%08x)\n", board->kernel_la)) : printf("\n");
+	printf("Kernel entry point     : 0x%08x ", BE32_TO_HOST(hdr->kernel_ep));
+	board ? (BE32_TO_HOST(hdr->kernel_ep) == board->kernel_ep ? printf("(OpenWrt's value)\n") : printf("                 (OpenWrt uses: 0x%08x)\n", board->kernel_ep)) : printf("\n");
+	printf("Rootfs data offset     : 0x%08x / %8d bytes ", BE32_TO_HOST(hdr->rootfs_ofs), BE32_TO_HOST(hdr->rootfs_ofs));
+	board ? (BE32_TO_HOST(hdr->rootfs_ofs) == board->rootfs_ofs ? printf("(OpenWrt's value)\n") : printf("(OpenWrt uses: 0x%08x / %8d bytes)\n", board->rootfs_ofs, board->rootfs_ofs)) : printf("\n");
+	printf("Rootfs data length     : 0x%08x / %8d bytes\n", BE32_TO_HOST(hdr->rootfs_len), BE32_TO_HOST(hdr->rootfs_len));
+	printf("Boot loader data offset: 0x%08x / %8d bytes\n", BE32_TO_HOST(hdr->boot_ofs), BE32_TO_HOST(hdr->boot_ofs));
+	printf("Boot loader data length: 0x%08x / %8d bytes\n", BE32_TO_HOST(hdr->boot_len), BE32_TO_HOST(hdr->boot_len));
+	printf("Total firmware length  : 0x%08x / %8d bytes\n", BE32_TO_HOST(hdr->fw_length), BE32_TO_HOST(hdr->fw_length));

These long lines are ugly a bit. Can you reformat them?

+
+	if (extract) {
+		FILE *fp;
+		char *filename;
+
+		filename = malloc(strlen(inspect_info.file_name) + 8);
+		sprintf(filename, "%s-kernel", inspect_info.file_name);
+		printf("\nExtracting kernel to \"%s\"...\n", filename);
+		fp = fopen(filename, "w");
+		if (fp)	{
+			fwrite(buf + BE32_TO_HOST(hdr->kernel_ofs), BE32_TO_HOST(hdr->kernel_len), 1, fp);
+			fclose(fp);
+		}
+		else
+			printf("error: %s!\n", strerror(errno));
+		free(filename);
+
+		filename = malloc(strlen(inspect_info.file_name) + 8);
+		sprintf(filename, "%s-rootfs", inspect_info.file_name);
+		printf("Extracting rootfs to \"%s\"...\n", filename);
+		fp = fopen(filename, "w");
+		if (fp)	{
+			fwrite(buf + BE32_TO_HOST(hdr->rootfs_ofs), BE32_TO_HOST(hdr->rootfs_len), 1, fp);
+			fclose(fp);
+		}
+		else
+			printf("error: %s!\n", strerror(errno));
+		free(filename);
+	}
+
+ out_free_buf:
+	free(buf);
+ out:
+	return ret;
+}
+
 int main(int argc, char *argv[])
 {
 	int ret = EXIT_FAILURE;
@@ -464,7 +628,7 @@
 	while ( 1 ) {
 		int c;
 
-		c = getopt(argc, argv, "B:V:N:ck:r:o:hs");
+		c = getopt(argc, argv, "B:E:L:V:N:ci:k:r:R:o:xhs");
 		if (c == -1)
 			break;
 
@@ -472,6 +636,12 @@
 		case 'B':
 			board_id = optarg;
 			break;
+		case 'E':
+			sscanf(optarg, "0x%x", &kernel_ep);
+			break;
+		case 'L':
+			sscanf(optarg, "0x%x", &kernel_la);
+			break;
 		case 'V':
 			version = optarg;
 			break;
@@ -487,12 +657,21 @@
 		case 'r':
 			rootfs_info.file_name = optarg;
 			break;
+		case 'R':
+			sscanf(optarg, "0x%x", &rootfs_ofs);
+			break;
 		case 'o':
 			ofname = optarg;
 			break;
 		case 's':
 			strip_padding = 1;
 			break;
+		case 'i':
+			inspect_info.file_name = optarg;
+			break;
+		case 'x':
+			extract = 1;
+			break;
 		case 'h':
 			usage(EXIT_SUCCESS);
 			break;
@@ -506,7 +685,10 @@
 	if (ret)
 		goto out;
 
-	ret = build_fw();
+	if (!inspect_info.file_name)
+		ret = build_fw();
+	else
+		ret = inspect_fw();
 
  out:
 	return ret;

Changed 6 years ago by Pieter "Fate" Hollants <pieter@…>

Revised version of patch adapted to OpenWrt coding style and some smaller changes (fixes, wording)

comment:2 follow-up: Changed 6 years ago by Pieter "Fate" Hollants <pieter@…>

Thanks for your comments, I tried to incorporate them. Let me know if there are still differences to the OpenWrt coding style.

comment:3 in reply to: ↑ 2 Changed 6 years ago by juhosg

  • Resolution set to fixed
  • Status changed from accepted to closed

Replying to Pieter "Fate" Hollants <pieter@…>:

Thanks for your comments, I tried to incorporate them. Let me know if there are still differences to the OpenWrt coding style.

It looks much better now. Thanks! Applied in r22169.

comment:4 Changed 2 years ago by jow

  • Milestone changed from Attitude Adjustment 12.09 to Barrier Breaker 14.07

Milestone Attitude Adjustment 12.09 deleted

Add Comment

Modify Ticket

Action
as closed .
The resolution will be deleted. Next status will be 'reopened'.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.