mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-19 19:36:11 +00:00
Some refactoring
This commit is contained in:
parent
fae260b1a1
commit
925eb15c9b
@ -113,12 +113,10 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
webserver(core)
|
webserver(core)
|
||||||
{
|
{
|
||||||
doLock = false;
|
doLock = false;
|
||||||
this->cursor_address = core->getOffset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MainWindow::~MainWindow()
|
MainWindow::~MainWindow()
|
||||||
{
|
{
|
||||||
qDeleteAll(asmSyntaxes);
|
|
||||||
delete core;
|
delete core;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +133,7 @@ void MainWindow::initUI()
|
|||||||
// Hide central tab widget tabs
|
// Hide central tab widget tabs
|
||||||
QTabBar *centralbar = ui->centralTabWidget->tabBar();
|
QTabBar *centralbar = ui->centralTabWidget->tabBar();
|
||||||
centralbar->setVisible(false);
|
centralbar->setVisible(false);
|
||||||
consoleWidget = new ConsoleWidget(core, this);
|
consoleWidget = new ConsoleWidget(this, this);
|
||||||
ui->tabVerticalLayout->addWidget(consoleWidget);
|
ui->tabVerticalLayout->addWidget(consoleWidget);
|
||||||
|
|
||||||
// Sepparator between back/forward and undo/redo buttons
|
// Sepparator between back/forward and undo/redo buttons
|
||||||
@ -690,55 +688,26 @@ void MainWindow::toggleDockWidget(DockWidget *dock_widget)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::seek(const QString &offset, const QString &name, bool raise_memory_dock)
|
void MainWindow::setCursorAddress(RVA addr)
|
||||||
{
|
{
|
||||||
// TODO: remove this method and use the one with RVA only!
|
this->cursorAddress = addr;
|
||||||
|
emit cursorAddressChanged(core->getOffset());
|
||||||
if (offset.length() < 2)
|
|
||||||
return;
|
|
||||||
|
|
||||||
bool ok;
|
|
||||||
RVA addr = offset.mid(2).toULongLong(&ok, 16);
|
|
||||||
if (!ok)
|
|
||||||
return;
|
|
||||||
|
|
||||||
seek(addr, name, raise_memory_dock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::seek(const RVA offset)
|
||||||
void MainWindow::seek(const RVA offset, const QString &name, bool raise_memory_dock)
|
|
||||||
{
|
{
|
||||||
{
|
|
||||||
this->memoryDock->setWindowTitle(name);
|
|
||||||
//this->current_address = name;
|
|
||||||
}
|
|
||||||
this->hexdumpTopOffset = 0;
|
|
||||||
this->hexdumpBottomOffset = 0;
|
|
||||||
core->seek(offset);
|
core->seek(offset);
|
||||||
emit globalSeekTo(offset);
|
setCursorAddress(core->getOffset());
|
||||||
setCursorAddress(offset);
|
emit seekChanged(core->getOffset());
|
||||||
|
|
||||||
//refreshMem();
|
|
||||||
this->memoryDock->disasTextEdit->setFocus();
|
|
||||||
|
|
||||||
// Rise and shine baby!
|
|
||||||
if (raise_memory_dock)
|
|
||||||
this->memoryDock->raise();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::refreshMem()
|
|
||||||
{
|
|
||||||
this->memoryDock->updateViews();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void MainWindow::backButton_clicked()
|
void MainWindow::backButton_clicked()
|
||||||
{
|
{
|
||||||
QList<RVA> seek_history = core->getSeekHistory();
|
QList<RVA> seek_history = core->getSeekHistory();
|
||||||
this->core->cmd("s-");
|
this->core->cmd("s-");
|
||||||
RVA offset = this->core->getOffset();
|
RVA offset = this->core->getOffset();
|
||||||
QString fcn = this->core->cmdFunctionAt(QString::number(offset));
|
//QString fcn = this->core->cmdFunctionAt(QString::number(offset));
|
||||||
this->seek(offset, fcn);
|
this->seek(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionCalculator_triggered()
|
void MainWindow::on_actionCalculator_triggered()
|
||||||
@ -894,7 +863,6 @@ void MainWindow::on_actionRun_Script_triggered()
|
|||||||
|
|
||||||
qDebug() << "Meow: " + fileName;
|
qDebug() << "Meow: " + fileName;
|
||||||
this->core->cmd(". " + fileName);
|
this->core->cmd(". " + fileName);
|
||||||
this->refreshMem();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionDark_Theme_triggered()
|
void MainWindow::on_actionDark_Theme_triggered()
|
||||||
@ -987,12 +955,6 @@ void MainWindow::on_actionQuit_triggered()
|
|||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::setCursorAddress(RVA addr)
|
|
||||||
{
|
|
||||||
this->cursor_address = addr;
|
|
||||||
emit cursorAddressChanged(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::refreshVisibleDockWidgets()
|
void MainWindow::refreshVisibleDockWidgets()
|
||||||
{
|
{
|
||||||
// There seems to be no convenience function to check if a QDockWidget
|
// There seems to be no convenience function to check if a QDockWidget
|
||||||
|
@ -64,9 +64,7 @@ public:
|
|||||||
void closeEvent(QCloseEvent *event);
|
void closeEvent(QCloseEvent *event);
|
||||||
void readSettings();
|
void readSettings();
|
||||||
void setFilename(const QString &fn);
|
void setFilename(const QString &fn);
|
||||||
//void setCore(QRCore *core);
|
void seek(RVA offset);
|
||||||
void seek(const QString &offset, const QString &name = NULL, bool raise_memory_dock = false);
|
|
||||||
void seek(const RVA offset, const QString &name = NULL, bool raise_memory_dock = false);
|
|
||||||
void updateFrames();
|
void updateFrames();
|
||||||
void refreshFunctions();
|
void refreshFunctions();
|
||||||
void refreshComments();
|
void refreshComments();
|
||||||
@ -79,8 +77,8 @@ public:
|
|||||||
void refreshOmniBar(const QStringList &flags);
|
void refreshOmniBar(const QStringList &flags);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void globalSeekTo(RVA address);
|
void seekChanged(RVA offset);
|
||||||
void cursorAddressChanged(RVA address);
|
void cursorAddressChanged(RVA offset);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
@ -184,6 +182,7 @@ private:
|
|||||||
void refreshMem();
|
void refreshMem();
|
||||||
ut64 hexdumpTopOffset;
|
ut64 hexdumpTopOffset;
|
||||||
ut64 hexdumpBottomOffset;
|
ut64 hexdumpBottomOffset;
|
||||||
|
RVA cursorAddress;
|
||||||
QString filename;
|
QString filename;
|
||||||
QList<DockWidget *> dockWidgets;
|
QList<DockWidget *> dockWidgets;
|
||||||
std::unique_ptr<Ui::MainWindow> ui;
|
std::unique_ptr<Ui::MainWindow> ui;
|
||||||
@ -207,19 +206,15 @@ private:
|
|||||||
ConsoleWidget *consoleWidget;
|
ConsoleWidget *consoleWidget;
|
||||||
RadareWebServer webserver;
|
RadareWebServer webserver;
|
||||||
|
|
||||||
RVA cursor_address;
|
|
||||||
QList<QAction *> asmSyntaxes;
|
|
||||||
|
|
||||||
void openProject(const QString &project_name);
|
void openProject(const QString &project_name);
|
||||||
void openNewFile(const QString &fn, int anal_level, QList<QString> advanced);
|
void openNewFile(const QString &fn, int anal_level, QList<QString> advanced);
|
||||||
|
|
||||||
void toggleDockWidget(DockWidget *dock_widget);
|
void toggleDockWidget(DockWidget *dock_widget);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RVA getCursorAddress() const { return cursor_address; }
|
RVA getCursorAddress() const { return cursorAddress; }
|
||||||
void setCursorAddress(RVA addr);
|
|
||||||
|
|
||||||
QString getFilename() const { return filename; }
|
QString getFilename() const { return filename; }
|
||||||
|
void setCursorAddress(RVA addr);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "cutter.h"
|
#include "cutter.h"
|
||||||
#include "sdb.h"
|
#include "sdb.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "MainWindow.h"
|
||||||
|
|
||||||
|
|
||||||
#define DB this->db
|
#define DB this->db
|
||||||
|
@ -32,6 +32,9 @@
|
|||||||
|
|
||||||
#define APPNAME "Cutter"
|
#define APPNAME "Cutter"
|
||||||
|
|
||||||
|
typedef ut64 RVA;
|
||||||
|
#define RVA_INVALID UT64_MAX
|
||||||
|
|
||||||
class RCoreLocked
|
class RCoreLocked
|
||||||
{
|
{
|
||||||
RCore *core;
|
RCore *core;
|
||||||
@ -44,12 +47,10 @@ public:
|
|||||||
~RCoreLocked();
|
~RCoreLocked();
|
||||||
operator RCore *() const;
|
operator RCore *() const;
|
||||||
RCore *operator->() const;
|
RCore *operator->() const;
|
||||||
|
RVA seek(RVA offset);
|
||||||
|
RVA getSeek();
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef ut64 RVA;
|
|
||||||
|
|
||||||
#define RVA_INVALID UT64_MAX
|
|
||||||
|
|
||||||
inline QString RAddressString(RVA addr)
|
inline QString RAddressString(RVA addr)
|
||||||
{
|
{
|
||||||
return QString::asprintf("%#010llx", addr);
|
return QString::asprintf("%#010llx", addr);
|
||||||
|
@ -80,7 +80,8 @@ void XrefsDialog::on_fromTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int
|
|||||||
|
|
||||||
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
|
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
|
||||||
RAnalFunction *fcn = this->main->core->functionAt(xref.to);
|
RAnalFunction *fcn = this->main->core->functionAt(xref.to);
|
||||||
this->main->seek(xref.to, fcn ? QString::fromUtf8(fcn->name) : QString::null, true);
|
// TODO Seek
|
||||||
|
//this->main->seek(xref.to, fcn ? QString::fromUtf8(fcn->name) : QString::null, true);
|
||||||
|
|
||||||
this->close();
|
this->close();
|
||||||
}
|
}
|
||||||
@ -91,7 +92,8 @@ void XrefsDialog::on_toTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int c
|
|||||||
|
|
||||||
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
|
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
|
||||||
RAnalFunction *fcn = this->main->core->functionAt(xref.from);
|
RAnalFunction *fcn = this->main->core->functionAt(xref.from);
|
||||||
this->main->seek(xref.from, fcn ? QString::fromUtf8(fcn->name) : QString::null, true);
|
// TODO Seek
|
||||||
|
//this->main->seek(xref.from, fcn ? QString::fromUtf8(fcn->name) : QString::null, true);
|
||||||
|
|
||||||
this->close();
|
this->close();
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,8 @@ void CommentsWidget::on_commentsTreeWidget_itemDoubleClicked(QTreeWidgetItem *it
|
|||||||
// Get offset and name of item double clicked
|
// Get offset and name of item double clicked
|
||||||
CommentDescription comment = item->data(0, Qt::UserRole).value<CommentDescription>();
|
CommentDescription comment = item->data(0, Qt::UserRole).value<CommentDescription>();
|
||||||
this->main->addDebugOutput(RAddressString(comment.offset) + ": " + comment.name);
|
this->main->addDebugOutput(RAddressString(comment.offset) + ": " + comment.name);
|
||||||
this->main->seek(comment.offset, comment.name, true);
|
this->main->seek(comment.offset);
|
||||||
|
//this->main->seek(comment.offset, comment.name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommentsWidget::on_toolButton_clicked()
|
void CommentsWidget::on_toolButton_clicked()
|
||||||
|
@ -90,10 +90,11 @@ static bool isForbidden(const QString &input)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
ConsoleWidget::ConsoleWidget(CutterCore *core, QWidget *parent) :
|
ConsoleWidget::ConsoleWidget(MainWindow *main, QWidget *parent) :
|
||||||
QWidget(parent),
|
QWidget(parent),
|
||||||
ui(new Ui::ConsoleWidget),
|
ui(new Ui::ConsoleWidget),
|
||||||
core(core),
|
core(main->core),
|
||||||
|
main(main),
|
||||||
debugOutputEnabled(true),
|
debugOutputEnabled(true),
|
||||||
maxHistoryEntries(100),
|
maxHistoryEntries(100),
|
||||||
lastHistoryPosition(invalidHistoryPos)
|
lastHistoryPosition(invalidHistoryPos)
|
||||||
@ -175,6 +176,17 @@ void ConsoleWidget::focusInputLineEdit()
|
|||||||
ui->inputLineEdit->setFocus();
|
ui->inputLineEdit->setFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ConsoleWidget::executeCommand(QString command)
|
||||||
|
{
|
||||||
|
RVA offset = this->core->getOffset();
|
||||||
|
QString res = this->core->cmd(command);
|
||||||
|
RVA newOffset = this->core->getOffset();
|
||||||
|
if (offset != newOffset) {
|
||||||
|
emit main->seekChanged(newOffset);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void ConsoleWidget::on_inputLineEdit_returnPressed()
|
void ConsoleWidget::on_inputLineEdit_returnPressed()
|
||||||
{
|
{
|
||||||
QString input = ui->inputLineEdit->text();
|
QString input = ui->inputLineEdit->text();
|
||||||
@ -182,9 +194,9 @@ void ConsoleWidget::on_inputLineEdit_returnPressed()
|
|||||||
{
|
{
|
||||||
if (!isForbidden(input))
|
if (!isForbidden(input))
|
||||||
{
|
{
|
||||||
ui->outputTextEdit->appendPlainText(this->core->cmd(input));
|
QString res = executeCommand(input);
|
||||||
|
ui->outputTextEdit->appendPlainText(res);
|
||||||
scrollOutputToEnd();
|
scrollOutputToEnd();
|
||||||
|
|
||||||
historyAdd(input);
|
historyAdd(input);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -211,6 +223,7 @@ void ConsoleWidget::showCustomContextMenu(const QPoint &pt)
|
|||||||
|
|
||||||
void ConsoleWidget::syncWithCoreToggled(bool checked)
|
void ConsoleWidget::syncWithCoreToggled(bool checked)
|
||||||
{
|
{
|
||||||
|
// TODO Core and Cutter are always in sync
|
||||||
if (checked)
|
if (checked)
|
||||||
{
|
{
|
||||||
//Enable core syncronization
|
//Enable core syncronization
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include "MainWindow.h"
|
||||||
|
|
||||||
class CutterCore;
|
class CutterCore;
|
||||||
class QAction;
|
class QAction;
|
||||||
@ -18,7 +19,7 @@ class ConsoleWidget : public QWidget
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ConsoleWidget(CutterCore *core, QWidget *parent = 0);
|
explicit ConsoleWidget(MainWindow *main, QWidget *parent = 0);
|
||||||
~ConsoleWidget();
|
~ConsoleWidget();
|
||||||
|
|
||||||
void addOutput(const QString &msg);
|
void addOutput(const QString &msg);
|
||||||
@ -49,9 +50,11 @@ private:
|
|||||||
void scrollOutputToEnd();
|
void scrollOutputToEnd();
|
||||||
void historyAdd(const QString &input);
|
void historyAdd(const QString &input);
|
||||||
void invalidateHistoryPosition();
|
void invalidateHistoryPosition();
|
||||||
|
QString executeCommand(QString command);
|
||||||
|
|
||||||
std::unique_ptr<Ui::ConsoleWidget> ui;
|
std::unique_ptr<Ui::ConsoleWidget> ui;
|
||||||
CutterCore *core;
|
CutterCore *core;
|
||||||
|
MainWindow *main;
|
||||||
QList<QAction *> actions;
|
QList<QAction *> actions;
|
||||||
bool debugOutputEnabled;
|
bool debugOutputEnabled;
|
||||||
int maxHistoryEntries;
|
int maxHistoryEntries;
|
||||||
|
@ -14,11 +14,12 @@
|
|||||||
#undef max
|
#undef max
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterCore *core)
|
DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, MainWindow *main)
|
||||||
: QAbstractScrollArea(parent),
|
: QAbstractScrollArea(parent),
|
||||||
//currentGraph(duint(0)),
|
//currentGraph(duint(0)),
|
||||||
//disasm(ConfigUint("Disassembler", "MaxModuleSize")),
|
//disasm(ConfigUint("Disassembler", "MaxModuleSize")),
|
||||||
mCore(core),
|
mCore(main->core),
|
||||||
|
mMain(main),
|
||||||
mFontMetrics(nullptr),
|
mFontMetrics(nullptr),
|
||||||
syncOrigin(false),
|
syncOrigin(false),
|
||||||
mCip(0),
|
mCip(0),
|
||||||
@ -46,11 +47,17 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterCore *core)
|
|||||||
this->saveGraph = false;
|
this->saveGraph = false;
|
||||||
|
|
||||||
//Create timer to automatically refresh view when it needs to be updated
|
//Create timer to automatically refresh view when it needs to be updated
|
||||||
|
//this->updateTimer = new QTimer();
|
||||||
|
//this->updateTimer->setInterval(1000); // TODO Probably too slow
|
||||||
|
//this->updateTimer->setSingleShot(false);
|
||||||
|
//connect(this->updateTimer, SIGNAL(timeout()), this, SLOT(updateTimerEvent()));
|
||||||
|
//this->updateTimer->start();
|
||||||
|
// Remove above comments?
|
||||||
|
// Draw the first graph after 1s
|
||||||
this->updateTimer = new QTimer();
|
this->updateTimer = new QTimer();
|
||||||
this->updateTimer->setInterval(1000); // TODO Probably too slow
|
this->updateTimer->setSingleShot(true);
|
||||||
this->updateTimer->setSingleShot(false);
|
|
||||||
connect(this->updateTimer, SIGNAL(timeout()), this, SLOT(updateTimerEvent()));
|
connect(this->updateTimer, SIGNAL(timeout()), this, SLOT(updateTimerEvent()));
|
||||||
this->updateTimer->start();
|
this->updateTimer->start(1000);
|
||||||
|
|
||||||
this->initFont();
|
this->initFont();
|
||||||
|
|
||||||
@ -68,6 +75,7 @@ DisassemblerGraphView::DisassemblerGraphView(QWidget *parent, CutterCore *core)
|
|||||||
setupContextMenu();
|
setupContextMenu();
|
||||||
|
|
||||||
//Connect to bridge
|
//Connect to bridge
|
||||||
|
connect(main, SIGNAL(seekChanged(RVA)), this, SLOT(on_seekChanged(RVA)));
|
||||||
//connect(Bridge::getBridge(), SIGNAL(loadGraph(BridgeCFGraphList*, duint)), this, SLOT(loadGraphSlot(BridgeCFGraphList*, duint)));
|
//connect(Bridge::getBridge(), SIGNAL(loadGraph(BridgeCFGraphList*, duint)), this, SLOT(loadGraphSlot(BridgeCFGraphList*, duint)));
|
||||||
//connect(Bridge::getBridge(), SIGNAL(graphAt(duint)), this, SLOT(graphAtSlot(duint)));
|
//connect(Bridge::getBridge(), SIGNAL(graphAt(duint)), this, SLOT(graphAtSlot(duint)));
|
||||||
//connect(Bridge::getBridge(), SIGNAL(updateGraph()), this, SLOT(updateGraphSlot()));
|
//connect(Bridge::getBridge(), SIGNAL(updateGraph()), this, SLOT(updateGraphSlot()));
|
||||||
@ -1057,8 +1065,6 @@ static void initVec(std::vector<T> & vec, size_t size, T value)
|
|||||||
|
|
||||||
void DisassemblerGraphView::renderFunction(Function & func)
|
void DisassemblerGraphView::renderFunction(Function & func)
|
||||||
{
|
{
|
||||||
qDebug() << "Render function...";
|
|
||||||
|
|
||||||
//Create render nodes
|
//Create render nodes
|
||||||
this->blocks.clear();
|
this->blocks.clear();
|
||||||
for(Block & block : func.blocks)
|
for(Block & block : func.blocks)
|
||||||
@ -1397,6 +1403,9 @@ void DisassemblerGraphView::renderFunction(Function & func)
|
|||||||
|
|
||||||
void DisassemblerGraphView::updateTimerEvent()
|
void DisassemblerGraphView::updateTimerEvent()
|
||||||
{
|
{
|
||||||
|
on_seekChanged(0);
|
||||||
|
return;
|
||||||
|
// TODO: Remove it if not used anymore
|
||||||
//qDebug() << status << this->status << this->function << this->ready << this->update_id << this->analysis.update_id;
|
//qDebug() << status << this->status << this->function << this->ready << this->update_id << this->analysis.update_id;
|
||||||
// TODO status is useless (for now at least)
|
// TODO status is useless (for now at least)
|
||||||
auto status = this->analysis.status;
|
auto status = this->analysis.status;
|
||||||
@ -1406,9 +1415,7 @@ void DisassemblerGraphView::updateTimerEvent()
|
|||||||
this->viewport()->update();
|
this->viewport()->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Dirty hack // TODO Use a global slot for seek command (xarkes)
|
if(this->function == 0)
|
||||||
auto s = sdb_atoi(mCore->cmd("s").toLocal8Bit().constData());
|
|
||||||
if(this->function == 0 || this->function != s)
|
|
||||||
{
|
{
|
||||||
loadCurrentGraph();
|
loadCurrentGraph();
|
||||||
return;
|
return;
|
||||||
@ -1549,8 +1556,6 @@ void DisassemblerGraphView::loadCurrentGraph()
|
|||||||
f.ready = true;
|
f.ready = true;
|
||||||
f.entry = func["offset"].toInt();
|
f.entry = func["offset"].toInt();
|
||||||
f.update_id = anal.update_id;
|
f.update_id = anal.update_id;
|
||||||
// TODO TMP HACK // TODO Use global slot for seeking (xarkes)
|
|
||||||
mCore->cmd(QString("s %1").arg(f.entry));
|
|
||||||
|
|
||||||
for (QJsonValueRef blockRef : func["blocks"].toArray()) {
|
for (QJsonValueRef blockRef : func["blocks"].toArray()) {
|
||||||
QJsonObject block = blockRef.toObject();
|
QJsonObject block = blockRef.toObject();
|
||||||
@ -1720,6 +1725,13 @@ void DisassemblerGraphView::loadCurrentGraph()
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void DisassemblerGraphView::on_seekChanged(RVA addr)
|
||||||
|
{
|
||||||
|
Q_UNUSED(addr);
|
||||||
|
loadCurrentGraph();
|
||||||
|
this->renderFunction(this->analysis.functions[this->function]);
|
||||||
|
}
|
||||||
|
|
||||||
void DisassemblerGraphView::graphAtSlot(duint addr)
|
void DisassemblerGraphView::graphAtSlot(duint addr)
|
||||||
{
|
{
|
||||||
Q_UNUSED(addr);
|
Q_UNUSED(addr);
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include "cutter.h"
|
#include "cutter.h"
|
||||||
|
#include "MainWindow.h"
|
||||||
#include "utils/RichTextPainter.h"
|
#include "utils/RichTextPainter.h"
|
||||||
#include "utils/CachedFontMetrics.h"
|
#include "utils/CachedFontMetrics.h"
|
||||||
|
|
||||||
@ -207,7 +208,7 @@ public:
|
|||||||
Narrow,
|
Narrow,
|
||||||
};
|
};
|
||||||
|
|
||||||
DisassemblerGraphView(QWidget *parent, CutterCore *core);
|
DisassemblerGraphView(QWidget *parent, MainWindow *main);
|
||||||
~DisassemblerGraphView();
|
~DisassemblerGraphView();
|
||||||
void initFont();
|
void initFont();
|
||||||
void adjustSize(int width, int height);
|
void adjustSize(int width, int height);
|
||||||
@ -257,6 +258,7 @@ signals:
|
|||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void updateTimerEvent();
|
void updateTimerEvent();
|
||||||
|
void on_seekChanged(RVA);
|
||||||
//void loadGraphSlot(BridgeCFGraphList* graph, duint addr);
|
//void loadGraphSlot(BridgeCFGraphList* graph, duint addr);
|
||||||
void graphAtSlot(duint addr);
|
void graphAtSlot(duint addr);
|
||||||
void updateGraphSlot();
|
void updateGraphSlot();
|
||||||
@ -285,6 +287,7 @@ public slots:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
CutterCore *mCore;
|
CutterCore *mCore;
|
||||||
|
MainWindow *mMain;
|
||||||
QString status;
|
QString status;
|
||||||
Analysis analysis;
|
Analysis analysis;
|
||||||
duint function;
|
duint function;
|
||||||
|
@ -60,5 +60,6 @@ void EntrypointWidget::setScrollMode()
|
|||||||
void EntrypointWidget::on_entrypointTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int /* column */)
|
void EntrypointWidget::on_entrypointTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int /* column */)
|
||||||
{
|
{
|
||||||
EntrypointDescription ep = item->data(0, Qt::UserRole).value<EntrypointDescription>();
|
EntrypointDescription ep = item->data(0, Qt::UserRole).value<EntrypointDescription>();
|
||||||
this->main->seek(ep.vaddr, ep.type, true);
|
this->main->seek(ep.vaddr);
|
||||||
|
//this->main->seek(ep.vaddr, ep.type, true);
|
||||||
}
|
}
|
||||||
|
@ -179,5 +179,5 @@ void ExportsWidget::setScrollMode()
|
|||||||
void ExportsWidget::on_exportsTreeView_doubleClicked(const QModelIndex &index)
|
void ExportsWidget::on_exportsTreeView_doubleClicked(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
ExportDescription exp = index.data(ExportsModel::ExportDescriptionRole).value<ExportDescription>();
|
ExportDescription exp = index.data(ExportsModel::ExportDescriptionRole).value<ExportDescription>();
|
||||||
this->main->seek(exp.vaddr, exp.flag_name, true);
|
this->main->seek(exp.vaddr);
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ void FlagsWidget::refresh()
|
|||||||
void FlagsWidget::on_flagsTreeView_doubleClicked(const QModelIndex &index)
|
void FlagsWidget::on_flagsTreeView_doubleClicked(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
FlagDescription flag = index.data(FlagsModel::FlagDescriptionRole).value<FlagDescription>();
|
FlagDescription flag = index.data(FlagsModel::FlagDescriptionRole).value<FlagDescription>();
|
||||||
this->main->seek(flag.offset, flag.name, true);
|
this->main->seek(flag.offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlagsWidget::on_flagspaceCombo_currentTextChanged(const QString &arg1)
|
void FlagsWidget::on_flagspaceCombo_currentTextChanged(const QString &arg1)
|
||||||
|
@ -219,8 +219,6 @@ void FunctionModel::cursorAddressChanged(RVA)
|
|||||||
|
|
||||||
void FunctionModel::updateCurrentIndex()
|
void FunctionModel::updateCurrentIndex()
|
||||||
{
|
{
|
||||||
RVA addr = main->getCursorAddress();
|
|
||||||
|
|
||||||
int index = -1;
|
int index = -1;
|
||||||
RVA offset = 0;
|
RVA offset = 0;
|
||||||
|
|
||||||
@ -228,7 +226,7 @@ void FunctionModel::updateCurrentIndex()
|
|||||||
{
|
{
|
||||||
const FunctionDescription &function = functions->at(i);
|
const FunctionDescription &function = functions->at(i);
|
||||||
|
|
||||||
if (function.contains(addr)
|
if (function.contains(this->main->core->getOffset())
|
||||||
&& function.offset >= offset)
|
&& function.offset >= offset)
|
||||||
{
|
{
|
||||||
offset = function.offset;
|
offset = function.offset;
|
||||||
@ -424,7 +422,7 @@ QTreeView *FunctionsWidget::getCurrentTreeView()
|
|||||||
void FunctionsWidget::functionsTreeView_doubleClicked(const QModelIndex &index)
|
void FunctionsWidget::functionsTreeView_doubleClicked(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
FunctionDescription function = index.data(FunctionModel::FunctionDescriptionRole).value<FunctionDescription>();
|
FunctionDescription function = index.data(FunctionModel::FunctionDescriptionRole).value<FunctionDescription>();
|
||||||
this->main->seek(function.offset, function.name, true);
|
this->main->seek(function.offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionsWidget::showFunctionsContextMenu(const QPoint &pt)
|
void FunctionsWidget::showFunctionsContextMenu(const QPoint &pt)
|
||||||
@ -462,7 +460,7 @@ void FunctionsWidget::on_actionDisasAdd_comment_triggered()
|
|||||||
// Rename function in r2 core
|
// Rename function in r2 core
|
||||||
this->main->core->setComment(function.offset, comment);
|
this->main->core->setComment(function.offset, comment);
|
||||||
// Seek to new renamed function
|
// Seek to new renamed function
|
||||||
this->main->seek(function.offset, function.name);
|
this->main->seek(function.offset);
|
||||||
// TODO: Refresh functions tree widget
|
// TODO: Refresh functions tree widget
|
||||||
}
|
}
|
||||||
this->main->refreshComments();
|
this->main->refreshComments();
|
||||||
|
@ -108,5 +108,5 @@ void ImportsWidget::setScrollMode()
|
|||||||
void ImportsWidget::on_importsTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int /* column */)
|
void ImportsWidget::on_importsTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, int /* column */)
|
||||||
{
|
{
|
||||||
ImportDescription imp = item->data(0, Qt::UserRole).value<ImportDescription>();
|
ImportDescription imp = item->data(0, Qt::UserRole).value<ImportDescription>();
|
||||||
this->main->seek(imp.plt, imp.name, true);
|
this->main->seek(imp.plt);
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ MemoryWidget::MemoryWidget(MainWindow *main) :
|
|||||||
|
|
||||||
// Create Graph View
|
// Create Graph View
|
||||||
ui->tabGraph->setLayout(new QGridLayout);
|
ui->tabGraph->setLayout(new QGridLayout);
|
||||||
mGraphView = new DisassemblerGraphView(ui->tabGraph, main->core);
|
mGraphView = new DisassemblerGraphView(ui->tabGraph, main);
|
||||||
ui->tabGraph->layout()->addWidget(mGraphView);
|
ui->tabGraph->layout()->addWidget(mGraphView);
|
||||||
|
|
||||||
// Space to switch between disassembly and graph
|
// Space to switch between disassembly and graph
|
||||||
@ -174,7 +174,7 @@ MemoryWidget::MemoryWidget(MainWindow *main) :
|
|||||||
connect(this->disasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
|
connect(this->disasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
|
||||||
connect(this->hexASCIIText->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hexScrolled()));
|
connect(this->hexASCIIText->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hexScrolled()));
|
||||||
|
|
||||||
connect(main, SIGNAL(globalSeekTo(RVA)), this, SLOT(on_globalSeekTo(RVA)));
|
connect(main, SIGNAL(seekChanged(RVA)), this, SLOT(on_seekChanged(RVA)));
|
||||||
connect(main, SIGNAL(cursorAddressChanged(RVA)), this, SLOT(on_cursorAddressChanged(RVA)));
|
connect(main, SIGNAL(cursorAddressChanged(RVA)), this, SLOT(on_cursorAddressChanged(RVA)));
|
||||||
connect(main->core, SIGNAL(flagsChanged()), this, SLOT(updateViews()));
|
connect(main->core, SIGNAL(flagsChanged()), this, SLOT(updateViews()));
|
||||||
connect(main->core, SIGNAL(commentsChanged()), this, SLOT(updateViews()));
|
connect(main->core, SIGNAL(commentsChanged()), this, SLOT(updateViews()));
|
||||||
@ -184,7 +184,7 @@ MemoryWidget::MemoryWidget(MainWindow *main) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MemoryWidget::on_globalSeekTo(RVA addr)
|
void MemoryWidget::on_seekChanged(RVA addr)
|
||||||
{
|
{
|
||||||
updateViews(addr);
|
updateViews(addr);
|
||||||
}
|
}
|
||||||
@ -420,7 +420,7 @@ void MemoryWidget::refresh()
|
|||||||
setScrollMode();
|
setScrollMode();
|
||||||
|
|
||||||
// TODO: honor the offset
|
// TODO: honor the offset
|
||||||
updateViews();
|
updateViews(RVA_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -451,11 +451,10 @@ void MemoryWidget::replaceTextDisasm(QString txt)
|
|||||||
bool MemoryWidget::loadMoreDisassembly()
|
bool MemoryWidget::loadMoreDisassembly()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
z * Add more disasm as the user scrolls
|
* Add more disasm as the user scrolls
|
||||||
* Not working properly when scrolling upwards
|
* Not working properly when scrolling upwards
|
||||||
* r2 doesn't handle properly 'pd-' for archs with variable instruction size
|
* r2 doesn't handle properly 'pd-' for archs with variable instruction size
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Disconnect scroll signals to add more content
|
// Disconnect scroll signals to add more content
|
||||||
disconnect(this->disasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
|
disconnect(this->disasTextEdit->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(disasmScrolled()));
|
||||||
|
|
||||||
@ -732,12 +731,10 @@ void MemoryWidget::seek_to(const QString &offset)
|
|||||||
this->disasTextEdit->moveCursor(QTextCursor::End);
|
this->disasTextEdit->moveCursor(QTextCursor::End);
|
||||||
this->disasTextEdit->find(offset, QTextDocument::FindBackward);
|
this->disasTextEdit->find(offset, QTextDocument::FindBackward);
|
||||||
this->disasTextEdit->moveCursor(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
|
this->disasTextEdit->moveCursor(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
|
||||||
//this->main->add_debug_output("OFFSET: " + offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryWidget::resizeHexdump()
|
void MemoryWidget::resizeHexdump()
|
||||||
{
|
{
|
||||||
//qDebug() << "size: " << ui->hexHexText->document()->size().width();
|
|
||||||
this->hexOffsetText->setMinimumWidth(this->hexOffsetText->document()->size().width());
|
this->hexOffsetText->setMinimumWidth(this->hexOffsetText->document()->size().width());
|
||||||
this->hexHexText->setMinimumWidth(this->hexHexText->document()->size().width());
|
this->hexHexText->setMinimumWidth(this->hexHexText->document()->size().width());
|
||||||
this->hexASCIIText->setMinimumWidth(this->hexASCIIText->document()->size().width());
|
this->hexASCIIText->setMinimumWidth(this->hexASCIIText->document()->size().width());
|
||||||
@ -1219,7 +1216,7 @@ void MemoryWidget::on_actionDisasAdd_comment_triggered()
|
|||||||
// Seek to new renamed function
|
// Seek to new renamed function
|
||||||
if (fcn)
|
if (fcn)
|
||||||
{
|
{
|
||||||
this->main->seek(fcn->name);
|
this->main->seek(fcn->addr);
|
||||||
}
|
}
|
||||||
// TODO: Refresh functions tree widget
|
// TODO: Refresh functions tree widget
|
||||||
}
|
}
|
||||||
@ -1265,7 +1262,7 @@ void MemoryWidget::on_actionFunctionsRename_triggered()
|
|||||||
// Rename function in r2 core
|
// Rename function in r2 core
|
||||||
this->main->core->renameFunction(fcn->name, new_name);
|
this->main->core->renameFunction(fcn->name, new_name);
|
||||||
// Seek to new renamed function
|
// Seek to new renamed function
|
||||||
this->main->seek(new_name);
|
this->main->seek(fcn->addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->main->refreshFunctions();
|
this->main->refreshFunctions();
|
||||||
@ -1316,15 +1313,13 @@ void MemoryWidget::on_action1column_triggered()
|
|||||||
void MemoryWidget::on_xreFromTreeWidget_2_itemDoubleClicked(QTreeWidgetItem *item, int /*column*/)
|
void MemoryWidget::on_xreFromTreeWidget_2_itemDoubleClicked(QTreeWidgetItem *item, int /*column*/)
|
||||||
{
|
{
|
||||||
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
|
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
|
||||||
RAnalFunction *fcn = this->main->core->functionAt(xref.to);
|
this->main->seek(xref.to);
|
||||||
this->main->seek(xref.to, fcn ? QString::fromUtf8(fcn->name) : QString::null, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryWidget::on_xrefToTreeWidget_2_itemDoubleClicked(QTreeWidgetItem *item, int /*column*/)
|
void MemoryWidget::on_xrefToTreeWidget_2_itemDoubleClicked(QTreeWidgetItem *item, int /*column*/)
|
||||||
{
|
{
|
||||||
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
|
XrefDescription xref = item->data(0, Qt::UserRole).value<XrefDescription>();
|
||||||
RAnalFunction *fcn = this->main->core->functionAt(xref.from);
|
this->main->seek(xref.from);
|
||||||
this->main->seek(xref.from, fcn ? QString::fromUtf8(fcn->name) : QString::null, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryWidget::on_xrefFromToolButton_2_clicked()
|
void MemoryWidget::on_xrefFromToolButton_2_clicked()
|
||||||
@ -1754,12 +1749,14 @@ bool MemoryWidget::eventFilter(QObject *obj, QEvent *event)
|
|||||||
QString fcn = this->main->core->cmdFunctionAt(jump);
|
QString fcn = this->main->core->cmdFunctionAt(jump);
|
||||||
if (!fcn.isEmpty())
|
if (!fcn.isEmpty())
|
||||||
{
|
{
|
||||||
this->main->seek(jump.trimmed(), fcn);
|
RVA addr = jump.trimmed().toULongLong(0, 16);
|
||||||
|
this->main->seek(addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->main->seek(this->main->core->cmd("?v " + jump), jump);
|
RVA addr = this->main->core->cmd("?v " + jump).toULongLong(0, 16);
|
||||||
|
this->main->seek(addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1838,7 +1835,7 @@ void MemoryWidget::on_memTabWidget_currentChanged(int /*index*/)
|
|||||||
this->main->add_debug_output("Last disasm: " + RAddressString(this->last_disasm_fcn));
|
this->main->add_debug_output("Last disasm: " + RAddressString(this->last_disasm_fcn));
|
||||||
this->main->add_debug_output("Last graph: " + RAddressString(this->last_graph_fcn));
|
this->main->add_debug_output("Last graph: " + RAddressString(this->last_graph_fcn));
|
||||||
this->main->add_debug_output("Last hexdump: " + RAddressString(this->last_hexdump_fcn));*/
|
this->main->add_debug_output("Last hexdump: " + RAddressString(this->last_hexdump_fcn));*/
|
||||||
this->updateViews();
|
this->updateViews(RVA_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryWidget::updateViews(RVA offset)
|
void MemoryWidget::updateViews(RVA offset)
|
||||||
|
@ -88,8 +88,6 @@ public slots:
|
|||||||
|
|
||||||
void selectHexPreview();
|
void selectHexPreview();
|
||||||
|
|
||||||
void updateViews(RVA offset = RVA_INVALID);
|
|
||||||
|
|
||||||
void showOffsets(bool show);
|
void showOffsets(bool show);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -117,8 +115,8 @@ private:
|
|||||||
bool loadMoreDisassembly();
|
bool loadMoreDisassembly();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_globalSeekTo(RVA addr);
|
|
||||||
void on_cursorAddressChanged(RVA addr);
|
void on_cursorAddressChanged(RVA addr);
|
||||||
|
void on_seekChanged(RVA addr);
|
||||||
|
|
||||||
void highlightCurrentLine();
|
void highlightCurrentLine();
|
||||||
|
|
||||||
@ -165,9 +163,7 @@ private slots:
|
|||||||
QList<QString> get_hexdump(const QString &offset);
|
QList<QString> get_hexdump(const QString &offset);
|
||||||
|
|
||||||
void showXrefsDialog();
|
void showXrefsDialog();
|
||||||
//void showDisas();
|
void updateViews(RVA offset = RVA_INVALID);
|
||||||
//void showHexdump();
|
|
||||||
//void showGraph();
|
|
||||||
void cycleViews();
|
void cycleViews();
|
||||||
void on_xreFromTreeWidget_2_itemDoubleClicked(QTreeWidgetItem *item, int column);
|
void on_xreFromTreeWidget_2_itemDoubleClicked(QTreeWidgetItem *item, int column);
|
||||||
void on_xrefToTreeWidget_2_itemDoubleClicked(QTreeWidgetItem *item, int column);
|
void on_xrefToTreeWidget_2_itemDoubleClicked(QTreeWidgetItem *item, int column);
|
||||||
|
@ -176,7 +176,7 @@ void Omnibar::on_gotoEntry_returnPressed()
|
|||||||
{
|
{
|
||||||
//this->main->seek(this->main->core->cmd("?v " + this->text()), this->text());
|
//this->main->seek(this->main->core->cmd("?v " + this->text()), this->text());
|
||||||
QString off = this->main->core->cmd("afo " + this->text());
|
QString off = this->main->core->cmd("afo " + this->text());
|
||||||
this->main->seek(off.trimmed(), this->text(), true);
|
this->main->seek(off.trimmed().toInt());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ void RelocsWidget::on_relocsTreeWidget_itemDoubleClicked(QTreeWidgetItem *item,
|
|||||||
|
|
||||||
// Get offset and name of item double clicked
|
// Get offset and name of item double clicked
|
||||||
RelocDescription reloc = item->data(0, Qt::UserRole).value<RelocDescription>();
|
RelocDescription reloc = item->data(0, Qt::UserRole).value<RelocDescription>();
|
||||||
main->seek(reloc.vaddr, reloc.name, true);
|
main->seek(reloc.vaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RelocsWidget::fillTreeWidget()
|
void RelocsWidget::fillTreeWidget()
|
||||||
|
@ -38,7 +38,7 @@ void StringsWidget::on_stringsTreeWidget_itemDoubleClicked(QTreeWidgetItem *item
|
|||||||
// Get offset and name of item double clicked
|
// Get offset and name of item double clicked
|
||||||
// TODO: use this info to change disasm contents
|
// TODO: use this info to change disasm contents
|
||||||
StringDescription str = item->data(0, Qt::UserRole).value<StringDescription>();
|
StringDescription str = item->data(0, Qt::UserRole).value<StringDescription>();
|
||||||
this->main->seek(str.vaddr, NULL, true);
|
this->main->seek(str.vaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringsWidget::fillTreeWidget()
|
void StringsWidget::fillTreeWidget()
|
||||||
|
@ -37,7 +37,7 @@ void SymbolsWidget::on_symbolsTreeWidget_itemDoubleClicked(QTreeWidgetItem *item
|
|||||||
|
|
||||||
// Get offset and name of item double clicked
|
// Get offset and name of item double clicked
|
||||||
SymbolDescription symbol = item->data(0, Qt::UserRole).value<SymbolDescription>();
|
SymbolDescription symbol = item->data(0, Qt::UserRole).value<SymbolDescription>();
|
||||||
this->main->seek(symbol.vaddr, symbol.name, true);
|
this->main->seek(symbol.vaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SymbolsWidget::fillSymbols()
|
void SymbolsWidget::fillSymbols()
|
||||||
|
Loading…
Reference in New Issue
Block a user