Example #1
0
    def __init__(self):
        self._reactor = Reactor(run_until_return=self._process_input)

        gateway_params = frida.EndpointParameters(port=8080)
        www = Path(__file__).parent.resolve() / "web_client" / "dist"

        self._service = frida.WebGatewayService(gateway_params, root=www)
Example #2
0
    def __init__(self):
        appdata_path = os.environ["appdata"]
        data_path = os.path.join(appdata_path,"winstrument")

        if not os.path.exists(data_path):
            os.mkdir(data_path)

        settings_path = os.path.join(data_path, "settings.toml")

        #unique temporary storage for each instance of the program
        self._db = DBConnection(os.path.join(data_path,f"db_{datetime.now().timestamp()}.sqlite3"))
        self.settings_controller = SettingsController(settings_path)
        default_settings = {'target': 'C:\\Windows\\System32\\Calc.exe' , "verbosity": 0}
        #settings won't exist on first run
        if self.settings_controller.get_module_settings(self.CORE_MODNAME) == {}:
            self.settings_controller.set_module_settings(self.CORE_MODNAME, default_settings)

        self.metadata = self.get_metadata()
        self._stop_requested = threading.Event()
        self._reactor = Reactor(run_until_return=lambda reactor: self._stop_requested.wait())
        self._device = frida.get_local_device()
        self._sessions = set()

        self._device.on("child-added", lambda child: self._reactor.schedule(lambda: self._on_child_added(child)))
        self._device.on("child-removed", lambda child: self._reactor.schedule(lambda: self._on_child_removed(child)))
        self._base_module = importlib.import_module("winstrument.base_module",None)
        self._modules_to_load=[]
        self._available_modules = self._enumerate_modules()
        self._loaded_modules = []
        self._instrumentations = []
    def __init__(self):
        self._stop_requested = threading.Event()
        self._reactor = Reactor(
            run_until_return=lambda reactor: self._stop_requested.wait())

        # self._device = frida.get_local_device()
        self._device = frida.get_usb_device()
        self._sessions = set()

        # self._device.on("delivered", lambda child: self._reactor.schedule(lambda: self._on_delivered(child)))
        self._device.on(
            "spawn-added", lambda child: self._reactor.schedule(
                lambda: self._on_delivered(child)))
Example #4
0
    def __init__(self):
        self._reactor = Reactor(run_until_return=self._process_input)

        cluster_params = frida.EndpointParameters(
            address="unix:/Users/oleavr/src/cluster",
            certificate="/Users/oleavr/src/identity2.pem",
            authentication=('token', "wow-such-secret"))

        if ENABLE_CONTROL_INTERFACE:
            control_params = frida.EndpointParameters(
                address="::1",
                port=27042,
                certificate="/Users/oleavr/src/identity.pem",
                authentication=('callback', self._authenticate))
        else:
            control_params = None

        service = frida.PortalService(cluster_params, control_params)
        self._service = service
        self._device = service.device
        self._peers = {}
        self._nicks = set()
        self._channels = {}

        service.on(
            'node-connected', lambda *args: self._reactor.schedule(
                lambda: self._on_node_connected(*args)))
        service.on(
            'node-joined', lambda *args: self._reactor.schedule(
                lambda: self._on_node_joined(*args)))
        service.on(
            'node-left', lambda *args: self._reactor.schedule(
                lambda: self._on_node_left(*args)))
        service.on(
            'node-disconnected', lambda *args: self._reactor.schedule(
                lambda: self._on_node_disconnected(*args)))
        service.on(
            'controller-connected', lambda *args: self._reactor.schedule(
                lambda: self._on_controller_connected(*args)))
        service.on(
            'controller-disconnected', lambda *args: self._reactor.schedule(
                lambda: self._on_controller_disconnected(*args)))
        service.on(
            'authenticated', lambda *args: self._reactor.schedule(
                lambda: self._on_authenticated(*args)))
        service.on(
            'subscribe', lambda *args: self._reactor.schedule(
                lambda: self._on_subscribe(*args)))
        service.on(
            'message', lambda *args: self._reactor.schedule(
                lambda: self._on_message(*args)))
Example #5
0
class Application(object):
    def __init__(self):
        self._reactor = Reactor(run_until_return=self._process_input)

        gateway_params = frida.EndpointParameters(port=8080)
        www = Path(__file__).parent.resolve() / "web_client" / "dist"

        self._service = frida.WebGatewayService(gateway_params, root=www)

    def run(self):
        self._reactor.schedule(self._start)
        self._reactor.run()

    def _start(self):
        self._service.start()

    def _stop(self):
        self._service.stop()

    def _process_input(self, reactor):
        while True:
            try:
                command = input("Enter command: ").strip()
            except KeyboardInterrupt:
                self._reactor.cancel_io()
                return

            if command == "stop":
                self._reactor.schedule(self._stop)
                break
Example #6
0
    def __init__(self, nick):
        self._reactor = Reactor(run_until_return=self._process_input)

        token = {
            'nick': nick,
            'secret': "knock-knock"
        }
        self._device = frida.get_device_manager().add_remote_device("::1",
                                                                    certificate="/Users/oleavr/src/cert.pem",
                                                                    token=json.dumps(token))

        self._bus = self._device.bus
        self._bus.on('message', lambda *args: self._reactor.schedule(lambda: self._on_bus_message(*args)))

        self._channel = None
        self._prompt = "> "
    def __init__(self):
        self._stop_requested = threading.Event()
        self._reactor = Reactor(
            run_until_return=lambda reactor: self._stop_requested.wait())

        self._device = frida.get_local_device()
        self._sessions = set()

        self._device.on(
            "child-added", lambda child: self._reactor.schedule(
                lambda: self._on_child_added(child)))
        self._device.on(
            "child-removed", lambda child: self._reactor.schedule(
                lambda: self._on_child_removed(child)))
        self._device.on(
            "output", lambda pid, fd, data: self._reactor.schedule(
                lambda: self._on_output(pid, fd, data)))
Example #8
0
    def __init__(self):
        self._stop_requested = threading.Event()
        self._reactor = Reactor(run_until_return=lambda reactor: self._stop_requested.wait())

        self._device = frida.get_local_device()
        self._sessions = set()

        self._device.on("child-added", lambda child: self._reactor.schedule(lambda: self._on_child_added(child)))
        self._device.on("child-removed", lambda child: self._reactor.schedule(lambda: self._on_child_removed(child)))
        self._device.on("output", lambda pid, fd, data: self._reactor.schedule(lambda: self._on_output(pid, fd, data)))
Example #9
0
    def __init__(self, argv, env):
        self._stop_requested = threading.Event()
        self._reactor = Reactor(
            run_until_return=lambda reactor: self._stop_requested.wait())

        self._device = frida.get_usb_device()
        self._sessions = set()

        self._device.on(
            "child-added", lambda child: self._reactor.schedule(
                lambda: self._on_child_added(child)))
        self._device.on(
            "child-removed", lambda child: self._reactor.schedule(
                lambda: self._on_child_removed(child)))
        self._device.on(
            "output", lambda pid, fd, data: self._reactor.schedule(
                lambda: self._on_output(pid, fd, data)))

        self.argv = argv
        self.env = env
        self.output = []  # stdout will pushed into array
Example #10
0
    def __init__(self, config, logger_obj, api_watch_list):

        self.config = config
        self.logger = logger_obj
        self.api_watch_list = api_watch_list
        self.execution_timeout = self.config.get_execution_timeout()
        self.capture_basic_behavior = self.config.get_capture_behavior_report_basic_flag()
        self.capture_complete_behavior = self.config.get_capture_behavior_report_complete_flag()
        self.process_list = []
        self.target_process_pid = None

        # Initialize Reporting Module
        self.report_generator = Reporting(config, logger_obj)

        # Initialize Queue for FriSpyGUI to receive the events
        self.event_queue_name = self.config.get_event_queue_name()
        self.msmqueue_event = MSMQCustom(self.event_queue_name)

        self.config_queue_name = self.config.get_config_queue_name()
        self.msmqueue_config = MSMQCustom(self.config_queue_name)

        # Initialize Controller
        self._stop_requested = threading.Event()
        self._reactor = Reactor(run_until_return=lambda reactor: self._stop_requested.wait())

        self._device = frida.get_local_device()
        self._sessions = set()
        try:

            self._device.on("child-added", lambda child: self._reactor.schedule(lambda: self._on_child_added(child)))
            self._device.on("child-removed", lambda child: self._reactor.schedule(lambda: self._on_child_removed(child)))
            self._device.on("output", lambda pid, fd, data: self._reactor.schedule(lambda: self._on_output(pid, fd, data)))
            self._device.on("process-crashed", lambda crash: self._reactor.schedule(lambda: self._on_process_crashed(crash)))
            self._device.on("lost", lambda crash: self._reactor.schedule(lambda: self._on_process_crashed(crash)))

        except Exception as e:
            self.logger.log("error", "Exception - FriSpyController : run : %s" %(str(e)))
            self.Controller_cleaup(None)
            sys.exit(1)
