Exemple #1
0
    def __init__(self,
                 device=None,
                 out_file=None,
                 package_name=None,
                 is_wearable=False):
        """
        initialize logcat connection
        :param device: a Device instance
        """
        self.logger = logging.getLogger(self.__class__.__name__)
        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device
        self.connected = False
        self.process = None
        if device.output_dir is None:
            self.out_file = None
        else:
            if out_file is not None:
                self.out_file = "%s/%s" % (device.output_dir, out_file)
            else:
                self.out_file = "%s/logcat.txt" % device.output_dir
        self.package_name = package_name
        self.is_wearable = is_wearable

        # NOTE: /dcsl/ Wear Event Mapper.
        #       Maps an observable state in the wearable (e.g., sensor activity, intra-device comm.)
        #       with a sequence of UI events (either in the mobile device or wearable device).
        self.wear_events = []
Exemple #2
0
    def __init__(self, device=None):
        """
        initiate a minicap connection
        :param device: instance of Device
        :return:
        """
        self.logger = logging.getLogger(self.__class__.__name__)
        self.host = "localhost"

        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device
        self.port = self.device.get_random_port()

        self.remote_minicap_path = "/data/local/tmp/minicap-devel"

        self.sock = None
        self.connected = False
        self.minicap_process = None
        self.banner = None
        self.width = -1
        self.height = -1
        self.orientation = -1

        self.last_screen = None
        self.last_screen_time = None
        self.last_views = []
Exemple #3
0
 def __init__(self, device=None):
     """
     initiate a emulator console via telnet
     :param device: instance of Device
     :return:
     """
     self.logger = logging.getLogger(self.__class__.__name__)
     if device is None:
         from droidbot.device import Device
         device = Device()
     self.device = device
     self.connected = False
Exemple #4
0
    def __init__(self, device=None):
        """
        initiate a ADB connection from serial no
        the serial no should be in output of `adb devices`
        :param device: instance of Device
        :return:
        """
        self.logger = logging.getLogger(self.__class__.__name__)
        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device

        self.cmd_prefix = ['adb', "-s", device.serial]
Exemple #5
0
    def __init__(self, device=None):
        """
        initialize connection
        :param device: a Device instance
        """
        self.logger = logging.getLogger(self.__class__.__name__)

        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device
        self.connected = False
        self.process = None
        # if device.output_dir is None:
        self.out_file = None
Exemple #6
0
    def __init__(self, device=None):
        """
        initiate a socket connection between droidbot and the fuzzer app installed
        on the device
        """
        self.logger = logging.getLogger(self.__class__.__name__)
        self.host = REMOTE_SERVER
        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device
        self.port = self.device.get_random_port()
        self.connected = False

        self.sock = None
    def __init__(self, device=None, auth_token=None):
        """
        initiate a emulator console via telnet
        :param device: instance of Device
        :return:
        """
        self.logger = logging.getLogger('TelnetConsole')

        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device
        self.auth_token = auth_token
        self.console = None
        self.__lock__ = threading.Lock()
 def __init__(self, device=None):
     """
     initialize logcat connection
     :param device: a Device instance
     """
     if device is None:
         from droidbot.device import Device
         device = Device()
     self.device = device
     self.connected = False
     self.process = None
     if device.output_dir is None:
         self.out_file = None
     else:
         self.out_file = "%s/logcat.txt" % device.output_dir
Exemple #9
0
    def __init__(self, device=None, auth_token=None):
        """
        Initiate a emulator console via telnet.
        On some devices, an authentication token is required to use telnet
        :param device: instance of Device
        :return:
        """
        self.logger = logging.getLogger(self.__class__.__name__)

        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device
        self.auth_token = auth_token
        self.console = None
        self.__lock__ = threading.Lock()
Exemple #10
0
    def __init__(self, device=None):
        """
        initiate a jdwp connection.
        :param device: instance of Device
        :return:
        """
        self.logger = logging.getLogger(self.__class__.__name__)
        self.host = "localhost"

        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device
        self.port = self.device.get_random_port()

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connected = False
    def __init__(self, device=None):
        """
        initiate a emulator console via telnet
        :param device: instance of Device
        :return:
        """
        self.logger = logging.getLogger('DroidBotAppConn')
        self.host = "localhost"
        self.port = 7336
        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device
        self.connected = False
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.last_acc_event = None
Exemple #12
0
 def __init__(self, device=None):
     """
     initialize logcat connection
     :param device: a Device instance
     """
     self.logger = logging.getLogger(self.__class__.__name__)
     if device is None:
         from droidbot.device import Device
         device = Device()
     self.device = device
     self.connected = False
     self.process = None
     self.parsers = []
     self.recent_lines = []
     if device.output_dir is None:
         self.out_file = None
     else:
         self.out_file = "%s/logcat.txt" % device.output_dir
