"Fossies" - the Fresh Open Source Software Archive

Member "cutter-1.10.3/src/CutterApplication.cpp" (8 May 2020, 15692 Bytes) of package /linux/privat/cutter-1.10.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "CutterApplication.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.10.2_vs_1.10.3.

    1 #include "common/PythonManager.h"
    2 #include "common/CrashHandler.h"
    3 #include "CutterApplication.h"
    4 #include "plugins/PluginManager.h"
    5 #include "CutterConfig.h"
    6 #include "common/Decompiler.h"
    7 
    8 #include <QApplication>
    9 #include <QFileOpenEvent>
   10 #include <QEvent>
   11 #include <QMenu>
   12 #include <QMessageBox>
   13 #include <QCommandLineParser>
   14 #include <QTextCodec>
   15 #include <QStringList>
   16 #include <QProcess>
   17 #include <QPluginLoader>
   18 #include <QDir>
   19 #include <QTranslator>
   20 #include <QLibraryInfo>
   21 #include <QFontDatabase>
   22 #ifdef Q_OS_WIN
   23 #include <QtNetwork/QtNetwork>
   24 #endif // Q_OS_WIN
   25 
   26 #include <cstdlib>
   27 
   28 #if CUTTER_R2GHIDRA_STATIC
   29 #include <R2GhidraDecompiler.h>
   30 #endif
   31 
   32 CutterApplication::CutterApplication(int &argc, char **argv) : QApplication(argc, argv)
   33 {
   34     // Setup application information
   35     setApplicationVersion(CUTTER_VERSION_FULL);
   36     setWindowIcon(QIcon(":/img/cutter.svg"));
   37     setAttribute(Qt::AA_UseHighDpiPixmaps);
   38     setLayoutDirection(Qt::LeftToRight);
   39 
   40     // WARN!!! Put initialization code below this line. Code above this line is mandatory to be run First
   41 
   42 #ifdef Q_OS_WIN
   43     // Hack to force Cutter load internet connection related DLL's
   44     QSslSocket s;
   45     s.sslConfiguration();
   46 #endif // Q_OS_WIN
   47 
   48     // Load translations
   49     if (!loadTranslations()) {
   50         qWarning() << "Cannot load translations";
   51     }
   52 
   53     // Load fonts
   54     int ret = QFontDatabase::addApplicationFont(":/fonts/Anonymous Pro.ttf");
   55     if (ret == -1) {
   56         qWarning() << "Cannot load Anonymous Pro font.";
   57     }
   58 
   59     ret = QFontDatabase::addApplicationFont(":/fonts/Inconsolata-Regular.ttf");
   60     if (ret == -1) {
   61         qWarning() << "Cannot load Incosolata-Regular font.";
   62     }
   63 
   64 
   65     // Set QString codec to UTF-8
   66     QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
   67 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
   68     QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
   69     QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
   70 #endif
   71 
   72     if (!parseCommandLineOptions()) {
   73         std::exit(1);
   74     }
   75 
   76     // Check r2 version
   77     QString r2version = r_core_version();
   78     QString localVersion = "" R2_GITTAP;
   79     if (r2version != localVersion) {
   80         QMessageBox msg;
   81         msg.setIcon(QMessageBox::Critical);
   82         msg.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
   83         msg.setWindowTitle(QObject::tr("Version mismatch!"));
   84         msg.setText(QString(
   85                         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(
   86                         localVersion, r2version));
   87         if (msg.exec() == QMessageBox::No) {
   88             std::exit(1);
   89         }
   90     }
   91 
   92 #ifdef CUTTER_ENABLE_PYTHON
   93     // Init python
   94     if (!clOptions.pythonHome.isEmpty()) {
   95         Python()->setPythonHome(clOptions.pythonHome);
   96     }
   97     Python()->initialize();
   98 #endif
   99 
  100 #ifdef Q_OS_WIN
  101     // Redefine r_sys_prefix() behaviour
  102     qputenv("R_ALT_SRC_DIR", "1");
  103 #endif
  104 
  105     Core()->initialize(clOptions.enableR2Plugins);
  106     Core()->setSettings();
  107     Config()->loadInitial();
  108     Core()->loadCutterRC();
  109 
  110     Config()->setOutputRedirectionEnabled(clOptions.outputRedirectionEnabled);
  111 
  112     if (R2DecDecompiler::isAvailable()) {
  113         Core()->registerDecompiler(new R2DecDecompiler(Core()));
  114     }
  115 
  116 #if CUTTER_R2GHIDRA_STATIC
  117     Core()->registerDecompiler(new R2GhidraDecompiler(Core()));
  118 #endif
  119 
  120     Plugins()->loadPlugins(clOptions.enableCutterPlugins);
  121 
  122     for (auto &plugin : Plugins()->getPlugins()) {
  123         plugin->registerDecompilers();
  124     }
  125 
  126     mainWindow = new MainWindow();
  127     installEventFilter(mainWindow);
  128 
  129     // set up context menu shortcut display fix
  130 #if QT_VERSION_CHECK(5, 10, 0) < QT_VERSION
  131     setStyle(new CutterProxyStyle());
  132 #endif // QT_VERSION_CHECK(5, 10, 0) < QT_VERSION
  133 
  134     if (clOptions.args.empty()) {
  135         // check if this is the first execution of Cutter in this computer
  136         // Note: the execution after the preferences been reset, will be considered as first-execution
  137         if (Config()->isFirstExecution()) {
  138             mainWindow->displayWelcomeDialog();
  139         }
  140         mainWindow->displayNewFileDialog();
  141     } else { // filename specified as positional argument
  142         bool askOptions = clOptions.analLevel != AutomaticAnalysisLevel::Ask;
  143         mainWindow->openNewFile(clOptions.fileOpenOptions, askOptions);
  144     }
  145 
  146 #ifdef CUTTER_APPVEYOR_R2DEC
  147     qputenv("R2DEC_HOME", "radare2\\lib\\plugins\\r2dec-js");
  148 #endif
  149 
  150 #ifdef APPIMAGE
  151     {
  152         auto appdir = QDir(QCoreApplication::applicationDirPath()); // appdir/bin
  153         appdir.cdUp(); // appdir
  154 
  155         auto sleighHome = appdir;
  156         sleighHome.cd("share/radare2/plugins/r2ghidra_sleigh"); // appdir/share/radare2/plugins/r2ghidra_sleigh
  157         Core()->setConfig("r2ghidra.sleighhome", sleighHome.absolutePath());
  158 
  159         auto r2decHome = appdir;
  160         r2decHome.cd("share/radare2/plugins/r2dec-js"); // appdir/share/radare2/plugins/r2dec-js
  161         qputenv("R2DEC_HOME", r2decHome.absolutePath().toLocal8Bit());
  162     }
  163 #endif
  164 
  165 #ifdef Q_OS_MACOS
  166     {
  167         auto r2prefix = QDir(QCoreApplication::applicationDirPath()); // Contents/MacOS
  168         r2prefix.cdUp(); // Contents
  169         r2prefix.cd("Resources/r2"); // Contents/Resources/r2
  170 
  171         auto sleighHome = r2prefix;
  172         sleighHome.cd("share/radare2/plugins/r2ghidra_sleigh"); // Contents/Resources/r2/share/radare2/plugins/r2ghidra_sleigh
  173         Core()->setConfig("r2ghidra.sleighhome", sleighHome.absolutePath());
  174 
  175         auto r2decHome = r2prefix;
  176         r2decHome.cd("share/radare2/plugins/r2dec-js"); // Contents/Resources/r2/share/radare2/plugins/r2dec-js
  177         qputenv("R2DEC_HOME", r2decHome.absolutePath().toLocal8Bit());
  178     }
  179 #endif
  180 
  181 #ifdef Q_OS_WIN
  182     {
  183         auto sleighHome = QDir(QCoreApplication::applicationDirPath());
  184         sleighHome.cd("radare2/lib/plugins/r2ghidra_sleigh");
  185         Core()->setConfig("r2ghidra.sleighhome", sleighHome.absolutePath());
  186     }
  187 #endif
  188 }
  189 
  190 CutterApplication::~CutterApplication()
  191 {
  192     Plugins()->destroyPlugins();
  193     delete mainWindow;
  194 #ifdef CUTTER_ENABLE_PYTHON
  195     Python()->shutdown();
  196 #endif
  197 }
  198 
  199 void CutterApplication::launchNewInstance(const QStringList &args)
  200 {
  201     QProcess process(this);
  202     process.setEnvironment(QProcess::systemEnvironment());
  203     QStringList allArgs;
  204     if (!clOptions.enableCutterPlugins) {
  205         allArgs.push_back("--no-cutter-plugins");
  206     }
  207     if (!clOptions.enableR2Plugins) {
  208         allArgs.push_back("--no-r2-plugins");
  209     }
  210     allArgs.append(args);
  211     process.startDetached(qApp->applicationFilePath(), allArgs);
  212 }
  213 
  214 bool CutterApplication::event(QEvent *e)
  215 {
  216     if (e->type() == QEvent::FileOpen) {
  217         QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(e);
  218         if (openEvent) {
  219             if (m_FileAlreadyDropped) {
  220                 // We already dropped a file in macOS, let's spawn another instance
  221                 // (Like the File -> Open)
  222                 QString fileName = openEvent->file();
  223                 launchNewInstance({fileName});
  224             } else {
  225                 QString fileName = openEvent->file();
  226                 m_FileAlreadyDropped = true;
  227                 mainWindow->closeNewFileDialog();
  228                 InitialOptions options;
  229                 options.filename = fileName;
  230                 mainWindow->openNewFile(options);
  231             }
  232         }
  233     }
  234     return QApplication::event(e);
  235 }
  236 
  237 bool CutterApplication::loadTranslations()
  238 {
  239     const QString &language = Config()->getCurrLocale().bcp47Name();
  240     if (language == QStringLiteral("en") || language.startsWith(QStringLiteral("en-"))) {
  241         return true;
  242     }
  243     const auto &allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
  244         QLocale::AnyCountry);
  245 
  246     bool cutterTrLoaded = false;
  247 
  248     for (const QLocale &it : allLocales) {
  249         const QString &langPrefix = it.bcp47Name();
  250         if (langPrefix == language) {
  251             QApplication::setLayoutDirection(it.textDirection());
  252             QLocale::setDefault(it);
  253 
  254             QTranslator *trCutter = new QTranslator;
  255             QTranslator *trQtBase = new QTranslator;
  256             QTranslator *trQt = new QTranslator;
  257 
  258             const QStringList &cutterTrPaths = Config()->getTranslationsDirectories();
  259 
  260             for (const auto &trPath : cutterTrPaths) {
  261                 if (trCutter && trCutter->load(it, QLatin1String("cutter"), QLatin1String("_"), trPath)) {
  262                     installTranslator(trCutter);
  263                     cutterTrLoaded = true;
  264                     trCutter = nullptr;
  265                 }
  266                 if (trQt && trQt->load(it, "qt", "_", trPath)) {
  267                     installTranslator(trQt);
  268                     trQt = nullptr;
  269                 }
  270 
  271                 if (trQtBase && trQtBase->load(it, "qtbase", "_", trPath)) {
  272                     installTranslator(trQtBase);
  273                     trQtBase = nullptr;
  274                 }
  275             }
  276 
  277             if (trCutter) {
  278                 delete trCutter;
  279             }
  280             if (trQt) {
  281                 delete trQt;
  282             }
  283             if (trQtBase) {
  284                 delete trQtBase;
  285             }
  286             return true;
  287         }
  288     }
  289     if (!cutterTrLoaded) {
  290         qWarning() << "Cannot load Cutter's translation for " << language;
  291     }
  292     return false;
  293 }
  294 
  295 bool CutterApplication::parseCommandLineOptions()
  296 {
  297     // Keep this function in sync with documentation
  298 
  299     QCommandLineParser cmd_parser;
  300     cmd_parser.setApplicationDescription(
  301         QObject::tr("A Qt and C++ GUI for radare2 reverse engineering framework"));
  302     cmd_parser.addHelpOption();
  303     cmd_parser.addVersionOption();
  304     cmd_parser.addPositionalArgument("filename", QObject::tr("Filename to open."));
  305 
  306     QCommandLineOption analOption({"A", "anal"},
  307                                   QObject::tr("Automatically open file and optionally start analysis. "
  308                                               "Needs filename to be specified. May be a value between 0 and 2:"
  309                                               " 0 = no analysis, 1 = aaa, 2 = aaaa (experimental)"),
  310                                   QObject::tr("level"));
  311     cmd_parser.addOption(analOption);
  312 
  313     QCommandLineOption formatOption({"F", "format"},
  314                                     QObject::tr("Force using a specific file format (bin plugin)"),
  315                                     QObject::tr("name"));
  316     cmd_parser.addOption(formatOption);
  317 
  318     QCommandLineOption baddrOption({"B", "base"},
  319                                    QObject::tr("Load binary at a specific base address"),
  320                                    QObject::tr("base address"));
  321     cmd_parser.addOption(baddrOption);
  322 
  323     QCommandLineOption scriptOption("i",
  324                                     QObject::tr("Run script file"),
  325                                     QObject::tr("file"));
  326     cmd_parser.addOption(scriptOption);
  327 
  328     QCommandLineOption pythonHomeOption("pythonhome",
  329                                         QObject::tr("PYTHONHOME to use for embedded python interpreter"),
  330                                         "PYTHONHOME");
  331     cmd_parser.addOption(pythonHomeOption);
  332 
  333     QCommandLineOption disableRedirectOption("no-output-redirect",
  334                                              QObject::tr("Disable output redirection."
  335                                                          " Some of the output in console widget will not be visible."
  336                                                          " Use this option when debuging a crash or freeze and output "
  337                                                          " redirection is causing some messages to be lost."));
  338     cmd_parser.addOption(disableRedirectOption);
  339 
  340     QCommandLineOption disablePlugins("no-plugins",
  341                                       QObject::tr("Do not load plugins"));
  342     cmd_parser.addOption(disablePlugins);
  343 
  344     QCommandLineOption disableCutterPlugins("no-cutter-plugins",
  345                                             QObject::tr("Do not load Cutter plugins"));
  346     cmd_parser.addOption(disableCutterPlugins);
  347 
  348     QCommandLineOption disableR2Plugins("no-r2-plugins",
  349                                         QObject::tr("Do not load radare2 plugins"));
  350     cmd_parser.addOption(disableR2Plugins);
  351 
  352     cmd_parser.process(*this);
  353 
  354     CutterCommandLineOptions opts;
  355     opts.args = cmd_parser.positionalArguments();
  356 
  357     if (cmd_parser.isSet(analOption)) {
  358         bool analLevelSpecified = false;
  359         int analLevel = cmd_parser.value(analOption).toInt(&analLevelSpecified);
  360 
  361         if (!analLevelSpecified || analLevel < 0 || analLevel > 2) {
  362             fprintf(stderr, "%s\n",
  363                     QObject::tr("Invalid Analysis Level. May be a value between 0 and 2.").toLocal8Bit().constData());
  364             return false;
  365         }
  366         switch (analLevel) {
  367         case 0:
  368             opts.analLevel = AutomaticAnalysisLevel::None;
  369             break;
  370         case 1:
  371             opts.analLevel = AutomaticAnalysisLevel::AAA;
  372             break;
  373         case 2:
  374             opts.analLevel = AutomaticAnalysisLevel::AAAA;
  375             break;
  376         }
  377     }
  378 
  379     if (opts.args.empty() && opts.analLevel != AutomaticAnalysisLevel::Ask) {
  380         fprintf(stderr, "%s\n",
  381                 QObject::tr("Filename must be specified to start analysis automatically.").toLocal8Bit().constData());
  382         return false;
  383     }
  384 
  385     InitialOptions options;
  386     if (!opts.args.isEmpty()) {
  387         opts.fileOpenOptions.filename = opts.args[0];
  388         opts.fileOpenOptions.forceBinPlugin = cmd_parser.value(formatOption);
  389         if (cmd_parser.isSet(baddrOption)) {
  390             bool ok;
  391             RVA baddr = cmd_parser.value(baddrOption).toULongLong(&ok, 0);
  392             if (ok) {
  393                 options.binLoadAddr = baddr;
  394             }
  395         }
  396         switch (opts.analLevel) {
  397         case AutomaticAnalysisLevel::Ask:
  398             break;
  399         case AutomaticAnalysisLevel::None:
  400             opts.fileOpenOptions.analCmd = {};
  401             break;
  402         case AutomaticAnalysisLevel::AAA:
  403             opts.fileOpenOptions.analCmd = { {"aaa", "Auto analysis"} };
  404             break;
  405         case AutomaticAnalysisLevel::AAAA:
  406             opts.fileOpenOptions.analCmd = { {"aaaa", "Auto analysis (experimental)"} };
  407             break;
  408         }
  409         opts.fileOpenOptions.script = cmd_parser.value(scriptOption);
  410     }
  411 
  412     if (cmd_parser.isSet(pythonHomeOption)) {
  413         opts.pythonHome = cmd_parser.value(pythonHomeOption);
  414     }
  415 
  416     opts.outputRedirectionEnabled = !cmd_parser.isSet(disableRedirectOption);
  417     if (cmd_parser.isSet(disablePlugins)) {
  418         opts.enableCutterPlugins = false;
  419         opts.enableR2Plugins = false;
  420     }
  421 
  422     if (cmd_parser.isSet(disableCutterPlugins)) {
  423         opts.enableCutterPlugins = false;
  424     }
  425 
  426     if (cmd_parser.isSet(disableR2Plugins)) {
  427         opts.enableR2Plugins = false;
  428     }
  429 
  430     this->clOptions = opts;
  431     return true;
  432 }
  433 
  434 
  435 void CutterProxyStyle::polish(QWidget *widget)
  436 {
  437     QProxyStyle::polish(widget);
  438 #if QT_VERSION_CHECK(5, 10, 0) < QT_VERSION
  439     // HACK: This is the only way I've found to force Qt (5.10 and newer) to
  440     //       display shortcuts in context menus on all platforms. It's ugly,
  441     //       but it gets the job done.
  442     if (auto menu = qobject_cast<QMenu *>(widget)) {
  443         const auto &actions = menu->actions();
  444         for (auto action : actions) {
  445             action->setShortcutVisibleInContextMenu(true);
  446         }
  447     }
  448 #endif // QT_VERSION_CHECK(5, 10, 0) < QT_VERSION
  449 }