Ejemplo n.º 1
0
def runInstaller(options, install_config, working_directory):
    try:
        sys.path.insert(0, options.installer_path)
        from installer import Installer
    except:
        raise ImportError('Installer path incorrect!')

    # Run the installer
    installer = Installer(working_directory=working_directory, rpm_path=options.rpm_path,
                          log_path=options.log_path)
    installer.configure(install_config)
    installer.execute()
Ejemplo n.º 2
0
    def __init__(self, options):
        install_config=None
        self.media_mount_path = None
        photon_media = None
        ks_path = options.install_config_file
        # Path to RPMS repository: local media or remote URL
        # If --repo-path= provided - use it,
        # if not provided - use kernel repo= parameter,
        # if not provided - use /RPMS path from photon_media,
        # exit otherwise.
        repo_path = options.repo_path

        with open('/proc/cmdline', 'r') as f:
            kernel_params = shlex.split(f.read().replace('\n', ''))

        for arg in kernel_params:
            if arg.startswith("ks="):
                if not ks_path:
                    ks_path = arg[len("ks="):]
            elif arg.startswith("repo="):
                if not repo_path:
                    repo_path = arg[len("repo="):]
            elif arg.startswith("photon.media="):
                photon_media = arg[len("photon.media="):]

        if photon_media:
            self.mount_media(photon_media)

        if not repo_path:
            if self.media_mount_path:
                repo_path = self.media_mount_path + "/RPMS"
            else:
                print("Please specify RPM repo path.")
                return

        if ks_path:
            install_config=self._load_ks_config(ks_path)

        if options.ui_config_file:
            ui_config = (JsonWrapper(options.ui_config_file)).read()
        else:
            ui_config={}
        ui_config['options_file'] = options.options_file

        # Run installer
        installer = Installer(rpm_path=repo_path, log_path="/var/log")

        installer.configure(install_config, ui_config)
        installer.execute()
Ejemplo n.º 3
0
    def __init__(self, options):
        install_config = None
        self.cd_mount_path = None
        cd_search = None
        ks_path = options.install_config_file
        repo_path = options.repo_path

        with open('/proc/cmdline', 'r') as f:
            kernel_params = shlex.split(f.read().replace('\n', ''))

        for arg in kernel_params:
            if arg.startswith("ks="):
                if not ks_path:
                    ks_path = arg[len("ks="):]
            elif arg.startswith("repo="):
                if not repo_path:
                    repo_path = arg[len("repo="):]
            elif arg.startswith("photon.media="):
                cd_search = arg[len("photon.media="):]

        if not repo_path:
            print("Please specify RPM repo path.")
            return

        if cd_search:
            self.mount_cd(cd_search)

        if ks_path:
            install_config = self._load_ks_config(ks_path)

        if options.ui_config_file:
            ui_config = (JsonWrapper(options.ui_config_file)).read()
        else:
            ui_config = {}
        ui_config['options_file'] = options.options_file

        # Run installer
        installer = Installer(rpm_path=repo_path, log_path="/var/log")

        installer.configure(install_config, ui_config)
        installer.execute()
