Example #1
0
    def _create_gui(self):
        self.t_fakefast_addr = QtWidgets.QLineEdit()
        self.t_fakefast_addr.setFixedWidth(150)

        self.tbl_fakefast = TTable(
            ['fast_id', 'fast_size', 'bytes to target', 'chunk address'])
        self.tbl_fakefast.customContextMenuRequested.connect(self.context_menu)

        self.t_fakefast_info = QtWidgets.QTextEdit()
        self.t_fakefast_info.setReadOnly(True)

        self.btn_find_fakefast = QtWidgets.QPushButton("Find")
        self.btn_find_fakefast.clicked.connect(self.find_fakefast_on_click)

        hbox_fakefast = QtWidgets.QHBoxLayout()
        hbox_fakefast.addWidget(QtWidgets.QLabel('Target address'))
        hbox_fakefast.addWidget(self.t_fakefast_addr)
        hbox_fakefast.addWidget(self.btn_find_fakefast)
        hbox_fakefast.addStretch(1)

        vbox_fakefast = QtWidgets.QVBoxLayout()
        vbox_fakefast.addLayout(hbox_fakefast)
        vbox_fakefast.addWidget(self.tbl_fakefast)
        vbox_fakefast.addStretch(1)
        vbox_fakefast.setContentsMargins(0, 0, 0, 0)

        self.setLayout(vbox_fakefast)
Example #2
0
    def _create_gui(self):
        self.tbl_offsets_vars = TTable(['name', 'offset'])
        self.tbl_offsets_vars.resize_columns([150, 100])
        self.tbl_offsets_vars.cellDoubleClicked.connect(
            self.tbl_offsets_double_clicked)
        self.tbl_offsets_vars.customContextMenuRequested.connect(
            self.context_menu)

        self.tbl_offsets_funcs = TTable(['name', 'offset'])
        self.tbl_offsets_funcs.resize_columns([150, 100])
        self.tbl_offsets_funcs.cellDoubleClicked.connect(
            self.tbl_offsets_double_clicked)
        self.tbl_offsets_funcs.customContextMenuRequested.connect(
            self.context_menu)

        vbox_offsets_vars = QtWidgets.QVBoxLayout()
        vbox_offsets_vars.addWidget(QtWidgets.QLabel('Variables'))
        vbox_offsets_vars.addWidget(self.tbl_offsets_vars)

        vbox_offsets_funcs = QtWidgets.QVBoxLayout()
        vbox_offsets_funcs.addWidget(QtWidgets.QLabel('Functions'))
        vbox_offsets_funcs.addWidget(self.tbl_offsets_funcs)

        hbox_libc_offsets = QtWidgets.QHBoxLayout()
        hbox_libc_offsets.addLayout(vbox_offsets_vars)
        hbox_libc_offsets.addLayout(vbox_offsets_funcs)
        hbox_libc_offsets.setContentsMargins(0, 0, 0, 0)

        self.setLayout(hbox_libc_offsets)
Example #3
0
 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)
Example #4
0
 def _create_table(self):
     self.tbl_traced_chunks = TTable(['#','User-address', 'Action', 'Arg 1', 'Arg 2', \
         'Thread', 'Caller', 'Info'])
     self.tbl_traced_chunks.setRowCount(0)
     self.tbl_traced_chunks.resize_columns([40, 155, 80, 80, 80, 80, 200, 200])
     self.tbl_traced_chunks.customContextMenuRequested.connect(self.context_menu)
     self.tbl_traced_chunks.itemSelectionChanged.connect(self.view_selected_chunk)
     self.tbl_traced_chunks.cellDoubleClicked.connect(self.traced_double_clicked)
Example #5
0
    def _create_table(self):

        # -----------------------------------------------------------------------
        # Tcache table

        self.tbl_tcache = TTable(['#', 'size', 'counts', 'next'])
        self.tbl_tcache.resize_columns([50, 100, 50, 155])
        self.tbl_tcache.customContextMenuRequested.connect(self.context_menu)
        self.tbl_tcache.cellDoubleClicked.connect(self.show_selected_chunk)
        self.tbl_tcache.itemSelectionChanged.connect(
            self.table_on_change_index)
Example #6
0
    def _create_tables(self):
        # --- Fastbins table
        self.tbl_fastbins = TTable(['#', 'size', 'fd'])
        self.tbl_fastbins.resize_columns([50, 100, 155])
        self.tbl_fastbins.customContextMenuRequested.connect(self.context_menu)
        self.tbl_fastbins.cellDoubleClicked.connect(self.table_on_change)
        self.tbl_fastbins.itemSelectionChanged.connect(self.table_on_change)

        # --- Unsortedbin
        self.tbl_unsortedbin = TTable(['#', 'fd', 'bk', 'base'])
        self.tbl_unsortedbin.resize_columns([50, 155, 155, 155])
        self.tbl_unsortedbin.customContextMenuRequested.connect(
            self.context_menu)
        self.tbl_unsortedbin.cellDoubleClicked.connect(self.table_on_change)
        self.tbl_unsortedbin.itemClicked.connect(self.table_on_change)

        # --- Smallbins
        self.tbl_smallbins = TTable(['#', 'size', 'fd', 'bk', 'base'])
        self.tbl_smallbins.resize_columns([50, 100, 155, 155, 155])
        self.tbl_smallbins.customContextMenuRequested.connect(
            self.context_menu)
        self.tbl_smallbins.cellDoubleClicked.connect(self.table_on_change)
        self.tbl_smallbins.itemSelectionChanged.connect(self.table_on_change)

        # --- Largebins
        self.tbl_largebins = TTable(['#', 'size', 'fd', 'bk', 'base'])
        self.tbl_largebins.resize_columns([50, 100, 155, 155, 155])
        self.tbl_largebins.customContextMenuRequested.connect(
            self.context_menu)
        self.tbl_largebins.cellDoubleClicked.connect(self.table_on_change)
        self.tbl_largebins.itemSelectionChanged.connect(self.table_on_change)

        self.bin_tables = {
            self.tbl_fastbins: {
                'title': 'fastbins',
                'address': 2,
                'size': 1,
                'base': None
            },
            self.tbl_unsortedbin: {
                'title': 'unsortedbin',
                'address': 1,
                'size': None,
                'base': 3
            },
            self.tbl_smallbins: {
                'title': 'smallbins',
                'address': 2,
                'size': 1,
                'base': 4
            },
            self.tbl_largebins: {
                'title': 'largebins',
                'address': 2,
                'size': 1,
                'base': 4
            }
        }
