metasploit-framework/lib/msf/ui/gtk2/frame/modules_tree.rb

475 lines
11 KiB
Ruby

module Msf
module Ui
module Gtk2
##
# This class describe the modules treeview
##
class MyModuleTree < MyGlade
@@completion = []
PIX, NAME, MOD, DESC, TYPE = *(0..5).to_a
DIR, EXP, AUX, PAY, ENC, NOP = *(0..5).to_a
include Msf::Ui::Gtk2::MyControls
def initialize(treeview, viewmodule)
super('menu_module')
@treeview1 = treeview
@treeview1.enable_search = true
@model = Gtk::TreeStore.new(
Gdk::Pixbuf, # pixbuf
String, # Module name
Object, # Module object
String, # Description
Fixnum # Module type
)
# Register the model for later use
$gtk2driver.module_model = @model
# Init buffer module with tags
buff = SkeletonTextBuffer.new()
viewmodule.set_buffer(buff)
viewmodule.set_editable(false)
viewmodule.set_cursor_visible(false)
@buffer = MyModuleView.new(buff)
# Renderer Module
renderer_pix = Gtk::CellRendererPixbuf.new
renderer_module = Gtk::CellRendererText.new
column_module = Gtk::TreeViewColumn.new
column_module.pack_start(renderer_pix, false)
column_module.set_cell_data_func(renderer_pix) do |column, cell, model, iter|
cell.pixbuf = iter[PIX]
end
column_module.pack_start(renderer_module, true)
column_module.set_cell_data_func(renderer_module) do |column, cell, model, iter|
cell.text = iter[NAME]
end
column_desc = Gtk::TreeViewColumn.new
column_desc.pack_start(renderer_module, true)
column_desc.set_cell_data_func(renderer_module) do |column, cell, model, iter|
cell.text = iter[DESC]
end
#set model to treeview
@treeview1.set_size_request(380, -1)
@treeview1.set_model(@model)
@treeview1.rules_hint = true
@selection = @treeview1.selection
@treeview1.selection.mode = Gtk::SELECTION_BROWSE
@treeview1.append_column(column_module)
@treeview1.append_column(column_desc)
# Signals
@treeview1.signal_connect('cursor-changed') do |widget, event|
widget.selection.selected_each do |model, path, iter|
active(iter)
end
end
@treeview1.signal_connect('button_press_event') do |treeview, event|
if event.kind_of? Gdk::EventButton
# Right click
if (event.button == 3)
path, column, x, y = treeview.get_path_at_pos(event.x, event.y)
begin
iter = @treeview1.model.get_iter(path)
if(true)
treeview.selection.select_path(path)
active(iter)
@menu_module.popup(nil, nil, event.button, event.time)
end
rescue
nil
end
# Double click
elsif (event.event_type == Gdk::Event::BUTTON2_PRESS)
path, column, x, y = treeview.get_path_at_pos(event.x, event.y)
begin
iter = @treeview1.model.get_iter(path)
if (iter.get_value(TYPE) == EXP)
treeview.selection.select_path(path)
active(iter)
MsfAssistant::Exploit.new(iter.get_value(MOD))
elsif (iter.get_value(TYPE) == AUX)
treeview.selection.select_path(path)
active(iter)
MsfAssistant::Auxiliary.new(iter.get_value(MOD))
elsif (iter.get_value(TYPE) == DIR)
# Ignore
else
treeview.selection.select_path(path)
active(iter)
MsfDialog::Error.new($gtk2driver.main, "Not available")
end
rescue
nil
end
end
end
end
@one_shot.signal_connect('activate') do |item|
if active_module = @selection.selected
type = active_module.get_value(TYPE)
if (type == EXP)
MsfAssistant::Exploit.new(active_module.get_value(MOD))
elsif (type == AUX)
MsfAssistant::Auxiliary.new(active_module.get_value(MOD))
elsif (type == DIR)
# Ignore
else
MsfDialog::Error.new($gtk2driver.main, "Not available")
end
end
end
@view_code.signal_connect('activate') do |item|
if active_module = @selection.selected
type = active_module.get_value(TYPE)
if (type == EXP or type == AUX)
MsfWindow::CodeView.new(active_module.get_value(MOD))
elsif (type == DIR)
# Ignore
else
MsfDialog::Error.new($gtk2driver.main, "Not available")
end
end
end
# Add modules in the Gtk::TreeView
add_modules()
# Configure the module completion handles for easy reference
$gtk2driver.module_completion = @@completion
# Modified hyperlink code from gtk-demo
viewmodule.signal_connect("event-after") do |viewtext, event|
if (
event.kind_of?(Gdk::EventButton) and
event.button == 1 and
event.event_type == Gdk::Event::BUTTON_PRESS
)
buffer = viewtext.buffer
# we shouldn't follow a link if the user has selected something
range = buffer.selection_bounds
if range and range[0].offset != range[1].offset
false
else
x, y = viewtext.window_to_buffer_coords(Gtk::TextView::WINDOW_WIDGET, event.x, event.y)
iter = viewtext.get_iter_at_location(x, y)
tags = iter.tags
tags.each do |tag|
if (tag.respond_to?('href') and tag.href)
Rex::Compat.open_browser(tag.href)
break
end
end
true
end
else
false
end
end
end # def initialize
#
# Add modules to a treeview store specified by hash
#
def add_modules_to_store(store, parent, text, entry, attrs={})
iter = store.append(parent)
if (entry.class == ::Hash)
iter[PIX] = driver.get_icon(attrs[:top_icon] || attrs[:dir_icon])
iter[NAME] = text
iter[MOD] = nil
iter[TYPE] = DIR
iter[DESC] = attrs[:top_desc] || ""
attrs.delete(:top_icon)
attrs.delete(:top_desc)
entry.keys.sort.each do |x|
add_modules_to_store(store, iter, x, entry[x], attrs)
end
else
iter[PIX] = driver.get_icon(attrs[:mod_icon])
iter[NAME] = entry.refname.split("/")[-1]
iter[MOD] = entry
iter[DESC] = entry.name
iter[TYPE] = attrs[:type]
@@completion.push(entry.name)
end
end
#
# Prune empty module directories from the hash
#
def prune_hash(key, hash)
cnt = 0
hash.keys.each do |k|
if(hash[k].class != ::Hash)
cnt += 1
next
end
num = prune_hash(k, hash[k])
if (num == 0)
hash.delete(k)
end
cnt += num
end
# $stdout.puts "#{key} == #{cnt}"
cnt
end
#
# Add Exploits module in the treeview
#
def add_modules(filter=/.*/)
mod_exploits = {}
mod_auxiliary = {}
if(not (@module_cache and filter == /.*/))
framework.exploits.each_module do |mod, obj|
# SEGV :(
# while (Gtk.events_pending?)
# Gtk.main_iteration
# end
parts = mod.split("/")
name = parts.pop
ref = mod_exploits
parts.each do |part|
ref[part] ||= {}
ref = ref[part]
end
ins = obj.new
if (
mod =~ filter or
ins.name =~ filter or
ins.description.gsub(/\s+/, " ") =~ filter or
ins.author_to_s =~ filter or
ins.references.map {|x| x.to_s}.join(" ") =~ filter
)
ref[name] = obj.new
end
end
prune_hash("exploits", mod_exploits)
framework.auxiliary.each_module do |mod, obj|
# SEGV :(
# while (Gtk.events_pending?)
# Gtk.main_iteration
# end
parts = mod.split("/")
name = parts.pop
ref = mod_auxiliary
parts.each do |part|
ref[part] ||= {}
ref = ref[part]
end
ins = obj.new
if (
mod =~ filter or
ins.name =~ filter or
ins.description.gsub(/\s+/, " ") =~ filter or
ins.author_to_s =~ filter or
ins.references.map {|x| x.to_s}.join(" ") =~ filter
)
ref[name] = obj.new
end
end
prune_hash("auxiliary", mod_auxiliary)
# Cache the entire tree
if(filter == /.*/)
@module_cache ||= {}
@module_cache[:exploits] = mod_exploits
@module_cache[:auxiliary] = mod_auxiliary
end
else
mod_exploits = @module_cache[:exploits]
mod_auxiliary = @module_cache[:auxiliary]
end
add_modules_to_store(
@model, nil, "Exploits", mod_exploits,
{
:top_icon => "bug.png",
:top_desc => "All loaded exploit modules (#{framework.stats.num_exploits.to_s})",
:dir_icon => "gnome-fs-directory.png",
:mod_icon => "bug.png",
:type => EXP
}
)
add_modules_to_store(
@model, nil, "Auxiliary", mod_auxiliary,
{
:top_icon => "zoom.png",
:top_desc => "All loaded auxiliary modules (#{framework.stats.num_auxiliary.to_s})",
:dir_icon => "gnome-fs-directory.png",
:mod_icon => "zoom.png",
:type => AUX
}
)
#
# TODO: To implement later ...
#
# # Add Parent "Payloads (nbr payloads)"
# iter = @model.append(nil)
# iter.set_value(PIX, driver.get_icon("bomb.png"))
# iter.set_value(NAME, "Payloads (#{framework.stats.num_payloads.to_s})")
# iter.set_value(MOD, nil)
# iter.set_value(ADV, true)
#
# # Add Payloads childs
# framework.payloads.each_module do |mod, obj|
# next if not mod.match(filter)
# t_module = obj.new.name
# child_iter = @model.append(iter)
# child_iter.set_value(NAME, t_module)
# child_iter.set_value(MOD, obj.new)
# child_iter.set_value(ADV, false)
# child_iter.set_value(APP, "Payloads")
# @@completion.push(t_module)
# end
#
# # Add Parent "Nops (nbr nops)"
# iter = @model.append(nil)
# iter.set_value(PIX, driver.get_icon("encoders.png"))
# iter.set_value(NAME, "NOPs (#{framework.stats.num_nops.to_s})")
# iter.set_value(MOD, nil)
# iter.set_value(ADV, true)
#
# # Add nops childs
# framework.nops.each_module do |mod, obj|
# next if not mod.match(filter)
# t_module = obj.new.name
# child_iter = @model.append(iter)
# child_iter.set_value(NAME, t_module)
# child_iter.set_value(MOD, obj.new)
# child_iter.set_value(ADV, false)
# child_iter.set_value(APP, "NOPs")
# @@completion.push(t_module)
# end
#
# # Add Parent "Encoders (nbr encoders)"
# iter = @model.append(nil)
# iter.set_value(PIX, driver.get_icon("encoders.png"))
# iter.set_value(NAME, "Encoders (#{framework.stats.num_encoders.to_s})")
# iter.set_value(MOD, nil)
# iter.set_value(ADV, true)
#
# # Add Encoders childs
# framework.encoders.each_module do |mod, obj|
# next if not mod.match(filter)
# t_module = obj.new.name
# child_iter = @model.append(iter)
# child_iter.set_value(NAME, t_module)
# child_iter.set_value(MOD, obj.new)
# child_iter.set_value(ADV, false)
# iter.set_value(APP, "Encoders")
# @@completion.push(t_module)
# end
end # def add_modules
#
# Display the module information
#
def active(iter)
if not iter[MOD].nil?
@buffer.insert_module(iter.get_value(MOD))
else
@buffer.insert_help(iter.get_value(NAME))
end
end
#
# Refresh the module treeview with all msf modules
#
def refresh(filter=/.*/)
@model.clear()
add_modules(filter)
@buffer.insert_help("Exploits")
end
#
# remove all iters in array_iter
#
def remove(iter_array)
# first loop to remove unmatched iter
iter_array.each do |iter|
next if iter[TYPE] == DIR
@model.remove(iter)
end
return
# second loop to update parent iter with child iter
no_child = []
@model.each do |model, path, iter|
no_child.push(iter) if not iter.has_child?
iter[NAME] = iter[NAME].sub(/[0-9]+/, iter.n_children.to_s)
end
# remove iter
no_child.each do |iter|
@model.remove(iter)
end
end
#
# expand the treeview
#
def expand
@treeview1.expand_all
end
end
end
end
end