Package IDAscope :: Package idascope :: Package widgets :: Module FunctionInspectionWidget
[hide private]
[frames] | no frames]

Source Code for Module IDAscope.idascope.widgets.FunctionInspectionWidget

  1  #!/usr/bin/python 
  2  ######################################################################## 
  3  # Copyright (c) 2012 
  4  # Daniel Plohmann <daniel.plohmann<at>gmail<dot>com> 
  5  # Alexander Hanel <alexander.hanel<at>gmail<dot>com> 
  6  # All rights reserved. 
  7  ######################################################################## 
  8  # 
  9  #  This file is part of IDAscope 
 10  # 
 11  #  IDAscope is free software: you can redistribute it and/or modify it 
 12  #  under the terms of the GNU General Public License as published by 
 13  #  the Free Software Foundation, either version 3 of the License, or 
 14  #  (at your option) any later version. 
 15  # 
 16  #  This program is distributed in the hope that it will be useful, but 
 17  #  WITHOUT ANY WARRANTY; without even the implied warranty of 
 18  #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 19  #  General Public License for more details. 
 20  # 
 21  #  You should have received a copy of the GNU General Public License 
 22  #  along with this program.  If not, see 
 23  #  <http://www.gnu.org/licenses/>. 
 24  # 
 25  ######################################################################## 
 26   
 27  from PySide import QtGui, QtCore 
 28  from PySide.QtGui import QIcon 
 29   
 30  from NumberQTableWidgetItem import NumberQTableWidgetItem 
 31   
 32   