Ejemplo n.º 4
0
class Device(object):
    # ==================================================================================================================
    # FRAMEWORK ATTRIBUTES
    # ==================================================================================================================
    # Connection Parameters
    _ip = ''
    _port = ''
    _username = ''
    _password = ''
    _tools_local = None
    _portforward = None
    _frida_server = None
    _debug_server = None
    # App specific
    _is_iOS8 = False
    _is_iOS9 = False
    _is_iOS10 = False
    _is_iOS7_or_less = False
    _applist = None
    _device_ready = False
    # On-Device Paths
    TEMP_FOLDER = Constants.DEVICE_PATH_TEMP_FOLDER
    DEVICE_TOOLS = Constants.DEVICE_TOOLS
    # On-Server Paths
    APP_DB_PATH = "/tmp/applicationState.db"  #for iOS 10
    # Reference to External Objects
    conn = None
    app = None
    installer = None
    local_op = None
    remote_op = None
    printer = None

    # ==================================================================================================================
    # INIT
    # ==================================================================================================================
    def __init__(self, ip, port, username, password, pub_key_auth, tools):
        # Setup params
        self._ip = ip
        self._port = port
        self._username = username
        self._password = password
        self._pub_key_auth = bool(pub_key_auth)
        self._tools_local = tools
        # Init related objects
        self.app = App(self)
        self.installer = Installer(self)
        self.local_op = LocalOperations()
        self.remote_op = RemoteOperations(self)
        self.printer = Printer()

    # ==================================================================================================================
    # UTILS - USB
    # ==================================================================================================================
    def _portforward_usb_start(self):
        """Setup USB port forwarding with TCPRelay."""
        # Check if the user chose a valid port
        if str(self._port) == '22':
            raise Exception(
                'Chosen port must be different from 22 in order to use USB over SSH'
            )
        # Setup the forwarding
        self.printer.verbose('Setting up USB port forwarding on port %s' %
                             self._port)
        cmd = '{app} -t 22:{port}'.format(app=self._tools_local['TCPRELAY'],
                                          port=self._port)
        self._portforward = self.local_op.command_subproc_start(cmd)

    def _portforward_usb_stop(self):
        """Stop USB port forwarding."""
        self.printer.verbose('Stopping USB port forwarding')
        self.local_op.command_subproc_stop(self._portforward)

    # ==================================================================================================================
    # UTILS - SSH
    # ==================================================================================================================
    def _connect_ssh(self):
        """Open a new connection using Paramiko."""
        try:
            self.printer.verbose('Setting up SSH connection...')
            self.conn = paramiko.SSHClient()
            self.conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            self.conn.connect(self._ip,
                              port=self._port,
                              username=self._username,
                              password=self._password,
                              allow_agent=self._pub_key_auth,
                              look_for_keys=self._pub_key_auth)

        except paramiko.AuthenticationException as e:
            raise Exception(
                'Authentication failed when connecting to %s. %s: %s' %
                (self._ip, type(e).__name__, e.message))
        except paramiko.SSHException as e:
            raise Exception(
                'Connection dropped. Please check your connection with the device, '
                'and reload the module. %s: %s' %
                (type(e).__name__, e.message))
        except Exception as e:
            raise Exception('Could not open a connection to %s. %s - %s' %
                            (self._ip, type(e).__name__, e.message))

    def _disconnect_ssh(self):
        """Close the connection, if available."""
        if self.conn:
            self.conn.close()

    def _exec_command_ssh(self, cmd, internal):
        """Execute a shell command on the device, then parse/print output."""
        def hotfix_67():
            # TODO: replace with a more long-term fix
            import time
            timeout = 30
            endtime = time.time() + timeout
            while not stdout.channel.eof_received:
                time.sleep(1)
                if time.time() > endtime:
                    stdout.channel.close()
                    break

        # Paramiko Exec Command
        stdin, stdout, stderr = self.conn.exec_command(cmd)
        hotfix_67()

        # Parse STDOUT/ERR
        out = stdout.readlines()
        err = stderr.readlines()
        if internal:
            # For processing, don't display output
            if err:
                # Show error and abort run
                err_str = ''.join(err)
                raise Exception(err_str)
        else:
            # Display output
            if out: map(lambda x: print('\t%s' % x, end=''), out)
            if err:
                map(
                    lambda x: print('\t%s%s%s' % (Colors.R, x, Colors.N),
                                    end=''), err)
        return out, err

    # ==================================================================================================================
    # FRIDA PORT FORWARDING
    # ==================================================================================================================
    def _portforward_frida_start(self):
        """Setup local port forward to enable communication with the Frida server running on the device"""
        localhost = '127.0.0.1'
        self._frida_server = SSHTunnelForwarder(
            (self._ip, int(self._port)),
            ssh_username=self._username,
            ssh_password=self._password,
            local_bind_address=(localhost, Constants.FRIDA_PORT),
            remote_bind_address=(localhost, Constants.FRIDA_PORT),
        )
        self._frida_server.start()

    def _portforward_frida_stop(self):
        """Stop local port forwarding"""
        if self._frida_server:
            self._frida_server.stop()

    # ==================================================================================================================
    # LLDB PORT FORWARDING
    # ==================================================================================================================
    def _portforward_debug_start(self):
        """Setup local port forward to enable communication with the debug server running on the device"""
        localhost = '127.0.0.1'
        self._debug_server = SSHTunnelForwarder(
            (self._ip, int(self._port)),
            ssh_username=self._username,
            ssh_password=self._password,
            local_bind_address=(localhost, Constants.DEBUG_PORT),
            remote_bind_address=(localhost, Constants.DEBUG_PORT),
        )
        self._debug_server.start()

    def _portforward_debug_stop(self):
        """Stop local port forwarding"""
        if self._debug_server:
            self._debug_server.stop()

    # ==================================================================================================================
    # UTILS - OS
    # ==================================================================================================================
    def _detect_ios_version(self):
        """Detect the iOS version running on the device."""
        if self.remote_op.file_exist(Constants.DEVICE_PATH_APPLIST_iOS8):
            self._is_iOS8 = True
        elif self.remote_op.file_exist(Constants.DEVICE_PATH_APPLIST_iOS9):
            self._is_iOS9 = True
        elif self.remote_op.file_exist(Constants.DEVICE_PATH_DB_iOS10):
            self._is_iOS10 = True
        else:
            self._is_iOS7_or_less = True

    def _list_apps(self):
        """List all the 3rd party apps installed on the device."""
        def list_iOS_7():
            raise Exception('Support for iOS < 8 not yet implemented')

        def list_iOS_8(applist):
            # Refresh UICache in case an app was installed after the last reboot
            self.printer.verbose("Refreshing list of installed apps...")
            self.remote_op.command_blocking(
                '/bin/su mobile -c /usr/bin/uicache', internal=True)
            # Parse plist file
            pl = self.remote_op.parse_plist(applist)
            self._applist = pl["User"]

        def list_iOS_10(applist):
            self.printer.verbose(
                "Respring if an application is not listed to rebuild the application db"
            )
            if self._applist == None:
                self._applist = dict()
            self.printer.verbose("fetching application state db to %s" %
                                 (self.APP_DB_PATH))
            #fetch the current database to parse
            self.pull(applist, self.APP_DB_PATH)
            #open the loaded db
            conn = sqlite3.connect(self.APP_DB_PATH)
            c = conn.cursor()
            c.execute(
                "SELECT id,application_identifier FROM application_identifier_tab"
            )
            for row in c:
                self._applist[row[1]] = row[0]  #key = application name
            c.close()

        # Dispatch
        self._detect_ios_version()
        if self._is_iOS8: list_iOS_8(Constants.DEVICE_PATH_APPLIST_iOS8)
        elif self._is_iOS9: list_iOS_8(Constants.DEVICE_PATH_APPLIST_iOS9)
        elif self._is_iOS10: list_iOS_10(Constants.DEVICE_PATH_DB_iOS10)
        else: list_iOS_7()

    def select_target_app(self):
        """List all 3rd party apps installed and let the user choose which one to target"""
        self._list_apps()
        self.printer.notify('Apps found:')
        app_name = choose_from_list(self._applist.keys())
        return app_name

    # ==================================================================================================================
    # EXPOSED COMMANDS
    # ==================================================================================================================
    def is_usb(self):
        """Returns true if using SSH over USB."""
        return self._ip == '127.0.0.1' or self._ip == 'localhost'

    def connect(self):
        """Connect to the device."""
        if self.is_usb():
            # Using SSH over USB, setup port forwarding first
            self._portforward_usb_start()
        # Connect
        self._connect_ssh()

    def disconnect(self):
        """Disconnect from the device."""
        if self._portforward:
            # Using SSH over USB, stop port forwarding
            self._portforward_usb_stop()
        self._disconnect_ssh()

    def setup(self, install_tools=True):
        """Create temp folder, and check if all tools are available"""
        # Setup temp folder
        self.printer.verbose("Creating temp folder: %s" % self.TEMP_FOLDER)
        self.remote_op.dir_create(self.TEMP_FOLDER)
        # Install tools
        if install_tools:
            if not self._device_ready:
                self.printer.info("Configuring device...")
                self._device_ready = self.installer.configure()
        else:
            self._device_ready = True

    def cleanup(self):
        """Remove temp folder from device."""
        self.printer.verbose("Cleaning up remote temp folder: %s" %
                             self.TEMP_FOLDER)
        self.remote_op.dir_delete(self.TEMP_FOLDER)

    def shell(self):
        """Spawn a system shell on the device."""
        cmd = 'sshpass -p "{password}" ssh {hostverification} -p {port} {username}@{ip}'.format(
            password=self._password,
            hostverification=Constants.DISABLE_HOST_VERIFICATION,
            port=self._port,
            username=self._username,
            ip=self._ip)
        self.local_op.command_interactive(cmd)

    def pull(self, src, dst):
        """Pull a file from the device."""
        self.printer.info("Pulling: %s -> %s" % (src, dst))
        self.remote_op.download(src, dst)

    def push(self, src, dst):
        """Push a file on the device."""
        self.printer.info("Pushing: %s -> %s" % (src, dst))
        self.remote_op.upload(src, dst)