Example #11
0
    def __init__(self, path, filename, queue, num_skip_calls):
        import frida
        from frida_tools.application import Reactor

        self._logger = logging.getLogger(self.__class__.__name__)

        self.path = path
        self.filename = filename
        self.states = queue
        self.num_skip_calls = num_skip_calls

        self._stop_requested = threading.Event()
        self._reactor = Reactor(
            run_until_return=lambda reactor: self._stop_requested.wait())

        self._device = frida.get_local_device()

        self._device.on("output", lambda pid, fd, data: self._reactor.schedule(
            lambda: self._on_output(pid, fd, data)))  # pylint: disable=undefined-variable
        self.output = io.StringIO()

        self._mallocs = 0
        self._frees = 0
        self._output_counter = 0
Example #12
0
 def test_basics(self):
     test_ui = TestUI()
     reactor = Reactor(lambda reactor: test_ui.on_result.wait())
     def start():
         d = Discoverer(reactor)
         d.start(self.session, test_ui)
         reactor.schedule(d.stop, 0.1)
     reactor.schedule(start)
     reactor.run()
     self.assertIsInstance(test_ui.module_functions, dict)
     self.assertIsInstance(test_ui.dynamic_functions, list)
Example #13
0
 def test_basics(self):
     done = threading.Event()
     reactor = Reactor(lambda reactor: done.wait())
     def start():
         tp = TracerProfileBuilder().include("open*")
         t = Tracer(reactor, MemoryRepository(), tp.build())
         targets = t.start_trace(self.session, 'late', {}, 'qjs', UI())
         t.stop()
         reactor.stop()
         done.set()
     reactor.schedule(start)
     reactor.run()
Example #14
0
class Shell(object):
    def __init__(self, argv, env):
        self._stop_requested = threading.Event()
        self._reactor = Reactor(
            run_until_return=lambda reactor: self._stop_requested.wait())

        self._device = frida.get_usb_device()
        self._sessions = set()

        self._device.on(
            "child-added", lambda child: self._reactor.schedule(
                lambda: self._on_child_added(child)))
        self._device.on(
            "child-removed", lambda child: self._reactor.schedule(
                lambda: self._on_child_removed(child)))
        self._device.on(
            "output", lambda pid, fd, data: self._reactor.schedule(
                lambda: self._on_output(pid, fd, data)))

        self.argv = argv
        self.env = env
        self.output = []  # stdout will pushed into array

    def exec(self):
        self._reactor.schedule(lambda: self._start())
        self._reactor.run()

    def _start(self):
        click.secho("✔ spawn(argv={})".format(self.argv), fg='green', dim=True)
        pid = self._device.spawn(self.argv, env=self.env, stdio='pipe')
        self._instrument(pid)

    def _stop_if_idle(self):
        if len(self._sessions) == 0:
            self._stop_requested.set()

    def _instrument(self, pid):
        click.secho("✔ attach(pid={})".format(pid), fg='green', dim=True)
        session = self._device.attach(pid)
        session.on(
            "detached",
            lambda reason: self._reactor.schedule(lambda: self._on_detached(
                pid, session, reason)))
        click.secho("✔ enable_child_gating()", fg='green', dim=True)
        session.enable_child_gating()
        # print("✔ resume(pid={})".format(pid))
        self._device.resume(pid)
        self._sessions.add(session)

    def _on_child_added(self, child):
        click.secho("⚡ child_added: {}".format(child), fg='green', dim=True)
        self._instrument(child.pid)

    @staticmethod
    def _on_child_removed(child):
        click.secho("⚡ child_removed: {}".format(child), fg='green', dim=True)

    def _on_output(self, pid, fd, data):
        # print("⚡ output: pid={}, fd={}, data={}".format(pid, fd, repr(data)))
        # fd=0 (input) fd=1(stdout) fd=2(stderr)
        if fd != 2:
            self.output.append(data)

    def _on_detached(self, pid, session, reason):
        click.secho("⚡ detached: pid={}, reason='{}'".format(pid, reason),
                    fg='green',
                    dim=True)
        self._sessions.remove(session)
        self._reactor.schedule(self._stop_if_idle, delay=0.5)

    @staticmethod
    def _on_message(pid, message):
        click.secho("⚡ message: pid={}, payload={}".format(pid, message),
                    fg='green',
                    dim=True)
Example #15
0
class Application(object):
    def __init__(self):
        self._stop_requested = threading.Event()
        self._reactor = Reactor(
            run_until_return=lambda reactor: self._stop_requested.wait())

        self._device = frida.get_local_device()
        self._sessions = set()

        self._device.on(
            "child-added", lambda child: self._reactor.schedule(
                lambda: self._on_child_added(child)))
        self._device.on(
            "child-removed", lambda child: self._reactor.schedule(
                lambda: self._on_child_removed(child)))
        self._device.on(
            "output", lambda pid, fd, data: self._reactor.schedule(
                lambda: self._on_output(pid, fd, data)))

    def run(self):
        self._reactor.schedule(lambda: self._start())
        self._reactor.run()

    def _start(self):
        argv = ["/bin/sh", "-c", "cat /etc/hosts"]
        env = {
            "BADGER": "badger-badger-badger",
            "SNAKE": "mushroom-mushroom",
        }
        print("✔ spawn(argv={})".format(argv))
        pid = self._device.spawn(argv, env=env, stdio='pipe')
        self._instrument(pid)

    def _stop_if_idle(self):
        if len(self._sessions) == 0:
            self._stop_requested.set()

    def _instrument(self, pid):
        print("✔ attach(pid={})".format(pid))
        session = self._device.attach(pid)
        session.on(
            "detached",
            lambda reason: self._reactor.schedule(lambda: self._on_detached(
                pid, session, reason)))
        print("✔ enable_child_gating()")
        session.enable_child_gating()
        print("✔ create_script()")
        script = session.create_script("""\
Interceptor.attach(Module.getExportByName(null, 'open'), {
  onEnter: function (args) {
    send({
      type: 'open',
      path: Memory.readUtf8String(args[0])
    });
  }
});
""")
        script.on(
            "message", lambda message, data: self._reactor.schedule(
                lambda: self._on_message(pid, message)))
        print("✔ load()")
        script.load()
        print("✔ resume(pid={})".format(pid))
        self._device.resume(pid)
        self._sessions.add(session)

    def _on_child_added(self, child):
        print("⚡ child_added: {}".format(child))
        self._instrument(child.pid)

    def _on_child_removed(self, child):
        print("⚡ child_removed: {}".format(child))

    def _on_output(self, pid, fd, data):
        print("⚡ output: pid={}, fd={}, data={}".format(pid, fd, repr(data)))

    def _on_detached(self, pid, session, reason):
        print("⚡ detached: pid={}, reason='{}'".format(pid, reason))
        self._sessions.remove(session)
        self._reactor.schedule(self._stop_if_idle, delay=0.5)

    def _on_message(self, pid, message):
        print("⚡ message: pid={}, payload={}".format(pid, message["payload"]))
Example #16
0
class Winstrument():
    #adapted from https://github.com/frida/frida-python/blob/master/examples/child_gating.py
    CORE_MODNAME = "core"

    def __init__(self):
        appdata_path = os.environ["appdata"]
        data_path = os.path.join(appdata_path,"winstrument")

        if not os.path.exists(data_path):
            os.mkdir(data_path)

        settings_path = os.path.join(data_path, "settings.toml")

        #unique temporary storage for each instance of the program
        self._db = DBConnection(os.path.join(data_path,f"db_{datetime.now().timestamp()}.sqlite3"))
        self.settings_controller = SettingsController(settings_path)
        default_settings = {'target': 'C:\\Windows\\System32\\Calc.exe' , "verbosity": 0}
        #settings won't exist on first run
        if self.settings_controller.get_module_settings(self.CORE_MODNAME) == {}:
            self.settings_controller.set_module_settings(self.CORE_MODNAME, default_settings)

        self.metadata = self.get_metadata()
        self._stop_requested = threading.Event()
        self._reactor = Reactor(run_until_return=lambda reactor: self._stop_requested.wait())
        self._device = frida.get_local_device()
        self._sessions = set()

        self._device.on("child-added", lambda child: self._reactor.schedule(lambda: self._on_child_added(child)))
        self._device.on("child-removed", lambda child: self._reactor.schedule(lambda: self._on_child_removed(child)))
        self._base_module = importlib.import_module("winstrument.base_module",None)
        self._modules_to_load=[]
        self._available_modules = self._enumerate_modules()
        self._loaded_modules = []
        self._instrumentations = []

    def get_metadata(self, filename="metadata.toml"):
        """
        Parse the metadata.toml file for module metadata like descriptions, if present.
        Returns dict of metadata, or None if the file is not present or invalid.
        """

        metadata_filepath = os.path.join(os.path.dirname(__file__),"modules",filename)
        try:
            metadata = toml.load(metadata_filepath)
            return dict((k.lower(), v) for k,v in metadata.items()) #modulenames are lowercase

        except toml.TomlDecodeError:
            metadata = None
        return metadata

    def get_available_modules(self):
        """
        Gets a list of all available modules (from modules/metadata.toml)
        returns a list with module names
        """
        return self._available_modules.copy()

    def get_loaded_modules(self):
        """
        Gets a list of all modules that have already been loaded
        returns a list of module names
        """
        return [mod for mod in self._loaded_modules]

    def export_all(self, outfile, formatter=utils.format_table):
        """
        Write the output for all modules to the given output stream in the desired format
        outfile - file stream object. This could be a normal file or sys.stdout
        formatter - callable which takes a list of ModuleMessage objects and returns a string to output. See utils.py
        No return, but writes the output stream
        """

        for module in self.get_available_modules():
            self.print_saved_output(module,formatter,outfile)

    def print_saved_output(self, modulename, formatter=utils.format_table, output=sys.stdout):
        """
        Write the output for the given module to the given output stream in the desired format.
        modulename - str
        formatter - callable which takes a list of ModuleMessage objects and returns a string to output. See utils.py
        output - file stream object. This could be a normal file or sys.stdout
        No return, but writes the output stream
        """
        messages = self._db.read_messages(modulename)
        if formatter is None:
            formatter = utils.format_table
        verbosity = self.settings_controller.get_setting_int(self.CORE_MODNAME,"verbosity") or 0
        output.write(formatter(messages,verbosity)+"\n")

    def unload_module(self, module):
        """
        Unloads the given module. It will not be injected with the target is run
        module - str
        """
        try:
            self._modules_to_load.remove(module)
            self._loaded_modules.remove(module)
        except ValueError:
            print (f"Can't unload because {module} wasn't loaded")
        if module not in self._available_modules:
            self._available_modules.append(module)
        self._initialize_modules()

    def load_module(self, module):
        """
        Loads the given module, if it hasn't already been loaded.
        modulename - str
        """
        if module not in self._modules_to_load:
            self._modules_to_load.append(module)
        else:
            print(f"Error: {module} is already loaded")
            return
        self._initialize_modules()

    def _enumerate_modules(self,moudlepath="modules"):
        """
        Returns a list of available modules. Uses metadata file if available, otherwise falls back to all modules in the modulepath
        """
        if self.metadata:
            available_modules = [key.lower() for key in self.metadata.keys()]
        else: #metadata file missing or broken, fall back to module discovery based on path
            available_modules = [name for _, name, _, in pkgutil.iter_modules([os.path.join(os.path.dirname(__file__), moudlepath)])]
        return available_modules

    def _initialize_modules(self, modulepath = "winstrument.modules"):
        """
        Import python modules that are set to be loaded. If the module is not found, print a warning to STDOUT and remove the module from the _modules_to_load list.
        modulepath: string - Python import path for modules. This is a Python path, not a Windows path.
        """
        for modulename in self._modules_to_load:
            try:
                module = importlib.import_module(f"{modulepath}.{modulename}")
                if module not in self._loaded_modules:
                    self._loaded_modules.append(modulename)

            except ImportError:
                print(f"Error: module '{modulename}' not found, skiping!")
                self._modules_to_load.remove(modulename)
                continue

    def run(self,target=None,arglist=None):
        """
        Schedule frida to spawn the target process, then instrument it.
        target: str - path to target to spawn
        arglist: list - arguments to pass to target when spawned
        """
        if target:
            process = target
            args = arglist
        else:
            process = self.settings_controller.get_setting(self.CORE_MODNAME,"target")
            args = self.settings_controller.get_setting(self.CORE_MODNAME,"args")
        self._reactor.schedule(lambda: self._start(process,args))
        self._reactor.run()

    def _start(self,target,args=None):
        """
        Spawn the target process and then instrument it with any available scripts.
        If not found, write a warning to STDERR.
        target: str - Path to the process to spawn
        args: list or None - list of command line arguments to use with the target
        """
        if target is None:
            sys.stderr.write(f"{Fore.RED} No target set. Use 'set target <target> to specify a program to instrument.\n{Style.RESET_ALL}")
            self.stop()
            return
        cmd = [target]
        if args:

            cmd.append(args)
        try:
            pid = self._device.spawn(cmd)
        except frida.ExecutableNotFoundError:
            sys.stderr.write(f"{Fore.RED}Target {target} not found! Make sure the path is correct.\n{Style.RESET_ALL}")
            self.stop()
            return
        print("Spawned " + str(pid))
        self._instrument(pid, target)

    def _stop_if_idle(self):
        """
        Helper function used with Frida reactor. Stops the reactor if there are no queued child sessions.
        """
        if len(self._sessions) == 0:
            self.stop()

    def stop(self):
        """
        Signal that the Frida reactor has been requested to stop, then stop it.
        """
        self._stop_requested.set()
        self._reactor.stop()

    def quit(self):
        """
        Save settings to settings file.
        """
        self.settings_controller.save_settings()
        self._db.close()

    def _instrument(self, pid, path):
        """
        Iterates over currently loaded modules and performs instrumentation on the target process for each.
        pid: int - PID of the spawned process
        path: str - filesystem path to the spawned process executable.
        """
        try:
            session = self._device.attach(pid)
        except frida.TransportError as e:
            sys.stderr.write(f"{Fore.RED} Got exception {repr(e)} when attaching to {pid}\n{Style.RESET_ALL}")
            return

        session.on('detached',lambda reason: self._reactor.schedule(lambda: self._on_detach(pid, session, reason)))
        session.enable_child_gating() #pause child processes until manually resumed
        for moduleclass in self._base_module.BaseInstrumentation.__subclasses__():
            if moduleclass.modulename in self._loaded_modules: # module might have been unloaded by user
                instrumentation = moduleclass(session, path, self._db)
                self._instrumentations.append(instrumentation)
                instrumentation.load_script()
        print(f"instrumented process with pid: {pid} and path: {path}")
        self._device.resume(pid)
        self._sessions.add(session)

    def _on_detach(self, pid, session, reason):
        """
        Callback called when the Frdia becomes detached froma  process.
        Calls the on_finish method for any loaded instrumentations, removes the old session and prints output, if verbosity is high enough.
        pid: int - PID of detached process
        session: Frida Session object - session corresponded to the detached process
        reason: str - Reason provided by Frida for why the target terminated
        """
        print (f"detached from {pid} for reason {reason}")
        for instrumentation in self._instrumentations:
            instrumentation.on_finish()
        self._instrumentations.clear() #reset for next session, if any
        self._sessions.remove(session)
        verbosity = self.settings_controller.get_setting_int(self.CORE_MODNAME,"verbosity") or 0
        self.export_all(sys.stdout)

        self._reactor.schedule(self._stop_if_idle, delay=0.5)

    def _on_child_added(self, child):
        """
        Callback called by Frida reactor when a new child is spawned from the target process.
        child - object
        """
        self._instrument(child.pid,child.path)

    def _on_child_removed(self,child):
        """
        Callback called by Frida reactor when a child process ends.
        child - object
        """
        print(f"Child removed: {child.pid}")
