mirror of
https://github.com/rescatux/chntpw
synced 2026-06-21 14:07:44 +00:00
4364 lines
119 KiB
C
4364 lines
119 KiB
C
/*
|
|
* ntreg.c - Windows (NT and up) Registry Hive access library
|
|
* should be able to handle most basic functions:
|
|
* iterate, add&delete keys and values, read stuff, change stuff etc
|
|
* no rename of keys or values yet..
|
|
* also contains some minor utility functions (string handling etc) for now
|
|
*
|
|
* 2014-jan: openhive() now more compatible with build on non-unix?
|
|
* 2013-aug: Enter buil-in buffer debugger only if in trace mode, else return error or abort()
|
|
* 2013-may-aug: Fixed critical bug in del_value which could
|
|
* thrash the hive when removing value in bottom of key.
|
|
* And a pointer not reinitialized when buffer reallocated in some cases, fixed.
|
|
* Thanks to Jacky To for reporting those two.
|
|
* Some minor adjustments for compiler. A few more utility functions.
|
|
* 2012-oct: Added set_val_type. some minor changes.
|
|
* 2011-may: Seems like large values >16k or something like that is split
|
|
* into several blocks (db), have tried to implement that.
|
|
* Vista seems to accept it. Not tested on others yet.
|
|
* 2011-may: Expansion now seems to be working, have lot of test accepted by
|
|
* vista and win7. But no warranties..
|
|
* 2011-may: Found a couple of nasty bugs inn add_key(), making Vista and newer
|
|
* reject (remove) keys on hive load.
|
|
* May have been there for a long time according to reports.
|
|
* 2011-apr: Fixed some problems with the allocator when at the end of a hbin.
|
|
* 2011-apr: .reg file import. Ugly one, but it seems to work. Found
|
|
* quite a lot of bugs in other places while testing it.
|
|
* String handling when international characters or wide (UTF-16)
|
|
* is a pain, and very ugly. May not work in some cases.
|
|
* Will keep wide (16 bit) characters in strings when importing from
|
|
* .reg file that has it, like what regedit.exe generates for example.
|
|
* 2011-apr: Added routines for hive expansion. Will add new hbin at end of file
|
|
* when needed. If using library, please read ugly warnings in "alloc_block()".
|
|
* 2010-jun: Patches from Frediano Ziglio adding support for wide characters
|
|
* and some bugfixes. Thank you!
|
|
* 2008-mar: Type QWORD (XP/Vista and newer) now recognized
|
|
* 2008-mar: Most functions accepting a path now also have a parameter specifying if
|
|
* the search should be exact or on first match basis
|
|
* 2008-mar: Fixed bug which skipped first indirect index table when deleting keys,
|
|
* usually leading to endless loop when recursive deleting.
|
|
* 2008-mar: Export to .reg file by Leo von Klenze, expanded a bit by me.
|
|
* 2008-mar: 64 bit compatible patch by Mike Doty, via Alon Bar-Lev
|
|
* http://bugs.gentoo.org/show_bug.cgi?id=185411
|
|
* 2007-sep: Verbosity/debug messages minor changes
|
|
* 2007-apr: LGPL license.
|
|
* 2004-aug: Deep indirect index support. NT351 support. Recursive delete.
|
|
* Debugged a lot in allocation routines. Still no expansion.
|
|
* 2004-jan: Verbosity updates
|
|
* 2003-jan: Allocation of new data, supports adding/deleting keys & stuff.
|
|
* Missing is expanding the file.
|
|
* 2003-jan: Seems there may be garbage pages at end of file, not zero pages
|
|
* now stops enumerating at first non 'hbin' page.
|
|
*
|
|
* NOTE: The API is not frozen. It can and will change every release.
|
|
*
|
|
*****
|
|
*
|
|
* NTREG - Window registry file reader / writer library
|
|
* Copyright (c) 1997-2014 Petter Nordahl-Hagen.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
* See file LGPL.txt for the full license.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <inttypes.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "ntreg.h"
|
|
|
|
/* Set to abort() and debug on more critical errors */
|
|
#define DOCORE 1
|
|
|
|
#define ZEROFILL 1 /* Fill blocks with zeroes when allocating and deallocating */
|
|
#define ZEROFILLONLOAD 0 /* Fill blocks marked as unused/deallocated with zeroes on load. FOR DEBUG */
|
|
|
|
const char ntreg_version[] = "ntreg lib routines, v0.95 140201, (c) Petter N Hagen";
|
|
|
|
const char *val_types[REG_MAX+1] = {
|
|
"REG_NONE", "REG_SZ", "REG_EXPAND_SZ", "REG_BINARY", "REG_DWORD", /* 0 - 4 */
|
|
"REG_DWORD_BIG_ENDIAN", "REG_LINK", /* 5 - 6 */
|
|
"REG_MULTI_SZ", "REG_RESOUCE_LIST", "REG_FULL_RES_DESC", "REG_RES_REQ", /* 7 - 10 */
|
|
"REG_QWORD", /* 11 */
|
|
};
|
|
|
|
static char * string_prog2regw(void *string, int len, int* out_len);
|
|
|
|
/* Utility routines */
|
|
|
|
/* toupper() table for registry hashing functions, so we don't have to
|
|
* dependent upon external locale lib files
|
|
*/
|
|
|
|
static const unsigned char reg_touppertable[] = {
|
|
|
|
/* ISO 8859-1 is probably not the one.. */
|
|
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
|
|
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
|
|
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
|
|
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
|
|
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
|
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
|
|
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
|
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
|
|
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
|
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
|
|
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
|
|
0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
|
|
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
|
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
|
|
0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
|
|
|
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
|
|
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
|
|
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
|
|
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
|
|
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
|
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x00, 0xb6, 0xb7, /* 0xb0-0xb7 */
|
|
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
|
|
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
|
|
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
|
|
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
|
|
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
|
|
0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
|
|
0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
|
|
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */
|
|
0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* 0xf8-0xff */
|
|
|
|
};
|
|
|
|
|
|
/* Use table above in strcasecmp else add_key may put names in wrong order
|
|
and windows actually verifies that on hive load!!
|
|
or at least it finds out in some cases..
|
|
*/
|
|
|
|
|
|
int strn_casecmp(const char *s1, const char *s2, size_t n)
|
|
{
|
|
char r;
|
|
|
|
while ( *s1 && *s2 && n ) {
|
|
r = (unsigned char)reg_touppertable[(unsigned char)*s1] - (unsigned char)reg_touppertable[(unsigned char)*s2];
|
|
if (r) return(r);
|
|
n--;
|
|
s1++;
|
|
s2++;
|
|
}
|
|
if ( (!*s1 && !*s2) || !n) return(0);
|
|
if ( !*s1 ) return(-1);
|
|
return(1);
|
|
}
|
|
|
|
|
|
char *str_dup( const char *str )
|
|
{
|
|
char *str_new;
|
|
|
|
if (!str) return(0);
|
|
|
|
CREATE( str_new, char, strlen(str) + 1 );
|
|
strcpy( str_new, str );
|
|
return str_new;
|
|
}
|
|
|
|
char *str_cat(char *str, char *add)
|
|
{
|
|
if (!add) return(str);
|
|
str = (char *) realloc(str, strlen(str) + strlen(add) + 3);
|
|
strcat (str, add);
|
|
return (str);
|
|
}
|
|
|
|
char *str_catf(char *str, const char *format, ... )
|
|
{
|
|
va_list ap;
|
|
char add[640] ;
|
|
|
|
va_start(ap, format) ;
|
|
vsprintf(add, format, ap) ;
|
|
va_end(ap) ;
|
|
|
|
str = (char *) realloc(str, strlen(str) + strlen(add) + 3);
|
|
strcat (str, add );
|
|
return(str);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy non-terminated string to buffer we allocate and null terminate it
|
|
* Uses length only, does not check for nulls
|
|
*/
|
|
|
|
char *mem_str( const char *str, int len )
|
|
{
|
|
char *str_new;
|
|
|
|
if (!str)
|
|
return 0 ;
|
|
|
|
CREATE( str_new, char, len + 1 );
|
|
memcpy( str_new, str, len);
|
|
*(str_new+len) = 0;
|
|
return str_new;
|
|
}
|
|
|
|
|
|
int fmyinput(char *prmpt, char *ibuf, int maxlen)
|
|
{
|
|
|
|
printf("%s",prmpt);
|
|
|
|
fgets(ibuf,maxlen+1,stdin);
|
|
|
|
ibuf[strlen(ibuf)-1] = 0;
|
|
|
|
return(strlen(ibuf));
|
|
}
|
|
|
|
/* Print len number of hexbytes */
|
|
|
|
void hexprnt(char *s, unsigned char *bytes, int len)
|
|
{
|
|
int i;
|
|
|
|
printf("%s",s);
|
|
for (i = 0; i < len; i++) {
|
|
printf("%02x ",bytes[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/* HexDump all or a part of some buffer */
|
|
|
|
void hexdump(char *hbuf, int start, int stop, int ascii)
|
|
{
|
|
char c;
|
|
int diff,i;
|
|
|
|
while (start < stop ) {
|
|
|
|
diff = stop - start;
|
|
if (diff > 16) diff = 16;
|
|
|
|
printf(":%05X ",start);
|
|
|
|
for (i = 0; i < diff; i++) {
|
|
printf("%02X ",(unsigned char)*(hbuf+start+i));
|
|
}
|
|
if (ascii) {
|
|
for (i = diff; i < 16; i++) printf(" ");
|
|
for (i = 0; i < diff; i++) {
|
|
c = *(hbuf+start+i);
|
|
printf("%c", isprint(c) ? c : '.');
|
|
}
|
|
}
|
|
printf("\n");
|
|
start += 16;
|
|
}
|
|
}
|
|
|
|
/* General search routine, find something in something else */
|
|
int find_in_buf(char *buf, char *what, int sz, int len, int start)
|
|
{
|
|
int i;
|
|
|
|
for (; start < sz; start++) {
|
|
for (i = 0; i < len; i++) {
|
|
if (*(buf+start+i) != *(what+i)) break;
|
|
}
|
|
if (i == len) return(start);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* Get INTEGER from memory. This is probably low-endian specific? */
|
|
int get_int( char *array )
|
|
{
|
|
return ((array[0]&0xff) + ((array[1]<<8)&0xff00) +
|
|
((array[2]<<16)&0xff0000) +
|
|
((array[3]<<24)&0xff000000));
|
|
}
|
|
|
|
|
|
/* Quick and dirty UNICODE to std. ascii */
|
|
|
|
void cheap_uni2ascii(char *src, char *dest, int l)
|
|
{
|
|
|
|
for (; l > 0; l -=2) {
|
|
*dest = *src;
|
|
dest++; src +=2;
|
|
}
|
|
*dest = 0;
|
|
}
|
|
|
|
|
|
/* Quick and dirty ascii to unicode */
|
|
|
|
void cheap_ascii2uni(char *src, char *dest, int l)
|
|
{
|
|
for (; l > 0; l--) {
|
|
*dest++ = *src++;
|
|
*dest++ = 0;
|
|
|
|
}
|
|
}
|
|
|
|
void skipspace(char **c)
|
|
{
|
|
while( **c == ' ' ) (*c)++;
|
|
}
|
|
|
|
int gethex(char **c)
|
|
{
|
|
int value;
|
|
|
|
skipspace(c);
|
|
|
|
if (!(**c)) return(0);
|
|
|
|
sscanf(*c,"%x",&value);
|
|
|
|
while( **c != ' ' && (**c)) (*c)++;
|
|
|
|
return(value);
|
|
}
|
|
|
|
/* Get a string of HEX bytes (space separated),
|
|
* or if first char is ' get an ASCII string instead.
|
|
*/
|
|
|
|
int gethexorstr(char **c, char *wb)
|
|
{
|
|
int l = 0;
|
|
|
|
skipspace(c);
|
|
|
|
if ( **c == '\'') {
|
|
(*c)++;
|
|
while ( **c ) {
|
|
*(wb++) = *((*c)++);
|
|
l++;
|
|
}
|
|
} else {
|
|
do {
|
|
*(wb++) = gethex(c);
|
|
l++;
|
|
skipspace(c);
|
|
} while ( **c );
|
|
}
|
|
return(l);
|
|
}
|
|
|
|
/* Simple buffer debugger, returns 1 if buffer dirty/edited */
|
|
|
|
int debugit(char *buf, int sz)
|
|
{
|
|
|
|
|
|
char inbuf[100],whatbuf[100],*bp;
|
|
|
|
int dirty=0,to,from,l,i,j,wlen,cofs = 0;
|
|
|
|
printf("Buffer debugger. '?' for help.\n");
|
|
|
|
while (1) {
|
|
l = fmyinput(".",inbuf,90);
|
|
bp = inbuf;
|
|
|
|
skipspace(&bp);
|
|
|
|
if (l > 0 && *bp) {
|
|
switch(*bp) {
|
|
case 'd' :
|
|
bp++;
|
|
if (*bp) {
|
|
from = gethex(&bp);
|
|
to = gethex(&bp);
|
|
} else {
|
|
from = cofs; to = 0;
|
|
}
|
|
if (to == 0) to = from + 0x100;
|
|
if (to > sz) to = sz;
|
|
hexdump(buf,from,to,1);
|
|
cofs = to;
|
|
break;
|
|
case 'a' :
|
|
bp++;
|
|
if (*bp) {
|
|
from = gethex(&bp);
|
|
to = gethex(&bp);
|
|
} else {
|
|
from = cofs; to = 0;
|
|
}
|
|
if (to == 0) to = from + 0x100;
|
|
if (to > sz) to = sz;
|
|
hexdump(buf,from,to,0);
|
|
cofs = to;
|
|
break;
|
|
case 'q':
|
|
return(0);
|
|
break;
|
|
case 's':
|
|
if (!dirty) printf("Buffer has not changed, no need to write..\n");
|
|
return(dirty);
|
|
break;
|
|
case 'h':
|
|
bp++;
|
|
if (*bp == 'a') {
|
|
from = 0;
|
|
to = sz;
|
|
bp++;
|
|
} else {
|
|
from = gethex(&bp);
|
|
to = gethex(&bp);
|
|
}
|
|
wlen = gethexorstr(&bp,whatbuf);
|
|
if (to > sz) to = sz;
|
|
printf("from: %x, to: %x, wlen: %d\n",from,to,wlen);
|
|
for (i = from; i < to; i++) {
|
|
for (j = 0; j < wlen; j++) {
|
|
if ( *(buf+i+j) != *(whatbuf+j)) break;
|
|
}
|
|
if (j == wlen) printf("%06x ",i);
|
|
}
|
|
printf("\n");
|
|
break;
|
|
case ':':
|
|
bp++;
|
|
if (!*bp) break;
|
|
from = gethex(&bp);
|
|
wlen = gethexorstr(&bp,whatbuf);
|
|
|
|
printf("from: %x, wlen: %d\n",from,wlen);
|
|
|
|
memcpy(buf+from,whatbuf,wlen);
|
|
dirty = 1;
|
|
break;
|
|
|
|
case '?':
|
|
printf("d [<from>] [<to>] - dump buffer within range\n");
|
|
printf("a [<from>] [<to>] - same as d, but without ascii-part (for cut'n'paste)\n");
|
|
printf(": <offset> <hexbyte> [<hexbyte> ...] - change bytes\n");
|
|
printf("h <from> <to> <hexbyte> [<hexbyte> ...] - hunt (search) for bytes\n");
|
|
printf("ha <hexbyte> [<hexbyte] - Hunt all (whole buffer)\n");
|
|
printf("s - save & quit\n");
|
|
printf("q - quit (no save)\n");
|
|
printf(" instead of <hexbyte> etc. you may give 'string to enter/search a string\n");
|
|
break;
|
|
default:
|
|
printf("?\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Utility function to copy and append two values into a new one
|
|
* Will allocate new buffer, but not touch the input ones
|
|
*/
|
|
|
|
struct keyval *reg_valcat(struct keyval *a, struct keyval *b)
|
|
{
|
|
int newsize = 0;
|
|
int asize = 0;
|
|
int bsize = 0;
|
|
struct keyval *result = NULL;
|
|
|
|
if (!a && !b) return(NULL);
|
|
|
|
if (a) asize = a->len;
|
|
if (b) bsize = b->len;
|
|
|
|
newsize = asize + bsize;
|
|
|
|
// printf("asize = %d, bsize = %d, newsize = %d\n",asize,bsize,newsize);
|
|
|
|
ALLOC(result, sizeof(struct keyval) + newsize, 1);
|
|
|
|
if (asize) memcpy(&result->data, &a->data, asize);
|
|
if (bsize) memcpy(&result->data + asize / sizeof(int), &b->data, bsize);
|
|
result->len = newsize;
|
|
|
|
//printf("reg_valcat done\n");
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ========================================================================= */
|
|
|
|
/* The following routines are mostly for debugging, I used it
|
|
* much during discovery. the -t command line option uses it,
|
|
* also the 'st' and 's' from the editor & hexdebugger.
|
|
* All offsets shown in these are unadjusted (ie you must add
|
|
* headerpage (most often 0x1000) to get file offset)
|
|
*/
|
|
|
|
/* Parse the nk datablock
|
|
* vofs = offset into struct (after size linkage)
|
|
*/
|
|
void parse_nk(struct hive *hdesc, int vofs, int blen)
|
|
{
|
|
|
|
struct nk_key *key;
|
|
int i;
|
|
|
|
printf("== nk at offset %0x\n",vofs);
|
|
|
|
/* #define D_OFFS2(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */
|
|
#define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs )
|
|
|
|
key = (struct nk_key *)(hdesc->buffer + vofs);
|
|
printf("%04x type = 0x%02x %s\n", D_OFFS(type) ,key->type,
|
|
(key->type == KEY_ROOT ? "ROOT_KEY" : "") );
|
|
printf("%04x timestamp skipped\n", D_OFFS(timestamp) );
|
|
printf("%04x parent key offset = 0x%0x\n", D_OFFS(ofs_parent) ,key->ofs_parent + 0x1000);
|
|
printf("%04x number of subkeys = %d\n", D_OFFS(no_subkeys),key->no_subkeys);
|
|
printf("%04x lf-record offset = 0x%0x\n",D_OFFS(ofs_lf),key->ofs_lf + 0x1000);
|
|
printf("%04x number of values = %d\n", D_OFFS(no_values),key->no_values);
|
|
printf("%04x val-list offset = 0x%0x\n",D_OFFS(ofs_vallist),key->ofs_vallist + 0x1000);
|
|
printf("%04x sk-record offset = 0x%0x\n",D_OFFS(ofs_sk),key->ofs_sk + 0x1000);
|
|
printf("%04x classname offset = 0x%0x\n",D_OFFS(ofs_classnam),key->ofs_classnam + 0x1000);
|
|
|
|
printf("%04x dummy3 = 0x%0x (%d)\n",D_OFFS(dummy3),key->dummy3,key->dummy3);
|
|
printf("%04x dummy4 = 0x%0x (%d)\n",D_OFFS(dummy4),key->dummy4,key->dummy4);
|
|
printf("%04x dummy5 = 0x%0x (%d)\n",D_OFFS(dummy5),key->dummy5,key->dummy5);
|
|
printf("%04x dummy6 = 0x%0x (%d)\n",D_OFFS(dummy6),key->dummy6,key->dummy6);
|
|
printf("%04x dummy7 = 0x%0x (%d)\n",D_OFFS(dummy7),key->dummy7,key->dummy7);
|
|
|
|
printf("%04x name length = %d\n", D_OFFS(len_name),key->len_name);
|
|
printf("%04x classname length = %d\n", D_OFFS(len_classnam),key->len_classnam);
|
|
|
|
printf("%04x Key name: <",D_OFFS(keyname) );
|
|
for(i = 0; i < key->len_name; i++) putchar(key->keyname[i]);
|
|
printf(">\n== End of key info.\n");
|
|
|
|
}
|
|
|
|
/* Parse the vk datablock
|
|
* vofs = offset into struct (after size linkage)
|
|
*/
|
|
void parse_vk(struct hive *hdesc, int vofs, int blen)
|
|
{
|
|
struct vk_key *key;
|
|
int i;
|
|
|
|
printf("== vk at offset %0x\n",vofs);
|
|
|
|
|
|
key = (struct vk_key *)(hdesc->buffer + vofs);
|
|
printf("%04x name length = %d (0x%0x)\n", D_OFFS(len_name),
|
|
key->len_name, key->len_name );
|
|
printf("%04x length of data = %d (0x%0x)\n", D_OFFS(len_data),
|
|
key->len_data, key->len_data );
|
|
printf("%04x data offset = 0x%0x\n",D_OFFS(ofs_data),key->ofs_data + 0x1000);
|
|
printf("%04x value type = 0x%0x %s\n", D_OFFS(val_type), key->val_type,
|
|
(key->val_type <= REG_MAX ? val_types[key->val_type] : "(unknown)") ) ;
|
|
|
|
printf("%04x flag = 0x%0x\n",D_OFFS(flag),key->flag);
|
|
printf("%04x *unused?* = 0x%0x\n",D_OFFS(dummy1),key->dummy1);
|
|
|
|
printf("%04x Key name: <",D_OFFS(keyname) );
|
|
for(i = 0; i < key->len_name; i++) putchar(key->keyname[i]);
|
|
printf(">\n== End of key info.\n");
|
|
|
|
}
|
|
|
|
/* Parse the sk datablock
|
|
* Gee, this is the security info. Who cares? *evil grin*
|
|
* vofs = offset into struct (after size linkage)
|
|
*/
|
|
void parse_sk(struct hive *hdesc, int vofs, int blen)
|
|
{
|
|
struct sk_key *key;
|
|
/* int i; */
|
|
|
|
printf("== sk at offset %0x\n",vofs);
|
|
|
|
key = (struct sk_key *)(hdesc->buffer + vofs);
|
|
printf("%04x *unused?* = %d\n" , D_OFFS(dummy1), key->dummy1 );
|
|
printf("%04x Offset to prev sk = 0x%0x\n", D_OFFS(ofs_prevsk), key->ofs_prevsk + 0x1000);
|
|
printf("%04x Offset to next sk = 0x%0x\n", D_OFFS(ofs_nextsk), key->ofs_nextsk + 0x1000);
|
|
printf("%04x Usage counter = %d (0x%0x)\n", D_OFFS(no_usage),
|
|
key->no_usage,key->no_usage);
|
|
printf("%04x Security data len = %d (0x%0x)\n", D_OFFS(len_sk),
|
|
key->len_sk,key->len_sk);
|
|
|
|
printf("== End of key info.\n");
|
|
|
|
}
|
|
|
|
|
|
/* Parse the lf datablock (>4.0 'nk' offsets lookuptable)
|
|
* vofs = offset into struct (after size linkage)
|
|
*/
|
|
void parse_lf(struct hive *hdesc, int vofs, int blen)
|
|
{
|
|
struct lf_key *key;
|
|
int i;
|
|
|
|
printf("== lf at offset %0x\n",vofs);
|
|
|
|
key = (struct lf_key *)(hdesc->buffer + vofs);
|
|
printf("%04x number of keys = %d\n", D_OFFS(no_keys), key->no_keys );
|
|
|
|
for(i = 0; i < key->no_keys; i++) {
|
|
printf("%04x %3d Offset: 0x%0x - <%c%c%c%c>\n",
|
|
D_OFFS(hash[i].ofs_nk), i,
|
|
key->hash[i].ofs_nk + 0x1000,
|
|
key->hash[i].name[0],
|
|
key->hash[i].name[1],
|
|
key->hash[i].name[2],
|
|
key->hash[i].name[3] );
|
|
}
|
|
|
|
printf("== End of key info.\n");
|
|
|
|
}
|
|
|
|
/* Parse the lh datablock (WinXP offsets lookuptable)
|
|
* vofs = offset into struct (after size linkage)
|
|
* The hash is most likely a base 37 conversion of the name string
|
|
*/
|
|
void parse_lh(struct hive *hdesc, int vofs, int blen)
|
|
{
|
|
struct lf_key *key;
|
|
int i;
|
|
|
|
printf("== lh at offset %0x\n",vofs);
|
|
|
|
key = (struct lf_key *)(hdesc->buffer + vofs);
|
|
printf("%04x number of keys = %d\n", D_OFFS(no_keys), key->no_keys );
|
|
|
|
for(i = 0; i < key->no_keys; i++) {
|
|
printf("%04x %3d Offset: 0x%0x - <hash: %08x>\n",
|
|
D_OFFS(lh_hash[i].ofs_nk), i,
|
|
key->lh_hash[i].ofs_nk + 0x1000,
|
|
key->lh_hash[i].hash );
|
|
}
|
|
|
|
printf("== End of key info.\n");
|
|
|
|
}
|
|
|
|
|
|
/* Parse the li datablock (3.x 'nk' offsets list)
|
|
* vofs = offset into struct (after size linkage)
|
|
*/
|
|
void parse_li(struct hive *hdesc, int vofs, int blen)
|
|
{
|
|
struct li_key *key;
|
|
int i;
|
|
|
|
printf("== li at offset %0x\n",vofs);
|
|
|
|
/* #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */
|
|
|
|
key = (struct li_key *)(hdesc->buffer + vofs);
|
|
printf("%04x number of keys = %d\n", D_OFFS(no_keys), key->no_keys );
|
|
|
|
for(i = 0; i < key->no_keys; i++) {
|
|
printf("%04x %3d Offset: 0x%0x\n",
|
|
D_OFFS(hash[i].ofs_nk), i,
|
|
key->hash[i].ofs_nk + 0x1000);
|
|
}
|
|
printf("== End of key info.\n");
|
|
|
|
}
|
|
|
|
/* Parse the ri subindex-datablock
|
|
* (Used to list li/lf/lh's when ~>500keys)
|
|
* vofs = offset into struct (after size linkage)
|
|
*/
|
|
void parse_ri(struct hive *hdesc, int vofs, int blen)
|
|
{
|
|
struct ri_key *key;
|
|
int i;
|
|
|
|
printf("== ri at offset %0x\n",vofs);
|
|
|
|
/* #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */
|
|
|
|
key = (struct ri_key *)(hdesc->buffer + vofs);
|
|
printf("%04x number of subindices = %d\n", D_OFFS(no_lis), key->no_lis );
|
|
|
|
for(i = 0; i < key->no_lis; i++) {
|
|
printf("%04x %3d Offset: 0x%0x\n",
|
|
D_OFFS(hash[i].ofs_li), i,
|
|
key->hash[i].ofs_li + 0x1000);
|
|
}
|
|
printf("== End of key info.\n");
|
|
|
|
}
|
|
|
|
|
|
/* Parse the db block (used when value data >4k or something)
|
|
* vofs = offset into struct (after size linkage)
|
|
*/
|
|
void parse_db(struct hive *hdesc, int vofs, int blen)
|
|
{
|
|
struct db_key *key;
|
|
|
|
printf("== db at offset %0x\n",vofs);
|
|
|
|
key = (struct db_key *)(hdesc->buffer + vofs);
|
|
printf("%04x number of parts = %d\n", D_OFFS(no_part), key->no_part );
|
|
|
|
printf("%04x Data list at offset: 0x%0x\n",
|
|
D_OFFS(ofs_data),
|
|
key->ofs_data + 0x1000);
|
|
|
|
printf("== End of key info.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse the datablock
|
|
* vofs = offset into struct (after size linkage)
|
|
*/
|
|
|
|
int parse_block(struct hive *hdesc, int vofs,int verbose)
|
|
{
|
|
unsigned short id;
|
|
int seglen;
|
|
|
|
seglen = get_int(hdesc->buffer+vofs);
|
|
|
|
// if (vofs > 0xaef000) verbose = 1;
|
|
|
|
#if 0
|
|
if (verbose || seglen == 0) {
|
|
printf("** Block at offset %0x\n",vofs);
|
|
printf("seglen: %d, %u, 0x%0x\n",seglen,seglen,seglen);
|
|
}
|
|
#endif
|
|
if (seglen == 0) {
|
|
printf("parse_block: FATAL! Zero data block size! (not registry or corrupt file?)\n");
|
|
if (verbose) debugit(hdesc->buffer,hdesc->size);
|
|
return(0);
|
|
}
|
|
|
|
if (seglen < 0) {
|
|
seglen = -seglen;
|
|
hdesc->usetot += seglen;
|
|
hdesc->useblk++;
|
|
if (verbose) {
|
|
printf("USED BLOCK @ %06x to %06x : %d, 0x%0x\n",vofs,vofs+seglen,seglen,seglen);
|
|
/* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */
|
|
}
|
|
} else {
|
|
hdesc->unusetot += seglen;
|
|
hdesc->unuseblk++;
|
|
/* Useful to zero blocks we think are empty when debugging.. */
|
|
#if ZEROFILLONLOAD
|
|
bzero(hdesc->buffer+vofs+4,seglen-4);
|
|
#endif
|
|
|
|
if (verbose) {
|
|
printf("FREE BLOCK @ %06x to %06x : %d, 0x%0x\n",vofs,vofs+seglen,seglen,seglen);
|
|
/* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */
|
|
}
|
|
}
|
|
|
|
|
|
vofs += 4;
|
|
id = (*(hdesc->buffer + vofs)<<8) + *(hdesc->buffer+vofs+1);
|
|
|
|
if (verbose > 1) {
|
|
switch (id) {
|
|
case 0x6e6b: /* nk */
|
|
parse_nk(hdesc, vofs, seglen);
|
|
break;
|
|
case 0x766b: /* vk */
|
|
parse_vk(hdesc, vofs, seglen);
|
|
break;
|
|
case 0x6c66: /* lf */
|
|
parse_lf(hdesc, vofs, seglen);
|
|
break;
|
|
case 0x6c68: /* lh */
|
|
parse_lh(hdesc, vofs, seglen);
|
|
break;
|
|
case 0x6c69: /* li */
|
|
parse_li(hdesc, vofs, seglen);
|
|
break;
|
|
case 0x736b: /* sk */
|
|
parse_sk(hdesc, vofs, seglen);
|
|
break;
|
|
case 0x7269: /* ri */
|
|
parse_ri(hdesc, vofs, seglen);
|
|
break;
|
|
case 0x6462: /* db */
|
|
parse_db(hdesc, vofs, seglen);
|
|
break;
|
|
default:
|
|
printf("value data, or not handeled yet!\n");
|
|
break;
|
|
}
|
|
}
|
|
return(seglen);
|
|
}
|
|
|
|
/* ================================================================ */
|
|
/* Scan and allocation routines */
|
|
|
|
/* Find start of page given a current pointer into the buffer
|
|
* hdesc = hive
|
|
* vofs = offset pointer into buffer
|
|
* returns: offset to start of page (and page header)
|
|
*/
|
|
|
|
int find_page_start(struct hive *hdesc, int vofs)
|
|
{
|
|
int r,prev;
|
|
struct hbin_page *h;
|
|
|
|
/* Again, assume start at 0x1000 */
|
|
|
|
r = 0x1000;
|
|
while (r < hdesc->size) {
|
|
prev = r;
|
|
h = (struct hbin_page *)(hdesc->buffer + r);
|
|
if (h->id != 0x6E696268) return(0);
|
|
if (h->ofs_next == 0) {
|
|
printf("find_page_start: zero len or ofs_next found in page at 0x%x\n",r);
|
|
return(0);
|
|
}
|
|
r += h->ofs_next;
|
|
if (r > vofs) return (prev);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* Find free space in page
|
|
* size = requested size in bytes
|
|
* pofs = offset to start of actual page header
|
|
* returns: offset to free block, or 0 for error
|
|
*/
|
|
|
|
#define FB_DEBUG 0
|
|
|
|
int find_free_blk(struct hive *hdesc, int pofs, int size)
|
|
{
|
|
int vofs = pofs + 0x20;
|
|
int seglen;
|
|
struct hbin_page *p;
|
|
|
|
p = (struct hbin_page *)(hdesc->buffer + pofs);
|
|
|
|
while (vofs-pofs < (p->ofs_next - HBIN_ENDFILL)) {
|
|
|
|
seglen = get_int(hdesc->buffer+vofs);
|
|
|
|
#if FB_DEBUG
|
|
if (vofs > 0x400000) {
|
|
printf("** Block at offset %0x\n",vofs);
|
|
printf("seglen: %d, %u, 0x%0x\n",seglen,seglen,seglen);
|
|
}
|
|
#endif
|
|
|
|
if (seglen == 0) {
|
|
printf("find_free_blk: FATAL! Zero data block size! (not registry or corrupt file?)\n");
|
|
printf(" : Block at offset %0x\n",vofs);
|
|
if ( (vofs - pofs) == (p->ofs_next - 4) ) {
|
|
printf("find_free_blk: at exact end of hbin, do not care..\n");
|
|
return(0);
|
|
}
|
|
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
|
|
else abort();
|
|
return(0);
|
|
}
|
|
|
|
if (seglen < 0) {
|
|
seglen = -seglen;
|
|
#if FB_DEBUG
|
|
if (vofs >0x400000) printf("USED BLOCK: %d, 0x%0x\n",seglen,seglen);
|
|
#endif
|
|
/* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */
|
|
} else {
|
|
#if FB_DEBUG
|
|
if (vofs >0x400000) printf("FREE BLOCK!\n");
|
|
#endif
|
|
/* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */
|
|
if (seglen >= size) {
|
|
#if FB_DEBUG
|
|
if (vofs >0x400000) printf("find_free_blk: found size %d block at 0x%x\n",seglen,vofs);
|
|
#endif
|
|
#if 0
|
|
if (vofs == 0x19fb8) {
|
|
printf("find_free_blk: vofs = %x, seglen = %x\n",vofs,seglen);
|
|
debugit(hdesc->buffer,hdesc->size);
|
|
abort();
|
|
}
|
|
#endif
|
|
return(vofs);
|
|
}
|
|
}
|
|
vofs += seglen;
|
|
}
|
|
return(0);
|
|
|
|
}
|
|
|
|
#undef FB_DEBUG
|
|
|
|
/* Search pages from start to find free block
|
|
* hdesc - hive
|
|
* size - space requested, in bytes
|
|
* returns: offset to free block, 0 if not found or error
|
|
*/
|
|
|
|
int find_free(struct hive *hdesc, int size)
|
|
{
|
|
int r,blk;
|
|
struct hbin_page *h;
|
|
|
|
/* Align to 8 byte boundary */
|
|
if (size & 7) size += (8 - (size & 7));
|
|
|
|
/* Again, assume start at 0x1000 */
|
|
|
|
r = 0x1000;
|
|
while (r < hdesc->endofs) {
|
|
h = (struct hbin_page *)(hdesc->buffer + r);
|
|
if (h->id != 0x6E696268) return(0);
|
|
if (h->ofs_next == 0) {
|
|
printf("find_free: zero len or ofs_next found in page at 0x%x\n",r);
|
|
return(0);
|
|
}
|
|
blk = find_free_blk(hdesc,r,size);
|
|
if (blk) return (blk);
|
|
r += h->ofs_next;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* Add new hbin to end of file. If file contains data at end
|
|
* that is not in a hbin, include that too
|
|
* hdesc - hive as usual
|
|
* size - minimum size (will be rounded up to next 0x1000 alignment)
|
|
* returns offset to first block in new hbin
|
|
*/
|
|
|
|
#define ADDBIN_DEBUG
|
|
int add_bin(struct hive *hdesc, int size)
|
|
{
|
|
int r,newsize,newbinofs;
|
|
struct hbin_page *newbin;
|
|
struct regf_header *hdr;
|
|
|
|
if (hdesc->state & HMODE_NOEXPAND) {
|
|
fprintf(stderr,"ERROR: Registry hive <%s> need to be expanded,\n"
|
|
"but that is not allowed according to selected options. Operations will fail.\n", hdesc->filename);
|
|
return(0);
|
|
}
|
|
|
|
r = ((size + 0x20 + 4) & ~0xfff) + HBIN_PAGESIZE; /* Add header and link, round up to page boundary, usually 0x1000 */
|
|
|
|
newbinofs = hdesc->endofs;
|
|
|
|
|
|
#ifdef ADDBIN_DEBUG
|
|
printf("add_bin: request size = %d [%x], rounded to %d [%x]\n",size,size,r,r);
|
|
printf("add_bin: old buffer size = %d [%x]\n",hdesc->size,hdesc->size);
|
|
printf("add_bin: firs nonbin off = %d [%x]\n",newbinofs,newbinofs);
|
|
printf("add_bin: free at end = %d [%x]\n",hdesc->size-newbinofs,hdesc->size-newbinofs);
|
|
#endif
|
|
|
|
if ( (newbinofs + r) >= hdesc->size) { /* We must allocate more buffer */
|
|
newsize = ( (newbinofs + r) & ~(REGF_FILEDIVISOR-1) ) + REGF_FILEDIVISOR; /* File normally multiple of 0x40000 bytes */
|
|
|
|
#ifdef ADDBIN_DEBUG
|
|
printf("add_bin: new buffer size = %d [%x]\n",newsize,newsize);
|
|
#endif
|
|
|
|
hdesc->buffer = realloc(hdesc->buffer, newsize);
|
|
if (!hdesc->buffer) {
|
|
perror("add_bin : realloc() ");
|
|
abort();
|
|
}
|
|
hdesc->size = newsize;
|
|
|
|
}
|
|
|
|
/* At this point, we have large enough space at end of file */
|
|
|
|
newbin = (struct hbin_page *)(hdesc->buffer + newbinofs);
|
|
|
|
bzero((void *)newbin, r); /* zero out new hbin, easier to debug too */
|
|
|
|
newbin->id = 0x6E696268; /* 'hbin' */
|
|
newbin->ofs_self = newbinofs - 0x1000; /* Point to ourselves minus regf. Seem to be that.. */
|
|
newbin->ofs_next = r; /* size of this new bin */
|
|
|
|
/* Wonder if anything else in the hbin header matters? */
|
|
|
|
/* Set whole hbin to be one contious unused block */
|
|
newbin->firstlink = (r - 0x20 - 0); /* Positive linkage = unused */
|
|
|
|
/* Update REGF header */
|
|
hdr = (struct regf_header *) hdesc->buffer;
|
|
hdr->filesize = newbinofs + r - 0x1000; /* Point header to new end of data */
|
|
|
|
#ifdef ADDBIN_DEBUG
|
|
printf("add_bin: adjusting size field in REGF: %d [%x]\n",hdr->filesize,hdr->filesize);
|
|
#endif
|
|
|
|
/* Update state */
|
|
|
|
hdesc->state |= HMODE_DIDEXPAND | HMODE_DIRTY;
|
|
hdesc->lastbin = newbinofs; /* Last bin */
|
|
hdesc->endofs = newbinofs + r; /* New data end */
|
|
|
|
return(newbinofs + 0x20);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate a block of requested size if possible
|
|
* hdesc - hive
|
|
* pofs - If >0 will try this page first (ptr may be inside page)
|
|
* size - number of bytes to allocate
|
|
* returns: 0 - failed, else pointer to allocated block.
|
|
* WARNING: Will realloc() buffer if it has to be expanded!
|
|
* ALL POINTERS TO BUFFER IS INVALID AFTER THAT. (offsets are still correct)
|
|
* Guess I'd better switch to mmap() one day..
|
|
* This function WILL CHANGE THE HIVE (change block linkage) if it
|
|
* succeeds.
|
|
*/
|
|
|
|
int alloc_block(struct hive *hdesc, int ofs, int size)
|
|
{
|
|
int pofs = 0;
|
|
int blk = 0;
|
|
int newbin;
|
|
int trail, trailsize, oldsz;
|
|
|
|
if (hdesc->state & HMODE_NOALLOC) {
|
|
printf("\nERROR: alloc_block: Hive <%s> is in no allocation safe mode,"
|
|
"new space not allocated. Operation will fail!\n", hdesc->filename);
|
|
return(0);
|
|
}
|
|
|
|
size += 4; /* Add linkage */
|
|
if (size & 7) size += (8 - (size & 7));
|
|
|
|
/* Check current page first */
|
|
if (ofs) {
|
|
pofs = find_page_start(hdesc,ofs);
|
|
blk = find_free_blk(hdesc,pofs,size);
|
|
}
|
|
|
|
/* Then check whole hive */
|
|
if (!blk) {
|
|
blk = find_free(hdesc,size);
|
|
}
|
|
|
|
if (blk) { /* Got the space */
|
|
oldsz = get_int(hdesc->buffer+blk);
|
|
#if 0
|
|
printf("Block at : %x\n",blk);
|
|
printf("Old block size is: %x\n",oldsz);
|
|
printf("New block size is: %x\n",size);
|
|
#endif
|
|
trailsize = oldsz - size;
|
|
|
|
if (trailsize == 4) {
|
|
trailsize = 0;
|
|
size += 4;
|
|
}
|
|
|
|
#if 1
|
|
if (trailsize & 7) { /* Trail must be 8 aligned */
|
|
trailsize -= (8 - (trailsize & 7));
|
|
size += (8 - (trailsize & 7));
|
|
}
|
|
if (trailsize == 4) {
|
|
trailsize = 0;
|
|
size += 4;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
printf("trail after comp: %x\n",trailsize);
|
|
printf("size after comp: %x\n",size);
|
|
#endif
|
|
|
|
/* Now change pointers on this to reflect new size */
|
|
*(int *)((hdesc->buffer)+blk) = -(size);
|
|
/* If the fit was exact (unused block was same size as wee need)
|
|
* there is no need for more, else make free block after end
|
|
* of newly allocated one */
|
|
|
|
hdesc->useblk++;
|
|
hdesc->unuseblk--;
|
|
hdesc->usetot += size;
|
|
hdesc->unusetot -= size;
|
|
|
|
if (trailsize) {
|
|
trail = blk + size;
|
|
|
|
*(int *)((hdesc->buffer)+trail) = (int)trailsize;
|
|
|
|
hdesc->useblk++; /* This will keep blockcount */
|
|
hdesc->unuseblk--;
|
|
hdesc->usetot += 4; /* But account for more linkage bytes */
|
|
hdesc->unusetot -= 4;
|
|
|
|
}
|
|
/* Clear the block data, makes it easier to debug */
|
|
#if ZEROFILL
|
|
bzero( (void *)(hdesc->buffer+blk+4), size-4);
|
|
#endif
|
|
|
|
hdesc->state |= HMODE_DIRTY;
|
|
|
|
#if 0
|
|
printf("alloc_block: returning %x\n",blk);
|
|
#endif
|
|
return(blk);
|
|
} else {
|
|
printf("alloc_block: failed to alloc %d bytes, trying to expand hive..\n",size);
|
|
|
|
newbin = add_bin(hdesc,size);
|
|
if (newbin) return(alloc_block(hdesc,newbin,size)); /* Nasty... recall ourselves. */
|
|
/* Fallthrough to fail if add_bin fails */
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/* Free a block in registry
|
|
* hdesc - hive
|
|
* blk - offset of block, MUST POINT TO THE LINKAGE!
|
|
* returns bytes freed (incl linkage bytes) or 0 if fail
|
|
* Will CHANGE HIVE IF SUCCESSFUL (changes linkage)
|
|
*/
|
|
|
|
#define FB_DEBUG 0
|
|
|
|
int free_block(struct hive *hdesc, int blk)
|
|
{
|
|
int pofs,vofs,seglen,prev,next,nextsz,prevsz,size;
|
|
struct hbin_page *p;
|
|
|
|
if (hdesc->state & HMODE_NOALLOC) {
|
|
printf("free_block: ERROR: Hive %s is in no allocation safe mode,"
|
|
"space not freed. Operation will fail!\n", hdesc->filename);
|
|
return(0);
|
|
}
|
|
|
|
size = get_int(hdesc->buffer+blk);
|
|
if (size >= 0) {
|
|
printf("free_block: trying to free already free block!\n");
|
|
#ifdef DOCORE
|
|
printf("blk = %x\n",blk);
|
|
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
|
|
abort();
|
|
#endif
|
|
return(0);
|
|
}
|
|
size = -size;
|
|
|
|
/* So, we must find start of the block BEFORE us */
|
|
pofs = find_page_start(hdesc,blk);
|
|
if (!pofs) return(0);
|
|
|
|
p = (struct hbin_page *)(hdesc->buffer + pofs);
|
|
vofs = pofs + 0x20;
|
|
|
|
prevsz = -32;
|
|
|
|
if (vofs != blk) { /* Block is not at start of page? */
|
|
while (vofs-pofs < (p->ofs_next - HBIN_ENDFILL) ) {
|
|
|
|
seglen = get_int(hdesc->buffer+vofs);
|
|
|
|
if (seglen == 0) {
|
|
printf("free_block: EEEK! Zero data block size! (not registry or corrupt file?)\n");
|
|
debugit(hdesc->buffer,hdesc->size);
|
|
return(0);
|
|
}
|
|
|
|
if (seglen < 0) {
|
|
seglen = -seglen;
|
|
/* hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */
|
|
}
|
|
prev = vofs;
|
|
vofs += seglen;
|
|
if (vofs == blk) break;
|
|
}
|
|
|
|
if (vofs != blk) {
|
|
printf("free_block: ran off end of page!?!? Error in chains?\n");
|
|
#ifdef DOCORE
|
|
printf("vofs = %x, pofs = %x, blk = %x\n",vofs,pofs,blk);
|
|
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
|
|
abort();
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
prevsz = get_int(hdesc->buffer+prev);
|
|
|
|
}
|
|
|
|
/* We also need details on next block (unless at end of page) */
|
|
next = blk + size;
|
|
|
|
nextsz = 0;
|
|
if (next-pofs < (p->ofs_next - HBIN_ENDFILL) ) nextsz = get_int(hdesc->buffer+next);
|
|
|
|
#if 0
|
|
printf("offset prev : %x , blk: %x , next: %x\n",prev,blk,next);
|
|
printf("size prev : %x , blk: %x , next: %x\n",prevsz,size,nextsz);
|
|
#endif
|
|
|
|
/* Now check if next block is free, if so merge it with the one to be freed */
|
|
if ( nextsz > 0) {
|
|
#if 0
|
|
printf("Swallow next\n");
|
|
#endif
|
|
size += nextsz; /* Swallow it in current block */
|
|
hdesc->useblk--;
|
|
hdesc->usetot -= 4;
|
|
hdesc->unusetot -= 4; /* FIXME !??!?? */
|
|
}
|
|
|
|
/* Now free the block (possibly with ajusted size as above) */
|
|
#if ZEROFILL
|
|
bzero( (void *)(hdesc->buffer+blk), size);
|
|
#endif
|
|
|
|
*(int *)((hdesc->buffer)+blk) = (int)size;
|
|
hdesc->usetot -= size;
|
|
hdesc->unusetot -= size; /* FIXME !?!? */
|
|
hdesc->unuseblk--;
|
|
|
|
hdesc->state |= HMODE_DIRTY;
|
|
|
|
/* Check if previous block is also free, if so, merge.. */
|
|
if (prevsz > 0) {
|
|
#if 0
|
|
printf("Swallow prev\n");
|
|
#endif
|
|
hdesc->usetot -= prevsz;
|
|
hdesc->unusetot += prevsz;
|
|
prevsz += size;
|
|
/* And swallow current.. */
|
|
#if ZEROFILL
|
|
bzero( (void *)(hdesc->buffer+prev), prevsz);
|
|
#endif
|
|
*(int *)((hdesc->buffer)+prev) = (int)prevsz;
|
|
hdesc->useblk--;
|
|
return(prevsz);
|
|
}
|
|
return(size);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ================================================================ */
|
|
|
|
/* ** Registry manipulation routines ** */
|
|
|
|
|
|
|
|
/* "directory scan", return next name/pointer of a subkey on each call
|
|
* nkofs = offset to directory to scan
|
|
* lfofs = pointer to int to hold the current scan position,
|
|
* set position to 0 to start.
|
|
* sptr = pointer to struct to hold a single result
|
|
* returns: -1 = error. 0 = end of key. 1 = more subkeys to scan
|
|
* NOTE: caller must free the name-buffer (struct ex_data *name)
|
|
*/
|
|
int ex_next_n(struct hive *hdesc, int nkofs, int *count, int *countri, struct ex_data *sptr)
|
|
{
|
|
struct nk_key *key, *newnkkey;
|
|
int newnkofs;
|
|
struct lf_key *lfkey;
|
|
struct li_key *likey;
|
|
struct ri_key *rikey;
|
|
|
|
|
|
if (!nkofs) return(-1);
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
if (key->id != 0x6b6e) {
|
|
printf("ex_next error: Not a 'nk' node at 0x%0x\n",nkofs);
|
|
return(-1);
|
|
}
|
|
|
|
#define EXNDEBUG 0
|
|
|
|
lfkey = (struct lf_key *)(hdesc->buffer + key->ofs_lf + 0x1004);
|
|
rikey = (struct ri_key *)(hdesc->buffer + key->ofs_lf + 0x1004);
|
|
|
|
if (rikey->id == 0x6972) { /* Is it extended 'ri'-block? */
|
|
#if EXNDEBUG
|
|
printf("%d , %d\n",*countri,*count);
|
|
#endif
|
|
if (*countri < 0 || *countri >= rikey->no_lis) { /* End of ri's? */
|
|
return(0);
|
|
}
|
|
/* Get the li of lf-struct that's current based on countri */
|
|
likey = (struct li_key *)( hdesc->buffer + rikey->hash[*countri].ofs_li + 0x1004 ) ;
|
|
if (likey->id == 0x696c) {
|
|
newnkofs = likey->hash[*count].ofs_nk + 0x1000;
|
|
} else {
|
|
lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[*countri].ofs_li + 0x1004 ) ;
|
|
newnkofs = lfkey->hash[*count].ofs_nk + 0x1000;
|
|
}
|
|
|
|
/* Check if current li/lf is exhausted */
|
|
#if EXNDEBUG
|
|
printf("likey->no_keys = %d\n",likey->no_keys);
|
|
#endif
|
|
if (*count >= likey->no_keys-1) { /* Last legal entry in li list? */
|
|
(*countri)++; /* Bump up ri count so we take next ri entry next time */
|
|
(*count) = -1; /* Reset li traverse counter for next round, not used later here */
|
|
}
|
|
} else { /* Plain handler */
|
|
if (key->no_subkeys <= 0 || *count >= key->no_subkeys) {
|
|
return(0);
|
|
}
|
|
if (lfkey->id == 0x696c) { /* Is it 3.x 'li' instead? */
|
|
likey = (struct li_key *)(hdesc->buffer + key->ofs_lf + 0x1004);
|
|
newnkofs = likey->hash[*count].ofs_nk + 0x1000;
|
|
} else {
|
|
newnkofs = lfkey->hash[*count].ofs_nk + 0x1000;
|
|
}
|
|
}
|
|
|
|
sptr->nkoffs = newnkofs;
|
|
newnkkey = (struct nk_key *)(hdesc->buffer + newnkofs + 4);
|
|
sptr->nk = newnkkey;
|
|
|
|
if (newnkkey->id != 0x6b6e) {
|
|
printf("ex_next: ERROR: not 'nk' node at 0x%0x\n",newnkofs);
|
|
|
|
return(-1);
|
|
} else {
|
|
if (newnkkey->len_name <= 0) {
|
|
printf("ex_next: nk at 0x%0x has no name!\n",newnkofs);
|
|
} else if (newnkkey->type & 0x20) {
|
|
#if 0
|
|
printf("dummy1 %x\n", *((int*)newnkkey->dummy1));
|
|
printf("dummy2 %x\n", *((int*)newnkkey->dummy2));
|
|
printf("type %x\n", newnkkey->type);
|
|
printf("timestamp+8 %x\n", *((int*)(newnkkey->timestamp+8)));
|
|
printf("dummy3+0 %x\n", *((int*)(newnkkey->dummy3+0)));
|
|
printf("dummy3+4 %x\n", *((int*)(newnkkey->dummy3+4)));
|
|
printf("dummy3+8 %x\n", *((int*)(newnkkey->dummy3+8)));
|
|
printf("dummy3+12 %x\n", *((int*)(newnkkey->dummy3+12)));
|
|
printf("dummy4 %x\n", *((int*)&newnkkey->dummy4));
|
|
printf("len %d\n", newnkkey->len_name);
|
|
printf("len class %d\n", newnkkey->len_classnam);
|
|
fflush(stdout);
|
|
#endif
|
|
|
|
sptr->name = mem_str(newnkkey->keyname,newnkkey->len_name);
|
|
// sptr->name = string_rega2prog(newnkkey->keyname, newnkkey->len_name);
|
|
} else {
|
|
sptr->name = string_regw2prog(newnkkey->keyname, newnkkey->len_name);
|
|
}
|
|
} /* if */
|
|
(*count)++;
|
|
return(1);
|
|
/* return( *count <= key->no_subkeys); */
|
|
}
|
|
|
|
/* "directory scan" for VALUES, return next name/pointer of a value on each call
|
|
* nkofs = offset to directory to scan
|
|
* lfofs = pointer to int to hold the current scan position,
|
|
* set position to 0 to start.
|
|
* sptr = pointer to struct to hold a single result
|
|
* returns: -1 = error. 0 = end of key. 1 = more values to scan
|
|
* NOTE: caller must free the name-buffer (struct vex_data *name)
|
|
*/
|
|
int ex_next_v(struct hive *hdesc, int nkofs, int *count, struct vex_data *sptr)
|
|
{
|
|
struct nk_key *key /* , *newnkkey */ ;
|
|
int vkofs,vlistofs;
|
|
int *vlistkey;
|
|
struct vk_key *vkkey;
|
|
|
|
|
|
if (!nkofs) return(-1);
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
if (key->id != 0x6b6e) {
|
|
printf("ex_next_v error: Not a 'nk' node at 0x%0x\n",nkofs);
|
|
return(-1);
|
|
}
|
|
|
|
if (key->no_values <= 0 || *count >= key->no_values) {
|
|
return(0);
|
|
}
|
|
|
|
vlistofs = key->ofs_vallist + 0x1004;
|
|
vlistkey = (int *)(hdesc->buffer + vlistofs);
|
|
|
|
vkofs = vlistkey[*count] + 0x1004;
|
|
vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
|
|
if (vkkey->id != 0x6b76) {
|
|
printf("ex_next_v: hit non valuekey (vk) node during scan at offs 0x%0x\n",vkofs);
|
|
return(-1);
|
|
}
|
|
|
|
/* parse_vk(hdesc, vkofs, 4); */
|
|
|
|
sptr->vk = vkkey;
|
|
sptr->vkoffs = vkofs;
|
|
sptr->name = 0;
|
|
sptr->size = (vkkey->len_data & 0x7fffffff);
|
|
|
|
if (vkkey->len_name >0) {
|
|
if (vkkey->flag & 1) {
|
|
|
|
sptr->name = mem_str(vkkey->keyname, vkkey->len_name);
|
|
// sptr->name = string_rega2prog(vkkey->keyname, vkkey->len_name);
|
|
} else {
|
|
sptr->name = string_regw2prog(vkkey->keyname, vkkey->len_name);
|
|
}
|
|
} else {
|
|
sptr->name = str_dup("");
|
|
}
|
|
|
|
sptr->type = vkkey->val_type;
|
|
|
|
if (sptr->size) {
|
|
if (vkkey->val_type == REG_DWORD) {
|
|
if (vkkey->len_data & 0x80000000) {
|
|
sptr->val = (int)(vkkey->ofs_data);
|
|
}
|
|
}
|
|
}
|
|
#if 0
|
|
else if (vkkey->len_data == 0x80000000) {
|
|
/* Data SIZE is 0, high bit set: special inline case, data is DWORD and in TYPE field!! */
|
|
/* Used a lot in SAM, and maybe in SECURITY I think */
|
|
sptr->val = (int)(vkkey->val_type);
|
|
sptr->size = 4;
|
|
sptr->type = REG_DWORD;
|
|
} else {
|
|
sptr->val = 0;
|
|
sptr->size = 0;
|
|
}
|
|
#endif
|
|
|
|
(*count)++;
|
|
return( *count <= key->no_values );
|
|
}
|
|
|
|
/* traceback - trace nk's back to root,
|
|
* building path string as we go.
|
|
* nkofs = offset to nk-node
|
|
* path = pointer to pathstring-buffer
|
|
* maxlen = max length of path-buffer
|
|
* return: length of path string
|
|
*/
|
|
|
|
int get_abs_path(struct hive *hdesc, int nkofs, char *path, int maxlen)
|
|
{
|
|
/* int newnkofs; */
|
|
struct nk_key *key;
|
|
char tmp[ABSPATHLEN+1];
|
|
char *keyname;
|
|
int len_name;
|
|
|
|
maxlen = (maxlen < ABSPATHLEN ? maxlen : ABSPATHLEN);
|
|
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
|
|
if (key->id != 0x6b6e) {
|
|
printf("get_abs_path: Not a 'nk' node!\n");
|
|
return(0);
|
|
}
|
|
|
|
if (key->type == KEY_ROOT) { /* We're at the root */
|
|
return(strlen(path));
|
|
}
|
|
|
|
strncpy(tmp,path,ABSPATHLEN-1);
|
|
|
|
if (key->type & 0x20)
|
|
keyname = mem_str(key->keyname, key->len_name);
|
|
// keyname = string_rega2prog(key->keyname, key->len_name);
|
|
else
|
|
keyname = string_regw2prog(key->keyname, key->len_name);
|
|
len_name = strlen(keyname);
|
|
if ( (strlen(path) + len_name) >= maxlen-6) {
|
|
free(keyname);
|
|
snprintf(path,maxlen,"(...)%s",tmp);
|
|
return(strlen(path)); /* Stop trace when string exhausted */
|
|
}
|
|
*path = '\\';
|
|
memcpy(path+1,keyname,len_name);
|
|
free(keyname);
|
|
strncpy(path+len_name+1,tmp,maxlen-6-len_name);
|
|
return(get_abs_path(hdesc, key->ofs_parent+0x1004, path, maxlen)); /* go back one more */
|
|
}
|
|
|
|
|
|
/* Value index table lookup
|
|
* hdesc - hive as usual
|
|
* vlistofs - offset of table
|
|
* name - value name to look for
|
|
* returns index into table or -1 if err
|
|
*/
|
|
|
|
int vlist_find(struct hive *hdesc, int vlistofs, int numval, char *name, int type)
|
|
{
|
|
struct vk_key *vkkey;
|
|
int i,vkofs,len;
|
|
int32_t *vlistkey;
|
|
int approx = -1;
|
|
|
|
len = strlen(name);
|
|
vlistkey = (int32_t *)(hdesc->buffer + vlistofs);
|
|
|
|
// printf("vlist_find: <%s> len = %d\n",name,len);
|
|
|
|
for (i = 0; i < numval; i++) {
|
|
vkofs = vlistkey[i] + 0x1004;
|
|
vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
|
|
if (vkkey->len_name == 0 && *name == '@' && len == 1) { /* @ is alias for nameless value */
|
|
return(i);
|
|
}
|
|
|
|
// printf("vlist_find: matching against: <%s> len = %d\n",vkkey->keyname,vkkey->len_name);
|
|
|
|
if ( (type & TPF_EXACT) && vkkey->len_name != len ) continue; /* Skip if exact match and not exact size */
|
|
|
|
if ( vkkey->len_name >= len ) { /* Only check for names that are longer or equal than we seek */
|
|
if ( !strncmp(name, vkkey->keyname, len) ) { /* Name match */
|
|
if (vkkey->len_name == len) return(i); /* Exact match always best, returns */
|
|
if (approx == -1) approx = i; /* Else remember first partial match */
|
|
}
|
|
}
|
|
|
|
}
|
|
return(approx);
|
|
|
|
}
|
|
|
|
/* Recursevely follow 'nk'-nodes based on a path-string,
|
|
* returning offset of last 'nk' or 'vk'
|
|
* vofs - offset to start node
|
|
* path - null-terminated pathname (relative to vofs, \ is separator)
|
|
* type - type to return TPF_??, see ntreg.h
|
|
* return: offset to nk or vk (or NULL if not found)
|
|
*/
|
|
|
|
int trav_path(struct hive *hdesc, int vofs, char *path, int type)
|
|
{
|
|
struct nk_key *key, *newnkkey;
|
|
struct lf_key *lfkey;
|
|
struct li_key *likey;
|
|
struct ri_key *rikey;
|
|
|
|
int32_t *vlistkey;
|
|
int newnkofs, plen, i, lfofs, vlistofs, adjust, r, ricnt, subs;
|
|
char *buf;
|
|
char part[ABSPATHLEN+1];
|
|
char *partptr;
|
|
|
|
if (!hdesc) return(0);
|
|
buf = hdesc->buffer;
|
|
|
|
// printf("trav_path: called with vofs = %x, path = <%s>, type = %x\n",vofs, path, type);
|
|
|
|
|
|
if (!vofs) vofs = hdesc->rootofs+4; /* No current key given , so start at root */
|
|
|
|
if ( !(type & TPF_ABS) && *path == '\\' && *(path+1) != '\\') { /* Start from root if path starts with \ */
|
|
path++;
|
|
vofs = hdesc->rootofs+4;
|
|
}
|
|
|
|
key = (struct nk_key *)(buf + vofs);
|
|
// printf("check of nk at offset: 0x%0x\n",vofs);
|
|
|
|
if (key->id != 0x6b6e) {
|
|
printf("trav_path: Error: Not a 'nk' node!\n");
|
|
return(0);
|
|
}
|
|
|
|
if ( !(type & TPF_ABS)) { /* Only traverse path if not absolute literal value name passed */
|
|
|
|
/* TODO: Need to rethink this.. */
|
|
|
|
/* Find \ delimiter or end of string, copying to name part buffer as we go,
|
|
rewriting double \\s */
|
|
partptr = part;
|
|
for(plen = 0; path[plen] && (path[plen] != '\\' || path[plen+1] == '\\'); plen++) {
|
|
if (path[plen] == '\\' && path[plen+1] == '\\') plen++; /* Skip one if double */
|
|
*partptr++ = path[plen];
|
|
}
|
|
*partptr = '\0';
|
|
|
|
#if 0
|
|
printf("Name part: <%s>\n",part);
|
|
printf("Name path: <%s>\n",path);
|
|
#endif
|
|
|
|
adjust = (path[plen] == '\\' ) ? 1 : 0;
|
|
// printf("Checking for <%s> with len %d\n",path,plen);
|
|
|
|
if (!plen) return(vofs-4); /* Path has no lenght - we're there! */
|
|
|
|
if ( (plen == 1) && (*(path+1) && *path == '.') && !(type & TPF_EXACT)) { /* Handle '.' current dir */
|
|
// printf("** handle current\n");
|
|
return(trav_path(hdesc,vofs,path+plen+adjust,type));
|
|
}
|
|
if ( !(type & TPF_EXACT) && (plen == 2) && !strncmp("..",path,2) ) { /* Get parent key */
|
|
newnkofs = key->ofs_parent + 0x1004;
|
|
/* Return parent (or only root if at the root) */
|
|
return(trav_path(hdesc, (key->type == KEY_ROOT ? vofs : newnkofs), path+plen+adjust, type));
|
|
}
|
|
|
|
}
|
|
|
|
/* at last name of path, and we want vk, and the nk has values */
|
|
if ((type & TPF_VK_ABS) || (!path[plen] && (type & TPF_VK) && key->no_values) ) {
|
|
// if ( (!path[plen] && (type & TPF_VK) && key->no_values) ) {
|
|
if (type & TPF_ABS) {
|
|
strcpy(part, path);
|
|
plen = de_escape(part,0);
|
|
partptr = part + plen;
|
|
}
|
|
|
|
// printf("VK namematch for <%s>, type = %d\n",part,type);
|
|
vlistofs = key->ofs_vallist + 0x1004;
|
|
vlistkey = (int32_t *)(buf + vlistofs);
|
|
i = vlist_find(hdesc, vlistofs, key->no_values, part, type);
|
|
if (i != -1) {
|
|
return(vlistkey[i] + 0x1000);
|
|
}
|
|
}
|
|
|
|
if (key->no_subkeys > 0) { /* If it has subkeys, loop through the hash */
|
|
char *partw = NULL;
|
|
int partw_len, part_len;
|
|
|
|
// printf("trav_path: subkey loop: path = %s, part = %s\n",path,part);
|
|
|
|
lfofs = key->ofs_lf + 0x1004; /* lf (hash) record */
|
|
lfkey = (struct lf_key *)(buf + lfofs);
|
|
|
|
if (lfkey->id == 0x6972) { /* ri struct need special parsing */
|
|
/* Prime loop state */
|
|
|
|
rikey = (struct ri_key *)lfkey;
|
|
ricnt = rikey->no_lis;
|
|
r = 0;
|
|
likey = (struct li_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ;
|
|
subs = likey->no_keys;
|
|
if (likey->id != 0x696c) { /* Bwah, not li anyway, XP uses lh usually which is actually smarter */
|
|
lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ;
|
|
likey = NULL;
|
|
}
|
|
} else {
|
|
if (lfkey->id == 0x696c) { /* li? */
|
|
likey = (struct li_key *)(buf + lfofs);
|
|
} else {
|
|
likey = NULL;
|
|
}
|
|
rikey = NULL;
|
|
ricnt = 0; r = 0; subs = key->no_subkeys;
|
|
}
|
|
|
|
partw = string_prog2regw(part, partptr-part, &partw_len);
|
|
// string_prog2rega(part, partptr-part);
|
|
part_len = strlen(part);
|
|
do {
|
|
for(i = 0; i < subs; i++) {
|
|
if (likey) newnkofs = likey->hash[i].ofs_nk + 0x1004;
|
|
else newnkofs = lfkey->hash[i].ofs_nk + 0x1004;
|
|
newnkkey = (struct nk_key *)(buf + newnkofs);
|
|
if (newnkkey->id != 0x6b6e) {
|
|
printf("ERROR: not 'nk' node! (strange?)\n");
|
|
} else {
|
|
if (newnkkey->len_name <= 0) {
|
|
printf("[No name]\n");
|
|
} else if (
|
|
( ( part_len <= newnkkey->len_name ) && !(type & TPF_EXACT) ) ||
|
|
( ( part_len == newnkkey->len_name ) && (type & TPF_EXACT) )
|
|
) {
|
|
/* Can't match if name is shorter than we look for */
|
|
int cmp;
|
|
// printf("trav_path: part = <%s>, part_len = %d\n",part,part_len);
|
|
if (newnkkey->type & 0x20)
|
|
cmp = strncmp(part,newnkkey->keyname,part_len);
|
|
else
|
|
cmp = memcmp(partw, newnkkey->keyname, partw_len);
|
|
if (!cmp) {
|
|
// printf("Key at 0x%0x matches! recursing! new path = %s\n",newnkofs,path+plen+adjust);
|
|
free(partw);
|
|
return(trav_path(hdesc, newnkofs, path+plen+adjust, type));
|
|
}
|
|
}
|
|
} /* if id OK */
|
|
} /* hash loop */
|
|
r++;
|
|
if (ricnt && r < ricnt) {
|
|
newnkofs = rikey->hash[r].ofs_li;
|
|
likey = (struct li_key *)( hdesc->buffer + newnkofs + 0x1004 ) ;
|
|
subs = likey->no_keys;
|
|
if (likey->id != 0x696c) { /* Bwah, not li anyway, XP uses lh usually which is actually smarter */
|
|
lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ;
|
|
likey = NULL;
|
|
}
|
|
}
|
|
} while (r < ricnt && ricnt);
|
|
free(partw);
|
|
|
|
} /* if subkeys */
|
|
|
|
/* Not found */
|
|
return(0);
|
|
}
|
|
|
|
|
|
/* ls - list a 'nk' nodes subkeys and values
|
|
* vofs - offset to start of data (skipping block linkage)
|
|
* type - 0 = full, 1 = keys only. 2 = values only
|
|
*/
|
|
void nk_ls(struct hive *hdesc, char *path, int vofs, int type)
|
|
{
|
|
struct nk_key *key;
|
|
int nkofs;
|
|
struct ex_data ex;
|
|
struct vex_data vex;
|
|
int count = 0, countri = 0;
|
|
|
|
|
|
nkofs = trav_path(hdesc, vofs, path, 0);
|
|
|
|
if(!nkofs) {
|
|
printf("nk_ls: Key <%s> not found\n",path);
|
|
return;
|
|
}
|
|
nkofs += 4;
|
|
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
VERBF(hdesc,"ls of node at offset 0x%0x\n",nkofs);
|
|
|
|
if (key->id != 0x6b6e) {
|
|
printf("Error: Not a 'nk' node at offset %x!\n",nkofs);
|
|
|
|
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
|
|
|
|
}
|
|
|
|
printf("Node has %d subkeys and %d values",key->no_subkeys,key->no_values);
|
|
if (key->len_classnam) printf(", and class-data of %d bytes",key->len_classnam);
|
|
printf("\n");
|
|
|
|
if (key->no_subkeys) {
|
|
printf(" key name\n");
|
|
while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) {
|
|
if (!(hdesc->state & HMODE_VERBOSE)) printf("%c <%s>\n", (ex.nk->len_classnam)?'*':' ',ex.name);
|
|
else printf("[%6x] %c <%s>\n", ex.nkoffs, (ex.nk->len_classnam)?'*':' ',ex.name);
|
|
FREE(ex.name);
|
|
}
|
|
}
|
|
count = 0;
|
|
if (key->no_values) {
|
|
printf(" size type value name [value if type DWORD]\n");
|
|
while ((ex_next_v(hdesc, nkofs, &count, &vex) > 0)) {
|
|
if (hdesc->state & HMODE_VERBOSE) printf("[%6x] %6d %x %-16s <%s>", vex.vkoffs - 4, vex.size, vex.type,
|
|
(vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name);
|
|
else
|
|
printf("%6d %x %-16s <%s>", vex.size, vex.type,
|
|
(vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name);
|
|
|
|
if (vex.type == REG_DWORD) printf(" %*d [0x%x]",25-(int)strlen(vex.name),vex.val , vex.val);
|
|
printf("\n");
|
|
FREE(vex.name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Get the type of a value */
|
|
int get_val_type(struct hive *hdesc, int vofs, char *path, int exact)
|
|
{
|
|
struct vk_key *vkkey;
|
|
int vkofs;
|
|
|
|
vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK);
|
|
if (!vkofs) {
|
|
return -1;
|
|
}
|
|
vkofs +=4;
|
|
vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
|
|
|
|
return(vkkey->val_type);
|
|
}
|
|
|
|
/* Set/change the type of a value. Strangely we need this in some situation in SAM
|
|
* and delete + add value is a bit overkill */
|
|
|
|
int set_val_type(struct hive *hdesc, int vofs, char *path, int exact, int type)
|
|
{
|
|
struct vk_key *vkkey;
|
|
int vkofs;
|
|
|
|
vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK);
|
|
if (!vkofs) {
|
|
return -1;
|
|
}
|
|
vkofs +=4;
|
|
vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
|
|
|
|
vkkey->val_type = type;
|
|
|
|
return(vkkey->val_type);
|
|
}
|
|
|
|
|
|
|
|
/* Get len of a value, given current key + path */
|
|
int get_val_len(struct hive *hdesc, int vofs, char *path, int exact)
|
|
{
|
|
struct vk_key *vkkey;
|
|
int vkofs;
|
|
int len;
|
|
|
|
vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK);
|
|
if (!vkofs) {
|
|
return -1;
|
|
}
|
|
vkofs +=4;
|
|
vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
|
|
|
|
len = vkkey->len_data & 0x7fffffff;
|
|
|
|
if ( vkkey->len_data == 0x80000000 && (exact & TPF_VK_SHORT)) { /* Special inline case, return size of 4 (dword) */
|
|
len = 4;
|
|
}
|
|
|
|
return(len);
|
|
}
|
|
|
|
/* Get void-pointer to value-data, also if inline.
|
|
* If val_type != 0 a check for correct value type is done
|
|
* Caller must keep track of value's length (call function above to get it)
|
|
*/
|
|
void *get_val_data(struct hive *hdesc, int vofs, char *path, int val_type, int exact)
|
|
{
|
|
struct vk_key *vkkey;
|
|
int vkofs;
|
|
|
|
// printf("get_val_data: path = %s\n",path);
|
|
|
|
vkofs = trav_path(hdesc,vofs,path,exact | TPF_VK);
|
|
if (!vkofs) {
|
|
printf("get_val_data: %s not found\n",path);
|
|
abort();
|
|
return NULL;
|
|
}
|
|
vkofs +=4;
|
|
vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
|
|
|
|
|
|
if (vkkey->len_data == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (vkkey->len_data == 0x80000000 && (exact & TPF_VK_SHORT)) { /* Special inline case (len = 0x80000000) */
|
|
return(&vkkey->val_type); /* Data (4 bytes?) in type field */
|
|
}
|
|
|
|
if (val_type && vkkey->val_type && (vkkey->val_type) != val_type) {
|
|
printf("Value <%s> is not of correct type!\n",path);
|
|
#if DOCORE
|
|
abort();
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
/* Negative len is inline, return ptr to offset-field which in
|
|
* this case contains the data itself
|
|
*/
|
|
if (vkkey->len_data & 0x80000000) return(&vkkey->ofs_data);
|
|
/* Normal return, return data pointer */
|
|
return(hdesc->buffer + vkkey->ofs_data + 0x1004);
|
|
}
|
|
|
|
|
|
/* Get and copy key data (if any) to buffer
|
|
* if kv==NULL will allocate needed return struct & buffer
|
|
* else will use buffer allocated for it (if it fits)
|
|
* return len+data or NULL if not found (or other error)
|
|
* NOTE: caller must deallocate buffer! a simple free(keyval) will suffice.
|
|
*/
|
|
struct keyval *get_val2buf(struct hive *hdesc, struct keyval *kv,
|
|
int vofs, char *path, int type, int exact )
|
|
{
|
|
int l,i,parts,list,blockofs,blocksize,point,copylen,restlen;
|
|
struct keyval *kr;
|
|
void *keydataptr;
|
|
struct db_key *db;
|
|
void *addr;
|
|
|
|
l = get_val_len(hdesc, vofs, path, exact);
|
|
if (l == -1) return(NULL); /* error */
|
|
if (kv && (kv->len < l)) return(NULL); /* Check for overflow of supplied buffer */
|
|
|
|
keydataptr = get_val_data(hdesc, vofs, path, type, exact);
|
|
// if (!keydataptr) return(NULL);
|
|
|
|
/* Allocate space for data + header, or use supplied buffer */
|
|
if (kv) {
|
|
kr = kv;
|
|
} else {
|
|
ALLOC(kr,1,l*sizeof(int)+4);
|
|
}
|
|
|
|
kr->len = l;
|
|
|
|
// printf("get_val2buf: keydataprtr = %x, l = %x\n",keydataptr,l);
|
|
|
|
|
|
if (l > VAL_DIRECT_LIMIT) { /* Where do the db indirects start? seems to be around 16k */
|
|
db = (struct db_key *)keydataptr;
|
|
if (db->id != 0x6264) abort();
|
|
parts = db->no_part;
|
|
list = db->ofs_data + 0x1004;
|
|
printf("get_val2buf: Long value: parts = %d, list = %x\n",parts,list);
|
|
|
|
point = 0;
|
|
restlen = l;
|
|
for (i = 0; i < parts; i++) {
|
|
blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000;
|
|
blocksize = -get_int(hdesc->buffer + blockofs) - 8;
|
|
|
|
/* Copy this part, up to size of block or rest lenght in last block */
|
|
copylen = (blocksize > restlen) ? restlen : blocksize;
|
|
|
|
printf("get_val2buf: Datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize);
|
|
printf(" : Point = %x, restlen = %x, copylen = %x\n",point,restlen,copylen);
|
|
|
|
addr = (void *)&(kr->data) + point;
|
|
memcpy( addr, hdesc->buffer + blockofs + 4, copylen);
|
|
|
|
// debugit((char *)&(kr->data), l);
|
|
|
|
point += copylen;
|
|
restlen -= copylen;
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
if (l && kr && keydataptr) memcpy(&(kr->data), keydataptr, l);
|
|
}
|
|
|
|
return(kr);
|
|
}
|
|
|
|
/* DWORDs are so common that I make a small function to get it easily */
|
|
|
|
int get_dword(struct hive *hdesc, int vofs, char *path, int exact)
|
|
{
|
|
struct keyval *v;
|
|
int dword;
|
|
|
|
v = get_val2buf(hdesc, NULL, vofs, path, REG_DWORD, exact | TPF_VK);
|
|
if (!v) return(-1); /* well... -1 COULD BE THE STORED VALUE TOO */
|
|
|
|
dword = (int)v->data;
|
|
|
|
FREE(v);
|
|
|
|
return(dword);
|
|
|
|
}
|
|
|
|
/* Sanity checker when transferring data into a block
|
|
* ofs = offset to data block, point to start of actual datablock linkage
|
|
* data = data to copy
|
|
* size = size of data to copy
|
|
*/
|
|
|
|
int fill_block(struct hive *hdesc, int ofs, void *data, int size)
|
|
{
|
|
int blksize;
|
|
|
|
blksize = get_int(hdesc->buffer + ofs);
|
|
blksize = -blksize;
|
|
|
|
#if 0
|
|
printf("fill_block: ofs = %x - %x, size = %x, blksize = %x\n",ofs,ofs+size,size,blksize);
|
|
#endif
|
|
/* if (blksize < size || ( (ofs & 0xfffff000) != ((ofs+size) & 0xfffff000) )) { */
|
|
if (blksize < size) {
|
|
printf("fill_block: ERROR: block to small for data: ofs = %x, size = %x, blksize = %x\n",ofs,size,blksize);
|
|
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
|
|
abort();
|
|
}
|
|
|
|
memcpy(hdesc->buffer + ofs + 4, data, size);
|
|
return(0);
|
|
}
|
|
|
|
|
|
/* Free actual data of a value, and update value descriptor
|
|
* hdesc - hive
|
|
* vofs - current value offset
|
|
*/
|
|
|
|
int free_val_data(struct hive *hdesc, int vkofs)
|
|
{
|
|
struct vk_key *vkkey;
|
|
struct db_key *db;
|
|
int len,i,blockofs,blocksize,parts,list;
|
|
|
|
|
|
vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
|
|
|
|
len = vkkey->len_data;
|
|
|
|
if (!(len & 0x80000000)) { /* Check for inline, if so, skip it, nothing to do */
|
|
|
|
if (len > VAL_DIRECT_LIMIT) { /* Where do the db indirects start? seems to be around 16k */
|
|
|
|
db = (struct db_key *)(hdesc->buffer + vkkey->ofs_data + 0x1004);
|
|
|
|
if (db->id != 0x6264) abort();
|
|
|
|
parts = db->no_part;
|
|
list = db->ofs_data + 0x1004;
|
|
|
|
printf("free_val_data: Long value: parts = %d, list = %x\n",parts,list);
|
|
|
|
for (i = 0; i < parts; i++) {
|
|
blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000;
|
|
blocksize = -get_int(hdesc->buffer + blockofs);
|
|
printf("free_val_data: Freeing long datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize);
|
|
free_block(hdesc, blockofs);
|
|
}
|
|
|
|
printf("free_val_data: Freeing indirect list at %x\n", list-4);
|
|
free_block(hdesc, list - 4);
|
|
printf("free_val_data: Freeing db structure at %x\n", vkkey->ofs_data + 0x1000);
|
|
} /* Fall through to regular which deallocs data or db block ofs_data point to */
|
|
|
|
if (len) free_block(hdesc, vkkey->ofs_data + 0x1000);
|
|
|
|
} /* inline check */
|
|
|
|
|
|
vkkey->len_data = 0;
|
|
vkkey->ofs_data = 0;
|
|
|
|
return(vkofs);
|
|
|
|
}
|
|
|
|
|
|
/* Allocate data for value. Frees old data (if any) which will be destroyed
|
|
* hdesc - hive
|
|
* vofs - current key
|
|
* path - path to value
|
|
* size - size of data
|
|
* Returns: 0 - error, >0 pointer to actual dataspace
|
|
*/
|
|
|
|
int alloc_val_data(struct hive *hdesc, int vofs, char *path, int size,int exact)
|
|
{
|
|
struct vk_key *vkkey;
|
|
struct db_key *db;
|
|
int vkofs,dbofs,listofs,blockofs,blocksize,parts;
|
|
int datablk,i;
|
|
int *ptr;
|
|
|
|
vkofs = trav_path(hdesc,vofs,path,exact);
|
|
if (!vkofs) {
|
|
return (0);
|
|
}
|
|
|
|
vkofs +=4;
|
|
vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
|
|
|
|
|
|
free_val_data(hdesc, vkofs); /* Get rid of old data if any */
|
|
|
|
/* Allocate space for new data */
|
|
if (size > 4) {
|
|
if (size > VAL_DIRECT_LIMIT) { /* We must allocate indirect stuff *sigh* */
|
|
parts = size / VAL_DIRECT_LIMIT + 1;
|
|
printf("alloc_val_data: doing large key: size = %x (%d), parts = %d\n",size,size,parts);
|
|
|
|
dbofs = alloc_block(hdesc, vkofs, sizeof(struct db_key)); /* Alloc db structure */
|
|
db = (struct db_key *)(hdesc->buffer + dbofs + 4);
|
|
db->id = 0x6264;
|
|
db->no_part = parts;
|
|
listofs = alloc_block(hdesc, vkofs, 4 * parts); /* block offset list */
|
|
db = (struct db_key *)(hdesc->buffer + dbofs + 4);
|
|
db->ofs_data = listofs - 0x1000;
|
|
printf("alloc_val_data: dbofs = %x, listofs = %x\n",dbofs,listofs);
|
|
|
|
for (i = 0; i < parts; i++) {
|
|
blocksize = VAL_DIRECT_LIMIT; /* Windows seem to alway allocate the whole block */
|
|
blockofs = alloc_block(hdesc, vkofs, blocksize);
|
|
printf("alloc_val_data: block # %d, blockofs = %x\n",i,blockofs);
|
|
ptr = (int *)(hdesc->buffer + listofs + 4 + (i << 2));
|
|
*ptr = blockofs - 0x1000;
|
|
}
|
|
datablk = dbofs;
|
|
|
|
} else { /* Regular size < 16 k direct alloc */
|
|
datablk = alloc_block(hdesc, vkofs, size);
|
|
}
|
|
|
|
|
|
|
|
} else { /* 4 bytes or less are inlined */
|
|
datablk = vkofs + (int32_t)&(vkkey->ofs_data) - (int32_t)vkkey;
|
|
size |= 0x80000000;
|
|
}
|
|
|
|
if (!datablk) return(0);
|
|
|
|
vkkey = (struct vk_key *)(hdesc->buffer + vkofs); /* alloc_block may move pointer, realloc() buf */
|
|
|
|
// printf("alloc_val_data: datablk = %x, size = %x, vkkey->len_data = %x\n",datablk, size, vkkey->len_data);
|
|
|
|
|
|
|
|
/* Link in new datablock */
|
|
if ( !(size & 0x80000000)) vkkey->ofs_data = datablk - 0x1000;
|
|
vkkey->len_data = size;
|
|
|
|
return(datablk + 4);
|
|
}
|
|
|
|
|
|
/* Add a value to a key.
|
|
* Just add the metadata (empty value), to put data into it, use
|
|
* put_buf2val afterwards
|
|
* hdesc - hive
|
|
* nkofs - current key
|
|
* name - name of value
|
|
* type - type of value
|
|
* returns: 0 err, >0 offset to value metadata
|
|
*/
|
|
|
|
struct vk_key *add_value(struct hive *hdesc, int nkofs, char *name, int type)
|
|
{
|
|
struct nk_key *nk;
|
|
int oldvlist = 0, newvlist, newvkofs;
|
|
struct vk_key *newvkkey;
|
|
char *blank="";
|
|
|
|
if (!name || !*name) return(NULL);
|
|
|
|
|
|
nk = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
if (nk->id != 0x6b6e) {
|
|
printf("add_value: Key pointer not to 'nk' node!\n");
|
|
return(NULL);
|
|
}
|
|
|
|
if (vlist_find(hdesc, nk->ofs_vallist + 0x1004, nk->no_values, name, TPF_EXACT) != -1) {
|
|
printf("add_value: value %s already exists\n",name);
|
|
return(NULL);
|
|
}
|
|
|
|
if (!strcmp(name,"@")) name = blank;
|
|
|
|
if (nk->no_values) oldvlist = nk->ofs_vallist;
|
|
|
|
newvlist = alloc_block(hdesc, nkofs, nk->no_values * 4 + 4);
|
|
if (!newvlist) {
|
|
printf("add_value: failed to allocate new value list!\n");
|
|
return(NULL);
|
|
}
|
|
|
|
nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved.. */
|
|
|
|
if (oldvlist) { /* Copy old data if any */
|
|
memcpy(hdesc->buffer + newvlist + 4, hdesc->buffer + oldvlist + 0x1004, nk->no_values * 4 + 4);
|
|
}
|
|
|
|
/* Allocate value descriptor including its name */
|
|
newvkofs = alloc_block(hdesc, newvlist, sizeof(struct vk_key) + strlen(name));
|
|
if (!newvkofs) {
|
|
printf("add_value: failed to allocate value descriptor\n");
|
|
free_block(hdesc, newvlist);
|
|
return(NULL);
|
|
}
|
|
|
|
nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved.. */
|
|
|
|
|
|
/* Success, now fill in the metadata */
|
|
|
|
newvkkey = (struct vk_key *)(hdesc->buffer + newvkofs + 4);
|
|
|
|
/* Add pointer in value list */
|
|
*(int *)(hdesc->buffer + newvlist + 4 + (nk->no_values * 4)) = newvkofs - 0x1000;
|
|
|
|
/* Fill in vk struct */
|
|
newvkkey->id = 0x6b76;
|
|
newvkkey->len_name = strlen(name);
|
|
if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) {
|
|
newvkkey->len_data = 0x80000004; /* Prime the DWORD inline stuff */
|
|
} else {
|
|
newvkkey->len_data = 0x80000000; /* Default inline zero size */
|
|
}
|
|
newvkkey->ofs_data = 0;
|
|
newvkkey->val_type = type;
|
|
newvkkey->flag = newvkkey->len_name ? 1 : 0; /* Seems to be 1, but 0 for no name default value */
|
|
newvkkey->dummy1 = 0;
|
|
memcpy((char *)&newvkkey->keyname, name, newvkkey->len_name); /* And copy name */
|
|
|
|
/* Finally update the key and free the old valuelist */
|
|
nk->no_values++;
|
|
nk->ofs_vallist = newvlist - 0x1000;
|
|
if (oldvlist) free_block(hdesc,oldvlist + 0x1000);
|
|
|
|
return(newvkkey);
|
|
|
|
}
|
|
|
|
/* Remove a vk-struct incl dataspace if any
|
|
* Mostly for use by higher level stuff
|
|
* hdesc - hive
|
|
* vkofs - offset to vk
|
|
*/
|
|
|
|
void del_vk(struct hive *hdesc, int vkofs)
|
|
{
|
|
struct vk_key *vk;
|
|
|
|
vk = (struct vk_key *)(hdesc->buffer + vkofs);
|
|
if (vk->id != 0x6b76) {
|
|
printf("del_vk: Key pointer not to 'vk' node!\n");
|
|
return;
|
|
}
|
|
|
|
if ( !(vk->len_data & 0x80000000) && vk->ofs_data) {
|
|
free_val_data(hdesc, vkofs);
|
|
}
|
|
|
|
free_block(hdesc, vkofs - 4);
|
|
}
|
|
|
|
/* Delete all values from key (used in recursive delete)
|
|
* hdesc - yer usual hive
|
|
* nkofs - current keyoffset
|
|
*/
|
|
|
|
void del_allvalues(struct hive *hdesc, int nkofs)
|
|
{
|
|
int vlistofs, o, vkofs;
|
|
int32_t *vlistkey;
|
|
struct nk_key *nk;
|
|
|
|
nk = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
if (nk->id != 0x6b6e) {
|
|
printf("del_allvalues: Key pointer not to 'nk' node!\n");
|
|
return;
|
|
}
|
|
|
|
if (!nk->no_values) {
|
|
/* printf("del_avalues: Key has no values!\n"); */
|
|
return;
|
|
}
|
|
|
|
vlistofs = nk->ofs_vallist + 0x1004;
|
|
vlistkey = (int32_t *)(hdesc->buffer + vlistofs);
|
|
|
|
/* Loop through index and delete all vk's */
|
|
for (o = 0; o < nk->no_values; o++) {
|
|
vkofs = vlistkey[o] + 0x1004;
|
|
del_vk(hdesc, vkofs);
|
|
}
|
|
|
|
/* Then zap the index, and update nk */
|
|
free_block(hdesc, vlistofs-4);
|
|
nk->ofs_vallist = -1;
|
|
nk->no_values = 0;
|
|
}
|
|
|
|
|
|
/* Delete single value from key
|
|
* hdesc - yer usual hive
|
|
* nkofs - current keyoffset
|
|
* name - name of value to delete
|
|
* exact - NKF_EXACT to do exact match, else first match
|
|
* returns: 0 - ok, 1 - failed
|
|
*/
|
|
|
|
int del_value(struct hive *hdesc, int nkofs, char *name, int exact)
|
|
{
|
|
int vlistofs, slot, o, n, vkofs, newlistofs;
|
|
int32_t *vlistkey, *tmplist, *newlistkey;
|
|
struct nk_key *nk;
|
|
char *blank="";
|
|
|
|
if (!name || !*name) return(1);
|
|
|
|
if (!strcmp(name,"@")) name = blank;
|
|
|
|
nk = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
if (nk->id != 0x6b6e) {
|
|
printf("del_value: Key pointer not to 'nk' node!\n");
|
|
return(1);
|
|
}
|
|
|
|
if (!nk->no_values) {
|
|
printf("del_value: Key has no values!\n");
|
|
return(1);
|
|
}
|
|
|
|
vlistofs = nk->ofs_vallist + 0x1004;
|
|
vlistkey = (int32_t *)(hdesc->buffer + vlistofs);
|
|
|
|
slot = vlist_find(hdesc, vlistofs, nk->no_values, name, TPF_VK);
|
|
|
|
if (slot == -1) {
|
|
printf("del_value: value %s not found!\n",name);
|
|
return(1);
|
|
}
|
|
|
|
/* Delete vk and data */
|
|
vkofs = vlistkey[slot] + 0x1004;
|
|
del_vk(hdesc, vkofs);
|
|
|
|
/* Copy out old index list */
|
|
CREATE(tmplist,int32_t,nk->no_values);
|
|
memcpy(tmplist, vlistkey, nk->no_values * sizeof(int32_t));
|
|
|
|
free_block(hdesc,vlistofs-4); /* Get rid of old list */
|
|
|
|
nk->no_values--;
|
|
|
|
if (nk->no_values) {
|
|
newlistofs = alloc_block(hdesc, vlistofs, nk->no_values * sizeof(int32_t));
|
|
if (!newlistofs) {
|
|
printf("del_value: FATAL: Was not able to alloc new index list\n");
|
|
abort();
|
|
}
|
|
nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved */
|
|
|
|
/* Now copy over, omitting deleted entry */
|
|
newlistkey = (int32_t *)(hdesc->buffer + newlistofs + 4);
|
|
for (n = 0, o = 0; n < nk->no_values; o++, n++) {
|
|
if (o == slot) o++;
|
|
newlistkey[n] = tmplist[o];
|
|
}
|
|
nk->ofs_vallist = newlistofs - 0x1000;
|
|
} else {
|
|
nk->ofs_vallist = -1;
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add a subkey to a key
|
|
* hdesc - usual..
|
|
* nkofs - offset of current nk
|
|
* name - name of key to add
|
|
* return: ptr to new keystruct, or NULL
|
|
*/
|
|
|
|
#undef AKDEBUG
|
|
|
|
struct nk_key *add_key(struct hive *hdesc, int nkofs, char *name)
|
|
{
|
|
|
|
int slot, newlfofs = 0, oldlfofs = 0, newliofs = 0;
|
|
int oldliofs = 0;
|
|
int o, n, i, onkofs, newnkofs, cmp;
|
|
int rimax, rislot, riofs, namlen;
|
|
struct ri_key *ri = NULL;
|
|
struct lf_key *newlf = NULL, *oldlf;
|
|
struct li_key *newli = NULL, *oldli;
|
|
struct nk_key *key, *newnk, *onk;
|
|
int32_t hash;
|
|
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
|
|
if (key->id != 0x6b6e) {
|
|
printf("add_key: current ptr not 'nk'\n");
|
|
return(NULL);
|
|
}
|
|
|
|
namlen = strlen(name);
|
|
|
|
slot = -1;
|
|
if (key->no_subkeys) { /* It already has subkeys */
|
|
|
|
oldlfofs = key->ofs_lf;
|
|
oldliofs = key->ofs_lf;
|
|
|
|
oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
|
|
if (oldlf->id != 0x666c && oldlf->id != 0x686c && oldlf->id != 0x696c && oldlf->id != 0x6972) {
|
|
printf("add_key: index type not supported: 0x%04x\n",oldlf->id);
|
|
return(NULL);
|
|
}
|
|
|
|
rimax = 0; ri = NULL; riofs = 0; rislot = -1;
|
|
if (oldlf->id == 0x6972) { /* Indirect index 'ri', init loop */
|
|
riofs = key->ofs_lf;
|
|
ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004);
|
|
rimax = ri->no_lis-1;
|
|
|
|
#ifdef AKDEBUG
|
|
printf("add_key: entering 'ri' traverse, rimax = %d\n",rimax);
|
|
#endif
|
|
|
|
oldliofs = ri->hash[rislot+1].ofs_li;
|
|
oldlfofs = ri->hash[rislot+1].ofs_li;
|
|
|
|
}
|
|
|
|
do { /* 'ri' loop, at least run once if no 'ri' deep index */
|
|
|
|
if (ri) { /* Do next 'ri' slot */
|
|
rislot++;
|
|
oldliofs = ri->hash[rislot].ofs_li;
|
|
oldlfofs = ri->hash[rislot].ofs_li;
|
|
oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
|
|
oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
|
|
}
|
|
|
|
oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
|
|
oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
|
|
|
|
#ifdef AKDEBUG
|
|
printf("add_key: top of ri-loop: rislot = %d, rimax = %d\n",rislot,rimax);
|
|
#endif
|
|
slot = -1;
|
|
|
|
if (oldli->id == 0x696c) { /* li */
|
|
|
|
#ifdef AKDEBUG
|
|
printf("add_key: li slot allocate\n");
|
|
#endif
|
|
|
|
FREE(newli);
|
|
ALLOC(newli, 8 + 4*oldli->no_keys + 4, 1);
|
|
newli->no_keys = oldli->no_keys;
|
|
newli->id = oldli->id;
|
|
|
|
/* Now copy old, checking where to insert (alphabetically) */
|
|
for (o = 0, n = 0; o < oldli->no_keys; o++,n++) {
|
|
onkofs = oldli->hash[o].ofs_nk;
|
|
onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004);
|
|
if (slot == -1) {
|
|
#if 1
|
|
printf("add_key: cmp <%s> with <%s>\n",name,onk->keyname);
|
|
#endif
|
|
|
|
cmp = strn_casecmp(name, onk->keyname, (namlen > onk->len_name) ? namlen : onk->len_name);
|
|
if (!cmp) {
|
|
printf("add_key: key %s already exists!\n",name);
|
|
FREE(newli);
|
|
return(NULL);
|
|
}
|
|
if ( cmp < 0) {
|
|
slot = o;
|
|
rimax = rislot; /* Cause end of 'ri' search, too */
|
|
n++;
|
|
#ifdef AKDEBUG
|
|
printf("add_key: li-match: slot = %d\n",o);
|
|
#endif
|
|
}
|
|
}
|
|
newli->hash[n].ofs_nk = oldli->hash[o].ofs_nk;
|
|
}
|
|
if (slot == -1) slot = oldli->no_keys;
|
|
|
|
} else { /* lf or lh */
|
|
|
|
oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
|
|
|
|
FREE(newlf);
|
|
ALLOC(newlf, 8 + 8*oldlf->no_keys + 8, 1);
|
|
newlf->no_keys = oldlf->no_keys;
|
|
newlf->id = oldlf->id;
|
|
#ifdef AKDEBUG
|
|
printf("add_key: new lf/lh no_keys: %d\n",newlf->no_keys);
|
|
#endif
|
|
|
|
/* Now copy old, checking where to insert (alphabetically) */
|
|
for (o = 0, n = 0; o < oldlf->no_keys; o++,n++) {
|
|
onkofs = oldlf->hash[o].ofs_nk;
|
|
onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004);
|
|
if (slot == -1) {
|
|
|
|
#if 0
|
|
printf("add_key: cmp <%s> with <%s>\n",name,onk->keyname);
|
|
#endif
|
|
cmp = strn_casecmp(name, onk->keyname, (namlen > onk->len_name) ? namlen : onk->len_name);
|
|
if (!cmp) {
|
|
printf("add_key: key %s already exists!\n",name);
|
|
FREE(newlf);
|
|
return(NULL);
|
|
}
|
|
if ( cmp < 0 ) {
|
|
slot = o;
|
|
rimax = rislot; /* Cause end of 'ri' search, too */
|
|
n++;
|
|
#ifdef AKDEBUG
|
|
printf("add_key: lf-match: slot = %d\n",o);
|
|
#endif
|
|
}
|
|
}
|
|
newlf->hash[n].ofs_nk = oldlf->hash[o].ofs_nk;
|
|
newlf->hash[n].name[0] = oldlf->hash[o].name[0];
|
|
newlf->hash[n].name[1] = oldlf->hash[o].name[1];
|
|
newlf->hash[n].name[2] = oldlf->hash[o].name[2];
|
|
newlf->hash[n].name[3] = oldlf->hash[o].name[3];
|
|
}
|
|
if (slot == -1) slot = oldlf->no_keys;
|
|
} /* li else check */
|
|
|
|
|
|
} while ( (rislot < rimax) && (rimax > 0)); /* 'ri' wrapper loop */
|
|
|
|
} else { /* Parent was empty, make new index block */
|
|
#ifdef AKDEBUG
|
|
printf("add_key: new index!\n");
|
|
#endif
|
|
ALLOC(newlf, 8 + 8, 1);
|
|
newlf->no_keys = 0 ; /* Will increment to 1 when filling in the offset later */
|
|
/* Use ID (lf, lh or li) we fetched from root node, so we use same as rest of hive */
|
|
newlf->id = hdesc->nkindextype;
|
|
slot = 0;
|
|
} /* if has keys before */
|
|
|
|
|
|
/* Make and fill in new nk */
|
|
newnkofs = alloc_block(hdesc, nkofs, sizeof(struct nk_key) + strlen(name));
|
|
if (!newnkofs) {
|
|
printf("add_key: unable to allocate space for new key descriptor for %s!\n",name);
|
|
FREE(newlf);
|
|
FREE(newli);
|
|
return(NULL);
|
|
}
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer moved */
|
|
newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4);
|
|
|
|
newnk->id = 0x6b6e;
|
|
newnk->type = KEY_NORMAL; /* Some versions use 0x1020 a lot.. */
|
|
newnk->ofs_parent = nkofs - 0x1004;
|
|
newnk->no_subkeys = 0;
|
|
newnk->ofs_lf = -1;
|
|
newnk->no_values = 0;
|
|
newnk->ofs_vallist = -1;
|
|
newnk->ofs_sk = key->ofs_sk; /* Get parents for now. 0 or -1 here crashes XP */
|
|
newnk->ofs_classnam = -1;
|
|
newnk->len_name = strlen(name);
|
|
newnk->len_classnam = 0;
|
|
memcpy(newnk->keyname, name, newnk->len_name);
|
|
|
|
if (newli) { /* Handle li */
|
|
|
|
#if AKDEBUG
|
|
printf("add_key: li fill at slot: %d\n",slot);
|
|
#endif
|
|
|
|
/* And put its offset into parents index list */
|
|
newli->hash[slot].ofs_nk = newnkofs - 0x1000;
|
|
newli->no_keys++;
|
|
|
|
/* Allocate space for our new li list and copy it into reg */
|
|
newliofs = alloc_block(hdesc, nkofs, 8 + 4*newli->no_keys);
|
|
if (!newliofs) {
|
|
printf("add_key: unable to allocate space for new index table for %s!\n",name);
|
|
FREE(newli);
|
|
free_block(hdesc,newnkofs);
|
|
return(NULL);
|
|
}
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer moved */
|
|
newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4);
|
|
|
|
/* memcpy(hdesc->buffer + newliofs + 4, newli, 8 + 4*newli->no_keys); */
|
|
fill_block(hdesc, newliofs, newli, 8 + 4*newli->no_keys);
|
|
|
|
|
|
} else { /* lh or lf */
|
|
|
|
#ifdef AKDEBUG
|
|
printf("add_key: lf/lh fill at slot: %d, rislot: %d\n",slot,rislot);
|
|
#endif
|
|
/* And put its offset into parents index list */
|
|
newlf->hash[slot].ofs_nk = newnkofs - 0x1000;
|
|
newlf->no_keys++;
|
|
if (newlf->id == 0x666c) { /* lf hash */
|
|
newlf->hash[slot].name[0] = 0;
|
|
newlf->hash[slot].name[1] = 0;
|
|
newlf->hash[slot].name[2] = 0;
|
|
newlf->hash[slot].name[3] = 0;
|
|
strncpy(newlf->hash[slot].name, name, 4);
|
|
} else if (newlf->id == 0x686c) { /* lh. XP uses this. hashes whole name */
|
|
for (i = 0,hash = 0; i < strlen(name); i++) {
|
|
hash *= 37;
|
|
hash += reg_touppertable[(unsigned char)name[i]];
|
|
}
|
|
newlf->lh_hash[slot].hash = hash;
|
|
}
|
|
|
|
/* Allocate space for our new lf list and copy it into reg */
|
|
newlfofs = alloc_block(hdesc, nkofs, 8 + 8*newlf->no_keys);
|
|
if (!newlfofs) {
|
|
printf("add_key: unable to allocate space for new index table for %s!\n",name);
|
|
FREE(newlf);
|
|
free_block(hdesc,newnkofs);
|
|
return(NULL);
|
|
}
|
|
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer moved */
|
|
newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4);
|
|
|
|
/* memcpy(hdesc->buffer + newlfofs + 4, newlf, 8 + 8*newlf->no_keys); */
|
|
fill_block(hdesc, newlfofs, newlf, 8 + 8*newlf->no_keys);
|
|
|
|
} /* li else */
|
|
|
|
|
|
/* Update parent, and free old lf list */
|
|
key->no_subkeys++;
|
|
if (ri) { /* ri index */
|
|
ri->hash[rislot].ofs_li = (newlf ? newlfofs : newliofs) - 0x1000;
|
|
} else { /* Parent key */
|
|
key->ofs_lf = (newlf ? newlfofs : newliofs) - 0x1000;
|
|
}
|
|
|
|
if (newlf && oldlfofs) free_block(hdesc,oldlfofs + 0x1000);
|
|
if (newli && oldliofs) free_block(hdesc,oldliofs + 0x1000);
|
|
|
|
FREE(newlf);
|
|
FREE(newli);
|
|
return(newnk);
|
|
|
|
}
|
|
|
|
/* Delete a subkey from a key
|
|
* hdesc - usual..
|
|
* nkofs - offset of current nk
|
|
* name - name of key to delete (must match exactly, also case)
|
|
* return: 1 - err, 0 - ok
|
|
*/
|
|
|
|
#undef DKDEBUG
|
|
|
|
int del_key(struct hive *hdesc, int nkofs, char *name)
|
|
{
|
|
|
|
int slot = 0, newlfofs = 0, oldlfofs = 0, o, n, onkofs, delnkofs;
|
|
int oldliofs = 0, no_keys = 0, newriofs = 0;
|
|
int namlen;
|
|
int rimax, riofs, rislot;
|
|
struct ri_key *ri, *newri = NULL;
|
|
struct lf_key *newlf = NULL, *oldlf = NULL;
|
|
struct li_key *newli = NULL, *oldli = NULL;
|
|
struct nk_key *key, *onk, *delnk;
|
|
char fullpath[501];
|
|
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
|
|
namlen = strlen(name);
|
|
|
|
#ifdef DKDEBUG
|
|
printf("del_key: deleting: <%s>\n",name);
|
|
#endif
|
|
|
|
|
|
if (key->id != 0x6b6e) {
|
|
printf("del_key: current ptr not nk\n");
|
|
return(1);
|
|
}
|
|
|
|
slot = -1;
|
|
if (!key->no_subkeys) {
|
|
printf("del_key: key has no subkeys!\n");
|
|
return(1);
|
|
}
|
|
|
|
oldlfofs = key->ofs_lf;
|
|
oldliofs = key->ofs_lf;
|
|
|
|
oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
|
|
if (oldlf->id != 0x666c && oldlf->id != 0x686c && oldlf->id != 0x696c && oldlf->id != 0x6972) {
|
|
printf("del_key: index other than 'lf', 'li' or 'lh' not supported yet. 0x%04x\n",oldlf->id);
|
|
return(1);
|
|
}
|
|
|
|
rimax = 0; ri = NULL; riofs = 0;
|
|
rislot = 0;
|
|
|
|
if (oldlf->id == 0x6972) { /* Indirect index 'ri', init loop */
|
|
riofs = key->ofs_lf;
|
|
ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004);
|
|
rimax = ri->no_lis-1;
|
|
|
|
#ifdef DKDEBUG
|
|
printf("del_key: entering 'ri' traverse, rimax = %d\n",rimax);
|
|
#endif
|
|
|
|
rislot = -1; /* Starts at slot 0 below */
|
|
|
|
}
|
|
|
|
do { /* 'ri' loop, at least run once if no 'ri' deep index */
|
|
|
|
if (ri) { /* Do next 'ri' slot */
|
|
rislot++;
|
|
oldliofs = ri->hash[rislot].ofs_li;
|
|
oldlfofs = ri->hash[rislot].ofs_li;
|
|
}
|
|
|
|
oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
|
|
oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
|
|
|
|
#ifdef DKDEBUG
|
|
printf("del_key: top of ri-loop: rislot = %d\n",rislot);
|
|
#endif
|
|
slot = -1;
|
|
|
|
if (oldlf->id == 0x696c) { /* 'li' handler */
|
|
#ifdef DKDEBUG
|
|
printf("del_key: li handler\n");
|
|
#endif
|
|
|
|
FREE(newli);
|
|
ALLOC(newli, 8 + 4*oldli->no_keys - 4, 1);
|
|
newli->no_keys = oldli->no_keys - 1; no_keys = newli->no_keys;
|
|
newli->id = oldli->id;
|
|
|
|
/* Now copy old, checking where to delete */
|
|
for (o = 0, n = 0; o < oldli->no_keys; o++,n++) {
|
|
onkofs = oldli->hash[o].ofs_nk;
|
|
onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004);
|
|
if (slot == -1 && onk->len_name == namlen && !strncmp(name, onk->keyname, (onk->len_name > namlen) ? onk->len_name : namlen)) {
|
|
slot = o;
|
|
delnkofs = onkofs; delnk = onk;
|
|
rimax = rislot;
|
|
o++;
|
|
}
|
|
newli->hash[n].ofs_nk = oldli->hash[o].ofs_nk;
|
|
}
|
|
|
|
|
|
} else { /* 'lf' or 'lh' are similar */
|
|
|
|
#ifdef DKDEBUG
|
|
printf("del_key: lf or lh handler\n");
|
|
#endif
|
|
FREE(newlf);
|
|
ALLOC(newlf, 8 + 8*oldlf->no_keys - 8, 1);
|
|
#ifdef DKDEBUG
|
|
printf("alloc newlf: %x\n",newlf);
|
|
#endif
|
|
newlf->no_keys = oldlf->no_keys - 1; no_keys = newlf->no_keys;
|
|
newlf->id = oldlf->id;
|
|
|
|
/* Now copy old, checking where to delete */
|
|
for (o = 0, n = 0; o < oldlf->no_keys; o++,n++) {
|
|
|
|
onkofs = oldlf->hash[o].ofs_nk;
|
|
onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004);
|
|
|
|
if (slot == -1 && (onk->len_name == namlen) && !strncmp(name, onk->keyname, onk->len_name)) {
|
|
slot = o;
|
|
delnkofs = onkofs; delnk = onk;
|
|
rimax = rislot;
|
|
o++;
|
|
}
|
|
|
|
if (n < newlf->no_keys) { /* Only store if not last index in old */
|
|
#ifdef DKDEBUG
|
|
printf("del_key: n = %d, o = %d\n",n,o);
|
|
#endif
|
|
newlf->hash[n].ofs_nk = oldlf->hash[o].ofs_nk;
|
|
newlf->hash[n].name[0] = oldlf->hash[o].name[0];
|
|
newlf->hash[n].name[1] = oldlf->hash[o].name[1];
|
|
newlf->hash[n].name[2] = oldlf->hash[o].name[2];
|
|
newlf->hash[n].name[3] = oldlf->hash[o].name[3];
|
|
}
|
|
|
|
}
|
|
} /* else lh or lf */
|
|
|
|
} while (rislot < rimax); /* ri traverse loop */
|
|
|
|
if (slot == -1) {
|
|
printf("del_key: subkey %s not found!\n",name);
|
|
FREE(newlf);
|
|
FREE(newli);
|
|
return(1);
|
|
}
|
|
|
|
#ifdef DKDEBUG
|
|
printf("del_key: key found at slot %d\n",slot);
|
|
#endif
|
|
|
|
if (delnk->no_values || delnk->no_subkeys) {
|
|
printf("del_key: subkey %s has subkeys or values. Not deleted.\n",name);
|
|
FREE(newlf);
|
|
FREE(newli);
|
|
return(1);
|
|
}
|
|
|
|
/* Allocate space for our new lf list and copy it into reg */
|
|
if ( no_keys && (newlf || newli) ) {
|
|
newlfofs = alloc_block(hdesc, nkofs, 8 + (newlf ? 8 : 4) * no_keys);
|
|
|
|
/* alloc_block may invalidate pointers if hive expanded. Recalculate this one.
|
|
* Thanks to Jacky To for reporting it here, and suggesting a fix
|
|
* (better would of course be for me to redesign stuff :)
|
|
*/
|
|
if (delnkofs) delnk = (struct nk_key *)(delnkofs + hdesc->buffer + 0x1004);
|
|
|
|
#ifdef DKDEBUG
|
|
printf("del_key: alloc_block for index returns: %x\n",newlfofs);
|
|
#endif
|
|
if (!newlfofs) {
|
|
printf("del_key: WARNING: unable to allocate space for new key descriptor for %s! Not deleted\n",name);
|
|
FREE(newlf);
|
|
return(1);
|
|
}
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
|
|
oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
|
|
|
|
|
|
|
|
/* memcpy(hdesc->buffer + newlfofs + 4,
|
|
((void *)newlf ? (void *)newlf : (void *)newli), 8 + (newlf ? 8 : 4) * no_keys);
|
|
*/
|
|
fill_block(hdesc, newlfofs,
|
|
((void *)newlf ? (void *)newlf : (void *)newli), 8 + (newlf ? 8 : 4) * no_keys);
|
|
|
|
|
|
} else { /* Last deleted, will throw away index */
|
|
newlfofs = 0xfff; /* We subtract 0x1000 later */
|
|
}
|
|
|
|
if (newlfofs < 0xfff) {
|
|
printf("del_key: ERROR: newlfofs = %x\n",newlfofs);
|
|
#if DOCORE
|
|
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
|
|
abort();
|
|
#endif
|
|
}
|
|
|
|
/* Check for CLASS data, if so, deallocate it too */
|
|
if (delnk->len_classnam) {
|
|
free_block(hdesc, delnk->ofs_classnam + 0x1000);
|
|
}
|
|
/* Now it's safe to zap the nk */
|
|
free_block(hdesc, delnkofs + 0x1000);
|
|
/* And the old index list */
|
|
free_block(hdesc, (oldlfofs ? oldlfofs : oldliofs) + 0x1000);
|
|
|
|
/* Update parent */
|
|
key->no_subkeys--;
|
|
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
|
|
oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
|
|
|
|
if (ri) {
|
|
ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004); /* In case realloc */
|
|
|
|
if (newlfofs == 0xfff) {
|
|
|
|
*fullpath = 0;
|
|
get_abs_path(hdesc, nkofs, fullpath, 480);
|
|
|
|
VERBF(hdesc,"del_key: need to delete ri-slot %d for %x - %s\n", rislot,nkofs,fullpath );
|
|
|
|
if (ri->no_lis > 1) { /* We have subindiceblocks left? */
|
|
/* Delete from array */
|
|
ALLOC(newri, 8 + 4*ri->no_lis - 4, 1);
|
|
newri->no_lis = ri->no_lis - 1;
|
|
newri->id = ri->id;
|
|
for (o = 0, n = 0; o < ri->no_lis; o++,n++) {
|
|
if (n == rislot) o++;
|
|
newri->hash[n].ofs_li = ri->hash[o].ofs_li;
|
|
}
|
|
newriofs = alloc_block(hdesc, nkofs, 8 + newri->no_lis*4 );
|
|
if (!newriofs) {
|
|
printf("del_key: WARNING: unable to allocate space for ri-index for %s! Not deleted\n",name);
|
|
FREE(newlf);
|
|
FREE(newri);
|
|
return(1);
|
|
}
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
|
|
oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
|
|
|
|
fill_block(hdesc, newriofs, newri, 8 + newri->no_lis * 4);
|
|
free_block(hdesc, riofs + 0x1000);
|
|
key->ofs_lf = newriofs - 0x1000;
|
|
FREE(newri);
|
|
} else { /* Last entry in ri was deleted, get rid of it, key is empty */
|
|
VERB(hdesc,"del_key: .. and that was the last one. key now empty!\n");
|
|
free_block(hdesc, riofs + 0x1000);
|
|
key->ofs_lf = -1;
|
|
}
|
|
} else {
|
|
ri->hash[rislot].ofs_li = newlfofs - 0x1000;
|
|
}
|
|
} else {
|
|
key->ofs_lf = newlfofs - 0x1000;
|
|
}
|
|
|
|
FREE(newlf);
|
|
return(0);
|
|
|
|
}
|
|
|
|
/* Recursive delete keys
|
|
* hdesc - usual..
|
|
* nkofs - offset of current nk
|
|
* name - name of key to delete
|
|
* return: 0 - ok, 1 fail
|
|
*/
|
|
void rdel_keys(struct hive *hdesc, char *path, int vofs)
|
|
{
|
|
struct nk_key *key;
|
|
int nkofs;
|
|
struct ex_data ex;
|
|
int count = 0, countri = 0;
|
|
|
|
|
|
if (!path || !*path) return;
|
|
|
|
nkofs = trav_path(hdesc, vofs, path, TPF_NK_EXACT);
|
|
|
|
if(!nkofs) {
|
|
printf("rdel_keys: Key <%s> not found\n",path);
|
|
return;
|
|
}
|
|
nkofs += 4;
|
|
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
|
|
/*
|
|
VERBF(hdesc,"rdel of node at offset 0x%0x\n",nkofs);
|
|
*/
|
|
|
|
if (key->id != 0x6b6e) {
|
|
printf("rdel_keys: ERROR: Not a 'nk' node!\n");
|
|
|
|
if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
printf("Node has %d subkeys and %d values\n",key->no_subkeys,key->no_values);
|
|
#endif
|
|
if (key->no_subkeys) {
|
|
while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) {
|
|
#if 0
|
|
printf("%s\n",ex.name);
|
|
#endif
|
|
rdel_keys(hdesc, ex.name, nkofs);
|
|
count = 0;
|
|
countri = 0;
|
|
FREE(ex.name);
|
|
}
|
|
}
|
|
|
|
del_allvalues(hdesc, nkofs);
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
del_key(hdesc, key->ofs_parent+0x1004, path);
|
|
|
|
}
|
|
|
|
|
|
/* Get and copy keys CLASS-data (if any) to buffer
|
|
* Returns a buffer with the data (first int32_t is size). see ntreg.h
|
|
* NOTE: caller must deallocate buffer! a simple free(keyval) will suffice.
|
|
*/
|
|
struct keyval *get_class(struct hive *hdesc,
|
|
int curnk, char *path)
|
|
{
|
|
int clen = 0, dofs = 0, nkofs;
|
|
struct nk_key *key;
|
|
struct keyval *data;
|
|
void *classdata;
|
|
|
|
if (!path && !curnk) return(NULL);
|
|
|
|
nkofs = trav_path(hdesc, curnk, path, 0);
|
|
|
|
if(!nkofs) {
|
|
printf("get_class: Key <%s> not found\n",path);
|
|
return(NULL);
|
|
}
|
|
nkofs += 4;
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
|
|
clen = key->len_classnam;
|
|
if (!clen) {
|
|
printf("get_class: Key has no class data.\n");
|
|
return(NULL);
|
|
}
|
|
|
|
dofs = key->ofs_classnam;
|
|
classdata = (void *)(hdesc->buffer + dofs + 0x1004);
|
|
|
|
#if 0
|
|
printf("get_class: len_classnam = %d\n",clen);
|
|
printf("get_class: ofs_classnam = 0x%x\n",dofs);
|
|
#endif
|
|
|
|
ALLOC(data, sizeof(struct keyval) + clen,1);
|
|
data->len = clen;
|
|
memcpy(&data->data, classdata, clen);
|
|
return(data);
|
|
}
|
|
|
|
|
|
/* Write to registry value.
|
|
* If same size as existing, copy back in place to avoid changing too much
|
|
* otherwise allocate new dataspace, then free the old
|
|
* Thus enough space to hold both new and old data is needed
|
|
* Pass inn buffer with data len as first DWORD (as routines above)
|
|
* returns: 0 - error, len - OK (len of data)
|
|
*/
|
|
|
|
int put_buf2val(struct hive *hdesc, struct keyval *kv,
|
|
int vofs, char *path, int type, int exact )
|
|
{
|
|
int l;
|
|
void *keydataptr, *addr;
|
|
struct db_key *db;
|
|
int copylen, blockofs, blocksize, restlen, point, i, list, parts;
|
|
|
|
if (!kv) return(0);
|
|
|
|
|
|
l = get_val_len(hdesc, vofs, path, exact);
|
|
if (l == -1) return(0); /* error */
|
|
// printf("put_buf2val: l = %d\n",l);
|
|
|
|
// printf("put_buf2val: %s, kv len = %d, l = %d\n",path,kv->len,l);
|
|
|
|
|
|
if (kv->len != l) { /* Realloc data block if not same size as existing */
|
|
if (!alloc_val_data(hdesc, vofs, path, kv->len, exact)) {
|
|
printf("put_buf2val: %s : alloc_val_data failed!\n",path);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
keydataptr = get_val_data(hdesc, vofs, path, type, exact);
|
|
if (!keydataptr) {
|
|
printf("put_buf2val: %s : get_val_data failed!\n",path);
|
|
return(0); /* error */
|
|
}
|
|
|
|
|
|
|
|
if (kv->len > VAL_DIRECT_LIMIT) { /* Where do the db indirects start? seems to be around 16k */
|
|
db = (struct db_key *)keydataptr;
|
|
if (db->id != 0x6264) abort();
|
|
parts = db->no_part;
|
|
list = db->ofs_data + 0x1004;
|
|
printf("put_buf2val: Long value: parts = %d, list = %x\n",parts,list);
|
|
|
|
point = 0;
|
|
restlen = kv->len;
|
|
for (i = 0; i < parts; i++) {
|
|
blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000;
|
|
blocksize = -get_int(hdesc->buffer + blockofs) - 8;
|
|
|
|
/* Copy this part, up to size of block or rest lenght in last block */
|
|
copylen = (blocksize > restlen) ? restlen : blocksize;
|
|
|
|
printf("put_buf2val: Datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize);
|
|
printf(" : Point = %x, restlen = %x, copylen = %x\n",point,restlen,copylen);
|
|
|
|
addr = (void *)&(kv->data) + point;
|
|
fill_block( hdesc, blockofs, addr, copylen);
|
|
|
|
// debugit((char *)&(kr->data), l);
|
|
|
|
point += copylen;
|
|
restlen -= copylen;
|
|
}
|
|
} else {
|
|
memcpy(keydataptr, &kv->data, kv->len);
|
|
}
|
|
|
|
hdesc->state |= HMODE_DIRTY;
|
|
|
|
return(kv->len);
|
|
}
|
|
|
|
/* And, yer basic DWORD write */
|
|
|
|
int put_dword(struct hive *hdesc, int vofs, char *path, int exact, int dword)
|
|
{
|
|
struct keyval *kr;
|
|
int r;
|
|
|
|
ALLOC(kr,1,sizeof(int)+sizeof(int));
|
|
|
|
kr->len = sizeof(int);
|
|
kr->data = dword;
|
|
|
|
r = put_buf2val(hdesc, kr, vofs, path, REG_DWORD, exact);
|
|
|
|
FREE(kr);
|
|
|
|
return(r);
|
|
}
|
|
|
|
/* ================================================================ */
|
|
|
|
/* Code to export registry entries to .reg file initiated by
|
|
* Leo von Klenze
|
|
* Then expanded a bit to handle more types etc.
|
|
*/
|
|
|
|
/*
|
|
* converts a value string from an registry entry into a c string. It does not
|
|
* use any encoding functions.
|
|
* It works very primitive by just taking every second char.
|
|
* The caller must free the resulting string, that was allocated with malloc.
|
|
*
|
|
* string: string where every second char is \0
|
|
* len: length of the string
|
|
* return: the converted string as char*
|
|
*/
|
|
char *
|
|
string_regw2prog(void *string, int len)
|
|
{
|
|
int i, k;
|
|
char *cstring;
|
|
|
|
int out_len = 0;
|
|
for(i = 0; i < len; i += 2)
|
|
{
|
|
unsigned v = ((unsigned char *)string)[i] + ((unsigned char *)string)[i+1] * 256u;
|
|
if (v < 128)
|
|
out_len += 1;
|
|
else if(v < 0x800)
|
|
out_len += 2;
|
|
else
|
|
out_len += 3;
|
|
}
|
|
CREATE(cstring,char,out_len+1);
|
|
|
|
for(i = 0, k = 0; i < len; i += 2)
|
|
{
|
|
unsigned v = ((unsigned char *)string)[i] + ((unsigned char *)string)[i+1] * 256u;
|
|
if (v < 128)
|
|
cstring[k++] = v;
|
|
else if(v < 0x800) {
|
|
cstring[k++] = 0xc0 | (v >> 6);
|
|
cstring[k++] = 0x80 | (v & 0x3f);
|
|
} else {
|
|
cstring[k++] = 0xe0 | (v >> 12);
|
|
cstring[k++] = 0x80 | ((v >> 6) & 0x3f);
|
|
cstring[k++] = 0x80 | (v & 0x3f);
|
|
}
|
|
}
|
|
cstring[out_len] = '\0';
|
|
|
|
return cstring;
|
|
}
|
|
|
|
#if 0 // Not used at the moment
|
|
static char *
|
|
string_rega2prog(void *string, int len)
|
|
{
|
|
int i, k;
|
|
char *cstring;
|
|
|
|
int out_len = 0;
|
|
for(i = 0; i < len; ++i)
|
|
{
|
|
unsigned v = ((unsigned char *)string)[i];
|
|
if (v < 128)
|
|
out_len += 1;
|
|
else
|
|
out_len += 2;
|
|
}
|
|
CREATE(cstring,char,out_len+1);
|
|
|
|
for(i = 0, k = 0; i < len; ++i)
|
|
{
|
|
unsigned v = ((unsigned char *)string)[i];
|
|
if (v < 128)
|
|
cstring[k++] = v;
|
|
else {
|
|
cstring[k++] = 0xc0 | (v >> 6);
|
|
cstring[k++] = 0x80 | (v & 0x3f);
|
|
}
|
|
}
|
|
cstring[out_len] = '\0';
|
|
|
|
return cstring;
|
|
}
|
|
|
|
static void
|
|
string_prog2rega(char *string, int len)
|
|
{
|
|
char *out = string;
|
|
unsigned char *in = (unsigned char*) string;
|
|
|
|
for (;*in; ++in) {
|
|
if (!(*in & 0x80)) {
|
|
*out++ = *in;
|
|
} else if ((in[0] & 0xe0) == 0xc0 && in[1]) {
|
|
*out++ = (in[0] & 0x1f) << 6 | (in[1] & 0x3f);
|
|
++in;
|
|
} else if (in[1] && in[2]) {
|
|
/* assume 3 byte*/
|
|
*out++ = (in[1] & 0xf) << 6 | (in[2] & 0x3f);
|
|
in += 2;
|
|
}
|
|
}
|
|
*out = 0;
|
|
}
|
|
#endif // Not used
|
|
|
|
|
|
static char *
|
|
string_prog2regw(void *string, int len, int *out_len)
|
|
{
|
|
unsigned char *regw = (unsigned char*) malloc(len*2+2);
|
|
unsigned char *out = regw;
|
|
unsigned char *in = (unsigned char*) string;
|
|
|
|
for (;len>0; ++in, --len) {
|
|
if (!(in[0] & 0x80)) {
|
|
*out++ = *in;
|
|
*out++ = 0;
|
|
} else if ((in[0] & 0xe0) == 0xc0 && len >= 2) {
|
|
*out++ = (in[0] & 0x1f) << 6 | (in[1] & 0x3f);
|
|
*out++ = (in[0] & 0x1f) >> 2;
|
|
++in, --len;
|
|
} else if (len >= 3) {
|
|
/* assume 3 byte*/
|
|
*out++ = (in[1] & 0xf) << 6 | (in[2] & 0x3f);
|
|
*out++ = (in[0] & 0xf) << 4 | ((in[1] & 0x3f) >> 2);
|
|
in += 2;
|
|
len -= 2;
|
|
}
|
|
}
|
|
*out_len = out - regw;
|
|
out[0] = out[1] = 0;
|
|
return (char *) regw;
|
|
}
|
|
|
|
static char *
|
|
quote_string(const char *s)
|
|
{
|
|
int len = strlen(s);
|
|
const char *p;
|
|
char *dst, *out;
|
|
|
|
for (p = s; *p; ++p)
|
|
if (*p == '\\' || *p == '\"')
|
|
++len;
|
|
|
|
dst = out = (char*) malloc(len + 1);
|
|
for (p = s; *p; ++p) {
|
|
if (*p == '\\' || *p == '\"')
|
|
*dst++ = '\\';
|
|
*dst++ = *p;
|
|
}
|
|
*dst = 0;
|
|
return out;
|
|
}
|
|
|
|
static void
|
|
export_bin(int type, char *value, int len, int col, FILE* file)
|
|
{
|
|
int byte;
|
|
|
|
if (type == REG_BINARY) {
|
|
fprintf(file, "hex:");
|
|
col += 4;
|
|
} else {
|
|
fprintf(file, "hex(%x):", type);
|
|
col += 7;
|
|
}
|
|
byte = 0;
|
|
int start = (col - 2) / 3;
|
|
while (byte + 1 < len) { /* go byte by byte.. probably slow.. */
|
|
fprintf(file, "%02x,", (unsigned char)value[byte]);
|
|
byte++;
|
|
if (!((byte + start) % 25)) fprintf(file, "\\\r\n ");
|
|
}
|
|
if (len)
|
|
fprintf(file, "%02x\r\n", (unsigned char)value[len - 1]);
|
|
}
|
|
|
|
/*
|
|
* Exports the named subkey and its values to the given file.
|
|
*
|
|
* hdesc: registry hive
|
|
* nkofs: offset of parent node
|
|
* name: name of key to export
|
|
* prefix: prefix for each key. This prefix is prepended to the keyname
|
|
* file: file descriptor of destination file
|
|
*/
|
|
void export_subkey(struct hive *hdesc, int nkofs, char *name, char *prefix, FILE *file)
|
|
{
|
|
int newofs;
|
|
int count = 0;
|
|
int countri = 0;
|
|
int len;
|
|
char *path = (char*) malloc(1024);
|
|
char *value;
|
|
struct nk_key *key;
|
|
struct ex_data ex;
|
|
struct vex_data vex;
|
|
|
|
|
|
// switch to key
|
|
newofs = trav_path(hdesc, nkofs, name, TPF_NK_EXACT);
|
|
if(!newofs)
|
|
{
|
|
printf("export_subkey: Key '%s' not found!\n", name);
|
|
free(path);
|
|
return;
|
|
}
|
|
nkofs = newofs + 4;
|
|
|
|
// get the key
|
|
key = (struct nk_key *)(hdesc->buffer + nkofs);
|
|
printf("Exporting key '%.*s' with %d subkeys and %d values...\n",
|
|
key->len_name, key->keyname, key->no_subkeys, key->no_values);
|
|
|
|
*path = 0;
|
|
get_abs_path(hdesc, nkofs, path, 1024);
|
|
|
|
// export the key
|
|
fprintf(file, "\r\n");
|
|
fprintf(file, "[%s\%s]\r\n", prefix, path);
|
|
// export values
|
|
if(key->no_values)
|
|
{
|
|
while ((ex_next_v(hdesc, nkofs, &count, &vex) > 0))
|
|
{
|
|
int col = 0;
|
|
char *name = quote_string(vex.name);
|
|
|
|
/* print name */
|
|
if (!name[0]) {
|
|
fprintf(file, "@=");
|
|
free(name);
|
|
name = str_dup("@");
|
|
col += 2;
|
|
} else {
|
|
fprintf(file, "\"%s\"=", name);
|
|
col += strlen(name) + 3;
|
|
}
|
|
|
|
if(vex.type == REG_DWORD)
|
|
{
|
|
fprintf(file, "dword:%08x\r\n", vex.val);
|
|
}
|
|
else if(vex.type == REG_SZ)
|
|
{
|
|
char *val, *quoted;
|
|
value = (char *)get_val_data(hdesc, nkofs, name, vex.type, TPF_VK_EXACT|TPF_ABS);
|
|
len = get_val_len(hdesc, nkofs, name, TPF_VK_EXACT|TPF_ABS);
|
|
|
|
val = string_regw2prog(value, len);
|
|
/* dump as binary if invalid characters are embedded */
|
|
if (strchr(val, 0xa) || strchr(val, 0xd))
|
|
{
|
|
free(val);
|
|
//if (len >= 2 && value[len-2] == 0 && value[len-1] == 0) len -= 2;
|
|
export_bin(vex.type, value, len, col, file);
|
|
}
|
|
else
|
|
{
|
|
quoted = quote_string(val);
|
|
free(val);
|
|
fprintf(file, "\"%s\"", quoted);
|
|
free(quoted);
|
|
fprintf(file, "\r\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* All other types seems to simply be hexdumped. Format is:
|
|
"valuename"=hex(typenum):xx,xx,xx,xx,xx...
|
|
for example:
|
|
"qword64"=hex(b):4e,03,51,db,fa,04,00,00
|
|
this is type = 0xb = 11 = REG_QWORD
|
|
"expandstring"=hex(2):46,00,6f,00,6f,00,62,00,61,00,72,00,20,00,25,00,73,00,20,\
|
|
00,42,00,61,00,7a,00,00,00
|
|
this is type 2 = REG_EXPAND_SZ and the line is continued with ,\<CR><LF><space><space>
|
|
don't know how many bytes for each line. Around 18-24 seems to be it?? depends on name lenght??
|
|
NOTE: Exception:
|
|
type = REG_BINARY starts like this: "valuename"=hex:xx,xx,xx...
|
|
*/
|
|
value = (char *)get_val_data(hdesc, nkofs, name, vex.type, TPF_VK_EXACT|TPF_ABS);
|
|
len = get_val_len(hdesc, nkofs, name, TPF_VK_EXACT|TPF_ABS);
|
|
|
|
export_bin(vex.type, value, len, col, file);
|
|
}
|
|
|
|
FREE(vex.name);
|
|
free(name);
|
|
}
|
|
}
|
|
|
|
// export subkeys
|
|
if (key->no_subkeys)
|
|
{
|
|
count = 0;
|
|
countri = 0;
|
|
while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0))
|
|
{
|
|
export_subkey(hdesc, nkofs, ex.name, prefix, file);
|
|
FREE(ex.name);
|
|
}
|
|
}
|
|
free(path);
|
|
}
|
|
|
|
/*
|
|
* Exports the given key to a windows .reg file that can be imported to the
|
|
* windows registry.
|
|
* The prefix is used to determine the destination hive in the windows
|
|
* registry, set it to HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE or whatever you
|
|
* want.
|
|
*
|
|
* hdesc: hive
|
|
* nkofs: offset of parent node
|
|
* name: name of subkey to export
|
|
* filename: name of destination .reg file (will be overridden)
|
|
* prefix: prefix for each exported key
|
|
*/
|
|
void export_key(struct hive *hdesc, int nkofs, char *name, char *filename, char *prefix)
|
|
{
|
|
FILE *file;
|
|
|
|
// open file
|
|
file = fopen(filename, "w");
|
|
if(!file)
|
|
{
|
|
printf("export: Cannot open file '%s'. %s (%d).\n", filename, strerror(errno),
|
|
errno);
|
|
return;
|
|
}
|
|
|
|
printf("Exporting to file '%s'...\n", filename);
|
|
fprintf(file, "Windows Registry Editor Version 5.00\r\n");
|
|
export_subkey(hdesc, nkofs, name, prefix, file);
|
|
|
|
fprintf(file,"\r\n"); /* Must end file with an empty line, windows does that */
|
|
|
|
fclose(file);
|
|
}
|
|
|
|
/* ================================================================ */
|
|
|
|
/* Import from .reg file routines */
|
|
|
|
#define MAXLINE 20000
|
|
|
|
/* Wide character fgetsw() may not be available on all small libraries..
|
|
* so.. roll our own fgets() that handles wide if needed
|
|
*/
|
|
|
|
char *my_fgets(char *s, char *w, int max, FILE *file, int wide)
|
|
{
|
|
|
|
int i = 0;
|
|
char prev = 1;
|
|
char c = 1;
|
|
|
|
|
|
while (c != '\n' && !feof(file) && max) {
|
|
c = (char)fgetc(file);
|
|
/* printf("char = %c\n",c); */
|
|
if (!c && (!prev && !wide) ) break; /* Stop on 1 (or 2 if wide) null */
|
|
prev = c;
|
|
if (c != '\r') {
|
|
*(s+i) = c;
|
|
*(w+i) = c;
|
|
i++;
|
|
}
|
|
max--;
|
|
}
|
|
*(s+i) = 0;
|
|
*(s+i+1) = 0;
|
|
*(w+i) = 0;
|
|
*(w+i+1) = 0;
|
|
|
|
if (wide) { /* Convert to C string, de widing it.. */
|
|
cheap_uni2ascii(w, s, i);
|
|
// printf("cheap returns len = %d : %s\n",strlen(s), s);
|
|
|
|
fgetc(file); /* Skip second byte of CR/LF termination */
|
|
}
|
|
|
|
// printf("my_fgets returning :\n");
|
|
//hexdump(w, 0, i, 1);
|
|
//printf("====== hexdump end\n");
|
|
return(s);
|
|
|
|
}
|
|
|
|
|
|
/* Read one line, while skipping blank lines, also checks for =
|
|
* line = line buffer
|
|
* file = you know..
|
|
* assigner - left part of value assignemt (before =)
|
|
* value - right part of assignment (after =)
|
|
* value = NULL if [KEY] line.
|
|
* assigner = NULL if value continuation line
|
|
* Returns total lenght of line
|
|
*/
|
|
|
|
|
|
#undef GETLINE_DEBUG
|
|
|
|
int get_line(char s[], char w[], FILE *file, char **assigner, char **value, int wide)
|
|
{
|
|
int l,q;
|
|
char *b; /* Start of line after spaces */
|
|
char *c;
|
|
|
|
*assigner = NULL;
|
|
*value = NULL;
|
|
|
|
do {
|
|
|
|
s[0] = 0;
|
|
my_fgets(s, w, MAXLINE, file, wide);
|
|
s[MAXLINE] = 0;
|
|
b = s;
|
|
|
|
l = strlen(s);
|
|
|
|
#ifdef GETLINE_DEBUG
|
|
printf("get_line: read line len %d : %s\n",l,s);
|
|
#endif
|
|
|
|
if (l == 0) {
|
|
if (feof(file)) return 0;
|
|
break;
|
|
}
|
|
|
|
while (isspace(*b)) b++;
|
|
c = b;
|
|
while (*c && *c != '\n' && *c != '\r') c++;
|
|
*c = 0; /* Terminate with null not cr/lf */
|
|
|
|
|
|
if (!*b) continue; /* Blank line */
|
|
|
|
l = strlen(s);
|
|
|
|
#ifdef GETLINE_DEBUG
|
|
printf("get_line: stripped line len %d : %s\n",l,s);
|
|
#endif
|
|
|
|
|
|
c = b;
|
|
|
|
if (*b == '[') { /* Key line starts with [ */
|
|
#ifdef GETLINE_DEBUG
|
|
printf("get_line: key line..\n");
|
|
#endif
|
|
while (*c && (*c != ']')) c++;
|
|
if (!*c) {
|
|
printf("get_line: WARNING: un-terminated key line..\n");
|
|
}
|
|
*c = 0;
|
|
*assigner = b+1;
|
|
*value = NULL;
|
|
return(l);
|
|
}
|
|
|
|
q = 0;
|
|
while (*c) {
|
|
/* printf(" char = %c : q = %d\n",*c,q); */
|
|
if (*c == '"') q ^= 1; /* Flip quote indicator */
|
|
if (*c == '=' && !q) { /* Found = outside quotes */
|
|
*c = 0;
|
|
*assigner = b;
|
|
*value = c + 1;
|
|
#ifdef GETLINE_DEBUG
|
|
printf("get_line: value line\n");
|
|
#endif
|
|
return(l);
|
|
}
|
|
c++;
|
|
}
|
|
/* At this point we don't have a = outside quotes, so probably a value cont line */
|
|
*assigner = NULL;
|
|
*value = b;
|
|
#ifdef GETLINE_DEBUG
|
|
printf("get_line: cont line\n");
|
|
#endif
|
|
return(l);
|
|
|
|
} while (!feof(file));
|
|
|
|
return(l);
|
|
}
|
|
|
|
|
|
/* Wide strlen */
|
|
int wide_strlen(char *s)
|
|
{
|
|
|
|
int l = 0;
|
|
|
|
while ( *(s+l) || *(s+l+1) ) l += 2;
|
|
|
|
return(l);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* De-quote a string (removing quotes first and last)
|
|
* Does nothing if no quoutes
|
|
* String MUST be null-terminated
|
|
*/
|
|
|
|
char *dequote(char *s)
|
|
{
|
|
int l;
|
|
|
|
if (*s != '"') return(s); /* No first quote, then don't change anything, even at end */
|
|
|
|
l = strlen(s);
|
|
|
|
if ( *(s+l-1) == '"' ) {
|
|
*(s+l-1) = 0;
|
|
}
|
|
|
|
return(s+1);
|
|
}
|
|
|
|
/* de-escape a string, handling \ backslash
|
|
* s = string buffer, WILL BE CHANGED
|
|
* wide = true to make it handle wide characters
|
|
* returns new lenght
|
|
*/
|
|
|
|
int de_escape(char *s, int wide)
|
|
{
|
|
int src = 0;
|
|
int dst = 0;
|
|
|
|
if (wide) {
|
|
while ( *(s + src) || *(s + src +1)) {
|
|
if ( *(s + src) == '\\' && *(s + src + 1) == 0) src += 2; /* Skip over backslash */
|
|
*(s + dst) = *(s + src);
|
|
*(s + dst + 1) = *(s + src + 1);
|
|
dst += 2;
|
|
src += 2;
|
|
}
|
|
*(s + dst) = 0;
|
|
*(s + dst + 1) = 0;
|
|
dst += 2;
|
|
} else {
|
|
while ( *(s + src) ) {
|
|
if ( *(s + src) == '\\' ) src++;
|
|
*(s + dst) = *(s + src);
|
|
dst++;
|
|
src++;
|
|
}
|
|
*(s + dst) = 0;
|
|
dst++;
|
|
}
|
|
|
|
return(dst);
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parse the value to be assigned
|
|
* s = string to parse
|
|
* bufptr = pointer to buffer pointer for binary data (will be allocated).
|
|
* First byte of buffer is value type.
|
|
* returns lenght
|
|
*
|
|
"stringvalue"="this is a string"
|
|
"binaryvalue"=hex:11,22,33,aa,bb,cc,12,34,01,02,ab,cd,ef,be,ef
|
|
"dwordvalue"=dword:12345678
|
|
"qwordvalue"=hex(b):ef,cd,ab,89,67,45,23,01
|
|
"multistringvalue"=hex(7):73,00,74,00,72,00,69,00,6e,00,67,00,20,00,31,00,00,\
|
|
00,73,00,74,00,72,00,69,00,6e,00,67,00,20,00,32,00,00,00,73,00,74,00,72,00,\
|
|
69,00,6e,00,67,00,20,00,33,00,00,00,00,00
|
|
"expstringvalue"=hex(2):73,00,74,00,72,00,69,00,6e,00,67,00,20,00,77,00,69,00,\
|
|
74,00,68,00,20,00,25,00,73,00,20,00,65,00,78,00,70,00,61,00,6e,00,73,00,69,\
|
|
00,6f,00,6e,00,00,00
|
|
*
|
|
*/
|
|
|
|
int parse_valuestring(char *s, char *w, int len, int wide, struct keyval **kvptr)
|
|
{
|
|
unsigned int dword;
|
|
int type = -1;
|
|
int i;
|
|
int quote;
|
|
int strstart;
|
|
uint8_t byte;
|
|
uint8_t *array;
|
|
char *widebuf = NULL;
|
|
struct keyval *kv = NULL;
|
|
|
|
|
|
// printf("parse_val: input string: <%s>\n",s);
|
|
|
|
if (!strncmp(s,"dword",4)) { /* DWORD */
|
|
sscanf(s,"dword:%x",&dword);
|
|
// printf("parse_vals: dword is %x\n",dword);
|
|
type = REG_DWORD;
|
|
len = 4;
|
|
ALLOC(kv,1,len + 8);
|
|
kv->data = dword;
|
|
|
|
} else if (!strncmp(s,"hex",3)) { /* Hex string */
|
|
if (!sscanf(s,"hex(%x):",&type)) type = REG_BINARY;
|
|
|
|
// printf("parse_vals: hex type is %d\n",type);
|
|
|
|
while (*s && *s != ':') s++; /* Move up to : */
|
|
s++;
|
|
len = strlen(s);
|
|
if (len > 0) len = len / 3 + 1; /* 3 characters per byte */
|
|
|
|
// printf("parse_vals: hex byte count %d\n", len);
|
|
|
|
ALLOC(kv,1,len + 8);
|
|
array = (uint8_t *)&kv->data;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (!sscanf(s,"%hhx",&byte)) {
|
|
fprintf(stderr,"parse_values: hex string parse error: %s\n",s);
|
|
abort();
|
|
}
|
|
// printf("parse_vals: adding byte: %02x\n",byte);
|
|
*(array+i) = byte;
|
|
s += 3;
|
|
}
|
|
|
|
} else { /* Assume it simply is a string */
|
|
type = REG_SZ;
|
|
|
|
if (wide) { /* File is wide, find string limits and de-escape it, then copy in as wide */
|
|
|
|
quote = 0;
|
|
i = 0;
|
|
strstart = 0;
|
|
while ( *(w+i) ) {
|
|
if ( *(w+i) == '"' && *(w+i+1) == 0) quote ^= 1;
|
|
if (!quote && !strstart && ( *(w+i) == '=' && *(w+i+1) == 0) ) {
|
|
strstart = i + 4; /* Skip past start quote */
|
|
}
|
|
i += 2;
|
|
}
|
|
if ( *(w+i-2) == '"' && *(w+i-1) == 0) { /* Remove end quoute */
|
|
*(w+i-2) = 0;
|
|
*(w+i-1) = 0;
|
|
i -= 2;
|
|
}
|
|
i += 2;
|
|
|
|
|
|
// hexdump(w+strstart,0,i-strstart,1);
|
|
|
|
len = de_escape(w + strstart, wide);
|
|
|
|
// hexdump(w+strstart,0,len,1);
|
|
|
|
// printf("wide string: i = %d, strstart = %d, len = %d\n",i,strstart,len);
|
|
|
|
ALLOC(kv,1,len + 8);
|
|
|
|
memcpy(&kv->data,w + strstart, len);
|
|
|
|
} else { /* File is not wide, so we must widen string before putting into registry */
|
|
|
|
len = strlen(s);
|
|
// printf("parse_vals: len %d string <%s>\n",len,s);
|
|
len = de_escape(s, 0);
|
|
// printf("parse_vals: after de-escape len %d string <%s>\n",len,s);
|
|
|
|
widebuf = string_prog2regw(s, strlen(s), &len);
|
|
len += 2; /* Also store the terminating NULLs */
|
|
// printf("parse_vals: len after wide expansion: %d\n",len);
|
|
|
|
ALLOC(kv,1,len + 8);
|
|
memcpy(&kv->data,widebuf,len);
|
|
|
|
FREE(widebuf);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if (kv) {
|
|
kv->len = len;
|
|
*kvptr = kv;
|
|
}
|
|
|
|
return(type);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Main import routine
|
|
* Only reading into one hive at a time supported.
|
|
* Will support 8 bit or 16 bit (wide) character .reg file.
|
|
* Windows regedit.exe usually generates 16 bit.
|
|
* NOTE: Not a lot of sanity checking, a broken input file may end in strange behaviour or corrupt registry!
|
|
* hdesc - Loaded hive
|
|
* filename - (path)name of .reg file to read
|
|
* prefix - HKEY_
|
|
*/
|
|
|
|
void import_reg(struct hive *hdesc, char *filename, char *prefix)
|
|
{
|
|
|
|
FILE *file;
|
|
char line[MAXLINE+2];
|
|
char wline[MAXLINE+2]; /* Wide buffer */
|
|
int l, plen;
|
|
char *assigner = NULL;
|
|
char *value = NULL;
|
|
int wide = 0;
|
|
int c,wl;
|
|
// void *valbuf;
|
|
char *valname = NULL;
|
|
char *plainname = NULL;
|
|
char *valstr, *key, *walstr = NULL;
|
|
int nk, prevnk; /* offsets to nk */
|
|
int type,oldtype;
|
|
struct keyval *valbinbuf = NULL;
|
|
int numkeys = 0;
|
|
int numkeyadd = 0;
|
|
int numtotvals = 0;
|
|
int numkeyvals = 0;
|
|
int bailout = 0;
|
|
|
|
plen = strlen(prefix);
|
|
|
|
file = fopen(filename, "r");
|
|
if(!file)
|
|
{
|
|
fprintf(stderr,"import_reg: Cannot open file '%s'. %s (%d).\n", filename, strerror(errno),
|
|
errno);
|
|
return;
|
|
}
|
|
|
|
c = fgetc(file);
|
|
|
|
if (c == 0xff) { /* Wide characters file */
|
|
fprintf(stderr,"import_reg: WARNING: Wide character (16 bit) file..\n"
|
|
"import_reg: WARNING: Implementation is not 100%% accurate, some things may not import correctly!\n");
|
|
c = fgetc(file); /* Get second wide indicator character */
|
|
wide = 1;
|
|
} else {
|
|
ungetc(c, file);
|
|
}
|
|
|
|
line[0] = 0;
|
|
my_fgets(line, wline, MAXLINE, file, wide);
|
|
line[MAXLINE] = 0;
|
|
|
|
|
|
if (strncmp("Windows Registry Editor",line,23)) {
|
|
fprintf(stderr,"import_reg: ERROR: Windows Registry Editor signature missing on first line\n");
|
|
fclose(file);
|
|
return;
|
|
}
|
|
|
|
do {
|
|
|
|
l = get_line(line, wline, file, &assigner, &value, wide);
|
|
|
|
if (!feof(file) && !assigner && value) {
|
|
// printf("import_reg: value continuation line: %s\n",value);
|
|
l = strlen(value);
|
|
if ( *(value+l-1) == '\\' ) { /* Strip off cont character */
|
|
*(value+l-1) = 0;
|
|
l--;
|
|
}
|
|
value = dequote(value);
|
|
l = strlen(value);
|
|
valstr = realloc(valstr, strlen(valstr) + l + 3);
|
|
if (valstr == NULL) {
|
|
perror("import_reg: realloc(valstr)");
|
|
abort();
|
|
}
|
|
strcat(valstr, value);
|
|
// printf("import_reg: build value string: %s\n",valstr);
|
|
if (wide) {
|
|
wl = wide_strlen(wline);
|
|
walstr = realloc(walstr, wide_strlen(walstr) + wl + 4);
|
|
if (walstr == NULL) {
|
|
perror("import_reg: realloc(valstr)");
|
|
abort();
|
|
}
|
|
memcpy(walstr + wl, wline, wl);
|
|
}
|
|
|
|
|
|
|
|
} else { /* End continuation, store built up value */
|
|
if (valname) {
|
|
// printf("import_reg: end of value %s, result string: %s\n\n",valname,valstr);
|
|
|
|
type = parse_valuestring(valstr, walstr, l, wide, &valbinbuf);
|
|
|
|
// printf("import_reg: got value type = %d\n",type);
|
|
// printf("import_reg: data lenght = %d\n",(*valbinbuf).len);
|
|
|
|
VERBF(hdesc," Value <%s> of type %d length %d",valname,type,(*valbinbuf).len);
|
|
|
|
oldtype = get_val_type(hdesc, nk + 4, valname, TPF_VK_ABS|TPF_EXACT);
|
|
|
|
|
|
if (oldtype == -1) {
|
|
// printf("Value <%s> not found, creating it new\n",valname);
|
|
plainname = str_dup(valname);
|
|
de_escape(plainname,0);
|
|
// printf("de-escaped to <%s> creating it new\n",plainname);
|
|
add_value(hdesc, nk + 4, plainname, type);
|
|
oldtype = get_val_type(hdesc, nk + 4, valname, TPF_VK_ABS|TPF_EXACT);
|
|
FREE(plainname);
|
|
VERB(hdesc," [CREATED]");
|
|
|
|
}
|
|
|
|
if (oldtype != type) {
|
|
fprintf(stderr,"ERROR: import_reg: unable to change value <%s>, new type is %d while old is %d\n",valname,type,oldtype);
|
|
bailout = 1;
|
|
} else {
|
|
|
|
VERB(hdesc,"\n");
|
|
put_buf2val(hdesc, valbinbuf, nk + 4, valname, type, TPF_VK_ABS|TPF_EXACT);
|
|
|
|
numkeyvals++;
|
|
numtotvals++;
|
|
|
|
FREE(valbinbuf);
|
|
FREE(valstr);
|
|
FREE(valname);
|
|
FREE(walstr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (assigner && !value) { /* Its a key line */
|
|
//printf("import_reg: read key name: %s\n",assigner);
|
|
if ( !strncmp(assigner,prefix,plen)) { /* Check and strip of prefix of key name */
|
|
assigner += plen;
|
|
} else {
|
|
fprintf(stderr,"import_reg: WARNING: found key <%s> not matching prefix <%s>\n",assigner,prefix);
|
|
abort();
|
|
}
|
|
|
|
if (numkeys) {
|
|
if (hdesc->state & HMODE_VERBOSE)
|
|
printf("--- END of key, with %d values\n",numkeyvals);
|
|
else
|
|
printf(" with %d values.\n",numkeyvals);
|
|
numkeyvals = 0;
|
|
}
|
|
|
|
printf("--- Import KEY <%s> ",assigner);
|
|
numkeys++;
|
|
|
|
key = strtok(assigner,"\\");
|
|
prevnk = hdesc->rootofs;
|
|
while (key) {
|
|
nk = trav_path(hdesc, prevnk + 4, key, TPF_NK_EXACT);
|
|
if (!nk) {
|
|
if (!add_key(hdesc, prevnk + 4, key)) {
|
|
fprintf(stderr,"\nERROR: import_reg: failed to add (sub)key <%s>\n",key);
|
|
bailout = 1;
|
|
} else {
|
|
printf(" [added <%s>] ",key);
|
|
nk = trav_path(hdesc, prevnk + 4, key, TPF_NK_EXACT);
|
|
numkeyadd++;
|
|
}
|
|
}
|
|
prevnk = nk;
|
|
key = strtok(NULL,"\\");
|
|
}
|
|
fflush(stdout);
|
|
VERB(hdesc,"\n");
|
|
}
|
|
|
|
|
|
if (assigner && value) {
|
|
// printf("import_reg: value assignment line: %s = %s\n",assigner,value);
|
|
valname = str_dup(dequote(assigner));
|
|
if (wide) {
|
|
FREE(walstr);
|
|
ALLOC(walstr, 1, wide_strlen(wline));
|
|
memcpy(walstr, wline, wide_strlen(wline));
|
|
}
|
|
|
|
l = strlen(value);
|
|
if ( *(value+l-1) == '\\' ) { /* Strip off cont character */
|
|
*(value+l-1) = 0;
|
|
l--;
|
|
}
|
|
value = dequote(value);
|
|
valstr = str_dup(value);
|
|
// valbuf = NULL;
|
|
//printf("import_reg: val name = %s\n",valname);
|
|
//printf("import_reg: val str = %s\n",valstr);
|
|
|
|
}
|
|
|
|
|
|
} while (!feof(file) && !bailout);
|
|
|
|
|
|
printf("\nEND OF IMPORT, file <%s>, operation %s!\n", filename, (bailout ? "FAILED" : "SUCCEEDED"));
|
|
printf("%d keys\n",numkeys);
|
|
printf("%d new keys added\n",numkeyadd);
|
|
printf("%d values total\n\n",numtotvals);
|
|
fclose(file);
|
|
|
|
if (bailout) hdesc->state &= ~HMODE_DIRTY; /* Don't save if error. Or should we? */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ================================================================ */
|
|
|
|
/* Hive control (load/save/close) etc */
|
|
|
|
void closeHive(struct hive *hdesc)
|
|
{
|
|
|
|
// printf("closing hive %s\n",hdesc->filename);
|
|
if (hdesc->state & HMODE_OPEN) {
|
|
close(hdesc->filedesc);
|
|
}
|
|
FREE(hdesc->filename);
|
|
FREE(hdesc->buffer);
|
|
FREE(hdesc);
|
|
|
|
}
|
|
|
|
|
|
/* Compute checksum of REGF header page
|
|
* hdesc = hive
|
|
* returns checksum value, 32 bit int
|
|
*/
|
|
|
|
int32_t calc_regfsum(struct hive *hdesc)
|
|
{
|
|
int32_t checksum = 0;
|
|
struct regf_header *hdr;
|
|
int i;
|
|
|
|
hdr = (struct regf_header *) hdesc->buffer;
|
|
|
|
for (i = 0; i < 0x1fc/4; ++i)
|
|
checksum ^= ((int32_t *) hdr)[i];
|
|
|
|
return(checksum);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write the hive back to disk (only if dirty & not readonly) */
|
|
|
|
int writeHive(struct hive *hdesc)
|
|
{
|
|
int len;
|
|
struct regf_header *hdr;
|
|
|
|
if (hdesc->state & HMODE_RO) return(0);
|
|
if ( !(hdesc->state & HMODE_DIRTY)) return(0);
|
|
|
|
if ( !(hdesc->state & HMODE_OPEN)) { /* File has been closed */
|
|
if (!(hdesc->filedesc = open(hdesc->filename,O_RDWR))) {
|
|
fprintf(stderr,"writeHive: open(%s) failed: %s, FILE NOT WRITTEN!\n",hdesc->filename,strerror(errno));
|
|
return(1);
|
|
}
|
|
hdesc->state |= HMODE_OPEN;
|
|
}
|
|
/* Seek back to begginning of file (in case it's already open) */
|
|
lseek(hdesc->filedesc, 0, SEEK_SET);
|
|
|
|
/* compute new checksum */
|
|
|
|
hdr = (struct regf_header *) hdesc->buffer;
|
|
|
|
hdr->checksum = calc_regfsum(hdesc);
|
|
|
|
|
|
len = write(hdesc->filedesc, hdesc->buffer, hdesc->size);
|
|
if (len != hdesc->size) {
|
|
fprintf(stderr,"writeHive: write of %s failed: %s.\n",hdesc->filename,strerror(errno));
|
|
return(1);
|
|
}
|
|
|
|
hdesc->state &= (~HMODE_DIRTY);
|
|
return(0);
|
|
}
|
|
|
|
#undef LOAD_DEBUG
|
|
|
|
struct hive *openHive(char *filename, int mode)
|
|
{
|
|
|
|
struct hive *hdesc;
|
|
int fmode,r,vofs;
|
|
struct stat sbuf;
|
|
uint32_t pofs;
|
|
int32_t checksum;
|
|
char *c;
|
|
int rt;
|
|
struct hbin_page *p;
|
|
struct regf_header *hdr;
|
|
struct nk_key *nk;
|
|
struct ri_key *rikey;
|
|
|
|
int verbose = (mode & HMODE_VERBOSE);
|
|
int trace = (mode & HMODE_TRACE) ? 1 : 0;
|
|
int info = (mode & HMODE_INFO);
|
|
|
|
if (!filename || !*filename) return(NULL);
|
|
|
|
CREATE(hdesc,struct hive,1);
|
|
|
|
hdesc->filename = str_dup(filename);
|
|
hdesc->state = 0;
|
|
hdesc->size = 0;
|
|
hdesc->buffer = NULL;
|
|
|
|
if ( (mode & HMODE_RO) ) {
|
|
fmode = O_RDONLY;
|
|
} else {
|
|
fmode = O_RDWR;
|
|
}
|
|
|
|
/* Some non-unix platforms may need this. Thanks to Dan Schmidt */
|
|
#ifdef O_BINARY
|
|
fmode |= O_BINARY;
|
|
#endif
|
|
|
|
hdesc->filedesc = open(hdesc->filename,fmode);
|
|
if (hdesc->filedesc < 0) {
|
|
fprintf(stderr,"openHive(%s) failed: %s, trying read-only\n",hdesc->filename,strerror(errno));
|
|
fmode = O_RDONLY;
|
|
mode |= HMODE_RO;
|
|
hdesc->filedesc = open(hdesc->filename,fmode);
|
|
if (hdesc->filedesc < 0) {
|
|
fprintf(stderr,"openHive(%s) in fallback RO-mode failed: %s\n",hdesc->filename,strerror(errno));
|
|
closeHive(hdesc);
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
|
|
if ( fstat(hdesc->filedesc,&sbuf) ) {
|
|
perror("stat()");
|
|
exit(1);
|
|
}
|
|
|
|
hdesc->size = sbuf.st_size;
|
|
hdesc->state = mode | HMODE_OPEN;
|
|
|
|
/* Read the whole file */
|
|
|
|
ALLOC(hdesc->buffer,1,hdesc->size);
|
|
|
|
rt = 0;
|
|
do { /* On some platforms read may not block, and read in chunks. handle that */
|
|
r = read(hdesc->filedesc, hdesc->buffer + rt, hdesc->size - rt);
|
|
rt += r;
|
|
} while ( !errno && (rt < hdesc->size) );
|
|
|
|
if (errno) {
|
|
perror("openHive(): read error: ");
|
|
closeHive(hdesc);
|
|
return(NULL);
|
|
}
|
|
if (rt < hdesc->size) {
|
|
fprintf(stderr,"Could not read file, got %d bytes while expecting %d\n",
|
|
r, hdesc->size);
|
|
closeHive(hdesc);
|
|
return(NULL);
|
|
}
|
|
|
|
/* Now run through file, tallying all pages */
|
|
/* NOTE/KLUDGE: Assume first page starts at offset 0x1000 */
|
|
|
|
pofs = 0x1000;
|
|
|
|
hdr = (struct regf_header *)hdesc->buffer;
|
|
if (hdr->id != 0x66676572) {
|
|
fprintf(stderr,"openHive(%s): File does not seem to be a registry hive!\n",filename);
|
|
return(hdesc);
|
|
}
|
|
|
|
checksum = calc_regfsum(hdesc);
|
|
|
|
#ifdef LOAD_DEBUG
|
|
printf("openhive: calculated checksum: %08x\n",checksum);
|
|
printf("openhive: file REGF checksum: %08x\n",hdr->checksum);
|
|
#endif
|
|
if (checksum != hdr->checksum) {
|
|
fprintf(stderr,"openHive(%s): WARNING: REGF header checksum mismatch! calc: 0x%08x != file: 0x%08x\n",filename,checksum,hdr->checksum);
|
|
}
|
|
|
|
hdesc->rootofs = hdr->ofs_rootkey + 0x1000;
|
|
|
|
if (info) {
|
|
printf("Hive <%s> name (from header): <",filename);
|
|
for (c = hdr->name; *c && (c < hdr->name + 64); c += 2) putchar(*c);
|
|
printf(">\nROOT KEY at offset: 0x%06x * ",hdesc->rootofs);
|
|
}
|
|
|
|
/* Cache the roots subkey index type (li,lf,lh) so we can use the correct
|
|
* one when creating the first subkey in a key */
|
|
|
|
nk = (struct nk_key *)(hdesc->buffer + hdesc->rootofs + 4);
|
|
if (nk->id == 0x6b6e) {
|
|
rikey = (struct ri_key *)(hdesc->buffer + nk->ofs_lf + 0x1004);
|
|
hdesc->nkindextype = rikey->id;
|
|
if (hdesc->nkindextype == 0x6972) { /* Gee, big root, must check indirectly */
|
|
fprintf(stderr,"openHive: DEBUG: BIG ROOT!\n");
|
|
rikey = (struct ri_key *)(hdesc->buffer + rikey->hash[0].ofs_li + 0x1004);
|
|
hdesc->nkindextype = rikey->id;
|
|
}
|
|
if (hdesc->nkindextype != 0x666c &&
|
|
hdesc->nkindextype != 0x686c &&
|
|
hdesc->nkindextype != 0x696c) {
|
|
hdesc->nkindextype = 0x666c;
|
|
}
|
|
|
|
if (info) printf("Subkey indexing type is: %04x <%c%c>\n",
|
|
hdesc->nkindextype,
|
|
hdesc->nkindextype & 0xff,
|
|
hdesc->nkindextype >> 8);
|
|
} else {
|
|
fprintf(stderr,"openHive: WARNING: ROOT key does not seem to be a key! (not type == nk)\n");
|
|
}
|
|
|
|
|
|
while (pofs < hdr->filesize + 0x1000) { /* Loop through hbins until end according to regf header */
|
|
#ifdef LOAD_DEBUG
|
|
int htrace = 1;
|
|
// if (htrace) hexdump(hdesc->buffer,pofs,pofs+0x20,1);
|
|
#endif
|
|
p = (struct hbin_page *)(hdesc->buffer + pofs);
|
|
if (p->id != 0x6E696268) {
|
|
if (info) printf("Page at 0x%x is not 'hbin', assuming file contains garbage at end\n",pofs);
|
|
break;
|
|
}
|
|
hdesc->pages++;
|
|
|
|
if (verbose) printf("###### Page at 0x%0x ofs_self 0x%0x, size (delta ofs_next) 0x%0x ######\n",
|
|
pofs,p->ofs_self,p->ofs_next);
|
|
|
|
if (p->ofs_next == 0) {
|
|
fprintf(stderr,"openHive: ERROR: Page at 0x%x has size zero! File may be corrupt, or program has a bug\n",pofs);
|
|
return(hdesc);
|
|
}
|
|
|
|
|
|
vofs = pofs + 0x20; /* Skip page header, and run through blocks in hbin */
|
|
|
|
while (vofs-pofs < p->ofs_next && vofs < hdesc->size) {
|
|
vofs += parse_block(hdesc,vofs,trace);
|
|
|
|
}
|
|
|
|
pofs += p->ofs_next;
|
|
|
|
} /* hbin loop */
|
|
|
|
|
|
hdesc->endofs = hdr->filesize + 0x1000;
|
|
hdesc->lastbin = pofs - p->ofs_next; /* Compensate for loop that added at end above */
|
|
|
|
if (verbose) {
|
|
printf("Last HBIN at offset : 0x%x\n",hdesc->lastbin);
|
|
printf("First non-HBIN page offset: 0x%x\n",hdesc->endofs);
|
|
printf("hdr->unknown4 (version?) : 0x%x\n",hdr->unknown4);
|
|
}
|
|
|
|
if (info || verbose) {
|
|
printf("File size %d [%x] bytes, containing %d pages (+ 1 headerpage)\n",hdesc->size,hdesc->size, hdesc->pages);
|
|
printf("Used for data: %d/%d blocks/bytes, unused: %d/%d blocks/bytes.\n\n",
|
|
hdesc->useblk,hdesc->usetot,hdesc->unuseblk,hdesc->unusetot);
|
|
}
|
|
|
|
/* So, let's guess what kind of hive this is, based on keys in its root */
|
|
|
|
hdesc->type = HTYPE_UNKNOWN;
|
|
|
|
if (trav_path(hdesc, 0, "\\SAM", 0)) hdesc->type = HTYPE_SAM;
|
|
else if (trav_path(hdesc, 0, "\\ControlSet", 0)) hdesc->type = HTYPE_SYSTEM;
|
|
else if (trav_path(hdesc, 0, "\\Policy", 0)) hdesc->type = HTYPE_SECURITY;
|
|
else if (trav_path(hdesc, 0, "\\Microsoft", 0)) hdesc->type = HTYPE_SOFTWARE;
|
|
if (verbose) printf("Type of hive guessed to be: %d\n",hdesc->type);
|
|
|
|
return(hdesc);
|
|
|
|
}
|
|
|