Exemple #13
0
    def __init__(self, device=None):
        """
        initiate a droidbot app connection
        :param device: instance of Device
        :return:
        """
        self.logger = logging.getLogger(self.__class__.__name__)
        self.host = "localhost"
        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device
        self.port = self.device.get_random_port()
        self.connected = False
        self.__can_wait = True

        self.sock = None
        self.last_acc_event = None
Exemple #14
0
    def __init__(self, device=None):
        """
        initiate a droidbot app connection
        :param device: instance of Device
        :return:
        """
        self.logger = logging.getLogger('DroidBotAppConn')
        self.host = "localhost"
        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device
        self.port = self.device.get_random_port()
        self.connected = False
        self.__can_wait = True

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        self.last_acc_event = None
Exemple #15
0
    def __init__(self, device=None):
        """
        initiate a emulator console via telnet
        :param device: instance of Device
        :return:
        """
        self.logger = logging.getLogger('minicap')
        self.host = "localhost"
        self.port = 7335
        self.device = device
        self.remote_minicap_path = "/data/local/tmp/minicap-devel"

        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connected = False
        self.minicap_process = None
        self.banner = None
        self.last_screen = None
        self.last_screen_time = None

        if self.device is None:
            from droidbot.device import Device
            self.device = Device()
Exemple #16
0
    def __init__(self, device=None):
        """
        initiate a droidbot app connection
        :param device: instance of Device
        :return:
        """
        self.logger = logging.getLogger(self.__class__.__name__)
        self.host = "localhost"
        if device is None:
            from droidbot.device import Device
            device = Device()
        self.device = device
        self.port = self.device.get_random_port()
        self.connected = False
        self.__can_wait = True

        self.sock = None
        self.last_acc_event = None
        self.enable_accessibility_hard = device.enable_accessibility_hard
        self.ignore_ad = device.ignore_ad
        if self.ignore_ad:
            import re
            self.__first_cap_re = re.compile("(.)([A-Z][a-z]+)")
            self.__all_cap_re = re.compile("([a-z0-9])([A-Z])")
Exemple #17
0
    def __init__(self,
                 app_path=None,
                 device_serial=None,
                 is_emulator=False,
                 output_dir=None,
                 cv_mode=False,
                 grant_perm=False,
                 debug_mode=False,
                 enable_accesibility_hard=False,
                 humanoid=None,
                 device2_serial=None,
                 wear_device=0,
                 replay_output=None):
        """
        Initiates orchestrator and required configurations
        :param app_path:
        :param device_serial:
        :param wear_device_serial:
        :param state_model:
        :param is_emulator:
        :param output_dir:
        :param cv_mode:
        :param grant_perm:
        :param enable_accesibility_hard:
        :param humanoid:
        :param device2_serial:
        :param wear_device:
        """
        logging.basicConfig(
            level=logging.DEBUG if debug_mode else logging.INFO,
            format=GV.LOG_FORMAT)

        self.logger = logging.getLogger('Orchestrator')

        self.keep_env = False
        Orchestrator.instance = self

        self.output_dir = output_dir
        self.device = None
        self.device_paired = None
        self.orchestrator = None
        self.monitor = None
        self.replay_output = replay_output
        self.enable_accesibility_hard = enable_accesibility_hard

        self.enabled = True
        self.comando = None

        try:

            device_adapters = {
                "adb": True,
                "telnet": False,
                "droidbot_app": False,
                "minicap": False,
                "logcat": True,
                "user_input_monitor": True,
                "process_monitor": True,
                "droidbot_ime": False
            }

            # setup to main device
            self.device = Device(
                device_serial,
                is_emulator,
                output_dir=self.output_dir,
                cv_mode=cv_mode,
                grant_perm=grant_perm,
                enable_accessibility_hard=self.enable_accesibility_hard,
                humanoid=humanoid,
                is_wearable=True if wear_device == 1 else False  #,
                # adapters=device_adapters
            )
            print("[ORHC] Device(s) started")

            # app (used for training)
            self.app = App(app_path, output_dir=self.output_dir)
            print("[ORHC] App started")

            # NOTE: /dcsl/ Setup secondary device (paired device)
            #       This device is optional. If the training was done in the mobile, then we will need a paired
            #       device (wearable). However, in case that no `device2_serial` is provided, this could mean
            #       that the training was done in the wearable and there is no need to instance this additional device.
            if device2_serial is not None:
                self.device_paired = Device(device_serial=device2_serial,
                                            output_dir=self.output_dir,
                                            is_wearable=True,
                                            package_name=self.app.package_name,
                                            adapters=device_adapters)
            else:
                self.device_paired = None

            # replay module
            self.replay = Replay(device_serial=device_serial,
                                 is_emulator=is_emulator,
                                 output_dir=output_dir)
            print("[ORHC] Replay Module started")

            # NOTE: /dcsl/ Socket command interface
            #       This adapter is used to communicate fuzz command to the target application.
            if wear_device == 1:
                self.comando = Comando(self.device)
                print("[ORHC] Command interface started on %s" %
                      self.device.serial)
            else:
                self.comando = Comando(self.device_paired)
                print("[ORHC] Command interface started on %s" %
                      self.device_paired.serial)

        except Exception:
            import traceback
            traceback.print_exc()
            self.stop()
            sys.exit(-1)
