Thursday 11 June 2020

Non-bootable Windows recovery (Part 4)

Context

This is the fourth post in a series dedicated to documenting a systematic approach of recovering a non-bootable Windows machine with Linux and open source tools. Make sure you read the first, second and third post to familiarize yourself with the context and previous activities.

After initial diagnostics, the investigation was narrowed down to a disk that fails S.M.A.R.T. extended self test, and misreports Reallocated Sector Count.

The first round of data recovery consisted of two ddrescue sessions taking a total of 48 hours but successfully extracting all the data with the exception of 1081 kilobytes spread across 16 ranges of consecutive bad sectors. It was captured, how the file system used the bad sector ranges, which files or what kind of file system structures seem to be affected.

Next, a pragmatic decision was taken to overwrite the last four of the bad sector ranges to force reallocation of bad sectors. These sector ranges were the longest four, and based on available information it was almost certian that these areas are unallocated, unused by the file system. Due to damages in the Master File Table, this could not be confirmed for 100%, but the risk involved was acceptable. The four bad sector areas were nuked via dd, a firmware misbehavior around misreporting sector reallocation count was observed and confirmed again, and another round of ddrescue was run, decreasing bad sectors to around 68 kilobytes spread across 15 bad areas.

Third round

During the investigation of the bad sector ranges there were errors reading 4 MFT entries 222148-222151. Therefore, any bad sector shown as unallocated by the system still could be in use but masked due to the MFT errors.

A more sophisticated approach was taken to progress data recovery without overwriting any further sector of the failing disk. The goal was to focus only on those bad sector ranges, which are known to hold either files or MFT entries, which was done by manipulating the mapfile ddrescue created to track the state of the rescue process. Successfully recovering the MFT entries would bring certainty about the usage of those sector ranges, which seem to be unallocated according to the damaged MFT. The key steps involved were the following:

  • Regenerate the list of affected files from the healthy drive, based on the mapfile resulting from the second round
  • Backup, then alter the mapfile by deleting bad blocks which are not certainly related to files or MFT
  • Save the deleted bad blocks to another mapfile for later processing
  • Run ddrescue on the failing drive with the mapfile that contains only high priority bad blocks

The individual commands were covered in previous posts in sufficient detail, the exact commands of the first step were as follows:


[root@sysresccd /mnt/REPO]# mkdir round-3
[root@sysresccd ~]# cp disk-sdb-to-sda.map /mnt/REPO/
[root@sysresccd /mnt/REPO]# ddrescuelog --list-blocks=- disk-sdb-to-sda.map | while read lba; do echo $(($lba - 1083392)); done >> bad-sectors-sdb4.txt
[root@sysresccd /mnt/REPO]# cat bad-sector-ranges-sdb4.txt 
3733376-3733383
9217344-9217351
9220448-9220455
9240544-9240551
9324120-9324127
10061520-10061527
16845920-16845927
17032080-17032081
17032808-17032815
42682272-42682287
47500880-47500927
89082175
103509823
103707887
139004815
[root@sysresccd /mnt/REPO]# cat bad-sector-ranges-sdb4.txt | while read range; do ntfscluster -s $range /dev/sda4; done > affected-files-sdb4.txt
[root@sysresccd /mnt/REPO]# cat affected-files-sdb4.txt 
Searching for sector range 3733376-3733383
Inode 112588 /ProgramData/Norton/{0C55C096-0F1D-4F28-AAA2-85EF591126E7}/NS_22.6.0.142/NCW/hlinks/ncwperfm.db.data/$DATA
* one inode found
Searching for sector range 9217344-9217351
* no inode found
Searching for sector range 9220448-9220455
* no inode found
Searching for sector range 9240544-9240551
Inode 248863 /Windows/ServiceProfiles/LocalService/AppData/Roaming/PeerNetworking/91e9854e5d4d7ac47cd0bf3ad8003817/922c235eb3d1d91e502cb02599cc24c7/grouping/edb02861.log/$DATA
* one inode found
Searching for sector range 9324120-9324127
Inode 96681 /$Extend/$UsnJrnl/$DATA($J)
* one inode found
Searching for sector range 10061520-10061527
Inode 0 /$MFT/$DATA
* one inode found
Searching for sector range 16845920-16845927
Inode 112588 /ProgramData/Norton/{0C55C096-0F1D-4F28-AAA2-85EF591126E7}/NS_22.6.0.142/NCW/hlinks/ncwperfm.db.data/$DATA
* one inode found
Searching for sector range 17032080-17032081
Inode 216164 /Windows/WinSxS/Catalogs/824be8ecd9b27e0ee92936dfba7c9f759df9d2e2999542e22d6f3bacab5b6140.cat/$DATA
* one inode found
Searching for sector range 17032808-17032815
Inode 75215 /Windows/WinSxS/Manifests/amd64_microsoft-windows-c..rintscan-deployment_31bf3856ad364e35_6.3.9600.16384_none_5cfa2773ab911611.manifest/$DATA
* one inode found
Searching for sector range 42682272-42682287
* no inode found
Searching for sector range 47500880-47500927
* no inode found
Searching for sector 89082175
* no inode found
Searching for sector 103509823
* no inode found
Searching for sector 103707887
* no inode found
Searching for sector 139004815
* no inode found