Example #17
0
class BinAnalyser:
    def __init__(self, path, filename, queue, num_skip_calls):
        import frida
        from frida_tools.application import Reactor

        self._logger = logging.getLogger(self.__class__.__name__)

        self.path = path
        self.filename = filename
        self.states = queue
        self.num_skip_calls = num_skip_calls

        self._stop_requested = threading.Event()
        self._reactor = Reactor(
            run_until_return=lambda reactor: self._stop_requested.wait())

        self._device = frida.get_local_device()

        self._device.on("output", lambda pid, fd, data: self._reactor.schedule(
            lambda: self._on_output(pid, fd, data)))  # pylint: disable=undefined-variable
        self.output = io.StringIO()

        self._mallocs = 0
        self._frees = 0
        self._output_counter = 0

    def exec(self):
        self._reactor.schedule(lambda: self._start())
        self._reactor.run()
        self.analyse()
        self.states.put('STOP')

    def _stop(self):
        self._stop_requested.set()

    def _start(self):
        self.pid = self._device.spawn(self.path, stdio='pipe')
        self._logger.info("Spawned new process. PID: %d", self.pid)

        session = self._device.attach(self.pid)
        session.on("detached", lambda reason: self._reactor.schedule(
            lambda: self._on_detached(self.pid, session, reason)))  # pylint: disable=undefined-variable
        self._logger.info("Session attached")
        session.enable_child_gating()

        script = session.create_script(
            INJECTED_SCRIPT.format(main_module_name=self.filename,
                                   num_skip_calls=self.num_skip_calls))
        script.on("message", lambda message, data: self._reactor.schedule(
            lambda: self._on_message(message, data)))  # pylint: disable=undefined-variable
        script.load()
        self._logger.info("Script loaded")
        self._print_progress(rewrite=False)

        self._device.resume(self.pid)

    # @profile
    def _on_output(self, pid, fd, data):
        if fd != 2 and pid == self.pid:
            self.output.write(data.decode())

            self._output_counter += 1
            if self._output_counter // 30 == 1:
                self._output_counter = 0
                self.analyse()

    def _on_detached(self, pid, session, reason):
        if pid == self.pid:
            self._logger.info("Session detached")
            self._reactor.schedule(self._stop, delay=0.5)

    def _on_message(self, message, data):
        if message['type'] == 'send':
            if message['payload'] == 'MALLOC':
                self._mallocs += 1
                self._print_progress()
            elif message['payload'] == 'FREE':
                self._frees += 1
                self._print_progress()
            else:
                self._logger.getChild('frida_script').info(message['payload'])
        else:
            self._logger.getChild('frida_script').error(message)

    def _print_progress(self, rewrite=True):
        # string = ''
        # if rewrite:
        #     string = '\r'

        # string += 'Mallocs: %d, Frees: %d' % (self._mallocs, self._frees)
        # sys.stdout.write(string)
        # sys.stdout.flush()
        pass

    # @profile
    def analyse(self):
        self.outputString = self.output.getvalue()
        self.output.close()
        self.output = io.StringIO()

        if not self.outputString:
            return

        split = self.outputString.split('LOG_START_1337\n')
        if len(split) == 1:
            return

        for string in split:
            split = string.split('LOG_END_1337', maxsplit=1)
            if len(split) == 2:
                state_string_raw = split[0].strip()
                state_string_raw_split = state_string_raw.split('\n',
                                                                maxsplit=1)
                if "LOG_MALLOC" in state_string_raw_split[
                        0] or "LOG_FREE" in state_string_raw_split[0]:
                    self.states.put(
                        AllocatorStateWrapper(
                            prev_op=state_string_raw_split[0].replace(
                                'LOG_', ''),
                            freelist_blocks=parse_freelist(
                                state_string_raw_split[1]),
                            tags=parse_tags(state_string_raw_split[1])))
                else:
                    self.states.put(
                        AllocatorStateWrapper(prev_op=None,
                                              freelist_blocks=parse_freelist(
                                                  state_string_raw_split[1]),
                                              tags=parse_tags(
                                                  state_string_raw_split[1])))
            else:
                if "Segmentation fault" in string:
                    logger.error("Segmentation fault occurred")
                    sys.exit()

                self.output.write('LOG_START_1337\n')
                self.output.write(string)
Example #18
0
class Application:
    def __init__(self):
        self._reactor = Reactor(run_until_return=self._process_input)

        cluster_params = frida.EndpointParameters(
            address="unix:/Users/oleavr/src/cluster",
            certificate="/Users/oleavr/src/identity2.pem",
            authentication=('token', "wow-such-secret"))

        if ENABLE_CONTROL_INTERFACE:
            control_params = frida.EndpointParameters(
                address="::1",
                port=27042,
                certificate="/Users/oleavr/src/identity.pem",
                authentication=('callback', self._authenticate))
        else:
            control_params = None

        service = frida.PortalService(cluster_params, control_params)
        self._service = service
        self._device = service.device
        self._peers = {}
        self._nicks = set()
        self._channels = {}

        service.on(
            'node-connected', lambda *args: self._reactor.schedule(
                lambda: self._on_node_connected(*args)))
        service.on(
            'node-joined', lambda *args: self._reactor.schedule(
                lambda: self._on_node_joined(*args)))
        service.on(
            'node-left', lambda *args: self._reactor.schedule(
                lambda: self._on_node_left(*args)))
        service.on(
            'node-disconnected', lambda *args: self._reactor.schedule(
                lambda: self._on_node_disconnected(*args)))
        service.on(
            'controller-connected', lambda *args: self._reactor.schedule(
                lambda: self._on_controller_connected(*args)))
        service.on(
            'controller-disconnected', lambda *args: self._reactor.schedule(
                lambda: self._on_controller_disconnected(*args)))
        service.on(
            'authenticated', lambda *args: self._reactor.schedule(
                lambda: self._on_authenticated(*args)))
        service.on(
            'subscribe', lambda *args: self._reactor.schedule(
                lambda: self._on_subscribe(*args)))
        service.on(
            'message', lambda *args: self._reactor.schedule(
                lambda: self._on_message(*args)))

    def run(self):
        self._reactor.schedule(self._start)
        self._reactor.run()

    def _start(self):
        self._service.start()

        self._device.enable_spawn_gating()

    def _stop(self):
        self._service.stop()

    def _process_input(self, reactor):
        while True:
            try:
                command = input("Enter command: ").strip()
            except KeyboardInterrupt:
                self._reactor.cancel_io()
                return

            if len(command) == 0:
                print("Processes:", self._device.enumerate_processes())
                continue

            if command == "stop":
                self._reactor.schedule(self._stop)
                break

    def _authenticate(self, raw_token):
        try:
            token = json.loads(raw_token)
            nick = str(token['nick'])
            secret = token['secret'].encode('utf-8')
        except:
            raise ValueError("invalid request")

        provided = hashlib.sha1(secret).digest()
        expected = hashlib.sha1("knock-knock".encode('utf-8')).digest()
        if not hmac.compare_digest(provided, expected):
            raise ValueError("get outta here")

        return {
            'nick': nick,
        }

    def _on_node_connected(self, connection_id, remote_address):
        print("on_node_connected()", connection_id, remote_address)

    def _on_node_joined(self, connection_id, application):
        print("on_node_joined()", connection_id, application)
        print("\ttags:", self._service.enumerate_tags(connection_id))

    def _on_node_left(self, connection_id, application):
        print("on_node_left()", connection_id, application)

    def _on_node_disconnected(self, connection_id, remote_address):
        print("on_node_disconnected()", connection_id, remote_address)

    def _on_controller_connected(self, connection_id, remote_address):
        print("on_controller_connected()", connection_id, remote_address)
        self._peers[connection_id] = Peer(connection_id, remote_address)

    def _on_controller_disconnected(self, connection_id, remote_address):
        print("on_controller_disconnected()", connection_id, remote_address)
        peer = self._peers.pop(connection_id)
        for channel in list(peer.memberships):
            channel.remove_member(peer)
        if peer.nick is not None:
            self._release_nick(peer.nick)

    def _on_authenticated(self, connection_id, session_info):
        print("on_authenticated()", connection_id, session_info)
        peer = self._peers.get(connection_id, None)
        if peer is None:
            return
        peer.nick = self._acquire_nick(session_info['nick'])

    def _on_subscribe(self, connection_id):
        print("on_subscribe()", connection_id)
        self._service.post(connection_id, {
            'type': 'welcome',
            'channels': list(self._channels.keys())
        })

    def _on_message(self, connection_id, message, data):
        peer = self._peers[connection_id]

        mtype = message['type']
        if mtype == 'join':
            self._get_channel(message['channel']).add_member(peer)
        elif mtype == 'part':
            channel = self._channels.get(message['channel'], None)
            if channel is None:
                return
            channel.remove_member(peer)
        elif mtype == 'say':
            channel = self._channels.get(message['channel'], None)
            if channel is None:
                return
            channel.post(message['text'], peer)
        elif mtype == 'announce':
            self._service.broadcast({
                'type': 'announce',
                'sender': peer.nick,
                'text': message['text']
            })
        else:
            print("Unhandled message:", message)

    def _acquire_nick(self, requested):
        candidate = requested
        serial = 2
        while candidate in self._nicks:
            candidate = requested + str(serial)
            serial += 1

        nick = candidate
        self._nicks.add(nick)

        return nick

    def _release_nick(self, nick):
        self._nicks.remove(nick)

    def _get_channel(self, name):
        channel = self._channels.get(name, None)
        if channel is None:
            channel = Channel(name, self._service)
            self._channels[name] = channel
        return channel
