metasploit-framework/lib/msf/ui/gtk2/console/skeleton.rb

335 lines
8.5 KiB
Ruby

module Msf
module Ui
module Gtk2
###
#
# Classic console herited from Gtk::Window
#
###
class SkeletonConsole < Gtk::Window
require 'rex/io/bidirectional_pipe'
include Msf::Ui::Gtk2::MyControls
###
#
# Basic command history class
#
###
class History
def initialize
@history = [""]
@position = @history.length - 1
end
#
# Get previous command in history array
#
def prev(current)
l = current
l = l[0,1] if (l.length > 0 and l[0,1] == '\n')
l = l[-1,1] if (l.length > 0 and l[-1,1] == '\n')
if (@position > 0)
if (@position == (@history.length - 1))
@history[@history.length - 1] = l
end
@position = @position - 1
return @history[@position]
end
return current
end
#
# Get next command in history array
#
def next(current)
if (@position < @history.length - 1)
@position = @position + 1
return @history[@position]
end
return current
end
#
# Append a new command to history
#
def append(cmd)
@position = @history.length - 1
return if cmd.length == 0
if ( (@position == 0) or (@position > 0 and cmd != @history[@position - 1]) )
@history[@position] = cmd
@position = @position + 1
@history.push('')
end
end
end
ID_SESSION, PEER, TYPE, O_SESSION, O_BUFFER = *(0..5).to_a
@@offset = 0
attr_accessor :type, :button_close
#
# Init the SkeletonConsole class
#
def initialize(iter)
# Style
console_style = File.join(driver.resource_directory, 'style', 'console.rc')
Gtk::RC.parse(console_style)
# Call the parent
super(Gtk::Window::TOPLEVEL)
# initialize the session var from the iter sessions tree
@session = iter[O_SESSION]
# Layout stuff
self.set_default_size(500, 400)
self.set_border_width(10)
# Set title with the tunnel peer
self.set_title(@session.tunnel_peer)
# Add a vertical box to the window
vbox = Gtk::VBox.new(false, 5)
self.add(vbox)
# Setup text view and buffer
@textview = Gtk::TextView.new
if iter[O_BUFFER].nil?
@buffer = Gtk::TextBuffer.new
iter[O_BUFFER] = @buffer
else
@buffer = iter[O_BUFFER]
end
scrolled_window = Gtk::ScrolledWindow.new
scrolled_window.add(@textview)
vbox.pack_start(scrolled_window, true, true, 5)
scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
# Setup text buffer
@textview.set_buffer(@buffer)
@textview.editable = true
@textview.set_cursor_visible(true)
@buffer.create_mark('end_mark', @buffer.end_iter, false)
# Setup button close
hbox = Gtk::HButtonBox.new
hbox.layout_style = Gtk::ButtonBox::END
@button_close = Gtk::Button.new(Gtk::Stock::CLOSE)
# Pack
hbox.pack_end(@button_close, false, false, 5)
vbox.pack_start(hbox, false, false, 0)
# Signal for the Return key pressed
signal_connect('key_press_event') do |edit, event|
on_key_pressed(event)
end
# Create the pipe interface
@pipe = Rex::IO::BidirectionalPipe.new
# Start the session interaction
@t_run = Thread.new do
@session.interact(@pipe, @pipe)
end
# Create a subscriber with a callback for the UI
@sid = @pipe.create_subscriber_proc() do |data|
insert_text(Rex::Text.to_utf8(data))
end
# Init an history object
@historic = History.new()
# Init the prompt variable with the session type
@type = @session.type
# Display all
self.show_all
end #intialize
#
# Send command to bidirectionnal_pipe
#
def send_cmd(cmd)
# What time is it ?
# update_access
# Write the command plus a newline to the input
@pipe.write_input(cmd + "\n")
end
#
# Just close the console, not kill !
#
def close_console
self.destroy
end
###########
protected #
###########
#
# update access
#
def update_access
last_access = Time.now
end
#
# meterpreter prompt
#
def prompt
null_prompt = ""
meta_prompt = "meterpreter >> "
if (@type == "meterpreter")
@buffer.insert(@buffer.end_iter, meta_prompt)
else
@buffer.insert(@buffer.end_iter, null_prompt)
end
@@offset = @buffer.end_iter.offset
end
#
# Get the current line
#
def current_line
# get the actual offset
start = @buffer.get_iter_at_offset(@@offset)
# get the command
line = @buffer.get_text(start, @buffer.end_iter)
return line
end
#
# Replace the current active line with another line
#
def replace(line)
# get the actual offset
start = @buffer.get_iter_at_offset(@@offset)
# Delete all
@buffer.delete(start, @buffer.end_iter)
# Save the new offset
@@offset = @buffer.end_iter.offset
# insert the old command
@buffer.insert(@buffer.end_iter, line)
end
#
# Catch the text from the textview
#
def catch_text
# get the actual offset
start = @buffer.get_iter_at_offset(@@offset)
# get the command
cmd = @buffer.get_text(start, @buffer.end_iter)
# Save the command to the history object
@historic.append(cmd)
# Write the command to our pipe
send_cmd(cmd)
# Add a return line to our buffer
insert_text("\n")
# Call the prompt
prompt()
# Create the mark tag if not exist
if (not @buffer.get_mark('end_mark'))
@buffer.create_mark('end_mark', @buffer.end_iter, false)
end
# Save our offset
@@offset = @buffer.end_iter.offset
end
#
# Insert the text into the buffer
#
def insert_text(text)
# Create the mark tag if not exist
@buffer.insert(@buffer.end_iter, text)
if (not @buffer.get_mark('end_mark'))
@buffer.create_mark('end_mark', @buffer.end_iter, false)
end
# Save our offset
@@offset = @buffer.end_iter.offset
# Scrolled the view until the end of the buffer
@textview.scroll_mark_onscreen(@buffer.get_mark('end_mark'))
end
#
# A key pressed handler
#
def on_key_pressed(event)
# Enter key
if (event.keyval == Gdk::Keyval::GDK_Return)
catch_text()
# Backspace key
elsif (event.keyval == Gdk::Keyval::GDK_BackSpace)
iter = @buffer.end_iter
if iter.offset == @@offset
return true
else
return false
end
# Delete key
elsif (event.keyval == Gdk::Keyval::GDK_Delete)
iter = @buffer.end_iter
if iter.offset == @@offset
return true
else
return false
end
# Previous command
elsif (event.keyval == Gdk::Keyval::GDK_Up)
cmd = @historic.prev(current_line())
replace(cmd)
return true
# Next command
elsif (event.keyval == Gdk::Keyval::GDK_Down)
cmd = @historic.next(current_line())
replace(cmd)
return true
end
end
end # SkeletonConsole
###
#
# This class surcharge the Rex::IO::BidirectionalPipe *original* behaviour
#
###
class GtkConsolePipe < Rex::IO::BidirectionalPipe
def prompting?
false
end
end
end
end
end