Index: trunk/include/regfi.h
===================================================================
--- trunk/include/regfi.h	(revision 101)
+++ trunk/include/regfi.h	(revision 102)
@@ -86,4 +86,5 @@
 #define REGFI_NK_MIN_LENGTH     0x4C
 #define REGFI_VK_MIN_LENGTH     0x14
+#define REGFI_SK_MIN_LENGTH     0x14
 
 /* Flags for the vk records */
@@ -188,4 +189,5 @@
   uint32 hbin_off;	/* offset from beginning of this hbin block */
   uint32 cell_size;	/* ((start_offset - end_offset) & 0xfffffff8) */
+  uint32 offset;        /* Real file offset of this record */
   
   uint32 sk_off;	/* offset parsed from NK record used as a key
@@ -196,6 +198,7 @@
   uint32 next_sk_off;
   uint32 ref_count;
-  uint32 size;
-  uint8  header[REC_HDR_SIZE];
+  uint32 desc_size;     /* size of security descriptor */
+  uint16 unknown_tag;
+  uint8  magic[REC_HDR_SIZE];
 } REGF_SK_REC;
 
Index: trunk/lib/regfi.c
===================================================================
--- trunk/lib/regfi.c	(revision 101)
+++ trunk/lib/regfi.c	(revision 102)
@@ -426,5 +426,5 @@
 
 /*******************************************************************
- Input a randon offset and receive the correpsonding HBIN 
+ Input a random offset and receive the correpsonding HBIN 
  block for it
 *******************************************************************/
@@ -548,52 +548,81 @@
 
 
+
 /*******************************************************************
  *******************************************************************/
-static bool hbin_prs_sk_rec( const char *desc, REGF_HBIN *hbin, int depth, REGF_SK_REC *sk )
-{
-  prs_struct *ps = &hbin->ps;
-  uint16 tag = 0xFFFF;
-  uint32 data_size, start_off, end_off;
-
-
-  depth++;
-
-  if ( !prs_set_offset( &hbin->ps, sk->sk_off + HBIN_MAGIC_SIZE - hbin->first_hbin_off ) )
-    return false;
-
-  /* backup and get the data_size */
-	
-  if ( !prs_set_offset( &hbin->ps, hbin->ps.data_offset-sizeof(uint32)) )
-    return false;
-  start_off = hbin->ps.data_offset;
-  if ( !prs_uint32( "cell_size", &hbin->ps, depth, &sk->cell_size ))
-    return false;
-
-  if (!prs_uint8s("header", ps, depth, sk->header, sizeof(sk->header)))
-    return false;
-  if ( !prs_uint16( "tag", ps, depth, &tag))
-    return false;
-
-  if ( !prs_uint32( "prev_sk_off", ps, depth, &sk->prev_sk_off))
-    return false;
-  if ( !prs_uint32( "next_sk_off", ps, depth, &sk->next_sk_off))
-    return false;
-  if ( !prs_uint32( "ref_count", ps, depth, &sk->ref_count))
-    return false;
-  if ( !prs_uint32( "size", ps, depth, &sk->size))
-    return false;
-
-  if ( !sec_io_desc( "sec_desc", &sk->sec_desc, ps, depth )) 
-    return false;
-
-  end_off = hbin->ps.data_offset;
-
-  /* data_size must be divisible by 8 and large enough to hold the original record */
-
-  data_size = ((start_off - end_off) & 0xfffffff8 );
-  /*  if ( data_size > sk->cell_size )*/
-    /*DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, sk->cell_size));*/
-
-  return true;
+REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, bool strict)
+{
+  REGF_SK_REC* ret_val;
+  uint32 cell_length, length;
+  prs_struct ps;
+  uint8 sk_header[REGFI_SK_MIN_LENGTH];
+  bool unalloc = false;
+
+
+  if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
+		       &cell_length, &unalloc))
+    return NULL;
+   
+  if(sk_header[0] != 's' || sk_header[1] != 'k')
+    return NULL;
+  
+  ret_val = (REGF_SK_REC*)zalloc(sizeof(REGF_SK_REC));
+  if(ret_val == NULL)
+    return NULL;
+
+  ret_val->offset = offset;
+  ret_val->cell_size = cell_length;
+
+  if(ret_val->cell_size > max_size)
+    ret_val->cell_size = max_size & 0xFFFFFFF8;
+  if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) 
+     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+
+  ret_val->magic[0] = sk_header[0];
+  ret_val->magic[1] = sk_header[1];
+
+  ret_val->unknown_tag = SVAL(sk_header, 0x2);
+  ret_val->prev_sk_off = IVAL(sk_header, 0x4);
+  ret_val->next_sk_off = IVAL(sk_header, 0x8);
+  ret_val->ref_count = IVAL(sk_header, 0xC);
+  ret_val->desc_size = IVAL(sk_header, 0x10);
+
+  if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  /* TODO: need to get rid of this, but currently the security descriptor
+   * code depends on the ps structure.
+   */
+  if(!prs_init(&ps, ret_val->desc_size, NULL, UNMARSHALL))
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  length = ret_val->desc_size;
+  if(regfi_read(file->fd, (uint8*)ps.data_p, &length) != 0 
+     || length != ret_val->desc_size)
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  if (!sec_io_desc("sec_desc", &ret_val->sec_desc, &ps, 0))
+  {
+    free(ret_val);
+    return NULL;
+  }
+
+  free(ps.data_p);
+
+  return ret_val;
 }
 