class Application(object):
    def __init__(self):
        self._stop_requested = threading.Event()
        self._reactor = Reactor(
            run_until_return=lambda reactor: self._stop_requested.wait())

        # self._device = frida.get_local_device()
        self._device = frida.get_usb_device()
        self._sessions = set()

        # self._device.on("delivered", lambda child: self._reactor.schedule(lambda: self._on_delivered(child)))
        self._device.on(
            "spawn-added", lambda child: self._reactor.schedule(
                lambda: self._on_delivered(child)))

    def run(self):
        self._reactor.schedule(lambda: self._start())
        self._reactor.run()

    def _start(self):
        argv = [
            "/data/local/tmp/dcow", "/data/local/tmp/run-as",
            "/system/bin/run-as"
        ]
        print("v spawn(argv={})".format(argv))
        pid = self._device.spawn(argv)
        self._instrument(pid)
        #package_name = "com.yk26gzrdq"
        #activity_name = "com.yk26gzrdq.DesktopActivity"
        #pid = self._device.spawn(package_name, activity=activity_name)
        #pid = self._device.spawn(package_name)
        #self._instrument(pid)

    def _stop_if_idle(self):
        if len(self._sessions) == 0:
            self._stop_requested.set()

    def _instrument(self, pid):
        print("v attach(pid={})".format(pid))
        session = self._device.attach(pid)
        session.on(
            "detached",
            lambda reason: self._reactor.schedule(lambda: self._on_detached(
                pid, session, reason)))
        print("v enable_child_gating()")
        session.enable_child_gating()
        print("v create_script()")
        script = session.create_script("""

/**
 * to demo how Frida can intercept dirtyCow vulnerability Apps
 * @author Vash Hsu
 * @date Octorber, 2019
*/

/**
 * Log the involked methods.
 * @param {string} typeString reporting type, to group multiple send-messages
 * @param {array} infoList array of meta data, where each is ASCII string
 */
function reportCall(typeString, infoList) {
  const sendData = {
    'c': 'report',
    'type': typeString,
    'cols': infoList,
  };
  send(JSON.stringify(sendData));
}

var libcIOmap = {};
var fileIOmap = {};


setImmediate(function() {
  var updateIOmap = function(myKey, myValue) {
    libcIOmap[myKey] = myValue;
  };
  var lookupIOmap = function(myKey) {
    if (myKey in libcIOmap) {
      return libcIOmap[myKey];
    } else {
      return '';
    }
  };
  var updateFileIOmap = function(myKey, myValue) {
    fileIOmap[myKey] = myValue;
  };
  var lookupFileIOmap = function(myKey) {
    if (myKey in fileIOmap) {
      return fileIOmap[myKey];
    } else {
      return '';
    }
  };
  const isInterestingPath = function(filePath) {
    if (filePath.startsWith('/system/') || filePath.startsWith('/data/') ||
      filePath.startsWith('/proc/') || filePath.startsWith('/sdcard/')) {
      return true;
    }
    return true;
  };

  // void *mmap(void *addr, size_t length, int prot, int flags,
  //    int fd, off_t offset);
  // #define PROT_READ      0x1  /* page can be read */
  // #define MAP_PRIVATE    0x0  /* changes are private */
  Interceptor.attach(Module.findExportByName('libc.so', 'mmap'), {
    onEnter: function(args) {
      this.addr = args[0];
      this.len = parseInt(args[1]);
      this.prot = parseInt(args[2]);
      if (this.prot === 1) {
        this.prot = 'PROT_READ';
      }
      this.flags = parseInt(args[3]);
      if (this.flags === 0) {
        this.flags = 'MAP_PRIVATE';
      }
      this.fd = parseInt(args[4]);
      this.offset = parseInt(args[5]);
    },
    onLeave: function(retval) {
      var result = parseInt(retval);
      var targetFile = lookupIOmap(this.fd);
      reportCall('libc.so',
        ['mmap', this.addr, "len="+this.len, "prop="+this.prot, "flag="+this.flags,
            targetFile, "fd="+this.fd, this.offset, retval]);
      return retval;
    },
  });
  // int madvise(void *addr, size_t length, int advice);
  // #define MADV_DONTNEED 4 /* dont need these pages */
  Interceptor.attach(Module.findExportByName('libc.so', 'madvise'), {
    onEnter: function(args) {
      this.addr = args[0];
      this.len = parseInt(args[1]);
      this.advice = parseInt(args[2]);
      if (this.advice === 4) {
        this.advice = 'MADV_DONTNEED';
      }
    },
    onLeave: function(retval) {
      reportCall('libc.so',
          ['madvise', this.addr, this.len, this.advice, retval]);

      return retval;
    },
  });

  // int open(const char *pathname, int flags, mode_t mode);
  Interceptor.attach(Module.findExportByName('libc.so', 'open'), {
    onEnter: function(args) {
      this.path = Memory.readUtf8String(args[0]);
      this.flag = args[1];
      if (parseInt(this.flag) === 0) {
        this.flag = 'O_RDONLY';
      }
      if (typeof args[2] !== 'undefined') {
        this.mode = args[2];
      } else {
        this.mode = 0;
      }
    },
    onLeave: function(retval) {
      var fd = parseInt(retval);
      if (fd != -1) {
        if (isInterestingPath(this.path)) {
          reportCall('libc.so',
              ['open', this.path, this.flag, this.mode, "fd="+fd]);
        }
        updateIOmap(fd, this.path);
      }
      return retval;
    },
  });
  // int openat(int dirfd, const char *pathname, int flags, mode_t mode);
  Interceptor.attach(Module.findExportByName('libc.so', 'openat'), {
    onEnter: function(args) {
      this.path = Memory.readUtf8String(args[1]);
      this.flag = args[2];
      if (parseInt(this.flag) === 0) {
        this.flag = 'O_RDONLY';
      }
      this.mode = args[3];
    },
    onLeave: function(retval) {
      var fd = parseInt(retval);
      if (fd != -1) {
        reportCall('libc.so',
            ['openat', this.path, this.flag, this.mode, "fd="+fd]);
        updateIOmap(fd, this.path);
      }
      return retval;
    },
  });
  // int close(int fildes);
  Interceptor.attach(Module.findExportByName('libc.so', 'close'), {
    onEnter: function(args) {
      this.fd = parseInt(args[0]);
    },
    onLeave: function(retval) {
      var result = parseInt(retval);
      var path = lookupIOmap(this.fd);
      reportCall('libc.so', ['close', path, "fd="+this.fd, result]);
      return retval;
    },
  });
  // ssize_t write(int fd, const void *buf, size_t count);
  Interceptor.attach(Module.findExportByName('libc.so', 'write'), {
    onEnter: function(args) {
      this.fd = parseInt(args[0]);
    },
    onLeave: function(retval) {
      var result = parseInt(retval);
      var path = lookupIOmap(this.fd);
      if (isInterestingPath(path)) {
        reportCall('libc.so', ['write', path, "fd="+this.fd, result]);
      }
      return retval;
    },
  });

  // FILE *fopen(const char *restrict filename, const char *restrict mode);
  Interceptor.attach(Module.findExportByName('libc.so', 'fopen'), {
    onEnter: function(args) {
      this.path = Memory.readUtf8String(args[0]);
      this.mode = Memory.readUtf8String(args[1]);
    },
    onLeave: function(retval) {
      reportCall('libc.so', ['fopen', this.path, this.mode, retval]);
      updateFileIOmap(retval, this.path, 'fptr='+retval);
      return retval;
    },
  });
  // int fclose(FILE *stream);
  Interceptor.attach(Module.findExportByName('libc.so', 'fclose'), {
    onEnter: function(args) {
      this.fptr = args[0];
    },
    onLeave: function(retval) {
      var path = lookupFileIOmap(this.fptr);
      reportCall('libc.so', ['fclose', path, 'fptr='+this.fptr, retval]);
      return retval;
    },
  });
  // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems,
  // FILE *restrict stream);
  Interceptor.attach(Module.findExportByName('libc.so', 'fwrite'), {
    onEnter: function(args) {
      this.fptr = args[3];
    },
    onLeave: function(retval) {
      var path = lookupFileIOmap(this.fptr);
      var totalBytes = parseInt(retval);
      reportCall('libc.so', ['fwrite', path, 'fptr='+this.fptr,totalBytes]);
    },
  });
});


""")
        script.on(
            "message", lambda message, data: self._reactor.schedule(
                lambda: self._on_message(pid, message)))
        print("v load()")
        script.load()
        print("v resume(pid={})".format(pid))
        self._device.resume(pid)
        self._sessions.add(session)

    def _on_delivered(self, child):
        print("/  delivered: {}".format(child))
        self._instrument(child.pid)

    def _on_detached(self, pid, session, reason):
        print("/  detached: pid={}, reason='{}'".format(pid, reason))
        self._sessions.remove(session)
        self._reactor.schedule(self._stop_if_idle, delay=0.5)

    def _on_message(self, pid, message):
        print("/  message: pid={}, payload={}".format(pid, message["payload"]))
