#!/usr/bin/python # coding: utf-8 # # HeapViewer - by @danigargu # import idc from PyQt5 import QtGui, QtCore, QtWidgets from PyQt5.QtCore import Qt from heap_viewer.widgets.custom import CustomWidget, TTable, InfoDialog from heap_viewer.misc import * from heap_viewer import config from heap_viewer import ptmalloc # ----------------------------------------------------------------------- class ArenaWidget(CustomWidget): def __init__(self, parent=None): super(ArenaWidget, self).__init__(parent) self._create_gui() def _create_gui(self): self._create_table() self._create_menu() def _create_menu(self): self.t_top_addr = QtWidgets.QLineEdit() self.t_top_addr.setFixedWidth(130) self.t_top_addr.setReadOnly(True) self.t_last_remainder = QtWidgets.QLineEdit() self.t_last_remainder.setFixedWidth(150) self.t_last_remainder.setReadOnly(True) self.lbl_top_warning = QtWidgets.QLabel() self.lbl_top_warning.setStyleSheet('color: red') self.lbl_top_warning.setVisible(False) self.t_attached_threads = QtWidgets.QLineEdit() self.t_attached_threads.setFixedWidth(130) self.t_attached_threads.setReadOnly(True) hbox_arena_top = QtWidgets.QHBoxLayout() hbox_arena_top.addWidget(QtWidgets.QLabel('Top:')) hbox_arena_top.addWidget(self.t_top_addr) hbox_arena_top.addWidget(QtWidgets.QLabel('Last remainder:')) hbox_arena_top.addWidget(self.t_last_remainder) btn_malloc_par = QtWidgets.QPushButton("Struct") btn_malloc_par.clicked.connect(self.btn_struct_on_click) hbox_arena_top.addWidget(btn_malloc_par) hbox_arena_top.addStretch(1) hbox_arena_others = QtWidgets.QHBoxLayout() hbox_arena_others.addWidget(self.lbl_top_warning) self.bold_font = QtGui.QFont() self.bold_font.setBold(True) grid_arenas = QtWidgets.QGridLayout() grid_arenas.addLayout(hbox_arena_top, 0, 0) grid_arenas.addLayout(hbox_arena_others, 1, 0) grid_arenas.addWidget(self.tbl_parsed_heap, 2, 0) self.setLayout(grid_arenas) def _create_table(self): self.tbl_parsed_heap = TTable(['address','prev','size','status','fd','bk']) self.tbl_parsed_heap.resize_columns([155, 40, 100, 120, 155, 155]) self.tbl_parsed_heap.customContextMenuRequested.connect(self.context_menu) self.tbl_parsed_heap.itemSelectionChanged.connect(self.view_selected_chunk) def context_menu(self, position): sender = self.sender() menu = QtWidgets.QMenu() copy_action = menu.addAction("Copy value") copy_row = menu.addAction("Copy row") view_chunk = menu.addAction("View chunk") jump_to = menu.addAction("Jump to chunk") jump_to_u = menu.addAction("Jump to user-data") check_freaable = menu.addAction("Check freeable") chunk_addr = int(sender.item(sender.currentRow(), 0).text(), 16) action = menu.exec_(sender.mapToGlobal(position)) if action == copy_action: sender.copy_selected_value() if action == copy_row: sender.copy_selected_row() elif action == jump_to: idc.jumpto(chunk_addr) elif action == jump_to_u: idc.jumpto(chunk_addr + (config.ptr_size*2)) elif action == view_chunk: self.view_selected_chunk() elif action == check_freaable: self.parent.check_freeable(chunk_addr) def view_selected_chunk(self): items = self.tbl_parsed_heap.selectedItems() if len(items) > 0: chunk_addr = int(items[0].text(), 16) self.parent.show_chunk_info(chunk_addr) def populate_table(self): cur_arena = self.cur_arena arena = self.heap.get_arena(cur_arena) self.t_top_addr.setText("%#x" % arena.top) self.t_last_remainder.setText("%#x" % arena.last_remainder) self.t_attached_threads.setText("%d" % arena.attached_threads) top_segname = idc.get_segm_name(arena.top) if not any(s in top_segname for s in ['heap','debug']): self.lbl_top_warning.setVisible(True) self.lbl_top_warning.setText("Top points to '%s' segment" % top_segname) else: self.lbl_top_warning.setVisible(False) self.tbl_parsed_heap.clearContents() self.tbl_parsed_heap.setRowCount(0) self.tbl_parsed_heap.setSortingEnabled(False) parsed_heap = self.heap.parse_heap(cur_arena) for idx, chunk in enumerate(parsed_heap): self.tbl_parsed_heap.insertRow(idx) it_address = QtWidgets.QTableWidgetItem("%#x" % chunk['address']) it_prev = QtWidgets.QTableWidgetItem("%#x" % chunk['prev']) it_size = QtWidgets.QTableWidgetItem("%#x" % chunk['size']) it_status = QtWidgets.QTableWidgetItem("%s" % chunk['status']) it_fd = QtWidgets.QTableWidgetItem("%#x" % chunk['fd']) it_bk = QtWidgets.QTableWidgetItem("%#x" % chunk['bk']) if 'Freed' in chunk['status']: it_status.setForeground(QtGui.QColor('red')) elif chunk['status'] == 'Corrupt': it_status.setForeground(QtGui.QColor.fromRgb(213, 94, 0)) it_status.setFont(self.bold_font) else: it_status.setForeground(QtGui.QColor('blue')) self.tbl_parsed_heap.setItem(idx, 0, it_address) self.tbl_parsed_heap.setItem(idx, 1, it_prev) self.tbl_parsed_heap.setItem(idx, 2, it_size) self.tbl_parsed_heap.setItem(idx, 3, it_status) self.tbl_parsed_heap.setItem(idx, 4, it_fd) self.tbl_parsed_heap.setItem(idx, 5, it_bk) self.tbl_parsed_heap.resizeRowsToContents() self.tbl_parsed_heap.resizeColumnsToContents() self.tbl_parsed_heap.setSortingEnabled(True) self.tbl_parsed_heap.sortByColumn(0, QtCore.Qt.DescendingOrder) def btn_struct_on_click(self): self.parent.show_malloc_state() # -----------------------------------------------------------------------