Last active
March 17, 2019 13:43
-
-
Save darkarnium/9463bf42a1ff0e7df55a149bf17eb92e to your computer and use it in GitHub Desktop.
NVRAM shim to allow certain embedded binaries to be used with QEMU - intended for use with LD_PRELOAD.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <sys/file.h> | |
/* From acosTypes. */ | |
#ifndef BOOL | |
#define BOOL int | |
#endif | |
#ifndef IN | |
#define IN | |
#endif | |
#ifndef OUT | |
#define OUT | |
#endif | |
static const char LOG_FILE[] = "nvram.log"; | |
static const char NVRAM_FILE[] = "nvram.ini"; | |
static const char NVRAM_TEMP[] = "nvram.new"; | |
/** | |
* Log to file. | |
*/ | |
void logOperation(char *operation, char *message) { | |
FILE *fin = fopen(LOG_FILE, "a"); | |
// If file can be opened, gain an exclusive lock and write. | |
if(fin != NULL) { | |
if(flock(fileno(fin), LOCK_EX) != 0) { | |
exit(-2); | |
} | |
fprintf(fin, "[Operation: %s] :: %s\n", operation, message); | |
} | |
flock(fileno(fin), LOCK_UN); | |
fclose(fin); | |
} | |
/** | |
* Attempt to read a given nvram 'tag' from the ini file. | |
*/ | |
char *readFromIni(const char *name) { | |
char *key; | |
char *val; | |
FILE *fin = fopen(NVRAM_FILE, "r"); | |
if(fin != NULL) { | |
char line [255]; | |
// Read the entire file, line by line and process key / value pairs. | |
while(fgets(line, sizeof line, fin) != NULL) { | |
key = strtok(line, "="); | |
val = strtok(NULL, "="); | |
// Check if the key matches the requested tag. | |
if(strstr(key, name)) { | |
return(val); | |
} | |
} | |
fclose(fin); | |
} | |
return(""); | |
} | |
/** | |
* Attempt to update a given nvram 'tag' in the ini file. | |
*/ | |
void writeToIni(const char *name, const char *value) { | |
char *key; | |
char *val; | |
FILE *fin = fopen(NVRAM_FILE, "r"); | |
FILE *fout = fopen(NVRAM_TEMP, "w"); | |
if(fin != NULL) { | |
char line [255]; | |
// Write to the new file. | |
while(fgets(line, sizeof line, fin) != NULL) { | |
key = strtok(line, "="); | |
val = strtok(NULL, "="); | |
if(strcmp(key, name) != 0) { | |
fprintf(fout, "%s=%s", key, val); | |
} | |
} | |
fprintf(fout, "%s=%s\n", name, value); | |
fclose(fin); | |
fclose(fout); | |
// TODO: Chance for a race here, use atomic counter-part (renameat2) | |
// if supported on target platform, or flock(). | |
unlink(NVRAM_FILE); | |
rename(NVRAM_TEMP, NVRAM_FILE); | |
} | |
} | |
/** | |
* Log 'get' operation and read from 'nvram.ini' file. | |
*/ | |
char *acosNvramConfig_get(const char *name) { | |
char logMessage [255]; | |
sprintf(logMessage, "%s", name); | |
logOperation("acosNvramConfig_get", logMessage); | |
return(readFromIni(name)); | |
} | |
/** | |
* Log 'set' operation and set and write to file. | |
*/ | |
int acosNvramConfig_set(const char *name, const char *value) { | |
char logMessage [255]; | |
sprintf(logMessage, "%s => %s", name, value); | |
logOperation("acosNvramConfig_set", logMessage); | |
writeToIni(name, value); | |
return(0); | |
} | |
/** | |
* Log 'read' operation and read from 'nvram.ini' file. | |
*/ | |
int acosNvramConfig_read(IN char *pcTagName, OUT char *pcValue, IN int iMaxBufSize) { | |
char logMessage [255]; | |
sprintf(logMessage, "%s into buffer of size %d", pcTagName, iMaxBufSize); | |
logOperation("acosNvramConfig_read", logMessage); | |
strncpy(pcValue, readFromIni(pcTagName), iMaxBufSize); | |
return(0); | |
} | |
/** | |
* Log 'read' operation and read from 'nvram.ini' file. | |
*/ | |
int acosNvramConfig_readDefault(IN char *pcTagName, OUT char *pcValue, IN int iMaxBufSize) { | |
char logMessage [255]; | |
sprintf(logMessage, "%s into buffer of size %d", pcTagName, iMaxBufSize); | |
logOperation("acosNvramConfig_readDefault", logMessage); | |
strncpy(pcValue, readFromIni(pcTagName), iMaxBufSize); | |
return(0); | |
} | |
/** | |
* Log 'read' operation and read from 'nvram.ini' file. | |
* TODO: This should be casting to the right type... | |
*/ | |
int acosNvramConfig_readAsInt(IN char *pcTagName, OUT int *pcValue, IN int iMaxBufSize) { | |
char logMessage [255]; | |
sprintf(logMessage, "%s into buffer of size %d", pcTagName, iMaxBufSize); | |
logOperation("acosNvramConfig_readAsInt", logMessage); | |
strncpy(pcValue, readFromIni(pcTagName), iMaxBufSize); | |
return(0); | |
} | |
/** | |
* Log 'write' operation and mock. | |
*/ | |
int acosNvramConfig_write(IN char *pcTagName, IN char *pcValue, IN BOOL blSaveImmediately) { | |
char logMessage [255]; | |
sprintf(logMessage, "%s => %s", pcTagName, pcValue); | |
logOperation("acosNvramConfig_write", logMessage); | |
return(0); | |
} | |
/** | |
* Log 'writeAsInt' operation and mock. | |
*/ | |
int acosNvramConfig_writeAsInt(IN char *pcTagName, IN int iValue, IN BOOL blSaveImmediately) { | |
char logMessage [255]; | |
sprintf(logMessage, "%s => %d", pcTagName, iValue); | |
logOperation("acosNvramConfig_writeAsInt", logMessage); | |
return(0); | |
} | |
/** | |
* Log 'match' operation and check. | |
*/ | |
int acosNvramConfig_match(char *name, char *match) { | |
char logMessage [255]; | |
sprintf(logMessage, "%s == %s", name, match); | |
logOperation("acosNvramConfig_match", logMessage); | |
if(strcmp(match, readFromIni(name))) { | |
return(0); | |
} else { | |
return(-1); | |
} | |
} | |
/** | |
* Log 'invmatch' operation and check. | |
*/ | |
int acosNvramConfig_invmatch(char *name, char *match) { | |
char logMessage [255]; | |
sprintf(logMessage, "%s != %s", name, match); | |
logOperation("acosNvramConfig_invmatch", logMessage); | |
if(strcmp(match, readFromIni(name))) { | |
return(0); | |
} else { | |
return(-1); | |
} | |
} | |
/** | |
* Log 'save' operation and mock. | |
*/ | |
int acosNvramConfig_save(void) { | |
logOperation("acosNvramConfig_save", ""); | |
return(0); | |
} | |
/** | |
* Log 'exist' operation and always return zero. | |
*/ | |
int acosNvramConfig_exist(char *name) { | |
logOperation("acosNvramConfig_exist", name); | |
return(1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To Compile.
For tool-chains, newer NetGear devices may ask for
arm-uclibc-3.4.6
which can be found through various means.arm-linux-gcc -shared NVRAM_Hammer.c -o NVRAM_Hammer.so
To Use.
nvram.ini
file in this directory and insert all required NVRAM tags in atag=value
format to use.rm -f ./tmp/shm_id; sudo chroot . ./qemu-arm-static -E LD_PRELOAD="/NVRAM_Hammer.so" usr/sbin/httpd
.CAVEATS!
There are likely a number of NVRAM tags that are required to have been pre-loaded into the system at some point. As this shim assumes that the
nvram.ini
file contains values already, these need to be resolved and populated. The following methods can assist in doing so, as the omission of certain tags may cause the target binaries to crash.If targeting newer NetGear devices, the the following may work - assuming that this is run from inside of the downloaded and unpacked the GPL directory for the given device:
However, in the case of devices such as those build on the
acos
platform (?) there are other tags which aren't present in this header file which are required for binaries to start. In the case of the device that this shim was developed for, these additional tags are present inside of an export from an object file calleddefaults.o
in the GPL sources, and can be used to further populate thenvram.ini
file with a bit of work.