1 /*
2  * ntreg.c - Windows (NT and up) Registry Hive access library
3  *           should be able to handle most basic functions:
4  *           iterate, add&delete keys and values, read stuff, change stuff etc
5  *           no rename of keys or values yet..
6  *           also contains some minor utility functions (string handling etc) for now
7  *
8  * 2014-jan: openhive() now more compatible with build on non-unix?
9  * 2013-aug: Enter buil-in buffer debugger only if in trace mode, else return error or abort()
10  * 2013-may-aug: Fixed critical bug in del_value which could
11  *           thrash the hive when removing value in bottom of key.
12  *           And a pointer not reinitialized when buffer reallocated in some cases, fixed.
13  *           Thanks to Jacky To for reporting those two.
14  *           Some minor adjustments for compiler. A few more utility functions.
15  * 2012-oct: Added set_val_type. some minor changes.
16  * 2011-may: Seems like large values >16k or something like that is split
17  *           into several blocks (db), have tried to implement that.
18  *           Vista seems to accept it. Not tested on others yet.
19  * 2011-may: Expansion now seems to be working, have lot of test accepted by
20  *           vista and win7. But no warranties..
21  * 2011-may: Found a couple of nasty bugs inn add_key(), making Vista and newer
22  *           reject (remove) keys on hive load.
23  *           May have been there for a long time according to reports.
24  * 2011-apr: Fixed some problems with the allocator when at the end of a hbin.
25  * 2011-apr: .reg file import. Ugly one, but it seems to work. Found
26  *           quite a lot of bugs in other places while testing it.
27  *           String handling when international characters or wide (UTF-16)
28  *           is a pain, and very ugly. May not work in some cases.
29  *           Will keep wide (16 bit) characters in strings when importing from
30  *           .reg file that has it, like what regedit.exe generates for example.
31  * 2011-apr: Added routines for hive expansion. Will add new hbin at end of file
32  *           when needed. If using library, please read ugly warnings in "alloc_block()".
33  * 2010-jun: Patches from Frediano Ziglio adding support for wide characters
34  *           and some bugfixes. Thank you!
35  * 2008-mar: Type QWORD (XP/Vista and newer) now recognized
36  * 2008-mar: Most functions accepting a path now also have a parameter specifying if
37  *           the search should be exact or on first match basis
38  * 2008-mar: Fixed bug which skipped first indirect index table when deleting keys,
39  *           usually leading to endless loop when recursive deleting.
40  * 2008-mar: Export to .reg file by Leo von Klenze, expanded a bit by me.
41  * 2008-mar: 64 bit compatible patch by Mike Doty, via Alon Bar-Lev
42  *           http://bugs.gentoo.org/show_bug.cgi?id=185411
43  * 2007-sep: Verbosity/debug messages minor changes
44  * 2007-apr: LGPL license.
45  * 2004-aug: Deep indirect index support. NT351 support. Recursive delete.
46  *           Debugged a lot in allocation routines. Still no expansion.
47  * 2004-jan: Verbosity updates
48  * 2003-jan: Allocation of new data, supports adding/deleting keys & stuff.
49  *           Missing is expanding the file.
50  * 2003-jan: Seems there may be garbage pages at end of file, not zero pages
51  *           now stops enumerating at first non 'hbin' page.
52  *
53  * NOTE: The API is not frozen. It can and will change every release.
54  *
55  *****
56  *
57  * NTREG - Window registry file reader / writer library
58  * Copyright (c) 1997-2014 Petter Nordahl-Hagen.
59  *
60  * This library is free software; you can redistribute it and/or
61  * modify it under the terms of the GNU Lesser General Public
62  * License as published by the Free Software Foundation;
63  * version 2.1 of the License.
64  *
65  * This library is distributed in the hope that it will be useful,
66  * but WITHOUT ANY WARRANTY; without even the implied warranty of
67  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
68  * Lesser General Public License for more details.
69  * See file LGPL.txt for the full license.
70  *
71  */
72 
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <ctype.h>
76 #include <sys/types.h>
77 #include <sys/stat.h>
78 #include <fcntl.h>
79 #include <errno.h>
80 #include <string.h>
81 #include <unistd.h>
82 #include <inttypes.h>
83 #include <stdarg.h>
84 
85 #include "ntreg.h"
86 
87 /* Set to abort() and debug on more critical errors */
88 #define DOCORE 1
89 
90 #define ZEROFILL      1  /* Fill blocks with zeroes when allocating and deallocating */
91 #define ZEROFILLONLOAD  0  /* Fill blocks marked as unused/deallocated with zeroes on load. FOR DEBUG */
92 
93 const char ntreg_version[] = "ntreg lib routines, v0.95 140201, (c) Petter N Hagen";
94 
95 const char *val_types[REG_MAX+1] = {
96   "REG_NONE", "REG_SZ", "REG_EXPAND_SZ", "REG_BINARY", "REG_DWORD",       /* 0 - 4 */
97   "REG_DWORD_BIG_ENDIAN", "REG_LINK",                                     /* 5 - 6 */
98   "REG_MULTI_SZ", "REG_RESOUCE_LIST", "REG_FULL_RES_DESC", "REG_RES_REQ", /* 7 - 10 */
99   "REG_QWORD",                                                            /* 11     */
100 };
101 
102 static char * string_prog2regw(void *string, int len, int* out_len);
103 
104 /* Utility routines */
105 
106 /* toupper() table for registry hashing functions, so we don't have to
107  * dependent upon external locale lib files
108  */
109 
110 static const unsigned char reg_touppertable[] = {
111 
112   /* ISO 8859-1 is probably not the one.. */
113 
114         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
115         0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
116         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
117         0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
118         0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
119         0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
120         0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
121         0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
122         0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
123         0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
124         0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
125         0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
126         0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
127         0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
128         0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
129         0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
130 
131         0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
132         0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
133         0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
134         0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
135         0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
136         0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
137         0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x00, 0xb6, 0xb7, /* 0xb0-0xb7 */
138         0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
139         0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
140         0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
141         0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
142         0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* 0xd8-0xdf */
143         0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
144         0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
145         0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */
146         0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* 0xf8-0xff */
147 
148 };
149 
150 
151 /* Use table above in strcasecmp else add_key may put names in wrong order
152    and windows actually verifies that on hive load!!
153    or at least it finds out in some cases..
154 */
155 
156 
strn_casecmp(const char * s1,const char * s2,size_t n)157 int strn_casecmp(const char *s1, const char *s2, size_t n)
158 {
159   char r;
160 
161   while ( *s1 && *s2 && n ) {
162     r = (unsigned char)reg_touppertable[(unsigned char)*s1] - (unsigned char)reg_touppertable[(unsigned char)*s2];
163     if (r) return(r);
164     n--;
165     s1++;
166     s2++;
167   }
168   if ( (!*s1 && !*s2) || !n) return(0);
169   if ( !*s1 ) return(-1);
170   return(1);
171 }
172 
173 
str_dup(const char * str)174 char *str_dup( const char *str )
175 {
176     char *str_new;
177 
178     if (!str) return(0);
179 
180     CREATE( str_new, char, strlen(str) + 1 );
181     strcpy( str_new, str );
182     return str_new;
183 }
184 
str_cat(char * str,char * add)185 char *str_cat(char *str, char *add)
186 {
187   if (!add) return(str);
188   str = (char *) realloc(str, strlen(str) + strlen(add) + 3);
189   strcat (str, add);
190   return (str);
191 }
192 
str_catf(char * str,const char * format,...)193 char *str_catf(char *str, const char *format, ... )
194 {
195     va_list ap;
196     char add[640] ;
197 
198     va_start(ap, format) ;
199     vsprintf(add, format, ap) ;
200     va_end(ap) ;
201 
202     str = (char *) realloc(str, strlen(str) + strlen(add) + 3);
203     strcat (str, add );
204     return(str);
205 }
206 
207 
208 
209 
210 /* Copy non-terminated string to buffer we allocate and null terminate it
211  * Uses length only, does not check for nulls
212  */
213 
mem_str(const char * str,int len)214 char *mem_str( const char *str, int len )
215 {
216     char *str_new;
217 
218     if (!str)
219         return 0 ;
220 
221     CREATE( str_new, char, len + 1 );
222     memcpy( str_new, str, len);
223     *(str_new+len) = 0;
224     return str_new;
225 }
226 
227 
fmyinput(char * prmpt,char * ibuf,int maxlen)228 int fmyinput(char *prmpt, char *ibuf, int maxlen)
229 {
230 
231    printf("%s",prmpt);
232 
233    fgets(ibuf,maxlen+1,stdin);
234 
235    ibuf[strlen(ibuf)-1] = 0;
236 
237    return(strlen(ibuf));
238 }
239 
240 /* Print len number of hexbytes */
241 
hexprnt(char * s,unsigned char * bytes,int len)242 void hexprnt(char *s, unsigned char *bytes, int len)
243 {
244 int i;
245 
246    printf("%s",s);
247    for (i = 0; i < len; i++) {
248       printf("%02x ",bytes[i]);
249    }
250    printf("\n");
251 }
252 
253 /* HexDump all or a part of some buffer */
254 
hexdump(char * hbuf,int start,int stop,int ascii)255 void hexdump(char *hbuf, int start, int stop, int ascii)
256 {
257    char c;
258    int diff,i;
259 
260    while (start < stop ) {
261 
262       diff = stop - start;
263       if (diff > 16) diff = 16;
264 
265       printf(":%05X  ",start);
266 
267       for (i = 0; i < diff; i++) {
268 	 printf("%02X ",(unsigned char)*(hbuf+start+i));
269       }
270       if (ascii) {
271 	for (i = diff; i < 16; i++) printf("   ");
272 	for (i = 0; i < diff; i++) {
273 	  c = *(hbuf+start+i);
274 	  printf("%c", isprint(c) ? c : '.');
275 	}
276       }
277       printf("\n");
278       start += 16;
279    }
280 }
281 
282 /* General search routine, find something in something else */
find_in_buf(char * buf,char * what,int sz,int len,int start)283 int find_in_buf(char *buf, char *what, int sz, int len, int start)
284 {
285    int i;
286 
287    for (; start < sz; start++) {
288       for (i = 0; i < len; i++) {
289 	if (*(buf+start+i) != *(what+i)) break;
290       }
291       if (i == len) return(start);
292    }
293    return(0);
294 }
295 
296 /* Get INTEGER from memory. This is probably low-endian specific? */
get_int(char * array)297 int get_int( char *array )
298 {
299 	return ((array[0]&0xff) + ((array[1]<<8)&0xff00) +
300 		   ((array[2]<<16)&0xff0000) +
301 		   ((array[3]<<24)&0xff000000));
302 }
303 
304 
305 /* Quick and dirty UNICODE to std. ascii */
306 
cheap_uni2ascii(char * src,char * dest,int l)307 void cheap_uni2ascii(char *src, char *dest, int l)
308 {
309 
310    for (; l > 0; l -=2) {
311       *dest = *src;
312       dest++; src +=2;
313    }
314    *dest = 0;
315 }
316 
317 
318 /* Quick and dirty ascii to unicode */
319 
cheap_ascii2uni(char * src,char * dest,int l)320 void cheap_ascii2uni(char *src, char *dest, int l)
321 {
322    for (; l > 0; l--) {
323       *dest++ = *src++;
324       *dest++ = 0;
325 
326    }
327 }
328 
skipspace(char ** c)329 void skipspace(char **c)
330 {
331    while( **c == ' ' ) (*c)++;
332 }
333 
gethex(char ** c)334 int gethex(char **c)
335 {
336    int value;
337 
338    skipspace(c);
339 
340    if (!(**c)) return(0);
341 
342    sscanf(*c,"%x",&value);
343 
344    while( **c != ' ' && (**c)) (*c)++;
345 
346    return(value);
347 }
348 
349 /* Get a string of HEX bytes (space separated),
350  * or if first char is ' get an ASCII string instead.
351  */
352 
gethexorstr(char ** c,char * wb)353 int gethexorstr(char **c, char *wb)
354 {
355    int l = 0;
356 
357    skipspace(c);
358 
359    if ( **c == '\'') {
360       (*c)++;
361       while ( **c ) {
362 	 *(wb++) = *((*c)++);
363 	 l++;
364       }
365    } else {
366       do {
367 	 *(wb++) = gethex(c);
368 	 l++;
369 	 skipspace(c);
370       } while ( **c );
371    }
372    return(l);
373 }
374 
375 /* Simple buffer debugger, returns 1 if buffer dirty/edited */
376 
debugit(char * buf,int sz)377 int debugit(char *buf, int sz)
378 {
379 
380 
381    char inbuf[100],whatbuf[100],*bp;
382 
383    int dirty=0,to,from,l,i,j,wlen,cofs = 0;
384 
385    printf("Buffer debugger. '?' for help.\n");
386 
387    while (1) {
388       l = fmyinput(".",inbuf,90);
389       bp = inbuf;
390 
391       skipspace(&bp);
392 
393       if (l > 0 && *bp) {
394 	 switch(*bp) {
395 	  case 'd' :
396 	    bp++;
397 	    if (*bp) {
398 	       from = gethex(&bp);
399 	       to   = gethex(&bp);
400 	    } else {
401 	       from = cofs; to = 0;
402 	    }
403 	    if (to == 0) to = from + 0x100;
404 	    if (to > sz) to = sz;
405 	    hexdump(buf,from,to,1);
406 	    cofs = to;
407 	    break;
408 	  case 'a' :
409 	    bp++;
410 	    if (*bp) {
411 	       from = gethex(&bp);
412 	       to   = gethex(&bp);
413 	    } else {
414 	       from = cofs; to = 0;
415 	    }
416 	    if (to == 0) to = from + 0x100;
417 	    if (to > sz) to = sz;
418 	    hexdump(buf,from,to,0);
419 	    cofs = to;
420 	    break;
421 	  case 'q':
422 	    return(0);
423 	    break;
424 	  case 's':
425 	    if (!dirty) printf("Buffer has not changed, no need to write..\n");
426 	    return(dirty);
427 	    break;
428 	  case 'h':
429 	    bp++;
430 	    if (*bp == 'a') {
431 	       from = 0;
432 	       to = sz;
433 	       bp++;
434 	    } else {
435 	       from = gethex(&bp);
436 	       to   = gethex(&bp);
437 	    }
438 	    wlen = gethexorstr(&bp,whatbuf);
439 	    if (to > sz) to = sz;
440 	    printf("from: %x, to: %x, wlen: %d\n",from,to,wlen);
441 	    for (i = from; i < to; i++) {
442 	       for (j = 0; j < wlen; j++) {
443 		  if ( *(buf+i+j) != *(whatbuf+j)) break;
444 	       }
445 	       if (j == wlen) printf("%06x ",i);
446 	    }
447 	    printf("\n");
448 	    break;
449 	  case ':':
450 	    bp++;
451 	    if (!*bp) break;
452 	    from = gethex(&bp);
453 	    wlen = gethexorstr(&bp,whatbuf);
454 
455 	    printf("from: %x, wlen: %d\n",from,wlen);
456 
457 	    memcpy(buf+from,whatbuf,wlen);
458 	    dirty = 1;
459 	    break;
460 
461 	  case '?':
462 	    printf("d [<from>] [<to>] - dump buffer within range\n");
463 	    printf("a [<from>] [<to>] - same as d, but without ascii-part (for cut'n'paste)\n");
464 	    printf(": <offset> <hexbyte> [<hexbyte> ...] - change bytes\n");
465 	    printf("h <from> <to> <hexbyte> [<hexbyte> ...] - hunt (search) for bytes\n");
466 	    printf("ha <hexbyte> [<hexbyte] - Hunt all (whole buffer)\n");
467 	    printf("s - save & quit\n");
468 	    printf("q - quit (no save)\n");
469 	    printf("  instead of <hexbyte> etc. you may give 'string to enter/search a string\n");
470 	    break;
471 	  default:
472 	    printf("?\n");
473 	    break;
474 	 }
475       }
476    }
477 }
478 
479 
480 /* Utility function to copy and append two values into a new one
481  * Will allocate new buffer, but not touch the input ones
482  */
483 
reg_valcat(struct keyval * a,struct keyval * b)484 struct keyval *reg_valcat(struct keyval *a, struct keyval *b)
485 {
486   int newsize = 0;
487   int asize = 0;
488   int bsize = 0;
489   struct keyval *result = NULL;
490 
491   if (!a && !b) return(NULL);
492 
493   if (a) asize = a->len;
494   if (b) bsize = b->len;
495 
496   newsize = asize + bsize;
497 
498   // printf("asize = %d, bsize = %d, newsize = %d\n",asize,bsize,newsize);
499 
500   ALLOC(result, sizeof(struct keyval) + newsize, 1);
501 
502   if (asize) memcpy(&result->data, &a->data, asize);
503   if (bsize) memcpy(&result->data + asize / sizeof(int), &b->data, bsize);
504   result->len = newsize;
505 
506   //printf("reg_valcat done\n");
507 
508   return(result);
509 
510 }
511 
512 
513 
514 
515 
516 
517 
518 /* ========================================================================= */
519 
520 /* The following routines are mostly for debugging, I used it
521  * much during discovery. the -t command line option uses it,
522  * also the 'st' and 's' from the editor & hexdebugger.
523  * All offsets shown in these are unadjusted (ie you must add
524  * headerpage (most often 0x1000) to get file offset)
525  */
526 
527 /* Parse the nk datablock
528  * vofs = offset into struct (after size linkage)
529  */
parse_nk(struct hive * hdesc,int vofs,int blen)530 void parse_nk(struct hive *hdesc, int vofs, int blen)
531 {
532 
533   struct nk_key *key;
534   int i;
535 
536   printf("== nk at offset %0x\n",vofs);
537 
538   /* #define D_OFFS2(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */
539 #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs )
540 
541   key = (struct nk_key *)(hdesc->buffer + vofs);
542   printf("%04x   type              = 0x%02x %s\n", D_OFFS(type)  ,key->type,
543 	                           (key->type == KEY_ROOT ? "ROOT_KEY" : "") );
544   printf("%04x   timestamp skipped\n", D_OFFS(timestamp) );
545   printf("%04x   parent key offset = 0x%0x\n", D_OFFS(ofs_parent) ,key->ofs_parent + 0x1000);
546   printf("%04x   number of subkeys = %d\n", D_OFFS(no_subkeys),key->no_subkeys);
547   printf("%04x   lf-record offset  = 0x%0x\n",D_OFFS(ofs_lf),key->ofs_lf + 0x1000);
548   printf("%04x   number of values  = %d\n", D_OFFS(no_values),key->no_values);
549   printf("%04x   val-list offset   = 0x%0x\n",D_OFFS(ofs_vallist),key->ofs_vallist + 0x1000);
550   printf("%04x   sk-record offset  = 0x%0x\n",D_OFFS(ofs_sk),key->ofs_sk + 0x1000);
551   printf("%04x   classname offset  = 0x%0x\n",D_OFFS(ofs_classnam),key->ofs_classnam + 0x1000);
552 
553   printf("%04x   dummy3            = 0x%0x (%d)\n",D_OFFS(dummy3),key->dummy3,key->dummy3);
554   printf("%04x   dummy4            = 0x%0x (%d)\n",D_OFFS(dummy4),key->dummy4,key->dummy4);
555   printf("%04x   dummy5            = 0x%0x (%d)\n",D_OFFS(dummy5),key->dummy5,key->dummy5);
556   printf("%04x   dummy6            = 0x%0x (%d)\n",D_OFFS(dummy6),key->dummy6,key->dummy6);
557   printf("%04x   dummy7            = 0x%0x (%d)\n",D_OFFS(dummy7),key->dummy7,key->dummy7);
558 
559   printf("%04x   name length       = %d\n", D_OFFS(len_name),key->len_name);
560   printf("%04x   classname length  = %d\n", D_OFFS(len_classnam),key->len_classnam);
561 
562   printf("%04x   Key name: <",D_OFFS(keyname) );
563   for(i = 0; i < key->len_name; i++) putchar(key->keyname[i]);
564   printf(">\n== End of key info.\n");
565 
566 }
567 
568 /* Parse the vk datablock
569  * vofs = offset into struct (after size linkage)
570  */
parse_vk(struct hive * hdesc,int vofs,int blen)571 void parse_vk(struct hive *hdesc, int vofs, int blen)
572 {
573   struct vk_key *key;
574   int i;
575 
576   printf("== vk at offset %0x\n",vofs);
577 
578 
579   key = (struct vk_key *)(hdesc->buffer + vofs);
580   printf("%04x   name length       = %d (0x%0x)\n", D_OFFS(len_name),
581 	                             key->len_name, key->len_name  );
582   printf("%04x   length of data    = %d (0x%0x)\n", D_OFFS(len_data),
583 	                             key->len_data, key->len_data  );
584   printf("%04x   data offset       = 0x%0x\n",D_OFFS(ofs_data),key->ofs_data + 0x1000);
585   printf("%04x   value type        = 0x%0x  %s\n", D_OFFS(val_type), key->val_type,
586                  (key->val_type <= REG_MAX ? val_types[key->val_type] : "(unknown)") ) ;
587 
588   printf("%04x   flag              = 0x%0x\n",D_OFFS(flag),key->flag);
589   printf("%04x   *unused?*         = 0x%0x\n",D_OFFS(dummy1),key->dummy1);
590 
591   printf("%04x   Key name: <",D_OFFS(keyname) );
592   for(i = 0; i < key->len_name; i++) putchar(key->keyname[i]);
593   printf(">\n== End of key info.\n");
594 
595 }
596 
597 /* Parse the sk datablock
598  * Gee, this is the security info. Who cares? *evil grin*
599  * vofs = offset into struct (after size linkage)
600  */
parse_sk(struct hive * hdesc,int vofs,int blen)601 void parse_sk(struct hive *hdesc, int vofs, int blen)
602 {
603   struct sk_key *key;
604   /* int i; */
605 
606   printf("== sk at offset %0x\n",vofs);
607 
608   key = (struct sk_key *)(hdesc->buffer + vofs);
609   printf("%04x   *unused?*         = %d\n"   , D_OFFS(dummy1),     key->dummy1    );
610   printf("%04x   Offset to prev sk = 0x%0x\n", D_OFFS(ofs_prevsk), key->ofs_prevsk + 0x1000);
611   printf("%04x   Offset to next sk = 0x%0x\n", D_OFFS(ofs_nextsk), key->ofs_nextsk + 0x1000);
612   printf("%04x   Usage counter     = %d (0x%0x)\n", D_OFFS(no_usage),
613 	                                            key->no_usage,key->no_usage);
614   printf("%04x   Security data len = %d (0x%0x)\n", D_OFFS(len_sk),
615 	                                            key->len_sk,key->len_sk);
616 
617   printf("== End of key info.\n");
618 
619 }
620 
621 
622 /* Parse the lf datablock (>4.0 'nk' offsets lookuptable)
623  * vofs = offset into struct (after size linkage)
624  */
parse_lf(struct hive * hdesc,int vofs,int blen)625 void parse_lf(struct hive *hdesc, int vofs, int blen)
626 {
627   struct lf_key *key;
628   int i;
629 
630   printf("== lf at offset %0x\n",vofs);
631 
632   key = (struct lf_key *)(hdesc->buffer + vofs);
633   printf("%04x   number of keys    = %d\n", D_OFFS(no_keys), key->no_keys  );
634 
635   for(i = 0; i < key->no_keys; i++) {
636     printf("%04x      %3d   Offset: 0x%0x  - <%c%c%c%c>\n",
637 	   D_OFFS(hash[i].ofs_nk), i,
638 	   key->hash[i].ofs_nk + 0x1000,
639            key->hash[i].name[0],
640            key->hash[i].name[1],
641            key->hash[i].name[2],
642            key->hash[i].name[3] );
643   }
644 
645   printf("== End of key info.\n");
646 
647 }
648 
649 /* Parse the lh datablock (WinXP offsets lookuptable)
650  * vofs = offset into struct (after size linkage)
651  * The hash is most likely a base 37 conversion of the name string
652  */
parse_lh(struct hive * hdesc,int vofs,int blen)653 void parse_lh(struct hive *hdesc, int vofs, int blen)
654 {
655   struct lf_key *key;
656   int i;
657 
658   printf("== lh at offset %0x\n",vofs);
659 
660   key = (struct lf_key *)(hdesc->buffer + vofs);
661   printf("%04x   number of keys    = %d\n", D_OFFS(no_keys), key->no_keys  );
662 
663   for(i = 0; i < key->no_keys; i++) {
664     printf("%04x      %3d   Offset: 0x%0x  - <hash: %08x>\n",
665 	   D_OFFS(lh_hash[i].ofs_nk), i,
666 	   key->lh_hash[i].ofs_nk + 0x1000,
667            key->lh_hash[i].hash );
668   }
669 
670   printf("== End of key info.\n");
671 
672 }
673 
674 
675 /* Parse the li datablock (3.x 'nk' offsets list)
676  * vofs = offset into struct (after size linkage)
677  */
parse_li(struct hive * hdesc,int vofs,int blen)678 void parse_li(struct hive *hdesc, int vofs, int blen)
679 {
680   struct li_key *key;
681   int i;
682 
683   printf("== li at offset %0x\n",vofs);
684 
685   /* #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */
686 
687   key = (struct li_key *)(hdesc->buffer + vofs);
688   printf("%04x   number of keys    = %d\n", D_OFFS(no_keys), key->no_keys  );
689 
690   for(i = 0; i < key->no_keys; i++) {
691     printf("%04x      %3d   Offset: 0x%0x\n",
692 	   D_OFFS(hash[i].ofs_nk), i,
693 	   key->hash[i].ofs_nk + 0x1000);
694   }
695   printf("== End of key info.\n");
696 
697 }
698 
699 /* Parse the ri subindex-datablock
700  * (Used to list li/lf/lh's when ~>500keys)
701  * vofs = offset into struct (after size linkage)
702  */
parse_ri(struct hive * hdesc,int vofs,int blen)703 void parse_ri(struct hive *hdesc, int vofs, int blen)
704 {
705   struct ri_key *key;
706   int i;
707 
708   printf("== ri at offset %0x\n",vofs);
709 
710   /* #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) */
711 
712   key = (struct ri_key *)(hdesc->buffer + vofs);
713   printf("%04x   number of subindices = %d\n", D_OFFS(no_lis), key->no_lis  );
714 
715   for(i = 0; i < key->no_lis; i++) {
716     printf("%04x      %3d   Offset: 0x%0x\n",
717 	   D_OFFS(hash[i].ofs_li), i,
718 	   key->hash[i].ofs_li + 0x1000);
719   }
720   printf("== End of key info.\n");
721 
722 }
723 
724 
725 /* Parse the db block (used when value data >4k or something)
726  * vofs = offset into struct (after size linkage)
727  */
parse_db(struct hive * hdesc,int vofs,int blen)728 void parse_db(struct hive *hdesc, int vofs, int blen)
729 {
730   struct db_key *key;
731 
732   printf("== db at offset %0x\n",vofs);
733 
734   key = (struct db_key *)(hdesc->buffer + vofs);
735   printf("%04x   number of parts    = %d\n", D_OFFS(no_part), key->no_part  );
736 
737   printf("%04x   Data list at offset: 0x%0x\n",
738 	   D_OFFS(ofs_data),
739 	   key->ofs_data + 0x1000);
740 
741   printf("== End of key info.\n");
742 
743 }
744 
745 
746 
747 /* Parse the datablock
748  * vofs = offset into struct (after size linkage)
749  */
750 
parse_block(struct hive * hdesc,int vofs,int verbose)751 int parse_block(struct hive *hdesc, int vofs,int verbose)
752 {
753   unsigned short id;
754   int seglen;
755 
756   seglen = get_int(hdesc->buffer+vofs);
757 
758   //  if (vofs > 0xaef000) verbose = 1;
759 
760 #if 0
761   if (verbose || seglen == 0) {
762     printf("** Block at offset %0x\n",vofs);
763     printf("seglen: %d, %u, 0x%0x\n",seglen,seglen,seglen);
764   }
765 #endif
766   if (seglen == 0) {
767     printf("parse_block: FATAL! Zero data block size! (not registry or corrupt file?)\n");
768     if (verbose) debugit(hdesc->buffer,hdesc->size);
769     return(0);
770   }
771 
772   if (seglen < 0) {
773     seglen = -seglen;
774     hdesc->usetot += seglen;
775     hdesc->useblk++;
776     if (verbose) {
777       printf("USED BLOCK @ %06x to %06x : %d, 0x%0x\n",vofs,vofs+seglen,seglen,seglen);
778       /*      hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */
779     }
780   } else {
781     hdesc->unusetot += seglen;
782     hdesc->unuseblk++;
783     /* Useful to zero blocks we think are empty when debugging.. */
784 #if ZEROFILLONLOAD
785     bzero(hdesc->buffer+vofs+4,seglen-4);
786 #endif
787 
788     if (verbose) {
789       printf("FREE BLOCK @ %06x to %06x : %d, 0x%0x\n",vofs,vofs+seglen,seglen,seglen);
790       /*      hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */
791     }
792   }
793 
794 
795   vofs += 4;
796   id = (*(hdesc->buffer + vofs)<<8) + *(hdesc->buffer+vofs+1);
797 
798   if (verbose > 1) {
799     switch (id) {
800     case 0x6e6b: /* nk */
801       parse_nk(hdesc, vofs, seglen);
802       break;
803     case 0x766b: /* vk */
804       parse_vk(hdesc, vofs, seglen);
805       break;
806     case 0x6c66: /* lf */
807       parse_lf(hdesc, vofs, seglen);
808       break;
809     case 0x6c68: /* lh */
810       parse_lh(hdesc, vofs, seglen);
811       break;
812     case 0x6c69: /* li */
813       parse_li(hdesc, vofs, seglen);
814       break;
815     case 0x736b: /* sk */
816       parse_sk(hdesc, vofs, seglen);
817       break;
818     case 0x7269: /* ri */
819       parse_ri(hdesc, vofs, seglen);
820       break;
821     case 0x6462: /* db */
822       parse_db(hdesc, vofs, seglen);
823       break;
824     default:
825       printf("value data, or not handeled yet!\n");
826       break;
827     }
828   }
829   return(seglen);
830 }
831 
832 /* ================================================================ */
833 /* Scan and allocation routines */
834 
835 /* Find start of page given a current pointer into the buffer
836  * hdesc = hive
837  * vofs = offset pointer into buffer
838  * returns: offset to start of page (and page header)
839  */
840 
find_page_start(struct hive * hdesc,int vofs)841 int find_page_start(struct hive *hdesc, int vofs)
842 {
843   int r,prev;
844   struct hbin_page *h;
845 
846   /* Again, assume start at 0x1000 */
847 
848   r = 0x1000;
849   while (r < hdesc->size) {
850     prev = r;
851     h = (struct hbin_page *)(hdesc->buffer + r);
852     if (h->id != 0x6E696268) return(0);
853     if (h->ofs_next == 0) {
854       printf("find_page_start: zero len or ofs_next found in page at 0x%x\n",r);
855       return(0);
856     }
857     r += h->ofs_next;
858     if (r > vofs) return (prev);
859   }
860   return(0);
861 }
862 
863 /* Find free space in page
864  * size = requested size in bytes
865  * pofs = offset to start of actual page header
866  * returns: offset to free block, or 0 for error
867  */
868 
869 #define FB_DEBUG 0
870 
find_free_blk(struct hive * hdesc,int pofs,int size)871 int find_free_blk(struct hive *hdesc, int pofs, int size)
872 {
873   int vofs = pofs + 0x20;
874   int seglen;
875   struct hbin_page *p;
876 
877   p = (struct hbin_page *)(hdesc->buffer + pofs);
878 
879   while (vofs-pofs < (p->ofs_next - HBIN_ENDFILL)) {
880 
881     seglen = get_int(hdesc->buffer+vofs);
882 
883 #if FB_DEBUG
884     if (vofs > 0x400000) {
885     printf("** Block at offset %0x\n",vofs);
886     printf("seglen: %d, %u, 0x%0x\n",seglen,seglen,seglen);
887     }
888 #endif
889 
890     if (seglen == 0) {
891       printf("find_free_blk: FATAL! Zero data block size! (not registry or corrupt file?)\n");
892       printf("             : Block at offset %0x\n",vofs);
893       if ( (vofs - pofs) == (p->ofs_next - 4) ) {
894 	printf("find_free_blk: at exact end of hbin, do not care..\n");
895 	return(0);
896       }
897       if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
898       else abort();
899       return(0);
900     }
901 
902     if (seglen < 0) {
903       seglen = -seglen;
904 #if FB_DEBUG
905       if (vofs >0x400000) printf("USED BLOCK: %d, 0x%0x\n",seglen,seglen);
906 #endif
907 	/*      hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */
908     } else {
909 #if FB_DEBUG
910 	if (vofs >0x400000) printf("FREE BLOCK!\n");
911 #endif
912 	/*      hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */
913 	if (seglen >= size) {
914 #if FB_DEBUG
915 	  if (vofs >0x400000) printf("find_free_blk: found size %d block at 0x%x\n",seglen,vofs);
916 #endif
917 #if 0
918 	  if (vofs == 0x19fb8) {
919 	    printf("find_free_blk: vofs = %x, seglen = %x\n",vofs,seglen);
920 	    debugit(hdesc->buffer,hdesc->size);
921 	    abort();
922 	  }
923 #endif
924 	  return(vofs);
925 	}
926     }
927     vofs += seglen;
928   }
929   return(0);
930 
931 }
932 
933 #undef FB_DEBUG
934 
935 /* Search pages from start to find free block
936  * hdesc - hive
937  * size - space requested, in bytes
938  * returns: offset to free block, 0 if not found or error
939  */
940 
find_free(struct hive * hdesc,int size)941 int find_free(struct hive *hdesc, int size)
942 {
943   int r,blk;
944   struct hbin_page *h;
945 
946   /* Align to 8 byte boundary */
947   if (size & 7) size += (8 - (size & 7));
948 
949   /* Again, assume start at 0x1000 */
950 
951   r = 0x1000;
952   while (r < hdesc->endofs) {
953     h = (struct hbin_page *)(hdesc->buffer + r);
954     if (h->id != 0x6E696268) return(0);
955     if (h->ofs_next == 0) {
956       printf("find_free: zero len or ofs_next found in page at 0x%x\n",r);
957       return(0);
958     }
959     blk = find_free_blk(hdesc,r,size);
960     if (blk) return (blk);
961     r += h->ofs_next;
962   }
963   return(0);
964 }
965 
966 /* Add new hbin to end of file. If file contains data at end
967  * that is not in a hbin, include that too
968  * hdesc - hive as usual
969  * size - minimum size (will be rounded up to next 0x1000 alignment)
970  * returns offset to first block in new hbin
971   */
972 
973 #define ADDBIN_DEBUG
add_bin(struct hive * hdesc,int size)974 int add_bin(struct hive *hdesc, int size)
975 {
976   int r,newsize,newbinofs;
977   struct hbin_page *newbin;
978   struct regf_header *hdr;
979 
980   if (hdesc->state & HMODE_NOEXPAND) {
981    fprintf(stderr,"ERROR: Registry hive <%s> need to be expanded,\n"
982 	   "but that is not allowed according to selected options. Operations will fail.\n", hdesc->filename);
983     return(0);
984   }
985 
986   r = ((size + 0x20 + 4) & ~0xfff) + HBIN_PAGESIZE;  /* Add header and link, round up to page boundary, usually 0x1000 */
987 
988   newbinofs = hdesc->endofs;
989 
990 
991 #ifdef ADDBIN_DEBUG
992   printf("add_bin: request size = %d [%x], rounded to %d [%x]\n",size,size,r,r);
993   printf("add_bin: old buffer size = %d [%x]\n",hdesc->size,hdesc->size);
994   printf("add_bin: firs nonbin off = %d [%x]\n",newbinofs,newbinofs);
995   printf("add_bin: free at end     = %d [%x]\n",hdesc->size-newbinofs,hdesc->size-newbinofs);
996 #endif
997 
998   if ( (newbinofs + r) >= hdesc->size) { /* We must allocate more buffer */
999     newsize = ( (newbinofs + r) & ~(REGF_FILEDIVISOR-1) ) + REGF_FILEDIVISOR; /* File normally multiple of 0x40000 bytes */
1000 
1001 #ifdef ADDBIN_DEBUG
1002     printf("add_bin: new buffer size = %d [%x]\n",newsize,newsize);
1003 #endif
1004 
1005     hdesc->buffer = realloc(hdesc->buffer, newsize);
1006     if (!hdesc->buffer) {
1007       perror("add_bin : realloc() ");
1008       abort();
1009     }
1010     hdesc->size = newsize;
1011 
1012   }
1013 
1014   /* At this point, we have large enough space at end of file */
1015 
1016   newbin = (struct hbin_page *)(hdesc->buffer + newbinofs);
1017 
1018   bzero((void *)newbin, r); /* zero out new hbin, easier to debug too */
1019 
1020   newbin->id = 0x6E696268; /* 'hbin' */
1021   newbin->ofs_self = newbinofs - 0x1000;     /* Point to ourselves minus regf. Seem to be that.. */
1022   newbin->ofs_next = r;                  /* size of this new bin */
1023 
1024   /* Wonder if anything else in the hbin header matters? */
1025 
1026   /* Set whole hbin to be one contious unused block */
1027   newbin->firstlink = (r - 0x20 - 0);  /* Positive linkage = unused */
1028 
1029   /* Update REGF header */
1030   hdr = (struct regf_header *) hdesc->buffer;
1031   hdr->filesize = newbinofs + r - 0x1000;               /* Point header to new end of data */
1032 
1033 #ifdef ADDBIN_DEBUG
1034   printf("add_bin: adjusting size field in REGF: %d [%x]\n",hdr->filesize,hdr->filesize);
1035 #endif
1036 
1037   /* Update state */
1038 
1039   hdesc->state |= HMODE_DIDEXPAND | HMODE_DIRTY;
1040   hdesc->lastbin = newbinofs;  /* Last bin */
1041   hdesc->endofs = newbinofs + r;   /* New data end */
1042 
1043  return(newbinofs + 0x20);
1044 
1045 }
1046 
1047 
1048 
1049 /* Allocate a block of requested size if possible
1050  * hdesc - hive
1051  * pofs - If >0 will try this page first (ptr may be inside page)
1052  * size - number of bytes to allocate
1053  * returns: 0 - failed, else pointer to allocated block.
1054  * WARNING: Will realloc() buffer if it has to be expanded!
1055  * ALL POINTERS TO BUFFER IS INVALID AFTER THAT. (offsets are still correct)
1056  * Guess I'd better switch to mmap() one day..
1057  * This function WILL CHANGE THE HIVE (change block linkage) if it
1058  * succeeds.
1059  */
1060 
alloc_block(struct hive * hdesc,int ofs,int size)1061 int alloc_block(struct hive *hdesc, int ofs, int size)
1062 {
1063   int pofs = 0;
1064   int blk = 0;
1065   int newbin;
1066   int trail, trailsize, oldsz;
1067 
1068   if (hdesc->state & HMODE_NOALLOC) {
1069     printf("\nERROR: alloc_block: Hive <%s> is in no allocation safe mode,"
1070 	   "new space not allocated. Operation will fail!\n", hdesc->filename);
1071     return(0);
1072   }
1073 
1074   size += 4;  /* Add linkage */
1075   if (size & 7) size += (8 - (size & 7));
1076 
1077   /* Check current page first */
1078   if (ofs) {
1079     pofs = find_page_start(hdesc,ofs);
1080     blk = find_free_blk(hdesc,pofs,size);
1081   }
1082 
1083   /* Then check whole hive */
1084   if (!blk) {
1085     blk = find_free(hdesc,size);
1086   }
1087 
1088   if (blk) {  /* Got the space */
1089     oldsz = get_int(hdesc->buffer+blk);
1090 #if 0
1091     printf("Block at         : %x\n",blk);
1092     printf("Old block size is: %x\n",oldsz);
1093     printf("New block size is: %x\n",size);
1094 #endif
1095     trailsize = oldsz - size;
1096 
1097     if (trailsize == 4) {
1098       trailsize = 0;
1099       size += 4;
1100     }
1101 
1102  #if 1
1103     if (trailsize & 7) { /* Trail must be 8 aligned */
1104       trailsize -= (8 - (trailsize & 7));
1105       size += (8 - (trailsize & 7));
1106     }
1107     if (trailsize == 4) {
1108       trailsize = 0;
1109       size += 4;
1110     }
1111 #endif
1112 
1113 #if 0
1114     printf("trail after comp: %x\n",trailsize);
1115     printf("size  after comp: %x\n",size);
1116 #endif
1117 
1118     /* Now change pointers on this to reflect new size */
1119     *(int *)((hdesc->buffer)+blk) = -(size);
1120     /* If the fit was exact (unused block was same size as wee need)
1121      * there is no need for more, else make free block after end
1122      * of newly allocated one */
1123 
1124     hdesc->useblk++;
1125     hdesc->unuseblk--;
1126     hdesc->usetot += size;
1127     hdesc->unusetot -= size;
1128 
1129     if (trailsize) {
1130       trail = blk + size;
1131 
1132       *(int *)((hdesc->buffer)+trail) = (int)trailsize;
1133 
1134       hdesc->useblk++;    /* This will keep blockcount */
1135       hdesc->unuseblk--;
1136       hdesc->usetot += 4; /* But account for more linkage bytes */
1137       hdesc->unusetot -= 4;
1138 
1139     }
1140     /* Clear the block data, makes it easier to debug */
1141 #if ZEROFILL
1142     bzero( (void *)(hdesc->buffer+blk+4), size-4);
1143 #endif
1144 
1145     hdesc->state |= HMODE_DIRTY;
1146 
1147 #if 0
1148     printf("alloc_block: returning %x\n",blk);
1149 #endif
1150     return(blk);
1151   } else {
1152     printf("alloc_block: failed to alloc %d bytes, trying to expand hive..\n",size);
1153 
1154     newbin = add_bin(hdesc,size);
1155     if (newbin) return(alloc_block(hdesc,newbin,size)); /* Nasty... recall ourselves. */
1156     /* Fallthrough to fail if add_bin fails */
1157   }
1158   return(0);
1159 }
1160 
1161 /* Free a block in registry
1162  * hdesc - hive
1163  * blk   - offset of block, MUST POINT TO THE LINKAGE!
1164  * returns bytes freed (incl linkage bytes) or 0 if fail
1165  * Will CHANGE HIVE IF SUCCESSFUL (changes linkage)
1166  */
1167 
1168 #define FB_DEBUG 0
1169 
free_block(struct hive * hdesc,int blk)1170 int free_block(struct hive *hdesc, int blk)
1171 {
1172   int pofs,vofs,seglen,prev,next,nextsz,prevsz,size;
1173   struct hbin_page *p;
1174 
1175   if (hdesc->state & HMODE_NOALLOC) {
1176     printf("free_block: ERROR: Hive %s is in no allocation safe mode,"
1177 	   "space not freed. Operation will fail!\n", hdesc->filename);
1178     return(0);
1179   }
1180 
1181   size = get_int(hdesc->buffer+blk);
1182   if (size >= 0) {
1183     printf("free_block: trying to free already free block!\n");
1184 #ifdef DOCORE
1185       printf("blk = %x\n",blk);
1186       if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
1187       abort();
1188 #endif
1189     return(0);
1190   }
1191   size = -size;
1192 
1193   /* So, we must find start of the block BEFORE us */
1194   pofs = find_page_start(hdesc,blk);
1195   if (!pofs) return(0);
1196 
1197   p = (struct hbin_page *)(hdesc->buffer + pofs);
1198   vofs = pofs + 0x20;
1199 
1200   prevsz = -32;
1201 
1202   if (vofs != blk) {  /* Block is not at start of page? */
1203     while (vofs-pofs < (p->ofs_next - HBIN_ENDFILL) ) {
1204 
1205       seglen = get_int(hdesc->buffer+vofs);
1206 
1207       if (seglen == 0) {
1208 	printf("free_block: EEEK! Zero data block size! (not registry or corrupt file?)\n");
1209 	debugit(hdesc->buffer,hdesc->size);
1210 	return(0);
1211       }
1212 
1213       if (seglen < 0) {
1214 	seglen = -seglen;
1215 	/*      hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); */
1216       }
1217       prev = vofs;
1218       vofs += seglen;
1219       if (vofs == blk) break;
1220     }
1221 
1222     if (vofs != blk) {
1223       printf("free_block: ran off end of page!?!? Error in chains?\n");
1224 #ifdef DOCORE
1225       printf("vofs = %x, pofs = %x, blk = %x\n",vofs,pofs,blk);
1226       if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
1227       abort();
1228 #endif
1229       return(0);
1230     }
1231 
1232     prevsz = get_int(hdesc->buffer+prev);
1233 
1234   }
1235 
1236   /* We also need details on next block (unless at end of page) */
1237   next = blk + size;
1238 
1239   nextsz = 0;
1240   if (next-pofs < (p->ofs_next - HBIN_ENDFILL) ) nextsz = get_int(hdesc->buffer+next);
1241 
1242 #if 0
1243   printf("offset prev : %x , blk: %x , next: %x\n",prev,blk,next);
1244   printf("size   prev : %x , blk: %x , next: %x\n",prevsz,size,nextsz);
1245 #endif
1246 
1247   /* Now check if next block is free, if so merge it with the one to be freed */
1248   if ( nextsz > 0) {
1249 #if 0
1250     printf("Swallow next\n");
1251 #endif
1252     size += nextsz;   /* Swallow it in current block */
1253     hdesc->useblk--;
1254     hdesc->usetot -= 4;
1255     hdesc->unusetot -= 4;   /* FIXME !??!?? */
1256   }
1257 
1258   /* Now free the block (possibly with ajusted size as above) */
1259 #if ZEROFILL
1260    bzero( (void *)(hdesc->buffer+blk), size);
1261 #endif
1262 
1263   *(int *)((hdesc->buffer)+blk) = (int)size;
1264   hdesc->usetot -= size;
1265   hdesc->unusetot -= size;  /* FIXME !?!? */
1266   hdesc->unuseblk--;
1267 
1268   hdesc->state |= HMODE_DIRTY;
1269 
1270   /* Check if previous block is also free, if so, merge.. */
1271   if (prevsz > 0) {
1272 #if 0
1273     printf("Swallow prev\n");
1274 #endif
1275     hdesc->usetot -= prevsz;
1276     hdesc->unusetot += prevsz;
1277     prevsz += size;
1278     /* And swallow current.. */
1279 #if ZEROFILL
1280       bzero( (void *)(hdesc->buffer+prev), prevsz);
1281 #endif
1282     *(int *)((hdesc->buffer)+prev) = (int)prevsz;
1283     hdesc->useblk--;
1284     return(prevsz);
1285   }
1286   return(size);
1287 }
1288 
1289 
1290 
1291 
1292 
1293 /* ================================================================ */
1294 
1295 /* ** Registry manipulation routines ** */
1296 
1297 
1298 
1299 /* "directory scan", return next name/pointer of a subkey on each call
1300  * nkofs = offset to directory to scan
1301  * lfofs = pointer to int to hold the current scan position,
1302  *         set position to 0 to start.
1303  * sptr  = pointer to struct to hold a single result
1304  * returns: -1 = error. 0 = end of key. 1 = more subkeys to scan
1305  * NOTE: caller must free the name-buffer (struct ex_data *name)
1306  */
ex_next_n(struct hive * hdesc,int nkofs,int * count,int * countri,struct ex_data * sptr)1307 int ex_next_n(struct hive *hdesc, int nkofs, int *count, int *countri, struct ex_data *sptr)
1308 {
1309   struct nk_key *key, *newnkkey;
1310   int newnkofs;
1311   struct lf_key *lfkey;
1312   struct li_key *likey;
1313   struct ri_key *rikey;
1314 
1315 
1316   if (!nkofs) return(-1);
1317   key = (struct nk_key *)(hdesc->buffer + nkofs);
1318   if (key->id != 0x6b6e) {
1319     printf("ex_next error: Not a 'nk' node at 0x%0x\n",nkofs);
1320     return(-1);
1321   }
1322 
1323 #define EXNDEBUG 0
1324 
1325   lfkey = (struct lf_key *)(hdesc->buffer + key->ofs_lf + 0x1004);
1326   rikey = (struct ri_key *)(hdesc->buffer + key->ofs_lf + 0x1004);
1327 
1328   if (rikey->id == 0x6972) {   /* Is it extended 'ri'-block? */
1329 #if EXNDEBUG
1330     printf("%d , %d\n",*countri,*count);
1331 #endif
1332     if (*countri < 0 || *countri >= rikey->no_lis) { /* End of ri's? */
1333       return(0);
1334     }
1335     /* Get the li of lf-struct that's current based on countri */
1336     likey = (struct li_key *)( hdesc->buffer + rikey->hash[*countri].ofs_li + 0x1004 ) ;
1337     if (likey->id == 0x696c) {
1338       newnkofs = likey->hash[*count].ofs_nk + 0x1000;
1339     } else {
1340       lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[*countri].ofs_li + 0x1004 ) ;
1341       newnkofs = lfkey->hash[*count].ofs_nk + 0x1000;
1342     }
1343 
1344     /* Check if current li/lf is exhausted */
1345 #if EXNDEBUG
1346     printf("likey->no_keys = %d\n",likey->no_keys);
1347 #endif
1348     if (*count >= likey->no_keys-1) { /* Last legal entry in li list? */
1349       (*countri)++;  /* Bump up ri count so we take next ri entry next time */
1350       (*count) = -1;  /* Reset li traverse counter for next round, not used later here */
1351     }
1352   } else { /* Plain handler */
1353     if (key->no_subkeys <= 0 || *count >= key->no_subkeys) {
1354       return(0);
1355     }
1356     if (lfkey->id == 0x696c) {   /* Is it 3.x 'li' instead? */
1357       likey = (struct li_key *)(hdesc->buffer + key->ofs_lf + 0x1004);
1358       newnkofs = likey->hash[*count].ofs_nk + 0x1000;
1359     } else {
1360       newnkofs = lfkey->hash[*count].ofs_nk + 0x1000;
1361     }
1362   }
1363 
1364   sptr->nkoffs = newnkofs;
1365   newnkkey = (struct nk_key *)(hdesc->buffer + newnkofs + 4);
1366   sptr->nk = newnkkey;
1367 
1368   if (newnkkey->id != 0x6b6e) {
1369     printf("ex_next: ERROR: not 'nk' node at 0x%0x\n",newnkofs);
1370 
1371     return(-1);
1372   } else {
1373     if (newnkkey->len_name <= 0) {
1374       printf("ex_next: nk at 0x%0x has no name!\n",newnkofs);
1375     } else if (newnkkey->type & 0x20) {
1376 #if 0
1377       printf("dummy1 %x\n", *((int*)newnkkey->dummy1));
1378       printf("dummy2 %x\n", *((int*)newnkkey->dummy2));
1379       printf("type %x\n", newnkkey->type);
1380       printf("timestamp+8 %x\n", *((int*)(newnkkey->timestamp+8)));
1381       printf("dummy3+0 %x\n", *((int*)(newnkkey->dummy3+0)));
1382       printf("dummy3+4 %x\n", *((int*)(newnkkey->dummy3+4)));
1383       printf("dummy3+8 %x\n", *((int*)(newnkkey->dummy3+8)));
1384       printf("dummy3+12 %x\n", *((int*)(newnkkey->dummy3+12)));
1385       printf("dummy4 %x\n", *((int*)&newnkkey->dummy4));
1386       printf("len %d\n", newnkkey->len_name);
1387       printf("len class %d\n", newnkkey->len_classnam);
1388       fflush(stdout);
1389 #endif
1390 
1391       sptr->name =  mem_str(newnkkey->keyname,newnkkey->len_name);
1392       //      sptr->name = string_rega2prog(newnkkey->keyname, newnkkey->len_name);
1393     } else {
1394       sptr->name = string_regw2prog(newnkkey->keyname, newnkkey->len_name);
1395     }
1396   } /* if */
1397   (*count)++;
1398   return(1);
1399   /*  return( *count <= key->no_subkeys); */
1400 }
1401 
1402 /* "directory scan" for VALUES, return next name/pointer of a value on each call
1403  * nkofs = offset to directory to scan
1404  * lfofs = pointer to int to hold the current scan position,
1405  *         set position to 0 to start.
1406  * sptr  = pointer to struct to hold a single result
1407  * returns: -1 = error. 0 = end of key. 1 = more values to scan
1408  * NOTE: caller must free the name-buffer (struct vex_data *name)
1409  */
ex_next_v(struct hive * hdesc,int nkofs,int * count,struct vex_data * sptr)1410 int ex_next_v(struct hive *hdesc, int nkofs, int *count, struct vex_data *sptr)
1411 {
1412   struct nk_key *key /* , *newnkkey */ ;
1413   int vkofs,vlistofs;
1414   int *vlistkey;
1415   struct vk_key *vkkey;
1416 
1417 
1418   if (!nkofs) return(-1);
1419   key = (struct nk_key *)(hdesc->buffer + nkofs);
1420   if (key->id != 0x6b6e) {
1421     printf("ex_next_v error: Not a 'nk' node at 0x%0x\n",nkofs);
1422     return(-1);
1423   }
1424 
1425   if (key->no_values <= 0 || *count >= key->no_values) {
1426     return(0);
1427   }
1428 
1429   vlistofs = key->ofs_vallist + 0x1004;
1430   vlistkey = (int *)(hdesc->buffer + vlistofs);
1431 
1432   vkofs = vlistkey[*count] + 0x1004;
1433   vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
1434   if (vkkey->id != 0x6b76) {
1435     printf("ex_next_v: hit non valuekey (vk) node during scan at offs 0x%0x\n",vkofs);
1436     return(-1);
1437   }
1438 
1439   /*  parse_vk(hdesc, vkofs, 4); */
1440 
1441   sptr->vk = vkkey;
1442   sptr->vkoffs = vkofs;
1443   sptr->name = 0;
1444   sptr->size = (vkkey->len_data & 0x7fffffff);
1445 
1446   if (vkkey->len_name >0) {
1447     if (vkkey->flag & 1) {
1448 
1449       sptr->name = mem_str(vkkey->keyname, vkkey->len_name);
1450       //      sptr->name = string_rega2prog(vkkey->keyname, vkkey->len_name);
1451     } else {
1452       sptr->name = string_regw2prog(vkkey->keyname, vkkey->len_name);
1453     }
1454   } else {
1455     sptr->name = str_dup("");
1456   }
1457 
1458   sptr->type = vkkey->val_type;
1459 
1460   if (sptr->size) {
1461     if (vkkey->val_type == REG_DWORD) {
1462       if (vkkey->len_data & 0x80000000) {
1463 	sptr->val = (int)(vkkey->ofs_data);
1464       }
1465     }
1466   }
1467 #if 0
1468  else if (vkkey->len_data == 0x80000000) {
1469     /* Data SIZE is 0, high bit set: special inline case, data is DWORD and in TYPE field!! */
1470     /* Used a lot in SAM, and maybe in SECURITY I think */
1471     sptr->val = (int)(vkkey->val_type);
1472     sptr->size = 4;
1473     sptr->type = REG_DWORD;
1474   } else {
1475     sptr->val = 0;
1476     sptr->size = 0;
1477   }
1478 #endif
1479 
1480   (*count)++;
1481   return( *count <= key->no_values );
1482 }
1483 
1484 /* traceback - trace nk's back to root,
1485  * building path string as we go.
1486  * nkofs  = offset to nk-node
1487  * path   = pointer to pathstring-buffer
1488  * maxlen = max length of path-buffer
1489  * return: length of path string
1490  */
1491 
get_abs_path(struct hive * hdesc,int nkofs,char * path,int maxlen)1492 int get_abs_path(struct hive *hdesc, int nkofs, char *path, int maxlen)
1493 {
1494   /* int newnkofs; */
1495   struct nk_key *key;
1496   char tmp[ABSPATHLEN+1];
1497   char *keyname;
1498   int len_name;
1499 
1500   maxlen = (maxlen < ABSPATHLEN ? maxlen : ABSPATHLEN);
1501 
1502   key = (struct nk_key *)(hdesc->buffer + nkofs);
1503 
1504   if (key->id != 0x6b6e) {
1505     printf("get_abs_path: Not a 'nk' node!\n");
1506     return(0);
1507   }
1508 
1509   if (key->type == KEY_ROOT) {   /* We're at the root */
1510     return(strlen(path));
1511   }
1512 
1513   strncpy(tmp,path,ABSPATHLEN-1);
1514 
1515   if (key->type & 0x20)
1516     keyname = mem_str(key->keyname, key->len_name);
1517   //    keyname = string_rega2prog(key->keyname, key->len_name);
1518   else
1519     keyname = string_regw2prog(key->keyname, key->len_name);
1520   len_name = strlen(keyname);
1521   if ( (strlen(path) + len_name) >= maxlen-6) {
1522     free(keyname);
1523     snprintf(path,maxlen,"(...)%s",tmp);
1524     return(strlen(path));   /* Stop trace when string exhausted */
1525   }
1526   *path = '\\';
1527   memcpy(path+1,keyname,len_name);
1528   free(keyname);
1529   strncpy(path+len_name+1,tmp,maxlen-6-len_name);
1530   return(get_abs_path(hdesc, key->ofs_parent+0x1004, path, maxlen)); /* go back one more */
1531 }
1532 
1533 
1534 /* Value index table lookup
1535  * hdesc - hive as usual
1536  * vlistofs - offset of table
1537  * name - value name to look for
1538  * returns index into table or -1 if err
1539  */
1540 
vlist_find(struct hive * hdesc,int vlistofs,int numval,char * name,int type)1541 int vlist_find(struct hive *hdesc, int vlistofs, int numval, char *name, int type)
1542 {
1543   struct vk_key *vkkey;
1544   int i,vkofs,len;
1545   int32_t *vlistkey;
1546   int approx = -1;
1547 
1548   len = strlen(name);
1549   vlistkey = (int32_t *)(hdesc->buffer + vlistofs);
1550 
1551   //  printf("vlist_find: <%s> len = %d\n",name,len);
1552 
1553   for (i = 0; i < numval; i++) {
1554     vkofs = vlistkey[i] + 0x1004;
1555     vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
1556     if (vkkey->len_name == 0 && *name == '@' && len == 1) { /* @ is alias for nameless value */
1557       return(i);
1558     }
1559 
1560     // printf("vlist_find: matching against: <%s> len = %d\n",vkkey->keyname,vkkey->len_name);
1561 
1562     if ( (type & TPF_EXACT) && vkkey->len_name != len ) continue;  /* Skip if exact match and not exact size */
1563 
1564     if ( vkkey->len_name >= len ) {                  /* Only check for names that are longer or equal than we seek */
1565       if ( !strncmp(name, vkkey->keyname, len) ) {    /* Name match */
1566 	if (vkkey->len_name == len) return(i);        /* Exact match always best, returns */
1567 	if (approx == -1) approx = i;                 /* Else remember first partial match */
1568       }
1569     }
1570 
1571   }
1572   return(approx);
1573 
1574 }
1575 
1576 /* Recursevely follow 'nk'-nodes based on a path-string,
1577  * returning offset of last 'nk' or 'vk'
1578  * vofs - offset to start node
1579  * path - null-terminated pathname (relative to vofs, \ is separator)
1580  * type - type to return TPF_??, see ntreg.h
1581  * return: offset to nk or vk (or NULL if not found)
1582  */
1583 
trav_path(struct hive * hdesc,int vofs,char * path,int type)1584 int trav_path(struct hive *hdesc, int vofs, char *path, int type)
1585 {
1586   struct nk_key *key, *newnkkey;
1587   struct lf_key *lfkey;
1588   struct li_key *likey;
1589   struct ri_key *rikey;
1590 
1591   int32_t *vlistkey;
1592   int newnkofs, plen, i, lfofs, vlistofs, adjust, r, ricnt, subs;
1593   char *buf;
1594   char part[ABSPATHLEN+1];
1595   char *partptr;
1596 
1597   if (!hdesc) return(0);
1598   buf = hdesc->buffer;
1599 
1600   //  printf("trav_path: called with vofs = %x, path = <%s>, type = %x\n",vofs, path, type);
1601 
1602 
1603   if (!vofs) vofs = hdesc->rootofs+4;     /* No current key given , so start at root */
1604 
1605   if ( !(type & TPF_ABS) && *path == '\\' && *(path+1) != '\\') {      /* Start from root if path starts with \ */
1606     path++;
1607     vofs = hdesc->rootofs+4;
1608   }
1609 
1610   key = (struct nk_key *)(buf + vofs);
1611   //  printf("check of nk at offset: 0x%0x\n",vofs);
1612 
1613   if (key->id != 0x6b6e) {
1614     printf("trav_path: Error: Not a 'nk' node!\n");
1615     return(0);
1616   }
1617 
1618   if ( !(type & TPF_ABS)) {  /* Only traverse path if not absolute literal value name passed */
1619 
1620     /* TODO: Need to rethink this.. */
1621 
1622     /* Find \ delimiter or end of string, copying to name part buffer as we go,
1623        rewriting double \\s */
1624     partptr = part;
1625     for(plen = 0; path[plen] && (path[plen] != '\\' || path[plen+1] == '\\'); plen++) {
1626       if (path[plen] == '\\' && path[plen+1] == '\\') plen++; /* Skip one if double */
1627       *partptr++ = path[plen];
1628     }
1629     *partptr = '\0';
1630 
1631 #if 0
1632       printf("Name part: <%s>\n",part);
1633       printf("Name path: <%s>\n",path);
1634 #endif
1635 
1636     adjust = (path[plen] == '\\' ) ? 1 : 0;
1637     // printf("Checking for <%s> with len %d\n",path,plen);
1638 
1639     if (!plen) return(vofs-4);     /* Path has no lenght - we're there! */
1640 
1641     if ( (plen == 1) && (*(path+1) && *path == '.') && !(type & TPF_EXACT)) {     /* Handle '.' current dir */
1642       //  printf("** handle current\n");
1643       return(trav_path(hdesc,vofs,path+plen+adjust,type));
1644     }
1645     if ( !(type & TPF_EXACT) && (plen == 2) && !strncmp("..",path,2) ) { /* Get parent key */
1646       newnkofs = key->ofs_parent + 0x1004;
1647       /* Return parent (or only root if at the root) */
1648       return(trav_path(hdesc, (key->type == KEY_ROOT ? vofs : newnkofs), path+plen+adjust, type));
1649     }
1650 
1651   }
1652 
1653   /* at last name of path, and we want vk, and the nk has values */
1654   if ((type & TPF_VK_ABS) || (!path[plen] && (type & TPF_VK) && key->no_values) ) {
1655     //  if ( (!path[plen] && (type & TPF_VK) && key->no_values) ) {
1656     if (type & TPF_ABS) {
1657       strcpy(part, path);
1658       plen = de_escape(part,0);
1659       partptr = part + plen;
1660     }
1661 
1662     //  printf("VK namematch for <%s>, type = %d\n",part,type);
1663     vlistofs = key->ofs_vallist + 0x1004;
1664     vlistkey = (int32_t *)(buf + vlistofs);
1665     i = vlist_find(hdesc, vlistofs, key->no_values, part, type);
1666     if (i != -1) {
1667       return(vlistkey[i] + 0x1000);
1668     }
1669   }
1670 
1671   if (key->no_subkeys > 0) {    /* If it has subkeys, loop through the hash */
1672     char *partw = NULL;
1673     int partw_len, part_len;
1674 
1675     // printf("trav_path: subkey loop: path = %s, part = %s\n",path,part);
1676 
1677     lfofs = key->ofs_lf + 0x1004;    /* lf (hash) record */
1678     lfkey = (struct lf_key *)(buf + lfofs);
1679 
1680     if (lfkey->id == 0x6972) { /* ri struct need special parsing */
1681       /* Prime loop state */
1682 
1683       rikey = (struct ri_key *)lfkey;
1684       ricnt = rikey->no_lis;
1685       r = 0;
1686       likey = (struct li_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ;
1687       subs = likey->no_keys;
1688       if (likey->id != 0x696c) {  /* Bwah, not li anyway, XP uses lh usually which is actually smarter */
1689 	lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ;
1690 	likey = NULL;
1691       }
1692     } else {
1693       if (lfkey->id == 0x696c) { /* li? */
1694 	likey = (struct li_key *)(buf + lfofs);
1695       } else {
1696 	likey = NULL;
1697       }
1698       rikey = NULL;
1699       ricnt = 0; r = 0; subs = key->no_subkeys;
1700     }
1701 
1702     partw = string_prog2regw(part, partptr-part, &partw_len);
1703     //    string_prog2rega(part, partptr-part);
1704     part_len = strlen(part);
1705     do {
1706       for(i = 0; i < subs; i++) {
1707 	if (likey) newnkofs = likey->hash[i].ofs_nk + 0x1004;
1708 	else newnkofs = lfkey->hash[i].ofs_nk + 0x1004;
1709 	newnkkey = (struct nk_key *)(buf + newnkofs);
1710 	if (newnkkey->id != 0x6b6e) {
1711 	  printf("ERROR: not 'nk' node! (strange?)\n");
1712 	} else {
1713 	  if (newnkkey->len_name <= 0) {
1714 	    printf("[No name]\n");
1715 	  } else if (
1716 		     ( ( part_len <= newnkkey->len_name ) && !(type & TPF_EXACT) ) ||
1717 		     ( ( part_len == newnkkey->len_name ) && (type & TPF_EXACT)  )
1718 		      ) {
1719 	    /* Can't match if name is shorter than we look for */
1720             int cmp;
1721 	    //	    printf("trav_path: part = <%s>, part_len = %d\n",part,part_len);
1722 	    if (newnkkey->type & 0x20)
1723               cmp = strncmp(part,newnkkey->keyname,part_len);
1724             else
1725               cmp = memcmp(partw, newnkkey->keyname, partw_len);
1726 	    if (!cmp) {
1727 	      //  printf("Key at 0x%0x matches! recursing! new path = %s\n",newnkofs,path+plen+adjust);
1728 	      free(partw);
1729 	      return(trav_path(hdesc, newnkofs, path+plen+adjust, type));
1730 	    }
1731 	  }
1732 	} /* if id OK */
1733       } /* hash loop */
1734       r++;
1735       if (ricnt && r < ricnt) {
1736 	newnkofs = rikey->hash[r].ofs_li;
1737 	likey = (struct li_key *)( hdesc->buffer + newnkofs + 0x1004 ) ;
1738 	subs = likey->no_keys;
1739 	if (likey->id != 0x696c) {  /* Bwah, not li anyway, XP uses lh usually which is actually smarter */
1740 	  lfkey = (struct lf_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ;
1741 	  likey = NULL;
1742 	}
1743       }
1744     } while (r < ricnt && ricnt);
1745     free(partw);
1746 
1747   } /* if subkeys */
1748 
1749   /* Not found */
1750   return(0);
1751 }
1752 
1753 
1754 /* ls - list a 'nk' nodes subkeys and values
1755  * vofs - offset to start of data (skipping block linkage)
1756  * type - 0 = full, 1 = keys only. 2 = values only
1757  */
nk_ls(struct hive * hdesc,char * path,int vofs,int type)1758 void nk_ls(struct hive *hdesc, char *path, int vofs, int type)
1759 {
1760   struct nk_key *key;
1761   int nkofs;
1762   struct ex_data ex;
1763   struct vex_data vex;
1764   int count = 0, countri = 0;
1765 
1766 
1767   nkofs = trav_path(hdesc, vofs, path, 0);
1768 
1769   if(!nkofs) {
1770     printf("nk_ls: Key <%s> not found\n",path);
1771     return;
1772   }
1773   nkofs += 4;
1774 
1775   key = (struct nk_key *)(hdesc->buffer + nkofs);
1776   VERBF(hdesc,"ls of node at offset 0x%0x\n",nkofs);
1777 
1778   if (key->id != 0x6b6e) {
1779     printf("Error: Not a 'nk' node at offset %x!\n",nkofs);
1780 
1781     if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
1782 
1783   }
1784 
1785   printf("Node has %d subkeys and %d values",key->no_subkeys,key->no_values);
1786   if (key->len_classnam) printf(", and class-data of %d bytes",key->len_classnam);
1787   printf("\n");
1788 
1789   if (key->no_subkeys) {
1790     printf("  key name\n");
1791     while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) {
1792       if (!(hdesc->state & HMODE_VERBOSE)) printf("%c <%s>\n", (ex.nk->len_classnam)?'*':' ',ex.name);
1793       else printf("[%6x] %c <%s>\n", ex.nkoffs, (ex.nk->len_classnam)?'*':' ',ex.name);
1794       FREE(ex.name);
1795     }
1796   }
1797   count = 0;
1798   if (key->no_values) {
1799     printf("  size     type              value name             [value if type DWORD]\n");
1800     while ((ex_next_v(hdesc, nkofs, &count, &vex) > 0)) {
1801       if (hdesc->state & HMODE_VERBOSE) printf("[%6x] %6d  %x %-16s   <%s>", vex.vkoffs - 4, vex.size, vex.type,
1802 					       (vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name);
1803       else
1804 	printf("%6d  %x %-16s   <%s>", vex.size, vex.type,
1805 	       (vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name);
1806 
1807       if (vex.type == REG_DWORD) printf(" %*d [0x%x]",25-(int)strlen(vex.name),vex.val , vex.val);
1808       printf("\n");
1809       FREE(vex.name);
1810     }
1811   }
1812 }
1813 
1814 /* Get the type of a value */
get_val_type(struct hive * hdesc,int vofs,char * path,int exact)1815 int get_val_type(struct hive *hdesc, int vofs, char *path, int exact)
1816 {
1817   struct vk_key *vkkey;
1818   int vkofs;
1819 
1820   vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK);
1821   if (!vkofs) {
1822     return -1;
1823   }
1824   vkofs +=4;
1825   vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
1826 
1827   return(vkkey->val_type);
1828 }
1829 
1830 /* Set/change the type of a value. Strangely we need this in some situation in SAM
1831  * and delete + add value is a bit overkill */
1832 
set_val_type(struct hive * hdesc,int vofs,char * path,int exact,int type)1833 int set_val_type(struct hive *hdesc, int vofs, char *path, int exact, int type)
1834 {
1835   struct vk_key *vkkey;
1836   int vkofs;
1837 
1838   vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK);
1839   if (!vkofs) {
1840     return -1;
1841   }
1842   vkofs +=4;
1843   vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
1844 
1845   vkkey->val_type = type;
1846 
1847   return(vkkey->val_type);
1848 }
1849 
1850 
1851 
1852 /* Get len of a value, given current key + path */
get_val_len(struct hive * hdesc,int vofs,char * path,int exact)1853 int get_val_len(struct hive *hdesc, int vofs, char *path, int exact)
1854 {
1855   struct vk_key *vkkey;
1856   int vkofs;
1857   int len;
1858 
1859   vkofs = trav_path(hdesc, vofs,path,exact | TPF_VK);
1860   if (!vkofs) {
1861     return -1;
1862   }
1863   vkofs +=4;
1864   vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
1865 
1866   len = vkkey->len_data & 0x7fffffff;
1867 
1868   if ( vkkey->len_data == 0x80000000 && (exact & TPF_VK_SHORT)) {  /* Special inline case, return size of 4 (dword) */
1869     len = 4;
1870   }
1871 
1872   return(len);
1873 }
1874 
1875 /* Get void-pointer to value-data, also if inline.
1876  * If val_type != 0 a check for correct value type is done
1877  * Caller must keep track of value's length (call function above to get it)
1878  */
get_val_data(struct hive * hdesc,int vofs,char * path,int val_type,int exact)1879 void *get_val_data(struct hive *hdesc, int vofs, char *path, int val_type, int exact)
1880 {
1881   struct vk_key *vkkey;
1882   int vkofs;
1883 
1884   //  printf("get_val_data: path = %s\n",path);
1885 
1886   vkofs = trav_path(hdesc,vofs,path,exact | TPF_VK);
1887   if (!vkofs) {
1888     printf("get_val_data: %s not found\n",path);
1889     abort();
1890     return NULL;
1891   }
1892   vkofs +=4;
1893   vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
1894 
1895 
1896   if (vkkey->len_data == 0) {
1897     return NULL;
1898   }
1899 
1900   if (vkkey->len_data == 0x80000000 && (exact & TPF_VK_SHORT)) {  /* Special inline case (len = 0x80000000) */
1901     return(&vkkey->val_type); /* Data (4 bytes?) in type field */
1902   }
1903 
1904   if (val_type && vkkey->val_type && (vkkey->val_type) != val_type) {
1905     printf("Value <%s> is not of correct type!\n",path);
1906 #if DOCORE
1907     abort();
1908 #endif
1909     return NULL;
1910   }
1911 
1912   /* Negative len is inline, return ptr to offset-field which in
1913    * this case contains the data itself
1914    */
1915   if (vkkey->len_data & 0x80000000) return(&vkkey->ofs_data);
1916   /* Normal return, return data pointer */
1917   return(hdesc->buffer + vkkey->ofs_data + 0x1004);
1918 }
1919 
1920 
1921 /* Get and copy key data (if any) to buffer
1922  * if kv==NULL will allocate needed return struct & buffer
1923  * else will use buffer allocated for it (if it fits)
1924  * return len+data or NULL if not found (or other error)
1925  * NOTE: caller must deallocate buffer! a simple free(keyval) will suffice.
1926  */
get_val2buf(struct hive * hdesc,struct keyval * kv,int vofs,char * path,int type,int exact)1927 struct keyval *get_val2buf(struct hive *hdesc, struct keyval *kv,
1928 			   int vofs, char *path, int type, int exact )
1929 {
1930   int l,i,parts,list,blockofs,blocksize,point,copylen,restlen;
1931   struct keyval *kr;
1932   void *keydataptr;
1933   struct db_key *db;
1934   void *addr;
1935 
1936   l = get_val_len(hdesc, vofs, path, exact);
1937   if (l == -1) return(NULL);  /* error */
1938   if (kv && (kv->len < l)) return(NULL); /* Check for overflow of supplied buffer */
1939 
1940   keydataptr = get_val_data(hdesc, vofs, path, type, exact);
1941   //  if (!keydataptr) return(NULL);
1942 
1943   /* Allocate space for data + header, or use supplied buffer */
1944   if (kv) {
1945     kr = kv;
1946   } else {
1947     ALLOC(kr,1,l*sizeof(int)+4);
1948   }
1949 
1950   kr->len = l;
1951 
1952   // printf("get_val2buf: keydataprtr = %x, l = %x\n",keydataptr,l);
1953 
1954 
1955   if (l > VAL_DIRECT_LIMIT) {       /* Where do the db indirects start? seems to be around 16k */
1956     db = (struct db_key *)keydataptr;
1957     if (db->id != 0x6264) abort();
1958     parts = db->no_part;
1959     list = db->ofs_data + 0x1004;
1960     printf("get_val2buf: Long value: parts = %d, list = %x\n",parts,list);
1961 
1962     point = 0;
1963     restlen = l;
1964     for (i = 0; i < parts; i++) {
1965       blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000;
1966       blocksize = -get_int(hdesc->buffer + blockofs) - 8;
1967 
1968       /* Copy this part, up to size of block or rest lenght in last block */
1969       copylen = (blocksize > restlen) ? restlen : blocksize;
1970 
1971       printf("get_val2buf: Datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize);
1972       printf("             : Point = %x, restlen = %x, copylen = %x\n",point,restlen,copylen);
1973 
1974       addr = (void *)&(kr->data) + point;
1975       memcpy( addr, hdesc->buffer + blockofs + 4, copylen);
1976 
1977       //      debugit((char *)&(kr->data), l);
1978 
1979       point += copylen;
1980       restlen -= copylen;
1981 
1982     }
1983 
1984 
1985   } else {
1986     if (l && kr && keydataptr) memcpy(&(kr->data), keydataptr, l);
1987   }
1988 
1989   return(kr);
1990 }
1991 
1992 /* DWORDs are so common that I make a small function to get it easily */
1993 
get_dword(struct hive * hdesc,int vofs,char * path,int exact)1994 int get_dword(struct hive *hdesc, int vofs, char *path, int exact)
1995 {
1996   struct keyval *v;
1997   int dword;
1998 
1999   v = get_val2buf(hdesc, NULL, vofs, path, REG_DWORD, exact | TPF_VK);
2000   if (!v) return(-1); /* well... -1 COULD BE THE STORED VALUE TOO */
2001 
2002   dword = (int)v->data;
2003 
2004   FREE(v);
2005 
2006   return(dword);
2007 
2008 }
2009 
2010 /* Sanity checker when transferring data into a block
2011  * ofs = offset to data block, point to start of actual datablock linkage
2012  * data = data to copy
2013  * size = size of data to copy
2014  */
2015 
fill_block(struct hive * hdesc,int ofs,void * data,int size)2016 int fill_block(struct hive *hdesc, int ofs, void *data, int size)
2017 {
2018   int blksize;
2019 
2020   blksize = get_int(hdesc->buffer + ofs);
2021   blksize = -blksize;
2022 
2023 #if 0
2024   printf("fill_block: ofs = %x - %x, size = %x, blksize = %x\n",ofs,ofs+size,size,blksize);
2025 #endif
2026   /*  if (blksize < size || ( (ofs & 0xfffff000) != ((ofs+size) & 0xfffff000) )) { */
2027   if (blksize < size) {
2028     printf("fill_block: ERROR: block to small for data: ofs = %x, size = %x, blksize = %x\n",ofs,size,blksize);
2029     if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
2030     abort();
2031   }
2032 
2033   memcpy(hdesc->buffer + ofs + 4, data, size);
2034   return(0);
2035 }
2036 
2037 
2038 /* Free actual data of a value, and update value descriptor
2039  * hdesc - hive
2040  * vofs  - current value offset
2041  */
2042 
free_val_data(struct hive * hdesc,int vkofs)2043 int free_val_data(struct hive *hdesc, int vkofs)
2044 {
2045   struct vk_key *vkkey;
2046   struct db_key *db;
2047   int len,i,blockofs,blocksize,parts,list;
2048 
2049 
2050   vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
2051 
2052   len = vkkey->len_data;
2053 
2054   if (!(len & 0x80000000)) {  /* Check for inline, if so, skip it, nothing to do */
2055 
2056     if (len > VAL_DIRECT_LIMIT) {       /* Where do the db indirects start? seems to be around 16k */
2057 
2058       db = (struct db_key *)(hdesc->buffer + vkkey->ofs_data + 0x1004);
2059 
2060       if (db->id != 0x6264) abort();
2061 
2062       parts = db->no_part;
2063       list = db->ofs_data + 0x1004;
2064 
2065       printf("free_val_data: Long value: parts = %d, list = %x\n",parts,list);
2066 
2067       for (i = 0; i < parts; i++) {
2068 	blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000;
2069 	blocksize = -get_int(hdesc->buffer + blockofs);
2070 	printf("free_val_data: Freeing long datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize);
2071 	free_block(hdesc, blockofs);
2072       }
2073 
2074       printf("free_val_data: Freeing indirect list at %x\n", list-4);
2075       free_block(hdesc, list - 4);
2076       printf("free_val_data: Freeing db structure at %x\n", vkkey->ofs_data + 0x1000);
2077     } /* Fall through to regular which deallocs data or db block ofs_data point to */
2078 
2079     if (len) free_block(hdesc, vkkey->ofs_data + 0x1000);
2080 
2081   } /* inline check */
2082 
2083 
2084   vkkey->len_data = 0;
2085   vkkey->ofs_data = 0;
2086 
2087   return(vkofs);
2088 
2089 }
2090 
2091 
2092 /* Allocate data for value. Frees old data (if any) which will be destroyed
2093  * hdesc - hive
2094  * vofs  - current key
2095  * path  - path to value
2096  * size  - size of data
2097  * Returns: 0 - error, >0 pointer to actual dataspace
2098  */
2099 
alloc_val_data(struct hive * hdesc,int vofs,char * path,int size,int exact)2100 int alloc_val_data(struct hive *hdesc, int vofs, char *path, int size,int exact)
2101 {
2102   struct vk_key *vkkey;
2103   struct db_key *db;
2104   int vkofs,dbofs,listofs,blockofs,blocksize,parts;
2105   int datablk,i;
2106   int *ptr;
2107 
2108   vkofs = trav_path(hdesc,vofs,path,exact);
2109   if (!vkofs) {
2110     return (0);
2111   }
2112 
2113   vkofs +=4;
2114   vkkey = (struct vk_key *)(hdesc->buffer + vkofs);
2115 
2116 
2117   free_val_data(hdesc, vkofs);   /* Get rid of old data if any */
2118 
2119   /* Allocate space for new data */
2120   if (size > 4) {
2121     if (size > VAL_DIRECT_LIMIT) {  /* We must allocate indirect stuff *sigh* */
2122       parts = size / VAL_DIRECT_LIMIT + 1;
2123       printf("alloc_val_data: doing large key: size = %x (%d), parts = %d\n",size,size,parts);
2124 
2125       dbofs = alloc_block(hdesc, vkofs, sizeof(struct db_key));    /* Alloc db structure */
2126       db = (struct db_key *)(hdesc->buffer + dbofs + 4);
2127       db->id = 0x6264;
2128       db->no_part = parts;
2129       listofs = alloc_block(hdesc, vkofs, 4 * parts);  /* block offset list */
2130       db = (struct db_key *)(hdesc->buffer + dbofs + 4);
2131       db->ofs_data = listofs - 0x1000;
2132       printf("alloc_val_data: dbofs = %x, listofs = %x\n",dbofs,listofs);
2133 
2134       for (i = 0; i < parts; i++) {
2135 	blocksize = VAL_DIRECT_LIMIT;      /* Windows seem to alway allocate the whole block */
2136 	blockofs = alloc_block(hdesc, vkofs, blocksize);
2137 	printf("alloc_val_data: block # %d, blockofs = %x\n",i,blockofs);
2138 	ptr = (int *)(hdesc->buffer + listofs + 4 + (i << 2));
2139 	*ptr = blockofs - 0x1000;
2140       }
2141       datablk = dbofs;
2142 
2143     } else { /* Regular size < 16 k direct alloc */
2144       datablk = alloc_block(hdesc, vkofs, size);
2145     }
2146 
2147 
2148 
2149   } else { /* 4 bytes or less are inlined */
2150     datablk = vkofs + (int32_t)&(vkkey->ofs_data) - (int32_t)vkkey;
2151     size |= 0x80000000;
2152   }
2153 
2154   if (!datablk) return(0);
2155 
2156   vkkey = (struct vk_key *)(hdesc->buffer + vkofs); /* alloc_block may move pointer, realloc() buf */
2157 
2158   // printf("alloc_val_data: datablk = %x, size = %x, vkkey->len_data = %x\n",datablk, size, vkkey->len_data);
2159 
2160 
2161 
2162   /* Link in new datablock */
2163   if ( !(size & 0x80000000)) vkkey->ofs_data = datablk - 0x1000;
2164   vkkey->len_data = size;
2165 
2166   return(datablk + 4);
2167 }
2168 
2169 
2170 /* Add a value to a key.
2171  * Just add the metadata (empty value), to put data into it, use
2172  * put_buf2val afterwards
2173  * hdesc - hive
2174  * nkofs - current key
2175  * name  - name of value
2176  * type  - type of value
2177  * returns: 0 err, >0 offset to value metadata
2178  */
2179 
add_value(struct hive * hdesc,int nkofs,char * name,int type)2180 struct vk_key *add_value(struct hive *hdesc, int nkofs, char *name, int type)
2181 {
2182   struct nk_key *nk;
2183   int oldvlist = 0, newvlist, newvkofs;
2184   struct vk_key *newvkkey;
2185   char *blank="";
2186 
2187   if (!name || !*name) return(NULL);
2188 
2189 
2190   nk = (struct nk_key *)(hdesc->buffer + nkofs);
2191   if (nk->id != 0x6b6e) {
2192     printf("add_value: Key pointer not to 'nk' node!\n");
2193     return(NULL);
2194   }
2195 
2196   if (vlist_find(hdesc, nk->ofs_vallist + 0x1004, nk->no_values, name, TPF_EXACT) != -1) {
2197     printf("add_value: value %s already exists\n",name);
2198     return(NULL);
2199   }
2200 
2201   if (!strcmp(name,"@")) name = blank;
2202 
2203   if (nk->no_values) oldvlist = nk->ofs_vallist;
2204 
2205   newvlist = alloc_block(hdesc, nkofs, nk->no_values * 4 + 4);
2206   if (!newvlist) {
2207     printf("add_value: failed to allocate new value list!\n");
2208     return(NULL);
2209   }
2210 
2211   nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved.. */
2212 
2213   if (oldvlist) {   /* Copy old data if any */
2214     memcpy(hdesc->buffer + newvlist + 4, hdesc->buffer + oldvlist + 0x1004, nk->no_values * 4 + 4);
2215   }
2216 
2217   /* Allocate value descriptor including its name */
2218   newvkofs = alloc_block(hdesc, newvlist, sizeof(struct vk_key) + strlen(name));
2219   if (!newvkofs) {
2220     printf("add_value: failed to allocate value descriptor\n");
2221     free_block(hdesc, newvlist);
2222     return(NULL);
2223   }
2224 
2225   nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved.. */
2226 
2227 
2228   /* Success, now fill in the metadata */
2229 
2230   newvkkey = (struct vk_key *)(hdesc->buffer + newvkofs + 4);
2231 
2232   /* Add pointer in value list */
2233   *(int *)(hdesc->buffer + newvlist + 4 + (nk->no_values * 4)) = newvkofs - 0x1000;
2234 
2235   /* Fill in vk struct */
2236   newvkkey->id = 0x6b76;
2237   newvkkey->len_name = strlen(name);
2238   if (type == REG_DWORD || type == REG_DWORD_BIG_ENDIAN) {
2239     newvkkey->len_data = 0x80000004;  /* Prime the DWORD inline stuff */
2240   } else {
2241     newvkkey->len_data = 0x80000000;  /* Default inline zero size */
2242   }
2243   newvkkey->ofs_data = 0;
2244   newvkkey->val_type = type;
2245   newvkkey->flag     = newvkkey->len_name ? 1 : 0;  /* Seems to be 1, but 0 for no name default value */
2246   newvkkey->dummy1   = 0;
2247   memcpy((char *)&newvkkey->keyname, name, newvkkey->len_name);  /* And copy name */
2248 
2249   /* Finally update the key and free the old valuelist */
2250   nk->no_values++;
2251   nk->ofs_vallist = newvlist - 0x1000;
2252   if (oldvlist) free_block(hdesc,oldvlist + 0x1000);
2253 
2254   return(newvkkey);
2255 
2256 }
2257 
2258 /* Remove a vk-struct incl dataspace if any
2259  * Mostly for use by higher level stuff
2260  * hdesc - hive
2261  * vkofs - offset to vk
2262  */
2263 
del_vk(struct hive * hdesc,int vkofs)2264 void del_vk(struct hive *hdesc, int vkofs)
2265 {
2266   struct vk_key *vk;
2267 
2268   vk = (struct vk_key *)(hdesc->buffer + vkofs);
2269   if (vk->id != 0x6b76) {
2270     printf("del_vk: Key pointer not to 'vk' node!\n");
2271     return;
2272   }
2273 
2274   if ( !(vk->len_data & 0x80000000) && vk->ofs_data) {
2275     free_val_data(hdesc, vkofs);
2276   }
2277 
2278   free_block(hdesc, vkofs - 4);
2279 }
2280 
2281 /* Delete all values from key (used in recursive delete)
2282  * hdesc - yer usual hive
2283  * nkofs - current keyoffset
2284  */
2285 
del_allvalues(struct hive * hdesc,int nkofs)2286 void del_allvalues(struct hive *hdesc, int nkofs)
2287 {
2288   int vlistofs, o, vkofs;
2289   int32_t *vlistkey;
2290   struct nk_key *nk;
2291 
2292   nk = (struct nk_key *)(hdesc->buffer + nkofs);
2293   if (nk->id != 0x6b6e) {
2294     printf("del_allvalues: Key pointer not to 'nk' node!\n");
2295     return;
2296   }
2297 
2298   if (!nk->no_values) {
2299     /*    printf("del_avalues: Key has no values!\n"); */
2300     return;
2301   }
2302 
2303   vlistofs = nk->ofs_vallist + 0x1004;
2304   vlistkey = (int32_t *)(hdesc->buffer + vlistofs);
2305 
2306   /* Loop through index and delete all vk's */
2307   for (o = 0; o < nk->no_values; o++) {
2308     vkofs = vlistkey[o] + 0x1004;
2309     del_vk(hdesc, vkofs);
2310   }
2311 
2312   /* Then zap the index, and update nk */
2313   free_block(hdesc, vlistofs-4);
2314   nk->ofs_vallist = -1;
2315   nk->no_values = 0;
2316 }
2317 
2318 
2319 /* Delete single value from key
2320  * hdesc - yer usual hive
2321  * nkofs - current keyoffset
2322  * name  - name of value to delete
2323  * exact - NKF_EXACT to do exact match, else first match
2324  * returns: 0 - ok, 1 - failed
2325  */
2326 
del_value(struct hive * hdesc,int nkofs,char * name,int exact)2327 int del_value(struct hive *hdesc, int nkofs, char *name, int exact)
2328 {
2329   int vlistofs, slot, o, n, vkofs, newlistofs;
2330   int32_t *vlistkey, *tmplist, *newlistkey;
2331   struct nk_key *nk;
2332   char *blank="";
2333 
2334   if (!name || !*name) return(1);
2335 
2336   if (!strcmp(name,"@")) name = blank;
2337 
2338   nk = (struct nk_key *)(hdesc->buffer + nkofs);
2339   if (nk->id != 0x6b6e) {
2340     printf("del_value: Key pointer not to 'nk' node!\n");
2341     return(1);
2342   }
2343 
2344   if (!nk->no_values) {
2345     printf("del_value: Key has no values!\n");
2346     return(1);
2347   }
2348 
2349   vlistofs = nk->ofs_vallist + 0x1004;
2350   vlistkey = (int32_t *)(hdesc->buffer + vlistofs);
2351 
2352   slot = vlist_find(hdesc, vlistofs, nk->no_values, name, TPF_VK);
2353 
2354   if (slot == -1) {
2355     printf("del_value: value %s not found!\n",name);
2356     return(1);
2357   }
2358 
2359   /* Delete vk and data */
2360   vkofs = vlistkey[slot] + 0x1004;
2361   del_vk(hdesc, vkofs);
2362 
2363   /* Copy out old index list */
2364   CREATE(tmplist,int32_t,nk->no_values);
2365   memcpy(tmplist, vlistkey, nk->no_values * sizeof(int32_t));
2366 
2367   free_block(hdesc,vlistofs-4);  /* Get rid of old list */
2368 
2369   nk->no_values--;
2370 
2371   if (nk->no_values) {
2372     newlistofs = alloc_block(hdesc, vlistofs, nk->no_values * sizeof(int32_t));
2373     if (!newlistofs) {
2374       printf("del_value: FATAL: Was not able to alloc new index list\n");
2375       abort();
2376     }
2377     nk = (struct nk_key *)(hdesc->buffer + nkofs); /* In case buffer was moved */
2378 
2379     /* Now copy over, omitting deleted entry */
2380     newlistkey = (int32_t *)(hdesc->buffer + newlistofs + 4);
2381     for (n = 0, o = 0; n < nk->no_values; o++, n++) {
2382       if (o == slot) o++;
2383       newlistkey[n] = tmplist[o];
2384     }
2385     nk->ofs_vallist = newlistofs - 0x1000;
2386   } else {
2387     nk->ofs_vallist = -1;
2388   }
2389   return(0);
2390 }
2391 
2392 
2393 
2394 
2395 /* Add a subkey to a key
2396  * hdesc - usual..
2397  * nkofs - offset of current nk
2398  * name  - name of key to add
2399  * return: ptr to new keystruct, or NULL
2400  */
2401 
2402 #undef AKDEBUG
2403 
add_key(struct hive * hdesc,int nkofs,char * name)2404 struct nk_key *add_key(struct hive *hdesc, int nkofs, char *name)
2405 {
2406 
2407   int slot, newlfofs = 0, oldlfofs = 0, newliofs = 0;
2408   int oldliofs = 0;
2409   int o, n, i, onkofs, newnkofs, cmp;
2410   int rimax, rislot, riofs, namlen;
2411   struct ri_key *ri = NULL;
2412   struct lf_key *newlf = NULL, *oldlf;
2413   struct li_key *newli = NULL, *oldli;
2414   struct nk_key *key, *newnk, *onk;
2415   int32_t hash;
2416 
2417   key = (struct nk_key *)(hdesc->buffer + nkofs);
2418 
2419   if (key->id != 0x6b6e) {
2420     printf("add_key: current ptr not 'nk'\n");
2421     return(NULL);
2422   }
2423 
2424   namlen = strlen(name);
2425 
2426   slot = -1;
2427   if (key->no_subkeys) {   /* It already has subkeys */
2428 
2429     oldlfofs = key->ofs_lf;
2430     oldliofs = key->ofs_lf;
2431 
2432     oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
2433     if (oldlf->id != 0x666c && oldlf->id != 0x686c && oldlf->id != 0x696c && oldlf->id != 0x6972)  {
2434       printf("add_key: index type not supported: 0x%04x\n",oldlf->id);
2435       return(NULL);
2436     }
2437 
2438     rimax = 0; ri = NULL; riofs = 0; rislot = -1;
2439     if (oldlf->id == 0x6972) {  /* Indirect index 'ri', init loop */
2440       riofs = key->ofs_lf;
2441       ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004);
2442       rimax = ri->no_lis-1;
2443 
2444 #ifdef AKDEBUG
2445       printf("add_key: entering 'ri' traverse, rimax = %d\n",rimax);
2446 #endif
2447 
2448       oldliofs = ri->hash[rislot+1].ofs_li;
2449       oldlfofs = ri->hash[rislot+1].ofs_li;
2450 
2451     }
2452 
2453     do {   /* 'ri' loop, at least run once if no 'ri' deep index */
2454 
2455       if (ri) { /* Do next 'ri' slot */
2456 	rislot++;
2457 	oldliofs = ri->hash[rislot].ofs_li;
2458 	oldlfofs = ri->hash[rislot].ofs_li;
2459 	oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
2460 	oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
2461       }
2462 
2463       oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
2464       oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
2465 
2466 #ifdef AKDEBUG
2467       printf("add_key: top of ri-loop: rislot = %d, rimax = %d\n",rislot,rimax);
2468 #endif
2469       slot = -1;
2470 
2471       if (oldli->id == 0x696c) {  /* li */
2472 
2473 #ifdef AKDEBUG
2474 	printf("add_key: li slot allocate\n");
2475 #endif
2476 
2477 	FREE(newli);
2478 	ALLOC(newli, 8 + 4*oldli->no_keys + 4, 1);
2479 	newli->no_keys = oldli->no_keys;
2480 	newli->id = oldli->id;
2481 
2482 	/* Now copy old, checking where to insert (alphabetically) */
2483 	for (o = 0, n = 0; o < oldli->no_keys; o++,n++) {
2484 	  onkofs = oldli->hash[o].ofs_nk;
2485 	  onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004);
2486 	  if (slot == -1) {
2487 #if 1
2488 	    printf("add_key: cmp <%s> with <%s>\n",name,onk->keyname);
2489 #endif
2490 
2491 	    cmp = strn_casecmp(name, onk->keyname, (namlen > onk->len_name) ? namlen : onk->len_name);
2492 	    if (!cmp) {
2493 	      printf("add_key: key %s already exists!\n",name);
2494 	      FREE(newli);
2495 	      return(NULL);
2496 	    }
2497 	    if ( cmp < 0) {
2498 	      slot = o;
2499 	      rimax = rislot; /* Cause end of 'ri' search, too */
2500 	      n++;
2501 #ifdef AKDEBUG
2502 	      printf("add_key: li-match: slot = %d\n",o);
2503 #endif
2504 	    }
2505 	  }
2506 	  newli->hash[n].ofs_nk = oldli->hash[o].ofs_nk;
2507 	}
2508 	if (slot == -1) slot = oldli->no_keys;
2509 
2510       } else { /* lf or lh */
2511 
2512 	oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
2513 
2514 	FREE(newlf);
2515 	ALLOC(newlf, 8 + 8*oldlf->no_keys + 8, 1);
2516 	newlf->no_keys = oldlf->no_keys;
2517 	newlf->id = oldlf->id;
2518 #ifdef AKDEBUG
2519 	printf("add_key: new lf/lh no_keys: %d\n",newlf->no_keys);
2520 #endif
2521 
2522 	/* Now copy old, checking where to insert (alphabetically) */
2523 	for (o = 0, n = 0; o < oldlf->no_keys; o++,n++) {
2524 	  onkofs = oldlf->hash[o].ofs_nk;
2525 	  onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004);
2526 	  if (slot == -1) {
2527 
2528 #if 0
2529 	    printf("add_key: cmp <%s> with <%s>\n",name,onk->keyname);
2530 #endif
2531 	    cmp = strn_casecmp(name, onk->keyname, (namlen > onk->len_name) ? namlen : onk->len_name);
2532 	    if (!cmp) {
2533 	      printf("add_key: key %s already exists!\n",name);
2534 	      FREE(newlf);
2535 	      return(NULL);
2536 	    }
2537 	    if ( cmp < 0 ) {
2538 	      slot = o;
2539 	      rimax = rislot;  /* Cause end of 'ri' search, too */
2540 	      n++;
2541 #ifdef AKDEBUG
2542 	      printf("add_key: lf-match: slot = %d\n",o);
2543 #endif
2544 	    }
2545 	  }
2546 	  newlf->hash[n].ofs_nk = oldlf->hash[o].ofs_nk;
2547 	  newlf->hash[n].name[0] = oldlf->hash[o].name[0];
2548 	  newlf->hash[n].name[1] = oldlf->hash[o].name[1];
2549 	  newlf->hash[n].name[2] = oldlf->hash[o].name[2];
2550 	  newlf->hash[n].name[3] = oldlf->hash[o].name[3];
2551 	}
2552 	if (slot == -1) slot = oldlf->no_keys;
2553       } /* li else check */
2554 
2555 
2556     } while ( (rislot < rimax) && (rimax > 0));  /* 'ri' wrapper loop */
2557 
2558   } else { /* Parent was empty, make new index block */
2559 #ifdef AKDEBUG
2560     printf("add_key: new index!\n");
2561 #endif
2562     ALLOC(newlf, 8 + 8, 1);
2563     newlf->no_keys = 0 ;    /* Will increment to 1 when filling in the offset later */
2564     /* Use ID (lf, lh or li) we fetched from root node, so we use same as rest of hive */
2565     newlf->id = hdesc->nkindextype;
2566     slot = 0;
2567   } /* if has keys before */
2568 
2569 
2570   /* Make and fill in new nk */
2571   newnkofs = alloc_block(hdesc, nkofs, sizeof(struct nk_key) + strlen(name));
2572   if (!newnkofs) {
2573     printf("add_key: unable to allocate space for new key descriptor for %s!\n",name);
2574     FREE(newlf);
2575     FREE(newli);
2576     return(NULL);
2577   }
2578   key = (struct nk_key *)(hdesc->buffer + nkofs);  /* In case buffer moved */
2579   newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4);
2580 
2581   newnk->id            = 0x6b6e;
2582   newnk->type          = KEY_NORMAL;    /* Some versions use 0x1020 a lot.. */
2583   newnk->ofs_parent    = nkofs - 0x1004;
2584   newnk->no_subkeys    = 0;
2585   newnk->ofs_lf        = -1;
2586   newnk->no_values     = 0;
2587   newnk->ofs_vallist   = -1;
2588   newnk->ofs_sk        = key->ofs_sk; /* Get parents for now. 0 or -1 here crashes XP */
2589   newnk->ofs_classnam  = -1;
2590   newnk->len_name      = strlen(name);
2591   newnk->len_classnam  = 0;
2592   memcpy(newnk->keyname, name, newnk->len_name);
2593 
2594   if (newli) {  /* Handle li */
2595 
2596 #if AKDEBUG
2597     printf("add_key: li fill at slot: %d\n",slot);
2598 #endif
2599 
2600     /* And put its offset into parents index list */
2601     newli->hash[slot].ofs_nk = newnkofs - 0x1000;
2602     newli->no_keys++;
2603 
2604     /* Allocate space for our new li list and copy it into reg */
2605     newliofs = alloc_block(hdesc, nkofs, 8 + 4*newli->no_keys);
2606     if (!newliofs) {
2607       printf("add_key: unable to allocate space for new index table for %s!\n",name);
2608       FREE(newli);
2609       free_block(hdesc,newnkofs);
2610       return(NULL);
2611     }
2612     key = (struct nk_key *)(hdesc->buffer + nkofs);  /* In case buffer moved */
2613     newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4);
2614 
2615     /*    memcpy(hdesc->buffer + newliofs + 4, newli, 8 + 4*newli->no_keys); */
2616     fill_block(hdesc, newliofs, newli, 8 + 4*newli->no_keys);
2617 
2618 
2619   } else {  /* lh or lf */
2620 
2621 #ifdef AKDEBUG
2622     printf("add_key: lf/lh fill at slot: %d, rislot: %d\n",slot,rislot);
2623 #endif
2624     /* And put its offset into parents index list */
2625     newlf->hash[slot].ofs_nk = newnkofs - 0x1000;
2626     newlf->no_keys++;
2627     if (newlf->id == 0x666c) {        /* lf hash */
2628       newlf->hash[slot].name[0] = 0;
2629       newlf->hash[slot].name[1] = 0;
2630       newlf->hash[slot].name[2] = 0;
2631       newlf->hash[slot].name[3] = 0;
2632       strncpy(newlf->hash[slot].name, name, 4);
2633     } else if (newlf->id == 0x686c) {  /* lh. XP uses this. hashes whole name */
2634       for (i = 0,hash = 0; i < strlen(name); i++) {
2635 	hash *= 37;
2636 	hash += reg_touppertable[(unsigned char)name[i]];
2637       }
2638       newlf->lh_hash[slot].hash = hash;
2639     }
2640 
2641     /* Allocate space for our new lf list and copy it into reg */
2642     newlfofs = alloc_block(hdesc, nkofs, 8 + 8*newlf->no_keys);
2643     if (!newlfofs) {
2644       printf("add_key: unable to allocate space for new index table for %s!\n",name);
2645       FREE(newlf);
2646       free_block(hdesc,newnkofs);
2647       return(NULL);
2648     }
2649 
2650     key = (struct nk_key *)(hdesc->buffer + nkofs);  /* In case buffer moved */
2651     newnk = (struct nk_key *)(hdesc->buffer + newnkofs + 4);
2652 
2653     /*    memcpy(hdesc->buffer + newlfofs + 4, newlf, 8 + 8*newlf->no_keys); */
2654     fill_block(hdesc, newlfofs, newlf, 8 + 8*newlf->no_keys);
2655 
2656   } /* li else */
2657 
2658 
2659   /* Update parent, and free old lf list */
2660   key->no_subkeys++;
2661   if (ri) {  /* ri index */
2662     ri->hash[rislot].ofs_li = (newlf ? newlfofs : newliofs) - 0x1000;
2663   } else { /* Parent key */
2664     key->ofs_lf = (newlf ? newlfofs : newliofs) - 0x1000;
2665   }
2666 
2667   if (newlf && oldlfofs) free_block(hdesc,oldlfofs + 0x1000);
2668   if (newli && oldliofs) free_block(hdesc,oldliofs + 0x1000);
2669 
2670   FREE(newlf);
2671   FREE(newli);
2672   return(newnk);
2673 
2674 }
2675 
2676 /* Delete a subkey from a key
2677  * hdesc - usual..
2678  * nkofs - offset of current nk
2679  * name  - name of key to delete (must match exactly, also case)
2680  * return: 1 - err, 0 - ok
2681  */
2682 
2683 #undef DKDEBUG
2684 
del_key(struct hive * hdesc,int nkofs,char * name)2685 int del_key(struct hive *hdesc, int nkofs, char *name)
2686 {
2687 
2688   int slot = 0, newlfofs = 0, oldlfofs = 0, o, n, onkofs,  delnkofs;
2689   int oldliofs = 0, no_keys = 0, newriofs = 0;
2690   int namlen;
2691   int rimax, riofs, rislot;
2692   struct ri_key *ri, *newri = NULL;
2693   struct lf_key *newlf = NULL, *oldlf = NULL;
2694   struct li_key *newli = NULL, *oldli = NULL;
2695   struct nk_key *key, *onk, *delnk;
2696   char fullpath[501];
2697 
2698   key = (struct nk_key *)(hdesc->buffer + nkofs);
2699 
2700   namlen = strlen(name);
2701 
2702 #ifdef DKDEBUG
2703   printf("del_key: deleting: <%s>\n",name);
2704 #endif
2705 
2706 
2707   if (key->id != 0x6b6e) {
2708     printf("del_key: current ptr not nk\n");
2709     return(1);
2710   }
2711 
2712   slot = -1;
2713   if (!key->no_subkeys) {
2714     printf("del_key: key has no subkeys!\n");
2715     return(1);
2716   }
2717 
2718   oldlfofs = key->ofs_lf;
2719   oldliofs = key->ofs_lf;
2720 
2721   oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
2722   if (oldlf->id != 0x666c && oldlf->id != 0x686c && oldlf->id != 0x696c && oldlf->id != 0x6972)  {
2723     printf("del_key: index other than 'lf', 'li' or 'lh' not supported yet. 0x%04x\n",oldlf->id);
2724     return(1);
2725   }
2726 
2727   rimax = 0; ri = NULL; riofs = 0;
2728   rislot = 0;
2729 
2730   if (oldlf->id == 0x6972) {  /* Indirect index 'ri', init loop */
2731     riofs = key->ofs_lf;
2732     ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004);
2733     rimax = ri->no_lis-1;
2734 
2735 #ifdef DKDEBUG
2736     printf("del_key: entering 'ri' traverse, rimax = %d\n",rimax);
2737 #endif
2738 
2739     rislot = -1; /* Starts at slot 0 below */
2740 
2741   }
2742 
2743   do {   /* 'ri' loop, at least run once if no 'ri' deep index */
2744 
2745     if (ri) { /* Do next 'ri' slot */
2746       rislot++;
2747       oldliofs = ri->hash[rislot].ofs_li;
2748       oldlfofs = ri->hash[rislot].ofs_li;
2749     }
2750 
2751     oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
2752     oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
2753 
2754 #ifdef DKDEBUG
2755     printf("del_key: top of ri-loop: rislot = %d\n",rislot);
2756 #endif
2757     slot = -1;
2758 
2759     if (oldlf->id == 0x696c) {   /* 'li' handler */
2760 #ifdef DKDEBUG
2761       printf("del_key: li handler\n");
2762 #endif
2763 
2764       FREE(newli);
2765       ALLOC(newli, 8 + 4*oldli->no_keys - 4, 1);
2766       newli->no_keys = oldli->no_keys - 1; no_keys = newli->no_keys;
2767       newli->id = oldli->id;
2768 
2769       /* Now copy old, checking where to delete */
2770       for (o = 0, n = 0; o < oldli->no_keys; o++,n++) {
2771 	onkofs = oldli->hash[o].ofs_nk;
2772 	onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004);
2773 	if (slot == -1 && onk->len_name == namlen && !strncmp(name, onk->keyname, (onk->len_name > namlen) ? onk->len_name : namlen)) {
2774 	  slot = o;
2775 	  delnkofs = onkofs; delnk = onk;
2776 	  rimax = rislot;
2777 	  o++;
2778 	}
2779 	newli->hash[n].ofs_nk = oldli->hash[o].ofs_nk;
2780       }
2781 
2782 
2783     } else { /* 'lf' or 'lh' are similar */
2784 
2785 #ifdef DKDEBUG
2786       printf("del_key: lf or lh handler\n");
2787 #endif
2788       FREE(newlf);
2789       ALLOC(newlf, 8 + 8*oldlf->no_keys - 8, 1);
2790 #ifdef DKDEBUG
2791       printf("alloc newlf: %x\n",newlf);
2792 #endif
2793       newlf->no_keys = oldlf->no_keys - 1; no_keys = newlf->no_keys;
2794       newlf->id = oldlf->id;
2795 
2796       /* Now copy old, checking where to delete */
2797       for (o = 0, n = 0; o < oldlf->no_keys; o++,n++) {
2798 
2799 	onkofs = oldlf->hash[o].ofs_nk;
2800 	onk = (struct nk_key *)(onkofs + hdesc->buffer + 0x1004);
2801 
2802 	if (slot == -1 && (onk->len_name == namlen) && !strncmp(name, onk->keyname, onk->len_name)) {
2803 	  slot = o;
2804 	  delnkofs = onkofs; delnk = onk;
2805 	  rimax = rislot;
2806 	  o++;
2807 	}
2808 
2809 	if (n < newlf->no_keys) { /* Only store if not last index in old */
2810 #ifdef DKDEBUG
2811 	  printf("del_key: n = %d, o = %d\n",n,o);
2812 #endif
2813 	  newlf->hash[n].ofs_nk = oldlf->hash[o].ofs_nk;
2814 	  newlf->hash[n].name[0] = oldlf->hash[o].name[0];
2815 	  newlf->hash[n].name[1] = oldlf->hash[o].name[1];
2816 	  newlf->hash[n].name[2] = oldlf->hash[o].name[2];
2817 	  newlf->hash[n].name[3] = oldlf->hash[o].name[3];
2818 	}
2819 
2820       }
2821     } /* else lh or lf */
2822 
2823   } while (rislot < rimax);  /* ri traverse loop */
2824 
2825   if (slot == -1) {
2826     printf("del_key: subkey %s not found!\n",name);
2827     FREE(newlf);
2828     FREE(newli);
2829     return(1);
2830   }
2831 
2832 #ifdef DKDEBUG
2833   printf("del_key: key found at slot %d\n",slot);
2834 #endif
2835 
2836   if (delnk->no_values || delnk->no_subkeys) {
2837     printf("del_key: subkey %s has subkeys or values. Not deleted.\n",name);
2838     FREE(newlf);
2839     FREE(newli);
2840     return(1);
2841   }
2842 
2843   /* Allocate space for our new lf list and copy it into reg */
2844   if ( no_keys && (newlf || newli) ) {
2845     newlfofs = alloc_block(hdesc, nkofs, 8 + (newlf ? 8 : 4) * no_keys);
2846 
2847     /* alloc_block may invalidate pointers if hive expanded. Recalculate this one.
2848      * Thanks to Jacky To for reporting it here, and suggesting a fix
2849      * (better would of course be for me to redesign stuff :)
2850      */
2851     if (delnkofs) delnk = (struct nk_key *)(delnkofs + hdesc->buffer + 0x1004);
2852 
2853 #ifdef DKDEBUG
2854     printf("del_key: alloc_block for index returns: %x\n",newlfofs);
2855 #endif
2856     if (!newlfofs) {
2857       printf("del_key: WARNING: unable to allocate space for new key descriptor for %s! Not deleted\n",name);
2858       FREE(newlf);
2859       return(1);
2860     }
2861     key = (struct nk_key *)(hdesc->buffer + nkofs);
2862     oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
2863     oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
2864 
2865 
2866 
2867     /*    memcpy(hdesc->buffer + newlfofs + 4,
2868 	   ((void *)newlf ? (void *)newlf : (void *)newli), 8 + (newlf ? 8 : 4) * no_keys);
2869     */
2870     fill_block(hdesc, newlfofs,
2871 	   ((void *)newlf ? (void *)newlf : (void *)newli), 8 + (newlf ? 8 : 4) * no_keys);
2872 
2873 
2874   } else {  /* Last deleted, will throw away index */
2875     newlfofs = 0xfff;  /* We subtract 0x1000 later */
2876   }
2877 
2878   if (newlfofs < 0xfff) {
2879     printf("del_key: ERROR: newlfofs = %x\n",newlfofs);
2880 #if DOCORE
2881     if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
2882     abort();
2883 #endif
2884   }
2885 
2886   /* Check for CLASS data, if so, deallocate it too */
2887   if (delnk->len_classnam) {
2888     free_block(hdesc, delnk->ofs_classnam + 0x1000);
2889   }
2890   /* Now it's safe to zap the nk */
2891   free_block(hdesc, delnkofs + 0x1000);
2892   /* And the old index list */
2893   free_block(hdesc, (oldlfofs ? oldlfofs : oldliofs) + 0x1000);
2894 
2895   /* Update parent */
2896   key->no_subkeys--;
2897 
2898   key = (struct nk_key *)(hdesc->buffer + nkofs);
2899   oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
2900   oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
2901 
2902   if (ri) {
2903     ri = (struct ri_key *)(hdesc->buffer + riofs + 0x1004); /* In case realloc */
2904 
2905     if (newlfofs == 0xfff) {
2906 
2907       *fullpath = 0;
2908       get_abs_path(hdesc, nkofs, fullpath, 480);
2909 
2910       VERBF(hdesc,"del_key: need to delete ri-slot %d for %x - %s\n", rislot,nkofs,fullpath );
2911 
2912       if (ri->no_lis > 1) {  /* We have subindiceblocks left? */
2913 	/* Delete from array */
2914 	ALLOC(newri, 8 + 4*ri->no_lis - 4, 1);
2915 	newri->no_lis = ri->no_lis - 1;
2916 	newri->id = ri->id;
2917 	for (o = 0, n = 0; o < ri->no_lis; o++,n++) {
2918 	  if (n == rislot) o++;
2919 	  newri->hash[n].ofs_li = ri->hash[o].ofs_li;
2920 	}
2921 	newriofs = alloc_block(hdesc, nkofs, 8 + newri->no_lis*4 );
2922 	if (!newriofs) {
2923 	  printf("del_key: WARNING: unable to allocate space for ri-index for %s! Not deleted\n",name);
2924 	  FREE(newlf);
2925 	  FREE(newri);
2926 	  return(1);
2927 	}
2928 	key = (struct nk_key *)(hdesc->buffer + nkofs);
2929 	oldli = (struct li_key *)(hdesc->buffer + oldliofs + 0x1004);
2930 	oldlf = (struct lf_key *)(hdesc->buffer + oldlfofs + 0x1004);
2931 
2932 	fill_block(hdesc, newriofs, newri, 8 + newri->no_lis * 4);
2933 	free_block(hdesc, riofs + 0x1000);
2934 	key->ofs_lf = newriofs - 0x1000;
2935 	FREE(newri);
2936       } else { /* Last entry in ri was deleted, get rid of it, key is empty */
2937 	VERB(hdesc,"del_key: .. and that was the last one. key now empty!\n");
2938 	free_block(hdesc, riofs + 0x1000);
2939 	key->ofs_lf = -1;
2940       }
2941     } else {
2942       ri->hash[rislot].ofs_li = newlfofs - 0x1000;
2943     }
2944   } else {
2945     key->ofs_lf = newlfofs - 0x1000;
2946   }
2947 
2948   FREE(newlf);
2949   return(0);
2950 
2951 }
2952 
2953 /* Recursive delete keys
2954  * hdesc - usual..
2955  * nkofs - offset of current nk
2956  * name  - name of key to delete
2957  * return: 0 - ok, 1 fail
2958  */
rdel_keys(struct hive * hdesc,char * path,int vofs)2959 void rdel_keys(struct hive *hdesc, char *path, int vofs)
2960 {
2961   struct nk_key *key;
2962   int nkofs;
2963   struct ex_data ex;
2964   int count = 0, countri = 0;
2965 
2966 
2967   if (!path || !*path) return;
2968 
2969   nkofs = trav_path(hdesc, vofs, path, TPF_NK_EXACT);
2970 
2971   if(!nkofs) {
2972     printf("rdel_keys: Key <%s> not found\n",path);
2973     return;
2974   }
2975   nkofs += 4;
2976 
2977   key = (struct nk_key *)(hdesc->buffer + nkofs);
2978 
2979   /*
2980   VERBF(hdesc,"rdel of node at offset 0x%0x\n",nkofs);
2981   */
2982 
2983   if (key->id != 0x6b6e) {
2984     printf("rdel_keys: ERROR: Not a 'nk' node!\n");
2985 
2986     if (hdesc->state & HMODE_TRACE) debugit(hdesc->buffer,hdesc->size);
2987     return;
2988   }
2989 
2990 #if 0
2991   printf("Node has %d subkeys and %d values\n",key->no_subkeys,key->no_values);
2992 #endif
2993   if (key->no_subkeys) {
2994     while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) {
2995 #if 0
2996       printf("%s\n",ex.name);
2997 #endif
2998       rdel_keys(hdesc, ex.name, nkofs);
2999       count = 0;
3000       countri = 0;
3001       FREE(ex.name);
3002     }
3003   }
3004 
3005   del_allvalues(hdesc, nkofs);
3006   key = (struct nk_key *)(hdesc->buffer + nkofs);
3007   del_key(hdesc, key->ofs_parent+0x1004, path);
3008 
3009 }
3010 
3011 
3012 /* Get and copy keys CLASS-data (if any) to buffer
3013  * Returns a buffer with the data (first int32_t is size). see ntreg.h
3014  * NOTE: caller must deallocate buffer! a simple free(keyval) will suffice.
3015  */
get_class(struct hive * hdesc,int curnk,char * path)3016 struct keyval *get_class(struct hive *hdesc,
3017 			    int curnk, char *path)
3018 {
3019   int clen = 0, dofs = 0, nkofs;
3020   struct nk_key *key;
3021   struct keyval *data;
3022   void *classdata;
3023 
3024   if (!path && !curnk) return(NULL);
3025 
3026   nkofs = trav_path(hdesc, curnk, path, 0);
3027 
3028   if(!nkofs) {
3029     printf("get_class: Key <%s> not found\n",path);
3030     return(NULL);
3031   }
3032   nkofs += 4;
3033   key = (struct nk_key *)(hdesc->buffer + nkofs);
3034 
3035   clen = key->len_classnam;
3036   if (!clen) {
3037     printf("get_class: Key has no class data.\n");
3038     return(NULL);
3039   }
3040 
3041   dofs = key->ofs_classnam;
3042   classdata = (void *)(hdesc->buffer + dofs + 0x1004);
3043 
3044 #if 0
3045   printf("get_class: len_classnam = %d\n",clen);
3046   printf("get_class: ofs_classnam = 0x%x\n",dofs);
3047 #endif
3048 
3049   ALLOC(data, sizeof(struct keyval) + clen,1);
3050   data->len = clen;
3051   memcpy(&data->data, classdata, clen);
3052   return(data);
3053 }
3054 
3055 
3056 /* Write to registry value.
3057  * If same size as existing, copy back in place to avoid changing too much
3058  * otherwise allocate new dataspace, then free the old
3059  * Thus enough space to hold both new and old data is needed
3060  * Pass inn buffer with data len as first DWORD (as routines above)
3061  * returns: 0 - error, len - OK (len of data)
3062  */
3063 
put_buf2val(struct hive * hdesc,struct keyval * kv,int vofs,char * path,int type,int exact)3064 int put_buf2val(struct hive *hdesc, struct keyval *kv,
3065 		int vofs, char *path, int type, int exact )
3066 {
3067   int l;
3068   void *keydataptr, *addr;
3069   struct db_key *db;
3070   int copylen, blockofs, blocksize, restlen, point, i, list, parts;
3071 
3072   if (!kv) return(0);
3073 
3074 
3075   l = get_val_len(hdesc, vofs, path, exact);
3076   if (l == -1) return(0);  /* error */
3077   //  printf("put_buf2val: l = %d\n",l);
3078 
3079   //  printf("put_buf2val: %s, kv len = %d, l = %d\n",path,kv->len,l);
3080 
3081 
3082   if (kv->len != l) {  /* Realloc data block if not same size as existing */
3083     if (!alloc_val_data(hdesc, vofs, path, kv->len, exact)) {
3084       printf("put_buf2val: %s : alloc_val_data failed!\n",path);
3085       return(0);
3086     }
3087   }
3088 
3089   keydataptr = get_val_data(hdesc, vofs, path, type, exact);
3090   if (!keydataptr) {
3091       printf("put_buf2val: %s : get_val_data failed!\n",path);
3092       return(0); /* error */
3093   }
3094 
3095 
3096 
3097   if (kv->len > VAL_DIRECT_LIMIT) {       /* Where do the db indirects start? seems to be around 16k */
3098     db = (struct db_key *)keydataptr;
3099     if (db->id != 0x6264) abort();
3100     parts = db->no_part;
3101     list = db->ofs_data + 0x1004;
3102     printf("put_buf2val: Long value: parts = %d, list = %x\n",parts,list);
3103 
3104     point = 0;
3105     restlen = kv->len;
3106     for (i = 0; i < parts; i++) {
3107       blockofs = get_int(hdesc->buffer + list + (i << 2)) + 0x1000;
3108       blocksize = -get_int(hdesc->buffer + blockofs) - 8;
3109 
3110       /* Copy this part, up to size of block or rest lenght in last block */
3111       copylen = (blocksize > restlen) ? restlen : blocksize;
3112 
3113       printf("put_buf2val: Datablock %d offset %x, size %x (%d)\n",i,blockofs,blocksize,blocksize);
3114       printf("             : Point = %x, restlen = %x, copylen = %x\n",point,restlen,copylen);
3115 
3116       addr = (void *)&(kv->data) + point;
3117       fill_block( hdesc, blockofs, addr, copylen);
3118 
3119       //      debugit((char *)&(kr->data), l);
3120 
3121       point += copylen;
3122       restlen -= copylen;
3123     }
3124   } else {
3125     memcpy(keydataptr, &kv->data, kv->len);
3126   }
3127 
3128   hdesc->state |= HMODE_DIRTY;
3129 
3130   return(kv->len);
3131 }
3132 
3133 /* And, yer basic DWORD write */
3134 
put_dword(struct hive * hdesc,int vofs,char * path,int exact,int dword)3135 int put_dword(struct hive *hdesc, int vofs, char *path, int exact, int dword)
3136 {
3137   struct keyval *kr;
3138   int r;
3139 
3140   ALLOC(kr,1,sizeof(int)+sizeof(int));
3141 
3142   kr->len = sizeof(int);
3143   kr->data = dword;
3144 
3145   r = put_buf2val(hdesc, kr, vofs, path, REG_DWORD, exact);
3146 
3147   FREE(kr);
3148 
3149   return(r);
3150 }
3151 
3152 /* ================================================================ */
3153 
3154 /* Code to export registry entries to .reg file initiated by
3155  * Leo von Klenze
3156  * Then expanded a bit to handle more types etc.
3157  */
3158 
3159 /*
3160  * converts a value string from an registry entry into a c string. It does not
3161  * use any encoding functions.
3162  * It works very primitive by just taking every second char.
3163  * The caller must free the resulting string, that was allocated with malloc.
3164  *
3165  * string:  string where every second char is \0
3166  * len:     length of the string
3167  * return:  the converted string as char*
3168  */
3169 char *
string_regw2prog(void * string,int len)3170 string_regw2prog(void *string, int len)
3171 {
3172     int i, k;
3173     char *cstring;
3174 
3175     int out_len = 0;
3176     for(i = 0; i < len; i += 2)
3177     {
3178         unsigned v = ((unsigned char *)string)[i] + ((unsigned char *)string)[i+1] * 256u;
3179         if (v < 128)
3180             out_len += 1;
3181         else if(v < 0x800)
3182             out_len += 2;
3183         else
3184             out_len += 3;
3185     }
3186     CREATE(cstring,char,out_len+1);
3187 
3188     for(i = 0, k = 0; i < len; i += 2)
3189     {
3190         unsigned v = ((unsigned char *)string)[i] + ((unsigned char *)string)[i+1] * 256u;
3191         if (v < 128)
3192             cstring[k++] = v;
3193         else if(v < 0x800) {
3194             cstring[k++] = 0xc0 | (v >> 6);
3195             cstring[k++] = 0x80 | (v & 0x3f);
3196         } else {
3197             cstring[k++] = 0xe0 | (v >> 12);
3198             cstring[k++] = 0x80 | ((v >> 6) & 0x3f);
3199             cstring[k++] = 0x80 | (v & 0x3f);
3200         }
3201     }
3202     cstring[out_len] = '\0';
3203 
3204     return cstring;
3205 }
3206 
3207 #if 0        // Not used at the moment
3208 static char *
3209 string_rega2prog(void *string, int len)
3210 {
3211     int i, k;
3212     char *cstring;
3213 
3214     int out_len = 0;
3215     for(i = 0; i < len; ++i)
3216     {
3217         unsigned v = ((unsigned char *)string)[i];
3218         if (v < 128)
3219             out_len += 1;
3220         else
3221             out_len += 2;
3222     }
3223     CREATE(cstring,char,out_len+1);
3224 
3225     for(i = 0, k = 0; i < len; ++i)
3226     {
3227         unsigned v = ((unsigned char *)string)[i];
3228         if (v < 128)
3229             cstring[k++] = v;
3230         else {
3231             cstring[k++] = 0xc0 | (v >> 6);
3232             cstring[k++] = 0x80 | (v & 0x3f);
3233         }
3234     }
3235     cstring[out_len] = '\0';
3236 
3237     return cstring;
3238 }
3239 
3240 static void
3241 string_prog2rega(char *string, int len)
3242 {
3243     char *out = string;
3244     unsigned char *in = (unsigned char*) string;
3245 
3246     for (;*in; ++in) {
3247         if (!(*in & 0x80)) {
3248             *out++ = *in;
3249         } else if ((in[0] & 0xe0) == 0xc0 && in[1]) {
3250             *out++ = (in[0] & 0x1f) << 6 | (in[1] & 0x3f);
3251             ++in;
3252         } else if (in[1] && in[2]) {
3253             /* assume 3 byte*/
3254             *out++ = (in[1] & 0xf) << 6 | (in[2] & 0x3f);
3255             in += 2;
3256         }
3257     }
3258     *out = 0;
3259 }
3260 #endif  // Not used
3261 
3262 
3263 static char *
string_prog2regw(void * string,int len,int * out_len)3264 string_prog2regw(void *string, int len, int *out_len)
3265 {
3266     unsigned char *regw = (unsigned char*) malloc(len*2+2);
3267     unsigned char *out = regw;
3268     unsigned char *in = (unsigned char*) string;
3269 
3270     for (;len>0; ++in, --len) {
3271         if (!(in[0] & 0x80)) {
3272             *out++ = *in;
3273             *out++ = 0;
3274         } else if ((in[0] & 0xe0) == 0xc0 && len >= 2) {
3275             *out++ = (in[0] & 0x1f) << 6 | (in[1] & 0x3f);
3276             *out++ = (in[0] & 0x1f) >> 2;
3277             ++in, --len;
3278         } else if (len >= 3) {
3279             /* assume 3 byte*/
3280             *out++ = (in[1] & 0xf) << 6 | (in[2] & 0x3f);
3281             *out++ = (in[0] & 0xf) << 4 | ((in[1] & 0x3f) >> 2);
3282             in += 2;
3283             len -= 2;
3284         }
3285     }
3286     *out_len = out - regw;
3287     out[0] = out[1] = 0;
3288     return (char *) regw;
3289 }
3290 
3291 static char *
quote_string(const char * s)3292 quote_string(const char *s)
3293 {
3294 	int len = strlen(s);
3295 	const char *p;
3296 	char *dst, *out;
3297 
3298 	for (p = s; *p; ++p)
3299 		if (*p == '\\' || *p == '\"')
3300 			++len;
3301 
3302 	dst = out = (char*) malloc(len + 1);
3303 	for (p = s; *p; ++p) {
3304 		if (*p == '\\' || *p == '\"')
3305 			*dst++ = '\\';
3306 		*dst++ = *p;
3307 	}
3308 	*dst = 0;
3309 	return out;
3310 }
3311 
3312 static void
export_bin(int type,char * value,int len,int col,FILE * file)3313 export_bin(int type, char *value, int len, int col, FILE* file)
3314 {
3315   int byte;
3316 
3317   if (type == REG_BINARY) {
3318     fprintf(file, "hex:");
3319     col += 4;
3320   } else {
3321     fprintf(file, "hex(%x):", type);
3322     col += 7;
3323   }
3324   byte = 0;
3325   int start = (col  - 2) / 3;
3326   while (byte + 1 < len) { /* go byte by byte.. probably slow.. */
3327     fprintf(file, "%02x,", (unsigned char)value[byte]);
3328     byte++;
3329     if (!((byte + start) % 25)) fprintf(file, "\\\r\n  ");
3330   }
3331   if (len)
3332     fprintf(file, "%02x\r\n", (unsigned char)value[len - 1]);
3333 }
3334 
3335 /*
3336  * Exports the named subkey and its values to the given file.
3337  *
3338  * hdesc:   registry hive
3339  * nkofs:   offset of parent node
3340  * name:    name of key to export
3341  * prefix:  prefix for each key. This prefix is prepended to the keyname
3342  * file:    file descriptor of destination file
3343  */
export_subkey(struct hive * hdesc,int nkofs,char * name,char * prefix,FILE * file)3344 void export_subkey(struct hive *hdesc, int nkofs, char *name, char *prefix, FILE *file)
3345 {
3346     int newofs;
3347     int count = 0;
3348     int countri = 0;
3349     int len;
3350     char *path = (char*) malloc(1024);
3351     char *value;
3352     struct nk_key *key;
3353     struct ex_data ex;
3354     struct vex_data vex;
3355 
3356 
3357     // switch to key
3358     newofs = trav_path(hdesc, nkofs, name, TPF_NK_EXACT);
3359     if(!newofs)
3360     {
3361         printf("export_subkey: Key '%s' not found!\n", name);
3362         free(path);
3363         return;
3364     }
3365     nkofs = newofs + 4;
3366 
3367     // get the key
3368     key = (struct nk_key *)(hdesc->buffer + nkofs);
3369     printf("Exporting key '%.*s' with %d subkeys and %d values...\n",
3370             key->len_name, key->keyname, key->no_subkeys, key->no_values);
3371 
3372     *path = 0;
3373     get_abs_path(hdesc, nkofs, path, 1024);
3374 
3375     // export the key
3376     fprintf(file, "\r\n");
3377     fprintf(file, "[%s\%s]\r\n", prefix, path);
3378     // export values
3379     if(key->no_values)
3380     {
3381         while ((ex_next_v(hdesc, nkofs, &count, &vex) > 0))
3382         {
3383             int col = 0;
3384             char *name = quote_string(vex.name);
3385 
3386             /* print name */
3387             if (!name[0]) {
3388               fprintf(file, "@=");
3389               free(name);
3390               name = str_dup("@");
3391               col += 2;
3392             } else {
3393               fprintf(file, "\"%s\"=", name);
3394               col += strlen(name) + 3;
3395             }
3396 
3397             if(vex.type == REG_DWORD)
3398             {
3399                 fprintf(file, "dword:%08x\r\n", vex.val);
3400             }
3401             else if(vex.type == REG_SZ)
3402             {
3403  	        char *val, *quoted;
3404 	        value = (char *)get_val_data(hdesc, nkofs, name, vex.type, TPF_VK_EXACT|TPF_ABS);
3405 	        len = get_val_len(hdesc, nkofs, name, TPF_VK_EXACT|TPF_ABS);
3406 
3407                 val = string_regw2prog(value, len);
3408                 /* dump as binary if invalid characters are embedded */
3409                 if (strchr(val, 0xa) || strchr(val, 0xd))
3410                 {
3411                     free(val);
3412                     //if (len >= 2 && value[len-2] == 0 && value[len-1] == 0) len -= 2;
3413                     export_bin(vex.type, value, len, col, file);
3414                 }
3415                 else
3416                 {
3417                     quoted = quote_string(val);
3418                     free(val);
3419                     fprintf(file, "\"%s\"", quoted);
3420                     free(quoted);
3421                     fprintf(file, "\r\n");
3422                }
3423             }
3424 	    else
3425             {
3426 	      /* All other types seems to simply be hexdumped. Format is:
3427 		 "valuename"=hex(typenum):xx,xx,xx,xx,xx...
3428 		 for example:
3429 		 "qword64"=hex(b):4e,03,51,db,fa,04,00,00
3430 		 this is type = 0xb = 11 = REG_QWORD
3431 		 "expandstring"=hex(2):46,00,6f,00,6f,00,62,00,61,00,72,00,20,00,25,00,73,00,20,\
3432 		 00,42,00,61,00,7a,00,00,00
3433 		 this is type 2 = REG_EXPAND_SZ and the line is continued with ,\<CR><LF><space><space>
3434 		 don't know how many bytes for each line. Around 18-24 seems to be it?? depends on name lenght??
3435 		 NOTE: Exception:
3436 		 type = REG_BINARY starts like this: "valuename"=hex:xx,xx,xx...
3437 	      */
3438 	      value = (char *)get_val_data(hdesc, nkofs, name, vex.type, TPF_VK_EXACT|TPF_ABS);
3439 	      len = get_val_len(hdesc, nkofs, name, TPF_VK_EXACT|TPF_ABS);
3440 
3441 	      export_bin(vex.type, value, len, col, file);
3442             }
3443 
3444             FREE(vex.name);
3445             free(name);
3446         }
3447     }
3448 
3449     // export subkeys
3450     if (key->no_subkeys)
3451     {
3452         count = 0;
3453         countri = 0;
3454         while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0))
3455         {
3456             export_subkey(hdesc, nkofs, ex.name, prefix, file);
3457             FREE(ex.name);
3458         }
3459     }
3460     free(path);
3461 }
3462 
3463 /*
3464  * Exports the given key to a windows .reg file that can be imported to the
3465  * windows registry.
3466  * The prefix is used to determine the destination hive in the windows
3467  * registry, set it to HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE or whatever you
3468  * want.
3469  *
3470  * hdesc:       hive
3471  * nkofs:       offset of parent node
3472  * name:        name of subkey to export
3473  * filename:    name of destination .reg file (will be overridden)
3474  * prefix:      prefix for each exported key
3475  */
export_key(struct hive * hdesc,int nkofs,char * name,char * filename,char * prefix)3476 void export_key(struct hive *hdesc, int nkofs, char *name, char *filename, char *prefix)
3477 {
3478     FILE *file;
3479 
3480     // open file
3481     file = fopen(filename, "w");
3482     if(!file)
3483     {
3484         printf("export: Cannot open file '%s'. %s (%d).\n", filename, strerror(errno),
3485                 errno);
3486         return;
3487     }
3488 
3489     printf("Exporting to file '%s'...\n", filename);
3490         fprintf(file, "Windows Registry Editor Version 5.00\r\n");
3491     export_subkey(hdesc, nkofs, name, prefix, file);
3492 
3493     fprintf(file,"\r\n"); /* Must end file with an empty line, windows does that */
3494 
3495     fclose(file);
3496 }
3497 
3498 /* ================================================================ */
3499 
3500 /* Import from .reg file routines */
3501 
3502 #define MAXLINE 20000
3503 
3504 /* Wide character fgetsw() may not be available on all small libraries..
3505  * so.. roll our own fgets() that handles wide if needed
3506  */
3507 
my_fgets(char * s,char * w,int max,FILE * file,int wide)3508 char *my_fgets(char *s, char *w, int max, FILE *file, int wide)
3509 {
3510 
3511   int i = 0;
3512   char prev = 1;
3513   char c = 1;
3514 
3515 
3516   while (c != '\n' && !feof(file) && max) {
3517     c = (char)fgetc(file);
3518     /*    printf("char = %c\n",c); */
3519     if (!c && (!prev && !wide) ) break; /* Stop on 1 (or 2 if wide) null */
3520     prev = c;
3521     if (c != '\r') {
3522       *(s+i) = c;
3523       *(w+i) = c;
3524       i++;
3525     }
3526     max--;
3527   }
3528   *(s+i) = 0;
3529   *(s+i+1) = 0;
3530   *(w+i) = 0;
3531   *(w+i+1) = 0;
3532 
3533   if (wide) {  /* Convert to C string, de widing it.. */
3534     cheap_uni2ascii(w, s, i);
3535     // printf("cheap returns len = %d : %s\n",strlen(s), s);
3536 
3537     fgetc(file);  /* Skip second byte of CR/LF termination */
3538   }
3539 
3540   //  printf("my_fgets returning :\n");
3541   //hexdump(w, 0, i, 1);
3542   //printf("====== hexdump end\n");
3543   return(s);
3544 
3545 }
3546 
3547 
3548 /* Read one line, while skipping blank lines, also checks for =
3549  * line = line buffer
3550  * file = you know..
3551  * assigner - left part of value assignemt (before =)
3552  * value - right part of assignment (after =)
3553  * value = NULL if [KEY] line.
3554  * assigner = NULL if value continuation line
3555  * Returns total lenght of line
3556  */
3557 
3558 
3559 #undef GETLINE_DEBUG
3560 
get_line(char s[],char w[],FILE * file,char ** assigner,char ** value,int wide)3561 int get_line(char s[], char w[], FILE *file, char **assigner, char **value, int wide)
3562 {
3563   int l,q;
3564   char *b; /* Start of line after spaces */
3565   char *c;
3566 
3567   *assigner = NULL;
3568   *value = NULL;
3569 
3570   do {
3571 
3572     s[0] = 0;
3573     my_fgets(s, w, MAXLINE, file, wide);
3574     s[MAXLINE] = 0;
3575     b = s;
3576 
3577     l = strlen(s);
3578 
3579 #ifdef GETLINE_DEBUG
3580     printf("get_line: read line len %d : %s\n",l,s);
3581 #endif
3582 
3583     if (l == 0) {
3584       if (feof(file)) return 0;
3585       break;
3586     }
3587 
3588     while (isspace(*b)) b++;
3589     c = b;
3590     while (*c && *c != '\n' && *c != '\r') c++;
3591     *c = 0; /* Terminate with null not cr/lf */
3592 
3593 
3594     if (!*b) continue;  /* Blank line */
3595 
3596     l = strlen(s);
3597 
3598 #ifdef GETLINE_DEBUG
3599     printf("get_line: stripped line len %d : %s\n",l,s);
3600 #endif
3601 
3602 
3603     c = b;
3604 
3605     if (*b == '[') {    /* Key line starts with [ */
3606 #ifdef GETLINE_DEBUG
3607       printf("get_line: key line..\n");
3608 #endif
3609       while (*c && (*c != ']')) c++;
3610       if (!*c) {
3611 	printf("get_line: WARNING: un-terminated key line..\n");
3612       }
3613       *c = 0;
3614       *assigner = b+1;
3615       *value = NULL;
3616       return(l);
3617     }
3618 
3619     q = 0;
3620     while (*c) {
3621       /*      printf("  char = %c : q = %d\n",*c,q); */
3622       if (*c == '"') q ^= 1;  /* Flip quote indicator */
3623       if (*c == '=' && !q) {  /* Found = outside quotes */
3624 	*c = 0;
3625 	*assigner = b;
3626 	*value = c + 1;
3627 #ifdef GETLINE_DEBUG
3628 	printf("get_line: value line\n");
3629 #endif
3630 	return(l);
3631       }
3632       c++;
3633     }
3634     /* At this point we don't have a = outside quotes, so probably a value cont line */
3635     *assigner = NULL;
3636     *value = b;
3637 #ifdef GETLINE_DEBUG
3638     printf("get_line: cont line\n");
3639 #endif
3640     return(l);
3641 
3642   } while (!feof(file));
3643 
3644   return(l);
3645 }
3646 
3647 
3648 /* Wide strlen */
wide_strlen(char * s)3649 int wide_strlen(char *s)
3650 {
3651 
3652   int l = 0;
3653 
3654   while ( *(s+l) || *(s+l+1) ) l += 2;
3655 
3656   return(l);
3657 
3658 }
3659 
3660 
3661 
3662 /* De-quote a string (removing quotes first and last)
3663  * Does nothing if no quoutes
3664  * String MUST be null-terminated
3665  */
3666 
dequote(char * s)3667 char *dequote(char *s)
3668 {
3669   int l;
3670 
3671   if (*s != '"') return(s);   /* No first quote, then don't change anything, even at end */
3672 
3673   l = strlen(s);
3674 
3675   if ( *(s+l-1) == '"' ) {
3676     *(s+l-1) = 0;
3677   }
3678 
3679   return(s+1);
3680 }
3681 
3682 /* de-escape a string, handling \ backslash
3683  * s = string buffer, WILL BE CHANGED
3684  * wide = true to make it handle wide characters
3685  * returns new lenght
3686  */
3687 
de_escape(char * s,int wide)3688 int de_escape(char *s, int wide)
3689 {
3690   int src = 0;
3691   int dst = 0;
3692 
3693   if (wide) {
3694     while ( *(s + src) || *(s + src +1)) {
3695       if ( *(s + src) == '\\' && *(s + src + 1) == 0) src += 2; /* Skip over backslash */
3696       *(s + dst) = *(s + src);
3697       *(s + dst + 1) = *(s + src + 1);
3698       dst += 2;
3699       src += 2;
3700     }
3701     *(s + dst) = 0;
3702     *(s + dst + 1) = 0;
3703     dst += 2;
3704   } else {
3705     while ( *(s + src) ) {
3706       if ( *(s + src) == '\\' ) src++;
3707       *(s + dst) = *(s + src);
3708       dst++;
3709       src++;
3710     }
3711     *(s + dst) = 0;
3712     dst++;
3713   }
3714 
3715   return(dst);
3716 }
3717 
3718 
3719 
3720 
3721 /* Parse the value to be assigned
3722  * s = string to parse
3723  * bufptr = pointer to buffer pointer for binary data (will be allocated).
3724  * First byte of buffer is value type.
3725  * returns lenght
3726  *
3727 "stringvalue"="this is a string"
3728 "binaryvalue"=hex:11,22,33,aa,bb,cc,12,34,01,02,ab,cd,ef,be,ef
3729 "dwordvalue"=dword:12345678
3730 "qwordvalue"=hex(b):ef,cd,ab,89,67,45,23,01
3731 "multistringvalue"=hex(7):73,00,74,00,72,00,69,00,6e,00,67,00,20,00,31,00,00,\
3732   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,\
3733   69,00,6e,00,67,00,20,00,33,00,00,00,00,00
3734 "expstringvalue"=hex(2):73,00,74,00,72,00,69,00,6e,00,67,00,20,00,77,00,69,00,\
3735   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,\
3736   00,6f,00,6e,00,00,00
3737   *
3738  */
3739 
parse_valuestring(char * s,char * w,int len,int wide,struct keyval ** kvptr)3740 int parse_valuestring(char *s, char *w, int len, int wide, struct keyval **kvptr)
3741 {
3742   unsigned int dword;
3743   int type = -1;
3744   int i;
3745   int quote;
3746   int strstart;
3747   uint8_t byte;
3748   uint8_t *array;
3749   char *widebuf = NULL;
3750   struct keyval *kv = NULL;
3751 
3752 
3753   //  printf("parse_val: input string: <%s>\n",s);
3754 
3755   if (!strncmp(s,"dword",4)) {  /* DWORD */
3756     sscanf(s,"dword:%x",&dword);
3757     //    printf("parse_vals: dword is %x\n",dword);
3758     type = REG_DWORD;
3759     len = 4;
3760     ALLOC(kv,1,len + 8);
3761     kv->data = dword;
3762 
3763   } else if (!strncmp(s,"hex",3)) { /* Hex string */
3764     if (!sscanf(s,"hex(%x):",&type)) type = REG_BINARY;
3765 
3766     //   printf("parse_vals: hex type is %d\n",type);
3767 
3768     while (*s && *s != ':') s++;   /* Move up to : */
3769     s++;
3770     len = strlen(s);
3771     if (len > 0) len = len / 3 + 1;           /* 3 characters per byte */
3772 
3773     // printf("parse_vals: hex byte count %d\n", len);
3774 
3775     ALLOC(kv,1,len + 8);
3776     array = (uint8_t *)&kv->data;
3777 
3778     for (i = 0; i < len; i++) {
3779       if (!sscanf(s,"%hhx",&byte)) {
3780 	fprintf(stderr,"parse_values: hex string parse error: %s\n",s);
3781 	abort();
3782       }
3783       //      printf("parse_vals: adding byte: %02x\n",byte);
3784       *(array+i) = byte;
3785       s += 3;
3786     }
3787 
3788   } else { /* Assume it simply is a string */
3789     type = REG_SZ;
3790 
3791     if (wide) {     /* File is wide, find string limits and de-escape it, then copy in as wide */
3792 
3793       quote = 0;
3794       i = 0;
3795       strstart = 0;
3796       while ( *(w+i) ) {
3797 	if ( *(w+i) == '"' && *(w+i+1) == 0) quote ^= 1;
3798 	if (!quote && !strstart && ( *(w+i) == '=' && *(w+i+1) == 0) ) {
3799 	  strstart = i + 4;  /* Skip past start quote */
3800 	}
3801 	i += 2;
3802       }
3803       if ( *(w+i-2) == '"' && *(w+i-1) == 0) { /* Remove end quoute */
3804 	*(w+i-2) = 0;
3805 	*(w+i-1) = 0;
3806 	i -= 2;
3807       }
3808       i += 2;
3809 
3810 
3811       //      hexdump(w+strstart,0,i-strstart,1);
3812 
3813       len = de_escape(w + strstart, wide);
3814 
3815       //      hexdump(w+strstart,0,len,1);
3816 
3817       //      printf("wide string: i = %d, strstart = %d, len = %d\n",i,strstart,len);
3818 
3819       ALLOC(kv,1,len + 8);
3820 
3821       memcpy(&kv->data,w + strstart, len);
3822 
3823     } else {       /* File is not wide, so we must widen string before putting into registry */
3824 
3825       len = strlen(s);
3826       //      printf("parse_vals: len %d string <%s>\n",len,s);
3827       len = de_escape(s, 0);
3828       // printf("parse_vals: after de-escape len %d string <%s>\n",len,s);
3829 
3830       widebuf = string_prog2regw(s, strlen(s), &len);
3831       len += 2;  /* Also store the terminating NULLs */
3832       // printf("parse_vals: len after wide expansion: %d\n",len);
3833 
3834       ALLOC(kv,1,len + 8);
3835       memcpy(&kv->data,widebuf,len);
3836 
3837       FREE(widebuf);
3838     }
3839 
3840 
3841   }
3842 
3843   if (kv) {
3844     kv->len = len;
3845     *kvptr = kv;
3846   }
3847 
3848   return(type);
3849 
3850 }
3851 
3852 
3853 
3854 
3855 /* Main import routine
3856  * Only reading into one hive at a time supported.
3857  * Will support 8 bit or 16 bit (wide) character .reg file.
3858  * Windows regedit.exe usually generates 16 bit.
3859  * NOTE: Not a lot of sanity checking, a broken input file may end in strange behaviour or corrupt registry!
3860  * hdesc - Loaded hive
3861  * filename - (path)name of .reg file to read
3862  * prefix - HKEY_
3863  */
3864 
import_reg(struct hive * hdesc,char * filename,char * prefix)3865 void import_reg(struct hive *hdesc, char *filename, char *prefix)
3866 {
3867 
3868     FILE *file;
3869     char line[MAXLINE+2];
3870     char wline[MAXLINE+2];  /* Wide buffer */
3871     int l, plen;
3872     char *assigner = NULL;
3873     char *value = NULL;
3874     int wide = 0;
3875     int c,wl;
3876     //    void *valbuf;
3877     char *valname = NULL;
3878     char *plainname = NULL;
3879     char *valstr, *key, *walstr = NULL;
3880     int nk, prevnk; /* offsets to nk */
3881     int type,oldtype;
3882     struct keyval *valbinbuf = NULL;
3883     int numkeys = 0;
3884     int numkeyadd = 0;
3885     int numtotvals = 0;
3886     int numkeyvals = 0;
3887     int bailout = 0;
3888 
3889     plen = strlen(prefix);
3890 
3891     file = fopen(filename, "r");
3892     if(!file)
3893     {
3894       fprintf(stderr,"import_reg: Cannot open file '%s'. %s (%d).\n", filename, strerror(errno),
3895                 errno);
3896         return;
3897     }
3898 
3899     c = fgetc(file);
3900 
3901     if (c == 0xff) { /* Wide characters file */
3902       fprintf(stderr,"import_reg: WARNING: Wide character (16 bit) file..\n"
3903 	      "import_reg: WARNING: Implementation is not 100%% accurate, some things may not import correctly!\n");
3904       c = fgetc(file); /* Get second wide indicator character */
3905       wide = 1;
3906     } else {
3907       ungetc(c, file);
3908     }
3909 
3910     line[0] = 0;
3911     my_fgets(line, wline, MAXLINE, file, wide);
3912     line[MAXLINE] = 0;
3913 
3914 
3915     if (strncmp("Windows Registry Editor",line,23)) {
3916       fprintf(stderr,"import_reg: ERROR: Windows Registry Editor signature missing on first line\n");
3917       fclose(file);
3918       return;
3919     }
3920 
3921     do {
3922 
3923      l = get_line(line, wline, file, &assigner, &value, wide);
3924 
3925      if (!feof(file) && !assigner && value) {
3926        //	printf("import_reg: value continuation line: %s\n",value);
3927 	l = strlen(value);
3928 	if ( *(value+l-1) == '\\' ) {  /* Strip off cont character */
3929 	  *(value+l-1) = 0;
3930 	  l--;
3931 	}
3932         value = dequote(value);
3933 	l = strlen(value);
3934 	valstr = realloc(valstr, strlen(valstr) + l + 3);
3935 	if (valstr == NULL) {
3936 	  perror("import_reg: realloc(valstr)");
3937 	  abort();
3938 	}
3939 	strcat(valstr, value);
3940 	//	printf("import_reg: build value string: %s\n",valstr);
3941 	if (wide) {
3942 	  wl = wide_strlen(wline);
3943 	  walstr = realloc(walstr, wide_strlen(walstr) + wl + 4);
3944 	  if (walstr == NULL) {
3945 	    perror("import_reg: realloc(valstr)");
3946 	    abort();
3947 	  }
3948 	  memcpy(walstr + wl, wline, wl);
3949 	}
3950 
3951 
3952 
3953      } else {    /* End continuation, store built up value */
3954        if (valname) {
3955 	 //	 printf("import_reg: end of value %s, result string: %s\n\n",valname,valstr);
3956 
3957 	 type = parse_valuestring(valstr, walstr, l, wide, &valbinbuf);
3958 
3959 	 //	 printf("import_reg: got value type = %d\n",type);
3960 	 //      printf("import_reg: data lenght    = %d\n",(*valbinbuf).len);
3961 
3962 	 VERBF(hdesc,"  Value <%s> of type %d length %d",valname,type,(*valbinbuf).len);
3963 
3964 	 oldtype = get_val_type(hdesc, nk + 4, valname, TPF_VK_ABS|TPF_EXACT);
3965 
3966 
3967 	 if (oldtype == -1) {
3968 	   //	   printf("Value <%s> not found, creating it new\n",valname);
3969 	   plainname = str_dup(valname);
3970 	   de_escape(plainname,0);
3971 	   // printf("de-escaped to <%s> creating it new\n",plainname);
3972 	   add_value(hdesc, nk + 4, plainname, type);
3973 	   oldtype = get_val_type(hdesc, nk + 4, valname, TPF_VK_ABS|TPF_EXACT);
3974 	   FREE(plainname);
3975 	   VERB(hdesc," [CREATED]");
3976 
3977 	 }
3978 
3979 	 if (oldtype != type) {
3980 	   fprintf(stderr,"ERROR: import_reg: unable to change value <%s>, new type is %d while old is %d\n",valname,type,oldtype);
3981 	   bailout = 1;
3982 	 } else {
3983 
3984 	   VERB(hdesc,"\n");
3985 	   put_buf2val(hdesc, valbinbuf, nk + 4, valname, type, TPF_VK_ABS|TPF_EXACT);
3986 
3987 	   numkeyvals++;
3988 	   numtotvals++;
3989 
3990 	   FREE(valbinbuf);
3991 	   FREE(valstr);
3992 	   FREE(valname);
3993 	   FREE(walstr);
3994 	 }
3995        }
3996      }
3997 
3998 
3999      if (assigner && !value) {      /* Its a key line */
4000        //printf("import_reg: read key name: %s\n",assigner);
4001 	if ( !strncmp(assigner,prefix,plen)) { /* Check and strip of prefix of key name */
4002 	  assigner += plen;
4003 	} else {
4004 	  fprintf(stderr,"import_reg: WARNING: found key <%s> not matching prefix <%s>\n",assigner,prefix);
4005 	  abort();
4006 	}
4007 
4008 	if (numkeys) {
4009 	  if (hdesc->state & HMODE_VERBOSE)
4010 	    printf("--- END of key, with %d values\n",numkeyvals);
4011 	  else
4012 	    printf(" with %d values.\n",numkeyvals);
4013 	  numkeyvals = 0;
4014 	}
4015 
4016 	printf("--- Import KEY <%s> ",assigner);
4017 	numkeys++;
4018 
4019 	key = strtok(assigner,"\\");
4020 	prevnk = hdesc->rootofs;
4021 	while (key) {
4022 	  nk = trav_path(hdesc, prevnk + 4, key, TPF_NK_EXACT);
4023 	  if (!nk) {
4024 	    if (!add_key(hdesc, prevnk + 4, key)) {
4025 	      fprintf(stderr,"\nERROR: import_reg: failed to add (sub)key <%s>\n",key);
4026 	      bailout = 1;
4027 	    } else {
4028 	      printf(" [added <%s>] ",key);
4029 	      nk = trav_path(hdesc, prevnk + 4, key, TPF_NK_EXACT);
4030 	      numkeyadd++;
4031 	    }
4032 	  }
4033 	  prevnk = nk;
4034 	  key = strtok(NULL,"\\");
4035 	}
4036 	fflush(stdout);
4037 	VERB(hdesc,"\n");
4038      }
4039 
4040 
4041      if (assigner && value) {
4042        // printf("import_reg: value assignment line: %s = %s\n",assigner,value);
4043        valname = str_dup(dequote(assigner));
4044        if (wide) {
4045 	 FREE(walstr);
4046 	 ALLOC(walstr, 1, wide_strlen(wline));
4047 	 memcpy(walstr, wline, wide_strlen(wline));
4048        }
4049 
4050        l = strlen(value);
4051        if ( *(value+l-1) == '\\' ) {  /* Strip off cont character */
4052 	 *(value+l-1) = 0;
4053 	 l--;
4054        }
4055        value = dequote(value);
4056        valstr = str_dup(value);
4057        //       valbuf = NULL;
4058        //printf("import_reg: val name = %s\n",valname);
4059        //printf("import_reg: val str  = %s\n",valstr);
4060 
4061      }
4062 
4063 
4064     } while (!feof(file) && !bailout);
4065 
4066 
4067     printf("\nEND OF IMPORT, file <%s>, operation %s!\n", filename, (bailout ? "FAILED" : "SUCCEEDED"));
4068     printf("%d keys\n",numkeys);
4069     printf("%d new keys added\n",numkeyadd);
4070     printf("%d values total\n\n",numtotvals);
4071     fclose(file);
4072 
4073     if (bailout) hdesc->state &= ~HMODE_DIRTY;    /* Don't save if error. Or should we? */
4074 
4075 }
4076 
4077 
4078 
4079 
4080 
4081 
4082 
4083 
4084 /* ================================================================ */
4085 
4086 /* Hive control (load/save/close) etc */
4087 
closeHive(struct hive * hdesc)4088 void closeHive(struct hive *hdesc)
4089 {
4090 
4091   //  printf("closing hive %s\n",hdesc->filename);
4092   if (hdesc->state & HMODE_OPEN) {
4093     close(hdesc->filedesc);
4094   }
4095   FREE(hdesc->filename);
4096   FREE(hdesc->buffer);
4097   FREE(hdesc);
4098 
4099 }
4100 
4101 
4102 /* Compute checksum of REGF header page
4103  * hdesc = hive
4104  * returns checksum value, 32 bit int
4105  */
4106 
calc_regfsum(struct hive * hdesc)4107 int32_t calc_regfsum(struct hive *hdesc)
4108 {
4109   int32_t checksum = 0;
4110   struct regf_header *hdr;
4111   int i;
4112 
4113   hdr = (struct regf_header *) hdesc->buffer;
4114 
4115   for (i = 0; i < 0x1fc/4; ++i)
4116     checksum ^= ((int32_t *) hdr)[i];
4117 
4118   return(checksum);
4119 
4120 }
4121 
4122 
4123 
4124 /* Write the hive back to disk (only if dirty & not readonly) */
4125 
writeHive(struct hive * hdesc)4126 int writeHive(struct hive *hdesc)
4127 {
4128   int len;
4129   struct regf_header *hdr;
4130 
4131   if (hdesc->state & HMODE_RO) return(0);
4132   if ( !(hdesc->state & HMODE_DIRTY)) return(0);
4133 
4134   if ( !(hdesc->state & HMODE_OPEN)) { /* File has been closed */
4135     if (!(hdesc->filedesc = open(hdesc->filename,O_RDWR))) {
4136       fprintf(stderr,"writeHive: open(%s) failed: %s, FILE NOT WRITTEN!\n",hdesc->filename,strerror(errno));
4137       return(1);
4138     }
4139     hdesc->state |= HMODE_OPEN;
4140   }
4141   /* Seek back to begginning of file (in case it's already open) */
4142   lseek(hdesc->filedesc, 0, SEEK_SET);
4143 
4144   /* compute new checksum */
4145 
4146   hdr = (struct regf_header *) hdesc->buffer;
4147 
4148   hdr->checksum = calc_regfsum(hdesc);
4149 
4150 
4151   len = write(hdesc->filedesc, hdesc->buffer, hdesc->size);
4152   if (len != hdesc->size) {
4153     fprintf(stderr,"writeHive: write of %s failed: %s.\n",hdesc->filename,strerror(errno));
4154     return(1);
4155   }
4156 
4157   hdesc->state &= (~HMODE_DIRTY);
4158   return(0);
4159 }
4160 
4161 #undef LOAD_DEBUG
4162 
openHive(char * filename,int mode)4163 struct hive *openHive(char *filename, int mode)
4164 {
4165 
4166   struct hive *hdesc;
4167   int fmode,r,vofs;
4168   struct stat sbuf;
4169   uint32_t pofs;
4170   int32_t checksum;
4171   char *c;
4172   int rt;
4173   struct hbin_page *p;
4174   struct regf_header *hdr;
4175   struct nk_key *nk;
4176   struct ri_key *rikey;
4177 
4178   int verbose = (mode & HMODE_VERBOSE);
4179   int trace   = (mode & HMODE_TRACE) ? 1 : 0;
4180   int info    = (mode & HMODE_INFO);
4181 
4182   if (!filename || !*filename) return(NULL);
4183 
4184   CREATE(hdesc,struct hive,1);
4185 
4186   hdesc->filename = str_dup(filename);
4187   hdesc->state = 0;
4188   hdesc->size = 0;
4189   hdesc->buffer = NULL;
4190 
4191   if ( (mode & HMODE_RO) ) {
4192     fmode = O_RDONLY;
4193   } else {
4194     fmode = O_RDWR;
4195   }
4196 
4197   /* Some non-unix platforms may need this. Thanks to Dan Schmidt */
4198 #ifdef O_BINARY
4199   fmode |= O_BINARY;
4200 #endif
4201 
4202   hdesc->filedesc = open(hdesc->filename,fmode);
4203   if (hdesc->filedesc < 0) {
4204     fprintf(stderr,"openHive(%s) failed: %s, trying read-only\n",hdesc->filename,strerror(errno));
4205     fmode = O_RDONLY;
4206     mode |= HMODE_RO;
4207     hdesc->filedesc = open(hdesc->filename,fmode);
4208     if (hdesc->filedesc < 0) {
4209       fprintf(stderr,"openHive(%s) in fallback RO-mode failed: %s\n",hdesc->filename,strerror(errno));
4210       closeHive(hdesc);
4211       return(NULL);
4212     }
4213   }
4214 
4215 
4216   if ( fstat(hdesc->filedesc,&sbuf) ) {
4217     perror("stat()");
4218     exit(1);
4219   }
4220 
4221   hdesc->size = sbuf.st_size;
4222   hdesc->state = mode | HMODE_OPEN;
4223 
4224   /* Read the whole file */
4225 
4226   ALLOC(hdesc->buffer,1,hdesc->size);
4227 
4228   rt = 0;
4229   do {  /* On some platforms read may not block, and read in chunks. handle that */
4230     r = read(hdesc->filedesc, hdesc->buffer + rt, hdesc->size - rt);
4231     rt += r;
4232   } while ( !errno && (rt < hdesc->size) );
4233 
4234   if (errno) {
4235     perror("openHive(): read error: ");
4236     closeHive(hdesc);
4237     return(NULL);
4238   }
4239   if (rt < hdesc->size) {
4240     fprintf(stderr,"Could not read file, got %d bytes while expecting %d\n",
4241 	    r, hdesc->size);
4242     closeHive(hdesc);
4243     return(NULL);
4244   }
4245 
4246   /* Now run through file, tallying all pages */
4247   /* NOTE/KLUDGE: Assume first page starts at offset 0x1000 */
4248 
4249    pofs = 0x1000;
4250 
4251    hdr = (struct regf_header *)hdesc->buffer;
4252    if (hdr->id != 0x66676572) {
4253      fprintf(stderr,"openHive(%s): File does not seem to be a registry hive!\n",filename);
4254      return(hdesc);
4255    }
4256 
4257    checksum = calc_regfsum(hdesc);
4258 
4259 #ifdef LOAD_DEBUG
4260    printf("openhive: calculated checksum: %08x\n",checksum);
4261    printf("openhive: file REGF  checksum: %08x\n",hdr->checksum);
4262 #endif
4263    if (checksum != hdr->checksum) {
4264      fprintf(stderr,"openHive(%s): WARNING: REGF header checksum mismatch! calc: 0x%08x != file: 0x%08x\n",filename,checksum,hdr->checksum);
4265    }
4266 
4267    hdesc->rootofs = hdr->ofs_rootkey + 0x1000;
4268 
4269    if (info) {
4270      printf("Hive <%s> name (from header): <",filename);
4271      for (c = hdr->name; *c && (c < hdr->name + 64); c += 2) putchar(*c);
4272      printf(">\nROOT KEY at offset: 0x%06x * ",hdesc->rootofs);
4273    }
4274 
4275    /* Cache the roots subkey index type (li,lf,lh) so we can use the correct
4276     * one when creating the first subkey in a key */
4277 
4278    nk = (struct nk_key *)(hdesc->buffer + hdesc->rootofs + 4);
4279    if (nk->id == 0x6b6e) {
4280      rikey = (struct ri_key *)(hdesc->buffer + nk->ofs_lf + 0x1004);
4281      hdesc->nkindextype = rikey->id;
4282      if (hdesc->nkindextype == 0x6972) {  /* Gee, big root, must check indirectly */
4283        fprintf(stderr,"openHive: DEBUG: BIG ROOT!\n");
4284        rikey = (struct ri_key *)(hdesc->buffer + rikey->hash[0].ofs_li + 0x1004);
4285        hdesc->nkindextype = rikey->id;
4286      }
4287      if (hdesc->nkindextype != 0x666c &&
4288 	 hdesc->nkindextype != 0x686c &&
4289 	 hdesc->nkindextype != 0x696c) {
4290        hdesc->nkindextype = 0x666c;
4291      }
4292 
4293      if (info) printf("Subkey indexing type is: %04x <%c%c>\n",
4294 	    hdesc->nkindextype,
4295 	    hdesc->nkindextype & 0xff,
4296 	    hdesc->nkindextype >> 8);
4297    } else {
4298      fprintf(stderr,"openHive: WARNING: ROOT key does not seem to be a key! (not type == nk)\n");
4299    }
4300 
4301 
4302    while (pofs < hdr->filesize + 0x1000) {   /* Loop through hbins until end according to regf header */
4303 #ifdef LOAD_DEBUG
4304      int htrace = 1;
4305      //     if (htrace) hexdump(hdesc->buffer,pofs,pofs+0x20,1);
4306 #endif
4307      p = (struct hbin_page *)(hdesc->buffer + pofs);
4308      if (p->id != 0x6E696268) {
4309        if (info) printf("Page at 0x%x is not 'hbin', assuming file contains garbage at end\n",pofs);
4310        break;
4311      }
4312      hdesc->pages++;
4313 
4314      if (verbose) printf("###### Page at 0x%0x ofs_self 0x%0x, size (delta ofs_next) 0x%0x ######\n",
4315 			pofs,p->ofs_self,p->ofs_next);
4316 
4317      if (p->ofs_next == 0) {
4318        fprintf(stderr,"openHive: ERROR: Page at 0x%x has size zero! File may be corrupt, or program has a bug\n",pofs);
4319        return(hdesc);
4320      }
4321 
4322 
4323      vofs = pofs + 0x20; /* Skip page header, and run through blocks in hbin */
4324 
4325      while (vofs-pofs < p->ofs_next && vofs < hdesc->size) {
4326        vofs += parse_block(hdesc,vofs,trace);
4327 
4328      }
4329 
4330      pofs += p->ofs_next;
4331 
4332    } /* hbin loop */
4333 
4334 
4335    hdesc->endofs  = hdr->filesize + 0x1000;
4336    hdesc->lastbin = pofs - p->ofs_next;  /* Compensate for loop that added at end above */
4337 
4338    if (verbose) {
4339      printf("Last HBIN at offset       : 0x%x\n",hdesc->lastbin);
4340      printf("First non-HBIN page offset: 0x%x\n",hdesc->endofs);
4341      printf("hdr->unknown4 (version?)  : 0x%x\n",hdr->unknown4);
4342    }
4343 
4344    if (info || verbose) {
4345      printf("File size %d [%x] bytes, containing %d pages (+ 1 headerpage)\n",hdesc->size,hdesc->size, hdesc->pages);
4346      printf("Used for data: %d/%d blocks/bytes, unused: %d/%d blocks/bytes.\n\n",
4347 	    hdesc->useblk,hdesc->usetot,hdesc->unuseblk,hdesc->unusetot);
4348    }
4349 
4350    /* So, let's guess what kind of hive this is, based on keys in its root */
4351 
4352    hdesc->type = HTYPE_UNKNOWN;
4353 
4354    if (trav_path(hdesc, 0, "\\SAM", 0)) hdesc->type = HTYPE_SAM;
4355    else if (trav_path(hdesc, 0, "\\ControlSet", 0)) hdesc->type = HTYPE_SYSTEM;
4356    else if (trav_path(hdesc, 0, "\\Policy", 0)) hdesc->type = HTYPE_SECURITY;
4357    else if (trav_path(hdesc, 0, "\\Microsoft", 0)) hdesc->type = HTYPE_SOFTWARE;
4358    if (verbose) printf("Type of hive guessed to be: %d\n",hdesc->type);
4359 
4360    return(hdesc);
4361 
4362 }
4363 
4364