예제 #1
0
class EmulatorConfigsDialog(QDialog):
    def __init__(self, dwarf, parent=None):
        super(EmulatorConfigsDialog, self).__init__(parent)
        self.dwarf = dwarf
        self._prefs = Prefs()

        layout = QVBoxLayout(self)

        self.setMinimumWidth(500)

        layout.addWidget(QLabel('callbacks path'))
        callbacks_layout = QHBoxLayout()
        pick_path = QPushButton('choose')
        pick_path.clicked.connect(self.pick_callbacks_path)
        current_callbacks_path = self._prefs.get(prefs.EMULATOR_CALLBACKS_PATH)
        if current_callbacks_path == '':
            current_callbacks_path = 'none'
        self.callbacks_path_label = QLabel(current_callbacks_path)
        callbacks_layout.addWidget(pick_path)
        callbacks_layout.addWidget(self.callbacks_path_label)
        layout.addLayout(callbacks_layout)

        layout.addWidget(QLabel('delay between instructions'))
        self.instructions_delay = QLineEdit()
        self.instructions_delay.setText(
            str(self._prefs.get(prefs.EMULATOR_INSTRUCTIONS_DELAY, 0.5)))
        layout.addWidget(self.instructions_delay)

        buttons = QHBoxLayout()
        cancel = QPushButton('cancel')
        cancel.clicked.connect(self.close)
        buttons.addWidget(cancel)
        accept = QPushButton('accept')
        accept.clicked.connect(self.accept)
        buttons.addWidget(accept)

        layout.addLayout(buttons)

    def pick_callbacks_path(self):
        r = QFileDialog.getOpenFileName()
        if len(r) > 0 and len(r[0]) > 0:
            self._prefs.put(prefs.EMULATOR_CALLBACKS_PATH, r[0])
            self.callbacks_path_label.setText(r[0])

    @staticmethod
    def show_dialog(dwarf):
        dialog = EmulatorConfigsDialog(dwarf)
        result = dialog.exec_()

        if result == QDialog.Accepted:
            try:
                dialog._prefs.put(prefs.EMULATOR_INSTRUCTIONS_DELAY,
                                  float(dialog.instructions_delay.text()))
            except:
                pass
