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(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(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)
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(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(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(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(EMULATOR_INSTRUCTIONS_DELAY, float(dialog.instructions_delay.text())) except: pass
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._last_emulated_instruction = None 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 # reset single instance preferences from ucdwarf.plugin import EMULATOR_CALLBACKS_PATH self._prefs.put(EMULATOR_CALLBACKS_PATH, '')
def copy_hex_to_clipboard(hex_str): """ Helper for copying hexstr in prefered style """ from dwarf.lib.prefs import Prefs _prefs = Prefs() uppercase = (_prefs.get('dwarf_ui_hexstyle', 'upper').lower() == 'upper') if isinstance(hex_str, str): if hex_str.startswith('0x'): if uppercase: hex_str = hex_str.upper().replace('0X', '0x') else: hex_str = hex_str.lower() pyperclip.copy(hex_str) elif isinstance(hex_str, int): str_fmt = '0x{0:x}' if uppercase: str_fmt = '0x{0:X}' pyperclip.copy(str_fmt.format(hex_str))
def __init__(self, parent=None, search_enabled=True): super(DwarfListView, self).__init__(parent=parent) self._search_enabled = search_enabled self._current_search = '' self._uppercase_hex = True _prefs = Prefs() self.rows_dualcolor = _prefs.get('dwarf_ui_alternaterowcolors', False) self.uppercase_hex = _prefs.get('dwarf_ui_hexstyle', 'upper').lower() == 'upper' self.setEditTriggers(self.NoEditTriggers) self.setHeaderHidden(False) self.setAutoFillBackground(True) self.setRootIsDecorated(False) # TODO: use filter self._proxy_model = QSortFilterProxyModel(self) self._proxy_model.setSourceModel(self.model())
def __init__(self, parent=None): super(DisassemblyView, self).__init__(parent=parent) _prefs = Prefs() self._uppercase_hex = (_prefs.get('dwarf_ui_hexstyle', 'upper').lower() == 'upper') self._app_window = parent self.debug_panel = None self.setAutoFillBackground(True) self._running_disasm = False # setting font self.font = utils.get_os_monospace_font() self.font.setFixedPitch(True) self.setFont(self.font) self._char_width = QFontMetricsF(self.font).width('#') # self.fontMetrics().width("#") if (self._char_width % 1) < .5: self.font.setLetterSpacing(QFont.AbsoluteSpacing, -(self._char_width % 1.0)) self._char_width -= self._char_width % 1.0 else: self.font.setLetterSpacing(QFont.AbsoluteSpacing, 1.0 - (self._char_width % 1.0)) self._char_width += 1.0 - (self._char_width % 1.0) self._char_height = self.fontMetrics().height() self._base_line = self.fontMetrics().ascent() self._history = [] self._lines = [] self._longest_bytes = 0 self._longest_mnemonic = 0 self._ctrl_colors = { 'background': QColor('#181818'), 'foreground': QColor('#666'), 'jump_arrows': QColor('#444'), 'jump_arrows_hover': QColor('#ef5350'), 'divider': QColor('#666'), 'line': QColor('#111'), 'selection_fg': QColor(Qt.white), 'selection_bg': QColor('#630000') } self._jump_color = QColor('#39a') self._header_height = 0 self._ver_spacing = 2 self._dash_pen = QPen(self._ctrl_colors['jump_arrows'], 2.0, Qt.DashLine) self._solid_pen = QPen(self._ctrl_colors['jump_arrows'], 2.0, Qt.SolidLine) self._line_pen = QPen(self._ctrl_colors['divider'], 0, Qt.SolidLine) self._breakpoint_linewidth = 5 self._jumps_width = 100 self.setMouseTracking(True) self.current_jump = -1 self._current_line = -1 self._highlighted_line = -1 self._display_jumps = True self._follow_jumps = True self.pos = 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)
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)
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 dwarf.lib import utils from dwarf.lib.git import Git from dwarf.lib.prefs import Prefs from dwarf.ui.app import AppWindow from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QApplication import dwarf.resources # pylint: disable=unused-import 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") qapp.setApplicationDisplayName('Dwarf') # set icon _icon = None if os.name == "nt": if os.path.exists(utils.resource_path('assets/dwarf.ico')): _icon = QIcon(utils.resource_path('assets/dwarf.ico')) else: _icon = QIcon(':/assets/dwarf.ico') else: if os.path.exists(utils.resource_path('assets/dwarf.png')): _icon = QIcon(utils.resource_path('assets/dwarf.png')) else: _icon = QIcon(':/assets/dwarf.png') if _icon: 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: from dwarf.ui.dialogs.dialog_setup import SetupDialog # 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: if os.path.exists(utils.resource_path('assets/dwarf.ico')): # write ini to show folder with dwarficon folder_stuff = "[.ShellClassInfo]\n" folder_stuff += "IconResource=dwarf\\assets\\dwarf.ico,0\n" folder_stuff += "[ViewState]\n" folder_stuff += "Mode=\n" folder_stuff += "Vid=\n" folder_stuff += "FolderType=Generic\n" try: ini_path = os.path.dirname( os.path.abspath(__file__) ) + os.sep + os.pardir + os.sep + 'desktop.ini' with open(ini_path, 'w') as ini: ini.writelines(folder_stuff) # set fileattributes to hidden + systemfile ctypes.windll.kernel32.SetFileAttributesW( ini_path, 0x02 | 0x04 | ~0x20 ) # 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 try: # parse target as pid args.pid = int(args.any) except ValueError: args.pid = 0 # default to local if not specified if args.target is None: args.target = 'local' app_window = AppWindow(args) if _icon: 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)
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._last_emulated_instruction = None 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 # reset single instance preferences from ucdwarf.plugin import EMULATOR_CALLBACKS_PATH self._prefs.put(EMULATOR_CALLBACKS_PATH, '') 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 self._last_emulated_instruction = 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 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 instruction.is_jump: # do not break at jump as we should calculate next instruction properly return if self.step_mode == STEP_MODE_SINGLE: self.stop() else: if instruction.is_call and self.step_mode == STEP_MODE_FUNCTION: self.stop() self._last_emulated_instruction = instruction 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 invalidate_configurations(self): from ucdwarf.plugin import EMULATOR_CALLBACKS_PATH, EMULATOR_INSTRUCTIONS_DELAY self.callbacks_path = self._prefs.get(EMULATOR_CALLBACKS_PATH, '') self.instructions_delay = self._prefs.get(EMULATOR_INSTRUCTIONS_DELAY, 0) def map_range(self, address): self.dwarf.io.read_range_async(address, self.on_memory_read) return 0 def on_memory_read(self, base, data, offset): size = len(data) try: self.uc.mem_map(base, size) except Exception as e: self.dwarf.log(e) return 301 try: self.uc.mem_write(base, data) except Exception as e: self.dwarf.log(e) return 302 self.log_to_ui("[*] Mapped %d at 0x%x" % (size, base)) self.onEmulatorMemoryRangeMapped.emit([base, size]) 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') self.onEmulatorStart.emit() # invalidate prefs before start self.invalidate_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 from ucdwarf.plugin import EMULATOR_CALLBACKS_PATH self._prefs.put(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)