Example #20
0
class Application(object):
    def __init__(self):
        self._reactor = Reactor(run_until_return=self._process_input)

        self._device = None
        self._session = None

    def run(self):
        self._reactor.schedule(lambda: self._start())
        self._reactor.run()

    def _start(self):
        device = frida.get_remote_device()
        self._device = device

        session = self._device.attach("hello2", persist_timeout=30)
        self._session = session
        session.on(
            'detached', lambda *args: self._reactor.schedule(
                lambda: self._on_detached(*args)))

        script = session.create_script("""
let _puts = null;

Interceptor.attach(DebugSymbol.getFunctionByName('f'), {
  onEnter(args) {
    const n = args[0].toInt32();
    send(n);
  }
});

rpc.exports.dispose = () => {
  puts('Script unloaded');
};

let serial = 1;
setInterval(() => {
  puts(`Agent still here! serial=${serial++}`);
}, 5000);

function puts(s) {
  if (_puts === null) {
    _puts = new NativeFunction(Module.getExportByName(null, 'puts'), 'int', ['pointer']);
  }
  _puts(Memory.allocUtf8String(s));
}
""")
        self._script = script
        script.on(
            'message', lambda *args: self._reactor.schedule(
                lambda: self._on_message(*args)))
        script.load()

    def _process_input(self, reactor):
        while True:
            try:
                command = input("> ").strip()
            except:
                self._reactor.cancel_io()
                return

            if command == "resume":
                try:
                    self._session.resume()
                except Exception as e:
                    print("Failed to resume:", e)
            else:
                print("Unknown command")

    def _on_detached(self, reason, crash):
        print("⚡ detached: reason={}, crash={}".format(reason, crash))

    def _on_message(self, message, data):
        print("⚡ message: {}".format(message))
Example #21
0
    def __init__(self):
        self._reactor = Reactor(run_until_return=self._process_input)

        self._device = None
        self._session = None
Example #22
0
class Application(object):
    def __init__(self):
        self._stop_requested = threading.Event()
        self._reactor = Reactor(
            run_until_return=lambda reactor: self._stop_requested.wait())

        #self._device = frida.get_local_device()
        self._device = frida.get_usb_device()
        self._sessions = set()

        self._device.on(
            "child-added", lambda child: self._reactor.schedule(
                lambda: self._on_child_added(child)))
        self._device.on(
            "child-removed", lambda child: self._reactor.schedule(
                lambda: self._on_child_removed(child)))
        self._device.on(
            "output", lambda pid, fd, data: self._reactor.schedule(
                lambda: self._on_output(pid, fd, data)))

    def run(self):
        self._reactor.schedule(lambda: self._start())
        self._reactor.run()

    def _start(self):
        # argv = ["/system/bin/sh", "-c", "cat /etc/hosts"]
        # env = {
        #     "BADGER": "badger-badger-badger",
        #     "SNAKE": "mushroom-mushroom",
        # }
        # # print("✔ spawn(argv={})".format(argv))
        # pid = self._device.spawn(argv, env=env, stdio='pipe')
        print("✔ spawn com.coolapk.market")
        pid = self._device.spawn(["com.coolapk.market"])
        self._instrument(pid)

    def _stop_if_idle(self):
        if len(self._sessions) == 0:
            self._stop_requested.set()

    def _instrument(self, pid):
        print("✔ attach(pid={})".format(pid))
        session = self._device.attach(pid)
        session.on(
            "detached",
            lambda reason: self._reactor.schedule(lambda: self._on_detached(
                pid, session, reason)))
        print("✔ enable_child_gating()")
        session.enable_child_gating()
        print("✔ create_script()")
        with open("hook_RegisterNatives.js") as f:
            script = session.create_script(f.read())
        script.on(
            "message", lambda message, data: self._reactor.schedule(
                lambda: self._on_message(pid, message)))
        print("✔ load()")
        script.load()
        print("✔ resume(pid={})".format(pid))
        self._device.resume(pid)
        # time.sleep(10)
        self._sessions.add(session)

    def _on_child_added(self, child):
        print("⚡ child_added: {}".format(child))
        self._instrument(child.pid)

    def _on_child_removed(self, child):
        print("⚡ child_removed: {}".format(child))

    def _on_output(self, pid, fd, data):
        print("⚡ output: pid={}, fd={}, data={}".format(pid, fd, repr(data)))

    def _on_detached(self, pid, session, reason):
        print("⚡ detached: pid={}, reason='{}'".format(pid, reason))
        self._sessions.remove(session)
        self._reactor.schedule(self._stop_if_idle, delay=0.5)

    def _on_message(self, pid, message):
        print("⚡ message: pid={}, payload={}".format(pid, message["payload"]))
Example #23
0
class Application:
    def __init__(self, nick):
        self._reactor = Reactor(run_until_return=self._process_input)

        token = {
            'nick': nick,
            'secret': "knock-knock"
        }
        self._device = frida.get_device_manager().add_remote_device("::1",
                                                                    certificate="/Users/oleavr/src/cert.pem",
                                                                    token=json.dumps(token))

        self._bus = self._device.bus
        self._bus.on('message', lambda *args: self._reactor.schedule(lambda: self._on_bus_message(*args)))

        self._channel = None
        self._prompt = "> "

    def run(self):
        self._reactor.schedule(self._start)
        self._reactor.run()

    def _start(self):
        self._bus.attach()

    def _process_input(self, reactor):
        while True:
            sys.stdout.write("\r")
            try:
                text = input(self._prompt).strip()
            except:
                self._reactor.cancel_io()
                return
            sys.stdout.write("\033[1A\033[K")
            sys.stdout.flush()

            if len(text) == 0:
                self._print("Processes:", self._device.enumerate_processes())
                continue

            if text.startswith("/join "):
                if self._channel is not None:
                    self._bus.post({
                        'type': 'part',
                        'channel': self._channel
                    })
                channel = text[6:]
                self._channel = channel
                self._prompt = "{} > ".format(channel)
                self._bus.post({
                    'type': 'join',
                    'channel': channel
                })
                continue

            if text.startswith("/announce "):
                self._bus.post({
                    'type': 'announce',
                    'text': text[10:]
                })
                continue

            if self._channel is not None:
                self._bus.post({
                    'channel': self._channel,
                    'type': 'say',
                    'text': text
                })
            else:
                self._print("*** Need to /join a channel first")

    def _on_bus_message(self, message, data):
        mtype = message['type']
        if mtype == 'welcome':
            self._print("*** Welcome! Available channels:", repr(message['channels']))
        elif mtype == 'membership':
            self._print("*** Joined", message['channel'])
            self._print("- Members:\n\t" + "\n\t".join(["{} (connected from {})".format(m['nick'], m['address']) for m in message['members']]))
            for item in message['history']:
                self._print("<{}> {}".format(item['sender'], item['text']))
        elif mtype == 'join':
            user = message['user']
            self._print("👋 {} ({}) joined {}".format(user['nick'], user['address'], message['channel']))
        elif mtype == 'part':
            user = message['user']
            self._print("🚪 {} ({}) left {}".format(user['nick'], user['address'], message['channel']))
        elif mtype == 'chat':
            self._print("<{}> {}".format(message['sender'], message['text']))
        elif mtype == 'announce':
            self._print("📣 <{}> {}".format(message['sender'], message['text']))
        else:
            self._print("Unhandled message:", message)

    def _print(self, *words):
        print("\r\033[K" + " ".join([str(word) for word in words]))
        sys.stdout.write(self._prompt)
        sys.stdout.flush()