Looking at the list of affected files, once can see that the second round covered in the previous post already decreased the amount of files, the Adobe language file was not present in the new listing.

Next, the map file was backed up and hand edited. The status of blocks of lower priority - the ones where no inode found was printed - was flipped from - to + and were marked with a hashmark which is just an annotation that does not influence the execution of ddrescue.


[root@sysresccd /mnt/REPO]# cat disk-sdb-to-sda-priority.map.backup 
# Mapfile. Created by GNU ddrescue version 1.25
# Command line: ddrescue --ask --verbose --binary-prefixes --idirect --retry=20 --force /dev/sdb /dev/sda disk-sdb-to-sda.map
# Start time:   2020-05-17 14:24:44
# Current time: 2020-05-17 17:40:41
# Finished
# current_pos  current_status  current_pass
0x92FF0200     +               20
#      pos        size  status
0x00000000  0x92FF0000  +
0x92FF0000  0x00001000  -
0x92FF1000  0xA75B7000  +
0x13A5A8000  0x00001000  + #
0x13A5A9000  0x00183000  +
0x13A72C000  0x00001000  + #
0x13A72D000  0x009CF000  +
0x13B0FC000  0x00001000  -
0x13B0FD000  0x028CE000  +
0x13D9CB000  0x00001000  -
0x13D9CC000  0x1680E000  +
0x1541DA000  0x00001000  -
0x1541DB000  0xCF0B1000  +
0x22328C000  0x00001000  -
0x22328D000  0x05AE5000  +
0x228D72000  0x00000400  -
0x228D72400  0x0005AC00  +
0x228DCD000  0x00001000  -
0x228DCE000  0x30EC26000  +
0x5379F4000  0x00002000  + #
0x5379F6000  0x930D4000  +
0x5CAACA000  0x00006000  + #
0x5CAAD0000  0x4F4F57E00  +
0xABFA27E00  0x00000200  + #
0xABFA28000  0x1B84BFE00  +
0xC77EE7E00  0x00000200  + #
0xC77EE8000  0x060B5E00  +
0xC7DF9DE00  0x00000200  + #
0xC7DF9E000  0x4352D3E00  +
0x10B3271E00  0x00000200  + #
0x10B3272000  0xD82DB44000  +
[root@sysresccd ~]# ddrescue --ask --verbose --binary-prefixes --idirect --retry=200 --force /dev/sdb /dev/sda /mnt/REPO/disk-sdb-to-sda-priority.map
GNU ddrescue 1.25
About to copy 953869 MiBytes
from '/dev/sdb' [UNKNOWN] (1_000_204_886_016)
  to '/dev/sda' [UNKNOWN] (1_000_204_886_016)
Proceed (y/N)? y
    Starting positions: infile = 0 B,  outfile = 0 B
    Copy block size: 128 sectors       Initial skip size: 19584 sectors
Sector size: 512 Bytes

Press Ctrl-C to interrupt
Initial status (read from mapfile)
rescued: 953869 MiB, tried: 25600 B, bad-sector: 25600 B, bad areas: 7

Current status
     ipos:   2351 MiB, non-trimmed:        0 B,  current rate:       0 B/s
     opos:   2351 MiB, non-scraped:        0 B,  average rate:       0 B/s
non-tried:        0 B,  bad-sector:    21504 B,    error rate:     128 B/s
  rescued: 953869 MiB,   bad areas:        6,        run time: 11h 37m 57s
pct rescued:   99.99%, read errors:     8826,  remaining time:         n/a
                              time since last successful read:  1h 16m 22s
Finished

A rather aggressive run of 200 retries laster more, than 11 hours but brought impressive results as it can be seen above. Further iterations of ddrescue were run to the point where no more progress could be made.

                          
[root@sysresccd ~]# ddrescue --ask --verbose --binary-prefixes --idirect --retry=100 --force /dev/sdb /dev/sda /mnt/REPO/disk-sdb-to-sda-priority.map
GNU ddrescue 1.25
About to copy 953869 MiBytes
from '/dev/sdb' [UNKNOWN] (1_000_204_886_016)
  to '/dev/sda' [UNKNOWN] (1_000_204_886_016)
Proceed (y/N)? y
    Starting positions: infile = 0 B,  outfile = 0 B
    Copy block size: 128 sectors       Initial skip size: 19584 sectors
Sector size: 512 Bytes