Ejemplo n.º 5
0
class Device(object):
    # ==================================================================================================================
    # FRAMEWORK ATTRIBUTES
    # ==================================================================================================================
    # Connection Parameters
    _ip, _port, _agent_port, _username, _password = '', '', '', '', ''
    _tools_local = None
    # Port Forwarding
    _frida_server = None
    _port_forward_ssh, _port_forward_agent = None, None
    # App specific
    _applist, _device_ready, _ios_version = None, None, None
    # Reference to External Objects
    ssh, agent = None, None
    app, installer = None, None
    local_op, remote_op = None, None
    printer = None
    # On-Device Paths
    TEMP_FOLDER = Constants.DEVICE_PATH_TEMP_FOLDER
    DEVICE_TOOLS = Constants.DEVICE_TOOLS

    # ==================================================================================================================
    # INIT
    # ==================================================================================================================
    def __init__(self, ip, port, agent_port, username, password, pub_key_auth,
                 tools):
        # Setup params
        self._ip = ip
        self._port = port
        self._agent_port = agent_port
        self._username = username
        self._password = password
        self._pub_key_auth = bool(pub_key_auth)
        self._tools_local = tools
        # Init related objects
        self.app = App(self)
        self.installer = Installer(self)
        self.local_op = LocalOperations()
        self.remote_op = RemoteOperations(self)
        self.printer = Printer()
        self.agent = NeedleAgent(self)

    # ==================================================================================================================
    # UTILS - USB
    # ==================================================================================================================
    def _portforward_usb_start(self):
        """Setup USB port forwarding with TCPRelay."""
        # Check if the user chose a valid port
        if str(self._port) == '22':
            raise Exception(
                'Chosen port must be different from 22 in order to use USB over SSH'
            )
        # Setup the forwarding
        self.printer.debug('Setting up USB port forwarding on port %s' %
                           self._port)
        cmd = '{app} -t 22:{port}'.format(app=self._tools_local['TCPRELAY'],
                                          port=self._port)
        self._port_forward_ssh = self.local_op.command_subproc_start(cmd)

    def _portforward_usb_stop(self):
        """Stop USB port forwarding."""
        self.printer.debug('Stopping USB port forwarding')
        self.local_op.command_subproc_stop(self._port_forward_ssh)

    # ==================================================================================================================
    # UTILS - SSH
    # ==================================================================================================================
    def _connect_ssh(self):
        """Open a new SSH connection using Paramiko."""
        try:
            self.printer.verbose("[SSH] Connecting ({}:{})...".format(
                self._ip, self._port))
            self.ssh = paramiko.SSHClient()
            self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            self.ssh.connect(self._ip,
                             port=self._port,
                             username=self._username,
                             password=self._password,
                             allow_agent=self._pub_key_auth,
                             look_for_keys=self._pub_key_auth)
            self.printer.notify("[SSH] Connected ({}:{})".format(
                self._ip, self._port))
        except paramiko.AuthenticationException as e:
            raise Exception(
                'Authentication failed when connecting to %s. %s: %s' %
                (self._ip, type(e).__name__, e.message))
        except paramiko.SSHException as e:
            raise Exception(
                'Connection dropped. Please check your connection with the device, '
                'and reload the module. %s: %s' %
                (type(e).__name__, e.message))
        except Exception as e:
            raise Exception('Could not open a connection to %s. %s - %s' %
                            (self._ip, type(e).__name__, e.message))

    def _disconnect_ssh(self):
        """Close the SSH connection, if available."""
        self.printer.verbose("[SSH] Disconnecting...")
        if self.ssh:
            self.ssh.close()

    def _exec_command_ssh(self, cmd, internal):
        """Execute a shell command on the device, then parse/print output."""
        def hotfix_67():
            # TODO: replace with a more long-term fix
            import time
            timeout = 30
            endtime = time.time() + timeout
            while not stdout.channel.eof_received:
                time.sleep(1)
                if time.time() > endtime:
                    stdout.channel.close()
                    break

        # Paramiko Exec Command
        stdin, stdout, stderr = self.ssh.exec_command(cmd)
        hotfix_67()
        # Parse STDOUT/ERR
        out = stdout.readlines()
        err = stderr.readlines()
        if internal:
            # For processing, don't display output
            if err:
                # Show error and abort run
                err_str = ''.join(err)
                raise Exception(err_str)
        else:
            # Display output
            if out: map(lambda x: print('\t%s' % x, end=''), out)
            if err:
                map(
                    lambda x: print('\t%s%s%s' % (Colors.R, x, Colors.N),
                                    end=''), err)
        return out, err

    # ==================================================================================================================
    # UTILS - AGENT
    # ==================================================================================================================
    def _portforward_agent_start(self):
        """Setup local port forward to enable communication with the Needle server running on the device."""
        self.printer.debug('{} Setting up port forwarding on port {}'.format(
            Constants.AGENT_TAG, self._agent_port))
        localhost = '127.0.0.1'
        self._port_forward_agent = SSHTunnelForwarder(
            (self._ip, int(self._port)),
            ssh_username=self._username,
            ssh_password=self._password,
            local_bind_address=(localhost, self._agent_port),
            remote_bind_address=(localhost, self._agent_port),
        )
        self._port_forward_agent.start()

    def _portforward_agent_stop(self):
        """Stop local port forwarding for Needle server."""
        self.printer.debug('{} Stopping port forwarding'.format(
            Constants.AGENT_TAG))
        if self._port_forward_agent:
            self._port_forward_agent.stop()

    def _connect_agent(self):
        self.agent.connect()

    def _disconnect_agent(self):
        self.agent.disconnect()

    # ==================================================================================================================
    # FRIDA PORT FORWARDING
    # ==================================================================================================================
    def _portforward_frida_start(self):
        """Setup local port forward to enable communication with the Frida server running on the device."""
        self.printer.debug('{} Setting up port forwarding on port {}'.format(
            "[FRIDA]", Constants.FRIDA_PORT))
        localhost = '127.0.0.1'
        self._frida_server = SSHTunnelForwarder(
            (self._ip, int(self._port)),
            ssh_username=self._username,
            ssh_password=self._password,
            local_bind_address=(localhost, Constants.FRIDA_PORT),
            remote_bind_address=(localhost, Constants.FRIDA_PORT),
        )
        self._frida_server.start()

    def _portforward_frida_stop(self):
        """Stop local port forwarding for Frida server."""
        self.printer.debug('{} Stopping port forwarding'.format("FRIDA"))
        if self._frida_server:
            self._frida_server.stop()

    # ==================================================================================================================
    # UTILS - OS
    # ==================================================================================================================
    def _list_apps(self):
        """Retrieve all the 3rd party apps installed on the device."""
        agent_list = self.agent.exec_command_agent(
            Constants.AGENT_CMD_LIST_APPS)
        self._applist = Utils.string_to_json(agent_list)

    def select_target_app(self):
        """List all 3rd party apps installed and let the user choose which one to target."""
        # List all 3rd party apps
        self._list_apps()
        # Show menu to user
        self.printer.notify('Apps found:')
        app_name = choose_from_list(self._applist.keys())
        return app_name

    # ==================================================================================================================
    # EXPOSED COMMANDS
    # ==================================================================================================================
    def is_usb(self):
        """Returns true if using SSH over USB."""
        return self._ip == '127.0.0.1' or self._ip == 'localhost'

    def connect(self):
        """Connect to the device (both SSH and AGENT)."""
        # Using USB, setup port forwarding first
        if self.is_usb():
            self._portforward_usb_start()
            self._portforward_agent_start()
        # Setup channels
        self._connect_agent()
        self._connect_ssh()

    def disconnect(self):
        """Disconnect from the device (both SSH and AGENT)."""
        # Close channels
        self._disconnect_ssh()
        self._disconnect_agent()
        # Using USB, stop port forwarding first
        if self._port_forward_ssh:
            self._portforward_usb_stop()
            self._portforward_agent_stop()

    def setup(self, install_tools=True):
        """Create temp folder, and check if all tools are available"""
        # Setup temp folder
        self.printer.debug("Creating temp folder: %s" % self.TEMP_FOLDER)
        self.remote_op.dir_create(self.TEMP_FOLDER)
        # Detect OS version
        self._ios_version = self.agent.exec_command_agent(
            Constants.AGENT_CMD_OS_VERSION)
        # Install tools
        if install_tools:
            if not self._device_ready:
                self.printer.info("Configuring device...")
                self._device_ready = self.installer.configure()
        else:
            self._device_ready = True

    def cleanup(self):
        """Remove temp folder from device."""
        self.printer.debug("Cleaning up remote temp folder: %s" %
                           self.TEMP_FOLDER)
        self.remote_op.dir_delete(self.TEMP_FOLDER)

    def shell(self):
        """Spawn a system shell on the device."""
        cmd = 'sshpass -p "{password}" ssh {hostverification} -p {port} {username}@{ip}'.format(
            password=self._password,
            hostverification=Constants.DISABLE_HOST_VERIFICATION,
            port=self._port,
            username=self._username,
            ip=self._ip)
        self.local_op.command_interactive(cmd)

    def pull(self, src, dst):
        """Pull a file from the device."""
        self.printer.info("Pulling: %s -> %s" % (src, dst))
        self.remote_op.download(src, dst)

    def push(self, src, dst):
        """Push a file on the device."""
        self.printer.info("Pushing: %s -> %s" % (src, dst))
        self.remote_op.upload(src, dst)