@@ -706,4 +735,5 @@
   uint32 nk_cell_offset;
   uint32 nk_max_length;
+  uint32 sk_max_length;
   int depth = 0;
 
@@ -757,6 +787,5 @@
   }
 
-  /* get the to the security descriptor.  First look if we have already parsed it */
-	
+  /* get the security descriptor.  First look if we have already parsed it */
   if ((nk->sk_off!=REGF_OFFSET_NONE) 
       && !(nk->sec_desc = find_sk_record_by_offset( file, nk->sk_off )))
@@ -775,13 +804,12 @@
     }
     
-    if ( !(nk->sec_desc = (REGF_SK_REC*)zalloc(sizeof(REGF_SK_REC) )) )
+    sk_max_length = sub_hbin->block_size - (nk->sk_off - sub_hbin->first_hbin_off);
+    nk->sec_desc = regfi_parse_sk(file, nk->sk_off + REGF_BLOCKSIZE, 
+				  sk_max_length, true);
+    if(nk->sec_desc == NULL)
       return NULL;
     nk->sec_desc->sk_off = nk->sk_off;
-    if ( !hbin_prs_sk_rec( "sk_rec", sub_hbin, depth, nk->sec_desc ))
-      return NULL;
 			
     /* add to the list of security descriptors (ref_count has been read from the files) */
-
-    nk->sec_desc->sk_off = nk->sk_off;
     /* XXX: this kind of caching needs to be re-evaluated */
     DLIST_ADD( file->sec_desc_list, nk->sec_desc );
