Skip to content

Instantly share code, notes, and snippets.

@kulp
Created October 5, 2011 15:14
Show Gist options
  • Save kulp/1264689 to your computer and use it in GitHub Desktop.
Save kulp/1264689 to your computer and use it in GitHub Desktop.
Convert .REG (exported registry files) into .NSH-style registry commands
/*
reg2nsh.c : Converts .REG (exported registry files) into .NSH-style
registry commands, for use with the NSIS ( http://nsis.sf.net/ ).
$Rev$
$Date$
Copyright (c) 2005 Darren Kulp under the terms of the MIT License
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
//TODO: Add support for more key types like REG_MULTI_SZ by flagging their
// existence and appending NSIS functions that handle them. Reference
// http://nsis.sourceforge.net/Docs/AppendixD.html#D.6 to get this working.
// Note: This program is likely to remain undeveloped, since a (regrettably
// non-libre) version is available at http://aarrtteemm.nm.ru/.
/* Tabstops at 4 */
#include "reg2nsh.h"
int main(int argc, char *argv[])
{
int i = 0,
header = 0,
lineno = 0,
havekey = 0;
FILE *inputfile = stdin;
FILE *outputfile = stdout;
regkey *rootkey = (regkey *)malloc(sizeof(regkey));
regkey *subkey = (regkey *)malloc(sizeof(regkey));
char *lastkey = (char *)malloc(MAX_KEY_LENGTH);
char *line = (char *)malloc(MAX_LINE_LENGTH);
char *tmp = (char *)malloc(MAX_LINE_LENGTH);
if (line == NULL || lastkey == NULL || tmp == NULL ||
rootkey == NULL || subkey == NULL) {
fprintf(stderr, "malloc() failed at code line %d!\n", __LINE__);
//FIXME: Memory is not freed, but atexit() would requires global vars
return E_BAD_PTR;
}
for (i = 0; i < argc; i++)
if (STREQ(argv[i], "-h") ||
STREQ(argv[i], "--help"))
return usage(argv[0]);
switch (argc) {
case 3:
outputfile = fopen(argv[2], "w");
// fallthrough
case 2:
inputfile = fopen(argv[1], "r");
// fallthrough
case 1:
break;
default:
return usage(argv[0]);
}
if (inputfile == NULL) {
fprintf(stderr, "Could not open input filehandle %d!\n", (int)inputfile);
return E_BAD_PTR;
}
if (outputfile == NULL) {
fprintf(stderr, "Could not open output filehandle %d!\n", (int)outputfile);
return E_BAD_PTR;
}
while (get_mogrified_line(inputfile, line) == E_OK) {
lineno++;
if (line == NULL) {
fprintf(stderr, "Bad pointer at input line %d!\n", lineno);
return E_BAD_PTR;
}
if (!header)
if (STREQN("Windows Registry Editor", line , 23) || //plain
STREQN("Windows Registry Editor", line + 2 , 23)) { //.REG
if (lineno == 1) {
header = 1;
continue;
} else {
fprintf(stderr, "Unexpected header at line %d!\n", lineno);
return E_BAD_INPUT;
}
}
// Is the line continued ("\" at the end)?
while (*(line + strlen(line) - 2) == '\\') {
if (get_mogrified_line(inputfile, tmp) == E_EOF) {
fprintf(stderr, "EOF reached: expected continued data line!\n");
return E_BAD_INPUT;
} else
lineno++;
if (tmp == NULL) {
fprintf(stderr, "Bad pointer at data line %d!\n", lineno);
return E_BAD_PTR;
}
tmp += 2; // Skip spaces at beginning of continued-string line
*(line + strlen(line) - 2) = '\0'; // Append null
strcat(line, tmp);
}
if (STREQN("[", line, 1)) { // Is this a key?
strncpy(lastkey, line + 1, strlen(line) - 2);
*(lastkey + strlen(line) - 2) = '\0'; // Append a null
if (parsekeyname(lastkey, rootkey, subkey)) {
fprintf(stderr, "Cannot parse key at line %d!\n", lineno);
return E_BAD_INPUT;
}
havekey++; //FIXME: Is havekey useful > 1?
continue;
} else if (STREQN("\"", line, 1) || STREQN("@", line, 1)) {
if (!havekey) {
fprintf(stderr, "No key specified for values at line %d!\n",
lineno);
return E_BAD_INPUT;
}
if (printvalue(line, rootkey, subkey, outputfile)) {
fprintf(stderr, "Cannot parse value at line %d!\n", lineno);
return E_BAD_INPUT;
}
} else if (strlen(line) == 0) // Empty lines mean nothing
continue;
else {
fprintf(stderr, "Bad input at input line %d!\n", lineno);
return E_BAD_INPUT;
}
}
free(line);
free(lastkey);
free(rootkey);
free(subkey);
fclose(inputfile);
fclose(outputfile);
return 0;
}
int printvalue(char *value, regkey *rootkey, regkey *subkey, FILE *outputfile)
{
/*
* Prints the NSH statement that corresponds to a value entry in the source
* REG file.
*/
regvalue *current = parsevalue(value);
char *tmp;
if (current == NULL) {
fprintf(stderr, "Received invalid parse result!\n");
return E_BAD_PTR;
}
switch (current->type /*& ~REG_HEX_FORMAT*/) {
case REG_EXPAND_SZ:
case REG_BINARY:
fprintf(outputfile, "WriteRegBin");
nsh_line_out(rootkey->name, subkey->name, current->name, outputfile);
if ((tmp = flatten_binary_data(current->value)) == NULL) {
fprintf(stderr, "Cannot print invalid binary data!\n");
return E_BAD_PTR;
}
fprintf(outputfile, " %s\n", tmp);
break;
case REG_DWORD:
fprintf(outputfile, "WriteRegDWORD");
nsh_line_out(rootkey->name, subkey->name, current->name, outputfile);
// Squelch the 'dword:'
fprintf(outputfile, " 0x%s\n", current->value + 6);
break;
case REG_SZ: // Value contains '"' already
fprintf(outputfile, "WriteRegStr");
nsh_line_out(rootkey->name, subkey->name, current->name, outputfile);
fprintf(outputfile, " %s\n", current->value);
break;
default:
fprintf(stderr, "Unknown value type %d!\n", current->type);
return E_BAD_INPUT;
}
return E_OK;
}
regvalue *parsevalue(char *value)
{
/*
* Distinguishes semantic tokens from "data" ones while extracting the two
* fields (name and value) in each value struct.
*/
regvalue *current = (regvalue *)malloc(sizeof(regvalue));
char *tmp = (char *)malloc(MAX_LINE_LENGTH);
char *i = value, // tracks backslashes
*j = value, // finds '=', semantic or not
*k = value; // finds '"'
int noescapes = 0, // If no escaped characters in string
firstrun = 1;
int defaultkey = 0;
/*
* Find escaped chars (usually quote marks) and skip them. While not
* passing the real '=', find the second real quote. The '=' is just
* after that.
*/
do {
if (!noescapes && (i = strchr(i,'\\')) == NULL)
noescapes = 1;
j = strchr(j, '=');
if (j == NULL) {
fprintf(stderr, "Error parsing value: no equals sign found\n");
return NULL;
}
if (firstrun) {
firstrun = 0;
// Find the first quote mark, then skip it
k = strchr(k, '"');
if (k > j) {
// Value name is probably '@'
if (*value == '@') {
// Default key, all right
defaultkey++;
k = j - 1;
break;
}
}
//FIXME: This k++ is not obvious
if (k != value || k++ == NULL) {
fprintf(stderr, "Garbage at beginning of line!\n");
return NULL;
}
}
k = strchr(k,'"');
if (!noescapes && k == i + 1) { // The '"' is escaped, not meaningful
// FIXME Quotes in values are not handled properly (need to be escaped with '$\' )
i += 2; // Skip the escaped char and carry on
continue;
}
if (j == k + 1) // We found the end of the value name
break;
if (k > j) // That '=' was in the name
while (k > j)
j = strchr(++j,'=');
else
k++;
} while (1);
if (defaultkey) {
strcpy(current->name, "");
} else {
strncpy(tmp, value + 1, (size_t)(k - value - 1));
*(tmp + (k - value - 1)) = '\0'; // Chop off all but the value name
strcpy(current->name, tmp);
}
strncpy(tmp, j + 1, strlen(value) - (j - value + 1));
*(tmp + (strlen(value) - (j - value + 1))) = '\0'; // Denote end of value
strcpy(current->value, tmp);
current->type = gettype(tmp);
if (current->type < 0) {
fprintf(stderr, "Unrecognizable value '%s' (type %d)!\n", value,
current->type);
return NULL;
}
free(tmp);
return current;
}
int gettype(char *value)
{
/*
* Deduces the type of value passed in by its prefix. Not yet fully
* implemented.
*/
int rval;
// Returns the type of value passed in
if (STREQN(value, "\"", 1)) rval = REG_SZ; else
if (STREQN(value, "dword", 5)) rval = REG_DWORD; else
if (STREQN(value, "hex", 3)) {
//TODO: add support for all these
if (STREQN(value + 3, ":", 1)) rval = REG_BINARY; else
// if (STREQN(value + 3, "(0):", 3)) rval = REG_NONE; else
if (STREQN(value + 3, "(1):", 3)) rval = REG_SZ; else
if (STREQN(value + 3, "(2):", 3)) rval = REG_EXPAND_SZ; else
if (STREQN(value + 3, "(3):", 3)) rval = REG_BINARY; /*else
if (STREQN(value + 3, "(4):", 3)) rval = REG_DWORD; else
if (STREQN(value + 3, "(5):", 3)) rval = REG_DWORD_BIG_ENDIAN; else
if (STREQN(value + 3, "(6):", 3)) rval = REG_LINK; else
if (STREQN(value + 3, "(7):", 3)) rval = REG_MULTI_SZ;*/
rval |= REG_HEX_FORMAT;
} else
rval = -E_BAD_INPUT;
return rval;
}
int parsekeyname(char *value, regkey *rootkey, regkey *subkey)
{
/*
* Parses the key name out of the appropriate input-file lines and returns
* pointers to structs describing the full key as a combination of a root
* key and a combined subkey.
*/
char *loc = strchr(value, '\\'); // Marks the end of the base key
char *tmp = (char *)malloc(MAX_LINE_LENGTH);
if (loc == NULL) // dealing with root key only (no subkey)
loc = strlen(value) + value;
// Copy rootkey name into tmp
strncpy(tmp, value, (size_t)(loc - value));
// strlen(rootkey name) == loc - value
*(tmp + (loc - value)) = '\0'; // Chop off all but rootkey name
if (STREQ("HKEY_USERS", tmp)) strcpy(tmp, "HKU" ); else
if (STREQ("HKEY_CLASSES_ROOT", tmp)) strcpy(tmp, "HKCR"); else
if (STREQ("HKEY_CURRENT_USER", tmp)) strcpy(tmp, "HKCU"); else
if (STREQ("HKEY_LOCAL_MACHINE", tmp)) strcpy(tmp, "HKLM"); else
if (STREQ("HKEY_CURRENT_CONFIG", tmp)) strcpy(tmp, "HKCC");
else {
fprintf(stderr, "Unknown root key '%s'!\n", tmp);
return E_BAD_INPUT;
}
strcpy(rootkey->name, tmp);
rootkey->parent = NULL;
// Are we are dealing with a root key only?
if (loc == strlen(value) + value) { // Yes
strcpy(subkey->name, "");
subkey->parent = NULL;
} else { // No
// Copy subkey name into tmp
strncpy(tmp, loc + 1, (size_t)(strlen(value) - (loc - value) - 1));
*(tmp + (strlen(value) - (loc - value) - 1)) = '\0';
strcpy(subkey->name, tmp);
subkey->parent = rootkey;
}
free(tmp);
return E_OK;
}
int usage(char *name)
{
fprintf(stderr, "Converts a Windows Registry export file (.REG) to NSIS syntax\n\n");
fprintf(stderr, "Usage: %s [inputfile [outputfile]]\n", name);
fprintf(stderr, "inputfile and outputfile default to stdin and stdout.\n");
return E_BAD_ARGS;
}
char *flatten_binary_data(char *data)
{
/*
* Return a comma-stripped version of the hex data passed in.
*/
char *colon = strchr(data, ':');
char *output = (char *)malloc(strlen(data));
char *tmp;
if (colon == NULL) {
fprintf(stderr, "Cannot decode input binary data: no colon!\n");
return NULL;
} else
colon++;
*output = '\0';
tmp = strtok(colon, ",");
do
strcat(output, tmp);
while ((tmp = strtok(NULL, ",")) != NULL);
return output;
}
int stripline(char *input, char *output)
{
/*
* Kills nulls and ending newlines in the input.
*/
int i = 0,
j = 0;
for (i = 0; input[i] != '\n' && input[i] != '\015'; i++) {
if (input[i]) {
output[j++] = input[i];
}
}
output[j] = '\0';
return E_OK;
}
int get_mogrified_line(FILE *inputfile, char *line)
{
/*
* Properly mogrifies the input lines and returns uniformly-formatted
* strings regardless of input file type.
*/
char *output = (char *)malloc(MAX_LINE_LENGTH);
char *input = fgets(line, MAX_LINE_LENGTH - 1, inputfile);
int rval = E_OK;
if (output == NULL) {
fprintf(stderr, "Bad pointer at code line %d!", __LINE__);
return E_BAD_PTR;
}
if (input == NULL) {
output[0] = '\0';
rval = E_EOF;
} else if (stripline(input, output) != E_OK) // EOF
rval = E_EOF;
if (rval == E_OK) {
strcpy(line,output);
strcpy(input,line);
} else {
free(output);
}
return rval;
}
void nsh_line_out(char *rkey, char *skey, char *value, FILE *outputfile)
{
fprintf(outputfile, " %s \"%s\" \"%s\"", rkey, skey, value);
}
/*
reg2nsh.h : header for reg2nsh.c.
$Rev$
$Date$
Copyright (c) 2005 Darren Kulp under the terms of the MIT License
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_MALLOC_H
# ifdef DARWIN
# include <sys/malloc.h>
# endif
#endif
#define REG_NONE 0
#define REG_SZ 1
#define REG_EXPAND_SZ 2
#define REG_BINARY 3
#define REG_DWORD 4
#define REG_DWORD_LITTLE_ENDIAN 4
#define REG_DWORD_BIG_ENDIAN 5
#define REG_LINK 6
#define REG_MULTI_SZ 7
// This arbitrary constant is ORed with values given in a hexadecimal format
#define REG_HEX_FORMAT 100
//FIXME: Make this less arbitrary
#define MAX_LINE_LENGTH 2048
// This constant is from the spec
#define MAX_KEY_LENGTH 256
#define STREQ(a,b) (strcmp(a,b) == 0)
#define STREQN(a,b,c) (strncmp(a,b,c) == 0)
enum RETURNCODE {
E_OK = 0,
E_BAD_PTR,
E_BAD_INPUT,
E_BAD_ARGS,
E_EOF
};
typedef struct _regkey {
struct _regkey *parent;
char name[MAX_KEY_LENGTH];
} regkey;
typedef struct _regvalue {
short type;
char name[MAX_LINE_LENGTH];
char value[MAX_LINE_LENGTH];
} regvalue;
char *flatten_binary_data(char *data);
int usage(char *name);
int gettype(char *value);
int stripline(char *data, char *output);
int get_mogrified_line(FILE *inputfile, char *line);
int parsekeyname(char *value, regkey *rootkey, regkey *subkey);
int printvalue(char *value, regkey *rootkey, regkey *subkey, FILE *ouputfile);
regvalue *parsevalue(char *value);
void nsh_line_out(char *rkey, char *skey, char *value, FILE * outputfile);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment