Forensics Matters 🕵️

Practical notes on digital forensics and incident response

dd vs dc3dd vs dcfldd: speed and reliability test

·

In a previous post I compared dd, dc3dd and dcfldd, but only on the features. Two questions remained open: which one is faster, and which one is more reliable when the drive has bad sectors? To answer them I made a test. In this post you will find how I made it and the results.

See also these posts:

The test environment

  • Linux VM with 2 vCPU (Intel Xeon 2.1 GHz) and 8 GB of RAM, Ubuntu 24.04
  • GNU dd (coreutils) 9.4
  • dc3dd 7.3.1 (Ubuntu package)
  • dcfldd 1.9.1
  • GNU ddrescue 1.27, only for the reliability test

Before each run I cleared the Linux cache, so every run reads the data from the source again:

sync; echo 1 > /proc/sys/vm/drop_caches

In the speed tests the output is /dev/null. In this way the test measures the read, the tool and the hash, and not the speed of the destination disk.

Test 1: speed with a fast source

The first source is a loop device of 2 GiB with the data in RAM. This source is faster than all the tools, so the test shows the speed of the tools themselves. I ran each command three times and I took the median.

Without hash:

CommandTimeSpeed
dd (default, bs=512)2.33 s922 MB/s
dd bs=4K0.77 s2,791 MB/s
dd bs=1M0.55 s3,877 MB/s
dcfldd (default, bs=32K)0.66 s3,242 MB/s
dcfldd bs=1M0.51 s4,194 MB/s
dc3dd (default)2.81 s764 MB/s
dc3dd bufsz=1M0.91 s2,361 MB/s

With SHA-256:

CommandTimeSpeed
sha256sum only (reference)2.02 s1,062 MB/s
dd bs=1M + sha256sum (2 reads)2.68 s802 MB/s
dcfldd hash=sha25612.24 s175 MB/s
dcfldd bs=1M hash=sha25611.01 s195 MB/s
dc3dd hash=sha25610.02 s214 MB/s
dc3dd bufsz=1M hash=sha2569.07 s236 MB/s

With MD5 and SHA-256:

CommandTimeSpeed
dd bs=1M + md5sum + sha256sum (3 reads)6.66 s322 MB/s
dcfldd hash=md5,sha25618.12 s118 MB/s
dc3dd hash=md5 hash=sha2569.82 s218 MB/s

What I see from these numbers:

  • Without hash, the block size is the most important parameter. dd with the default block size of 512 bytes is slow. With bs=1M, dd and dcfldd have about the same speed. dc3dd is faster with bufsz=1M.
  • With a hash, the hash is the bottleneck. On the same machine, the SHA-256 inside dcfldd and dc3dd is about 200 MB/s, while sha256sum is about 1,000 MB/s.
  • With two algorithms, dc3dd keeps the same speed. dcfldd becomes slower (118 MB/s). It seems that dc3dd calculates the hashes in parallel.

Test 2: speed with a normal drive (about 100 MB/s)

A source in RAM is not the normal case. A hard disk or a USB 3 stick reads at about 100 MB/s. To simulate it, I limited the read speed of a loop device of 1 GiB to 100 MiB/s with the blkio cgroup:

mkdir /sys/fs/cgroup/blkio/slow
echo "7:2 104857600" > /sys/fs/cgroup/blkio/slow/blkio.throttle.read_bps_device
echo $$ > /sys/fs/cgroup/blkio/slow/tasks

Command explanation:

  • 7:2 is the major and minor number of the loop device (/dev/loop2)
  • 104857600 is the maximum read speed in bytes per second (100 MiB/s)
  • the last line moves the current shell in the cgroup, so all the commands that you run from it have the limit

I ran each command two times and I took the average:

CommandTimeSpeed
dd bs=1M (no hash)10.15 s105 MB/s
dd bs=1M + sha256sum20.40 s52 MB/s
dd bs=1M + md5sum + sha256sum30.56 s35 MB/s
dcfldd hash=sha25610.22 s105 MB/s
dcfldd hash=md5,sha25610.63 s101 MB/s
dc3dd hash=sha25610.24 s104 MB/s
dc3dd hash=md5 hash=sha25610.34 s103 MB/s

Here the result is different. The drive is the bottleneck, and dcfldd and dc3dd calculate the hashes at the same speed of the drive. With dd you must read the drive again for each hash: one hash, two times the time; two hashes, three times the time. On a 1 TB drive at 100 MB/s, an acquisition with MD5 and SHA-256 takes about 3 hours with dcfldd or dc3dd. With dd + md5sum + sha256sum it takes about 8 hours.

Test 3: reliability with bad sectors

