libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

forensics.c (14022B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2026 Vidyut Samanta and Christian Grothoff
      4 
      5      libextractor is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libextractor is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libextractor; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19  */
     20 /**
     21  * @file plugins/forensics.c
     22  * @brief helpers shared by the plugins that read provenance out of
     23  *        binary headers
     24  * @author Christian Grothoff
     25  */
     26 #include "platform.h"
     27 #include "forensics.h"
     28 
     29 #include <math.h>
     30 #include <stdarg.h>
     31 
     32 
     33 int
     34 EXTRACTOR_forensic_read_ (struct EXTRACTOR_ExtractContext *ec,
     35                           int64_t offset,
     36                           void *buf,
     37                           size_t len)
     38 {
     39   unsigned char *out = buf;
     40   size_t got = 0;
     41 
     42   if (0 == len)
     43     return 1;
     44   if ( (0 <= offset) &&
     45        (offset != ec->seek (ec->cls,
     46                             offset,
     47                             SEEK_SET)) )
     48     return 0;
     49   while (got < len)
     50   {
     51     void *data;
     52     ssize_t ret;
     53 
     54     ret = ec->read (ec->cls,
     55                     &data,
     56                     len - got);
     57     if (0 >= ret)
     58       return 0;
     59     if (((size_t) ret) > len - got)
     60       return 0;   /* the IPC layer is misbehaving; do not overrun */
     61     memcpy (&out[got],
     62             data,
     63             (size_t) ret);
     64     got += (size_t) ret;
     65   }
     66   return 1;
     67 }
     68 
     69 
     70 int
     71 EXTRACTOR_forensic_utf8_valid_ (const char *data,
     72                                 size_t len)
     73 {
     74   const unsigned char *p = (const unsigned char *) data;
     75   size_t i = 0;
     76 
     77   while (i < len)
     78   {
     79     unsigned int extra;
     80     uint32_t cp;
     81 
     82     if (p[i] < 0x80)
     83     {
     84       i++;
     85       continue;
     86     }
     87     if ((0xC2 <= p[i]) && (p[i] <= 0xDF))
     88     {
     89       extra = 1;
     90       cp = p[i] & 0x1F;
     91     }
     92     else if ((0xE0 <= p[i]) && (p[i] <= 0xEF))
     93     {
     94       extra = 2;
     95       cp = p[i] & 0x0F;
     96     }
     97     else if ((0xF0 <= p[i]) && (p[i] <= 0xF4))
     98     {
     99       extra = 3;
    100       cp = p[i] & 0x07;
    101     }
    102     else
    103     {
    104       return 0;   /* continuation byte or overlong lead */
    105     }
    106     if (i + extra >= len)
    107       return 0;   /* truncated multi-byte sequence */
    108     for (unsigned int k = 1; k <= extra; k++)
    109     {
    110       if (0x80 != (p[i + k] & 0xC0))
    111         return 0;
    112       cp = (cp << 6) | (p[i + k] & 0x3F);
    113     }
    114     /* reject overlong encodings, surrogates and out-of-range values */
    115     if ( (1 == extra) && (cp < 0x80) )
    116       return 0;
    117     if ( (2 == extra) && (cp < 0x800) )
    118       return 0;
    119     if ( (3 == extra) && (cp < 0x10000) )
    120       return 0;
    121     if ( (0xD800 <= cp) && (cp <= 0xDFFF) )
    122       return 0;
    123     if (cp > 0x10FFFF)
    124       return 0;
    125     i += extra + 1;
    126   }
    127   return 1;
    128 }
    129 
    130 
    131 size_t
    132 EXTRACTOR_forensic_trim_ (const char *data,
    133                           size_t len)
    134 {
    135   while ( (0 < len) &&
    136           ( ('\0' == data[len - 1]) ||
    137             (' ' == data[len - 1]) ||
    138             ('\t' == data[len - 1]) ||
    139             ('\r' == data[len - 1]) ||
    140             ('\n' == data[len - 1]) ) )
    141     len--;
    142   return len;
    143 }
    144 
    145 
    146 int
    147 EXTRACTOR_forensic_parse_octal_ (const char *data,
    148                                  size_t len,
    149                                  uint64_t *value)
    150 {
    151   uint64_t v = 0;
    152   size_t i = 0;
    153   int digits = 0;
    154 
    155   while ( (i < len) &&
    156           (' ' == data[i]) )
    157     i++;
    158   while ( (i < len) &&
    159           ('0' <= data[i]) &&
    160           ('7' >= data[i]) )
    161   {
    162     if (v > (UINT64_MAX >> 3))
    163       return 0;   /* would overflow */
    164     v = (v << 3) | (uint64_t) (data[i] - '0');
    165     digits++;
    166     i++;
    167   }
    168   if (0 == digits)
    169     return 0;
    170   /* what follows must be padding, not more number */
    171   while (i < len)
    172   {
    173     if ( ('\0' != data[i]) &&
    174          (' ' != data[i]) )
    175       return 0;
    176     i++;
    177   }
    178   *value = v;
    179   return 1;
    180 }
    181 
    182 
    183 double
    184 EXTRACTOR_forensic_entropy_ (const unsigned char *data,
    185                              size_t len)
    186 {
    187   unsigned long counts[256];
    188   double entropy = 0.0;
    189 
    190   if (0 == len)
    191     return 0.0;
    192   memset (counts,
    193           0,
    194           sizeof (counts));
    195   for (size_t i = 0; i < len; i++)
    196     counts[data[i]]++;
    197   for (unsigned int i = 0; i < 256; i++)
    198   {
    199     double p;
    200 
    201     if (0 == counts[i])
    202       continue;
    203     p = ((double) counts[i]) / ((double) len);
    204     entropy -= p * log2 (p);
    205   }
    206   return entropy;
    207 }
    208 
    209 
    210 int
    211 EXTRACTOR_forensic_emit_ (struct EXTRACTOR_ExtractContext *ec,
    212                           const char *plugin,
    213                           enum EXTRACTOR_MetaType type,
    214                           const char *fmt,
    215                           ...)
    216 {
    217   char buf[EXTRACTOR_FORENSIC_MAX_STRING];
    218   va_list ap;
    219   int len;
    220 
    221   va_start (ap, fmt);
    222   len = vsnprintf (buf,
    223                    sizeof (buf),
    224                    fmt,
    225                    ap);
    226   va_end (ap);
    227   if (0 >= len)
    228     return 0;
    229   if (((size_t) len) >= sizeof (buf))
    230     len = sizeof (buf) - 1;
    231   return (0 != ec->proc (ec->cls,
    232                          plugin,
    233                          type,
    234                          EXTRACTOR_METAFORMAT_UTF8,
    235                          "text/plain",
    236                          buf,
    237                          (size_t) len + 1)) ? 1 : 0;
    238 }
    239 
    240 
    241 int
    242 EXTRACTOR_forensic_emit_text_ (struct EXTRACTOR_ExtractContext *ec,
    243                                const char *plugin,
    244                                enum EXTRACTOR_MetaType type,
    245                                const char *data,
    246                                size_t len)
    247 {
    248   char buf[EXTRACTOR_FORENSIC_MAX_STRING];
    249   size_t out = 0;
    250 
    251   /* a NUL inside the field ends the string: these are C strings in
    252      fixed-width slots far more often than they are counted strings */
    253   for (size_t i = 0; i < len; i++)
    254     if ('\0' == data[i])
    255     {
    256       len = i;
    257       break;
    258     }
    259   len = EXTRACTOR_forensic_trim_ (data,
    260                                   len);
    261   if (0 == len)
    262     return 0;
    263   if (len > sizeof (buf) - 1)
    264     len = sizeof (buf) - 1;
    265   for (size_t i = 0; i < len; i++)
    266   {
    267     unsigned char c = (unsigned char) data[i];
    268 
    269     /* Collapse control characters rather than dropping the value: a
    270        path with a stray tab in it is still the path we want to show,
    271        but it must not be able to forge line structure in the output. */
    272     if ( (c < 0x20) || (0x7F == c) )
    273       buf[out++] = ' ';
    274     else
    275       buf[out++] = (char) c;
    276   }
    277   out = EXTRACTOR_forensic_trim_ (buf,
    278                                   out);
    279   if (0 == out)
    280     return 0;
    281   buf[out] = '\0';
    282   if (! EXTRACTOR_forensic_utf8_valid_ (buf,
    283                                         out))
    284     return 0;   /* not text we can hand out; the caller may know better */
    285   return (0 != ec->proc (ec->cls,
    286                          plugin,
    287                          type,
    288                          EXTRACTOR_METAFORMAT_UTF8,
    289                          "text/plain",
    290                          buf,
    291                          out + 1)) ? 1 : 0;
    292 }
    293 
    294 
    295 int
    296 EXTRACTOR_forensic_emit_utf16le_ (struct EXTRACTOR_ExtractContext *ec,
    297                                   const char *plugin,
    298                                   enum EXTRACTOR_MetaType type,
    299                                   const unsigned char *data,
    300                                   size_t bytes)
    301 {
    302   char buf[EXTRACTOR_FORENSIC_MAX_STRING];
    303   size_t out = 0;
    304   size_t i = 0;
    305 
    306   while (i + 1 < bytes)
    307   {
    308     uint32_t cp = EXTRACTOR_forensic_le16_ (&data[i]);
    309 
    310     i += 2;
    311     if (0 == cp)
    312       break;   /* NUL terminator */
    313     if ( (0xD800 <= cp) && (cp <= 0xDBFF) )
    314     {
    315       uint32_t lo;
    316 
    317       if (i + 1 >= bytes)
    318         break;   /* truncated surrogate pair */
    319       lo = EXTRACTOR_forensic_le16_ (&data[i]);
    320       if ( (lo < 0xDC00) || (lo > 0xDFFF) )
    321         break;   /* unpaired surrogate; stop rather than guess */
    322       i += 2;
    323       cp = 0x10000 + ((cp - 0xD800) << 10) + (lo - 0xDC00);
    324     }
    325     else if ( (0xDC00 <= cp) && (cp <= 0xDFFF) )
    326     {
    327       break;   /* stray low surrogate */
    328     }
    329     if ( (cp < 0x20) || (0x7F == cp) )
    330       cp = ' ';
    331     if (out + 4 >= sizeof (buf))
    332       break;
    333     if (cp < 0x80)
    334     {
    335       buf[out++] = (char) cp;
    336     }
    337     else if (cp < 0x800)
    338     {
    339       buf[out++] = (char) (0xC0 | (cp >> 6));
    340       buf[out++] = (char) (0x80 | (cp & 0x3F));
    341     }
    342     else if (cp < 0x10000)
    343     {
    344       buf[out++] = (char) (0xE0 | (cp >> 12));
    345       buf[out++] = (char) (0x80 | ((cp >> 6) & 0x3F));
    346       buf[out++] = (char) (0x80 | (cp & 0x3F));
    347     }
    348     else
    349     {
    350       buf[out++] = (char) (0xF0 | (cp >> 18));
    351       buf[out++] = (char) (0x80 | ((cp >> 12) & 0x3F));
    352       buf[out++] = (char) (0x80 | ((cp >> 6) & 0x3F));
    353       buf[out++] = (char) (0x80 | (cp & 0x3F));
    354     }
    355   }
    356   out = EXTRACTOR_forensic_trim_ (buf,
    357                                   out);
    358   if (0 == out)
    359     return 0;
    360   buf[out] = '\0';
    361   return (0 != ec->proc (ec->cls,
    362                          plugin,
    363                          type,
    364                          EXTRACTOR_METAFORMAT_UTF8,
    365                          "text/plain",
    366                          buf,
    367                          out + 1)) ? 1 : 0;
    368 }
    369 
    370 
    371 int
    372 EXTRACTOR_forensic_emit_unix_time_ (struct EXTRACTOR_ExtractContext *ec,
    373                                     const char *plugin,
    374                                     enum EXTRACTOR_MetaType type,
    375                                     int64_t when)
    376 {
    377   char buf[32];
    378   struct tm tm;
    379   time_t t = (time_t) when;
    380 
    381   /* Zero and 0xFFFFFFFF are how these fields look when they were never
    382      filled in; anything outside a plausible range is a misparse. */
    383   if ( (when < 315532800LL) ||      /* 1980-01-01 */
    384        (when > 4102444800LL) )      /* 2100-01-01 */
    385     return 0;
    386   if (NULL == gmtime_r (&t,
    387                         &tm))
    388     return 0;
    389   if (0 == strftime (buf,
    390                      sizeof (buf),
    391                      "%Y-%m-%dT%H:%M:%SZ",
    392                      &tm))
    393     return 0;
    394   return (0 != ec->proc (ec->cls,
    395                          plugin,
    396                          type,
    397                          EXTRACTOR_METAFORMAT_UTF8,
    398                          "text/plain",
    399                          buf,
    400                          strlen (buf) + 1)) ? 1 : 0;
    401 }
    402 
    403 
    404 int
    405 EXTRACTOR_forensic_emit_filetime_ (struct EXTRACTOR_ExtractContext *ec,
    406                                    const char *plugin,
    407                                    enum EXTRACTOR_MetaType type,
    408                                    uint64_t filetime)
    409 {
    410   /* FILETIME counts 100ns intervals from 1601-01-01; 11644473600 is the
    411      number of seconds from there to the Unix epoch. */
    412   if (0 == filetime)
    413     return 0;
    414   if (filetime > (uint64_t) 200000000000000000ULL)
    415     return 0;   /* well past year 2234; a misparse */
    416   return EXTRACTOR_forensic_emit_unix_time_ (ec,
    417                                              plugin,
    418                                              type,
    419                                              (int64_t) (filetime / 10000000ULL)
    420                                              - 11644473600LL);
    421 }
    422 
    423 
    424 int
    425 EXTRACTOR_forensic_emit_size_ (struct EXTRACTOR_ExtractContext *ec,
    426                                const char *plugin,
    427                                enum EXTRACTOR_MetaType type,
    428                                uint64_t bytes)
    429 {
    430   return EXTRACTOR_forensic_emit_ (ec,
    431                                    plugin,
    432                                    type,
    433                                    "%llu",
    434                                    (unsigned long long) bytes);
    435 }
    436 
    437 
    438 int
    439 EXTRACTOR_forensic_emit_hex_ (struct EXTRACTOR_ExtractContext *ec,
    440                               const char *plugin,
    441                               enum EXTRACTOR_MetaType type,
    442                               const unsigned char *data,
    443                               size_t len)
    444 {
    445   static const char hex[] = "0123456789abcdef";
    446   char buf[129];
    447 
    448   if ( (0 == len) ||
    449        (len > (sizeof (buf) - 1) / 2) )
    450     return 0;
    451   for (size_t i = 0; i < len; i++)
    452   {
    453     buf[2 * i] = hex[data[i] >> 4];
    454     buf[2 * i + 1] = hex[data[i] & 0x0F];
    455   }
    456   buf[2 * len] = '\0';
    457   return (0 != ec->proc (ec->cls,
    458                          plugin,
    459                          type,
    460                          EXTRACTOR_METAFORMAT_UTF8,
    461                          "text/plain",
    462                          buf,
    463                          2 * len + 1)) ? 1 : 0;
    464 }
    465 
    466 
    467 int
    468 EXTRACTOR_forensic_emit_guid_ (struct EXTRACTOR_ExtractContext *ec,
    469                                const char *plugin,
    470                                enum EXTRACTOR_MetaType type,
    471                                const unsigned char *guid,
    472                                int mixed_endian)
    473 {
    474   uint32_t d1;
    475   uint16_t d2;
    476   uint16_t d3;
    477   int all_zero = 1;
    478 
    479   for (unsigned int i = 0; i < 16; i++)
    480     if (0 != guid[i])
    481     {
    482       all_zero = 0;
    483       break;
    484     }
    485   if (all_zero)
    486     return 0;
    487   if (mixed_endian)
    488   {
    489     d1 = EXTRACTOR_forensic_le32_ (guid);
    490     d2 = EXTRACTOR_forensic_le16_ (&guid[4]);
    491     d3 = EXTRACTOR_forensic_le16_ (&guid[6]);
    492   }
    493   else
    494   {
    495     d1 = EXTRACTOR_forensic_be32_ (guid);
    496     d2 = EXTRACTOR_forensic_be16_ (&guid[4]);
    497     d3 = EXTRACTOR_forensic_be16_ (&guid[6]);
    498   }
    499   return EXTRACTOR_forensic_emit_ (ec,
    500                                    plugin,
    501                                    type,
    502                                    "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
    503                                    (unsigned int) d1,
    504                                    (unsigned int) d2,
    505                                    (unsigned int) d3,
    506                                    guid[8], guid[9],
    507                                    guid[10], guid[11], guid[12],
    508                                    guid[13], guid[14], guid[15]);
    509 }
    510 
    511 
    512 /* end of forensics.c */