Add flag in disassembly context menu, changes in disassembly refreshing (#197)

* Prepare Add Flag
* Flag Dialog
* MainWindow::globalSeekTo() signal
* Load more disassembly in refresh if necessary
This commit is contained in:
Florian Märkl 2017-07-11 13:05:42 +02:00 committed by radare
parent 771eccc125
commit 6ed212a4ef
15 changed files with 378 additions and 99 deletions

View File

@ -7,11 +7,11 @@
<x>0</x>
<y>0</y>
<width>400</width>
<height>61</height>
<height>75</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
<string>Comment</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">

View File

@ -0,0 +1,35 @@
#include "ui_flagdialog.h"
#include "flagdialog.h"
FlagDialog::FlagDialog(IaitoRCore *core, RVA offset, QWidget *parent) :
QDialog(parent),
ui(new Ui::FlagDialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
this->core = core;
this->offset = offset;
auto size_validator = new QIntValidator(ui->sizeEdit);
size_validator->setBottom(1);
ui->sizeEdit->setValidator(size_validator);
}
FlagDialog::~FlagDialog()
{
delete ui;
}
void FlagDialog::on_buttonBox_accepted()
{
QString name = ui->nameEdit->text();
RVA size = ui->sizeEdit->text().toULongLong();
core->addFlag(offset, name, size);
}
void FlagDialog::on_buttonBox_rejected()
{
close();
}

31
src/dialogs/flagdialog.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef FLAGDIALOG_H
#define FLAGDIALOG_H
#include <QDialog>
#include <iaitorcore.h>
namespace Ui
{
class FlagDialog;
}
class FlagDialog : public QDialog
{
Q_OBJECT
public:
explicit FlagDialog(IaitoRCore *core, RVA offset, QWidget *parent = 0);
~FlagDialog();
private slots:
void on_buttonBox_accepted();
void on_buttonBox_rejected();
private:
Ui::FlagDialog *ui;
IaitoRCore *core;
RVA offset;
};
#endif // FLAGDIALOG_H

126
src/dialogs/flagdialog.ui Normal file
View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FlagDialog</class>
<widget class="QDialog" name="FlagDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>452</width>
<height>121</height>
</rect>
</property>
<property name="windowTitle">
<string>Add Flag</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QFormLayout" name="formLayout">
<property name="horizontalSpacing">
<number>12</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Flag:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="nameEdit">
<property name="frame">
<bool>false</bool>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="sizeEdit">
<property name="maximumSize">
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>1</string>
</property>
<property name="frame">
<bool>false</bool>
</property>
<property name="placeholderText">
<string notr="true"/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Size:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>FlagDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>FlagDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -66,7 +66,8 @@ SOURCES += \
hexhighlighter.cpp \
widgets/sectionsdock.cpp \
widgets/consolewidget.cpp \
radarewebserver.cpp
radarewebserver.cpp \
dialogs/flagdialog.cpp
HEADERS += \
mainwindow.h \
@ -106,7 +107,8 @@ HEADERS += \
radarewebserver.h \
settings.h \
iaitorcore.h \
iaitordisasm.h
iaitordisasm.h \
dialogs/flagdialog.h
FORMS += \
mainwindow.ui \
@ -131,7 +133,8 @@ FORMS += \
widgets/dashboard.ui \
dialogs/xrefsdialog.ui \
widgets/sectionsdock.ui \
widgets/consolewidget.ui
widgets/consolewidget.ui \
dialogs/flagdialog.ui
RESOURCES += \
resources.qrc

View File

@ -46,6 +46,8 @@ public:
typedef ut64 RVA;
#define RVA_INVALID UT64_MAX
inline QString RAddressString(RVA addr)
{
return QString::asprintf("%#010llx", addr);
@ -163,6 +165,7 @@ public:
~IaitoRCore();
RVA getOffset() const { return core_->offset; }
static QString sanitizeStringForCommand(QString s);
int getCycloComplex(ut64 addr);
int getFcnSize(ut64 addr);
int fcnCyclomaticComplexity(ut64 addr);
@ -172,7 +175,6 @@ public:
QJsonDocument cmdj(const QString &str);
void renameFunction(QString prev_name, QString new_name);
void setComment(RVA addr, QString cmt);
void setComment(QString addr, QString cmt);
void delComment(ut64 addr);
QMap<QString, QList<QList<QString>>> getNestedComments();
void setOptions(QString key);
@ -236,6 +238,8 @@ public:
QList<XrefDescription> getXRefs(RVA addr, bool to, bool whole_function, const QString &filterType = QString::null);
void addFlag(RVA offset, QString name, RVA size);
RCoreLocked core() const;
/* fields */
@ -243,7 +247,10 @@ public:
Sdb *db;
signals:
// TODO: create a more sophisticated update-event system
void functionRenamed(QString prev_name, QString new_name);
void flagsChanged();
void commentsChanged();
public slots:

View File

@ -767,9 +767,10 @@ void MainWindow::seek(const RVA offset, const QString &name, bool raise_memory_d
this->hexdumpTopOffset = 0;
this->hexdumpBottomOffset = 0;
core->seek(offset);
emit globalSeekTo(offset);
setCursorAddress(offset);
refreshMem();
//refreshMem();
this->memoryDock->disasTextEdit->setFocus();
// Rise and shine baby!

View File

@ -80,6 +80,7 @@ public:
void refreshOmniBar(const QStringList &flags);
signals:
void globalSeekTo(RVA address);
void cursorAddressChanged(RVA address);
public slots:

View File

@ -167,6 +167,12 @@ IaitoRCore::~IaitoRCore()
r_cons_free();
}
QString IaitoRCore::sanitizeStringForCommand(QString s)
{
static const QRegExp regexp(";|@");
return s.replace(regexp, "_");
}
QString IaitoRCore::cmd(const QString &str)
{
CORE_LOCK();
@ -325,13 +331,9 @@ void IaitoRCore::setComment(RVA addr, QString cmt)
{
//r_meta_add (core->anal, 'C', addr, 1, cmt.toUtf8());
cmd("CC " + cmt + " @ " + QString::number(addr));
emit commentsChanged();
}
void IaitoRCore::setComment(QString addr, QString cmt)
{
//r_meta_add (core->anal, 'C', addr, 1, cmt.toUtf8());
cmd("CC " + cmt + " @ " + addr);
}
void IaitoRCore::delComment(ut64 addr)
{
@ -1125,4 +1127,11 @@ QList<XrefDescription> IaitoRCore::getXRefs(RVA addr, bool to, bool whole_functi
}
return ret;
}
void IaitoRCore::addFlag(RVA offset, QString name, RVA size)
{
name = sanitizeStringForCommand(name);
cmd(QString("f %1 %2 @ %3").arg(name).arg(size).arg(offset));
emit flagsChanged();
}

View File

@ -28,6 +28,8 @@ CommentsWidget::CommentsWidget(MainWindow *main, QWidget *parent) :
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
this, SLOT(showTitleContextMenu(const QPoint &)));
connect(main->core, SIGNAL(commentsChanged()), this, SLOT(refreshTree()));
// Hide the buttons frame
ui->frame->hide();
}

View File

@ -140,6 +140,8 @@ FlagsWidget::FlagsWidget(MainWindow *main, QWidget *parent) :
connect(ui->filterLineEdit, SIGNAL(textChanged(const QString &)), flags_proxy_model, SLOT(setFilterWildcard(const QString &)));
ui->flagsTreeView->setModel(flags_proxy_model);
ui->flagsTreeView->sortByColumn(FlagsModel::OFFSET, Qt::AscendingOrder);
connect(main->core, SIGNAL(flagsChanged()), this, SLOT(flagsChanged()));
}
FlagsWidget::~FlagsWidget()
@ -172,6 +174,11 @@ void FlagsWidget::on_flagspaceCombo_currentTextChanged(const QString &arg1)
refreshFlags();
}
void FlagsWidget::flagsChanged()
{
refreshFlagspaces();
}
void FlagsWidget::refreshFlagspaces()
{
int cur_idx = ui->flagspaceCombo->currentIndex();

View File

@ -70,6 +70,8 @@ private slots:
void on_flagsTreeView_doubleClicked(const QModelIndex &index);
void on_flagspaceCombo_currentTextChanged(const QString &arg1);
void flagsChanged();
private:
Ui::FlagsWidget *ui;
MainWindow *main;

View File

@ -6,6 +6,7 @@
#include "dialogs/xrefsdialog.h"
#include "dialogs/renamedialog.h"
#include "dialogs/commentsdialog.h"
#include "dialogs/flagdialog.h"
#include <QTemporaryFile>
#include <QFontDialog>
@ -41,10 +42,12 @@ MemoryWidget::MemoryWidget(MainWindow *main) :
this->memTabWidget = ui->memTabWidget;
this->last_fcn = "entry0";
this->last_disasm_fcn = 0; //"";
this->last_graph_fcn = 0; //"";
this->last_hexdump_fcn = 0; //"";
disasm_top_offset = 0;
next_disasm_top_offset = 0;
// Increase asm text edit margin
QTextDocument *asm_docu = this->disasTextEdit->document();
asm_docu->setDocumentMargin(10);
@ -188,12 +191,19 @@ MemoryWidget::MemoryWidget(MainWindow *main) :
connect(ui->graphWebView->page(), SIGNAL(loadFinished(bool)), this, SLOT(frameLoadFinished(bool)));
connect(main, SIGNAL(globalSeekTo(RVA)), this, SLOT(on_globalSeekTo(RVA)));
connect(main, SIGNAL(cursorAddressChanged(RVA)), this, SLOT(on_cursorAddressChanged(RVA)));
connect(main->core, SIGNAL(flagsChanged()), this, SLOT(updateViews()));
connect(main->core, SIGNAL(commentsChanged()), this, SLOT(updateViews()));
fillPlugins();
}
void MemoryWidget::on_globalSeekTo(RVA addr)
{
updateViews(addr);
}
void MemoryWidget::on_cursorAddressChanged(RVA addr)
{
@ -386,6 +396,23 @@ void MemoryWidget::highlightDecoCurrentLine()
ui->decoTextEdit->setExtraSelections(extraSelections);
}
RVA MemoryWidget::readCurrentDisassemblyOffset()
{
// TODO: do this in a different way without parsing the disassembly text
QTextCursor tc = this->disasTextEdit->textCursor();
tc.select(QTextCursor::LineUnderCursor);
QString lastline = tc.selectedText();
QStringList parts = lastline.split(" ", QString::SkipEmptyParts);
if (parts.isEmpty())
return RVA_INVALID;
QString ele = parts[0];
if (!ele.contains("0x"))
return RVA_INVALID;
return ele.toULongLong(0, 16);
}
MemoryWidget::~MemoryWidget()
{
@ -397,11 +424,13 @@ void MemoryWidget::setup()
setScrollMode();
const QString off = main->core->cmd("afo entry0").trimmed();
RVA offset = off.toULongLong(0, 16);
updateViews(offset);
refreshDisasm(off);
refreshHexdump(off);
create_graph(off);
get_refs_data(off.toLongLong(0, 16));
//refreshDisasm();
//refreshHexdump(off);
//create_graph(off);
get_refs_data(offset);
//setFcnName(off);
}
@ -438,31 +467,32 @@ void MemoryWidget::replaceTextDisasm(QString txt)
ui->disasTextEdit_2->setPlainText(txt);
}
void MemoryWidget::disasmScrolled()
bool MemoryWidget::loadMoreDisassembly()
{
/*
* Add more disasm as the user scrolls
* Not working properly when scrolling upwards
* r2 doesn't handle properly 'pd-' for archs with variable instruction size
*/
* Add more disasm as the user scrolls
* Not working properly when scrolling upwards
* r2 doesn't handle properly 'pd-' for archs with variable instruction size
*/
// Disconnect scroll signals to add more content
disconnect(this->disasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
QScrollBar *sb = this->disasTextEdit->verticalScrollBar();
bool loaded = false;
if (sb->value() > sb->maximum() - 10)
{
//this->main->add_debug_output("End is coming");
QTextCursor tc = this->disasTextEdit->textCursor();
tc.movePosition(QTextCursor::End);
tc.select(QTextCursor::LineUnderCursor);
QString lastline = tc.selectedText();
QString ele = lastline.split(" ", QString::SkipEmptyParts)[0];
if (ele.contains("0x"))
RVA offset = readCurrentDisassemblyOffset();
if (offset != RVA_INVALID)
{
this->main->core->seek(ele);
main->core->seek(offset);
QString raw = this->main->core->cmd("pd 200");
QString txt = raw.section("\n", 1, -1);
//this->disasTextEdit->appendPlainText(" ;\n ; New content here\n ;\n " + txt.trimmed());
@ -475,6 +505,9 @@ void MemoryWidget::disasmScrolled()
QString lastline = tc.selectedText();
this->main->addDebugOutput("Last line: " + lastline);
}
loaded = true;
// Code below will be used to append more disasm upwards, one day
} /* else if (sb->value() < sb->minimum() + 10) {
//this->main->add_debug_output("Begining is coming");
@ -510,65 +543,63 @@ void MemoryWidget::disasmScrolled()
// Reconnect scroll signals
connect(this->disasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
return loaded;
}
void MemoryWidget::refreshDisasm(const QString &offset)
void MemoryWidget::disasmScrolled()
{
loadMoreDisassembly();
}
void MemoryWidget::refreshDisasm()
{
RCoreLocked lcore = this->main->core->core();
// we must store those ranges somewhere, to handle scroll
//ut64 addr = lcore->offset;
//int length = lcore->num->value;
//printf("refreshDisasm %s\n", offset.toLocal8Bit().constData());
// Prevent further scroll
disconnect(this->disasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
disconnect(this->disasTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(on_disasTextEdit_2_cursorPositionChanged()));
// Get disas at offset
if (!offset.isEmpty())
RVA offset = next_disasm_top_offset;
next_disasm_top_offset = RVA_INVALID;
bool offset_changed = offset != RVA_INVALID;
if (offset_changed) // new offset (seek)
{
this->main->core->cmd("s " + offset);
disasm_top_offset = offset;
this->main->core->cmd(QString("s %1").arg(offset));
}
else
else // simple refresh
{
// Get current offset
QTextCursor tc = this->disasTextEdit->textCursor();
tc.select(QTextCursor::LineUnderCursor);
QString lastline = tc.selectedText();
QStringList elements = lastline.split(" ", QString::SkipEmptyParts);
if (elements.length() > 0)
{
QString ele = elements[0];
if (ele.contains("0x"))
{
QString fcn = this->main->core->cmdFunctionAt(ele);
if (fcn != "")
{
this->main->core->cmd("s " + fcn);
}
else
{
this->main->core->cmd("s " + ele);
}
}
}
main->core->cmd(QString("s %1").arg(disasm_top_offset));
}
QString txt2 = this->main->core->cmd("pd 100");
QString txt2 = this->main->core->cmd("pd 200");
disasTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
// if the offset changed, jump to the top
// otherwise try to retain the position
int cursor_pos = offset_changed ? 0 : disasTextEdit->textCursor().position();
int scroll_pos = offset_changed ? 0 : disasTextEdit->verticalScrollBar()->value();
this->disasTextEdit->setPlainText(txt2.trimmed());
// TODO: Fixx this ugly code
//QString temp_seek = this->main->core->cmd("s").split("0x")[1].trimmed();
QString s = this->normalize_addr(this->main->core->cmd("s"));
//this->main->add_debug_output("Offset to search: " + s);
this->disasTextEdit->ensureCursorVisible();
/*this->disasTextEdit->moveCursor(QTextCursor::End);
auto cursor = disasTextEdit->textCursor();
cursor.setPosition(cursor_pos);
disasTextEdit->setTextCursor(cursor);
while (this->disasTextEdit->find(QRegExp("^" + s), QTextDocument::FindBackward))
disasTextEdit->verticalScrollBar()->setValue(scroll_pos);
// load more disassembly if necessary
static const int load_more_limit = 10; // limit passes, so it can't take forever
for (int load_more_i = 0; load_more_i < load_more_limit; load_more_i++)
{
this->disasTextEdit->moveCursor(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
}*/
if (!loadMoreDisassembly())
break;
disasTextEdit->verticalScrollBar()->setValue(scroll_pos);
}
connect(this->disasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
connect(this->disasTextEdit, SIGNAL(cursorPositionChanged()), this, SLOT(on_disasTextEdit_2_cursorPositionChanged()));
@ -998,6 +1029,7 @@ void MemoryWidget::showDisasContextMenu(const QPoint &pt)
// Add menu actions
menu->clear();
menu->addAction(ui->actionDisasAdd_comment);
menu->addAction(ui->actionAddFlag);
menu->addAction(ui->actionFunctionsRename);
menu->addAction(ui->actionFunctionsUndefine);
menu->addSeparator();
@ -1260,31 +1292,43 @@ void MemoryWidget::on_actionSend_to_Notepad_triggered()
void MemoryWidget::on_actionDisasAdd_comment_triggered()
{
// Get current offset
QTextCursor tc = this->disasTextEdit->textCursor();
tc.select(QTextCursor::LineUnderCursor);
QString lastline = tc.selectedText();
QString ele = lastline.split(" ", QString::SkipEmptyParts)[0];
if (ele.contains("0x"))
RVA offset = readCurrentDisassemblyOffset();
// Get function for clicked offset
RAnalFunction *fcn = this->main->core->functionAt(offset);
CommentsDialog *c = new CommentsDialog(this);
if (c->exec())
{
// Get function for clicked offset
RAnalFunction *fcn = this->main->core->functionAt(ele.toLongLong(0, 16));
CommentsDialog *c = new CommentsDialog(this);
if (c->exec())
// Get new function name
QString comment = c->getComment();
//this->main->add_debug_output("Comment: " + comment + " at: " + ele);
// Rename function in r2 core
this->main->core->setComment(offset, comment);
// Seek to new renamed function
if (fcn)
{
// Get new function name
QString comment = c->getComment();
//this->main->add_debug_output("Comment: " + comment + " at: " + ele);
// Rename function in r2 core
this->main->core->setComment(ele, comment);
// Seek to new renamed function
if (fcn)
{
this->main->seek(fcn->name);
}
// TODO: Refresh functions tree widget
this->main->seek(fcn->name);
}
// TODO: Refresh functions tree widget
}
this->main->refreshComments();
}
void MemoryWidget::on_actionAddFlag_triggered()
{
RVA offset = readCurrentDisassemblyOffset();
FlagDialog *dialog = new FlagDialog(main->core, offset, this);
if (dialog->exec())
{
//QString comment = dialog->getFlagName();
// Rename function in r2 core
//this->main->core->setComment(offset, comment);
}
this->main->refreshComments();
}
@ -1939,7 +1983,7 @@ void MemoryWidget::on_memTabWidget_currentChanged(int /*index*/)
this->updateViews();
}
void MemoryWidget::updateViews()
void MemoryWidget::updateViews(RVA offset)
{
// Update only the selected view to improve performance
@ -1949,14 +1993,13 @@ void MemoryWidget::updateViews()
QString cursor_addr_string = RAddressString(cursor_addr);
if (offset != RVA_INVALID)
next_disasm_top_offset = offset;
if (index == 0)
{
// Disasm
if (this->last_disasm_fcn != cursor_addr)
{
this->refreshDisasm(cursor_addr_string);
this->last_disasm_fcn = cursor_addr;
}
this->refreshDisasm();
}
else if (index == 1)
{

View File

@ -66,7 +66,7 @@ public slots:
void replaceTextDisasm(QString txt);
void refreshDisasm(const QString &offset = QString());
void refreshDisasm();
void refreshHexdump(const QString &where = QString());
@ -92,7 +92,7 @@ public slots:
void frameLoadFinished(bool ok);
void updateViews();
void updateViews(RVA offset = RVA_INVALID);
protected:
void resizeEvent(QResizeEvent *event) override;
@ -105,7 +105,9 @@ private:
ut64 hexdumpBottomOffset;
QString last_fcn;
RVA last_disasm_fcn;
RVA disasm_top_offset;
RVA next_disasm_top_offset;
RVA last_graph_fcn;
RVA last_hexdump_fcn;
@ -114,7 +116,10 @@ private:
void setScrollMode();
bool loadMoreDisassembly();
private slots:
void on_globalSeekTo(RVA addr);
void on_cursorAddressChanged(RVA addr);
void highlightCurrentLine();
@ -122,6 +127,7 @@ private slots:
void highlightHexCurrentLine();
void highlightPreviewCurrentLine();
void highlightDecoCurrentLine();
RVA readCurrentDisassemblyOffset();
void setFonts(QFont font);
void highlightHexWords(const QString &str);
@ -138,6 +144,7 @@ private slots:
void showHexASCIIContextMenu(const QPoint &pt);
void on_actionSend_to_Notepad_triggered();
void on_actionDisasAdd_comment_triggered();
void on_actionAddFlag_triggered();
void on_actionFunctionsRename_triggered();
void on_actionDisas_ShowHideBytes_triggered();

View File

@ -3036,6 +3036,11 @@ QToolTip {
<string>Show stack pointer</string>
</property>
</action>
<action name="actionAddFlag">
<property name="text">
<string>Add flag</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
@ -3050,7 +3055,7 @@ QToolTip {
<connections/>
<buttongroups>
<buttongroup name="buttonGroup_3"/>
<buttongroup name="buttonGroup"/>
<buttongroup name="buttonGroup_2"/>
<buttongroup name="buttonGroup"/>
</buttongroups>
</ui>