Ejemplo n.º 6
0
    def __init__(self, options):
        install_config=None
        self.media_mount_path = None
        photon_media = None
        ks_path = options.install_config_file
        # Path to RPMS repository: local media or remote URL
        # If --repo-path= provided - use it,
        # if not provided - use kernel repo= parameter,
        # if not provided - use /RPMS path from photon_media,
        # exit otherwise.
        repo_path = options.repo_path
        self.insecure_installation = None

        with open('/proc/cmdline', 'r') as f:
            kernel_params = shlex.split(f.read().replace('\n', ''))

        for arg in kernel_params:
            if arg.startswith("ks="):
                if not ks_path:
                    ks_path = arg[len("ks="):]
            elif arg.startswith("repo="):
                if not repo_path:
                    repo_path = arg[len("repo="):]
            elif arg.startswith("photon.media="):
                photon_media = arg[len("photon.media="):]
            elif arg.startswith("insecure_installation="):
                self.insecure_installation = bool(int(arg[len("insecure_installation="):]))

        if photon_media:
            self.mount_media(photon_media)

        if not repo_path:
            if self.media_mount_path:
                repo_path = self.media_mount_path + "/RPMS"
            else:
                print("Please specify RPM repo path.")
                return

        if ks_path:
            install_config = self._load_ks_config(ks_path)


        # insecure_installation flag added through commandline overrides that of ks_config
        if self.insecure_installation:
            if not install_config:
                install_config = {}
            install_config['insecure_installation'] = self.insecure_installation

        if not install_config:
            install_config = {}
        install_config['photon_release_version'] = options.photon_release_version

        if options.ui_config_file:
            ui_config = (JsonWrapper(options.ui_config_file)).read()
        else:
            ui_config={}
        ui_config['options_file'] = options.options_file

        #initializing eula file path
        ui_config['eula_file_path'] = options.eula_file_path

        #initializing license display text
        ui_config['license_display_title'] = options.license_display_title


        # Run installer
        installer = Installer(rpm_path=repo_path, log_path="/var/log")

        installer.configure(install_config, ui_config)
        installer.execute()
