def __init__(self, parent=None, device_type='usb'): super().__init__(parent=parent) # dont show for local if device_type != 'usb': return self.parent = parent self.wait_for_devtype = device_type self.is_waiting = True self._adb = Adb() if not self._adb.min_required: raise Exception('Adb missing or no Device') self._git = Git() self.setAutoFillBackground(True) self.setStyleSheet( 'background-color: crimson; color: white; font-weight: bold; margin: 0; padding: 10px;' ) self.setup() self._timer = QTimer() self._timer.setInterval(500) self._timer.timeout.connect(self._on_timer) self._timer.start() self._timer_step = 0 frida.get_device_manager().on('added', self._on_device) frida.get_device_manager().on('removed', self._on_device) self.devices_thread = DevicesUpdateThread(self) self.devices_thread.onAddDevice.connect(self.on_add_deviceitem) self.devices_thread.onDevicesUpdated.connect(self._on_devices_finished) self._update_thread = FridaUpdateThread(self) self._update_thread._adb = self._adb self._update_thread.onStatusUpdate.connect(self._update_statuslbl) self._update_thread.onFinished.connect(self._frida_updated) self._update_thread.onError.connect(self._on_download_error) self.updated_frida_version = '' self.updated_frida_assets_url = {} self._device_id = None self._devices = [] remote_frida = self._git.get_frida_version() if remote_frida is None: self.updated_frida_version = '' self.updated_frida_assets_url.clear() else: remote_frida = remote_frida[0] self.updated_frida_version = remote_frida['tag_name'] for asset in remote_frida['assets']: if 'android-' not in asset: continue try: name = asset['name'] tag_start = name.index('android-') if name.index('server') >= 0: tag = name[tag_start + 8:-3] self.updated_frida_assets_url[tag] = asset[ 'browser_download_url'] except ValueError: pass
def __init__(self, app_window): super(ScriptsDialog, self).__init__(app_window) self.script = None self._app_window = app_window self._script_manager = ScriptsManager() self._git = Git() self.setMinimumWidth(800) box = QVBoxLayout() self.table = ScriptsTable(self) self.table.onScriptSelected.connect(self._item_selected) self.table.setMinimumWidth(800) # create a centered dot icon _section_width = self.table.header().sectionSize(3) self._new_pixmap = QPixmap(max(_section_width, 40), 20) self._new_pixmap.fill(Qt.transparent) painter = QPainter(self._new_pixmap) rect = QRect((_section_width * 0.5) - 5, 0, 20, 20) painter.setBrush(QColor('#666')) painter.setPen(QColor('#666')) painter.drawEllipse(rect) self._dot_icon = QIcon(self._new_pixmap) box.addWidget(self.table) lbl = QLabel('OS Support - A: Android I: IOS W: Windows') box.addWidget(lbl) self.setLayout(box) self._init_list()
class ScriptsManager(QObject): """ ScriptManager signals: scriptsUpdated() """ scriptsUpdated = pyqtSignal(name='scriptsUpdated') def __init__(self): super(ScriptsManager, self).__init__() self._git = Git() self.scripts = {} self.update_scripts() def update_scripts(self): scripts = self._git.get_dwarf_scripts() if scripts is None: return scripts = scripts.replace(' ', '').replace('\t', '').split('\n') submodule_path = '[submodule"' url_path = 'url=' module_name = '' for line in scripts: if line.startswith(submodule_path): module_name = line.replace(submodule_path, "") module_name = module_name[:-2] elif line.startswith(url_path): url = line.replace(url_path, "") if url.endswith('.git'): url = url[:-4] url = url.replace('https://github.com', 'https://raw.githubusercontent.com') info_url = url + '/master/dwarf.json' script_url = url + '/master/script.js' info = self._git.get_script_info(info_url) if info is None: continue self.scripts[module_name] = { 'info': info, 'script': script_url } self.scriptsUpdated.emit() def get_script(self, script_name): return self.scripts[script_name] def get_scripts(self): return self.scripts
def run(self): self.on_status_text.emit('fetching commit list...') try: utils.do_shell_command('git --version') except IOError as io_error: if io_error.errno == 2: # git command not available self.on_status_text.emit( 'error: git not available on your system') return _git = Git() data = _git.get_dwarf_commits() if data is None: self.on_status_text.emit('Failed to fetch commit list. Try later.') return # TODO: check for dev/release version and updated version most_recent_remote_commit = '' most_recent_local_commit = utils.do_shell_command( 'git log -1 master --pretty=format:%H') most_recent_date = '' for commit in data: if most_recent_remote_commit == '': most_recent_remote_commit = commit['sha'] if most_recent_remote_commit != most_recent_local_commit: self.on_update_available.emit() commit = commit['commit'] date = commit['committer']['date'].split('T') if most_recent_date != date[0]: if most_recent_date != '': self.on_add_commit.emit('', True) self.on_add_commit.emit(date[0], True) most_recent_date = date[0] s = ('{0} - {1} ({2})'.format(date[1][:-1], commit['message'], commit['author']['name'])) self.on_add_commit.emit(s, False) if most_recent_remote_commit != most_recent_local_commit: self.on_finished.emit( 'There is an newer Version available... You can use the UpdateButton in Menu' ) else: # keep: it clears status text self.on_finished.emit('')
class DeviceBar(QWidget): """ DeviceBar Signals: onDeviceUpdated() onDeviceSelected(str) # str = id onDeviceChanged(str) # str = id """ onDeviceUpdated = pyqtSignal(str, name="onDeviceUpdated") onDeviceSelected = pyqtSignal(str, name="onDeviceSelected") onDeviceChanged = pyqtSignal(str, name="onDeviceChanged") def __init__(self, parent=None, device_type='usb'): super().__init__(parent=parent) # dont show for local if device_type != 'usb': return self.parent = parent self.wait_for_devtype = device_type self.is_waiting = True self._adb = Adb() if not self._adb.min_required: raise Exception('Adb missing or no Device') self._git = Git() self.setAutoFillBackground(True) self.setStyleSheet( 'background-color: crimson; color: white; font-weight: bold; margin: 0; padding: 10px;' ) self.setup() self._timer = QTimer() self._timer.setInterval(500) self._timer.timeout.connect(self._on_timer) self._timer.start() self._timer_step = 0 frida.get_device_manager().on('added', self._on_device) frida.get_device_manager().on('removed', self._on_device) self.devices_thread = DevicesUpdateThread(self) self.devices_thread.onAddDevice.connect(self.on_add_deviceitem) self.devices_thread.onDevicesUpdated.connect(self._on_devices_finished) self._update_thread = FridaUpdateThread(self) self._update_thread._adb = self._adb self._update_thread.onStatusUpdate.connect(self._update_statuslbl) self._update_thread.onFinished.connect(self._frida_updated) self._update_thread.onError.connect(self._on_download_error) self.updated_frida_version = '' self.updated_frida_assets_url = {} self._device_id = None self._devices = [] remote_frida = self._git.get_frida_version() if remote_frida is None: self.updated_frida_version = '' self.updated_frida_assets_url.clear() else: remote_frida = remote_frida[0] self.updated_frida_version = remote_frida['tag_name'] for asset in remote_frida['assets']: if 'name' not in asset: continue if 'android-' not in asset['name']: continue try: name = asset['name'] tag_start = name.index('android-') if name.index('server') >= 0: tag = name[tag_start + 8:-3] self.updated_frida_assets_url[tag] = asset[ 'browser_download_url'] except ValueError: pass def setup(self): """ Setup ui """ h_box = QHBoxLayout() h_box.setContentsMargins(0, 0, 0, 0) self.update_label = QLabel('Waiting for Device') self.update_label.setFixedWidth(self.parent.width()) self.update_label.setOpenExternalLinks(True) self.update_label.setTextFormat(Qt.RichText) self.update_label.setFixedHeight(35) self.update_label.setTextInteractionFlags(Qt.TextBrowserInteraction) self._install_btn = QPushButton('Install Frida', self.update_label) self._install_btn.setStyleSheet('padding: 0; border-color: white;') self._install_btn.setGeometry(self.update_label.width() - 110, 5, 100, 25) self._install_btn.clicked.connect(self._on_install_btn) self._install_btn.setVisible(False) self._start_btn = QPushButton('Start Frida', self.update_label) self._start_btn.setStyleSheet('padding: 0; border-color: white;') self._start_btn.setGeometry(self.update_label.width() - 110, 5, 100, 25) self._start_btn.clicked.connect(self._on_start_btn) self._start_btn.setVisible(False) self._update_btn = QPushButton('Update Frida', self.update_label) self._update_btn.setStyleSheet('padding: 0; border-color: white;') self._update_btn.setGeometry(self.update_label.width() - 110, 5, 100, 25) self._update_btn.clicked.connect(self._on_install_btn) self._update_btn.setVisible(False) self._restart_btn = QPushButton('Restart Frida', self.update_label) self._restart_btn.setStyleSheet('padding: 0; border-color: white;') self._restart_btn.setGeometry(self.update_label.width() - 110, 5, 100, 25) self._restart_btn.clicked.connect(self._on_restart_btn) self._restart_btn.setVisible(False) self._devices_combobox = QComboBox(self.update_label) self._devices_combobox.setStyleSheet( 'padding: 2px 5px; border-color: white;') self._devices_combobox.setGeometry(self.update_label.width() - 320, 5, 200, 25) self._devices_combobox.currentIndexChanged.connect( self._on_device_changed) self._devices_combobox.setVisible(False) h_box.addWidget(self.update_label) self.setLayout(h_box) def on_add_deviceitem(self, device_ident): """ Adds an Item to the DeviceComboBox """ if device_ident['type'] == self.wait_for_devtype: if device_ident['name'] not in self._devices: self._devices.append(device_ident) self._timer_step = -1 self.is_waiting = False def _on_device_changed(self, index): device = None device_id = self._devices_combobox.itemData(index) if device_id: try: device = frida.get_device(device_id) except: return if device: self._device_id = device.id self._check_device(device) self.onDeviceChanged.emit(self._device_id) def _check_device(self, frida_device): self.update_label.setStyleSheet('background-color: crimson;') self._install_btn.setVisible(False) self._update_btn.setVisible(False) self._start_btn.setVisible(False) self._restart_btn.setVisible(False) self._adb.device = frida_device.id self._device_id = frida_device.id if self._adb.available(): self.update_label.setText('Device: ' + frida_device.name) # try getting frida version device_frida = self._adb.get_frida_version() # frida not found show install button if device_frida is None: self._install_btn.setVisible(True) else: # frida is old show update button if self.updated_frida_version != device_frida: self._start_btn.setVisible(True) self._update_btn.setVisible(False) # old frida is running allow use of this version if self._adb.is_frida_running(): self._start_btn.setVisible(False) if self.updated_frida_assets_url: self._update_btn.setVisible(True) self.update_label.setStyleSheet( 'background-color: yellowgreen;') self.onDeviceUpdated.emit(frida_device.id) # frida not running show start button elif device_frida and not self._adb.is_frida_running(): self._start_btn.setVisible(True) # frida is running with last version show restart button elif device_frida and self._adb.is_frida_running(): self.update_label.setStyleSheet( 'background-color: yellowgreen;') self._restart_btn.setVisible(True) self.onDeviceUpdated.emit(frida_device.id) elif self._adb.non_root_available(): self.update_label.setText('Device: ' + frida_device.name + ' (NOROOT!)') self.onDeviceUpdated.emit(frida_device.id) def _on_devices_finished(self): if self._devices: if len(self._devices) > 1: self._devices_combobox.clear() self._devices_combobox.setVisible(True) self.update_label.setText('Please select the Device: ') for device in self._devices: self._devices_combobox.addItem(device['name'], device['id']) else: self._devices_combobox.setVisible(False) try: device = frida.get_device(self._devices[0]['id']) self._check_device(device) except: pass def _on_timer(self): if self._timer_step == -1: self._timer.stop() return if self._timer_step == 0: self.update_label.setText(self.update_label.text() + ' .') self._timer_step = 1 elif self._timer_step == 1: self.update_label.setText(self.update_label.text() + '.') self._timer_step = 2 elif self._timer_step == 2: self.update_label.setText(self.update_label.text() + '.') self._timer_step = 3 else: self.update_label.setText( self.update_label.text()[:-self._timer_step]) self._timer_step = 0 if self.is_waiting and self.devices_thread is not None: if not self.devices_thread.isRunning(): self.devices_thread.start() def _on_download_error(self, text): self._timer_step = -1 self.update_label.setStyleSheet('background-color: crimson;') self.update_label.setText(text) self._install_btn.setVisible(True) self._update_btn.setVisible(False) def _on_device(self): self.update_label.setText('Waiting for Device ...') self._timer_step = 3 self.is_waiting = True self._on_timer() def _on_install_btn(self): # urls are empty if not self.updated_frida_assets_url: return arch = self._adb.get_device_arch() request_url = '' if arch is not None and len(arch) > 1: arch = arch.join(arch.split()) if arch == 'arm64' or arch == 'arm64-v8a': request_url = self.updated_frida_assets_url['arm64'] elif arch == 'armeabi-v7a': request_url = self.updated_frida_assets_url['arm'] else: if arch in self.updated_frida_assets_url: request_url = self.updated_frida_assets_url[arch] try: if self._adb.available() and request_url.index( 'https://') == 0: self._install_btn.setVisible(False) self._update_btn.setVisible(False) qApp.processEvents() if self._update_thread is not None: if not self._update_thread.isRunning(): self._update_thread.frida_update_url = request_url self._update_thread.adb = self._adb self._update_thread.start() except ValueError: # something wrong in .git_cache folder print("request_url not set") def _update_statuslbl(self, text): self._timer.stop() self._timer_step = 0 self._timer.start() self.update_label.setText(text) def _frida_updated(self): # self._timer_step = 3 # self.is_waiting = True self._on_devices_finished() def _on_start_btn(self): if self._adb.available(): self._start_btn.setVisible(False) qApp.processEvents() if self._adb.start_frida(): # self.onDeviceUpdated.emit(self._device_id) self._on_devices_finished() else: self._start_btn.setVisible(True) def _on_restart_btn(self): if self._adb.available(): self._restart_btn.setVisible(False) qApp.processEvents() if self._adb.start_frida(restart=True): self._restart_btn.setVisible(True) # self.onDeviceUpdated.emit(self._device_id) self._on_devices_finished()
class ScriptsDialog(QDialog): """ Scripts """ def __init__(self, app_window): super(ScriptsDialog, self).__init__(app_window) self.script = None self._app_window = app_window self._script_manager = ScriptsManager() self._git = Git() self.setMinimumWidth(800) box = QVBoxLayout() self.table = ScriptsTable(self) self.table.onScriptSelected.connect(self._item_selected) self.table.setMinimumWidth(800) # create a centered dot icon _section_width = self.table.header().sectionSize(3) self._new_pixmap = QPixmap(max(_section_width, 40), 20) self._new_pixmap.fill(Qt.transparent) painter = QPainter(self._new_pixmap) rect = QRect((_section_width * 0.5) - 5, 0, 20, 20) painter.setBrush(QColor('#666')) painter.setPen(QColor('#666')) painter.drawEllipse(rect) self._dot_icon = QIcon(self._new_pixmap) box.addWidget(self.table) lbl = QLabel('OS Support - A: Android I: IOS W: Windows') box.addWidget(lbl) self.setLayout(box) self._init_list() def _init_list(self): for script_name in sorted(self._script_manager.get_scripts().keys()): script = self._script_manager.get_script(script_name) info = script['info'] if 'dwarf' in info: continue _name = QStandardItem() _name.setText(script_name) _name.setToolTip(info['name']) _author = QStandardItem() if 'author' in info: _author.setTextAlignment(Qt.AlignCenter) _author.setText(info['author']) _android = QStandardItem() if 'android' in info: _android.setIcon(self._dot_icon) _ios = QStandardItem() if 'ios' in info: _ios.setIcon(self._dot_icon) _windows = QStandardItem() if 'windows' in info: _windows.setIcon(self._dot_icon) _desc = QStandardItem() if 'description' in info: _desc.setText(info['description']) self.table.add_item( [_name, _author, _android, _ios, _windows, _desc]) def _item_selected(self, script_name): script_url = self._script_manager.get_script(script_name)['script'] script = self._git.get_script(script_url) self.script = script self.accept() @staticmethod def pick(app): """ helper """ dialog = ScriptsDialog(app) result = dialog.exec_() return result == QDialog.Accepted, dialog.script
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_debugger.lib import utils from dwarf_debugger.lib.git import Git from dwarf_debugger.lib.prefs import Prefs from dwarf_debugger.ui.app import AppWindow from PyQt5.QtCore import Qt from PyQt5.QtGui import QIcon from PyQt5.QtWidgets import QApplication import dwarf_debugger.resources # pylint: disable=unused-import QApplication.setDesktopSettingsAware(True) QApplication.setAttribute(Qt.AA_EnableHighDpiScaling) QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps) QApplication.setLayoutDirection(Qt.LeftToRight) QApplication.setOrganizationName("https://github.com/iGio90/Dwarf") QApplication.setApplicationName("Dwarf") QApplication.setApplicationDisplayName('Dwarf') qapp = QApplication([]) # 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_debugger.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['tag_name']: print('Updating local frida version to ' + remote_frida['tag_name']) try: res = utils.do_shell_command( 'pip3 install frida --upgrade --user') if 'Successfully installed frida-' + remote_frida[ '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)
def __init__(self): super(ScriptsManager, self).__init__() self._git = Git() self.scripts = {} self.update_scripts()
class ScriptsManager(QObject): """ ScriptManager signals: scriptsUpdated() """ scriptsUpdated = pyqtSignal(name='scriptsUpdated') def __init__(self): super(ScriptsManager, self).__init__() self._git = Git() self.scripts = {} self.update_scripts() def _check_version(self, required_dwarf): from dwarf_debugger.version import DWARF_VERSION required_dwarf = required_dwarf.split('.') dwarf_version = DWARF_VERSION.split('.') print(required_dwarf) if int(dwarf_version[0]) < int(required_dwarf[0]): return False elif (int(dwarf_version[0]) <= int(required_dwarf[0])) and (int( dwarf_version[1]) < int(required_dwarf[1])): return False elif (int(dwarf_version[1]) <= int(required_dwarf[1])) and (int( dwarf_version[2]) < int(required_dwarf[2])): return False return True def update_scripts(self): scripts = self._git.get_dwarf_scripts() if scripts is None: return scripts = scripts.replace(' ', '').replace('\t', '').split('\n') submodule_path = '[submodule"' url_path = 'url=' module_name = '' for line in scripts: if line.startswith(submodule_path): module_name = line.replace(submodule_path, "") module_name = module_name[:-2] elif line.startswith(url_path): url = line.replace(url_path, "") if url.endswith('.git'): url = url[:-4] url = url.replace('https://github.com', 'https://raw.githubusercontent.com') info_url = url + '/master/dwarf.json' script_url = url + '/master/script.js' info = self._git.get_script_info(info_url) if info is None: continue if 'dwarf' in info: if not self._check_version(info['dwarf']): continue self.scripts[module_name] = { 'info': info, 'script': script_url } self.scriptsUpdated.emit() def get_script(self, script_name): return self.scripts[script_name] def get_scripts(self): return self.scripts