1 #include <QJsonArray>
2 #include <QJsonObject>
3 #include <QRegularExpression>
4 #include <QDir>
5 #include <QCoreApplication>
6 #include <QVector>
7 #include <QStringList>
8 #include <QStandardPaths>
9 
10 #include <cassert>
11 #include <memory>
12 
13 #include "common/TempConfig.h"
14 #include "common/BasicInstructionHighlighter.h"
15 #include "common/Configuration.h"
16 #include "common/AsyncTask.h"
17 #include "common/R2Task.h"
18 #include "common/Json.h"
19 #include "core/Cutter.h"
20 #include "Decompiler.h"
21 #include "r_asm.h"
22 #include "r_core.h"
23 #include "r_cmd.h"
24 #include "sdb.h"
25 
26 Q_GLOBAL_STATIC(CutterCore, uniqueInstance)
27 
28 #define R_JSON_KEY(name) static const QString name = QStringLiteral(#name)
29 
30 namespace RJsonKey {
31     R_JSON_KEY(addr);
32     R_JSON_KEY(addrs);
33     R_JSON_KEY(addr_end);
34     R_JSON_KEY(arrow);
35     R_JSON_KEY(baddr);
36     R_JSON_KEY(bind);
37     R_JSON_KEY(blocks);
38     R_JSON_KEY(blocksize);
39     R_JSON_KEY(bytes);
40     R_JSON_KEY(calltype);
41     R_JSON_KEY(cc);
42     R_JSON_KEY(classname);
43     R_JSON_KEY(code);
44     R_JSON_KEY(comment);
45     R_JSON_KEY(comments);
46     R_JSON_KEY(cost);
47     R_JSON_KEY(data);
48     R_JSON_KEY(description);
49     R_JSON_KEY(ebbs);
50     R_JSON_KEY(edges);
51     R_JSON_KEY(enabled);
52     R_JSON_KEY(entropy);
53     R_JSON_KEY(fcn_addr);
54     R_JSON_KEY(fcn_name);
55     R_JSON_KEY(fields);
56     R_JSON_KEY(file);
57     R_JSON_KEY(flags);
58     R_JSON_KEY(flagname);
59     R_JSON_KEY(format);
60     R_JSON_KEY(from);
61     R_JSON_KEY(functions);
62     R_JSON_KEY(graph);
63     R_JSON_KEY(haddr);
64     R_JSON_KEY(hw);
65     R_JSON_KEY(in_functions);
66     R_JSON_KEY(index);
67     R_JSON_KEY(jump);
68     R_JSON_KEY(laddr);
69     R_JSON_KEY(lang);
70     R_JSON_KEY(len);
71     R_JSON_KEY(length);
72     R_JSON_KEY(license);
73     R_JSON_KEY(methods);
74     R_JSON_KEY(name);
75     R_JSON_KEY(realname);
76     R_JSON_KEY(nargs);
77     R_JSON_KEY(nbbs);
78     R_JSON_KEY(nlocals);
79     R_JSON_KEY(offset);
80     R_JSON_KEY(opcode);
81     R_JSON_KEY(opcodes);
82     R_JSON_KEY(ordinal);
83     R_JSON_KEY(libname);
84     R_JSON_KEY(outdegree);
85     R_JSON_KEY(paddr);
86     R_JSON_KEY(path);
87     R_JSON_KEY(perm);
88     R_JSON_KEY(pid);
89     R_JSON_KEY(plt);
90     R_JSON_KEY(prot);
91     R_JSON_KEY(ref);
92     R_JSON_KEY(refs);
93     R_JSON_KEY(reg);
94     R_JSON_KEY(rwx);
95     R_JSON_KEY(section);
96     R_JSON_KEY(sections);
97     R_JSON_KEY(size);
98     R_JSON_KEY(stackframe);
99     R_JSON_KEY(status);
100     R_JSON_KEY(string);
101     R_JSON_KEY(strings);
102     R_JSON_KEY(symbols);
103     R_JSON_KEY(text);
104     R_JSON_KEY(to);
105     R_JSON_KEY(trace);
106     R_JSON_KEY(type);
107     R_JSON_KEY(uid);
108     R_JSON_KEY(vaddr);
109     R_JSON_KEY(value);
110     R_JSON_KEY(vsize);
111 }
112 
113 #undef R_JSON_KEY
114 
updateOwnedCharPtr(char * & variable,const QString & newValue)115 static void updateOwnedCharPtr(char *&variable, const QString &newValue)
116 {
117     auto data = newValue.toUtf8();
118     R_FREE(variable)
119     variable = strdup(data.data());
120 }
121 
fromOwnedCharPtr(char * str)122 static QString fromOwnedCharPtr(char *str) {
123     QString result(str ? str : "");
124     r_mem_free(str);
125     return result;
126 }
127 
RCoreLocked(CutterCore * core)128 RCoreLocked::RCoreLocked(CutterCore *core)
129     : core(core)
130 {
131     core->coreMutex.lock();
132     assert(core->coreLockDepth >= 0);
133     core->coreLockDepth++;
134     if (core->coreLockDepth == 1) {
135         assert(core->coreBed);
136         r_cons_sleep_end(core->coreBed);
137         core->coreBed = nullptr;
138     }
139 }
140 
~RCoreLocked()141 RCoreLocked::~RCoreLocked()
142 {
143     assert(core->coreLockDepth > 0);
144     core->coreLockDepth--;
145     if (core->coreLockDepth == 0) {
146         core->coreBed = r_cons_sleep_begin();
147     }
148     core->coreMutex.unlock();
149 }
150 
operator RCore*() const151 RCoreLocked::operator RCore *() const
152 {
153     return core->core_;
154 }
155 
operator ->() const156 RCore *RCoreLocked::operator->() const
157 {
158     return core->core_;
159 }
160 
161 #define CORE_LOCK() RCoreLocked core(this)
162 
cutterREventCallback(REvent *,int type,void * user,void * data)163 static void cutterREventCallback(REvent *, int type, void *user, void *data)
164 {
165     auto core = reinterpret_cast<CutterCore *>(user);
166     core->handleREvent(type, data);
167 }
168 
CutterCore(QObject * parent)169 CutterCore::CutterCore(QObject *parent):
170     QObject(parent)
171 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
172     , coreMutex(QMutex::Recursive)
173 #endif
174 {
175 }
176 
instance()177 CutterCore *CutterCore::instance()
178 {
179     return uniqueInstance;
180 }
181 
initialize(bool loadPlugins)182 void CutterCore::initialize(bool loadPlugins)
183 {
184     r_cons_new();  // initialize console
185     core_ = r_core_new();
186     r_core_task_sync_begin(&core_->tasks);
187     coreBed = r_cons_sleep_begin();
188     CORE_LOCK();
189 
190     r_event_hook(core_->anal->ev, R_EVENT_ALL, cutterREventCallback, this);
191 #if 0
192 #if defined(APPIMAGE) || defined(MACOS_R2_BUNDLED)
193     auto prefix = QDir(QCoreApplication::applicationDirPath());
194 #ifdef APPIMAGE
195     // Executable is in appdir/bin
196     prefix.cdUp();
197     qInfo() << "Setting r2 prefix =" << prefix.absolutePath() << " for AppImage.";
198 #else // MACOS_R2_BUNDLED
199     // Executable is in Contents/MacOS, prefix is Contents/Resources/r2
200     prefix.cdUp();
201     prefix.cd("Resources");
202     prefix.cd("r2");
203     qInfo() << "Setting r2 prefix =" << prefix.absolutePath() << " for macOS Application Bundle.";
204 #endif
205     setConfig("dir.prefix", prefix.absolutePath());
206 
207     auto pluginsDir = prefix;
208     if (pluginsDir.cd("share/radare2/plugins")) {
209         qInfo() << "Setting r2 plugins dir =" << pluginsDir.absolutePath();
210         setConfig("dir.plugins", pluginsDir.absolutePath());
211     } else {
212         qInfo() << "r2 plugins dir =" << pluginsDir.absolutePath() << "does not exist!";
213     }
214 #endif
215 #endif
216 
217     if (!loadPlugins) {
218         setConfig("cfg.plugins", 0);
219     }
220     if (getConfigi("cfg.plugins")) {
221         r_core_loadlibs(this->core_, R_CORE_LOADLIBS_ALL, nullptr);
222     }
223     // IMPLICIT r_bin_iobind (core_->bin, core_->io);
224 
225     // Otherwise r2 may ask the user for input and Cutter would freeze
226     setConfig("scr.interactive", false);
227 
228     // Initialize graph node highlighter
229     bbHighlighter = new BasicBlockHighlighter();
230 
231     // Initialize Async tasks manager
232     asyncTaskManager = new AsyncTaskManager(this);
233 }
234 
~CutterCore()235 CutterCore::~CutterCore()
236 {
237     delete bbHighlighter;
238     r_cons_sleep_end(coreBed);
239     r_core_task_sync_end(&core_->tasks);
240     r_core_free(this->core_);
241     r_cons_free();
242 }
243 
core()244 RCoreLocked CutterCore::core()
245 {
246     return RCoreLocked(this);
247 }
248 
getCutterRCDefaultDirectory() const249 QDir CutterCore::getCutterRCDefaultDirectory() const
250 {
251     return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
252 }
253 
getCutterRCFilePaths() const254 QVector<QString> CutterCore::getCutterRCFilePaths() const
255 {
256     QVector<QString> result;
257     result.push_back(QFileInfo(QDir::home(), ".cutterrc").absoluteFilePath());
258     QStringList locations = QStandardPaths::standardLocations(QStandardPaths::AppConfigLocation);
259     for (auto &location : locations) {
260         result.push_back(QFileInfo(QDir(location), ".cutterrc").absoluteFilePath());
261     }
262     result.push_back(QFileInfo(getCutterRCDefaultDirectory(), "rc").absoluteFilePath()); // File in config editor is from this path
263     return result;
264 }
265 
loadCutterRC()266 void CutterCore::loadCutterRC()
267 {
268     CORE_LOCK();
269     const auto result = getCutterRCFilePaths();
270     for(auto &cutterRCFilePath : result){
271         auto cutterRCFileInfo = QFileInfo(cutterRCFilePath);
272         if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) {
273             continue;
274         }
275         qInfo() << "Loading initialization file from " << cutterRCFilePath;
276         r_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
277     }
278 }
279 
loadDefaultCutterRC()280 void CutterCore::loadDefaultCutterRC()
281 {
282     CORE_LOCK();
283     auto cutterRCFilePath = QFileInfo(getCutterRCDefaultDirectory(), "rc").absoluteFilePath();
284     const auto cutterRCFileInfo = QFileInfo(cutterRCFilePath);
285     if (!cutterRCFileInfo.exists() || !cutterRCFileInfo.isFile()) {
286         return;
287     }
288     qInfo() << "Loading initialization file from " << cutterRCFilePath;
289     r_core_cmd_file(core, cutterRCFilePath.toUtf8().constData());
290 }
291 
292 
sdbList(QString path)293 QList<QString> CutterCore::sdbList(QString path)
294 {
295     CORE_LOCK();
296     QList<QString> list = QList<QString>();
297     Sdb *root = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);
298     if (root) {
299         void *vsi;
300         ls_iter_t *iter;
301         ls_foreach(root->ns, iter, vsi) {
302             SdbNs *nsi = (SdbNs *)vsi;
303             list << nsi->name;
304         }
305     }
306     return list;
307 }
308 
309 using SdbListPtr = std::unique_ptr<SdbList, decltype (&ls_free)>;
makeSdbListPtr(SdbList * list)310 static SdbListPtr makeSdbListPtr(SdbList *list)
311 {
312     return {list, ls_free};
313 }
314 
sdbListKeys(QString path)315 QList<QString> CutterCore::sdbListKeys(QString path)
316 {
317     CORE_LOCK();
318     QList<QString> list = QList<QString>();
319     Sdb *root = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);
320     if (root) {
321         void *vsi;
322         ls_iter_t *iter;
323         SdbListPtr l = makeSdbListPtr(sdb_foreach_list(root, false));
324         ls_foreach(l, iter, vsi) {
325             SdbKv *nsi = (SdbKv *)vsi;
326             list << reinterpret_cast<char *>(nsi->base.key);
327         }
328     }
329     return list;
330 }
331 
sdbGet(QString path,QString key)332 QString CutterCore::sdbGet(QString path, QString key)
333 {
334     CORE_LOCK();
335     Sdb *db = sdb_ns_path(core->sdb, path.toUtf8().constData(), 0);
336     if (db) {
337         const char *val = sdb_const_get(db, key.toUtf8().constData(), 0);
338         if (val && *val)
339             return val;
340     }
341     return QString();
342 }
343 
sdbSet(QString path,QString key,QString val)344 bool CutterCore::sdbSet(QString path, QString key, QString val)
345 {
346     CORE_LOCK();
347     Sdb *db = sdb_ns_path(core->sdb, path.toUtf8().constData(), 1);
348     if (!db) return false;
349     return sdb_set(db, key.toUtf8().constData(), val.toUtf8().constData(), 0);
350 }
351 
sanitizeStringForCommand(QString s)352 QString CutterCore::sanitizeStringForCommand(QString s)
353 {
354     static const QRegularExpression regexp(";|@");
355     return s.replace(regexp, QStringLiteral("_"));
356 }
357 
cmd(const char * str)358 QString CutterCore::cmd(const char *str)
359 {
360     CORE_LOCK();
361 
362     RVA offset = core->offset;
363     char *res = r_core_cmd_str(core, str);
364     QString o = fromOwnedCharPtr(res);
365 
366     if (offset != core->offset) {
367         updateSeek();
368     }
369     return o;
370 }
371 
isRedirectableDebugee()372 bool CutterCore::isRedirectableDebugee()
373 {
374     if (!currentlyDebugging || currentlyAttachedToPID != -1) {
375         return false;
376     }
377 
378     // We are only able to redirect locally debugged unix processes
379     QJsonArray openFilesArray = cmdj("oj").array();;
380     for (QJsonValue value : openFilesArray) {
381         QJsonObject openFile = value.toObject();
382         QString URI = openFile["uri"].toString();
383         if (URI.contains("ptrace") | URI.contains("mach")) {
384             return true;
385         }
386     }
387     return false;
388 }
389 
isDebugTaskInProgress()390 bool CutterCore::isDebugTaskInProgress()
391 {
392     if (!debugTask.isNull()) {
393         return true;
394     }
395 
396     return false;
397 }
398 
asyncCmdEsil(const char * command,QSharedPointer<R2Task> & task)399 bool CutterCore::asyncCmdEsil(const char *command, QSharedPointer<R2Task> &task)
400 {
401     asyncCmd(command, task);
402 
403     if (task.isNull()) {
404         return false;
405     }
406 
407     connect(task.data(), &R2Task::finished, task.data(), [this, task] () {
408         QString res = task.data()->getResult();
409 
410         if (res.contains(QStringLiteral("[ESIL] Stopped execution in an invalid instruction"))) {
411             msgBox.showMessage("Stopped when attempted to run an invalid instruction. You can disable this in Preferences");
412         }
413     });
414 
415     return true;
416 }
417 
asyncCmd(const char * str,QSharedPointer<R2Task> & task)418 bool CutterCore::asyncCmd(const char *str, QSharedPointer<R2Task> &task)
419 {
420     if (!task.isNull()) {
421         return false;
422     }
423 
424     CORE_LOCK();
425 
426     RVA offset = core->offset;
427 
428     task = QSharedPointer<R2Task>(new R2Task(str, true));
429     connect(task.data(), &R2Task::finished, task.data(), [this, offset, task] () {
430         CORE_LOCK();
431 
432         if (offset != core->offset) {
433             updateSeek();
434         }
435     });
436 
437     return true;
438 }
439 
cmdRawAt(const char * cmd,RVA address)440 QString CutterCore::cmdRawAt(const char *cmd, RVA address)
441 {
442     QString res;
443     RVA oldOffset = getOffset();
444     seekSilent(address);
445 
446     res = cmdRaw(cmd);
447 
448     seekSilent(oldOffset);
449     return res;
450 }
451 
cmdRaw(const char * cmd)452 QString CutterCore::cmdRaw(const char *cmd)
453 {
454     QString res;
455     CORE_LOCK();
456     r_cons_push ();
457 
458     // r_cmd_call does not return the output of the command
459     r_cmd_call(core->rcmd, cmd);
460 
461     // we grab the output straight from r_cons
462     res = r_cons_get_buffer();
463 
464     // cleaning up
465     r_cons_pop ();
466     r_cons_echo (NULL);
467 
468     return res;
469 }
470 
cmdj(const char * str)471 QJsonDocument CutterCore::cmdj(const char *str)
472 {
473     char *res;
474     {
475         CORE_LOCK();
476         res = r_core_cmd_str(core, str);
477     }
478 
479     QJsonDocument doc = parseJson(res, str);
480     r_mem_free(res);
481 
482     return doc;
483 }
484 
cmdjAt(const char * str,RVA address)485 QJsonDocument CutterCore::cmdjAt(const char *str, RVA address)
486 {
487     QJsonDocument res;
488     RVA oldOffset = getOffset();
489     seekSilent(address);
490 
491     res = cmdj(str);
492 
493     seekSilent(oldOffset);
494     return res;
495 }
496 
cmdTask(const QString & str)497 QString CutterCore::cmdTask(const QString &str)
498 {
499     R2Task task(str);
500     task.startTask();
501     task.joinTask();
502     return task.getResult();
503 }
504 
cmdjTask(const QString & str)505 QJsonDocument CutterCore::cmdjTask(const QString &str)
506 {
507     R2Task task(str);
508     task.startTask();
509     task.joinTask();
510     return parseJson(task.getResultRaw(), str);
511 }
512 
parseJson(const char * res,const char * cmd)513 QJsonDocument CutterCore::parseJson(const char *res, const char *cmd)
514 {
515     QByteArray json(res);
516 
517     if (json.isEmpty()) {
518         return QJsonDocument();
519     }
520 
521     QJsonParseError jsonError;
522     QJsonDocument doc = QJsonDocument::fromJson(json, &jsonError);
523 
524     if (jsonError.error != QJsonParseError::NoError) {
525         if (cmd) {
526             eprintf("Failed to parse JSON for command \"%s\": %s\n", cmd,
527                     jsonError.errorString().toLocal8Bit().constData());
528         } else {
529             eprintf("Failed to parse JSON: %s\n", jsonError.errorString().toLocal8Bit().constData());
530         }
531         const int MAX_JSON_DUMP_SIZE = 8 * 1024;
532         if (json.length() > MAX_JSON_DUMP_SIZE) {
533             int originalSize = json.length();
534             json.resize(MAX_JSON_DUMP_SIZE);
535             eprintf("%d bytes total: %s ...\n", originalSize, json.constData());
536         } else {
537             eprintf("%s\n", json.constData());
538         }
539     }
540 
541     return doc;
542 }
543 
autocomplete(const QString & cmd,RLinePromptType promptType,size_t limit)544 QStringList CutterCore::autocomplete(const QString &cmd, RLinePromptType promptType, size_t limit)
545 {
546     RLineBuffer buf;
547     int c = snprintf(buf.data, sizeof(buf.data), "%s", cmd.toUtf8().constData());
548     if (c < 0) {
549         return {};
550     }
551     buf.index = buf.length = std::min((int)(sizeof(buf.data) - 1), c);
552 
553     RLineCompletion completion;
554     r_line_completion_init(&completion, limit);
555     r_core_autocomplete(core(), &completion, &buf, promptType);
556 
557     QStringList r;
558     r.reserve(r_pvector_len(&completion.args));
559     for (size_t i = 0; i < r_pvector_len(&completion.args); i++) {
560         r.push_back(QString::fromUtf8(reinterpret_cast<const char *>(r_pvector_at(&completion.args, i))));
561     }
562 
563     r_line_completion_fini(&completion);
564     return r;
565 }
566 
567 /**
568  * @brief CutterCore::loadFile
569  * Load initial file. TODO Maybe use the "o" commands?
570  * @param path File path
571  * @param baddr Base (RBin) address
572  * @param mapaddr Map address
573  * @param perms
574  * @param va
575  * @param loadbin Load RBin information
576  * @param forceBinPlugin
577  * @return
578  */
loadFile(QString path,ut64 baddr,ut64 mapaddr,int perms,int va,bool loadbin,const QString & forceBinPlugin)579 bool CutterCore::loadFile(QString path, ut64 baddr, ut64 mapaddr, int perms, int va,
580                           bool loadbin, const QString &forceBinPlugin)
581 {
582     CORE_LOCK();
583     RIODesc *f;
584     r_config_set_i(core->config, "io.va", va);
585 
586     f = r_core_file_open(core, path.toUtf8().constData(), perms, mapaddr);
587     if (!f) {
588         eprintf("r_core_file_open failed\n");
589         return false;
590     }
591 
592     if (!forceBinPlugin.isNull()) {
593         r_bin_force_plugin(r_core_get_bin(core), forceBinPlugin.toUtf8().constData());
594     }
595 
596     if (loadbin && va) {
597         if (!r_core_bin_load(core, path.toUtf8().constData(), baddr)) {
598             eprintf("CANNOT GET RBIN INFO\n");
599         }
600 
601 #if HAVE_MULTIPLE_RBIN_FILES_INSIDE_SELECT_WHICH_ONE
602         if (!r_core_file_open(core, path.toUtf8(), R_IO_READ | (rw ? R_IO_WRITE : 0, mapaddr))) {
603             eprintf("Cannot open file\n");
604         } else {
605             // load RBin information
606             // XXX only for sub-bins
607             r_core_bin_load(core, path.toUtf8(), baddr);
608             r_bin_select_idx(core->bin, NULL, idx);
609         }
610 #endif
611     } else {
612         // Not loading RBin info coz va = false
613     }
614 
615     auto iod = core->io ? core->io->desc : NULL;
616 /*
617     auto debug = core->file && iod && (core->file->fd == iod->fd) && iod->plugin && \
618                  iod->plugin->isdbg;
619 */
620     auto debug = r_config_get_i (core->config, "cfg.debug");
621 
622     if (!debug && r_flag_get (core->flags, "entry0")) {
623         r_core_cmd0 (core, "s entry0");
624     }
625 
626     if (perms & R_PERM_W) {
627         r_core_cmd0 (core, "omfg+w");
628     }
629 
630     fflush(stdout);
631     return true;
632 }
633 
tryFile(QString path,bool rw)634 bool CutterCore::tryFile(QString path, bool rw)
635 {
636     CORE_LOCK();
637     RIODesc *cf;
638     int flags = R_PERM_R;
639     if (rw) flags = R_PERM_RW;
640     cf = r_core_file_open(core, path.toUtf8().constData(), flags, 0LL);
641     if (!cf) {
642         return false;
643     }
644 
645     r_core_cmdf (core, "o-%d", cf->fd);
646 
647     return true;
648 }
649 
650 /**
651  * @brief Maps a file using r2 API
652  * @param path Path to file
653  * @param mapaddr Map Address
654  * @return bool
655  */
mapFile(QString path,RVA mapaddr)656 bool CutterCore::mapFile(QString path, RVA mapaddr)
657 {
658     CORE_LOCK();
659     RVA addr = mapaddr != RVA_INVALID ? mapaddr : 0;
660     ut64 baddr = Core()->getFileInfo().object()["bin"].toObject()["baddr"].toVariant().toULongLong();
661     if (r_core_file_open(core, path.toUtf8().constData(), R_PERM_RX, addr)) {
662         r_core_bin_load(core, path.toUtf8().constData(), baddr);
663     } else {
664         return false;
665     }
666     return true;
667 }
668 
renameFunction(const RVA offset,const QString & newName)669 void CutterCore::renameFunction(const RVA offset, const QString &newName)
670 {
671     cmdRaw("afn " + newName + " " + RAddressString(offset));
672     emit functionRenamed(offset, newName);
673 }
674 
delFunction(RVA addr)675 void CutterCore::delFunction(RVA addr)
676 {
677     cmdRaw("af- " + RAddressString(addr));
678     emit functionsChanged();
679 }
680 
renameFlag(QString old_name,QString new_name)681 void CutterCore::renameFlag(QString old_name, QString new_name)
682 {
683     cmdRaw("fr " + old_name + " " + new_name);
684     emit flagsChanged();
685 }
686 
renameFunctionVariable(QString newName,QString oldName,RVA functionAddress)687 void CutterCore::renameFunctionVariable(QString newName, QString oldName, RVA functionAddress)
688 {
689     CORE_LOCK();
690     RAnalFunction *function = r_anal_get_function_at(core->anal, functionAddress);
691     RAnalVar *variable = r_anal_function_get_var_byname(function, oldName.toUtf8().constData());
692     if (variable) {
693         r_anal_var_rename(variable, newName.toUtf8().constData(), true);
694     }
695     emit refreshCodeViews();
696 }
697 
delFlag(RVA addr)698 void CutterCore::delFlag(RVA addr)
699 {
700     cmdRawAt("f-", addr);
701     emit flagsChanged();
702 }
703 
delFlag(const QString & name)704 void CutterCore::delFlag(const QString &name)
705 {
706     cmdRaw("f-" + name);
707     emit flagsChanged();
708 }
709 
getInstructionBytes(RVA addr)710 QString CutterCore::getInstructionBytes(RVA addr)
711 {
712     return cmdj("aoj @ " + RAddressString(addr)).array().first().toObject()[RJsonKey::bytes].toString();
713 }
714 
getInstructionOpcode(RVA addr)715 QString CutterCore::getInstructionOpcode(RVA addr)
716 {
717     return cmdj("aoj @ " + RAddressString(addr)).array().first().toObject()[RJsonKey::opcode].toString();
718 }
719 
editInstruction(RVA addr,const QString & inst)720 void CutterCore::editInstruction(RVA addr, const QString &inst)
721 {
722     cmdRawAt(QString("wa %1").arg(inst), addr);
723     emit instructionChanged(addr);
724 }
725 
nopInstruction(RVA addr)726 void CutterCore::nopInstruction(RVA addr)
727 {
728     cmdRawAt("wao nop", addr);
729     emit instructionChanged(addr);
730 }
731 
jmpReverse(RVA addr)732 void CutterCore::jmpReverse(RVA addr)
733 {
734     cmdRawAt("wao recj", addr);
735     emit instructionChanged(addr);
736 }
737 
editBytes(RVA addr,const QString & bytes)738 void CutterCore::editBytes(RVA addr, const QString &bytes)
739 {
740     cmdRawAt(QString("wx %1").arg(bytes), addr);
741     emit instructionChanged(addr);
742 }
743 
editBytesEndian(RVA addr,const QString & bytes)744 void CutterCore::editBytesEndian(RVA addr, const QString &bytes)
745 {
746     cmdRawAt(QString("wv %1").arg(bytes), addr);
747     emit stackChanged();
748 }
749 
setToCode(RVA addr)750 void CutterCore::setToCode(RVA addr)
751 {
752     cmdRawAt("Cd-", addr);
753     emit instructionChanged(addr);
754 }
755 
setAsString(RVA addr,int size,StringTypeFormats type)756 void CutterCore::setAsString(RVA addr, int size, StringTypeFormats type)
757 {
758     if(RVA_INVALID == addr)
759     {
760         return;
761     }
762 
763     QString command;
764 
765     switch(type)
766     {
767     case StringTypeFormats::None:
768     {
769         command = "Cs";
770         break;
771     }
772     case StringTypeFormats::ASCII_LATIN1:
773     {
774         command = "Csa";
775         break;
776     }
777     case StringTypeFormats::UTF8:
778     {
779         command = "Cs8";
780         break;
781     }
782     default:
783         return;
784     }
785 
786     seekAndShow(addr);
787 
788     cmdRawAt(QString("%1 %2").arg(command).arg(size), addr);
789     emit instructionChanged(addr);
790 }
791 
removeString(RVA addr)792 void CutterCore::removeString(RVA addr)
793 {
794     cmdRawAt("Cs-", addr);
795     emit instructionChanged(addr);
796 }
797 
getString(RVA addr)798 QString CutterCore::getString(RVA addr)
799 {
800     return cmdRawAt("ps", addr);
801 }
802 
setToData(RVA addr,int size,int repeat)803 void CutterCore::setToData(RVA addr, int size, int repeat)
804 {
805     if (size <= 0 || repeat <= 0) {
806         return;
807     }
808     cmdRawAt("Cd-", addr);
809     cmdRawAt(QString("Cd %1 %2").arg(size).arg(repeat), addr);
810     emit instructionChanged(addr);
811 }
812 
sizeofDataMeta(RVA addr)813 int CutterCore::sizeofDataMeta(RVA addr)
814 {
815     bool ok;
816     int size = cmdRawAt("Cd.", addr).toInt(&ok);
817     return (ok ? size : 0);
818 }
819 
setComment(RVA addr,const QString & cmt)820 void CutterCore::setComment(RVA addr, const QString &cmt)
821 {
822     cmdRawAt(QString("CCu base64:%1").arg(QString(cmt.toLocal8Bit().toBase64())), addr);
823     emit commentsChanged(addr);
824 }
825 
delComment(RVA addr)826 void CutterCore::delComment(RVA addr)
827 {
828     cmdRawAt("CC-", addr);
829     emit commentsChanged(addr);
830 }
831 
832 /**
833  * @brief Gets the comment present at a specific address
834  * @param addr The address to be checked
835  * @return String containing comment
836  */
getCommentAt(RVA addr)837 QString CutterCore::getCommentAt(RVA addr)
838 {
839     CORE_LOCK();
840     return r_meta_get_string(core->anal, R_META_TYPE_COMMENT, addr);
841 }
842 
setImmediateBase(const QString & r2BaseName,RVA offset)843 void CutterCore::setImmediateBase(const QString &r2BaseName, RVA offset)
844 {
845     if (offset == RVA_INVALID) {
846         offset = getOffset();
847     }
848 
849     this->cmdRawAt(QString("ahi %1").arg(r2BaseName), offset);
850     emit instructionChanged(offset);
851 }
852 
setCurrentBits(int bits,RVA offset)853 void CutterCore::setCurrentBits(int bits, RVA offset)
854 {
855     if (offset == RVA_INVALID) {
856         offset = getOffset();
857     }
858 
859     this->cmdRawAt(QString("ahb %1").arg(bits), offset);
860     emit instructionChanged(offset);
861 }
862 
applyStructureOffset(const QString & structureOffset,RVA offset)863 void CutterCore::applyStructureOffset(const QString &structureOffset, RVA offset)
864 {
865     if (offset == RVA_INVALID) {
866         offset = getOffset();
867     }
868 
869     this->cmdRawAt("aht " + structureOffset, offset);
870     emit instructionChanged(offset);
871 }
872 
seekSilent(ut64 offset)873 void CutterCore::seekSilent(ut64 offset)
874 {
875     CORE_LOCK();
876     if (offset == RVA_INVALID) {
877         return;
878     }
879     r_core_seek(core, offset, true);
880 }
881 
seek(ut64 offset)882 void CutterCore::seek(ut64 offset)
883 {
884     // Slower than using the API, but the API is not complete
885     // which means we either have to duplicate code from radare2
886     // here, or refactor radare2 API.
887     CORE_LOCK();
888     if (offset == RVA_INVALID) {
889         return;
890     }
891 
892     // use cmd and not cmdRaw to make sure seekChanged is emitted
893     cmd(QString("s %1").arg(offset));
894     // cmd already does emit seekChanged(core_->offset);
895 }
896 
showMemoryWidget()897 void CutterCore::showMemoryWidget()
898 {
899     emit showMemoryWidgetRequested();
900 }
901 
seekAndShow(ut64 offset)902 void CutterCore::seekAndShow(ut64 offset)
903 {
904     seek(offset);
905     showMemoryWidget();
906 }
907 
seekAndShow(QString offset)908 void CutterCore::seekAndShow(QString offset)
909 {
910     seek(offset);
911     showMemoryWidget();
912 }
913 
seek(QString thing)914 void CutterCore::seek(QString thing)
915 {
916     cmdRaw(QString("s %1").arg(thing));
917     updateSeek();
918 }
919 
seekPrev()920 void CutterCore::seekPrev()
921 {
922     // Use cmd because cmdRaw does not work with seek history
923     cmd("s-");
924 }
925 
seekNext()926 void CutterCore::seekNext()
927 {
928     // Use cmd because cmdRaw does not work with seek history
929     cmd("s+");
930 }
931 
updateSeek()932 void CutterCore::updateSeek()
933 {
934     emit seekChanged(getOffset());
935 }
936 
prevOpAddr(RVA startAddr,int count)937 RVA CutterCore::prevOpAddr(RVA startAddr, int count)
938 {
939     CORE_LOCK();
940     bool ok;
941     RVA offset = cmdRawAt(QString("/O %1").arg(count), startAddr).toULongLong(&ok, 16);
942     return ok ? offset : startAddr - count;
943 }
944 
nextOpAddr(RVA startAddr,int count)945 RVA CutterCore::nextOpAddr(RVA startAddr, int count)
946 {
947     CORE_LOCK();
948 
949     QJsonArray array = Core()->cmdj("pdj " + QString::number(count + 1) + "@" + QString::number(
950                                         startAddr)).array();
951     if (array.isEmpty()) {
952         return startAddr + 1;
953     }
954 
955     QJsonValue instValue = array.last();
956     if (!instValue.isObject()) {
957         return startAddr + 1;
958     }
959 
960     bool ok;
961     RVA offset = instValue.toObject()[RJsonKey::offset].toVariant().toULongLong(&ok);
962     if (!ok) {
963         return startAddr + 1;
964     }
965 
966     return offset;
967 }
968 
getOffset()969 RVA CutterCore::getOffset()
970 {
971     return core_->offset;
972 }
973 
math(const QString & expr)974 ut64 CutterCore::math(const QString &expr)
975 {
976     CORE_LOCK();
977     return r_num_math(core ? core->num : NULL, expr.toUtf8().constData());
978 }
979 
num(const QString & expr)980 ut64 CutterCore::num(const QString &expr)
981 {
982     CORE_LOCK();
983     return r_num_get(core ? core->num : NULL, expr.toUtf8().constData());
984 }
985 
itoa(ut64 num,int rdx)986 QString CutterCore::itoa(ut64 num, int rdx)
987 {
988     return QString::number(num, rdx);
989 }
990 
setConfig(const char * k,const char * v)991 void CutterCore::setConfig(const char *k, const char *v)
992 {
993     CORE_LOCK();
994     r_config_set(core->config, k, v);
995 }
996 
setConfig(const QString & k,const char * v)997 void CutterCore::setConfig(const QString &k, const char *v)
998 {
999     CORE_LOCK();
1000     r_config_set(core->config, k.toUtf8().constData(), v);
1001 }
1002 
setConfig(const char * k,const QString & v)1003 void CutterCore::setConfig(const char *k, const QString &v)
1004 {
1005     CORE_LOCK();
1006     r_config_set(core->config, k, v.toUtf8().constData());
1007 }
1008 
setConfig(const char * k,int v)1009 void CutterCore::setConfig(const char *k, int v)
1010 {
1011     CORE_LOCK();
1012     r_config_set_i(core->config, k, static_cast<ut64>(v));
1013 }
1014 
setConfig(const char * k,bool v)1015 void CutterCore::setConfig(const char *k, bool v)
1016 {
1017     CORE_LOCK();
1018     r_config_set_i(core->config, k, v ? 1 : 0);
1019 }
1020 
getConfigi(const char * k)1021 int CutterCore::getConfigi(const char *k)
1022 {
1023     CORE_LOCK();
1024     return static_cast<int>(r_config_get_i(core->config, k));
1025 }
1026 
getConfigut64(const char * k)1027 ut64 CutterCore::getConfigut64(const char *k)
1028 {
1029     CORE_LOCK();
1030     return r_config_get_i(core->config, k);
1031 }
1032 
getConfigb(const char * k)1033 bool CutterCore::getConfigb(const char *k)
1034 {
1035     CORE_LOCK();
1036     return r_config_get_i(core->config, k) != 0;
1037 }
1038 
getConfigDescription(const char * k)1039 QString CutterCore::getConfigDescription(const char *k)
1040 {
1041     CORE_LOCK();
1042     RConfigNode *node = r_config_node_get (core->config, k);
1043     return node ? QString(node->desc) : QString("Unrecognized configuration key");
1044 }
1045 
triggerRefreshAll()1046 void CutterCore::triggerRefreshAll()
1047 {
1048     emit refreshAll();
1049 }
1050 
triggerAsmOptionsChanged()1051 void CutterCore::triggerAsmOptionsChanged()
1052 {
1053     emit asmOptionsChanged();
1054 }
1055 
triggerGraphOptionsChanged()1056 void CutterCore::triggerGraphOptionsChanged()
1057 {
1058     emit graphOptionsChanged();
1059 }
1060 
message(const QString & msg,bool debug)1061 void CutterCore::message(const QString &msg, bool debug)
1062 {
1063     if (msg.isEmpty())
1064         return;
1065     if (debug) {
1066         qDebug() << msg;
1067         emit newDebugMessage(msg);
1068         return;
1069     }
1070     emit newMessage(msg);
1071 }
1072 
getConfig(const char * k)1073 QString CutterCore::getConfig(const char *k)
1074 {
1075     CORE_LOCK();
1076     return QString(r_config_get(core->config, k));
1077 }
1078 
setConfig(const char * k,const QVariant & v)1079 void CutterCore::setConfig(const char *k, const QVariant &v)
1080 {
1081     switch (v.type()) {
1082     case QVariant::Type::Bool:
1083         setConfig(k, v.toBool());
1084         break;
1085     case QVariant::Type::Int:
1086         setConfig(k, v.toInt());
1087         break;
1088     default:
1089         setConfig(k, v.toString());
1090         break;
1091     }
1092 }
1093 
setCPU(QString arch,QString cpu,int bits)1094 void CutterCore::setCPU(QString arch, QString cpu, int bits)
1095 {
1096     if (arch != nullptr) {
1097         setConfig("asm.arch", arch);
1098     }
1099     if (cpu != nullptr) {
1100         setConfig("asm.cpu", cpu);
1101     }
1102     setConfig("asm.bits", bits);
1103 }
1104 
setEndianness(bool big)1105 void CutterCore::setEndianness(bool big)
1106 {
1107     setConfig("cfg.bigendian", big);
1108 }
1109 
assemble(const QString & code)1110 QByteArray CutterCore::assemble(const QString &code)
1111 {
1112     CORE_LOCK();
1113     RAsmCode *ac = r_asm_massemble(core->rasm, code.toUtf8().constData());
1114     QByteArray res;
1115     if (ac && ac->bytes) {
1116         res = QByteArray(reinterpret_cast<const char *>(ac->bytes), ac->len);
1117     }
1118     r_asm_code_free(ac);
1119     return res;
1120 }
1121 
disassemble(const QByteArray & data)1122 QString CutterCore::disassemble(const QByteArray &data)
1123 {
1124     CORE_LOCK();
1125     RAsmCode *ac = r_asm_mdisassemble(core->rasm, reinterpret_cast<const ut8 *>(data.constData()), data.length());
1126     QString code;
1127     if (ac && ac->assembly) {
1128         code = QString::fromUtf8(ac->assembly);
1129     }
1130     r_asm_code_free(ac);
1131     return code;
1132 }
1133 
disassembleSingleInstruction(RVA addr)1134 QString CutterCore::disassembleSingleInstruction(RVA addr)
1135 {
1136     return cmdRawAt("pi 1", addr).simplified();
1137 }
1138 
functionIn(ut64 addr)1139 RAnalFunction *CutterCore::functionIn(ut64 addr)
1140 {
1141     CORE_LOCK();
1142     RList *fcns = r_anal_get_functions_in (core->anal, addr);
1143     RAnalFunction *fcn = !r_list_empty(fcns) ? reinterpret_cast<RAnalFunction *>(r_list_first(fcns)) : nullptr;
1144     r_list_free(fcns);
1145     return fcn;
1146 }
1147 
functionAt(ut64 addr)1148 RAnalFunction *CutterCore::functionAt(ut64 addr)
1149 {
1150     CORE_LOCK();
1151     return r_anal_get_function_at(core->anal, addr);
1152 }
1153 
1154 /**
1155  * @brief finds the start address of a function in a given address
1156  * @param addr - an address which belongs to a function
1157  * @returns if function exists, return its start address. Otherwise return RVA_INVALID
1158  */
getFunctionStart(RVA addr)1159 RVA CutterCore::getFunctionStart(RVA addr)
1160 {
1161     CORE_LOCK();
1162     RAnalFunction *fcn = Core()->functionIn(addr);
1163     return fcn ? fcn->addr : RVA_INVALID;
1164 }
1165 
1166 /**
1167  * @brief finds the end address of a function in a given address
1168  * @param addr - an address which belongs to a function
1169  * @returns if function exists, return its end address. Otherwise return RVA_INVALID
1170  */
getFunctionEnd(RVA addr)1171 RVA CutterCore::getFunctionEnd(RVA addr)
1172 {
1173     CORE_LOCK();
1174     RAnalFunction *fcn = Core()->functionIn(addr);
1175     return fcn ? fcn->addr : RVA_INVALID;
1176 }
1177 
1178 /**
1179  * @brief finds the last instruction of a function in a given address
1180  * @param addr - an address which belongs to a function
1181  * @returns if function exists, return the address of its last instruction. Otherwise return RVA_INVALID
1182  */
getLastFunctionInstruction(RVA addr)1183 RVA CutterCore::getLastFunctionInstruction(RVA addr)
1184 {
1185     CORE_LOCK();
1186     RAnalFunction *fcn = Core()->functionIn(addr);
1187     if (!fcn) {
1188         return RVA_INVALID;
1189     }
1190     RAnalBlock *lastBB = (RAnalBlock *)r_list_last(fcn->bbs);
1191     return lastBB ? lastBB->addr + r_anal_bb_offset_inst(lastBB, lastBB->ninstr-1) : RVA_INVALID;
1192 }
1193 
cmdFunctionAt(QString addr)1194 QString CutterCore::cmdFunctionAt(QString addr)
1195 {
1196     QString ret;
1197     // Use cmd because cmdRaw would not work with grep
1198     ret = cmd(QString("fd @ %1~[0]").arg(addr));
1199     return ret.trimmed();
1200 }
1201 
cmdFunctionAt(RVA addr)1202 QString CutterCore::cmdFunctionAt(RVA addr)
1203 {
1204     return cmdFunctionAt(QString::number(addr));
1205 }
1206 
cmdEsil(const char * command)1207 void CutterCore::cmdEsil(const char *command)
1208 {
1209     // use cmd and not cmdRaw because of unexpected commands
1210     QString res = cmd(command);
1211     if (res.contains(QStringLiteral("[ESIL] Stopped execution in an invalid instruction"))) {
1212         msgBox.showMessage("Stopped when attempted to run an invalid instruction. You can disable this in Preferences");
1213     }
1214 }
1215 
createFunctionAt(RVA addr)1216 QString CutterCore::createFunctionAt(RVA addr)
1217 {
1218     QString ret = cmdRaw(QString("af %1").arg(addr));
1219     emit functionsChanged();
1220     return ret;
1221 }
1222 
createFunctionAt(RVA addr,QString name)1223 QString CutterCore::createFunctionAt(RVA addr, QString name)
1224 {
1225     static const QRegularExpression regExp("[^a-zA-Z0-9_]");
1226     name.remove(regExp);
1227     QString ret = cmdRawAt(QString("af %1").arg(name), addr);
1228     emit functionsChanged();
1229     return ret;
1230 }
1231 
getRegistersInfo()1232 QJsonDocument CutterCore::getRegistersInfo()
1233 {
1234     return cmdj("aeafj");
1235 }
1236 
getOffsetJump(RVA addr)1237 RVA CutterCore::getOffsetJump(RVA addr)
1238 {
1239     bool ok;
1240     RVA value = cmdj("aoj @" + QString::number(
1241                          addr)).array().first().toObject().value(RJsonKey::jump).toVariant().toULongLong(&ok);
1242 
1243     if (!ok) {
1244         return RVA_INVALID;
1245     }
1246 
1247     return value;
1248 }
1249 
1250 
getDecompilers()1251 QList<Decompiler *> CutterCore::getDecompilers()
1252 {
1253     return decompilers;
1254 }
1255 
getDecompilerById(const QString & id)1256 Decompiler *CutterCore::getDecompilerById(const QString &id)
1257 {
1258     for (Decompiler *dec : decompilers) {
1259         if (dec->getId() == id) {
1260             return dec;
1261         }
1262     }
1263     return nullptr;
1264 }
1265 
registerDecompiler(Decompiler * decompiler)1266 bool CutterCore::registerDecompiler(Decompiler *decompiler)
1267 {
1268     if (getDecompilerById(decompiler->getId())) {
1269         return false;
1270     }
1271     decompiler->setParent(this);
1272     decompilers.push_back(decompiler);
1273     return true;
1274 }
1275 
getFileInfo()1276 QJsonDocument CutterCore::getFileInfo()
1277 {
1278     return cmdj("ij");
1279 }
1280 
getFileVersionInfo()1281 QJsonDocument CutterCore::getFileVersionInfo()
1282 {
1283     return cmdj("iVj");
1284 }
1285 
getSignatureInfo()1286 QJsonDocument CutterCore::getSignatureInfo()
1287 {
1288     return cmdj("iCj");
1289 }
1290 
1291 // Utility function to check if a telescoped item exists and add it with prefixes to the desc
appendVar(QString & dst,const QString val,const QString prepend_val,const QString append_val)1292 static inline const QString appendVar(QString &dst, const QString val, const QString prepend_val,
1293                                        const QString append_val)
1294 {
1295     if (!val.isEmpty()) {
1296         dst += prepend_val + val + append_val;
1297     }
1298     return val;
1299 }
1300 
formatRefDesc(QJsonObject refItem)1301 RefDescription CutterCore::formatRefDesc(QJsonObject refItem)
1302 {
1303     RefDescription desc;
1304 
1305     // Ignore empty refs and refs that only contain addr
1306     if (refItem.size() <= 1) {
1307         return desc;
1308     }
1309 
1310     QString str = refItem["string"].toVariant().toString();
1311     if (!str.isEmpty()) {
1312         desc.ref = str;
1313         desc.refColor = ConfigColor("comment");
1314     } else {
1315         QString type, string;
1316         do {
1317             desc.ref += " ->";
1318             appendVar(desc.ref, refItem["reg"].toVariant().toString(), " @", "");
1319             appendVar(desc.ref, refItem["mapname"].toVariant().toString(), " (", ")");
1320             appendVar(desc.ref, refItem["section"].toVariant().toString(), " (", ")");
1321             appendVar(desc.ref, refItem["func"].toVariant().toString(), " ", "");
1322             type = appendVar(desc.ref, refItem["type"].toVariant().toString(), " ", "");
1323             appendVar(desc.ref, refItem["perms"].toVariant().toString(), " ", "");
1324             appendVar(desc.ref, refItem["asm"].toVariant().toString(), " \"", "\"");
1325             string = appendVar(desc.ref, refItem["string"].toVariant().toString(), " ", "");
1326             if (!string.isNull()) {
1327                 // There is no point in adding ascii and addr info after a string
1328                 break;
1329             }
1330             if (!refItem["value"].isNull()) {
1331                 appendVar(desc.ref, RAddressString(refItem["value"].toVariant().toULongLong()), " ", "");
1332             }
1333             refItem = refItem["ref"].toObject();
1334         } while (!refItem.empty());
1335 
1336         // Set the ref's color according to the last item type
1337         if (type == "ascii" || !string.isEmpty()) {
1338             desc.refColor = ConfigColor("comment");
1339         } else if (type == "program") {
1340             desc.refColor = ConfigColor("fname");
1341         } else if (type == "library") {
1342             desc.refColor = ConfigColor("floc");
1343         } else if (type == "stack") {
1344             desc.refColor = ConfigColor("offset");
1345         }
1346     }
1347 
1348     return desc;
1349 }
1350 
getRegisterRefs(int depth)1351 QList<QJsonObject> CutterCore::getRegisterRefs(int depth)
1352 {
1353     QList<QJsonObject> ret;
1354     if (!currentlyDebugging) {
1355         return ret;
1356     }
1357 
1358     QJsonObject registers = cmdj("drj").object();
1359 
1360     for (const QString &key : registers.keys()) {
1361         QJsonObject reg;
1362         reg["value"] = registers.value(key);
1363         reg["ref"] = getAddrRefs(registers.value(key).toVariant().toULongLong(), depth);
1364         reg["name"] = key;
1365         ret.append(reg);
1366     }
1367 
1368     return ret;
1369 }
1370 
getStack(int size,int depth)1371 QList<QJsonObject> CutterCore::getStack(int size, int depth)
1372 {
1373     QList<QJsonObject> stack;
1374     if (!currentlyDebugging) {
1375         return stack;
1376     }
1377 
1378     CORE_LOCK();
1379     bool ret;
1380     RVA addr = cmdRaw("dr SP").toULongLong(&ret, 16);
1381     if (!ret) {
1382         return stack;
1383     }
1384 
1385     int base = core->anal->bits;
1386     for (int i = 0; i < size; i += base / 8) {
1387         if ((base == 32 && addr + i >= UT32_MAX) || (base == 16 && addr + i >= UT16_MAX)) {
1388             break;
1389         }
1390 
1391         stack.append(getAddrRefs(addr + i, depth));
1392     }
1393 
1394     return stack;
1395 }
1396 
getAddrRefs(RVA addr,int depth)1397 QJsonObject CutterCore::getAddrRefs(RVA addr, int depth) {
1398     QJsonObject json;
1399     if (depth < 1 || addr == UT64_MAX) {
1400         return json;
1401     }
1402 
1403     CORE_LOCK();
1404     int bits = core->rasm->bits;
1405     QByteArray buf = QByteArray();
1406     ut64 type = r_core_anal_address(core, addr);
1407 
1408     json["addr"] = QString::number(addr);
1409 
1410     // Search for the section the addr is in, avoid duplication for heap/stack with type
1411     if(!(type & R_ANAL_ADDR_TYPE_HEAP || type & R_ANAL_ADDR_TYPE_STACK)) {
1412         // Attempt to find the address within a map
1413         RDebugMap *map = r_debug_map_get(core->dbg, addr);
1414         if (map && map->name && map->name[0]) {
1415             json["mapname"] = map->name;
1416         }
1417 
1418         RBinSection *sect = r_bin_get_section_at(r_bin_cur_object (core->bin), addr, true);
1419         if (sect && sect->name[0]) {
1420             json["section"] = sect->name;
1421         }
1422     }
1423 
1424     // Check if the address points to a register
1425     RFlagItem *fi = r_flag_get_i(core->flags, addr);
1426     if (fi) {
1427         RRegItem *r = r_reg_get(core->dbg->reg, fi->name, -1);
1428         if (r) {
1429             json["reg"] = r->name;
1430         }
1431     }
1432 
1433     // Attempt to find the address within a function
1434     RAnalFunction *fcn = r_anal_get_fcn_in(core->anal, addr, 0);
1435     if (fcn) {
1436         json["fcn"] = fcn->name;
1437     }
1438 
1439     // Update type and permission information
1440     if (type != 0) {
1441         if (type & R_ANAL_ADDR_TYPE_HEAP) {
1442             json["type"] = "heap";
1443         } else if (type & R_ANAL_ADDR_TYPE_STACK) {
1444             json["type"] = "stack";
1445         } else if (type & R_ANAL_ADDR_TYPE_PROGRAM) {
1446             json["type"] = "program";
1447         } else if (type & R_ANAL_ADDR_TYPE_LIBRARY) {
1448             json["type"] = "library";
1449         } else if (type & R_ANAL_ADDR_TYPE_ASCII) {
1450             json["type"] = "ascii";
1451         } else if (type & R_ANAL_ADDR_TYPE_SEQUENCE) {
1452             json["type"] = "sequence";
1453         }
1454 
1455         QString perms = "";
1456         if (type & R_ANAL_ADDR_TYPE_READ) {
1457             perms += "r";
1458         }
1459         if (type & R_ANAL_ADDR_TYPE_WRITE) {
1460             perms += "w";
1461         }
1462         if (type & R_ANAL_ADDR_TYPE_EXEC) {
1463             RAsmOp op;
1464             buf.resize(32);
1465             perms += "x";
1466             // Instruction disassembly
1467             r_io_read_at(core->io, addr, (unsigned char*)buf.data(), buf.size());
1468             r_asm_set_pc(core->rasm, addr);
1469             r_asm_disassemble(core->rasm, &op, (unsigned char*)buf.data(), buf.size());
1470             json["asm"] = r_asm_op_get_asm(&op);
1471         }
1472 
1473         if (!perms.isEmpty()) {
1474             json["perms"] = perms;
1475         }
1476     }
1477 
1478     // Try to telescope further if depth permits it
1479     if ((type & R_ANAL_ADDR_TYPE_READ) && !(type & R_ANAL_ADDR_TYPE_EXEC)) {
1480         buf.resize(64);
1481         ut32 *n32 = (ut32 *)buf.data();
1482         ut64 *n64 = (ut64 *)buf.data();
1483         r_io_read_at(core->io, addr, (unsigned char*)buf.data(), buf.size());
1484         ut64 n = (bits == 64)? *n64: *n32;
1485         // The value of the next address will serve as an indication that there's more to
1486         // telescope if we have reached the depth limit
1487         json["value"] = QString::number(n);
1488         if (depth && n != addr) {
1489             // Make sure we aren't telescoping the same address
1490             QJsonObject ref = getAddrRefs(n, depth - 1);
1491             if (!ref.empty() && !ref["type"].isNull()) {
1492                 // If the dereference of the current pointer is an ascii character we
1493                 // might have a string in this address
1494                 if (ref["type"].toString().contains("ascii")) {
1495                     buf.resize(128);
1496                     r_io_read_at(core->io, addr, (unsigned char*)buf.data(), buf.size());
1497                     QString strVal = QString(buf);
1498                     // Indicate that the string is longer than the printed value
1499                     if (strVal.size() == buf.size()) {
1500                         strVal += "...";
1501                     }
1502                     json["string"] = strVal;
1503                 }
1504                 json["ref"] = ref;
1505             }
1506         }
1507     }
1508     return json;
1509 }
1510 
getProcessThreads(int pid)1511 QJsonDocument CutterCore::getProcessThreads(int pid)
1512 {
1513     if (-1 == pid) {
1514         // Return threads list of the currently debugged PID
1515         return cmdj("dptj");
1516     } else {
1517         return cmdj("dptj " + QString::number(pid));
1518     }
1519 }
1520 
getChildProcesses(int pid)1521 QJsonDocument CutterCore::getChildProcesses(int pid)
1522 {
1523     // Return the currently debugged process and it's children
1524     if (-1 == pid) {
1525         return cmdj("dpj");
1526     }
1527     // Return the given pid and it's child processes
1528     return cmdj("dpj " + QString::number(pid));
1529 }
1530 
getRegisterValues()1531 QJsonDocument CutterCore::getRegisterValues()
1532 {
1533     return cmdj("drj");
1534 }
1535 
getVariables(RVA at)1536 QList<VariableDescription> CutterCore::getVariables(RVA at)
1537 {
1538     QList<VariableDescription> ret;
1539     QJsonObject varsObject = cmdj(QString("afvj @ %1").arg(at)).object();
1540 
1541     auto addVars = [&](VariableDescription::RefType refType, const QJsonArray &array) {
1542         for (const QJsonValue &varValue : array) {
1543             QJsonObject varObject = varValue.toObject();
1544             VariableDescription desc;
1545             desc.refType = refType;
1546             desc.name = varObject["name"].toString();
1547             desc.type = varObject["type"].toString();
1548             ret << desc;
1549         }
1550     };
1551 
1552     addVars(VariableDescription::RefType::SP, varsObject["sp"].toArray());
1553     addVars(VariableDescription::RefType::BP, varsObject["bp"].toArray());
1554     addVars(VariableDescription::RefType::Reg, varsObject["reg"].toArray());
1555 
1556     return ret;
1557 }
1558 
getRegisterRefValues()1559 QVector<RegisterRefValueDescription> CutterCore::getRegisterRefValues()
1560 {
1561     QJsonArray registerRefArray = cmdj("drrj").array();
1562     QVector<RegisterRefValueDescription> result;
1563 
1564     for (const QJsonValue value : registerRefArray) {
1565         QJsonObject regRefObject = value.toObject();
1566 
1567         RegisterRefValueDescription desc;
1568         desc.name = regRefObject[RJsonKey::reg].toString();
1569         desc.value = regRefObject[RJsonKey::value].toString();
1570         desc.ref = regRefObject[RJsonKey::ref].toString();
1571 
1572         result.push_back(desc);
1573     }
1574     return result;
1575 }
1576 
getRegisterName(QString registerRole)1577 QString CutterCore::getRegisterName(QString registerRole)
1578 {
1579     return cmdRaw("drn " + registerRole).trimmed();
1580 }
1581 
getProgramCounterValue()1582 RVA CutterCore::getProgramCounterValue()
1583 {
1584     bool ok;
1585     if (currentlyDebugging) {
1586         // Use cmd because cmdRaw would not work with inner command backticked
1587         // TODO: Risky command due to changes in API, search for something safer
1588         RVA addr = cmd("dr `drn PC`").toULongLong(&ok, 16);
1589         if (ok) {
1590             return addr;
1591         }
1592     }
1593     return RVA_INVALID;
1594 }
1595 
setRegister(QString regName,QString regValue)1596 void CutterCore::setRegister(QString regName, QString regValue)
1597 {
1598     cmdRaw(QString("dr %1=%2").arg(regName).arg(regValue));
1599     emit registersChanged();
1600     emit refreshCodeViews();
1601 }
1602 
setCurrentDebugThread(int tid)1603 void CutterCore::setCurrentDebugThread(int tid)
1604 {
1605     if (!asyncCmd("dpt=" + QString::number(tid), debugTask)) {
1606         return;
1607     }
1608 
1609     emit debugTaskStateChanged();
1610     connect(debugTask.data(), &R2Task::finished, this, [this] () {
1611         debugTask.clear();
1612         emit registersChanged();
1613         emit refreshCodeViews();
1614         emit stackChanged();
1615         syncAndSeekProgramCounter();
1616         emit switchedThread();
1617         emit debugTaskStateChanged();
1618     });
1619 
1620     debugTask->startTask();
1621 }
1622 
setCurrentDebugProcess(int pid)1623 void CutterCore::setCurrentDebugProcess(int pid)
1624 {
1625     if (!currentlyDebugging || !asyncCmd("dp=" + QString::number(pid), debugTask)) {
1626         return;
1627     }
1628 
1629     emit debugTaskStateChanged();
1630     connect(debugTask.data(), &R2Task::finished, this, [this] () {
1631         debugTask.clear();
1632         emit registersChanged();
1633         emit refreshCodeViews();
1634         emit stackChanged();
1635         emit flagsChanged();
1636         syncAndSeekProgramCounter();
1637         emit switchedProcess();
1638         emit debugTaskStateChanged();
1639     });
1640 
1641     debugTask->startTask();
1642 }
1643 
startDebug()1644 void CutterCore::startDebug()
1645 {
1646     if (!currentlyDebugging) {
1647         offsetPriorDebugging = getOffset();
1648     }
1649     currentlyOpenFile = getConfig("file.path");
1650 
1651     if (!asyncCmd("ood", debugTask)) {
1652         return;
1653     }
1654 
1655     emit debugTaskStateChanged();
1656 
1657     connect(debugTask.data(), &R2Task::finished, this, [this] () {
1658         if (debugTaskDialog) {
1659             delete debugTaskDialog;
1660         }
1661         debugTask.clear();
1662 
1663         emit registersChanged();
1664         if (!currentlyDebugging) {
1665             setConfig("asm.flags", false);
1666             currentlyDebugging = true;
1667             emit toggleDebugView();
1668             emit refreshCodeViews();
1669         }
1670 
1671         emit codeRebased();
1672         emit stackChanged();
1673         emit debugTaskStateChanged();
1674     });
1675 
1676     debugTaskDialog = new R2TaskDialog(debugTask);
1677     debugTaskDialog->setBreakOnClose(true);
1678     debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
1679     debugTaskDialog->setDesc(tr("Starting native debug..."));
1680     debugTaskDialog->show();
1681 
1682     debugTask->startTask();
1683 }
1684 
startEmulation()1685 void CutterCore::startEmulation()
1686 {
1687     if (!currentlyDebugging) {
1688         offsetPriorDebugging = getOffset();
1689     }
1690 
1691     // clear registers, init esil state, stack, progcounter at current seek
1692     asyncCmd("aei; aeim; aeip", debugTask);
1693 
1694     emit debugTaskStateChanged();
1695 
1696     connect(debugTask.data(), &R2Task::finished, this, [this] () {
1697         if (debugTaskDialog) {
1698             delete debugTaskDialog;
1699         }
1700         debugTask.clear();
1701 
1702         if (!currentlyDebugging || !currentlyEmulating) {
1703             // prevent register flags from appearing during debug/emul
1704             setConfig("asm.flags", false);
1705             // allows to view self-modifying code changes or other binary changes
1706             setConfig("io.cache", true);
1707             currentlyDebugging = true;
1708             currentlyEmulating = true;
1709             emit toggleDebugView();
1710         }
1711 
1712         emit registersChanged();
1713         emit stackChanged();
1714         emit codeRebased();
1715         emit refreshCodeViews();
1716         emit debugTaskStateChanged();
1717     });
1718 
1719     debugTaskDialog = new R2TaskDialog(debugTask);
1720     debugTaskDialog->setBreakOnClose(true);
1721     debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
1722     debugTaskDialog->setDesc(tr("Starting emulation..."));
1723     debugTaskDialog->show();
1724 
1725     debugTask->startTask();
1726 }
1727 
attachRemote(const QString & uri)1728 void CutterCore::attachRemote(const QString &uri)
1729 {
1730     if (!currentlyDebugging) {
1731         offsetPriorDebugging = getOffset();
1732     }
1733 
1734     // connect to a debugger with the given plugin
1735     asyncCmd("e cfg.debug = true; oodf " + uri, debugTask);
1736     emit debugTaskStateChanged();
1737 
1738     connect(debugTask.data(), &R2Task::finished, this, [this, uri] () {
1739         if (debugTaskDialog) {
1740             delete debugTaskDialog;
1741         }
1742         debugTask.clear();
1743         // Check if we actually connected
1744         bool connected = false;
1745         QJsonArray openFilesArray = getOpenedFiles();
1746         for (QJsonValue value : openFilesArray) {
1747             QJsonObject openFile = value.toObject();
1748             QString fileUri= openFile["uri"].toString();
1749             if (!fileUri.compare(uri)) {
1750                 connected = true;
1751             }
1752         }
1753         seekAndShow(getProgramCounterValue());
1754         if (!connected) {
1755             emit attachedRemote(false);
1756             emit debugTaskStateChanged();
1757             return;
1758         }
1759 
1760         emit registersChanged();
1761         if (!currentlyDebugging || !currentlyEmulating) {
1762             // prevent register flags from appearing during debug/emul
1763             setConfig("asm.flags", false);
1764             currentlyDebugging = true;
1765             emit toggleDebugView();
1766         }
1767 
1768         emit codeRebased();
1769         emit attachedRemote(true);
1770         emit debugTaskStateChanged();
1771     });
1772 
1773     debugTaskDialog = new R2TaskDialog(debugTask);
1774     debugTaskDialog->setBreakOnClose(true);
1775     debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
1776     debugTaskDialog->setDesc(tr("Connecting to: ") + uri);
1777     debugTaskDialog->show();
1778 
1779     debugTask->startTask();
1780 }
1781 
attachDebug(int pid)1782 void CutterCore::attachDebug(int pid)
1783 {
1784     if (!currentlyDebugging) {
1785         offsetPriorDebugging = getOffset();
1786     }
1787 
1788     // attach to process with dbg plugin
1789     asyncCmd("e cfg.debug = true; oodf dbg://" + QString::number(pid), debugTask);
1790     emit debugTaskStateChanged();
1791 
1792     connect(debugTask.data(), &R2Task::finished, this, [this, pid] () {
1793         if (debugTaskDialog) {
1794             delete debugTaskDialog;
1795         }
1796         debugTask.clear();
1797 
1798         syncAndSeekProgramCounter();
1799         if (!currentlyDebugging || !currentlyEmulating) {
1800             // prevent register flags from appearing during debug/emul
1801             setConfig("asm.flags", false);
1802             currentlyDebugging = true;
1803             currentlyOpenFile = getConfig("file.path");
1804             currentlyAttachedToPID = pid;
1805             emit toggleDebugView();
1806         }
1807 
1808         emit codeRebased();
1809         emit debugTaskStateChanged();
1810     });
1811 
1812     debugTaskDialog = new R2TaskDialog(debugTask);
1813     debugTaskDialog->setBreakOnClose(true);
1814     debugTaskDialog->setAttribute(Qt::WA_DeleteOnClose);
1815     debugTaskDialog->setDesc(tr("Attaching to process (") + QString::number(pid) + ")...");
1816     debugTaskDialog->show();
1817 
1818     debugTask->startTask();
1819 }
1820 
suspendDebug()1821 void CutterCore::suspendDebug()
1822 {
1823     debugTask->breakTask();
1824 }
1825 
stopDebug()1826 void CutterCore::stopDebug()
1827 {
1828     if (!currentlyDebugging) {
1829         return;
1830     }
1831 
1832     if (!debugTask.isNull()) {
1833         suspendDebug();
1834     }
1835 
1836     currentlyDebugging = false;
1837     emit debugTaskStateChanged();
1838 
1839     if (currentlyEmulating) {
1840         cmdEsil("aeim-; aei-; wcr; .ar-");
1841         currentlyEmulating = false;
1842     } else if (currentlyAttachedToPID != -1) {
1843         // Use cmd because cmdRaw would not work with command concatenation
1844         cmd(QString("dp- %1; o %2; .ar-").arg(
1845             QString::number(currentlyAttachedToPID), currentlyOpenFile));
1846         currentlyAttachedToPID = -1;
1847     } else {
1848         QString ptraceFiles = "";
1849         // close ptrace file descriptors left open
1850         QJsonArray openFilesArray = cmdj("oj").array();;
1851         for (QJsonValue value : openFilesArray) {
1852             QJsonObject openFile = value.toObject();
1853             QString URI = openFile["uri"].toString();
1854             if (URI.contains("ptrace")) {
1855                 ptraceFiles += "o-" + QString::number(openFile["fd"].toInt()) + ";";
1856             }
1857         }
1858         // Use cmd because cmdRaw would not work with command concatenation
1859         cmd("doc" + ptraceFiles);
1860     }
1861 
1862     syncAndSeekProgramCounter();
1863     setConfig("asm.flags", true);
1864     setConfig("io.cache", false);
1865     emit codeRebased();
1866     emit toggleDebugView();
1867     offsetPriorDebugging = getOffset();
1868     emit debugTaskStateChanged();
1869 }
1870 
syncAndSeekProgramCounter()1871 void CutterCore::syncAndSeekProgramCounter()
1872 {
1873     seekAndShow(getProgramCounterValue());
1874     emit registersChanged();
1875 }
1876 
continueDebug()1877 void CutterCore::continueDebug()
1878 {
1879     if (!currentlyDebugging) {
1880         return;
1881     }
1882 
1883     if (currentlyEmulating) {
1884         if (!asyncCmdEsil("aec", debugTask)) {
1885             return;
1886         }
1887     } else {
1888         if (!asyncCmd("dc", debugTask)) {
1889             return;
1890         }
1891     }
1892 
1893     emit debugTaskStateChanged();
1894     connect(debugTask.data(), &R2Task::finished, this, [this] () {
1895         debugTask.clear();
1896         syncAndSeekProgramCounter();
1897         emit registersChanged();
1898         emit refreshCodeViews();
1899         emit debugTaskStateChanged();
1900     });
1901 
1902     debugTask->startTask();
1903 }
1904 
continueUntilDebug(QString offset)1905 void CutterCore::continueUntilDebug(QString offset)
1906 {
1907     if (!currentlyDebugging) {
1908         return;
1909     }
1910 
1911     if (currentlyEmulating) {
1912         if (!asyncCmdEsil("aecu " + offset, debugTask)) {
1913             return;
1914         }
1915     } else {
1916         if (!asyncCmd("dcu " + offset, debugTask)) {
1917             return;
1918         }
1919     }
1920 
1921     emit debugTaskStateChanged();
1922     connect(debugTask.data(), &R2Task::finished, this, [this] () {
1923         debugTask.clear();
1924         syncAndSeekProgramCounter();
1925         emit registersChanged();
1926         emit stackChanged();
1927         emit refreshCodeViews();
1928         emit debugTaskStateChanged();
1929     });
1930 
1931     debugTask->startTask();
1932 }
1933 
continueUntilCall()1934 void CutterCore::continueUntilCall()
1935 {
1936     if (!currentlyDebugging) {
1937         return;
1938     }
1939 
1940     if (currentlyEmulating) {
1941         if (!asyncCmdEsil("aecc", debugTask)) {
1942             return;
1943         }
1944     } else {
1945         if (!asyncCmd("dcc", debugTask)) {
1946             return;
1947         }
1948     }
1949 
1950     emit debugTaskStateChanged();
1951     connect(debugTask.data(), &R2Task::finished, this, [this] () {
1952         debugTask.clear();
1953         syncAndSeekProgramCounter();
1954         emit debugTaskStateChanged();
1955     });
1956 
1957     debugTask->startTask();
1958 }
1959 
continueUntilSyscall()1960 void CutterCore::continueUntilSyscall()
1961 {
1962     if (!currentlyDebugging) {
1963         return;
1964     }
1965 
1966     if (currentlyEmulating) {
1967         if (!asyncCmdEsil("aecs", debugTask)) {
1968             return;
1969         }
1970     } else {
1971         if (!asyncCmd("dcs", debugTask)) {
1972             return;
1973         }
1974     }
1975 
1976     emit debugTaskStateChanged();
1977     connect(debugTask.data(), &R2Task::finished, this, [this] () {
1978         debugTask.clear();
1979         syncAndSeekProgramCounter();
1980         emit debugTaskStateChanged();
1981     });
1982 
1983     debugTask->startTask();
1984 }
1985 
stepDebug()1986 void CutterCore::stepDebug()
1987 {
1988     if (!currentlyDebugging) {
1989         return;
1990     }
1991 
1992     if (currentlyEmulating) {
1993         if (!asyncCmdEsil("aes", debugTask)) {
1994             return;
1995         }
1996     } else {
1997         if (!asyncCmd("ds", debugTask)) {
1998             return;
1999         }
2000     }
2001 
2002     emit debugTaskStateChanged();
2003     connect(debugTask.data(), &R2Task::finished, this, [this] () {
2004         debugTask.clear();
2005         syncAndSeekProgramCounter();
2006         emit debugTaskStateChanged();
2007     });
2008 
2009     debugTask->startTask();
2010 }
2011 
stepOverDebug()2012 void CutterCore::stepOverDebug()
2013 {
2014     if (!currentlyDebugging) {
2015         return;
2016     }
2017 
2018     if (currentlyEmulating) {
2019         if (!asyncCmdEsil("aeso", debugTask)) {
2020             return;
2021         }
2022     } else {
2023         if (!asyncCmd("dso", debugTask)) {
2024             return;
2025         }
2026     }
2027 
2028     emit debugTaskStateChanged();
2029     connect(debugTask.data(), &R2Task::finished, this, [this] () {
2030         debugTask.clear();
2031         syncAndSeekProgramCounter();
2032         emit debugTaskStateChanged();
2033     });
2034 
2035     debugTask->startTask();
2036 }
2037 
stepOutDebug()2038 void CutterCore::stepOutDebug()
2039 {
2040     if (!currentlyDebugging) {
2041         return;
2042     }
2043 
2044     emit debugTaskStateChanged();
2045     if (!asyncCmd("dsf", debugTask)) {
2046         return;
2047     }
2048 
2049     connect(debugTask.data(), &R2Task::finished, this, [this] () {
2050         debugTask.clear();
2051         syncAndSeekProgramCounter();
2052         emit debugTaskStateChanged();
2053     });
2054 
2055     debugTask->startTask();
2056 }
2057 
getDebugPlugins()2058 QStringList CutterCore::getDebugPlugins()
2059 {
2060     QStringList plugins;
2061     QJsonArray pluginArray = cmdj("dLj").array();
2062 
2063     for (const QJsonValue &value : pluginArray) {
2064         QJsonObject pluginObject = value.toObject();
2065 
2066         QString plugin = pluginObject[RJsonKey::name].toString();
2067 
2068         plugins << plugin;
2069     }
2070     return plugins;
2071 }
2072 
getActiveDebugPlugin()2073 QString CutterCore::getActiveDebugPlugin()
2074 {
2075     return getConfig("dbg.backend");
2076 }
2077 
setDebugPlugin(QString plugin)2078 void CutterCore::setDebugPlugin(QString plugin)
2079 {
2080     setConfig("dbg.backend", plugin);
2081 }
2082 
toggleBreakpoint(RVA addr)2083 void CutterCore::toggleBreakpoint(RVA addr)
2084 {
2085     cmdRaw(QString("dbs %1").arg(addr));
2086     emit breakpointsChanged(addr);
2087 }
2088 
addBreakpoint(const BreakpointDescription & config)2089 void CutterCore::addBreakpoint(const BreakpointDescription &config)
2090 {
2091     CORE_LOCK();
2092     RBreakpointItem *breakpoint = nullptr;
2093     int watchpoint_prot = 0;
2094     if (config.hw) {
2095         watchpoint_prot = config.permission & ~(R_BP_PROT_EXEC);
2096     }
2097 
2098     auto address = config.addr;
2099     char *module = nullptr;
2100     QByteArray moduleNameData;
2101     if (config.type == BreakpointDescription::Named) {
2102         address = Core()->math(config.positionExpression);
2103     } else if (config.type == BreakpointDescription::Module) {
2104         address = 0;
2105         moduleNameData = config.positionExpression.toUtf8();
2106         module = moduleNameData.data();
2107     }
2108     breakpoint = r_debug_bp_add(core->dbg, address, (config.hw && watchpoint_prot == 0),
2109                                 watchpoint_prot, watchpoint_prot,
2110                                 module, config.moduleDelta);
2111     if (!breakpoint) {
2112         QMessageBox::critical(nullptr, tr("Breakpoint error"), tr("Failed to create breakpoint"));
2113         return;
2114     }
2115     if (config.type == BreakpointDescription::Named) {
2116         updateOwnedCharPtr(breakpoint->expr, config.positionExpression);
2117     }
2118 
2119     if (config.hw) {
2120         breakpoint->size = config.size;
2121     }
2122     if (config.type == BreakpointDescription::Named) {
2123         updateOwnedCharPtr(breakpoint->name, config.positionExpression);
2124     }
2125 
2126     int index = std::find(core->dbg->bp->bps_idx,
2127                           core->dbg->bp->bps_idx + core->dbg->bp->bps_idx_count,
2128                           breakpoint) - core->dbg->bp->bps_idx;
2129 
2130     breakpoint->enabled = config.enabled;
2131     if (config.trace) {
2132         setBreakpointTrace(index, config.trace);
2133     }
2134     if (!config.condition.isEmpty()) {
2135         updateOwnedCharPtr(breakpoint->cond, config.condition);
2136     }
2137     if (!config.command.isEmpty()) {
2138         updateOwnedCharPtr(breakpoint->data, config.command);
2139     }
2140     emit breakpointsChanged(breakpoint->addr);
2141 }
2142 
updateBreakpoint(int index,const BreakpointDescription & config)2143 void CutterCore::updateBreakpoint(int index, const BreakpointDescription &config)
2144 {
2145     CORE_LOCK();
2146     if (auto bp = r_bp_get_index(core->dbg->bp, index)) {
2147         r_bp_del(core->dbg->bp, bp->addr);
2148     }
2149     // Delete by index currently buggy,
2150     // required for breakpoints with non address based position
2151     //r_bp_del_index(core->dbg->bp, index);
2152     addBreakpoint(config);
2153 }
2154 
delBreakpoint(RVA addr)2155 void CutterCore::delBreakpoint(RVA addr)
2156 {
2157     cmdRaw("db- " + RAddressString(addr));
2158     emit breakpointsChanged(addr);
2159 }
2160 
delAllBreakpoints()2161 void CutterCore::delAllBreakpoints()
2162 {
2163     cmdRaw("db-*");
2164     emit refreshCodeViews();
2165 }
2166 
enableBreakpoint(RVA addr)2167 void CutterCore::enableBreakpoint(RVA addr)
2168 {
2169     cmdRaw("dbe " + RAddressString(addr));
2170     emit breakpointsChanged(addr);
2171 }
2172 
disableBreakpoint(RVA addr)2173 void CutterCore::disableBreakpoint(RVA addr)
2174 {
2175     cmdRaw("dbd " + RAddressString(addr));
2176     emit breakpointsChanged(addr);
2177 }
2178 
setBreakpointTrace(int index,bool enabled)2179 void CutterCore::setBreakpointTrace(int index, bool enabled)
2180 {
2181     if (enabled) {
2182         cmdRaw(QString("dbite %1").arg(index));
2183     } else {
2184         cmdRaw(QString("dbitd %1").arg(index));
2185     }
2186 }
2187 
breakpointDescriptionFromR2(int index,r_bp_item_t * bpi)2188 static BreakpointDescription breakpointDescriptionFromR2(int index, r_bp_item_t *bpi)
2189 {
2190     BreakpointDescription bp;
2191     bp.addr = bpi->addr;
2192     bp.index = index;
2193     bp.size = bpi->size;
2194     if (bpi->expr) {
2195         bp.positionExpression = bpi->expr;
2196         bp.type = BreakpointDescription::Named;
2197     }
2198     bp.name = bpi->name;
2199     bp.permission = bpi->perm;
2200     bp.command = bpi->data;
2201     bp.condition = bpi->cond;
2202     bp.hw = bpi->hw;
2203     bp.trace = bpi->trace;
2204     bp.enabled = bpi->enabled;
2205     return bp;
2206 }
2207 
breakpointIndexAt(RVA addr)2208 int CutterCore::breakpointIndexAt(RVA addr)
2209 {
2210     CORE_LOCK();
2211     return r_bp_get_index_at(core->dbg->bp, addr);
2212 }
2213 
getBreakpointAt(RVA addr)2214 BreakpointDescription CutterCore::getBreakpointAt(RVA addr)
2215 {
2216     CORE_LOCK();
2217     int index = breakpointIndexAt(addr);
2218     auto bp = r_bp_get_index(core->dbg->bp, index);
2219     if (bp) {
2220         return breakpointDescriptionFromR2(index, bp);
2221     }
2222     return BreakpointDescription();
2223 }
2224 
getBreakpoints()2225 QList<BreakpointDescription> CutterCore::getBreakpoints()
2226 {
2227     CORE_LOCK();
2228     QList<BreakpointDescription> ret;
2229     //TODO: use higher level API, don't touch r2 bps_idx directly
2230     for (int i = 0; i < core->dbg->bp->bps_idx_count; i++) {
2231         if (auto bpi = core->dbg->bp->bps_idx[i]) {
2232             ret.push_back(breakpointDescriptionFromR2(i, bpi));
2233         }
2234     }
2235 
2236     return ret;
2237 }
2238 
2239 
getBreakpointsAddresses()2240 QList<RVA> CutterCore::getBreakpointsAddresses()
2241 {
2242     QList<RVA> bpAddresses;
2243     for (const BreakpointDescription &bp : getBreakpoints()) {
2244         bpAddresses << bp.addr;
2245     }
2246 
2247     return bpAddresses;
2248 }
2249 
getBreakpointsInFunction(RVA funcAddr)2250 QList<RVA> CutterCore::getBreakpointsInFunction(RVA funcAddr)
2251 {
2252     QList<RVA> allBreakpoints = getBreakpointsAddresses();
2253     QList<RVA> functionBreakpoints;
2254 
2255     // Use std manipulations to take only the breakpoints that belong to this function
2256     std::copy_if(allBreakpoints.begin(),
2257              allBreakpoints.end(),
2258              std::back_inserter(functionBreakpoints),
2259              [this, funcAddr](RVA BPadd) { return getFunctionStart(BPadd) == funcAddr; });
2260     return functionBreakpoints;
2261 }
2262 
isBreakpoint(const QList<RVA> & breakpoints,RVA addr)2263 bool CutterCore::isBreakpoint(const QList<RVA> &breakpoints, RVA addr)
2264 {
2265     return breakpoints.contains(addr);
2266 }
2267 
getBacktrace()2268 QJsonDocument CutterCore::getBacktrace()
2269 {
2270     return cmdj("dbtj");
2271 }
2272 
getAllProcesses()2273 QList<ProcessDescription> CutterCore::getAllProcesses()
2274 {
2275     QList<ProcessDescription> ret;
2276     QJsonArray processArray = cmdj("dplj").array();
2277 
2278     for (const QJsonValue &value : processArray) {
2279         QJsonObject procObject = value.toObject();
2280 
2281         ProcessDescription proc;
2282 
2283         proc.pid = procObject[RJsonKey::pid].toInt();
2284         proc.uid = procObject[RJsonKey::uid].toInt();
2285         proc.status = procObject[RJsonKey::status].toString();
2286         proc.path = procObject[RJsonKey::path].toString();
2287 
2288         ret << proc;
2289     }
2290 
2291     return ret;
2292 }
2293 
getMemoryMap()2294 QList<MemoryMapDescription> CutterCore::getMemoryMap()
2295 {
2296     QList<MemoryMapDescription> ret;
2297     QJsonArray memoryMapArray = cmdj("dmj").array();
2298 
2299     for (const QJsonValue &value : memoryMapArray) {
2300         QJsonObject memMapObject = value.toObject();
2301 
2302         MemoryMapDescription memMap;
2303 
2304         memMap.name = memMapObject[RJsonKey::name].toString();
2305         memMap.fileName = memMapObject[RJsonKey::file].toString();
2306         memMap.addrStart = memMapObject[RJsonKey::addr].toVariant().toULongLong();
2307         memMap.addrEnd = memMapObject[RJsonKey::addr_end].toVariant().toULongLong();
2308         memMap.type = memMapObject[RJsonKey::type].toString();
2309         memMap.permission = memMapObject[RJsonKey::perm].toString();
2310 
2311         ret << memMap;
2312     }
2313 
2314     return ret;
2315 }
2316 
getStats()2317 QStringList CutterCore::getStats()
2318 {
2319     QStringList stats;
2320     cmdRaw("fs functions");
2321 
2322     // The cmd coomand is frequently used in this function because
2323     // cmdRaw would not work with grep
2324     stats << cmd("f~?").trimmed();
2325 
2326     QString imps = cmd("ii~?").trimmed();
2327     stats << imps;
2328 
2329     cmdRaw("fs symbols");
2330     stats << cmd("f~?").trimmed();
2331     cmdRaw("fs strings");
2332     stats << cmd("f~?").trimmed();
2333     cmdRaw("fs relocs");
2334     stats << cmd("f~?").trimmed();
2335     cmdRaw("fs sections");
2336     stats << cmd("f~?").trimmed();
2337     cmdRaw("fs *");
2338     stats << cmd("f~?").trimmed();
2339 
2340     return stats;
2341 }
2342 
setGraphEmpty(bool empty)2343 void CutterCore::setGraphEmpty(bool empty)
2344 {
2345     emptyGraph = empty;
2346 }
2347 
isGraphEmpty()2348 bool CutterCore::isGraphEmpty()
2349 {
2350     return emptyGraph;
2351 }
2352 
getOpcodes()2353 void CutterCore::getOpcodes()
2354 {
2355     this->opcodes = cmdList("?O");
2356     this->regs = cmdList("drp~[1]");
2357 }
2358 
setSettings()2359 void CutterCore::setSettings()
2360 {
2361     setConfig("scr.interactive", false);
2362 
2363     setConfig("hex.pairs", false);
2364     setConfig("asm.xrefs", false);
2365 
2366     setConfig("asm.tabs.once", true);
2367     setConfig("asm.flags.middle", 2);
2368 
2369     setConfig("anal.hasnext", false);
2370     setConfig("asm.lines.call", false);
2371 
2372     setConfig("cfg.fortunes.tts", false);
2373 
2374     // Colors
2375     setConfig("scr.color", COLOR_MODE_DISABLED);
2376 
2377     // Don't show hits
2378     setConfig("search.flags", false);
2379 }
2380 
getSeekHistory()2381 QList<RVA> CutterCore::getSeekHistory()
2382 {
2383     CORE_LOCK();
2384     QList<RVA> ret;
2385 
2386     QJsonArray jsonArray = cmdj("sj").array();
2387     for (const QJsonValue &value : jsonArray)
2388         ret << value.toVariant().toULongLong();
2389 
2390     return ret;
2391 }
2392 
getAsmPluginNames()2393 QStringList CutterCore::getAsmPluginNames()
2394 {
2395     CORE_LOCK();
2396     RListIter *it;
2397     QStringList ret;
2398 
2399     RAsmPlugin *ap;
2400     CutterRListForeach(core->rasm->plugins, it, RAsmPlugin, ap) {
2401         ret << ap->name;
2402     }
2403 
2404     return ret;
2405 }
2406 
getAnalPluginNames()2407 QStringList CutterCore::getAnalPluginNames()
2408 {
2409     CORE_LOCK();
2410     RListIter *it;
2411     QStringList ret;
2412 
2413     RAnalPlugin *ap;
2414     CutterRListForeach(core->anal->plugins, it, RAnalPlugin, ap) {
2415         ret << ap->name;
2416     }
2417 
2418     return ret;
2419 }
2420 
getProjectNames()2421 QStringList CutterCore::getProjectNames()
2422 {
2423     CORE_LOCK();
2424     QStringList ret;
2425 
2426     QJsonArray jsonArray = cmdj("Pj").array();
2427     for (const QJsonValue &value : jsonArray)
2428         ret.append(value.toString());
2429 
2430     return ret;
2431 }
2432 
getRBinPluginDescriptions(const QString & type)2433 QList<RBinPluginDescription> CutterCore::getRBinPluginDescriptions(const QString &type)
2434 {
2435     QList<RBinPluginDescription> ret;
2436 
2437     QJsonObject jsonRoot = cmdj("iLj").object();
2438     for (const QString &key : jsonRoot.keys()) {
2439         if (!type.isNull() && key != type)
2440             continue;
2441 
2442         QJsonArray pluginArray = jsonRoot[key].toArray();
2443 
2444         for (const QJsonValue &pluginValue : pluginArray) {
2445             QJsonObject pluginObject = pluginValue.toObject();
2446 
2447             RBinPluginDescription desc;
2448 
2449             desc.name = pluginObject[RJsonKey::name].toString();
2450             desc.description = pluginObject[RJsonKey::description].toString();
2451             desc.license = pluginObject[RJsonKey::license].toString();
2452             desc.type = key;
2453 
2454             ret.append(desc);
2455         }
2456     }
2457 
2458     return ret;
2459 }
2460 
getRIOPluginDescriptions()2461 QList<RIOPluginDescription> CutterCore::getRIOPluginDescriptions()
2462 {
2463     QList<RIOPluginDescription> ret;
2464 
2465     QJsonArray plugins = cmdj("oLj").object()["io_plugins"].toArray();
2466     for (const QJsonValue &pluginValue : plugins) {
2467         QJsonObject pluginObject = pluginValue.toObject();
2468 
2469         RIOPluginDescription plugin;
2470 
2471         plugin.name = pluginObject["name"].toString();
2472         plugin.description = pluginObject["description"].toString();
2473         plugin.license = pluginObject["license"].toString();
2474         plugin.permissions = pluginObject["permissions"].toString();
2475         for (const auto &uri : pluginObject["uris"].toArray()) {
2476             plugin.uris << uri.toString();
2477         }
2478 
2479         ret << plugin;
2480     }
2481 
2482     return ret;
2483 }
2484 
getRCorePluginDescriptions()2485 QList<RCorePluginDescription> CutterCore::getRCorePluginDescriptions()
2486 {
2487     QList<RCorePluginDescription> ret;
2488 
2489     QJsonArray plugins = cmdj("Lcj").array();
2490     for (const QJsonValue &pluginValue : plugins) {
2491         QJsonObject pluginObject = pluginValue.toObject();
2492 
2493         RCorePluginDescription plugin;
2494 
2495         plugin.name = pluginObject["Name"].toString();
2496         plugin.description = pluginObject["Description"].toString();
2497 
2498         ret << plugin;
2499     }
2500 
2501     return ret;
2502 }
2503 
getRAsmPluginDescriptions()2504 QList<RAsmPluginDescription> CutterCore::getRAsmPluginDescriptions()
2505 {
2506     CORE_LOCK();
2507     RListIter *it;
2508     QList<RAsmPluginDescription> ret;
2509 
2510     RAsmPlugin *ap;
2511     CutterRListForeach(core->rasm->plugins, it, RAsmPlugin, ap) {
2512         RAsmPluginDescription plugin;
2513 
2514         plugin.name = ap->name;
2515         plugin.architecture = ap->arch;
2516         plugin.author = ap->author;
2517         plugin.version = ap->version;
2518         plugin.cpus = ap->cpus;
2519         plugin.description = ap->desc;
2520         plugin.license = ap->license;
2521 
2522         ret << plugin;
2523     }
2524 
2525     return ret;
2526 }
2527 
getAllFunctions()2528 QList<FunctionDescription> CutterCore::getAllFunctions()
2529 {
2530     CORE_LOCK();
2531 
2532     QList<FunctionDescription> funcList;
2533     funcList.reserve(r_list_length(core->anal->fcns));
2534 
2535     RListIter *iter;
2536     RAnalFunction *fcn;
2537     CutterRListForeach (core->anal->fcns, iter, RAnalFunction, fcn) {
2538         FunctionDescription function;
2539         function.offset = fcn->addr;
2540         function.linearSize = r_anal_function_linear_size(fcn);
2541         function.nargs = r_anal_var_count(core->anal, fcn, 'b', 1) +
2542             r_anal_var_count(core->anal, fcn, 'r', 1) +
2543             r_anal_var_count(core->anal, fcn, 's', 1);
2544         function.nlocals = r_anal_var_count(core->anal, fcn, 'b', 0) +
2545             r_anal_var_count(core->anal, fcn, 'r', 0) +
2546             r_anal_var_count(core->anal, fcn, 's', 0);
2547         function.nbbs = r_list_length (fcn->bbs);
2548         function.calltype = fcn->cc ? QString::fromUtf8(fcn->cc) : QString();
2549         function.name = fcn->name ? QString::fromUtf8(fcn->name) : QString();
2550         function.edges = r_anal_function_count_edges(fcn, nullptr);
2551         function.stackframe = fcn->maxstack;
2552         funcList.append(function);
2553     }
2554 
2555     return funcList;
2556 }
2557 
getAllImports()2558 QList<ImportDescription> CutterCore::getAllImports()
2559 {
2560     CORE_LOCK();
2561     QList<ImportDescription> ret;
2562 
2563     QJsonArray importsArray = cmdj("iij").array();
2564 
2565     for (const QJsonValue &value : importsArray) {
2566         QJsonObject importObject = value.toObject();
2567 
2568         ImportDescription import;
2569 
2570         import.plt = importObject[RJsonKey::plt].toVariant().toULongLong();
2571         import.ordinal = importObject[RJsonKey::ordinal].toInt();
2572         import.bind = importObject[RJsonKey::bind].toString();
2573         import.type = importObject[RJsonKey::type].toString();
2574         import.libname = importObject[RJsonKey::libname].toString();
2575         import.name = importObject[RJsonKey::name].toString();
2576 
2577         ret << import;
2578     }
2579 
2580     return ret;
2581 }
2582 
getAllExports()2583 QList<ExportDescription> CutterCore::getAllExports()
2584 {
2585     CORE_LOCK();
2586     QList<ExportDescription> ret;
2587 
2588     QJsonArray exportsArray = cmdj("iEj").array();
2589 
2590     for (const QJsonValue &value : exportsArray) {
2591         QJsonObject exportObject = value.toObject();
2592 
2593         ExportDescription exp;
2594 
2595         exp.vaddr = exportObject[RJsonKey::vaddr].toVariant().toULongLong();
2596         exp.paddr = exportObject[RJsonKey::paddr].toVariant().toULongLong();
2597         exp.size = exportObject[RJsonKey::size].toVariant().toULongLong();
2598         exp.type = exportObject[RJsonKey::type].toString();
2599         exp.name = exportObject[RJsonKey::name].toString();
2600         exp.flag_name = exportObject[RJsonKey::flagname].toString();
2601 
2602         ret << exp;
2603     }
2604 
2605     return ret;
2606 }
2607 
getAllSymbols()2608 QList<SymbolDescription> CutterCore::getAllSymbols()
2609 {
2610     CORE_LOCK();
2611     RListIter *it;
2612 
2613     QList<SymbolDescription> ret;
2614 
2615     RBinSymbol *bs;
2616     if (core && core->bin && core->bin->cur && core->bin->cur->o) {
2617         CutterRListForeach(core->bin->cur->o->symbols, it, RBinSymbol, bs) {
2618             QString type = QString(bs->bind) + " " + QString(bs->type);
2619             SymbolDescription symbol;
2620             symbol.vaddr = bs->vaddr;
2621             symbol.name = QString(bs->name);
2622             symbol.bind = QString(bs->bind);
2623             symbol.type = QString(bs->type);
2624             ret << symbol;
2625         }
2626 
2627         /* list entrypoints as symbols too */
2628         int n = 0;
2629         RBinAddr *entry;
2630         CutterRListForeach(core->bin->cur->o->entries, it, RBinAddr, entry) {
2631             SymbolDescription symbol;
2632             symbol.vaddr = entry->vaddr;
2633             symbol.name = QString("entry") + QString::number(n++);
2634             symbol.bind.clear();
2635             symbol.type = "entry";
2636             ret << symbol;
2637         }
2638     }
2639 
2640     return ret;
2641 }
2642 
getAllHeaders()2643 QList<HeaderDescription> CutterCore::getAllHeaders()
2644 {
2645     CORE_LOCK();
2646     QList<HeaderDescription> ret;
2647 
2648     QJsonArray headersArray = cmdj("ihj").array();
2649 
2650     for (const QJsonValue &value : headersArray) {
2651         QJsonObject headerObject = value.toObject();
2652 
2653         HeaderDescription header;
2654 
2655         header.vaddr = headerObject[RJsonKey::vaddr].toVariant().toULongLong();
2656         header.paddr = headerObject[RJsonKey::paddr].toVariant().toULongLong();
2657         header.value = headerObject[RJsonKey::comment].toString();
2658         header.name = headerObject[RJsonKey::name].toString();
2659 
2660         ret << header;
2661     }
2662 
2663     return ret;
2664 }
2665 
getAllZignatures()2666 QList<ZignatureDescription> CutterCore::getAllZignatures()
2667 {
2668     CORE_LOCK();
2669     QList<ZignatureDescription> zignatures;
2670 
2671     QJsonArray zignaturesArray = cmdj("zj").array();
2672 
2673     for (const QJsonValue &value : zignaturesArray) {
2674         QJsonObject zignatureObject = value.toObject();
2675 
2676         ZignatureDescription zignature;
2677 
2678         zignature.name = zignatureObject[RJsonKey::name].toString();
2679         zignature.bytes = zignatureObject[RJsonKey::bytes].toString();
2680         zignature.offset = zignatureObject[RJsonKey::offset].toVariant().toULongLong();
2681         for (const QJsonValue &ref : zignatureObject[RJsonKey::refs].toArray()) {
2682             zignature.refs << ref.toString();
2683         }
2684 
2685         QJsonObject graphObject = zignatureObject[RJsonKey::graph].toObject();
2686         zignature.cc = graphObject[RJsonKey::cc].toVariant().toULongLong();
2687         zignature.nbbs = graphObject[RJsonKey::nbbs].toVariant().toULongLong();
2688         zignature.edges = graphObject[RJsonKey::edges].toVariant().toULongLong();
2689         zignature.ebbs = graphObject[RJsonKey::ebbs].toVariant().toULongLong();
2690 
2691         zignatures << zignature;
2692     }
2693 
2694     return zignatures;
2695 }
2696 
getAllComments(const QString & filterType)2697 QList<CommentDescription> CutterCore::getAllComments(const QString &filterType)
2698 {
2699     CORE_LOCK();
2700     QList<CommentDescription> ret;
2701 
2702     QJsonArray commentsArray = cmdj("CCj").array();
2703     for (const QJsonValue &value : commentsArray) {
2704         QJsonObject commentObject = value.toObject();
2705 
2706         QString type = commentObject[RJsonKey::type].toString();
2707         if (type != filterType)
2708             continue;
2709 
2710         CommentDescription comment;
2711         comment.offset = commentObject[RJsonKey::offset].toVariant().toULongLong();
2712         comment.name = commentObject[RJsonKey::name].toString();
2713 
2714         ret << comment;
2715     }
2716     return ret;
2717 }
2718 
getAllRelocs()2719 QList<RelocDescription> CutterCore::getAllRelocs()
2720 {
2721     CORE_LOCK();
2722     QList<RelocDescription> ret;
2723 
2724     if (core && core->bin && core->bin->cur && core->bin->cur->o) {
2725         auto relocs = core->bin->cur->o->relocs;
2726         RBIter iter;
2727         RBinReloc *br;
2728         r_rbtree_foreach (relocs, iter, br, RBinReloc, vrb) {
2729             RelocDescription reloc;
2730 
2731             reloc.vaddr = br->vaddr;
2732             reloc.paddr = br->paddr;
2733             reloc.type = (br->additive ? "ADD_" : "SET_") + QString::number(br->type);
2734 
2735             if (br->import)
2736                 reloc.name = br->import->name;
2737             else
2738                 reloc.name = QString("reloc_%1").arg(QString::number(br->vaddr, 16));
2739 
2740             ret << reloc;
2741         }
2742     }
2743 
2744     return ret;
2745 }
2746 
getAllStrings()2747 QList<StringDescription> CutterCore::getAllStrings()
2748 {
2749     return parseStringsJson(cmdjTask("izzj"));
2750 }
2751 
parseStringsJson(const QJsonDocument & doc)2752 QList<StringDescription> CutterCore::parseStringsJson(const QJsonDocument &doc)
2753 {
2754     QList<StringDescription> ret;
2755 
2756     QJsonArray stringsArray = doc.array();
2757     for (const QJsonValue &value : stringsArray) {
2758         QJsonObject stringObject = value.toObject();
2759 
2760         StringDescription string;
2761 
2762         string.string = stringObject[RJsonKey::string].toString();
2763         string.vaddr = stringObject[RJsonKey::vaddr].toVariant().toULongLong();
2764         string.type = stringObject[RJsonKey::type].toString();
2765         string.size = stringObject[RJsonKey::size].toVariant().toUInt();
2766         string.length = stringObject[RJsonKey::length].toVariant().toUInt();
2767         string.section = stringObject[RJsonKey::section].toString();
2768 
2769         ret << string;
2770     }
2771 
2772     return ret;
2773 }
2774 
getAllFlagspaces()2775 QList<FlagspaceDescription> CutterCore::getAllFlagspaces()
2776 {
2777     CORE_LOCK();
2778     QList<FlagspaceDescription> ret;
2779 
2780     QJsonArray flagspacesArray = cmdj("fsj").array();
2781     for (const QJsonValue &value : flagspacesArray) {
2782         QJsonObject flagspaceObject = value.toObject();
2783 
2784         FlagspaceDescription flagspace;
2785 
2786         flagspace.name = flagspaceObject[RJsonKey::name].toString();
2787 
2788         ret << flagspace;
2789     }
2790     return ret;
2791 }
2792 
getAllFlags(QString flagspace)2793 QList<FlagDescription> CutterCore::getAllFlags(QString flagspace)
2794 {
2795     CORE_LOCK();
2796     QList<FlagDescription> ret;
2797 
2798     if (!flagspace.isEmpty())
2799         cmdRaw("fs " + flagspace);
2800     else
2801         cmdRaw("fs *");
2802 
2803     QJsonArray flagsArray = cmdj("fj").array();
2804     for (const QJsonValue &value : flagsArray) {
2805         QJsonObject flagObject = value.toObject();
2806 
2807         FlagDescription flag;
2808 
2809         flag.offset = flagObject[RJsonKey::offset].toVariant().toULongLong();
2810         flag.size = flagObject[RJsonKey::size].toVariant().toULongLong();
2811         flag.name = flagObject[RJsonKey::name].toString();
2812         flag.realname = flagObject[RJsonKey::realname].toString();
2813 
2814         ret << flag;
2815     }
2816     return ret;
2817 }
2818 
getAllSections()2819 QList<SectionDescription> CutterCore::getAllSections()
2820 {
2821     CORE_LOCK();
2822     QList<SectionDescription> sections;
2823 
2824     QJsonDocument sectionsDoc = cmdj("iSj entropy");
2825     QJsonObject sectionsObj = sectionsDoc.object();
2826     QJsonArray sectionsArray = sectionsObj[RJsonKey::sections].toArray();
2827 
2828     for (const QJsonValue &value : sectionsArray) {
2829         QJsonObject sectionObject = value.toObject();
2830 
2831         QString name = sectionObject[RJsonKey::name].toString();
2832         if (name.isEmpty())
2833             continue;
2834 
2835         SectionDescription section;
2836         section.name = name;
2837         section.vaddr = sectionObject[RJsonKey::vaddr].toVariant().toULongLong();
2838         section.vsize = sectionObject[RJsonKey::vsize].toVariant().toULongLong();
2839         section.paddr = sectionObject[RJsonKey::paddr].toVariant().toULongLong();
2840         section.size = sectionObject[RJsonKey::size].toVariant().toULongLong();
2841         section.perm = sectionObject[RJsonKey::perm].toString();
2842         section.entropy =  sectionObject[RJsonKey::entropy].toString();
2843 
2844         sections << section;
2845     }
2846     return sections;
2847 }
2848 
getSectionList()2849 QStringList CutterCore::getSectionList()
2850 {
2851     CORE_LOCK();
2852     QStringList ret;
2853 
2854     QJsonArray sectionsArray = cmdj("iSj").array();
2855     for (const QJsonValue &value : sectionsArray) {
2856         ret << value.toObject()[RJsonKey::name].toString();
2857     }
2858     return ret;
2859 }
2860 
getAllSegments()2861 QList<SegmentDescription> CutterCore::getAllSegments()
2862 {
2863     CORE_LOCK();
2864     QList<SegmentDescription> ret;
2865 
2866     QJsonArray segments = cmdj("iSSj").array();
2867 
2868     for (const QJsonValue &value : segments) {
2869         QJsonObject segmentObject = value.toObject();
2870 
2871         QString name = segmentObject[RJsonKey::name].toString();
2872         if (name.isEmpty())
2873             continue;
2874 
2875         SegmentDescription segment;
2876         segment.name = name;
2877         segment.vaddr = segmentObject[RJsonKey::vaddr].toVariant().toULongLong();
2878         segment.paddr = segmentObject[RJsonKey::paddr].toVariant().toULongLong();
2879         segment.size = segmentObject[RJsonKey::size].toVariant().toULongLong();
2880         segment.vsize = segmentObject[RJsonKey::vsize].toVariant().toULongLong();
2881         segment.perm =  segmentObject[RJsonKey::perm].toString();
2882 
2883         ret << segment;
2884     }
2885     return ret;
2886 }
2887 
getAllEntrypoint()2888 QList<EntrypointDescription> CutterCore::getAllEntrypoint()
2889 {
2890     CORE_LOCK();
2891     QList<EntrypointDescription> ret;
2892 
2893     QJsonArray entrypointsArray = cmdj("iej").array();
2894     for (const QJsonValue &value : entrypointsArray) {
2895         QJsonObject entrypointObject = value.toObject();
2896 
2897         EntrypointDescription entrypoint;
2898 
2899         entrypoint.vaddr = entrypointObject[RJsonKey::vaddr].toVariant().toULongLong();
2900         entrypoint.paddr = entrypointObject[RJsonKey::paddr].toVariant().toULongLong();
2901         entrypoint.baddr = entrypointObject[RJsonKey::baddr].toVariant().toULongLong();
2902         entrypoint.laddr = entrypointObject[RJsonKey::laddr].toVariant().toULongLong();
2903         entrypoint.haddr = entrypointObject[RJsonKey::haddr].toVariant().toULongLong();
2904         entrypoint.type = entrypointObject[RJsonKey::type].toString();
2905 
2906         ret << entrypoint;
2907     }
2908     return ret;
2909 }
2910 
getAllClassesFromBin()2911 QList<BinClassDescription> CutterCore::getAllClassesFromBin()
2912 {
2913     CORE_LOCK();
2914     QList<BinClassDescription> ret;
2915 
2916     QJsonArray classesArray = cmdj("icj").array();
2917     for (const QJsonValue &value : classesArray) {
2918         QJsonObject classObject = value.toObject();
2919 
2920         BinClassDescription cls;
2921 
2922         cls.name = classObject[RJsonKey::classname].toString();
2923         cls.addr = classObject[RJsonKey::addr].toVariant().toULongLong();
2924         cls.index = classObject[RJsonKey::index].toVariant().toULongLong();
2925 
2926         for (const QJsonValue &value2 : classObject[RJsonKey::methods].toArray()) {
2927             QJsonObject methObject = value2.toObject();
2928 
2929             BinClassMethodDescription meth;
2930 
2931             meth.name = methObject[RJsonKey::name].toString();
2932             meth.addr = methObject[RJsonKey::addr].toVariant().toULongLong();
2933 
2934             cls.methods << meth;
2935         }
2936 
2937         for (const QJsonValue &value2 : classObject[RJsonKey::fields].toArray()) {
2938             QJsonObject fieldObject = value2.toObject();
2939 
2940             BinClassFieldDescription field;
2941 
2942             field.name = fieldObject[RJsonKey::name].toString();
2943             field.addr = fieldObject[RJsonKey::addr].toVariant().toULongLong();
2944 
2945             cls.fields << field;
2946         }
2947 
2948         ret << cls;
2949     }
2950     return ret;
2951 }
2952 
getAllClassesFromFlags()2953 QList<BinClassDescription> CutterCore::getAllClassesFromFlags()
2954 {
2955     static const QRegularExpression classFlagRegExp("^class\\.(.*)$");
2956     static const QRegularExpression methodFlagRegExp("^method\\.([^\\.]*)\\.(.*)$");
2957 
2958     CORE_LOCK();
2959     QList<BinClassDescription> ret;
2960     QMap<QString, BinClassDescription *> classesCache;
2961 
2962     QJsonArray flagsArray = cmdj("fj@F:classes").array();
2963     for (const QJsonValue &value : flagsArray) {
2964         QJsonObject flagObject = value.toObject();
2965 
2966         QString flagName = flagObject[RJsonKey::name].toString();
2967 
2968         QRegularExpressionMatch match = classFlagRegExp.match(flagName);
2969         if (match.hasMatch()) {
2970             QString className = match.captured(1);
2971             BinClassDescription *desc = nullptr;
2972             auto it = classesCache.find(className);
2973             if (it == classesCache.end()) {
2974                 BinClassDescription cls = {};
2975                 ret << cls;
2976                 desc = &ret.last();
2977                 classesCache[className] = desc;
2978             } else {
2979                 desc = it.value();
2980             }
2981             desc->name = match.captured(1);
2982             desc->addr = flagObject[RJsonKey::offset].toVariant().toULongLong();
2983             desc->index = RVA_INVALID;
2984             continue;
2985         }
2986 
2987         match = methodFlagRegExp.match(flagName);
2988         if (match.hasMatch()) {
2989             QString className = match.captured(1);
2990             BinClassDescription *classDesc = nullptr;
2991             auto it = classesCache.find(className);
2992             if (it == classesCache.end()) {
2993                 // add a new stub class, will be replaced if class flag comes after it
2994                 BinClassDescription cls;
2995                 cls.name = tr("Unknown (%1)").arg(className);
2996                 cls.addr = RVA_INVALID;
2997                 cls.index = 0;
2998                 ret << cls;
2999                 classDesc = &ret.last();
3000                 classesCache[className] = classDesc;
3001             } else {
3002                 classDesc = it.value();
3003             }
3004 
3005             BinClassMethodDescription meth;
3006             meth.name = match.captured(2);
3007             meth.addr = flagObject[RJsonKey::offset].toVariant().toULongLong();
3008             classDesc->methods << meth;
3009             continue;
3010         }
3011     }
3012     return ret;
3013 }
3014 
getAllAnalClasses(bool sorted)3015 QList<QString> CutterCore::getAllAnalClasses(bool sorted)
3016 {
3017     CORE_LOCK();
3018     QList<QString> ret;
3019 
3020     SdbListPtr l = makeSdbListPtr(r_anal_class_get_all(core->anal, sorted));
3021     if (!l) {
3022         return ret;
3023     }
3024     ret.reserve(static_cast<int>(l->length));
3025 
3026     SdbListIter *it;
3027     void *entry;
3028     ls_foreach(l, it, entry) {
3029         auto kv = reinterpret_cast<SdbKv *>(entry);
3030         ret.append(QString::fromUtf8(reinterpret_cast<const char *>(kv->base.key)));
3031     }
3032 
3033     return ret;
3034 }
3035 
getAnalClassMethods(const QString & cls)3036 QList<AnalMethodDescription> CutterCore::getAnalClassMethods(const QString &cls)
3037 {
3038     CORE_LOCK();
3039     QList<AnalMethodDescription> ret;
3040 
3041     RVector *meths = r_anal_class_method_get_all(core->anal, cls.toUtf8().constData());
3042     if (!meths) {
3043         return ret;
3044     }
3045 
3046     ret.reserve(static_cast<int>(meths->len));
3047     RAnalMethod *meth;
3048     CutterRVectorForeach(meths, meth, RAnalMethod) {
3049         AnalMethodDescription desc;
3050         desc.name = QString::fromUtf8(meth->name);
3051         desc.addr = meth->addr;
3052         desc.vtableOffset = meth->vtable_offset;
3053         ret.append(desc);
3054     }
3055     r_vector_free(meths);
3056 
3057     return ret;
3058 }
3059 
getAnalClassBaseClasses(const QString & cls)3060 QList<AnalBaseClassDescription> CutterCore::getAnalClassBaseClasses(const QString &cls)
3061 {
3062     CORE_LOCK();
3063     QList<AnalBaseClassDescription> ret;
3064 
3065     RVector *bases = r_anal_class_base_get_all(core->anal, cls.toUtf8().constData());
3066     if (!bases) {
3067         return ret;
3068     }
3069 
3070     ret.reserve(static_cast<int>(bases->len));
3071     RAnalBaseClass *base;
3072     CutterRVectorForeach(bases, base, RAnalBaseClass) {
3073         AnalBaseClassDescription desc;
3074         desc.id = QString::fromUtf8(base->id);
3075         desc.offset = base->offset;
3076         desc.className = QString::fromUtf8(base->class_name);
3077         ret.append(desc);
3078     }
3079     r_vector_free(bases);
3080 
3081     return ret;
3082 }
3083 
getAnalClassVTables(const QString & cls)3084 QList<AnalVTableDescription> CutterCore::getAnalClassVTables(const QString &cls)
3085 {
3086     CORE_LOCK();
3087     QList<AnalVTableDescription> acVtables;
3088 
3089     RVector *vtables = r_anal_class_vtable_get_all(core->anal, cls.toUtf8().constData());
3090     if (!vtables) {
3091         return acVtables;
3092     }
3093 
3094     acVtables.reserve(static_cast<int>(vtables->len));
3095     RAnalVTable *vtable;
3096     CutterRVectorForeach(vtables, vtable, RAnalVTable) {
3097         AnalVTableDescription desc;
3098         desc.id = QString::fromUtf8(vtable->id);
3099         desc.offset = vtable->offset;
3100         desc.addr = vtable->addr;
3101         acVtables.append(desc);
3102     }
3103     r_vector_free(vtables);
3104 
3105     return acVtables;
3106 }
3107 
createNewClass(const QString & cls)3108 void CutterCore::createNewClass(const QString &cls)
3109 {
3110     CORE_LOCK();
3111     r_anal_class_create(core->anal, cls.toUtf8().constData());
3112 }
3113 
renameClass(const QString & oldName,const QString & newName)3114 void CutterCore::renameClass(const QString &oldName, const QString &newName)
3115 {
3116     CORE_LOCK();
3117     r_anal_class_rename(core->anal, oldName.toUtf8().constData(), newName.toUtf8().constData());
3118 }
3119 
deleteClass(const QString & cls)3120 void CutterCore::deleteClass(const QString &cls)
3121 {
3122     CORE_LOCK();
3123     r_anal_class_delete(core->anal, cls.toUtf8().constData());
3124 }
3125 
getAnalMethod(const QString & cls,const QString & meth,AnalMethodDescription * desc)3126 bool CutterCore::getAnalMethod(const QString &cls, const QString &meth, AnalMethodDescription *desc)
3127 {
3128     CORE_LOCK();
3129     RAnalMethod analMeth;
3130     if (r_anal_class_method_get(core->anal, cls.toUtf8().constData(), meth.toUtf8().constData(), &analMeth) != R_ANAL_CLASS_ERR_SUCCESS) {
3131         return false;
3132     }
3133     desc->name = QString::fromUtf8(analMeth.name);
3134     desc->addr = analMeth.addr;
3135     desc->vtableOffset = analMeth.vtable_offset;
3136     r_anal_class_method_fini(&analMeth);
3137     return true;
3138 }
3139 
setAnalMethod(const QString & className,const AnalMethodDescription & meth)3140 void CutterCore::setAnalMethod(const QString &className, const AnalMethodDescription &meth)
3141 {
3142     CORE_LOCK();
3143     RAnalMethod analMeth;
3144     analMeth.name = strdup (meth.name.toUtf8().constData());
3145     analMeth.addr = meth.addr;
3146     analMeth.vtable_offset = meth.vtableOffset;
3147     r_anal_class_method_set(core->anal, className.toUtf8().constData(), &analMeth);
3148     r_anal_class_method_fini(&analMeth);
3149 }
3150 
renameAnalMethod(const QString & className,const QString & oldMethodName,const QString & newMethodName)3151 void CutterCore::renameAnalMethod(const QString &className, const QString &oldMethodName, const QString &newMethodName)
3152 {
3153     CORE_LOCK();
3154     r_anal_class_method_rename(core->anal, className.toUtf8().constData(), oldMethodName.toUtf8().constData(), newMethodName.toUtf8().constData());
3155 }
3156 
getAllResources()3157 QList<ResourcesDescription> CutterCore::getAllResources()
3158 {
3159     CORE_LOCK();
3160     QList<ResourcesDescription> resources;
3161 
3162     QJsonArray resourcesArray = cmdj("iRj").array();
3163     for (const QJsonValue &value : resourcesArray) {
3164         QJsonObject resourceObject = value.toObject();
3165 
3166         ResourcesDescription res;
3167 
3168         res.name = resourceObject[RJsonKey::name].toString();
3169         res.vaddr = resourceObject[RJsonKey::vaddr].toVariant().toULongLong();
3170         res.index = resourceObject[RJsonKey::index].toVariant().toULongLong();
3171         res.type = resourceObject[RJsonKey::type].toString();
3172         res.size = resourceObject[RJsonKey::size].toVariant().toULongLong();
3173         res.lang = resourceObject[RJsonKey::lang].toString();
3174 
3175         resources << res;
3176     }
3177     return resources;
3178 }
3179 
getAllVTables()3180 QList<VTableDescription> CutterCore::getAllVTables()
3181 {
3182     CORE_LOCK();
3183     QList<VTableDescription> vtables;
3184 
3185     QJsonArray vTablesArray = cmdj("avj").array();
3186     for (const QJsonValue &vTableValue : vTablesArray) {
3187         QJsonObject vTableObject = vTableValue.toObject();
3188 
3189         VTableDescription res;
3190 
3191         res.addr = vTableObject[RJsonKey::offset].toVariant().toULongLong();
3192         QJsonArray methodArray = vTableObject[RJsonKey::methods].toArray();
3193 
3194         for (const QJsonValue &methodValue : methodArray) {
3195             QJsonObject methodObject = methodValue.toObject();
3196 
3197             BinClassMethodDescription method;
3198 
3199             method.addr = methodObject[RJsonKey::offset].toVariant().toULongLong();
3200             method.name = methodObject[RJsonKey::name].toString();
3201 
3202             res.methods << method;
3203         }
3204 
3205         vtables << res;
3206     }
3207     return vtables;
3208 }
3209 
getAllTypes()3210 QList<TypeDescription> CutterCore::getAllTypes()
3211 {
3212     QList<TypeDescription> types;
3213 
3214     types.append(getAllPrimitiveTypes());
3215     types.append(getAllUnions());
3216     types.append(getAllStructs());
3217     types.append(getAllEnums());
3218     types.append(getAllTypedefs());
3219 
3220     return types;
3221 }
3222 
getAllPrimitiveTypes()3223 QList<TypeDescription> CutterCore::getAllPrimitiveTypes()
3224 {
3225     CORE_LOCK();
3226     QList<TypeDescription> primitiveTypes;
3227 
3228     QJsonArray typesArray = cmdj("tj").array();
3229     for (const QJsonValue &value : typesArray) {
3230         QJsonObject typeObject = value.toObject();
3231 
3232         TypeDescription exp;
3233 
3234         exp.type = typeObject[RJsonKey::type].toString();
3235         exp.size = typeObject[RJsonKey::size].toVariant().toULongLong();
3236         exp.format = typeObject[RJsonKey::format].toString();
3237         exp.category = tr("Primitive");
3238         primitiveTypes << exp;
3239     }
3240 
3241     return primitiveTypes;
3242 }
3243 
getAllUnions()3244 QList<TypeDescription> CutterCore::getAllUnions()
3245 {
3246     CORE_LOCK();
3247     QList<TypeDescription> unions;
3248 
3249     QJsonArray typesArray = cmdj("tuj").array();
3250     for (const QJsonValue value: typesArray) {
3251         QJsonObject typeObject = value.toObject();
3252 
3253         TypeDescription exp;
3254 
3255         exp.type = typeObject[RJsonKey::type].toString();
3256         exp.size = typeObject[RJsonKey::size].toVariant().toULongLong();
3257         exp.category = "Union";
3258         unions << exp;
3259     }
3260 
3261     return unions;
3262 }
3263 
getAllStructs()3264 QList<TypeDescription> CutterCore::getAllStructs()
3265 {
3266     CORE_LOCK();
3267     QList<TypeDescription> structs;
3268 
3269     QJsonArray typesArray = cmdj("tsj").array();
3270     for (const QJsonValue value: typesArray) {
3271         QJsonObject typeObject = value.toObject();
3272 
3273         TypeDescription exp;
3274 
3275         exp.type = typeObject[RJsonKey::type].toString();
3276         exp.size = typeObject[RJsonKey::size].toVariant().toULongLong();
3277         exp.category = "Struct";
3278         structs << exp;
3279     }
3280 
3281     return structs;
3282 }
3283 
getAllEnums()3284 QList<TypeDescription> CutterCore::getAllEnums()
3285 {
3286     CORE_LOCK();
3287     QList<TypeDescription> enums;
3288 
3289     QJsonObject typesObject = cmdj("tej").object();
3290     for (QString key: typesObject.keys()) {
3291         TypeDescription exp;
3292         exp.type = key;
3293         exp.size = 0;
3294         exp.category = "Enum";
3295         enums << exp;
3296     }
3297 
3298     return enums;
3299 }
3300 
getAllTypedefs()3301 QList<TypeDescription> CutterCore::getAllTypedefs()
3302 {
3303     CORE_LOCK();
3304     QList<TypeDescription> typeDefs;
3305 
3306     QJsonObject typesObject = cmdj("ttj").object();
3307     for (QString key: typesObject.keys()) {
3308         TypeDescription exp;
3309         exp.type = key;
3310         exp.size = 0;
3311         exp.category = "Typedef";
3312         typeDefs << exp;
3313     }
3314 
3315     return typeDefs;
3316 }
3317 
addTypes(const char * str)3318 QString CutterCore::addTypes(const char *str)
3319 {
3320     CORE_LOCK();
3321     char *error_msg = nullptr;
3322     char *parsed = r_parse_c_string(core->anal, str, &error_msg);
3323     QString error;
3324 
3325     if (!parsed) {
3326          if (error_msg) {
3327              error = error_msg;
3328              r_mem_free(error_msg);
3329          }
3330          return error;
3331     }
3332 
3333     r_anal_save_parsed_type(core->anal, parsed);
3334     r_mem_free(parsed);
3335 
3336     if (error_msg) {
3337         error = error_msg;
3338         r_mem_free(error_msg);
3339     }
3340 
3341     return error;
3342 }
3343 
getTypeAsC(QString name,QString category)3344 QString CutterCore::getTypeAsC(QString name, QString category)
3345 {
3346     CORE_LOCK();
3347     QString output = "Failed to fetch the output.";
3348     if (name.isEmpty() || category.isEmpty()) {
3349         return output;
3350     }
3351     QString typeName = sanitizeStringForCommand(name);
3352     if (category == "Struct") {
3353         output = cmdRaw(QString("tsc %1").arg(typeName));
3354     } else if (category == "Union") {
3355         output = cmdRaw(QString("tuc %1").arg(typeName));
3356     } else if(category == "Enum") {
3357         output = cmdRaw(QString("tec %1").arg(typeName));
3358     } else if(category == "Typedef") {
3359         output = cmdRaw(QString("ttc %1").arg(typeName));
3360     }
3361     return output;
3362 }
3363 
isAddressMapped(RVA addr)3364 bool CutterCore::isAddressMapped(RVA addr)
3365 {
3366     // If value returned by "om. @ addr" is empty means that address is not mapped
3367     return !Core()->cmdRawAt(QString("om."), addr).isEmpty();
3368 }
3369 
getAllSearch(QString search_for,QString space)3370 QList<SearchDescription> CutterCore::getAllSearch(QString search_for, QString space)
3371 {
3372     CORE_LOCK();
3373     QList<SearchDescription> searchRef;
3374 
3375     QJsonArray searchArray = cmdj(space + QString(" ") + search_for).array();
3376 
3377     if (space == "/Rj") {
3378         for (const QJsonValue &value : searchArray) {
3379             QJsonObject searchObject = value.toObject();
3380 
3381             SearchDescription exp;
3382 
3383             exp.code.clear();
3384             for (const QJsonValue &value2 : searchObject[RJsonKey::opcodes].toArray()) {
3385                 QJsonObject gadget = value2.toObject();
3386                 exp.code += gadget[RJsonKey::opcode].toString() + ";  ";
3387             }
3388 
3389             exp.offset =
3390                 searchObject[RJsonKey::opcodes].toArray().first().toObject()[RJsonKey::offset].toVariant().toULongLong();
3391             exp.size = searchObject[RJsonKey::size].toVariant().toULongLong();
3392 
3393             searchRef << exp;
3394         }
3395     } else {
3396         for (const QJsonValue &value : searchArray) {
3397             QJsonObject searchObject = value.toObject();
3398 
3399             SearchDescription exp;
3400 
3401             exp.offset = searchObject[RJsonKey::offset].toVariant().toULongLong();
3402             exp.size = searchObject[RJsonKey::len].toVariant().toULongLong();
3403             exp.code = searchObject[RJsonKey::code].toString();
3404             exp.data = searchObject[RJsonKey::data].toString();
3405 
3406             searchRef << exp;
3407         }
3408     }
3409     return searchRef;
3410 }
3411 
getBlockStatistics(unsigned int blocksCount)3412 BlockStatistics CutterCore::getBlockStatistics(unsigned int blocksCount)
3413 {
3414     BlockStatistics blockStats;
3415     if (blocksCount == 0) {
3416         blockStats.from = blockStats.to = blockStats.blocksize = 0;
3417         return blockStats;
3418     }
3419 
3420     QJsonObject statsObj;
3421 
3422     // User TempConfig here to set the search boundaries to all sections. This makes sure
3423     // that the Visual Navbar will show all the relevant addresses.
3424     {
3425         TempConfig tempConfig;
3426         tempConfig.set("search.in", "bin.sections");
3427         statsObj = cmdj("p-j " + QString::number(blocksCount)).object();
3428     }
3429 
3430     blockStats.from = statsObj[RJsonKey::from].toVariant().toULongLong();
3431     blockStats.to = statsObj[RJsonKey::to].toVariant().toULongLong();
3432     blockStats.blocksize = statsObj[RJsonKey::blocksize].toVariant().toULongLong();
3433 
3434     QJsonArray blocksArray = statsObj[RJsonKey::blocks].toArray();
3435 
3436     for (const QJsonValue &value : blocksArray) {
3437         QJsonObject blockObj = value.toObject();
3438 
3439         BlockDescription block;
3440 
3441         block.addr = blockObj[RJsonKey::offset].toVariant().toULongLong();
3442         block.size = blockObj[RJsonKey::size].toVariant().toULongLong();
3443         block.flags = blockObj[RJsonKey::flags].toInt(0);
3444         block.functions = blockObj[RJsonKey::functions].toInt(0);
3445         block.inFunctions = blockObj[RJsonKey::in_functions].toInt(0);
3446         block.comments = blockObj[RJsonKey::comments].toInt(0);
3447         block.symbols = blockObj[RJsonKey::symbols].toInt(0);
3448         block.strings = blockObj[RJsonKey::strings].toInt(0);
3449 
3450         block.rwx = 0;
3451         QString rwxStr = blockObj[RJsonKey::rwx].toString();
3452         if (rwxStr.length() == 3) {
3453             if (rwxStr[0] == 'r') {
3454                 block.rwx |= (1 << 0);
3455             }
3456             if (rwxStr[1] == 'w') {
3457                 block.rwx |= (1 << 1);
3458             }
3459             if (rwxStr[2] == 'x') {
3460                 block.rwx |= (1 << 2);
3461             }
3462         }
3463 
3464         blockStats.blocks << block;
3465     }
3466 
3467     return blockStats;
3468 }
3469 
getXRefsForVariable(QString variableName,bool findWrites,RVA offset)3470 QList<XrefDescription> CutterCore::getXRefsForVariable(QString variableName, bool findWrites, RVA offset)
3471 {
3472     QList<XrefDescription> xrefList = QList<XrefDescription>();
3473     QJsonArray xrefsArray;
3474     if (findWrites) {
3475         xrefsArray = cmdjAt("afvWj", offset).array();
3476     } else {
3477         xrefsArray = cmdjAt("afvRj", offset).array();
3478     }
3479     for (const QJsonValue &value : xrefsArray) {
3480         QJsonObject xrefObject = value.toObject();
3481         QString name = xrefObject[RJsonKey::name].toString();
3482         if (name == variableName) {
3483             QJsonArray addressArray = xrefObject[RJsonKey::addrs].toArray();
3484             for (const QJsonValue &address : addressArray) {
3485                 XrefDescription xref;
3486                 RVA addr = address.toVariant().toULongLong();
3487                 xref.from = addr;
3488                 xref.to = addr;
3489                 if (findWrites) {
3490                     xref.from_str = RAddressString(addr);
3491                 } else {
3492                     xref.to_str = RAddressString(addr);
3493                 }
3494                 xrefList << xref;
3495             }
3496         }
3497     }
3498     return xrefList;
3499 }
3500 
getXRefs(RVA addr,bool to,bool whole_function,const QString & filterType)3501 QList<XrefDescription> CutterCore::getXRefs(RVA addr, bool to, bool whole_function,
3502                                             const QString &filterType)
3503 {
3504     QList<XrefDescription> xrefList = QList<XrefDescription>();
3505 
3506     QJsonArray xrefsArray;
3507 
3508     if (to) {
3509         xrefsArray = cmdj("axtj@" + QString::number(addr)).array();
3510     } else {
3511         xrefsArray = cmdj("axfj@" + QString::number(addr)).array();
3512     }
3513 
3514     for (const QJsonValue &value : xrefsArray) {
3515         QJsonObject xrefObject = value.toObject();
3516 
3517         XrefDescription xref;
3518 
3519         xref.type = xrefObject[RJsonKey::type].toString();
3520 
3521         if (!filterType.isNull() && filterType != xref.type)
3522             continue;
3523 
3524         xref.from = xrefObject[RJsonKey::from].toVariant().toULongLong();
3525         if (!to) {
3526             xref.from_str = RAddressString(xref.from);
3527         } else {
3528             QString fcn = xrefObject[RJsonKey::fcn_name].toString();
3529             if (!fcn.isEmpty()) {
3530                 RVA fcnAddr = xrefObject[RJsonKey::fcn_addr].toVariant().toULongLong();
3531                 xref.from_str = fcn + " + 0x" + QString::number(xref.from - fcnAddr, 16);
3532             } else {
3533                 xref.from_str = RAddressString(xref.from);
3534             }
3535         }
3536 
3537         if (!whole_function && !to && xref.from != addr) {
3538             continue;
3539         }
3540 
3541         if (to && !xrefObject.contains(RJsonKey::to)) {
3542             xref.to = addr;
3543         } else {
3544             xref.to = xrefObject[RJsonKey::to].toVariant().toULongLong();
3545         }
3546         xref.to_str = Core()->cmdRaw(QString("fd %1").arg(xref.to)).trimmed();
3547 
3548         xrefList << xref;
3549     }
3550 
3551     return xrefList;
3552 }
3553 
addFlag(RVA offset,QString name,RVA size)3554 void CutterCore::addFlag(RVA offset, QString name, RVA size)
3555 {
3556     name = sanitizeStringForCommand(name);
3557     cmdRawAt(QString("f %1 %2").arg(name).arg(size), offset);
3558     emit flagsChanged();
3559 }
3560 
3561 /**
3562  * @brief Gets all the flags present at a specific address
3563  * @param addr The address to be checked
3564  * @return String containing all the flags which are comma-separated
3565  */
listFlagsAsStringAt(RVA addr)3566 QString CutterCore::listFlagsAsStringAt(RVA addr)
3567 {
3568     CORE_LOCK();
3569     char *flagList = r_flag_get_liststr (core->flags, addr);
3570     QString result = fromOwnedCharPtr(flagList);
3571     return result;
3572 }
3573 
nearestFlag(RVA offset,RVA * flagOffsetOut)3574 QString CutterCore::nearestFlag(RVA offset, RVA *flagOffsetOut)
3575 {
3576     auto r = cmdj(QString("fdj @") + QString::number(offset)).object();
3577     QString name = r.value("name").toString();
3578     if (flagOffsetOut) {
3579         int queryOffset = r.value("offset").toInt(0);
3580         *flagOffsetOut = offset  + static_cast<RVA>(-queryOffset);
3581     }
3582     return name;
3583 }
3584 
handleREvent(int type,void * data)3585 void CutterCore::handleREvent(int type, void *data)
3586 {
3587     switch (type) {
3588     case R_EVENT_CLASS_NEW: {
3589         auto ev = reinterpret_cast<REventClass *>(data);
3590         emit classNew(QString::fromUtf8(ev->name));
3591         break;
3592     }
3593     case R_EVENT_CLASS_DEL: {
3594         auto ev = reinterpret_cast<REventClass *>(data);
3595         emit classDeleted(QString::fromUtf8(ev->name));
3596         break;
3597     }
3598     case R_EVENT_CLASS_RENAME: {
3599         auto ev = reinterpret_cast<REventClassRename *>(data);
3600         emit classRenamed(QString::fromUtf8(ev->name_old), QString::fromUtf8(ev->name_new));
3601         break;
3602     }
3603     case R_EVENT_CLASS_ATTR_SET: {
3604         auto ev = reinterpret_cast<REventClassAttrSet *>(data);
3605         emit classAttrsChanged(QString::fromUtf8(ev->attr.class_name));
3606         break;
3607     }
3608     case R_EVENT_CLASS_ATTR_DEL: {
3609         auto ev = reinterpret_cast<REventClassAttr *>(data);
3610         emit classAttrsChanged(QString::fromUtf8(ev->class_name));
3611         break;
3612     }
3613     case R_EVENT_CLASS_ATTR_RENAME: {
3614         auto ev = reinterpret_cast<REventClassAttrRename *>(data);
3615         emit classAttrsChanged(QString::fromUtf8(ev->attr.class_name));
3616         break;
3617     }
3618     case R_EVENT_DEBUG_PROCESS_FINISHED: {
3619         auto ev = reinterpret_cast<REventDebugProcessFinished*>(data);
3620         emit debugProcessFinished(ev->pid);
3621         break;
3622     }
3623     default:
3624         break;
3625     }
3626 }
3627 
triggerFlagsChanged()3628 void CutterCore::triggerFlagsChanged()
3629 {
3630     emit flagsChanged();
3631 }
3632 
triggerVarsChanged()3633 void CutterCore::triggerVarsChanged()
3634 {
3635     emit varsChanged();
3636 }
3637 
triggerFunctionRenamed(const RVA offset,const QString & newName)3638 void CutterCore::triggerFunctionRenamed(const RVA offset, const QString &newName)
3639 {
3640     emit functionRenamed(offset, newName);
3641 }
3642 
loadPDB(const QString & file)3643 void CutterCore::loadPDB(const QString &file)
3644 {
3645     cmdRaw("idp " + sanitizeStringForCommand(file));
3646 }
3647 
openProject(const QString & name)3648 void CutterCore::openProject(const QString &name)
3649 {
3650     cmdRaw("Po " + name);
3651 
3652     QString notes = QString::fromUtf8(QByteArray::fromBase64(cmdRaw("Pnj").toUtf8()));
3653 }
3654 
saveProject(const QString & name)3655 void CutterCore::saveProject(const QString &name)
3656 {
3657     const QString &rv = cmdRaw("Ps " + name.trimmed()).trimmed();
3658     const bool ok = rv == name.trimmed();
3659     cmdRaw(QString("Pnj %1").arg(QString(notes.toUtf8().toBase64())));
3660     emit projectSaved(ok, name);
3661 }
3662 
deleteProject(const QString & name)3663 void CutterCore::deleteProject(const QString &name)
3664 {
3665     cmdRaw("Pd " + name);
3666 }
3667 
isProjectNameValid(const QString & name)3668 bool CutterCore::isProjectNameValid(const QString &name)
3669 {
3670     // see is_valid_project_name() in libr/core/project.
3671 
3672     QString pattern(R"(^[a-zA-Z0-9\\\._:-]{1,}$)");
3673     // The below construct mimics the behaviour of QRegexP::exactMatch(), which was here before
3674     static const QRegularExpression regexp("\\A(?:" + pattern + ")\\z");
3675     return regexp.match(name).hasMatch() && !name.endsWith(".zip") ;
3676 }
3677 
disassembleLines(RVA offset,int lines)3678 QList<DisassemblyLine> CutterCore::disassembleLines(RVA offset, int lines)
3679 {
3680     QJsonArray array = cmdj(QString("pdJ ") + QString::number(lines) + QString(" @ ") + QString::number(
3681                                 offset)).array();
3682     QList<DisassemblyLine> r;
3683 
3684     for (const QJsonValueRef &value : array) {
3685         QJsonObject object = value.toObject();
3686         DisassemblyLine line;
3687         line.offset = object[RJsonKey::offset].toVariant().toULongLong();
3688         line.text = ansiEscapeToHtml(object[RJsonKey::text].toString());
3689         const auto& arrow = object[RJsonKey::arrow];
3690         line.arrow = arrow.isNull()
3691                      ? RVA_INVALID
3692                      : arrow.toVariant().toULongLong();
3693         r << line;
3694     }
3695 
3696     return r;
3697 }
3698 
3699 
3700 /**
3701  * @brief return hexdump of <size> from an <offset> by a given formats
3702  * @param address - the address from which to print the hexdump
3703  * @param size - number of bytes to print
3704  * @param format - the type of hexdump (qwords, words. decimal, etc)
3705  */
hexdump(RVA address,int size,HexdumpFormats format)3706 QString CutterCore::hexdump(RVA address, int size, HexdumpFormats format)
3707 {
3708     QString command = "px";
3709     switch (format) {
3710     case HexdumpFormats::Normal:
3711         break;
3712     case HexdumpFormats::Half:
3713         command += "h";
3714         break;
3715     case HexdumpFormats::Word:
3716         command += "w";
3717         break;
3718     case HexdumpFormats::Quad:
3719         command += "q";
3720         break;
3721     case HexdumpFormats::Signed:
3722         command += "d";
3723         break;
3724     case HexdumpFormats::Octal:
3725         command += "o";
3726         break;
3727     }
3728 
3729     return cmdRawAt(QString("%1 %2")
3730                         .arg(command)
3731                         .arg(size),
3732                         address);
3733 }
3734 
hexStringToBytes(const QString & hex)3735 QByteArray CutterCore::hexStringToBytes(const QString &hex)
3736 {
3737     QByteArray hexChars = hex.toUtf8();
3738     QByteArray bytes;
3739     bytes.reserve(hexChars.length() / 2);
3740     int size = r_hex_str2bin(hexChars.constData(), reinterpret_cast<ut8 *>(bytes.data()));
3741     bytes.resize(size);
3742     return bytes;
3743 }
3744 
bytesToHexString(const QByteArray & bytes)3745 QString CutterCore::bytesToHexString(const QByteArray &bytes)
3746 {
3747     QByteArray hex;
3748     hex.resize(bytes.length() * 2);
3749     r_hex_bin2str(reinterpret_cast<const ut8 *>(bytes.constData()), bytes.size(), hex.data());
3750     return QString::fromUtf8(hex);
3751 }
3752 
loadScript(const QString & scriptname)3753 void CutterCore::loadScript(const QString &scriptname)
3754 {
3755     {
3756         CORE_LOCK();
3757         r_core_cmd_file(core, scriptname.toUtf8().constData());
3758     }
3759     triggerRefreshAll();
3760 }
3761 
getVersionInformation()3762 QString CutterCore::getVersionInformation()
3763 {
3764     int i;
3765     QString versionInfo;
3766     struct vcs_t {
3767         const char *name;
3768         const char *(*callback)();
3769     } vcs[] = {
3770         { "r_anal", &r_anal_version },
3771         { "r_lib", &r_lib_version },
3772         { "r_egg", &r_egg_version },
3773         { "r_asm", &r_asm_version },
3774         { "r_bin", &r_bin_version },
3775         { "r_cons", &r_cons_version },
3776         { "r_flag", &r_flag_version },
3777         { "r_core", &r_core_version },
3778         { "r_crypto", &r_crypto_version },
3779         { "r_bp", &r_bp_version },
3780         { "r_debug", &r_debug_version },
3781         { "r_hash", &r_hash_version },
3782         { "r_fs", &r_fs_version },
3783         { "r_io", &r_io_version },
3784 #if !USE_LIB_MAGIC
3785         { "r_magic", &r_magic_version },
3786 #endif
3787         { "r_parse", &r_parse_version },
3788         { "r_reg", &r_reg_version },
3789         { "r_sign", &r_sign_version },
3790         { "r_search", &r_search_version },
3791         { "r_syscall", &r_syscall_version },
3792         { "r_util", &r_util_version },
3793         /* ... */
3794         {NULL, NULL}
3795     };
3796     versionInfo.append(QString("%1 r2\n").arg(R2_GITTAP));
3797     for (i = 0; vcs[i].name; i++) {
3798         struct vcs_t *v = &vcs[i];
3799         const char *name = v->callback ();
3800         versionInfo.append(QString("%1 %2\n").arg(name, v->name));
3801     }
3802     return versionInfo;
3803 }
3804 
getOpenedFiles()3805 QJsonArray CutterCore::getOpenedFiles()
3806 {
3807     QJsonDocument files = cmdj("oj");
3808     return files.array();
3809 }
3810 
getColorThemes()3811 QList<QString> CutterCore::getColorThemes()
3812 {
3813     QList<QString> r;
3814     QJsonDocument themes = cmdj("ecoj");
3815     for (const QJsonValue &s : themes.array()) {
3816         r << s.toString();
3817     }
3818     return r;
3819 }
3820 
ansiEscapeToHtml(const QString & text)3821 QString CutterCore::ansiEscapeToHtml(const QString &text)
3822 {
3823     int len;
3824     char *html = r_cons_html_filter(text.toUtf8().constData(), &len);
3825     if (!html) {
3826         return QString();
3827     }
3828     QString r = QString::fromUtf8(html, len);
3829     r_mem_free(html);
3830     return r;
3831 }
3832 
getBBHighlighter()3833 BasicBlockHighlighter* CutterCore::getBBHighlighter()
3834 {
3835     return bbHighlighter;
3836 }
3837 
getBIHighlighter()3838 BasicInstructionHighlighter* CutterCore::getBIHighlighter()
3839 {
3840     return &biHighlighter;
3841 }
3842 
setIOCache(bool enabled)3843 void CutterCore::setIOCache(bool enabled)
3844 {
3845     if (enabled) {
3846         // disable write mode when cache is enabled
3847         setWriteMode(false);
3848     }
3849     setConfig("io.cache", enabled);
3850     this->iocache = enabled;
3851 
3852     emit ioCacheChanged(enabled);
3853     emit ioModeChanged();
3854 }
3855 
isIOCacheEnabled() const3856 bool CutterCore::isIOCacheEnabled() const
3857 {
3858     return iocache;
3859 }
3860 
commitWriteCache()3861 void CutterCore::commitWriteCache()
3862 {
3863     // Temporarily disable cache mode
3864     TempConfig tempConfig;
3865     tempConfig.set("io.cache", false);
3866     if (!isWriteModeEnabled()) {
3867         cmdRaw("oo+");
3868         cmdRaw("wci");
3869         cmdRaw("oo");
3870     } else {
3871         cmdRaw("wci");
3872     }
3873 }
3874 
3875 // Enable or disable write-mode. Avoid unecessary changes if not need.
setWriteMode(bool enabled)3876 void CutterCore::setWriteMode(bool enabled)
3877 {
3878     bool writeModeState = isWriteModeEnabled();
3879 
3880     if (writeModeState == enabled && !this->iocache) {
3881         // New mode is the same as current and IO Cache is disabled. Do nothing.
3882         return;
3883     }
3884 
3885     // Change from read-only to write-mode
3886     if (enabled && !writeModeState) {
3887         cmdRaw("oo+");
3888     // Change from write-mode to read-only
3889     } else {
3890         cmdRaw("oo");
3891     }
3892     // Disable cache mode because we specifically set write or
3893     // read-only modes.
3894     setIOCache(false);
3895     writeModeChanged (enabled);
3896     emit ioModeChanged();
3897 }
3898 
isWriteModeEnabled()3899 bool CutterCore::isWriteModeEnabled()
3900 {
3901     using namespace std;
3902     QJsonArray ans = cmdj("oj").array();
3903     return find_if(begin(ans), end(ans), [](const QJsonValue &v) {
3904         return v.toObject().value("raised").toBool();
3905     })->toObject().value("writable").toBool();
3906 }
3907 
3908 /**
3909  * @brief get a compact disassembly preview for tooltips
3910  * @param address - the address from which to print the disassembly
3911  * @param num_of_lines - number of instructions to print
3912  */
getDisassemblyPreview(RVA address,int num_of_lines)3913 QStringList CutterCore::getDisassemblyPreview(RVA address, int num_of_lines)
3914 {
3915      QList<DisassemblyLine> disassemblyLines;
3916         {
3917             // temporarily simplify the disasm output to get it colorful and simple to read
3918             TempConfig tempConfig;
3919             tempConfig
3920                 .set("scr.color", COLOR_MODE_16M)
3921                 .set("asm.lines", false)
3922                 .set("asm.var", false)
3923                 .set("asm.comments", false)
3924                 .set("asm.bytes", false)
3925                 .set("asm.lines.fcn", false)
3926                 .set("asm.lines.out", false)
3927                 .set("asm.lines.bb", false)
3928                 .set("asm.bb.line", false);
3929 
3930             disassemblyLines = disassembleLines(address, num_of_lines + 1);
3931         }
3932         QStringList disasmPreview;
3933         for (const DisassemblyLine &line : disassemblyLines) {
3934             disasmPreview << line.text;
3935             if (disasmPreview.length() >= num_of_lines) {
3936                 disasmPreview << "...";
3937                 break;
3938             }
3939         }
3940         if (!disasmPreview.isEmpty()) {
3941             return disasmPreview;
3942         } else {
3943             return QStringList();
3944         }
3945 }
3946 
3947 /**
3948  * @brief get a compact hexdump preview for tooltips
3949  * @param address - the address from which to print the hexdump
3950  * @param size - number of bytes to print
3951  */
getHexdumpPreview(RVA address,int size)3952 QString CutterCore::getHexdumpPreview(RVA address, int size)
3953 {
3954     // temporarily simplify the disasm output to get it colorful and simple to read
3955     TempConfig tempConfig;
3956     tempConfig
3957         .set("scr.color", COLOR_MODE_16M)
3958         .set("asm.offset", true)
3959         .set("hex.header", false)
3960         .set("hex.cols", 16);
3961     return ansiEscapeToHtml(hexdump(address, size, HexdumpFormats::Normal)).replace(QLatin1Char('\n'), "<br>");
3962 }
3963 
ioRead(RVA addr,int len)3964 QByteArray CutterCore::ioRead(RVA addr, int len)
3965 {
3966     CORE_LOCK();
3967 
3968     QByteArray array;
3969 
3970     if (len <= 0)
3971         return array;
3972 
3973     /* Zero-copy */
3974     array.resize(len);
3975     if (!r_io_read_at(core->io, addr, (uint8_t *)array.data(), len)) {
3976         qWarning() << "Can't read data" << addr << len;
3977         array.fill(0xff);
3978     }
3979 
3980     return  array;
3981 }
3982