Ejemplo n.º 7
0
    parser.add_argument("-e",
                        "--eula-file",
                        dest="eula_file_path",
                        default=None)
    parser.add_argument("-t",
                        "--license-title",
                        dest="license_display_title",
                        default=None)

    options = parser.parse_args()

    if options.image_type == 'iso':
        from isoInstaller import IsoInstaller
        IsoInstaller(options)

    else:
        from installer import Installer
        import json
        install_config = None
        if options.install_config_file:
            with open(options.install_config_file) as f:
                install_config = json.load(f)
        else:
            raise Exception('install config file not provided')

        installer = Installer(working_directory=working_directory,
                              rpm_path=options.rpm_path,
                              log_path=options.log_path)
        installer.configure(install_config)
        installer.execute()
Ejemplo n.º 8
0
class Device(object):
    # ==================================================================================================================
    # FRAMEWORK ATTRIBUTES
    # ==================================================================================================================
    # Connection Parameters
    _ip = Constants.GLOBAL_IP
    _port = Constants.GLOBAL_PORT
    _username = Constants.GLOBAL_USERNAME
    _password = Constants.GLOBAL_PASSWORD
    _pub_key_auth = bool(Constants.GLOBAL_PUB_KEY_AUTH)
    _tools_local = Constants.PATH_TOOLS_LOCAL
    _portforward = None
    _frida_server = None
    _debug_server = None
    # App specific
    _is_iOS8 = False
    _is_iOS9 = False
    _is_iOS7_or_less = False
    _applist = None
    _device_not_ready = bool(Constants.GLOBAL_SETUP_DEVICE)
    # On-Device Paths
    TEMP_FOLDER = Constants.DEVICE_PATH_TEMP_FOLDER
    DEVICE_TOOLS = Constants.DEVICE_TOOLS
    # Reference to External Objects
    conn = None
    app = None
    installer = None
    local_op = None
    remote_op = None
    printer = None

    # ==================================================================================================================
    # INIT
    # ==================================================================================================================
    def __init__(self):
        # Init related objects
        self.app = App(self)
        self.installer = Installer(self)
        self.local_op = LocalOperations()
        self.remote_op = RemoteOperations(self)
        self.printer = Printer()
        self.connect()
        self.setup()

    # ==================================================================================================================
    # UTILS - USB
    # ==================================================================================================================
    def _portforward_usb_start(self):
        """Setup USB port forwarding with TCPRelay."""
        # Check if the user chose a valid port
        if str(self._port) == '22':
            raise Exception(
                'Chosen port must be different from 22 in order to use USB over SSH'
            )
        # Setup the forwarding
        self.printer.verbose('Setting up USB port forwarding on port %s' %
                             self._port)
        cmd = '{app} -t 22:{port}'.format(app=self._tools_local['TCPRELAY'],
                                          port=self._port)
        self._portforward = self.local_op.command_subproc_start(cmd)

    def _portforward_usb_stop(self):
        """Stop USB port forwarding."""
        self.printer.verbose('Stopping USB port forwarding')
        self.local_op.command_subproc_stop(self._portforward)

    # ==================================================================================================================
    # UTILS - SSH
    # ==================================================================================================================
    def _connect_ssh(self):
        """Open a new connection using Paramiko."""
        try:
            path = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
            key = paramiko.RSAKey.from_private_key_file(path)
            self.printer.verbose('Setting up SSH connection...')
            self.conn = paramiko.SSHClient()
            self.conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            self.conn.connect(self._ip,
                              port=self._port,
                              username=self._username,
                              password=self._password,
                              allow_agent=self._pub_key_auth,
                              pkey=key)

        except paramiko.AuthenticationException as e:
            raise Exception(
                'Authentication failed when connecting to %s. %s: %s' %
                (self._ip, type(e).__name__, e.message))
        except paramiko.SSHException as e:
            raise Exception(
                'Connection dropped. Please check your connection with the device, '
                'and reload the module. %s: %s' %
                (type(e).__name__, e.message))
        except Exception as e:
            raise Exception('Could not open a connection to %s. %s - %s' %
                            (self._ip, type(e).__name__, e.message))

    def _disconnect_ssh(self):
        """Close the connection, if available."""
        if self.conn:
            self.conn.close()

    def _exec_command_ssh(self, cmd, internal):
        """Execute a shell command on the device, then parse/print output."""
        # Paramiko Exec Command
        stdin, stdout, stderr = self.conn.exec_command(cmd)
        # Parse STDOUT/ERR
        out = stdout.readlines()
        err = stderr.readlines()
        if internal:
            # For processing, don't display output
            if err:
                # Show error and abort run
                err_str = ''.join(err)
                raise Exception(err_str)
        else:
            # Display output
            if out: map(lambda x: print('\t%s' % x, end=''), out)
            if err:
                map(
                    lambda x: print('\t%s%s%s' % (Colors.R, x, Colors.N),
                                    end=''), err)
        return out, err

    # ==================================================================================================================
    # FRIDA PORT FORWARDING
    # ==================================================================================================================
    def _portforward_frida_start(self):
        """Setup local port forward to enable communication with the Frida server running on the device"""
        localhost = '127.0.0.1'
        self._frida_server = SSHTunnelForwarder(
            (self._ip, int(self._port)),
            ssh_username=self._username,
            ssh_password=self._password,
            local_bind_address=(localhost, Constants.FRIDA_PORT),
            remote_bind_address=(localhost, Constants.FRIDA_PORT),
        )
        self._frida_server.start()

    def _portforward_frida_stop(self):
        """Stop local port forwarding"""
        if self._frida_server:
            self._frida_server.stop()

    # ==================================================================================================================
    # LLDB PORT FORWARDING
    # ==================================================================================================================
    def _portforward_debug_start(self):
        """Setup local port forward to enable communication with the debug server running on the device"""
        localhost = '127.0.0.1'
        self._debug_server = SSHTunnelForwarder(
            (self._ip, int(self._port)),
            ssh_username=self._username,
            ssh_password=self._password,
            local_bind_address=(localhost, Constants.DEBUG_PORT),
            remote_bind_address=(localhost, Constants.DEBUG_PORT),
        )
        self._debug_server.start()

    def _portforward_debug_stop(self):
        """Stop local port forwarding"""
        if self._debug_server:
            self._debug_server.stop()

    # ==================================================================================================================
    # UTILS - OS
    # ==================================================================================================================
    def _detect_ios_version(self):
        """Detect the iOS version running on the device."""
        if self.remote_op.file_exist(Constants.DEVICE_PATH_APPLIST_iOS8):
            self._is_iOS8 = True
        elif self.remote_op.file_exist(Constants.DEVICE_PATH_APPLIST_iOS9):
            self._is_iOS9 = True
        else:
            self._is_iOS7_or_less = True

    def _list_apps(self):
        """List all the 3rd party apps installed on the device."""
        def list_iOS_7():
            raise Exception('Support for iOS < 8 not yet implemented')

        def list_iOS_89(applist):
            # Refresh UICache in case an app was installed after the last reboot
            self.printer.verbose("Refreshing list of installed apps...")
            self.remote_op.command_blocking(
                '/bin/su mobile -c /usr/bin/uicache', internal=True)
            # Parse plist file
            pl = self.remote_op.parse_plist(applist)
            self._applist = pl["User"]

        # Dispatch
        self._detect_ios_version()
        if self._is_iOS8: list_iOS_89(Constants.DEVICE_PATH_APPLIST_iOS8)
        elif self._is_iOS9: list_iOS_89(Constants.DEVICE_PATH_APPLIST_iOS9)
        else: list_iOS_7()

    # ==================================================================================================================
    # EXPOSED COMMANDS
    # ==================================================================================================================
    def is_usb(self):
        """Returns true if using SSH over USB."""
        return self._ip == '127.0.0.1' or self._ip == 'localhost'

    def connect(self):
        """Connect to the device."""
        if self.is_usb():
            # Using SSH over USB, setup port forwarding first
            self._portforward_usb_start()
        # Connect
        self._connect_ssh()

    def disconnect(self):
        """Disconnect from the device."""
        if self._portforward:
            # Using SSH over USB, stop port forwarding
            self._portforward_usb_stop()
        self._disconnect_ssh()

    def setup(self):
        """Create temp folder, and check if all tools are available"""
        # Setup temp folder
        self.printer.verbose("Creating temp folder: %s" % self.TEMP_FOLDER)
        self.remote_op.dir_create(self.TEMP_FOLDER)
        # Install tools
        if self._device_not_ready:
            self.printer.info("Configuring device...")
            self._device_not_ready = self.installer.configure()

    def cleanup(self):
        """Remove temp folder from device."""
        self.printer.verbose("Cleaning up temp folder: %s" % self.TEMP_FOLDER)
        self.remote_op.dir_delete(self.TEMP_FOLDER)

    def shell(self):
        """Spawn a system shell on the device."""
        cmd = 'sshpass -p "{password}" ssh {hostverification} -p {port} {username}@{ip}'.format(
            password=self._password,
            hostverification=Constants.DISABLE_HOST_VERIFICATION,
            port=self._port,
            username=self._username,
            ip=self._ip)
        self.local_op.command_interactive(cmd)

    def pull(self, src, dst):
        """Pull a file from the device."""
        self.printer.info("Pulling: %s -> %s" % (src, dst))
        self.remote_op.download(src, dst)

    def push(self, src, dst):
        """Push a file on the device."""
        self.printer.info("Pushing: %s -> %s" % (src, dst))
        self.remote_op.upload(src, dst)

    def sync_files(self, src, dst):
        """sync files with device."""
        device_ip = self.remote_op.get_ip()
        device_ip = str(device_ip[0].strip())
        self.printer.verbose("The Device IP address is: %s" % device_ip)
        remote_dir = self._username + "@" + device_ip + ":" + src
        self.printer.verbose("Start to sync data from %s >> %s" %
                             (remote_dir, dst))
        subprocess.check_call(["rsync", "-avz", "--delete", remote_dir, dst])

    def install_ipa(self, src):
        """Install app with ipa file."""
        self.printer.verbose("Start to install %s to device" % src)
        dst = self.remote_op.build_temp_path_for_file("app.ipa")
        # Upload binary to device
        self.printer.verbose("Uploading binary: %s" % src)
        self.remote_op.upload(src, dst)
        # Install
        self.printer.verbose("Installing binary...")
        cmd = "{bin} {app}".format(bin=self.DEVICE_TOOLS['IPAINSTALLER'],
                                   app=dst)
        self.remote_op.command_interactive_tty(cmd)

    def uninstall_app(self, identifier):
        self.printer.verbose("Uninstall binary...")
        cmd = "{bin} -u {app}".format(bin=self.DEVICE_TOOLS['IPAINSTALLER'],
                                      app=identifier)
        self.remote_op.command_interactive_tty(cmd)

    def have_installed(self, app_name):
        self.printer.verbose("Start to check App whether have been installed")
        self._list_apps()
        if app_name in self._applist.keys():
            self.printer.verbose("The %s App have been installed" % app_name)
            return True
        else:
            self.printer.verbose("The %s App not been installed" % app_name)
            return False

    def get_keyboard_cache(self, LOCAL_KeyboardCache_DIR):
        """get keyboard cache from device."""
        self.printer.verbose("Start to get Keyobard cache data from device.")
        try:
            self.remote_op.download(Constants.KEYBOARD_CACHE +
                                    "en-dynamic.lm/",
                                    LOCAL_KeyboardCache_DIR,
                                    recursive=True)
            self.pull(Constants.KEYBOARD_CACHE + "dynamic-text.dat",
                      LOCAL_KeyboardCache_DIR + ".")
        except:
            self.printer.error("Cannot sync the keyboard cache data.")

    def get_cookies(self, LOCAL_COOKIES_DIR):
        """get Cookies file from device."""
        self.printer.verbose("Start to get cookies files from device.")
        try:
            self.remote_op.download(Constants.COOKIES_PATH,
                                    LOCAL_COOKIES_DIR,
                                    recursive=True)
        except:
            self.printer.error("Cannot sync the cookies files.")

    def analyze_cookies(self, fname):
        cmd = 'python {bin} {temp_file}'.format(
            bin=self._tools_local['BINARYCOOKIEREADER'], temp_file=fname)
        out = self.local_op.command_interactive(cmd)
        self.printer.verbose("COOKIES's value %s" % out)
        return out

    def dump_keychain(self):
        self.printer.verbose("Start to dump keychain data from device.")
        keychaindata = ''
        cmd = '{} --action dump'.format(self.DEVICE_TOOLS['KEYCHAIN_DUMP'])
        stdin, stdout, stderr = self.conn.exec_command(cmd)
        out = stdout.read()
        data = json.loads(out)
        for key in data:
            keychaindata += "," + (json.dumps(data[key]))
        return keychaindata[1:]

    def dump_head_memory(self, app_name, local_head_folder):
        try:
            self._list_apps()
            metadata = self.app.get_metadata(app_name)
            self.printer.info("Launching the app...")
            self.app.open(metadata['bundle_id'])
            pid = self.app.search_pid(metadata['name'])
            # Create temp files/folders
            dir_dumps = self.remote_op.build_temp_path_for_file("gdb_dumps")
            fname_mach = self.remote_op.build_temp_path_for_file("gdb_mach")
            fname_ranges = self.remote_op.build_temp_path_for_file(
                "gdb_ranges")
            self.remote_op.write_file(fname_mach, "info mach-regions")
            if self.remote_op.dir_exist(dir_dumps):
                self.remote_op.dir_delete(dir_dumps)
            self.remote_op.dir_create(dir_dumps)
            # Enumerate Mach Regions
            self.printer.info("Enumerating mach regions...")
            cmd = '''\
             gdb --pid="%s" --batch --command=%s 2>/dev/null | grep sub-regions | awk '{print $3,$5}' | while read range; do
               echo "mach-regions: $range"
               cmd="dump binary memory %s/dump`echo $range| awk '{print $1}'`.dmp $range"
               echo "$cmd" >> %s
           done ''' % (pid, fname_mach, dir_dumps, fname_ranges)
            self.remote_op.command_blocking(cmd)

            # Dump memory
            self.printer.info("Dumping memory (it might take a while)...")
            cmd = 'gdb --pid="%s" --batch --command=%s &>>/dev/null' % (
                pid, fname_ranges)
            self.remote_op.command_blocking(cmd)
            self.printer.info("Dump memory done.be stored under %s" %
                              dir_dumps)

            self.remote_op.download(dir_dumps, local_head_folder, True)
            #return dir_dumps
            '''
           # Check if we have dumps
           self.printer.verbose("Checking if we have dumps...")
           file_list = self.remote_op.dir_list(dir_dumps, recursive=True)
           failure = filter(lambda x: 'total 0' in x, file_list)
           if failure:
              self.printer.error('It was not possible to attach to the process (known issue in iOS9. A Fix is coming soon)')
              return
         '''
        except:
            self.printer.error("Can't dump head memory data, Plese retry!!! ")