mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-18 10:56:11 +00:00
Initial heap dock widget with glibc support (#2705)
This commit is contained in:
parent
04894b4757
commit
771fa6102c
2
rizin
2
rizin
@ -1 +1 @@
|
||||
Subproject commit e60f06775ebffc7e91903d0b21ad8fee1c86c1ac
|
||||
Subproject commit 92ad8bef001843df5882e71c23761987cfa3d9f4
|
@ -141,6 +141,9 @@ set(SOURCES
|
||||
widgets/AddressableDockWidget.cpp
|
||||
dialogs/preferences/AnalOptionsWidget.cpp
|
||||
common/DecompilerHighlighter.cpp
|
||||
dialogs/GlibcHeapInfoDialog.cpp
|
||||
widgets/HeapDockWidget.cpp
|
||||
widgets/GlibcHeapWidget.cpp
|
||||
)
|
||||
set(HEADER_FILES
|
||||
core/Cutter.h
|
||||
@ -294,6 +297,9 @@ set(HEADER_FILES
|
||||
widgets/AddressableDockWidget.h
|
||||
dialogs/preferences/AnalOptionsWidget.h
|
||||
common/DecompilerHighlighter.h
|
||||
dialogs/GlibcHeapInfoDialog.h
|
||||
widgets/HeapDockWidget.h
|
||||
widgets/GlibcHeapWidget.h
|
||||
)
|
||||
set(UI_FILES
|
||||
dialogs/AboutDialog.ui
|
||||
@ -360,6 +366,9 @@ set(UI_FILES
|
||||
dialogs/LayoutManager.ui
|
||||
widgets/RizinGraphWidget.ui
|
||||
dialogs/preferences/AnalOptionsWidget.ui
|
||||
dialogs/GlibcHeapInfoDialog.ui
|
||||
widgets/HeapDockWidget.ui
|
||||
widgets/GlibcHeapWidget.ui
|
||||
)
|
||||
set(QRC_FILES
|
||||
resources.qrc
|
||||
|
@ -1556,6 +1556,69 @@ QJsonDocument CutterCore::getProcessThreads(int pid)
|
||||
}
|
||||
}
|
||||
|
||||
QVector<Chunk> CutterCore::getHeapChunks(RVA arena_addr)
|
||||
{
|
||||
CORE_LOCK();
|
||||
QVector<Chunk> chunks_vector;
|
||||
ut64 m_arena;
|
||||
|
||||
if (!arena_addr) {
|
||||
// if arena_addr is zero get base address of main arena
|
||||
RzList *arenas = rz_heap_arenas_list(core);
|
||||
if (arenas->length == 0) {
|
||||
rz_list_free(arenas);
|
||||
return chunks_vector;
|
||||
}
|
||||
m_arena = ((RzArenaListItem *)arenas->head->data)->addr;
|
||||
rz_list_free(arenas);
|
||||
} else {
|
||||
m_arena = arena_addr;
|
||||
}
|
||||
|
||||
// Get chunks using api and store them in a chunks_vector
|
||||
RzList *chunks = rz_heap_chunks_list(core, m_arena);
|
||||
RzListIter *iter;
|
||||
RzHeapChunkListItem *data;
|
||||
CutterRListForeach(chunks, iter, RzHeapChunkListItem, data)
|
||||
{
|
||||
Chunk chunk;
|
||||
chunk.offset = data->addr;
|
||||
chunk.size = (int)data->size;
|
||||
chunk.status = QString(data->status);
|
||||
chunks_vector.append(chunk);
|
||||
}
|
||||
|
||||
rz_list_free(chunks);
|
||||
return chunks_vector;
|
||||
}
|
||||
|
||||
QVector<Arena> CutterCore::getArenas()
|
||||
{
|
||||
CORE_LOCK();
|
||||
QVector<Arena> arena_vector;
|
||||
|
||||
// get arenas using API and store them in arena_vector
|
||||
RzList *arenas = rz_heap_arenas_list(core);
|
||||
RzListIter *iter;
|
||||
RzArenaListItem *data;
|
||||
CutterRListForeach(arenas, iter, RzArenaListItem, data)
|
||||
{
|
||||
Arena arena;
|
||||
arena.offset = data->addr;
|
||||
arena.type = QString(data->type);
|
||||
arena_vector.append(arena);
|
||||
}
|
||||
|
||||
rz_list_free(arenas);
|
||||
return arena_vector;
|
||||
}
|
||||
|
||||
RzHeapChunkSimple *CutterCore::getHeapChunk(ut64 addr)
|
||||
{
|
||||
CORE_LOCK();
|
||||
return rz_heap_chunk(core, addr);
|
||||
}
|
||||
|
||||
QJsonDocument CutterCore::getChildProcesses(int pid)
|
||||
{
|
||||
// Return the currently debugged process and it's children
|
||||
@ -1803,6 +1866,7 @@ void CutterCore::attachRemote(const QString &uri)
|
||||
emit toggleDebugView();
|
||||
}
|
||||
|
||||
currentlyRemoteDebugging = true;
|
||||
emit codeRebased();
|
||||
emit attachedRemote(true);
|
||||
emit debugTaskStateChanged();
|
||||
@ -1873,6 +1937,7 @@ void CutterCore::stopDebug()
|
||||
|
||||
currentlyDebugging = false;
|
||||
currentlyTracing = false;
|
||||
currentlyRemoteDebugging = false;
|
||||
emit debugTaskStateChanged();
|
||||
|
||||
if (currentlyEmulating) {
|
||||
|
@ -397,6 +397,28 @@ public:
|
||||
*/
|
||||
QJsonDocument getChildProcesses(int pid);
|
||||
QJsonDocument getBacktrace();
|
||||
/**
|
||||
* @brief Get a list of heap chunks
|
||||
* Uses RZ_API rz_heap_chunks_list to get vector of chunks
|
||||
* If arena_addr is zero return the chunks for main arena
|
||||
* @param arena_addr base address for the arena
|
||||
* @return Vector of heap chunks for the given arena
|
||||
*/
|
||||
QVector<Chunk> getHeapChunks(RVA arena_addr);
|
||||
|
||||
/**
|
||||
* @brief Get a list of heap arenas
|
||||
* Uses RZ_API rz_heap_arenas_list to get list of arenas
|
||||
* @return Vector of arenas
|
||||
*/
|
||||
QVector<Arena> getArenas();
|
||||
|
||||
/**
|
||||
* @brief Get detailed information about a heap chunk
|
||||
* Uses RZ_API rz_heap_chunk
|
||||
* @return RzHeapChunkSimple struct pointer for the heap chunk
|
||||
*/
|
||||
RzHeapChunkSimple *getHeapChunk(ut64 addr);
|
||||
void startDebug();
|
||||
void startEmulation();
|
||||
/**
|
||||
@ -456,6 +478,7 @@ public:
|
||||
bool currentlyDebugging = false;
|
||||
bool currentlyEmulating = false;
|
||||
bool currentlyTracing = false;
|
||||
bool currentlyRemoteDebugging = false;
|
||||
int currentlyAttachedToPID = -1;
|
||||
QString currentlyOpenFile;
|
||||
|
||||
|
@ -364,6 +364,19 @@ struct RegisterRefValueDescription
|
||||
QString ref;
|
||||
};
|
||||
|
||||
struct Chunk
|
||||
{
|
||||
RVA offset;
|
||||
QString status;
|
||||
int size;
|
||||
};
|
||||
|
||||
struct Arena
|
||||
{
|
||||
RVA offset;
|
||||
QString type;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(FunctionDescription)
|
||||
Q_DECLARE_METATYPE(ImportDescription)
|
||||
Q_DECLARE_METATYPE(ExportDescription)
|
||||
|
@ -71,6 +71,7 @@
|
||||
#include "widgets/HexWidget.h"
|
||||
#include "widgets/RizinGraphWidget.h"
|
||||
#include "widgets/CallGraph.h"
|
||||
#include "widgets/HeapDockWidget.h"
|
||||
|
||||
// Qt Headers
|
||||
#include <QActionGroup>
|
||||
@ -374,12 +375,15 @@ void MainWindow::initDocks()
|
||||
commentsDock = new CommentsWidget(this);
|
||||
stringsDock = new StringsWidget(this);
|
||||
|
||||
QList<CutterDockWidget *> debugDocks = {
|
||||
stackDock = new StackWidget(this), threadsDock = new ThreadsWidget(this),
|
||||
processesDock = new ProcessesWidget(this), backtraceDock = new BacktraceWidget(this),
|
||||
registersDock = new RegistersWidget(this), memoryMapDock = new MemoryMapWidget(this),
|
||||
breakpointDock = new BreakpointWidget(this), registerRefsDock = new RegisterRefsWidget(this)
|
||||
};
|
||||
QList<CutterDockWidget *> debugDocks = { stackDock = new StackWidget(this),
|
||||
threadsDock = new ThreadsWidget(this),
|
||||
processesDock = new ProcessesWidget(this),
|
||||
backtraceDock = new BacktraceWidget(this),
|
||||
registersDock = new RegistersWidget(this),
|
||||
memoryMapDock = new MemoryMapWidget(this),
|
||||
breakpointDock = new BreakpointWidget(this),
|
||||
registerRefsDock = new RegisterRefsWidget(this),
|
||||
heapDock = new HeapDockWidget(this) };
|
||||
|
||||
QList<CutterDockWidget *> infoDocks = {
|
||||
classesDock = new ClassesWidget(this),
|
||||
@ -709,7 +713,8 @@ RzProjectErr MainWindow::saveProjectAs(bool *canceled)
|
||||
QFileDialog fileDialog(this);
|
||||
// Append 'rzdb' suffix if it does not exist
|
||||
fileDialog.setDefaultSuffix("rzdb");
|
||||
QString file = fileDialog.getSaveFileName(this, tr("Save Project"), projectFile, PROJECT_FILE_FILTER);
|
||||
QString file =
|
||||
fileDialog.getSaveFileName(this, tr("Save Project"), projectFile, PROJECT_FILE_FILTER);
|
||||
if (file.isEmpty()) {
|
||||
if (canceled) {
|
||||
*canceled = true;
|
||||
@ -918,6 +923,7 @@ void MainWindow::restoreDocks()
|
||||
tabifyDockWidget(stackDock, backtraceDock);
|
||||
tabifyDockWidget(backtraceDock, threadsDock);
|
||||
tabifyDockWidget(threadsDock, processesDock);
|
||||
tabifyDockWidget(processesDock, heapDock);
|
||||
|
||||
for (auto dock : pluginDocks) {
|
||||
dockOnMainArea(dock);
|
||||
@ -928,7 +934,7 @@ bool MainWindow::isDebugWidget(QDockWidget *dock) const
|
||||
{
|
||||
return dock == stackDock || dock == registersDock || dock == backtraceDock
|
||||
|| dock == threadsDock || dock == memoryMapDock || dock == breakpointDock
|
||||
|| dock == processesDock || dock == registerRefsDock;
|
||||
|| dock == processesDock || dock == registerRefsDock || dock == heapDock;
|
||||
}
|
||||
|
||||
bool MainWindow::isExtraMemoryWidget(QDockWidget *dock) const
|
||||
@ -1264,9 +1270,13 @@ void MainWindow::showZenDocks()
|
||||
|
||||
void MainWindow::showDebugDocks()
|
||||
{
|
||||
const QList<QDockWidget *> debugDocks = { functionsDock, stringsDock, searchDock,
|
||||
stackDock, registersDock, backtraceDock,
|
||||
threadsDock, memoryMapDock, breakpointDock };
|
||||
QList<QDockWidget *> debugDocks = {
|
||||
functionsDock, stringsDock, searchDock, stackDock, registersDock,
|
||||
backtraceDock, threadsDock, memoryMapDock, breakpointDock,
|
||||
};
|
||||
if (QSysInfo::kernelType() == "linux" || Core()->currentlyRemoteDebugging) {
|
||||
debugDocks.append(heapDock);
|
||||
}
|
||||
functionDockWidthToRestore = functionsDock->maximumWidth();
|
||||
functionsDock->setMaximumWidth(200);
|
||||
auto registerWidth = qhelpers::forceWidth(registersDock, std::min(500, this->width() / 4));
|
||||
|
@ -54,6 +54,7 @@ class DecompilerWidget;
|
||||
class OverviewWidget;
|
||||
class RizinGraphWidget;
|
||||
class CallGraphWidget;
|
||||
class HeapWidget;
|
||||
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
@ -259,6 +260,7 @@ private:
|
||||
RizinGraphWidget *rzGraphDock = nullptr;
|
||||
CallGraphWidget *callGraphDock = nullptr;
|
||||
CallGraphWidget *globalCallGraphDock = nullptr;
|
||||
CutterDockWidget *heapDock = nullptr;
|
||||
|
||||
QMenu *disassemblyContextMenuExtensions = nullptr;
|
||||
QMenu *addressableContextMenuExtensions = nullptr;
|
||||
|
53
src/dialogs/GlibcHeapInfoDialog.cpp
Normal file
53
src/dialogs/GlibcHeapInfoDialog.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include "GlibcHeapInfoDialog.h"
|
||||
|
||||
#include <utility>
|
||||
#include "ui_GlibcHeapInfoDialog.h"
|
||||
|
||||
GlibcHeapInfoDialog::GlibcHeapInfoDialog(RVA offset, QString status, QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui::GlibcHeapInfoDialog), offset(offset), status(std::move(status))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// disable all the radio buttons for flag field so they are not user editable
|
||||
this->ui->rbIM->setEnabled(false);
|
||||
this->ui->rbNMA->setEnabled(false);
|
||||
this->ui->rbPI->setEnabled(false);
|
||||
|
||||
this->setWindowTitle(QString("Chunk @ ") + RAddressString(offset)
|
||||
+ QString("(" + this->status + ")"));
|
||||
updateFields();
|
||||
}
|
||||
|
||||
GlibcHeapInfoDialog::~GlibcHeapInfoDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void GlibcHeapInfoDialog::updateFields()
|
||||
{
|
||||
// get data about the heap chunk from the API
|
||||
RzHeapChunkSimple *chunk = Core()->getHeapChunk(offset);
|
||||
if (!chunk) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the information in the widget
|
||||
this->ui->baseEdit->setText(RAddressString(offset));
|
||||
this->ui->sizeEdit->setText(RHexString(chunk->size));
|
||||
this->ui->bkEdit->setText(RAddressString(chunk->bk));
|
||||
this->ui->fdEdit->setText(RAddressString(chunk->fd));
|
||||
this->ui->bknsEdit->setText(RAddressString(chunk->bk_nextsize));
|
||||
this->ui->fdnsEdit->setText(RAddressString(chunk->fd_nextsize));
|
||||
this->ui->prevSizeEdit->setText(RHexString(chunk->prev_size));
|
||||
if (chunk->is_mmapped) {
|
||||
this->ui->rbIM->setChecked(true);
|
||||
}
|
||||
if (chunk->prev_inuse) {
|
||||
this->ui->rbPI->setChecked(true);
|
||||
}
|
||||
if (chunk->non_main_arena) {
|
||||
this->ui->rbNMA->setChecked(true);
|
||||
}
|
||||
|
||||
free(chunk);
|
||||
}
|
26
src/dialogs/GlibcHeapInfoDialog.h
Normal file
26
src/dialogs/GlibcHeapInfoDialog.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef HEAPINFODIALOG_H
|
||||
#define HEAPINFODIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "core/Cutter.h"
|
||||
|
||||
namespace Ui {
|
||||
class GlibcHeapInfoDialog;
|
||||
}
|
||||
|
||||
class GlibcHeapInfoDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GlibcHeapInfoDialog(RVA offset, QString status, QWidget *parent = nullptr);
|
||||
~GlibcHeapInfoDialog();
|
||||
|
||||
private:
|
||||
Ui::GlibcHeapInfoDialog *ui;
|
||||
void updateFields();
|
||||
RVA offset;
|
||||
QString status;
|
||||
};
|
||||
|
||||
#endif // HEAPINFODIALOG_H
|
196
src/dialogs/GlibcHeapInfoDialog.ui
Normal file
196
src/dialogs/GlibcHeapInfoDialog.ui
Normal file
@ -0,0 +1,196 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>GlibcHeapInfoDialog</class>
|
||||
<widget class="QDialog" name="GlibcHeapInfoDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>453</width>
|
||||
<height>263</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Base</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="baseEdit">
|
||||
<property name="toolTip">
|
||||
<string>Base address of the chunk</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="sizeEdit">
|
||||
<property name="toolTip">
|
||||
<string>Size of the heap chunk including metadata</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Fd</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="fdEdit">
|
||||
<property name="toolTip">
|
||||
<string>Link to next free chunk in bin's linked list</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Bk</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="bkEdit">
|
||||
<property name="toolTip">
|
||||
<string>Link to previous free chunk in bin's linked list</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLineEdit" name="fdnsEdit">
|
||||
<property name="toolTip">
|
||||
<string>Link to next larger free chunk (only for large chunks)</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>Fd-nextsize</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Bk-nextsize</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QLineEdit" name="bknsEdit">
|
||||
<property name="toolTip">
|
||||
<string>Link to next smaller free chunk (for large chunks)</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="prevSizeEdit">
|
||||
<property name="toolTip">
|
||||
<string>Size of previous chunk (if free)</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>PrevSize</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbNMA">
|
||||
<property name="toolTip">
|
||||
<string>If the chunk was obtained from a non-main arena</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>NON_MAIN_ARENA</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoExclusive">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbIM">
|
||||
<property name="toolTip">
|
||||
<string>The chunk was obtained with mmap()</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>IS_MMAPED</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoExclusive">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="rbPI">
|
||||
<property name="toolTip">
|
||||
<string>Previous adjacent chunk is in use</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>PREV_INUSE</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoExclusive">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
204
src/widgets/GlibcHeapWidget.cpp
Normal file
204
src/widgets/GlibcHeapWidget.cpp
Normal file
@ -0,0 +1,204 @@
|
||||
#include "GlibcHeapWidget.h"
|
||||
#include "ui_GlibcHeapWidget.h"
|
||||
#include "core/MainWindow.h"
|
||||
#include "QHeaderView"
|
||||
#include "dialogs/GlibcHeapInfoDialog.h"
|
||||
|
||||
GlibcHeapWidget::GlibcHeapWidget(MainWindow *main, QWidget *parent)
|
||||
: QWidget(parent),
|
||||
ui(new Ui::GlibcHeapWidget),
|
||||
addressableItemContextMenu(this, main),
|
||||
main(main)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
viewHeap->setFont(Config()->getFont());
|
||||
viewHeap->setModel(modelHeap);
|
||||
viewHeap->verticalHeader()->hide();
|
||||
// change the scroll mode to ScrollPerPixel
|
||||
viewHeap->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
viewHeap->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
ui->verticalLayout->addWidget(viewHeap);
|
||||
ui->verticalLayout->addWidget(arenaSelectorView);
|
||||
chunkInfoAction = new QAction(tr("Detailed Chunk Info"), this);
|
||||
viewHeap->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
connect(Core(), &CutterCore::refreshAll, this, &GlibcHeapWidget::updateContents);
|
||||
connect(Core(), &CutterCore::debugTaskStateChanged, this, &GlibcHeapWidget::updateContents);
|
||||
connect(viewHeap, &QAbstractItemView::doubleClicked, this, &GlibcHeapWidget::onDoubleClicked);
|
||||
connect<void (QComboBox::*)(int)>(arenaSelectorView, &QComboBox::currentIndexChanged, this,
|
||||
&GlibcHeapWidget::onArenaSelected);
|
||||
connect(viewHeap, &QWidget::customContextMenuRequested, this,
|
||||
&GlibcHeapWidget::customMenuRequested);
|
||||
connect(viewHeap->selectionModel(), &QItemSelectionModel::currentChanged, this,
|
||||
&GlibcHeapWidget::onCurrentChanged);
|
||||
connect(chunkInfoAction, &QAction::triggered, this, &GlibcHeapWidget::viewChunkInfo);
|
||||
|
||||
addressableItemContextMenu.addAction(chunkInfoAction);
|
||||
addActions(addressableItemContextMenu.actions());
|
||||
|
||||
refreshDeferrer = dynamic_cast<CutterDockWidget *>(parent)->createRefreshDeferrer(
|
||||
[this]() { updateContents(); });
|
||||
}
|
||||
|
||||
GlibcHeapWidget::~GlibcHeapWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
GlibcHeapModel::GlibcHeapModel(QObject *parent) : QAbstractTableModel(parent) {}
|
||||
|
||||
void GlibcHeapWidget::updateArenas()
|
||||
{
|
||||
arenas = Core()->getArenas();
|
||||
|
||||
// store the currently selected arena's index
|
||||
int currentIndex = arenaSelectorView->currentIndex();
|
||||
arenaSelectorView->clear();
|
||||
|
||||
// add the new arenas to the arena selector
|
||||
for (auto &arena : arenas) {
|
||||
arenaSelectorView->addItem(RAddressString(arena.offset)
|
||||
+ QString(" (" + arena.type + " Arena)"));
|
||||
}
|
||||
|
||||
// check if arenas reduced or invalid index and restore the previously selected arena
|
||||
if (arenaSelectorView->count() < currentIndex || currentIndex == -1) {
|
||||
currentIndex = 0;
|
||||
}
|
||||
arenaSelectorView->setCurrentIndex(currentIndex);
|
||||
}
|
||||
|
||||
void GlibcHeapWidget::onArenaSelected(int index)
|
||||
{
|
||||
if (index == -1) {
|
||||
modelHeap->arena_addr = 0;
|
||||
} else {
|
||||
modelHeap->arena_addr = arenas[index].offset;
|
||||
}
|
||||
|
||||
updateChunks();
|
||||
}
|
||||
|
||||
void GlibcHeapWidget::updateContents()
|
||||
{
|
||||
if (!refreshDeferrer->attemptRefresh(nullptr) || Core()->isDebugTaskInProgress()) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateArenas();
|
||||
updateChunks();
|
||||
}
|
||||
|
||||
void GlibcHeapWidget::updateChunks()
|
||||
{
|
||||
modelHeap->reload();
|
||||
viewHeap->resizeColumnsToContents();
|
||||
}
|
||||
|
||||
void GlibcHeapWidget::customMenuRequested(QPoint pos)
|
||||
{
|
||||
addressableItemContextMenu.exec(viewHeap->viewport()->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void GlibcHeapModel::reload()
|
||||
{
|
||||
beginResetModel();
|
||||
values.clear();
|
||||
values = Core()->getHeapChunks(arena_addr);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
int GlibcHeapModel::columnCount(const QModelIndex &) const
|
||||
{
|
||||
return ColumnCount;
|
||||
}
|
||||
|
||||
int GlibcHeapModel::rowCount(const QModelIndex &) const
|
||||
{
|
||||
return this->values.size();
|
||||
}
|
||||
|
||||
QVariant GlibcHeapModel::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 OffsetColumn:
|
||||
return RAddressString(item.offset);
|
||||
case SizeColumn:
|
||||
return RHexString(item.size);
|
||||
case StatusColumn:
|
||||
return item.status;
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant GlibcHeapModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
Q_UNUSED(orientation);
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
switch (section) {
|
||||
case OffsetColumn:
|
||||
return tr("Offset");
|
||||
case SizeColumn:
|
||||
return tr("Size");
|
||||
case StatusColumn:
|
||||
return tr("Status");
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
void GlibcHeapWidget::onDoubleClicked(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int column = index.column();
|
||||
if (column == GlibcHeapModel::OffsetColumn) {
|
||||
QString item = index.data().toString();
|
||||
Core()->seek(item);
|
||||
main->showMemoryWidget(MemoryWidgetType::Hexdump);
|
||||
}
|
||||
}
|
||||
|
||||
void GlibcHeapWidget::onCurrentChanged(const QModelIndex ¤t, const QModelIndex &prev)
|
||||
{
|
||||
Q_UNUSED(current)
|
||||
Q_UNUSED(prev)
|
||||
|
||||
auto currentIndex = viewHeap->selectionModel()->currentIndex();
|
||||
QString offsetString = currentIndex.sibling(currentIndex.row(), GlibcHeapModel::OffsetColumn)
|
||||
.data()
|
||||
.toString();
|
||||
addressableItemContextMenu.setTarget(Core()->math(offsetString));
|
||||
}
|
||||
|
||||
void GlibcHeapWidget::viewChunkInfo()
|
||||
{
|
||||
auto currentIndex = viewHeap->selectionModel()->currentIndex();
|
||||
QString offsetString = currentIndex.sibling(currentIndex.row(), GlibcHeapModel::OffsetColumn)
|
||||
.data()
|
||||
.toString();
|
||||
QString status = currentIndex.sibling(currentIndex.row(), GlibcHeapModel::StatusColumn)
|
||||
.data()
|
||||
.toString();
|
||||
|
||||
GlibcHeapInfoDialog heapInfoDialog(Core()->math(offsetString), status, this);
|
||||
heapInfoDialog.exec();
|
||||
}
|
61
src/widgets/GlibcHeapWidget.h
Normal file
61
src/widgets/GlibcHeapWidget.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef GLIBCHEAPWIDGET_H
|
||||
#define GLIBCHEAPWIDGET_H
|
||||
|
||||
#include <QDockWidget>
|
||||
#include "CutterDockWidget.h"
|
||||
#include "core/Cutter.h"
|
||||
#include <QTableView>
|
||||
#include <QComboBox>
|
||||
#include <AddressableItemContextMenu.h>
|
||||
|
||||
namespace Ui {
|
||||
class GlibcHeapWidget;
|
||||
}
|
||||
|
||||
class GlibcHeapModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GlibcHeapModel(QObject *parent = nullptr);
|
||||
enum Column { OffsetColumn = 0, SizeColumn, StatusColumn, ColumnCount };
|
||||
void reload();
|
||||
int rowCount(const QModelIndex &parent) const override;
|
||||
int columnCount(const QModelIndex &parent) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
RVA arena_addr = 0;
|
||||
|
||||
private:
|
||||
QVector<Chunk> values;
|
||||
};
|
||||
|
||||
class GlibcHeapWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GlibcHeapWidget(MainWindow *main, QWidget *parent);
|
||||
~GlibcHeapWidget();
|
||||
private slots:
|
||||
void updateContents();
|
||||
void onDoubleClicked(const QModelIndex &index);
|
||||
void onArenaSelected(int index);
|
||||
void customMenuRequested(QPoint pos);
|
||||
void onCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
void viewChunkInfo();
|
||||
|
||||
private:
|
||||
void updateArenas();
|
||||
void updateChunks();
|
||||
Ui::GlibcHeapWidget *ui;
|
||||
QTableView *viewHeap = new QTableView(this);
|
||||
QComboBox *arenaSelectorView = new QComboBox(this);
|
||||
GlibcHeapModel *modelHeap = new GlibcHeapModel(this);
|
||||
QVector<Arena> arenas;
|
||||
QAction *chunkInfoAction;
|
||||
AddressableItemContextMenu addressableItemContextMenu;
|
||||
RefreshDeferrer *refreshDeferrer {};
|
||||
MainWindow *main;
|
||||
};
|
||||
|
||||
#endif // GLIBCHEAPWIDGET_H
|
20
src/widgets/GlibcHeapWidget.ui
Normal file
20
src/widgets/GlibcHeapWidget.ui
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>GlibcHeapWidget</class>
|
||||
<widget class="QWidget" name="GlibcHeapWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
41
src/widgets/HeapDockWidget.cpp
Normal file
41
src/widgets/HeapDockWidget.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "HeapDockWidget.h"
|
||||
#include "ui_HeapDockWidget.h"
|
||||
#include "widgets/GlibcHeapWidget.h"
|
||||
|
||||
HeapDockWidget::HeapDockWidget(MainWindow *main)
|
||||
: CutterDockWidget(main), ui(new Ui::HeapDockWidget), main(main)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->allocatorSelector->addItem("Glibc Heap");
|
||||
ui->verticalLayout->setMargin(0);
|
||||
|
||||
connect<void (QComboBox::*)(int)>(ui->allocatorSelector, &QComboBox::currentIndexChanged, this,
|
||||
&HeapDockWidget::onAllocatorSelected);
|
||||
|
||||
// select Glibc heap by default
|
||||
onAllocatorSelected(0);
|
||||
}
|
||||
|
||||
HeapDockWidget::~HeapDockWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void HeapDockWidget::onAllocatorSelected(int index)
|
||||
{
|
||||
if (index >= AllocatorCount)
|
||||
return;
|
||||
|
||||
// remove the current heap widget from layout
|
||||
if (currentHeapWidget) {
|
||||
ui->verticalLayout->removeWidget(currentHeapWidget);
|
||||
delete currentHeapWidget;
|
||||
}
|
||||
|
||||
// change widget depending upon selected allocator
|
||||
if (index == Glibc) {
|
||||
currentHeapWidget = new GlibcHeapWidget(main, this);
|
||||
}
|
||||
ui->verticalLayout->addWidget(currentHeapWidget);
|
||||
}
|
28
src/widgets/HeapDockWidget.h
Normal file
28
src/widgets/HeapDockWidget.h
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef HEAPDOCKWIDGET_H
|
||||
#define HEAPDOCKWIDGET_H
|
||||
|
||||
#include <QDockWidget>
|
||||
#include "CutterDockWidget.h"
|
||||
|
||||
namespace Ui {
|
||||
class HeapDockWidget;
|
||||
}
|
||||
|
||||
class HeapDockWidget : public CutterDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit HeapDockWidget(MainWindow *main);
|
||||
~HeapDockWidget();
|
||||
private slots:
|
||||
void onAllocatorSelected(int index);
|
||||
|
||||
private:
|
||||
enum Allocator { Glibc = 0, AllocatorCount };
|
||||
Ui::HeapDockWidget *ui;
|
||||
MainWindow *main;
|
||||
QWidget* currentHeapWidget = nullptr;
|
||||
};
|
||||
|
||||
#endif // HEAPDOCKWIDGET_H
|
26
src/widgets/HeapDockWidget.ui
Normal file
26
src/widgets/HeapDockWidget.ui
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>HeapDockWidget</class>
|
||||
<widget class="QDockWidget" name="HeapDockWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Heap</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="allocatorSelector"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
Loading…
Reference in New Issue
Block a user