#include #include "GlibcHeapBinsDialog.h" #include "ui_GlibcHeapBinsDialog.h" #include "GlibcHeapInfoDialog.h" GlibcHeapBinsDialog::GlibcHeapBinsDialog(RVA m_state, MainWindow *main, QWidget *parent) : QDialog(parent), ui(new Ui::GlibcHeapBinsDialog), m_state(m_state), binsModel(new BinsModel(m_state, this)), main(main) { ui->setupUi(this); ui->viewBins->setModel(binsModel); ui->viewBins->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); ui->viewBins->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); ui->viewBins->verticalHeader()->hide(); ui->viewBins->resizeColumnsToContents(); connect(ui->viewBins->selectionModel(), &QItemSelectionModel::currentChanged, this, &GlibcHeapBinsDialog::onCurrentChanged); connect(ui->lineEdit, &QLineEdit::returnPressed, this, &GlibcHeapBinsDialog::showHeapInfoDialog); binsModel->reload(); ui->viewBins->resizeColumnsToContents(); graphView = nullptr; this->setWindowTitle(tr("Bins info for arena @ ") + RzAddressString(m_state)); } GlibcHeapBinsDialog::~GlibcHeapBinsDialog() { delete ui; } void GlibcHeapBinsDialog::onCurrentChanged(const QModelIndex ¤t, const QModelIndex &prev) { Q_UNUSED(current); Q_UNUSED(prev); auto currentIndex = ui->viewBins->selectionModel()->currentIndex(); setChainInfo(currentIndex.row()); setGraphView(currentIndex.row()); } void GlibcHeapBinsDialog::setChainInfo(int index) { // get chunks for the selected bin and construct chain info string RzListIter *iter; RzHeapChunkListItem *item; RzList *chunks = binsModel->getChunks(index); QString chainInfo; CutterRzListForeach(chunks, iter, RzHeapChunkListItem, item) { chainInfo += " → " + RzAddressString(item->addr); } // Add bin message at the end of the list // responsible for messages like corrupted list, double free QString message = binsModel->getBinMessage(index); if (!message.isEmpty()) { chainInfo += " " + message; } ui->chainInfoEdit->setPlainText(chainInfo); } void GlibcHeapBinsDialog::showHeapInfoDialog() { QString str = ui->lineEdit->text(); if (!str.isEmpty()) { // summon glibcHeapInfoDialog box with the offset entered RVA offset = Core()->math(str); if (!offset) { ui->lineEdit->setText(QString()); return; } GlibcHeapInfoDialog dialog(offset, QString(), this); dialog.exec(); } } void GlibcHeapBinsDialog::setGraphView(int index) { if (graphView) { ui->horizontalLayout->removeWidget(graphView); delete graphView; } graphView = new HeapBinsGraphView(this, binsModel->values[index], main); ui->horizontalLayout->addWidget(graphView); graphView->refreshView(); } BinsModel::BinsModel(RVA arena_addr, QObject *parent) : QAbstractTableModel(parent), arena_addr(arena_addr) { } void BinsModel::reload() { beginResetModel(); clearData(); values = Core()->getHeapBins(arena_addr); endResetModel(); } int BinsModel::rowCount(const QModelIndex &) const { return values.size(); } int BinsModel::columnCount(const QModelIndex &) const { return ColumnCount; } QVariant BinsModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= values.count()) return QVariant(); const auto &item = values.at(index.row()); switch (role) { case Qt::DisplayRole: switch (index.column()) { case BinNumColumn: return item->bin_num; case FdColumn: return (item->fd == 0) ? tr("N/A") : RzAddressString(item->fd); case BkColumn: return (item->bk == 0) ? tr("N/A") : RzAddressString(item->bk); case TypeColumn: return tr(item->type); case CountColumn: return rz_list_length(item->chunks); case SizeColumn: return (item->size == 0) ? tr("N/A") : RzHexString(item->size); default: return QVariant(); } default: return QVariant(); } } QVariant BinsModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation); switch (role) { case Qt::DisplayRole: switch (section) { case BinNumColumn: return tr("#"); case FdColumn: return tr("Fd"); case BkColumn: return tr("Bk"); case TypeColumn: return tr("Type"); case CountColumn: return tr("Chunks count"); case SizeColumn: return tr("Chunks size"); default: return QVariant(); } case Qt::ToolTipRole: switch (section) { case BinNumColumn: return tr("Bin number in NBINS or fastbinsY array"); case FdColumn: return tr("Pointer to first chunk of the bin"); case BkColumn: return tr("Pointer to last chunk of the bin"); case TypeColumn: return tr("Type of bin"); case CountColumn: return tr("Number of chunks in the bin"); case SizeColumn: return tr("Size of all chunks in the bin"); default: return QVariant(); } default: return QVariant(); } } void BinsModel::clearData() { for (auto item : values) { rz_heap_bin_free_64(item); } } RzList *BinsModel::getChunks(int index) { return values[index]->chunks; } QString BinsModel::getBinMessage(int index) { if (values[index]->message) { return QString(values[index]->message); } else { return QString(); } }