Changeset 111 for trunk/src/reglookup.c


Ignore:
Timestamp:
05/01/08 00:06:22 (16 years ago)
Author:
tim
Message:

Switched license to GPLv3

Added early version of new tool, reglookup-recover

Many library changes made to support this new tool

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/reglookup.c

    r110 r111  
    33 * Gerald Carter''s regfio interface.
    44 *
    5  * Copyright (C) 2005-2007 Timothy D. Morgan
     5 * Copyright (C) 2005-2008 Timothy D. Morgan
    66 * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com
    77 *
    88 * This program is free software; you can redistribute it and/or modify
    99 * it under the terms of the GNU General Public License as published by
    10  * the Free Software Foundation; version 2 of the License.
     10 * the Free Software Foundation; version 3 of the License.
    1111 *
    1212 * This program is distributed in the hope that it will be useful,
     
    2929#include <strings.h>
    3030#include <time.h>
    31 #include <iconv.h>
    3231#include "../include/regfi.h"
    3332#include "../include/void_stack.h"
     
    4544/* Other globals */
    4645REGF_FILE* f;
    47 const char* key_special_chars = ",\"\\/";
    48 const char* subfield_special_chars = ",\"\\|";
    49 const char* common_special_chars = ",\"\\";
    50 
    51 iconv_t conv_desc;
    52 
    53 
    54 void bailOut(int code, char* message)
    55 {
    56   fprintf(stderr, message);
    57   exit(code);
    58 }
    59 
    60 
    61 /* Returns a newly malloc()ed string which contains original buffer,
    62  * except for non-printable or special characters are quoted in hex
    63  * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
    64  * character.  A null terminator is added, since only ascii, not binary,
    65  * is returned.
     46
     47
     48/* TODO: a hack to share some functions with reglookup-recover.c.
     49 *       Should move these into a properly library at some point.
    6650 */
    67 static char* quote_buffer(const unsigned char* str,
    68                           unsigned int len, const char* special)
    69 {
    70   unsigned int i, added_len;
    71   unsigned int num_written = 0;
    72 
    73   unsigned int buf_len = sizeof(char)*(len+1);
    74   char* ret_val = malloc(buf_len);
    75   char* tmp_buf;
    76 
    77   if(ret_val == NULL)
    78     return NULL;
    79 
    80   for(i=0; i<len; i++)
    81   {
    82     if(buf_len <= (num_written+5))
    83     {
    84       /* Expand the buffer by the memory consumption rate seen so far
    85        * times the amount of input left to process.  The expansion is bounded
    86        * below by a minimum safety increase, and above by the maximum possible
    87        * output string length.  This should minimize both the number of
    88        * reallocs() and the amount of wasted memory.
    89        */
    90       added_len = (len-i)*num_written/(i+1);
    91       if((buf_len+added_len) > (len*4+1))
    92         buf_len = len*4+1;
    93       else
    94       {
    95         if (added_len < 5)
    96           buf_len += 5;
    97         else
    98           buf_len += added_len;
    99       }
    100 
    101       tmp_buf = realloc(ret_val, buf_len);
    102       if(tmp_buf == NULL)
    103       {
    104         free(ret_val);
    105         return NULL;
    106       }
    107       ret_val = tmp_buf;
    108     }
    109    
    110     if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL)
    111     {
    112       num_written += snprintf(ret_val + num_written, buf_len - num_written,
    113                               "\\x%.2X", str[i]);
    114     }
     51#include "common.c"
     52
     53
     54void printValue(const REGF_VK_REC* vk, char* prefix)
     55{
     56  char* quoted_value = NULL;
     57  char* quoted_name = NULL;
     58  char* conv_error = NULL;
     59  const char* str_type = NULL;
     60  uint32 size = vk->data_size;
     61
     62  /* Microsoft's documentation indicates that "available memory" is
     63   * the limit on value sizes.  Annoying.  We limit it to 1M which
     64   * should rarely be exceeded, unless the file is corrupt or
     65   * malicious. For more info, see:
     66   *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
     67   */
     68  if(size > VK_MAX_DATA_LENGTH)
     69  {
     70    fprintf(stderr, "WARNING: value data size %d larger than "
     71            "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
     72    size = VK_MAX_DATA_LENGTH;
     73  }
     74 
     75  quoted_name = quote_string(vk->valuename, key_special_chars);
     76  if (quoted_name == NULL)
     77  { /* Value names are NULL when we're looking at the "(default)" value.
     78     * Currently we just return a 0-length string to try an eliminate
     79     * ambiguity with a literal "(default)" value.  The data type of a line
     80     * in the output allows one to differentiate between the parent key and
     81     * this value.
     82     */
     83    quoted_name = malloc(1*sizeof(char));
     84    if(quoted_name == NULL)
     85      bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
     86    quoted_name[0] = '\0';
     87  }
     88
     89  quoted_value = data_to_ascii(vk->data, size, vk->type, &conv_error);
     90  if(quoted_value == NULL)
     91  {
     92    if(conv_error == NULL)
     93      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
     94              "Memory allocation failure likely.\n", prefix, quoted_name);
     95    else if(print_verbose)
     96      fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
     97              "Returned error: %s\n", prefix, quoted_name, conv_error);
     98  }
     99  /* XXX: should these always be printed? */
     100  else if(conv_error != NULL && print_verbose)
     101    fprintf(stderr, "VERBOSE: While quoting value for '%s/%s', "
     102            "warning returned: %s\n", prefix, quoted_name, conv_error);
     103
     104  str_type = regfi_type_val2str(vk->type);
     105  if(print_security)
     106  {
     107    if(str_type == NULL)
     108      printf("%s/%s,0x%.8X,%s,,,,,\n", prefix, quoted_name,
     109             vk->type, quoted_value);
    115110    else
    116       ret_val[num_written++] = str[i];
    117   }
    118   ret_val[num_written] = '\0';
    119 
    120   return ret_val;
    121 }
    122 
    123 
    124 /* Returns a newly malloc()ed string which contains original string,
    125  * except for non-printable or special characters are quoted in hex
    126  * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted
    127  * character.
    128  */
    129 static char* quote_string(const char* str, const char* special)
    130 {
    131   unsigned int len;
    132 
    133   if(str == NULL)
    134     return NULL;
    135 
    136   len = strlen(str);
    137   return quote_buffer((const unsigned char*)str, len, special);
    138 }
    139 
    140 
    141 /*
    142  * Convert from UTF-16LE to ASCII.  Accepts a Unicode buffer, uni, and
    143  * it's length, uni_max.  Writes ASCII to the buffer ascii, whose size
    144  * is ascii_max.  Writes at most (ascii_max-1) bytes to ascii, and null
    145  * terminates the string.  Returns the length of the string stored in
    146  * ascii.  On error, returns a negative errno code.
    147  */
    148 static int uni_to_ascii(unsigned char* uni, char* ascii,
    149                         unsigned int uni_max, unsigned int ascii_max)
    150 {
    151   char* inbuf = (char*)uni;
    152   char* outbuf = ascii;
    153   size_t in_len = (size_t)uni_max;
    154   size_t out_len = (size_t)(ascii_max-1);
    155   int ret;
    156 
    157   /* Set up conversion descriptor. */
    158   conv_desc = iconv_open("US-ASCII", "UTF-16LE");
    159 
    160   ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len);
    161   if(ret == -1)
    162   {
    163     iconv_close(conv_desc);
    164     return -errno;
    165   }
    166   *outbuf = '\0';
    167 
    168   iconv_close(conv_desc); 
    169   return strlen(ascii);
    170 }
    171 
    172 
    173 /*
    174  * Convert a data value to a string for display.  Returns NULL on error,
    175  * and the string to display if there is no error, or a non-fatal
    176  * error.  On any error (fatal or non-fatal) occurs, (*error_msg) will
    177  * be set to a newly allocated string, containing an error message.  If
    178  * a memory allocation failure occurs while generating the error
    179  * message, both the return value and (*error_msg) will be NULL.  It
    180  * is the responsibility of the caller to free both a non-NULL return
    181  * value, and a non-NULL (*error_msg).
    182  */
    183 static char* data_to_ascii(unsigned char *datap, uint32 len, uint32 type,
    184                            char** error_msg)
    185 {
    186   char* asciip;
    187   char* ascii;
    188   unsigned char* cur_str;
    189   char* cur_ascii;
    190   char* cur_quoted;
    191   char* tmp_err;
    192   const char* str_type;
    193   uint32 i;
    194   uint32 cur_str_len;
    195   uint32 ascii_max, cur_str_max;
    196   uint32 str_rem, cur_str_rem, alen;
    197   int ret_err;
    198   unsigned short num_nulls;
    199 
    200   *error_msg = NULL;
    201 
    202   switch (type)
    203   {
    204   case REG_SZ:
    205   case REG_EXPAND_SZ:
    206     /* REG_LINK is a symbolic link, stored as a unicode string. */
    207   case REG_LINK:
    208     ascii_max = sizeof(char)*(len+1);
    209     ascii = malloc(ascii_max);
    210     if(ascii == NULL)
    211       return NULL;
    212    
    213     /* Sometimes values have binary stored in them.  If the unicode
    214      * conversion fails, just quote it raw.
    215      */
    216     ret_err = uni_to_ascii(datap, ascii, len, ascii_max);
    217     if(ret_err < 0)
    218     {
    219       tmp_err = strerror(-ret_err);
    220       str_type = regfi_type_val2str(type);
    221       *error_msg = (char*)malloc(65+strlen(str_type)+strlen(tmp_err)+1);
    222       if(*error_msg == NULL)
    223       {
    224         free(ascii);
    225         return NULL;
    226       }
    227       sprintf(*error_msg, "Unicode conversion failed on %s field; "
    228                "printing as binary.  Error: %s", str_type, tmp_err);
    229      
    230       cur_quoted = quote_buffer(datap, len, common_special_chars);
    231     }
     111      printf("%s/%s,%s,%s,,,,,\n", prefix, quoted_name,
     112             str_type, quoted_value);
     113  }
     114  else
     115  {
     116    if(str_type == NULL)
     117      printf("%s/%s,0x%.8X,%s,\n", prefix, quoted_name,
     118             vk->type, quoted_value);
    232119    else
    233       cur_quoted = quote_string(ascii, common_special_chars);
    234     free(ascii);
    235     if(cur_quoted == NULL)
    236     {
    237       *error_msg = (char*)malloc(27+1);
    238       if(*error_msg != NULL)
    239         strcpy(*error_msg, "Buffer could not be quoted.");
    240     }
    241     return cur_quoted;
    242     break;
    243 
    244   case REG_DWORD:
    245     ascii_max = sizeof(char)*(8+2+1);
    246     ascii = malloc(ascii_max);
    247     if(ascii == NULL)
    248       return NULL;
    249 
    250     snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X",
    251              datap[3], datap[2], datap[1], datap[0]);
    252     return ascii;
    253     break;
    254 
    255   case REG_DWORD_BE:
    256     ascii_max = sizeof(char)*(8+2+1);
    257     ascii = malloc(ascii_max);
    258     if(ascii == NULL)
    259       return NULL;
    260 
    261     snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X",
    262              datap[0], datap[1], datap[2], datap[3]);
    263     return ascii;
    264     break;
    265 
    266   case REG_QWORD:
    267     ascii_max = sizeof(char)*(16+2+1);
    268     ascii = malloc(ascii_max);
    269     if(ascii == NULL)
    270       return NULL;
    271 
    272     snprintf(ascii, ascii_max, "0x%.2X%.2X%.2X%.2X%.2X%.2X%.2X%.2X",
    273              datap[7], datap[6], datap[5], datap[4],
    274              datap[3], datap[2], datap[1], datap[0]);
    275     return ascii;
    276     break;
    277    
    278 
    279   /* XXX: this MULTI_SZ parser is pretty inefficient.  Should be
    280    *      redone with fewer malloc calls and better string concatenation.
    281    *      Also, gives lame output when "\0\0" is the string.
    282    */
    283   case REG_MULTI_SZ:
    284     ascii_max = sizeof(char)*(len*4+1);
    285     cur_str_max = sizeof(char)*(len+1);
    286     cur_str = malloc(cur_str_max);
    287     cur_ascii = malloc(cur_str_max);
    288     ascii = malloc(ascii_max);
    289     if(ascii == NULL || cur_str == NULL || cur_ascii == NULL)
    290       return NULL;
    291 
    292     /* Reads until it reaches 4 consecutive NULLs,
    293      * which is two nulls in unicode, or until it reaches len, or until we
    294      * run out of buffer.  The latter should never happen, but we shouldn't
    295      * trust our file to have the right lengths/delimiters.
    296      */
    297     asciip = ascii;
    298     num_nulls = 0;
    299     str_rem = ascii_max;
    300     cur_str_rem = cur_str_max;
    301     cur_str_len = 0;
    302 
    303     for(i=0; (i < len) && str_rem > 0; i++)
    304     {
    305       *(cur_str+cur_str_len) = *(datap+i);
    306       if(*(cur_str+cur_str_len) == 0)
    307         num_nulls++;
    308       else
    309         num_nulls = 0;
    310       cur_str_len++;
    311 
    312       if(num_nulls == 2)
    313       {
    314         ret_err = uni_to_ascii(cur_str, cur_ascii, cur_str_len-1, cur_str_max);
    315         if(ret_err < 0)
    316         {
    317           /* XXX: should every sub-field error be enumerated? */
    318           if(*error_msg == NULL)
    319           {
    320             tmp_err = strerror(-ret_err);
    321             *error_msg = (char*)malloc(90+strlen(tmp_err)+1);
    322             if(*error_msg == NULL)
    323             {
    324               free(cur_str);
    325               free(cur_ascii);
    326               free(ascii);
    327               return NULL;
    328             }
    329             sprintf(*error_msg, "Unicode conversion failed on at least one "
    330                     "MULTI_SZ sub-field; printing as binary.  Error: %s",
    331                     tmp_err);
    332           }
    333           cur_quoted = quote_buffer(cur_str, cur_str_len-1,
    334                                     subfield_special_chars);
    335         }
    336         else
    337           cur_quoted = quote_string(cur_ascii, subfield_special_chars);
    338 
    339         alen = snprintf(asciip, str_rem, "%s", cur_quoted);
    340         asciip += alen;
    341         str_rem -= alen;
    342         free(cur_quoted);
    343 
    344         if(*(datap+i+1) == 0 && *(datap+i+2) == 0)
    345           break;
    346         else
    347         {
    348           if(str_rem > 0)
    349           {
    350             asciip[0] = '|';
    351             asciip[1] = '\0';
    352             asciip++;
    353             str_rem--;
    354           }
    355           memset(cur_str, 0, cur_str_max);
    356           cur_str_len = 0;
    357           num_nulls = 0;
    358           /* To eliminate leading nulls in subsequent strings. */
    359           i++;
    360         }
    361       }
    362     }
    363     *asciip = 0;
    364     free(cur_str);
    365     free(cur_ascii);
    366     return ascii;
    367     break;
    368 
    369   /* XXX: Dont know what to do with these yet, just print as binary... */
    370   default:
    371     fprintf(stderr, "WARNING: Unrecognized registry data type (0x%.8X); quoting as binary.\n", type);
    372    
    373   case REG_NONE:
    374   case REG_RESOURCE_LIST:
    375   case REG_FULL_RESOURCE_DESCRIPTOR:
    376   case REG_RESOURCE_REQUIREMENTS_LIST:
    377 
    378   case REG_BINARY:
    379     return quote_buffer(datap, len, common_special_chars);
    380     break;
    381   }
    382 
    383   return NULL;
    384 }
     120      printf("%s/%s,%s,%s,\n", prefix, quoted_name,
     121             str_type, quoted_value);
     122  }
     123
     124  if(quoted_value != NULL)
     125    free(quoted_value);
     126  if(quoted_name != NULL)
     127    free(quoted_name);
     128  if(conv_error != NULL)
     129    free(conv_error);
     130}
     131
    385132
    386133
     
    527274
    528275
    529 void printValue(const REGF_VK_REC* vk, char* prefix)
    530 {
    531   char* quoted_value = NULL;
    532   char* quoted_name = NULL;
    533   char* conv_error = NULL;
    534   const char* str_type = NULL;
    535   uint32 size;
    536 
    537   /* Microsoft's documentation indicates that "available memory" is
    538    * the limit on value sizes.  Annoying.  We limit it to 1M which
    539    * should rarely be exceeded, unless the file is corrupt or
    540    * malicious. For more info, see:
    541    *   http://msdn2.microsoft.com/en-us/library/ms724872.aspx
    542    */
    543   if(size > VK_MAX_DATA_LENGTH)
    544   {
    545     fprintf(stderr, "WARNING: value data size %d larger than "
    546             "%d, truncating...\n", size, VK_MAX_DATA_LENGTH);
    547     size = VK_MAX_DATA_LENGTH;
    548   }
    549  
    550   quoted_value = data_to_ascii(vk->data, vk->data_size,
    551                                vk->type, &conv_error);
    552 
    553  
    554   /* XXX: Sometimes value names can be NULL in registry.  Need to
    555    *      figure out why and when, and generate the appropriate output
    556    *      for that condition.
    557    */
    558   quoted_name = quote_string(vk->valuename, common_special_chars);
    559   if (quoted_name == NULL)
    560   {
    561     quoted_name = malloc(1*sizeof(char));
    562     if(quoted_name == NULL)
    563       bailOut(EX_OSERR, "ERROR: Could not allocate sufficient memory.\n");
    564     quoted_name[0] = '\0';
    565   }
    566 
    567   if(quoted_value == NULL)
    568   {
    569     if(conv_error == NULL)
    570       fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
    571               "Memory allocation failure likely.\n", prefix, quoted_name);
    572     else
    573       fprintf(stderr, "WARNING: Could not quote value for '%s/%s'.  "
    574               "Returned error: %s\n", prefix, quoted_name, conv_error);
    575   }
    576   /* XXX: should these always be printed? */
    577   else if(conv_error != NULL && print_verbose)
    578       fprintf(stderr, "VERBOSE: While quoting value for '%s/%s', "
    579               "warning returned: %s\n", prefix, quoted_name, conv_error);
    580 
    581   str_type = regfi_type_val2str(vk->type);
    582   if(print_security)
    583   {
    584     if(str_type == NULL)
    585       printf("%s/%s,0x%.8X,%s,,,,,\n", prefix, quoted_name,
    586              vk->type, quoted_value);
    587     else
    588       printf("%s/%s,%s,%s,,,,,\n", prefix, quoted_name,
    589              str_type, quoted_value);
    590   }
    591   else
    592   {
    593     if(str_type == NULL)
    594       printf("%s/%s,0x%.8X,%s,\n", prefix, quoted_name,
    595              vk->type, quoted_value);
    596     else
    597       printf("%s/%s,%s,%s,\n", prefix, quoted_name,
    598              str_type, quoted_value);
    599   }
    600 
    601   if(quoted_value != NULL)
    602     free(quoted_value);
    603   if(quoted_name != NULL)
    604     free(quoted_name);
    605   if(conv_error != NULL)
    606     free(conv_error);
    607 }
    608 
    609 
    610276void printValueList(REGFI_ITERATOR* i, char* prefix)
    611277{
     
    825491          " [-p <PATH_FILTER>] [-t <TYPE_FILTER>]"
    826492          " <REGISTRY_FILE>\n");
    827   fprintf(stderr, "Version: 0.4.0\n");
     493  fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION);
    828494  fprintf(stderr, "Options:\n");
    829495  fprintf(stderr, "\t-v\t sets verbose mode.\n");
Note: See TracChangeset for help on using the changeset viewer.