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 */