Files
2014-10-18 22:25:05 +02:00

698 lines
17 KiB
C

/*
* edlib.c - Registry edit interactive fuctions.
*
* Point of this is so that interactive registry editor
* can be accessed from several other programs
*
* 2013-aug: Some minor bugfixes and adjustments
* Thanks to David Collett for catching and fixing bug in REG_MULTI_SZ editing.
* 2010-jun: New function from Aleksander Wojdyga: dpi, decode product ID
* Mostly used on \Microsoft\Windows NT\CurrentVersion\DigitalProductId
* Now as command in registry editor, but may be moved to chnpw menu later.
* 2010-apr: Lots of bugfix and other patches from
* Frediano Ziglio <freddy77@gmail.com>
* His short patch comments:
* remove leak
* fix default value, bin and quote
* support wide char in key
* support wide character into value names
* fix export for string with embedded end lines
* remove some warnings
* compute checksum writing
*
* 2008-mar: First version. Moved from chntpw.c
* See HISTORY.txt for more detailed info on history.
*
*****
*
* Copyright (c) 1997-2014 Petter Nordahl-Hagen.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* See file GPL.txt for the full license.
*
*****
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "ntreg.h"
const char edlib_version[] = "edlib version 0.1 140201, (c) Petter N Hagen";
#define ALLOC_DEBUG 0 /* Reg allocation debug hooks */
#define ADDBIN_DEBUG 0 /* Reg expansion debug hooks */
extern char *val_types[REG_MAX+1];
struct cmds {
char cmd_str[12];
int cmd_num;
};
#define MCMD_CD 1
#define MCMD_LS 2
#define MCMD_QUIT 3
#define MCMD_CAT 4
#define MCMD_STRUCT 5
#define MCMD_DEBUG 6
#define MCMD_HELP 7
#define MCMD_EXPORTKEY 8
#define MCMD_HIVE 9
#define MCMD_EDIT 10
#define MCMD_ALLOC 11
#define MCMD_FREE 12
#define MCMD_ADDV 13
#define MCMD_DELV 14
#define MCMD_DELVALL 15
#define MCMD_NEWKEY 16
#define MCMD_DELKEY 17
#define MCMD_CATHEX 18
#define MCMD_RDEL 19
#define MCMD_CK 20
#define MCMD_CAT_DPI 21
#define MCMD_ADDBIN 22
struct cmds maincmds[] = {
{ "cd" , MCMD_CD } ,
{ "ls" , MCMD_LS } ,
{ "dir", MCMD_LS } ,
{ "q" , MCMD_QUIT } ,
{ "cat", MCMD_CAT } ,
{ "type",MCMD_CAT } ,
{ "st" , MCMD_STRUCT } ,
{ "debug", MCMD_DEBUG } ,
{ "hive", MCMD_HIVE } ,
{ "ed", MCMD_EDIT } ,
#if ALLOC_DEBUG
{ "alloc", MCMD_ALLOC } ,
{ "free", MCMD_FREE } ,
#endif
#if ADDBIN_DEBUG
{ "addbin", MCMD_ADDBIN },
#endif
{ "nv", MCMD_ADDV } ,
{ "dv", MCMD_DELV } ,
{ "delallv", MCMD_DELVALL } ,
{ "nk", MCMD_NEWKEY } ,
{ "dk", MCMD_DELKEY } ,
{ "hex", MCMD_CATHEX } ,
{ "rdel", MCMD_RDEL } ,
{ "ek", MCMD_EXPORTKEY },
{ "ck", MCMD_CK } ,
{ "?", MCMD_HELP } ,
{ "dpi", MCMD_CAT_DPI } ,
{ "", 0 }
};
/* display decoded DigitalProductId
* nkofs = node
* path = "DigitalProductId" or some other
*/
void cat_dpi(struct hive *hdesc, int nkofs, char *path)
{
void *data;
int len,i,type;
type = get_val_type(hdesc, nkofs, path, 0);
if (type == -1) {
printf("cat_dpi: No such value <%s>\n",path);
return;
}
len = get_val_len(hdesc, nkofs, path, 0);
if (len < 67) {
printf("cat_dpi: Value <%s> is too short for decoding\n",path);
return;
}
data = (void *)get_val_data(hdesc, nkofs, path, 0, 0);
if (!data) {
printf("cat_dpi: Value <%s> references NULL-pointer (bad boy!)\n",path);
abort();
return;
}
if (type != REG_BINARY) {
printf ("Only binary values\n");
return;
}
printf("Value <%s> of type %s, data length %d [0x%x]\n", path,
(type < REG_MAX ? val_types[type] : "(unknown)"), len, len);
char digits[] = {'B','C','D','F','G','H','J','K','M','P','Q','R','T','V','W','X','Y','2','3','4','6','7','8','9'};
#define RESULT_LEN 26
char result[RESULT_LEN];
memset (result, 0, RESULT_LEN);
#define START_OFFSET 52
#define BUF_LEN 15
unsigned char buf[BUF_LEN];
memcpy (buf, data + START_OFFSET, BUF_LEN);
for (i = RESULT_LEN - 2; i >= 0; i--) {
unsigned int x = 0;
int j;
for (j = BUF_LEN - 1; j >= 0; j--) {
x = (x << 8) + buf[j];
buf[j] = x / 24;
x = x % 24;
}
result[i] = digits[x];
}
printf ("\nDecoded product ID: [%s]\n", result);
}
/* display (cat) the value,
* vofs = offset to 'nk' node, paths relative to this (or 0 for root)
* path = path string to value
* Does not handle all types yet (does a hexdump instead)
*/
void cat_vk(struct hive *hdesc, int nkofs, char *path, int dohex)
{
void *data;
int len,i,type;
// char string[SZ_MAX+1];
char *string = NULL;
struct keyval *kv = NULL;
type = get_val_type(hdesc, nkofs, path, TPF_VK);
if (type == -1) {
printf("cat_vk: No such value <%s>\n",path);
return;
}
len = get_val_len(hdesc, nkofs, path, TPF_VK);
if (!len) {
printf("cat_vk: Value <%s> has zero length\n",path);
return;
}
#if 0
data = (void *)get_val_data(hdesc, nkofs, path, 0, TPF_VK);
if (!data) {
printf("cat_vk: Value <%s> references NULL-pointer (bad boy!)\n",path);
abort();
return;
}
#endif
kv = get_val2buf(hdesc, NULL, nkofs, path, 0, TPF_VK);
if (!kv) {
printf("cat_vk: Value <%s> could not fetch data\n",path);
abort();
}
data = (void *)&(kv->data);
printf("Value <%s> of type %s (%x), data length %d [0x%x]\n", path,
(type < REG_MAX ? val_types[type] : "(unknown)"), type, len, len);
if (dohex) type = REG_BINARY;
switch (type) {
case REG_SZ:
case REG_EXPAND_SZ:
case REG_MULTI_SZ:
string = string_regw2prog(data, len);
// cheap_uni2ascii(data,string,len);
for (i = 0; i < (len>>1)-1; i++) {
if (string[i] == 0) string[i] = '\n';
if (type == REG_SZ) break;
}
puts(string);
FREE(string);
break;
case REG_DWORD:
printf("0x%08x",*(unsigned short *)data);
break;
default:
printf("Don't know how to handle type yet!\n");
case REG_BINARY:
hexdump((char *)data, 0, len, 1);
}
putchar('\n');
FREE(kv);
}
/* Edit value: Invoke whatever is needed to edit it
* based on its type
*/
void edit_val(struct hive *h, int nkofs, char *path)
{
struct keyval *kv, *newkv;
int type,len,n,i,in,go, newsize, d = 0, done, insert = 0;
char inbuf[SZ_MAX+4];
char origstring[SZ_MAX+4];
char *newstring;
char *dbuf;
type = get_val_type(h, nkofs, path, TPF_VK);
if (type == -1) {
printf("Value <%s> not found!\n",path);
return;
}
kv = get_val2buf(h, NULL, nkofs, path, type, TPF_VK);
if (!kv) {
printf("Unable to get data of value <%s>\n",path);
return;
}
len = kv->len;
printf("EDIT: <%s> of type %s (%x) with length %d [0x%x]\n", path,
(type < REG_MAX ? val_types[type] : "(unknown)"), type,
len, len);
switch(type) {
case REG_DWORD:
printf("DWORD: Old value %d [0x%x], ", kv->data, kv->data);
fmyinput("enter new value (prepend 0x if hex, empty to keep old value)\n-> ",
inbuf, 12);
if (*inbuf) {
sscanf(inbuf,"%i",&kv->data);
d = 1;
}
printf("DWORD: New value %d [0x%x], ", kv->data, kv->data);
break;
case REG_SZ:
case REG_EXPAND_SZ:
case REG_MULTI_SZ:
newstring = NULL;
dbuf = (char *)&kv->data;
cheap_uni2ascii(dbuf,origstring,len);
n = 0; i = 0;
while (i < (len>>1)-1) {
printf("[%2d]: %s\n",n,origstring+i);
i += strlen(origstring+i) + 1;
n++;
}
printf("\nNow enter new strings, one by one.\n");
printf("Enter nothing to keep old.\n");
if (type == REG_MULTI_SZ) {
printf("'--n' to quit (remove rest of strings)\n");
printf("'--i' insert new string at this point\n");
printf("'--q' to quit (leaving remaining strings as is)\n");
printf("'--Q' to quit and discard all changes\n");
printf("'--e' for empty string in this position\n");
}
n = 0; i = 0; in = 0; go = 0; done = 0;
/* Now this one is RATHER UGLY :-} */
while (i < (len>>1)-1 || !done) {
printf("[%2d]: %s\n",n, insert == 1 ? "[INSERT]" : ((i < (len>>1)-1 ) ? origstring+i : "[NEW]"));
if (insert) insert++;
if (!go) fmyinput("-> ",inbuf, 500);
else *inbuf = 0;
if (*inbuf && strcmp("--q", inbuf)) {
if (!strcmp("--n", inbuf) || !strcmp("--Q", inbuf)) { /* Zap rest */
i = (len>>1) ; done = 1;
} else if (strcmp("--i", inbuf)) { /* Copy out given string */
if (!strcmp("--e",inbuf)) *inbuf = '\0';
if (newstring) newstring = realloc(newstring, in+strlen(inbuf)+1);
else newstring = malloc(in+strlen(inbuf)+1);
strcpy(newstring+in, inbuf);
in += strlen(inbuf)+1;
} else {
insert = 1;
}
} else { /* Copy out default string */
if (newstring) newstring = realloc(newstring, in+strlen(origstring+i)+1);
else newstring = malloc(in + strlen(origstring+i) + 1);
strcpy(newstring+in, origstring+i);
in += strlen(origstring+i)+1;
if (!strcmp("--q", inbuf)) {
go = 1; done = 1;
if (!(i < (len>>1)-1 )) {
in--; /* remove last empty if in NEW-mode */
}
}
}
if (!insert) i += strlen(origstring+i) + 1;
if (insert != 1) n++;
if (insert == 2) insert = 0;
if (type != REG_MULTI_SZ) {
i = (len<<1);
done = 1;
}
}
if (strcmp("--Q", inbuf)) { /* We didn't bail out */
if (newstring) newstring = realloc(newstring, in+1);
else newstring = malloc(in+1);
if (type == REG_MULTI_SZ) {
*(newstring+in) = '\0'; /* Must add null termination */
in++;
}
ALLOC(newkv,1,(in<<1)+sizeof(int));
newkv->len = in<<1;
VERBF(h,"newkv->len: %d\n",newkv->len);
cheap_ascii2uni(newstring, (char *)&(newkv->data), in);
d = 1;
FREE(kv);
kv = newkv;
}
break;
default:
printf("Type not handeled (yet), invoking hex editor on data!\n");
case REG_BINARY:
fmyinput("New length (ENTER to keep same): ",inbuf,90);
if (*inbuf) {
newsize = atoi(inbuf);
ALLOC(newkv,1,newsize+sizeof(int)+4);
bzero(newkv,newsize+sizeof(int)+4);
memcpy(newkv, kv, ((len < newsize) ? (len) : (newsize)) + sizeof(int));
FREE(kv);
kv = newkv;
kv->len = newsize;
}
d = debugit((char *)&kv->data, kv->len);
break;
}
if (d) {
if (!(put_buf2val(h, kv, nkofs, path, type, TPF_VK))) {
printf("Failed to set value!?\n");
}
}
FREE(kv);
}
/* look up command in array
*/
int parsecmd(char **s, struct cmds *cmd)
{
int l = 0;
while ((*s)[l] && ((*s)[l] != ' ')) {
l++;
}
while (cmd->cmd_num) {
if (!strncmp(*s, cmd->cmd_str, l)) {
*s += l;
return(cmd->cmd_num);
}
cmd++;
}
return(0);
}
/* Lot of people didn't understand the "nv" command.
* Actually the command should understand the type names too, but.. some later time
*/
void nv_help(void)
{
int i;
printf("Command syntax is:\n\n"
" nv <type> <valuename>\n\n"
"where <type> should be the HEX NUMBER from one of these registry value types:\n\n");
for (i=0; i < REG_MAX; i++) {
printf(" %2x : %s\n",i,val_types[i]);
}
printf("\nExample:\n nv 4 foobar\n");
printf("to make a new value named foobar of the type REG_DWORD\n\n");
}
/* Interactive registry editor
* hive - list of loaded hives (array pointing to hive structs)
* no_hives - max number of hives loaded
*/
void regedit_interactive(struct hive *hive[], int no_hives)
{
struct hive *hdesc;
int cdofs, newofs;
// struct nk_key *cdkey;
char inbuf[100], *bp, *file, *prefix;
char path[1000];
int l, vkofs, nh, i;
int usehive = 0;
struct keyval *kv;
#if ALLOC_DEBUG
int pagestart;
int freetest;
#endif
hdesc = hive[usehive];
cdofs = hdesc->rootofs;
printf("Simple registry editor. ? for help.\n");
while (1) {
// cdkey = (struct nk_key *)(hdesc->buffer + cdofs);
*path = 0;
get_abs_path(hdesc,cdofs+4, path, 50);
#if ALLOC_DEBUG
pagestart = find_page_start(hdesc,cdofs);
printf("find_page_start: 0x%x\n",pagestart);
freetest = find_free_blk(hdesc,pagestart,10);
printf("find_free_blk: 0x%x\n",freetest);
#endif
if (hdesc->state & HMODE_VERBOSE) printf("\n[%0x] %s> ",cdofs,path);
else printf("\n%s> ",path);
l = fmyinput("",inbuf,90);
bp = inbuf;
skipspace(&bp);
if (l > 0 && *bp) {
switch(parsecmd(&bp,maincmds)) {
case MCMD_HELP:
printf("Simple registry editor:\n");
printf("hive [<n>] - list loaded hives or switch to hive numer n\n");
printf("cd <key> - change current key\n");
printf("ls | dir [<key>] - show subkeys & values,\n");
printf("cat | type <value> - show key value\n");
printf("dpi <value> - show decoded DigitalProductId value\n");
printf("hex <value> - hexdump of value data\n");
printf("ck [<keyname>] - Show keys class data, if it has any\n");
printf("nk <keyname> - add key\n");
printf("dk <keyname> - delete key (must be empty)\n");
printf("ed <value> - Edit value\n");
printf("nv <type#> <valuename> - Add value\n");
printf("dv <valuename> - Delete value\n");
printf("delallv - Delete all values in current key\n");
printf("rdel <keyname> - Recursively delete key & subkeys\n");
printf("ek <filename> <prefix> <keyname> - export key to <filename> (Windows .reg file format)\n");
printf("debug - enter buffer hexeditor\n");
printf("st [<hexaddr>] - debug function: show struct info\n");
printf("q - quit\n");
break;
case MCMD_DELKEY :
bp++;
skipspace(&bp);
del_key(hdesc, cdofs + 4, bp);
break;
case MCMD_NEWKEY :
bp++;
skipspace(&bp);
add_key(hdesc, cdofs + 4, bp);
break;
case MCMD_DELVALL :
bp++;
skipspace(&bp);
del_allvalues(hdesc, cdofs + 4);
break;
case MCMD_DELV :
bp++;
skipspace(&bp);
del_value(hdesc, cdofs + 4, bp, 0);
break;
case MCMD_ADDV :
bp++;
skipspace(&bp);
if (!isxdigit(*bp)) {
nv_help();
break;
}
nh = gethex(&bp);
skipspace(&bp);
if (!*bp) {
nv_help();
break;
}
add_value(hdesc, cdofs+4, bp, nh);
break;
#if ALLOC_DEBUG
case MCMD_FREE :
bp++;
skipspace(&bp);
nh = gethex(&bp);
free_block(hdesc, nh);
break;
case MCMD_ALLOC :
bp++;
skipspace(&bp);
nh = gethex(&bp);
alloc_block(hdesc, cdofs+4, nh);
break;
#endif
#if ADDBIN_DEBUG
case MCMD_ADDBIN :
bp++;
skipspace(&bp);
nh = gethex(&bp);
add_bin(hdesc, nh);
break;
#endif
case MCMD_LS :
bp++;
skipspace(&bp);
nk_ls(hdesc, bp, cdofs+4, 0);
break;
case MCMD_CK :
bp++;
skipspace(&bp);
kv = get_class(hdesc, cdofs+4, bp);
if (kv) {
hexdump((char *)&kv->data, 0, kv->len, 1);
FREE(kv);
}
break;
case MCMD_RDEL :
bp++;
skipspace(&bp);
rdel_keys(hdesc, bp, cdofs+4);
break;
case MCMD_EDIT :
bp++;
skipspace(&bp);
edit_val(hdesc, cdofs+4, bp);
break;
case MCMD_HIVE :
bp++;
skipspace(&bp);
if (*bp) {
nh = gethex(&bp);
if (nh >= 0 && nh < no_hives) {
usehive = nh;
printf("Switching to hive #%d, named <%s>, size %d [0x%x]\n",
usehive, hive[usehive]->filename,
hive[usehive]->size,
hive[usehive]->size);
hdesc = hive[usehive];
cdofs = hdesc->rootofs;
}
} else {
for (nh = 0; nh < no_hives; nh++) {
printf("%c %c %2d %9d 0x%08x <%s>\n", (nh == usehive) ? '*' : ' ',
(hive[nh]->state & HMODE_DIRTY) ? 'D' : ' ',
nh,
hive[nh]->size,
hive[nh]->size, hive[nh]->filename);
}
}
break;
case MCMD_CD :
bp++;
skipspace(&bp);
newofs = trav_path(hdesc, cdofs+4,bp,TPF_NK);
if (newofs) cdofs = newofs;
else printf("Key %s not found!\n",bp);
break;
case MCMD_CAT:
bp++;
skipspace(&bp);
cat_vk(hdesc,cdofs+4,bp,0);
break;
case MCMD_CAT_DPI:
bp++;
skipspace(&bp);
cat_dpi (hdesc, cdofs+4, bp);
break;
case MCMD_CATHEX:
bp++;
skipspace(&bp);
cat_vk(hdesc,cdofs+4,bp,1);
break;
case MCMD_EXPORTKEY :
bp++;
skipspace(&bp);
file = bp;
i = 0;
while(*bp != ' ' && (*bp))
{
i++;
bp++;
}
file[i] = '\0';
bp++;
skipspace(&bp);
prefix = bp;
i = 0;
while(*bp != ' ' && (*bp))
{
i++;
bp++;
}
prefix[i] = '\0';
bp++;
skipspace(&bp);
export_key(hdesc, cdofs + 4, bp, file, prefix);
break;
case MCMD_STRUCT:
bp++;
skipspace(&bp);
vkofs = cdofs;
if (*bp) {
vkofs = gethex(&bp);
}
parse_block(hdesc,vkofs,2);
break;
case MCMD_DEBUG:
if (debugit(hdesc->buffer,hdesc->size)) hdesc->state |= HMODE_DIRTY;
break;
case MCMD_QUIT:
return;
break;
default:
printf("Unknown command: %s, type ? for help\n",bp);
break;
}
}
}
}