[openwrt/openwrt] firmware: add JBOOT based devices config extractor

LEDE Commits lede-commits at lists.infradead.org
Sun Mar 18 23:51:55 PDT 2018


mkresin pushed a commit to openwrt/openwrt.git, branch master:
https://git.lede-project.org/5323477184cf39f4f20003142ce320ac1d626863

commit 5323477184cf39f4f20003142ce320ac1d626863
Author: Pawel Dembicki <paweldembicki at gmail.com>
AuthorDate: Thu Mar 1 20:11:01 2018 +0100

    firmware: add JBOOT based devices config extractor
    
    Adds tool to extract MAC and pre-calibration data required for JBOOT
    based D-Link routers.
    
    Signed-off-by: Pawel Dembicki <paweldembicki at gmail.com>
---
 package/utils/jboot-tools/Makefile                |  28 ++
 package/utils/jboot-tools/README.md               |  46 +++
 package/utils/jboot-tools/src/CMakeLists.txt      |  11 +
 package/utils/jboot-tools/src/jboot_config_read.c | 427 ++++++++++++++++++++++
 4 files changed, 512 insertions(+)

diff --git a/package/utils/jboot-tools/Makefile b/package/utils/jboot-tools/Makefile
new file mode 100644
index 0000000..ce9758b
--- /dev/null
+++ b/package/utils/jboot-tools/Makefile
@@ -0,0 +1,28 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=jboot-tools
+PKG_RELEASE:=1
+CMAKE_INSTALL:=1
+PKG_FLAGS:=nonshared
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/cmake.mk
+
+define Package/jboot-tools
+  SECTION:=firmware
+  CATEGORY:=Firmware
+  DEPENDS:=@TARGET_ramips
+  TITLE:=Utilites for accessing JBOOT based D-Link devices Calibration data
+endef
+
+define Package/jboot-tools/description
+ This package contains:
+ jboot_config_read.c: partially read the config partition of JBOOT based D-Link devices.
+endef
+
+define Package/jboot-tools/install
+	$(INSTALL_DIR) $(1)/usr/bin
+	$(INSTALL_BIN) $(PKG_BUILD_DIR)/jboot_config_read $(1)/usr/bin/
+endef
+
+$(eval $(call BuildPackage,jboot-tools))
diff --git a/package/utils/jboot-tools/README.md b/package/utils/jboot-tools/README.md
new file mode 100644
index 0000000..0d1aeac
--- /dev/null
+++ b/package/utils/jboot-tools/README.md
@@ -0,0 +1,46 @@
+Userspace utilties for jboot based devices config partition read
+
+## Building
+
+```
+mkdir build
+cd build
+cmake /path/to/jboot-tools
+make
+```
+
+## Usage
+
+All command line parameters are documented:
+```
+jboot_config_read -h
+```
+
+Show all stored MACs:
+```
+jboot_config_read -m -i PATH_TO_CONFIG_PARTITIO
+```
+
+Extract wifi eeprom data:
+```
+jboot_config_read  -i PATH_TO_CONFIG_PARTITION -e OUTPUT_PATH
+```
+
+
+## LICENSE
+
+See `LICENSE`:
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
diff --git a/package/utils/jboot-tools/src/CMakeLists.txt b/package/utils/jboot-tools/src/CMakeLists.txt
new file mode 100644
index 0000000..98fbab3
--- /dev/null
+++ b/package/utils/jboot-tools/src/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 2.6)
+
+PROJECT(jboot-tools C)
+ADD_DEFINITIONS(-Wall -Werror --std=gnu99 -Wmissing-declarations)
+
+SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
+
+ADD_EXECUTABLE(jboot_config_read jboot_config_read.c)
+TARGET_LINK_LIBRARIES(jboot_config_read)
+
+INSTALL(TARGETS jboot_config_read RUNTIME DESTINATION bin)
diff --git a/package/utils/jboot-tools/src/jboot_config_read.c b/package/utils/jboot-tools/src/jboot_config_read.c
new file mode 100644
index 0000000..c65b091
--- /dev/null
+++ b/package/utils/jboot-tools/src/jboot_config_read.c
@@ -0,0 +1,427 @@
+/*
+ * jboot_config_read
+ *
+ * Copyright (C) 2018 Paweł Dembicki <paweldembicki at gmail.com>
+ *
+ * This tool is based on mkdlinkfw.
+ * Copyright (C) 2018 Paweł Dembicki <paweldembicki at gmail.com>
+ * Copyright (C) 2009 Gabor Juhos <juhosg at openwrt.org>
+ * Copyright (C) 2008,2009 Wang Jian <lark at linux.net.cn>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>		/* for unlink() */
+#include <libgen.h>
+#include <getopt.h>		/* for getopt() */
+#include <stdarg.h>
+#include <stdbool.h>
+#include <endian.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+
+
+#define ERR(fmt, ...) do { \
+	fflush(0); \
+	fprintf(stderr, "[%s] *** error: " fmt "\n", \
+			progname, ## __VA_ARGS__); \
+} while (0)
+
+#define ERRS(fmt, ...) do { \
+	int save = errno; \
+	fflush(0); \
+	fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \
+			progname, ## __VA_ARGS__, strerror(save)); \
+} while (0)
+
+#define VERBOSE(fmt, ...) do { \
+	if (verbose) { \
+		fprintf(stdout, "[%s] " fmt "\n", progname, ## __VA_ARGS__); \
+	} \
+} while (0)
+
+#define STAG_SIZE 16
+#define STAG_MAGIC	0x2B24
+#define STAG_ID		0x02
+
+#define CSXF_SIZE 16
+#define CSXF_MAGIC	0x5343
+
+#define MAX_DATA_HEADER 128
+#define DATA_HEADER_UNKNOWN 0x8000
+#define DATA_HEADER_EEPROM 0xF5
+#define DATA_HEADER_CONFIG 0x42
+#define DATA_HEADER_SIZE 6
+
+#define DATA_HEADER_ID_MAC 0x30
+#define DATA_HEADER_ID_CAL 0x0
+
+/* ARM update header 2.0
+ * used only in factory images to erase and flash selected area
+ */
+struct stag_header {		/* used only of sch2 wrapped kernel data */
+	uint8_t cmark;		/* in factory 0xFF ,in sysuograde must be the same as id */
+	uint8_t id;		/* 0x04 */
+	uint16_t magic;		/* magic 0x2B24 */
+	uint32_t time_stamp;	/* timestamp calculated in jboot way */
+	uint32_t image_length;	/* lentgh of kernel + sch2 header */
+	uint16_t image_checksum;	/* negated jboot_checksum of sch2 + kernel */
+	uint16_t tag_checksum;	/* negated jboot_checksum of stag header data */
+};
+
+struct csxf_header {
+	uint16_t magic;		/* 0x5343, 'CS' in little endian */
+	uint16_t checksum;	/* checksum, include header & body */
+	uint32_t body_length;	/* length of body */
+	uint8_t body_encoding;	/* encoding method of body */
+	uint8_t reserved[3];
+	uint32_t raw_length;	/* length of body before encoded */
+};
+
+struct data_header {
+	uint8_t id;
+	uint8_t type;		/* 0x42xx for config 0xF5xx for eeprom */
+	uint16_t unknown;
+	uint16_t length;	/* length of body */
+	uint8_t data[];		/* encoding method of body */
+};
+
+/* globals */
+
+char *ofname;
+char *ifname;
+char *progname;
+
+uint8_t *buffer;
+uint32_t config_size;
+
+uint32_t start_offset;
+uint8_t mac_duplicate;
+uint8_t mac_print;
+uint8_t print_data;
+uint8_t verbose;
+
+static void usage(int status)
+{
+	fprintf(stderr, "Usage: %s [OPTIONS...]\n", progname);
+	fprintf(stderr,
+		"\n"
+		"Options:\n"
+		"  -i <file>       config partition file <file>\n"
+		"  -m              print mac address\n"
+		"  -e <file>       save eeprom calibration data image to the file <file>\n"
+		"  -o <offset>     set start offset to <ofset>\n"
+		"  -p              print config data\n"
+		"  -v              verbose\n"
+		"  -h              show this screen\n");
+
+	exit(status);
+}
+
+static void print_data_header(struct data_header *printed_header)
+{
+	printf("id: 0x%02X "
+	       "type: 0x%02X "
+	       "unknown: 0x%04X "
+	       "length: 0x%04X\n"
+	       "data: ",
+	       printed_header->id,
+	       printed_header->type,
+	       printed_header->unknown, printed_header->length);
+
+	for (uint16_t i = 0; i < printed_header->length; i++)
+		printf("%02X ", printed_header->data[i]);
+
+	printf("\n");
+
+}
+
+static uint16_t jboot_checksum(uint16_t start_val, uint16_t *data, int size)
+{
+	uint32_t counter = start_val;
+	uint16_t *ptr = data;
+
+	while (size > 1) {
+		counter += *ptr;
+		++ptr;
+		while (counter >> 16)
+			counter = (uint16_t) counter + (counter >> 16);
+		size -= 2;
+	}
+	if (size > 0) {
+		counter += *(uint8_t *) ptr;
+		counter -= 0xFF;
+	}
+	while (counter >> 16)
+		counter = (uint16_t) counter + (counter >> 16);
+	return counter;
+}
+
+static int find_header(uint8_t *buf, uint32_t buf_size,
+		       struct data_header **data_table)
+{
+	uint8_t *tmp_buf = buf + start_offset;
+	uint8_t tmp_hdr[4] = { STAG_ID, STAG_ID, (STAG_MAGIC & 0xFF), (STAG_MAGIC >> 8) };
+	struct csxf_header *tmp_csxf_header;
+	uint16_t tmp_checksum = 0;
+	uint16_t data_header_counter = 0;
+	int ret = EXIT_FAILURE;
+
+	VERBOSE("Looking for STAG header!");
+
+	while ((uint32_t) tmp_buf - (uint32_t) buf <= buf_size) {
+		if (!memcmp(tmp_buf, tmp_hdr, 4)) {
+			if (((struct stag_header *)tmp_buf)->tag_checksum ==
+			    (uint16_t) ~jboot_checksum(0, (uint16_t *) tmp_buf,
+							STAG_SIZE - 2)) {
+				VERBOSE("Found proper STAG header at: 0x%X.",
+					tmp_buf - buf);
+				break;
+			}
+		}
+		tmp_buf++;
+	}
+
+	tmp_csxf_header = (struct csxf_header *)(tmp_buf + STAG_SIZE);
+	if (tmp_csxf_header->magic != CSXF_MAGIC) {
+		ERR("CSXF magic incorrect! 0x%X != 0x%X",
+		    tmp_csxf_header->magic, CSXF_MAGIC);
+		goto out;
+	}
+	VERBOSE("CSXF magic ok.");
+	tmp_checksum = tmp_csxf_header->checksum;
+	tmp_csxf_header->checksum = 0;
+
+	tmp_csxf_header->checksum =
+	    (uint16_t) ~jboot_checksum(0, (uint16_t *) (tmp_buf + STAG_SIZE),
+					tmp_csxf_header->raw_length +
+					CSXF_SIZE);
+
+	if (tmp_checksum != tmp_csxf_header->checksum) {
+		ERR("CSXF checksum incorrect! Stored: 0x%X Calculated: 0x%X",
+		    tmp_checksum, tmp_csxf_header->checksum);
+		goto out;
+	}
+	VERBOSE("CSXF image checksum ok.");
+
+	tmp_buf = tmp_buf + STAG_SIZE + CSXF_SIZE;
+
+	while ((uint32_t) tmp_buf - (uint32_t) buf <= buf_size) {
+
+		struct data_header *tmp_data_header =
+		    (struct data_header *)tmp_buf;
+
+		if (tmp_data_header->unknown != DATA_HEADER_UNKNOWN) {
+			tmp_buf++;
+			continue;
+		}
+		if (tmp_data_header->type != DATA_HEADER_EEPROM
+		    && tmp_data_header->type != DATA_HEADER_CONFIG) {
+			tmp_buf++;
+			continue;
+		}
+
+		data_table[data_header_counter] = tmp_data_header;
+		tmp_buf +=
+		    DATA_HEADER_SIZE + data_table[data_header_counter]->length;
+		data_header_counter++;
+
+	}
+
+	ret = data_header_counter;
+
+ out:
+	return ret;
+}
+
+static int read_file(char *file_name)
+{
+	int ret = EXIT_FAILURE;
+	uint32_t file_size = 0;
+	FILE *fp;
+
+	fp = fopen(file_name, "r");
+
+	if (!fp) {
+		ERR("Failed to open config input file %s", file_name);
+		goto out;
+	}
+
+	fseek(fp, 0L, SEEK_END);
+	file_size = ftell(fp);
+	fseek(fp, 0L, SEEK_SET);
+
+	buffer = malloc(file_size);
+	VERBOSE("Allocated %d bytes.", file_size);
+
+	if (fread(buffer, 1, file_size, fp) != file_size) {
+		ERR("Failed to read config input file %s", file_name);
+		goto out_free_buf;
+	}
+
+	VERBOSE("Read %d bytes of config input file %s", file_size, file_name);
+	config_size = file_size;
+	ret = EXIT_SUCCESS;
+	goto out;
+
+ out_free_buf:
+	free(buffer);
+	fclose(fp);
+ out:
+	return ret;
+}
+
+static int write_file(const char *ofname, const uint8_t *data, int len)
+{
+	FILE *f;
+	int ret = EXIT_FAILURE;
+
+	f = fopen(ofname, "w");
+	if (f == NULL) {
+		ERRS("could not open \"%s\" for writing", ofname);
+		goto out;
+	}
+
+	errno = 0;
+	fwrite(data, len, 1, f);
+	if (errno) {
+		ERRS("unable to write output file");
+		goto out_flush;
+	}
+
+	VERBOSE("firmware file \"%s\" completed", ofname);
+
+	ret = EXIT_SUCCESS;
+
+ out_flush:
+	fflush(f);
+	fclose(f);
+	if (ret != EXIT_SUCCESS)
+		unlink(ofname);
+ out:
+	return ret;
+}
+
+static void print_mac(struct data_header **data_table, int cnt)
+{
+
+	for (int i = 0; i < cnt; i++) {
+		if (data_table[i]->type == DATA_HEADER_CONFIG
+		    && data_table[i]->id == DATA_HEADER_ID_MAC) {
+			int j;
+			for (j = 0; j < 5; j++)
+				printf("%02x:", data_table[i]->data[j]);
+			printf("%02x\n", data_table[i]->data[j]);
+		}
+
+	}
+
+}
+
+static int write_eeprom(struct data_header **data_table, int cnt)
+{
+	int ret = EXIT_FAILURE;
+
+	for (int i = 0; i < cnt; i++) {
+		if (data_table[i]->type == DATA_HEADER_EEPROM
+		    && data_table[i]->id == DATA_HEADER_ID_CAL) {
+			ret =
+			    write_file(ofname, data_table[i]->data,
+				       data_table[i]->length);
+			break;
+		}
+
+	}
+
+	return ret;
+}
+
+int main(int argc, char *argv[])
+{
+	int ret = EXIT_FAILURE;
+	int configs_counter = 0;
+	struct data_header *configs_table[MAX_DATA_HEADER];
+	buffer = NULL;
+	config_size = 0;
+
+	progname = basename(argv[0]);
+	start_offset = 0;
+	mac_print = 0;
+	print_data = 0;
+	verbose = 0;
+	ofname = NULL;
+	ifname = NULL;
+
+	while (1) {
+		int c;
+
+		c = getopt(argc, argv, "de:hi:mo:pv");
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'm':
+			mac_print = 1;
+			break;
+		case 'i':
+			ifname = optarg;
+			break;
+		case 'e':
+			ofname = optarg;
+			break;
+		case 'o':
+			sscanf(optarg, "0x%x", &start_offset);
+			break;
+		case 'p':
+			print_data = 1;
+			break;
+		case 'v':
+			verbose = 1;
+			VERBOSE("Enable verbose!");
+			break;
+		default:
+			usage(EXIT_FAILURE);
+			break;
+		}
+	}
+
+	if (!ifname)
+		usage(EXIT_FAILURE);
+
+	ret = read_file(ifname);
+
+	if (ret || config_size <= 0)
+		goto out;
+
+	configs_counter = find_header(buffer, config_size, configs_table);
+
+	if (configs_counter <= 0)
+		goto out_free_buf;
+
+	if (print_data || verbose) {
+		for (int i = 0; i < configs_counter; i++)
+			print_data_header(configs_table[i]);
+	}
+
+	if (mac_print)
+		print_mac(configs_table, configs_counter);
+
+	ret = EXIT_SUCCESS;
+
+	if (ofname)
+		ret = write_eeprom(configs_table, configs_counter);
+
+ out_free_buf:
+	free(buffer);
+ out:
+	return ret;
+
+}



More information about the lede-commits mailing list