Exemple #18
0
class Orchestrator(object):
    """
    Bork. The main class for the orchestrator module.
    Principal module that guide the fuzz testing

    The exploration (replay of events) is done in the main device. Since there's the possibility of having
    exploration in both devices (mobile and wearable), the orchestrator will have to be invoked twice (for each
    of the devices).
    """

    # this is a single instance class
    instance = None

    def __init__(self,
                 app_path=None,
                 device_serial=None,
                 is_emulator=False,
                 output_dir=None,
                 cv_mode=False,
                 grant_perm=False,
                 debug_mode=False,
                 enable_accesibility_hard=False,
                 humanoid=None,
                 device2_serial=None,
                 wear_device=0,
                 replay_output=None):
        """
        Initiates orchestrator and required configurations
        :param app_path:
        :param device_serial:
        :param wear_device_serial:
        :param state_model:
        :param is_emulator:
        :param output_dir:
        :param cv_mode:
        :param grant_perm:
        :param enable_accesibility_hard:
        :param humanoid:
        :param device2_serial:
        :param wear_device:
        """
        logging.basicConfig(
            level=logging.DEBUG if debug_mode else logging.INFO,
            format=GV.LOG_FORMAT)

        self.logger = logging.getLogger('Orchestrator')

        self.keep_env = False
        Orchestrator.instance = self

        self.output_dir = output_dir
        self.device = None
        self.device_paired = None
        self.orchestrator = None
        self.monitor = None
        self.replay_output = replay_output
        self.enable_accesibility_hard = enable_accesibility_hard

        self.enabled = True
        self.comando = None

        try:

            device_adapters = {
                "adb": True,
                "telnet": False,
                "droidbot_app": False,
                "minicap": False,
                "logcat": True,
                "user_input_monitor": True,
                "process_monitor": True,
                "droidbot_ime": False
            }

            # setup to main device
            self.device = Device(
                device_serial,
                is_emulator,
                output_dir=self.output_dir,
                cv_mode=cv_mode,
                grant_perm=grant_perm,
                enable_accessibility_hard=self.enable_accesibility_hard,
                humanoid=humanoid,
                is_wearable=True if wear_device == 1 else False  #,
                # adapters=device_adapters
            )
            print("[ORHC] Device(s) started")

            # app (used for training)
            self.app = App(app_path, output_dir=self.output_dir)
            print("[ORHC] App started")

            # NOTE: /dcsl/ Setup secondary device (paired device)
            #       This device is optional. If the training was done in the mobile, then we will need a paired
            #       device (wearable). However, in case that no `device2_serial` is provided, this could mean
            #       that the training was done in the wearable and there is no need to instance this additional device.
            if device2_serial is not None:
                self.device_paired = Device(device_serial=device2_serial,
                                            output_dir=self.output_dir,
                                            is_wearable=True,
                                            package_name=self.app.package_name,
                                            adapters=device_adapters)
            else:
                self.device_paired = None

            # replay module
            self.replay = Replay(device_serial=device_serial,
                                 is_emulator=is_emulator,
                                 output_dir=output_dir)
            print("[ORHC] Replay Module started")

            # NOTE: /dcsl/ Socket command interface
            #       This adapter is used to communicate fuzz command to the target application.
            if wear_device == 1:
                self.comando = Comando(self.device)
                print("[ORHC] Command interface started on %s" %
                      self.device.serial)
            else:
                self.comando = Comando(self.device_paired)
                print("[ORHC] Command interface started on %s" %
                      self.device_paired.serial)

        except Exception:
            import traceback
            traceback.print_exc()
            self.stop()
            sys.exit(-1)

    @staticmethod
    def get_instance():
        if Orchestrator.instance is None:
            print("Error: Orchestrator is not initiated!")
            sys.exit(-1)
        return Orchestrator.instance

    def start(self):
        """
        Run the orchestrator (start the fuzz)
        """
        if not self.enabled:
            return
        self.logger.info("Starting fuzzer")

        try:

            # setup/connect to mobile device
            self.device.set_up()

            if not self.enabled:
                return

            self.device.connect()

            # setup/connect to wearable device
            if self.device_paired:
                self.device_paired.set_up()

                if not self.enabled:
                    return

                self.device_paired.connect()

            # FIXME: /eba/ 7/27/19 Enable monitor
            # # start monitor
            # self.monitor = Monitor(wear_device=self.wear_device)
            # self.monitor.run()

            if not self.enabled:
                return

            # establish connection with device
            self.comando.connect()

            if not self.enabled:
                return

            self.run()

        except KeyboardInterrupt:
            self.logger.info("Keyboard interrupt.")
            pass
        except Exception:
            import traceback
            traceback.print_exc()
            self.stop()
            sys.exit(-1)

        self.stop()
        self.logger.info("Orchestrator Stopped")

    def run(self):
        # Load state model graph
        self.G = nx.DiGraph()
        self.__load_graph()

        # temp
        # nx.draw(self.G)
        # self.__loop()
        # p.show()
        # sys.exit(0)

        # for each state:
        #  (1) call replay module to steer the device
        #  (2) do a fuzz campaign according to the state

        # iterate model
        self.__loop()

    def stop(self):
        """
        Gracefully shutdown or close open resources (e.g. devices)
        :return:
        """
        if self.comando:
            self.comando.disconnect()
        if self.device:
            self.device.disconnect()
        if self.device_paired:
            self.device_paired.disconnect()
        if not self.keep_env:
            # self.monitor.stop()
            self.device.tear_down()
            if self.device_paired:
                self.device_paired.tear_down()

    def __loop(self):
        """
        Iterate thru the state model
        :return:
        """

        nodes = self.G.nodes()

        print("looping")
        # set root source (to initiate the exploration)
        source = self.G.nodes()[self.source]

        print(source)

        # FIXME: \eba\ 7/27/19 Definetely replace this form to start the app, since is not working and is too much overhead
        # Every traverse of the graph starts from the main activity of the app
        self.device.start_app(self.app)

        print("\eba\ Replaying to replay events ...")

        # FIXME. This probably will require some work in the near future
        # The basic problem is the granularity of state and the relation of the state with the transitions, because is
        # possible to have in the same state more than one activity (if we only consider sensor/communication). This
        # problem will be more relevant in the mobile device.
        edges = nx.dfs_edges(self.G, source['name'])
        for edge in edges:
            # steer to this state
            data = self.G.get_edge_data(*edge)
            # replay event
            event_json_file_path = "%sevents/event_%s.json" % (
                self.replay_output, data['tag'])
            print(event_json_file_path)
            self.replay.run(event_json_file_path)

            # do fuzz
            self.__fuzz(data)

        # self.__fuzz_intent(self.FUZZER_ACTION_START)

    def __load_graph(self):
        """
        Load state model graph
        :param digraph: the json file that represents the state model of the app
        :return:
        """

        # read model from json
        input = os.path.join(self.replay_output, "utg.js")
        with open(input) as f:

            # skip first line ("var utg = ")
            f.readline()
            data = json.load(f)

            nodes = data['nodes']
            edges = data['edges']

            for node in nodes:
                label = 'label_{}'.format(node['id'])
                self.G.add_node(node['id'], label=label, name=node['id'])

            for edge in edges:
                label = 'label-{}-{}_{}'.format(edge['from'], edge['to'],
                                                edge['id'])
                self.G.add_edge(edge['from'],
                                edge['to'],
                                label=label,
                                wear_events=['wear_events'],
                                tag=edge['tag'])

            # for node in self.G.nodes():
            #     print(node)
            #     for edge in self.G.edges(node):
            #         print(edge)

            # source node
            self.source = data['first_activity']

    # TODO: (3/27/19) This is probably better to move in a separate file.
    # Since we are going to have richer fuzzing strategy, is better to have all the fuzzing
    # strategy in a separate file, or at least the interface to interact/call/invoke them.
    def __fuzz(self, data):

        if (FUZZ_ENABLED):
            print("\eba\ do fuzzing")
        else:
            return

        type = 1

        # NOTE: /dcsl/ Fuzzing Strategies
        #       The application of the strategies depends on the state of the target device.

        if (type == 1):
            # Intent Fuzzing Strategy.
            # This campaign is only applied to the initial states or a state marked as `vulnerable`.
            action = {
                "target": self.app.get_package_name(),
                "action": ACTION_FUZZ_INTENT
            }
            self.__fuzz_intent(action)
        elif (type == 2):
            # Communication Fuzzing Strategy.
            # This require a interface with frida server (incl. instrumentation of the target app).
            pass
        elif (type == 3):
            # FCM (Firebase Cloud Messaging)
            # This either require an interface with frida server (incl. instrumentation of the target app)
            # or/and root access on the target device.
            pass

    def __fuzz_intent(self, action):
        """
        Invoke intent injection campaign.
        :return:
        """
        rtn = self.comando.send(action)
        print('status: %s' % rtn)