class Application(object):
    def __init__(self):
        self._stop_requested = threading.Event()
        self._reactor = Reactor(
            run_until_return=lambda reactor: self._stop_requested.wait())

        self._device = frida.get_local_device()
        self._sessions = set()

        self._device.on(
            "child-added", lambda child: self._reactor.schedule(
                lambda: self._on_child_added(child)))
        self._device.on(
            "child-removed", lambda child: self._reactor.schedule(
                lambda: self._on_child_removed(child)))
        self._device.on(
            "output", lambda pid, fd, data: self._reactor.schedule(
                lambda: self._on_output(pid, fd, data)))

    def run(self):
        self._reactor.schedule(lambda: self._start())
        self._reactor.run()

    def _start(self):
        self._instrument(1)

    def _stop_if_idle(self):
        if len(self._sessions) == 0:
            self._stop_requested.set()

    def _instrument(self, pid):
        print("✔ attach(pid={})".format(pid))
        session = self._device.attach(pid)
        session.on(
            "detached",
            lambda reason: self._reactor.schedule(lambda: self._on_detached(
                pid, session, reason)))
        print("✔ enable_child_gating()")
        session.enable_child_gating()
        print("✔ create_script()")
        script = session.create_script(open('frida-ssl-pin.js', 'r').read())
        script.on(
            "message", lambda message, data: self._reactor.schedule(
                lambda: self._on_message(pid, message)))
        print("✔ load()")
        script.load()
        print("✔ resume(pid={})".format(pid))
        try:
            self._device.resume(pid)
        except Exception as e:
            print(e)
        self._sessions.add(session)

    def _on_child_added(self, child):
        print("⚡ child_added: {}".format(child))
        self._instrument(child.pid)

    def _on_child_removed(self, child):
        print("⚡ child_removed: {}".format(child))

    def _on_output(self, pid, fd, data):
        print("⚡ output: pid={}, fd={}, data={}".format(pid, fd, repr(data)))

    def _on_detached(self, pid, session, reason):
        print("⚡ detached: pid={}, reason='{}'".format(pid, reason))
        self._sessions.remove(session)
        self._reactor.schedule(self._stop_if_idle, delay=0.5)

    def _on_message(self, pid, message):
        print("⚡ message: pid={}, payload={}".format(pid, message["payload"]))
Example #25
0
class Application(object):
    def __init__(self):
        self._stop_requested = threading.Event()
        self._reactor = Reactor(run_until_return=lambda reactor: self._stop_requested.wait())

        self._device = frida.get_local_device()
        self._sessions = set()

        self._device.on("child-added", lambda child: self._reactor.schedule(lambda: self._on_child_added(child)))
        self._device.on("child-removed", lambda child: self._reactor.schedule(lambda: self._on_child_removed(child)))
        self._device.on("output", lambda pid, fd, data: self._reactor.schedule(lambda: self._on_output(pid, fd, data)))

    def run(self):
        self._reactor.schedule(lambda: self._start())
        self._reactor.run()

    def _start(self):
        argv = ["/bin/sh", "-c", "cat /etc/hosts"]
        env = {
            "BADGER": "badger-badger-badger",
            "SNAKE": "mushroom-mushroom",
        }
        print("✔ spawn(argv={})".format(argv))
        pid = self._device.spawn(argv, env=env, stdio='pipe')
        self._instrument(pid)

    def _stop_if_idle(self):
        if len(self._sessions) == 0:
            self._stop_requested.set()

    def _instrument(self, pid):
        print("✔ attach(pid={})".format(pid))
        session = self._device.attach(pid)
        session.on("detached", lambda reason: self._reactor.schedule(lambda: self._on_detached(pid, session, reason)))
        print("✔ enable_child_gating()")
        session.enable_child_gating()
        print("✔ create_script()")
        script = session.create_script("""\
Interceptor.attach(Module.getExportByName(null, 'open'), {
  onEnter: function (args) {
    send({
      type: 'open',
      path: Memory.readUtf8String(args[0])
    });
  }
});
""")
        script.on("message", lambda message, data: self._reactor.schedule(lambda: self._on_message(pid, message)))
        print("✔ load()")
        script.load()
        print("✔ resume(pid={})".format(pid))
        self._device.resume(pid)
        self._sessions.add(session)

    def _on_child_added(self, child):
        print("⚡ child_added: {}".format(child))
        self._instrument(child.pid)

    def _on_child_removed(self, child):
        print("⚡ child_removed: {}".format(child))

    def _on_output(self, pid, fd, data):
        print("⚡ output: pid={}, fd={}, data={}".format(pid, fd, repr(data)))

    def _on_detached(self, pid, session, reason):
        print("⚡ detached: pid={}, reason='{}'".format(pid, reason))
        self._sessions.remove(session)
        self._reactor.schedule(self._stop_if_idle, delay=0.5)

    def _on_message(self, pid, message):
        print("⚡ message: pid={}, payload={}".format(pid, message["payload"]))
