From: Rafał Miłecki Date: Sat, 19 Nov 2016 06:58:25 +0000 (+0100) Subject: osafeloader: new util for extracting partitions from SafeLoader X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=1c6fd8c1ee9d07bee558ccaa0703bfab6d8c125f;p=librecmc%2Flibrecmc.git osafeloader: new util for extracting partitions from SafeLoader SafeLoader is image format used by some TP-LINK devices. This tool allows extracting selected partitions out of it. It can be used for sysupgrade. Signed-off-by: Rafał Miłecki --- diff --git a/package/utils/osafeloader/Makefile b/package/utils/osafeloader/Makefile new file mode 100644 index 0000000000..883d7ae603 --- /dev/null +++ b/package/utils/osafeloader/Makefile @@ -0,0 +1,38 @@ +# +# Copyright (C) 2016 Rafał Miłecki +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=osafeloader +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/osafeloader + SECTION:=utils + CATEGORY:=Base system + TITLE:=Utility for handling TP-LINK SafeLoader images + MAINTAINER:=Rafał Miłecki + DEPENDS:=@TARGET_bcm53xx +endef + +define Package/osafeloader/description + This package contains an utility that allows handling SafeLoader images. +endef + +define Build/Compile + $(MAKE) -C $(PKG_BUILD_DIR) \ + CC="$(TARGET_CC)" \ + CFLAGS="$(TARGET_CFLAGS) -Wall" +endef + +define Package/osafeloader/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/osafeloader $(1)/usr/bin/ +endef + +$(eval $(call BuildPackage,osafeloader)) diff --git a/package/utils/osafeloader/src/Makefile b/package/utils/osafeloader/src/Makefile new file mode 100644 index 0000000000..acc6f0fe65 --- /dev/null +++ b/package/utils/osafeloader/src/Makefile @@ -0,0 +1,7 @@ +all: osafeloader + +osafeloader: + $(CC) $(CFLAGS) -Wall osafeloader.c md5.c -o $@ $^ + +clean: + rm -f osafeloader diff --git a/package/utils/osafeloader/src/md5.c b/package/utils/osafeloader/src/md5.c new file mode 100644 index 0000000000..52d96accd3 --- /dev/null +++ b/package/utils/osafeloader/src/md5.c @@ -0,0 +1,296 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. + */ + +#ifndef HAVE_OPENSSL + +#include + +#include "md5.h" + +/* + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. + */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); + +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * doesn't work. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(MD5_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) +#else +#define SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) +#endif + +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There are no alignment requirements. + */ +static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) +{ + const unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; +} + +void MD5_Init(MD5_CTX *ctx) +{ + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; +} + +void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) +{ + MD5_u32plus saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) { + available = 64 - used; + + if (size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); +} + +void MD5_Final(unsigned char *result, MD5_CTX *ctx) +{ + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + memset(ctx, 0, sizeof(*ctx)); +} + +#endif diff --git a/package/utils/osafeloader/src/md5.h b/package/utils/osafeloader/src/md5.h new file mode 100644 index 0000000000..2da44bf355 --- /dev/null +++ b/package/utils/osafeloader/src/md5.h @@ -0,0 +1,45 @@ +/* + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. + */ + +#ifdef HAVE_OPENSSL +#include +#elif !defined(_MD5_H) +#define _MD5_H + +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD5_u32plus; + +typedef struct { + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; +} MD5_CTX; + +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); + +#endif diff --git a/package/utils/osafeloader/src/osafeloader.c b/package/utils/osafeloader/src/osafeloader.c new file mode 100644 index 0000000000..9ffa5fe3cf --- /dev/null +++ b/package/utils/osafeloader/src/osafeloader.c @@ -0,0 +1,263 @@ +/* + * osafeloader + * + * Copyright (C) 2016 Rafał Miłecki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "md5.h" + +#if !defined(__BYTE_ORDER) +#error "Unknown byte order" +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_be32(x) (x) +#define be32_to_cpu(x) (x) +#define cpu_to_be16(x) (x) +#define be16_to_cpu(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_be32(x) bswap_32(x) +#define be32_to_cpu(x) bswap_32(x) +#define cpu_to_be16(x) bswap_16(x) +#define be16_to_cpu(x) bswap_16(x) +#else +#error "Unsupported endianness" +#endif + +struct safeloader_header { + uint32_t imagesize; + uint8_t md5[16]; +} __attribute__ ((packed)); + +char *safeloader_path; +char *partition_name; +char *out_path; + +static inline size_t osafeloader_min(size_t x, size_t y) { + return x < y ? x : y; +} + +static const uint8_t md5_salt[16] = { + 0x7a, 0x2b, 0x15, 0xed, + 0x9b, 0x98, 0x59, 0x6d, + 0xe5, 0x04, 0xab, 0x44, + 0xac, 0x2a, 0x9f, 0x4e, +}; + +/************************************************** + * Info + **************************************************/ + +static int osafeloader_info(int argc, char **argv) { + FILE *safeloader; + struct safeloader_header hdr; + MD5_CTX ctx; + size_t bytes, imagesize; + uint8_t buf[1024]; + uint8_t md5[16]; + char name[32]; + int base, size, i; + int err = 0; + + if (argc < 3) { + fprintf(stderr, "No SafeLoader file passed\n"); + err = -EINVAL; + goto out; + } + safeloader_path = argv[2]; + + safeloader = fopen(safeloader_path, "r"); + if (!safeloader) { + fprintf(stderr, "Couldn't open %s\n", safeloader_path); + err = -EACCES; + goto out; + } + + bytes = fread(&hdr, 1, sizeof(hdr), safeloader); + if (bytes != sizeof(hdr)) { + fprintf(stderr, "Couldn't read %s header\n", safeloader_path); + err = -EIO; + goto err_close; + } + imagesize = be32_to_cpu(hdr.imagesize); + + MD5_Init(&ctx); + MD5_Update(&ctx, md5_salt, sizeof(md5_salt)); + while ((bytes = fread(buf, 1, osafeloader_min(sizeof(buf), imagesize), safeloader)) > 0) { + MD5_Update(&ctx, buf, bytes); + imagesize -= bytes; + } + MD5_Final(md5, &ctx); + + if (memcmp(md5, hdr.md5, 16)) { + fprintf(stderr, "Broken SafeLoader file with invalid MD5\n"); + err = -EIO; + goto err_close; + } + + printf("%10s: %d\n", "Image size", be32_to_cpu(hdr.imagesize)); + printf("%10s: ", "MD5"); + for (i = 0; i < 16; i++) + printf("%02x", md5[i]); + printf("\n"); + + /* Skip header & vendor info */ + fseek(safeloader, 0x1014, SEEK_SET); + + while (fscanf(safeloader, "fwup-ptn %s base 0x%x size 0x%x\t\r\n", name, &base, &size) == 3) { + printf("%10s: %s (0x%x - 0x%x)\n", "Partition", name, base, base + size); + } + +err_close: + fclose(safeloader); +out: + return err; +} + +/************************************************** + * Extract + **************************************************/ + +static void osafeloader_extract_parse_options(int argc, char **argv) { + int c; + + while ((c = getopt(argc, argv, "p:o:")) != -1) { + switch (c) { + case 'p': + partition_name = optarg; + break; + case 'o': + out_path = optarg; + break; + } + } +} + +static int osafeloader_extract(int argc, char **argv) { + FILE *safeloader; + FILE *out; + struct safeloader_header hdr; + size_t bytes; + char name[32]; + int base, size; + int err = 0; + + if (argc < 3) { + fprintf(stderr, "No SafeLoader file passed\n"); + err = -EINVAL; + goto out; + } + safeloader_path = argv[2]; + + optind = 3; + osafeloader_extract_parse_options(argc, argv); + if (!partition_name) { + fprintf(stderr, "No partition name specified\n"); + err = -EINVAL; + goto out; + } else if (!out_path) { + fprintf(stderr, "No output file specified\n"); + err = -EINVAL; + goto out; + } + + safeloader = fopen(safeloader_path, "r"); + if (!safeloader) { + fprintf(stderr, "Couldn't open %s\n", safeloader_path); + err = -EACCES; + goto out; + } + + out = fopen(out_path, "w"); + if (!out) { + fprintf(stderr, "Couldn't open %s\n", out_path); + err = -EACCES; + goto err_close_safeloader; + } + + bytes = fread(&hdr, 1, sizeof(hdr), safeloader); + if (bytes != sizeof(hdr)) { + fprintf(stderr, "Couldn't read %s header\n", safeloader_path); + err = -EIO; + goto err_close_out; + } + + /* Skip vendor info */ + fseek(safeloader, 0x1000, SEEK_CUR); + + err = -ENOENT; + while (fscanf(safeloader, "fwup-ptn %s base 0x%x size 0x%x\t\r\n", name, &base, &size) == 3) { + uint8_t buf[1024]; + + if (strcmp(name, partition_name)) + continue; + + err = 0; + + fseek(safeloader, sizeof(hdr) + 0x1000 + base, SEEK_SET); + + while ((bytes = fread(buf, 1, osafeloader_min(sizeof(buf), size), safeloader)) > 0) { + if (fwrite(buf, 1, bytes, out) != bytes) { + fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path); + err = -EIO; + break; + } + size -= bytes; + } + + if (size) { + fprintf(stderr, "Couldn't extract whole partition %s from %s (%d B left)\n", partition_name, safeloader_path, size); + err = -EIO; + } + + break; + } + +err_close_out: + fclose(out); +err_close_safeloader: + fclose(safeloader); +out: + return err; +} + +/************************************************** + * Start + **************************************************/ + +static void usage() { + printf("Usage:\n"); + printf("\n"); + printf("Info about SafeLoader:\n"); + printf("\tosafeloader info \n"); + printf("\n"); + printf("Extract from SafeLoader:\n"); + printf("\tosafeloader extract [options]\n"); + printf("\t-p name\t\t\t\tname of partition to extract\n"); + printf("\t-o file\t\t\t\toutput file\n"); +} + +int main(int argc, char **argv) { + if (argc > 1) { + if (!strcmp(argv[1], "info")) + return osafeloader_info(argc, argv); + else if (!strcmp(argv[1], "extract")) + return osafeloader_extract(argc, argv); + } + + usage(); + return 0; +}