예제 #2
0
파일: app.py 프로젝트: lex0tanil/Dwarf
class AppWindow(QMainWindow):
    onRestart = pyqtSignal(name='onRestart')

    def __init__(self, dwarf_args, flags=None):
        super(AppWindow, self).__init__(flags)

        self.dwarf_args = dwarf_args

        self.session_manager = SessionManager(self)
        self.session_manager.sessionCreated.connect(self.session_created)
        self.session_manager.sessionStopped.connect(self.session_stopped)
        self.session_manager.sessionClosed.connect(self.session_closed)

        self._tab_order = [
            'memory', 'modules', 'ranges', 'jvm-inspector', 'jvm-debugger'
        ]

        self.menu = self.menuBar()
        self._is_newer_dwarf = False
        self.view_menu = None

        #dockwidgets
        self.watchers_dwidget = None
        self.hooks_dwiget = None
        self.bookmarks_dwiget = None
        self.registers_dock = None
        self.console_dock = None
        self.backtrace_dock = None
        self.threads_dock = None
        #panels
        self.asm_panel = None
        self.console_panel = None
        self.context_panel = None
        self.backtrace_panel = None
        self.contexts_list_panel = None
        self.data_panel = None
        self.emulator_panel = None
        self.ftrace_panel = None
        self.hooks_panel = None
        self.bookmarks_panel = None
        self.smali_panel = None
        self.java_inspector_panel = None
        self.java_explorer_panel = None
        self.java_trace_panel = None
        self.memory_panel = None
        self.modules_panel = None
        self.ranges_panel = None
        self.search_panel = None
        self.trace_panel = None
        self.watchers_panel = None
        self.welcome_window = None

        self._ui_elems = []

        self.setWindowTitle(
            'Dwarf - A debugger for reverse engineers, crackers and security analyst'
        )

        # load external assets
        _app = QApplication.instance()

        self.remove_tmp_dir()

        # themes
        self.prefs = Prefs()
        self.set_theme(self.prefs.get('dwarf_ui_theme', 'black'))

        # load font
        if os.path.exists(utils.resource_path('assets/Anton.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/Anton.ttf'))
        if os.path.exists(utils.resource_path('assets/OpenSans-Regular.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/OpenSans-Regular.ttf'))
            _app.setFont(QFont("OpenSans", 9, QFont.Normal))
            if os.path.exists(utils.resource_path('assets/OpenSans-Bold.ttf')):
                QFontDatabase.addApplicationFont(
                    utils.resource_path('assets/OpenSans-Bold.ttf'))

        # mainwindow statusbar
        self.progressbar = QProgressBar()
        self.progressbar.setRange(0, 0)
        self.progressbar.setVisible(False)
        self.progressbar.setFixedHeight(15)
        self.progressbar.setFixedWidth(100)
        self.progressbar.setTextVisible(False)
        self.progressbar.setValue(30)
        self.statusbar = QStatusBar(self)
        self.statusbar.setAutoFillBackground(False)
        self.statusbar.addPermanentWidget(self.progressbar)
        self.statusbar.setObjectName("statusbar")
        self.setStatusBar(self.statusbar)

        self.main_tabs = QTabWidget(self)
        self.main_tabs.setMovable(False)
        self.main_tabs.setTabsClosable(True)
        self.main_tabs.setAutoFillBackground(True)
        self.main_tabs.tabCloseRequested.connect(self._on_close_tab)
        self.setCentralWidget(self.main_tabs)

        if self.dwarf_args.package is None:
            self.welcome_window = WelcomeDialog(self)
            self.welcome_window.setModal(True)
            self.welcome_window.onIsNewerVersion.connect(
                self._enable_update_menu)
            self.welcome_window.onUpdateComplete.connect(
                self._on_dwarf_updated)
            self.welcome_window.setWindowTitle(
                'Welcome to Dwarf - A debugger for reverse engineers, crackers and security analyst'
            )
            self.welcome_window.onSessionSelected.connect(self._start_session)
            self.welcome_window.onSessionRestore.connect(self._restore_session)
            # wait for welcome screen
            self.hide()
            self.welcome_window.show()
        else:
            if dwarf_args.package is not None:
                if dwarf_args.type is None:
                    # no device given check if package is local path
                    if os.path.exists(dwarf_args.package):
                        print('* Starting new LocalSession')
                        self._start_session('local')
                    else:
                        print('use -t to set sessiontype')
                        exit(0)
                else:
                    print('* Starting new Session')
                    self._start_session(dwarf_args.type)

    def _setup_main_menu(self):
        self.menu = self.menuBar()
        dwarf_menu = QMenu('Dwarf', self)
        theme = QMenu('Theme', dwarf_menu)
        theme.addAction('Black')
        theme.addAction('Dark')
        theme.addAction('Light')
        theme.triggered.connect(self._set_theme)
        dwarf_menu.addMenu(theme)
        dwarf_menu.addSeparator()
        if self._is_newer_dwarf:
            dwarf_menu.addAction('Update', self._update_dwarf)
        dwarf_menu.addAction('Close', self.session_manager.session.stop)
        self.menu.addMenu(dwarf_menu)

        session = self.session_manager.session
        if session is not None:
            session_menu = session.main_menu
            if isinstance(session_menu, list):
                for menu in session_menu:
                    self.menu.addMenu(menu)
            else:
                self.menu.addMenu(session_menu)

        self.view_menu = QMenu('View', self)
        subview_menu = QMenu('Subview', self.view_menu)
        subview_menu.addAction('Search',
                               lambda: self.show_main_tab('search'),
                               shortcut=QKeySequence(Qt.CTRL + Qt.Key_F3))
        subview_menu.addAction('Emulator',
                               lambda: self.show_main_tab('emulator'),
                               shortcut=QKeySequence(Qt.CTRL + Qt.Key_F2))
        subview_menu.addAction('Disassembly',
                               lambda: self.show_main_tab('disassembly'),
                               shortcut=QKeySequence(Qt.CTRL + Qt.Key_F5))
        self.view_menu.addMenu(subview_menu)
        self.view_menu.addSeparator()
        self.menu.addMenu(self.view_menu)

        if self.dwarf_args.debug_script:
            debug_menu = QMenu('Debug', self)
            debug_menu.addAction('Reload core', self._menu_reload_core)
            debug_menu.addAction('Debug dwarf js core',
                                 self._menu_debug_dwarf_js)
            self.menu.addMenu(debug_menu)

        about_menu = QMenu('About', self)
        about_menu.addAction('Dwarf on GitHub', self._menu_github)
        about_menu.addAction('Documention', self._menu_documentation)
        about_menu.addAction('Api', self._menu_api)
        about_menu.addAction('Slack', self._menu_slack)
        about_menu.addSeparator()
        about_menu.addAction('Info', self._show_about_dlg)
        self.menu.addMenu(about_menu)

    def _enable_update_menu(self):
        self._is_newer_dwarf = True

    def _update_dwarf(self):
        if self.welcome_window:
            self.welcome_window._update_dwarf()

    def _on_close_tab(self, index):
        tab_text = self.main_tabs.tabText(index)
        if tab_text:
            if tab_text.lower() in self.session_manager.session.non_closable:
                return
            try:
                self._ui_elems.remove(tab_text.lower())
            except ValueError:  # recheck ValueError: list.remove(x): x not in list
                pass
            self.main_tabs.removeTab(index)

    def _handle_tab_change(self):
        for index in range(self.main_tabs.count()):
            tab_name = self.main_tabs.tabText(index).lower().replace(' ', '-')
            if tab_name in self.session_manager.session.non_closable:
                self.main_tabs.tabBar().setTabButton(index, QTabBar.RightSide,
                                                     None)

                if tab_name in self._tab_order:
                    should_index = self._tab_order.index(tab_name)
                    if index != should_index:
                        self.main_tabs.tabBar().moveTab(index, should_index)

    def _on_dwarf_updated(self):
        self.onRestart.emit()

    def remove_tmp_dir(self):
        if os.path.exists('.tmp'):
            shutil.rmtree('.tmp', ignore_errors=True)

    def _set_theme(self, qaction):
        if qaction:
            self.set_theme(qaction.text())

    def _menu_reload_core(self):
        self.dwarf.load_script()

    def _menu_debug_dwarf_js(self):
        you_know_what_to_do = json.loads(
            self.dwarf._script.exports.debugdwarfjs())
        return you_know_what_to_do

    def show_main_tab(self, name):
        # elem doesnt exists? create it
        if name not in self._ui_elems:
            self._create_ui_elem(name)

        index = 0
        name = name.join(name.split()).lower()
        if name == 'memory':
            index = self.main_tabs.indexOf(self.memory_panel)
        elif name == 'ranges':
            index = self.main_tabs.indexOf(self.ranges_panel)
        elif name == 'search':
            index = self.main_tabs.indexOf(self.search_panel)
        elif name == 'modules':
            index = self.main_tabs.indexOf(self.modules_panel)
        elif name == 'disassembly':
            index = self.main_tabs.indexOf(self.asm_panel)
        elif name == 'trace':
            index = self.main_tabs.indexOf(self.trace_panel)
        elif name == 'data':
            index = self.main_tabs.indexOf(self.data_panel)
        elif name == 'emulator':
            index = self.main_tabs.indexOf(self.emulator_panel)
        elif name == 'java-trace':
            index = self.main_tabs.indexOf(self.java_trace_panel)
        elif name == 'jvm-inspector':
            index = self.main_tabs.indexOf(self.java_inspector_panel)
        elif name == 'jvm-debugger':
            index = self.main_tabs.indexOf(self.java_explorer_panel)
        elif name == 'smali':
            index = self.main_tabs.indexOf(self.smali_panel)

        self.main_tabs.setCurrentIndex(index)

    def jump_to_address(self, ptr, show_panel=True):
        if self.memory_panel is not None:
            if show_panel:
                self.show_main_tab('memory')
            self.memory_panel.read_memory(ptr)

    @pyqtSlot(name='mainMenuGitHub')
    def _menu_github(self):
        QDesktopServices.openUrl(QUrl('https://github.com/iGio90/Dwarf'))

    @pyqtSlot(name='mainMenuDocumentation')
    def _menu_api(self):
        QDesktopServices.openUrl(QUrl('https://igio90.github.io/Dwarf/'))

    @pyqtSlot(name='mainMenuApi')
    def _menu_documentation(self):
        QDesktopServices.openUrl(QUrl('https://igio90.github.io/Dwarf/api'))

    @pyqtSlot(name='mainMenuSlack')
    def _menu_slack(self):
        QDesktopServices.openUrl(
            QUrl('https://join.slack.com/t/resecret/shared_invite'
                 '/enQtMzc1NTg4MzE3NjA1LTlkNzYxNTIwYTc2ZTYyOWY1MT'
                 'Q1NzBiN2ZhYjQwYmY0ZmRhODQ0NDE3NmRmZjFiMmE1MDYwN'
                 'WJlNDVjZDcwNGE'))

    def _show_about_dlg(self):
        about_dlg = AboutDialog(self)
        about_dlg.show()

    def _create_ui_elem(self, elem):
        if not isinstance(elem, str):
            return

        if elem not in self._ui_elems:
            self._ui_elems.append(elem)

        if elem == 'watchers':
            from ui.panel_watchers import WatchersPanel
            self.watchers_dwidget = QDockWidget('Watchers', self)
            self.watchers_panel = WatchersPanel(self)
            # dont respond to dblclick mem cant be shown
            # self.watchers_panel.onItemDoubleClicked.connect(
            #    self._on_watcher_clicked)
            self.watchers_panel.onItemRemoved.connect(
                self._on_watcher_removeditem)
            self.watchers_panel.onItemAdded.connect(self._on_watcher_added)
            self.watchers_dwidget.setWidget(self.watchers_panel)
            self.watchers_dwidget.setObjectName('WatchersPanel')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.watchers_dwidget)
            self.view_menu.addAction(self.watchers_dwidget.toggleViewAction())
        elif elem == 'hooks':
            from ui.panel_hooks import HooksPanel
            self.hooks_dwiget = QDockWidget('Breakpoints', self)
            self.hooks_panel = HooksPanel(self)
            self.hooks_panel.onShowMemoryRequest.connect(
                self._on_watcher_clicked)
            self.hooks_panel.onHookRemoved.connect(self._on_hook_removed)
            self.hooks_dwiget.setWidget(self.hooks_panel)
            self.hooks_dwiget.setObjectName('HooksPanel')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.hooks_dwiget)
            self.view_menu.addAction(self.hooks_dwiget.toggleViewAction())
        elif elem == 'bookmarks':
            from ui.panel_bookmarks import BookmarksPanel
            self.bookmarks_dwiget = QDockWidget('Boomarks', self)
            self.bookmarks_panel = BookmarksPanel(self)
            self.bookmarks_panel.onShowMemoryRequest.connect(
                self._on_watcher_clicked)
            self.bookmarks_dwiget.setWidget(self.bookmarks_panel)
            self.bookmarks_dwiget.setObjectName('BookmarksPanel')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.bookmarks_dwiget)
            self.view_menu.addAction(self.bookmarks_dwiget.toggleViewAction())
        elif elem == 'registers':
            from ui.panel_context import ContextPanel
            self.registers_dock = QDockWidget('Context', self)
            self.context_panel = ContextPanel(self)
            self.registers_dock.setWidget(self.context_panel)
            self.registers_dock.setObjectName('ContextsPanel')
            self.addDockWidget(Qt.RightDockWidgetArea, self.registers_dock)
            self.view_menu.addAction(self.registers_dock.toggleViewAction())
        elif elem == 'memory':
            from ui.panel_memory import MemoryPanel
            self.memory_panel = MemoryPanel(self)
            self.memory_panel.onShowDisassembly.connect(
                self._disassemble_range)
            self.memory_panel.dataChanged.connect(self._on_memory_modified)
            self.memory_panel.statusChanged.connect(self.set_status_text)
            self.main_tabs.addTab(self.memory_panel, 'Memory')
        elif elem == 'jvm-debugger':
            from ui.panel_java_explorer import JavaExplorerPanel
            self.java_explorer_panel = JavaExplorerPanel(self)
            self.main_tabs.addTab(self.java_explorer_panel, 'JVM debugger')
            self.main_tabs.tabBar().moveTab(
                self.main_tabs.indexOf(self.java_explorer_panel), 1)
        elif elem == 'jvm-inspector':
            from ui.panel_java_inspector import JavaInspector
            self.java_inspector_panel = JavaInspector(self)
            self.main_tabs.addTab(self.java_inspector_panel, 'JVM inspector')
        elif elem == 'console':
            from ui.panel_console import ConsolePanel
            self.console_dock = QDockWidget('Console', self)
            self.console_panel = ConsolePanel(self)
            self.dwarf.onLogToConsole.connect(self._log_js_output)
            self.console_dock.setWidget(self.console_panel)
            self.console_dock.setObjectName('ConsolePanel')
            self.addDockWidget(Qt.BottomDockWidgetArea, self.console_dock)
            self.view_menu.addAction(self.console_dock.toggleViewAction())
        elif elem == 'backtrace':
            from ui.panel_backtrace import BacktracePanel
            self.backtrace_dock = QDockWidget('Backtrace', self)
            self.backtrace_panel = BacktracePanel(self)
            self.backtrace_dock.setWidget(self.backtrace_panel)
            self.backtrace_dock.setObjectName('BacktracePanel')
            self.backtrace_panel.onShowMemoryRequest.connect(
                self._on_watcher_clicked)
            self.addDockWidget(Qt.RightDockWidgetArea, self.backtrace_dock)
            self.view_menu.addAction(self.backtrace_dock.toggleViewAction())
        elif elem == 'threads':
            from ui.panel_contexts_list import ContextsListPanel
            self.threads_dock = QDockWidget('Threads', self)
            self.contexts_list_panel = ContextsListPanel(self)
            self.dwarf.onThreadResumed.connect(
                self.contexts_list_panel.resume_tid)
            self.contexts_list_panel.onItemDoubleClicked.connect(
                self._manually_apply_context)
            self.threads_dock.setWidget(self.contexts_list_panel)
            self.threads_dock.setObjectName('ThreadPanel')
            self.addDockWidget(Qt.RightDockWidgetArea, self.threads_dock)
            self.view_menu.addAction(self.threads_dock.toggleViewAction())
        elif elem == 'modules':
            from ui.panel_modules import ModulesPanel
            self.modules_panel = ModulesPanel(self)
            self.modules_panel.onModuleSelected.connect(
                self._on_module_dblclicked)
            self.modules_panel.onModuleFuncSelected.connect(
                self._on_modulefunc_dblclicked)
            self.modules_panel.onAddHook.connect(self._on_addmodule_hook)
            self.modules_panel.onDumpBinary.connect(self._on_dumpmodule)
            self.main_tabs.addTab(self.modules_panel, 'Modules')
        elif elem == 'ranges':
            from ui.panel_ranges import RangesPanel
            self.ranges_panel = RangesPanel(self)
            self.ranges_panel.onItemDoubleClicked.connect(
                self._range_dblclicked)
            self.ranges_panel.onDumpBinary.connect(self._on_dumpmodule)
            # connect to watcherpanel func
            self.ranges_panel.onAddWatcher.connect(
                self.watchers_panel.do_addwatcher_dlg)
            self.main_tabs.addTab(self.ranges_panel, 'Ranges')
        elif elem == 'search':
            from ui.panel_search import SearchPanel
            self.search_panel = SearchPanel(self)
            self.search_panel.onShowMemoryRequest.connect(
                self._on_watcher_clicked)
            self.main_tabs.addTab(self.search_panel, 'Search')
        elif elem == 'data':
            from ui.panel_data import DataPanel
            self.data_panel = DataPanel(self)
            self.main_tabs.addTab(self.data_panel, 'Data')
        elif elem == 'trace':
            from ui.panel_trace import TracePanel
            self.trace_panel = TracePanel(self)
            self.main_tabs.addTab(self.trace_panel, 'Trace')
        elif elem == 'disassembly':
            from ui.widgets.disasm_view import DisassemblyView
            self.asm_panel = DisassemblyView(self)
            self.asm_panel.onShowMemoryRequest.connect(self._on_disasm_showmem)
            self.main_tabs.addTab(self.asm_panel, 'Disassembly')
        elif elem == 'emulator':
            from ui.panel_emulator import EmulatorPanel
            self.emulator_panel = EmulatorPanel(self)
            self.main_tabs.addTab(self.emulator_panel, 'Emulator')
        elif elem == 'java-trace':
            from ui.panel_java_trace import JavaTracePanel
            self.java_trace_panel = JavaTracePanel(self)
            self.main_tabs.addTab(self.java_trace_panel, 'JVM tracer')
        elif elem == 'smali':
            from ui.panel_smali import SmaliPanel
            self.smali_panel = SmaliPanel()
            self.main_tabs.addTab(self.smali_panel, 'Smali')
        else:
            print('no handler for elem: ' + elem)

        # make tabs unclosable and sort
        self._handle_tab_change()

        # TODO: remove add @2x
        for item in self.findChildren(QDockWidget):
            if item:
                if 'darwin' in sys.platform:
                    item.setStyleSheet(
                        'QDockWidget::title { padding-left:-30px; } QDockWidget::close-button, QDockWidget::float-button  { width: 10px; height:10px }'
                    )

    def set_theme(self, theme):
        if theme:
            theme = theme.replace(os.pardir, '').replace('.', '')
            theme = theme.join(theme.split()).lower()
            theme_style = 'assets/' + theme + '_style.qss'
            if not os.path.exists(utils.resource_path(theme_style)):
                return

            self.prefs.put('dwarf_ui_theme', theme)

            try:
                _app = QApplication.instance()
                with open(theme_style) as stylesheet:
                    _app.setStyleSheet(_app.styleSheet() + '\n' +
                                       stylesheet.read())
            except Exception as e:
                pass
                # err = self.dwarf.spawn(dwarf_args.package, dwarf_args.script)

    def set_status_text(self, txt):
        self.statusbar.showMessage(txt)

    # ************************************************************************
    # **************************** Properties ********************************
    # ************************************************************************
    @property
    def disassembly(self):
        return self.asm_panel

    @property
    def backtrace(self):
        return self.backtrace_panel

    @property
    def console(self):
        return self.console_panel

    @property
    def context(self):
        return self.context_panel

    @property
    def threads(self):
        return self.contexts_list_panel

    @property
    def emulator(self):
        return self.emulator_panel

    @property
    def ftrace(self):
        return self.ftrace_panel

    @property
    def hooks(self):
        return self.hooks_panel

    @property
    def java_inspector(self):
        return self.java_inspector_panel

    @property
    def java_explorer(self):
        return self.java_explorer_panel

    @property
    def memory(self):
        return self.memory_panel

    @property
    def modules(self):
        return self.memory_panel

    @property
    def ranges(self):
        return self.ranges_panel

    @property
    def trace(self):
        return self.trace_panel

    @property
    def watchers(self):
        return self.watchers_panel

    @property
    def dwarf(self):
        if self.session_manager.session is not None:
            return self.session_manager.session.dwarf
        else:
            return None

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    # session handlers
    def _start_session(self, session_type, session_data=None):
        if self.welcome_window is not None:
            self.welcome_window.close()
        self.session_manager.create_session(session_type,
                                            session_data=session_data)

    def _restore_session(self, session_data):
        if 'session' in session_data:
            session_type = session_data['session']
            self._start_session(session_type, session_data=session_data)

    def session_created(self):
        # session init done create ui for it
        session = self.session_manager.session
        self._setup_main_menu()
        for ui_elem in session.session_ui_sections:
            ui_elem = ui_elem.join(ui_elem.split()).lower()
            self._create_ui_elem(ui_elem)

        self.dwarf.onAttached.connect(self._on_attached)
        self.dwarf.onScriptLoaded.connect(self._on_script_loaded)

        # hookup
        self.dwarf.onSetRanges.connect(self._on_setranges)
        self.dwarf.onSetModules.connect(self._on_setmodules)

        self.dwarf.onAddNativeHook.connect(self._on_add_hook)
        self.dwarf.onApplyContext.connect(self._apply_context)
        self.dwarf.onThreadResumed.connect(self.on_tid_resumed)

        self.dwarf.onTraceData.connect(self._on_tracer_data)
        self.dwarf.onSetData.connect(self._on_set_data)

        self.session_manager.start_session(self.dwarf_args)
        q_settings = QSettings("dwarf_window_pos.ini", QSettings.IniFormat)
        ui_state = q_settings.value('dwarf_ui_state')
        if ui_state:
            self.restoreGeometry(ui_state)
        window_state = q_settings.value('dwarf_ui_window', self.saveState())
        if window_state:
            self.restoreState(window_state)

        self.showMaximized()

    def session_stopped(self):
        self.remove_tmp_dir()
        self.menu.clear()

        self.main_tabs.clear()

        # actually we need to kill this. needs a refactor
        if self.java_trace_panel is not None:
            self.java_trace_panel = None

        for elem in self._ui_elems:
            if elem == 'watchers':
                self.watchers_panel.clear_list()
                self.watchers_panel.close()
                self.watchers_panel = None
                self.removeDockWidget(self.watchers_dwidget)
                self.watchers_dwidget = None
            elif elem == 'hooks':
                self.hooks_panel.close()
                self.hooks_panel = None
                self.removeDockWidget(self.hooks_dwiget)
                self.hooks_dwiget = None
            elif elem == 'registers':
                self.context_panel.close()
                self.context_panel = None
                self.removeDockWidget(self.registers_dock)
                self.registers_dock = None
            elif elem == 'memory':
                self.memory_panel.close()
                self.memory_panel = None
                self.main_tabs.removeTab(0)
                # self.main_tabs
            elif elem == 'jvm-debugger':
                self.java_explorer_panel.close()
                self.java_explorer_panel = None
                self.removeDockWidget(self.watchers_dwidget)
            elif elem == 'console':
                self.console_panel.close()
                self.console_panel = None
                self.removeDockWidget(self.console_dock)
                self.console_dock = None
            elif elem == 'backtrace':
                self.backtrace_panel.close()
                self.backtrace_panel = None
                self.removeDockWidget(self.backtrace_dock)
            elif elem == 'threads':
                self.contexts_list_panel.close()
                self.contexts_list_panel = None
                self.removeDockWidget(self.threads_dock)
                self.threads_dock = None
            elif elem == 'bookmarks':
                self.bookmarks_panel.close()
                self.bookmarks_panel = None
                self.removeDockWidget(self.bookmarks_dwiget)
                self.bookmarks_dwiget = None

    def session_closed(self):
        self._ui_elems = []
        self.hide()
        if self.welcome_window is not None:
            self.welcome_window.exec()

        # close if it was a commandline session
        if self.welcome_window is None:
            if self.dwarf_args.package:
                self.close()

    # ui handler
    def closeEvent(self, event):
        """ Window closed
            save stuff or whatever at exit

            detaches dwarf
        """
        # save windowstuff
        q_settings = QSettings("dwarf_window_pos.ini", QSettings.IniFormat)
        q_settings.setValue('dwarf_ui_state', self.saveGeometry())
        q_settings.setValue('dwarf_ui_window', self.saveState())

        if self.dwarf:
            self.dwarf.detach()
        super().closeEvent(event)

    def _on_watcher_clicked(self, ptr):
        """ Address in Watcher/Hookpanel was clicked
            show Memory
        """
        if '.' in ptr:  # java_hook
            file_path = ptr.replace('.', os.path.sep)
            if os.path.exists('.tmp/smali/' + file_path + '.smali'):
                if self.smali_panel is None:
                    self._create_ui_elem('smali')
                self.smali_panel.set_file('.tmp/smali/' + file_path + '.smali')
                self.show_main_tab('smali')
        else:
            self.memory_panel.read_memory(ptr=ptr)
            self.show_main_tab('memory')

    def _on_disasm_showmem(self, ptr, length):
        """ Address in Disasm was clicked
            adds temphighlight for bytes from current instruction
        """
        self.memory_panel.read_memory(ptr)
        self.memory_panel.add_highlight(
            HighLight('attention', utils.parse_ptr(ptr), length))
        self.show_main_tab('memory')

    def _on_watcher_added(self, ptr):
        """ Watcher Entry was added
        """
        try:
            # set highlight
            self.memory_panel.add_highlight(
                HighLight('watcher', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

    def _on_watcher_removeditem(self, ptr):
        """ Watcher Entry was removed
            remove highlight too
        """
        self.memory_panel.remove_highlight(ptr)

    def _on_module_dblclicked(self, data):
        """ Module in ModulePanel was doubleclicked
        """
        addr, size = data
        addr = utils.parse_ptr(addr)
        size = int(size, 10)
        self.memory_panel.read_memory(ptr=addr, length=size)
        self.show_main_tab('Memory')

    def _on_modulefunc_dblclicked(self, ptr):
        """ Function in ModulePanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.memory_panel.read_memory(ptr=ptr)
        self.show_main_tab('Memory')

    def _on_dumpmodule(self, data):
        """ DumpBinary MenuItem in ModulePanel was selected
        """
        ptr, size = data
        ptr = utils.parse_ptr(ptr)
        size = int(size, 10)
        self.dwarf.dump_memory(ptr=ptr, length=size)

    def _disassemble_range(self, mem_range):
        """ Disassemble MenuItem in Hexview was selected
        """
        if mem_range:
            if self.asm_panel is None:
                self._create_ui_elem('disassembly')

            if mem_range:
                self.asm_panel.disassemble(mem_range)
                self.show_main_tab('disassembly')

    def _range_dblclicked(self, ptr):
        """ Range in RangesPanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.memory_panel.read_memory(ptr=ptr)
        self.show_main_tab('Memory')

    # dwarf handlers
    def _log_js_output(self, output):
        if self.console_panel is not None:
            self.console_panel.get_js_console().log(output)

    def _on_setranges(self, ranges):
        """ Dwarf wants to set Ranges
            only hooked up to switch tab or create ui
            its connected in panel after creation
        """
        if self.ranges_panel is None:
            self.show_main_tab('ranges')
            # forward only now to panel it connects after creation
            self.ranges_panel.set_ranges(ranges)

    def _on_setmodules(self, modules):
        """ Dwarf wants to set Modules
            only hooked up to switch tab or create ui
            its connected in panel after creation
        """
        if self.modules_panel is None:
            self._create_ui_elem('modules')
            self.modules_panel.set_modules(modules)

        if self.modules_panel is not None:
            self.show_main_tab('modules')

    def _manually_apply_context(self, context):
        """
        perform additional operation if the context has been manually applied from the context list
        """
        self._apply_context(context, manual=True)

    def _apply_context(self, context, manual=False):
        # update current context tid
        # this should be on top as any further api from js needs to be executed on that thread
        is_initial_hook = context['reason'] >= 0
        if manual or (self.dwarf.context_tid and not is_initial_hook):
            self.dwarf.context_tid = context['tid']

        if 'context' in context:
            if not manual:
                self.threads.add_context(context)

            is_java = context['is_java']
            if is_java:
                if self.java_explorer_panel is None:
                    self._create_ui_elem('jvm-debugger')
                self.context_panel.set_context(context['ptr'], 1,
                                               context['context'])
                self.java_explorer_panel.set_handle_arg(-1)
                self.show_main_tab('jvm-debugger')
            else:
                self.context_panel.set_context(context['ptr'], 0,
                                               context['context'])

                if 'pc' in context['context']:
                    if not 'disassembly' in self._ui_elems:
                        from lib.range import Range
                        _range = Range(Range.SOURCE_TARGET, self.dwarf)
                        _range.init_with_address(
                            int(context['context']['pc']['value'], 16))

                        self._disassemble_range(_range)

        if 'backtrace' in context:
            self.backtrace_panel.set_backtrace(context['backtrace'])

    def _on_add_hook(self, hook):
        try:
            # set highlight
            ptr = hook.get_ptr()
            ptr = utils.parse_ptr(ptr)
            self.memory_panel.add_highlight(
                HighLight('hook', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

    def _on_hook_removed(self, ptr):
        ptr = utils.parse_ptr(ptr)
        self.memory_panel.remove_highlight(ptr)

    def _on_addmodule_hook(self, data):
        ptr, name = data
        self.dwarf.hook_native(ptr, own_input=name)

    def on_tid_resumed(self, tid):
        if self.dwarf:
            if self.dwarf.context_tid == tid:
                # clear backtrace
                if 'backtrace' in self._ui_elems:
                    if self.backtrace_panel is not None:
                        self.backtrace_panel.clear()

                # remove thread
                if 'threads' in self._ui_elems:
                    if self.contexts_list_panel is not None:
                        self.contexts_list_panel.resume_tid(tid)

                # clear registers
                if 'registers' in self._ui_elems:
                    if self.context_panel is not None:
                        self.context_panel.clear()

                # clear jvm explorer
                if 'jvm-debugger' in self._ui_elems:
                    if self.java_explorer_panel is not None:
                        self.java_explorer_panel.clear_panel()

                # invalidate dwarf context tid
                self.dwarf.context_tid = 0

    def _on_tracer_data(self, data):
        if not data:
            return

        if self.trace_panel is None:
            self._create_ui_elem('trace')

        if self.trace_panel is not None:
            self.show_main_tab('Trace')
            self.trace_panel.start()

            trace_events_parts = data[1].split(',')
            while trace_events_parts:
                trace_event = TraceEvent(trace_events_parts.pop(0),
                                         trace_events_parts.pop(0),
                                         trace_events_parts.pop(0),
                                         trace_events_parts.pop(0))
                self.trace_panel.event_queue.append(trace_event)

    def _on_set_data(self, data):
        if not isinstance(data, list):
            return

        if self.data_panel is None:
            self._create_ui_elem('data')

        if self.data_panel is not None:
            self.show_main_tab('Data')
            self.data_panel.append_data(data[0], data[1], data[2])

    def show_progress(self, text):
        self.progressbar.setVisible(True)
        self.set_status_text(text)

    def hide_progress(self):
        self.progressbar.setVisible(False)
        self.set_status_text('')

    def _on_attached(self, data):
        self.setWindowTitle('Dwarf - Attached to %s (%s)' % (data[1], data[0]))

    def _on_script_loaded(self):
        # restore the loaded session if any
        self.session_manager.restore_session()

    def _on_memory_modified(self, pos, length):
        data_pos = self.memory_panel.base + pos
        data = self.memory_panel.data[pos:pos + length]
        data = [data[0]]  # todo: strange js part

        if self.dwarf.dwarf_api('writeBytes', [data_pos, data]):
            pass
        else:
            utils.show_message_box('Failed to write Memory')

    def on_add_bookmark(self, ptr):
        """
        provide ptr as int
        """
        if self.bookmarks_panel is not None:
            self.bookmarks_panel._create_bookmark(ptr=hex(ptr))
예제 #3
0
파일: app.py 프로젝트: anubi5egypt/Dwarf
class AppWindow(QMainWindow):
    onRestart = pyqtSignal(name='onRestart')

    def __init__(self, dwarf_args, flags=None):
        super(AppWindow, self).__init__(flags)

        self.dwarf_args = dwarf_args

        self.session_manager = SessionManager(self)
        self.session_manager.sessionCreated.connect(self.session_created)
        self.session_manager.sessionStopped.connect(self.session_stopped)
        self.session_manager.sessionClosed.connect(self.session_closed)

        self.menu = self.menuBar()
        self._is_newer_dwarf = False
        self.view_menu = None

        self.asm_panel = None
        self.console_panel = None
        self.context_panel = None
        self.backtrace_panel = None
        self.contexts_list_panel = None
        self.data_panel = None
        self.emulator_panel = None
        self.ftrace_panel = None
        self.hooks_panel = None
        self.smali_panel = None
        self.java_inspector_panel = None
        self.java_explorer_panel = None
        self.java_trace_panel = None
        self.memory_panel = None
        self.modules_panel = None
        self.ranges_panel = None
        self.search_panel = None
        self.trace_panel = None
        self.watchers_panel = None
        self.welcome_window = None

        self._ui_elems = []

        self.setWindowTitle(
            'Dwarf - A debugger for reverse engineers, crackers and security analyst'
        )

        # load external assets
        _app = QApplication.instance()

        self.remove_tmp_dir()

        # themes
        self.prefs = Prefs()
        self.set_theme(self.prefs.get('dwarf_ui_theme', 'black'))

        # set icon
        if os.name == 'nt':
            # windows stuff
            import ctypes
            try:
                # write ini to show folder with dwarficon
                folder_stuff = "[.ShellClassInfo]\nIconResource=assets\dwarf.ico,0\n[ViewState]\nMode=\nVid=\nFolderType=Generic\n"
                try:
                    with open('desktop.ini', 'w') as ini:
                        ini.writelines(folder_stuff)

                    FILE_ATTRIBUTE_HIDDEN = 0x02
                    FILE_ATTRIBUTE_SYSTEM = 0x04

                    # set fileattributes to hidden + systemfile
                    ctypes.windll.kernel32.SetFileAttributesW(
                        r'desktop.ini',
                        FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)
                except PermissionError:
                    # its hidden+system already
                    pass

                # fix for showing dwarf icon in windows taskbar instead of pythonicon
                _appid = u'iGio90.dwarf.debugger'
                ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
                    _appid)

                if os.path.exists(utils.resource_path('assets/dwarf.png')):
                    _icon = QIcon(utils.resource_path('assets/dwarf.png'))
                    _app.setWindowIcon(_icon)
                    self.setWindowIcon(_icon)
            except:
                pass
        else:
            if os.path.exists(utils.resource_path('assets/dwarf.png')):
                _icon = QIcon(utils.resource_path('assets/dwarf.png'))
                _app.setWindowIcon(_icon)
                self.setWindowIcon(_icon)

        # load font
        if os.path.exists(utils.resource_path('assets/Anton.ttf')):
            QFontDatabase.addApplicationFont('assets/Anton.ttf')
        if os.path.exists(utils.resource_path('assets/OpenSans-Regular.ttf')):
            QFontDatabase.addApplicationFont('assets/OpenSans-Regular.ttf')
            _app.setFont(QFont("OpenSans", 9, QFont.Normal))
            if os.path.exists(utils.resource_path('assets/OpenSans-Bold.ttf')):
                QFontDatabase.addApplicationFont('assets/OpenSans-Bold.ttf')

        # mainwindow statusbar
        self.progressbar = QProgressBar()
        self.progressbar.setRange(0, 0)
        self.progressbar.setVisible(False)
        self.progressbar.setFixedHeight(15)
        self.progressbar.setFixedWidth(100)
        self.progressbar.setTextVisible(False)
        self.progressbar.setValue(30)
        self.statusbar = QStatusBar(self)
        self.statusbar.setAutoFillBackground(False)
        self.statusbar.addPermanentWidget(self.progressbar)
        self.statusbar.setObjectName("statusbar")
        self.setStatusBar(self.statusbar)

        self.main_tabs = QTabWidget(self)
        self.main_tabs.setMovable(True)
        self.main_tabs.setAutoFillBackground(True)
        self.setCentralWidget(self.main_tabs)

        if self.dwarf_args.package is None:
            self.welcome_window = WelcomeDialog(self)
            self.welcome_window.setModal(True)
            self.welcome_window.onIsNewerVersion.connect(
                self._enable_update_menu)
            self.welcome_window.onUpdateComplete.connect(
                self._on_dwarf_updated)
            self.welcome_window.setWindowTitle(
                'Welcome to Dwarf - A debugger for reverse engineers, crackers and security analyst'
            )
            self.welcome_window.onSessionSelected.connect(self._start_session)
            # wait for welcome screen
            self.hide()
            self.welcome_window.show()
        else:
            if dwarf_args.package is not None:
                if dwarf_args.type is None:
                    # no device given check if package is local path
                    if os.path.exists(dwarf_args.package):
                        print('* Starting new LocalSession')
                        self._start_session('local')
                    else:
                        print('use -t to set sessiontype')
                        exit(0)
                else:
                    print('* Starting new Session')
                    self._start_session(dwarf_args.type)

    def _setup_main_menu(self):
        self.menu = self.menuBar()
        dwarf_menu = QMenu('Dwarf', self)
        if self._is_newer_dwarf:
            dwarf_menu.addAction('Update', self._update_dwarf)
        dwarf_menu.addAction('Exit', self.session_closed)
        self.menu.addMenu(dwarf_menu)

        session = self.session_manager.session
        if session is not None:
            session_menu = session.main_menu
            if isinstance(session_menu, list):
                for menu in session_menu:
                    self.menu.addMenu(menu)
            else:
                self.menu.addMenu(session_menu)

        self.view_menu = QMenu('View', self)
        theme = QMenu('Theme', self.view_menu)
        theme.addAction('Black')
        theme.addAction('Dark')
        theme.addAction('Light')
        theme.triggered.connect(self._set_theme)
        self.view_menu.addMenu(theme)
        self.view_menu.addSeparator()
        self.menu.addMenu(self.view_menu)

        if self.dwarf_args.debug_script:
            debug_menu = QMenu('Debug', self)
            debug_menu.addAction('Reload core', self._menu_reload_core)
            self.menu.addMenu(debug_menu)

        about_menu = QMenu('About', self)
        about_menu.addAction('Dwarf on GitHub', self._menu_github)
        about_menu.addAction('Documention', self._menu_documentation)
        about_menu.addAction('Api', self._menu_api)
        about_menu.addAction('Slack', self._menu_slack)
        about_menu.addSeparator()
        about_menu.addAction('Info')
        self.menu.addMenu(about_menu)

    def _enable_update_menu(self):
        self._is_newer_dwarf = True

    def _update_dwarf(self):
        if self.welcome_window:
            self.welcome_window._update_dwarf()

    def _on_dwarf_updated(self):
        self.onRestart.emit()

    def remove_tmp_dir(self):
        if os.path.exists('.tmp'):
            shutil.rmtree('.tmp', ignore_errors=True)

    def _set_theme(self, qaction):
        if qaction:
            self.set_theme(qaction.text())

    def _menu_reload_core(self):
        self.dwarf.load_script()

    def show_main_tab(self, name):
        index = 0
        name = name.join(name.split()).lower()
        if name == 'memory':
            index = self.main_tabs.indexOf(self.memory_panel)
        elif name == 'ranges':
            index = self.main_tabs.indexOf(self.ranges_panel)
        elif name == 'search':
            index = self.main_tabs.indexOf(self.search_panel)
        elif name == 'modules':
            index = self.main_tabs.indexOf(self.modules_panel)
        elif name == 'disassembly':
            index = self.main_tabs.indexOf(self.asm_panel)
        elif name == 'trace':
            index = self.main_tabs.indexOf(self.trace_panel)
        elif name == 'data':
            index = self.main_tabs.indexOf(self.data_panel)
        elif name == 'emulator':
            index = self.main_tabs.indexOf(self.emulator_panel)
        elif name == 'java-trace':
            index = self.main_tabs.indexOf(self.java_trace_panel)
        elif name == 'java-inspector':
            index = self.main_tabs.indexOf(self.java_inspector_panel)
        elif name == 'java-explorer':
            index = self.main_tabs.indexOf(self.java_explorer_panel)
        elif name == 'smali':
            index = self.main_tabs.indexOf(self.smali_panel)

        self.main_tabs.setCurrentIndex(index)

    def jump_to_address(self, ptr):
        if self.memory_panel is not None:
            self.show_main_tab('memory')
            self.memory_panel.read_memory(ptr)

    @pyqtSlot(name='mainMenuGitHub')
    def _menu_github(self):
        QDesktopServices.openUrl(QUrl('https://github.com/iGio90/Dwarf'))

    @pyqtSlot(name='mainMenuDocumentation')
    def _menu_api(self):
        QDesktopServices.openUrl(QUrl('https://igio90.github.io/Dwarf/'))

    @pyqtSlot(name='mainMenuApi')
    def _menu_documentation(self):
        QDesktopServices.openUrl(QUrl('https://igio90.github.io/Dwarf/api'))

    @pyqtSlot(name='mainMenuSlack')
    def _menu_slack(self):
        QDesktopServices.openUrl(
            QUrl('https://join.slack.com/t/resecret/shared_invite'
                 '/enQtMzc1NTg4MzE3NjA1LTlkNzYxNTIwYTc2ZTYyOWY1MT'
                 'Q1NzBiN2ZhYjQwYmY0ZmRhODQ0NDE3NmRmZjFiMmE1MDYwN'
                 'WJlNDVjZDcwNGE'))

    def _create_ui_elem(self, elem):
        if not isinstance(elem, str):
            return

        if elem not in self._ui_elems:
            self._ui_elems.append(elem)

        if elem == 'watchers':
            from ui.panel_watchers import WatchersPanel
            self.watchers_dwidget = QDockWidget('Watchers', self)
            self.watchers_panel = WatchersPanel(self)
            # dont respond to dblclick mem cant be shown
            # self.watchers_panel.onItemDoubleClicked.connect(
            #    self._on_watcher_clicked)
            self.watchers_panel.onItemRemoved.connect(
                self._on_watcher_removeditem)
            self.watchers_panel.onItemAdded.connect(self._on_watcher_added)
            self.watchers_dwidget.setWidget(self.watchers_panel)
            self.watchers_dwidget.setObjectName('WatchersPanel')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.watchers_dwidget)
            self.view_menu.addAction(self.watchers_dwidget.toggleViewAction())
        elif elem == 'hooks':
            from ui.panel_hooks import HooksPanel
            self.hooks_dwiget = QDockWidget('Hooks', self)
            self.hooks_panel = HooksPanel(self)
            self.hooks_panel.onShowMemoryRequest.connect(
                self._on_watcher_clicked)
            self.hooks_panel.onHookRemoved.connect(self._on_hook_removed)
            self.hooks_dwiget.setWidget(self.hooks_panel)
            self.hooks_dwiget.setObjectName('HooksPanel')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.hooks_dwiget)
            self.view_menu.addAction(self.hooks_dwiget.toggleViewAction())
        elif elem == 'registers':
            from ui.panel_context import ContextPanel
            self.registers_dock = QDockWidget('Context', self)
            self.context_panel = ContextPanel(self)
            self.registers_dock.setWidget(self.context_panel)
            self.registers_dock.setObjectName('ContextsPanel')
            self.addDockWidget(Qt.RightDockWidgetArea, self.registers_dock)
            self.view_menu.addAction(self.registers_dock.toggleViewAction())
        elif elem == 'memory':
            from ui.panel_memory import MemoryPanel
            self.memory_panel = MemoryPanel(self)
            self.memory_panel.onShowDisassembly.connect(
                self._disassemble_range)
            self.memory_panel.dataChanged.connect(self._on_memory_modified)
            self.memory_panel.statusChanged.connect(self.set_status_text)
            self.main_tabs.addTab(self.memory_panel, 'Memory')
        elif elem == 'java-explorer':
            from ui.panel_java_explorer import JavaExplorerPanel
            self.java_explorer_panel = JavaExplorerPanel(self)
            self.main_tabs.addTab(self.java_explorer_panel, 'JVM debugger')
        elif elem == 'java-inspector':
            from ui.panel_java_inspector import JavaInspector
            self.java_inspector_panel = JavaInspector(self)
            self.main_tabs.addTab(self.java_inspector_panel, 'JVM inspector')
        elif elem == 'console':
            from ui.panel_console import ConsolePanel
            self.console_dock = QDockWidget('Console', self)
            self.console_panel = ConsolePanel(self)
            self.dwarf.onLogToConsole.connect(self._log_js_output)
            self.console_dock.setWidget(self.console_panel)
            self.console_dock.setObjectName('ConsolePanel')
            self.addDockWidget(Qt.BottomDockWidgetArea, self.console_dock)
            self.view_menu.addAction(self.console_dock.toggleViewAction())
        elif elem == 'backtrace':
            from ui.panel_backtrace import BacktracePanel
            self.backtrace_dock = QDockWidget('Backtrace', self)
            self.backtrace_panel = BacktracePanel(self)
            self.backtrace_dock.setWidget(self.backtrace_panel)
            self.backtrace_dock.setObjectName('BacktracePanel')
            self.backtrace_panel.onShowMemoryRequest.connect(
                self._on_watcher_clicked)
            self.addDockWidget(Qt.RightDockWidgetArea, self.backtrace_dock)
            self.view_menu.addAction(self.backtrace_dock.toggleViewAction())
        elif elem == 'threads':
            from ui.panel_contexts_list import ContextsListPanel
            self.threads_dock = QDockWidget('Threads', self)
            self.contexts_list_panel = ContextsListPanel(self)
            self.dwarf.onThreadResumed.connect(
                self.contexts_list_panel.resume_tid)
            self.contexts_list_panel.onItemDoubleClicked.connect(
                self._apply_context)
            self.threads_dock.setWidget(self.contexts_list_panel)
            self.threads_dock.setObjectName('ThreadPanel')
            self.addDockWidget(Qt.RightDockWidgetArea, self.threads_dock)
            self.view_menu.addAction(self.threads_dock.toggleViewAction())
        elif elem == 'modules':
            from ui.panel_modules import ModulesPanel
            self.modules_panel = ModulesPanel(self)
            self.modules_panel.onModuleSelected.connect(
                self._on_module_dblclicked)
            self.modules_panel.onModuleFuncSelected.connect(
                self._on_modulefunc_dblclicked)
            self.modules_panel.onAddHook.connect(self._on_addmodule_hook)
            self.modules_panel.onDumpBinary.connect(self._on_dumpmodule)
            self.main_tabs.addTab(self.modules_panel, 'Modules')
        elif elem == 'ranges':
            from ui.panel_ranges import RangesPanel
            self.ranges_panel = RangesPanel(self)
            self.ranges_panel.onItemDoubleClicked.connect(
                self._range_dblclicked)
            self.ranges_panel.onDumpBinary.connect(self._on_dumpmodule)
            # connect to watcherpanel func
            self.ranges_panel.onAddWatcher.connect(
                self.watchers_panel.do_addwatcher_dlg)
            self.main_tabs.addTab(self.ranges_panel, 'Ranges')
        elif elem == 'search':
            from ui.panel_search import SearchPanel
            self.search_panel = SearchPanel(self)
            self.search_panel.onShowMemoryRequest.connect(
                self._on_watcher_clicked)
            self.main_tabs.addTab(self.search_panel, 'Search')
        elif elem == 'data':
            from ui.panel_data import DataPanel
            self.data_panel = DataPanel(self)
            self.main_tabs.addTab(self.data_panel, 'Data')
        elif elem == 'trace':
            from ui.panel_trace import TracePanel
            self.trace_panel = TracePanel(self)
            self.main_tabs.addTab(self.trace_panel, 'Trace')
        elif elem == 'disassembly':
            from ui.disasm_view import DisassemblyView
            self.asm_panel = DisassemblyView(self)
            self.asm_panel.onShowMemoryRequest.connect(self._on_disasm_showmem)
            self.main_tabs.addTab(self.asm_panel, 'Disassembly')
        elif elem == 'emulator':
            from ui.panel_emulator import EmulatorPanel
            self.emulator_panel = EmulatorPanel(self)
            self.main_tabs.addTab(self.emulator_panel, 'Emulator')
        elif elem == 'java-trace':
            from ui.panel_java_trace import JavaTracePanel
            self.java_trace_panel = JavaTracePanel(self)
            self.main_tabs.addTab(self.java_trace_panel, 'JVM tracer')
        elif elem == 'smali':
            from ui.panel_smali import SmaliPanel
            self.smali_panel = SmaliPanel()
            self.main_tabs.addTab(self.smali_panel, 'Smali')
        else:
            print('no handler for elem: ' + elem)

    def set_theme(self, theme):
        if theme:
            theme = theme.replace(os.pardir, '').replace('.', '')
            theme = theme.join(theme.split()).lower()
            theme_style = 'assets/' + theme + '_style.qss'
            if not os.path.exists(utils.resource_path(theme_style)):
                return

            self.prefs.put('dwarf_ui_theme', theme)

            try:
                _app = QApplication.instance()
                with open(theme_style) as stylesheet:
                    _app.setStyleSheet(_app.styleSheet() + '\n' +
                                       stylesheet.read())
            except Exception as e:
                pass
                #err = self.dwarf.spawn(dwarf_args.package, dwarf_args.script)

    def set_status_text(self, txt):
        self.statusbar.showMessage(txt)

    # ************************************************************************
    # **************************** Properties ********************************
    # ************************************************************************
    @property
    def disassembly(self):
        return self.asm_panel

    @property
    def backtrace(self):
        return self.backtrace_panel

    @property
    def console(self):
        return self.console_panel

    @property
    def context(self):
        return self.context_panel

    @property
    def threads(self):
        return self.contexts_list_panel

    @property
    def emulator(self):
        return self.emulator_panel

    @property
    def ftrace(self):
        return self.ftrace_panel

    @property
    def hooks(self):
        return self.hooks_panel

    @property
    def java_inspector(self):
        return self.java_inspector_panel

    @property
    def java_explorer(self):
        return self.java_explorer_panel

    @property
    def memory(self):
        return self.memory_panel

    @property
    def modules(self):
        return self.memory_panel

    @property
    def ranges(self):
        return self.ranges_panel

    @property
    def trace(self):
        return self.trace_panel

    @property
    def watchers(self):
        return self.watchers_panel

    @property
    def dwarf(self):
        if self.session_manager.session is not None:
            return self.session_manager.session.dwarf
        else:
            return None

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    # session handlers
    def _start_session(self, session_type):
        if self.welcome_window is not None:
            self.welcome_window.close()
        self.session_manager.create_session(session_type)

    def session_created(self):
        # session init done create ui for it
        session = self.session_manager.session
        self._setup_main_menu()
        for ui_elem in session.session_ui_sections:
            ui_elem = ui_elem.join(ui_elem.split()).lower()
            self._create_ui_elem(ui_elem)

        self.dwarf.onAttached.connect(self._on_attached)
        # hookup
        self.dwarf.onSetRanges.connect(self._on_setranges)
        self.dwarf.onSetModules.connect(self._on_setmodules)

        self.dwarf.onAddNativeHook.connect(self._on_add_hook)
        self.dwarf.onApplyContext.connect(self._apply_context)
        self.dwarf.onThreadResumed.connect(self.on_tid_resumed)

        self.dwarf.onTraceData.connect(self._on_tracer_data)
        self.dwarf.onSetData.connect(self._on_set_data)

        self.session_manager.start_session(self.dwarf_args)
        q_settings = QSettings("dwarf_window_pos.ini", QSettings.IniFormat)
        ui_state = q_settings.value('dwarf_ui_state')
        if ui_state:
            self.restoreGeometry(ui_state)
        window_state = q_settings.value('dwarf_ui_window', self.saveState())
        if window_state:
            self.restoreState(window_state)

        self.showMaximized()

    def session_stopped(self):
        self.remove_tmp_dir()
        self.menu.clear()

        self.main_tabs.clear()

        for elem in self._ui_elems:
            if elem == 'watchers':
                self.watchers_panel.clear_list()
                self.watchers_panel.close()
                self.watchers_panel = None
                self.removeDockWidget(self.watchers_dwidget)
                self.watchers_dwidget = None
            elif elem == 'hooks':
                self.hooks_panel.close()
                self.hooks_panel = None
                self.removeDockWidget(self.hooks_dwiget)
                self.hooks_dwiget = None
            elif elem == 'registers':
                self.context_panel.close()
                self.context_panel = None
                self.removeDockWidget(self.registers_dock)
                self.registers_dock = None
            elif elem == 'memory':
                self.memory_panel.close()
                self.memory_panel = None
                self.main_tabs.removeTab(0)
                # self.main_tabs
            elif elem == 'java-explorer':
                self.java_explorer_panel.close()
                self.java_explorer_panel = None
                self.removeDockWidget(self.watchers_dwidget)
            elif elem == 'console':
                self.console_panel.close()
                self.console_panel = None
                self.removeDockWidget(self.console_dock)
                self.console_dock = None
            elif elem == 'backtrace':
                self.backtrace_panel.close()
                self.backtrace_panel = None
                self.removeDockWidget(self.backtrace_dock)
            elif elem == 'threads':
                self.contexts_list_panel.close()
                self.contexts_list_panel = None
                self.removeDockWidget(self.threads_dock)
                self.threads_dock = None

    def session_closed(self):
        self._ui_elems = []
        self.hide()
        if self.welcome_window is not None:
            self.welcome_window.exec()

        # close if it was a commandline session
        if self.welcome_window is None:
            if self.dwarf_args.package:
                self.close()

    # ui handler
    def closeEvent(self, event):
        """ Window closed
            save stuff or whatever at exit

            detaches dwarf
        """
        # save windowstuff
        q_settings = QSettings("dwarf_window_pos.ini", QSettings.IniFormat)
        q_settings.setValue('dwarf_ui_state', self.saveGeometry())
        q_settings.setValue('dwarf_ui_window', self.saveState())

        if self.dwarf:
            self.dwarf.detach()
        super().closeEvent(event)

    def _on_watcher_clicked(self, ptr):
        """ Address in Watcher/Hookpanel was clicked
            show Memory
        """
        if '.' in ptr:  # java_hook
            file_path = ptr.replace('.', os.path.sep)
            if os.path.exists('.tmp/smali/' + file_path + '.smali'):
                if self.smali_panel is None:
                    self._create_ui_elem('smali')
                self.smali_panel.set_file('.tmp/smali/' + file_path + '.smali')
                self.show_main_tab('smali')
        else:
            self.memory_panel.read_memory(ptr=ptr)
            self.show_main_tab('memory')

    def _on_disasm_showmem(self, ptr, length):
        """ Address in Disasm was clicked
            adds temphighlight for bytes from current instruction
        """
        self.memory_panel.read_memory(ptr)
        self.memory_panel.add_highlight(
            HighLight('attention', utils.parse_ptr(ptr), length))
        self.show_main_tab('memory')

    def _on_watcher_added(self, ptr):
        """ Watcher Entry was added
        """
        try:
            # set highlight
            self.memory_panel.add_highlight(
                HighLight('watcher', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

    def _on_watcher_removeditem(self, ptr):
        """ Watcher Entry was removed
            remove highlight too
        """
        self.memory_panel.remove_highlight(ptr)

    def _on_module_dblclicked(self, data):
        """ Module in ModulePanel was doubleclicked
        """
        addr, size = data
        addr = utils.parse_ptr(addr)
        size = int(size, 10)
        self.memory_panel.read_memory(ptr=addr, length=size)
        self.show_main_tab('Memory')

    def _on_modulefunc_dblclicked(self, ptr):
        """ Function in ModulePanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.memory_panel.read_memory(ptr=ptr)
        self.show_main_tab('Memory')

    def _on_dumpmodule(self, data):
        """ DumpBinary MenuItem in ModulePanel was selected
        """
        ptr, size = data
        ptr = utils.parse_ptr(ptr)
        size = int(size, 10)
        self.dwarf.dump_memory(ptr=ptr, length=size)

    def _disassemble_range(self, mem_range):
        """ Disassemble MenuItem in Hexview was selected
        """
        if mem_range:
            if self.asm_panel is None:
                self._create_ui_elem('disassembly')

            if mem_range:
                self.asm_panel.disassemble(mem_range)
                self.show_main_tab('disassembly')

    def _range_dblclicked(self, ptr):
        """ Range in RangesPanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.memory_panel.read_memory(ptr=ptr)
        self.show_main_tab('Memory')

    # dwarf handlers
    def _log_js_output(self, output):
        if self.console_panel is not None:
            self.console_panel.get_js_console().log(output)

    def _on_setranges(self, ranges):
        """ Dwarf wants to set Ranges
            only hooked up to switch tab or create ui
            its connected in panel after creation
        """
        if self.ranges_panel is None:
            self._create_ui_elem('ranges')
            # forward only now to panel it connects after creation
            self.ranges_panel.set_ranges(ranges)

        # once we got ranges in place from our target we can create the search panel as well
        if self.search_panel is None:
            self._create_ui_elem('search')
            self.search_panel.set_ranges(ranges)

        if self.ranges_panel is not None:
            self.show_main_tab('ranges')

    def _on_setmodules(self, modules):
        """ Dwarf wants to set Modules
            only hooked up to switch tab or create ui
            its connected in panel after creation
        """
        if self.modules_panel is None:
            self._create_ui_elem('modules')
            self.modules_panel.set_modules(modules)

        if self.modules_panel is not None:
            self.show_main_tab('modules')

    def _apply_context(self, context):
        if 'context' in context:
            is_java = context['is_java']

            if is_java:
                if self.java_explorer_panel is None:
                    self._create_ui_elem('java-explorer')
                self.context_panel.set_context(context['ptr'], 1,
                                               context['context'])
                self.java_explorer_panel.set_handle_arg(-1)
                self.show_main_tab('java-explorer')
            else:
                self.context_panel.set_context(context['ptr'], 0,
                                               context['context'])

        if 'backtrace' in context:
            self.backtrace_panel.set_backtrace(context['backtrace'])

        # update current context tid
        self.dwarf.context_tid = context['tid']

    def _on_add_hook(self, hook):
        try:
            # set highlight
            ptr = hook.get_ptr()
            ptr = utils.parse_ptr(ptr)
            self.memory_panel.add_highlight(
                HighLight('hook', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

    def _on_hook_removed(self, ptr):
        ptr = utils.parse_ptr(ptr)
        self.memory_panel.remove_highlight(ptr)

    def _on_addmodule_hook(self, data):
        ptr, name = data
        self.dwarf.hook_native(ptr, own_input=name)

    def on_tid_resumed(self, tid):
        if self.dwarf:
            if self.dwarf.context_tid == tid:

                # clear backtrace
                if 'backtrace' in self._ui_elems:
                    if self.backtrace_panel is not None:
                        self.backtrace_panel.clear()

                # remove thread
                if 'threads' in self._ui_elems:
                    if self.contexts_list_panel is not None:
                        self.contexts_list_panel.resume_tid(tid)

                # clear registers
                if 'registers' in self._ui_elems:
                    if self.context_panel is not None:
                        self.context_panel.clear()

                # clear jvm explorer
                if 'java-explorer' in self._ui_elems:
                    if self.java_explorer_panel is not None:
                        self.java_explorer_panel.clear_panel()

    def _on_tracer_data(self, data):
        if not data:
            return

        if self.trace_panel is None:
            self._create_ui_elem('trace')

        if self.trace_panel is not None:
            self.show_main_tab('Trace')
            self.trace_panel.start()

            trace_events_parts = data[1].split(',')
            while trace_events_parts:
                trace_event = TraceEvent(trace_events_parts.pop(0),
                                         trace_events_parts.pop(0),
                                         trace_events_parts.pop(0),
                                         trace_events_parts.pop(0))
                self.trace_panel.event_queue.append(trace_event)

    def _on_set_data(self, data):
        if not isinstance(data, list):
            return

        if self.data_panel is None:
            self._create_ui_elem('data')

        if self.data_panel is not None:
            self.show_main_tab('Data')
            self.data_panel.append_data(data[0], data[1], data[2])

    def show_progress(self, text):
        self.progressbar.setVisible(True)
        self.set_status_text(text)

    def hide_progress(self):
        self.progressbar.setVisible(False)
        self.set_status_text('')

    def _on_attached(self, data):
        self.setWindowTitle('Dwarf - Attached to %s (%s)' % (data[1], data[0]))

    def _on_memory_modified(self, pos, length):
        data_pos = self.memory_panel.base + pos
        data = self.memory_panel.data[pos:pos + length]
        data = [data[0]]  # todo: strange js part

        if self.dwarf.dwarf_api('writeBytes', [data_pos, data]):
            pass
        else:
            utils.show_message_box('Failed to write Memory')
예제 #4
0
class Emulator(QThread):
    class EmulatorSetupFailedError(Exception):
        """ Setup Failed
        """

    class EmulatorAlreadyRunningError(Exception):
        """ isrunning
        """

    onEmulatorSetup = pyqtSignal(list, name='onEmulatorSetup')
    onEmulatorStart = pyqtSignal(name='onEmulatorStart')
    onEmulatorStop = pyqtSignal(name='onEmulatorStop')
    onEmulatorStep = pyqtSignal(name='onEmulatorStep')
    onEmulatorHook = pyqtSignal(Instruction, name='onEmulatorHook')
    onEmulatorMemoryHook = pyqtSignal(list, name='onEmulatorMemoryHook')
    onEmulatorMemoryRangeMapped = pyqtSignal(
        list, name='onEmulatorMemoryRangeMapped')
    onEmulatorLog = pyqtSignal(str, name='onEmulatorLog')

    # setup errors
    ERR_INVALID_TID = 1
    ERR_INVALID_CONTEXT = 2
    ERR_SETUP_FAILED = 3

    def __init__(self, dwarf):
        super(Emulator, self).__init__()

        self.setTerminationEnabled(True)
        self.dwarf = dwarf
        self._prefs = Prefs()

        self._setup_done = False
        self._blacklist_regs = []

        self.cs = None
        self.uc = None

        self.context = None
        self.thumb = False
        self.end_ptr = 0
        self.step_mode = STEP_MODE_NONE

        self.current_context = None

        self._current_instruction = 0
        self._next_instruction = 0
        self._current_cpu_mode = 0

        self._request_stop = False

        # configurations
        self.callbacks_path = None
        self.callbacks = None
        self.instructions_delay = 0

        self._start_address = 0
        self._end_address = 0

        # prevent emulator loop for any reason
        # i.e through custom callback
        # we don't want any UI freeze, so we just setup a n00b way to check if we are looping
        # inside the same instruction for N times.
        # notice that when an unmapped memory region is required during emulation, this will be taken from target proc
        # and mapped into unicorn context. Later, the code fallback to execute the same instruction once again
        self._anti_loop = 0

    def setup_arm(self):
        self.thumb = self.context.pc.thumb
        if self.thumb:
            self._current_cpu_mode = unicorn.UC_MODE_THUMB
            self.cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
            self.uc = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB)
            # Enable VFP instr
            self.uc.mem_map(0x1000, 1024)
            self.uc.mem_write(0x1000, binascii.unhexlify(VFP))
            self.uc.emu_start(0x1000 | 1, 0x1000 + len(VFP))
            self.uc.mem_unmap(0x1000, 1024)
        else:
            self.cs = Cs(CS_ARCH_ARM, CS_MODE_ARM)
            self.uc = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_ARM)
            self._current_cpu_mode = unicorn.UC_MODE_ARM

    def setup_arm64(self):
        self.uc = unicorn.Uc(unicorn.UC_ARCH_ARM64,
                             unicorn.UC_MODE_LITTLE_ENDIAN)
        self.cs = Cs(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN)
        self._current_cpu_mode = unicorn.UC_MODE_LITTLE_ENDIAN

    def setup_x86(self):
        self.uc = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
        self.cs = Cs(CS_ARCH_X86, CS_MODE_32)

    def setup_x64(self):
        self.uc = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_64)
        self.cs = Cs(CS_ARCH_X86, CS_MODE_64)

    def _setup(self,
               user_arch=None,
               user_mode=None,
               cs_arch=None,
               cs_mode=None):
        if user_arch is not None and user_mode is not None:
            try:
                self.uc = unicorn.Uc(user_arch, user_mode)
                self.cs = Cs(cs_arch, cs_mode)

                self.thumb = user_mode == unicorn.UC_MODE_THUMB
            except:
                raise self.EmulatorSetupFailedError('Unsupported arch')
        else:
            if self.dwarf.arch == 'arm':
                self.setup_arm()
            elif self.dwarf.arch == 'arm64':
                self.setup_arm64()
            elif self.dwarf.arch == 'ia32':
                self.setup_x86()
            elif self.dwarf.arch == 'x64':
                self.setup_x64()
            else:
                # unsupported arch
                raise self.EmulatorSetupFailedError('Unsupported arch')

        if not self.uc or not self.cs:
            raise self.EmulatorSetupFailedError('Unicorn or Capstone missing')

        # enable capstone details
        if self.cs is not None:
            self.cs.detail = True

        if not self.context.is_native_context:
            raise self.EmulatorSetupFailedError(
                'Cannot run emulator on non-native context')

        err = self.map_range(self.context.pc.value)
        if err:
            raise self.EmulatorSetupFailedError('Mapping failed')

        self.current_context = EmulatorContext(self.dwarf)
        for reg in self.current_context._unicorn_registers:
            if reg in self.context.__dict__:
                if reg not in self._blacklist_regs:
                    self.uc.reg_write(
                        self.current_context._unicorn_registers[reg],
                        self.context.__dict__[reg].value)

        self.uc.hook_add(unicorn.UC_HOOK_CODE, self.hook_code)
        self.uc.hook_add(unicorn.UC_HOOK_MEM_WRITE | unicorn.UC_HOOK_MEM_READ,
                         self.hook_mem_access)
        self.uc.hook_add(
            unicorn.UC_HOOK_MEM_FETCH_UNMAPPED
            | unicorn.UC_HOOK_MEM_WRITE_UNMAPPED
            | unicorn.UC_HOOK_MEM_READ_UNMAPPED, self.hook_unmapped)
        self.current_context.set_context(self.uc)
        return 0

    def run(self):
        # dont call this func
        if not self._setup_done:
            return
        try:
            if self.thumb and self._start_address % 2 != 1:
                self._start_address += 1
            self.uc.emu_start(
                self._start_address,
                0xffffffffffffffff)  # end is handled in hook_code
        except unicorn.UcError as e:
            self.log_to_ui('[*] error: ' + str(e))
        except Exception as e:
            self.log_to_ui('[*] error: ' + str(e))

        self._setup_done = False
        self.onEmulatorStop.emit()

    def api(self, parts):
        """
        expose api to js side for allowing emulator interaction while scripting
        :param parts: arr -> cmd api split by ":::"
        :return: the result from the api
        """
        cmd = parts[0]
        if cmd == 'clean':
            return self.clean()
        elif cmd == 'setup':
            custom_uc_arch = None
            custom_cs_arch = None
            custom_uc_mode = None
            custom_cs_mode = None
            if len(parts) > 3:
                try:
                    arch = 'UC_ARCH_' + parts[2].upper()
                    if arch in unicorn.__dict__:
                        custom_uc_arch = unicorn.__dict__[arch]
                        arch = 'CS_ARCH_' + parts[2].upper()
                        custom_cs_arch = capstone.__dict__[arch]
                    mode = 'UC_MODE_' + parts[3].upper()
                    if mode in unicorn.__dict__:
                        custom_uc_mode = unicorn.__dict__[mode]
                        mode = 'CS_MODE_' + parts[3].upper()
                        custom_cs_mode = capstone.__dict__[mode]
                except:
                    custom_uc_arch = None
                    custom_cs_arch = None
                    custom_uc_mode = None
                    custom_cs_mode = None

            if custom_uc_arch is not None and custom_uc_mode is not None:
                err = self.setup(parts[1],
                                 user_arch=custom_uc_arch,
                                 user_mode=custom_uc_mode,
                                 cs_arch=custom_cs_arch,
                                 cs_mode=custom_cs_mode)
            else:
                err = self.setup(parts[1])
            if err > 0:
                self.context = None
            return err
        elif cmd == 'start':
            until = 0
            if len(parts) > 1:
                try:
                    until = int(parts[1])
                except:
                    pass
            return self.emulate(until=until)
        elif cmd == 'step':
            step_mode = STEP_MODE_SINGLE
            if len(parts) > 1:
                try:
                    step_mode = int(parts[1])
                except:
                    pass
            return self.emulate(step_mode=step_mode)

    def clean(self):
        if self.isRunning():
            raise self.EmulatorAlreadyRunningError()

        self._current_instruction = 0
        self._next_instruction = 0
        self._current_cpu_mode = 0
        self.context = None
        return 0

    def hook_code(self, uc, address, size, user_data):
        # QApplication.processEvents()
        if self._request_stop:
            self.log_to_ui('Error: Emulator stopped - reached end')
            self.stop()
            return

        # anti loop checks
        if self._anti_loop == address:
            self.log_to_ui('Error: Emulator stopped - looping')
            self.stop()
            return

        self._current_instruction = address

        # check if pc/eip is end_ptr
        pc = 0  # address should be pc too ???
        if self.dwarf.arch == 'arm':
            pc = uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC)
        elif self.dwarf.arch == 'arm64':
            pc = uc.reg_read(unicorn.arm64_const.UC_ARM64_REG_PC)
        elif self.dwarf.arch == 'ia32':
            pc = uc.reg_read(unicorn.x86_const.UC_X86_REG_EIP)
        elif self.dwarf.arch == 'x64':
            pc = uc.reg_read(unicorn.x86_const.UC_X86_REG_RIP)

        if self.thumb:
            pc = pc | 1

        if pc == self._end_address:
            self._request_stop = True

        # set the current context
        self.current_context.set_context(uc)

        instruction = None
        try:
            try:
                data = bytes(uc.mem_read(address, size))
                assembly = self.cs.disasm(data, address)
            except:
                self.log_to_ui('Error: Emulator stopped - disasm')
                self.stop()
                return

            for i in assembly:
                # QApplication.processEvents()

                instruction = Instruction(self.dwarf,
                                          i,
                                          context=self.current_context)

                self.onEmulatorHook.emit(instruction)
                if self.callbacks is not None:
                    try:
                        self.callbacks.hook_code(self, instruction, address,
                                                 size)
                    except:
                        # hook code not implemented in callbacks
                        pass

                if not instruction.is_jump and not instruction.is_call:
                    self._next_instruction = address + i.size
                else:
                    if instruction.is_call:
                        self._next_instruction = instruction.call_address
                    elif instruction.is_jump:
                        self._next_instruction = instruction.jump_address

                    if instruction.should_change_arm_instruction_set:
                        if self.thumb:
                            self._current_cpu_mode = unicorn.UC_MODE_ARM
                            self.thumb = False
                        else:
                            self._current_cpu_mode = unicorn.UC_MODE_THUMB
                            self.thumb = True
                        self.cs.mode(self._current_cpu_mode)
                break

            # time.sleep(self.instructions_delay)
        except:
            self.log_to_ui('Error: Emulator stopped')
            self.stop()
            return

        if self.step_mode != STEP_MODE_NONE:
            if self.step_mode == STEP_MODE_SINGLE:
                self.stop()
            elif self.step_mode == STEP_MODE_FUNCTION:
                if instruction is not None and instruction.is_call:
                    self.stop()
            elif self.step_mode == STEP_MODE_JUMP:
                if instruction is not None and instruction.is_jump:
                    self.stop()

    def hook_mem_access(self, uc, access, address, size, value, user_data):
        v = value
        if access == unicorn.UC_MEM_READ:
            v = int.from_bytes(uc.mem_read(address, size), 'little')
        self.onEmulatorMemoryHook.emit([uc, access, address, v])
        if self.callbacks is not None:
            try:
                self.callbacks.hook_memory_access(self, access, address, size,
                                                  v)
            except:
                # hook code not implemented in callbacks
                pass

    def hook_unmapped(self, uc, access, address, size, value, user_data):
        self.log_to_ui(
            "[*] Trying to access an unmapped memory address at 0x%x" %
            address)
        err = self.map_range(address)
        if err > 0:
            self.log_to_ui('[*] Error %d mapping range at %s' %
                           (err, hex(address)))
            return False
        return True

    def invalida_configurations(self):
        self.callbacks_path = self._prefs.get(prefs.EMULATOR_CALLBACKS_PATH,
                                              '')
        self.instructions_delay = self._prefs.get(
            prefs.EMULATOR_INSTRUCTIONS_DELAY, 0)

    def map_range(self, address):
        range_ = Range(Range.SOURCE_TARGET, self.dwarf)
        if range_.init_with_address(address) > 0:
            return 300
        try:
            self.uc.mem_map(range_.base, range_.size)
        except Exception as e:
            self.dwarf.log(e)
            return 301

        try:
            self.uc.mem_write(range_.base, range_.data)
        except Exception as e:
            self.dwarf.log(e)
            return 302

        self.log_to_ui("[*] Mapped %d at 0x%x" % (range_.size, range_.base))
        self.onEmulatorMemoryRangeMapped.emit([range_.base, range_.size])
        return 0

    def setup(self,
              tid=0,
              user_arch=None,
              user_mode=None,
              cs_arch=None,
              cs_mode=None):
        if tid == 0:
            # get current context tid if none provided
            tid = self.dwarf.context_tid

        # make sure it's int < pp: why make sure its int and then using str(tid) later??
        #                       when calling from api its str
        if isinstance(tid, str):
            try:
                tid = int(tid)
            except ValueError:
                return self.ERR_INVALID_TID

        if not isinstance(tid, int):
            return self.ERR_INVALID_TID

        self.context = None
        if str(tid) in self.dwarf.contexts:
            self.context = self.dwarf.contexts[str(tid)]

        if tid == 0 or self.context is None or not self.context.is_native_context:
            # prevent emulation if out-of-context
            return self.ERR_INVALID_CONTEXT

        try:
            self._setup(user_arch=user_arch,
                        user_mode=user_mode,
                        cs_arch=cs_arch,
                        cs_mode=cs_mode)
            self.onEmulatorSetup.emit([user_arch, user_mode])
        except self.EmulatorSetupFailedError:
            return self.ERR_SETUP_FAILED
        return 0

    def start(self, priority=QThread.HighPriority):
        # dont call this func
        if not self._setup_done:
            return
        return super().start(priority=priority)

    def emulate(self,
                until=0,
                step_mode=STEP_MODE_NONE,
                user_arch=None,
                user_mode=None,
                cs_arch=None,
                cs_mode=None):
        if self.isRunning():
            raise self.EmulatorAlreadyRunningError()

        if isinstance(until, str):
            try:
                until = int(until, 16)
            except ValueError:
                until = 0

        if until and isinstance(until, int):
            self.end_ptr = utils.parse_ptr(until)
            if self.end_ptr == 0:
                # invalid end pointer
                raise self.EmulatorSetupFailedError('Invalid EndPtr')

        if self.context is None:
            err = self.setup(user_arch=user_arch,
                             user_mode=user_mode,
                             cs_arch=cs_arch,
                             cs_mode=cs_mode)
            if err > 0:
                # make sure context is None if setup failed for any reason. we want a clean setup later
                self.context = None

                err_msg = 'unhandled error'
                if err == self.ERR_INVALID_TID:
                    err_msg = 'invalid thread id'
                elif err == self.ERR_INVALID_CONTEXT:
                    err_msg = 'invalid context'
                raise self.EmulatorSetupFailedError('Setup failed: %s' %
                                                    err_msg)

        # calculate the start address
        address = self._next_instruction
        if address == 0:
            if self.uc._arch == unicorn.UC_ARCH_ARM:
                address = self.uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC)
            elif self.uc._arch == unicorn.UC_ARCH_ARM64:
                address = self.uc.reg_read(unicorn.arm64_const.UC_ARM64_REG_PC)
            elif self.uc._arch == unicorn.UC_ARCH_X86 and self.uc._mode == unicorn.UC_MODE_32:
                address = self.uc.reg_read(unicorn.x86_const.UC_X86_REG_EIP)
            elif self.uc._arch == unicorn.UC_ARCH_X86 and self.uc._mode == unicorn.UC_MODE_64:
                address = self.uc.reg_read(unicorn.x86_const.UC_X86_REG_RIP)
            else:
                raise self.EmulatorSetupFailedError('Unsupported arch')

        if until > 0:
            self.log_to_ui('[*] start emulation from %s to %s' %
                           (hex(address), hex(self.end_ptr)))
        else:
            if step_mode == STEP_MODE_NONE or step_mode == STEP_MODE_SINGLE:
                self.log_to_ui('[*] stepping %s' % hex(address))
            elif step_mode == STEP_MODE_FUNCTION:
                self.log_to_ui('[*] stepping to next function call')
            elif step_mode == STEP_MODE_JUMP:
                self.log_to_ui('[*] stepping to next jump')
        self.onEmulatorStart.emit()

        # invalidate prefs before start
        self.invalida_configurations()

        # load callbacks if needed
        if self.callbacks_path is not None and self.callbacks_path != '':
            try:
                spec = spec_from_loader(
                    "callbacks",
                    SourceFileLoader("callbacks", self.callbacks_path))
                self.callbacks = module_from_spec(spec)
                spec.loader.exec_module(self.callbacks)
            except Exception as e:
                self.log_to_ui('[*] failed to load callbacks: %s' % str(e))
                # reset callbacks path
                self._prefs.put(prefs.EMULATOR_CALLBACKS_PATH, '')
                self.callbacks_path = ''
                self.callbacks = None
        else:
            self.callbacks = None

        # until is 0 (i.e we are stepping)
        if until == 0 and step_mode == STEP_MODE_NONE:
            self.step_mode = STEP_MODE_SINGLE
        else:
            self.step_mode = step_mode

        self._start_address = address
        if self.thumb:
            if self._start_address % 2 == 0:
                self._start_address = self._start_address | 1
        else:
            if self._start_address % 2 != 0:
                self._start_address -= 1
        self._end_address = self.end_ptr
        self._setup_done = True
        self.start()

    def stop(self):
        if self.isRunning():
            self.uc.emu_stop()

    def log_to_ui(self, what):
        self.onEmulatorLog.emit(what)