Press Ctrl-C to interrupt
Initial status (read from mapfile)
rescued: 953869 MiB, tried: 21504 B, bad-sector: 21504 B, bad areas: 6

Current status
     ipos:   2351 MiB, non-trimmed:        0 B,  current rate:       0 B/s
     opos:   2351 MiB, non-scraped:        0 B,  average rate:       0 B/s
non-tried:        0 B,  bad-sector:    20992 B,    error rate:     102 B/s
  rescued: 953869 MiB,   bad areas:        6,        run time:  5h 27m  5s
pct rescued:   99.99%, read errors:     4124,  remaining time:         n/a
                              time since last successful read:  4h  3m 54s
Finished                                     
[root@sysresccd ~]# ddrescue --ask --verbose --binary-prefixes --idirect --retry=100 --force /dev/sdb /dev/sda /mnt/REPO/disk-sdb-to-sda-priority.map
GNU ddrescue 1.25
About to copy 953869 MiBytes
from '/dev/sdb' [UNKNOWN] (1_000_204_886_016)
  to '/dev/sda' [UNKNOWN] (1_000_204_886_016)
Proceed (y/N)? y
    Starting positions: infile = 0 B,  outfile = 0 B
    Copy block size: 128 sectors       Initial skip size: 19584 sectors
Sector size: 512 Bytes

Press Ctrl-C to interrupt
Initial status (read from mapfile)
rescued: 953869 MiB, tried: 20992 B, bad-sector: 20992 B, bad areas: 6

Current status
     ipos:   2351 MiB, non-trimmed:        0 B,  current rate:       0 B/s
     opos:   2351 MiB, non-scraped:        0 B,  average rate:       0 B/s
non-tried:        0 B,  bad-sector:    20480 B,    error rate:     102 B/s
  rescued: 953869 MiB,   bad areas:        5,        run time:  5h 23m 26s
pct rescued:   99.99%, read errors:     4075,  remaining time:         n/a
                              time since last successful read:  1h 19m 28s
Finished 
[root@sysresccd ~]# ddrescue --ask --verbose --binary-prefixes --idirect --retry=100 --force /dev/sdb /dev/sda /mnt/REPO/disk-sdb-to-sda-priority.map
GNU ddrescue 1.25
About to copy 953869 MiBytes
from '/dev/sdb' [UNKNOWN] (1_000_204_886_016)
  to '/dev/sda' [UNKNOWN] (1_000_204_886_016)
Proceed (y/N)? y
    Starting positions: infile = 0 B,  outfile = 0 B
    Copy block size: 128 sectors       Initial skip size: 19584 sectors
Sector size: 512 Bytes

Press Ctrl-C to interrupt
Initial status (read from mapfile)
rescued: 953869 MiB, tried: 20480 B, bad-sector: 20480 B, bad areas: 5

Current status
     ipos:   2351 MiB, non-trimmed:        0 B,  current rate:       0 B/s
     opos:   2351 MiB, non-scraped:        0 B,  average rate:       0 B/s
non-tried:        0 B,  bad-sector:    20480 B,    error rate:     102 B/s
  rescued: 953869 MiB,   bad areas:        5,        run time:  5h 17m 47s
pct rescued:   99.99%, read errors:     4000,  remaining time:         n/a
                              time since last successful read:         n/a
Finished 

After the first run that did not bring a single successful read, only 5 instances remained, with 8 sectors each. As this milestone was achieved in the middle of the night, the decision was taken to launch another session 200 retries and take a nap.

                                    
[root@sysresccd ~]# ddrescue --ask --verbose --binary-prefixes --idirect --retry=200 --force /dev/sdb /dev/sda /mnt/REPO/disk-sdb-to-sda-priority.map
GNU ddrescue 1.25
About to copy 953869 MiBytes
from '/dev/sdb' [UNKNOWN] (1_000_204_886_016)
  to '/dev/sda' [UNKNOWN] (1_000_204_886_016)
Proceed (y/N)? y
    Starting positions: infile = 0 B,  outfile = 0 B
    Copy block size: 128 sectors       Initial skip size: 19584 sectors
Sector size: 512 Bytes

Press Ctrl-C to interrupt
Initial status (read from mapfile)
rescued: 953869 MiB, tried: 20480 B, bad-sector: 20480 B, bad areas: 5

Current status
     ipos:   2351 MiB, non-trimmed:        0 B,  current rate:       0 B/s
     opos:   2351 MiB, non-scraped:        0 B,  average rate:       0 B/s
non-tried:        0 B,  bad-sector:    20480 B,    error rate:     102 B/s
  rescued: 953869 MiB,   bad areas:        5,        run time: 10h 35m 32s
pct rescued:   99.99%, read errors:     8000,  remaining time:         n/a
                              time since last successful read:         n/a
Finished

As the session completed without further progress, the data recovery process entered the final phase. Read the next post to learn more!

No comments:

Post a Comment