Ejemplo n.º 1
0
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()
Ejemplo n.º 2
0
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)