libextractor

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

diskimage_extractor.c (57917B)


      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/diskimage_extractor.c
     22  * @brief plugin to support virtual disk images: QCOW/QCOW2, VMDK,
     23  *        VHD and VHDX
     24  * @author Christian Grothoff
     25  *
     26  * These four formats answer the same questions, so one plugin answers
     27  * them once: how big does the disk claim to be, which virtual machine
     28  * or tool wrote it, is it a differencing image (and what is its parent,
     29  * because then there are sibling files the investigator also needs),
     30  * and is it encrypted.
     31  *
     32  * Deliberately *not* done here: identifying the guest file system
     33  * inside the image.  That means following the allocation tables to the
     34  * first sectors of the guest disk, which is a second-pass job; this
     35  * plugin never reads more than a handful of headers.
     36  *
     37  * References:
     38  * - QCOW2: `docs/interop/qcow2.txt' in the QEMU source tree.
     39  * - VMDK: "Virtual Disk Format 5.0" (VMware technical note).
     40  * - VHD: "Virtual Hard Disk Image Format Specification" 1.0 (Microsoft).
     41  * - VHDX: [MS-VHDX], "Virtual Hard Disk v2 (VHDX) File Format".
     42  */
     43 #include "platform.h"
     44 #include "extractor.h"
     45 #include "forensics.h"
     46 
     47 
     48 /**
     49  * Name we report our meta data under.
     50  */
     51 #define DISKIMAGE "diskimage"
     52 
     53 /**
     54  * Evaluate @a call and return 1 from the enclosing function if the
     55  * caller asked us to stop extracting.
     56  */
     57 #define CHECK(call) do { if (0 != (call)) return 1; } while (0)
     58 
     59 /**
     60  * Largest VMDK text descriptor we will read.  Real descriptors are a
     61  * few hundred bytes; the header field giving the size is attacker
     62  * controlled, so it needs a hard cap.
     63  */
     64 #define VMDK_MAX_DESCRIPTOR (32 * 1024)
     65 
     66 /**
     67  * Largest number of lines we look at in a VMDK descriptor.
     68  */
     69 #define VMDK_MAX_LINES 1024
     70 
     71 /**
     72  * Largest number of region table entries [MS-VHDX] allows.
     73  */
     74 #define VHDX_MAX_REGIONS 2047
     75 
     76 /**
     77  * Largest number of metadata table entries [MS-VHDX] allows.
     78  */
     79 #define VHDX_MAX_METADATA 2047
     80 
     81 /**
     82  * Largest single VHDX metadata item we will read.  The largest one we
     83  * care about is the parent locator; everything else is 4 to 16 bytes.
     84  */
     85 #define VHDX_MAX_ITEM (16 * 1024)
     86 
     87 /**
     88  * Seconds between the VHD epoch (2000-01-01T00:00:00Z) and the Unix
     89  * epoch.
     90  */
     91 #define VHD_EPOCH 946684800LL
     92 
     93 
     94 /**
     95  * Compare a NUL-terminated key from a file against a literal, ignoring
     96  * ASCII case.  Not `strcasecmp()' because that is locale-dependent.
     97  *
     98  * @param key NUL-terminated key from the file
     99  * @param want the literal to compare against
    100  * @return 1 if they match, 0 if not
    101  */
    102 static int
    103 key_is (const char *key,
    104         const char *want)
    105 {
    106   size_t i;
    107 
    108   for (i = 0; '\0' != want[i]; i++)
    109   {
    110     char a = key[i];
    111     char b = want[i];
    112 
    113     if ( ('A' <= a) && ('Z' >= a) )
    114       a = (char) (a - 'A' + 'a');
    115     if ( ('A' <= b) && ('Z' >= b) )
    116       b = (char) (b - 'A' + 'a');
    117     if (a != b)
    118       return 0;
    119   }
    120   return ('\0' == key[i]);
    121 }
    122 
    123 
    124 /**
    125  * Copy @a src into @a dst, truncating if it does not fit.
    126  *
    127  * @param dst destination buffer
    128  * @param dst_size number of bytes in @a dst, including the NUL
    129  * @param src NUL-terminated source string
    130  */
    131 static void
    132 copy_bounded (char *dst,
    133               size_t dst_size,
    134               const char *src)
    135 {
    136   size_t i;
    137 
    138   for (i = 0; (i + 1 < dst_size) && ('\0' != src[i]); i++)
    139     dst[i] = src[i];
    140   dst[i] = '\0';
    141 }
    142 
    143 
    144 /**
    145  * Strip leading and trailing white space from @a s in place.
    146  *
    147  * @param s the string to trim, modified in place
    148  * @return pointer into @a s at the first non-blank character
    149  */
    150 static char *
    151 trim_inplace (char *s)
    152 {
    153   size_t len;
    154 
    155   while ( (' ' == *s) ||
    156           ('\t' == *s) ||
    157           ('\r' == *s) )
    158     s++;
    159   len = strlen (s);
    160   while ( (0 < len) &&
    161           ( (' ' == s[len - 1]) ||
    162             ('\t' == s[len - 1]) ||
    163             ('\r' == s[len - 1]) ) )
    164     len--;
    165   s[len] = '\0';
    166   return s;
    167 }
    168 
    169 
    170 /**
    171  * Remove one layer of double quotes from @a s, in place.
    172  *
    173  * @param s the string, modified in place
    174  * @return pointer to the unquoted string
    175  */
    176 static char *
    177 unquote_inplace (char *s)
    178 {
    179   size_t len = strlen (s);
    180 
    181   if ( (2 <= len) &&
    182        ('"' == s[0]) &&
    183        ('"' == s[len - 1]) )
    184   {
    185     s[len - 1] = '\0';
    186     return &s[1];
    187   }
    188   return s;
    189 }
    190 
    191 
    192 /**
    193  * Parse an unsigned decimal number, rejecting anything that is not
    194  * exactly a number.
    195  *
    196  * @param s the string
    197  * @param[out] value where to store the result
    198  * @return 1 on success, 0 if @a s is not a bounded decimal number
    199  */
    200 static int
    201 parse_u64 (const char *s,
    202            uint64_t *value)
    203 {
    204   uint64_t v = 0;
    205   int digits = 0;
    206 
    207   for (size_t i = 0; '\0' != s[i]; i++)
    208   {
    209     if ( ('0' > s[i]) ||
    210          ('9' < s[i]) )
    211       return 0;
    212     if (v > (UINT64_MAX - 9) / 10)
    213       return 0;   /* would overflow */
    214     v = v * 10 + (uint64_t) (s[i] - '0');
    215     digits++;
    216     if (20 < digits)
    217       return 0;
    218   }
    219   if (0 == digits)
    220     return 0;
    221   *value = v;
    222   return 1;
    223 }
    224 
    225 
    226 /**
    227  * Emit the MIME type of the format we just identified.
    228  *
    229  * @param ec extraction context
    230  * @param mime the MIME type
    231  * @return 1 if the caller should stop extracting, 0 to continue
    232  */
    233 static int
    234 emit_mime (struct EXTRACTOR_ExtractContext *ec,
    235            const char *mime)
    236 {
    237   return (0 != ec->proc (ec->cls,
    238                          DISKIMAGE,
    239                          EXTRACTOR_METATYPE_MIMETYPE,
    240                          EXTRACTOR_METAFORMAT_UTF8,
    241                          "text/plain",
    242                          mime,
    243                          strlen (mime) + 1)) ? 1 : 0;
    244 }
    245 
    246 
    247 /* ------------------------------------------------------------------ */
    248 /* QCOW / QCOW2                                                       */
    249 /* ------------------------------------------------------------------ */
    250 
    251 
    252 /**
    253  * Report the QCOW2 version 3 feature bits that say something about the
    254  * state the image was left in.
    255  *
    256  * @param ec extraction context
    257  * @param incompatible the incompatible feature bit field
    258  * @param compatible the compatible feature bit field
    259  * @param autoclear the autoclear feature bit field
    260  * @return 1 if the caller should stop extracting, 0 to continue
    261  */
    262 static int
    263 qcow_features (struct EXTRACTOR_ExtractContext *ec,
    264                uint64_t incompatible,
    265                uint64_t compatible,
    266                uint64_t autoclear)
    267 {
    268   /* Bit 0 is left set while the image is open for writing: seeing it
    269      in a file at rest means the writer died without closing it.  Bit 1
    270      is set by QEMU itself when it detects inconsistent metadata. */
    271   if (0 != (incompatible & 1ULL))
    272     CHECK (EXTRACTOR_forensic_emit_ (ec,
    273                                      DISKIMAGE,
    274                                      EXTRACTOR_METATYPE_ATTRIBUTES,
    275                                      "%s",
    276                                      "dirty bit set"
    277                                      " (image was not closed cleanly)"));
    278   if (0 != (incompatible & 2ULL))
    279     CHECK (EXTRACTOR_forensic_emit_ (ec,
    280                                      DISKIMAGE,
    281                                      EXTRACTOR_METATYPE_ATTRIBUTES,
    282                                      "%s",
    283                                      "corrupt bit set"
    284                                      " (image marked corrupt by QEMU)"));
    285   if (0 != (incompatible & 4ULL))
    286     CHECK (EXTRACTOR_forensic_emit_ (ec,
    287                                      DISKIMAGE,
    288                                      EXTRACTOR_METATYPE_ATTRIBUTES,
    289                                      "%s",
    290                                      "data is in an external data file"));
    291   if (0 != (incompatible & 16ULL))
    292     CHECK (EXTRACTOR_forensic_emit_ (ec,
    293                                      DISKIMAGE,
    294                                      EXTRACTOR_METATYPE_ATTRIBUTES,
    295                                      "%s",
    296                                      "extended L2 entries"));
    297   if (0 != (compatible & 1ULL))
    298     CHECK (EXTRACTOR_forensic_emit_ (ec,
    299                                      DISKIMAGE,
    300                                      EXTRACTOR_METATYPE_ATTRIBUTES,
    301                                      "%s",
    302                                      "lazy refcounts"));
    303   if (0 != (autoclear & 1ULL))
    304     CHECK (EXTRACTOR_forensic_emit_ (ec,
    305                                      DISKIMAGE,
    306                                      EXTRACTOR_METATYPE_ATTRIBUTES,
    307                                      "%s",
    308                                      "persistent dirty bitmaps"));
    309   return 0;
    310 }
    311 
    312 
    313 /**
    314  * Extract from a QCOW or QCOW2 image.  The magic has already matched.
    315  *
    316  * @param ec extraction context
    317  * @return 1 if the caller should stop extracting, 0 to continue
    318  */
    319 static int
    320 extract_qcow (struct EXTRACTOR_ExtractContext *ec)
    321 {
    322   unsigned char hdr[112];
    323   size_t have;
    324   uint32_t version;
    325   uint64_t backing_offset;
    326   uint32_t backing_size;
    327   uint64_t size;
    328 
    329   memset (hdr,
    330           0,
    331           sizeof (hdr));
    332   /* A version 3 header is 104 or 112 bytes, a version 2 header 72 and
    333      a version 1 header 48; take the largest that the file holds. */
    334   if (EXTRACTOR_forensic_read_ (ec, 0, hdr, sizeof (hdr)))
    335     have = sizeof (hdr);
    336   else if (EXTRACTOR_forensic_read_ (ec, 0, hdr, 72))
    337     have = 72;
    338   else if (EXTRACTOR_forensic_read_ (ec, 0, hdr, 48))
    339     have = 48;
    340   else
    341     return 0;   /* magic matched but there is no header behind it */
    342   version = EXTRACTOR_forensic_be32_ (&hdr[4]);
    343   if ( (0 == version) ||
    344        (3 < version) )
    345     return 0;   /* not a QCOW generation we know how to read */
    346   CHECK (emit_mime (ec,
    347                     "application/x-qemu-disk"));
    348   CHECK (EXTRACTOR_forensic_emit_ (ec,
    349                                    DISKIMAGE,
    350                                    EXTRACTOR_METATYPE_FORMAT,
    351                                    "%s",
    352                                    (1 == version) ? "QCOW" : "QCOW2"));
    353   CHECK (EXTRACTOR_forensic_emit_ (ec,
    354                                    DISKIMAGE,
    355                                    EXTRACTOR_METATYPE_FORMAT_VERSION,
    356                                    "%u",
    357                                    (unsigned int) version));
    358   /* The backing file reference and the virtual size sit at the same
    359      place in all three generations. */
    360   size = EXTRACTOR_forensic_be64_ (&hdr[24]);
    361   if ( (0 != size) &&
    362        (size < (1ULL << 62)) )
    363     CHECK (EXTRACTOR_forensic_emit_size_ (ec,
    364                                           DISKIMAGE,
    365                                           EXTRACTOR_METATYPE_VOLUME_SIZE,
    366                                           size));
    367   backing_offset = EXTRACTOR_forensic_be64_ (&hdr[8]);
    368   backing_size = EXTRACTOR_forensic_be32_ (&hdr[16]);
    369   if ( (0 != backing_offset) &&
    370        (0 != backing_size) &&
    371        (EXTRACTOR_FORENSIC_MAX_STRING >= backing_size) &&
    372        (backing_offset <= (uint64_t) INT64_MAX - backing_size) )
    373   {
    374     char name[EXTRACTOR_FORENSIC_MAX_STRING];
    375 
    376     if (EXTRACTOR_forensic_read_ (ec,
    377                                   (int64_t) backing_offset,
    378                                   name,
    379                                   backing_size))
    380       CHECK (EXTRACTOR_forensic_emit_text_ (ec,
    381                                             DISKIMAGE,
    382                                             EXTRACTOR_METATYPE_PARENT_IMAGE,
    383                                             name,
    384                                             backing_size));
    385   }
    386   if ( (1 == version) ||
    387        (72 > have) )
    388     return 0;   /* everything below moved in, or arrived with, version 2 */
    389   {
    390     uint32_t cluster_bits = EXTRACTOR_forensic_be32_ (&hdr[20]);
    391     uint32_t crypt = EXTRACTOR_forensic_be32_ (&hdr[32]);
    392     uint32_t snapshots = EXTRACTOR_forensic_be32_ (&hdr[60]);
    393 
    394     /* 9..21 is what QEMU itself accepts; outside that the field is
    395        either garbage or would make 1 << cluster_bits undefined. */
    396     if ( (9 <= cluster_bits) &&
    397          (21 >= cluster_bits) )
    398       CHECK (EXTRACTOR_forensic_emit_size_ (ec,
    399                                             DISKIMAGE,
    400                                             EXTRACTOR_METATYPE_BLOCK_SIZE,
    401                                             1ULL << cluster_bits));
    402     if (0 != crypt)
    403       CHECK (EXTRACTOR_forensic_emit_ (ec,
    404                                        DISKIMAGE,
    405                                        EXTRACTOR_METATYPE_ENCRYPTION,
    406                                        "%s",
    407                                        (1 == crypt)
    408                                        ? "AES"
    409                                        : ( (2 == crypt)
    410                                            ? "LUKS"
    411                                            : "unknown")));
    412     CHECK (EXTRACTOR_forensic_emit_ (ec,
    413                                      DISKIMAGE,
    414                                      EXTRACTOR_METATYPE_ENTRY_COUNT,
    415                                      "%u",
    416                                      (unsigned int) snapshots));
    417   }
    418   if ( (3 == version) &&
    419        (104 <= have) )
    420   {
    421     uint64_t incompatible = EXTRACTOR_forensic_be64_ (&hdr[72]);
    422     uint64_t compatible = EXTRACTOR_forensic_be64_ (&hdr[80]);
    423     uint64_t autoclear = EXTRACTOR_forensic_be64_ (&hdr[88]);
    424     uint32_t header_length = EXTRACTOR_forensic_be32_ (&hdr[100]);
    425 
    426     CHECK (qcow_features (ec,
    427                           incompatible,
    428                           compatible,
    429                           autoclear));
    430     /* The compression type byte only exists when the matching
    431        incompatible bit is set; otherwise the image is plain zlib. */
    432     if ( (0 != (incompatible & 8ULL)) &&
    433          (104 < header_length) &&
    434          (105 <= have) )
    435       CHECK (EXTRACTOR_forensic_emit_ (ec,
    436                                        DISKIMAGE,
    437                                        EXTRACTOR_METATYPE_COMMENT,
    438                                        "compression type: %s",
    439                                        (0 == hdr[104])
    440                                        ? "zlib"
    441                                        : ( (1 == hdr[104])
    442                                            ? "zstd"
    443                                            : "unknown")));
    444   }
    445   return 0;
    446 }
    447 
    448 
    449 /* ------------------------------------------------------------------ */
    450 /* VMDK                                                               */
    451 /* ------------------------------------------------------------------ */
    452 
    453 
    454 /**
    455  * Parse one `RW <sectors> <type> "<file>"' extent line of a VMDK text
    456  * descriptor.
    457  *
    458  * @param ec extraction context
    459  * @param line the line, NUL-terminated, modified in place
    460  * @param[in,out] extents number of extents seen so far
    461  * @param[in,out] sectors total number of sectors seen so far
    462  * @return 1 if the caller should stop extracting, 0 to continue
    463  */
    464 static int
    465 vmdk_extent_line (struct EXTRACTOR_ExtractContext *ec,
    466                   char *line,
    467                   unsigned int *extents,
    468                   uint64_t *sectors)
    469 {
    470   char *p = line;
    471   char *count;
    472   uint64_t n;
    473 
    474   (*extents)++;
    475   while ( ('\0' != *p) &&
    476           (' ' != *p) )
    477     p++;   /* skip the access mode */
    478   while (' ' == *p)
    479     p++;
    480   count = p;
    481   while ( ('0' <= *p) &&
    482           ('9' >= *p) )
    483     p++;
    484   if (' ' == *p)
    485   {
    486     *p = '\0';
    487     p++;
    488     if ( (parse_u64 (count,
    489                      &n)) &&
    490          (n < (1ULL << 53)) )
    491       *sectors += n;
    492     while (' ' == *p)
    493       p++;
    494     while ( ('\0' != *p) &&
    495             (' ' != *p) )
    496       p++;   /* skip the extent type */
    497     while (' ' == *p)
    498       p++;
    499   }
    500   if ('"' == *p)
    501   {
    502     char *end;
    503 
    504     p++;
    505     end = p;
    506     while ( ('\0' != *end) &&
    507             ('"' != *end) )
    508       end++;
    509     if ('"' == *end)
    510     {
    511       *end = '\0';
    512       /* The extent files are sibling files an investigator has to
    513          collect as well, so name them -- but only the first few. */
    514       if (EXTRACTOR_FORENSIC_MAX_ITEMS >= *extents)
    515         CHECK (EXTRACTOR_forensic_emit_text_ (ec,
    516                                               DISKIMAGE,
    517                                               EXTRACTOR_METATYPE_FILENAME,
    518                                               p,
    519                                               strlen (p)));
    520     }
    521   }
    522   return 0;
    523 }
    524 
    525 
    526 /**
    527  * Parse the text descriptor of a VMDK, either the copy embedded in a
    528  * sparse extent header or a stand-alone descriptor file.
    529  *
    530  * @param ec extraction context
    531  * @param text the descriptor, modified in place; need not be
    532  *        NUL-terminated, @a len bounds it
    533  * @param len number of bytes in @a text
    534  * @param sparse 1 if this is the copy embedded in a sparse extent
    535  *        header, whose binary fields already gave us the version and
    536  *        the virtual size; 0 for a stand-alone descriptor file, where
    537  *        both have to come from the text
    538  * @return 1 if the caller should stop extracting, 0 to continue
    539  */
    540 static int
    541 vmdk_descriptor (struct EXTRACTOR_ExtractContext *ec,
    542                  char *text,
    543                  size_t len,
    544                  int sparse)
    545 {
    546   char parent_cid[64];
    547   char parent_hint[EXTRACTOR_FORENSIC_MAX_STRING];
    548   char adapter[64];
    549   uint64_t cylinders = 0;
    550   uint64_t heads = 0;
    551   uint64_t track_sectors = 0;
    552   uint64_t total_sectors = 0;
    553   unsigned int extents = 0;
    554   unsigned int lines = 0;
    555   unsigned int keys = 0;
    556   size_t pos = 0;
    557 
    558   parent_cid[0] = '\0';
    559   parent_hint[0] = '\0';
    560   adapter[0] = '\0';
    561   /* A NUL inside the descriptor ends it: everything past it is the
    562      zero padding of the descriptor slot. */
    563   for (size_t i = 0; i < len; i++)
    564     if ('\0' == text[i])
    565     {
    566       len = i;
    567       break;
    568     }
    569   while ( (pos < len) &&
    570           (VMDK_MAX_LINES > lines) )
    571   {
    572     char *line;
    573     char *eq;
    574     size_t end = pos;
    575 
    576     lines++;
    577     while ( (end < len) &&
    578             ('\n' != text[end]) )
    579       end++;
    580     line = &text[pos];
    581     text[end] = '\0';   /* safe: either the '\n' or the end of the slot */
    582     pos = end + 1;
    583     line = trim_inplace (line);
    584     if ( ('\0' == line[0]) ||
    585          ('#' == line[0]) )
    586       continue;
    587     if ( (0 == strncmp (line, "RW ", 3)) ||
    588          (0 == strncmp (line, "RDONLY ", 7)) ||
    589          (0 == strncmp (line, "NOACCESS ", 9)) )
    590     {
    591       CHECK (vmdk_extent_line (ec,
    592                                line,
    593                                &extents,
    594                                &total_sectors));
    595       continue;
    596     }
    597     eq = strchr (line,
    598                  '=');
    599     if (NULL == eq)
    600       continue;
    601     *eq = '\0';
    602     {
    603       char *key = trim_inplace (line);
    604       char *value = unquote_inplace (trim_inplace (&eq[1]));
    605 
    606       if ('\0' == value[0])
    607         continue;
    608       /* A descriptor that repeats a key a thousand times must not turn
    609          into a thousand meta data items; a real one has about fifteen
    610          keys, so stopping here loses nothing. */
    611       if (EXTRACTOR_FORENSIC_MAX_ITEMS <= keys)
    612         continue;
    613       keys++;
    614       if (key_is (key,
    615                   "CID"))
    616         CHECK (EXTRACTOR_forensic_emit_text_ (ec,
    617                                               DISKIMAGE,
    618                                               EXTRACTOR_METATYPE_VOLUME_SERIAL,
    619                                               value,
    620                                               strlen (value)));
    621       else if (key_is (key,
    622                        "parentCID"))
    623         copy_bounded (parent_cid,
    624                       sizeof (parent_cid),
    625                       value);
    626       else if (key_is (key,
    627                        "parentFileNameHint"))
    628         copy_bounded (parent_hint,
    629                       sizeof (parent_hint),
    630                       value);
    631       else if ( (! sparse) &&
    632                 (key_is (key,
    633                          "version")) )
    634         CHECK (EXTRACTOR_forensic_emit_text_ (ec,
    635                                               DISKIMAGE,
    636                                               EXTRACTOR_METATYPE_FORMAT_VERSION,
    637                                               value,
    638                                               strlen (value)));
    639       else if (key_is (key,
    640                        "createType"))
    641         CHECK (EXTRACTOR_forensic_emit_text_ (ec,
    642                                               DISKIMAGE,
    643                                               EXTRACTOR_METATYPE_FORMAT,
    644                                               value,
    645                                               strlen (value)));
    646       else if (key_is (key,
    647                        "ddb.virtualHWVersion"))
    648         CHECK (EXTRACTOR_forensic_emit_ (ec,
    649                                          DISKIMAGE,
    650                                          EXTRACTOR_METATYPE_SOFTWARE_VERSION,
    651                                          "virtual hardware version %.16s",
    652                                          value));
    653       else if (key_is (key,
    654                        "ddb.toolsVersion"))
    655         CHECK (EXTRACTOR_forensic_emit_ (ec,
    656                                          DISKIMAGE,
    657                                          EXTRACTOR_METATYPE_SOFTWARE_VERSION,
    658                                          "VMware Tools version %.16s",
    659                                          value));
    660       else if (key_is (key,
    661                        "ddb.adapterType"))
    662         copy_bounded (adapter,
    663                       sizeof (adapter),
    664                       value);
    665       else if (key_is (key,
    666                        "ddb.geometry.cylinders"))
    667         (void) parse_u64 (value,
    668                           &cylinders);
    669       else if (key_is (key,
    670                        "ddb.geometry.heads"))
    671         (void) parse_u64 (value,
    672                           &heads);
    673       else if (key_is (key,
    674                        "ddb.geometry.sectors"))
    675         (void) parse_u64 (value,
    676                           &track_sectors);
    677       else if ( (0 == strncmp (key, "ddb.uuid", 8)) ||
    678                 (key_is (key, "ddb.longContentID")) )
    679         CHECK (EXTRACTOR_forensic_emit_text_ (ec,
    680                                               DISKIMAGE,
    681                                               EXTRACTOR_METATYPE_VOLUME_SERIAL,
    682                                               value,
    683                                               strlen (value)));
    684     }
    685   }
    686   if ('\0' != adapter[0])
    687     CHECK (EXTRACTOR_forensic_emit_ (ec,
    688                                      DISKIMAGE,
    689                                      EXTRACTOR_METATYPE_COMMENT,
    690                                      "adapter type: %.32s",
    691                                      adapter));
    692   if ( (0 != cylinders) &&
    693        (0 != heads) &&
    694        (0 != track_sectors) )
    695   {
    696     CHECK (EXTRACTOR_forensic_emit_ (ec,
    697                                      DISKIMAGE,
    698                                      EXTRACTOR_METATYPE_COMMENT,
    699                                      "geometry: %llu cylinders,"
    700                                      " %llu heads, %llu sectors per track",
    701                                      (unsigned long long) cylinders,
    702                                      (unsigned long long) heads,
    703                                      (unsigned long long) track_sectors));
    704   }
    705   if (0 != extents)
    706     CHECK (EXTRACTOR_forensic_emit_ (ec,
    707                                      DISKIMAGE,
    708                                      EXTRACTOR_METATYPE_ENTRY_COUNT,
    709                                      "%u",
    710                                      extents));
    711   /* A stand-alone descriptor has no binary header to take the virtual
    712      size from, so add up the extents instead. */
    713   if ( (! sparse) &&
    714        (0 != total_sectors) &&
    715        (total_sectors < (1ULL << 53)) )
    716     CHECK (EXTRACTOR_forensic_emit_size_ (ec,
    717                                           DISKIMAGE,
    718                                           EXTRACTOR_METATYPE_VOLUME_SIZE,
    719                                           total_sectors * 512ULL));
    720   /* `ffffffff' is the documented "no parent" value. */
    721   if ( ('\0' != parent_cid[0]) &&
    722        (! key_is (parent_cid,
    723                   "ffffffff")) )
    724   {
    725     if ('\0' != parent_hint[0])
    726       CHECK (EXTRACTOR_forensic_emit_text_ (ec,
    727                                             DISKIMAGE,
    728                                             EXTRACTOR_METATYPE_PARENT_IMAGE,
    729                                             parent_hint,
    730                                             strlen (parent_hint)));
    731     else
    732       CHECK (EXTRACTOR_forensic_emit_ (ec,
    733                                        DISKIMAGE,
    734                                        EXTRACTOR_METATYPE_PARENT_IMAGE,
    735                                        "parent CID %.32s",
    736                                        parent_cid));
    737   }
    738   return 0;
    739 }
    740 
    741 
    742 /**
    743  * Extract from a VMDK sparse extent.  The `KDMV' magic has already
    744  * matched.
    745  *
    746  * @param ec extraction context
    747  * @return 1 if the caller should stop extracting, 0 to continue
    748  */
    749 static int
    750 extract_vmdk_sparse (struct EXTRACTOR_ExtractContext *ec)
    751 {
    752   unsigned char hdr[80];
    753   uint64_t capacity;
    754   uint64_t grain;
    755   uint64_t desc_offset;
    756   uint64_t desc_size;
    757 
    758   if (! EXTRACTOR_forensic_read_ (ec,
    759                                   0,
    760                                   hdr,
    761                                   sizeof (hdr)))
    762     return 0;
    763   CHECK (emit_mime (ec,
    764                     "application/x-vmdk"));
    765   CHECK (EXTRACTOR_forensic_emit_ (ec,
    766                                    DISKIMAGE,
    767                                    EXTRACTOR_METATYPE_FORMAT,
    768                                    "%s",
    769                                    "VMDK"));
    770   CHECK (EXTRACTOR_forensic_emit_ (ec,
    771                                    DISKIMAGE,
    772                                    EXTRACTOR_METATYPE_FORMAT_VERSION,
    773                                    "%u",
    774                                    (unsigned int) EXTRACTOR_forensic_le32_ (
    775                                      &hdr[4])));
    776   capacity = EXTRACTOR_forensic_le64_ (&hdr[12]);
    777   if ( (0 != capacity) &&
    778        (capacity < (1ULL << 53)) )
    779   {
    780     CHECK (EXTRACTOR_forensic_emit_size_ (ec,
    781                                           DISKIMAGE,
    782                                           EXTRACTOR_METATYPE_VOLUME_SIZE,
    783                                           capacity * 512ULL));
    784   }
    785   grain = EXTRACTOR_forensic_le64_ (&hdr[20]);
    786   if ( (0 != grain) &&
    787        (grain < (1ULL << 32)) )
    788     CHECK (EXTRACTOR_forensic_emit_size_ (ec,
    789                                           DISKIMAGE,
    790                                           EXTRACTOR_METATYPE_BLOCK_SIZE,
    791                                           grain * 512ULL));
    792   /* An unclean shutdown flag survives in the file, so it says the VM
    793      was not powered down before the image was taken. */
    794   if (0 != hdr[72])
    795     CHECK (EXTRACTOR_forensic_emit_ (ec,
    796                                      DISKIMAGE,
    797                                      EXTRACTOR_METATYPE_ATTRIBUTES,
    798                                      "%s",
    799                                      "unclean shutdown"));
    800   if (0 != EXTRACTOR_forensic_le16_ (&hdr[77]))
    801     CHECK (EXTRACTOR_forensic_emit_ (ec,
    802                                      DISKIMAGE,
    803                                      EXTRACTOR_METATYPE_COMMENT,
    804                                      "%s",
    805                                      "grains are DEFLATE compressed"));
    806   desc_offset = EXTRACTOR_forensic_le64_ (&hdr[28]);
    807   desc_size = EXTRACTOR_forensic_le64_ (&hdr[36]);
    808   if ( (0 == desc_offset) ||
    809        (0 == desc_size) )
    810     return 0;   /* descriptor lives in a separate file */
    811   if ( (desc_offset > (uint64_t) INT64_MAX / 512) ||
    812        (desc_size > VMDK_MAX_DESCRIPTOR / 512) )
    813     return 0;   /* nonsense, or more than we are willing to read */
    814   {
    815     size_t bytes = (size_t) (desc_size * 512);
    816     char *text;
    817     int ret;
    818 
    819     if (NULL == (text = malloc (bytes + 1)))
    820       return 0;
    821     if (! EXTRACTOR_forensic_read_ (ec,
    822                                     (int64_t) (desc_offset * 512),
    823                                     text,
    824                                     bytes))
    825     {
    826       free (text);
    827       return 0;
    828     }
    829     text[bytes] = '\0';
    830     ret = vmdk_descriptor (ec,
    831                            text,
    832                            bytes,
    833                            1);
    834     free (text);
    835     return ret;
    836   }
    837 }
    838 
    839 
    840 /**
    841  * Extract from a stand-alone VMDK text descriptor file.  The
    842  * `# Disk DescriptorFile' banner has already matched.
    843  *
    844  * @param ec extraction context
    845  * @return 1 if the caller should stop extracting, 0 to continue
    846  */
    847 static int
    848 extract_vmdk_text (struct EXTRACTOR_ExtractContext *ec)
    849 {
    850   uint64_t fsize = ec->get_size (ec->cls);
    851   size_t bytes;
    852   char *text;
    853   int ret;
    854 
    855   CHECK (emit_mime (ec,
    856                     "application/x-vmdk"));
    857   CHECK (EXTRACTOR_forensic_emit_ (ec,
    858                                    DISKIMAGE,
    859                                    EXTRACTOR_METATYPE_FORMAT,
    860                                    "%s",
    861                                    "VMDK"));
    862   if ( (UINT64_MAX == fsize) ||
    863        (0 == fsize) )
    864     return 0;
    865   bytes = (fsize > VMDK_MAX_DESCRIPTOR) ? VMDK_MAX_DESCRIPTOR : (size_t) fsize;
    866   if (NULL == (text = malloc (bytes + 1)))
    867     return 0;
    868   if (! EXTRACTOR_forensic_read_ (ec,
    869                                   0,
    870                                   text,
    871                                   bytes))
    872   {
    873     free (text);
    874     return 0;
    875   }
    876   text[bytes] = '\0';
    877   ret = vmdk_descriptor (ec,
    878                          text,
    879                          bytes,
    880                          0);
    881   free (text);
    882   return ret;
    883 }
    884 
    885 
    886 /* ------------------------------------------------------------------ */
    887 /* VHD (Microsoft Virtual PC / Hyper-V version 1)                     */
    888 /* ------------------------------------------------------------------ */
    889 
    890 
    891 /**
    892  * Read the dynamic disk header of a VHD and report what it says about
    893  * the block size and, for a differencing disk, the parent.
    894  *
    895  * @param ec extraction context
    896  * @param offset file offset of the dynamic disk header
    897  * @param differencing 1 if the footer said this is a differencing disk
    898  * @return 1 if the caller should stop extracting, 0 to continue
    899  */
    900 static int
    901 vhd_dynamic_header (struct EXTRACTOR_ExtractContext *ec,
    902                     uint64_t offset,
    903                     int differencing)
    904 {
    905   unsigned char dyn[576];
    906   unsigned char name[512];
    907   uint32_t block_size;
    908 
    909   if (offset > (uint64_t) INT64_MAX - sizeof (dyn))
    910     return 0;
    911   if (! EXTRACTOR_forensic_read_ (ec,
    912                                   (int64_t) offset,
    913                                   dyn,
    914                                   sizeof (dyn)))
    915     return 0;
    916   if (0 != memcmp (dyn,
    917                    "cxsparse",
    918                    8))
    919     return 0;
    920   block_size = EXTRACTOR_forensic_be32_ (&dyn[32]);
    921   if ( (0 != block_size) &&
    922        (block_size <= (64 * 1024 * 1024)) )
    923     CHECK (EXTRACTOR_forensic_emit_size_ (ec,
    924                                           DISKIMAGE,
    925                                           EXTRACTOR_METATYPE_BLOCK_SIZE,
    926                                           block_size));
    927   if (! differencing)
    928     return 0;
    929   CHECK (EXTRACTOR_forensic_emit_guid_ (ec,
    930                                         DISKIMAGE,
    931                                         EXTRACTOR_METATYPE_PARENT_IMAGE,
    932                                         &dyn[40],
    933                                         0));
    934   /* The parent name is UTF-16 *big* endian here, unlike everywhere
    935      else in Windows; swap it so the shared helper can take it. */
    936   for (size_t i = 0; i + 1 < sizeof (name); i += 2)
    937   {
    938     name[i] = dyn[64 + i + 1];
    939     name[i + 1] = dyn[64 + i];
    940   }
    941   CHECK (EXTRACTOR_forensic_emit_utf16le_ (ec,
    942                                            DISKIMAGE,
    943                                            EXTRACTOR_METATYPE_PARENT_IMAGE,
    944                                            name,
    945                                            sizeof (name)));
    946   return 0;
    947 }
    948 
    949 
    950 /**
    951  * Extract from a VHD image given its 512 byte footer.
    952  *
    953  * @param ec extraction context
    954  * @param foot the footer
    955  * @return 1 if the caller should stop extracting, 0 to continue
    956  */
    957 static int
    958 extract_vhd (struct EXTRACTOR_ExtractContext *ec,
    959              const unsigned char *foot)
    960 {
    961   uint32_t format_version;
    962   uint32_t host_os;
    963   uint32_t disk_type;
    964   uint64_t current_size;
    965   uint64_t data_offset;
    966   int64_t created;
    967 
    968   CHECK (emit_mime (ec,
    969                     "application/x-vhd"));
    970   CHECK (EXTRACTOR_forensic_emit_ (ec,
    971                                    DISKIMAGE,
    972                                    EXTRACTOR_METATYPE_FORMAT,
    973                                    "%s",
    974                                    "VHD"));
    975   format_version = EXTRACTOR_forensic_be32_ (&foot[12]);
    976   CHECK (EXTRACTOR_forensic_emit_ (ec,
    977                                    DISKIMAGE,
    978                                    EXTRACTOR_METATYPE_FORMAT_VERSION,
    979                                    "%u.%u",
    980                                    (unsigned int) (format_version >> 16),
    981                                    (unsigned int) (format_version & 0xFFFF)));
    982   /* The creator application is a four character code -- `vpc ' for
    983      Virtual PC, `win ' for Hyper-V, `qemu', `tap ' for Xen, `d2v ' for
    984      Disk2vhd -- followed by a major.minor version. */
    985   {
    986     uint32_t cver = EXTRACTOR_forensic_be32_ (&foot[32]);
    987     char app[5];
    988 
    989     memcpy (app,
    990             &foot[28],
    991             4);
    992     app[4] = '\0';
    993     for (unsigned int i = 0; i < 4; i++)
    994       if ( (0x20 > (unsigned char) app[i]) ||
    995            (0x7E < (unsigned char) app[i]) )
    996         app[i] = ' ';
    997     CHECK (EXTRACTOR_forensic_emit_ (ec,
    998                                      DISKIMAGE,
    999                                      EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE,
   1000                                      "%s %u.%u",
   1001                                      app,
   1002                                      (unsigned int) (cver >> 16),
   1003                                      (unsigned int) (cver & 0xFFFF)));
   1004   }
   1005   host_os = EXTRACTOR_forensic_be32_ (&foot[36]);
   1006   if (0x5769326BU == host_os)
   1007     CHECK (EXTRACTOR_forensic_emit_ (ec,
   1008                                      DISKIMAGE,
   1009                                      EXTRACTOR_METATYPE_AUTHORING_OS,
   1010                                      "%s",
   1011                                      "Windows"));
   1012   else if (0x4D616320U == host_os)
   1013     CHECK (EXTRACTOR_forensic_emit_ (ec,
   1014                                      DISKIMAGE,
   1015                                      EXTRACTOR_METATYPE_AUTHORING_OS,
   1016                                      "%s",
   1017                                      "Macintosh"));
   1018   /* The time stamp counts seconds from 2000-01-01, not from the Unix
   1019      epoch. */
   1020   created = (int64_t) EXTRACTOR_forensic_be32_ (&foot[24]);
   1021   CHECK (EXTRACTOR_forensic_emit_unix_time_ (ec,
   1022                                              DISKIMAGE,
   1023                                              EXTRACTOR_METATYPE_CREATION_DATE,
   1024                                              created + VHD_EPOCH));
   1025   current_size = EXTRACTOR_forensic_be64_ (&foot[48]);
   1026   if ( (0 != current_size) &&
   1027        (current_size < (1ULL << 62)) )
   1028     CHECK (EXTRACTOR_forensic_emit_size_ (ec,
   1029                                           DISKIMAGE,
   1030                                           EXTRACTOR_METATYPE_VOLUME_SIZE,
   1031                                           current_size));
   1032   CHECK (EXTRACTOR_forensic_emit_guid_ (ec,
   1033                                         DISKIMAGE,
   1034                                         EXTRACTOR_METATYPE_VOLUME_SERIAL,
   1035                                         &foot[68],
   1036                                         0));
   1037   disk_type = EXTRACTOR_forensic_be32_ (&foot[60]);
   1038   switch (disk_type)
   1039   {
   1040   case 2:
   1041     CHECK (EXTRACTOR_forensic_emit_ (ec,
   1042                                      DISKIMAGE,
   1043                                      EXTRACTOR_METATYPE_ATTRIBUTES,
   1044                                      "%s",
   1045                                      "fixed disk"));
   1046     break;
   1047   case 3:
   1048     CHECK (EXTRACTOR_forensic_emit_ (ec,
   1049                                      DISKIMAGE,
   1050                                      EXTRACTOR_METATYPE_ATTRIBUTES,
   1051                                      "%s",
   1052                                      "dynamic disk"));
   1053     break;
   1054   case 4:
   1055     CHECK (EXTRACTOR_forensic_emit_ (ec,
   1056                                      DISKIMAGE,
   1057                                      EXTRACTOR_METATYPE_ATTRIBUTES,
   1058                                      "%s",
   1059                                      "differencing disk"));
   1060     break;
   1061   default:
   1062     break;
   1063   }
   1064   if (0 != (foot[84] & 1))
   1065     CHECK (EXTRACTOR_forensic_emit_ (ec,
   1066                                      DISKIMAGE,
   1067                                      EXTRACTOR_METATYPE_ATTRIBUTES,
   1068                                      "%s",
   1069                                      "saved state present (VM was suspended)"));
   1070   data_offset = EXTRACTOR_forensic_be64_ (&foot[16]);
   1071   if ( ( (3 == disk_type) ||
   1072          (4 == disk_type) ) &&
   1073        (0 != data_offset) &&
   1074        (UINT64_MAX != data_offset) )
   1075     return vhd_dynamic_header (ec,
   1076                                data_offset,
   1077                                4 == disk_type);
   1078   return 0;
   1079 }
   1080 
   1081 
   1082 /* ------------------------------------------------------------------ */
   1083 /* VHDX                                                               */
   1084 /* ------------------------------------------------------------------ */
   1085 
   1086 /**
   1087  * Region table GUID of the VHDX metadata region,
   1088  * 8B7CA206-4790-4B9A-B8FE-575F050F886E, in file order.
   1089  */
   1090 static const unsigned char vhdx_metadata_region[16] = {
   1091   0x06, 0xA2, 0x7C, 0x8B, 0x90, 0x47, 0x9A, 0x4B,
   1092   0xB8, 0xFE, 0x57, 0x5F, 0x05, 0x0F, 0x88, 0x6E
   1093 };
   1094 
   1095 /**
   1096  * Metadata item GUID "File Parameters",
   1097  * CAA16737-FA36-4D43-B3B6-33F0AA44E76B, in file order.
   1098  */
   1099 static const unsigned char vhdx_file_parameters[16] = {
   1100   0x37, 0x67, 0xA1, 0xCA, 0x36, 0xFA, 0x43, 0x4D,
   1101   0xB3, 0xB6, 0x33, 0xF0, 0xAA, 0x44, 0xE7, 0x6B
   1102 };
   1103 
   1104 /**
   1105  * Metadata item GUID "Virtual Disk Size",
   1106  * 2FA54224-CD1B-4876-B211-5DBED83BF4B8, in file order.
   1107  */
   1108 static const unsigned char vhdx_virtual_disk_size[16] = {
   1109   0x24, 0x42, 0xA5, 0x2F, 0x1B, 0xCD, 0x76, 0x48,
   1110   0xB2, 0x11, 0x5D, 0xBE, 0xD8, 0x3B, 0xF4, 0xB8
   1111 };
   1112 
   1113 /**
   1114  * Metadata item GUID "Virtual Disk ID",
   1115  * BECA12AB-B2E6-4523-93EF-C309E000C746, in file order.
   1116  */
   1117 static const unsigned char vhdx_virtual_disk_id[16] = {
   1118   0xAB, 0x12, 0xCA, 0xBE, 0xE6, 0xB2, 0x23, 0x45,
   1119   0x93, 0xEF, 0xC3, 0x09, 0xE0, 0x00, 0xC7, 0x46
   1120 };
   1121 
   1122 /**
   1123  * Metadata item GUID "Logical Sector Size",
   1124  * 8141BF1D-A96F-4709-BA47-F233A8FAAB5F, in file order.
   1125  */
   1126 static const unsigned char vhdx_logical_sector_size[16] = {
   1127   0x1D, 0xBF, 0x41, 0x81, 0x6F, 0xA9, 0x09, 0x47,
   1128   0xBA, 0x47, 0xF2, 0x33, 0xA8, 0xFA, 0xAB, 0x5F
   1129 };
   1130 
   1131 /**
   1132  * Metadata item GUID "Physical Sector Size",
   1133  * CDA348C7-445D-4471-9CC9-E9885251C556, in file order.
   1134  */
   1135 static const unsigned char vhdx_physical_sector_size[16] = {
   1136   0xC7, 0x48, 0xA3, 0xCD, 0x5D, 0x44, 0x71, 0x44,
   1137   0x9C, 0xC9, 0xE9, 0x88, 0x52, 0x51, 0xC5, 0x56
   1138 };
   1139 
   1140 /**
   1141  * Metadata item GUID "Parent Locator",
   1142  * A8D35F2D-B30B-454D-ABF7-D3D84834AB0C, in file order.
   1143  */
   1144 static const unsigned char vhdx_parent_locator[16] = {
   1145   0x2D, 0x5F, 0xD3, 0xA8, 0x0B, 0xB3, 0x4D, 0x45,
   1146   0xAB, 0xF7, 0xD3, 0xD8, 0x48, 0x34, 0xAB, 0x0C
   1147 };
   1148 
   1149 
   1150 /**
   1151  * Report what the VHDX parent locator says about the parent image.
   1152  *
   1153  * The locator is a small table of UTF-16LE key/value pairs; the keys
   1154  * that matter are `relative_path', `volume_path' and
   1155  * `absolute_win32_path', each of which names the file the differencing
   1156  * image depends on.
   1157  *
   1158  * @param ec extraction context
   1159  * @param item the parent locator metadata item
   1160  * @param len number of bytes in @a item
   1161  * @return 1 if the caller should stop extracting, 0 to continue
   1162  */
   1163 static int
   1164 vhdx_parent_paths (struct EXTRACTOR_ExtractContext *ec,
   1165                    const unsigned char *item,
   1166                    size_t len)
   1167 {
   1168   uint16_t count;
   1169 
   1170   if (20 > len)
   1171     return 0;
   1172   count = EXTRACTOR_forensic_le16_ (&item[18]);
   1173   if (EXTRACTOR_FORENSIC_MAX_ITEMS < count)
   1174     count = EXTRACTOR_FORENSIC_MAX_ITEMS;
   1175   for (uint16_t i = 0; i < count; i++)
   1176   {
   1177     size_t base = 20 + (size_t) i * 12;
   1178     uint32_t key_offset;
   1179     uint32_t value_offset;
   1180     uint16_t key_len;
   1181     uint16_t value_len;
   1182     char key[64];
   1183 
   1184     if (base + 12 > len)
   1185       break;
   1186     key_offset = EXTRACTOR_forensic_le32_ (&item[base]);
   1187     value_offset = EXTRACTOR_forensic_le32_ (&item[base + 4]);
   1188     key_len = EXTRACTOR_forensic_le16_ (&item[base + 8]);
   1189     value_len = EXTRACTOR_forensic_le16_ (&item[base + 10]);
   1190     if ( (key_offset > len) ||
   1191          (key_len > len - key_offset) ||
   1192          (value_offset > len) ||
   1193          (value_len > len - value_offset) )
   1194       continue;   /* the entry points outside the item */
   1195     if ( (0 == value_len) ||
   1196          (0 == key_len) ||
   1197          (sizeof (key) * 2 <= key_len) )
   1198       continue;
   1199     /* The keys are ASCII spelled in UTF-16LE; fold them down by hand
   1200        so we can compare them without another conversion buffer. */
   1201     {
   1202       size_t out = 0;
   1203 
   1204       for (size_t k = 0; k + 1 < key_len; k += 2)
   1205       {
   1206         if (0 != item[key_offset + k + 1])
   1207         {
   1208           out = 0;
   1209           break;   /* not ASCII, so not a key we know */
   1210         }
   1211         key[out++] = (char) item[key_offset + k];
   1212       }
   1213       if (0 == out)
   1214         continue;
   1215       key[out] = '\0';
   1216     }
   1217     if ( (key_is (key,
   1218                   "relative_path")) ||
   1219          (key_is (key,
   1220                   "volume_path")) ||
   1221          (key_is (key,
   1222                   "absolute_win32_path")) )
   1223       CHECK (EXTRACTOR_forensic_emit_utf16le_ (ec,
   1224                                                DISKIMAGE,
   1225                                                EXTRACTOR_METATYPE_PARENT_IMAGE,
   1226                                                &item[value_offset],
   1227                                                value_len));
   1228   }
   1229   return 0;
   1230 }
   1231 
   1232 
   1233 /**
   1234  * Read one VHDX metadata item and report it.
   1235  *
   1236  * @param ec extraction context
   1237  * @param guid the item GUID from the metadata table
   1238  * @param offset absolute file offset of the item
   1239  * @param len number of bytes in the item
   1240  * @return 1 if the caller should stop extracting, 0 to continue
   1241  */
   1242 static int
   1243 vhdx_metadata_item (struct EXTRACTOR_ExtractContext *ec,
   1244                     const unsigned char *guid,
   1245                     uint64_t offset,
   1246                     uint32_t len)
   1247 {
   1248   unsigned char item[64];
   1249 
   1250   if (0 == memcmp (guid,
   1251                    vhdx_virtual_disk_size,
   1252                    sizeof (vhdx_virtual_disk_size)))
   1253   {
   1254     uint64_t size;
   1255 
   1256     if ( (8 > len) ||
   1257          (! EXTRACTOR_forensic_read_ (ec,
   1258                                       (int64_t) offset,
   1259                                       item,
   1260                                       8)) )
   1261       return 0;
   1262     size = EXTRACTOR_forensic_le64_ (item);
   1263     if ( (0 != size) &&
   1264          (size < (1ULL << 62)) )
   1265       CHECK (EXTRACTOR_forensic_emit_size_ (ec,
   1266                                             DISKIMAGE,
   1267                                             EXTRACTOR_METATYPE_VOLUME_SIZE,
   1268                                             size));
   1269     return 0;
   1270   }
   1271   if (0 == memcmp (guid,
   1272                    vhdx_virtual_disk_id,
   1273                    sizeof (vhdx_virtual_disk_id)))
   1274   {
   1275     if ( (16 > len) ||
   1276          (! EXTRACTOR_forensic_read_ (ec,
   1277                                       (int64_t) offset,
   1278                                       item,
   1279                                       16)) )
   1280       return 0;
   1281     return EXTRACTOR_forensic_emit_guid_ (ec,
   1282                                           DISKIMAGE,
   1283                                           EXTRACTOR_METATYPE_VOLUME_SERIAL,
   1284                                           item,
   1285                                           1);
   1286   }
   1287   if (0 == memcmp (guid,
   1288                    vhdx_file_parameters,
   1289                    sizeof (vhdx_file_parameters)))
   1290   {
   1291     uint32_t block_size;
   1292     uint32_t flags;
   1293 
   1294     if ( (8 > len) ||
   1295          (! EXTRACTOR_forensic_read_ (ec,
   1296                                       (int64_t) offset,
   1297                                       item,
   1298                                       8)) )
   1299       return 0;
   1300     block_size = EXTRACTOR_forensic_le32_ (item);
   1301     flags = EXTRACTOR_forensic_le32_ (&item[4]);
   1302     if ( (0 != block_size) &&
   1303          (block_size <= (256 * 1024 * 1024)) )
   1304       CHECK (EXTRACTOR_forensic_emit_size_ (ec,
   1305                                             DISKIMAGE,
   1306                                             EXTRACTOR_METATYPE_BLOCK_SIZE,
   1307                                             block_size));
   1308     if (0 != (flags & 1U))
   1309       CHECK (EXTRACTOR_forensic_emit_ (ec,
   1310                                        DISKIMAGE,
   1311                                        EXTRACTOR_METATYPE_ATTRIBUTES,
   1312                                        "%s",
   1313                                        "fixed disk (blocks left allocated)"));
   1314     if (0 != (flags & 2U))
   1315       CHECK (EXTRACTOR_forensic_emit_ (ec,
   1316                                        DISKIMAGE,
   1317                                        EXTRACTOR_METATYPE_ATTRIBUTES,
   1318                                        "%s",
   1319                                        "differencing disk"));
   1320     return 0;
   1321   }
   1322   if ( (0 == memcmp (guid,
   1323                      vhdx_logical_sector_size,
   1324                      sizeof (vhdx_logical_sector_size))) ||
   1325        (0 == memcmp (guid,
   1326                      vhdx_physical_sector_size,
   1327                      sizeof (vhdx_physical_sector_size))) )
   1328   {
   1329     int logical = (0 == memcmp (guid,
   1330                                 vhdx_logical_sector_size,
   1331                                 sizeof (vhdx_logical_sector_size)));
   1332 
   1333     if ( (4 > len) ||
   1334          (! EXTRACTOR_forensic_read_ (ec,
   1335                                       (int64_t) offset,
   1336                                       item,
   1337                                       4)) )
   1338       return 0;
   1339     return EXTRACTOR_forensic_emit_ (ec,
   1340                                      DISKIMAGE,
   1341                                      EXTRACTOR_METATYPE_COMMENT,
   1342                                      "%s sector size: %u bytes",
   1343                                      logical ? "logical" : "physical",
   1344                                      (unsigned int) EXTRACTOR_forensic_le32_ (
   1345                                        item));
   1346   }
   1347   if (0 == memcmp (guid,
   1348                    vhdx_parent_locator,
   1349                    sizeof (vhdx_parent_locator)))
   1350   {
   1351     unsigned char *locator;
   1352     int ret;
   1353 
   1354     if ( (20 > len) ||
   1355          (VHDX_MAX_ITEM < len) )
   1356       return 0;
   1357     if (NULL == (locator = malloc (len)))
   1358       return 0;
   1359     if (! EXTRACTOR_forensic_read_ (ec,
   1360                                     (int64_t) offset,
   1361                                     locator,
   1362                                     len))
   1363     {
   1364       free (locator);
   1365       return 0;
   1366     }
   1367     ret = vhdx_parent_paths (ec,
   1368                              locator,
   1369                              len);
   1370     free (locator);
   1371     return ret;
   1372   }
   1373   return 0;
   1374 }
   1375 
   1376 
   1377 /**
   1378  * Walk the VHDX metadata table and report the items we understand.
   1379  *
   1380  * @param ec extraction context
   1381  * @param region_offset file offset of the metadata region
   1382  * @param region_length number of bytes in the metadata region
   1383  * @return 1 if the caller should stop extracting, 0 to continue
   1384  */
   1385 static int
   1386 vhdx_metadata_table (struct EXTRACTOR_ExtractContext *ec,
   1387                      uint64_t region_offset,
   1388                      uint32_t region_length)
   1389 {
   1390   unsigned char head[32];
   1391   unsigned char *table;
   1392   uint16_t count;
   1393   size_t table_bytes;
   1394   int ret = 0;
   1395 
   1396   if (! EXTRACTOR_forensic_read_ (ec,
   1397                                   (int64_t) region_offset,
   1398                                   head,
   1399                                   sizeof (head)))
   1400     return 0;
   1401   if (0 != memcmp (head,
   1402                    "metadata",
   1403                    8))
   1404     return 0;
   1405   count = EXTRACTOR_forensic_le16_ (&head[10]);
   1406   if (VHDX_MAX_METADATA < count)
   1407     return 0;   /* more than the format allows: do not trust the table */
   1408   if (0 == count)
   1409     return 0;
   1410   table_bytes = (size_t) count * 32;
   1411   if (region_length < 32 + table_bytes)
   1412     return 0;
   1413   if (NULL == (table = malloc (table_bytes)))
   1414     return 0;
   1415   if (! EXTRACTOR_forensic_read_ (ec,
   1416                                   (int64_t) (region_offset + 32),
   1417                                   table,
   1418                                   table_bytes))
   1419   {
   1420     free (table);
   1421     return 0;
   1422   }
   1423   for (uint16_t i = 0; i < count; i++)
   1424   {
   1425     const unsigned char *e = &table[(size_t) i * 32];
   1426     uint32_t item_offset = EXTRACTOR_forensic_le32_ (&e[16]);
   1427     uint32_t item_len = EXTRACTOR_forensic_le32_ (&e[20]);
   1428 
   1429     if ( (0 == item_len) ||
   1430          (VHDX_MAX_ITEM < item_len) )
   1431       continue;
   1432     /* The item offset is relative to the start of the region and both
   1433        it and the item have to stay inside it. */
   1434     if ( (item_offset < 32) ||
   1435          (item_offset > region_length) ||
   1436          (item_len > region_length - item_offset) )
   1437       continue;
   1438     if (0 != vhdx_metadata_item (ec,
   1439                                  e,
   1440                                  region_offset + item_offset,
   1441                                  item_len))
   1442     {
   1443       ret = 1;
   1444       break;
   1445     }
   1446   }
   1447   free (table);
   1448   return ret;
   1449 }
   1450 
   1451 
   1452 /**
   1453  * Walk a VHDX region table looking for the metadata region.
   1454  *
   1455  * @param ec extraction context
   1456  * @param offset file offset of the region table (192KB or 256KB)
   1457  * @param fsize size of the file, UINT64_MAX if unknown
   1458  * @param[out] found set to 1 if the metadata region was reported
   1459  * @return 1 if the caller should stop extracting, 0 to continue
   1460  */
   1461 static int
   1462 vhdx_region_table (struct EXTRACTOR_ExtractContext *ec,
   1463                    uint64_t offset,
   1464                    uint64_t fsize,
   1465                    int *found)
   1466 {
   1467   unsigned char head[16];
   1468   unsigned char *entries;
   1469   uint32_t count;
   1470   int ret = 0;
   1471 
   1472   if (! EXTRACTOR_forensic_read_ (ec,
   1473                                   (int64_t) offset,
   1474                                   head,
   1475                                   sizeof (head)))
   1476     return 0;
   1477   if (0 != memcmp (head,
   1478                    "regi",
   1479                    4))
   1480     return 0;
   1481   count = EXTRACTOR_forensic_le32_ (&head[8]);
   1482   if ( (0 == count) ||
   1483        (VHDX_MAX_REGIONS < count) )
   1484     return 0;
   1485   if (NULL == (entries = malloc ((size_t) count * 32)))
   1486     return 0;
   1487   if (! EXTRACTOR_forensic_read_ (ec,
   1488                                   (int64_t) (offset + 16),
   1489                                   entries,
   1490                                   (size_t) count * 32))
   1491   {
   1492     free (entries);
   1493     return 0;
   1494   }
   1495   for (uint32_t i = 0; i < count; i++)
   1496   {
   1497     const unsigned char *e = &entries[(size_t) i * 32];
   1498     uint64_t region_offset;
   1499     uint32_t region_length;
   1500 
   1501     if (0 != memcmp (e,
   1502                      vhdx_metadata_region,
   1503                      sizeof (vhdx_metadata_region)))
   1504       continue;
   1505     region_offset = EXTRACTOR_forensic_le64_ (&e[16]);
   1506     region_length = EXTRACTOR_forensic_le32_ (&e[24]);
   1507     if ( (0 == region_offset) ||
   1508          (0 == region_length) ||
   1509          (region_offset > (uint64_t) INT64_MAX - region_length) )
   1510       continue;
   1511     if ( (UINT64_MAX != fsize) &&
   1512          (region_offset + region_length > fsize) )
   1513       continue;   /* truncated file: the region is not there */
   1514     *found = 1;
   1515     ret = vhdx_metadata_table (ec,
   1516                                region_offset,
   1517                                region_length);
   1518     break;
   1519   }
   1520   free (entries);
   1521   return ret;
   1522 }
   1523 
   1524 
   1525 /**
   1526  * Extract from a VHDX image.  The `vhdxfile' signature has already
   1527  * matched.
   1528  *
   1529  * @param ec extraction context
   1530  * @return 1 if the caller should stop extracting, 0 to continue
   1531  */
   1532 static int
   1533 extract_vhdx (struct EXTRACTOR_ExtractContext *ec)
   1534 {
   1535   unsigned char creator[512];
   1536   unsigned char head[80];
   1537   uint64_t best_sequence = 0;
   1538   int have_head = 0;
   1539   uint64_t fsize;
   1540   int found = 0;
   1541 
   1542   memset (head,
   1543           0,
   1544           sizeof (head));
   1545   CHECK (emit_mime (ec,
   1546                     "application/x-vhdx"));
   1547   CHECK (EXTRACTOR_forensic_emit_ (ec,
   1548                                    DISKIMAGE,
   1549                                    EXTRACTOR_METATYPE_FORMAT,
   1550                                    "%s",
   1551                                    "VHDX"));
   1552   if (EXTRACTOR_forensic_read_ (ec,
   1553                                 8,
   1554                                 creator,
   1555                                 sizeof (creator)))
   1556     CHECK (EXTRACTOR_forensic_emit_utf16le_ (ec,
   1557                                              DISKIMAGE,
   1558                                              EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE,
   1559                                              creator,
   1560                                              sizeof (creator)));
   1561   /* Two copies of the header live at 64KB and 128KB; the one with the
   1562      higher sequence number is current.  We do not verify the CRC-32C
   1563      the format puts on each of them, so a corrupt header is reported
   1564      as if it were good. */
   1565   for (unsigned int i = 0; i < 2; i++)
   1566   {
   1567     unsigned char candidate[80];
   1568     uint64_t sequence;
   1569 
   1570     if (! EXTRACTOR_forensic_read_ (ec,
   1571                                     (int64_t) (0x10000 + i * 0x10000),
   1572                                     candidate,
   1573                                     sizeof (candidate)))
   1574       continue;
   1575     if (0 != memcmp (candidate,
   1576                      "head",
   1577                      4))
   1578       continue;
   1579     sequence = EXTRACTOR_forensic_le64_ (&candidate[8]);
   1580     if ( (have_head) &&
   1581          (sequence < best_sequence) )
   1582       continue;
   1583     memcpy (head,
   1584             candidate,
   1585             sizeof (head));
   1586     best_sequence = sequence;
   1587     have_head = 1;
   1588   }
   1589   if (have_head)
   1590   {
   1591     CHECK (EXTRACTOR_forensic_emit_ (ec,
   1592                                      DISKIMAGE,
   1593                                      EXTRACTOR_METATYPE_FORMAT_VERSION,
   1594                                      "%u",
   1595                                      (unsigned int)
   1596                                      EXTRACTOR_forensic_le16_ (&head[66])));
   1597     /* A non-zero LogGuid means the log holds entries that were never
   1598        replayed into the image: the writer did not shut down. */
   1599     {
   1600       int empty = 1;
   1601 
   1602       for (unsigned int i = 0; i < 16; i++)
   1603         if (0 != head[48 + i])
   1604         {
   1605           empty = 0;
   1606           break;
   1607         }
   1608       if (! empty)
   1609         CHECK (EXTRACTOR_forensic_emit_ (ec,
   1610                                          DISKIMAGE,
   1611                                          EXTRACTOR_METATYPE_COMMENT,
   1612                                          "%s",
   1613                                          "unflushed log present"
   1614                                          " (image was not shut down cleanly)"));
   1615     }
   1616   }
   1617   fsize = ec->get_size (ec->cls);
   1618   /* The region table sits at a fixed 192KB, with a backup at 256KB. */
   1619   CHECK (vhdx_region_table (ec,
   1620                             0x30000,
   1621                             fsize,
   1622                             &found));
   1623   if (! found)
   1624     CHECK (vhdx_region_table (ec,
   1625                               0x40000,
   1626                               fsize,
   1627                               &found));
   1628   return 0;
   1629 }
   1630 
   1631 
   1632 /* ------------------------------------------------------------------ */
   1633 
   1634 
   1635 /**
   1636  * Main entry method for the virtual disk image extraction plugin.
   1637  *
   1638  * @param ec extraction context provided to the plugin
   1639  */
   1640 void
   1641 EXTRACTOR_diskimage_extract_method (struct EXTRACTOR_ExtractContext *ec);
   1642 
   1643 void
   1644 EXTRACTOR_diskimage_extract_method (struct EXTRACTOR_ExtractContext *ec)
   1645 {
   1646   unsigned char magic[16];
   1647   unsigned char foot[512];
   1648   uint64_t fsize;
   1649 
   1650   if (! EXTRACTOR_forensic_read_ (ec,
   1651                                   0,
   1652                                   magic,
   1653                                   sizeof (magic)))
   1654     return;
   1655   if (0 == memcmp (magic,
   1656                    "QFI\xfb",
   1657                    4))
   1658   {
   1659     (void) extract_qcow (ec);
   1660     return;
   1661   }
   1662   if (0 == memcmp (magic,
   1663                    "KDMV",
   1664                    4))
   1665   {
   1666     (void) extract_vmdk_sparse (ec);
   1667     return;
   1668   }
   1669   if (0 == memcmp (magic,
   1670                    "# Disk Descripto",
   1671                    16))
   1672   {
   1673     (void) extract_vmdk_text (ec);
   1674     return;
   1675   }
   1676   if (0 == memcmp (magic,
   1677                    "vhdxfile",
   1678                    8))
   1679   {
   1680     (void) extract_vhdx (ec);
   1681     return;
   1682   }
   1683   if (0 == memcmp (magic,
   1684                    "conectix",
   1685                    8))
   1686   {
   1687     /* A dynamic or differencing VHD mirrors its footer at the front,
   1688        so we already have it and need no seek. */
   1689     if (EXTRACTOR_forensic_read_ (ec,
   1690                                   0,
   1691                                   foot,
   1692                                   sizeof (foot)))
   1693       (void) extract_vhd (ec,
   1694                           foot);
   1695     return;
   1696   }
   1697   /* Only a fixed VHD is left, and its footer is at the very end.  That
   1698      costs a seek on every file we were handed, so pay it only for the
   1699      files that could be one: a VHD is a whole number of 512 byte
   1700      sectors plus the footer, hence always 512 byte aligned.  (Virtual
   1701      PC 2004 and older wrote a 511 byte footer; those are not
   1702      recognised.) */
   1703   fsize = ec->get_size (ec->cls);
   1704   if ( (UINT64_MAX == fsize) ||
   1705        (1024 > fsize) ||
   1706        ((uint64_t) INT64_MAX < fsize) ||
   1707        (0 != (fsize % 512)) )
   1708     return;
   1709   if (! EXTRACTOR_forensic_read_ (ec,
   1710                                   (int64_t) (fsize - 512),
   1711                                   foot,
   1712                                   sizeof (foot)))
   1713     return;
   1714   if (0 != memcmp (foot,
   1715                    "conectix",
   1716                    8))
   1717     return;
   1718   (void) extract_vhd (ec,
   1719                       foot);
   1720 }
   1721 
   1722 
   1723 /* end of diskimage_extractor.c */