www.pudn.com > chntpw-source-000607.zip > ntreg.c
/* * ntreg.c - NT Registry Hive access library (read only for now) * * Copyright (c) 1997-2000 Petter Nordahl-Hagen. * Freely distributable in source or binary for noncommercial purposes. * * Please see the COPYING file for more details on * copyrights & credits. * * THIS SOFTWARE IS PROVIDED BY PETTER NORDAHL-HAGEN `AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include#include #include #include #include #include #include #include #include #include "ntreg.h" const char ntreg_version[] = "ntreg lib routines, v0.81 000607 , (c) Petter N Hagen"; char *val_types[REG_MAX+1] = { "REG_NONE", "REG_SZ", "REG_EXPAND_SZ", "REG_BINARY", "REG_DWORD", /* 0 - 4 */ "REG_DWORD_BIG_ENDIAN", "REG_LINK", /* 5 - 6 */ "REG_MULTI_SZ", "REG_RESOUCE_LIST", "REG_FULL_RES_DESC", "REG_RES_REQ", /* 7 - 10 */ }; /* Utility routines */ char *str_dup( const char *str ) { char *str_new; if (!str) return 0 ; CREATE( str_new, char, strlen(str) + 1 ); strcpy( str_new, str ); return str_new; } int fmyinput(char *prmpt, char *ibuf, int maxlen) { printf("%s",prmpt); fgets(ibuf,maxlen+1,stdin); ibuf[strlen(ibuf)-1] = 0; return(strlen(ibuf)); } /* Print len number of hexbytes */ void hexprnt(char *s, unsigned char *bytes, int len) { int i; printf("%s",s); for (i = 0; i < len; i++) { printf("%02x ",bytes[i]); } printf("\n"); } /* HexDump all or a part of some buffer */ void hexdump(char *hbuf, int start, int stop, int ascii) { char c; int diff,i; while (start < stop ) { diff = stop - start; if (diff > 16) diff = 16; printf(":%05X ",start); for (i = 0; i < diff; i++) { printf("%02X ",(unsigned char)*(hbuf+start+i)); } if (ascii) { for (i = diff; i < 16; i++) printf(" "); for (i = 0; i < diff; i++) { c = *(hbuf+start+i); printf("%c", isprint(c) ? c : '.'); } } printf("\n"); start += 16; } } /* General search routine, find something in something else */ int find_in_buf(char *buf, char *what, int sz, int len, int start) { int i; for (; start < sz; start++) { for (i = 0; i < len; i++) { if (*(buf+start+i) != *(what+i)) break; } if (i == len) return(start); } return(0); } /* Get INTEGER from memory. This is probably low-endian specific? */ int get_int( char *array ) { return ((array[0]&0xff) + ((array[1]<<8)&0xff00) + ((array[2]<<16)&0xff0000) + ((array[3]<<24)&0xff000000)); } /* Quick and dirty UNICODE to std. ascii */ void cheap_uni2ascii(char *src, char *dest, int l) { for (; l > 0; l -=2) { *dest = *src; dest++; src +=2; } *dest = 0; } /* Quick and dirty ascii to unicode */ void cheap_ascii2uni(char *src, char *dest, int l) { for (; l > 0; l--) { *dest++ = *src++; *dest++ = 0; } } void skipspace(char **c) { while( **c == ' ' ) (*c)++; } int gethex(char **c) { int value; skipspace(c); if (!(**c)) return(0); sscanf(*c,"%x",&value); while( **c != ' ' && (**c)) (*c)++; return(value); } /* Get a string of HEX bytes (space separated), * or if first char is ' get an ASCII string instead. */ int gethexorstr(char **c, char *wb) { int l = 0; skipspace(c); if ( **c == '\'') { (*c)++; while ( **c ) { *(wb++) = *((*c)++); l++; } } else { do { *(wb++) = gethex(c); l++; skipspace(c); } while ( **c ); } return(l); } /* Simple buffer debugger, returns 1 if buffer dirty/edited */ int debugit(char *buf, int sz) { char inbuf[100],whatbuf[100],*bp; int dirty=0,to,from,l,i,j,wlen,cofs = 0; printf("Buffer debugger. '?' for help.\n"); while (1) { l = fmyinput(".",inbuf,90); bp = inbuf; skipspace(&bp); if (l > 0 && *bp) { switch(*bp) { case 'd' : bp++; if (*bp) { from = gethex(&bp); to = gethex(&bp); } else { from = cofs; to = 0; } if (to == 0) to = from + 0x100; if (to > sz) to = sz; hexdump(buf,from,to,1); cofs = to; break; case 'a' : bp++; if (*bp) { from = gethex(&bp); to = gethex(&bp); } else { from = cofs; to = 0; } if (to == 0) to = from + 0x100; if (to > sz) to = sz; hexdump(buf,from,to,0); cofs = to; break; #if 0 case 'k' : bp++; if (*bp) { from = gethex(&bp); } else { from = cofs; } if (to > sz) to = sz; parse_block(from,1); cofs = to; break; #endif #if 0 case 'l' : bp++; if (*bp) { from = gethex(&bp); } else { from = cofs; } if (to > sz) to = sz; nk_ls(from+4,0); cofs = to; break; #endif case 'q': return(0); break; case 's': if (!dirty) printf("Buffer has not changed, no need to write..\n"); return(dirty); break; case 'h': bp++; if (*bp == 'a') { from = 0; to = sz; bp++; } else { from = gethex(&bp); to = gethex(&bp); } wlen = gethexorstr(&bp,whatbuf); if (to > sz) to = sz; printf("from: %x, to: %x, wlen: %d\n",from,to,wlen); for (i = from; i < to; i++) { for (j = 0; j < wlen; j++) { if ( *(buf+i+j) != *(whatbuf+j)) break; } if (j == wlen) printf("%06x ",i); } printf("\n"); break; case ':': bp++; if (!*bp) break; from = gethex(&bp); wlen = gethexorstr(&bp,whatbuf); printf("from: %x, wlen: %d\n",from,wlen); memcpy(buf+from,whatbuf,wlen); dirty = 1; break; #if 0 case 'p': j = 0; if (*(++bp) != 0) { from = gethex(&bp); } if (*(++bp) != 0) { j = gethex(&bp); } printf("from: %x, rid: %x\n",from,j); seek_n_destroy(from,j,500,0); break; #endif case '?': printf("d [ ] [ ] - dump buffer within range\n"); printf("a [ ] [ ] - same as d, but without ascii-part (for cut'n'paste)\n"); printf(": [ ...] - change bytes\n"); printf("h [ ...] - hunt (search) for bytes\n"); printf("ha [ etc. you may give 'string to enter/search a string\n"); break; default: printf("?\n"); break; } } } } /* ========================================================================= */ /* The following routines are mostly for debugging, I used it * much during discovery. the -t command line option uses it, * also the 'st' and 's' from the editor & hexdebugger. * All offsets shown in these are unadjusted (ie you must add * headerpage (most often 0x1000) to get file offset) */ /* Parse the nk datablock * vofs = offset into struct (after size linkage) */ void parse_nk(struct hive *hdesc, int vofs, int blen) { struct nk_key *key; int i; printf("== nk at offset %0x\n",vofs); #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) key = (struct nk_key *)(hdesc->buffer + vofs); printf("%04x type = 0x%02x %s\n", D_OFFS(type) , key->type, (key->type == KEY_ROOT ? "ROOT_KEY" : "") ); printf("%04x timestamp skipped\n", D_OFFS(timestamp) ); printf("%04x parent key offset = 0x%0x\n", D_OFFS(ofs_parent) ,key->ofs_parent); printf("%04x number of subkeys = %d\n", D_OFFS(no_subkeys),key->no_subkeys); printf("%04x lf-record offset = 0x%0x\n",D_OFFS(ofs_lf),key->ofs_lf); printf("%04x number of values = %d\n", D_OFFS(no_values),key->no_values); printf("%04x val-list offset = 0x%0x\n",D_OFFS(ofs_vallist),key->ofs_vallist); printf("%04x sk-record offset = 0x%0x\n",D_OFFS(ofs_sk),key->ofs_sk); printf("%04x classname offset = 0x%0x\n",D_OFFS(ofs_classnam),key->ofs_classnam); printf("%04x *unused?* = 0x%0x\n",D_OFFS(dummy4),key->dummy4); printf("%04x name length = %d\n", D_OFFS(len_name),key->len_name); printf("%04x classname length = %d\n", D_OFFS(len_classnam),key->len_classnam); printf("%04x Key name: <",D_OFFS(keyname) ); for(i = 0; i < key->len_name; i++) putchar(key->keyname[i]); printf(">\n== End of key info.\n"); } /* Parse the vk datablock * vofs = offset into struct (after size linkage) */ void parse_vk(struct hive *hdesc, int vofs, int blen) { struct vk_key *key; int i; printf("== vk at offset %0x\n",vofs); key = (struct vk_key *)(hdesc->buffer + vofs); printf("%04x name length = %d (0x%0x)\n", D_OFFS(len_name), key->len_name, key->len_name ); printf("%04x length of data = %d (0x%0x)\n", D_OFFS(len_data), key->len_data, key->len_data ); printf("%04x data offset = 0x%0x\n",D_OFFS(ofs_data),key->ofs_data); printf("%04x value type = 0x%0x %s\n", D_OFFS(val_type), key->val_type, (key->val_type <= REG_MAX ? val_types[key->val_type] : "(unknown)") ) ; printf("%04x flag = 0x%0x\n",D_OFFS(flag),key->flag); printf("%04x *unused?* = 0x%0x\n",D_OFFS(dummy1),key->dummy1); printf("%04x Key name: <",D_OFFS(keyname) ); for(i = 0; i < key->len_name; i++) putchar(key->keyname[i]); printf(">\n== End of key info.\n"); } /* Parse the sk datablock * Gee, this is the security info. Who cares? *evil grin* * vofs = offset into struct (after size linkage) */ void parse_sk(struct hive *hdesc, int vofs, int blen) { struct sk_key *key; int i; printf("== sk at offset %0x\n",vofs); key = (struct sk_key *)(hdesc->buffer + vofs); printf("%04x *unused?* = %d\n" , D_OFFS(dummy1), key->dummy1 ); printf("%04x Offset to prev sk = 0x%0x\n", D_OFFS(ofs_prevsk), key->ofs_prevsk); printf("%04x Offset to next sk = 0x%0x\n", D_OFFS(ofs_nextsk), key->ofs_nextsk); printf("%04x Usage counter = %d (0x%0x)\n", D_OFFS(no_usage), key->no_usage,key->no_usage); printf("%04x Security data len = %d (0x%0x)\n", D_OFFS(len_sk), key->len_sk,key->len_sk); printf("== End of key info.\n"); } /* Parse the lf datablock (>4.0 'nk' offsets lookuptable) * vofs = offset into struct (after size linkage) */ void parse_lf(struct hive *hdesc, int vofs, int blen) { struct lf_key *key; int i; printf("== lf at offset %0x\n",vofs); key = (struct lf_key *)(hdesc->buffer + vofs); printf("%04x number of keys = %d\n", D_OFFS(no_keys), key->no_keys ); for(i = 0; i < key->no_keys; i++) { printf("%04x %3d Offset: 0x%0x - <%c%c%c%c>\n", D_OFFS(hash[i].ofs_nk), i, key->hash[i].ofs_nk, key->hash[i].name[0], key->hash[i].name[1], key->hash[i].name[2], key->hash[i].name[3] ); } printf("== End of key info.\n"); } /* Parse the li datablock (3.x 'nk' offsets list) * vofs = offset into struct (after size linkage) */ void parse_li(struct hive *hdesc, int vofs, int blen) { struct li_key *key; int i; printf("== li at offset %0x\n",vofs); #define D_OFFS(o) ( (void *)&(key->o)-(void *)hdesc->buffer-vofs ) key = (struct li_key *)(hdesc->buffer + vofs); printf("%04x number of keys = %d\n", D_OFFS(no_keys), key->no_keys ); for(i = 0; i < key->no_keys; i++) { printf("%04x %3d Offset: 0x%0x\n", D_OFFS(hash[i].ofs_nk), i, key->hash[i].ofs_nk); } printf("== End of key info.\n"); } /* Parse the datablock * vofs = offset into struct (after size linkage) */ int parse_block(struct hive *hdesc, int vofs,int verbose) { unsigned short id; int seglen; seglen = get_int(hdesc->buffer+vofs); if (verbose || seglen == 0) { printf("** Block at offset %0x\n",vofs); printf("seglen: %d, %u, 0x%0x\n",seglen,seglen,seglen); } if (seglen == 0) { printf("Whoops! FATAL! Zero data block size! (not registry or corrupt file?)\n"); debugit(hdesc->buffer,hdesc->size); return(0); } if (seglen < 0) { seglen = -seglen; hdesc->usetot += seglen; hdesc->useblk++; if (verbose) { printf("USED BLOCK: %d, 0x%0x\n",seglen,seglen); hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); } } else { hdesc->unusetot += seglen; hdesc->unuseblk++; if (verbose) { printf("FREE BLOCK!\n"); hexdump(hdesc->buffer,vofs,vofs+seglen+4,1); } } vofs += 4; id = (*(hdesc->buffer + vofs)<<8) + *(hdesc->buffer+vofs+1); if (verbose) { switch (id) { case 0x6e6b: /* nk */ parse_nk(hdesc, vofs, seglen); break; case 0x766b: /* vk */ parse_vk(hdesc, vofs, seglen); break; case 0x6c66: /* lf */ parse_lf(hdesc, vofs, seglen); break; case 0x6c69: /* li */ parse_li(hdesc, vofs, seglen); break; case 0x736b: /* sk */ parse_sk(hdesc, vofs, seglen); break; default: printf("Not handeled yet!\n"); break; } } return(seglen); } /* ================================================================ */ /* ** Registry manipulation routines ** */ /* "directory scan", return next name/pointer of a subkey on each call * nkofs = offset to directory to scan * lfofs = pointer to int to hold the current scan position, * set position to 0 to start. * sptr = pointer to struct to hold a single result * returns: -1 = error. 0 = end of key. 1 = more subkeys to scan * NOTE: caller must free the name-buffer (struct ex_data *name) */ int ex_next_n(struct hive *hdesc, int nkofs, int *count, int *countri, struct ex_data *sptr) { struct nk_key *key, *newnkkey; int newnkofs; struct lf_key *lfkey; struct li_key *likey; struct ri_key *rikey; if (!nkofs) return(-1); key = (struct nk_key *)(hdesc->buffer + nkofs); if (key->id != 0x6b6e) { printf("ex_next error: Not a 'nk' node at 0x%0x\n",nkofs); return(-1); } #undef EXNDEBUG lfkey = (struct lf_key *)(hdesc->buffer + key->ofs_lf + 0x1004); rikey = (struct ri_key *)(hdesc->buffer + key->ofs_lf + 0x1004); if (rikey->id == 0x6972) { /* Is it extended 'ri'-block? */ #if EXNDEBUG printf("%d , %d\n",*countri,*count); #endif if (*countri < 0 || *countri >= rikey->no_lis) { /* End of ri's? */ return(0); } /* Get the li-struct that's current based on countri */ likey = (struct li_key *)( hdesc->buffer + rikey->hash[*countri].ofs_li + 0x1004 ) ; newnkofs = likey->hash[*count].ofs_nk + 0x1000; /* Check if current li is exhausted */ #if EXNDEBUG printf("likey->no_keys = %d\n",likey->no_keys); #endif if (*count >= likey->no_keys-1) { /* Last legal entry in li list? */ (*countri)++; /* Bump up ri count so we take next ri entry next time */ (*count) = -1; /* Reset li traverse counter for next round, not used later here */ } } else { /* Plain handler */ if (key->no_subkeys <= 0 || *count >= key->no_subkeys) { return(0); } if (lfkey->id == 0x696c) { /* Is it 3.x 'li' instead? */ likey = (struct li_key *)(hdesc->buffer + key->ofs_lf + 0x1004); newnkofs = likey->hash[*count].ofs_nk + 0x1000; } else { newnkofs = lfkey->hash[*count].ofs_nk + 0x1000; } } sptr->nkoffs = newnkofs; newnkkey = (struct nk_key *)(hdesc->buffer + newnkofs + 4); sptr->nk = newnkkey; if (newnkkey->id != 0x6b6e) { printf("ex_next: ERROR: not 'nk' node at 0x%0x\n",newnkofs); return(-1); } else { if (newnkkey->len_name <= 0) { printf("ex_next: nk at 0x%0x has no name!\n",newnkofs); } else { sptr->name = (char *)malloc(newnkkey->len_name+1); if (!sptr->name) { printf("FATAL! ex_next: malloc() failed! Out of memory?\n"); abort(); } strncpy(sptr->name,newnkkey->keyname,newnkkey->len_name); sptr->name[newnkkey->len_name] = 0; } } /* if */ (*count)++; return(1); /* return( *count <= key->no_subkeys); */ } /* "directory scan" for VALUES, return next name/pointer of a value on each call * nkofs = offset to directory to scan * lfofs = pointer to int to hold the current scan position, * set position to 0 to start. * sptr = pointer to struct to hold a single result * returns: -1 = error. 0 = end of key. 1 = more values to scan * NOTE: caller must free the name-buffer (struct vex_data *name) */ int ex_next_v(struct hive *hdesc, int nkofs, int *count, struct vex_data *sptr) { struct nk_key *key, *newnkkey; int vkofs,vlistofs; int *vlistkey; struct vk_key *vkkey; if (!nkofs) return(-1); key = (struct nk_key *)(hdesc->buffer + nkofs); if (key->id != 0x6b6e) { printf("ex_next_v error: Not a 'nk' node at 0x%0x\n",nkofs); return(-1); } if (key->no_values <= 0 || *count >= key->no_values) { return(0); } vlistofs = key->ofs_vallist + 0x1004; vlistkey = (int *)(hdesc->buffer + vlistofs); vkofs = vlistkey[*count] + 0x1004; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); if (vkkey->id != 0x6b76) { printf("ex_next_v: hit non valuekey (vk) node during scan at offs 0x%0x\n",vkofs); return(-1); } /* parse_vk(hdesc, vkofs, 4); */ sptr->vk = vkkey; sptr->vkoffs = vkofs; sptr->name = 0; sptr->size = (vkkey->len_data & 0x7fffffff); if (vkkey->len_name >0) { CREATE(sptr->name,char,vkkey->len_name+1); memcpy(sptr->name,vkkey->keyname,vkkey->len_name); sptr->name[vkkey->len_name] = 0; } else { sptr->name = str_dup("@"); } sptr->type = vkkey->val_type; if (sptr->size) { if (vkkey->val_type == REG_DWORD) { if (vkkey->len_data & 0x80000000) { sptr->val = (int)(vkkey->ofs_data); } } } else { /* Data SIZE is 0, special inline case, data is DWORD and in TYPE field!! */ sptr->val = sptr->type; sptr->type = REG_DWORD; sptr->size = 4; } (*count)++; return( *count <= key->no_values ); } /* traceback - trace nk's back to root, * building path string as we go. * nkofs = offset to nk-node * path = pointer to pathstring-buffer * maxlen = max length of path-buffer * return: length of path string */ int get_abs_path(struct hive *hdesc, int nkofs, char *path, int maxlen) { int newnkofs; struct nk_key *key; char tmp[ABSPATHLEN+1]; maxlen = (maxlen < ABSPATHLEN ? maxlen : ABSPATHLEN); key = (struct nk_key *)(hdesc->buffer + nkofs); if (key->id != 0x6b6e) { printf("get_abs_path: Not a 'nk' node!\n"); return(0); } if (key->type == KEY_ROOT) { /* We're at the root */ return(strlen(path)); } strncpy(tmp,path,ABSPATHLEN-1); if ( (strlen(path) + key->len_name) >= maxlen-6) { snprintf(path,maxlen,"(...)%s",tmp); return(strlen(path)); /* Stop trace when string exhausted */ } *path = '\\'; memcpy(path+1,key->keyname,key->len_name); strncpy(path+key->len_name+1,tmp,maxlen); return(get_abs_path(hdesc, key->ofs_parent+0x1004, path, maxlen)); /* go back one more */ } /* Recursevely follow 'nk'-nodes based on a path-string, * returning offset of last 'nk' or 'vk' * vofs - offset to start node * path - null-terminated pathname (relative to vofs, \ is separator) * type - type to return 0=nk 1=vk * return: offset to nk or vk (or NULL if not found) */ int trav_path(struct hive *hdesc, int vofs, char *path, int type) { struct nk_key *key, *newnkkey; struct lf_key *lfkey; struct li_key *likey; struct vk_key *vkkey; struct ri_key *rikey; long *vlistkey; int newnkofs, plen, i, lfofs, vkofs, vlistofs, adjust, r, ricnt, subs; char *buf; buf = hdesc->buffer; if (*path == '\\') { /* Start from root if path starts with \ */ path++; vofs = hdesc->rootofs+4; } key = (struct nk_key *)(buf + vofs); /* printf("check of nk at offset: 0x%0x\n",vofs); */ if (key->id != 0x6b6e) { printf("trav_path: Error: Not a 'nk' node!\n"); return(0); } for(plen = 0; path[plen] && path[plen] != '\\'; plen++) ; adjust = path[plen] == '\\' ? 1 : 0; /* printf("Checking for <%s> with len %d\n",path,plen); */ if (!plen) return(vofs-4); /* Path has no lenght - we're there! */ if ( (plen == 1) && (*path == '.')) { /* Handle '.' current dir */ return(trav_path(hdesc,vofs,path+plen+adjust,type)); } if ( (plen == 2) && !strncmp("..",path,2) ) { /* Get parent key */ newnkofs = key->ofs_parent + 0x1004; /* Return parent (or only root if at the root) */ return(trav_path(hdesc, (key->type == KEY_ROOT ? vofs : newnkofs), path+plen+adjust, type)); } /* at last name of path, and we want vk, and the nk has values */ if (!path[plen] && type == 1 && key->no_values) { vlistofs = key->ofs_vallist + 0x1004; vlistkey = (long *)(buf + vlistofs); for (i = 0; i < key->no_values; i++) { vkofs = vlistkey[i] + 0x1004; vkkey = (struct vk_key *)(buf + vkofs); if (vkkey->len_name == 0 && *path == '@') { /* @ is alias for nameless value */ return(vkofs-4); } if (!strncmp(path, vkkey->keyname, plen)) { /* name match? */ return(vkofs-4); } } } if (key->no_subkeys > 0) { /* If it has subkeys, loop through the hash */ lfofs = key->ofs_lf + 0x1004; /* lf (hash) record */ lfkey = (struct lf_key *)(buf + lfofs); if (lfkey->id == 0x6972) { /* ri struct need special parsing */ /* Prime loop state */ rikey = (struct ri_key *)lfkey; ricnt = rikey->no_lis; r = 0; likey = (struct li_key *)( hdesc->buffer + rikey->hash[r].ofs_li + 0x1004 ) ; subs = likey->no_keys; } else { if (lfkey->id == 0x696c) { /* li? */ likey = (struct li_key *)(buf + lfofs); } else { likey = NULL; } ricnt = 0; r = 0; subs = key->no_subkeys; } do { for(i = 0; i < subs; i++) { if (likey) newnkofs = likey->hash[i].ofs_nk + 0x1004; else newnkofs = lfkey->hash[i].ofs_nk + 0x1004; newnkkey = (struct nk_key *)(buf + newnkofs); if (newnkkey->id != 0x6b6e) { printf("ERROR: not 'nk' node! (strange?)\n"); } else { if (newnkkey->len_name <= 0) { printf("[No name]\n"); } else { if (!strncmp(path,newnkkey->keyname,plen)) { /* printf("Key at 0x%0x matches! recursing!\n",newnkofs); */ return(trav_path(hdesc, newnkofs, path+plen+adjust, type)); } } } /* if id OK */ } /* hash loop */ r++; if (ricnt && r < ricnt) { newnkofs = rikey->hash[r].ofs_li; likey = (struct li_key *)( hdesc->buffer + newnkofs + 0x1004 ) ; subs = likey->no_keys; } } while (r < ricnt && ricnt); } /* if subkeys */ /* Not found */ return(0); } /* ls - list a 'nk' nodes subkeys and values * vofs - offset to start of data (skipping block linkage) * type - 0 = full, 1 = keys only. 2 = values only */ void nk_ls(struct hive *hdesc, char *path, int vofs, int type) { struct nk_key *key; int nkofs; struct ex_data ex; struct vex_data vex; int count = 0, countri = 0; nkofs = trav_path(hdesc, vofs, path, 0); if(!nkofs) { printf("nk_ls: Key <%s> not found\n",path); abort(); return; } nkofs += 4; key = (struct nk_key *)(hdesc->buffer + nkofs); printf("ls of node at offset 0x%0x\n",nkofs); if (key->id != 0x6b6e) { printf("Error: Not a 'nk' node!\n"); debugit(hdesc->buffer,hdesc->size); } printf("Node has %d subkeys and %d values\n",key->no_subkeys,key->no_values); if (key->no_subkeys) { printf("offs key name\n"); while ((ex_next_n(hdesc, nkofs, &count, &countri, &ex) > 0)) { printf("[%6x] <%s>\n", ex.nkoffs, ex.name); FREE(ex.name); } } count = 0; if (key->no_values) { printf("offs size type value name [value if type DWORD]\n"); while ((ex_next_v(hdesc, nkofs, &count, &vex) > 0)) { printf("[%6x] %6d %-16s <%s>", vex.vkoffs, vex.size, (vex.type < REG_MAX ? val_types[vex.type] : "(unknown)"), vex.name); if (vex.type == REG_DWORD) printf(" %*d [0x%x]",25-strlen(vex.name),vex.val , vex.val); printf("\n"); FREE(vex.name); } } } /* Get the type of a value */ int get_val_type(struct hive *hdesc, int vofs, char *path) { struct vk_key *vkkey; int vkofs; vkofs = trav_path(hdesc, vofs,path,1); if (!vkofs) { return -1; } vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); if (vkkey->len_data & 0x80000000) return(REG_DWORD); /* Special case of INLINE storage */ return(vkkey->val_type); } /* Get len of a value, given current key + path */ int get_val_len(struct hive *hdesc, int vofs, char *path) { struct vk_key *vkkey; int vkofs; int len; vkofs = trav_path(hdesc, vofs,path,1); if (!vkofs) { return -1; } vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); len = vkkey->len_data & 0x7fffffff; if ( !len ) { /* Special inline case, return size of 4 (dword) */ len = 4; } return(len); } /* Get void-pointer to value-data, also if inline. * If val_type != 0 a check for correct value type is done * Caller must keep track of value's length (call function above to get it) */ void *get_val_data(struct hive *hdesc, int vofs, char *path, int val_type) { struct vk_key *vkkey; int vkofs; vkofs = trav_path(hdesc,vofs,path,1); if (!vkofs) { return NULL; } vkofs +=4; vkkey = (struct vk_key *)(hdesc->buffer + vkofs); if (vkkey->len_data == 0) return NULL; if (!(vkkey->len_data & 0x7fffffff)) { /* Special inline case (len = 0x80000000) */ return(&vkkey->val_type); /* Data (4 bytes?) in type field */ } if (val_type && vkkey->val_type && (vkkey->val_type) != val_type) { printf("Value <%s> is not of correct type!\n",path); return NULL; } /* Negative len is inline, return ptr to offset-field which in * this case contains the data itself */ if (vkkey->len_data & 0x80000000) return(&vkkey->ofs_data); /* Normal return, return data pointer */ return(hdesc->buffer + vkkey->ofs_data + 0x1004); } /* Get and copy key data (if any) to buffer * if kv==NULL will allocate needed return struct & buffer * else will use buffer allocated for it (if it fits) * return len+data or NULL if not found (or other error) * NOTE: caller must deallocate buffer! a simple free(keyval) will suffice. */ struct keyval *get_val2buf(struct hive *hdesc, struct keyval *kv, int vofs, char *path, int type ) { int l; struct keyval *kr; void *keydataptr; l = get_val_len(hdesc, vofs, path); if (l == -1) return(NULL); /* error */ if (kv && (kv->len < l)) return(NULL); /* Check for overflow of supplied buffer */ keydataptr = get_val_data(hdesc, vofs, path, type); if (!keydataptr) return(NULL); /* error */ /* Allocate space for data + header, or use supplied buffer */ if (kv) { kr = kv; } else { ALLOC(kr,1,l+sizeof(int)+4); } kr->len = l; memcpy(&(kr->data), keydataptr, l); return(kr); } /* DWORDs are so common that I make a small function to get it easily */ int get_dword(struct hive *hdesc, int vofs, char *path) { struct keyval *v; int dword; v = get_val2buf(hdesc, NULL, vofs, path, REG_DWORD); if (!v) return(-1); /* well... -1 COULD BE THE STORED VALUE TOO */ dword = (int)v->data; FREE(v); return(dword); } /* Write to registry value. Only permits writes in-place, ie * excact writes to overwrite an existing value, since allocation * of new stuff not implemented yet * Pass inn buffer with data len as first DWORD (as routines above) * returns: 0 - error, len - OK (len of data) */ int put_buf2val(struct hive *hdesc, struct keyval *kv, int vofs, char *path, int type ) { int l; void *keydataptr; l = get_val_len(hdesc, vofs, path); if (l == -1) return(0); /* error */ if (!kv || (kv->len != l)) return(0); /* Check for exact match of supplied buffer */ keydataptr = get_val_data(hdesc, vofs, path, type); if (!keydataptr) return(0); /* error */ memcpy(keydataptr, &kv->data, l); hdesc->state |= HMODE_DIRTY; return(l); } /* And, yer basic DWORD write */ int put_dword(struct hive *hdesc, int vofs, char *path, int dword) { struct keyval *kr; int r; ALLOC(kr,1,sizeof(int)+sizeof(int)); kr->len = sizeof(int); (int)kr->data = dword; r = put_buf2val(hdesc, kr, vofs, path, REG_DWORD); FREE(kr); return(r); } /* ================================================================ */ /* Hive control (load/save/close) etc */ void closeHive(struct hive *hdesc) { printf("closing hive %s\n",hdesc->filename); FREE(hdesc->filename); FREE(hdesc->buffer); FREE(hdesc); } /* Write the hive back to disk (only if dirty & not readonly */ int writeHive(struct hive *hdesc) { int len; if (hdesc->state & HMODE_RO) return(0); if ( !(hdesc->state & HMODE_DIRTY)) return(0); if ( !(hdesc->state & HMODE_OPEN)) { /* File has been closed */ if (!(hdesc->filedesc = open(hdesc->filename,O_RDWR))) { fprintf(stderr,"writeHive: open(%s) failed: %s, FILE NOT WRITTEN!\n",hdesc->filename,strerror(errno)); return(1); } hdesc->state |= HMODE_OPEN; } /* Seek back to begginning of file (in case it's already open) */ lseek(hdesc->filedesc, 0, SEEK_SET); len = write(hdesc->filedesc, hdesc->buffer, hdesc->size); if (len != hdesc->size) { fprintf(stderr,"writeHive: write of %s failed: %s.\n",hdesc->filename,strerror(errno)); return(1); } hdesc->state &= (~HMODE_DIRTY); return(0); } struct hive *openHive(char *filename, int mode) { struct hive *hdesc; int fmode,r,vofs; struct stat sbuf; unsigned long pofs; off_t l; char *c; struct hbin_page *p; struct regf_header *hdr; int verbose = (mode & HMODE_VERBOSE); CREATE(hdesc,struct hive,1); hdesc->filename = str_dup(filename); hdesc->state = 0; hdesc->size = 0; hdesc->buffer = NULL; if ( (mode & HMODE_RO) ) { fmode = O_RDONLY; } else { fmode = O_RDWR; } hdesc->filedesc = open(hdesc->filename,fmode); if (!(hdesc->filedesc)) { fprintf(stderr,"openHive(%s) failed: %s, trying read-only\n",hdesc->filename,strerror(errno)); fmode = O_RDONLY; mode |= HMODE_RO; hdesc->filedesc = open(hdesc->filename,fmode); if (!(hdesc->filedesc)) { fprintf(stderr,"openHive(%s) in fallback RO-mode failed: %s\n",hdesc->filename,strerror(errno)); closeHive(hdesc); return(NULL); } } if ( fstat(hdesc->filedesc,&sbuf) ) { perror("stat()"); exit(1); } hdesc->size = sbuf.st_size; hdesc->state = mode | HMODE_OPEN; /* fprintf(stderr,"hiveOpen(%s) successful\n",hdesc->filename); */ /* Read the whole file */ ALLOC(hdesc->buffer,1,hdesc->size); r = read(hdesc->filedesc,hdesc->buffer,hdesc->size); if (r < hdesc->size) { fprintf(stderr,"Could not read file, got %d bytes while expecting %d\n", r, hdesc->size); closeHive(hdesc); return(NULL); } /* Now run through file, tallying all pages */ /* NOTE/KLUDGE: Assume first page starts at offset 0x1000 */ pofs = 0x1000; hdr = (struct regf_header *)hdesc->buffer; if (hdr->id != 0x66676572) { printf("openHive(%s): File does not seem to be a registry hive!\n",filename); return(hdesc); } printf("Hive's name (from header): <"); for (c = hdr->name; *c && (c < hdr->name + 64); c += 2) putchar(*c); hdesc->rootofs = hdr->ofs_rootkey + 0x1000; /* printf(">\nROOT KEY at offset: 0x%06x\n",hdesc->rootofs); */ while (pofs < hdesc->size) { if (verbose) hexdump(hdesc->buffer,pofs,pofs+0x20,1); p = (struct hbin_page *)(hdesc->buffer + pofs); hdesc->pages++; if (verbose) printf("\n###### Page at 0x%0x has size 0x%0x, next at 0x%0x ######\n",pofs,p->len_page,p->ofs_next); if (p->len_page == 0 || p->ofs_next == 0) { if (verbose) printf("openhive debug: bailing out.. pagesize zero!\n"); break; } vofs = pofs + 0x20; /* Skip page header */ while (vofs-pofs < p->ofs_next) { vofs += parse_block(hdesc,vofs,verbose); } pofs += p->ofs_next; } printf(">\nFile size %d [%x] bytes, containing %d pages (+ 1 headerpage)\n",hdesc->size,hdesc->size, hdesc->pages); printf("Used for data: %d/%d blocks/bytes, unused: %d/%d blocks/bytes.\n", hdesc->useblk,hdesc->usetot,hdesc->unuseblk,hdesc->unusetot); return(hdesc); } #if 0 int main(void) { int d = 0; int val = 0; struct hive *hd; int nk; char p[ABSPATHLEN]; hd = openHive("system.wrks",HMODE_RO); printf("Loaded..\n"); /* d = debugit(hd->buffer,hd->size); printf("Buffer was %s\n", d ? "dirty" : "clean"); */ val = get_dword(hd, 0, "\\Select\\Current"); printf("old value: %d [%x]\n",val,val); val = 0xabcd1234; printf("Changig value to %d, returned err=%d\n",val, put_dword(hd,0,"\\Select\\Cgurrent",val)); val = get_dword(hd, 0, "\\Select\\Current"); printf("new value: %u [%x]\n",val,val); nk_ls(hd, "\\ControlSet001\\Services\\Xga",0,0); printf("Gurba\n"); nk = trav_path(hd, 0, "\\ControlSet001\\Services\\Xga",0); p[0] = 0; printf("get_abs_path returns: %d\n",get_abs_path(hd, nk+4, p, 50)); printf("And path = %s\n",p); closeHive(hd); return(0); } #endif