Example #7
0
class FakefastWidget(CustomWidget):
    def __init__(self, parent=None):
        super(FakefastWidget, self).__init__(parent)
        self._create_gui()

    def _create_gui(self):
        self.t_fakefast_addr = QtWidgets.QLineEdit()
        self.t_fakefast_addr.setFixedWidth(150)

        self.tbl_fakefast = TTable(
            ['fast_id', 'fast_size', 'bytes to target', 'chunk address'])
        self.tbl_fakefast.customContextMenuRequested.connect(self.context_menu)

        self.t_fakefast_info = QtWidgets.QTextEdit()
        self.t_fakefast_info.setReadOnly(True)

        self.btn_find_fakefast = QtWidgets.QPushButton("Find")
        self.btn_find_fakefast.clicked.connect(self.find_fakefast_on_click)

        hbox_fakefast = QtWidgets.QHBoxLayout()
        hbox_fakefast.addWidget(QtWidgets.QLabel('Target address'))
        hbox_fakefast.addWidget(self.t_fakefast_addr)
        hbox_fakefast.addWidget(self.btn_find_fakefast)
        hbox_fakefast.addStretch(1)

        vbox_fakefast = QtWidgets.QVBoxLayout()
        vbox_fakefast.addLayout(hbox_fakefast)
        vbox_fakefast.addWidget(self.tbl_fakefast)
        vbox_fakefast.addStretch(1)
        vbox_fakefast.setContentsMargins(0, 0, 0, 0)

        self.setLayout(vbox_fakefast)

    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")

        chunk_addr = int(sender.item(sender.currentRow(), 3).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 == view_chunk:
            self.parent.parent.show_chunk_info(chunk_addr)

    def find_fakefast_on_click(self):
        start_addr = int(self.t_fakefast_addr.text(), 16)
        fake_chunks = self.heap.find_fakefast(start_addr)

        if len(fake_chunks) == 0:
            idaapi.info("Fakefast: 0 results")
            return

        self.tbl_fakefast.clearContents()
        self.tbl_fakefast.setRowCount(0)
        self.tbl_fakefast.setSortingEnabled(False)

        for idx, chunk in enumerate(fake_chunks):
            self.tbl_fakefast.insertRow(idx)
            self.tbl_fakefast.setItem(
                idx, 0, QtWidgets.QTableWidgetItem("%d" % chunk['fast_id']))
            self.tbl_fakefast.setItem(
                idx, 1, QtWidgets.QTableWidgetItem("0x%x" % chunk['size']))
            self.tbl_fakefast.setItem(
                idx, 2, QtWidgets.QTableWidgetItem("%d" % chunk['bytes_to']))
            self.tbl_fakefast.setItem(
                idx, 3, QtWidgets.QTableWidgetItem("0x%x" % chunk['address']))

        self.tbl_fakefast.resizeRowsToContents()
        self.tbl_fakefast.resizeColumnsToContents()
        self.tbl_fakefast.setSortingEnabled(True)
Example #8
0
class LibcOffsetsWidget(CustomWidget):
    def __init__(self, parent=None):
        super(LibcOffsetsWidget, self).__init__(parent)
        self.libc_base = None
        self._create_gui()
        self.populate_table()

    def _create_gui(self):
        self.tbl_offsets_vars = TTable(['name', 'offset'])
        self.tbl_offsets_vars.resize_columns([150, 100])
        self.tbl_offsets_vars.cellDoubleClicked.connect(
            self.tbl_offsets_double_clicked)
        self.tbl_offsets_vars.customContextMenuRequested.connect(
            self.context_menu)

        self.tbl_offsets_funcs = TTable(['name', 'offset'])
        self.tbl_offsets_funcs.resize_columns([150, 100])
        self.tbl_offsets_funcs.cellDoubleClicked.connect(
            self.tbl_offsets_double_clicked)
        self.tbl_offsets_funcs.customContextMenuRequested.connect(
            self.context_menu)

        vbox_offsets_vars = QtWidgets.QVBoxLayout()
        vbox_offsets_vars.addWidget(QtWidgets.QLabel('Variables'))
        vbox_offsets_vars.addWidget(self.tbl_offsets_vars)

        vbox_offsets_funcs = QtWidgets.QVBoxLayout()
        vbox_offsets_funcs.addWidget(QtWidgets.QLabel('Functions'))
        vbox_offsets_funcs.addWidget(self.tbl_offsets_funcs)

        hbox_libc_offsets = QtWidgets.QHBoxLayout()
        hbox_libc_offsets.addLayout(vbox_offsets_vars)
        hbox_libc_offsets.addLayout(vbox_offsets_funcs)
        hbox_libc_offsets.setContentsMargins(0, 0, 0, 0)

        self.setLayout(hbox_libc_offsets)

    def context_menu(self, position):
        sender = self.sender()

        menu = QtWidgets.QMenu()
        action_copy = menu.addAction("Copy value")
        action_copy_row = menu.addAction("Copy row")
        action_jump_to = menu.addAction("Jump to address")

        action = menu.exec_(sender.mapToGlobal(position))

        if action == action_copy:
            sender.copy_selected_value()

        elif action == action_copy_row:
            sender.copy_selected_row()

        elif action == action_jump_to:

            offset = int(sender.item(sender.currentRow(), 1).text(), 16)

            address = self.libc_base + offset
            idc.jumpto(address)

    def tbl_offsets_double_clicked(self):
        sender = self.sender()
        offset = int(sender.item(sender.currentRow(), 1).text(), 16)
        address = self.libc_base + offset
        idc.jumpto(address)

    def populate_table(self):
        self.tbl_offsets_vars.clearContents()
        self.tbl_offsets_funcs.clearContents()

        self.tbl_offsets_vars.setRowCount(0)
        self.tbl_offsets_funcs.setRowCount(0)

        self.tbl_offsets_vars.setSortingEnabled(False)
        self.tbl_offsets_funcs.setSortingEnabled(False)

        offsets = self.get_libc_offsets()

        variables = offsets['variables']
        functions = offsets['functions']

        for idx, (name, offset) in enumerate(variables.items()):
            self.tbl_offsets_vars.insertRow(idx)
            self.tbl_offsets_vars.setItem(idx, 0,
                                          QtWidgets.QTableWidgetItem(name))
            self.tbl_offsets_vars.setItem(
                idx, 1, QtWidgets.QTableWidgetItem("0x%x" % offset))

        for idx, (name, offset) in enumerate(functions.items()):
            self.tbl_offsets_funcs.insertRow(idx)
            self.tbl_offsets_funcs.setItem(idx, 0,
                                           QtWidgets.QTableWidgetItem(name))
            self.tbl_offsets_funcs.setItem(
                idx, 1, QtWidgets.QTableWidgetItem("0x%x" % offset))

        self.tbl_offsets_vars.resizeRowsToContents()
        self.tbl_offsets_funcs.resizeRowsToContents()

        self.tbl_offsets_vars.setSortingEnabled(True)
        self.tbl_offsets_funcs.setSortingEnabled(True)

    def get_libc_offsets(self):
        libc_symbols = {
            'variables': [
                'environ',
                '__environ',
                '__free_hook',
                '__malloc_hook',
                '__realloc_hook',
                '_IO_list_all',
                '_IO_2_1_stdin_',
                '_IO_2_1_stdout_',
                '_IO_2_1_stderr_',
            ],
            'functions': [
                'system',
                '__libc_system',
                'execve',
                'open',
                '__open64',
                'read',
                'write',
                '__write',
                '_IO_gets',
                'gets',
                'setcontext+0x35',
            ]
        }
        result = {
            'variables': OrderedDict(),
            'functions': OrderedDict(),
        }

        self.libc_base = get_libc_base()
        if not self.libc_base:
            return result

        libc_names = get_libc_names()
        if not libc_names:
            idaapi.warning("Unable to get glibc symbols")
            return result

        for s_type, symbols in libc_symbols.items():
            for sym in symbols:

                name_expr = parse_name_expr(sym)
                if not name_expr:
                    continue

                name, offset = name_expr
                addr = libc_names.get(name)

                if addr:
                    addr += offset
                    offset = addr - self.libc_base
                    result[s_type][sym] = offset
        return result
Example #9
0
class TcacheWidget(CustomWidget):
    def __init__(self, parent=None):
        super(TcacheWidget, self).__init__(parent)
        self._create_gui()

    def _create_gui(self):
        self._create_table()

        self.te_tcache_chain = QtWidgets.QTextEdit()
        self.te_tcache_chain.setFixedHeight(100)
        self.te_tcache_chain.setReadOnly(True)

        vbox_tcache_chain = QtWidgets.QVBoxLayout()
        vbox_tcache_chain.addWidget(QtWidgets.QLabel('Chain info'))
        vbox_tcache_chain.addWidget(self.te_tcache_chain)

        vbox_tcache = QtWidgets.QVBoxLayout()
        vbox_tcache.addWidget(QtWidgets.QLabel('Tcache entries'))
        vbox_tcache.addWidget(self.tbl_tcache)
        vbox_tcache.addLayout(vbox_tcache_chain)
        vbox_tcache.addStretch(1)

        self.setLayout(vbox_tcache)

    def _create_table(self):

        # -----------------------------------------------------------------------
        # Tcache table

        self.tbl_tcache = TTable(['#', 'size', 'counts', 'next'])
        self.tbl_tcache.resize_columns([50, 100, 50, 155])
        self.tbl_tcache.customContextMenuRequested.connect(self.context_menu)
        self.tbl_tcache.cellDoubleClicked.connect(self.show_selected_chunk)
        self.tbl_tcache.itemSelectionChanged.connect(
            self.table_on_change_index)

    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")
        graphview_action = menu.addAction("GraphView")

        action = menu.exec_(sender.mapToGlobal(position))
        fd_addr = int(sender.item(sender.currentRow(), 3).text(), 16)

        if action == copy_action:
            sender.copy_selected_value()

        if action == copy_row:
            sender.copy_selected_row()

        elif action == jump_to:
            idc.Jump(fd_addr)

        elif action == view_chunk:
            self.show_selected_chunk()

        elif action == graphview_action:
            idx = int(sender.item(sender.currentRow(), 0).text())
            size = int(sender.item(sender.currentRow(), 1).text(), 16)
            fd = int(sender.item(sender.currentRow(), 3).text(), 16)

            graph = BinGraph(self,
                             info={
                                 'type': 'tcache',
                                 'bin_id': idx,
                                 'size': size,
                             })
            graph.Show()

    def populate_table(self):
        self.tbl_tcache.clearContents()
        self.tbl_tcache.setRowCount(0)
        self.tbl_tcache.setSortingEnabled(False)

        tcache = self.heap.get_tcache(self.cur_arena)

        if not tcache:
            return

        idx = 0
        for i, (size, entry) in enumerate(tcache.iteritems()):

            if entry['counts'] == 0 and entry['next'] == 0:
                continue

            self.tbl_tcache.insertRow(idx)

            it_entry_id = QtWidgets.QTableWidgetItem("%d" % i)
            it_size = QtWidgets.QTableWidgetItem("%#x" % size)
            it_counts = QtWidgets.QTableWidgetItem("%d" % entry['counts'])
            it_address = QtWidgets.QTableWidgetItem("%#x" % entry['next'])

            self.tbl_tcache.setItem(idx, 0, it_entry_id)
            self.tbl_tcache.setItem(idx, 1, it_size)
            self.tbl_tcache.setItem(idx, 2, it_counts)
            self.tbl_tcache.setItem(idx, 3, it_address)

            idx += 1

        self.tbl_tcache.resizeRowsToContents()
        self.tbl_tcache.resizeColumnsToContents()
        self.tbl_tcache.setSortingEnabled(True)

    def table_on_change_index(self):
        sender = self.sender()
        items = sender.selectedItems()

        if items and len(items):
            entry_addr = int(items[3].text(), 16)
            entry_size = int(items[1].text(), 16)
            self.show_chain(entry_addr, entry_size)

    def show_selected_chunk(self):
        items = self.sender().selectedItems()
        entry_addr = int(items[3].text(), 16)
        norm_address = "0x%x-0x%x" % (entry_addr, config.ptr_size * 2)
        self.parent.show_chunk_info(norm_address)

    def show_chain(self, address, size):
        title = 'Tcache[0x%02x]' % size
        chain, b_error = self.heap.tcache_chain(address)
        html_chain = make_html_chain(title, chain, b_error)
        self.te_tcache_chain.clear()
        self.te_tcache_chain.insertHtml(html_chain)
Example #10
0
class BinsWidget(CustomWidget):
    def __init__(self, parent=None):
        super(BinsWidget, self).__init__(parent)
        self.show_bases = False
        self._create_gui()

    def _create_gui(self):
        self._create_tables()
        vbox_fastbins = QtWidgets.QVBoxLayout()
        vbox_fastbins.addWidget(QtWidgets.QLabel("Fastbins"))
        vbox_fastbins.addWidget(self.tbl_fastbins)

        vbox_unsortedbin = QtWidgets.QVBoxLayout()
        vbox_unsortedbin.addWidget(QtWidgets.QLabel("Unsorted"))
        vbox_unsortedbin.addWidget(self.tbl_unsortedbin)

        vbox_smallbins = QtWidgets.QVBoxLayout()
        vbox_smallbins.addWidget(QtWidgets.QLabel("Small bins"))
        vbox_smallbins.addWidget(self.tbl_smallbins)

        vbox_largebins = QtWidgets.QVBoxLayout()
        vbox_largebins.addWidget(QtWidgets.QLabel("Large bins"))
        vbox_largebins.addWidget(self.tbl_largebins)

        self.te_chain_info = QtWidgets.QTextEdit()
        self.te_chain_info.setReadOnly(True)

        vbox_chain = QtWidgets.QVBoxLayout()
        vbox_chain.addWidget(QtWidgets.QLabel("Chain info"))
        vbox_chain.addWidget(self.te_chain_info)

        grid_bins = QtWidgets.QGridLayout()
        grid_bins.addLayout(vbox_fastbins, 0, 0)
        grid_bins.addLayout(vbox_unsortedbin, 0, 1)
        grid_bins.addLayout(vbox_smallbins, 1, 0)
        grid_bins.addLayout(vbox_largebins, 1, 1)
        grid_bins.addLayout(vbox_chain, 2, 0, 2, 2)
        self.setLayout(grid_bins)

    def _create_tables(self):
        # --- Fastbins table
        self.tbl_fastbins = TTable(['#', 'size', 'fd'])
        self.tbl_fastbins.resize_columns([50, 100, 155])
        self.tbl_fastbins.customContextMenuRequested.connect(self.context_menu)
        self.tbl_fastbins.cellDoubleClicked.connect(self.table_on_change)
        self.tbl_fastbins.itemSelectionChanged.connect(self.table_on_change)

        # --- Unsortedbin
        self.tbl_unsortedbin = TTable(['#', 'fd', 'bk', 'base'])
        self.tbl_unsortedbin.resize_columns([50, 155, 155, 155])
        self.tbl_unsortedbin.customContextMenuRequested.connect(
            self.context_menu)
        self.tbl_unsortedbin.cellDoubleClicked.connect(self.table_on_change)
        self.tbl_unsortedbin.itemClicked.connect(self.table_on_change)

        # --- Smallbins
        self.tbl_smallbins = TTable(['#', 'size', 'fd', 'bk', 'base'])
        self.tbl_smallbins.resize_columns([50, 100, 155, 155, 155])
        self.tbl_smallbins.customContextMenuRequested.connect(
            self.context_menu)
        self.tbl_smallbins.cellDoubleClicked.connect(self.table_on_change)
        self.tbl_smallbins.itemSelectionChanged.connect(self.table_on_change)

        # --- Largebins
        self.tbl_largebins = TTable(['#', 'size', 'fd', 'bk', 'base'])
        self.tbl_largebins.resize_columns([50, 100, 155, 155, 155])
        self.tbl_largebins.customContextMenuRequested.connect(
            self.context_menu)
        self.tbl_largebins.cellDoubleClicked.connect(self.table_on_change)
        self.tbl_largebins.itemSelectionChanged.connect(self.table_on_change)

        self.bin_tables = {
            self.tbl_fastbins: {
                'title': 'fastbins',
                'address': 2,
                'size': 1,
                'base': None
            },
            self.tbl_unsortedbin: {
                'title': 'unsortedbin',
                'address': 1,
                'size': None,
                'base': 3
            },
            self.tbl_smallbins: {
                'title': 'smallbins',
                'address': 2,
                'size': 1,
                'base': 4
            },
            self.tbl_largebins: {
                'title': 'largebins',
                'address': 2,
                'size': 1,
                'base': 4
            }
        }

    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")
        graphview_action = menu.addAction("GraphView")
        self.show_uninit = menu.addAction("Show uninitialized bins (bases)")
        self.show_uninit.setCheckable(True)
        self.show_uninit.setChecked(self.show_bases)

        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:
            self.jmp_to_selected_chunk()

        elif action == view_chunk:
            self.show_selected_chunk()

        elif action == self.show_uninit:
            self.show_bases = self.show_uninit.isChecked()
            self.populate_tables()

        elif action == graphview_action:

            if sender is self.tbl_fastbins:
                idx = int(sender.item(sender.currentRow(), 0).text())
                size = int(sender.item(sender.currentRow(), 1).text(), 16)

                graph = BinGraph(self,
                                 info={
                                     'type': 'fastbin',
                                     'fastbin_id': idx,
                                     'size': size
                                 })
                graph.Show()

            elif sender is self.tbl_unsortedbin:
                idx = sender.item(sender.currentRow(), 0).text()
                base = int(sender.item(sender.currentRow(), 3).text(), 16)

                graph = BinGraph(self,
                                 info={
                                     'type': 'unsortedbin',
                                     'bin_id': idx,
                                     'bin_base': base
                                 })
                graph.Show()

            elif sender is self.tbl_smallbins:
                idx = sender.item(sender.currentRow(), 0).text()
                size = int(sender.item(sender.currentRow(), 1).text(), 16)
                base = int(sender.item(sender.currentRow(), 4).text(), 16)

                graph = BinGraph(self,
                                 info={
                                     'type': 'smallbin',
                                     'bin_id': idx,
                                     'size': size,
                                     'bin_base': base
                                 })
                graph.Show()

            elif sender is self.tbl_largebins:
                idx = sender.item(sender.currentRow(), 0).text()
                size = int(sender.item(sender.currentRow(), 1).text(), 16)
                base = int(sender.item(sender.currentRow(), 4).text(), 16)

                graph = BinGraph(self,
                                 info={
                                     'type': 'largebin',
                                     'bin_id': idx,
                                     'size': size,
                                     'bin_base': base
                                 })
                graph.Show()

    def show_bin_chain(self):
        sender = self.sender()
        stop = 0
        size = None

        row = sender.selectedItems()

        if not len(row):
            return

        bin_cols = self.bin_tables[sender]
        address = int(row[bin_cols['address']].text(), 16)

        if bin_cols['size']:
            size = int(row[bin_cols['size']].text(), 16)

        if bin_cols['base']:
            stop = int(row[bin_cols['base']].text(), 16)

        self.show_chain(bin_cols['title'], address, size, stop)

    def show_selected_chunk(self):
        chunk_addr = self.get_selected_chunk_addr()
        if chunk_addr:
            self.parent.show_chunk_info(chunk_addr)

    def jmp_to_selected_chunk(self):
        chunk_addr = self.get_selected_chunk_addr()
        if chunk_addr:
            idc.Jump(chunk_addr)

    def get_selected_chunk_addr(self):
        sender = self.sender()
        items = sender.selectedItems()
        if len(items):
            col_id = self.bin_tables[sender]['address']
            address = int(items[col_id].text(), 16)
            return address
        return None

    def table_on_change(self):
        self.show_selected_chunk()
        self.show_bin_chain()

    def show_chain(self, title, address, size, stop=0):
        if size:
            title = '%s[0x%02x]' % (title, size)

        if address != 0 and address != idc.BADADDR:
            chain, b_error = self.heap.chunk_chain(address, stop)
            html_chain = make_html_chain(title, chain, b_error)
            self.te_chain_info.clear()
            self.te_chain_info.insertHtml(html_chain)

    def populate_tbl_fastbins(self):
        self.tbl_fastbins.clearContents()
        self.tbl_fastbins.setRowCount(0)
        self.tbl_fastbins.setSortingEnabled(False)

        idx = 0
        fastbins = self.heap.get_fastbins(self.cur_arena)
        for id_fast, (size, fast_chunk) in enumerate(fastbins.iteritems()):

            if not self.show_bases and fast_chunk == 0:
                continue

            self.tbl_fastbins.insertRow(idx)

            it_bin_num = QtWidgets.QTableWidgetItem("%d" % id_fast)
            it_size = QtWidgets.QTableWidgetItem("%#x" % size)
            it_fastchunk = QtWidgets.QTableWidgetItem("%#x" % fast_chunk)

            self.tbl_fastbins.setItem(idx, 0, it_bin_num)
            self.tbl_fastbins.setItem(idx, 1, it_size)
            self.tbl_fastbins.setItem(idx, 2, it_fastchunk)

            idx += 1

        if idx:
            self.tbl_fastbins.resize_to_contents()
            self.tbl_fastbins.setSortingEnabled(True)

    def populate_tbl_unsortedbin(self):
        base, fd, bk = self.heap.get_unsortedbin(self.cur_arena)

        self.tbl_unsortedbin.clearContents()
        self.tbl_unsortedbin.setRowCount(0)

        # points to current base
        if not self.show_bases and fd == base:
            return

        self.tbl_unsortedbin.setSortingEnabled(False)
        self.tbl_unsortedbin.insertRow(0)

        it_bin_num = QtWidgets.QTableWidgetItem("1")
        it_fd = QtWidgets.QTableWidgetItem("%#x" % fd)
        it_bk = QtWidgets.QTableWidgetItem("%#x" % bk)
        it_base = QtWidgets.QTableWidgetItem("%#x" % base)

        self.tbl_unsortedbin.setItem(0, 0, it_bin_num)
        self.tbl_unsortedbin.setItem(0, 1, it_fd)
        self.tbl_unsortedbin.setItem(0, 2, it_bk)
        self.tbl_unsortedbin.setItem(0, 3, it_base)

        self.tbl_unsortedbin.resizeRowsToContents()
        self.tbl_unsortedbin.resizeColumnsToContents()
        self.tbl_unsortedbin.setSortingEnabled(True)

    def populate_tbl_smallbins(self):
        self.tbl_smallbins.clearContents()
        self.tbl_smallbins.setRowCount(0)
        self.tbl_smallbins.setSortingEnabled(False)

        smallbins = self.heap.get_smallbins(self.cur_arena)

        idx = 0
        for bin_id, smallbin in smallbins.iteritems():

            # point to himself
            if not self.show_bases and smallbin['base'] == smallbin['fd']:
                continue

            self.tbl_smallbins.insertRow(idx)
            it_bin_num = QtWidgets.QTableWidgetItem("%d" % bin_id)
            it_size = QtWidgets.QTableWidgetItem("%#x" % smallbin['size'])
            it_fd = QtWidgets.QTableWidgetItem("%#x" % smallbin['fd'])
            it_bk = QtWidgets.QTableWidgetItem("%#x" % smallbin['bk'])
            it_base = QtWidgets.QTableWidgetItem("%#x" % smallbin['base'])

            self.tbl_smallbins.setItem(idx, 0, it_bin_num)
            self.tbl_smallbins.setItem(idx, 1, it_size)
            self.tbl_smallbins.setItem(idx, 2, it_fd)
            self.tbl_smallbins.setItem(idx, 3, it_bk)
            self.tbl_smallbins.setItem(idx, 4, it_base)

            idx += 1

        self.tbl_smallbins.resizeRowsToContents()
        self.tbl_smallbins.resizeColumnsToContents()
        self.tbl_smallbins.setSortingEnabled(True)

    def populate_tbl_largebins(self):
        self.tbl_largebins.clearContents()
        self.tbl_largebins.setRowCount(0)
        self.tbl_largebins.setSortingEnabled(False)

        largebins = self.heap.get_largebins(self.cur_arena)

        idx = 0
        for bin_id, largebin in largebins.iteritems():

            # point to himself
            if not self.show_bases and largebin['base'] == largebin['fd']:
                continue

            self.tbl_largebins.insertRow(idx)
            it_bin_num = QtWidgets.QTableWidgetItem("%d" % bin_id)
            it_size = QtWidgets.QTableWidgetItem("%#x" % largebin['size'])
            it_fd = QtWidgets.QTableWidgetItem("%#x" % largebin['fd'])
            it_bk = QtWidgets.QTableWidgetItem("%#x" % largebin['bk'])
            it_base = QtWidgets.QTableWidgetItem("%#x" % largebin['base'])

            self.tbl_largebins.setItem(idx, 0, it_bin_num)
            self.tbl_largebins.setItem(idx, 1, it_size)
            self.tbl_largebins.setItem(idx, 2, it_fd)
            self.tbl_largebins.setItem(idx, 3, it_bk)
            self.tbl_largebins.setItem(idx, 4, it_base)

            idx += 1

        self.tbl_largebins.resizeRowsToContents()
        self.tbl_largebins.resizeColumnsToContents()
        self.tbl_largebins.setSortingEnabled(True)

    def populate_tables(self):
        self.populate_tbl_fastbins()
        self.populate_tbl_unsortedbin()
        self.populate_tbl_smallbins()
        self.populate_tbl_largebins()
Example #11
0
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()
Example #12
0
class TracerWidget(CustomWidget):
    def __init__(self, parent=None):
        super(TracerWidget, self).__init__(parent)
        self.tracer = None
        self.free_chunks = {}
        self.allocated_chunks = {}
        self.row_info = {}
        self._create_gui()

    def _create_gui(self):
        self._create_table()
        self._create_menu()

    def _create_table(self):
        self.tbl_traced_chunks = TTable(['#','User-address', 'Action', 'Arg 1', 'Arg 2', \
            'Thread', 'Caller', 'Info'])
        self.tbl_traced_chunks.setRowCount(0)
        self.tbl_traced_chunks.resize_columns(
            [40, 155, 80, 80, 80, 80, 200, 200])
        self.tbl_traced_chunks.customContextMenuRequested.connect(
            self.context_menu)
        self.tbl_traced_chunks.itemSelectionChanged.connect(
            self.view_selected_chunk)
        self.tbl_traced_chunks.cellDoubleClicked.connect(
            self.traced_double_clicked)

    def _create_menu(self):
        cb_enable_trace = QtWidgets.QCheckBox()
        cb_enable_trace.stateChanged.connect(self.cb_tracing_changed)
        self.cb_stop_during_tracing = QtWidgets.QCheckBox()

        btn_dump_trace = QtWidgets.QPushButton("Dump trace")
        btn_dump_trace.clicked.connect(self.btn_dump_trace_on_click)

        btn_villoc_trace = QtWidgets.QPushButton("Villoc")
        btn_villoc_trace.clicked.connect(self.btn_villoc_on_click)

        btn_clear_trace = QtWidgets.QPushButton("Clear")
        btn_clear_trace.clicked.connect(self.btn_clear_on_click)

        hbox_enable_trace = QtWidgets.QHBoxLayout()
        hbox_enable_trace.addWidget(QtWidgets.QLabel("Enable tracing"))
        hbox_enable_trace.addWidget(cb_enable_trace)
        hbox_enable_trace.addWidget(QtWidgets.QLabel("Stop during tracing"))
        hbox_enable_trace.addWidget(self.cb_stop_during_tracing)
        hbox_enable_trace.addWidget(btn_dump_trace)
        hbox_enable_trace.addWidget(btn_villoc_trace)
        hbox_enable_trace.addWidget(btn_clear_trace)
        hbox_enable_trace.addStretch(1)

        hbox_trace = QtWidgets.QVBoxLayout()
        hbox_trace.addLayout(hbox_enable_trace)
        hbox_trace.addWidget(QtWidgets.QLabel("Traced chunks"))
        hbox_trace.addWidget(self.tbl_traced_chunks)

        self.cb_stop_during_tracing.setChecked(config.stop_during_tracing)
        cb_enable_trace.setChecked(config.start_tracing_at_startup)

        self.setLayout(hbox_trace)

    def context_menu(self, position):
        sender = self.sender()
        menu = QtWidgets.QMenu()

        show_warn_info = None

        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")
        goto_caller = menu.addAction("Jump to caller")

        current_row = self.sender().currentRow()
        if current_row in self.row_info:
            show_warn_info = menu.addAction("Show warning info")

        chunk_addr = int(sender.item(sender.currentRow(), 1).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.Jump(chunk_addr - (config.ptr_size * 2))

        elif action == jump_to_u:
            idc.Jump(chunk_addr)

        elif action == goto_caller:
            caller_str = str(sender.item(sender.currentRow(), 6).text())
            if caller_str:
                idc.Jump(str2ea(caller_str))

        elif action == view_chunk:
            self.view_selected_chunk()

        elif show_warn_info and action == show_warn_info:
            self.traced_double_clicked()

    def enable_tracing(self):
        stop_during_tracing = not self.cb_stop_during_tracing.isChecked()
        self.tracer = HeapTracer(self.append_chunk, stop_during_tracing)
        self.tracer.hook()
        self.parent.tracer = self.tracer
        self.free_chunks = {}
        self.allocated_chunks = {}
        log("Tracer enabled")

    def disable_tracing(self):
        if self.tracer:
            self.tracer.remove_bps()
            self.tracer.unhook()
            self.parent.tracer = None
        log("Trace disabled")

    def cb_tracing_changed(self, state):
        if state == QtCore.Qt.Checked:
            self.cb_stop_during_tracing.setEnabled(False)
            self.enable_tracing()
        else:
            self.cb_stop_during_tracing.setEnabled(True)
            self.disable_tracing()

    def get_value(self, row, name):
        cols = {
            'address': 1,
            'action': 2,
            'arg1': 3,
            'arg2': 4,
            'thread': 5,
            'caller': 6,
            'info': 7
        }
        return self.tbl_traced_chunks.item(row, cols[name]).text()

    def dump_table_for_villoc(self):
        result = ''
        for i in range(self.tbl_traced_chunks.rowCount()):
            action = self.get_value(i, "action")
            info = self.get_value(i, "info")

            if info != "N/A":
                result += "@villoc(%s) = <void>\n" % info

            if action == "malloc":
                arg1 = self.get_value(i, "arg1")
                ret = self.get_value(i, "address")
                result += "malloc(%d) = " % int(arg1, 16)
                result += ret if ret.startswith("0x") else '<error>'

            elif action == "free":
                address = self.get_value(i, "address")
                result += "free(%s) = <void>" % address

            elif action == "realloc":
                address = self.get_value(i, "address")
                arg1 = self.get_value(i, "arg1")
                arg2 = self.get_value(i, "arg2")
                result += "realloc(%s, %d) = %s" % (arg1, int(arg2,
                                                              16), address)

            elif action == "calloc":
                address = self.get_value(i, "address")
                arg1 = self.get_value(i, "arg1")
                arg2 = self.get_value(i, "arg2")
                result += "calloc(%d, %d) = %s" % (int(arg1, 16), int(
                    arg2, 16), address)

            result += '\n'
        return result

    def btn_villoc_on_click(self):
        if self.tbl_traced_chunks.rowCount() == 0:
            idaapi.warning("Empty table")
            return

        try:
            villoc.Block.header = config.ptr_size * 2
            villoc.Block.round = self.parent.heap.malloc_alignment
            villoc.Block.minsz = self.parent.heap.min_chunk_size

            result = self.dump_table_for_villoc()
            html = villoc.build_html(result)

            h, filename = tempfile.mkstemp(suffix='.html')

            with open(filename, 'wb') as f:
                f.write(html)

            url = QtCore.QUrl.fromLocalFile(filename)
            QtGui.QDesktopServices.openUrl(url)

        except Exception as e:
            idaapi.warning(traceback.format_exc())

    def btn_dump_trace_on_click(self):
        if self.tbl_traced_chunks.rowCount() == 0:
            idaapi.warning("Empty table")
            return

        filename = AskFile(1, "*.csv",
                           "Select the file to store tracing results")
        if not filename:
            return
        try:
            result = self.tbl_traced_chunks.dump_table_as_csv()
            with open(filename, 'wb') as f:
                f.write(result)

        except Exception as e:
            idaapi.warning(traceback.format_exc())

    def btn_clear_on_click(self):
        self.tbl_traced_chunks.clear_table()

    def view_selected_chunk(self):
        items = self.tbl_traced_chunks.selectedItems()
        if len(items) > 0:
            chunk_addr = int(items[1].text(), 16)
            norm_address = "0x%x-0x%x" % (chunk_addr, config.ptr_size * 2)
            self.parent.show_chunk_info(norm_address)

    def traced_double_clicked(self):
        current_row = self.sender().currentRow()
        if current_row in self.row_info:
            idaapi.info(self.row_info[current_row])

    def update_allocated_chunks(self):
        for start, info in self.allocated_chunks.iteritems():
            try:
                chunk = self.heap.get_chunk(start)
                if chunk.norm_size != info['size']:
                    info['size'] = chunk.norm_size
                    info['end'] = start + chunk.norm_size
            except:
                continue

    def malloc_consolidate(self):
        fastbins = self.heap.get_all_fastbins_chunks(self.cur_arena)
        if len(fastbins) == 0:
            return False

        self.free_chunks = {}
        for addr in fastbins:
            chunk = self.get_chunk(chunk)
            self.free_chunks[addr] = {
                'size': chunk.norm_size,
                'end': addr + chunk.norm_size
            }
        return True

    def append_chunk(self,
                     addr,
                     action,
                     arg1,
                     arg2,
                     thread_id,
                     caller,
                     from_ret=True):
        warn_msg = None
        row_msg = None
        row_color = None

        warn_types = {
            'overlap': ['Overlap detected',
                        QtGui.QColor(255, 204, 0)],
            'double_free':
            ['Double free detected',
             QtGui.QColor(255, 153, 102)]
        }

        if addr == 0:
            return

        if action == "free" and from_ret:
            self.parent.reload_gui_info()
            return

        if config.detect_double_frees_and_overlaps:

            if action == "malloc":
                chunk_addr = self.heap.mem2chunk(addr)
                chunk_size = self.heap.get_chunk(chunk_addr).norm_size
                chunk_end = chunk_addr + chunk_size

                chunk = {'size': chunk_size, 'end': chunk_end}
                overlap = check_overlap(chunk_addr, chunk_size,
                                        self.allocated_chunks)
                if overlap is not None:

                    overlap_size = self.allocated_chunks[overlap]['size']
                    row_msg = "<h3>Heap overlap detected</h3>"
                    row_msg += "<ul><li>malloc(<b>%d</b>) = <b>%#x</b> (<b>%#x</b>) of size %#x</li>\n" % \
                         (arg1, addr, chunk_addr, chunk_size)
                    row_msg += "<li><b>%#x</b> overlaps in-used chunk (<b>%#x</b>) " % (
                        chunk_addr, overlap)
                    row_msg += "of size <b>%#x</b></li></ul>\n" % overlap_size

                    warn_msg, row_color = warn_types['overlap']
                    del self.allocated_chunks[overlap]

                self.allocated_chunks[chunk_addr] = chunk

                if chunk_addr in self.free_chunks:
                    free_chunk = self.free_chunks[chunk_addr]

                    del self.free_chunks[chunk_addr]

                    if chunk['size'] != free_chunk['size']:
                        split_addr = chunk_addr + chunk_size
                        split_size = free_chunk['size'] - chunk['size']
                        split_end = split_addr + split_size

                        self.free_chunks[split_addr] = {
                            'size': split_size,
                            'end': split_end
                        }

                if arg1 >= self.heap.min_large_size:
                    """ If this is a large request, consolidate fastbins """
                    self.malloc_consolidate()

            elif action == "free":

                chunk_addr = self.heap.mem2chunk(addr)
                chunk = self.heap.get_chunk(chunk_addr)
                chunk_size = chunk.norm_size
                prev_inuse = chunk.prev_inuse
                chunk_end = chunk_addr + chunk_size

                overlap = check_overlap(chunk_addr, chunk_size,
                                        self.free_chunks)
                if overlap is not None:

                    row_msg = "<h3>Double free detected</h3>"
                    row_msg += "<ul><li>free (<b>%#x</b>) of size <b>%#x</b></li>" % \
                        (chunk_addr, chunk_size)
                    row_msg += "<li>Double free: <b>%#x</b> of size <b>%#x</b></li></ul>\n" % \
                         (overlap, self.free_chunks[overlap]['size'])

                    warn_msg, row_color = warn_types['double_free']
                    del self.free_chunks[overlap]

                in_tcache = False
                tc_idx = self.heap.csize2tidx(chunk_size)

                if self.heap.tcache_enabled and tc_idx < TCACHE_BINS:

                    tcache = self.heap.get_tcache_struct(self.cur_arena)
                    tc_idx = self.heap.csize2tidx(chunk_size)

                    if tcache.counts[tc_idx] < TCACHE_COUNT:
                        self.free_chunks[chunk_addr] = {
                            'size': chunk_size,
                            'end': chunk_end
                        }
                        in_tcache = True

                        if chunk_addr in self.allocated_chunks:
                            del self.allocated_chunks[chunk_addr]

                if not in_tcache:

                    # fastbins
                    if chunk_size <= config.ptr_size * 16:

                        self.free_chunks[chunk_addr] = {
                            'size': chunk_size,
                            'end': chunk_end
                        }

                        if chunk_addr in self.allocated_chunks:
                            del self.allocated_chunks[chunk_addr]

                    # > global_max_fast
                    else:
                        if prev_inuse == 0:
                            prev_size = chunk.prev_size
                            prev_addr = chunk_addr - prev_size

                            if prev_addr in self.free_chunks:
                                prev_size += chunk_size
                                del self.free_chunks[prev_addr]

                        next_chunk = {}
                        av = self.heap.get_arena(self.cur_arena)
                        next_addr = chunk_addr + chunk_size

                        if next_addr == av.top:
                            if chunk_addr in self.allocated_chunks:
                                del self.allocated_chunks[chunk_addr]

                        else:
                            next_chunk = self.heap.get_chunk(next_addr)
                            next_size = next_chunk.norm_size
                            next_inuse = self.heap.get_chunk(
                                next_addr + next_size).prev_inuse

                            if next_inuse == 0:

                                if prev_inuse:
                                    if next_addr in self.free_chunks:
                                        chunk_size += next_size
                                        del self.free_chunks[next_addr]
                                else:
                                    if next_addr in self.free_chunks:
                                        prev_size += next_size
                                        del self.free_chunks[next_addr]

                            # prev freed
                            if prev_inuse == 0:
                                if chunk_addr in self.allocated_chunks:
                                    del self.allocated_chunks[chunk_addr]

                                chunk_addr = prev_addr
                                chunk_size = prev_size
                                chunk_end = prev_addr + prev_size

                            self.free_chunks[chunk_addr] = {
                                'size': chunk_size,
                                'end': chunk_end
                            }
                            if chunk_addr in self.allocated_chunks:
                                del self.allocated_chunks[chunk_addr]

                            if chunk_size > 0x10000:
                                self.malloc_consolidate()

            elif action == "realloc":
                self.update_allocated_chunks()

        num_rows = self.tbl_traced_chunks.rowCount()
        if row_msg is not None:
            self.row_info[num_rows] = row_msg

        self.tbl_traced_chunks.setSortingEnabled(False)
        self.tbl_traced_chunks.insertRow(num_rows)

        caller_name = get_func_name_offset(caller) if caller else ''

        arg1 = "%#x" % arg1 if arg1 is not None else 'N/A'
        arg2 = "%#x" % arg2 if arg2 is not None else 'N/A'
        warn_msg_s = warn_msg if warn_msg else 'N/A'

        it_count = QtWidgets.QTableWidgetItem("%d" % num_rows)
        it_chunk = QtWidgets.QTableWidgetItem("%#x" % addr)
        it_action = QtWidgets.QTableWidgetItem(action)
        it_arg1 = QtWidgets.QTableWidgetItem(arg1)
        it_arg2 = QtWidgets.QTableWidgetItem(arg2)
        it_thread = QtWidgets.QTableWidgetItem("%d" % thread_id)
        it_caller = QtWidgets.QTableWidgetItem(caller_name)
        it_info = QtWidgets.QTableWidgetItem(warn_msg_s)

        self.tbl_traced_chunks.setItem(num_rows, 0, it_count)
        self.tbl_traced_chunks.setItem(num_rows, 1, it_chunk)
        self.tbl_traced_chunks.setItem(num_rows, 2, it_action)
        self.tbl_traced_chunks.setItem(num_rows, 3, it_arg1)
        self.tbl_traced_chunks.setItem(num_rows, 4, it_arg2)
        self.tbl_traced_chunks.setItem(num_rows, 5, it_thread)
        self.tbl_traced_chunks.setItem(num_rows, 6, it_caller)
        self.tbl_traced_chunks.setItem(num_rows, 7, it_info)

        self.tbl_traced_chunks.resizeRowsToContents()
        self.tbl_traced_chunks.resizeColumnsToContents()
        self.tbl_traced_chunks.setSortingEnabled(True)

        if warn_msg:
            self.tbl_traced_chunks.set_row_color(num_rows, row_color)

        # only reload when the function returns
        if from_ret:
            self.parent.reload_gui_info()