mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-23 13:25:27 +00:00
381 lines
14 KiB
C++
381 lines
14 KiB
C++
#include "functionswidget.h"
|
|
#include "ui_functionswidget.h"
|
|
|
|
#include "dialogs/commentsdialog.h"
|
|
#include "dialogs/renamedialog.h"
|
|
#include "dialogs/xrefsdialog.h"
|
|
#include "mainwindow.h"
|
|
|
|
#include <QMenu>
|
|
#include <QDebug>
|
|
#include <QString>
|
|
|
|
FunctionsWidget::FunctionsWidget(MainWindow *main, QWidget *parent) :
|
|
QDockWidget(parent),
|
|
ui(new Ui::FunctionsWidget)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
// Radare core found in:
|
|
this->main = main;
|
|
this->functionsTreeWidget = ui->functionsTreeWidget;
|
|
this->functionsTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
//this->functionsTreeWidget->setFont(QFont("Monospace", 8));
|
|
// Set Functions context menu
|
|
connect(ui->functionsTreeWidget, SIGNAL(customContextMenuRequested(const QPoint &)),
|
|
this, SLOT(showFunctionsContextMenu(const QPoint &)));
|
|
connect(ui->nestedFunctionsTree, SIGNAL(customContextMenuRequested(const QPoint &)),
|
|
this, SLOT(showFunctionsContextMenu(const QPoint &)));
|
|
|
|
// Hide the tabs
|
|
QTabBar *tabs = ui->tabWidget->tabBar();
|
|
tabs->setVisible(false);
|
|
|
|
// Use a custom context menu on the dock title bar
|
|
//this->title_bar = this->titleBarWidget();
|
|
ui->actionHorizontal->setChecked(true);
|
|
this->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
|
|
this, SLOT(showTitleContextMenu(const QPoint &)));
|
|
|
|
// Resize eventfilter
|
|
ui->functionsTreeWidget->viewport()->installEventFilter(this);
|
|
}
|
|
|
|
FunctionsWidget::~FunctionsWidget()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
void FunctionsWidget::fillFunctions() {
|
|
this->functionsTreeWidget->clear();
|
|
ui->nestedFunctionsTree->clear();
|
|
for (auto i: this->main->core->getList ("anal", "functions")) {
|
|
QStringList a = i.split (",");
|
|
// off,sz,unk,name
|
|
// "0x0804ada3,1,13,,fcn.0804ada3"
|
|
// "0x0804ad4a,6,,1,,fcn.0804ad4a"
|
|
if (a.length() == 5) {
|
|
// Add list function
|
|
this->main->appendRow(this->functionsTreeWidget,a[0],a[1],a[4]);
|
|
// Add nested function
|
|
QTreeWidgetItem *item = new QTreeWidgetItem(ui->nestedFunctionsTree);
|
|
item->setText(0, a[4]);
|
|
QTreeWidgetItem *size_it = new QTreeWidgetItem();
|
|
size_it->setText(0, "Offset: " + a[0]);
|
|
item->addChild(size_it);
|
|
QTreeWidgetItem *off_it = new QTreeWidgetItem();
|
|
off_it->setText(0, "Size: " + a[1]);
|
|
item->addChild(off_it);
|
|
ui->nestedFunctionsTree->addTopLevelItem(item);
|
|
} else if (a.length() == 6) {
|
|
// Add list function
|
|
this->main->appendRow(this->functionsTreeWidget,a[0],a[1],a[5]);
|
|
// Add nested function
|
|
QTreeWidgetItem *item = new QTreeWidgetItem(ui->nestedFunctionsTree);
|
|
item->setText(0, a[5]);
|
|
QTreeWidgetItem *size_it = new QTreeWidgetItem();
|
|
size_it->setText(0, "Offset: " + a[0]);
|
|
item->addChild(size_it);
|
|
QTreeWidgetItem *off_it = new QTreeWidgetItem();
|
|
off_it->setText(0, "Size: " + a[1]);
|
|
item->addChild(off_it);
|
|
ui->nestedFunctionsTree->addTopLevelItem(item);
|
|
} else {
|
|
qDebug() << "fillFunctions()" << a;
|
|
}
|
|
}
|
|
this->functionsTreeWidget->sortByColumn(3, Qt::AscendingOrder);
|
|
ui->nestedFunctionsTree->sortByColumn(0, Qt::AscendingOrder);
|
|
this->main->adjustColumns(this->functionsTreeWidget);
|
|
|
|
this->addTooltips();
|
|
}
|
|
|
|
void FunctionsWidget::on_functionsTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int column)
|
|
{
|
|
QString offset = item->text(1);
|
|
QString name = item->text(3);
|
|
this->main->seek(offset, name);
|
|
this->main->memoryDock->raise();
|
|
}
|
|
|
|
void FunctionsWidget::showFunctionsContextMenu(const QPoint &pt)
|
|
{
|
|
// Set functions popup menu
|
|
QMenu *menu = new QMenu(ui->functionsTreeWidget);
|
|
menu->clear();
|
|
menu->addAction(ui->actionDisasAdd_comment);
|
|
menu->addAction(ui->actionFunctionsRename);
|
|
menu->addAction(ui->actionFunctionsUndefine);
|
|
menu->addSeparator();
|
|
menu->addAction(ui->action_References);
|
|
|
|
if(ui->tabWidget->currentIndex() == 0) {
|
|
this->functionsTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
menu->exec(ui->functionsTreeWidget->mapToGlobal(pt));
|
|
} else {
|
|
ui->nestedFunctionsTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
menu->exec(ui->nestedFunctionsTree->mapToGlobal(pt));
|
|
}
|
|
delete menu;
|
|
}
|
|
|
|
void FunctionsWidget::refreshTree() {
|
|
this->functionsTreeWidget->clear();
|
|
ui->nestedFunctionsTree->clear();
|
|
for (auto i: this->main->core->getList ("anal", "functions")) {
|
|
QStringList a = i.split (",");
|
|
// off,sz,unk,name
|
|
// "0x0804ada3,1,13,,fcn.0804ada3"
|
|
// "0x0804ad4a,6,,1,,fcn.0804ad4a"
|
|
if (a.length() == 5) {
|
|
// Add list function
|
|
this->main->appendRow(this->functionsTreeWidget,a[0],a[1],a[4]);
|
|
// Add nested function
|
|
QTreeWidgetItem *item = new QTreeWidgetItem(ui->nestedFunctionsTree);
|
|
item->setText(0, a[4]);
|
|
QTreeWidgetItem *size_it = new QTreeWidgetItem();
|
|
size_it->setText(0, "Offset: " + a[0]);
|
|
item->addChild(size_it);
|
|
QTreeWidgetItem *off_it = new QTreeWidgetItem();
|
|
off_it->setText(0, "Size: " + a[1]);
|
|
item->addChild(off_it);
|
|
ui->nestedFunctionsTree->addTopLevelItem(item);
|
|
} else if (a.length() == 6) {
|
|
// Add list function
|
|
this->main->appendRow(this->functionsTreeWidget,a[0],a[1],a[5]);
|
|
// Add nested function
|
|
QTreeWidgetItem *item = new QTreeWidgetItem(ui->nestedFunctionsTree);
|
|
item->setText(0, a[5]);
|
|
QTreeWidgetItem *size_it = new QTreeWidgetItem();
|
|
size_it->setText(0, "Offset: " + a[0]);
|
|
item->addChild(size_it);
|
|
QTreeWidgetItem *off_it = new QTreeWidgetItem();
|
|
off_it->setText(0, "Size: " + a[1]);
|
|
item->addChild(off_it);
|
|
ui->nestedFunctionsTree->addTopLevelItem(item);
|
|
} else {
|
|
qDebug() << "fillFunctions()" << a;
|
|
}
|
|
}
|
|
this->functionsTreeWidget->sortByColumn(3, Qt::AscendingOrder);
|
|
ui->nestedFunctionsTree->sortByColumn(0, Qt::AscendingOrder);
|
|
this->main->adjustColumns(this->functionsTreeWidget);
|
|
|
|
this->addTooltips();
|
|
}
|
|
|
|
void FunctionsWidget::on_actionDisasAdd_comment_triggered()
|
|
{
|
|
QString fcn_name = "";
|
|
// Create dialog
|
|
CommentsDialog* c = new CommentsDialog(this);
|
|
// Get selected item in functions tree widget
|
|
if(ui->tabWidget->currentIndex() == 0) {
|
|
QList<QTreeWidgetItem *> selected_rows = ui->functionsTreeWidget->selectedItems();
|
|
// Get selected function name
|
|
fcn_name = selected_rows.first()->text(3);
|
|
} else {
|
|
QList<QTreeWidgetItem *> selected_rows = ui->nestedFunctionsTree->selectedItems();
|
|
// Get selected function name
|
|
fcn_name = selected_rows.first()->text(0);
|
|
}
|
|
if (c->exec()) {
|
|
// Get new function name
|
|
QString comment = c->getComment();
|
|
this->main->add_debug_output("Comment: " + comment + " at: " + fcn_name);
|
|
// Rename function in r2 core
|
|
this->main->core->setComment(fcn_name, comment);
|
|
// Seek to new renamed function
|
|
this->main->seek(fcn_name);
|
|
// TODO: Refresh functions tree widget
|
|
}
|
|
this->main->refreshComments();
|
|
}
|
|
|
|
void FunctionsWidget::addTooltips() {
|
|
|
|
// Add comments to list functions
|
|
QList<QTreeWidgetItem*> clist = this->functionsTreeWidget->findItems("*", Qt::MatchWildcard, 3);
|
|
foreach(QTreeWidgetItem* item, clist)
|
|
{
|
|
QString name = item->text(3);
|
|
QList<QString> info = this->main->core->cmd("afi @ " + name).split("\n");
|
|
if (info.length() > 2) {
|
|
QString size = info[4].split(" ")[1];
|
|
QString complex = info[8].split(" ")[1];
|
|
QString bb = info[11].split(" ")[1];
|
|
item->setToolTip(3, "Summary:\n\n Size: " + size +
|
|
"\n Cyclomatic complexity: " + complex +
|
|
"\n Basic blocks: " + bb +
|
|
"\n\nDisasm preview:\n\n" + this->main->core->cmd("pdi 10 @ " + name) +
|
|
"\nStrings:\n\n" + this->main->core->cmd("pdsf @ " + name));
|
|
//"\nStrings:\n\n" + this->main->core->cmd("pds @ " + name + "!$F"));
|
|
}
|
|
}
|
|
|
|
// Add comments to nested functions
|
|
QList<QTreeWidgetItem*> nlist = ui->nestedFunctionsTree->findItems("*", Qt::MatchWildcard, 0);
|
|
foreach(QTreeWidgetItem* item, nlist)
|
|
{
|
|
QString name = item->text(0);
|
|
QList<QString> info = this->main->core->cmd("afi @ " + name).split("\n");
|
|
if (info.length() > 2) {
|
|
QString size = info[4].split(" ")[1];
|
|
QString complex = info[8].split(" ")[1];
|
|
QString bb = info[11].split(" ")[1];
|
|
item->setToolTip(0, "Summary:\n\n Size: " + size +
|
|
"\n Cyclomatic complexity: " + complex +
|
|
"\n Basic blocks: " + bb +
|
|
"\n\nDisasm preview:\n\n" + this->main->core->cmd("pdi 10 @ " + name) +
|
|
"\nStrings:\n\n" + this->main->core->cmd("pdsf @ " + name));
|
|
//"\nStrings:\n\n" + this->main->core->cmd("pds @ " + name + "!$F"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void FunctionsWidget::on_actionFunctionsRename_triggered()
|
|
{
|
|
// Create dialog
|
|
RenameDialog* r = new RenameDialog(this);
|
|
// Get selected item in functions tree widget
|
|
QList<QTreeWidgetItem *> selected_rows = ui->functionsTreeWidget->selectedItems();
|
|
// Get selected function name
|
|
QString old_name = selected_rows.first()->text(3);
|
|
// Set function name in dialog
|
|
r->setFunctionName(old_name);
|
|
// If user accepted
|
|
if (r->exec()) {
|
|
// Get new function name
|
|
QString new_name = r->getFunctionName();
|
|
// Rename function in r2 core
|
|
this->main->core->renameFunction(old_name, new_name);
|
|
// Change name in functions tree widget
|
|
selected_rows.first()->setText(3, new_name);
|
|
// Scroll to show the new name in functions tree widget
|
|
/*
|
|
* QAbstractItemView::EnsureVisible
|
|
* QAbstractItemView::PositionAtTop
|
|
* QAbstractItemView::PositionAtBottom
|
|
* QAbstractItemView::PositionAtCenter
|
|
*/
|
|
ui->functionsTreeWidget->scrollToItem(selected_rows.first(), QAbstractItemView::PositionAtTop);
|
|
// Seek to new renamed function
|
|
this->main->seek(new_name);
|
|
}
|
|
}
|
|
|
|
void FunctionsWidget::on_action_References_triggered()
|
|
{
|
|
QList<QTreeWidgetItem *> selected_rows = ui->functionsTreeWidget->selectedItems();
|
|
// Get selected function address
|
|
QString address = selected_rows.first()->text(1);
|
|
|
|
//this->main->add_debug_output("Addr: " + address);
|
|
|
|
// Get function for clicked offset
|
|
RAnalFunction *fcn = this->main->core->functionAt(address.toLongLong(0, 16));
|
|
|
|
XrefsDialog* x = new XrefsDialog(this->main, this);
|
|
x->setWindowTitle("X-Refs for function " + QString::fromUtf8(fcn->name));
|
|
|
|
// Get Refs and Xrefs
|
|
bool ok;
|
|
QList<QStringList> ret_refs;
|
|
QList<QStringList> ret_xrefs;
|
|
|
|
// refs = calls q hace esa funcion
|
|
QList<QString> refs = this->main->core->getFunctionRefs(fcn->addr, 'C');
|
|
if (refs.size() > 0) {
|
|
for (int i = 0; i < refs.size(); ++i) {
|
|
//this->main->add_debug_output(refs.at(i));
|
|
QStringList retlist = refs.at(i).split(",");
|
|
QStringList temp;
|
|
QString addr = retlist.at(2);
|
|
temp << addr;
|
|
QString op = this->main->core->cmd("pi 1 @ " + addr);
|
|
temp << op.simplified();
|
|
ret_refs << temp;
|
|
}
|
|
}
|
|
|
|
// xrefs = calls a esa funcion
|
|
//qDebug() << this->main->core->getFunctionXrefs(offset.toLong(&ok, 16));
|
|
QList<QString> xrefs = this->main->core->getFunctionXrefs(fcn->addr);
|
|
if (xrefs.size() > 0) {
|
|
for (int i = 0; i < xrefs.size(); ++i) {
|
|
//this->main->add_debug_output(xrefs.at(i));
|
|
QStringList retlist = xrefs.at(i).split(",");
|
|
QStringList temp;
|
|
QString addr = retlist.at(1);
|
|
temp << addr;
|
|
QString op = this->main->core->cmd("pi 1 @ " + addr);
|
|
temp << op.simplified();
|
|
ret_xrefs << temp;
|
|
}
|
|
}
|
|
x->fillRefs(ret_refs, ret_xrefs);
|
|
x->exec();
|
|
}
|
|
|
|
void FunctionsWidget::showTitleContextMenu(const QPoint &pt)
|
|
{
|
|
// Set functions popup menu
|
|
QMenu *menu = new QMenu(this);
|
|
menu->clear();
|
|
menu->addAction(ui->actionHorizontal);
|
|
menu->addAction(ui->actionVertical);
|
|
|
|
if (ui->tabWidget->currentIndex() == 0) {
|
|
ui->actionHorizontal->setChecked(true);
|
|
ui->actionVertical->setChecked(false);
|
|
} else {
|
|
ui->actionVertical->setChecked(true);
|
|
ui->actionHorizontal->setChecked(false);
|
|
}
|
|
|
|
this->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
|
menu->exec(this->mapToGlobal(pt));
|
|
delete menu;
|
|
}
|
|
|
|
void FunctionsWidget::on_actionHorizontal_triggered()
|
|
{
|
|
ui->tabWidget->setCurrentIndex(0);
|
|
}
|
|
|
|
void FunctionsWidget::on_actionVertical_triggered()
|
|
{
|
|
ui->tabWidget->setCurrentIndex(1);
|
|
}
|
|
|
|
void FunctionsWidget::on_nestedFunctionsTree_itemDoubleClicked(QTreeWidgetItem *item, int column)
|
|
{
|
|
//QString offset = item->text(1);
|
|
QString name = item->text(0);
|
|
QString offset = item->child(0)->text(0).split(":")[1];
|
|
this->main->seek(offset, name);
|
|
this->main->memoryDock->raise();
|
|
}
|
|
|
|
bool FunctionsWidget::eventFilter(QObject *obj, QEvent *event) {
|
|
if (this->main->responsive) {
|
|
if (event->type() == QEvent::Resize && obj == this && this->isVisible()) {
|
|
QResizeEvent *resizeEvent = static_cast<QResizeEvent*>(event);
|
|
//qDebug("Dock Resized (New Size) - Width: %d Height: %d",
|
|
// resizeEvent->size().width(),
|
|
// resizeEvent->size().height());
|
|
if (resizeEvent->size().width() >= resizeEvent->size().height()) {
|
|
// Set horizontal view (list)
|
|
this->on_actionHorizontal_triggered();
|
|
} else {
|
|
// Set vertical view (Tree)
|
|
this->on_actionVertical_triggered();
|
|
}
|
|
}
|
|
}
|
|
return false; //allow the event to be handled further
|
|
}
|