#include "CutterApplication.h" #include #include #include #include #include #include #include #include #include #include #ifdef CUTTER_ENABLE_JUPYTER #include "utils/JupyterConnection.h" #endif #include "plugins/CutterPlugin.h" #include "CutterConfig.h" CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv) { setOrganizationName("Cutter"); setApplicationName("Cutter"); setApplicationVersion(CUTTER_VERSION_FULL); setWindowIcon(QIcon(":/img/cutter.svg")); setAttribute(Qt::AA_DontShowIconsInMenus); // Set QString codec to UTF-8 QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); #if QT_VERSION < QT_VERSION_CHECK(5,0,0) QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); #endif QCommandLineParser cmd_parser; cmd_parser.setApplicationDescription( QObject::tr("A Qt and C++ GUI for radare2 reverse engineering framework")); cmd_parser.addHelpOption(); cmd_parser.addVersionOption(); cmd_parser.addPositionalArgument("filename", QObject::tr("Filename to open.")); QCommandLineOption analOption({"A", "anal"}, QObject::tr("Automatically open file and optionally start analysis. Needs filename to be specified. May be a value between 0 and 2: 0 = no analysis, 1 = aaa, 2 = aaaa (experimental)"), QObject::tr("level")); cmd_parser.addOption(analOption); QCommandLineOption scriptOption("i", QObject::tr("Run script file"), QObject::tr("file")); cmd_parser.addOption(scriptOption); #ifdef CUTTER_ENABLE_JUPYTER QCommandLineOption pythonHomeOption("pythonhome", QObject::tr("PYTHONHOME to use for Jupyter"), "PYTHONHOME"); cmd_parser.addOption(pythonHomeOption); #endif cmd_parser.process(*this); QStringList args = cmd_parser.positionalArguments(); // Check r2 version QString r2version = r_core_version(); QString localVersion = "" R2_GITTAP; if (r2version != localVersion) { QMessageBox msg; msg.setIcon(QMessageBox::Critical); msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msg.setWindowTitle(QObject::tr("Version mismatch!")); msg.setText(QString( QObject::tr("The version used to compile Cutter (%1) does not match the binary version of radare2 (%2). This could result in unexpected behaviour. Are you sure you want to continue?")).arg( localVersion, r2version)); if (msg.exec() == QMessageBox::No) exit(1); } #ifdef CUTTER_ENABLE_JUPYTER if (cmd_parser.isSet(pythonHomeOption)) { Jupyter()->setPythonHome(cmd_parser.value(pythonHomeOption)); } #endif bool analLevelSpecified = false; int analLevel = 0; // Initialize CutterCore and set default settings Core()->setSettings(); if (cmd_parser.isSet(analOption)) { analLevel = cmd_parser.value(analOption).toInt(&analLevelSpecified); if (!analLevelSpecified || analLevel < 0 || analLevel > 2) { printf("%s\n", QObject::tr("Invalid Analysis Level. May be a value between 0 and 2.").toLocal8Bit().constData()); exit(1); } } mainWindow = new MainWindow(); installEventFilter(mainWindow); if (args.empty()) { if (analLevelSpecified) { printf("%s\n", QObject::tr("Filename must be specified to start analysis automatically.").toLocal8Bit().constData()); exit(1); } mainWindow->displayNewFileDialog(); } else { // filename specified as positional argument InitialOptions options; options.filename = args[0]; if (analLevelSpecified) { switch (analLevel) { case 0: default: options.analCmd = {}; break; case 1: options.analCmd = { "aaa" }; break; case 2: options.analCmd = { "aaaa" }; break; } } options.script = cmd_parser.value(scriptOption); mainWindow->openNewFile(options, analLevelSpecified); } // Load plugins loadPlugins(); } CutterApplication::~CutterApplication() { delete mainWindow; } bool CutterApplication::event(QEvent *e) { if (e->type() == QEvent::FileOpen) { QFileOpenEvent *openEvent = static_cast(e); if (openEvent) { if (m_FileAlreadyDropped) { // We already dropped a file in macOS, let's spawn another instance // (Like the File -> Open) QString fileName = openEvent->file(); QProcess process(this); process.setEnvironment(QProcess::systemEnvironment()); QStringList args = QStringList(fileName); process.startDetached(qApp->applicationFilePath(), args); } else { QString fileName = openEvent->file(); m_FileAlreadyDropped = true; mainWindow->closeNewFileDialog(); InitialOptions options; options.filename = fileName; mainWindow->openNewFile(options); } } } return QApplication::event(e); } void CutterApplication::loadPlugins() { QList plugins; QDir pluginsDir(qApp->applicationDirPath()); #if defined(Q_OS_WIN) if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release") pluginsDir.cdUp(); #elif defined(Q_OS_MAC) if (pluginsDir.dirName() == "MacOS") { pluginsDir.cdUp(); pluginsDir.cdUp(); pluginsDir.cdUp(); } #endif if (!pluginsDir.cd("plugins")) return; foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); QObject *plugin = pluginLoader.instance(); if (plugin) { CutterPlugin *cutterPlugin = qobject_cast(plugin); if (cutterPlugin) { cutterPlugin->setupPlugin(Core()); plugins.append(cutterPlugin); } } } Core()->setCutterPlugins(plugins); }