Extracting unallocated clusters from a shadow copy

Yes, shadow copies may contain a relatively small number of unallocated clusters. In this post, I will describe a new way to extract such clusters for further analysis.

The problem with unallocated clusters in shadow copies is that the volsnap driver doesn’t care about them. This driver can snapshot some unallocated ranges, but most of them are out-of-scope.

When reading unallocated clusters from a shadow copy, data from the current state of a volume can be returned. Obviously, this data has nothing to do with the shadow copy:

However unexpectedly when I ran the Encase Recover Folders feature across the HarddiskShadowcopy5 volume it found traces of the Sony folder and in fact many other files post dating the creation of the shadow copy.
The Encase Recover Folders feature parses unallocated clusters looking for folder metadata. It seems that it found data in unallocated clusters relating to the current volume. Therefore I believe that any deleted but recoverable data within the shadow copies needs to be treated with caution.

Null bytes instead of real data can be returned as well.

There is no way to distinguish between “real” and “fake” unallocated data when reading a shadow copy using the device exposed by the volsnap driver (“HarddiskVolumeShadowCopy<N>“).

However, a custom parser can perform better. In the latest release of dfir_ntfs, there is an option for the vsc_mount tool (–mount-altfill, * — note that there are two hyphens, but the WordPress engine replaced them with a single dash) to replace empty blocks and blocks pointing to their original locations within the current volume with corresponding ASCII placeholders. This would make a mounted shadow copy unmountable as a file system (because the resulting flat image doesn’t contain a valid NTFS file system).

Do you remember scoped shadow copies? Here is data from a “broken” file (its clusters were marked as out-of-scope):

00000000  45 4d 50 54 59 20 20 20  20 20 20 20 20 20 20 20  |EMPTY           |
00003000  4f 52 49 47 49 4e 41 4c  20 20 20 20 20 20 20 20  |ORIGINAL        |

(“*” means that the bytes are repeated.)

The output means that the file contains these ranges:

  • 0x0000-0x3000: null bytes;
  • 0x3000-0x36ec: bytes from the original volume (there is no snapshot of this data range).

When the same shadow copy is mounted in the normal mode, that file looks like:

00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00003000  5a e7 f9 56 23 09 ad 21  78 3b 92 c0 f1 1b 87 ad  |Z..V#..!x;......|
00003010  a7 7f 83 0c 95 e5 06 ab  9e 95 91 40 be cb 81 fc  |...........@....|
00003020  70 86 db 54 d0 73 eb 40  e6 58 73 e0 ba a6 ac c0  |p..T.s.@.Xs.....|
00003030  b5 62 d7 6c c9 4a 7c 0b  8c 4a 9a c5 42 d1 71 0f  |.b.l.J|..J..B.q.|
00003040  32 c2 c6 42 36 ca 0d 37  4a 1f 4f aa 4f dc 94 e8  |2..B6..7J.O.O...|
00003050  55 10 dd 49 3f 70 af e2  49 c7 14 a8 16 13 7b d8  |U..I?p..I.....{.|
00003060  b2 9a 3e 31 c6 8a a7 cd  86 8e 4a 5e d3 47 54 6e  |..>1......J^.GTn|
00003070  6e 97 9f 27 a5 88 b3 d2  f3 69 52 ae cf 0a 8b 8a  |n..'.....iR.....|
00003080  48 9c b4 cd 47 8e 48 9c  73 0d ce cc 46 27 2e 1c  |H...G.H.s...F'..|
00003090  79 e4 5a 6d bd 7a c3 89  4d 72 20 ae 12 7d 28 66  |y.Zm.z..Mr ..}(f|
000030a0  39 01 83 02 98 ef c4 c3  df 4e 14 e5 cd bf 74 82  |9........N....t.|
000030b0  7c 53 6d 17 de f4 23 ba  f0 9f fa 31 05 b0 fe 05  ||Sm...#....1....|
000030c0  00 00 ff ff 03 00 50 4b  03 04 14 00 06 00 08 00  |......PK........|
000030d0  00 00 21 00 f9 25 ec d8  d9 01 00 00 da 03 00 00  |..!..%..........|
000030e0  10 00 08 01 64 6f 63 50  72 6f 70 73 2f 61 70 70  |....docProps/app|
000030f0  2e 78 6d 6c 20 a2 04 01  28 a0 00 01 00 00 00 00  |.xml ...(.......|
00003100  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000036e0  0c 00 01 03 00 00 d5 33  00 00 00 00              |.......3....|

So, you can see that the file is “broken” not because its original version was “broken” too, but because the volsnap driver put an empty block as the first range. (When viewing that file from the same shadow copy using a Windows operating system, it has the same layout. Again, this is not a bug of a parser.)

Let’s define four types of blocks (each block is a multiple of 512 bytes, the smallest one is 512 bytes):

  1. an empty (null) block: it’s replaced with the “EMPTY [spaces]” placeholder;
  2. an original block: it’s replaced with the “ORIGINAL [spaces]” placeholder;
  3. a snapshotted block: it’s returned as is (note that snapshotted data could be taken either from the current shadow copy or from a next one, if any);
  4. a snapshotted block filled with null bytes: it’s the same as above, but contains valid (snapshotted) null bytes.

Now, let’s mount two shadow copies from the 2018 Lone Wolf Scenario (using the normal mode: the “–mount” argument*) and extract the “$Bitmap” file from each shadow copy using the following command: “icat <mount point>/image.raw 6 > bitmap-<N>“.

Then, mount two shadow copies again, but let’s use the “–mount-altfill” argument* instead and extract unallocated clusters using this script. The script accepts a bitmap file, a flat image (which can be found in the mount point of a shadow copy), and a cluster size (in bytes), extracts unallocated sectors (and skips those marked as empty or original), then prints snapshotted unallocated blocks found to the standard output. It will also print some statistics when done (note that the tool counts 512-byte sectors, not clusters, because a single cluster can contain different types of blocks, see above).

For the latest shadow copy (#2), the tool reports:

Empty (null) sectors: 931399072, original sectors: 2908680, valid unallocated sectors: 313120, valid sectors with nulls: 13857

For the first (oldest) shadow copy (#1), the tool reports:

Empty (null) sectors: 955519456, original sectors: 48, valid unallocated sectors: 438480, valid sectors with nulls: 25865

As you can see, most unallocated sectors (931399072 and 955519456) belong to empty blocks, they are returned as null bytes in the normal mode. Some sectors (2908680 and 48) point to original offsets within the volume (these two values are so different because many blocks in the first shadow copy are redirected to the second shadow copy instead of the original volume, thus not counted as original ones). And a relatively small number of sectors contain snapshotted unallocated data; of those, 13857 and 25865 contain null bytes only (still, extracted unallocated space from the first shadow copy can include some data from the second shadow copy, but most “fake” unallocated data has gone away).

2 thoughts on “Extracting unallocated clusters from a shadow copy

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s