cutter/src/core/Cutter.cpp

3887 lines
104 KiB
C++
Raw Normal View History

#include <QJsonArray>
#include <QJsonObject>
#include <QRegularExpression>
#include <QDir>
#include <QCoreApplication>
#include <QVector>
#include <QStringList>
#include <QStandardPaths>
#include <cassert>
#include <memory>
2018-10-17 07:55:53 +00:00
#include "common/TempConfig.h"
#include "common/BasicInstructionHighlighter.h"
2018-10-17 07:55:53 +00:00
#include "common/Configuration.h"
#include "common/AsyncTask.h"
#include "common/R2Task.h"
#include "common/Json.h"
#include "core/Cutter.h"
#include "Decompiler.h"
2019-03-24 09:39:54 +00:00
#include "r_asm.h"
#include "r_cmd.h"
2017-09-25 12:55:41 +00:00
#include "sdb.h"
2019-02-20 17:52:11 +00:00
Q_GLOBAL_STATIC(CutterCore, uniqueInstance)
#define R_JSON_KEY(name) static const QString name = QStringLiteral(#name)
namespace RJsonKey {
R_JSON_KEY(addr);
R_JSON_KEY(addr_end);
R_JSON_KEY(arrow);
R_JSON_KEY(baddr);
R_JSON_KEY(bind);
R_JSON_KEY(blocks);
R_JSON_KEY(blocksize);
R_JSON_KEY(bytes);
R_JSON_KEY(calltype);
R_JSON_KEY(cc);
R_JSON_KEY(classname);
R_JSON_KEY(code);
R_JSON_KEY(comment);
R_JSON_KEY(comments);
R_JSON_KEY(cost);
R_JSON_KEY(data);
R_JSON_KEY(description);
R_JSON_KEY(ebbs);
R_JSON_KEY(edges);
R_JSON_KEY(enabled);
R_JSON_KEY(entropy);
R_JSON_KEY(fcn_addr);
R_JSON_KEY(fcn_name);
R_JSON_KEY(fields);
R_JSON_KEY(file);
R_JSON_KEY(flags);
R_JSON_KEY(flagname);
R_JSON_KEY(format);
R_JSON_KEY(from);
R_JSON_KEY(functions);
R_JSON_KEY(graph);
R_JSON_KEY(haddr);
R_JSON_KEY(hw);
R_JSON_KEY(in_functions);
R_JSON_KEY(index);
R_JSON_KEY(jump);
R_JSON_KEY(laddr);
R_JSON_KEY(lang);
R_JSON_KEY(len);
R_JSON_KEY(length);
R_JSON_KEY(license);
R_JSON_KEY(methods);
R_JSON_KEY(name);
R_JSON_KEY(realname);
R_JSON_KEY(nargs);
R_JSON_KEY(nbbs);
R_JSON_KEY(nlocals);
R_JSON_KEY(offset);
R_JSON_KEY(opcode);
R_JSON_KEY(opcodes);
R_JSON_KEY(ordinal);
R_JSON_KEY(libname);
R_JSON_KEY(outdegree);
R_JSON_KEY(paddr);
R_JSON_KEY(path);
R_JSON_KEY(perm);
R_JSON_KEY(pid);
R_JSON_KEY(plt);
R_JSON_KEY(prot);
R_JSON_KEY(ref);
R_JSON_KEY(refs);
R_JSON_KEY(reg);
R_JSON_KEY(rwx);
R_JSON_KEY(section);
R_JSON_KEY(sections);
R_JSON_KEY(size);
R_JSON_KEY(stackframe);
R_JSON_KEY(status);
R_JSON_KEY(string);
R_JSON_KEY(strings);
R_JSON_KEY(symbols);
R_JSON_KEY(text);
R_JSON_KEY(to);
R_JSON_KEY(trace);
R_JSON_KEY(type);
R_JSON_KEY(uid);
R_JSON_KEY(vaddr);
R_JSON_KEY(value);
R_JSON_KEY(vsize);
}
#undef R_JSON_KEY
static void updateOwnedCharPtr(char *&variable, const QString &newValue)
{
auto data = newValue.toUtf8();
R_FREE(variable)
variable = strdup(data.data());
}
2019-08-30 15:31:30 +00:00
RCoreLocked::RCoreLocked(CutterCore *core)
: core(core)
{
2019-08-30 15:31:30 +00:00
core->coreMutex.lock();
assert(core->coreLockDepth >= 0);
core->coreLockDepth++;
if (core->coreLockDepth == 1) {
assert(core->coreBed);
r_cons_sleep_end(core->coreBed);
core->coreBed = nullptr;
}
}
RCoreLocked::~RCoreLocked()
{
2019-08-30 15:31:30 +00:00
assert(core->coreLockDepth > 0);
core->coreLockDepth--;
if (core->coreLockDepth == 0) {
core->coreBed = r_cons_sleep_begin();
}
core->coreMutex.unlock();
}
2017-04-09 19:55:06 +00:00
RCoreLocked::operator RCore *() const
{
2019-08-30 15:31:30 +00:00
return core->core_;
}
2017-04-09 19:55:06 +00:00
RCore *RCoreLocked::operator->() const
{
2019-08-30 15:31:30 +00:00
return core->core_;
}
2019-08-30 15:31:30 +00:00
#define CORE_LOCK() RCoreLocked core(this)
static void cutterREventCallback(REvent *, int type, void *user, void *data)
{
auto core = reinterpret_cast<CutterCore *>(user);
core->handleREvent(type, data);
}
2017-09-25 12:55:41 +00:00
CutterCore::CutterCore(QObject *parent) :
2019-08-30 15:31:30 +00:00
QObject(parent), coreMutex(QMutex::Recursive)
2019-02-20 17:52:11 +00:00
{
}
CutterCore *CutterCore::instance()
{
return uniqueInstance;
}
void CutterCore::initialize()
{
2017-04-09 19:55:06 +00:00
r_cons_new(); // initialize console
core_ = r_core_new();
r_core_task_sync_begin(&core_->tasks);
2019-08-30 15:31:30 +00:00
coreBed = r_cons_sleep_begin();
CORE_LOCK();
r_event_hook(core_->anal->ev, R_EVENT_ALL, cutterREventCallback, this);
#if defined(APPIMAGE) || defined(MACOS_R2_BUNDLED)
auto prefix = QDir(QCoreApplication::applicationDirPath());
2018-09-05 05:46:40 +00:00
#ifdef APPIMAGE
2018-09-30 20:00:53 +00:00
// Executable is in appdir/bin
prefix.cdUp();
qInfo() << "Setting r2 prefix =" << prefix.absolutePath() << " for AppImage.";
2018-09-05 05:46:40 +00:00
#else // MACOS_R2_BUNDLED
2018-09-30 20:00:53 +00:00
// Executable is in Contents/MacOS, prefix is Contents/Resources/r2
prefix.cdUp();
prefix.cd("Resources");
prefix.cd("r2");
qInfo() << "Setting r2 prefix =" << prefix.absolutePath() << " for macOS Application Bundle.";
2018-09-05 05:46:40 +00:00
#endif
setConfig("dir.prefix", prefix.absolutePath());
2019-08-30 11:41:14 +00:00
auto pluginsDir = prefix;
if (pluginsDir.cd("share/radare2/plugins")) {
qInfo() << "Setting r2 plugins dir =" << pluginsDir.absolutePath();
setConfig("dir.plugins", pluginsDir.absolutePath());
} else {
qInfo() << "r2 plugins dir =" << pluginsDir.absolutePath() << "does not exist!";
}
#endif
2018-05-26 16:21:23 +00:00
2018-07-14 19:04:13 +00:00
r_core_loadlibs(this->core_, R_CORE_LOADLIBS_ALL, NULL);
// IMPLICIT r_bin_iobind (core_->bin, core_->io);
// Otherwise r2 may ask the user for input and Cutter would freeze
setConfig("scr.interactive", false);
2019-02-19 18:56:59 +00:00
// Initialize graph node highlighter
bbHighlighter = new BasicBlockHighlighter();
// Initialize Async tasks manager
2018-05-26 16:21:23 +00:00
asyncTaskManager = new AsyncTaskManager(this);
}
2019-08-30 15:31:30 +00:00
CutterCore::~CutterCore()
{
delete bbHighlighter;
r_cons_sleep_end(coreBed);
r_core_task_sync_end(&core_->tasks);
2019-08-30 15:31:30 +00:00
r_core_free(this->core_);
r_cons_free();
}
RCoreLocked CutterCore::core()
{
return RCoreLocked(this);
}
QVector<QDir> CutterCore::getCutterRCDirectories() const
{
QVector<QDir> result;
result.push_back(QDir::home());
QStringList locations = QStandardPaths::standardLocations(QStandardPaths::AppConfigLocation);
for (auto &location : locations) {
result.push_back(QDir(location));
}
return result;
}
2019-07-11 10:32:56 +00:00
void CutterCore::loadCutterRC()
{
2019-08-30 15:31:30 +00:00
CORE_LOCK();
const auto result = getCutterRCDirectories();
for(auto &dir : result){
if(!dir.exists())continue;
auto cutterRCFileInfo = QFileInfo(dir, ".cutterrc");
auto path = cutterRCFileInfo.absoluteFilePath();
if (!cutterRCFileInfo.isFile()) {
continue;
}
qInfo() << "Loading initialization file from" << path;
r_core_cmd_file(core, path.toUtf8().constData());
2019-07-11 10:32:56 +00:00
}
}
2017-09-25 12:55:41 +00:00
QList<QString> CutterCore::sdbList(QString path)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
QList<QString> list = QList<QString>();
2019-08-30 15:31:30 +00:00
Sdb *root = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);
2018-03-21 20:32:32 +00:00
if (root) {
void *vsi;
ls_iter_t *iter;
2018-03-21 20:32:32 +00:00
ls_foreach(root->ns, iter, vsi) {
2017-04-09 19:55:06 +00:00
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};
}
2017-09-25 12:55:41 +00:00
QList<QString> CutterCore::sdbListKeys(QString path)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
QList<QString> list = QList<QString>();
2019-08-30 15:31:30 +00:00
Sdb *root = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);
2018-03-21 20:32:32 +00:00
if (root) {
void *vsi;
ls_iter_t *iter;
SdbListPtr l = makeSdbListPtr(sdb_foreach_list(root, false));
2018-03-21 20:32:32 +00:00
ls_foreach(l, iter, vsi) {
2017-04-09 19:55:06 +00:00
SdbKv *nsi = (SdbKv *)vsi;
list << reinterpret_cast<char *>(nsi->base.key);
}
}
return list;
}
2017-09-25 12:55:41 +00:00
QString CutterCore::sdbGet(QString path, QString key)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
Sdb *db = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);
2018-03-21 20:32:32 +00:00
if (db) {
const char *val = sdb_const_get(db, key.toUtf8().constData(), 0);
if (val && *val)
return val;
}
2019-03-23 10:54:34 +00:00
return QString();
}
2017-09-25 12:55:41 +00:00
bool CutterCore::sdbSet(QString path, QString key, QString val)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
Sdb *db = sdb_ns_path(core->sdb, path.toUtf8().constData(), 1);
if (!db) return false;
2017-04-09 19:55:06 +00:00
return sdb_set(db, key.toUtf8().constData(), val.toUtf8().constData(), 0);
}
2017-09-25 12:55:41 +00:00
QString CutterCore::sanitizeStringForCommand(QString s)
{
static const QRegularExpression regexp(";|@");
return s.replace(regexp, QStringLiteral("_"));
}
QString CutterCore::cmd(const char *str)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
RVA offset = core->offset;
char *res = r_core_cmd_str(core, str);
QString o = QString(res ? res : "");
2017-03-30 03:07:34 +00:00
r_mem_free(res);
2019-08-30 15:31:30 +00:00
if (offset != core->offset) {
updateSeek();
2017-10-09 18:08:35 +00:00
}
return o;
}
bool CutterCore::isRedirectableDebugee()
{
if (!currentlyDebugging || currentlyAttachedToPID != -1) {
return false;
}
// We are only able to redirect locally debugged unix processes
QJsonArray openFilesArray = cmdj("oj").array();;
for (QJsonValue value : openFilesArray) {
QJsonObject openFile = value.toObject();
QString URI = openFile["uri"].toString();
if (URI.contains("ptrace") | URI.contains("mach")) {
return true;
}
}
return false;
}
bool CutterCore::isDebugTaskInProgress()
{
if (!debugTask.isNull()) {
return true;
}
return false;
}
2019-12-11 11:26:54 +00:00
bool CutterCore::asyncCmdEsil(const char *command, QSharedPointer<R2Task> &task)
{
asyncCmd(command, task);
if (task.isNull()) {
2019-12-11 11:26:54 +00:00
return false;
}
connect(task.data(), &R2Task::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");
}
});
2019-12-11 11:26:54 +00:00
return true;
}
2019-12-11 11:26:54 +00:00
bool CutterCore::asyncCmd(const char *str, QSharedPointer<R2Task> &task)
{
if (!task.isNull()) {
2019-12-11 11:26:54 +00:00
return false;
}
CORE_LOCK();
RVA offset = core->offset;
task = QSharedPointer<R2Task>(new R2Task(str, true));
connect(task.data(), &R2Task::finished, task.data(), [this, offset, task] () {
CORE_LOCK();
if (offset != core->offset) {
updateSeek();
}
});
2019-12-11 11:26:54 +00:00
return true;
}
QString CutterCore::cmdRawAt(const char *cmd, RVA address)
{
QString res;
RVA oldOffset = getOffset();
seekSilent(address);
2020-03-19 17:29:04 +00:00
res = cmdRaw(cmd);
seekSilent(oldOffset);
return res;
}
2020-03-19 17:29:04 +00:00
QString CutterCore::cmdRaw(const char *cmd)
{
2020-03-19 17:29:04 +00:00
QString res;
CORE_LOCK();
r_cons_push ();
// r_cmd_call does not return the output of the command
2020-03-21 13:25:50 +00:00
r_cmd_call(core->rcmd, cmd);
2020-03-19 17:29:04 +00:00
// we grab the output straight from r_cons
res = r_cons_get_buffer();
// cleaning up
r_cons_pop ();
r_cons_echo (NULL);
return res;
}
QJsonDocument CutterCore::cmdj(const char *str)
{
2019-08-30 15:31:30 +00:00
char *res;
{
CORE_LOCK();
res = r_core_cmd_str(core, str);
}
2018-05-29 16:19:59 +00:00
QJsonDocument doc = parseJson(res, str);
r_mem_free(res);
return doc;
}
2018-05-29 16:19:59 +00:00
QString CutterCore::cmdTask(const QString &str)
{
2018-07-22 13:04:13 +00:00
R2Task task(str);
task.startTask();
task.joinTask();
return task.getResult();
2018-05-29 16:19:59 +00:00
}
QJsonDocument CutterCore::cmdjTask(const QString &str)
{
2018-07-22 13:04:13 +00:00
R2Task task(str);
task.startTask();
task.joinTask();
return parseJson(task.getResultRaw(), str);
2018-05-29 16:19:59 +00:00
}
QJsonDocument CutterCore::parseJson(const char *res, const char *cmd)
2018-05-29 16:19:59 +00:00
{
QByteArray json(res);
if (json.isEmpty()) {
return QJsonDocument();
}
QJsonParseError jsonError;
QJsonDocument doc = QJsonDocument::fromJson(json, &jsonError);
2018-03-21 20:32:32 +00:00
if (jsonError.error != QJsonParseError::NoError) {
if (cmd) {
eprintf("Failed to parse JSON for command \"%s\": %s\n", cmd,
2018-05-29 16:19:59 +00:00
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, RLinePromptType promptType, size_t limit)
{
RLineBuffer 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);
RLineCompletion completion;
r_line_completion_init(&completion, limit);
r_core_autocomplete(core(), &completion, &buf, promptType);
QStringList r;
r.reserve(r_pvector_len(&completion.args));
for (size_t i = 0; i < r_pvector_len(&completion.args); i++) {
r.push_back(QString::fromUtf8(reinterpret_cast<const char *>(r_pvector_at(&completion.args, i))));
}
r_line_completion_fini(&completion);
return r;
}
2018-06-20 09:24:28 +00:00
/**
* @brief CutterCore::loadFile
* Load initial file. TODO Maybe use the "o" commands?
* @param path File path
* @param baddr Base (RBin) address
* @param mapaddr Map address
* @param perms
* @param va
* @param loadbin Load RBin information
* @param forceBinPlugin
* @return
*/
2018-05-05 13:20:14 +00:00
bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int va,
bool loadbin, const QString &forceBinPlugin)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
RCoreFile *f;
2019-08-30 15:31:30 +00:00
r_config_set_i(core->config, "io.va", va);
2019-08-30 15:31:30 +00:00
f = r_core_file_open(core, path.toUtf8().constData(), perms, mapaddr);
2018-03-21 20:32:32 +00:00
if (!f) {
2017-04-09 19:55:06 +00:00
eprintf("r_core_file_open failed\n");
return false;
}
2018-03-21 20:32:32 +00:00
if (!forceBinPlugin.isNull()) {
2019-08-30 15:31:30 +00:00
r_bin_force_plugin(r_core_get_bin(core), forceBinPlugin.toUtf8().constData());
}
2018-05-05 13:20:14 +00:00
if (loadbin && va) {
2019-08-30 15:31:30 +00:00
if (!r_core_bin_load(core, path.toUtf8().constData(), baddr)) {
2018-05-05 13:20:14 +00:00
eprintf("CANNOT GET RBIN INFO\n");
}
#if HAVE_MULTIPLE_RBIN_FILES_INSIDE_SELECT_WHICH_ONE
2018-03-21 20:32:32 +00:00
if (!r_core_file_open(core, path.toUtf8(), R_IO_READ | (rw ? R_IO_WRITE : 0, mapaddr))) {
2017-04-09 19:55:06 +00:00
eprintf("Cannot open file\n");
2018-03-21 20:32:32 +00:00
} else {
// load RBin information
// XXX only for sub-bins
2018-05-05 13:20:14 +00:00
r_core_bin_load(core, path.toUtf8(), baddr);
2019-08-30 15:31:30 +00:00
r_bin_select_idx(core->bin, NULL, idx);
}
#endif
2018-03-21 20:32:32 +00:00
} else {
// Not loading RBin info coz va = false
}
2019-08-30 15:31:30 +00:00
auto iod = core->io ? core->io->desc : NULL;
auto debug = core->file && iod && (core->file->fd == iod->fd) && iod->plugin && \
2018-03-21 20:32:32 +00:00
iod->plugin->isdbg;
2019-08-30 15:31:30 +00:00
if (!debug && r_flag_get (core->flags, "entry0")) {
r_core_cmd0 (core, "s entry0");
}
2018-09-22 16:00:21 +00:00
if (perms & R_PERM_W) {
2019-08-30 15:31:30 +00:00
r_core_cmd0 (core, "omfg+w");
}
ut64 hashLimit = getConfigut64("cfg.hashlimit");
r_bin_file_compute_hashes(core->bin, hashLimit);
2017-04-09 19:55:06 +00:00
fflush(stdout);
return true;
}
2018-06-20 09:24:28 +00:00
bool CutterCore::tryFile(QString path, bool rw)
{
CORE_LOCK();
RCoreFile *cf;
2018-09-22 16:00:21 +00:00
int flags = R_PERM_R;
if (rw) flags = R_PERM_RW;
2019-08-30 15:31:30 +00:00
cf = r_core_file_open(core, path.toUtf8().constData(), flags, 0LL);
2018-06-20 09:24:28 +00:00
if (!cf) {
return false;
}
2019-08-30 15:31:30 +00:00
r_core_file_close (core, cf);
2018-06-20 09:24:28 +00:00
return true;
}
2019-03-06 20:30:39 +00:00
/**
2020-04-07 13:20:52 +00:00
* @brief Maps a file using r2 API
2019-03-06 20:30:39 +00:00
* @param path Path to file
* @param mapaddr Map Address
* @return bool
*/
2020-04-07 13:20:52 +00:00
bool CutterCore::mapFile(QString path, RVA mapaddr)
2018-06-20 09:24:28 +00:00
{
CORE_LOCK();
RVA addr = mapaddr != RVA_INVALID ? mapaddr : 0;
ut64 baddr = Core()->getFileInfo().object()["bin"].toObject()["baddr"].toVariant().toULongLong();
2019-08-30 15:31:30 +00:00
if (r_core_file_open(core, path.toUtf8().constData(), R_PERM_RX, addr)) {
r_core_bin_load(core, path.toUtf8().constData(), baddr);
2018-06-20 09:24:28 +00:00
} else {
return false;
2018-06-20 09:24:28 +00:00
}
return true;
2018-06-20 09:24:28 +00:00
}
void CutterCore::renameFunction(const QString &oldName, const QString &newName)
2017-04-09 19:55:06 +00:00
{
cmdRaw("afn " + newName + " " + oldName);
emit functionRenamed(oldName, newName);
2017-11-27 08:22:52 +00:00
}
void CutterCore::delFunction(RVA addr)
{
2020-03-21 13:25:50 +00:00
cmdRaw("af- " + RAddressString(addr));
emit functionsChanged();
}
2017-11-27 08:22:52 +00:00
void CutterCore::renameFlag(QString old_name, QString new_name)
{
cmdRaw("fr " + old_name + " " + new_name);
emit flagsChanged();
}
void CutterCore::delFlag(RVA addr)
{
2020-03-21 13:25:50 +00:00
cmdRawAt("f-", addr);
emit flagsChanged();
}
2018-03-11 16:40:52 +00:00
void CutterCore::delFlag(const QString &name)
{
cmdRaw("f-" + name);
emit flagsChanged();
}
QString CutterCore::getInstructionBytes(RVA addr)
{
return cmdj("aoj @ " + RAddressString(addr)).array().first().toObject()[RJsonKey::bytes].toString();
}
QString CutterCore::getInstructionOpcode(RVA addr)
{
return cmdj("aoj @ " + RAddressString(addr)).array().first().toObject()[RJsonKey::opcode].toString();
}
void CutterCore::editInstruction(RVA addr, const QString &inst)
{
2020-03-21 13:25:50 +00:00
cmdRawAt(QString("wa %1").arg(inst), addr);
emit instructionChanged(addr);
}
void CutterCore::nopInstruction(RVA addr)
{
2020-03-21 13:25:50 +00:00
cmdRawAt("wao nop", addr);
emit instructionChanged(addr);
}
void CutterCore::jmpReverse(RVA addr)
{
2020-03-21 13:25:50 +00:00
cmdRawAt("wao recj", addr);
emit instructionChanged(addr);
}
void CutterCore::editBytes(RVA addr, const QString &bytes)
{
2020-03-21 13:25:50 +00:00
cmdRawAt(QString("wx %1").arg(bytes), addr);
emit instructionChanged(addr);
}
void CutterCore::editBytesEndian(RVA addr, const QString &bytes)
{
2020-03-21 13:25:50 +00:00
cmdRawAt(QString("wv %1").arg(bytes), addr);
emit stackChanged();
}
2018-08-04 18:05:56 +00:00
void CutterCore::setToCode(RVA addr)
{
2020-03-21 13:25:50 +00:00
cmdRawAt("Cd-", addr);
2018-08-04 18:05:56 +00:00
emit instructionChanged(addr);
}
void CutterCore::setAsString(RVA addr, int size, StringTypeFormats type)
2018-11-16 21:27:07 +00:00
{
if(RVA_INVALID == addr)
{
return;
}
QString command;
switch(type)
{
case StringTypeFormats::None:
{
2020-03-21 13:25:50 +00:00
command = "Cs";
break;
}
case StringTypeFormats::ASCII_LATIN1:
{
2020-03-21 13:25:50 +00:00
command = "Csa";
break;
}
case StringTypeFormats::UTF8:
{
2020-03-21 13:25:50 +00:00
command = "Cs8";
break;
}
default:
return;
}
seekAndShow(addr);
2020-03-21 13:25:50 +00:00
cmdRawAt(QString("%1 %2").arg(command).arg(size), addr);
emit instructionChanged(addr);
}
void CutterCore::removeString(RVA addr)
{
2020-03-21 13:25:50 +00:00
cmdRawAt("Cs-", addr);
2018-11-16 21:27:07 +00:00
emit instructionChanged(addr);
}
QString CutterCore::getString(RVA addr)
{
2020-03-21 13:25:50 +00:00
return cmdRawAt("ps", addr);
}
2018-08-04 18:05:56 +00:00
void CutterCore::setToData(RVA addr, int size, int repeat)
{
if (size <= 0 || repeat <= 0) {
return;
}
2020-03-21 13:25:50 +00:00
cmdRawAt("Cd-", addr);
cmdRawAt(QString("Cd %1 %2").arg(size).arg(repeat), addr);
2018-08-04 18:05:56 +00:00
emit instructionChanged(addr);
}
int CutterCore::sizeofDataMeta(RVA addr)
{
bool ok;
2020-03-21 13:25:50 +00:00
int size = cmdRawAt("Cd.", addr).toInt(&ok);
2018-08-04 18:05:56 +00:00
return (ok ? size : 0);
}
void CutterCore::setComment(RVA addr, const QString &cmt)
{
2020-03-21 13:25:50 +00:00
cmdRawAt(QString("CCu base64:%1").arg(QString(cmt.toLocal8Bit().toBase64())), addr);
emit commentsChanged();
}
void CutterCore::delComment(RVA addr)
2017-04-09 19:55:06 +00:00
{
2020-03-21 13:25:50 +00:00
cmdRawAt("CC-", addr);
emit commentsChanged();
}
/**
* @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)
{
return Core()->cmdRawAt("CC.", addr);
}
void CutterCore::setImmediateBase(const QString &r2BaseName, RVA offset)
{
2018-03-21 20:32:32 +00:00
if (offset == RVA_INVALID) {
offset = getOffset();
}
2020-03-21 13:25:50 +00:00
this->cmdRawAt(QString("ahi %1").arg(r2BaseName), offset);
emit instructionChanged(offset);
}
2018-02-12 09:48:06 +00:00
void CutterCore::setCurrentBits(int bits, RVA offset)
{
2018-03-21 20:32:32 +00:00
if (offset == RVA_INVALID) {
2018-02-12 09:48:06 +00:00
offset = getOffset();
}
2020-03-21 13:25:50 +00:00
this->cmdRawAt(QString("ahb %1").arg(bits), offset);
2018-02-12 09:48:06 +00:00
emit instructionChanged(offset);
}
void CutterCore::applyStructureOffset(const QString &structureOffset, RVA offset)
{
if (offset == RVA_INVALID) {
offset = getOffset();
}
2020-03-21 13:25:50 +00:00
this->cmdRawAt("aht " + structureOffset, offset);
emit instructionChanged(offset);
}
void CutterCore::seekSilent(ut64 offset)
{
CORE_LOCK();
if (offset == RVA_INVALID) {
return;
}
r_core_seek(core, offset, true);
}
2018-01-27 13:11:30 +00:00
void CutterCore::seek(ut64 offset)
2017-04-09 19:55:06 +00:00
{
2017-10-16 19:00:47 +00:00
// Slower than using the API, but the API is not complete
// which means we either have to duplicate code from radare2
// here, or refactor radare2 API.
CORE_LOCK();
2018-03-21 20:32:32 +00:00
if (offset == RVA_INVALID) {
2018-01-27 13:11:30 +00:00
return;
}
2020-03-21 13:25:50 +00:00
// use cmd and not cmdRaw to make sure seekChanged is emitted
2018-01-27 13:11:30 +00:00
cmd(QString("s %1").arg(offset));
// cmd already does emit seekChanged(core_->offset);
}
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)
2017-04-09 19:55:06 +00:00
{
cmdRaw(QString("s %1").arg(thing));
}
2017-10-16 19:00:47 +00:00
void CutterCore::seekPrev()
{
2020-03-21 13:25:50 +00:00
// Use cmd because cmdRaw does not work with seek history
2017-10-16 19:00:47 +00:00
cmd("s-");
}
2017-10-16 19:00:47 +00:00
void CutterCore::seekNext()
{
2020-03-21 13:25:50 +00:00
// Use cmd because cmdRaw does not work with seek history
2017-10-16 19:00:47 +00:00
cmd("s+");
}
2018-09-06 17:32:12 +00:00
void CutterCore::updateSeek()
{
2019-08-30 15:31:30 +00:00
emit seekChanged(getOffset());
2018-09-06 17:32:12 +00:00
}
RVA CutterCore::prevOpAddr(RVA startAddr, int count)
{
CORE_LOCK();
bool ok;
2020-03-21 13:25:50 +00:00
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();
2018-03-21 20:32:32 +00:00
QJsonArray array = Core()->cmdj("pdj " + QString::number(count + 1) + "@" + QString::number(
startAddr)).array();
if (array.isEmpty()) {
return startAddr + 1;
}
QJsonValue instValue = array.last();
2018-03-21 20:32:32 +00:00
if (!instValue.isObject()) {
return startAddr + 1;
}
bool ok;
RVA offset = instValue.toObject()[RJsonKey::offset].toVariant().toULongLong(&ok);
2018-03-21 20:32:32 +00:00
if (!ok) {
return startAddr + 1;
}
return offset;
}
2017-10-16 19:00:47 +00:00
RVA CutterCore::getOffset()
{
return core_->offset;
}
2017-09-25 12:55:41 +00:00
ut64 CutterCore::math(const QString &expr)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
return r_num_math(core ? core->num : NULL, expr.toUtf8().constData());
}
ut64 CutterCore::num(const QString &expr)
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
return r_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 QString &v)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
r_config_set(core->config, k, v.toUtf8().constData());
}
void CutterCore::setConfig(const char *k, int v)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
r_config_set_i(core->config, k, static_cast<ut64>(v));
}
void CutterCore::setConfig(const char *k, bool v)
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
r_config_set_i(core->config, k, v ? 1 : 0);
}
int CutterCore::getConfigi(const char *k)
2017-08-31 17:43:46 +00:00
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
return static_cast<int>(r_config_get_i(core->config, k));
}
ut64 CutterCore::getConfigut64(const char *k)
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
return r_config_get_i(core->config, k);
}
bool CutterCore::getConfigb(const char *k)
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
return r_config_get_i(core->config, k) != 0;
}
QString CutterCore::getConfigDescription(const char *k)
{
CORE_LOCK();
RConfigNode *node = r_config_node_get (core->config, k);
return QString(node->desc);
}
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)
2017-09-02 08:17:48 +00:00
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
return QString(r_config_get(core->config, k));
2017-09-02 08:17:48 +00:00
}
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;
}
}
2018-05-05 13:20:14 +00:00
void CutterCore::setCPU(QString arch, QString cpu, int bits)
2017-04-09 19:55:06 +00:00
{
if (arch != nullptr) {
setConfig("asm.arch", arch);
}
if (cpu != nullptr) {
setConfig("asm.cpu", cpu);
}
setConfig("asm.bits", bits);
}
2018-01-20 10:35:31 +00:00
void CutterCore::setEndianness(bool big)
{
setConfig("cfg.bigendian", big);
}
QByteArray CutterCore::assemble(const QString &code)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
RAsmCode *ac = r_asm_massemble(core->assembler, code.toUtf8().constData());
QByteArray res;
if (ac && ac->bytes) {
res = QByteArray(reinterpret_cast<const char *>(ac->bytes), ac->len);
}
2017-04-09 19:55:06 +00:00
r_asm_code_free(ac);
return res;
}
QString CutterCore::disassemble(const QByteArray &data)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
2019-08-30 15:31:30 +00:00
RAsmCode *ac = r_asm_mdisassemble(core->assembler, reinterpret_cast<const ut8 *>(data.constData()), data.length());
QString code;
if (ac && ac->assembly) {
code = QString::fromUtf8(ac->assembly);
}
2017-04-09 19:55:06 +00:00
r_asm_code_free(ac);
return code;
}
2017-09-25 12:55:41 +00:00
QString CutterCore::disassembleSingleInstruction(RVA addr)
2017-06-07 15:48:36 +00:00
{
2020-03-21 13:25:50 +00:00
return cmdRawAt("pi 1", addr).simplified();
2017-06-07 15:48:36 +00:00
}
RAnalFunction *CutterCore::functionIn(ut64 addr)
2017-04-09 19:55:06 +00:00
{
CORE_LOCK();
RList *fcns = r_anal_get_functions_in (core->anal, addr);
RAnalFunction *fcn = !r_list_empty(fcns) ? reinterpret_cast<RAnalFunction *>(r_list_first(fcns)) : nullptr;
r_list_free(fcns);
return fcn;
}
RAnalFunction *CutterCore::functionAt(ut64 addr)
{
CORE_LOCK();
return r_anal_get_function_at(core->anal, 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();
RAnalFunction *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();
RAnalFunction *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();
RAnalFunction *fcn = Core()->functionIn(addr);
if (!fcn) {
return RVA_INVALID;
}
RAnalBlock *lastBB = (RAnalBlock *)r_list_last(fcn->bbs);
return lastBB ? lastBB->addr + r_anal_bb_offset_inst(lastBB, lastBB->ninstr-1) : RVA_INVALID;
}
2017-09-25 12:55:41 +00:00
QString CutterCore::cmdFunctionAt(QString addr)
2017-04-09 19:55:06 +00:00
{
QString ret;
2020-03-21 13:25:50 +00:00
// Use cmd because cmdRaw would not work with grep
ret = cmd(QString("fd @ %1~[0]").arg(addr));
return ret.trimmed();
}
2017-09-25 12:55:41 +00:00
QString CutterCore::cmdFunctionAt(RVA addr)
{
return cmdFunctionAt(QString::number(addr));
}
void CutterCore::cmdEsil(const char *command)
{
2020-03-21 13:25:50 +00:00
// 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)
{
2020-03-21 13:25:50 +00:00
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);
2020-03-21 13:25:50 +00:00
QString ret = cmdRawAt(QString("af %1").arg(name), addr);
emit functionsChanged();
return ret;
}
QJsonDocument CutterCore::getRegistersInfo()
{
return cmdj("aeafj");
}
2017-11-28 11:56:38 +00:00
RVA CutterCore::getOffsetJump(RVA addr)
2017-04-09 19:55:06 +00:00
{
2017-11-28 11:56:38 +00:00
bool ok;
2018-03-21 20:32:32 +00:00
RVA value = cmdj("aoj @" + QString::number(
addr)).array().first().toObject().value(RJsonKey::jump).toVariant().toULongLong(&ok);
2017-11-28 11:56:38 +00:00
2018-03-21 20:32:32 +00:00
if (!ok) {
2017-11-28 11:56:38 +00:00
return RVA_INVALID;
}
return value;
}
QList<Decompiler *> CutterCore::getDecompilers()
2017-04-09 19:55:06 +00:00
{
return decompilers;
}
Decompiler *CutterCore::getDecompilerById(const QString &id)
2018-09-08 07:12:08 +00:00
{
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;
2018-09-08 07:12:08 +00:00
}
QJsonDocument CutterCore::getFileInfo()
2017-04-09 19:55:06 +00:00
{
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, RAddressString(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;
2020-03-21 13:25:50 +00:00
RVA addr = cmdRaw("dr SP").toULongLong(&ret, 16);
if (!ret) {
return stack;
}
int base = core->anal->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->assembler->bits;
QByteArray buf = QByteArray();
ut64 type = r_core_anal_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 & R_ANAL_ADDR_TYPE_HEAP || type & R_ANAL_ADDR_TYPE_STACK)) {
// Attempt to find the address within a map
RDebugMap *map = r_debug_map_get(core->dbg, addr);
if (map && map->name && map->name[0]) {
json["mapname"] = map->name;
}
RBinSection *sect = r_bin_get_section_at(r_bin_cur_object (core->bin), addr, true);
if (sect && sect->name[0]) {
json["section"] = sect->name;
}
}
// Check if the address points to a register
RFlagItem *fi = r_flag_get_i(core->flags, addr);
if (fi) {
RRegItem *r = r_reg_get(core->dbg->reg, fi->name, -1);
if (r) {
json["reg"] = r->name;
}
}
// Attempt to find the address within a function
RAnalFunction *fcn = r_anal_get_fcn_in(core->anal, addr, 0);
if (fcn) {
json["fcn"] = fcn->name;
}
// Update type and permission information
if (type != 0) {
if (type & R_ANAL_ADDR_TYPE_HEAP) {
json["type"] = "heap";
} else if (type & R_ANAL_ADDR_TYPE_STACK) {
json["type"] = "stack";
} else if (type & R_ANAL_ADDR_TYPE_PROGRAM) {
json["type"] = "program";
} else if (type & R_ANAL_ADDR_TYPE_LIBRARY) {
json["type"] = "library";
} else if (type & R_ANAL_ADDR_TYPE_ASCII) {
json["type"] = "ascii";
} else if (type & R_ANAL_ADDR_TYPE_SEQUENCE) {
json["type"] = "sequence";
}
QString perms = "";
if (type & R_ANAL_ADDR_TYPE_READ) {
perms += "r";
}
if (type & R_ANAL_ADDR_TYPE_WRITE) {
perms += "w";
}
if (type & R_ANAL_ADDR_TYPE_EXEC) {
RAsmOp op;
buf.resize(32);
perms += "x";
// Instruction disassembly
r_io_read_at(core->io, addr, (unsigned char*)buf.data(), buf.size());
r_asm_set_pc(core->assembler, addr);
r_asm_disassemble(core->assembler, &op, (unsigned char*)buf.data(), buf.size());
json["asm"] = r_asm_op_get_asm(&op);
}
if (!perms.isEmpty()) {
json["perms"] = perms;
}
}
// Try to telescope further if depth permits it
if ((type & R_ANAL_ADDR_TYPE_READ) && !(type & R_ANAL_ADDR_TYPE_EXEC)) {
buf.resize(64);
ut32 *n32 = (ut32 *)buf.data();
ut64 *n64 = (ut64 *)buf.data();
r_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) {
// 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);
r_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));
}
}
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;
}
QJsonObject CutterCore::getRegisterJson()
{
QJsonArray registerRefArray = cmdj("drrj").array();
QJsonObject registerJson;
for (const QJsonValue &value : registerRefArray) {
QJsonObject regRefObject = value.toObject();
QJsonObject registers;
registers.insert(RJsonKey::value, regRefObject[RJsonKey::value]);
registers.insert(RJsonKey::ref, regRefObject[RJsonKey::ref]);
registerJson.insert(regRefObject[RJsonKey::reg].toString(), registers);
}
return registerJson;
}
QString CutterCore::getRegisterName(QString registerRole)
{
2020-03-21 13:25:50 +00:00
return cmdRaw("drn " + registerRole).trimmed();
}
RVA CutterCore::getProgramCounterValue()
{
bool ok;
if (currentlyDebugging) {
2020-03-21 13:25:50 +00:00
// 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)
{
2020-03-21 13:25:50 +00:00
cmdRaw(QString("dr %1=%2").arg(regName).arg(regValue));
2018-06-12 08:43:14 +00:00
emit registersChanged();
emit refreshCodeViews();
2018-06-12 08:43:14 +00:00
}
void CutterCore::setCurrentDebugThread(int tid)
{
2019-12-11 11:26:54 +00:00
if (!asyncCmd("dpt=" + QString::number(tid), debugTask)) {
return;
}
2019-12-11 11:26:54 +00:00
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
emit registersChanged();
emit refreshCodeViews();
emit stackChanged();
syncAndSeekProgramCounter();
emit switchedThread();
emit debugTaskStateChanged();
});
debugTask->startTask();
}
void CutterCore::setCurrentDebugProcess(int pid)
{
2019-12-11 11:26:54 +00:00
if (!currentlyDebugging || !asyncCmd("dp=" + QString::number(pid), debugTask)) {
return;
}
emit debugTaskStateChanged();
2019-12-11 11:26:54 +00:00
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
emit registersChanged();
emit refreshCodeViews();
emit stackChanged();
emit flagsChanged();
syncAndSeekProgramCounter();
emit switchedProcess();
emit debugTaskStateChanged();
2019-12-11 11:26:54 +00:00
});
debugTask->startTask();
}
2018-06-12 08:43:14 +00:00
void CutterCore::startDebug()
{
2018-06-15 17:33:17 +00:00
if (!currentlyDebugging) {
offsetPriorDebugging = getOffset();
}
currentlyOpenFile = getConfig("file.path");
2019-12-11 11:26:54 +00:00
if (!asyncCmd("ood", debugTask)) {
return;
}
2019-12-11 11:26:54 +00:00
emit debugTaskStateChanged();
2019-12-11 11:26:54 +00:00
connect(debugTask.data(), &R2Task::finished, this, [this] () {
if (debugTaskDialog) {
delete debugTaskDialog;
}
debugTask.clear();
emit registersChanged();
if (!currentlyDebugging) {
setConfig("asm.flags", false);
currentlyDebugging = true;
emit toggleDebugView();
2019-12-11 11:26:54 +00:00
emit refreshCodeViews();
}
emit codeRebased();
2019-12-11 11:26:54 +00:00
emit stackChanged();
emit debugTaskStateChanged();
});
debugTaskDialog = new R2TaskDialog(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();
}
2019-12-11 11:26:54 +00:00
// clear registers, init esil state, stack, progcounter at current seek
2019-12-11 11:26:54 +00:00
asyncCmd("aei; aeim; aeip", debugTask);
emit debugTaskStateChanged();
2019-12-11 11:26:54 +00:00
connect(debugTask.data(), &R2Task::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();
2019-12-11 11:26:54 +00:00
}
2019-12-11 11:26:54 +00:00
emit registersChanged();
emit stackChanged();
emit codeRebased();
2019-12-11 11:26:54 +00:00
emit refreshCodeViews();
emit debugTaskStateChanged();
});
debugTaskDialog = new R2TaskDialog(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(), &R2Task::finished, this, [this, uri] () {
if (debugTaskDialog) {
delete debugTaskDialog;
}
debugTask.clear();
// Check if we actually connected
bool connected = false;
QJsonArray openFilesArray = getOpenedFiles();
for (QJsonValue value : openFilesArray) {
QJsonObject openFile = value.toObject();
QString fileUri= openFile["uri"].toString();
if (!fileUri.compare(uri)) {
connected = true;
}
}
2020-03-21 13:25:50 +00:00
// Use cmd because cmdRaw would not with inner command backticked
QString programCounterValue = cmd("dr?`drn PC`").trimmed();
seekAndShow(programCounterValue);
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();
}
emit codeRebased();
emit attachedRemote(true);
emit debugTaskStateChanged();
});
debugTaskDialog = new R2TaskDialog(debugTask);
debugTaskDialog->setBreakOnClose(true);
debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
2019-12-11 11:26:54 +00:00
debugTaskDialog->setDesc(tr("Connecting to: ") + uri);
debugTaskDialog->show();
2019-12-11 11:26:54 +00:00
debugTask->startTask();
}
void CutterCore::attachDebug(int pid)
{
if (!currentlyDebugging) {
offsetPriorDebugging = getOffset();
}
// attach to process with dbg plugin
asyncCmd("e cfg.debug = true; oodf dbg://" + QString::number(pid), debugTask);
emit debugTaskStateChanged();
2019-12-11 11:26:54 +00:00
connect(debugTask.data(), &R2Task::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();
2019-12-11 11:26:54 +00:00
}
emit codeRebased();
emit debugTaskStateChanged();
2019-12-11 11:26:54 +00:00
});
debugTaskDialog = new R2TaskDialog(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();
}
void CutterCore::stopDebug()
{
2019-12-11 11:26:54 +00:00
if (!currentlyDebugging) {
return;
}
if (!debugTask.isNull()) {
suspendDebug();
}
currentlyDebugging = false;
emit debugTaskStateChanged();
if (currentlyEmulating) {
cmdEsil("aeim-; aei-; wcr; .ar-");
currentlyEmulating = false;
} else if (currentlyAttachedToPID != -1) {
2020-03-21 13:25:50 +00:00
// Use cmd because cmdRaw would not work with command concatenation
2019-12-11 11:26:54 +00:00
cmd(QString("dp- %1; o %2; .ar-").arg(
QString::number(currentlyAttachedToPID), currentlyOpenFile));
currentlyAttachedToPID = -1;
} else {
QString ptraceFiles = "";
// close ptrace file descriptors left open
QJsonArray openFilesArray = cmdj("oj").array();;
for (QJsonValue value : openFilesArray) {
QJsonObject openFile = value.toObject();
QString URI = openFile["uri"].toString();
if (URI.contains("ptrace")) {
ptraceFiles += "o-" + QString::number(openFile["fd"].toInt()) + ";";
}
}
2020-03-21 13:25:50 +00:00
// Use cmd because cmdRaw would not work with command concatenation
cmd("doc" + ptraceFiles);
}
2019-12-11 11:26:54 +00:00
syncAndSeekProgramCounter();
setConfig("asm.flags", true);
setConfig("io.cache", false);
emit codeRebased();
emit toggleDebugView();
2019-12-11 11:26:54 +00:00
offsetPriorDebugging = getOffset();
emit debugTaskStateChanged();
2018-06-12 08:43:14 +00:00
}
void CutterCore::syncAndSeekProgramCounter()
{
2020-03-21 13:25:50 +00:00
// Use cmd because cmdRaw would not work with inner command backticked
QString programCounterValue = cmd("dr?`drn PC`").trimmed();
seekAndShow(programCounterValue);
emit registersChanged();
}
2018-06-12 08:43:14 +00:00
void CutterCore::continueDebug()
{
2019-12-11 11:26:54 +00:00
if (!currentlyDebugging) {
return;
}
if (currentlyEmulating) {
if (!asyncCmdEsil("aec", debugTask)) {
return;
}
2019-12-11 11:26:54 +00:00
} else {
if (!asyncCmd("dc", debugTask)) {
return;
2018-08-06 11:48:26 +00:00
}
}
2019-12-11 11:26:54 +00:00
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit registersChanged();
emit refreshCodeViews();
emit debugTaskStateChanged();
});
debugTask->startTask();
2018-06-12 08:43:14 +00:00
}
void CutterCore::continueUntilDebug(QString offset)
{
2019-12-11 11:26:54 +00:00
if (!currentlyDebugging) {
return;
}
2019-12-11 11:26:54 +00:00
if (currentlyEmulating) {
if (!asyncCmdEsil("aecu " + offset, debugTask)) {
return;
}
2019-12-11 11:26:54 +00:00
} else {
if (!asyncCmd("dcu " + offset, debugTask)) {
return;
}
}
2019-12-11 11:26:54 +00:00
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit registersChanged();
emit stackChanged();
emit refreshCodeViews();
emit debugTaskStateChanged();
});
debugTask->startTask();
2018-06-12 08:43:14 +00:00
}
void CutterCore::continueUntilCall()
{
2019-12-11 11:26:54 +00:00
if (!currentlyDebugging) {
return;
}
if (currentlyEmulating) {
if (!asyncCmdEsil("aecc", debugTask)) {
return;
}
2019-12-11 11:26:54 +00:00
} else {
if (!asyncCmd("dcc", debugTask)) {
return;
}
}
2019-12-11 11:26:54 +00:00
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
debugTask->startTask();
}
void CutterCore::continueUntilSyscall()
{
2019-12-11 11:26:54 +00:00
if (!currentlyDebugging) {
return;
}
if (currentlyEmulating) {
if (!asyncCmdEsil("aecs", debugTask)) {
return;
}
2019-12-11 11:26:54 +00:00
} else {
if (!asyncCmd("dcs", debugTask)) {
return;
}
}
2019-12-11 11:26:54 +00:00
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
debugTask->startTask();
}
2018-06-12 08:43:14 +00:00
void CutterCore::stepDebug()
{
2019-12-11 11:26:54 +00:00
if (!currentlyDebugging) {
return;
}
if (currentlyEmulating) {
if (!asyncCmdEsil("aes", debugTask)) {
return;
}
2019-12-11 11:26:54 +00:00
} else {
if (!asyncCmd("ds", debugTask)) {
return;
}
}
2019-12-11 11:26:54 +00:00
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
debugTask->startTask();
2018-06-12 08:43:14 +00:00
}
void CutterCore::stepOverDebug()
{
2019-12-11 11:26:54 +00:00
if (!currentlyDebugging) {
return;
}
if (currentlyEmulating) {
if (!asyncCmdEsil("aeso", debugTask)) {
return;
}
2019-12-11 11:26:54 +00:00
} else {
if (!asyncCmd("dso", debugTask)) {
return;
}
}
2019-12-11 11:26:54 +00:00
emit debugTaskStateChanged();
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
emit debugTaskStateChanged();
});
debugTask->startTask();
2018-06-12 08:43:14 +00:00
}
void CutterCore::stepOutDebug()
{
2019-12-11 11:26:54 +00:00
if (!currentlyDebugging) {
return;
}
2019-12-11 11:26:54 +00:00
emit debugTaskStateChanged();
if (!asyncCmd("dsf", debugTask)) {
return;
}
connect(debugTask.data(), &R2Task::finished, this, [this] () {
debugTask.clear();
syncAndSeekProgramCounter();
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::toggleBreakpoint(RVA addr)
2018-06-12 08:43:14 +00:00
{
2020-03-21 13:25:50 +00:00
cmdRaw(QString("dbs %1").arg(addr));
2018-06-12 08:43:14 +00:00
emit instructionChanged(addr);
emit breakpointsChanged();
}
void CutterCore::toggleBreakpoint(QString addr)
{
cmdRaw("dbs " + addr);
emit instructionChanged(addr.toULongLong());
emit breakpointsChanged();
}
void CutterCore::addBreakpoint(QString addr)
{
cmdRaw("db " + addr);
emit instructionChanged(addr.toULongLong());
emit breakpointsChanged();
}
void CutterCore::addBreakpoint(const BreakpointDescription &config)
{
CORE_LOCK();
RBreakpointItem *breakpoint = nullptr;
int watchpoint_prot = 0;
if (config.hw) {
watchpoint_prot = config.permission & ~(R_BP_PROT_EXEC);
}
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 = r_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 instructionChanged(breakpoint->addr);
emit breakpointsChanged();
}
void CutterCore::updateBreakpoint(int index, const BreakpointDescription &config)
{
CORE_LOCK();
if (auto bp = r_bp_get_index(core->dbg->bp, index)) {
r_bp_del(core->dbg->bp, bp->addr);
}
// Delete by index currently buggy,
// required for breakpoints with non address based position
//r_bp_del_index(core->dbg->bp, index);
addBreakpoint(config);
}
void CutterCore::delBreakpoint(RVA addr)
{
2020-03-21 13:25:50 +00:00
cmdRaw("db- " + RAddressString(addr));
emit instructionChanged(addr);
emit breakpointsChanged();
}
void CutterCore::delAllBreakpoints()
{
2020-03-21 13:25:50 +00:00
cmdRaw("db-*");
emit refreshCodeViews();
}
void CutterCore::enableBreakpoint(RVA addr)
{
2020-03-21 13:25:50 +00:00
cmdRaw("dbe " + RAddressString(addr));
emit instructionChanged(addr);
emit breakpointsChanged();
}
void CutterCore::disableBreakpoint(RVA addr)
{
2020-03-21 13:25:50 +00:00
cmdRaw("dbd " + RAddressString(addr));
emit instructionChanged(addr);
emit breakpointsChanged();
}
void CutterCore::setBreakpointTrace(int index, bool enabled)
{
if (enabled) {
2020-03-21 13:25:50 +00:00
cmdRaw(QString("dbite %1").arg(index));
} else {
2020-03-21 13:25:50 +00:00
cmdRaw(QString("dbitd %1").arg(index));
}
}
static BreakpointDescription breakpointDescriptionFromR2(int index, r_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 r_bp_get_index_at(core->dbg->bp, addr);
}
BreakpointDescription CutterCore::getBreakpointAt(RVA addr)
{
CORE_LOCK();
int index = breakpointIndexAt(addr);
auto bp = r_bp_get_index(core->dbg->bp, index);
if (bp) {
return breakpointDescriptionFromR2(index, bp);
}
return BreakpointDescription();
}
QList<BreakpointDescription> CutterCore::getBreakpoints()
{
CORE_LOCK();
QList<BreakpointDescription> ret;
//TODO: use higher level API, don't touch r2 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(breakpointDescriptionFromR2(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;
}
2018-06-12 08:43:14 +00:00
QList<MemoryMapDescription> CutterCore::getMemoryMap()
{
QList<MemoryMapDescription> ret;
QJsonArray memoryMapArray = cmdj("dmj").array();
for (const QJsonValue &value : memoryMapArray) {
2018-06-12 08:43:14 +00:00
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();
2018-06-12 08:43:14 +00:00
ret << memMap;
}
return ret;
}
2017-09-25 12:55:41 +00:00
QStringList CutterCore::getStats()
2017-04-09 19:55:06 +00:00
{
QStringList stats;
2020-03-21 13:25:50 +00:00
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;
2020-03-21 13:25:50 +00:00
cmdRaw("fs symbols");
stats << cmd("f~?").trimmed();
2020-03-21 13:25:50 +00:00
cmdRaw("fs strings");
stats << cmd("f~?").trimmed();
2020-03-21 13:25:50 +00:00
cmdRaw("fs relocs");
stats << cmd("f~?").trimmed();
2020-03-21 13:25:50 +00:00
cmdRaw("fs sections");
stats << cmd("f~?").trimmed();
2020-03-21 13:25:50 +00:00
cmdRaw("fs *");
stats << cmd("f~?").trimmed();
return stats;
}
void CutterCore::setGraphEmpty(bool empty)
{
emptyGraph = empty;
}
bool CutterCore::isGraphEmpty()
{
return emptyGraph;
}
2017-09-25 12:55:41 +00:00
void CutterCore::getOpcodes()
2017-04-09 19:55:06 +00:00
{
2019-03-23 10:54:34 +00:00
this->opcodes = cmdList("?O");
this->regs = cmdList("drp~[1]");
}
2017-09-25 12:55:41 +00:00
void CutterCore::setSettings()
2017-04-09 19:55:06 +00:00
{
setConfig("scr.interactive", false);
setConfig("hex.pairs", false);
setConfig("asm.xrefs", false);
2018-05-24 06:21:12 +00:00
setConfig("asm.tabs.once", true);
setConfig("asm.flags.middle", 2);
2017-11-28 13:24:35 +00:00
setConfig("anal.hasnext", false);
setConfig("asm.lines.call", false);
2017-08-31 17:43:46 +00:00
setConfig("cfg.fortunes.tts", false);
2017-10-22 10:21:44 +00:00
// Colors
setConfig("scr.color", COLOR_MODE_DISABLED);
2018-03-08 13:02:34 +00:00
// Don't show hits
setConfig("search.flags", false);
}
2017-09-25 12:55:41 +00:00
QList<RVA> CutterCore::getSeekHistory()
{
CORE_LOCK();
QList<RVA> ret;
QJsonArray jsonArray = cmdj("sj").array();
for (const QJsonValue &value : jsonArray)
ret << value.toVariant().toULongLong();
return ret;
}
2017-09-25 12:55:41 +00:00
QStringList CutterCore::getAsmPluginNames()
{
CORE_LOCK();
RListIter *it;
QStringList ret;
RAsmPlugin *ap;
2019-08-30 15:31:30 +00:00
CutterRListForeach(core->assembler->plugins, it, RAsmPlugin, ap) {
ret << ap->name;
}
return ret;
}
2017-09-25 12:55:41 +00:00
QStringList CutterCore::getAnalPluginNames()
{
CORE_LOCK();
RListIter *it;
QStringList ret;
RAnalPlugin *ap;
2019-08-30 15:31:30 +00:00
CutterRListForeach(core->anal->plugins, it, RAnalPlugin, ap) {
ret << ap->name;
}
return ret;
}
2017-09-25 12:55:41 +00:00
QStringList CutterCore::getProjectNames()
{
CORE_LOCK();
QStringList ret;
2018-04-11 09:44:01 +00:00
QJsonArray jsonArray = cmdj("Pj").array();
for (const QJsonValue &value : jsonArray)
ret.append(value.toString());
return ret;
}
QList<RBinPluginDescription> CutterCore::getRBinPluginDescriptions(const QString &type)
{
QList<RBinPluginDescription> ret;
QJsonObject jsonRoot = cmdj("iLj").object();
2018-03-21 20:32:32 +00:00
for (const QString &key : jsonRoot.keys()) {
if (!type.isNull() && key != type)
continue;
QJsonArray pluginArray = jsonRoot[key].toArray();
for (const QJsonValue &pluginValue : pluginArray) {
QJsonObject pluginObject = pluginValue.toObject();
RBinPluginDescription desc;
desc.name = pluginObject[RJsonKey::name].toString();
desc.description = pluginObject[RJsonKey::description].toString();
desc.license = pluginObject[RJsonKey::license].toString();
desc.type = key;
ret.append(desc);
}
}
return ret;
}
QList<RIOPluginDescription> CutterCore::getRIOPluginDescriptions()
{
QList<RIOPluginDescription> ret;
2019-05-17 07:09:10 +00:00
QJsonArray plugins = cmdj("oLj").object()["io_plugins"].toArray();
for (const QJsonValue &pluginValue : plugins) {
QJsonObject pluginObject = pluginValue.toObject();
RIOPluginDescription plugin;
2019-05-17 07:09:10 +00:00
plugin.name = pluginObject["name"].toString();
plugin.description = pluginObject["description"].toString();
plugin.license = pluginObject["license"].toString();
plugin.permissions = pluginObject["permissions"].toString();
for (const auto &uri : pluginObject["uris"].toArray()) {
plugin.uris << uri.toString();
}
ret << plugin;
}
return ret;
}
QList<RCorePluginDescription> CutterCore::getRCorePluginDescriptions()
{
QList<RCorePluginDescription> ret;
QJsonArray plugins = cmdj("Lsj").array();
for (const QJsonValue &pluginValue : plugins) {
QJsonObject pluginObject = pluginValue.toObject();
RCorePluginDescription plugin;
plugin.name = pluginObject["Name"].toString();
plugin.description = pluginObject["Description"].toString();
ret << plugin;
}
return ret;
}
QList<RAsmPluginDescription> CutterCore::getRAsmPluginDescriptions()
{
CORE_LOCK();
RListIter *it;
QList<RAsmPluginDescription> ret;
RAsmPlugin *ap;
2019-08-30 15:31:30 +00:00
CutterRListForeach(core->assembler->plugins, it, RAsmPlugin, ap) {
RAsmPluginDescription 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;
}
2017-09-25 12:55:41 +00:00
QList<FunctionDescription> CutterCore::getAllFunctions()
{
2019-05-01 11:34:15 +00:00
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<FunctionDescription> funcList;
2019-08-30 15:31:30 +00:00
funcList.reserve(r_list_length(core->anal->fcns));
2019-05-01 11:34:15 +00:00
RListIter *iter;
RAnalFunction *fcn;
2019-08-30 15:31:30 +00:00
CutterRListForeach (core->anal->fcns, iter, RAnalFunction, fcn) {
2019-05-01 11:34:15 +00:00
FunctionDescription function;
function.offset = fcn->addr;
function.linearSize = r_anal_function_linear_size(fcn);
2019-08-30 15:31:30 +00:00
function.nargs = r_anal_var_count(core->anal, fcn, 'b', 1) +
r_anal_var_count(core->anal, fcn, 'r', 1) +
r_anal_var_count(core->anal, fcn, 's', 1);
function.nlocals = r_anal_var_count(core->anal, fcn, 'b', 0) +
r_anal_var_count(core->anal, fcn, 'r', 0) +
r_anal_var_count(core->anal, fcn, 's', 0);
2019-05-01 11:34:15 +00:00
function.nbbs = r_list_length (fcn->bbs);
function.calltype = fcn->cc ? QString::fromUtf8(fcn->cc) : QString();
function.name = fcn->name ? QString::fromUtf8(fcn->name) : QString();
function.edges = r_anal_function_count_edges(fcn, nullptr);
2019-05-01 11:34:15 +00:00
function.stackframe = fcn->maxstack;
2019-05-17 11:40:50 +00:00
funcList.append(function);
2019-05-01 11:34:15 +00:00
}
2019-05-17 11:40:50 +00:00
return funcList;
}
2017-09-25 12:55:41 +00:00
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;
}
2017-09-25 12:55:41 +00:00
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;
}
2017-09-25 12:55:41 +00:00
QList<SymbolDescription> CutterCore::getAllSymbols()
{
CORE_LOCK();
RListIter *it;
QList<SymbolDescription> ret;
RBinSymbol *bs;
2019-08-30 15:31:30 +00:00
if (core && core->bin && core->bin->cur && core->bin->cur->o) {
CutterRListForeach(core->bin->cur->o->symbols, it, RBinSymbol, 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;
RBinAddr *entry;
2019-08-30 15:31:30 +00:00
CutterRListForeach(core->bin->cur->o->entries, it, RBinAddr, entry) {
SymbolDescription symbol;
symbol.vaddr = entry->vaddr;
symbol.name = QString("entry") + QString::number(n++);
2019-03-23 10:54:34 +00:00
symbol.bind.clear();
symbol.type = "entry";
ret << symbol;
}
}
return ret;
}
2018-05-21 17:34:41 +00:00
QList<HeaderDescription> CutterCore::getAllHeaders()
{
CORE_LOCK();
QList<HeaderDescription> ret;
QJsonArray headersArray = cmdj("ihj").array();
for (const QJsonValue &value : headersArray) {
2018-05-21 17:34:41 +00:00
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();
2018-05-21 17:34:41 +00:00
ret << header;
}
return ret;
}
2018-05-24 15:37:37 +00:00
QList<ZignatureDescription> CutterCore::getAllZignatures()
{
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<ZignatureDescription> zignatures;
2018-05-24 15:37:37 +00:00
QJsonArray zignaturesArray = cmdj("zj").array();
for (const QJsonValue &value : zignaturesArray) {
2018-05-24 15:37:37 +00:00
QJsonObject zignatureObject = value.toObject();
ZignatureDescription zignature;
zignature.name = zignatureObject[RJsonKey::name].toString();
zignature.bytes = zignatureObject[RJsonKey::bytes].toString();
zignature.offset = zignatureObject[RJsonKey::offset].toVariant().toULongLong();
for (const QJsonValue &ref : zignatureObject[RJsonKey::refs].toArray()) {
2018-05-24 15:37:37 +00:00
zignature.refs << ref.toString();
}
QJsonObject graphObject = zignatureObject[RJsonKey::graph].toObject();
zignature.cc = graphObject[RJsonKey::cc].toVariant().toULongLong();
zignature.nbbs = graphObject[RJsonKey::nbbs].toVariant().toULongLong();
zignature.edges = graphObject[RJsonKey::edges].toVariant().toULongLong();
zignature.ebbs = graphObject[RJsonKey::ebbs].toVariant().toULongLong();
2018-05-24 15:37:37 +00:00
2019-05-17 11:40:50 +00:00
zignatures << zignature;
2018-05-24 15:37:37 +00:00
}
2019-05-17 11:40:50 +00:00
return zignatures;
2018-05-24 15:37:37 +00:00
}
2017-09-25 12:55:41 +00:00
QList<CommentDescription> CutterCore::getAllComments(const QString &filterType)
{
CORE_LOCK();
QList<CommentDescription> ret;
QJsonArray commentsArray = cmdj("CCj").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;
}
2017-09-25 12:55:41 +00:00
QList<RelocDescription> CutterCore::getAllRelocs()
{
CORE_LOCK();
QList<RelocDescription> ret;
2019-08-30 15:31:30 +00:00
if (core && core->bin && core->bin->cur && core->bin->cur->o) {
auto relocs = core->bin->cur->o->relocs;
RBIter iter;
RBinReloc *br;
r_rbtree_foreach (relocs, iter, br, RBinReloc, vrb) {
RelocDescription reloc;
reloc.vaddr = br->vaddr;
reloc.paddr = br->paddr;
reloc.type = (br->additive ? "ADD_" : "SET_") + QString::number(br->type);
if (br->import)
reloc.name = br->import->name;
else
reloc.name = QString("reloc_%1").arg(QString::number(br->vaddr, 16));
ret << reloc;
}
}
return ret;
}
2017-09-25 12:55:41 +00:00
QList<StringDescription> CutterCore::getAllStrings()
{
2018-05-29 16:19:59 +00:00
return parseStringsJson(cmdjTask("izzj"));
}
QList<StringDescription> CutterCore::parseStringsJson(const QJsonDocument &doc)
{
QList<StringDescription> ret;
2018-05-28 15:19:58 +00:00
2018-08-24 16:06:07 +00:00
QJsonArray stringsArray = doc.array();
for (const QJsonValue &value : stringsArray) {
2018-02-10 17:50:00 +00:00
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();
2018-02-10 17:50:00 +00:00
ret << string;
}
return ret;
}
2017-09-25 12:55:41 +00:00
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;
}
2017-09-25 12:55:41 +00:00
QList<FlagDescription> CutterCore::getAllFlags(QString flagspace)
{
CORE_LOCK();
QList<FlagDescription> ret;
if (!flagspace.isEmpty())
2020-03-21 13:25:50 +00:00
cmdRaw("fs " + flagspace);
else
2020-03-21 13:25:50 +00:00
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;
}
2017-09-25 12:55:41 +00:00
QList<SectionDescription> CutterCore::getAllSections()
{
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<SectionDescription> sections;
2018-05-05 10:11:44 +00:00
QJsonDocument sectionsDoc = cmdj("iSj entropy");
QJsonObject sectionsObj = sectionsDoc.object();
QJsonArray sectionsArray = sectionsObj[RJsonKey::sections].toArray();
2018-05-05 10:11:44 +00:00
for (const QJsonValue &value : sectionsArray) {
QJsonObject sectionObject = value.toObject();
QString name = sectionObject[RJsonKey::name].toString();
if (name.isEmpty())
continue;
SectionDescription section;
section.name = name;
section.vaddr = sectionObject[RJsonKey::vaddr].toVariant().toULongLong();
section.vsize = sectionObject[RJsonKey::vsize].toVariant().toULongLong();
section.paddr = sectionObject[RJsonKey::paddr].toVariant().toULongLong();
section.size = sectionObject[RJsonKey::size].toVariant().toULongLong();
section.perm = sectionObject[RJsonKey::perm].toString();
section.entropy = sectionObject[RJsonKey::entropy].toString();
2019-05-17 11:40:50 +00:00
sections << section;
}
2019-05-17 11:40:50 +00:00
return sections;
}
2017-06-07 10:56:55 +00:00
QStringList CutterCore::getSectionList()
{
CORE_LOCK();
QStringList ret;
QJsonArray sectionsArray = cmdj("iSj").array();
for (const QJsonValue &value : sectionsArray) {
ret << value.toObject()[RJsonKey::name].toString();
}
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;
}
2017-09-25 12:55:41 +00:00
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()
2017-12-23 16:42:42 +00:00
{
CORE_LOCK();
QList<BinClassDescription> ret;
2017-12-23 16:42:42 +00:00
QJsonArray classesArray = cmdj("icj").array();
for (const QJsonValue &value : classesArray) {
2017-12-23 16:42:42 +00:00
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();
2017-12-23 16:42:42 +00:00
for (const QJsonValue &value2 : classObject[RJsonKey::methods].toArray()) {
2017-12-23 16:42:42 +00:00
QJsonObject methObject = value2.toObject();
2019-01-31 16:45:58 +00:00
BinClassMethodDescription meth;
meth.name = methObject[RJsonKey::name].toString();
meth.addr = methObject[RJsonKey::addr].toVariant().toULongLong();
2017-12-23 16:42:42 +00:00
cls.methods << meth;
}
for (const QJsonValue &value2 : classObject[RJsonKey::fields].toArray()) {
2017-12-23 16:42:42 +00:00
QJsonObject fieldObject = value2.toObject();
2019-01-31 16:45:58 +00:00
BinClassFieldDescription field;
field.name = fieldObject[RJsonKey::name].toString();
field.addr = fieldObject[RJsonKey::addr].toVariant().toULongLong();
2017-12-23 16:42:42 +00:00
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);
2018-03-21 20:32:32 +00:00
if (match.hasMatch()) {
QString className = match.captured(1);
BinClassDescription *desc = nullptr;
auto it = classesCache.find(className);
2018-03-21 20:32:32 +00:00
if (it == classesCache.end()) {
BinClassDescription cls = {};
ret << cls;
desc = &ret.last();
classesCache[className] = desc;
2018-03-21 20:32:32 +00:00
} 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);
2018-03-21 20:32:32 +00:00
if (match.hasMatch()) {
QString className = match.captured(1);
BinClassDescription *classDesc = nullptr;
auto it = classesCache.find(className);
2018-03-21 20:32:32 +00:00
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;
2018-03-21 20:32:32 +00:00
} else {
classDesc = it.value();
}
2019-01-31 16:45:58 +00:00
BinClassMethodDescription meth;
meth.name = match.captured(2);
meth.addr = flagObject[RJsonKey::offset].toVariant().toULongLong();
classDesc->methods << meth;
continue;
}
}
return ret;
}
QList<QString> CutterCore::getAllAnalClasses(bool sorted)
{
2019-08-30 15:31:30 +00:00
CORE_LOCK();
QList<QString> ret;
2019-08-30 15:31:30 +00:00
SdbListPtr l = makeSdbListPtr(r_anal_class_get_all(core->anal, sorted));
if (!l) {
return ret;
}
2019-01-31 16:45:58 +00:00
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;
}
2019-01-31 16:45:58 +00:00
QList<AnalMethodDescription> CutterCore::getAnalClassMethods(const QString &cls)
{
2019-08-30 15:31:30 +00:00
CORE_LOCK();
2019-01-31 16:45:58 +00:00
QList<AnalMethodDescription> ret;
2019-08-30 15:31:30 +00:00
RVector *meths = r_anal_class_method_get_all(core->anal, cls.toUtf8().constData());
2019-01-31 16:45:58 +00:00
if (!meths) {
return ret;
}
ret.reserve(static_cast<int>(meths->len));
RAnalMethod *meth;
CutterRVectorForeach(meths, meth, RAnalMethod) {
AnalMethodDescription desc;
desc.name = QString::fromUtf8(meth->name);
desc.addr = meth->addr;
desc.vtableOffset = meth->vtable_offset;
2019-02-01 14:51:29 +00:00
ret.append(desc);
2019-01-31 16:45:58 +00:00
}
r_vector_free(meths);
return ret;
}
QList<AnalBaseClassDescription> CutterCore::getAnalClassBaseClasses(const QString &cls)
{
2019-08-30 15:31:30 +00:00
CORE_LOCK();
2019-01-31 16:45:58 +00:00
QList<AnalBaseClassDescription> ret;
2019-08-30 15:31:30 +00:00
RVector *bases = r_anal_class_base_get_all(core->anal, cls.toUtf8().constData());
2019-01-31 16:45:58 +00:00
if (!bases) {
return ret;
}
ret.reserve(static_cast<int>(bases->len));
RAnalBaseClass *base;
CutterRVectorForeach(bases, base, RAnalBaseClass) {
AnalBaseClassDescription desc;
desc.id = QString::fromUtf8(base->id);
desc.offset = base->offset;
desc.className = QString::fromUtf8(base->class_name);
2019-02-01 14:51:29 +00:00
ret.append(desc);
2019-01-31 16:45:58 +00:00
}
r_vector_free(bases);
return ret;
}
QList<AnalVTableDescription> CutterCore::getAnalClassVTables(const QString &cls)
{
2019-08-30 15:31:30 +00:00
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<AnalVTableDescription> acVtables;
2019-01-31 16:45:58 +00:00
2019-08-30 15:31:30 +00:00
RVector *vtables = r_anal_class_vtable_get_all(core->anal, cls.toUtf8().constData());
2019-01-31 16:45:58 +00:00
if (!vtables) {
2019-05-17 11:40:50 +00:00
return acVtables;
2019-01-31 16:45:58 +00:00
}
2019-05-17 11:40:50 +00:00
acVtables.reserve(static_cast<int>(vtables->len));
2019-01-31 16:45:58 +00:00
RAnalVTable *vtable;
CutterRVectorForeach(vtables, vtable, RAnalVTable) {
AnalVTableDescription desc;
desc.id = QString::fromUtf8(vtable->id);
desc.offset = vtable->offset;
desc.addr = vtable->addr;
2019-05-17 11:40:50 +00:00
acVtables.append(desc);
2019-01-31 16:45:58 +00:00
}
r_vector_free(vtables);
2019-05-17 11:40:50 +00:00
return acVtables;
2019-01-31 16:45:58 +00:00
}
void CutterCore::createNewClass(const QString &cls)
{
2019-08-30 15:31:30 +00:00
CORE_LOCK();
r_anal_class_create(core->anal, cls.toUtf8().constData());
}
void CutterCore::renameClass(const QString &oldName, const QString &newName)
{
2019-08-30 15:31:30 +00:00
CORE_LOCK();
r_anal_class_rename(core->anal, oldName.toUtf8().constData(), newName.toUtf8().constData());
}
void CutterCore::deleteClass(const QString &cls)
{
2019-08-30 15:31:30 +00:00
CORE_LOCK();
r_anal_class_delete(core->anal, cls.toUtf8().constData());
}
bool CutterCore::getAnalMethod(const QString &cls, const QString &meth, AnalMethodDescription *desc)
{
2019-08-30 15:31:30 +00:00
CORE_LOCK();
RAnalMethod analMeth;
2019-08-30 15:31:30 +00:00
if (r_anal_class_method_get(core->anal, cls.toUtf8().constData(), meth.toUtf8().constData(), &analMeth) != R_ANAL_CLASS_ERR_SUCCESS) {
return false;
}
desc->name = QString::fromUtf8(analMeth.name);
desc->addr = analMeth.addr;
desc->vtableOffset = analMeth.vtable_offset;
r_anal_class_method_fini(&analMeth);
return true;
}
void CutterCore::setAnalMethod(const QString &className, const AnalMethodDescription &meth)
{
2019-08-30 15:31:30 +00:00
CORE_LOCK();
RAnalMethod analMeth;
analMeth.name = strdup (meth.name.toUtf8().constData());
analMeth.addr = meth.addr;
analMeth.vtable_offset = meth.vtableOffset;
2019-08-30 15:31:30 +00:00
r_anal_class_method_set(core->anal, className.toUtf8().constData(), &analMeth);
r_anal_class_method_fini(&analMeth);
}
void CutterCore::renameAnalMethod(const QString &className, const QString &oldMethodName, const QString &newMethodName)
{
2019-08-30 15:31:30 +00:00
CORE_LOCK();
r_anal_class_method_rename(core->anal, className.toUtf8().constData(), oldMethodName.toUtf8().constData(), newMethodName.toUtf8().constData());
}
2018-02-04 14:32:18 +00:00
QList<ResourcesDescription> CutterCore::getAllResources()
{
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<ResourcesDescription> resources;
2018-02-04 14:32:18 +00:00
QJsonArray resourcesArray = cmdj("iRj").array();
for (const QJsonValue &value : resourcesArray) {
2018-02-04 14:32:18 +00:00
QJsonObject resourceObject = value.toObject();
ResourcesDescription res;
res.name = resourceObject[RJsonKey::name].toInt();
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();
2018-02-04 14:32:18 +00:00
2019-05-17 11:40:50 +00:00
resources << res;
2018-02-04 14:32:18 +00:00
}
2019-05-17 11:40:50 +00:00
return resources;
2018-02-04 14:32:18 +00:00
}
2018-02-26 22:26:18 +00:00
QList<VTableDescription> CutterCore::getAllVTables()
{
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<VTableDescription> vtables;
2018-02-26 22:26:18 +00:00
QJsonArray vTablesArray = cmdj("avj").array();
for (const QJsonValue &vTableValue : vTablesArray) {
2018-02-26 22:26:18 +00:00
QJsonObject vTableObject = vTableValue.toObject();
VTableDescription res;
res.addr = vTableObject[RJsonKey::offset].toVariant().toULongLong();
QJsonArray methodArray = vTableObject[RJsonKey::methods].toArray();
2018-02-26 22:26:18 +00:00
for (const QJsonValue &methodValue : methodArray) {
2018-02-26 22:26:18 +00:00
QJsonObject methodObject = methodValue.toObject();
2019-01-31 16:45:58 +00:00
BinClassMethodDescription method;
method.addr = methodObject[RJsonKey::offset].toVariant().toULongLong();
method.name = methodObject[RJsonKey::name].toString();
2018-02-26 22:26:18 +00:00
res.methods << method;
}
2019-05-17 11:40:50 +00:00
vtables << res;
2018-02-26 22:26:18 +00:00
}
2019-05-17 11:40:50 +00:00
return vtables;
2018-02-26 22:26:18 +00:00
}
2018-03-06 17:21:48 +00:00
QList<TypeDescription> CutterCore::getAllTypes()
{
2019-05-17 11:40:50 +00:00
QList<TypeDescription> types;
2019-05-17 11:40:50 +00:00
types.append(getAllPrimitiveTypes());
types.append(getAllUnions());
types.append(getAllStructs());
types.append(getAllEnums());
types.append(getAllTypedefs());
2019-05-17 11:40:50 +00:00
return types;
}
QList<TypeDescription> CutterCore::getAllPrimitiveTypes()
2018-03-06 17:21:48 +00:00
{
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<TypeDescription> primitiveTypes;
2018-03-06 17:21:48 +00:00
QJsonArray typesArray = cmdj("tj").array();
for (const QJsonValue &value : typesArray) {
2018-03-06 17:21:48 +00:00
QJsonObject typeObject = value.toObject();
TypeDescription exp;
exp.type = typeObject[RJsonKey::type].toString();
exp.size = typeObject[RJsonKey::size].toVariant().toULongLong();
exp.format = typeObject[RJsonKey::format].toString();
exp.category = tr("Primitive");
2019-05-17 11:40:50 +00:00
primitiveTypes << exp;
}
2019-05-17 11:40:50 +00:00
return primitiveTypes;
}
2018-03-06 17:21:48 +00:00
QList<TypeDescription> CutterCore::getAllUnions()
{
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<TypeDescription> unions;
QJsonArray typesArray = cmdj("tuj").array();
for (const QJsonValue value: typesArray) {
QJsonObject typeObject = value.toObject();
TypeDescription exp;
exp.type = typeObject[RJsonKey::type].toString();
exp.size = typeObject[RJsonKey::size].toVariant().toULongLong();
exp.category = "Union";
2019-05-17 11:40:50 +00:00
unions << exp;
}
2019-05-17 11:40:50 +00:00
return unions;
}
QList<TypeDescription> CutterCore::getAllStructs()
{
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<TypeDescription> structs;
QJsonArray typesArray = cmdj("tsj").array();
for (const QJsonValue value: typesArray) {
QJsonObject typeObject = value.toObject();
TypeDescription exp;
exp.type = typeObject[RJsonKey::type].toString();
exp.size = typeObject[RJsonKey::size].toVariant().toULongLong();
exp.category = "Struct";
2019-05-17 11:40:50 +00:00
structs << exp;
}
2019-05-17 11:40:50 +00:00
return structs;
}
QList<TypeDescription> CutterCore::getAllEnums()
{
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<TypeDescription> enums;
QJsonObject typesObject = cmdj("tej").object();
for (QString key: typesObject.keys()) {
TypeDescription exp;
exp.type = key;
exp.size = 0;
exp.category = "Enum";
2019-05-17 11:40:50 +00:00
enums << exp;
}
2019-05-17 11:40:50 +00:00
return enums;
}
QList<TypeDescription> CutterCore::getAllTypedefs()
{
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<TypeDescription> typeDefs;
QJsonObject typesObject = cmdj("ttj").object();
for (QString key: typesObject.keys()) {
TypeDescription exp;
exp.type = key;
exp.size = 0;
exp.category = "Typedef";
2019-05-17 11:40:50 +00:00
typeDefs << exp;
2018-03-06 17:21:48 +00:00
}
2019-05-17 11:40:50 +00:00
return typeDefs;
2018-03-06 17:21:48 +00:00
}
QString CutterCore::addTypes(const char *str)
{
CORE_LOCK();
char *error_msg = nullptr;
2019-08-30 15:31:30 +00:00
char *parsed = r_parse_c_string(core->anal, str, &error_msg);
2019-05-17 11:40:50 +00:00
QString error;
if (!parsed) {
if (error_msg) {
2019-05-17 11:40:50 +00:00
error = error_msg;
r_mem_free(error_msg);
}
2019-05-17 11:40:50 +00:00
return error;
}
2019-08-30 15:31:30 +00:00
r_anal_save_parsed_type(core->anal, parsed);
r_mem_free(parsed);
if (error_msg) {
2019-05-17 11:40:50 +00:00
error = error_msg;
r_mem_free(error_msg);
}
2019-05-17 11:40:50 +00:00
return error;
}
QString CutterCore::getTypeAsC(QString name, QString category)
{
CORE_LOCK();
QString output = "Failed to fetch the output.";
if (name.isEmpty() || category.isEmpty()) {
return output;
}
QString typeName = sanitizeStringForCommand(name);
if (category == "Struct") {
2020-03-21 13:25:50 +00:00
output = cmdRaw(QString("tsc %1").arg(typeName));
} else if (category == "Union") {
2020-03-21 13:25:50 +00:00
output = cmdRaw(QString("tuc %1").arg(typeName));
} else if(category == "Enum") {
2020-03-21 13:25:50 +00:00
output = cmdRaw(QString("tec %1").arg(typeName));
} else if(category == "Typedef") {
2020-03-21 13:25:50 +00:00
output = cmdRaw(QString("ttc %1").arg(typeName));
}
return output;
}
bool CutterCore::isAddressMapped(RVA addr)
{
// If value returned by "om. @ addr" is empty means that address is not mapped
2020-03-21 13:25:50 +00:00
return !Core()->cmdRawAt(QString("om."), addr).isEmpty();
}
QList<SearchDescription> CutterCore::getAllSearch(QString search_for, QString space)
{
CORE_LOCK();
2019-05-17 11:40:50 +00:00
QList<SearchDescription> searchRef;
QJsonArray searchArray = cmdj(space + QString(" ") + search_for).array();
2018-03-21 20:32:32 +00:00
if (space == "/Rj") {
for (const QJsonValue &value : searchArray) {
2018-03-16 09:07:41 +00:00
QJsonObject searchObject = value.toObject();
2018-03-16 09:07:41 +00:00
SearchDescription exp;
2019-03-23 10:54:34 +00:00
exp.code.clear();
for (const QJsonValue &value2 : searchObject[RJsonKey::opcodes].toArray()) {
2018-03-16 09:07:41 +00:00
QJsonObject gadget = value2.toObject();
exp.code += gadget[RJsonKey::opcode].toString() + "; ";
2018-03-16 09:07:41 +00:00
}
2018-03-21 20:32:32 +00:00
exp.offset =
searchObject[RJsonKey::opcodes].toArray().first().toObject()[RJsonKey::offset].toVariant().toULongLong();
exp.size = searchObject[RJsonKey::size].toVariant().toULongLong();
2019-05-17 11:40:50 +00:00
searchRef << exp;
2018-03-16 09:07:41 +00:00
}
2018-03-21 20:32:32 +00:00
} else {
for (const QJsonValue &value : searchArray) {
2018-03-16 09:07:41 +00:00
QJsonObject searchObject = value.toObject();
2018-03-16 09:07:41 +00:00
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();
2018-03-16 09:07:41 +00:00
2019-05-17 11:40:50 +00:00
searchRef << exp;
2018-03-16 09:07:41 +00:00
}
}
2019-05-17 11:40:50 +00:00
return searchRef;
}
2018-06-29 10:34:01 +00:00
BlockStatistics CutterCore::getBlockStatistics(unsigned int blocksCount)
{
2019-05-17 11:40:50 +00:00
BlockStatistics blockStats;
2018-07-06 16:00:26 +00:00
if (blocksCount == 0) {
2019-05-17 11:40:50 +00:00
blockStats.from = blockStats.to = blockStats.blocksize = 0;
return blockStats;
2018-07-06 16:00:26 +00:00
}
2019-05-17 11:40:50 +00:00
2018-06-29 12:35:02 +00:00
QJsonObject statsObj = cmdj("p-j " + QString::number(blocksCount)).object();
2018-06-29 10:34:01 +00:00
2019-05-17 11:40:50 +00:00
blockStats.from = statsObj[RJsonKey::from].toVariant().toULongLong();
blockStats.to = statsObj[RJsonKey::to].toVariant().toULongLong();
blockStats.blocksize = statsObj[RJsonKey::blocksize].toVariant().toULongLong();
2018-06-29 10:34:01 +00:00
QJsonArray blocksArray = statsObj[RJsonKey::blocks].toArray();
2018-07-01 12:29:01 +00:00
2018-06-29 12:35:02 +00:00
for (const QJsonValue &value : blocksArray) {
2018-06-29 10:34:01 +00:00
QJsonObject blockObj = value.toObject();
2018-06-29 10:34:01 +00:00
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);
2018-06-29 10:34:01 +00:00
block.rwx = 0;
QString rwxStr = blockObj[RJsonKey::rwx].toString();
2018-06-29 10:34:01 +00:00
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);
}
}
2019-05-17 11:40:50 +00:00
blockStats.blocks << block;
2018-06-29 10:34:01 +00:00
}
2018-06-29 12:35:02 +00:00
2019-05-17 11:40:50 +00:00
return blockStats;
2018-06-29 10:34:01 +00:00
}
2018-03-21 20:32:32 +00:00
QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_function,
const QString &filterType)
2017-06-07 10:56:55 +00:00
{
2019-05-17 11:40:50 +00:00
QList<XrefDescription> xrefList = QList<XrefDescription>();
2017-06-07 10:56:55 +00:00
QJsonArray xrefsArray;
2018-11-05 21:51:27 +00:00
if (to) {
2017-06-07 10:56:55 +00:00
xrefsArray = cmdj("axtj@" + QString::number(addr)).array();
2018-11-05 21:51:27 +00:00
} else {
2017-06-07 10:56:55 +00:00
xrefsArray = cmdj("axfj@" + QString::number(addr)).array();
2018-11-05 21:51:27 +00:00
}
2017-06-07 10:56:55 +00:00
for (const QJsonValue &value : xrefsArray) {
2017-06-07 10:56:55 +00:00
QJsonObject xrefObject = value.toObject();
XrefDescription xref;
xref.type = xrefObject[RJsonKey::type].toString();
2017-06-07 10:56:55 +00:00
if (!filterType.isNull() && filterType != xref.type)
continue;
xref.from = xrefObject[RJsonKey::from].toVariant().toULongLong();
2018-09-11 09:30:28 +00:00
if (!to) {
xref.from_str = RAddressString(xref.from);
} else {
QString fcn = xrefObject[RJsonKey::fcn_name].toString();
2018-09-11 09:30:28 +00:00
if (!fcn.isEmpty()) {
RVA fcnAddr = xrefObject[RJsonKey::fcn_addr].toVariant().toULongLong();
2018-09-11 09:30:28 +00:00
xref.from_str = fcn + " + 0x" + QString::number(xref.from - fcnAddr, 16);
} else {
xref.from_str = RAddressString(xref.from);
}
}
2017-06-07 10:56:55 +00:00
2018-11-05 21:51:27 +00:00
if (!whole_function && !to && xref.from != addr) {
continue;
2018-11-05 21:51:27 +00:00
}
if (to && !xrefObject.contains(RJsonKey::to)) {
2017-06-07 10:56:55 +00:00
xref.to = addr;
2018-11-05 21:51:27 +00:00
} else {
xref.to = xrefObject[RJsonKey::to].toVariant().toULongLong();
2018-11-05 21:51:27 +00:00
}
2020-03-21 13:25:50 +00:00
xref.to_str = Core()->cmdRaw(QString("fd %1").arg(xref.to)).trimmed();
2017-06-07 10:56:55 +00:00
2019-05-17 11:40:50 +00:00
xrefList << xref;
2017-06-07 10:56:55 +00:00
}
2019-05-17 11:40:50 +00:00
return xrefList;
}
2017-09-25 12:55:41 +00:00
void CutterCore::addFlag(RVA offset, QString name, RVA size)
{
name = sanitizeStringForCommand(name);
2020-03-21 13:25:50 +00:00
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 = r_flag_get_liststr (core->flags, addr);
QString result(flagList);
r_mem_free(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) {
int queryOffset = r.value("offset").toInt(0);
*flagOffsetOut = offset + static_cast<RVA>(-queryOffset);
}
return name;
}
void CutterCore::handleREvent(int type, void *data)
{
switch (type) {
case R_EVENT_CLASS_NEW: {
auto ev = reinterpret_cast<REventClass *>(data);
emit classNew(QString::fromUtf8(ev->name));
break;
}
case R_EVENT_CLASS_DEL: {
auto ev = reinterpret_cast<REventClass *>(data);
emit classDeleted(QString::fromUtf8(ev->name));
break;
}
case R_EVENT_CLASS_RENAME: {
auto ev = reinterpret_cast<REventClassRename *>(data);
emit classRenamed(QString::fromUtf8(ev->name_old), QString::fromUtf8(ev->name_new));
break;
}
case R_EVENT_CLASS_ATTR_SET: {
auto ev = reinterpret_cast<REventClassAttrSet *>(data);
emit classAttrsChanged(QString::fromUtf8(ev->attr.class_name));
break;
}
case R_EVENT_CLASS_ATTR_DEL: {
auto ev = reinterpret_cast<REventClassAttr *>(data);
emit classAttrsChanged(QString::fromUtf8(ev->class_name));
break;
}
case R_EVENT_CLASS_ATTR_RENAME: {
auto ev = reinterpret_cast<REventClassAttrRename *>(data);
emit classAttrsChanged(QString::fromUtf8(ev->attr.class_name));
break;
}
case R_EVENT_DEBUG_PROCESS_FINISHED: {
auto ev = reinterpret_cast<REventDebugProcessFinished*>(data);
emit debugProcessFinished(ev->pid);
break;
}
default:
break;
}
}
void CutterCore::triggerFlagsChanged()
{
emit flagsChanged();
}
void CutterCore::triggerVarsChanged()
{
emit varsChanged();
}
void CutterCore::triggerFunctionRenamed(const QString &prevName, const QString &newName)
{
emit functionRenamed(prevName, newName);
}
void CutterCore::loadPDB(const QString &file)
{
2020-03-21 13:25:50 +00:00
cmdRaw("idp " + sanitizeStringForCommand(file));
}
void CutterCore::openProject(const QString &name)
{
2020-03-21 13:25:50 +00:00
cmdRaw("Po " + name);
2020-03-21 13:25:50 +00:00
QString notes = QString::fromUtf8(QByteArray::fromBase64(cmdRaw("Pnj").toUtf8()));
}
void CutterCore::saveProject(const QString &name)
{
2020-03-21 13:25:50 +00:00
const QString &rv = cmdRaw("Ps " + name.trimmed()).trimmed();
const bool ok = rv == name.trimmed();
2020-03-21 13:25:50 +00:00
cmdRaw(QString("Pnj %1").arg(QString(notes.toUtf8().toBase64())));
emit projectSaved(ok, name);
}
void CutterCore::deleteProject(const QString &name)
{
2020-03-21 13:25:50 +00:00
cmdRaw("Pd " + name);
}
bool CutterCore::isProjectNameValid(const QString &name)
{
// see is_valid_project_name() in libr/core/project.
QString pattern(R"(^[a-zA-Z0-9\\\._:-]{1,}$)");
// The below construct mimics the behaviour of QRegexP::exactMatch(), which was here before
static const QRegularExpression regexp("\\A(?:" + pattern + ")\\z");
return regexp.match(name).hasMatch() && !name.endsWith(".zip") ;
}
QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
{
2018-03-21 20:32:32 +00:00
QJsonArray array = cmdj(QString("pdJ ") + QString::number(lines) + QString(" @ ") + QString::number(
offset)).array();
QList<DisassemblyLine> r;
2019-02-15 12:33:23 +00:00
for (const QJsonValueRef &value : array) {
QJsonObject object = value.toObject();
DisassemblyLine line;
line.offset = object[RJsonKey::offset].toVariant().toULongLong();
2019-02-15 12:33:23 +00:00
line.text = ansiEscapeToHtml(object[RJsonKey::text].toString());
2019-06-29 22:26:48 +00:00
const auto& arrow = object[RJsonKey::arrow];
line.arrow = arrow.isNull()
? RVA_INVALID
: arrow.toVariant().toULongLong();
r << line;
}
return r;
}
2017-12-14 12:45:03 +00:00
/**
* @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;
}
2020-03-21 13:25:50 +00:00
return cmdRawAt(QString("%1 %2")
.arg(command)
2020-03-21 13:25:50 +00:00
.arg(size),
address);
}
QByteArray CutterCore::hexStringToBytes(const QString &hex)
{
QByteArray hexChars = hex.toUtf8();
QByteArray bytes;
bytes.reserve(hexChars.length() / 2);
int size = r_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);
r_hex_bin2str(reinterpret_cast<const ut8 *>(bytes.constData()), bytes.size(), hex.data());
return QString::fromUtf8(hex);
}
2017-12-14 12:45:03 +00:00
void CutterCore::loadScript(const QString &scriptname)
{
2019-08-30 15:31:30 +00:00
{
CORE_LOCK();
r_core_cmd_file(core, scriptname.toUtf8().constData());
}
triggerRefreshAll();
2017-12-14 12:45:03 +00:00
}
2018-07-24 17:50:55 +00:00
2017-12-15 16:09:04 +00:00
QString CutterCore::getVersionInformation()
{
int i;
2019-05-17 11:40:50 +00:00
QString versionInfo;
2017-12-15 16:09:04 +00:00
struct vcs_t {
const char *name;
const char *(*callback)();
} vcs[] = {
{ "r_anal", &r_anal_version },
{ "r_lib", &r_lib_version },
{ "r_egg", &r_egg_version },
{ "r_asm", &r_asm_version },
{ "r_bin", &r_bin_version },
{ "r_cons", &r_cons_version },
{ "r_flag", &r_flag_version },
{ "r_core", &r_core_version },
{ "r_crypto", &r_crypto_version },
{ "r_bp", &r_bp_version },
{ "r_debug", &r_debug_version },
{ "r_hash", &r_hash_version },
{ "r_fs", &r_fs_version },
{ "r_io", &r_io_version },
2019-01-15 18:45:53 +00:00
#if !USE_LIB_MAGIC
2017-12-15 16:09:04 +00:00
{ "r_magic", &r_magic_version },
2019-01-14 20:28:03 +00:00
#endif
2017-12-15 16:09:04 +00:00
{ "r_parse", &r_parse_version },
{ "r_reg", &r_reg_version },
{ "r_sign", &r_sign_version },
{ "r_search", &r_search_version },
{ "r_syscall", &r_syscall_version },
{ "r_util", &r_util_version },
/* ... */
2018-03-21 20:32:32 +00:00
{NULL, NULL}
2017-12-15 16:09:04 +00:00
};
2019-05-17 11:40:50 +00:00
versionInfo.append(QString("%1 r2\n").arg(R2_GITTAP));
2017-12-15 16:09:04 +00:00
for (i = 0; vcs[i].name; i++) {
2018-03-21 20:32:32 +00:00
struct vcs_t *v = &vcs[i];
const char *name = v->callback ();
2019-05-17 11:40:50 +00:00
versionInfo.append(QString("%1 %2\n").arg(name, v->name));
2017-12-15 16:09:04 +00:00
}
2019-05-17 11:40:50 +00:00
return versionInfo;
2017-12-15 16:09:04 +00:00
}
2018-06-25 19:28:34 +00:00
QJsonArray CutterCore::getOpenedFiles()
{
QJsonDocument files = cmdj("oj");
return files.array();
}
2018-05-29 16:19:59 +00:00
QList<QString> CutterCore::getColorThemes()
{
QList<QString> r;
QJsonDocument themes = cmdj("ecoj");
for (const QJsonValue &s : themes.array()) {
r << s.toString();
2018-11-05 21:51:27 +00:00
}
return r;
}
2018-05-29 16:19:59 +00:00
2019-02-15 12:33:23 +00:00
QString CutterCore::ansiEscapeToHtml(const QString &text)
{
int len;
char *html = r_cons_html_filter(text.toUtf8().constData(), &len);
if (!html) {
return QString();
}
QString r = QString::fromUtf8(html, len);
r_mem_free(html);
2019-02-15 12:33:23 +00:00
return r;
}
2019-02-19 18:56:59 +00:00
BasicBlockHighlighter* CutterCore::getBBHighlighter()
{
return bbHighlighter;
}
BasicInstructionHighlighter* CutterCore::getBIHighlighter()
{
return &biHighlighter;
}
void CutterCore::setIOCache(bool enabled)
{
setConfig("io.cache", enabled);
this->iocache = enabled;
emit ioCacheChanged(enabled);
}
bool CutterCore::isIOCacheEnabled() const
{
return iocache;
}
void CutterCore::commitWriteCache()
{
if (!isWriteModeEnabled()) {
setWriteMode (true);
2020-03-21 13:25:50 +00:00
cmdRaw("wci");
cmdRaw("oo");
} else {
2020-03-21 13:25:50 +00:00
cmdRaw("wci");
}
}
// Enable or disable write-mode. Avoid unecessary changes if not need.
void CutterCore::setWriteMode(bool enabled)
{
bool writeModeState = isWriteModeEnabled();
if (writeModeState == enabled) {
// New mode is the same as current. Do nothing.
return;
}
// Change from read-only to write-mode
if (enabled && !writeModeState) {
2020-03-21 13:25:50 +00:00
cmdRaw("oo+");
// Change from write-mode to read-only
} else {
2020-03-21 13:25:50 +00:00
cmdRaw("oo");
}
writeModeChanged (enabled);
}
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>");
}
2019-05-16 16:03:48 +00:00
QByteArray CutterCore::ioRead(RVA addr, int len)
{
CORE_LOCK();
QByteArray array;
if (len <= 0)
return array;
/* Zero-copy */
array.resize(len);
2019-08-30 15:31:30 +00:00
if (!r_io_read_at(core->io, addr, (uint8_t *)array.data(), len)) {
2019-05-16 16:03:48 +00:00
qWarning() << "Can't read data" << addr << len;
array.fill(0xff);
}
return array;
}