Skip to content

Instantly share code, notes, and snippets.

@gbence
Created March 22, 2023 00:17
Show Gist options
  • Save gbence/a04ca0b081002af9f58ecf4d1ada2873 to your computer and use it in GitHub Desktop.
Save gbence/a04ca0b081002af9f58ecf4d1ada2873 to your computer and use it in GitHub Desktop.
Recover (all) files recursively with Sleuth Kit
source 'https://rubygems.org'
gem 'pry'
#!/usr/bin/env ruby
# Usage
# =====
#
# 1. Create `entries.list` containing all the files and directories of the
# filesystem in question:
#
# ```
# $ fls -f ntfs -l -p -r /dev/sde6 | tee entries.list
# ```
#
# 2. Modify `DEVICE` and `RECOVERY_DIR` and then run this script.
#
require 'bundler/setup'
require 'csv'
require 'pry'
require 'fileutils'
class Object
def try method, *args
respond_to?(method) ? send(method, *args) : nil
end
end
DEVICE = '/dev/sde6'
RECOVERY_DIR = '/mnt/usb/data'
class String
def normalize_path
self
.gsub(/\$/, '\\$')
.gsub(/\s/, '\\ ')
end
end
entries = CSV.read('entries.list', col_sep: ?\t, headers: false).map do |entry|
type = entry[0].match(/^(.\/.)/).try(:[], 1)
inode = entry[0].match(/(\d{1,}-\d{1,}-\d{1,}):$/).try(:[], 1)
{
type: type,
inode: inode,
filename: entry[1],
original: entry,
}
end
SIZE = entries.size
def puts index, type, message
Kernel.puts '%05d/%05d %15s %s' % [index, SIZE, type, message]
end
entries.each_with_index do |entry,index|
if entry[:type] =~ /^d|d$/ && !entry[:inode].nil?
# directory
directory_path = File.expand_path(entry[:filename], RECOVERY_DIR)
puts index, 'new directory', directory_path
FileUtils.mkdir_p(directory_path)
metadata_path = File.expand_path('_.dir', directory_path)
puts index, 'metadata', metadata_path
ok = `icat -f ntfs #{DEVICE} #{entry[:inode]} > #{metadata_path.normalize_path}`
raise 'execute error' unless ok
elsif !entry[:inode].nil?
# file
file_path = File.expand_path(entry[:filename], RECOVERY_DIR)
parent_path = File.dirname(file_path)
unless File.directory?(parent_path)
puts index, 'new directory', parent_path
FileUtils.mkdir_p(parent_path)
end
puts index, 'new file', file_path
ok = `icat -f ntfs #{DEVICE} #{entry[:inode]} > #{file_path.normalize_path}`
raise 'execute error' unless ok
else
puts index, 'unresolved', %{#{entry[:filename]} (#{entry[:type]} : #{entry[:inode]})}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment