Example #1
0
    def load_script(args, proc, spawned=False):
        try:
            if not os.path.exists(utils.resource_path('lib/core.js')):
                print('core.js not found!')
                exit(0)

            with open(utils.resource_path('lib/core.js'), 'r') as core_script:
                script_content = core_script.read()

            _script = proc.create_script(script_content, runtime='v8')
            #_script.on('message', _on_message)
            #_script.on('destroyed', _on_script_destroyed)
            _script.load()

            is_debug = args.debug_script
            # this break_at_start have same behavior from args or from the checkbox i added
            _script.exports.init(args.break_start, is_debug, spawned)

            if args.script is not None:
                if os.path.exists(args.script):
                    with open(args.script, 'r') as script_file:
                        user_script = script_file.read()

                    _script.exports.api(0, 'evaluateFunction', [user_script])

            plugin_manager = PluginManager(None)
            plugin_manager.reload_plugins()

            for plugin in plugin_manager.plugins:
                plugin_instance = plugin_manager.plugins[plugin]
                try:
                    _script.exports.api(0, 'evaluateFunction',
                                        plugin_instance.__get_agent__())
                except Exception as e:
                    pass

            return 0
        except frida.ProcessNotFoundError:
            error_msg = 'Process not found (ProcessNotFoundError)'
            was_error = True
        except frida.ProcessNotRespondingError:
            error_msg = 'Process not responding (ProcessNotRespondingError)'
            was_error = True
        except frida.TimedOutError:
            error_msg = 'Frida timeout (TimedOutError)'
            was_error = True
        except frida.ServerNotRunningError:
            error_msg = 'Frida not running (ServerNotRunningError)'
            was_error = True
        except frida.TransportError:
            error_msg = 'Frida timeout was reached (TransportError)'
            was_error = True

        if was_error:
            utils.show_message_box(error_msg)
        return 1
Example #2
0
    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 = [
            'debug', 'modules', 'ranges', 'jvm-inspector', 'jvm-debugger'
        ]

        self._is_newer_dwarf = False
        self.q_settings = QSettings(utils.home_path() + "dwarf_window_pos.ini",
                                    QSettings.IniFormat)

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

        self._initialize_ui_elements()

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

        # load external assets
        _app = QApplication.instance()

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

        # load font
        if os.path.exists(utils.resource_path('assets/Anton.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/Anton.ttf'))
        else:
            QFontDatabase.addApplicationFont(':/assets/Anton.ttf')

        if os.path.exists(utils.resource_path('assets/OpenSans-Regular.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/OpenSans-Regular.ttf'))
        else:
            QFontDatabase.addApplicationFont(':/assets/OpenSans-Regular.ttf')

        if os.path.exists(utils.resource_path('assets/OpenSans-Bold.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/OpenSans-Bold.ttf'))
        else:
            QFontDatabase.addApplicationFont(':/assets/OpenSans-Bold.ttf')

        font = QFont("OpenSans", 9, QFont.Normal)
        # TODO: add settingsdlg
        font_size = self.prefs.get('dwarf_ui_font_size', 12)
        font.setPixelSize(font_size)
        _app.setFont(font)

        # 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)

        # pluginmanager
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.reload_plugins()

        if dwarf_args.any == '':
            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:
            print('* Starting new Session')
            self._start_session(dwarf_args.target)
Example #3
0
class AppWindow(QMainWindow):
    onRestart = pyqtSignal(name='onRestart')
    onSystemUIElementCreated = pyqtSignal(str,
                                          QWidget,
                                          name='onSystemUIElementCreated')
    onSystemUIElementRemoved = pyqtSignal(str, name='onSystemUIElementRemoved')

    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 = [
            'debug', 'modules', 'ranges', 'jvm-inspector', 'jvm-debugger'
        ]

        self._is_newer_dwarf = False
        self.q_settings = QSettings(utils.home_path() + "dwarf_window_pos.ini",
                                    QSettings.IniFormat)

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

        self._initialize_ui_elements()

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

        # load external assets
        _app = QApplication.instance()

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

        # load font
        if os.path.exists(utils.resource_path('assets/Anton.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/Anton.ttf'))
        else:
            QFontDatabase.addApplicationFont(':/assets/Anton.ttf')

        if os.path.exists(utils.resource_path('assets/OpenSans-Regular.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/OpenSans-Regular.ttf'))
        else:
            QFontDatabase.addApplicationFont(':/assets/OpenSans-Regular.ttf')

        if os.path.exists(utils.resource_path('assets/OpenSans-Bold.ttf')):
            QFontDatabase.addApplicationFont(
                utils.resource_path('assets/OpenSans-Bold.ttf'))
        else:
            QFontDatabase.addApplicationFont(':/assets/OpenSans-Bold.ttf')

        font = QFont("OpenSans", 9, QFont.Normal)
        # TODO: add settingsdlg
        font_size = self.prefs.get('dwarf_ui_font_size', 12)
        font.setPixelSize(font_size)
        _app.setFont(font)

        # 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)

        # pluginmanager
        self.plugin_manager = PluginManager(self)
        self.plugin_manager.reload_plugins()

        if dwarf_args.any == '':
            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:
            print('* Starting new Session')
            self._start_session(dwarf_args.target)

    def _initialize_ui_elements(self):
        # dockwidgets
        self.watchpoints_dwidget = None
        self.breakpoint_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.backtrace_panel = None
        self.bookmarks_panel = None
        self.console_panel = None
        self.context_panel = None
        self.debug_panel = None
        self.contexts_list_panel = None
        self.data_panel = None
        self.ftrace_panel = None
        self.breakpoints_panel = None
        self.objc_inspector_panel = None
        self.java_inspector_panel = None
        self.java_explorer_panel = None
        self.java_trace_panel = None
        self.modules_panel = None
        self.ranges_panel = None
        self.search_panel = None
        self.smali_panel = None
        self.watchpoints_panel = None
        self.welcome_window = None

        self._ui_elems = []

    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)

        # plugins
        if self.plugin_manager.plugins:
            self.plugin_menu = QMenu('Plugins', self)
            for plugin in self.plugin_manager.plugins:
                plugin_instance = self.plugin_manager.plugins[plugin]
                plugin_sub_menu = self.plugin_menu.addMenu(
                    plugin_instance.name)

                try:
                    actions = plugin_instance.__get_top_menu_actions__()
                    for action in actions:
                        plugin_sub_menu.addAction(action)
                except:
                    pass

                if not plugin_sub_menu.isEmpty():
                    plugin_sub_menu.addSeparator()

                about = plugin_sub_menu.addAction('About')
                about.triggered.connect(
                    lambda x, item=plugin: self._show_plugin_about(item))

            if not self.plugin_menu.isEmpty():
                self.menu.addMenu(self.plugin_menu)

        self.view_menu = QMenu('View', self)
        self.panels_menu = QMenu('Panels', self.view_menu)
        self.panels_menu.addAction('Search',
                                   lambda: self.show_main_tab('search'),
                                   shortcut=QKeySequence(Qt.CTRL + Qt.Key_F3))
        self.panels_menu.addAction('Modules',
                                   lambda: self.show_main_tab('modules'))
        self.panels_menu.addAction('Ranges',
                                   lambda: self.show_main_tab('ranges'))

        self.view_menu.addMenu(self.panels_menu)

        self.debug_view_menu = self.view_menu.addMenu('Debug')

        self.view_menu.addSeparator()

        self.view_menu.addAction('Hide all',
                                 self._hide_all_widgets,
                                 shortcut=QKeySequence(Qt.CTRL + Qt.Key_F1))
        self.view_menu.addAction('Show all',
                                 self._show_all_widgets,
                                 shortcut=QKeySequence(Qt.CTRL + Qt.Key_F2))

        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)

        # tools
        _tools = self.prefs.get('tools')
        if _tools:
            tools_menu = QMenu('Tools', self)

            for _tool in _tools:
                if _tool and _tool['name']:
                    if _tool['name'] == 'sep':
                        tools_menu.addSeparator()
                        continue

                    _cmd = _tool['cmd']

                    tools_menu.addAction(_tool['name'])

            if not tools_menu.isEmpty():
                tools_menu.triggered.connect(self._execute_tool)
                self.menu.addMenu(tools_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 _show_plugin_about(self, plugin):
        plugin = self.plugin_manager.plugins[plugin]
        if plugin:
            info = plugin.__get_plugin_info__()

            version = utils.safe_read_map(info, 'version', '')
            description = utils.safe_read_map(info, 'description', '')
            author = utils.safe_read_map(info, 'author', '')
            homepage = utils.safe_read_map(info, 'homepage', '')
            license_ = utils.safe_read_map(info, 'license', '')

            utils.show_message_box(
                'Name: {0}\nVersion: {1}\nDescription: {2}\nAuthor: {3}\nHomepage: {4}\nLicense: {5}'
                .format(plugin.name, version, description, author, homepage,
                        license_))

    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:
            tab_text = tab_text.lower().replace(' ', '-')
            try:
                self._ui_elems.remove(tab_text)
            except ValueError:  # recheck ValueError: list.remove(x): x not in list
                pass
            self.main_tabs.removeTab(index)

            self.onSystemUIElementRemoved.emit(tab_text)

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

    def _execute_tool(self, qaction):
        if qaction:
            _tools = self.prefs.get('tools')
            if _tools:
                for _tool in _tools:
                    if _tool and _tool['name'] and _tool['name'] != 'sep':
                        if qaction.text() == _tool['name']:
                            try:
                                import subprocess
                                subprocess.Popen(_tool['cmd'],
                                                 creationflags=subprocess.
                                                 CREATE_NEW_CONSOLE)
                            except:
                                pass
                            break

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

    def _hide_all_widgets(self):
        self.watchpoints_dwidget.hide()
        self.breakpoint_dwiget.hide()
        self.bookmarks_dwiget.hide()
        self.registers_dock.hide()
        self.console_dock.hide()
        self.backtrace_dock.hide()
        self.threads_dock.hide()

    def _show_all_widgets(self):
        self.watchpoints_dwidget.show()
        self.breakpoint_dwiget.show()
        self.bookmarks_dwiget.show()
        self.registers_dock.show()
        self.console_dock.show()
        self.backtrace_dock.show()
        self.threads_dock.show()

    def _menu_reload_core(self):
        self.dwarf.script.exports.reload()

    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):
        name = name.lower()
        # 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 == '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 == 'data':
            index = self.main_tabs.indexOf(self.data_panel)
        elif name == 'jvm-tracer':
            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 == 'objc-inspector':
            index = self.main_tabs.indexOf(self.objc_inspector_panel)
        elif name == 'smali':
            index = self.main_tabs.indexOf(self.smali_panel)

        self.main_tabs.setCurrentIndex(index)

    def jump_to_address(self, ptr, view=0, show_panel=True):
        if show_panel:
            self.show_main_tab('debug')
        self.debug_panel.jump_to_address(ptr, view=view)

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

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

    @pyqtSlot(name='mainMenuDocumentation')
    def _menu_documentation(self):
        QDesktopServices.openUrl(QUrl('http://www.giovanni-rocca.com/dwarf/'))

    @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):
        elem = elem.lower()

        if not isinstance(elem, str):
            return

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

        elem_wiget = None

        if elem == 'watchpoints':
            from dwarf.ui.session_widgets.watchpoints import WatchpointsWidget
            self.watchpoints_dwidget = QDockWidget('Watchpoints', self)
            self.watchpoints_panel = WatchpointsWidget(self)
            # dont respond to dblclick mem cant be shown
            # self.watchpoints_panel.onItemDoubleClicked.connect(
            #    self._on_watchpoint_clicked)
            self.watchpoints_panel.onItemRemoved.connect(
                self._on_watchpoint_removeditem)
            self.watchpoints_panel.onItemAdded.connect(
                self._on_watchpoint_added)
            self.watchpoints_dwidget.setWidget(self.watchpoints_panel)
            self.watchpoints_dwidget.setObjectName('WatchpointsWidget')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.watchpoints_dwidget)
            self.view_menu.addAction(
                self.watchpoints_dwidget.toggleViewAction())
            elem_wiget = self.watchpoints_panel
        elif elem == 'breakpoints':
            from dwarf.ui.session_widgets.breakpoints import BreakpointsWidget
            self.breakpoint_dwiget = QDockWidget('breakpoint', self)
            self.breakpoints_panel = BreakpointsWidget(self)
            self.breakpoints_panel.onBreakpointRemoved.connect(
                self._on_breakpoint_removed)
            self.breakpoint_dwiget.setWidget(self.breakpoints_panel)
            self.breakpoint_dwiget.setObjectName('breakpointWidget')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.breakpoint_dwiget)
            self.view_menu.addAction(self.breakpoint_dwiget.toggleViewAction())
            elem_wiget = self.breakpoints_panel
        elif elem == 'bookmarks':
            from dwarf.ui.session_widgets.bookmarks import BookmarksWidget
            self.bookmarks_dwiget = QDockWidget('Boomarks', self)
            self.bookmarks_panel = BookmarksWidget(self)
            self.bookmarks_dwiget.setWidget(self.bookmarks_panel)
            self.bookmarks_dwiget.setObjectName('BookmarksWidget')
            self.addDockWidget(Qt.LeftDockWidgetArea, self.bookmarks_dwiget)
            self.view_menu.addAction(self.bookmarks_dwiget.toggleViewAction())
            elem_wiget = self.bookmarks_panel
        elif elem == 'registers':
            from dwarf.ui.session_widgets.context import ContextWidget
            self.registers_dock = QDockWidget('Context', self)
            self.context_panel = ContextWidget(self)
            self.registers_dock.setWidget(self.context_panel)
            self.registers_dock.setObjectName('ContextWidget')
            self.addDockWidget(Qt.RightDockWidgetArea, self.registers_dock)
            self.view_menu.addAction(self.registers_dock.toggleViewAction())
            elem_wiget = self.context_panel
        elif elem == 'debug':
            from dwarf.ui.panels.panel_debug import QDebugPanel
            self.debug_panel = QDebugPanel(self)
            self.main_tabs.addTab(self.debug_panel, 'Debug')
            elem_wiget = self.debug_panel
        elif elem == 'jvm-debugger':
            from dwarf.ui.panels.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)
            elem_wiget = self.java_explorer_panel
        elif elem == 'jvm-inspector':
            from dwarf.ui.panels.panel_java_inspector import JavaInspector
            self.java_inspector_panel = JavaInspector(self)
            self.main_tabs.addTab(self.java_inspector_panel, 'JVM inspector')
            elem_wiget = self.java_inspector_panel
        elif elem == 'objc-inspector':
            from dwarf.ui.panels.panel_objc_inspector import ObjCInspector
            self.objc_inspector_panel = ObjCInspector(self)
            self.main_tabs.addTab(self.objc_inspector_panel, 'ObjC inspector')
            elem_wiget = self.objc_inspector_panel
        elif elem == 'console':
            from dwarf.ui.session_widgets.console import ConsoleWidget
            self.console_dock = QDockWidget('Console', self)
            self.console_panel = ConsoleWidget(self)
            if self.dwarf_args.script and len(
                    self.dwarf_args.script) > 0 and os.path.exists(
                        self.dwarf_args.script):
                with open(self.dwarf_args.script, 'r') as f:
                    self.console_panel.get_js_console(
                    ).script_file = self.dwarf_args.script
                    self.console_panel.get_js_console(
                    ).function_content = f.read()
            self.dwarf.onLogToConsole.connect(self._log_js_output)
            self.dwarf.onLogEvent.connect(self._log_event)
            self.console_dock.setWidget(self.console_panel)
            self.console_dock.setObjectName('ConsoleWidget')
            self.addDockWidget(Qt.BottomDockWidgetArea, self.console_dock)
            self.view_menu.addAction(self.console_dock.toggleViewAction())
            elem_wiget = self.console_panel
        elif elem == 'backtrace':
            from dwarf.ui.session_widgets.backtrace import BacktraceWidget
            self.backtrace_dock = QDockWidget('Backtrace', self)
            self.backtrace_panel = BacktraceWidget(self)
            self.backtrace_dock.setWidget(self.backtrace_panel)
            self.backtrace_dock.setObjectName('BacktraceWidget')
            self.backtrace_panel.onShowMemoryRequest.connect(
                self._on_showmemory_request)
            self.addDockWidget(Qt.RightDockWidgetArea, self.backtrace_dock)
            self.view_menu.addAction(self.backtrace_dock.toggleViewAction())
            elem_wiget = self.backtrace_panel
        elif elem == 'threads':
            from dwarf.ui.session_widgets.threads import ThreadsWidget
            self.threads_dock = QDockWidget('Threads', self)
            self.contexts_list_panel = ThreadsWidget(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())
            elem_wiget = self.contexts_list_panel
        elif elem == 'modules':
            from dwarf.ui.panels.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.onAddBreakpoint.connect(
                self._on_addmodule_breakpoint)
            self.modules_panel.onDumpBinary.connect(self._on_dump_module)
            self.main_tabs.addTab(self.modules_panel, 'Modules')
            elem_wiget = self.modules_panel
        elif elem == 'ranges':
            from dwarf.ui.panels.panel_ranges import RangesPanel
            self.ranges_panel = RangesPanel(self)
            self.ranges_panel.onItemDoubleClicked.connect(
                self._range_dblclicked)
            self.ranges_panel.onDumpBinary.connect(self._on_dump_module)
            # connect to watchpointpanel func
            self.ranges_panel.onAddWatchpoint.connect(
                self.watchpoints_panel.do_addwatchpoint_dlg)
            self.main_tabs.addTab(self.ranges_panel, 'Ranges')
            elem_wiget = self.ranges_panel
        elif elem == 'search':
            from dwarf.ui.panels.panel_search import SearchPanel
            self.search_panel = SearchPanel(self)
            self.main_tabs.addTab(self.search_panel, 'Search')
            elem_wiget = self.search_panel
        elif elem == 'data':
            from dwarf.ui.panels.panel_data import DataPanel
            self.data_panel = DataPanel(self)
            self.main_tabs.addTab(self.data_panel, 'Data')
            elem_wiget = self.data_panel
        elif elem == 'jvm-tracer':
            from dwarf.ui.panels.panel_java_trace import JavaTracePanel
            self.java_trace_panel = JavaTracePanel(self)
            self.main_tabs.addTab(self.java_trace_panel, 'JVM tracer')
            elem_wiget = self.java_trace_panel
        elif elem == 'smali':
            from dwarf.ui.panels.panel_smali import SmaliPanel
            self.smali_panel = SmaliPanel()
            self.main_tabs.addTab(self.smali_panel, 'Smali')
            elem_wiget = self.smali_panel
        else:
            print('no handler for elem: ' + elem)

        if elem_wiget is not None:
            self.onSystemUIElementCreated.emit(elem, elem_wiget)

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

    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 ftrace(self):
        return self.ftrace_panel

    @property
    def breakpoint(self):
        return self.breakpoints_panel

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

    @property
    def objc_inspector(self):
        return self.objc_inspector_panel

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

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

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

    @property
    def watchpoints(self):
        return self.watchpoints_panel

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

    @property
    def ui_elements(self):
        return self._ui_elems

    # ************************************************************************
    # **************************** 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.dwarf_args.any = session_data['package']
            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.onProcessAttached.connect(self._on_attached)
        self.dwarf.onProcessDetached.connect(self._on_detached)
        self.dwarf.onScriptLoaded.connect(self._on_script_loaded)

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

        self.dwarf.onAddNativeBreakpoint.connect(self._on_add_breakpoint)
        self.dwarf.onApplyContext.connect(self._apply_context)
        self.dwarf.onThreadResumed.connect(self.on_tid_resumed)
        self.dwarf.onHitModuleInitializationBreakpoint.connect(
            self._on_hit_module_initialization_breakpoint)

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

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

        self.showMaximized()

    def session_stopped(self):
        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 == 'watchpoints':
                self.watchpoints_panel.clear_list()
                self.watchpoints_panel.close()
                self.watchpoints_panel = None
                self.removeDockWidget(self.watchpoints_dwidget)
                self.watchpoints_dwidget = None
            elif elem == 'breakpoint':
                self.breakpoints_panel.close()
                self.breakpoints_panel = None
                self.removeDockWidget(self.breakpoint_dwiget)
                self.breakpoint_dwiget = None
            elif elem == 'registers':
                self.context_panel.close()
                self.context_panel = None
                self.removeDockWidget(self.registers_dock)
                self.registers_dock = None
            elif elem == 'debug':
                self.debug_panel.close()
                self.debug_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.watchpoints_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

        self._initialize_ui_elements()

    def session_closed(self):
        self._initialize_ui_elements()
        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.any != '':
                self.close()

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

            detaches dwarf
        """
        # save windowstuff
        self.q_settings.setValue('dwarf_ui_state', self.saveGeometry())
        self.q_settings.setValue('dwarf_ui_window', self.saveState())

        if self.dwarf:
            try:
                self.dwarf.detach()
            except:
                pass
        super().closeEvent(event)

    def _on_watchpoint_clicked(self, ptr):
        """ Address in Watchpoint/Breakpointpanel was clicked
            show Memory
        """
        if '.' in ptr:  # java_breakpoint
            file_path = ptr.replace('.', os.path.sep)
        else:
            self.jump_to_address(ptr)

    def _on_watchpoint_added(self, ptr):
        """ Watchpoint Entry was added
        """
        try:
            # set highlight
            self.debug_panel.memory_panel.add_highlight(
                HighLight('watchpoint', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

    def _on_watchpoint_removeditem(self, ptr):
        """ Watchpoint Entry was removed
            remove highlight too
        """
        self.debug_panel.memory_panel.remove_highlight(ptr)

    def _on_module_dblclicked(self, data):
        """ Module in ModulePanel was doubleclicked
        """
        addr, size = data
        addr = utils.parse_ptr(addr)
        self.jump_to_address(addr)

    def _on_modulefunc_dblclicked(self, ptr):
        """ Function in ModulePanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.jump_to_address(ptr)

    def _on_dump_module(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 _range_dblclicked(self, ptr):
        """ Range in RangesPanel was doubleclicked
        """
        ptr = utils.parse_ptr(ptr)
        self.jump_to_address(ptr)

    # dwarf handlers
    def _log_js_output(self, output):
        if self.console_panel is not None:
            time_prefix = True
            if len(output.split('\n')) > 1 or len(output.split('<br />')) > 1:
                time_prefix = False
            self.console_panel.get_js_console().log(output,
                                                    time_prefix=time_prefix)

    def _log_event(self, output):
        if self.console_panel is not None:
            self.console_panel.get_events_console().log(output)

    def _on_setranges(self, ranges):
        """ Dwarf wants to set Ranges
            only breakpointed 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)
        else:
            self.show_main_tab('ranges')

    def _on_setmodules(self, modules):
        """ Dwarf wants to set Modules
            only breakpointed 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)
        else:
            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 _on_hit_module_initialization_breakpoint(self, data):
        if self.debug_panel.memory_panel.number_of_lines() == 0:
            data = data[1]
            module_base = int(data['moduleBase'], 16)
            self.jump_to_address(module_base)

    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
        reason = context['reason']
        is_initial_setup = reason == -1
        if manual or (self.dwarf.context_tid and not is_initial_setup):
            self.dwarf.context_tid = context['tid']

        if is_initial_setup:
            self.debug_panel.on_context_setup()

        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.init()
                self.show_main_tab('jvm-debugger')
            else:
                self.context_panel.set_context(context['ptr'], 0,
                                               context['context'])

                if reason == 0:
                    if 'pc' in context['context']:
                        if self.debug_panel.disassembly_panel.number_of_lines(
                        ) == 0 or manual:
                            self.jump_to_address(
                                context['context']['pc']['value'], view=1)
                elif reason == 3:
                    # step
                    # we make the frontend believe we are in the real step pc instead of the frida space
                    context['context']['pc'] = context['ptr']
                    if 'rip' in context['context']:
                        context['context']['rip'] = context['ptr']

                    self.jump_to_address(context['ptr'], view=1)

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

    def _on_add_breakpoint(self, breakpoint):
        try:
            # set highlight
            ptr = breakpoint.get_target()
            ptr = utils.parse_ptr(ptr)
            self.debug_panel.memory_panel.add_highlight(
                HighLight('breakpoint', ptr, self.dwarf.pointer_size))
        except HighlightExistsError:
            pass

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

    def _on_addmodule_breakpoint(self, data):
        ptr, name = data
        self.dwarf.breakpoint_native(input_=ptr)

    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_set_data(self, data):
        if not isinstance(data, list):
            return

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

        if self.data_panel is not None:
            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_detached(self, data):
        reason = data[1]

        if reason == 'application-requested':
            self.session_manager.session.stop()
            return 0

        if self.dwarf is not None:
            ret = QDialogDetached.show_dialog(self.dwarf, data[0], data[1],
                                              data[2])
            if ret == 0:
                self.dwarf.restart_proc()
            elif ret == 1:
                self.session_manager.session.stop()

        return 0

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

    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))

    def _on_showmemory_request(self, ptr):
        # its simple ptr show in memorypanel
        if isinstance(ptr, str):
            ptr = utils.parse_ptr(ptr)
            self.jump_to_address(ptr, 0)

        elif isinstance(ptr, list):
            # TODO: extend
            caller, ptr = ptr
            ptr = utils.parse_ptr(ptr)
            if caller == 'backtrace' or caller == 'bt':
                # jumpto in disasm
                self.jump_to_address(ptr, 1)
Example #4
0
def main():
    import argparse
    import frida
    import os
    import sys
    from dwarf.lib import utils

    plugin_manager = PluginManager(None)

    def process_args():
        """ process commandline params
        """
        parser = argparse.ArgumentParser()

        parser.add_argument(
            "-t",
            "--target",
            default='local',
            type=str,
            help="SessionType - android, ios, local, remote - default: local")

        parser.add_argument(
            "-s",
            "--script",
            type=str,
            help=
            "Path to an additional script to load with dwarf and frida js api")

        parser.add_argument("-dev",
                            "--device",
                            help="DeviceSerial adb devices")

        parser.add_argument("-bs",
                            "--break-start",
                            action='store_true',
                            help="break at start")

        parser.add_argument("-ds",
                            "--debug-script",
                            action='store_true',
                            help="debug outputs from frida script")

        parser.add_argument('any',
                            nargs='?',
                            default='',
                            help='path/pid/package')
        parser.add_argument('args', nargs='*', default=[''], help='arguments')

        args = parser.parse_args()

        return args

    def attach(args, device):
        """ Attach to pid
        """

        _process = None
        was_error = False
        error_msg = ''
        pid = args.pid

        # for commandline arg
        if isinstance(pid, str):
            try:
                process = device.get_process(pid)
                pid = [process.pid, process.name]
            except frida.ProcessNotFoundError as error:
                raise Exception('Frida Error: ' + str(error))

        if isinstance(pid, list):
            pid = pid[0]

        if not isinstance(pid, int):
            raise Exception('Error pid!=int')

        try:
            _process = device.attach(pid)
            _pid = pid
        except frida.ProcessNotFoundError:
            error_msg = 'Process not found (ProcessNotFoundError)'
            was_error = True
        except frida.ProcessNotRespondingError:
            error_msg = 'Process not responding (ProcessNotRespondingError)'
            was_error = True
        except frida.TimedOutError:
            error_msg = 'Frida timeout (TimedOutError)'
            was_error = True
        except frida.ServerNotRunningError:
            error_msg = 'Frida not running (ServerNotRunningError)'
            was_error = True
        except frida.TransportError:
            error_msg = 'Frida timeout was reached (TransportError)'
            was_error = True
        # keep for debug
        except Exception as error:  # pylint: disable=broad-except
            error_msg = error
            was_error = True

        if was_error:
            raise Exception(error_msg)

        load_script(args, _process)

    def spawn(args, device):
        _process = None
        _pid = 0
        package = args.any
        package_args = args.args

        if args is None:
            args = []

        try:
            if package == '-':
                args.pid = os.getpid()
                attach(args, device)
            else:
                if device.type == 'local':
                    _pid = device.spawn([package] + package_args)
                else:
                    # args not supported in remote targets
                    _pid = device.spawn(package)
                _package = package
                _process = device.attach(_pid)
                #_process.on('detached', self._on_detached)
                _spawned = True
        except Exception as e:
            raise Exception('Frida Error: ' + str(e))

        load_script(args, _process, spawned=True)
        device.resume(_pid)

    def on_message(message, payload):
        for plugin in plugin_manager.plugins:
            plugin_instance = plugin_manager.plugins[plugin]
            try:
                plugin_instance.on_frida_message(message, payload)
            except:
                pass

    def load_script(args, proc, spawned=False):
        try:
            if not os.path.exists(utils.resource_path('lib/core.js')):
                print('core.js not found!')
                exit(0)

            with open(utils.resource_path('lib/core.js'), 'r') as core_script:
                script_content = core_script.read()

            _script = proc.create_script(script_content, runtime='v8')
            _script.on('message', on_message)
            #_script.on('destroyed', _on_script_destroyed)
            _script.load()

            is_debug = args.debug_script
            # this break_at_start have same behavior from args or from the checkbox i added
            _script.exports.init(args.break_start, is_debug, spawned)

            plugin_manager.reload_plugins()

            for plugin in plugin_manager.plugins:
                plugin_instance = plugin_manager.plugins[plugin]
                try:
                    _script.exports.api(0, 'evaluateFunction',
                                        [plugin_instance.__get_agent__()])
                    plugin_instance.set_script(_script)
                except Exception as e:
                    pass

            if args.script is not None:
                if os.path.exists(args.script):
                    with open(args.script, 'r') as script_file:
                        user_script = script_file.read()

                    _script.exports.api(0, 'evaluateFunction', [user_script])

            return 0
        except frida.ProcessNotFoundError:
            error_msg = 'Process not found (ProcessNotFoundError)'
            was_error = True
        except frida.ProcessNotRespondingError:
            error_msg = 'Process not responding (ProcessNotRespondingError)'
            was_error = True
        except frida.TimedOutError:
            error_msg = 'Frida timeout (TimedOutError)'
            was_error = True
        except frida.ServerNotRunningError:
            error_msg = 'Frida not running (ServerNotRunningError)'
            was_error = True
        except frida.TransportError:
            error_msg = 'Frida timeout was reached (TransportError)'
            was_error = True

        if was_error:
            utils.show_message_box(error_msg)
        return 1

    ########
    # INIT #
    ########
    args = process_args()

    if not args.target and not args.device:
        print(
            'missing session type. use -t local|android|ios|remote to define the session type'
            ' or specify a device id with --device')
        exit(0)

    if args.any == '':
        print('missing file or package name to attach')
        exit(0)

    device = None
    try:
        if args.device:
            device = frida.get_device(id=args.device)
        else:
            session_type = args.target.lower()
            if session_type == 'local':
                device = frida.get_local_device()
            elif session_type == 'android' or session_type == 'ios':
                device = frida.get_usb_device(5)
            elif session_type == 'remote':
                device = frida.get_remote_device()
    except Exception as e:
        print('failed to get frida device')
        print(e)

    if device is not None:
        try:
            # parse target as pid
            args.pid = int(args.any)
        except ValueError:
            args.pid = 0

        if args.pid > 0:
            print('* Trying to attach to {0}'.format(args.pid))
            try:
                attach(args, device)
            except Exception as e:  # pylint: disable=broad-except
                print('-failed-')
                print('Reason: ' + str(e))
                print('Help: you can use -sp to force spawn')
                exit(0)
        else:
            print('* Trying to spawn {0}'.format(args.any))
            try:
                spawn(args, device)
            except Exception as e:  # pylint: disable=broad-except
                print('-failed-')
                print('Reason: ' + str(e))
                exit(0)

        sys.stdin.read()