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