# treeviews.py # -*- coding: utf-8 -*- # # Copyright 2011 Hugo Teso <hugo.teso@gmail.com> # Copyright 2014 David MartÃnez Moreno <ender@debian.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. from gi.repository import Gtk from gi.repository import GdkPixbuf from lib.common import datafile_path class TreeViews(Gtk.TreeView): '''Main TextView elements''' def __init__(self, core, textviews): self.store = Gtk.ListStore(GdkPixbuf.Pixbuf, str, str, str, str) super(TreeViews,self).__init__(self.store) self.uicore = core self.textviews = textviews self.set_rules_hint(True) self.set_has_tooltip(True) # Connect right click popup search menu self.popup_handler = self.connect('button-press-event', self.popup_menu) self.popup_handler = self.connect('row-activated', self.popup_menu) def create_functions_columns(self): rendererText = Gtk.CellRendererText() rendererText.tooltip_handle = self.connect('motion-notify-event', self.fcn_tooltip) rendererPix = Gtk.CellRendererPixbuf() self.fcn_pix = GdkPixbuf.Pixbuf.new_from_file(datafile_path('function.png')) self.bb_pix = GdkPixbuf.Pixbuf.new_from_file(datafile_path('block.png')) column = Gtk.TreeViewColumn("Function") column.set_spacing(5) column.pack_start(rendererPix, False) column.pack_start(rendererText, True) column.set_attributes(rendererText, text=1) column.set_attributes(rendererPix, pixbuf=0) column.set_sort_column_id(1) self.store.set_sort_column_id(1,Gtk.SortType.ASCENDING) self.append_column(column) self.set_model(self.store) def create_relocs_columns(self): self.data_sec_pix = GdkPixbuf.Pixbuf.new_from_file(datafile_path('data-sec.png')) rendererPix = Gtk.CellRendererPixbuf() rendererText = Gtk.CellRendererText() column = Gtk.TreeViewColumn("Name") column.set_spacing(5) column.pack_start(rendererPix, False) column.pack_start(rendererText, True) column.set_attributes(rendererText, text=1) column.set_attributes(rendererPix, pixbuf=0) column.set_sort_column_id(0) self.append_column(column) rendererText = Gtk.CellRendererText() column = Gtk.TreeViewColumn("Virtual Address", rendererText, text=2) self.store.set_sort_column_id(2,Gtk.SortType.ASCENDING) column.set_sort_column_id(2) self.append_column(column) rendererText = Gtk.CellRendererText() column = Gtk.TreeViewColumn("Size", rendererText, text=3) column.set_sort_column_id(3) self.append_column(column) def create_exports_columns(self): self.exp_pix = GdkPixbuf.Pixbuf.new_from_file(datafile_path('export.png')) rendererPix = Gtk.CellRendererPixbuf() rendererText = Gtk.CellRendererText() column = Gtk.TreeViewColumn("Offset") column.set_spacing(5) column.pack_start(rendererPix, False) column.pack_start(rendererText, True) column.set_attributes(rendererText, text=1) column.set_attributes(rendererPix, pixbuf=0) self.store.set_sort_column_id(1,Gtk.SortType.ASCENDING) column.set_sort_column_id(1) self.append_column(column) rendererText = Gtk.CellRendererText() column = Gtk.TreeViewColumn("Name", rendererText, text=2) column.set_sort_column_id(2) self.append_column(column) rendererText = Gtk.CellRendererText() column = Gtk.TreeViewColumn("Ordinal", rendererText, text=3) column.set_sort_column_id(3) self.append_column(column) self.set_model(self.store) def remove_columns(self): columns = self.get_columns() for column in columns: self.remove_column(column) def create_tree(self, imps): # Create the column imports = Gtk.TreeViewColumn() imports.set_title("Imports") imports.set_spacing(5) self.treestore = Gtk.TreeStore(GdkPixbuf.Pixbuf, str) self.imp_pix = GdkPixbuf.Pixbuf.new_from_file(datafile_path('import.png')) rendererPix = Gtk.CellRendererPixbuf() rendererText = Gtk.CellRendererText() imports.pack_start(rendererPix, False) imports.pack_start(rendererText, True) imports.set_attributes(rendererText, text=1) imports.set_attributes(rendererPix, pixbuf=0) # Iterate imports and add to the tree for element in imps.keys(): it = self.treestore.append(None, [self.fcn_pix, element]) for imp in imps[element]: self.treestore.append(it, [self.imp_pix, imp[0] + '\t' + imp[1]]) # Add column to tree self.append_column(imports) self.set_model(self.treestore) self.expand_all() def search_and_graph(self, widget, link_name): self.textviews.search(widget, link_name) if self.dograph: self.textviews.update_graph(widget, link_name) def fcn_tooltip(self, widget, event): x = int(event.x) y = int(event.y) tup = widget.get_path_at_pos(x, y) if "Function" == tup[1].get_title(): model = widget.get_model() tree_iter = model.get_iter(tup[0]) fcn = model.get_value(tree_iter, 1) value = self.uicore.send_cmd_str('pdi 15 @ ' + fcn) widget.set_tooltip_markup("<span font_family=\"monospace\">" + value.rstrip() + "</span>") else: widget.set_tooltip_markup("") def popup_menu(self, tv, event, row=None): '''Controls the behavior of the treeviews on the left: Left-click or Enter/Space: Goes to the corresponding graph/address/etc. Right-click: Shows a menu. @param tv: The treeview. @parameter event: The GTK event (Gdk.Event) in case this is a mouse click. Otherwise it's the activated row index in format (n,). @parameter row: A Gtk.TreeViewColumn object in case it's a keypress, otherwise None. The function works by abstracting the event type and then defining primary_action (True if left-click or Enter on a row, False if double_click). ''' self.dograph = False # Let's get the row clicked whether it was by mouse or keyboard. if row: # Keyboard. path = event primary_action = True else: # Mouse. # I do this to return fast (and to avoid leaking memory in 'e io.va' for now). if (event.button != 1) and (event.button !=3): return False elif event.button == 1: # Left-click. primary_action = True else: primary_action = False coordinates = tv.get_path_at_pos(int(event.x), int(event.y)) # coordinates is None if the click is outside the rows but inside # the widget. if not coordinates: return False (path, column, x, y) = coordinates # FIXME: We should do this on the uicore, possibly in every operation. if self.uicore.use_va: self.uicore.core.cmd0('e io.va=0') else: self.uicore.core.cmd0('e io.va=1') # Main loop, deciding whether to take an action or display a pop-up. if primary_action: # It's a left click or Enter on a row. # Is it over a plugin name? # Get the information about the row. if len(path) == 1: link_name = self.store[path][1] # Special for exports if '0x' in link_name: link_name = self.store[path][2] else: link_name = self.treestore[path][1] # Detect if search string is from URL or PE/ELF link_name = link_name.split("\t") # Elf/PE (function) if len( link_name ) == 1: if '0x' in link_name[0]: link_name = link_name[0] elif 'reloc.' in link_name[0]: link_name = link_name[0] else: # Just get graph for functions if not 'loc.' in link_name[0] and link_name[0][0] != '.': self.dograph = True # Adjust section name to search inside r2 flags link_name = "0x%08x" % self.uicore.core.num.get(link_name[0]) # Elf/PE (import/export) elif len( link_name ) == 2 and link_name[1] != '': link_name = "0x%08x" % int(link_name[0], 16) self.search_and_graph(self, link_name) self.dograph = False else: # It's a right click! _time = event.time # Is it over a plugin name? # Get the information about the click. if len(path) == 1: link_name = self.store[path][1] else: link_name = self.treestore[path][1] # Detect if search string is from URL or PE/ELF link_name = link_name.split("\t") # Elf/PE (function) if len(link_name) == 1: if '0x' in link_name[0]: link_name = link_name[0] elif 'reloc.' in link_name[0]: link_name = link_name[0] else: # Just get graph for functions if not 'loc.' in link_name[0] and link_name[0][0] != '.': self.dograph = True # Adjust section name to search inside r2 flags link_name = "0x%08x" % self.uicore.core.num.get(link_name[0]) # Elf/PE (import/export) elif len( link_name ) == 2 and link_name[1] != '': link_name = "0x%08x" % int(link_name[0], 16) # Ok, now I show the popup menu ! # Create the popup menu gm = Gtk.Menu() # And the items menuitem_goto = Gtk.MenuItem("Go to") menuitem_goto.connect('activate', self.search_and_graph, link_name) gm.append(menuitem_goto) if self.dograph: menuitem_graph = Gtk.MenuItem("Show graph") menuitem_graph.connect('activate', self.textviews.update_graph, link_name) gm.append(menuitem_graph) gm.show_all() gm.popup(None, None, None, None, event.button, _time)