Uses ntfswalk and sleuthkit.
Fetch MFT, find offset, update code near cluster_to_byte_offset.
Example ntfswalk usage:
ntfswalk32 -mftfile ../mft.raw -out ntfswalk-results -csv -action_include_clusterinfo
| class Damage | |
| def initialize(start, size) | |
| @start = start; @size = size | |
| @stop = @start + @size | |
| end | |
| def overlaps?(start, stop) | |
| intersection_start = if start >= @start then start else @start end # max | |
| intersection_stop = if stop <= @stop then stop else @stop end # min | |
| return intersection_stop >= intersection_start | |
| end | |
| def self.read(f) | |
| r = [] | |
| File.open(f, "r") do |f| | |
| f.each_line do |l| | |
| if l.match(/(0x[A-F0-9]{8}) (0x[A-F0-9]{8}) -/) | |
| r.push(Damage.new $1.to_i(16), $2.to_i(16)) | |
| end | |
| end | |
| end | |
| r | |
| end | |
| end | |
| class NTFSWalk | |
| def self.read(f) | |
| File.open(f, "r") do |fh| | |
| fh.each_line do |l| | |
| if l.match(/^0x/) | |
| yield l.split(',') | |
| end | |
| end | |
| end | |
| end | |
| end | |
| NTFS_OFFSET = 718848 * 512 | |
| def cluster_to_byte_offset(x) | |
| x * 4096 + NTFS_OFFSET | |
| end | |
| damaged_regions = Damage.read("ddrescue.log") | |
| puts damaged_regions.map { |x| x.inspect } | |
| NTFSWalk.read('ntfswalk-results') do |mft_entry,seqnum,parent_mft,type,ext,ref,size_data,date,time,macb,data_type,other_info,path_and_filename,clusters| | |
| if clusters.match(/(0x[A-Fa-f0-9]+) -> (0x[A-Fa-f0-9]+)/) | |
| byte_start = cluster_to_byte_offset($1.to_i(16)) | |
| byte_stop = cluster_to_byte_offset($2.to_i(16) + 1) | |
| elsif clusters.match(/(0x[A-Fa-f0-9]+)/) | |
| byte_start = cluster_to_byte_offset($1.to_i(16)) | |
| byte_stop = cluster_to_byte_offset($1.to_i(16) + 1) | |
| else | |
| next | |
| end | |
| damaged_regions.each do |d| | |
| if d.overlaps? byte_start, byte_stop | |
| puts "Damaged file!" | |
| puts d.inspect | |
| puts "file range=#{byte_start} to #{byte_stop}" | |
| puts clusters | |
| puts path_and_filename | |
| end | |
| end | |
| end |