@@ -792,76 +820,45 @@
 
 
-/*******************************************************************
- *******************************************************************/
-static bool next_record( REGF_HBIN *hbin, const char *hdr, bool *eob )
-{
-  uint8 header[REC_HDR_SIZE] = "";
-  uint32 record_size;
-  uint32 curr_off, block_size;
+/******************************************************************************
+
+ ******************************************************************************/
+static bool regfi_find_root_nk(REGF_FILE* file, uint32 offset, uint32 hbin_size,
+			       uint32* root_offset)
+{
+  uint8 tmp[4];
+  int32 record_size;
+  uint32 length, hbin_offset = 0;
+  REGF_NK_REC* nk = NULL;
   bool found = false;
-  prs_struct *ps = &hbin->ps;
-	
-  curr_off = ps->data_offset;
-  if ( curr_off == 0 )
-    prs_set_offset( ps, HBIN_HEADER_REC_SIZE+4 );
-
-  /* assume that the current offset is at the reacord header 
-     and we need to backup to read the record size */
-  curr_off -= sizeof(uint32);
-
-  block_size = ps->buffer_size;
-  record_size = 0;
-  while ( !found ) 
-  {
-    curr_off = curr_off+record_size;
-    if ( curr_off >= block_size ) 
-      break;
-
-    if ( !prs_set_offset( &hbin->ps, curr_off) )
+
+  for(record_size=0; !found && (hbin_offset < hbin_size); )
+  {
+    if(lseek(file->fd, offset+hbin_offset, SEEK_SET) == -1)
       return false;
-
-    if ( !prs_uint32( "record_size", ps, 0, &record_size ) )
+    
+    length = 4;
+    if((regfi_read(file->fd, tmp, &length) != 0) || length != 4)
       return false;
-    if ( !prs_uint8s("header", ps, 0, header, REC_HDR_SIZE ) )
-      return false;
-
-    if ( record_size & 0x80000000 ) {
-      /* absolute_value(record_size) */
-      record_size = (record_size ^ 0xffffffff) + 1;
+    record_size = IVALS(tmp, 0);
+
+    if(record_size < 0)
+    {
+      record_size = record_size*(-1);
+      nk = regfi_parse_nk(file, offset+hbin_offset, hbin_size-hbin_offset, true);
+      if(nk != NULL)
+      {
+	if(nk->key_type == NK_TYPE_ROOTKEY)
+	{
+	  found = true;
+	  *root_offset = nk->offset;
+	}
+	free(nk);
+      }
     }
 
-    if ( memcmp( header, hdr, REC_HDR_SIZE ) == 0 ) {
-      found = true;
-      curr_off += sizeof(uint32);
-    }
-  } 
-
-  /* mark prs_struct as done ( at end ) if no more SK records */
-  /* mark end-of-block as true */	
-  if ( !found )
-  {
-    prs_set_offset( &hbin->ps, hbin->ps.buffer_size );
-    *eob = true;
-    return false;
-  }
-
-  if (!prs_set_offset(ps, curr_off))
-    return false;
-
-  return true;
-}
-
-
-/*******************************************************************
- *******************************************************************/
-static REGF_NK_REC* next_nk_record(REGF_FILE *file, REGF_HBIN *hbin, bool *eob)
-{
-  REGF_NK_REC* ret_val;
-  if(next_record(hbin, "nk", eob)
-     && (ret_val = hbin_prs_key(file, hbin)) != NULL)
-    return ret_val;
-
-fprintf(stderr, "ACK!");
-  return NULL;
+    hbin_offset += record_size;
+  }
+
+  return found;
 }
 
@@ -930,54 +927,34 @@
  * on my experience.  --jerry
  *****************************************************************************/
-REGF_NK_REC* regfi_rootkey( REGF_FILE *file )
-{
-  REGF_NK_REC *nk;
-  REGF_HBIN   *hbin;
-  uint32      offset = REGF_BLOCKSIZE;
-  bool        found = false;
-  bool        eob;
+REGF_NK_REC* regfi_rootkey(REGF_FILE *file)
+{
+  REGF_NK_REC* nk = NULL;
+  REGF_HBIN*   hbin;
+  uint32       offset = REGF_BLOCKSIZE;
+  uint32       root_offset;
   
   if(!file)
     return NULL;
 
-  /* scan through the file on HBIN block at a time looking 
+  /* Scan through the file one HBIN block at a time looking 
      for an NK record with a type == 0x002c.
      Normally this is the first nk record in the first hbin 
      block (but I'm not assuming that for now) */
-	
+
   while((hbin = regfi_parse_hbin(file, offset, true, false))) 
   {
-    eob = false;
-
-    while(!eob) 
+    if(regfi_find_root_nk(file, hbin->file_off+HBIN_HEADER_REC_SIZE, 
+			  hbin->block_size-HBIN_HEADER_REC_SIZE, &root_offset))
     {
-      if((nk = next_nk_record(file, hbin, &eob)) != NULL) 
-      {
-	if ( nk->key_type == NK_TYPE_ROOTKEY ) 
-	{
-	  found = true;
-	  break;
-	}
-      }
-      if(hbin->ps.is_dynamic)
-	SAFE_FREE(hbin->ps.data_p);
-      hbin->ps.is_dynamic = false;
-      hbin->ps.buffer_size = 0;
-      hbin->ps.data_offset = 0;
+      if(!prs_set_offset(&hbin->ps, root_offset + 4 
+			 - hbin->first_hbin_off - REGF_BLOCKSIZE))
+	return NULL;
+      
+      nk = hbin_prs_key(file, hbin);
+      break;
     }
-		
-    if(found) 
-      break;
 
     offset += hbin->block_size;
   }
-  
-  if (!found) {
-    /*DEBUG(0,("regfi_rootkey: corrupt registry file ?  No root key record located\n"));*/
-    return NULL;
-  }
-
-  /* XXX: this kind of caching needs to be re-evaluated */
-  DLIST_ADD( file->block_list, hbin );
 
   return nk;
@@ -1374,5 +1351,5 @@
 {
   uint8 file_header[REGF_BLOCKSIZE];
-  uint32 ret, length;
+  uint32 length;
   uint32 file_length;
   struct stat sbuf;
@@ -1396,5 +1373,5 @@
 
   length = REGF_BLOCKSIZE;
-  if((ret = regfi_read(fd, file_header, &length)) != 0 
+  if((regfi_read(fd, file_header, &length)) != 0 
      || length != REGF_BLOCKSIZE)
   {
@@ -1792,8 +1769,9 @@
   uint8* ret_val;
   uint32 read_length, cell_length;
+  uint8 i;
   bool unalloc;
 
   /* The data is stored in the offset if the size <= 4 */
-  if (length & VK_DATA_IN_OFFSET)   
+  if (length & VK_DATA_IN_OFFSET)
   {
     length = length & ~VK_DATA_IN_OFFSET;
@@ -1803,5 +1781,8 @@
     if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
       return NULL;
-    memcpy(ret_val, &offset, length);
+
+    offset = offset - REGF_BLOCKSIZE;
+    for(i = 0; i < length; i++)
+      ret_val[i] = (uint8)((offset >> i*8) & 0xFF);
   }
   else
Index: trunk/src/reglookup.c
===================================================================
--- trunk/src/reglookup.c	(revision 101)
+++ trunk/src/reglookup.c	(revision 102)
@@ -248,5 +248,5 @@
 
     snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
-	     datap[0], datap[1], datap[2], datap[3]);
+	     datap[3], datap[2], datap[1], datap[0]);
     return ascii;
     break;
@@ -259,5 +259,5 @@
 
     snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X", 
-	     datap[3], datap[2], datap[1], datap[0]);
+	     datap[0], datap[1], datap[2], datap[3]);
     return ascii;
     break;
@@ -277,5 +277,6 @@
 
   /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
-   *      redone with fewer malloc calls and better string concatenation. 
+   *      redone with fewer malloc calls and better string concatenation.
+   *      Also, gives lame output when "\0\0" is the string.
    */
   case REG_MULTI_SZ:
@@ -532,43 +533,21 @@
   const char* str_type = NULL;
   uint32 size;
-  uint8 tmp_buf[4];
-
-  /* Thanks Microsoft for making this process so straight-forward!!! */
-  /* XXX: this logic should be abstracted  and pushed into the regfi 
-   *      interface.  This includes the size limits.
+
+  /* Microsoft's documentation indicates that "available memory" is 
+   * the limit on value sizes.  Annoying.  We limit it to 1M which 
+   * should rarely be exceeded, unless the file is corrupt or 
+   * malicious. For more info, see:
+   *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
    */
-  size = (vk->data_size & ~VK_DATA_IN_OFFSET);
-  if(vk->data_size & VK_DATA_IN_OFFSET)
-  {
-    tmp_buf[0] = (uint8)((vk->data_off >> 3) & 0xFF);
-    tmp_buf[1] = (uint8)((vk->data_off >> 2) & 0xFF);
-    tmp_buf[2] = (uint8)((vk->data_off >> 1) & 0xFF);
-    tmp_buf[3] = (uint8)(vk->data_off & 0xFF);
-    if(size > 4)
-    {
-      fprintf(stderr, "WARNING: value stored in offset larger than 4. "
-	      "Truncating...\n");
-      size = 4;
-    }
-    quoted_value = data_to_ascii(tmp_buf, 4, vk->type, &conv_error);
-  }
-  else
-  {
-    /* Microsoft's documentation indicates that "available memory" is 
-     * the limit on value sizes.  Annoying.  We limit it to 1M which 
-     * should rarely be exceeded, unless the file is corrupt or 
-     * malicious. For more info, see:
-     *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
-     */
-    if(size > VK_MAX_DATA_LENGTH)
-    {
-      fprintf(stderr, "WARNING: value data size %d larger than "
-	      "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
-      size = VK_MAX_DATA_LENGTH;
-    }
-
-    quoted_value = data_to_ascii(vk->data, vk->data_size, 
-				 vk->type, &conv_error);
-  }
+  if(size > VK_MAX_DATA_LENGTH)
+  {
+    fprintf(stderr, "WARNING: value data size %d larger than "
+	    "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
+    size = VK_MAX_DATA_LENGTH;
+  }
+  
+  quoted_value = data_to_ascii(vk->data, vk->data_size, 
+			       vk->type, &conv_error);
+
   
   /* XXX: Sometimes value names can be NULL in registry.  Need to
