You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openwrt/package/utils/rbextract/src/rbextract.c

498 lines
11 KiB
C

/*
* RouterBoot helper routines
*
* Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2018 Chris Schimp <silverchris@gmail.com>
* Copyright (C) 2019 Robert Marko <robimarko@gmail.com>
*
* 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 <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <endian.h>
#include <arpa/inet.h>
#include <lzo/lzo1x.h>
#include "rle.h"
#include "routerboot.h"
inline uint32_t
get_u32(const void *buf)
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
return *(uint32_t *)buf;
#elif __BYTE_ORDER == __BIG_ENDIAN
const uint8_t *p = buf;
return ((uint32_t) p[3] + ((uint32_t) p[2] << 8) +
((uint32_t) p[1] << 16) + ((uint32_t) p[0] << 24));
#else
#error "Unknown byte order!"
#endif
}
int
routerboot_find_tag(uint8_t *buf, unsigned int buflen, uint16_t tag_id,
uint8_t **tag_data, uint16_t *tag_len)
{
uint16_t id;
uint16_t len;
uint32_t magic;
bool align = false;
int ret;
if (buflen < 4)
return 1;
magic = get_u32(buf);
switch (magic) {
case RB_MAGIC_LZOR:
buf += 4;
buflen -= 4;
break;
case RB_MAGIC_ERD:
align = true;
/* fall trough */
case RB_MAGIC_HARD:
/* skip magic value */
buf += 4;
buflen -= 4;
break;
case RB_MAGIC_SOFT:
if (buflen < 8)
return 1;
/* skip magic and CRC value */
buf += 8;
buflen -= 8;
break;
default:
return 1;
}
ret = 1;
while (buflen > 4){
uint32_t id_and_len = get_u32(buf);
buf += 4;
buflen -= 4;
id = id_and_len & 0xFFFF;
len = id_and_len >> 16;
if (align)
len += (4 - len % 4) % 4;
if (id == RB_ID_TERMINATOR) {
break;
}
if (buflen < len)
break;
if (id == tag_id) {
*tag_len = len;
*tag_data = buf;
ret = 0;
break;
}
buf += len;
buflen -= len;
}
return ret;
}
inline int
rb_find_hard_cfg_tag(uint16_t tag_id, uint8_t **tag_data, uint16_t *tag_len)
{
if (!rb_hardconfig ||
!rb_hardconfig_len)
return 1;
return routerboot_find_tag(rb_hardconfig,
rb_hardconfig_len,
tag_id, tag_data, tag_len);
}
const uint8_t *
rb_get_board_product_code(void)
{
uint16_t tag_len;
uint8_t *tag;
int err;
err = rb_find_hard_cfg_tag(RB_ID_BOARD_PRODUCT_CODE, &tag, &tag_len);
if (err)
return NULL;
return tag;
}
uint32_t
rb_get_board_mac(void)
{
uint16_t tag_len;
uint8_t *tag;
int err;
err = rb_find_hard_cfg_tag(RB_ID_MAC_ADDRESS_PACK, &tag, &tag_len);
if (err)
return 0;
return htonl(get_u32(tag));
}
const uint8_t *
rb_get_board_serial(void)
{
uint16_t tag_len;
uint8_t *tag;
int err;
err = rb_find_hard_cfg_tag(RB_ID_SERIAL_NUMBER, &tag, &tag_len);
if (err)
return NULL;
return tag;
}
const uint8_t *
rb_get_board_identifier(void)
{
uint16_t tag_len;
uint8_t *tag;
int err;
err = rb_find_hard_cfg_tag(RB_ID_BOARD_IDENTIFIER, &tag, &tag_len);
if (err)
return NULL;
return tag;
}
const uint8_t *
rb_get_board_name(void)
{
uint16_t tag_len;
uint8_t *tag;
int err;
err = rb_find_hard_cfg_tag(RB_ID_BOARD_NAME, &tag, &tag_len);
if (err)
return NULL;
return tag;
}
const uint8_t *
rb_get_factory_booter_version(void)
{
uint16_t tag_len;
uint8_t *tag;
int err;
err = rb_find_hard_cfg_tag(RB_ID_BIOS_VERSION, &tag, &tag_len);
if (err)
return NULL;
return tag;
}
uint32_t
rb_get_flash_info(void)
{
uint16_t tag_len;
uint8_t *tag;
int err;
err = rb_find_hard_cfg_tag(RB_ID_FLASH_INFO, &tag, &tag_len);
if (err)
return 0;
return htonl(get_u32(tag));
}
uint32_t
rb_get_hw_options(void)
{
uint16_t tag_len;
uint8_t *tag;
int err;
err = rb_find_hard_cfg_tag(RB_ID_HW_OPTIONS, &tag, &tag_len);
if (err)
return 0;
return htonl(get_u32(tag));
}
uint8_t *
__rb_get_wlan_data(void)
{
uint16_t tag_len;
uint8_t *tag;
uint16_t erd_tag_len;
uint8_t *erd_tag;
uint8_t *buf_lzo_in;
uint8_t *buf_lzo_out;
uint8_t *buf_rle_out;
int err;
uint32_t magic;
uint32_t erd_magic;
uint32_t erd_offset;
size_t lzo_out_len;
err = rb_find_hard_cfg_tag(RB_ID_WLAN_DATA, &tag, &tag_len);
if (err) {
printf("no calibration data found\n");
goto err;
}
buf_lzo_in = malloc(RB_ART_SIZE);
if (buf_lzo_in == NULL) {
printf("no memory for calibration data\n");
goto err;
}
buf_rle_out = malloc(RB_ART_SIZE);
if (buf_rle_out == NULL) {
printf("no memory for calibration data\n");
goto err_free_lzo_out;
}
buf_lzo_out = malloc(RB_ART_SIZE);
if (buf_lzo_out == NULL) {
printf("no memory for calibration data\n");
goto err_free_lzo_in;
}
magic = get_u32(tag);
if (magic == RB_MAGIC_LZOR) {
tag += 4;
tag_len -= 4;
if (tag_len + sizeof(lzo_prefix) > RB_ART_SIZE) {
printf("Calibration data too large\n");
goto err_free_lzo_in;
}
printf("Copying fixed LZO prefix (size: %d)\n", sizeof(lzo_prefix));
memcpy(buf_lzo_in, lzo_prefix, sizeof(lzo_prefix));
printf("Copying input data (size: %d)\n", tag_len);
memcpy(buf_lzo_in + sizeof(lzo_prefix), tag, tag_len);
printf("Decompressing with LZO\n");
lzo_out_len = RB_ART_SIZE;
err = lzo1x_decompress_safe(buf_lzo_in, tag_len + sizeof(lzo_prefix),
buf_lzo_out, &lzo_out_len, NULL);
/* For some reason, I get this "input not consumed" error
* even though the output is correct, so ignore it. */
if (err && err != LZO_E_INPUT_NOT_CONSUMED) {
printf("unable to decompress calibration data: %d\n",
err);
goto err_free_lzo_out;
}
printf("Looking for ERD data in decompressed output\n");
erd_magic = 0;
for (erd_offset = 0; erd_offset < lzo_out_len; erd_offset++) {
erd_magic = get_u32(buf_lzo_out + erd_offset);
if (erd_magic == RB_MAGIC_ERD)
break;
}
if (erd_magic != RB_MAGIC_ERD) {
printf("no ERD data found\n");
goto err_free_lzo_out;
}
printf("Found ERD magic at offset %d\n", erd_offset);
err = routerboot_find_tag(buf_lzo_out + erd_offset,
lzo_out_len - erd_offset,
0x1, &erd_tag, &erd_tag_len);
if (err) {
printf("No ERD chunk found\n");
goto err_free_lzo_out;
}
printf("Decompress ERD data with RLE\n");
err = rle_decode(erd_tag, erd_tag_len, buf_rle_out, RB_ART_SIZE,
NULL, NULL);
if (err) {
printf("unable to decode ERD data\n");
goto err_free_rle_out;
}
}
/* Older ath79-based boards directly show the RB_MAGIC_ERD bytes followed by
the LZO-compressed calibration data with no RLE */
else if (magic == RB_MAGIC_ERD) {
if (tag_len > RB_ART_SIZE) {
printf("Calibration data too large\n");
goto err_free_lzo_in;
}
err = routerboot_find_tag(tag, tag_len,
0x1, &buf_lzo_in, &erd_tag_len);
if (err) {
printf("No ERD chunk found\n");
goto err_free_lzo_out;
}
printf("Decompressing with LZO\n");
lzo_out_len = RB_ART_SIZE;
err = lzo1x_decompress_safe(buf_lzo_in, tag_len,
buf_lzo_out, &lzo_out_len, NULL);
/* For some reason, I get this "input not consumed" error
* even though the output is correct, so ignore it. */
if (err && err != LZO_E_INPUT_NOT_CONSUMED) {
printf("unable to decompress calibration data: %d\n",
err);
goto err_free_lzo_out;
}
buf_rle_out = buf_lzo_out;
}
/* Even older ath79-base boards directly have RLE-encoded calibration data,
without any LZO compresion nor showing RB_MAGIC_ERD bytes */
else {
printf("Decode calibration data with RLE\n");
err = rle_decode(tag, tag_len, buf_rle_out, RB_ART_SIZE,
NULL, NULL);
if (err) {
printf("unable to decode ERD data\n");
goto err_free_rle_out;
}
}
return buf_rle_out;
err_free_rle_out:
free(buf_rle_out);
err_free_lzo_out:
free(buf_lzo_out);
err_free_lzo_in:
free(buf_lzo_in);
err:
return NULL;
}
int
main(int argc, char **argv)
{
FILE *infile;
FILE *outfile;
uint8_t *buf;
uint32_t magic;
uint32_t i;
if(argc < 2){
printf("Not enough arguments\n");
printf("Use -h for help\n");
exit(1);
}
if(strcmp(argv[1], "-h") == 0){
printf("This program can extract various data from MikroTik devices hard_config partition\n");
printf("Usage: rbextract <options> <hard_config_location> <output_file> (Optional)\n");
printf("Options:\n");
printf("-a Prints all possible info\n");
printf("-n Prints board name\n");
printf("-p Prints board product code\n");
printf("-i Prints board identifier\n");
printf("-s Prints board serial number\n");
printf("-m Prints board MAC\n");
printf("-o Prints board HW options\n");
printf("-r Prints board RouterBoot factory version\n");
printf("-f Prints board flash identifier\n");
printf("-e Extract board radio calibration\n");
printf("hard_config_location: Path to hard_config partiton\n");
printf("output_file: Path to where caldata will be output\n");
} else {
infile = fopen(argv[2], "r");
if(infile == NULL){
printf("Cant open given path\n");
return 1;
}
fseek(infile, 0L, SEEK_END);
rb_hardconfig_len = ftell(infile);
fseek(infile, 0L, SEEK_SET);
rb_hardconfig = (uint8_t*)calloc(rb_hardconfig_len, sizeof(uint8_t));
if(rb_hardconfig == NULL)
return 1;
fread(rb_hardconfig, sizeof(uint8_t), rb_hardconfig_len, infile);
fclose(infile);
magic = get_u32(rb_hardconfig);
if(magic != RB_MAGIC_HARD){
printf("Routerboot Hard Config not found\n");
exit(1);
}
if(strcmp(argv[1], "-a") == 0){
printf("Board name: %s\n", rb_get_board_name());
printf("Board product code: %s\n", rb_get_board_product_code());
printf("Board identifier: %s\n", rb_get_board_identifier());
printf("Board serial: %s\n", rb_get_board_serial());
printf("Board MAC: %X\n", rb_get_board_mac());
printf("HW Options %x\n", rb_get_hw_options());
printf("Factory RouterBoot version: %s\n", rb_get_factory_booter_version());
printf("Flash identifier: %x\n", rb_get_flash_info());
} else if(strcmp(argv[1], "-n") == 0){
printf("%s\n", rb_get_board_name());
} else if(strcmp(argv[1], "-p") == 0){
printf("%s\n", rb_get_board_product_code());
} else if(strcmp(argv[1], "-i") == 0){
printf("%s\n", rb_get_board_identifier());
} else if(strcmp(argv[1], "-s") == 0){
printf("%s\n", rb_get_board_serial());
} else if(strcmp(argv[1], "-m") == 0){
printf("%x\n", rb_get_board_mac());
} else if(strcmp(argv[1], "-o") == 0){
printf("%x\n", rb_get_hw_options());
} else if(strcmp(argv[1], "-r") == 0){
printf("%s\n", rb_get_factory_booter_version());
} else if(strcmp(argv[1], "-f") == 0){
printf("%x\n", rb_get_flash_info());
} else if(strcmp(argv[1], "-e") == 0){
buf = __rb_get_wlan_data();
if (buf == NULL) {
printf("Could not extract calibration data\n");
return 1;
}
if(argv[3] == NULL){
printf("Missing output file argument\n");
return 1;
}
outfile = fopen(argv[3], "wb");
if(outfile == NULL){
printf("Cant open given path\n");
return 1;
}
/* Write 65536 bytes of caldata */
for(i = 0; i<RB_ART_SIZE; i++){
fwrite(&buf[i], sizeof(uint8_t), sizeof(uint8_t), outfile);
}
fclose(outfile);
}
}
exit(0);
}