Changeset 102


Ignore:
Timestamp:
04/02/08 22:30:26 (17 years ago)
Author:
tim
Message:

simplified root key search routines

rewrote sk record parsing

fixed nasty bug in parsing data-in-offset

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/regfi.h

    r101 r102  
    8686#define REGFI_NK_MIN_LENGTH     0x4C
    8787#define REGFI_VK_MIN_LENGTH     0x14
     88#define REGFI_SK_MIN_LENGTH     0x14
    8889
    8990/* Flags for the vk records */
     
    188189  uint32 hbin_off;      /* offset from beginning of this hbin block */
    189190  uint32 cell_size;     /* ((start_offset - end_offset) & 0xfffffff8) */
     191  uint32 offset;        /* Real file offset of this record */
    190192 
    191193  uint32 sk_off;        /* offset parsed from NK record used as a key
     
    196198  uint32 next_sk_off;
    197199  uint32 ref_count;
    198   uint32 size;
    199   uint8  header[REC_HDR_SIZE];
     200  uint32 desc_size;     /* size of security descriptor */
     201  uint16 unknown_tag;
     202  uint8  magic[REC_HDR_SIZE];
    200203} REGF_SK_REC;
    201204
  • trunk/lib/regfi.c

    r101 r102  
    426426
    427427/*******************************************************************
    428  Input a randon offset and receive the correpsonding HBIN
     428 Input a random offset and receive the correpsonding HBIN
    429429 block for it
    430430*******************************************************************/
     
    548548
    549549
     550
    550551/*******************************************************************
    551552 *******************************************************************/
    552 static bool hbin_prs_sk_rec( const char *desc, REGF_HBIN *hbin, int depth, REGF_SK_REC *sk )
    553 {
    554   prs_struct *ps = &hbin->ps;
    555   uint16 tag = 0xFFFF;
    556   uint32 data_size, start_off, end_off;
    557 
    558 
    559   depth++;
    560 
    561   if ( !prs_set_offset( &hbin->ps, sk->sk_off + HBIN_MAGIC_SIZE - hbin->first_hbin_off ) )
    562     return false;
    563 
    564   /* backup and get the data_size */
    565        
    566   if ( !prs_set_offset( &hbin->ps, hbin->ps.data_offset-sizeof(uint32)) )
    567     return false;
    568   start_off = hbin->ps.data_offset;
    569   if ( !prs_uint32( "cell_size", &hbin->ps, depth, &sk->cell_size ))
    570     return false;
    571 
    572   if (!prs_uint8s("header", ps, depth, sk->header, sizeof(sk->header)))
    573     return false;
    574   if ( !prs_uint16( "tag", ps, depth, &tag))
    575     return false;
    576 
    577   if ( !prs_uint32( "prev_sk_off", ps, depth, &sk->prev_sk_off))
    578     return false;
    579   if ( !prs_uint32( "next_sk_off", ps, depth, &sk->next_sk_off))
    580     return false;
    581   if ( !prs_uint32( "ref_count", ps, depth, &sk->ref_count))
    582     return false;
    583   if ( !prs_uint32( "size", ps, depth, &sk->size))
    584     return false;
    585 
    586   if ( !sec_io_desc( "sec_desc", &sk->sec_desc, ps, depth ))
    587     return false;
    588 
    589   end_off = hbin->ps.data_offset;
    590 
    591   /* data_size must be divisible by 8 and large enough to hold the original record */
    592 
    593   data_size = ((start_off - end_off) & 0xfffffff8 );
    594   /*  if ( data_size > sk->cell_size )*/
    595     /*DEBUG(10,("Encountered reused record (0x%x < 0x%x)\n", data_size, sk->cell_size));*/
    596 
    597   return true;
     553REGF_SK_REC* regfi_parse_sk(REGF_FILE* file, uint32 offset, uint32 max_size, bool strict)
     554{
     555  REGF_SK_REC* ret_val;
     556  uint32 cell_length, length;
     557  prs_struct ps;
     558  uint8 sk_header[REGFI_SK_MIN_LENGTH];
     559  bool unalloc = false;
     560
     561
     562  if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH,
     563                       &cell_length, &unalloc))
     564    return NULL;
     565   
     566  if(sk_header[0] != 's' || sk_header[1] != 'k')
     567    return NULL;
     568 
     569  ret_val = (REGF_SK_REC*)zalloc(sizeof(REGF_SK_REC));
     570  if(ret_val == NULL)
     571    return NULL;
     572
     573  ret_val->offset = offset;
     574  ret_val->cell_size = cell_length;
     575
     576  if(ret_val->cell_size > max_size)
     577    ret_val->cell_size = max_size & 0xFFFFFFF8;
     578  if((ret_val->cell_size < REGFI_SK_MIN_LENGTH)
     579     || (strict && ret_val->cell_size != (ret_val->cell_size & 0xFFFFFFF8)))
     580  {
     581    free(ret_val);
     582    return NULL;
     583  }
     584
     585
     586  ret_val->magic[0] = sk_header[0];
     587  ret_val->magic[1] = sk_header[1];
     588
     589  ret_val->unknown_tag = SVAL(sk_header, 0x2);
     590  ret_val->prev_sk_off = IVAL(sk_header, 0x4);
     591  ret_val->next_sk_off = IVAL(sk_header, 0x8);
     592  ret_val->ref_count = IVAL(sk_header, 0xC);
     593  ret_val->desc_size = IVAL(sk_header, 0x10);
     594
     595  if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size)
     596  {
     597    free(ret_val);
     598    return NULL;
     599  }
     600
     601  /* TODO: need to get rid of this, but currently the security descriptor
     602   * code depends on the ps structure.
     603   */
     604  if(!prs_init(&ps, ret_val->desc_size, NULL, UNMARSHALL))
     605  {
     606    free(ret_val);
     607    return NULL;
     608  }
     609
     610  length = ret_val->desc_size;
     611  if(regfi_read(file->fd, (uint8*)ps.data_p, &length) != 0
     612     || length != ret_val->desc_size)
     613  {
     614    free(ret_val);
     615    return NULL;
     616  }
     617
     618  if (!sec_io_desc("sec_desc", &ret_val->sec_desc, &ps, 0))
     619  {
     620    free(ret_val);
     621    return NULL;
     622  }
     623
     624  free(ps.data_p);
     625
     626  return ret_val;
    598627}
    599628
     
    706735  uint32 nk_cell_offset;
    707736  uint32 nk_max_length;
     737  uint32 sk_max_length;
    708738  int depth = 0;
    709739
     
    757787  }
    758788
    759   /* get the to the security descriptor.  First look if we have already parsed it */
    760        
     789  /* get the security descriptor.  First look if we have already parsed it */
    761790  if ((nk->sk_off!=REGF_OFFSET_NONE)
    762791      && !(nk->sec_desc = find_sk_record_by_offset( file, nk->sk_off )))
     
    775804    }
    776805   
    777     if ( !(nk->sec_desc = (REGF_SK_REC*)zalloc(sizeof(REGF_SK_REC) )) )
     806    sk_max_length = sub_hbin->block_size - (nk->sk_off - sub_hbin->first_hbin_off);
     807    nk->sec_desc = regfi_parse_sk(file, nk->sk_off + REGF_BLOCKSIZE,
     808                                  sk_max_length, true);
     809    if(nk->sec_desc == NULL)
    778810      return NULL;
    779811    nk->sec_desc->sk_off = nk->sk_off;
    780     if ( !hbin_prs_sk_rec( "sk_rec", sub_hbin, depth, nk->sec_desc ))
    781       return NULL;
    782812                       
    783813    /* add to the list of security descriptors (ref_count has been read from the files) */
    784 
    785     nk->sec_desc->sk_off = nk->sk_off;
    786814    /* XXX: this kind of caching needs to be re-evaluated */
    787815    DLIST_ADD( file->sec_desc_list, nk->sec_desc );
     
    792820
    793821
    794 /*******************************************************************
    795  *******************************************************************/
    796 static bool next_record( REGF_HBIN *hbin, const char *hdr, bool *eob )
    797 {
    798   uint8 header[REC_HDR_SIZE] = "";
    799   uint32 record_size;
    800   uint32 curr_off, block_size;
     822/******************************************************************************
     823
     824 ******************************************************************************/
     825static bool regfi_find_root_nk(REGF_FILE* file, uint32 offset, uint32 hbin_size,
     826                               uint32* root_offset)
     827{
     828  uint8 tmp[4];
     829  int32 record_size;
     830  uint32 length, hbin_offset = 0;
     831  REGF_NK_REC* nk = NULL;
    801832  bool found = false;
    802   prs_struct *ps = &hbin->ps;
    803        
    804   curr_off = ps->data_offset;
    805   if ( curr_off == 0 )
    806     prs_set_offset( ps, HBIN_HEADER_REC_SIZE+4 );
    807 
    808   /* assume that the current offset is at the reacord header
    809      and we need to backup to read the record size */
    810   curr_off -= sizeof(uint32);
    811 
    812   block_size = ps->buffer_size;
    813   record_size = 0;
    814   while ( !found )
    815   {
    816     curr_off = curr_off+record_size;
    817     if ( curr_off >= block_size )
    818       break;
    819 
    820     if ( !prs_set_offset( &hbin->ps, curr_off) )
     833
     834  for(record_size=0; !found && (hbin_offset < hbin_size); )
     835  {
     836    if(lseek(file->fd, offset+hbin_offset, SEEK_SET) == -1)
    821837      return false;
    822 
    823     if ( !prs_uint32( "record_size", ps, 0, &record_size ) )
     838   
     839    length = 4;
     840    if((regfi_read(file->fd, tmp, &length) != 0) || length != 4)
    824841      return false;
    825     if ( !prs_uint8s("header", ps, 0, header, REC_HDR_SIZE ) )
    826       return false;
    827 
    828     if ( record_size & 0x80000000 ) {
    829       /* absolute_value(record_size) */
    830       record_size = (record_size ^ 0xffffffff) + 1;
     842    record_size = IVALS(tmp, 0);
     843
     844    if(record_size < 0)
     845    {
     846      record_size = record_size*(-1);
     847      nk = regfi_parse_nk(file, offset+hbin_offset, hbin_size-hbin_offset, true);
     848      if(nk != NULL)
     849      {
     850        if(nk->key_type == NK_TYPE_ROOTKEY)
     851        {
     852          found = true;
     853          *root_offset = nk->offset;
     854        }
     855        free(nk);
     856      }
    831857    }
    832858
    833     if ( memcmp( header, hdr, REC_HDR_SIZE ) == 0 ) {
    834       found = true;
    835       curr_off += sizeof(uint32);
    836     }
    837   }
    838 
    839   /* mark prs_struct as done ( at end ) if no more SK records */
    840   /* mark end-of-block as true */       
    841   if ( !found )
    842   {
    843     prs_set_offset( &hbin->ps, hbin->ps.buffer_size );
    844     *eob = true;
    845     return false;
    846   }
    847 
    848   if (!prs_set_offset(ps, curr_off))
    849     return false;
    850 
    851   return true;
    852 }
    853 
    854 
    855 /*******************************************************************
    856  *******************************************************************/
    857 static REGF_NK_REC* next_nk_record(REGF_FILE *file, REGF_HBIN *hbin, bool *eob)
    858 {
    859   REGF_NK_REC* ret_val;
    860   if(next_record(hbin, "nk", eob)
    861      && (ret_val = hbin_prs_key(file, hbin)) != NULL)
    862     return ret_val;
    863 
    864 fprintf(stderr, "ACK!");
    865   return NULL;
     859    hbin_offset += record_size;
     860  }
     861
     862  return found;
    866863}
    867864
     
    930927 * on my experience.  --jerry
    931928 *****************************************************************************/
    932 REGF_NK_REC* regfi_rootkey( REGF_FILE *file )
    933 {
    934   REGF_NK_REC *nk;
    935   REGF_HBIN   *hbin;
    936   uint32      offset = REGF_BLOCKSIZE;
    937   bool        found = false;
    938   bool        eob;
     929REGF_NK_REC* regfi_rootkey(REGF_FILE *file)
     930{
     931  REGF_NK_REC* nk = NULL;
     932  REGF_HBIN*   hbin;
     933  uint32       offset = REGF_BLOCKSIZE;
     934  uint32       root_offset;
    939935 
    940936  if(!file)
    941937    return NULL;
    942938
    943   /* scan through the file on HBIN block at a time looking
     939  /* Scan through the file one HBIN block at a time looking
    944940     for an NK record with a type == 0x002c.
    945941     Normally this is the first nk record in the first hbin
    946942     block (but I'm not assuming that for now) */
    947        
     943
    948944  while((hbin = regfi_parse_hbin(file, offset, true, false)))
    949945  {
    950     eob = false;
    951 
    952     while(!eob)
     946    if(regfi_find_root_nk(file, hbin->file_off+HBIN_HEADER_REC_SIZE,
     947                          hbin->block_size-HBIN_HEADER_REC_SIZE, &root_offset))
    953948    {
    954       if((nk = next_nk_record(file, hbin, &eob)) != NULL)
    955       {
    956         if ( nk->key_type == NK_TYPE_ROOTKEY )
    957         {
    958           found = true;
    959           break;
    960         }
    961       }
    962       if(hbin->ps.is_dynamic)
    963         SAFE_FREE(hbin->ps.data_p);
    964       hbin->ps.is_dynamic = false;
    965       hbin->ps.buffer_size = 0;
    966       hbin->ps.data_offset = 0;
     949      if(!prs_set_offset(&hbin->ps, root_offset + 4
     950                         - hbin->first_hbin_off - REGF_BLOCKSIZE))
     951        return NULL;
     952     
     953      nk = hbin_prs_key(file, hbin);
     954      break;
    967955    }
    968                
    969     if(found)
    970       break;
    971956
    972957    offset += hbin->block_size;
    973958  }
    974  
    975   if (!found) {
    976     /*DEBUG(0,("regfi_rootkey: corrupt registry file ?  No root key record located\n"));*/
    977     return NULL;
    978   }
    979 
    980   /* XXX: this kind of caching needs to be re-evaluated */
    981   DLIST_ADD( file->block_list, hbin );
    982959
    983960  return nk;
     
    13741351{
    13751352  uint8 file_header[REGF_BLOCKSIZE];
    1376   uint32 ret, length;
     1353  uint32 length;
    13771354  uint32 file_length;
    13781355  struct stat sbuf;
     
    13961373
    13971374  length = REGF_BLOCKSIZE;
    1398   if((ret = regfi_read(fd, file_header, &length)) != 0
     1375  if((regfi_read(fd, file_header, &length)) != 0
    13991376     || length != REGF_BLOCKSIZE)
    14001377  {
     
    17921769  uint8* ret_val;
    17931770  uint32 read_length, cell_length;
     1771  uint8 i;
    17941772  bool unalloc;
    17951773
    17961774  /* The data is stored in the offset if the size <= 4 */
    1797   if (length & VK_DATA_IN_OFFSET)   
     1775  if (length & VK_DATA_IN_OFFSET)
    17981776  {
    17991777    length = length & ~VK_DATA_IN_OFFSET;
     
    18031781    if((ret_val = (uint8*)zalloc(sizeof(uint8)*length)) == NULL)
    18041782      return NULL;
    1805     memcpy(ret_val, &offset, length);
     1783
     1784    offset = offset - REGF_BLOCKSIZE;
     1785    for(i = 0; i < length; i++)
     1786      ret_val[i] = (uint8)((offset >> i*8) & 0xFF);
    18061787  }
    18071788  else
  • trunk/src/reglookup.c

    r97 r102  
    248248
    249249    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X",
    250              datap[0], datap[1], datap[2], datap[3]);
     250             datap[3], datap[2], datap[1], datap[0]);
    251251    return ascii;
    252252    break;
     
    259259
    260260    snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X",
    261              datap[3], datap[2], datap[1], datap[0]);
     261             datap[0], datap[1], datap[2], datap[3]);
    262262    return ascii;
    263263    break;
     
    277277
    278278  /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
    279    *      redone with fewer malloc calls and better string concatenation.
     279   *      redone with fewer malloc calls and better string concatenation.
     280   *      Also, gives lame output when "\0\0" is the string.
    280281   */
    281282  case REG_MULTI_SZ:
     
    532533  const char* str_type = NULL;
    533534  uint32 size;
    534   uint8 tmp_buf[4];
    535 
    536   /* Thanks Microsoft for making this process so straight-forward!!! */
    537   /* XXX: this logic should be abstracted  and pushed into the regfi
    538    *      interface.  This includes the size limits.
     535
     536  /* Microsoft's documentation indicates that "available memory" is
     537   * the limit on value sizes.  Annoying.  We limit it to 1M which
     538   * should rarely be exceeded, unless the file is corrupt or
     539   * malicious. For more info, see:
     540   *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
    539541   */
    540   size = (vk->data_size & ~VK_DATA_IN_OFFSET);
    541   if(vk->data_size & VK_DATA_IN_OFFSET)
    542   {
    543     tmp_buf[0] = (uint8)((vk->data_off >> 3) & 0xFF);
    544     tmp_buf[1] = (uint8)((vk->data_off >> 2) & 0xFF);
    545     tmp_buf[2] = (uint8)((vk->data_off >> 1) & 0xFF);
    546     tmp_buf[3] = (uint8)(vk->data_off & 0xFF);
    547     if(size > 4)
    548     {
    549       fprintf(stderr, "WARNING: value stored in offset larger than 4. "
    550               "Truncating...\n");
    551       size = 4;
    552     }
    553     quoted_value = data_to_ascii(tmp_buf, 4, vk->type, &conv_error);
    554   }
    555   else
    556   {
    557     /* Microsoft's documentation indicates that "available memory" is
    558      * the limit on value sizes.  Annoying.  We limit it to 1M which
    559      * should rarely be exceeded, unless the file is corrupt or
    560      * malicious. For more info, see:
    561      *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
    562      */
    563     if(size > VK_MAX_DATA_LENGTH)
    564     {
    565       fprintf(stderr, "WARNING: value data size %d larger than "
    566               "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
    567       size = VK_MAX_DATA_LENGTH;
    568     }
    569 
    570     quoted_value = data_to_ascii(vk->data, vk->data_size,
    571                                  vk->type, &conv_error);
    572   }
     542  if(size > VK_MAX_DATA_LENGTH)
     543  {
     544    fprintf(stderr, "WARNING: value data size %d larger than "
     545            "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
     546    size = VK_MAX_DATA_LENGTH;
     547  }
     548 
     549  quoted_value = data_to_ascii(vk->data, vk->data_size,
     550                               vk->type, &conv_error);
     551
    573552 
    574553  /* XXX: Sometimes value names can be NULL in registry.  Need to
Note: See TracChangeset for help on using the changeset viewer.