33 -class FunctionInspectionWidget(QtGui.QMainWindow):
34 """ 35 This widget is the front-end for the semantic inspection. 36 """ 37
38 - def __init__(self, parent):
39 QtGui.QMainWindow.__init__(self) 40 print "Loading FunctionInspectionWidget" 41 # enable access to shared IDAscope modules 42 self.parent = parent 43 self.name = "Function Inspection" 44 self.icon = QIcon(self.parent.config.icon_file_path + "semantics.png") 45 # This widget relies on the semantic identifier and uses some functions via IDA proxy 46 self.si = self.parent.semantic_identifier 47 self.dh = self.parent.documentation_helper 48 self.ida_proxy = self.parent.ida_proxy 49 # references to Qt-specific modules 50 self.QtGui = QtGui 51 self.QtCore = QtCore 52 self.NumberQTableWidgetItem = NumberQTableWidgetItem 53 self.central_widget = self.QtGui.QWidget() 54 self.setCentralWidget(self.central_widget) 55 self.createGui()
56
57 - def createGui(self):
58 """ 59 Create the main GUI with its components. 60 """ 61 self.funcs_label = QtGui.QLabel("Functions of Interest (0/0)") 62 self.calls_label = QtGui.QLabel("Selected function contains the following API references with parameters:") 63 64 self.create_toolbar() 65 66 self.create_functions_table() 67 self.create_calls_table() 68 self.create_parameter_table() 69 70 # layout and fill the widget 71 semantics_layout = QtGui.QVBoxLayout() 72 73 function_info_widget = QtGui.QWidget() 74 function_info_layout = QtGui.QHBoxLayout() 75 self.function_dummy_only_cb = QtGui.QCheckBox("Only dummy names") 76 self.function_dummy_only_cb.stateChanged.connect(self.populate_function_table) 77 function_info_layout.addWidget(self.funcs_label) 78 function_info_layout.addWidget(self.function_dummy_only_cb) 79 function_info_widget.setLayout(function_info_layout) 80 81 upper_table_widget = QtGui.QWidget() 82 upper_table_layout = QtGui.QVBoxLayout() 83 upper_table_layout.addWidget(function_info_widget) 84 upper_table_layout.addWidget(self.funcs_table) 85 upper_table_widget.setLayout(upper_table_layout) 86 87 calls_params_widget = QtGui.QWidget() 88 calls_params_layout = QtGui.QHBoxLayout() 89 calls_params_layout.addWidget(self.calls_table) 90 calls_params_layout.addWidget(self.parameter_table) 91 calls_params_widget.setLayout(calls_params_layout) 92 93 lower_tables_widget = QtGui.QWidget() 94 lower_tables_layout = QtGui.QVBoxLayout() 95 lower_tables_layout.addWidget(self.calls_label) 96 lower_tables_layout.addWidget(calls_params_widget) 97 lower_tables_widget.setLayout(lower_tables_layout) 98 99 splitter = self.QtGui.QSplitter(self.QtCore.Qt.Vertical) 100 q_clean_style = QtGui.QStyleFactory.create('Plastique') 101 splitter.setStyle(q_clean_style) 102 splitter.addWidget(upper_table_widget) 103 splitter.addWidget(lower_tables_widget) 104 semantics_layout.addWidget(splitter) 105 106 self.central_widget.setLayout(semantics_layout) 107 108 self.populate_function_table() 109 self.update_functions_label()
110
111 - def create_toolbar(self):
112 """ 113 Create the toolbar, containing some of the actions that can be performed with this widget. 114 """ 115 self.create_refresh_action() 116 self.create_rename_action() 117 self.create_coloring_action() 118 self.create_fix_unknown_code_action() 119 self.create_rename_wrappers_action() 120 121 self.toolbar = self.addToolBar('Function Inspection Toobar') 122 self.toolbar.addAction(self.refreshAction) 123 self.toolbar.addAction(self.annotateAction) 124 self.toolbar.addAction(self.toggleColorAction) 125 self.toolbar.addAction(self.fixUnknownCodeAction) 126 self.toolbar.addAction(self.renameWrappersAction)
127
128 - def create_refresh_action(self):
129 """ 130 Create the refresh action for the toolbar. On activiation, it triggers a scan of I{SemanticIdentifier} and 131 updates the GUI. 132 """ 133 self.refreshAction = QtGui.QAction(QIcon(self.parent.config.icon_file_path + "refresh.png"), "Refresh the " \ 134 + "view by scanning all named references again.", self) 135 self.refreshAction.triggered.connect(self.onRefreshButtonClicked)
136
137 - def create_rename_action(self):
138 """ 139 Create the action which performs renaming of the function names in the IDB that are covered by the scan of 140 the I{SemanticIdentifier}. 141 """ 142 self.annotateAction = QtGui.QAction(QIcon(self.parent.config.icon_file_path + "tags.png"), "Rename functions " \ 143 + "according to the identified tags", self) 144 self.annotateAction.triggered.connect(self.onRenameButtonClicked)
145
146 - def create_coloring_action(self):
147 """ 148 Create the action which cycles through the semantic code coloring modes via I{DocumentationHelper}. 149 """ 150 self.toggleColorAction = QtGui.QAction(QIcon(self.parent.config.icon_file_path + "colors.png"), \ 151 "Toggle semantic coloring", self) 152 self.toggleColorAction.triggered.connect(self.onColoringButtonClicked)
153
155 """ 156 Create the action which fixes unknown code to functions via I{DocumentationHelper}. 157 """ 158 self.fixUnknownCodeAction = QtGui.QAction(QIcon(self.parent.config.icon_file_path + "fix.png"), \ 159 "Fix unknown code to functions", self) 160 self.fixUnknownCodeAction.triggered.connect(self.onFixUnknownCodeButtonClicked)
161
163 """ 164 Create the action which fixes unknown code to functions via I{DocumentationHelper}. 165 """ 166 self.renameWrappersAction = QtGui.QAction(QIcon(self.parent.config.icon_file_path + "unwrap.png"), \ 167 "Rename potential wrapper functions", self) 168 self.renameWrappersAction.triggered.connect(self.onRenameWrappersButtonClicked)
169
170 - def create_functions_table(self):
171 """ 172 Create the top table used for showing all functions covered by scanning for semantic information. 173 """ 174 self.funcs_table = QtGui.QTableWidget() 175 self.funcs_table.clicked.connect(self.onFunctionClicked) 176 self.funcs_table.doubleClicked.connect(self.onFunctionDoubleClicked)
177
178 - def create_calls_table(self):
179 """ 180 Create the bottom left table used for showing all identified API calls that are contained in the function 181 selected in the function table. 182 """ 183 self.calls_table = QtGui.QTableWidget() 184 self.calls_table.clicked.connect(self.onCallClicked) 185 self.calls_table.doubleClicked.connect(self.onCallDoubleClicked)
186
187 - def create_parameter_table(self):
188 """ 189 Create the bottom right table used for showing all parameters for the API call selected in the calls table. 190 """ 191 self.parameter_table = QtGui.QTableWidget() 192 self.parameter_table.doubleClicked.connect(self.onParameterDoubleClicked)
193
194 - def populate_function_table(self):
195 """ 196 Populate the function table with information from the last scan of I{SemanticIdentifier}. 197 """ 198 self.funcs_table.setSortingEnabled(False) 199 self.funcs_header_labels = ["Address", "Name", "Blocks"] 200 for tag in sorted(self.si.get_tags()): 201 self.funcs_header_labels.append(tag) 202 self.funcs_table.clear() 203 self.funcs_table.setColumnCount(len(self.funcs_header_labels)) 204 self.funcs_table.setHorizontalHeaderLabels(self.funcs_header_labels) 205 # Identify number of table entries and prepare addresses to display 206 function_addresses = [] 207 if self.function_dummy_only_cb.isChecked(): 208 function_addresses = self.si.get_identified_dummy_function_addresses() 209 else: 210 function_addresses = self.si.get_identified_function_addresses() 211 if self.ida_proxy.BAD_ADDR in function_addresses: 212 self.funcs_table.setRowCount(len(function_addresses) - 1) 213 else: 214 self.funcs_table.setRowCount(len(function_addresses)) 215 self.funcs_table.resizeRowToContents(0) 216 217 for row, function_address in enumerate(function_addresses): 218 # we don't want to render entries in the table that appear because analysis failed on broken code. 219 if function_address == self.ida_proxy.BAD_ADDR: 220 continue 221 for column, column_name in enumerate(self.funcs_header_labels): 222 tmp_item = None 223 if column == 0: 224 tmp_item = self.QtGui.QTableWidgetItem("0x%x" % function_address) 225 elif column == 1: 226 tmp_item = self.QtGui.QTableWidgetItem(self.ida_proxy.GetFunctionName(function_address)) 227 elif column == 2: 228 tmp_item = self.NumberQTableWidgetItem("%d" % \ 229 self.si.get_number_of_basic_blocks_for_function_address(function_address)) 230 else: 231 tmp_item = self.NumberQTableWidgetItem("%d" % \ 232 self.si.get_tag_count_for_function_address(column_name, function_address)) 233 tmp_item.setFlags(tmp_item.flags() & ~self.QtCore.Qt.ItemIsEditable) 234 self.funcs_table.setItem(row, column, tmp_item) 235 self.funcs_table.resizeRowToContents(row) 236 self.funcs_table.setSelectionMode(self.QtGui.QAbstractItemView.SingleSelection) 237 self.funcs_table.resizeColumnsToContents() 238 self.funcs_table.setSortingEnabled(True) 239 self.update_functions_label()
240
241 - def populate_calls_table(self, function_address):
242 """ 243 Populate the calls table based on the selected function in the functions table. 244 """ 245 self.calls_table.setSortingEnabled(False) 246 self.calls_header_labels = ["Address", "API", "Tag"] 247 self.calls_table.clear() 248 self.calls_table.setColumnCount(len(self.calls_header_labels)) 249 self.calls_table.setHorizontalHeaderLabels(self.calls_header_labels) 250 251 tagged_call_contexts = self.si.get_tagged_apis_for_function_address(function_address) 252 self.calls_table.setRowCount(len(tagged_call_contexts)) 253 for row, tagged_call_ctx in enumerate(tagged_call_contexts): 254 for column, column_name in enumerate(self.calls_header_labels): 255 if column == 0: 256 tmp_item = self.QtGui.QTableWidgetItem("0x%x" % tagged_call_ctx.address_of_call) 257 elif column == 1: 258 tmp_item = self.QtGui.QTableWidgetItem(tagged_call_ctx.called_function_name) 259 elif column == 2: 260 tmp_item = self.QtGui.QTableWidgetItem(tagged_call_ctx.tag) 261 tmp_item.setFlags(tmp_item.flags() & ~self.QtCore.Qt.ItemIsEditable) 262 self.calls_table.setItem(row, column, tmp_item) 263 self.calls_table.resizeRowToContents(row) 264 self.calls_table.setSelectionMode(self.QtGui.QAbstractItemView.SingleSelection) 265 self.calls_table.resizeColumnsToContents() 266 self.calls_table.setSortingEnabled(True)
267
268 - def populate_parameter_table(self, call_address):
269 """ 270 Populate the parameter table based on the selected API call in the calls table. 271 """ 272 self.parameter_table.setSortingEnabled(False) 273 self.parameter_header_labels = ["Address", "Type", "Name", "Value"] 274 self.parameter_table.clear() 275 self.parameter_table.setColumnCount(len(self.parameter_header_labels)) 276 self.parameter_table.setHorizontalHeaderLabels(self.parameter_header_labels) 277 278 parameter_contexts = self.si.get_parameters_for_call_address(call_address) 279 self.parameter_table.setRowCount(len(parameter_contexts)) 280 for row, parameter_ctx in enumerate(parameter_contexts): 281 for column, column_name in enumerate(self.parameter_header_labels): 282 if column == 0: 283 tmp_item = self.QtGui.QTableWidgetItem(parameter_ctx.get_rendered_push_address()) 284 elif column == 1: 285 tmp_item = self.QtGui.QTableWidgetItem(parameter_ctx.parameter_type) 286 elif column == 2: 287 tmp_item = self.QtGui.QTableWidgetItem(parameter_ctx.parameter_name) 288 elif column == 3: 289 tmp_item = self.QtGui.QTableWidgetItem(parameter_ctx.get_rendered_value()) 290 tmp_item.setFlags(tmp_item.flags() & ~self.QtCore.Qt.ItemIsEditable) 291 self.parameter_table.setItem(row, column, tmp_item) 292 self.parameter_table.resizeRowToContents(row) 293 self.parameter_table.setSelectionMode(self.QtGui.QAbstractItemView.SingleSelection) 294 self.parameter_table.resizeColumnsToContents() 295 self.parameter_table.setSortingEnabled(True)
296
297 - def update_functions_label(self):
298 num_displayed_functions = 0 299 if self.function_dummy_only_cb.isChecked(): 300 num_displayed_functions = len(self.si.get_identified_dummy_function_addresses()) 301 else: 302 num_displayed_functions = len(self.si.get_identified_function_addresses()) 303 self.funcs_label.setText("Functions of Interest (%d/%d)" % 304 (num_displayed_functions, self.si.calculate_number_of_functions()))
305
306 - def onRenameButtonClicked(self):
307 """ 308 Action for renaming functions when the rename action from the toolbar is activated. 309 """ 310 self.si.rename_functions() 311 self.onRefreshButtonClicked()
312
313 - def onRefreshButtonClicked(self):
314 """ 315 Action for refreshing the window data by performing another scan of I{SemanticIdentifier}. 316 """ 317 self.si.scan() 318 self.populate_function_table()
319
320 - def onColoringButtonClicked(self):
321 """ 322 Action for performing semantic coloring of instructions. 323 """ 324 self.dh.colorize(self.si.get_last_result())
325
327 """ 328 Action for fixing unknown parts of code (red in address bar) to functions. 329 """ 330 self.dh.convert_non_function_code()
331
333 """ 334 Action for renaming potential wrapper functions to the wrapped API if they have a dummy name. 335 """ 336 self.si.rename_potential_wrapper_functions()
337
338 - def onFunctionClicked(self, mi):
339 """ 340 If a function in the functions table is clicked, the view of the calls and parameter table are updated. 341 """ 342 clicked_function_address = int(self.funcs_table.item(mi.row(), \ 343 0).text(), 16) 344 self.populate_calls_table(clicked_function_address)
345
346 - def onFunctionDoubleClicked(self, mi):
347 """ 348 If a function in the functions table is doubleclicked, IDA View is located to the corresponding address. 349 """ 350 clicked_function_address = self.funcs_table.item(mi.row(), 0).text() 351 self.ida_proxy.Jump(int(clicked_function_address, 16))
352
353 - def onCallClicked(self, mi):
354 """ 355 If an API call in the calls table is clicked, the view of the parameter table is updated. 356 """ 357 clicked_function_address = int(self.calls_table.item(mi.row(), \ 358 0).text(), 16) 359 self.populate_parameter_table(clicked_function_address)
360
361 - def onCallDoubleClicked(self, mi):
362 """ 363 If an API in the calls table is doubleclicked, IDA View is located to the corresponding address. 364 """ 365 if mi.column() == 1: 366 for widget in self.parent.idascope_widgets: 367 if widget.name == "WinAPI Browsing": 368 widget.navigate(self.calls_table.item(mi.row(), mi.column()).text()) 369 self.parent.setTabFocus("WinAPI Browsing") 370 else: 371 clicked_function_address = self.calls_table.item(mi.row(), 0).text() 372 self.ida_proxy.Jump(int(clicked_function_address, 16))
373
374 - def onParameterDoubleClicked(self, mi):
375 """ 376 If a parameter in the parameter table is doubleclicked, IDA View is located to the corresponding address. 377 """ 378 clicked_function_address = self.parameter_table.item(mi.row(), 0).text() 379 self.ida_proxy.Jump(int(clicked_function_address, 16))
380