예제 #5
0
파일: emulator.py 프로젝트: yb1994917/Dwarf
class Emulator(QThread):
    class EmulatorSetupFailedError(Exception):
        """ Setup Failed
        """

    class EmulatorAlreadyRunningError(Exception):
        """ isrunning
        """

    onEmulatorStart = pyqtSignal(name='onEmulatorStart')
    onEmulatorStop = pyqtSignal(name='onEmulatorStop')
    onEmulatorStep = pyqtSignal(name='onEmulatorStep')
    onEmulatorHook = pyqtSignal(Instruction, name='onEmulatorHook')
    onEmulatorMemoryHook = pyqtSignal(list, name='onEmulatorMemoryHook')
    onEmulatorMemoryRangeMapped = pyqtSignal(
        list, name='onEmulatorMemoryRangeMapped')

    onEmulatorLog = pyqtSignal(str, name='onEmulatorLog')

    def __init__(self, dwarf):
        super(Emulator, self).__init__()

        self.setTerminationEnabled(True)
        self.dwarf = dwarf
        self._prefs = Prefs()

        self._setup_done = False
        self._blacklist_regs = []

        self.cs = None
        self.uc = None

        self.context = None
        self.thumb = False
        self.end_ptr = 0

        self.current_context = None

        self.stepping = [False, False]
        self._current_instruction = 0
        self._current_cpu_mode = 0

        self._request_stop = False

        # configurations
        self.callbacks_path = None
        self.callbacks = None
        self.instructions_delay = 0

        self._start_address = 0
        self._end_address = 0

    def setup_arm(self):
        self.thumb = self.context.pc.thumb
        if self.thumb:
            self.cs = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
            self.uc = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB)
            self._current_cpu_mode = unicorn.UC_MODE_THUMB
            # Enable VFP instr
            self.uc.mem_map(0x1000, 1024)
            self.uc.mem_write(0x1000, binascii.unhexlify(VFP))
            self.uc.emu_start(0x1000 | 1, 0x1000 + len(VFP))
            self.uc.mem_unmap(0x1000, 1024)
        else:
            self.cs = Cs(CS_ARCH_ARM, CS_MODE_ARM)
            self.uc = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_ARM)
            self._current_cpu_mode = unicorn.UC_MODE_ARM

    def setup_arm64(self):
        self.uc = unicorn.Uc(unicorn.UC_ARCH_ARM64,
                             unicorn.UC_MODE_LITTLE_ENDIAN)
        self.cs = Cs(CS_ARCH_ARM64, CS_MODE_LITTLE_ENDIAN)
        self._current_cpu_mode = unicorn.UC_MODE_LITTLE_ENDIAN

    def setup_x86(self):
        self.uc = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_32)
        self.cs = Cs(CS_ARCH_X86, CS_MODE_32)

    def setup_x64(self):
        self.uc = unicorn.Uc(unicorn.UC_ARCH_X86, unicorn.UC_MODE_64)
        self.cs = Cs(CS_ARCH_X86, CS_MODE_64)

    def _setup(self):
        if self.dwarf.arch == 'arm':
            self.setup_arm()
        elif self.dwarf.arch == 'arm64':
            self.setup_arm64()
        elif self.dwarf.arch == 'ia32':
            self.setup_x86()
        elif self.dwarf.arch == 'x64':
            self.setup_x64()
        else:
            # unsupported arch
            raise self.EmulatorSetupFailedError('Unsupported arch')

        if not self.uc or not self.cs:
            raise self.EmulatorSetupFailedError('Unicorn or Capstone missing')

        # enable capstone details
        if self.cs is not None:
            self.cs.detail = True

        err = self.map_range(self.context.pc.value)
        if err:
            raise self.EmulatorSetupFailedError('Mapping failed')

        self.current_context = EmulatorContext(self.dwarf)
        for reg in self.current_context._unicorn_registers:
            if reg in self.context.__dict__:
                if reg not in self._blacklist_regs:
                    self.uc.reg_write(
                        self.current_context._unicorn_registers[reg],
                        self.context.__dict__[reg].value)

        self.uc.hook_add(unicorn.UC_HOOK_CODE, self.hook_code)
        self.uc.hook_add(unicorn.UC_HOOK_MEM_WRITE | unicorn.UC_HOOK_MEM_READ,
                         self.hook_mem_access)
        self.uc.hook_add(
            unicorn.UC_HOOK_MEM_FETCH_UNMAPPED
            | unicorn.UC_HOOK_MEM_WRITE_UNMAPPED
            | unicorn.UC_HOOK_MEM_READ_UNMAPPED, self.hook_unmapped)
        self.current_context.set_context(self.uc)
        return True

    def run(self):
        # dont call this func
        if not self._setup_done:
            return
        try:
            self.uc.emu_start(
                self._start_address,
                0xffffffffffffffff)  # end is handled in hook_code
        except unicorn.UcError as e:
            self.log_to_ui('[*] error: ' + str(e))
        except Exception as e:
            self.log_to_ui('[*] error: ' + str(e))

        self._setup_done = False
        self.onEmulatorStop.emit()

    def api(self, parts):
        """
        expose api to js side for allowing emulator interaction while scripting
        :param parts: arr -> cmd api split by ":::"
        :return: the result from the api
        """
        cmd = parts[0]
        if cmd == 'clean':
            return self.clean()
        elif cmd == 'setup':
            return self.setup(parts[1])
        elif cmd == 'start':
            return self.emulate(parts[1])

    def clean(self):
        if self.isRunning():
            return False

        self.stepping = [False, False]
        self._current_instruction = 0
        self._current_cpu_mode = 0

        return self._setup()

    def hook_code(self, uc, address, size, user_data):
        # QApplication.processEvents()
        if self._request_stop:
            self.log_to_ui('Error: Emulator stopped - reached end')
            self.stop()
            return

        if self._current_instruction == address:
            # we should never be here or it is looping
            self.log_to_ui('Error: Emulator stopped - looping')
            self.stop()

        self._current_instruction = address

        # check if pc/eip is end_ptr
        pc = 0  # address should be pc too ???
        if self.dwarf.arch == 'arm':
            pc = uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC)
        elif self.dwarf.arch == 'arm64':
            pc = uc.reg_read(unicorn.arm64_const.UC_ARM64_REG_PC)
        elif self.dwarf.arch == 'ia32':
            pc = uc.reg_read(unicorn.x86_const.UC_X86_REG_EIP)
        elif self.dwarf.arch == 'x64':
            pc = uc.reg_read(unicorn.x86_const.UC_X86_REG_RIP)

        if pc == self._end_address:
            self._request_stop = True

        # set the current context
        self.current_context.set_context(uc)

        # if it's arm we query the cpu mode to detect switches between arm and thumb and set capstone mode if needed
        if self.cs.arch == CS_ARCH_ARM:
            mode = self.uc.query(unicorn.UC_QUERY_MODE)
            if self._current_cpu_mode != mode:
                self._current_cpu_mode = mode
                self.cs.mode = self._current_cpu_mode
                self.thumb = self._current_cpu_mode == unicorn.UC_MODE_THUMB

        if self.stepping[0]:
            if self.stepping[1]:
                uc.emu_stop()
                return
            else:
                self.stepping[1] = True

        try:
            try:
                assembly = self.cs.disasm(bytes(uc.mem_read(address, size)),
                                          address)
            except:
                self.log_to_ui('Error: Emulator stopped - disasm')
                self.stop()

            for i in assembly:
                # QApplication.processEvents()
                instruction = Instruction(self.dwarf, i)
                self.onEmulatorHook.emit(instruction)
                if self.callbacks is not None:
                    try:
                        self.callbacks.hook_code(self, instruction, address,
                                                 size)
                    except:
                        # hook code not implemented in callbacks
                        pass

            # time.sleep(self.instructions_delay)
        except:
            self.log_to_ui('Error: Emulator stopped')
            self.stop()

    def hook_mem_access(self, uc, access, address, size, value, user_data):
        v = value
        if access == unicorn.UC_MEM_READ:
            v = int.from_bytes(uc.mem_read(address, size), 'little')
        self.onEmulatorMemoryHook.emit([uc, access, address, v])
        if self.callbacks is not None:
            try:
                self.callbacks.hook_memory_access(self, access, address, size,
                                                  v)
            except:
                # hook code not implemented in callbacks
                pass

    def hook_unmapped(self, uc, access, address, size, value, user_data):
        self.log_to_ui(
            "[*] Trying to access an unmapped memory address at 0x%x" %
            address)
        err = self.map_range(address)
        if err > 0:
            self.log_to_ui('[*] Error %d mapping range at %s' %
                           (err, hex(address)))
            return False
        return True

    def invalida_configurations(self):
        self.callbacks_path = self._prefs.get(prefs.EMULATOR_CALLBACKS_PATH,
                                              '')
        self.instructions_delay = self._prefs.get(
            prefs.EMULATOR_INSTRUCTIONS_DELAY, 0)

    def map_range(self, address):
        range_ = Range(Range.SOURCE_TARGET, self.dwarf)
        if range_.init_with_address(address) > 0:
            return 300
        try:
            self.uc.mem_map(range_.base, range_.size)
        except Exception as e:
            self.dwarf.log(e)
            return 301

        try:
            self.uc.mem_write(range_.base, range_.data)
        except Exception as e:
            self.dwarf.log(e)
            return 302

        self.log_to_ui("[*] Mapped %d at 0x%x" % (range_.size, range_.base))
        self.onEmulatorMemoryRangeMapped.emit([range_.base, range_.size])

        return 0

    def setup(self, tid=0):
        if tid == 0:
            # get current context tid if none provided
            tid = self.dwarf.context_tid

        # make sure it's int < pp: why make sure its int and then using str(tid) later??
        #                       when calling from api its str
        if isinstance(tid, str):
            try:
                tid = int(tid)
            except ValueError:
                return False

        if not isinstance(tid, int):
            return False

        self.context = None

        if str(tid) in self.dwarf.contexts:
            self.context = self.dwarf.contexts[str(tid)]

        if self.context is None:
            return False

        try:
            self._setup()
        except self.EmulatorSetupFailedError:
            return False

        return True

    def start(self, priority=QThread.HighPriority):
        # dont call this func
        if not self._setup_done:
            return
        return super().start(priority=priority)

    def emulate(self, until=0):
        if self.isRunning():
            raise self.EmulatorAlreadyRunningError()

        if isinstance(until, str):
            try:
                until = int(until, 16)
            except ValueError:
                until = 0

        if until and isinstance(until, int):
            self.end_ptr = utils.parse_ptr(until)
            if self.end_ptr == 0:
                # invalid end pointer
                raise self.EmulatorSetupFailedError('Invalid EndPtr')

        if self.context is None:
            if not self.setup():
                raise self.EmulatorSetupFailedError('Setup failed')

        # calculate the start address
        address = self._current_instruction
        if address == 0:
            if self.uc._arch == unicorn.UC_ARCH_ARM:
                address = self.uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC)
            elif self.uc._arch == unicorn.UC_ARCH_ARM64:
                address = self.uc.reg_read(unicorn.arm64_const.UC_ARM64_REG_PC)
            elif self.uc._arch == unicorn.UC_ARCH_X86 and self.uc._mode == unicorn.UC_MODE_32:
                address = self.uc.reg_read(unicorn.x86_const.UC_X86_REG_EIP)
            elif self.uc._arch == unicorn.UC_ARCH_X86 and self.uc._mode == unicorn.UC_MODE_64:
                address = self.uc.reg_read(unicorn.x86_const.UC_X86_REG_RIP)
            else:
                raise self.EmulatorSetupFailedError('Unsupported arch')

        if until > 0:
            self.log_to_ui('[*] start emulation from %s to %s' %
                           (hex(address), hex(self.end_ptr)))
        else:
            self.log_to_ui('[*] stepping %s' % hex(address))
        self.onEmulatorStart.emit()

        if self.thumb:
            address = address | 1

        # invalidate prefs before start
        self.invalida_configurations()

        # load callbacks if needed
        if self.callbacks_path is not None and self.callbacks_path != '':
            try:
                spec = spec_from_loader(
                    "callbacks",
                    SourceFileLoader("callbacks", self.callbacks_path))
                self.callbacks = module_from_spec(spec)
                spec.loader.exec_module(self.callbacks)
            except Exception as e:
                self.log_to_ui('[*] failed to load callbacks: %s' % str(e))
                # reset callbacks path
                self._prefs.put(prefs.EMULATOR_CALLBACKS_PATH, '')
                self.callbacks_path = ''
                self.callbacks = None
        else:
            self.callbacks = None

        # until is 0 (i.e we are stepping)
        if until == 0:
            self.stepping = [True, False]
            # self.end_ptr = address + (self.dwarf.pointer_size * 2) stupid
        else:
            self.stepping = [False, False]

        self._start_address = address
        self._end_address = self.end_ptr
        self._setup_done = True
        self.start()

    def stop(self):
        if self.isRunning():
            self.uc.emu_stop()

    def log_to_ui(self, what):
        self.onEmulatorLog.emit(what)
예제 #6
0
파일: dwarf.py 프로젝트: jack51706/Dwarf
def run_dwarf():
    """ fire it up
    """
    os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
    # os.environ["QT_SCALE_FACTOR"] = "1"
    # os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "0"
    # os.environ["QT_SCREEN_SCALE_FACTORS"] = "1"

    from lib import utils
    from lib.git import Git
    from lib.prefs import Prefs
    from ui.app import AppWindow

    from PyQt5.QtCore import Qt
    from PyQt5.QtGui import QIcon
    from PyQt5.QtWidgets import QApplication

    qapp = QApplication([])

    qapp.setDesktopSettingsAware(True)
    qapp.setAttribute(Qt.AA_EnableHighDpiScaling)
    qapp.setAttribute(Qt.AA_UseHighDpiPixmaps)
    qapp.setLayoutDirection(Qt.LeftToRight)

    qapp.setOrganizationName("https://github.com/iGio90/Dwarf")
    qapp.setApplicationName("dwarf")

    # set icon
    _icon = None
    if os.name == "nt" and os.path.exists(
            utils.resource_path('assets/dwarf.ico')):
        _icon = QIcon(utils.resource_path('assets/dwarf.ico'))
        qapp.setWindowIcon(_icon)
    else:
        if os.path.exists(utils.resource_path('assets/dwarf.png')):
            _icon = QIcon(utils.resource_path('assets/dwarf.png'))
            qapp.setWindowIcon(_icon)

    _prefs = Prefs()
    local_update_disabled = _prefs.get('disable_local_frida_update', False)

    args = process_args()

    did_first_run = _prefs.get('did_first_run', False)
    if False:  # did_first_run:
        _prefs.put('did_first_run', True)
        SetupDialog.showDialog(_prefs)

    if not local_update_disabled:
        _git = Git()
        import frida
        remote_frida = _git.get_frida_version()
        local_frida = frida.__version__

        if remote_frida and local_frida != remote_frida[0]['tag_name']:
            print('Updating local frida version to ' +
                  remote_frida[0]['tag_name'])
            try:
                res = utils.do_shell_command(
                    'pip3 install frida --upgrade --user')
                if 'Successfully installed frida-' + remote_frida[0][
                        'tag_name'] in res:
                    _on_restart()
                elif 'Requirement already up-to-date' in res:
                    if os.path.exists('.git_cache'):
                        shutil.rmtree('.git_cache', ignore_errors=True)
                else:
                    print('failed to update local frida')
                    print(res)
            except Exception as e:  # pylint: disable=broad-except, invalid-name
                print('failed to update local frida')
                print(str(e))

    if os.name == 'nt':
        # windows stuff
        import ctypes
        try:
            # write ini to show folder with dwarficon
            folder_stuff = "[.ShellClassInfo]\n"
            folder_stuff += "IconResource=assets\\dwarf.ico,0\n"
            folder_stuff += "[ViewState]\n"
            folder_stuff += "Mode=\n"
            folder_stuff += "Vid=\n"
            folder_stuff += "FolderType=Generic\n"
            try:
                with open('desktop.ini', 'w') as ini:
                    ini.writelines(folder_stuff)

                # set fileattributes to hidden + systemfile
                ctypes.windll.kernel32.SetFileAttributesW(
                    r'desktop.ini', 0x02 | 0x04
                )  # FILE_ATTRIBUTE_HIDDEN = 0x02 | FILE_ATTRIBUTE_SYSTEM = 0x04
            except PermissionError:
                # its hidden+system already
                pass

            # fix for showing dwarf icon in windows taskbar instead of pythonicon
            _appid = u'iGio90.dwarf.debugger'
            ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
                _appid)

            ctypes.windll.user32.SetProcessDPIAware()

        except Exception:  # pylint: disable=broad-except
            pass

    app_window = AppWindow(args)
    app_window.setWindowIcon(_icon)
    app_window.onRestart.connect(_on_restart)

    try:
        sys.exit(qapp.exec_())
    except SystemExit as sys_err:
        if sys_err.code == 0:
            # thanks for using dwarf
            print('Thank\'s for using Dwarf\nHave a nice day...')
        else:
            # something was wrong
            print('sysexit with: %d' % sys_err.code)