""" Dwarf - Copyright (C) 2018-2020 Giovanni Rocca (iGio90) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/> """ import os import sys import argparse import shutil DWARF_VERSION = '1.0.0' __version__ = DWARF_VERSION def pip_install_package(package_name): try: from dwarf_debugger.lib.utils import do_shell_command res = do_shell_command('pip3 install ' + package_name + ' --upgrade --user') if 'Successfully installed' in res: return True elif 'Requirement already up-to-date' in res: return True else: return False except Exception: # pylint: disable=broad-except return False def _check_package_version(package_name, min_version): try: installed_version = None if package_name == 'frida': import frida installed_version = frida.__version__ elif package_name == 'capstone': import capstone installed_version = capstone.__version__ elif package_name == 'requests': import requests installed_version = requests.__version__ elif package_name == 'pyqt5': from PyQt5 import QtCore installed_version = QtCore.PYQT_VERSION_STR elif package_name == 'pyperclip': import pyperclip installed_version = pyperclip.__version__ if installed_version is not None: installed_version = installed_version.split('.') _min_version = min_version.split('.') needs_update = False if int(installed_version[0]) < int(_min_version[0]): needs_update = True elif (int(installed_version[0]) <= int(_min_version[0])) and ( int(installed_version[1]) < int(_min_version[1])): needs_update = True elif (int(installed_version[1]) <= int(_min_version[1])) and ( int(installed_version[2]) < int(_min_version[2])): needs_update = True if needs_update: print('updating ' + package_name + '... to ' + min_version) if pip_install_package(package_name + '>=' + min_version): print('*** success ***') except Exception: # pylint: disable=broad-except print('installing ' + package_name + '...') if pip_install_package(package_name + '>=' + min_version): print('*** success ***') def _check_dependencies(): _check_package_version('frida', '12.6.23') _check_package_version('requests', '2.18.4') _check_package_version('pyqt5', '5.13.2') _check_package_version('pyperclip', '1.7.0') _check_package_version('capstone', '4.0.0') # problem with 4.0.1 as installed 4.0.1 returns 4.0.0 def process_args(): """ process commandline params """ parser = argparse.ArgumentParser() parser.add_argument( "-t", "--target", type=str, help="SessionType - android, ios, local, remote - default: local") parser.add_argument( "-s", "--script", type=str, help="Path to an additional script to load with dwarf and frida js api" ) parser.add_argument("-dev", "--device", help="DeviceSerial adb devices") parser.add_argument( "-bs", "--break-start", action='store_true', help="break at start") parser.add_argument( "-ds", "--debug-script", action='store_true', help="debug outputs from frida script") parser.add_argument('any', nargs='?', default='', help='path/pid/package') parser.add_argument('args', nargs='*', default=[''], help='arguments') args = parser.parse_args() return args def _on_restart(): print('restarting dwarf...') os.execl(sys.executable, os.path.abspath(__file__), *sys.argv) 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 main(): run_dwarf()