This is the most interesting part. I created a source of 256 MB with three areas that cannot be read:

  • one sector of 4 KiB at about 100 MiB
  • one sector of 4 KiB at about 143 MiB
  • a damaged area of 64 KiB at 200 MiB

In total, 73,728 bytes cannot be read. This is the minimum data that a tool must lose.

To simulate the bad sectors, I wrote a small FUSE filesystem. It gives access to a file and returns an I/O error when a read touches a bad area. This is the read function:

static int b_read(const char *p, char *buf, size_t n, off_t o, struct fuse_file_info *fi) {
  for (int i = 0; i < nbad; i++)
    if (o < bad_off[i] + bad_len[i] && o + (off_t)n > bad_off[i]) return -EIO;
  return pread(bfd, buf, n, o);
}

Then I attached the file to a loop device, in read-only mode:

losetup -r --show -f /mnt/badfs/disk.img

In this way the tools read a real block device (/dev/loop0) that returns real I/O errors. After each acquisition I compared the image with the original data, sector by sector.

These are the results. I ran the main commands three times, and the results were the same every time:

CommandImage sizeData lost
dd bs=4096 conv=noerror,syncCorrect200,704 bytes
dd bs=64K conv=noerror,syncCorrect327,680 bytes
dd bs=1M conv=noerror,syncCorrect3,145,728 bytes
dd bs=4096 conv=noerror,sync iflag=directCorrect73,728 bytes (only the bad sectors)
dd bs=4096 (without conv=noerror)Stops at 100 MiBEverything after the first error
dcfldd conv=noerror,sync (default, bs=32K)32 KiB too big118,562,816 bytes (data shifted)
dcfldd bs=4096 conv=noerror,syncCorrect200,704 bytes
dc3dd (default)Correct73,728 bytes (only the bad sectors)
ddrescueCorrect393,216 bytes
ddrescue -dCorrect73,728 bytes (only the bad sectors)

dc3dd

dc3dd was the best tool with the default options. It lost only the bad sectors and it wrote a clear summary at the end:

input results for device `/dev/loop0':
   524288 sectors in
   144 bad sectors replaced by zeros
   aeefb1506d6449c3f4a3450979b87400a5739c0fbf169f0d78e308270f255a1c (sha256)

The hash in the output is the hash of the image file, so you can use it directly in your report.

dcfldd

With the default block size (32 KiB), dcfldd created a broken image. After the second bad area the image is 32 KiB bigger than the source, and all the data after that point is at the wrong offset. dcfldd did not show any warning, only the I/O errors. The only hint is in the last lines: 8193+0 records out instead of 8192.

It seems related to a short read just before the bad sector, but I did not check the source code. With bs=4096 the image was correct.

There is also a second problem. When there are read errors, the hash that dcfldd writes is not the hash of the image file. In my test with bs=4096:

  • hash written by dcfldd: 803fe811…
  • sha256sum of the image file: a2e459df…

The reason is the option hashconv. By default dcfldd calculates the hash before the conversions, so the zeros added by sync are not in the hash. Add hashconv=after and the hash matches the image file:

sudo dcfldd if=/dev/sdb of=/evidence/image.dd bs=4096 conv=noerror,sync hash=sha256 hashconv=after hashlog=/evidence/hash.txt

dd and ddrescue

dd never created a broken image, but with normal reads it lost more data than the bad sectors. The reason is the Linux page cache: in my test, Linux marked as unreadable an area of 128 KiB around the first bad sector. With a bigger block size dd lost more data, because sync fills the full block with zeros: with bs=1M it lost 3 MB.

With iflag=direct dd reads the drive without the page cache, and it lost only the bad sectors. ddrescue had the same behaviour: without -d it lost 393 KB, with -d (direct disc access) only the bad sectors.

Please note that a real damaged drive is different from my simulation. It can be very slow near the bad sectors, it can get worse at every read, and two runs can give different results. ddrescue is made for this: it reads the good areas first and goes back to the bad areas at the end.

So, which one should I use?

  • Healthy drive (hard disk, USB): dcfldd or dc3dd. They read the drive one time and calculate the hash at the same time. dd plus sha256sum takes two times longer.
  • Fast drive (SSD, NVMe): in my test the hash inside dcfldd and dc3dd stops at about 200 MB/s. If you need two hashes, dc3dd is faster.
  • Drive with bad sectors: dc3dd with the default options, or ddrescue with -d. If you use dcfldd, add bs=4096 and hashconv=after. If you use dd, add bs=4096 iflag=direct conv=noerror,sync.

In all cases, at the end compare the size of the image with the size of the source, and check the hash.

If you made similar tests, especially on real damaged drives, please share your results in the comments!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.