Example #26
0
class FriSpyControllerApp(object):
    def __init__(self, config, logger_obj, api_watch_list):

        self.config = config
        self.logger = logger_obj
        self.api_watch_list = api_watch_list
        self.execution_timeout = self.config.get_execution_timeout()
        self.capture_basic_behavior = self.config.get_capture_behavior_report_basic_flag()
        self.capture_complete_behavior = self.config.get_capture_behavior_report_complete_flag()
        self.process_list = []
        self.target_process_pid = None

        # Initialize Reporting Module
        self.report_generator = Reporting(config, logger_obj)

        # Initialize Queue for FriSpyGUI to receive the events
        self.event_queue_name = self.config.get_event_queue_name()
        self.msmqueue_event = MSMQCustom(self.event_queue_name)

        self.config_queue_name = self.config.get_config_queue_name()
        self.msmqueue_config = MSMQCustom(self.config_queue_name)

        # Initialize Controller
        self._stop_requested = threading.Event()
        self._reactor = Reactor(run_until_return=lambda reactor: self._stop_requested.wait())

        self._device = frida.get_local_device()
        self._sessions = set()
        try:

            self._device.on("child-added", lambda child: self._reactor.schedule(lambda: self._on_child_added(child)))
            self._device.on("child-removed", lambda child: self._reactor.schedule(lambda: self._on_child_removed(child)))
            self._device.on("output", lambda pid, fd, data: self._reactor.schedule(lambda: self._on_output(pid, fd, data)))
            self._device.on("process-crashed", lambda crash: self._reactor.schedule(lambda: self._on_process_crashed(crash)))
            self._device.on("lost", lambda crash: self._reactor.schedule(lambda: self._on_process_crashed(crash)))

        except Exception as e:
            self.logger.log("error", "Exception - FriSpyController : run : %s" %(str(e)))
            self.Controller_cleaup(None)
            sys.exit(1)

    def run(self, target_process):

        # Schedule the execution of target process
        try:
            if not target_process:
                self.logger.log("error", "FriSpyController: run: FriSpy Controller failed to locate target process ")
                self.Controller_cleaup(None)
                sys.exit(1)

            self._reactor.schedule(lambda: self._start(target_process))

        except Exception as e:
            self.logger.log("error", "Exception - FriSpyController : schedule_start : %s" %(str(e)))
            self.Controller_cleaup(None)
            sys.exit(1)

        # Initiate the execution of target process
        try:            
            self._reactor.run()
        except Exception as e:
            self.logger.log("error", "Exception - FriSpyController : run : %s" %(str(e)))
            self.Controller_cleaup(None)
            sys.exit(1)

    def _start(self, target_process):

        # Spawn the target process
        try:
            pid = self._device.spawn((target_process,))
            if pid:
                self.target_process_pid = pid
                try:
                    self._instrument(pid, target_process)

                except Exception as e:
                    self.logger.log("error", "Exception -  FriSpyController : _start : Failed to instrument : %s" % (traceback.print_exc(file=sys.stdout)))
                    self.Controller_cleaup(None)
                    sys.exit(1)

            else:
                self.logger.log("error", "Error -  FriSpyController : _start : Failed to instrument")
                self.Controller_cleaup(None)
                sys.exit(1)

        except Exception as e:
            self.logger.log("error", "Exception -  FriSpyController : Failed to spawn : %s" %(str(traceback.print_exc(file=sys.stdout))))
            self.Controller_cleaup(None)
            sys.exit(1)

    def _stop_if_idle(self):
        if len(self._sessions) == 0:
            self._stop_requested.set()

    def prepare_script(self):

        try:
            self.win_lib_dir = self.config.get_win_lib_directory()
            self.common_lib_dir = self.config.get_common_lib_directory()
            self.common_def_file_name =  self.config.get_common_def_file()
            self.common_def_file_path = os.path.join(self.common_lib_dir, self.common_def_file_name)
            self.script_file_content = ""

            #Load common-lib
            common_lib_content = self.get_api_lib_implementation(self.common_def_file_path)

            if common_lib_content == None:
                self.logger.log("error", "FriSpyController: prepare_script : Failed to load common lib")
                return False

            timeout_checker = '''

    var timeout_check = function(timeout){
        var send_data = {}
        send_data.Date = Date()
        send_data.api_name = "timeout_check"
        send_data.module_name = null
        send_data.api_arguments = null
        send(JSON.stringify(send_data, null, 4));
    }
    setInterval(timeout_check, %d)
                '''
            timeout_in_milliseconds = self.config.get_execution_timeout() * 1000# converting seconds to milliseconds
            timeout_checker_content = ((timeout_checker) % timeout_in_milliseconds)
            self.script_file_content = "//////// Common Lib ////////\n\n%s\n%s\n" % (common_lib_content, timeout_checker_content)
            
            self.logger.log("info", "common-lib loaded successfully")

            appended_lib_content = ""
            for lib_info in self.api_watch_list:

                # Get API lib info
                lib_file_parent = lib_info.split("_")[0]
                lib_file_name = lib_info.split("_")[1]
                lib_file_path = os.path.join(self.win_lib_dir ,lib_file_parent, lib_file_name) + ".js"
                lib_content = self.get_api_lib_implementation(lib_file_path)
                if lib_content != None:
                    appended_lib_content = "%s\n//%s\n%s\n" % (appended_lib_content, lib_file_path, lib_content)
            if appended_lib_content == "":
                self.logger.log("error", "FriSpyController: prepare_script : Failed to prepare Script")
                return False

            self.script_file_content =  "%s\n%s\n" % (self.script_file_content, appended_lib_content)
            
            status = self.save_script()
            if not status:
                return False
        except Exception as e:
            self.logger.log("error", "Exception - FriSpyController: prepare_script : Failed to prepare Script: %s"% (str(e)))
            return False

        return True

    def save_script(self):

        self.api_injector_file = os.path.join(self.config.get_config_directory(), self.config.get_api_injector())
        self.logger.log("info", "Saving Script...")
        if len(self.script_file_content) > 0:
            try:
                with open(self.api_injector_file, "w") as filehandle:
                    filehandle.write(self.script_file_content)
                    self.logger.log("info", "Script saved successfully")# : '%s'" % (self.script_file_content))

            except Exception as e:
                self.logger.log("error", "Exception: Failed to save Script watch list : %s" % str(e))
                return False
        else:
            self.logger.log("error", "Injector Script is empty")
            return False
        return True

    def get_api_lib_implementation(self, api_lib_file_path):
        
        lib_content = None
        if os.path.exists(api_lib_file_path):
            try:
                with open (api_lib_file_path, "r") as libhandle:
                    lib_content = libhandle.read()

                    self.logger.log("info", "API Launch Successful : %s" % api_lib_file_path)
        
            except Exception as e:
                self.logger.log("warn", "Exception: Launch Failed : %s"% str(e))
        else:
            self.logger.log("warn", "Launch Failed : %s"% api_lib_file_path)
        return lib_content
            
    def _instrument(self, pid, path):
        try:
            self.logger.log("","Target process attach : pid=({})".format(pid))

            processinfo = {}
            processinfo["pid"] = pid
            processinfo["path"] = path
            self.logger.log_to_gui("ProcessCreate", "%s" %(json.dumps(processinfo)))

            self.process_list.append(pid)
            session = self._device.attach(pid)
            session.on("detached", lambda reason: self._reactor.schedule(lambda: self._on_detached(pid, session, reason)))

            session.enable_child_gating()

            scriptFile = self.script_file_content
            script = session.create_script(scriptFile)
            script.on("message", lambda message, data: self._reactor.schedule(lambda: self._on_message(pid, message)))
            self.logger.log("","Load Injector module")
            script.load()
            self.logger.log("","Resume Execution : pid=({})".format(pid))
            self.target_exec_start_time = datetime.now()
            self.logger.log("","Eventing started {} ".format(self.target_exec_start_time))
            self.target_exec_end_time = self.target_exec_start_time + timedelta(seconds=self.execution_timeout)
            self._device.resume(pid)
            self._sessions.add(session)

        except Exception as e:
            self.logger.log("error", "Exception: instrument(): %s" % (str(e)))
            self.Controller_cleaup(None)
            sys.exit(1)

    def _on_child_added(self, child):
        self.logger.log(""," child_added: {}".format(child))
        self._instrument(child.pid, child.path)

    def _on_child_removed(self, child):
        self.logger.log(""," child_removed: {}".format(child))

    def _on_output(self, pid, fd, data):
        self.logger.log(""," output: pid={}, fd={}, data={}".format(pid, fd, repr(data)))

    def _on_detached(self, pid, session, reason):
        self.logger.log(""," detached: pid={}, reason='{}'".format(pid, reason))
        self._device.kill(pid)
        self._sessions.remove(session)
        self._reactor.schedule(self._stop_if_idle, delay=0.5)
        if len(self._sessions) == 0:
            self.logger.log("","Exiting FriSpy...")
    
    def _on_process_crashed(crash):
        print("on_process_crashed")
        print("\tcrash:", crash)

    def is_ready_to_process(self, jsonobj):

        if "ObjectAttributes" in jsonobj["api_arguments"] and jsonobj["api_arguments"]["ObjectAttributes"] == "0x0":
            return False
        if jsonobj["api_name"] == "OpenProcessToken" and "ProcessHandle" in jsonobj["api_arguments"] and jsonobj["api_arguments"]["ProcessHandle"] == "0xffffffff":
            return False
        if (jsonobj["api_name"] == "CreateEventExW" or jsonobj["api_name"] == "CreateEventExA" or jsonobj["api_name"] == "CreateEventA" or jsonobj["api_name"] == "CreateEventW" or jsonobj["api_name"] == "CreateMutexExW" or jsonobj["api_name"] == "CreateMutexW" or jsonobj["api_name"] == "CreateSemaphoreExW") and "lpName" in jsonobj["api_arguments"] and jsonobj["api_arguments"]["lpName"] == "0x0":
            return False
        return True

    def _on_message(self, pid, message):

        try:
            if message["type"] == "error":
                
                # Check for  errors in loading Injector Module
                print (message)
                self.logger.log("error","Error: Injector Module: %s - %s" % ( message["description"], message["stack"]))
                self.Controller_cleaup(pid)
                sys.exit(1)

            # Get the events from CoreEngine
            jsobj = (json.loads(message["payload"])) 

            # Abort the execution if the Execution Timeout is triggered
            if "api_name" in jsobj and jsobj["api_name"] == "timeout_check":
                self.logger.log("info", "Execution timeout triggered : %s ..." % (jsobj["Date"]))
                self.logger.log_to_gui("Timeout", "Execution timeout triggered. Aborting FriSpy..")
                self.Controller_cleaup(pid)
                return
                #sys.exit(1)

            if "api_retval" in jsobj and jsobj["api_retval"] == "warn":
                self.logger.log("warn", "%s" % (jsobj["api_arguments"]))
            else:
                # Collect the events 
                self.logger.log ("",jsobj)
                jsobj["process_id"] = str(pid)
                #print(self._sessions)
                if self.is_ready_to_process(jsobj):

                    # Enrich the events from CoreEngine
                    argument_report_basic_json_obj = {}
                    argument_report_complete_json_obj = {}
                    (argument_report_basic_json_obj, argument_report_complete_json_obj) = self.report_generator.add_behavior_to_report(jsobj)
                    
                    msg_label = "Event"
                    msg_body = None
                    if self.capture_basic_behavior and argument_report_basic_json_obj:
                        msg_body = json.dumps(argument_report_basic_json_obj)
                    if self.capture_complete_behavior and argument_report_complete_json_obj:
                        msg_body = json.dumps(argument_report_complete_json_obj)
                    # Send events to FriSpyGUI
                    if msg_body:
                        self.msmqueue_event.open_queue(2, 0)#MQ_SEND_ACCESS
                        self.msmqueue_event.send_to_queue(msg_label, msg_body)
                        self.msmqueue_event.close_queue()


        except Exception as e:
            self.logger.log("error","Exception: on_message : %s : %s: %s " % (str(e), str(jsobj), str(traceback.print_exc(file=sys.stdout))))
            self.Controller_cleaup(pid)
            sys.exit(1)

    def Controller_cleaup(self, pid):

        # Terminate the processes
        print(self.process_list)
        for pid in self.process_list:
            if pid:
                try: 
                    os.kill(pid,signal.SIGTERM)
                    #self.process_list.remove(pid)
                except Exception as e:
                    self.logger.log("Error", "%s" % (str(e)))
                    #spass
                
        if self._reactor.is_running():
            self._reactor.stop()
            self._reactor.cancel_all()
        self._stop_requested.set()