Skip to content

Instantly share code, notes, and snippets.

@armarti
Created July 24, 2020 16:14
Show Gist options
  • Save armarti/bd7582c587e6b62a09b1379444e410cf to your computer and use it in GitHub Desktop.
Save armarti/bd7582c587e6b62a09b1379444e410cf to your computer and use it in GitHub Desktop.
Small CLI utility for converting FreshTomato (and probably many other Tomato) .cfg backup files to readable nvram values
/**
* Converted from https://bitbucket.org/pedro311/freshtomato-arm/raw/86fd29f5a23b1ab4b2dfb4bdae2a625fd3a134a9/release/src-rt-6.x.4708/router/nvram_arm/main.c
* which contained the following copyright.
*/
/*
* Frontend command-line utility for Linux NVRAM layer
*
* Copyright (C) 2012, Broadcom Corporation. All Rights Reserved.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id: main.c 325698 2012-04-04 12:40:07Z $
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NVRAM_SPACE 0x20000
#define PROFILE_HEADER "HDR1"
#define PROFILE_HEADER_NEW "HDR2"
#define INFILE_NOT_READABLE -1
#define OUTFILE_NOT_WRITABLE -2
#define INVALID_CFG_FORMAT -3
int nvram_restore_to_stdout(char * file);
int nvram_restore_to_file(char * file, char * outfile);
int nvram_restore_new(char * file, FILE * ofp);
int tell_error(int rv, char * infile, const char * outfile) {
switch(rv) {
case INFILE_NOT_READABLE:
fprintf(stderr, "Cannot read input file \"%s\"\n", infile);
return -1 * rv;
case OUTFILE_NOT_WRITABLE:
fprintf(stderr, "Cannot write to output file \"%s\"\n", outfile);
return -1 * rv;
case INVALID_CFG_FORMAT:
fprintf(stderr, "Invalid cfg file format for \"%s\"\n", infile);
return -1 * rv;
default:
fprintf(stderr, "Wrote %d bytes to ", rv);
fprintf(stderr, "%s\n", outfile);
return 0;
}
}
int main(int argc, char ** argv) {
char * progname = argv[0];
if(argc < 2) {
fprintf(stderr, "Usage: %s <infile path> [outfile path]\n", progname);
return 1;
}
char *infile = argv[1];
int rv;
if(argc > 2) {
char * outfile = argv[2];
int rtn = nvram_restore_to_file(infile, outfile);
rv = tell_error(rtn, infile, (const char *)outfile);
} else {
int rtn = nvram_restore_to_stdout(infile);
rv = tell_error(rtn, infile, "stdout");
}
return 0;
}
int nvram_restore_to_stdout(char * file) {
return nvram_restore_new(file, stdout);
}
int nvram_restore_to_file(char * file, char * outfile) {
FILE *ofp;
if((ofp = fopen(outfile, "w+")) == NULL) {
return OUTFILE_NOT_WRITABLE;
}
int rv = nvram_restore_new(file, ofp);
fclose(ofp);
return rv;
}
int nvram_restore_new(char * file, FILE * ofp) {
FILE *fp;
char header[8], *p, *v, buf[MAX_NVRAM_SPACE];
unsigned long count, filelen, *filelenptr, i;
unsigned char rand, *randptr;
unsigned long nbytes = 0;
if((fp = fopen(file, "r+")) == NULL) {
return INFILE_NOT_READABLE;
}
count = fread(header, 1, 8, fp);
if(count>=8 && strncmp(header, PROFILE_HEADER, 4) == 0) {
filelenptr = (unsigned long *)(header + 4);
fread(buf, 1, *filelenptr, fp);
} else if(count>=8 && strncmp(header, PROFILE_HEADER_NEW, 4) == 0) {
filelenptr = (unsigned long *)(header + 4);
filelen = *filelenptr & 0xffffff;
randptr = (unsigned char *)(header + 7);
rand = *randptr;
count = fread(buf, 1, filelen, fp);
for(i = 0; i < count; i++) {
if((unsigned char) buf[i] > ( 0xfd - 0x1)) {
/* e.g.: to skip the case: 0x61 0x62 0x63 0x00 0x00 0x61 0x62 0x63 */
if(i > 0 && buf[i-1] != 0x0) {
buf[i] = 0x0;
}
} else {
buf[i] = 0xff + rand - buf[i];
}
}
} else {
fclose(fp);
return INVALID_CFG_FORMAT;
}
fclose(fp);
p = buf;
while(*p) {
// e.g.: to skip the case: 00 2e 30 2e 32 38 00 ff 77 61 6e
if(*p == '\0' || *p < 32 || *p > 127 ) {
p = p + 1;
continue;
}
v = strchr(p, '=');
if(v != NULL) {
*v++ = '\0';
nbytes += fprintf(ofp, "%s", p);
nbytes += fprintf(ofp, "=%s\n", v);
p = v + strlen(v) + 1;
} else {
nbytes += fprintf(ofp, "%s=\n", p);
p = p + 1;
}
}
return nbytes;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment