mirror of
https://github.com/rizinorg/cutter.git
synced 2024-12-19 11:26:11 +00:00
46d29c1e85
* Added flirt view and removed zignature one * Added menu for apply signature from file and create new sig/pat files * Updated rizin to dev branch
4246 lines
114 KiB
C++
4246 lines
114 KiB
C++
#include <QJsonArray>
|
|
#include <QJsonObject>
|
|
#include <QRegularExpression>
|
|
#include <QDir>
|
|
#include <QCoreApplication>
|
|
#include <QVector>
|
|
#include <QStringList>
|
|
#include <QStandardPaths>
|
|
|
|
#include <cassert>
|
|
#include <memory>
|
|
|
|
#include "common/TempConfig.h"
|
|
#include "common/BasicInstructionHighlighter.h"
|
|
#include "common/Configuration.h"
|
|
#include "common/AsyncTask.h"
|
|
#include "common/RizinTask.h"
|
|
#include "dialogs/RizinTaskDialog.h"
|
|
#include "common/Json.h"
|
|
#include "core/Cutter.h"
|
|
#include "Decompiler.h"
|
|
|
|
#include <rz_asm.h>
|
|
#include <rz_cmd.h>
|
|
#include <sdb.h>
|
|
|
|
Q_GLOBAL_STATIC(CutterCore, uniqueInstance)
|
|
|
|
#define RZ_JSON_KEY(name) static const QString name = QStringLiteral(#name)
|
|
|
|
namespace RJsonKey {
|
|
RZ_JSON_KEY(addr);
|
|
RZ_JSON_KEY(addrs);
|
|
RZ_JSON_KEY(addr_end);
|
|
RZ_JSON_KEY(arrow);
|
|
RZ_JSON_KEY(baddr);
|
|
RZ_JSON_KEY(bind);
|
|
RZ_JSON_KEY(blocks);
|
|
RZ_JSON_KEY(blocksize);
|
|
RZ_JSON_KEY(bytes);
|
|
RZ_JSON_KEY(calltype);
|
|
RZ_JSON_KEY(cc);
|
|
RZ_JSON_KEY(classname);
|
|
RZ_JSON_KEY(code);
|
|
RZ_JSON_KEY(comment);
|
|
RZ_JSON_KEY(comments);
|
|
RZ_JSON_KEY(cost);
|
|
RZ_JSON_KEY(data);
|
|
RZ_JSON_KEY(description);
|
|
RZ_JSON_KEY(ebbs);
|
|
RZ_JSON_KEY(edges);
|
|
RZ_JSON_KEY(enabled);
|
|
RZ_JSON_KEY(entropy);
|
|
RZ_JSON_KEY(fcn_addr);
|
|
RZ_JSON_KEY(fcn_name);
|
|
RZ_JSON_KEY(fields);
|
|
RZ_JSON_KEY(file);
|
|
RZ_JSON_KEY(flags);
|
|
RZ_JSON_KEY(flagname);
|
|
RZ_JSON_KEY(format);
|
|
RZ_JSON_KEY(from);
|
|
RZ_JSON_KEY(functions);
|
|
RZ_JSON_KEY(graph);
|
|
RZ_JSON_KEY(haddr);
|
|
RZ_JSON_KEY(hw);
|
|
RZ_JSON_KEY(in_functions);
|
|
RZ_JSON_KEY(index);
|
|
RZ_JSON_KEY(jump);
|
|
RZ_JSON_KEY(laddr);
|
|
RZ_JSON_KEY(lang);
|
|
RZ_JSON_KEY(len);
|
|
RZ_JSON_KEY(length);
|
|
RZ_JSON_KEY(license);
|
|
RZ_JSON_KEY(methods);
|
|
RZ_JSON_KEY(name);
|
|
RZ_JSON_KEY(realname);
|
|
RZ_JSON_KEY(nargs);
|
|
RZ_JSON_KEY(nbbs);
|
|
RZ_JSON_KEY(nlocals);
|
|
RZ_JSON_KEY(offset);
|
|
RZ_JSON_KEY(opcode);
|
|
RZ_JSON_KEY(opcodes);
|
|
RZ_JSON_KEY(ordinal);
|
|
RZ_JSON_KEY(libname);
|
|
RZ_JSON_KEY(outdegree);
|
|
RZ_JSON_KEY(paddr);
|
|
RZ_JSON_KEY(path);
|
|
RZ_JSON_KEY(perm);
|
|
RZ_JSON_KEY(pid);
|
|
RZ_JSON_KEY(plt);
|
|
RZ_JSON_KEY(prot);
|
|
RZ_JSON_KEY(ref);
|
|
RZ_JSON_KEY(refs);
|
|
RZ_JSON_KEY(reg);
|
|
RZ_JSON_KEY(rwx);
|
|
RZ_JSON_KEY(section);
|
|
RZ_JSON_KEY(sections);
|
|
RZ_JSON_KEY(size);
|
|
RZ_JSON_KEY(stackframe);
|
|
RZ_JSON_KEY(status);
|
|
RZ_JSON_KEY(string);
|
|
RZ_JSON_KEY(strings);
|
|
RZ_JSON_KEY(symbols);
|
|
RZ_JSON_KEY(text);
|
|
RZ_JSON_KEY(to);
|
|
RZ_JSON_KEY(trace);
|
|
RZ_JSON_KEY(type);
|
|
RZ_JSON_KEY(uid);
|
|
RZ_JSON_KEY(vaddr);
|
|
RZ_JSON_KEY(value);
|
|
RZ_JSON_KEY(vsize);
|
|
}
|
|
|
|
#undef RZ_JSON_KEY
|
|
|
|
static void updateOwnedCharPtr(char *&variable, const QString &newValue)
|
|
{
|
|
auto data = newValue.toUtf8();
|
|
RZ_FREE(variable)
|
|
variable = strdup(data.data());
|
|
}
|
|
|
|
static QString fromOwnedCharPtr(char *str)
|
|
{
|
|
QString result(str ? str : "");
|
|
rz_mem_free(str);
|
|
return result;
|
|
}
|
|
|
|
RzCoreLocked::RzCoreLocked(CutterCore *core) : core(core)
|
|
{
|
|
core->coreMutex.lock();
|
|
assert(core->coreLockDepth >= 0);
|
|
core->coreLockDepth++;
|
|
if (core->coreLockDepth == 1) {
|
|
assert(core->coreBed);
|
|
rz_cons_sleep_end(core->coreBed);
|
|
core->coreBed = nullptr;
|
|
}
|
|
}
|
|
|
|
RzCoreLocked::~RzCoreLocked()
|
|
{
|
|
assert(core->coreLockDepth > 0);
|
|
core->coreLockDepth--;
|
|
if (core->coreLockDepth == 0) {
|
|
core->coreBed = rz_cons_sleep_begin();
|
|
}
|
|
core->coreMutex.unlock();
|
|
}
|
|
|
|
RzCoreLocked::operator RzCore *() const
|
|
{
|
|
return core->core_;
|
|
}
|
|
|
|
RzCore *RzCoreLocked::operator->() const
|
|
{
|
|
return core->core_;
|
|
}
|
|
|
|
#define CORE_LOCK() RzCoreLocked core(this)
|
|
|
|
static void cutterREventCallback(RzEvent *, int type, void *user, void *data)
|
|
{
|
|
auto core = reinterpret_cast<CutterCore *>(user);
|
|
core->handleREvent(type, data);
|
|
}
|
|
|
|
CutterCore::CutterCore(QObject *parent)
|
|
: QObject(parent)
|
|
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
|
|
,
|
|
coreMutex(QMutex::Recursive)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
CutterCore *CutterCore::instance()
|
|
{
|
|
return uniqueInstance;
|
|
}
|
|
|
|
void CutterCore::initialize(bool loadPlugins)
|
|
{
|
|
auto prefix = QDir(QCoreApplication::applicationDirPath());
|
|
|
|
#if defined(CUTTER_ENABLE_PACKAGING) && defined(Q_OS_WIN)
|
|
auto prefixBytes = prefix.absolutePath().toUtf8();
|
|
rz_path_prefix(prefixBytes.constData());
|
|
#endif
|
|
|
|
rz_cons_new(); // initialize console
|
|
core_ = rz_core_new();
|
|
rz_core_task_sync_begin(&core_->tasks);
|
|
coreBed = rz_cons_sleep_begin();
|
|
CORE_LOCK();
|
|
|
|
rz_event_hook(core_->analysis->ev, RZ_EVENT_ALL, cutterREventCallback, this);
|
|
|
|
#if defined(APPIMAGE) || defined(MACOS_RZ_BUNDLED)
|
|
# ifdef APPIMAGE
|
|
// Executable is in appdir/bin
|
|
prefix.cdUp();
|
|
qInfo() << "Setting Rizin prefix =" << prefix.absolutePath() << " for AppImage.";
|
|
# else // MACOS_RZ_BUNDLED
|
|
// Executable is in Contents/MacOS, prefix is Contents/Resources/rz
|
|
prefix.cdUp();
|
|
prefix.cd("Resources");
|
|
qInfo() << "Setting Rizin prefix =" << prefix.absolutePath()
|
|
<< " for macOS Application Bundle.";
|
|
setConfig("dir.prefix", prefix.absolutePath());
|
|
# endif
|
|
|
|
auto pluginsDir = prefix;
|
|
if (pluginsDir.cd("share/rizin/plugins")) {
|
|
qInfo() << "Setting Rizin plugins dir =" << pluginsDir.absolutePath();
|
|
setConfig("dir.plugins", pluginsDir.absolutePath());
|
|
} else {
|
|
qInfo() << "Rizin plugins dir under" << pluginsDir.absolutePath() << "does not exist!";
|
|
}
|
|
#endif
|
|
|
|
if (!loadPlugins) {
|
|
setConfig("cfg.plugins", 0);
|
|
}
|
|
if (getConfigi("cfg.plugins")) {
|
|
rz_core_loadlibs(this->core_, RZ_CORE_LOADLIBS_ALL);
|
|
}
|
|
// IMPLICIT rz_bin_iobind (core_->bin, core_->io);
|
|
|
|
// Otherwise Rizin may ask the user for input and Cutter would freeze
|
|
setConfig("scr.interactive", false);
|
|
|
|
// Initialize graph node highlighter
|
|
bbHighlighter = new BasicBlockHighlighter();
|
|
|
|
// Initialize Async tasks manager
|
|
asyncTaskManager = new AsyncTaskManager(this);
|
|
}
|
|
|
|
CutterCore::~CutterCore()
|
|
{
|
|
delete bbHighlighter;
|
|
rz_cons_sleep_end(coreBed);
|
|
rz_core_task_sync_end(&core_->tasks);
|
|
rz_core_free(this->core_);
|
|
rz_cons_free();
|
|
}
|
|
|
|
RzCoreLocked CutterCore::core()
|
|
{
|
|
return RzCoreLocked(this);
|
|
}
|
|
|
|
QDir CutterCore::getCutterRCDefaultDirectory() const
|
|
{
|
|
return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
|
|
}
|
|
|
|
QVector<QString> CutterCore::getCutterRCFilePaths() const
|
|
{
|
|
QVector<QString> result;
|
|
result.push_back(QFileInfo(QDir::home(), ".cutterrc").absoluteFilePath());
|
|
QStringList locations = QStandardPaths::standardLocations(QStandardPaths::AppConfigLocation);
|
|
for (auto &location : locations) {
|
|
result.push_back(QFileInfo(QDir(location), ".cutterrc").absoluteFilePath());
|
|
}
|
|
result.push_back(QFileInfo(getCutterRCDefaultDirectory(), "rc")
|
|
.absoluteFilePath()); // File in config editor is from this path
|
|
return result;
|
|
}
|
|
|
|
void CutterCore::loadCutterRC()
|
|
{
|
|
CORE_LOCK();
|
|
const auto result = getCutterRCFilePaths();
|
|
for (auto &cutterRCFilePath : result) {
|
|
auto cutterRCFileInfo = QFileInfo(cutterRCFilePath);
|
|
if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) {
|
|
continue;
|
|
}
|
|
qInfo() << "Loading initialization file from " << cutterRCFilePath;
|
|
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
|
|
}
|
|
}
|
|
|
|
void CutterCore::loadDefaultCutterRC()
|
|
{
|
|
CORE_LOCK();
|
|
auto cutterRCFilePath = QFileInfo(getCutterRCDefaultDirectory(), "rc").absoluteFilePath();
|
|
const auto cutterRCFileInfo = QFileInfo(cutterRCFilePath);
|
|
if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) {
|
|
return;
|
|
}
|
|
qInfo() << "Loading initialization file from " << cutterRCFilePath;
|
|
rz_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
|
|
}
|
|
|
|
QList<QString> CutterCore::sdbList(QString path)
|
|
{
|
|
CORE_LOCK();
|
|
QList<QString> list = QList<QString>();
|
|
Sdb *root = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);
|
|
if (root) {
|
|
void *vsi;
|
|
ls_iter_t *iter;
|
|
ls_foreach(root->ns, iter, vsi)
|
|
{
|
|
SdbNs *nsi = (SdbNs *)vsi;
|
|
list << nsi->name;
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
using SdbListPtr = std::unique_ptr<SdbList, decltype(&ls_free)>;
|
|
static SdbListPtr makeSdbListPtr(SdbList *list)
|
|
{
|
|
return { list, ls_free };
|
|
}
|
|
|
|
QList<QString> CutterCore::sdbListKeys(QString path)
|
|
{
|
|
CORE_LOCK();
|
|
QList<QString> list = QList<QString>();
|
|
Sdb *root = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);
|
|
if (root) {
|
|
void *vsi;
|
|
ls_iter_t *iter;
|
|
SdbListPtr l = makeSdbListPtr(sdb_foreach_list(root, false));
|
|
ls_foreach(l, iter, vsi)
|
|
{
|
|
SdbKv *nsi = (SdbKv *)vsi;
|
|
list << reinterpret_cast<char *>(nsi->base.key);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
QString CutterCore::sdbGet(QString path, QString key)
|
|
{
|
|
CORE_LOCK();
|
|
Sdb *db = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);
|
|
if (db) {
|
|
const char *val = sdb_const_get(db, key.toUtf8().constData(), 0);
|
|
if (val && *val)
|
|
return val;
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
bool CutterCore::sdbSet(QString path, QString key, QString val)
|
|
{
|
|
CORE_LOCK();
|
|
Sdb *db = sdb_ns_path(core->sdb, path.toUtf8().constData(), 1);
|
|
if (!db)
|
|
return false;
|
|
return sdb_set(db, key.toUtf8().constData(), val.toUtf8().constData(), 0);
|
|
}
|
|
|
|
QString CutterCore::sanitizeStringForCommand(QString s)
|
|
{
|
|
static const QRegularExpression regexp(";|@");
|
|
return s.replace(regexp, QStringLiteral("_"));
|
|
}
|
|
|
|
QString CutterCore::cmd(const char *str)
|
|
{
|
|
CORE_LOCK();
|
|
|
|
RVA offset = core->offset;
|
|
char *res = rz_core_cmd_str(core, str);
|
|
QString o = fromOwnedCharPtr(res);
|
|
|
|
if (offset != core->offset) {
|
|
updateSeek();
|
|
}
|
|
return o;
|
|
}
|
|
|
|
bool CutterCore::isRedirectableDebugee()
|
|
{
|
|
if (!currentlyDebugging || currentlyAttachedToPID != -1) {
|
|
return false;
|
|
}
|
|
|
|
// We are only able to redirect locally debugged unix processes
|
|
RzCoreLocked core(Core());
|
|
RzList *descs = rz_id_storage_list(core->io->files);
|
|
RzListIter *it;
|
|
RzIODesc *desc;
|
|
CutterRzListForeach (descs, it, RzIODesc, desc) {
|
|
QString URI = QString(desc->uri);
|
|
if (URI.contains("ptrace") || URI.contains("mach")) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CutterCore::isDebugTaskInProgress()
|
|
{
|
|
if (!debugTask.isNull()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CutterCore::asyncCmdEsil(const char *command, QSharedPointer<RizinCmdTask> &task)
|
|
{
|
|
asyncCmd(command, task);
|
|
|
|
if (task.isNull()) {
|
|
return false;
|
|
}
|
|
|
|
connect(task.data(), &RizinCmdTask::finished, task.data(), [this, task]() {
|
|
QString res = task.data()->getResult();
|
|
|
|
if (res.contains(QStringLiteral("[ESIL] Stopped execution in an invalid instruction"))) {
|
|
msgBox.showMessage("Stopped when attempted to run an invalid instruction. You can "
|
|
"disable this in Preferences");
|
|
}
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CutterCore::asyncCmd(const char *str, QSharedPointer<RizinCmdTask> &task)
|
|
{
|
|
if (!task.isNull()) {
|
|
return false;
|
|
}
|
|
|
|
CORE_LOCK();
|
|
|
|
RVA offset = core->offset;
|
|
|
|
task = QSharedPointer<RizinCmdTask>(new RizinCmdTask(str, true));
|
|
connect(task.data(), &RizinCmdTask::finished, task.data(), [this, offset, task]() {
|
|
CORE_LOCK();
|
|
|
|
if (offset != core->offset) {
|
|
updateSeek();
|
|
}
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
QString CutterCore::cmdRawAt(const char *cmd, RVA address)
|
|
{
|
|
QString res;
|
|
RVA oldOffset = getOffset();
|
|
seekSilent(address);
|
|
|
|
res = cmdRaw(cmd);
|
|
|
|
seekSilent(oldOffset);
|
|
return res;
|
|
}
|
|
|
|
QString CutterCore::cmdRaw(const char *cmd)
|
|
{
|
|
QString res;
|
|
CORE_LOCK();
|
|
rz_cons_push();
|
|
|
|
// rz_cmd_call does not return the output of the command
|
|
rz_cmd_call(core->rcmd, cmd);
|
|
|
|
// we grab the output straight from rz_cons
|
|
res = rz_cons_get_buffer();
|
|
|
|
// cleaning up
|
|
rz_cons_pop();
|
|
rz_cons_echo(NULL);
|
|
|
|
return res;
|
|
}
|
|
|
|
QJsonDocument CutterCore::cmdj(const char *str)
|
|
{
|
|
char *res;
|
|
{
|
|
CORE_LOCK();
|
|
res = rz_core_cmd_str(core, str);
|
|
}
|
|
|
|
QJsonDocument doc = parseJson(res, str);
|
|
rz_mem_free(res);
|
|
|
|
return doc;
|
|
}
|
|
|
|
QJsonDocument CutterCore::cmdjAt(const char *str, RVA address)
|
|
{
|
|
QJsonDocument res;
|
|
RVA oldOffset = getOffset();
|
|
seekSilent(address);
|
|
|
|
res = cmdj(str);
|
|
|
|
seekSilent(oldOffset);
|
|
return res;
|
|
}
|
|
|
|
QString CutterCore::cmdTask(const QString &str)
|
|
{
|
|
RizinCmdTask task(str);
|
|
task.startTask();
|
|
task.joinTask();
|
|
return task.getResult();
|
|
}
|
|
|
|
QJsonDocument CutterCore::cmdjTask(const QString &str)
|
|
{
|
|
RizinCmdTask task(str);
|
|
task.startTask();
|
|
task.joinTask();
|
|
return parseJson(task.getResultRaw(), str);
|
|
}
|
|
|
|
QJsonDocument CutterCore::parseJson(const char *res, const char *cmd)
|
|
{
|
|
QByteArray json(res);
|
|
|
|
if (json.isEmpty()) {
|
|
return QJsonDocument();
|
|
}
|
|
|
|
QJsonParseError jsonError;
|
|
QJsonDocument doc = QJsonDocument::fromJson(json, &jsonError);
|
|
|
|
if (jsonError.error != QJsonParseError::NoError) {
|
|
// don't call trimmed() before knowing that parsing failed to avoid copying huge jsons all
|
|
// the time
|
|
if (json.trimmed().isEmpty()) {
|
|
return doc;
|
|
}
|
|
if (cmd) {
|
|
eprintf("Failed to parse JSON for command \"%s\": %s\n", cmd,
|
|
jsonError.errorString().toLocal8Bit().constData());
|
|
} else {
|
|
eprintf("Failed to parse JSON: %s\n",
|
|
jsonError.errorString().toLocal8Bit().constData());
|
|
}
|
|
const int MAX_JSON_DUMP_SIZE = 8 * 1024;
|
|
if (json.length() > MAX_JSON_DUMP_SIZE) {
|
|
int originalSize = json.length();
|
|
json.resize(MAX_JSON_DUMP_SIZE);
|
|
eprintf("%d bytes total: %s ...\n", originalSize, json.constData());
|
|
} else {
|
|
eprintf("%s\n", json.constData());
|
|
}
|
|
}
|
|
|
|
return doc;
|
|
}
|
|
|
|
QStringList CutterCore::autocomplete(const QString &cmd, RzLinePromptType promptType, size_t limit)
|
|
{
|
|
RzLineBuffer buf;
|
|
int c = snprintf(buf.data, sizeof(buf.data), "%s", cmd.toUtf8().constData());
|
|
if (c < 0) {
|
|
return {};
|
|
}
|
|
buf.index = buf.length = std::min((int)(sizeof(buf.data) - 1), c);
|
|
|
|
RzLineCompletion completion;
|
|
rz_line_completion_init(&completion, limit);
|
|
rz_core_autocomplete(core(), &completion, &buf, promptType);
|
|
|
|
QStringList r;
|
|
r.reserve(rz_pvector_len(&completion.args));
|
|
for (size_t i = 0; i < rz_pvector_len(&completion.args); i++) {
|
|
r.push_back(QString::fromUtf8(
|
|
reinterpret_cast<const char *>(rz_pvector_at(&completion.args, i))));
|
|
}
|
|
|
|
rz_line_completion_fini(&completion);
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* @brief CutterCore::loadFile
|
|
* Load initial file.
|
|
* @param path File path
|
|
* @param baddr Base (RzBin) address
|
|
* @param mapaddr Map address
|
|
* @param perms
|
|
* @param va
|
|
* @param loadbin Load RzBin information
|
|
* @param forceBinPlugin
|
|
* @return
|
|
*/
|
|
bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int va, bool loadbin,
|
|
const QString &forceBinPlugin)
|
|
{
|
|
CORE_LOCK();
|
|
RzCoreFile *f;
|
|
rz_config_set_i(core->config, "io.va", va);
|
|
|
|
f = rz_core_file_open(core, path.toUtf8().constData(), perms, mapaddr);
|
|
if (!f) {
|
|
eprintf("rz_core_file_open failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!forceBinPlugin.isNull()) {
|
|
rz_bin_force_plugin(rz_core_get_bin(core), forceBinPlugin.toUtf8().constData());
|
|
}
|
|
|
|
if (loadbin && va) {
|
|
if (!rz_core_bin_load(core, path.toUtf8().constData(), baddr)) {
|
|
eprintf("CANNOT GET RBIN INFO\n");
|
|
}
|
|
|
|
#if HAVE_MULTIPLE_RBIN_FILES_INSIDE_SELECT_WHICH_ONE
|
|
if (!rz_core_file_open(core, path.toUtf8(), RZ_IO_READ | (rw ? RZ_IO_WRITE : 0, mapaddr))) {
|
|
eprintf("Cannot open file\n");
|
|
} else {
|
|
// load RzBin information
|
|
// XXX only for sub-bins
|
|
rz_core_bin_load(core, path.toUtf8(), baddr);
|
|
rz_bin_select_idx(core->bin, NULL, idx);
|
|
}
|
|
#endif
|
|
} else {
|
|
// Not loading RzBin info coz va = false
|
|
}
|
|
|
|
auto iod = core->io ? core->io->desc : NULL;
|
|
auto debug =
|
|
core->file && iod && (core->file->fd == iod->fd) && iod->plugin && iod->plugin->isdbg;
|
|
|
|
if (!debug && rz_flag_get(core->flags, "entry0")) {
|
|
rz_core_cmd0(core, "s entry0");
|
|
}
|
|
|
|
if (perms & RZ_PERM_W) {
|
|
rz_core_cmd0(core, "omfg+w");
|
|
}
|
|
|
|
fflush(stdout);
|
|
return true;
|
|
}
|
|
|
|
bool CutterCore::tryFile(QString path, bool rw)
|
|
{
|
|
CORE_LOCK();
|
|
RzCoreFile *cf;
|
|
int flags = RZ_PERM_R;
|
|
if (rw)
|
|
flags = RZ_PERM_RW;
|
|
cf = rz_core_file_open(core, path.toUtf8().constData(), flags, 0LL);
|
|
if (!cf) {
|
|
return false;
|
|
}
|
|
|
|
rz_core_file_close(cf);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Maps a file using Rizin API
|
|
* @param path Path to file
|
|
* @param mapaddr Map Address
|
|
* @return bool
|
|
*/
|
|
bool CutterCore::mapFile(QString path, RVA mapaddr)
|
|
{
|
|
CORE_LOCK();
|
|
RVA addr = mapaddr != RVA_INVALID ? mapaddr : 0;
|
|
ut64 baddr =
|
|
Core()->getFileInfo().object()["bin"].toObject()["baddr"].toVariant().toULongLong();
|
|
if (rz_core_file_open(core, path.toUtf8().constData(), RZ_PERM_RX, addr)) {
|
|
rz_core_bin_load(core, path.toUtf8().constData(), baddr);
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CutterCore::renameFunction(const RVA offset, const QString &newName)
|
|
{
|
|
cmdRaw("afn " + newName + " " + RzAddressString(offset));
|
|
emit functionRenamed(offset, newName);
|
|
}
|
|
|
|
void CutterCore::delFunction(RVA addr)
|
|
{
|
|
cmdRaw("af- " + RzAddressString(addr));
|
|
emit functionsChanged();
|
|
}
|
|
|
|
void CutterCore::renameFlag(QString old_name, QString new_name)
|
|
{
|
|
cmdRaw("fr " + old_name + " " + new_name);
|
|
emit flagsChanged();
|
|
}
|
|
|
|
void CutterCore::renameFunctionVariable(QString newName, QString oldName, RVA functionAddress)
|
|
{
|
|
CORE_LOCK();
|
|
RzAnalysisFunction *function = rz_analysis_get_function_at(core->analysis, functionAddress);
|
|
RzAnalysisVar *variable =
|
|
rz_analysis_function_get_var_byname(function, oldName.toUtf8().constData());
|
|
if (variable) {
|
|
rz_analysis_var_rename(variable, newName.toUtf8().constData(), true);
|
|
}
|
|
emit refreshCodeViews();
|
|
}
|
|
|
|
void CutterCore::delFlag(RVA addr)
|
|
{
|
|
cmdRawAt("f-", addr);
|
|
emit flagsChanged();
|
|
}
|
|
|
|
void CutterCore::delFlag(const QString &name)
|
|
{
|
|
cmdRaw("f-" + name);
|
|
emit flagsChanged();
|
|
}
|
|
|
|
QString CutterCore::getInstructionBytes(RVA addr)
|
|
{
|
|
return cmdj("aoj @ " + RzAddressString(addr))
|
|
.array()
|
|
.first()
|
|
.toObject()[RJsonKey::bytes]
|
|
.toString();
|
|
}
|
|
|
|
QString CutterCore::getInstructionOpcode(RVA addr)
|
|
{
|
|
return cmdj("aoj @ " + RzAddressString(addr))
|
|
.array()
|
|
.first()
|
|
.toObject()[RJsonKey::opcode]
|
|
.toString();
|
|
}
|
|
|
|
void CutterCore::editInstruction(RVA addr, const QString &inst)
|
|
{
|
|
CORE_LOCK();
|
|
rz_core_write_assembly(core, addr, inst.trimmed().toStdString().c_str(), false, false);
|
|
emit instructionChanged(addr);
|
|
}
|
|
|
|
void CutterCore::nopInstruction(RVA addr)
|
|
{
|
|
CORE_LOCK();
|
|
applyAtSeek([&]() { rz_core_hack(core, "nop"); }, addr);
|
|
emit instructionChanged(addr);
|
|
}
|
|
|
|
void CutterCore::jmpReverse(RVA addr)
|
|
{
|
|
CORE_LOCK();
|
|
applyAtSeek([&]() { rz_core_hack(core, "recj"); }, addr);
|
|
emit instructionChanged(addr);
|
|
}
|
|
|
|
void CutterCore::editBytes(RVA addr, const QString &bytes)
|
|
{
|
|
cmdRawAt(QString("wx %1").arg(bytes), addr);
|
|
emit instructionChanged(addr);
|
|
}
|
|
|
|
void CutterCore::editBytesEndian(RVA addr, const QString &bytes)
|
|
{
|
|
CORE_LOCK();
|
|
ut64 value = rz_num_math(core->num, bytes.toUtf8().constData());
|
|
if (core->num->nc.errors) {
|
|
return;
|
|
}
|
|
rz_core_write_value_at(core, addr, value, 0);
|
|
emit stackChanged();
|
|
}
|
|
|
|
void CutterCore::setToCode(RVA addr)
|
|
{
|
|
CORE_LOCK();
|
|
rz_meta_del(core->analysis, RZ_META_TYPE_STRING, core->offset, 1);
|
|
rz_meta_del(core->analysis, RZ_META_TYPE_DATA, core->offset, 1);
|
|
emit instructionChanged(addr);
|
|
}
|
|
|
|
void CutterCore::setAsString(RVA addr, int size, StringTypeFormats type)
|
|
{
|
|
if (RVA_INVALID == addr) {
|
|
return;
|
|
}
|
|
|
|
QString command;
|
|
|
|
switch (type) {
|
|
case StringTypeFormats::None: {
|
|
command = "Cs";
|
|
break;
|
|
}
|
|
case StringTypeFormats::ASCII_LATIN1: {
|
|
command = "Csa";
|
|
break;
|
|
}
|
|
case StringTypeFormats::UTF8: {
|
|
command = "Cs8";
|
|
break;
|
|
}
|
|
default:
|
|
return;
|
|
}
|
|
|
|
seekAndShow(addr);
|
|
|
|
cmdRawAt(QString("%1 %2").arg(command).arg(size), addr);
|
|
emit instructionChanged(addr);
|
|
}
|
|
|
|
void CutterCore::removeString(RVA addr)
|
|
{
|
|
cmdRawAt("Cs-", addr);
|
|
emit instructionChanged(addr);
|
|
}
|
|
|
|
QString CutterCore::getString(RVA addr)
|
|
{
|
|
return cmdRawAt("ps", addr);
|
|
}
|
|
|
|
void CutterCore::setToData(RVA addr, int size, int repeat)
|
|
{
|
|
if (size <= 0 || repeat <= 0) {
|
|
return;
|
|
}
|
|
cmdRawAt("Cd-", addr);
|
|
cmdRawAt(QString("Cd %1 %2").arg(size).arg(repeat), addr);
|
|
emit instructionChanged(addr);
|
|
}
|
|
|
|
int CutterCore::sizeofDataMeta(RVA addr)
|
|
{
|
|
bool ok;
|
|
int size = cmdRawAt("Cd.", addr).toInt(&ok);
|
|
return (ok ? size : 0);
|
|
}
|
|
|
|
void CutterCore::setComment(RVA addr, const QString &cmt)
|
|
{
|
|
CORE_LOCK();
|
|
rz_meta_set_string(core->analysis, RZ_META_TYPE_COMMENT, addr, cmt.toStdString().c_str());
|
|
emit commentsChanged(addr);
|
|
}
|
|
|
|
void CutterCore::delComment(RVA addr)
|
|
{
|
|
CORE_LOCK();
|
|
rz_meta_del(core->analysis, RZ_META_TYPE_COMMENT, addr, 1);
|
|
emit commentsChanged(addr);
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the comment present at a specific address
|
|
* @param addr The address to be checked
|
|
* @return String containing comment
|
|
*/
|
|
QString CutterCore::getCommentAt(RVA addr)
|
|
{
|
|
CORE_LOCK();
|
|
return rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, addr);
|
|
}
|
|
|
|
void CutterCore::setImmediateBase(const QString &rzBaseName, RVA offset)
|
|
{
|
|
if (offset == RVA_INVALID) {
|
|
offset = getOffset();
|
|
}
|
|
|
|
this->cmdRawAt(QString("ahi %1").arg(rzBaseName), offset);
|
|
emit instructionChanged(offset);
|
|
}
|
|
|
|
void CutterCore::setCurrentBits(int bits, RVA offset)
|
|
{
|
|
if (offset == RVA_INVALID) {
|
|
offset = getOffset();
|
|
}
|
|
|
|
this->cmdRawAt(QString("ahb %1").arg(bits), offset);
|
|
emit instructionChanged(offset);
|
|
}
|
|
|
|
void CutterCore::applyStructureOffset(const QString &structureOffset, RVA offset)
|
|
{
|
|
if (offset == RVA_INVALID) {
|
|
offset = getOffset();
|
|
}
|
|
|
|
this->cmdRawAt("aht " + structureOffset, offset);
|
|
emit instructionChanged(offset);
|
|
}
|
|
|
|
void CutterCore::seekSilent(ut64 offset)
|
|
{
|
|
CORE_LOCK();
|
|
if (offset == RVA_INVALID) {
|
|
return;
|
|
}
|
|
rz_core_seek(core, offset, true);
|
|
}
|
|
|
|
void CutterCore::seek(ut64 offset)
|
|
{
|
|
// Slower than using the API, but the API is not complete
|
|
// which means we either have to duplicate code from rizin
|
|
// here, or refactor rizin API.
|
|
CORE_LOCK();
|
|
if (offset == RVA_INVALID) {
|
|
return;
|
|
}
|
|
|
|
RVA o_offset = core->offset;
|
|
rz_core_seek_and_save(core, offset, true);
|
|
if (o_offset != core->offset) {
|
|
updateSeek();
|
|
}
|
|
}
|
|
|
|
void CutterCore::showMemoryWidget()
|
|
{
|
|
emit showMemoryWidgetRequested();
|
|
}
|
|
|
|
void CutterCore::seekAndShow(ut64 offset)
|
|
{
|
|
seek(offset);
|
|
showMemoryWidget();
|
|
}
|
|
|
|
void CutterCore::seekAndShow(QString offset)
|
|
{
|
|
seek(offset);
|
|
showMemoryWidget();
|
|
}
|
|
|
|
void CutterCore::seek(QString thing)
|
|
{
|
|
CORE_LOCK();
|
|
ut64 addr = rz_num_math(core->num, thing.toUtf8().constData());
|
|
if (core->num->nc.errors) {
|
|
return;
|
|
}
|
|
rz_core_seek_and_save(core, addr, true);
|
|
updateSeek();
|
|
}
|
|
|
|
void CutterCore::seekPrev()
|
|
{
|
|
CORE_LOCK();
|
|
rz_core_seek_undo(core);
|
|
updateSeek();
|
|
}
|
|
|
|
void CutterCore::seekNext()
|
|
{
|
|
CORE_LOCK();
|
|
rz_core_seek_redo(core);
|
|
updateSeek();
|
|
}
|
|
|
|
void CutterCore::updateSeek()
|
|
{
|
|
emit seekChanged(getOffset());
|
|
}
|
|
|
|
RVA CutterCore::prevOpAddr(RVA startAddr, int count)
|
|
{
|
|
CORE_LOCK();
|
|
bool ok;
|
|
RVA offset = cmdRawAt(QString("/O %1").arg(count), startAddr).toULongLong(&ok, 16);
|
|
return ok ? offset : startAddr - count;
|
|
}
|
|
|
|
RVA CutterCore::nextOpAddr(RVA startAddr, int count)
|
|
{
|
|
CORE_LOCK();
|
|
|
|
QJsonArray array =
|
|
Core()->cmdj("pdj " + QString::number(count + 1) + " @ " + QString::number(startAddr))
|
|
.array();
|
|
if (array.isEmpty()) {
|
|
return startAddr + 1;
|
|
}
|
|
|
|
QJsonValue instValue = array.last();
|
|
if (!instValue.isObject()) {
|
|
return startAddr + 1;
|
|
}
|
|
|
|
bool ok;
|
|
RVA offset = instValue.toObject()[RJsonKey::offset].toVariant().toULongLong(&ok);
|
|
if (!ok) {
|
|
return startAddr + 1;
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
RVA CutterCore::getOffset()
|
|
{
|
|
return core_->offset;
|
|
}
|
|
|
|
void CutterCore::applySignature(const QString &filepath)
|
|
{
|
|
CORE_LOCK();
|
|
int old_cnt, new_cnt;
|
|
const char *arch = rz_config_get(core->config, "asm.arch");
|
|
ut8 expected_arch = rz_core_flirt_arch_from_name(arch);
|
|
if (expected_arch == RZ_FLIRT_SIG_ARCH_ANY && filepath.endsWith(".sig", Qt::CaseInsensitive)) {
|
|
QMessageBox::warning(
|
|
nullptr, tr("Signatures"),
|
|
tr("Cannot apply signature because the requested arch is not supported by .sig "
|
|
"files\n"));
|
|
return;
|
|
}
|
|
old_cnt = rz_flag_count(core->flags, "flirt");
|
|
rz_sign_flirt_apply(core->analysis, filepath.toStdString().c_str(), expected_arch);
|
|
new_cnt = rz_flag_count(core->flags, "flirt");
|
|
QMessageBox::information(nullptr, tr("Signatures"),
|
|
tr("Found %1 signatures!").arg(new_cnt - old_cnt));
|
|
}
|
|
|
|
void CutterCore::createSignature(const QString &filepath)
|
|
{
|
|
CORE_LOCK();
|
|
ut32 n_modules = 0;
|
|
if (!rz_core_flirt_create_file(core, filepath.toStdString().c_str(), &n_modules)) {
|
|
QMessageBox::warning(
|
|
nullptr, tr("Signatures"),
|
|
tr("Cannot create signature file (check the console for more details).\n"));
|
|
return;
|
|
}
|
|
QMessageBox::information(nullptr, tr("Signatures"),
|
|
tr("Written %1 signatures to %2").arg(n_modules).arg(filepath));
|
|
}
|
|
|
|
ut64 CutterCore::math(const QString &expr)
|
|
{
|
|
CORE_LOCK();
|
|
return rz_num_math(core ? core->num : NULL, expr.toUtf8().constData());
|
|
}
|
|
|
|
ut64 CutterCore::num(const QString &expr)
|
|
{
|
|
CORE_LOCK();
|
|
return rz_num_get(core ? core->num : NULL, expr.toUtf8().constData());
|
|
}
|
|
|
|
QString CutterCore::itoa(ut64 num, int rdx)
|
|
{
|
|
return QString::number(num, rdx);
|
|
}
|
|
|
|
void CutterCore::setConfig(const char *k, const char *v)
|
|
{
|
|
CORE_LOCK();
|
|
rz_config_set(core->config, k, v);
|
|
}
|
|
|
|
void CutterCore::setConfig(const QString &k, const char *v)
|
|
{
|
|
CORE_LOCK();
|
|
rz_config_set(core->config, k.toUtf8().constData(), v);
|
|
}
|
|
|
|
void CutterCore::setConfig(const char *k, const QString &v)
|
|
{
|
|
CORE_LOCK();
|
|
rz_config_set(core->config, k, v.toUtf8().constData());
|
|
}
|
|
|
|
void CutterCore::setConfig(const char *k, int v)
|
|
{
|
|
CORE_LOCK();
|
|
rz_config_set_i(core->config, k, static_cast<ut64>(v));
|
|
}
|
|
|
|
void CutterCore::setConfig(const char *k, bool v)
|
|
{
|
|
CORE_LOCK();
|
|
rz_config_set_i(core->config, k, v ? 1 : 0);
|
|
}
|
|
|
|
int CutterCore::getConfigi(const char *k)
|
|
{
|
|
CORE_LOCK();
|
|
return static_cast<int>(rz_config_get_i(core->config, k));
|
|
}
|
|
|
|
ut64 CutterCore::getConfigut64(const char *k)
|
|
{
|
|
CORE_LOCK();
|
|
return rz_config_get_i(core->config, k);
|
|
}
|
|
|
|
bool CutterCore::getConfigb(const char *k)
|
|
{
|
|
CORE_LOCK();
|
|
return rz_config_get_i(core->config, k) != 0;
|
|
}
|
|
|
|
QString CutterCore::getConfigDescription(const char *k)
|
|
{
|
|
CORE_LOCK();
|
|
RzConfigNode *node = rz_config_node_get(core->config, k);
|
|
return node ? QString(node->desc) : QString("Unrecognized configuration key");
|
|
}
|
|
|
|
void CutterCore::triggerRefreshAll()
|
|
{
|
|
emit refreshAll();
|
|
}
|
|
|
|
void CutterCore::triggerAsmOptionsChanged()
|
|
{
|
|
emit asmOptionsChanged();
|
|
}
|
|
|
|
void CutterCore::triggerGraphOptionsChanged()
|
|
{
|
|
emit graphOptionsChanged();
|
|
}
|
|
|
|
void CutterCore::message(const QString &msg, bool debug)
|
|
{
|
|
if (msg.isEmpty())
|
|
return;
|
|
if (debug) {
|
|
qDebug() << msg;
|
|
emit newDebugMessage(msg);
|
|
return;
|
|
}
|
|
emit newMessage(msg);
|
|
}
|
|
|
|
QString CutterCore::getConfig(const char *k)
|
|
{
|
|
CORE_LOCK();
|
|
return QString(rz_config_get(core->config, k));
|
|
}
|
|
|
|
void CutterCore::setConfig(const char *k, const QVariant &v)
|
|
{
|
|
switch (v.type()) {
|
|
case QVariant::Type::Bool:
|
|
setConfig(k, v.toBool());
|
|
break;
|
|
case QVariant::Type::Int:
|
|
setConfig(k, v.toInt());
|
|
break;
|
|
default:
|
|
setConfig(k, v.toString());
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CutterCore::setCPU(QString arch, QString cpu, int bits)
|
|
{
|
|
if (arch != nullptr) {
|
|
setConfig("asm.arch", arch);
|
|
}
|
|
if (cpu != nullptr) {
|
|
setConfig("asm.cpu", cpu);
|
|
}
|
|
setConfig("asm.bits", bits);
|
|
}
|
|
|
|
void CutterCore::setEndianness(bool big)
|
|
{
|
|
setConfig("cfg.bigendian", big);
|
|
}
|
|
|
|
QByteArray CutterCore::assemble(const QString &code)
|
|
{
|
|
CORE_LOCK();
|
|
RzAsmCode *ac = rz_asm_massemble(core->rasm, code.toUtf8().constData());
|
|
QByteArray res;
|
|
if (ac && ac->bytes) {
|
|
res = QByteArray(reinterpret_cast<const char *>(ac->bytes), ac->len);
|
|
}
|
|
rz_asm_code_free(ac);
|
|
return res;
|
|
}
|
|
|
|
QString CutterCore::disassemble(const QByteArray &data)
|
|
{
|
|
CORE_LOCK();
|
|
RzAsmCode *ac = rz_asm_mdisassemble(core->rasm, reinterpret_cast<const ut8 *>(data.constData()),
|
|
data.length());
|
|
QString code;
|
|
if (ac && ac->assembly) {
|
|
code = QString::fromUtf8(ac->assembly);
|
|
}
|
|
rz_asm_code_free(ac);
|
|
return code;
|
|
}
|
|
|
|
QString CutterCore::disassembleSingleInstruction(RVA addr)
|
|
{
|
|
return cmdRawAt("pi 1", addr).simplified();
|
|
}
|
|
|
|
RzAnalysisFunction *CutterCore::functionIn(ut64 addr)
|
|
{
|
|
CORE_LOCK();
|
|
RzList *fcns = rz_analysis_get_functions_in(core->analysis, addr);
|
|
RzAnalysisFunction *fcn = !rz_list_empty(fcns)
|
|
? reinterpret_cast<RzAnalysisFunction *>(rz_list_first(fcns))
|
|
: nullptr;
|
|
rz_list_free(fcns);
|
|
return fcn;
|
|
}
|
|
|
|
RzAnalysisFunction *CutterCore::functionAt(ut64 addr)
|
|
{
|
|
CORE_LOCK();
|
|
return rz_analysis_get_function_at(core->analysis, addr);
|
|
}
|
|
|
|
/**
|
|
* @brief finds the start address of a function in a given address
|
|
* @param addr - an address which belongs to a function
|
|
* @returns if function exists, return its start address. Otherwise return RVA_INVALID
|
|
*/
|
|
RVA CutterCore::getFunctionStart(RVA addr)
|
|
{
|
|
CORE_LOCK();
|
|
RzAnalysisFunction *fcn = Core()->functionIn(addr);
|
|
return fcn ? fcn->addr : RVA_INVALID;
|
|
}
|
|
|
|
/**
|
|
* @brief finds the end address of a function in a given address
|
|
* @param addr - an address which belongs to a function
|
|
* @returns if function exists, return its end address. Otherwise return RVA_INVALID
|
|
*/
|
|
RVA CutterCore::getFunctionEnd(RVA addr)
|
|
{
|
|
CORE_LOCK();
|
|
RzAnalysisFunction *fcn = Core()->functionIn(addr);
|
|
return fcn ? fcn->addr : RVA_INVALID;
|
|
}
|
|
|
|
/**
|
|
* @brief finds the last instruction of a function in a given address
|
|
* @param addr - an address which belongs to a function
|
|
* @returns if function exists, return the address of its last instruction. Otherwise return
|
|
* RVA_INVALID
|
|
*/
|
|
RVA CutterCore::getLastFunctionInstruction(RVA addr)
|
|
{
|
|
CORE_LOCK();
|
|
RzAnalysisFunction *fcn = Core()->functionIn(addr);
|
|
if (!fcn) {
|
|
return RVA_INVALID;
|
|
}
|
|
RzAnalysisBlock *lastBB = (RzAnalysisBlock *)rz_list_last(fcn->bbs);
|
|
return lastBB ? rz_analysis_block_get_op_addr(lastBB, lastBB->ninstr - 1) : RVA_INVALID;
|
|
}
|
|
|
|
QString CutterCore::cmdFunctionAt(QString addr)
|
|
{
|
|
QString ret;
|
|
// Use cmd because cmdRaw would not work with grep
|
|
ret = cmd(QString("fd @ %1~[0]").arg(addr));
|
|
return ret.trimmed();
|
|
}
|
|
|
|
QString CutterCore::cmdFunctionAt(RVA addr)
|
|
{
|
|
return cmdFunctionAt(QString::number(addr));
|
|
}
|
|
|
|
void CutterCore::cmdEsil(const char *command)
|
|
{
|
|
// use cmd and not cmdRaw because of unexpected commands
|
|
QString res = cmd(command);
|
|
if (res.contains(QStringLiteral("[ESIL] Stopped execution in an invalid instruction"))) {
|
|
msgBox.showMessage("Stopped when attempted to run an invalid instruction. You can disable "
|
|
"this in Preferences");
|
|
}
|
|
}
|
|
|
|
QString CutterCore::createFunctionAt(RVA addr)
|
|
{
|
|
QString ret = cmdRaw(QString("af %1").arg(addr));
|
|
emit functionsChanged();
|
|
return ret;
|
|
}
|
|
|
|
QString CutterCore::createFunctionAt(RVA addr, QString name)
|
|
{
|
|
static const QRegularExpression regExp("[^a-zA-Z0-9_.]");
|
|
name.remove(regExp);
|
|
QString ret = cmdRawAt(QString("af %1").arg(name), addr);
|
|
emit functionsChanged();
|
|
return ret;
|
|
}
|
|
|
|
QJsonDocument CutterCore::getRegistersInfo()
|
|
{
|
|
return cmdj("aeafj");
|
|
}
|
|
|
|
RVA CutterCore::getOffsetJump(RVA addr)
|
|
{
|
|
bool ok;
|
|
RVA value = cmdj("aoj @" + QString::number(addr))
|
|
.array()
|
|
.first()
|
|
.toObject()
|
|
.value(RJsonKey::jump)
|
|
.toVariant()
|
|
.toULongLong(&ok);
|
|
|
|
if (!ok) {
|
|
return RVA_INVALID;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
QList<Decompiler *> CutterCore::getDecompilers()
|
|
{
|
|
return decompilers;
|
|
}
|
|
|
|
Decompiler *CutterCore::getDecompilerById(const QString &id)
|
|
{
|
|
for (Decompiler *dec : decompilers) {
|
|
if (dec->getId() == id) {
|
|
return dec;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool CutterCore::registerDecompiler(Decompiler *decompiler)
|
|
{
|
|
if (getDecompilerById(decompiler->getId())) {
|
|
return false;
|
|
}
|
|
decompiler->setParent(this);
|
|
decompilers.push_back(decompiler);
|
|
return true;
|
|
}
|
|
|
|
QJsonDocument CutterCore::getFileInfo()
|
|
{
|
|
return cmdj("ij");
|
|
}
|
|
|
|
QJsonDocument CutterCore::getFileVersionInfo()
|
|
{
|
|
return cmdj("iVj");
|
|
}
|
|
|
|
QJsonDocument CutterCore::getSignatureInfo()
|
|
{
|
|
return cmdj("iCj");
|
|
}
|
|
|
|
// Utility function to check if a telescoped item exists and add it with prefixes to the desc
|
|
static inline const QString appendVar(QString &dst, const QString val, const QString prepend_val,
|
|
const QString append_val)
|
|
{
|
|
if (!val.isEmpty()) {
|
|
dst += prepend_val + val + append_val;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
RefDescription CutterCore::formatRefDesc(QJsonObject refItem)
|
|
{
|
|
RefDescription desc;
|
|
|
|
// Ignore empty refs and refs that only contain addr
|
|
if (refItem.size() <= 1) {
|
|
return desc;
|
|
}
|
|
|
|
QString str = refItem["string"].toVariant().toString();
|
|
if (!str.isEmpty()) {
|
|
desc.ref = str;
|
|
desc.refColor = ConfigColor("comment");
|
|
} else {
|
|
QString type, string;
|
|
do {
|
|
desc.ref += " ->";
|
|
appendVar(desc.ref, refItem["reg"].toVariant().toString(), " @", "");
|
|
appendVar(desc.ref, refItem["mapname"].toVariant().toString(), " (", ")");
|
|
appendVar(desc.ref, refItem["section"].toVariant().toString(), " (", ")");
|
|
appendVar(desc.ref, refItem["func"].toVariant().toString(), " ", "");
|
|
type = appendVar(desc.ref, refItem["type"].toVariant().toString(), " ", "");
|
|
appendVar(desc.ref, refItem["perms"].toVariant().toString(), " ", "");
|
|
appendVar(desc.ref, refItem["asm"].toVariant().toString(), " \"", "\"");
|
|
string = appendVar(desc.ref, refItem["string"].toVariant().toString(), " ", "");
|
|
if (!string.isNull()) {
|
|
// There is no point in adding ascii and addr info after a string
|
|
break;
|
|
}
|
|
if (!refItem["value"].isNull()) {
|
|
appendVar(desc.ref, RzAddressString(refItem["value"].toVariant().toULongLong()),
|
|
" ", "");
|
|
}
|
|
refItem = refItem["ref"].toObject();
|
|
} while (!refItem.empty());
|
|
|
|
// Set the ref's color according to the last item type
|
|
if (type == "ascii" || !string.isEmpty()) {
|
|
desc.refColor = ConfigColor("comment");
|
|
} else if (type == "program") {
|
|
desc.refColor = ConfigColor("fname");
|
|
} else if (type == "library") {
|
|
desc.refColor = ConfigColor("floc");
|
|
} else if (type == "stack") {
|
|
desc.refColor = ConfigColor("offset");
|
|
}
|
|
}
|
|
|
|
return desc;
|
|
}
|
|
|
|
QList<QJsonObject> CutterCore::getRegisterRefs(int depth)
|
|
{
|
|
QList<QJsonObject> ret;
|
|
if (!currentlyDebugging) {
|
|
return ret;
|
|
}
|
|
|
|
QJsonObject registers = cmdj("drj").object();
|
|
|
|
for (const QString &key : registers.keys()) {
|
|
QJsonObject reg;
|
|
reg["value"] = registers.value(key);
|
|
reg["ref"] = getAddrRefs(registers.value(key).toVariant().toULongLong(), depth);
|
|
reg["name"] = key;
|
|
ret.append(reg);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<QJsonObject> CutterCore::getStack(int size, int depth)
|
|
{
|
|
QList<QJsonObject> stack;
|
|
if (!currentlyDebugging) {
|
|
return stack;
|
|
}
|
|
|
|
CORE_LOCK();
|
|
bool ret;
|
|
RVA addr = cmdRaw("dr SP").toULongLong(&ret, 16);
|
|
if (!ret) {
|
|
return stack;
|
|
}
|
|
|
|
int base = core->analysis->bits;
|
|
for (int i = 0; i < size; i += base / 8) {
|
|
if ((base == 32 && addr + i >= UT32_MAX) || (base == 16 && addr + i >= UT16_MAX)) {
|
|
break;
|
|
}
|
|
|
|
stack.append(getAddrRefs(addr + i, depth));
|
|
}
|
|
|
|
return stack;
|
|
}
|
|
|
|
QJsonObject CutterCore::getAddrRefs(RVA addr, int depth)
|
|
{
|
|
QJsonObject json;
|
|
if (depth < 1 || addr == UT64_MAX) {
|
|
return json;
|
|
}
|
|
|
|
CORE_LOCK();
|
|
int bits = core->rasm->bits;
|
|
QByteArray buf = QByteArray();
|
|
ut64 type = rz_core_analysis_address(core, addr);
|
|
|
|
json["addr"] = QString::number(addr);
|
|
|
|
// Search for the section the addr is in, avoid duplication for heap/stack with type
|
|
if (!(type & RZ_ANALYSIS_ADDR_TYPE_HEAP || type & RZ_ANALYSIS_ADDR_TYPE_STACK)) {
|
|
// Attempt to find the address within a map
|
|
RzDebugMap *map = rz_debug_map_get(core->dbg, addr);
|
|
if (map && map->name && map->name[0]) {
|
|
json["mapname"] = map->name;
|
|
}
|
|
|
|
RzBinSection *sect = rz_bin_get_section_at(rz_bin_cur_object(core->bin), addr, true);
|
|
if (sect && sect->name[0]) {
|
|
json["section"] = sect->name;
|
|
}
|
|
}
|
|
|
|
// Check if the address points to a register
|
|
RzFlagItem *fi = rz_flag_get_i(core->flags, addr);
|
|
if (fi) {
|
|
RzRegItem *r = rz_reg_get(core->dbg->reg, fi->name, -1);
|
|
if (r) {
|
|
json["reg"] = r->name;
|
|
}
|
|
}
|
|
|
|
// Attempt to find the address within a function
|
|
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);
|
|
if (fcn) {
|
|
json["fcn"] = fcn->name;
|
|
}
|
|
|
|
// Update type and permission information
|
|
if (type != 0) {
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_HEAP) {
|
|
json["type"] = "heap";
|
|
} else if (type & RZ_ANALYSIS_ADDR_TYPE_STACK) {
|
|
json["type"] = "stack";
|
|
} else if (type & RZ_ANALYSIS_ADDR_TYPE_PROGRAM) {
|
|
json["type"] = "program";
|
|
} else if (type & RZ_ANALYSIS_ADDR_TYPE_LIBRARY) {
|
|
json["type"] = "library";
|
|
} else if (type & RZ_ANALYSIS_ADDR_TYPE_ASCII) {
|
|
json["type"] = "ascii";
|
|
} else if (type & RZ_ANALYSIS_ADDR_TYPE_SEQUENCE) {
|
|
json["type"] = "sequence";
|
|
}
|
|
|
|
QString perms = "";
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_READ) {
|
|
perms += "r";
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_WRITE) {
|
|
perms += "w";
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_EXEC) {
|
|
RzAsmOp op;
|
|
buf.resize(32);
|
|
perms += "x";
|
|
// Instruction disassembly
|
|
rz_io_read_at(core->io, addr, (unsigned char *)buf.data(), buf.size());
|
|
rz_asm_set_pc(core->rasm, addr);
|
|
rz_asm_disassemble(core->rasm, &op, (unsigned char *)buf.data(), buf.size());
|
|
json["asm"] = rz_asm_op_get_asm(&op);
|
|
}
|
|
|
|
if (!perms.isEmpty()) {
|
|
json["perms"] = perms;
|
|
}
|
|
}
|
|
|
|
// Try to telescope further if depth permits it
|
|
if ((type & RZ_ANALYSIS_ADDR_TYPE_READ)) {
|
|
buf.resize(64);
|
|
ut32 *n32 = (ut32 *)buf.data();
|
|
ut64 *n64 = (ut64 *)buf.data();
|
|
rz_io_read_at(core->io, addr, (unsigned char *)buf.data(), buf.size());
|
|
ut64 n = (bits == 64) ? *n64 : *n32;
|
|
// The value of the next address will serve as an indication that there's more to
|
|
// telescope if we have reached the depth limit
|
|
json["value"] = QString::number(n);
|
|
if (depth && n != addr && !(type & RZ_ANALYSIS_ADDR_TYPE_EXEC)) {
|
|
// Make sure we aren't telescoping the same address
|
|
QJsonObject ref = getAddrRefs(n, depth - 1);
|
|
if (!ref.empty() && !ref["type"].isNull()) {
|
|
// If the dereference of the current pointer is an ascii character we
|
|
// might have a string in this address
|
|
if (ref["type"].toString().contains("ascii")) {
|
|
buf.resize(128);
|
|
rz_io_read_at(core->io, addr, (unsigned char *)buf.data(), buf.size());
|
|
QString strVal = QString(buf);
|
|
// Indicate that the string is longer than the printed value
|
|
if (strVal.size() == buf.size()) {
|
|
strVal += "...";
|
|
}
|
|
json["string"] = strVal;
|
|
}
|
|
json["ref"] = ref;
|
|
}
|
|
}
|
|
}
|
|
return json;
|
|
}
|
|
|
|
QJsonDocument CutterCore::getProcessThreads(int pid)
|
|
{
|
|
if (-1 == pid) {
|
|
// Return threads list of the currently debugged PID
|
|
return cmdj("dptj");
|
|
} else {
|
|
return cmdj("dptj " + QString::number(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;
|
|
CutterRzListForeach (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;
|
|
}
|
|
|
|
int CutterCore::getArchBits()
|
|
{
|
|
CORE_LOCK();
|
|
return core->dbg->bits;
|
|
}
|
|
|
|
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;
|
|
CutterRzListForeach (arenas, iter, RzArenaListItem, data) {
|
|
Arena arena;
|
|
arena.offset = data->addr;
|
|
arena.type = QString(data->type);
|
|
arena.last_remainder = data->arena->last_remainder;
|
|
arena.top = data->arena->top;
|
|
arena.next = data->arena->next;
|
|
arena.next_free = data->arena->next_free;
|
|
arena.system_mem = data->arena->system_mem;
|
|
arena.max_system_mem = data->arena->max_system_mem;
|
|
arena_vector.append(arena);
|
|
}
|
|
|
|
rz_list_free(arenas);
|
|
return arena_vector;
|
|
}
|
|
|
|
RzHeapChunkSimple *CutterCore::getHeapChunk(ut64 addr)
|
|
{
|
|
CORE_LOCK();
|
|
return rz_heap_chunk(core, addr);
|
|
}
|
|
|
|
QVector<RzHeapBin *> CutterCore::getHeapBins(ut64 arena_addr)
|
|
{
|
|
CORE_LOCK();
|
|
QVector<RzHeapBin *> bins_vector;
|
|
|
|
MallocState *arena = rz_heap_get_arena(core, arena_addr);
|
|
if (!arena) {
|
|
return bins_vector;
|
|
}
|
|
|
|
// get small, large, unsorted bins
|
|
for (int i = 0; i <= NBINS - 2; i++) {
|
|
RzHeapBin *bin = rz_heap_bin_content(core, arena, i, arena_addr);
|
|
if (!bin) {
|
|
continue;
|
|
}
|
|
if (!rz_list_length(bin->chunks)) {
|
|
rz_heap_bin_free_64(bin);
|
|
continue;
|
|
}
|
|
bins_vector.append(bin);
|
|
}
|
|
// get fastbins
|
|
for (int i = 0; i < 10; i++) {
|
|
RzHeapBin *bin = rz_heap_fastbin_content(core, arena, i);
|
|
if (!bin) {
|
|
continue;
|
|
}
|
|
if (!rz_list_length(bin->chunks)) {
|
|
rz_heap_bin_free_64(bin);
|
|
continue;
|
|
}
|
|
bins_vector.append(bin);
|
|
}
|
|
// get tcache bins
|
|
RzList *tcache_bins = rz_heap_tcache_content(core, arena_addr);
|
|
RzListIter *iter;
|
|
RzHeapBin *bin;
|
|
CutterRzListForeach (tcache_bins, iter, RzHeapBin, bin) {
|
|
if (!bin) {
|
|
continue;
|
|
}
|
|
if (!rz_list_length(bin->chunks)) {
|
|
rz_heap_bin_free_64(bin);
|
|
continue;
|
|
}
|
|
bins_vector.append(bin);
|
|
}
|
|
return bins_vector;
|
|
}
|
|
|
|
bool CutterCore::writeHeapChunk(RzHeapChunkSimple *chunk_simple)
|
|
{
|
|
CORE_LOCK();
|
|
return rz_heap_write_chunk(core, chunk_simple);
|
|
}
|
|
|
|
QJsonDocument CutterCore::getChildProcesses(int pid)
|
|
{
|
|
// Return the currently debugged process and it's children
|
|
if (-1 == pid) {
|
|
return cmdj("dpj");
|
|
}
|
|
// Return the given pid and it's child processes
|
|
return cmdj("dpj " + QString::number(pid));
|
|
}
|
|
|
|
QJsonDocument CutterCore::getRegisterValues()
|
|
{
|
|
return cmdj("drj");
|
|
}
|
|
|
|
QList<VariableDescription> CutterCore::getVariables(RVA at)
|
|
{
|
|
QList<VariableDescription> ret;
|
|
QJsonObject varsObject = cmdj(QString("afvj @ %1").arg(at)).object();
|
|
|
|
auto addVars = [&](VariableDescription::RefType refType, const QJsonArray &array) {
|
|
for (const QJsonValue &varValue : array) {
|
|
QJsonObject varObject = varValue.toObject();
|
|
VariableDescription desc;
|
|
desc.refType = refType;
|
|
desc.name = varObject["name"].toString();
|
|
desc.type = varObject["type"].toString();
|
|
ret << desc;
|
|
}
|
|
};
|
|
|
|
addVars(VariableDescription::RefType::SP, varsObject["sp"].toArray());
|
|
addVars(VariableDescription::RefType::BP, varsObject["bp"].toArray());
|
|
addVars(VariableDescription::RefType::Reg, varsObject["reg"].toArray());
|
|
|
|
return ret;
|
|
}
|
|
|
|
QVector<RegisterRefValueDescription> CutterCore::getRegisterRefValues()
|
|
{
|
|
QJsonArray registerRefArray = cmdj("drrj").array();
|
|
QVector<RegisterRefValueDescription> result;
|
|
|
|
for (const QJsonValue value : registerRefArray) {
|
|
QJsonObject regRefObject = value.toObject();
|
|
|
|
RegisterRefValueDescription desc;
|
|
desc.name = regRefObject[RJsonKey::reg].toString();
|
|
desc.value = regRefObject[RJsonKey::value].toString();
|
|
desc.ref = regRefObject[RJsonKey::ref].toString();
|
|
|
|
result.push_back(desc);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
QString CutterCore::getRegisterName(QString registerRole)
|
|
{
|
|
return cmdRaw("drn " + registerRole).trimmed();
|
|
}
|
|
|
|
RVA CutterCore::getProgramCounterValue()
|
|
{
|
|
bool ok;
|
|
if (currentlyDebugging) {
|
|
// Use cmd because cmdRaw would not work with inner command backticked
|
|
// TODO: Risky command due to changes in API, search for something safer
|
|
RVA addr = cmd("dr `drn PC`").toULongLong(&ok, 16);
|
|
if (ok) {
|
|
return addr;
|
|
}
|
|
}
|
|
return RVA_INVALID;
|
|
}
|
|
|
|
void CutterCore::setRegister(QString regName, QString regValue)
|
|
{
|
|
cmdRaw(QString("dr %1=%2").arg(regName).arg(regValue));
|
|
emit registersChanged();
|
|
emit refreshCodeViews();
|
|
}
|
|
|
|
void CutterCore::setCurrentDebugThread(int tid)
|
|
{
|
|
if (!asyncCmd("dpt=" + QString::number(tid), debugTask)) {
|
|
return;
|
|
}
|
|
|
|
emit debugTaskStateChanged();
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
debugTask.clear();
|
|
emit registersChanged();
|
|
emit refreshCodeViews();
|
|
emit stackChanged();
|
|
syncAndSeekProgramCounter();
|
|
emit switchedThread();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::setCurrentDebugProcess(int pid)
|
|
{
|
|
if (!currentlyDebugging || !asyncCmd("dp=" + QString::number(pid), debugTask)) {
|
|
return;
|
|
}
|
|
|
|
emit debugTaskStateChanged();
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
debugTask.clear();
|
|
emit registersChanged();
|
|
emit refreshCodeViews();
|
|
emit stackChanged();
|
|
emit flagsChanged();
|
|
syncAndSeekProgramCounter();
|
|
emit switchedProcess();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::startDebug()
|
|
{
|
|
if (!currentlyDebugging) {
|
|
offsetPriorDebugging = getOffset();
|
|
}
|
|
currentlyOpenFile = getConfig("file.path");
|
|
|
|
if (!asyncCmd("ood", debugTask)) {
|
|
return;
|
|
}
|
|
|
|
emit debugTaskStateChanged();
|
|
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
if (debugTaskDialog) {
|
|
delete debugTaskDialog;
|
|
}
|
|
debugTask.clear();
|
|
|
|
emit registersChanged();
|
|
if (!currentlyDebugging) {
|
|
setConfig("asm.flags", false);
|
|
currentlyDebugging = true;
|
|
emit toggleDebugView();
|
|
emit refreshCodeViews();
|
|
}
|
|
|
|
emit codeRebased();
|
|
emit stackChanged();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTaskDialog = new RizinTaskDialog(debugTask);
|
|
debugTaskDialog->setBreakOnClose(true);
|
|
debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
debugTaskDialog->setDesc(tr("Starting native debug..."));
|
|
debugTaskDialog->show();
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::startEmulation()
|
|
{
|
|
if (!currentlyDebugging) {
|
|
offsetPriorDebugging = getOffset();
|
|
}
|
|
|
|
// clear registers, init esil state, stack, progcounter at current seek
|
|
asyncCmd("aei; aeim; aeip", debugTask);
|
|
|
|
emit debugTaskStateChanged();
|
|
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
if (debugTaskDialog) {
|
|
delete debugTaskDialog;
|
|
}
|
|
debugTask.clear();
|
|
|
|
if (!currentlyDebugging || !currentlyEmulating) {
|
|
// prevent register flags from appearing during debug/emul
|
|
setConfig("asm.flags", false);
|
|
// allows to view self-modifying code changes or other binary changes
|
|
setConfig("io.cache", true);
|
|
currentlyDebugging = true;
|
|
currentlyEmulating = true;
|
|
emit toggleDebugView();
|
|
}
|
|
|
|
emit registersChanged();
|
|
emit stackChanged();
|
|
emit codeRebased();
|
|
emit refreshCodeViews();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTaskDialog = new RizinTaskDialog(debugTask);
|
|
debugTaskDialog->setBreakOnClose(true);
|
|
debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
debugTaskDialog->setDesc(tr("Starting emulation..."));
|
|
debugTaskDialog->show();
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::attachRemote(const QString &uri)
|
|
{
|
|
if (!currentlyDebugging) {
|
|
offsetPriorDebugging = getOffset();
|
|
}
|
|
|
|
// connect to a debugger with the given plugin
|
|
asyncCmd("e cfg.debug=true; oodf " + uri, debugTask);
|
|
emit debugTaskStateChanged();
|
|
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this, uri]() {
|
|
if (debugTaskDialog) {
|
|
delete debugTaskDialog;
|
|
}
|
|
debugTask.clear();
|
|
// Check if we actually connected
|
|
bool connected = false;
|
|
RzCoreLocked core(Core());
|
|
RzList *descs = rz_id_storage_list(core->io->files);
|
|
RzListIter *it;
|
|
RzIODesc *desc;
|
|
CutterRzListForeach (descs, it, RzIODesc, desc) {
|
|
QString fileUri = QString(desc->uri);
|
|
if (!fileUri.compare(uri)) {
|
|
connected = true;
|
|
}
|
|
}
|
|
seekAndShow(getProgramCounterValue());
|
|
if (!connected) {
|
|
emit attachedRemote(false);
|
|
emit debugTaskStateChanged();
|
|
return;
|
|
}
|
|
|
|
emit registersChanged();
|
|
if (!currentlyDebugging || !currentlyEmulating) {
|
|
// prevent register flags from appearing during debug/emul
|
|
setConfig("asm.flags", false);
|
|
currentlyDebugging = true;
|
|
emit toggleDebugView();
|
|
}
|
|
|
|
currentlyRemoteDebugging = true;
|
|
emit codeRebased();
|
|
emit attachedRemote(true);
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTaskDialog = new RizinTaskDialog(debugTask);
|
|
debugTaskDialog->setBreakOnClose(true);
|
|
debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
debugTaskDialog->setDesc(tr("Connecting to: ") + uri);
|
|
debugTaskDialog->show();
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::attachDebug(int pid)
|
|
{
|
|
if (!currentlyDebugging) {
|
|
offsetPriorDebugging = getOffset();
|
|
}
|
|
|
|
QString attach_command = currentlyOpenFile.isEmpty() ? "o" : "oodf";
|
|
// attach to process with dbg plugin
|
|
asyncCmd("e cfg.debug=true;" + attach_command + " dbg://" + QString::number(pid), debugTask);
|
|
|
|
emit debugTaskStateChanged();
|
|
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this, pid]() {
|
|
if (debugTaskDialog) {
|
|
delete debugTaskDialog;
|
|
}
|
|
debugTask.clear();
|
|
|
|
syncAndSeekProgramCounter();
|
|
if (!currentlyDebugging || !currentlyEmulating) {
|
|
// prevent register flags from appearing during debug/emul
|
|
setConfig("asm.flags", false);
|
|
currentlyDebugging = true;
|
|
currentlyOpenFile = getConfig("file.path");
|
|
currentlyAttachedToPID = pid;
|
|
emit toggleDebugView();
|
|
}
|
|
|
|
emit codeRebased();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTaskDialog = new RizinTaskDialog(debugTask);
|
|
debugTaskDialog->setBreakOnClose(true);
|
|
debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
debugTaskDialog->setDesc(tr("Attaching to process (") + QString::number(pid) + ")...");
|
|
debugTaskDialog->show();
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::suspendDebug()
|
|
{
|
|
debugTask->breakTask();
|
|
debugTask->joinTask();
|
|
}
|
|
|
|
void CutterCore::stopDebug()
|
|
{
|
|
if (!currentlyDebugging) {
|
|
return;
|
|
}
|
|
|
|
if (!debugTask.isNull()) {
|
|
suspendDebug();
|
|
}
|
|
|
|
currentlyDebugging = false;
|
|
currentlyTracing = false;
|
|
currentlyRemoteDebugging = false;
|
|
emit debugTaskStateChanged();
|
|
|
|
if (currentlyEmulating) {
|
|
cmdEsil("aeim-; aei-; wcr; .ar-; aets-");
|
|
currentlyEmulating = false;
|
|
} else if (currentlyAttachedToPID != -1) {
|
|
// Use cmd because cmdRaw would not work with command concatenation
|
|
cmd(QString("dp- %1; o %2; .ar-")
|
|
.arg(QString::number(currentlyAttachedToPID), currentlyOpenFile));
|
|
currentlyAttachedToPID = -1;
|
|
} else {
|
|
QString ptraceFiles = "";
|
|
// close ptrace file descriptors left open
|
|
RzCoreLocked core(Core());
|
|
RzList *descs = rz_id_storage_list(core->io->files);
|
|
RzListIter *it;
|
|
RzIODesc *desc;
|
|
CutterRzListForeach (descs, it, RzIODesc, desc) {
|
|
QString URI = QString(desc->uri);
|
|
if (URI.contains("ptrace")) {
|
|
ptraceFiles += "o-" + QString::number(desc->fd) + ";";
|
|
}
|
|
}
|
|
// Use cmd because cmdRaw would not work with command concatenation
|
|
cmd("doc" + ptraceFiles);
|
|
}
|
|
|
|
syncAndSeekProgramCounter();
|
|
setConfig("asm.flags", true);
|
|
setConfig("io.cache", false);
|
|
emit codeRebased();
|
|
emit toggleDebugView();
|
|
offsetPriorDebugging = getOffset();
|
|
emit debugTaskStateChanged();
|
|
}
|
|
|
|
void CutterCore::syncAndSeekProgramCounter()
|
|
{
|
|
seekAndShow(getProgramCounterValue());
|
|
emit registersChanged();
|
|
}
|
|
|
|
void CutterCore::continueDebug()
|
|
{
|
|
if (!currentlyDebugging) {
|
|
return;
|
|
}
|
|
|
|
if (currentlyEmulating) {
|
|
if (!asyncCmdEsil("aec", debugTask)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!asyncCmd("dc", debugTask)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
emit debugTaskStateChanged();
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
debugTask.clear();
|
|
syncAndSeekProgramCounter();
|
|
emit refreshCodeViews();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::continueBackDebug()
|
|
{
|
|
if (!currentlyDebugging) {
|
|
return;
|
|
}
|
|
|
|
if (currentlyEmulating) {
|
|
if (!asyncCmdEsil("aecb", debugTask)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!asyncCmd("dcb", debugTask)) {
|
|
return;
|
|
}
|
|
}
|
|
emit debugTaskStateChanged();
|
|
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
debugTask.clear();
|
|
syncAndSeekProgramCounter();
|
|
emit refreshCodeViews();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::continueUntilDebug(QString offset)
|
|
{
|
|
if (!currentlyDebugging) {
|
|
return;
|
|
}
|
|
|
|
if (currentlyEmulating) {
|
|
if (!asyncCmdEsil("aecu " + offset, debugTask)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!asyncCmd("dcu " + offset, debugTask)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
emit debugTaskStateChanged();
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
debugTask.clear();
|
|
syncAndSeekProgramCounter();
|
|
emit refreshCodeViews();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::continueUntilCall()
|
|
{
|
|
if (!currentlyDebugging) {
|
|
return;
|
|
}
|
|
|
|
if (currentlyEmulating) {
|
|
if (!asyncCmdEsil("aecc", debugTask)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!asyncCmd("dcc", debugTask)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
emit debugTaskStateChanged();
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
debugTask.clear();
|
|
syncAndSeekProgramCounter();
|
|
emit refreshCodeViews();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::continueUntilSyscall()
|
|
{
|
|
if (!currentlyDebugging) {
|
|
return;
|
|
}
|
|
|
|
if (currentlyEmulating) {
|
|
if (!asyncCmdEsil("aecs", debugTask)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!asyncCmd("dcs", debugTask)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
emit debugTaskStateChanged();
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
debugTask.clear();
|
|
syncAndSeekProgramCounter();
|
|
emit refreshCodeViews();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::stepDebug()
|
|
{
|
|
if (!currentlyDebugging) {
|
|
return;
|
|
}
|
|
|
|
if (currentlyEmulating) {
|
|
if (!asyncCmdEsil("aes", debugTask)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!asyncCmd("ds", debugTask)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
emit debugTaskStateChanged();
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
debugTask.clear();
|
|
syncAndSeekProgramCounter();
|
|
emit refreshCodeViews();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::stepOverDebug()
|
|
{
|
|
if (!currentlyDebugging) {
|
|
return;
|
|
}
|
|
|
|
if (currentlyEmulating) {
|
|
if (!asyncCmdEsil("aeso", debugTask)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!asyncCmd("dso", debugTask)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
emit debugTaskStateChanged();
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
debugTask.clear();
|
|
syncAndSeekProgramCounter();
|
|
emit refreshCodeViews();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::stepOutDebug()
|
|
{
|
|
if (!currentlyDebugging) {
|
|
return;
|
|
}
|
|
|
|
emit debugTaskStateChanged();
|
|
if (!asyncCmd("dsf", debugTask)) {
|
|
return;
|
|
}
|
|
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
debugTask.clear();
|
|
syncAndSeekProgramCounter();
|
|
emit refreshCodeViews();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::stepBackDebug()
|
|
{
|
|
if (!currentlyDebugging) {
|
|
return;
|
|
}
|
|
|
|
if (currentlyEmulating) {
|
|
if (!asyncCmdEsil("aesb", debugTask)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!asyncCmd("dsb", debugTask)) {
|
|
return;
|
|
}
|
|
}
|
|
emit debugTaskStateChanged();
|
|
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
debugTask.clear();
|
|
syncAndSeekProgramCounter();
|
|
emit refreshCodeViews();
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
QStringList CutterCore::getDebugPlugins()
|
|
{
|
|
QStringList plugins;
|
|
QJsonArray pluginArray = cmdj("dLj").array();
|
|
|
|
for (const QJsonValue &value : pluginArray) {
|
|
QJsonObject pluginObject = value.toObject();
|
|
|
|
QString plugin = pluginObject[RJsonKey::name].toString();
|
|
|
|
plugins << plugin;
|
|
}
|
|
return plugins;
|
|
}
|
|
|
|
QString CutterCore::getActiveDebugPlugin()
|
|
{
|
|
return getConfig("dbg.backend");
|
|
}
|
|
|
|
void CutterCore::setDebugPlugin(QString plugin)
|
|
{
|
|
setConfig("dbg.backend", plugin);
|
|
}
|
|
|
|
void CutterCore::startTraceSession()
|
|
{
|
|
if (!currentlyDebugging || currentlyTracing) {
|
|
return;
|
|
}
|
|
|
|
if (currentlyEmulating) {
|
|
if (!asyncCmdEsil("aets+", debugTask)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!asyncCmd("dts+", debugTask)) {
|
|
return;
|
|
}
|
|
}
|
|
emit debugTaskStateChanged();
|
|
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
if (debugTaskDialog) {
|
|
delete debugTaskDialog;
|
|
}
|
|
debugTask.clear();
|
|
|
|
currentlyTracing = true;
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTaskDialog = new RizinTaskDialog(debugTask);
|
|
debugTaskDialog->setBreakOnClose(true);
|
|
debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
debugTaskDialog->setDesc(tr("Creating debug tracepoint..."));
|
|
debugTaskDialog->show();
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::stopTraceSession()
|
|
{
|
|
if (!currentlyDebugging || !currentlyTracing) {
|
|
return;
|
|
}
|
|
|
|
if (currentlyEmulating) {
|
|
if (!asyncCmdEsil("aets-", debugTask)) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (!asyncCmd("dts-", debugTask)) {
|
|
return;
|
|
}
|
|
}
|
|
emit debugTaskStateChanged();
|
|
|
|
connect(debugTask.data(), &RizinTask::finished, this, [this]() {
|
|
if (debugTaskDialog) {
|
|
delete debugTaskDialog;
|
|
}
|
|
debugTask.clear();
|
|
|
|
currentlyTracing = false;
|
|
emit debugTaskStateChanged();
|
|
});
|
|
|
|
debugTaskDialog = new RizinTaskDialog(debugTask);
|
|
debugTaskDialog->setBreakOnClose(true);
|
|
debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
|
|
debugTaskDialog->setDesc(tr("Stopping debug session..."));
|
|
debugTaskDialog->show();
|
|
|
|
debugTask->startTask();
|
|
}
|
|
|
|
void CutterCore::toggleBreakpoint(RVA addr)
|
|
{
|
|
cmdRaw(QString("dbs %1").arg(addr));
|
|
emit breakpointsChanged(addr);
|
|
}
|
|
|
|
void CutterCore::addBreakpoint(const BreakpointDescription &config)
|
|
{
|
|
CORE_LOCK();
|
|
RzBreakpointItem *breakpoint = nullptr;
|
|
int watchpoint_prot = 0;
|
|
if (config.hw) {
|
|
watchpoint_prot = config.permission & ~(RZ_PERM_X);
|
|
}
|
|
|
|
auto address = config.addr;
|
|
char *module = nullptr;
|
|
QByteArray moduleNameData;
|
|
if (config.type == BreakpointDescription::Named) {
|
|
address = Core()->math(config.positionExpression);
|
|
} else if (config.type == BreakpointDescription::Module) {
|
|
address = 0;
|
|
moduleNameData = config.positionExpression.toUtf8();
|
|
module = moduleNameData.data();
|
|
}
|
|
breakpoint = rz_debug_bp_add(core->dbg, address, (config.hw && watchpoint_prot == 0),
|
|
watchpoint_prot, watchpoint_prot, module, config.moduleDelta);
|
|
if (!breakpoint) {
|
|
QMessageBox::critical(nullptr, tr("Breakpoint error"), tr("Failed to create breakpoint"));
|
|
return;
|
|
}
|
|
if (config.type == BreakpointDescription::Named) {
|
|
updateOwnedCharPtr(breakpoint->expr, config.positionExpression);
|
|
}
|
|
|
|
if (config.hw) {
|
|
breakpoint->size = config.size;
|
|
}
|
|
if (config.type == BreakpointDescription::Named) {
|
|
updateOwnedCharPtr(breakpoint->name, config.positionExpression);
|
|
}
|
|
|
|
int index = std::find(core->dbg->bp->bps_idx,
|
|
core->dbg->bp->bps_idx + core->dbg->bp->bps_idx_count, breakpoint)
|
|
- core->dbg->bp->bps_idx;
|
|
|
|
breakpoint->enabled = config.enabled;
|
|
if (config.trace) {
|
|
setBreakpointTrace(index, config.trace);
|
|
}
|
|
if (!config.condition.isEmpty()) {
|
|
updateOwnedCharPtr(breakpoint->cond, config.condition);
|
|
}
|
|
if (!config.command.isEmpty()) {
|
|
updateOwnedCharPtr(breakpoint->data, config.command);
|
|
}
|
|
emit breakpointsChanged(breakpoint->addr);
|
|
}
|
|
|
|
void CutterCore::updateBreakpoint(int index, const BreakpointDescription &config)
|
|
{
|
|
CORE_LOCK();
|
|
if (auto bp = rz_bp_get_index(core->dbg->bp, index)) {
|
|
rz_bp_del(core->dbg->bp, bp->addr);
|
|
}
|
|
// Delete by index currently buggy,
|
|
// required for breakpoints with non address based position
|
|
// rz_bp_del_index(core->dbg->bp, index);
|
|
addBreakpoint(config);
|
|
}
|
|
|
|
void CutterCore::delBreakpoint(RVA addr)
|
|
{
|
|
cmdRaw("db- " + RzAddressString(addr));
|
|
emit breakpointsChanged(addr);
|
|
}
|
|
|
|
void CutterCore::delAllBreakpoints()
|
|
{
|
|
cmdRaw("db-*");
|
|
emit refreshCodeViews();
|
|
}
|
|
|
|
void CutterCore::enableBreakpoint(RVA addr)
|
|
{
|
|
cmdRaw("dbe " + RzAddressString(addr));
|
|
emit breakpointsChanged(addr);
|
|
}
|
|
|
|
void CutterCore::disableBreakpoint(RVA addr)
|
|
{
|
|
cmdRaw("dbd " + RzAddressString(addr));
|
|
emit breakpointsChanged(addr);
|
|
}
|
|
|
|
void CutterCore::setBreakpointTrace(int index, bool enabled)
|
|
{
|
|
if (enabled) {
|
|
cmdRaw(QString("dbite %1").arg(index));
|
|
} else {
|
|
cmdRaw(QString("dbitd %1").arg(index));
|
|
}
|
|
}
|
|
|
|
static BreakpointDescription breakpointDescriptionFromRizin(int index, rz_bp_item_t *bpi)
|
|
{
|
|
BreakpointDescription bp;
|
|
bp.addr = bpi->addr;
|
|
bp.index = index;
|
|
bp.size = bpi->size;
|
|
if (bpi->expr) {
|
|
bp.positionExpression = bpi->expr;
|
|
bp.type = BreakpointDescription::Named;
|
|
}
|
|
bp.name = bpi->name;
|
|
bp.permission = bpi->perm;
|
|
bp.command = bpi->data;
|
|
bp.condition = bpi->cond;
|
|
bp.hw = bpi->hw;
|
|
bp.trace = bpi->trace;
|
|
bp.enabled = bpi->enabled;
|
|
return bp;
|
|
}
|
|
|
|
int CutterCore::breakpointIndexAt(RVA addr)
|
|
{
|
|
CORE_LOCK();
|
|
return rz_bp_get_index_at(core->dbg->bp, addr);
|
|
}
|
|
|
|
BreakpointDescription CutterCore::getBreakpointAt(RVA addr)
|
|
{
|
|
CORE_LOCK();
|
|
int index = breakpointIndexAt(addr);
|
|
auto bp = rz_bp_get_index(core->dbg->bp, index);
|
|
if (bp) {
|
|
return breakpointDescriptionFromRizin(index, bp);
|
|
}
|
|
return BreakpointDescription();
|
|
}
|
|
|
|
QList<BreakpointDescription> CutterCore::getBreakpoints()
|
|
{
|
|
CORE_LOCK();
|
|
QList<BreakpointDescription> ret;
|
|
// TODO: use higher level API, don't touch rizin bps_idx directly
|
|
for (int i = 0; i < core->dbg->bp->bps_idx_count; i++) {
|
|
if (auto bpi = core->dbg->bp->bps_idx[i]) {
|
|
ret.push_back(breakpointDescriptionFromRizin(i, bpi));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<RVA> CutterCore::getBreakpointsAddresses()
|
|
{
|
|
QList<RVA> bpAddresses;
|
|
for (const BreakpointDescription &bp : getBreakpoints()) {
|
|
bpAddresses << bp.addr;
|
|
}
|
|
|
|
return bpAddresses;
|
|
}
|
|
|
|
QList<RVA> CutterCore::getBreakpointsInFunction(RVA funcAddr)
|
|
{
|
|
QList<RVA> allBreakpoints = getBreakpointsAddresses();
|
|
QList<RVA> functionBreakpoints;
|
|
|
|
// Use std manipulations to take only the breakpoints that belong to this function
|
|
std::copy_if(allBreakpoints.begin(), allBreakpoints.end(),
|
|
std::back_inserter(functionBreakpoints),
|
|
[this, funcAddr](RVA BPadd) { return getFunctionStart(BPadd) == funcAddr; });
|
|
return functionBreakpoints;
|
|
}
|
|
|
|
bool CutterCore::isBreakpoint(const QList<RVA> &breakpoints, RVA addr)
|
|
{
|
|
return breakpoints.contains(addr);
|
|
}
|
|
|
|
QJsonDocument CutterCore::getBacktrace()
|
|
{
|
|
return cmdj("dbtj");
|
|
}
|
|
|
|
QList<ProcessDescription> CutterCore::getAllProcesses()
|
|
{
|
|
QList<ProcessDescription> ret;
|
|
QJsonArray processArray = cmdj("dplj").array();
|
|
|
|
for (const QJsonValue &value : processArray) {
|
|
QJsonObject procObject = value.toObject();
|
|
|
|
ProcessDescription proc;
|
|
|
|
proc.pid = procObject[RJsonKey::pid].toInt();
|
|
proc.uid = procObject[RJsonKey::uid].toInt();
|
|
proc.status = procObject[RJsonKey::status].toString();
|
|
proc.path = procObject[RJsonKey::path].toString();
|
|
|
|
ret << proc;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<MemoryMapDescription> CutterCore::getMemoryMap()
|
|
{
|
|
QList<MemoryMapDescription> ret;
|
|
QJsonArray memoryMapArray = cmdj("dmj").array();
|
|
|
|
for (const QJsonValue &value : memoryMapArray) {
|
|
QJsonObject memMapObject = value.toObject();
|
|
|
|
MemoryMapDescription memMap;
|
|
|
|
memMap.name = memMapObject[RJsonKey::name].toString();
|
|
memMap.fileName = memMapObject[RJsonKey::file].toString();
|
|
memMap.addrStart = memMapObject[RJsonKey::addr].toVariant().toULongLong();
|
|
memMap.addrEnd = memMapObject[RJsonKey::addr_end].toVariant().toULongLong();
|
|
memMap.type = memMapObject[RJsonKey::type].toString();
|
|
memMap.permission = memMapObject[RJsonKey::perm].toString();
|
|
|
|
ret << memMap;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QStringList CutterCore::getStats()
|
|
{
|
|
QStringList stats;
|
|
cmdRaw("fs functions");
|
|
|
|
// The cmd coomand is frequently used in this function because
|
|
// cmdRaw would not work with grep
|
|
stats << cmd("f~?").trimmed();
|
|
|
|
QString imps = cmd("ii~?").trimmed();
|
|
stats << imps;
|
|
|
|
cmdRaw("fs symbols");
|
|
stats << cmd("f~?").trimmed();
|
|
cmdRaw("fs strings");
|
|
stats << cmd("f~?").trimmed();
|
|
cmdRaw("fs relocs");
|
|
stats << cmd("f~?").trimmed();
|
|
cmdRaw("fs sections");
|
|
stats << cmd("f~?").trimmed();
|
|
cmdRaw("fs *");
|
|
stats << cmd("f~?").trimmed();
|
|
|
|
return stats;
|
|
}
|
|
|
|
void CutterCore::setGraphEmpty(bool empty)
|
|
{
|
|
emptyGraph = empty;
|
|
}
|
|
|
|
bool CutterCore::isGraphEmpty()
|
|
{
|
|
return emptyGraph;
|
|
}
|
|
|
|
void CutterCore::getOpcodes()
|
|
{
|
|
this->opcodes = cmdList("?O");
|
|
this->regs = cmdList("drp~[1]");
|
|
}
|
|
|
|
void CutterCore::setSettings()
|
|
{
|
|
setConfig("scr.interactive", false);
|
|
|
|
setConfig("hex.pairs", false);
|
|
setConfig("asm.xrefs", false);
|
|
|
|
setConfig("asm.tabs.once", true);
|
|
setConfig("asm.flags.middle", 2);
|
|
|
|
setConfig("analysis.hasnext", false);
|
|
setConfig("asm.lines.call", false);
|
|
|
|
// Colors
|
|
setConfig("scr.color", COLOR_MODE_DISABLED);
|
|
|
|
// Don't show hits
|
|
setConfig("search.flags", false);
|
|
}
|
|
|
|
QList<RVA> CutterCore::getSeekHistory()
|
|
{
|
|
CORE_LOCK();
|
|
QList<RVA> ret;
|
|
RzListIter *it;
|
|
RzCoreSeekItem *undo;
|
|
RzList *list = rz_core_seek_list(core);
|
|
CutterRzListForeach (list, it, RzCoreSeekItem, undo) {
|
|
ret << undo->offset;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QStringList CutterCore::getAsmPluginNames()
|
|
{
|
|
CORE_LOCK();
|
|
RzListIter *it;
|
|
QStringList ret;
|
|
|
|
RzAsmPlugin *ap;
|
|
CutterRzListForeach (core->rasm->plugins, it, RzAsmPlugin, ap) {
|
|
ret << ap->name;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QStringList CutterCore::getAnalysisPluginNames()
|
|
{
|
|
CORE_LOCK();
|
|
RzListIter *it;
|
|
QStringList ret;
|
|
|
|
RzAnalysisPlugin *ap;
|
|
CutterRzListForeach (core->analysis->plugins, it, RzAnalysisPlugin, ap) {
|
|
ret << ap->name;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<RzBinPluginDescription> CutterCore::getBinPluginDescriptions(bool bin, bool xtr)
|
|
{
|
|
CORE_LOCK();
|
|
QList<RzBinPluginDescription> ret;
|
|
RzListIter *it;
|
|
if (bin) {
|
|
RzBinPlugin *bp;
|
|
CutterRzListForeach (core->bin->plugins, it, RzBinPlugin, bp) {
|
|
RzBinPluginDescription desc;
|
|
desc.name = bp->name ? bp->name : "";
|
|
desc.description = bp->desc ? bp->desc : "";
|
|
desc.license = bp->license ? bp->license : "";
|
|
desc.type = "bin";
|
|
ret.append(desc);
|
|
}
|
|
}
|
|
if (xtr) {
|
|
RzBinXtrPlugin *bx;
|
|
CutterRzListForeach (core->bin->binxtrs, it, RzBinXtrPlugin, bx) {
|
|
RzBinPluginDescription desc;
|
|
desc.name = bx->name ? bx->name : "";
|
|
desc.description = bx->desc ? bx->desc : "";
|
|
desc.license = bx->license ? bx->license : "";
|
|
desc.type = "xtr";
|
|
ret.append(desc);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QList<RzIOPluginDescription> CutterCore::getRIOPluginDescriptions()
|
|
{
|
|
CORE_LOCK();
|
|
QList<RzIOPluginDescription> ret;
|
|
RzListIter *it;
|
|
RzIOPlugin *p;
|
|
CutterRzListForeach (core->io->plugins, it, RzIOPlugin, p) {
|
|
RzIOPluginDescription desc;
|
|
desc.name = p->name ? p->name : "";
|
|
desc.description = p->desc ? p->desc : "";
|
|
desc.license = p->license ? p->license : "";
|
|
desc.permissions = QString("r") + (p->write ? "w" : "_") + (p->isdbg ? "d" : "_");
|
|
if (p->uris) {
|
|
desc.uris = QString::fromUtf8(p->uris).split(",");
|
|
}
|
|
ret.append(desc);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QList<RzCorePluginDescription> CutterCore::getRCorePluginDescriptions()
|
|
{
|
|
CORE_LOCK();
|
|
QList<RzCorePluginDescription> ret;
|
|
RzListIter *it;
|
|
RzCorePlugin *p;
|
|
CutterRzListForeach (core->plugins, it, RzCorePlugin, p) {
|
|
RzCorePluginDescription desc;
|
|
desc.name = p->name ? p->name : "";
|
|
desc.description = p->desc ? p->desc : "";
|
|
desc.license = p->license ? p->license : "";
|
|
ret.append(desc);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QList<RzAsmPluginDescription> CutterCore::getRAsmPluginDescriptions()
|
|
{
|
|
CORE_LOCK();
|
|
RzListIter *it;
|
|
QList<RzAsmPluginDescription> ret;
|
|
|
|
RzAsmPlugin *ap;
|
|
CutterRzListForeach (core->rasm->plugins, it, RzAsmPlugin, ap) {
|
|
RzAsmPluginDescription plugin;
|
|
|
|
plugin.name = ap->name;
|
|
plugin.architecture = ap->arch;
|
|
plugin.author = ap->author;
|
|
plugin.version = ap->version;
|
|
plugin.cpus = ap->cpus;
|
|
plugin.description = ap->desc;
|
|
plugin.license = ap->license;
|
|
|
|
ret << plugin;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<FunctionDescription> CutterCore::getAllFunctions()
|
|
{
|
|
CORE_LOCK();
|
|
|
|
QList<FunctionDescription> funcList;
|
|
funcList.reserve(rz_list_length(core->analysis->fcns));
|
|
|
|
RzListIter *iter;
|
|
RzAnalysisFunction *fcn;
|
|
CutterRzListForeach (core->analysis->fcns, iter, RzAnalysisFunction, fcn) {
|
|
FunctionDescription function;
|
|
function.offset = fcn->addr;
|
|
function.linearSize = rz_analysis_function_linear_size(fcn);
|
|
function.nargs = rz_analysis_var_count(core->analysis, fcn, 'b', 1)
|
|
+ rz_analysis_var_count(core->analysis, fcn, 'r', 1)
|
|
+ rz_analysis_var_count(core->analysis, fcn, 's', 1);
|
|
function.nlocals = rz_analysis_var_count(core->analysis, fcn, 'b', 0)
|
|
+ rz_analysis_var_count(core->analysis, fcn, 'r', 0)
|
|
+ rz_analysis_var_count(core->analysis, fcn, 's', 0);
|
|
function.nbbs = rz_list_length(fcn->bbs);
|
|
function.calltype = fcn->cc ? QString::fromUtf8(fcn->cc) : QString();
|
|
function.name = fcn->name ? QString::fromUtf8(fcn->name) : QString();
|
|
function.edges = rz_analysis_function_count_edges(fcn, nullptr);
|
|
function.stackframe = fcn->maxstack;
|
|
funcList.append(function);
|
|
}
|
|
|
|
return funcList;
|
|
}
|
|
|
|
QList<ImportDescription> CutterCore::getAllImports()
|
|
{
|
|
CORE_LOCK();
|
|
QList<ImportDescription> ret;
|
|
|
|
QJsonArray importsArray = cmdj("iij").array();
|
|
|
|
for (const QJsonValue &value : importsArray) {
|
|
QJsonObject importObject = value.toObject();
|
|
|
|
ImportDescription import;
|
|
|
|
import.plt = importObject[RJsonKey::plt].toVariant().toULongLong();
|
|
import.ordinal = importObject[RJsonKey::ordinal].toInt();
|
|
import.bind = importObject[RJsonKey::bind].toString();
|
|
import.type = importObject[RJsonKey::type].toString();
|
|
import.libname = importObject[RJsonKey::libname].toString();
|
|
import.name = importObject[RJsonKey::name].toString();
|
|
|
|
ret << import;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<ExportDescription> CutterCore::getAllExports()
|
|
{
|
|
CORE_LOCK();
|
|
QList<ExportDescription> ret;
|
|
|
|
QJsonArray exportsArray = cmdj("iEj").array();
|
|
|
|
for (const QJsonValue &value : exportsArray) {
|
|
QJsonObject exportObject = value.toObject();
|
|
|
|
ExportDescription exp;
|
|
|
|
exp.vaddr = exportObject[RJsonKey::vaddr].toVariant().toULongLong();
|
|
exp.paddr = exportObject[RJsonKey::paddr].toVariant().toULongLong();
|
|
exp.size = exportObject[RJsonKey::size].toVariant().toULongLong();
|
|
exp.type = exportObject[RJsonKey::type].toString();
|
|
exp.name = exportObject[RJsonKey::name].toString();
|
|
exp.flag_name = exportObject[RJsonKey::flagname].toString();
|
|
|
|
ret << exp;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<SymbolDescription> CutterCore::getAllSymbols()
|
|
{
|
|
CORE_LOCK();
|
|
RzListIter *it;
|
|
|
|
QList<SymbolDescription> ret;
|
|
|
|
RzBinSymbol *bs;
|
|
if (core && core->bin && core->bin->cur && core->bin->cur->o) {
|
|
CutterRzListForeach (core->bin->cur->o->symbols, it, RzBinSymbol, bs) {
|
|
QString type = QString(bs->bind) + " " + QString(bs->type);
|
|
SymbolDescription symbol;
|
|
symbol.vaddr = bs->vaddr;
|
|
symbol.name = QString(bs->name);
|
|
symbol.bind = QString(bs->bind);
|
|
symbol.type = QString(bs->type);
|
|
ret << symbol;
|
|
}
|
|
|
|
/* list entrypoints as symbols too */
|
|
int n = 0;
|
|
RzBinAddr *entry;
|
|
CutterRzListForeach (core->bin->cur->o->entries, it, RzBinAddr, entry) {
|
|
SymbolDescription symbol;
|
|
symbol.vaddr = entry->vaddr;
|
|
symbol.name = QString("entry") + QString::number(n++);
|
|
symbol.bind.clear();
|
|
symbol.type = "entry";
|
|
ret << symbol;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<HeaderDescription> CutterCore::getAllHeaders()
|
|
{
|
|
CORE_LOCK();
|
|
QList<HeaderDescription> ret;
|
|
|
|
QJsonArray headersArray = cmdj("ihj").array();
|
|
|
|
for (const QJsonValue &value : headersArray) {
|
|
QJsonObject headerObject = value.toObject();
|
|
|
|
HeaderDescription header;
|
|
|
|
header.vaddr = headerObject[RJsonKey::vaddr].toVariant().toULongLong();
|
|
header.paddr = headerObject[RJsonKey::paddr].toVariant().toULongLong();
|
|
header.value = headerObject[RJsonKey::comment].toString();
|
|
header.name = headerObject[RJsonKey::name].toString();
|
|
|
|
ret << header;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<FlirtDescription> CutterCore::getSignaturesDB()
|
|
{
|
|
CORE_LOCK();
|
|
QList<FlirtDescription> sigdb;
|
|
const char *sigdb_path = rz_config_get(core->config, "flirt.sigdb.path");
|
|
if (RZ_STR_ISEMPTY(sigdb_path)) {
|
|
return sigdb;
|
|
}
|
|
|
|
RzList *list = rz_sign_sigdb_load_database(sigdb_path, true);
|
|
void *ptr = NULL;
|
|
RzListIter *iter = NULL;
|
|
|
|
rz_list_foreach(list, iter, ptr)
|
|
{
|
|
RzSigDBEntry *sig = static_cast<RzSigDBEntry *>(ptr);
|
|
FlirtDescription flirt;
|
|
flirt.bin_name = sig->bin_name;
|
|
flirt.arch_name = sig->arch_name;
|
|
flirt.base_name = sig->base_name;
|
|
flirt.short_path = sig->short_path;
|
|
flirt.file_path = sig->file_path;
|
|
flirt.details = sig->details;
|
|
flirt.n_modules = QString::number(sig->n_modules);
|
|
flirt.arch_bits = QString::number(sig->arch_bits);
|
|
sigdb << flirt;
|
|
}
|
|
return sigdb;
|
|
}
|
|
|
|
QList<CommentDescription> CutterCore::getAllComments(const QString &filterType)
|
|
{
|
|
CORE_LOCK();
|
|
QList<CommentDescription> ret;
|
|
|
|
QJsonArray commentsArray = cmdj("CClj").array();
|
|
for (const QJsonValue &value : commentsArray) {
|
|
QJsonObject commentObject = value.toObject();
|
|
|
|
QString type = commentObject[RJsonKey::type].toString();
|
|
if (type != filterType)
|
|
continue;
|
|
|
|
CommentDescription comment;
|
|
comment.offset = commentObject[RJsonKey::offset].toVariant().toULongLong();
|
|
comment.name = commentObject[RJsonKey::name].toString();
|
|
|
|
ret << comment;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QList<RelocDescription> CutterCore::getAllRelocs()
|
|
{
|
|
CORE_LOCK();
|
|
QList<RelocDescription> ret;
|
|
|
|
if (core && core->bin && core->bin->cur && core->bin->cur->o) {
|
|
auto relocs = rz_bin_object_patch_relocs(core->bin->cur, core->bin->cur->o);
|
|
if (!relocs) {
|
|
return ret;
|
|
}
|
|
for (size_t i = 0; i < relocs->relocs_count; i++) {
|
|
RzBinReloc *reloc = relocs->relocs[i];
|
|
RelocDescription desc;
|
|
desc.vaddr = reloc->vaddr;
|
|
desc.paddr = reloc->paddr;
|
|
desc.type = (reloc->additive ? "ADD_" : "SET_") + QString::number(reloc->type);
|
|
|
|
if (reloc->import)
|
|
desc.name = reloc->import->name;
|
|
else
|
|
desc.name = QString("reloc_%1").arg(QString::number(reloc->vaddr, 16));
|
|
|
|
ret << desc;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<StringDescription> CutterCore::getAllStrings()
|
|
{
|
|
return parseStringsJson(cmdjTask("izzj"));
|
|
}
|
|
|
|
QList<StringDescription> CutterCore::parseStringsJson(const QJsonDocument &doc)
|
|
{
|
|
QList<StringDescription> ret;
|
|
|
|
QJsonArray stringsArray = doc.array();
|
|
for (const QJsonValue &value : stringsArray) {
|
|
QJsonObject stringObject = value.toObject();
|
|
|
|
StringDescription string;
|
|
|
|
string.string = stringObject[RJsonKey::string].toString();
|
|
string.vaddr = stringObject[RJsonKey::vaddr].toVariant().toULongLong();
|
|
string.type = stringObject[RJsonKey::type].toString();
|
|
string.size = stringObject[RJsonKey::size].toVariant().toUInt();
|
|
string.length = stringObject[RJsonKey::length].toVariant().toUInt();
|
|
string.section = stringObject[RJsonKey::section].toString();
|
|
|
|
ret << string;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<FlagspaceDescription> CutterCore::getAllFlagspaces()
|
|
{
|
|
CORE_LOCK();
|
|
QList<FlagspaceDescription> ret;
|
|
|
|
QJsonArray flagspacesArray = cmdj("fsj").array();
|
|
for (const QJsonValue &value : flagspacesArray) {
|
|
QJsonObject flagspaceObject = value.toObject();
|
|
|
|
FlagspaceDescription flagspace;
|
|
|
|
flagspace.name = flagspaceObject[RJsonKey::name].toString();
|
|
|
|
ret << flagspace;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QList<FlagDescription> CutterCore::getAllFlags(QString flagspace)
|
|
{
|
|
CORE_LOCK();
|
|
QList<FlagDescription> ret;
|
|
|
|
if (!flagspace.isEmpty())
|
|
cmdRaw("fs " + flagspace);
|
|
else
|
|
cmdRaw("fs *");
|
|
|
|
QJsonArray flagsArray = cmdj("fj").array();
|
|
for (const QJsonValue &value : flagsArray) {
|
|
QJsonObject flagObject = value.toObject();
|
|
|
|
FlagDescription flag;
|
|
|
|
flag.offset = flagObject[RJsonKey::offset].toVariant().toULongLong();
|
|
flag.size = flagObject[RJsonKey::size].toVariant().toULongLong();
|
|
flag.name = flagObject[RJsonKey::name].toString();
|
|
flag.realname = flagObject[RJsonKey::realname].toString();
|
|
|
|
ret << flag;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QList<SectionDescription> CutterCore::getAllSections()
|
|
{
|
|
CORE_LOCK();
|
|
QList<SectionDescription> sections;
|
|
|
|
RzBinObject *o = rz_bin_cur_object(core->bin);
|
|
if (!o) {
|
|
return sections;
|
|
}
|
|
|
|
RzList *sects = rz_bin_object_get_sections(o);
|
|
if (!sects) {
|
|
return sections;
|
|
}
|
|
RzList *hashnames = rz_list_newf(free);
|
|
if (!hashnames) {
|
|
return sections;
|
|
}
|
|
rz_list_push(hashnames, rz_str_new("entropy"));
|
|
RzListIter *it;
|
|
RzBinSection *sect;
|
|
CutterRzListForeach (sects, it, RzBinSection, sect) {
|
|
if (RZ_STR_ISEMPTY(sect->name))
|
|
continue;
|
|
|
|
SectionDescription section;
|
|
section.name = sect->name;
|
|
section.vaddr = sect->vaddr;
|
|
section.vsize = sect->vsize;
|
|
section.paddr = sect->paddr;
|
|
section.size = sect->size;
|
|
section.perm = rz_str_rwx_i(sect->perm);
|
|
if (sect->size > 0) {
|
|
HtPP *digests = rz_core_bin_create_digests(core, sect->paddr, sect->size, hashnames);
|
|
if (!digests) {
|
|
continue;
|
|
}
|
|
const char *entropy = (const char *)ht_pp_find(digests, "entropy", NULL);
|
|
section.entropy = rz_str_get(entropy);
|
|
ht_pp_free(digests);
|
|
}
|
|
section.entropy = "";
|
|
|
|
sections << section;
|
|
}
|
|
rz_list_free(sects);
|
|
return sections;
|
|
}
|
|
|
|
QStringList CutterCore::getSectionList()
|
|
{
|
|
CORE_LOCK();
|
|
QStringList ret;
|
|
|
|
RzBinObject *o = rz_bin_cur_object(core->bin);
|
|
if (!o) {
|
|
return ret;
|
|
}
|
|
|
|
RzList *sects = rz_bin_object_get_sections(o);
|
|
if (!sects) {
|
|
return ret;
|
|
}
|
|
RzListIter *it;
|
|
RzBinSection *sect;
|
|
CutterRzListForeach (sects, it, RzBinSection, sect) {
|
|
ret << sect->name;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QList<SegmentDescription> CutterCore::getAllSegments()
|
|
{
|
|
CORE_LOCK();
|
|
QList<SegmentDescription> ret;
|
|
|
|
QJsonArray segments = cmdj("iSSj").array();
|
|
|
|
for (const QJsonValue &value : segments) {
|
|
QJsonObject segmentObject = value.toObject();
|
|
|
|
QString name = segmentObject[RJsonKey::name].toString();
|
|
if (name.isEmpty())
|
|
continue;
|
|
|
|
SegmentDescription segment;
|
|
segment.name = name;
|
|
segment.vaddr = segmentObject[RJsonKey::vaddr].toVariant().toULongLong();
|
|
segment.paddr = segmentObject[RJsonKey::paddr].toVariant().toULongLong();
|
|
segment.size = segmentObject[RJsonKey::size].toVariant().toULongLong();
|
|
segment.vsize = segmentObject[RJsonKey::vsize].toVariant().toULongLong();
|
|
segment.perm = segmentObject[RJsonKey::perm].toString();
|
|
|
|
ret << segment;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QList<EntrypointDescription> CutterCore::getAllEntrypoint()
|
|
{
|
|
CORE_LOCK();
|
|
QList<EntrypointDescription> ret;
|
|
|
|
QJsonArray entrypointsArray = cmdj("iej").array();
|
|
for (const QJsonValue &value : entrypointsArray) {
|
|
QJsonObject entrypointObject = value.toObject();
|
|
|
|
EntrypointDescription entrypoint;
|
|
|
|
entrypoint.vaddr = entrypointObject[RJsonKey::vaddr].toVariant().toULongLong();
|
|
entrypoint.paddr = entrypointObject[RJsonKey::paddr].toVariant().toULongLong();
|
|
entrypoint.baddr = entrypointObject[RJsonKey::baddr].toVariant().toULongLong();
|
|
entrypoint.laddr = entrypointObject[RJsonKey::laddr].toVariant().toULongLong();
|
|
entrypoint.haddr = entrypointObject[RJsonKey::haddr].toVariant().toULongLong();
|
|
entrypoint.type = entrypointObject[RJsonKey::type].toString();
|
|
|
|
ret << entrypoint;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QList<BinClassDescription> CutterCore::getAllClassesFromBin()
|
|
{
|
|
CORE_LOCK();
|
|
QList<BinClassDescription> ret;
|
|
|
|
QJsonArray classesArray = cmdj("icj").array();
|
|
for (const QJsonValue &value : classesArray) {
|
|
QJsonObject classObject = value.toObject();
|
|
|
|
BinClassDescription cls;
|
|
|
|
cls.name = classObject[RJsonKey::classname].toString();
|
|
cls.addr = classObject[RJsonKey::addr].toVariant().toULongLong();
|
|
cls.index = classObject[RJsonKey::index].toVariant().toULongLong();
|
|
|
|
for (const QJsonValue &value2 : classObject[RJsonKey::methods].toArray()) {
|
|
QJsonObject methObject = value2.toObject();
|
|
|
|
BinClassMethodDescription meth;
|
|
|
|
meth.name = methObject[RJsonKey::name].toString();
|
|
meth.addr = methObject[RJsonKey::addr].toVariant().toULongLong();
|
|
|
|
cls.methods << meth;
|
|
}
|
|
|
|
for (const QJsonValue &value2 : classObject[RJsonKey::fields].toArray()) {
|
|
QJsonObject fieldObject = value2.toObject();
|
|
|
|
BinClassFieldDescription field;
|
|
|
|
field.name = fieldObject[RJsonKey::name].toString();
|
|
field.addr = fieldObject[RJsonKey::addr].toVariant().toULongLong();
|
|
|
|
cls.fields << field;
|
|
}
|
|
|
|
ret << cls;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QList<BinClassDescription> CutterCore::getAllClassesFromFlags()
|
|
{
|
|
static const QRegularExpression classFlagRegExp("^class\\.(.*)$");
|
|
static const QRegularExpression methodFlagRegExp("^method\\.([^\\.]*)\\.(.*)$");
|
|
|
|
CORE_LOCK();
|
|
QList<BinClassDescription> ret;
|
|
QMap<QString, BinClassDescription *> classesCache;
|
|
|
|
QJsonArray flagsArray = cmdj("fj@F:classes").array();
|
|
for (const QJsonValue &value : flagsArray) {
|
|
QJsonObject flagObject = value.toObject();
|
|
|
|
QString flagName = flagObject[RJsonKey::name].toString();
|
|
|
|
QRegularExpressionMatch match = classFlagRegExp.match(flagName);
|
|
if (match.hasMatch()) {
|
|
QString className = match.captured(1);
|
|
BinClassDescription *desc = nullptr;
|
|
auto it = classesCache.find(className);
|
|
if (it == classesCache.end()) {
|
|
BinClassDescription cls = {};
|
|
ret << cls;
|
|
desc = &ret.last();
|
|
classesCache[className] = desc;
|
|
} else {
|
|
desc = it.value();
|
|
}
|
|
desc->name = match.captured(1);
|
|
desc->addr = flagObject[RJsonKey::offset].toVariant().toULongLong();
|
|
desc->index = RVA_INVALID;
|
|
continue;
|
|
}
|
|
|
|
match = methodFlagRegExp.match(flagName);
|
|
if (match.hasMatch()) {
|
|
QString className = match.captured(1);
|
|
BinClassDescription *classDesc = nullptr;
|
|
auto it = classesCache.find(className);
|
|
if (it == classesCache.end()) {
|
|
// add a new stub class, will be replaced if class flag comes after it
|
|
BinClassDescription cls;
|
|
cls.name = tr("Unknown (%1)").arg(className);
|
|
cls.addr = RVA_INVALID;
|
|
cls.index = 0;
|
|
ret << cls;
|
|
classDesc = &ret.last();
|
|
classesCache[className] = classDesc;
|
|
} else {
|
|
classDesc = it.value();
|
|
}
|
|
|
|
BinClassMethodDescription meth;
|
|
meth.name = match.captured(2);
|
|
meth.addr = flagObject[RJsonKey::offset].toVariant().toULongLong();
|
|
classDesc->methods << meth;
|
|
continue;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QList<QString> CutterCore::getAllAnalysisClasses(bool sorted)
|
|
{
|
|
CORE_LOCK();
|
|
QList<QString> ret;
|
|
|
|
SdbListPtr l = makeSdbListPtr(rz_analysis_class_get_all(core->analysis, sorted));
|
|
if (!l) {
|
|
return ret;
|
|
}
|
|
ret.reserve(static_cast<int>(l->length));
|
|
|
|
SdbListIter *it;
|
|
void *entry;
|
|
ls_foreach(l, it, entry)
|
|
{
|
|
auto kv = reinterpret_cast<SdbKv *>(entry);
|
|
ret.append(QString::fromUtf8(reinterpret_cast<const char *>(kv->base.key)));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<AnalysisMethodDescription> CutterCore::getAnalysisClassMethods(const QString &cls)
|
|
{
|
|
CORE_LOCK();
|
|
QList<AnalysisMethodDescription> ret;
|
|
|
|
RzVector *meths = rz_analysis_class_method_get_all(core->analysis, cls.toUtf8().constData());
|
|
if (!meths) {
|
|
return ret;
|
|
}
|
|
|
|
ret.reserve(static_cast<int>(meths->len));
|
|
RzAnalysisMethod *meth;
|
|
CutterRzVectorForeach(meths, meth, RzAnalysisMethod)
|
|
{
|
|
AnalysisMethodDescription desc;
|
|
desc.name = QString::fromUtf8(meth->name);
|
|
desc.realName = QString::fromUtf8(meth->real_name);
|
|
desc.addr = meth->addr;
|
|
desc.vtableOffset = meth->vtable_offset;
|
|
ret.append(desc);
|
|
}
|
|
rz_vector_free(meths);
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<AnalysisBaseClassDescription> CutterCore::getAnalysisClassBaseClasses(const QString &cls)
|
|
{
|
|
CORE_LOCK();
|
|
QList<AnalysisBaseClassDescription> ret;
|
|
|
|
RzVector *bases = rz_analysis_class_base_get_all(core->analysis, cls.toUtf8().constData());
|
|
if (!bases) {
|
|
return ret;
|
|
}
|
|
|
|
ret.reserve(static_cast<int>(bases->len));
|
|
RzAnalysisBaseClass *base;
|
|
CutterRzVectorForeach(bases, base, RzAnalysisBaseClass)
|
|
{
|
|
AnalysisBaseClassDescription desc;
|
|
desc.id = QString::fromUtf8(base->id);
|
|
desc.offset = base->offset;
|
|
desc.className = QString::fromUtf8(base->class_name);
|
|
ret.append(desc);
|
|
}
|
|
rz_vector_free(bases);
|
|
|
|
return ret;
|
|
}
|
|
|
|
QList<AnalysisVTableDescription> CutterCore::getAnalysisClassVTables(const QString &cls)
|
|
{
|
|
CORE_LOCK();
|
|
QList<AnalysisVTableDescription> acVtables;
|
|
|
|
RzVector *vtables = rz_analysis_class_vtable_get_all(core->analysis, cls.toUtf8().constData());
|
|
if (!vtables) {
|
|
return acVtables;
|
|
}
|
|
|
|
acVtables.reserve(static_cast<int>(vtables->len));
|
|
RzAnalysisVTable *vtable;
|
|
CutterRzVectorForeach(vtables, vtable, RzAnalysisVTable)
|
|
{
|
|
AnalysisVTableDescription desc;
|
|
desc.id = QString::fromUtf8(vtable->id);
|
|
desc.offset = vtable->offset;
|
|
desc.addr = vtable->addr;
|
|
acVtables.append(desc);
|
|
}
|
|
rz_vector_free(vtables);
|
|
|
|
return acVtables;
|
|
}
|
|
|
|
void CutterCore::createNewClass(const QString &cls)
|
|
{
|
|
CORE_LOCK();
|
|
rz_analysis_class_create(core->analysis, cls.toUtf8().constData());
|
|
}
|
|
|
|
void CutterCore::renameClass(const QString &oldName, const QString &newName)
|
|
{
|
|
CORE_LOCK();
|
|
rz_analysis_class_rename(core->analysis, oldName.toUtf8().constData(),
|
|
newName.toUtf8().constData());
|
|
}
|
|
|
|
void CutterCore::deleteClass(const QString &cls)
|
|
{
|
|
CORE_LOCK();
|
|
rz_analysis_class_delete(core->analysis, cls.toUtf8().constData());
|
|
}
|
|
|
|
bool CutterCore::getAnalysisMethod(const QString &cls, const QString &meth,
|
|
AnalysisMethodDescription *desc)
|
|
{
|
|
CORE_LOCK();
|
|
RzAnalysisMethod analysisMeth;
|
|
if (rz_analysis_class_method_get(core->analysis, cls.toUtf8().constData(),
|
|
meth.toUtf8().constData(), &analysisMeth)
|
|
!= RZ_ANALYSIS_CLASS_ERR_SUCCESS) {
|
|
return false;
|
|
}
|
|
desc->name = QString::fromUtf8(analysisMeth.name);
|
|
desc->realName = QString::fromUtf8(analysisMeth.real_name);
|
|
desc->addr = analysisMeth.addr;
|
|
desc->vtableOffset = analysisMeth.vtable_offset;
|
|
rz_analysis_class_method_fini(&analysisMeth);
|
|
return true;
|
|
}
|
|
|
|
void CutterCore::setAnalysisMethod(const QString &className, const AnalysisMethodDescription &meth)
|
|
{
|
|
CORE_LOCK();
|
|
RzAnalysisMethod analysisMeth;
|
|
analysisMeth.name = rz_str_new(meth.name.toUtf8().constData());
|
|
analysisMeth.real_name = rz_str_new(meth.realName.toUtf8().constData());
|
|
analysisMeth.addr = meth.addr;
|
|
analysisMeth.vtable_offset = meth.vtableOffset;
|
|
rz_analysis_class_method_set(core->analysis, className.toUtf8().constData(), &analysisMeth);
|
|
rz_analysis_class_method_fini(&analysisMeth);
|
|
}
|
|
|
|
void CutterCore::renameAnalysisMethod(const QString &className, const QString &oldMethodName,
|
|
const QString &newMethodName)
|
|
{
|
|
CORE_LOCK();
|
|
rz_analysis_class_method_rename(core->analysis, className.toUtf8().constData(),
|
|
oldMethodName.toUtf8().constData(),
|
|
newMethodName.toUtf8().constData());
|
|
}
|
|
|
|
QList<ResourcesDescription> CutterCore::getAllResources()
|
|
{
|
|
CORE_LOCK();
|
|
QList<ResourcesDescription> resources;
|
|
|
|
QJsonArray resourcesArray = cmdj("iRj").array();
|
|
for (const QJsonValue &value : resourcesArray) {
|
|
QJsonObject resourceObject = value.toObject();
|
|
|
|
ResourcesDescription res;
|
|
|
|
res.name = resourceObject[RJsonKey::name].toString();
|
|
res.vaddr = resourceObject[RJsonKey::vaddr].toVariant().toULongLong();
|
|
res.index = resourceObject[RJsonKey::index].toVariant().toULongLong();
|
|
res.type = resourceObject[RJsonKey::type].toString();
|
|
res.size = resourceObject[RJsonKey::size].toVariant().toULongLong();
|
|
res.lang = resourceObject[RJsonKey::lang].toString();
|
|
|
|
resources << res;
|
|
}
|
|
return resources;
|
|
}
|
|
|
|
QList<VTableDescription> CutterCore::getAllVTables()
|
|
{
|
|
CORE_LOCK();
|
|
QList<VTableDescription> vtables;
|
|
|
|
QJsonArray vTablesArray = cmdj("avj").array();
|
|
for (const QJsonValue &vTableValue : vTablesArray) {
|
|
QJsonObject vTableObject = vTableValue.toObject();
|
|
|
|
VTableDescription res;
|
|
|
|
res.addr = vTableObject[RJsonKey::offset].toVariant().toULongLong();
|
|
QJsonArray methodArray = vTableObject[RJsonKey::methods].toArray();
|
|
|
|
for (const QJsonValue &methodValue : methodArray) {
|
|
QJsonObject methodObject = methodValue.toObject();
|
|
|
|
BinClassMethodDescription method;
|
|
|
|
method.addr = methodObject[RJsonKey::offset].toVariant().toULongLong();
|
|
method.name = methodObject[RJsonKey::name].toString();
|
|
|
|
res.methods << method;
|
|
}
|
|
|
|
vtables << res;
|
|
}
|
|
return vtables;
|
|
}
|
|
|
|
QList<TypeDescription> CutterCore::getAllTypes()
|
|
{
|
|
QList<TypeDescription> types;
|
|
|
|
types.append(getAllPrimitiveTypes());
|
|
types.append(getAllUnions());
|
|
types.append(getAllStructs());
|
|
types.append(getAllEnums());
|
|
types.append(getAllTypedefs());
|
|
|
|
return types;
|
|
}
|
|
|
|
QList<TypeDescription> CutterCore::getBaseType(RzBaseTypeKind kind, const char *category)
|
|
{
|
|
CORE_LOCK();
|
|
QList<TypeDescription> types;
|
|
|
|
RzList *ts = rz_type_db_get_base_types_of_kind(core->analysis->typedb, kind);
|
|
RzBaseType *type;
|
|
RzListIter *iter;
|
|
|
|
CutterRzListForeach (ts, iter, RzBaseType, type) {
|
|
TypeDescription exp;
|
|
|
|
exp.type = type->name;
|
|
exp.size = rz_type_db_base_get_bitsize(core->analysis->typedb, type);
|
|
exp.format = rz_type_format(core->analysis->typedb, type->name);
|
|
exp.category = tr(category);
|
|
types << exp;
|
|
}
|
|
rz_list_free(ts);
|
|
|
|
return types;
|
|
}
|
|
|
|
QList<TypeDescription> CutterCore::getAllPrimitiveTypes()
|
|
{
|
|
return getBaseType(RZ_BASE_TYPE_KIND_ATOMIC, "Primitive");
|
|
}
|
|
|
|
QList<TypeDescription> CutterCore::getAllUnions()
|
|
{
|
|
return getBaseType(RZ_BASE_TYPE_KIND_UNION, "Union");
|
|
}
|
|
|
|
QList<TypeDescription> CutterCore::getAllStructs()
|
|
{
|
|
return getBaseType(RZ_BASE_TYPE_KIND_STRUCT, "Struct");
|
|
}
|
|
|
|
QList<TypeDescription> CutterCore::getAllEnums()
|
|
{
|
|
return getBaseType(RZ_BASE_TYPE_KIND_ENUM, "Enum");
|
|
}
|
|
|
|
QList<TypeDescription> CutterCore::getAllTypedefs()
|
|
{
|
|
return getBaseType(RZ_BASE_TYPE_KIND_TYPEDEF, "Typedef");
|
|
}
|
|
|
|
QString CutterCore::getTypeAsC(QString name)
|
|
{
|
|
CORE_LOCK();
|
|
QString output = "Failed to fetch the output.";
|
|
if (name.isEmpty()) {
|
|
return output;
|
|
}
|
|
char *earg = rz_cmd_escape_arg(name.toUtf8().constData(), RZ_CMD_ESCAPE_ONE_ARG);
|
|
// TODO: use API for `tc` command once available
|
|
QString result = cmd(QString("tc %1").arg(earg));
|
|
free(earg);
|
|
return result;
|
|
}
|
|
|
|
bool CutterCore::isAddressMapped(RVA addr)
|
|
{
|
|
// If value returned by "om. @ addr" is empty means that address is not mapped
|
|
return !Core()->cmdRawAt(QString("om."), addr).isEmpty();
|
|
}
|
|
|
|
QList<SearchDescription> CutterCore::getAllSearch(QString searchFor, QString space, QString in)
|
|
{
|
|
CORE_LOCK();
|
|
QList<SearchDescription> searchRef;
|
|
|
|
QJsonArray searchArray;
|
|
{
|
|
TempConfig cfg;
|
|
cfg.set("search.in", in);
|
|
searchArray = cmdj(QString("%1 %2").arg(space, searchFor)).array();
|
|
}
|
|
|
|
if (space == "/Rj") {
|
|
for (const QJsonValue &value : searchArray) {
|
|
QJsonObject searchObject = value.toObject();
|
|
|
|
SearchDescription exp;
|
|
|
|
exp.code.clear();
|
|
for (const QJsonValue &value2 : searchObject[RJsonKey::opcodes].toArray()) {
|
|
QJsonObject gadget = value2.toObject();
|
|
exp.code += gadget[RJsonKey::opcode].toString() + "; ";
|
|
}
|
|
|
|
exp.offset = searchObject[RJsonKey::opcodes]
|
|
.toArray()
|
|
.first()
|
|
.toObject()[RJsonKey::offset]
|
|
.toVariant()
|
|
.toULongLong();
|
|
exp.size = searchObject[RJsonKey::size].toVariant().toULongLong();
|
|
|
|
searchRef << exp;
|
|
}
|
|
} else {
|
|
for (const QJsonValue &value : searchArray) {
|
|
QJsonObject searchObject = value.toObject();
|
|
|
|
SearchDescription exp;
|
|
|
|
exp.offset = searchObject[RJsonKey::offset].toVariant().toULongLong();
|
|
exp.size = searchObject[RJsonKey::len].toVariant().toULongLong();
|
|
exp.code = searchObject[RJsonKey::code].toString();
|
|
exp.data = searchObject[RJsonKey::data].toString();
|
|
|
|
searchRef << exp;
|
|
}
|
|
}
|
|
return searchRef;
|
|
}
|
|
|
|
BlockStatistics CutterCore::getBlockStatistics(unsigned int blocksCount)
|
|
{
|
|
BlockStatistics blockStats;
|
|
if (blocksCount == 0) {
|
|
blockStats.from = blockStats.to = blockStats.blocksize = 0;
|
|
return blockStats;
|
|
}
|
|
|
|
QJsonObject statsObj;
|
|
|
|
// User TempConfig here to set the search boundaries to all sections. This makes sure
|
|
// that the Visual Navbar will show all the relevant addresses.
|
|
{
|
|
TempConfig tempConfig;
|
|
tempConfig.set("search.in", "bin.sections");
|
|
statsObj = cmdj("p-j " + QString::number(blocksCount)).object();
|
|
}
|
|
|
|
blockStats.from = statsObj[RJsonKey::from].toVariant().toULongLong();
|
|
blockStats.to = statsObj[RJsonKey::to].toVariant().toULongLong();
|
|
blockStats.blocksize = statsObj[RJsonKey::blocksize].toVariant().toULongLong();
|
|
|
|
QJsonArray blocksArray = statsObj[RJsonKey::blocks].toArray();
|
|
|
|
for (const QJsonValue &value : blocksArray) {
|
|
QJsonObject blockObj = value.toObject();
|
|
|
|
BlockDescription block;
|
|
|
|
block.addr = blockObj[RJsonKey::offset].toVariant().toULongLong();
|
|
block.size = blockObj[RJsonKey::size].toVariant().toULongLong();
|
|
block.flags = blockObj[RJsonKey::flags].toInt(0);
|
|
block.functions = blockObj[RJsonKey::functions].toInt(0);
|
|
block.inFunctions = blockObj[RJsonKey::in_functions].toInt(0);
|
|
block.comments = blockObj[RJsonKey::comments].toInt(0);
|
|
block.symbols = blockObj[RJsonKey::symbols].toInt(0);
|
|
block.strings = blockObj[RJsonKey::strings].toInt(0);
|
|
|
|
block.rwx = 0;
|
|
QString rwxStr = blockObj[RJsonKey::rwx].toString();
|
|
if (rwxStr.length() == 3) {
|
|
if (rwxStr[0] == 'r') {
|
|
block.rwx |= (1 << 0);
|
|
}
|
|
if (rwxStr[1] == 'w') {
|
|
block.rwx |= (1 << 1);
|
|
}
|
|
if (rwxStr[2] == 'x') {
|
|
block.rwx |= (1 << 2);
|
|
}
|
|
}
|
|
|
|
blockStats.blocks << block;
|
|
}
|
|
|
|
return blockStats;
|
|
}
|
|
|
|
QList<XrefDescription> CutterCore::getXRefsForVariable(QString variableName, bool findWrites,
|
|
RVA offset)
|
|
{
|
|
QList<XrefDescription> xrefList = QList<XrefDescription>();
|
|
QJsonArray xrefsArray;
|
|
if (findWrites) {
|
|
xrefsArray = cmdjAt("afvWj", offset).array();
|
|
} else {
|
|
xrefsArray = cmdjAt("afvRj", offset).array();
|
|
}
|
|
for (const QJsonValue &value : xrefsArray) {
|
|
QJsonObject xrefObject = value.toObject();
|
|
QString name = xrefObject[RJsonKey::name].toString();
|
|
if (name == variableName) {
|
|
QJsonArray addressArray = xrefObject[RJsonKey::addrs].toArray();
|
|
for (const QJsonValue &address : addressArray) {
|
|
XrefDescription xref;
|
|
RVA addr = address.toVariant().toULongLong();
|
|
xref.from = addr;
|
|
xref.to = addr;
|
|
if (findWrites) {
|
|
xref.from_str = RzAddressString(addr);
|
|
} else {
|
|
xref.to_str = RzAddressString(addr);
|
|
}
|
|
xrefList << xref;
|
|
}
|
|
}
|
|
}
|
|
return xrefList;
|
|
}
|
|
|
|
QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_function,
|
|
const QString &filterType)
|
|
{
|
|
QList<XrefDescription> xrefList = QList<XrefDescription>();
|
|
|
|
RzList *xrefs = nullptr;
|
|
{
|
|
CORE_LOCK();
|
|
if (to) {
|
|
xrefs = rz_analysis_xrefs_get_to(core->analysis, addr);
|
|
} else {
|
|
xrefs = rz_analysis_xrefs_get_from(core->analysis, addr);
|
|
}
|
|
}
|
|
|
|
RzListIter *it;
|
|
RzAnalysisXRef *xref;
|
|
CutterRzListForeach (xrefs, it, RzAnalysisXRef, xref) {
|
|
XrefDescription xd;
|
|
xd.from = xref->from;
|
|
xd.to = xref->to;
|
|
xd.type = rz_analysis_xrefs_type_tostring(xref->type);
|
|
|
|
if (!filterType.isNull() && filterType != xd.type)
|
|
continue;
|
|
if (!whole_function && !to && xd.from != addr) {
|
|
continue;
|
|
}
|
|
|
|
xd.from_str = RzAddressString(xd.from);
|
|
xd.to_str = Core()->cmdRaw(QString("fd %1").arg(xd.to)).trimmed();
|
|
|
|
xrefList << xd;
|
|
}
|
|
rz_list_free(xrefs);
|
|
return xrefList;
|
|
}
|
|
|
|
void CutterCore::addFlag(RVA offset, QString name, RVA size)
|
|
{
|
|
name = sanitizeStringForCommand(name);
|
|
cmdRawAt(QString("f %1 %2").arg(name).arg(size), offset);
|
|
emit flagsChanged();
|
|
}
|
|
|
|
/**
|
|
* @brief Gets all the flags present at a specific address
|
|
* @param addr The address to be checked
|
|
* @return String containing all the flags which are comma-separated
|
|
*/
|
|
QString CutterCore::listFlagsAsStringAt(RVA addr)
|
|
{
|
|
CORE_LOCK();
|
|
char *flagList = rz_flag_get_liststr(core->flags, addr);
|
|
QString result = fromOwnedCharPtr(flagList);
|
|
return result;
|
|
}
|
|
|
|
QString CutterCore::nearestFlag(RVA offset, RVA *flagOffsetOut)
|
|
{
|
|
auto r = cmdj(QString("fdj @ ") + QString::number(offset)).object();
|
|
QString name = r.value("name").toString();
|
|
if (flagOffsetOut) {
|
|
auto offsetValue = r.value("offset");
|
|
*flagOffsetOut = offsetValue.isUndefined() ? offset : offsetValue.toVariant().toULongLong();
|
|
}
|
|
return name;
|
|
}
|
|
|
|
void CutterCore::handleREvent(int type, void *data)
|
|
{
|
|
switch (type) {
|
|
case RZ_EVENT_CLASS_NEW: {
|
|
auto ev = reinterpret_cast<RzEventClass *>(data);
|
|
emit classNew(QString::fromUtf8(ev->name));
|
|
break;
|
|
}
|
|
case RZ_EVENT_CLASS_DEL: {
|
|
auto ev = reinterpret_cast<RzEventClass *>(data);
|
|
emit classDeleted(QString::fromUtf8(ev->name));
|
|
break;
|
|
}
|
|
case RZ_EVENT_CLASS_RENAME: {
|
|
auto ev = reinterpret_cast<RzEventClassRename *>(data);
|
|
emit classRenamed(QString::fromUtf8(ev->name_old), QString::fromUtf8(ev->name_new));
|
|
break;
|
|
}
|
|
case RZ_EVENT_CLASS_ATTR_SET: {
|
|
auto ev = reinterpret_cast<RzEventClassAttrSet *>(data);
|
|
emit classAttrsChanged(QString::fromUtf8(ev->attr.class_name));
|
|
break;
|
|
}
|
|
case RZ_EVENT_CLASS_ATTR_DEL: {
|
|
auto ev = reinterpret_cast<RzEventClassAttr *>(data);
|
|
emit classAttrsChanged(QString::fromUtf8(ev->class_name));
|
|
break;
|
|
}
|
|
case RZ_EVENT_CLASS_ATTR_RENAME: {
|
|
auto ev = reinterpret_cast<RzEventClassAttrRename *>(data);
|
|
emit classAttrsChanged(QString::fromUtf8(ev->attr.class_name));
|
|
break;
|
|
}
|
|
case RZ_EVENT_DEBUG_PROCESS_FINISHED: {
|
|
auto ev = reinterpret_cast<RzEventDebugProcessFinished *>(data);
|
|
emit debugProcessFinished(ev->pid);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CutterCore::triggerFlagsChanged()
|
|
{
|
|
emit flagsChanged();
|
|
}
|
|
|
|
void CutterCore::triggerVarsChanged()
|
|
{
|
|
emit varsChanged();
|
|
}
|
|
|
|
void CutterCore::triggerFunctionRenamed(const RVA offset, const QString &newName)
|
|
{
|
|
emit functionRenamed(offset, newName);
|
|
}
|
|
|
|
void CutterCore::loadPDB(const QString &file)
|
|
{
|
|
CORE_LOCK();
|
|
rz_core_bin_pdb_load(core, file.toUtf8().constData());
|
|
}
|
|
|
|
QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
|
|
{
|
|
QJsonArray array = cmdj(QString("pdJ ") + QString::number(lines) + QString(" @ ")
|
|
+ QString::number(offset))
|
|
.array();
|
|
QList<DisassemblyLine> r;
|
|
|
|
for (const QJsonValueRef &value : array) {
|
|
QJsonObject object = value.toObject();
|
|
DisassemblyLine line;
|
|
line.offset = object[RJsonKey::offset].toVariant().toULongLong();
|
|
line.text = ansiEscapeToHtml(object[RJsonKey::text].toString());
|
|
const auto &arrow = object[RJsonKey::arrow];
|
|
line.arrow = arrow.isNull() ? RVA_INVALID : arrow.toVariant().toULongLong();
|
|
r << line;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
/**
|
|
* @brief return hexdump of <size> from an <offset> by a given formats
|
|
* @param address - the address from which to print the hexdump
|
|
* @param size - number of bytes to print
|
|
* @param format - the type of hexdump (qwords, words. decimal, etc)
|
|
*/
|
|
QString CutterCore::hexdump(RVA address, int size, HexdumpFormats format)
|
|
{
|
|
QString command = "px";
|
|
switch (format) {
|
|
case HexdumpFormats::Normal:
|
|
break;
|
|
case HexdumpFormats::Half:
|
|
command += "h";
|
|
break;
|
|
case HexdumpFormats::Word:
|
|
command += "w";
|
|
break;
|
|
case HexdumpFormats::Quad:
|
|
command += "q";
|
|
break;
|
|
case HexdumpFormats::Signed:
|
|
command += "d";
|
|
break;
|
|
case HexdumpFormats::Octal:
|
|
command += "o";
|
|
break;
|
|
}
|
|
|
|
return cmdRawAt(QString("%1 %2").arg(command).arg(size), address);
|
|
}
|
|
|
|
QByteArray CutterCore::hexStringToBytes(const QString &hex)
|
|
{
|
|
QByteArray hexChars = hex.toUtf8();
|
|
QByteArray bytes;
|
|
bytes.reserve(hexChars.length() / 2);
|
|
int size = rz_hex_str2bin(hexChars.constData(), reinterpret_cast<ut8 *>(bytes.data()));
|
|
bytes.resize(size);
|
|
return bytes;
|
|
}
|
|
|
|
QString CutterCore::bytesToHexString(const QByteArray &bytes)
|
|
{
|
|
QByteArray hex;
|
|
hex.resize(bytes.length() * 2);
|
|
rz_hex_bin2str(reinterpret_cast<const ut8 *>(bytes.constData()), bytes.size(), hex.data());
|
|
return QString::fromUtf8(hex);
|
|
}
|
|
|
|
void CutterCore::loadScript(const QString &scriptname)
|
|
{
|
|
{
|
|
CORE_LOCK();
|
|
rz_core_cmd_file(core, scriptname.toUtf8().constData());
|
|
}
|
|
triggerRefreshAll();
|
|
}
|
|
|
|
QString CutterCore::getRizinVersionReadable()
|
|
{
|
|
return QString("%1 (%2)").arg(QString::fromUtf8(RZ_VERSION),
|
|
QString::fromUtf8(RZ_GITTIP).left(7));
|
|
}
|
|
|
|
QString CutterCore::getVersionInformation()
|
|
{
|
|
int i;
|
|
QString versionInfo;
|
|
struct vcs_t
|
|
{
|
|
const char *name;
|
|
const char *(*callback)();
|
|
} vcs[] = {
|
|
{ "rz_analysis", &rz_analysis_version },
|
|
{ "rz_lib", &rz_lib_version },
|
|
{ "rz_egg", &rz_egg_version },
|
|
{ "rz_asm", &rz_asm_version },
|
|
{ "rz_bin", &rz_bin_version },
|
|
{ "rz_cons", &rz_cons_version },
|
|
{ "rz_flag", &rz_flag_version },
|
|
{ "rz_core", &rz_core_version },
|
|
{ "rz_crypto", &rz_crypto_version },
|
|
{ "rz_bp", &rz_bp_version },
|
|
{ "rz_debug", &rz_debug_version },
|
|
{ "rz_msg_digest", &rz_msg_digest_version },
|
|
{ "rz_io", &rz_io_version },
|
|
#if !USE_LIB_MAGIC
|
|
{ "rz_magic", &rz_magic_version },
|
|
#endif
|
|
{ "rz_parse", &rz_parse_version },
|
|
{ "rz_reg", &rz_reg_version },
|
|
{ "rz_sign", &rz_sign_version },
|
|
{ "rz_search", &rz_search_version },
|
|
{ "rz_syscall", &rz_syscall_version },
|
|
{ "rz_util", &rz_util_version },
|
|
/* ... */
|
|
{ NULL, NULL }
|
|
};
|
|
versionInfo.append(QString("%1 rz\n").arg(getRizinVersionReadable()));
|
|
for (i = 0; vcs[i].name; i++) {
|
|
struct vcs_t *v = &vcs[i];
|
|
const char *name = v->callback();
|
|
versionInfo.append(QString("%1 %2\n").arg(name, v->name));
|
|
}
|
|
return versionInfo;
|
|
}
|
|
|
|
QList<QString> CutterCore::getColorThemes()
|
|
{
|
|
QList<QString> r;
|
|
QJsonDocument themes = cmdj("ecoj");
|
|
for (const QJsonValue &s : themes.array()) {
|
|
r << s.toString();
|
|
}
|
|
return r;
|
|
}
|
|
|
|
QString CutterCore::ansiEscapeToHtml(const QString &text)
|
|
{
|
|
int len;
|
|
char *html = rz_cons_html_filter(text.toUtf8().constData(), &len);
|
|
if (!html) {
|
|
return QString();
|
|
}
|
|
QString r = QString::fromUtf8(html, len);
|
|
rz_mem_free(html);
|
|
return r;
|
|
}
|
|
|
|
BasicBlockHighlighter *CutterCore::getBBHighlighter()
|
|
{
|
|
return bbHighlighter;
|
|
}
|
|
|
|
BasicInstructionHighlighter *CutterCore::getBIHighlighter()
|
|
{
|
|
return &biHighlighter;
|
|
}
|
|
|
|
void CutterCore::setIOCache(bool enabled)
|
|
{
|
|
if (enabled) {
|
|
// disable write mode when cache is enabled
|
|
setWriteMode(false);
|
|
}
|
|
setConfig("io.cache", enabled);
|
|
this->iocache = enabled;
|
|
|
|
emit ioCacheChanged(enabled);
|
|
emit ioModeChanged();
|
|
}
|
|
|
|
bool CutterCore::isIOCacheEnabled() const
|
|
{
|
|
return iocache;
|
|
}
|
|
|
|
void CutterCore::commitWriteCache()
|
|
{
|
|
// Temporarily disable cache mode
|
|
TempConfig tempConfig;
|
|
tempConfig.set("io.cache", false);
|
|
if (!isWriteModeEnabled()) {
|
|
cmdRaw("oo+");
|
|
cmdRaw("wci");
|
|
cmdRaw("oo");
|
|
} else {
|
|
cmdRaw("wci");
|
|
}
|
|
}
|
|
|
|
// Enable or disable write-mode. Avoid unecessary changes if not need.
|
|
void CutterCore::setWriteMode(bool enabled)
|
|
{
|
|
bool writeModeState = isWriteModeEnabled();
|
|
|
|
if (writeModeState == enabled && !this->iocache) {
|
|
// New mode is the same as current and IO Cache is disabled. Do nothing.
|
|
return;
|
|
}
|
|
|
|
// Change from read-only to write-mode
|
|
if (enabled && !writeModeState) {
|
|
cmdRaw("oo+");
|
|
// Change from write-mode to read-only
|
|
} else {
|
|
cmdRaw("oo");
|
|
}
|
|
// Disable cache mode because we specifically set write or
|
|
// read-only modes.
|
|
setIOCache(false);
|
|
writeModeChanged(enabled);
|
|
emit ioModeChanged();
|
|
}
|
|
|
|
bool CutterCore::isWriteModeEnabled()
|
|
{
|
|
using namespace std;
|
|
QJsonArray ans = cmdj("oj").array();
|
|
return find_if(begin(ans), end(ans),
|
|
[](const QJsonValue &v) { return v.toObject().value("raised").toBool(); })
|
|
->toObject()
|
|
.value("writable")
|
|
.toBool();
|
|
}
|
|
|
|
/**
|
|
* @brief get a compact disassembly preview for tooltips
|
|
* @param address - the address from which to print the disassembly
|
|
* @param num_of_lines - number of instructions to print
|
|
*/
|
|
QStringList CutterCore::getDisassemblyPreview(RVA address, int num_of_lines)
|
|
{
|
|
QList<DisassemblyLine> disassemblyLines;
|
|
{
|
|
// temporarily simplify the disasm output to get it colorful and simple to read
|
|
TempConfig tempConfig;
|
|
tempConfig.set("scr.color", COLOR_MODE_16M)
|
|
.set("asm.lines", false)
|
|
.set("asm.var", false)
|
|
.set("asm.comments", false)
|
|
.set("asm.bytes", false)
|
|
.set("asm.lines.fcn", false)
|
|
.set("asm.lines.out", false)
|
|
.set("asm.lines.bb", false)
|
|
.set("asm.bb.line", false);
|
|
|
|
disassemblyLines = disassembleLines(address, num_of_lines + 1);
|
|
}
|
|
QStringList disasmPreview;
|
|
for (const DisassemblyLine &line : disassemblyLines) {
|
|
disasmPreview << line.text;
|
|
if (disasmPreview.length() >= num_of_lines) {
|
|
disasmPreview << "...";
|
|
break;
|
|
}
|
|
}
|
|
if (!disasmPreview.isEmpty()) {
|
|
return disasmPreview;
|
|
} else {
|
|
return QStringList();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief get a compact hexdump preview for tooltips
|
|
* @param address - the address from which to print the hexdump
|
|
* @param size - number of bytes to print
|
|
*/
|
|
QString CutterCore::getHexdumpPreview(RVA address, int size)
|
|
{
|
|
// temporarily simplify the disasm output to get it colorful and simple to read
|
|
TempConfig tempConfig;
|
|
tempConfig.set("scr.color", COLOR_MODE_16M)
|
|
.set("asm.offset", true)
|
|
.set("hex.header", false)
|
|
.set("hex.cols", 16);
|
|
return ansiEscapeToHtml(hexdump(address, size, HexdumpFormats::Normal))
|
|
.replace(QLatin1Char('\n'), "<br>");
|
|
}
|
|
|
|
QByteArray CutterCore::ioRead(RVA addr, int len)
|
|
{
|
|
CORE_LOCK();
|
|
|
|
QByteArray array;
|
|
|
|
if (len <= 0)
|
|
return array;
|
|
|
|
/* Zero-copy */
|
|
array.resize(len);
|
|
if (!rz_io_read_at(core->io, addr, (uint8_t *)array.data(), len)) {
|
|
qWarning() << "Can't read data" << addr << len;
|
|
array.fill(0xff);
|
|
}
|
|
|
|
return array;
|
|
}
|