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