示例#1
0
def var_class(args: list) -> None:
    search = str(search)
    if len(search) == 0:
        click.secho('Usage: android hooking search classes <name>', bold=True)
        return

    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('hooking/search-class'),
                              search=search)
    runner.run()

    response = runner.get_last_message()

    if not response.is_successful():
        click.secho('Failed to search for classes with error: {0}'.format(
            response.error_reason),
                    fg='red')
        return None

    if response.data:

        # dump the classes to screen
        for classname in response.data:
            click.secho(classname)

        click.secho('\nFound {0} classes'.format(len(response.data)),
                    bold=True)

    else:
        click.secho('No classes found')
示例#2
0
def search_method(args: list) -> None:
    """
        Search for Objective-C methods by name.

        :param args:
        :return:
    """

    if len(clean_argument_flags(args)) < 1:
        click.secho('Usage: ios hooking search methods <name>', bold=True)
        return

    search = args[0]

    runner = FridaRunner()
    runner.set_hook_with_data(ios_hook('hooking/search-method'), search=search)
    runner.run()

    response = runner.get_last_message()

    if not response.is_successful():
        click.secho('Failed to search for methods with error: {0}'.format(response.error_reason), fg='red')
        return None

    if response.data:

        # dump the methods to screen
        for method in response.data:
            click.secho(method)

        click.secho('\nFound {0} methods'.format(len(response.data)), bold=True)

    else:
        click.secho('No methods found')
示例#3
0
def show_android_class_methods(args: list = None) -> None:
    """
        Shows the methods available on an Android class.

        :param args:
        :return:
    """

    if len(clean_argument_flags(args)) <= 0:
        click.secho('Usage: android hooking list class_methods <class name>',
                    bold=True)
        return

    class_name = args[0]

    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('hooking/list-class-methods'),
                              class_name=class_name)

    runner.run()
    response = runner.get_last_message()

    if not response.is_successful():
        click.secho('Failed to list class methods with error: {0}'.format(
            response.error_reason),
                    fg='red')
        return None

    # print the enumerated classes
    for class_name in sorted(response.data):
        click.secho(class_name)

    click.secho('\nFound {0} method(s)'.format(len(response.data)), bold=True)
示例#4
0
def search_class(args: list) -> None:
    """
        Searching for Objective-C classes in the current
        application by name.

        :param args:
        :return:
    """

    if len(clean_argument_flags(args)) < 1:
        click.secho('Usage: ios hooking search classes <name>', bold=True)
        return

    search = args[0]

    runner = FridaRunner()
    runner.set_hook_with_data(ios_hook('hooking/search-class'), search=search)
    runner.run()

    response = runner.get_last_message()

    if not response.is_successful():
        click.secho('Failed to search for classes with error: {0}'.format(response.error_reason), fg='red')
        return None

    if response.data:

        # dump the classes to screen
        for classname in response.data:
            click.secho(classname)

        click.secho('\nFound {0} classes'.format(len(response.data)), bold=True)

    else:
        click.secho('No classes found')
示例#5
0
def watch_class_method(args: list) -> None:
    """
        Watches for invocations of an Android Java class method.
        All overloads are watched.

        :param args:
        :return:
    """

    if len(args) < 2:
        click.secho(
            ('Usage: android hooking watch class_method <class> <method>'
             ' (eg: com.example.test dologin)'),
            bold=True)
        return

    target_class = args[0]
    target_method = args[1]

    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('hooking/watch-method'),
                              target_class=target_class,
                              target_method=target_method)

    runner.run_as_job(name='watch-java-method')
示例#6
0
def watch_class_method(args: list) -> None:
    """
        Starts an objection jon that hooks into a specific class method
        and reports on invocations.

        :param args:
        :return:
    """

    if len(args) <= 0:
        click.secho((
            'Usage: ios hooking watch method <selector>'
            ' (eg: -[ClassName methodName:]) (optional: --include-backtrace)'),
                    bold=True)
        return

    selector = args[0]

    runner = FridaRunner()
    runner.set_hook_with_data(
        ios_hook('hooking/watch-method'),
        selector=selector,
        include_backtrace=_should_include_backtrace(args))

    runner.run_as_job(name='watch-method')
示例#7
0
def execute(args: list) -> None:
    """
        Runs a shell command on an Android device.

        :param args:
        :return:
    """

    command = ' '.join(args)

    click.secho('Running command: {0}\n'.format(command), dim=True)

    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('command/exec'), command=command)
    runner.run()

    response = runner.get_last_message()

    if not response.is_successful():
        click.secho('Failed to run command with error: {0}'.format(
            response.error_reason),
                    fg='red')
        return

    if response.stdout:
        click.secho(response.stdout, bold=True)

    if response.stderr:
        click.secho(response.stderr, bold=True, fg='red')
示例#8
0
def watch_class_method(args: list) -> None:
    """
        Watches for invocations of an Android Java class method.
        All overloads for the same method are also watched.

        Optionally, this method will dump the watched methods arguments,
        backtrace as well as return value.

        :param args:
        :return:
    """

    if len(clean_argument_flags(args)) < 2:
        click.secho(
            ('Usage: android hooking watch class_method <class> <method> '
             '(eg: com.example.test dologin) '
             '(optional: --dump-args) '
             '(optional: --dump-backtrace) '
             '(optional: --dump-return)'),
            bold=True)
        return

    target_class = args[0]
    target_method = args[1]

    runner = FridaRunner()

    runner.set_hook_with_data(android_hook('hooking/watch-method'),
                              target_class=target_class,
                              target_method=target_method,
                              dump_args=_should_dump_args(args),
                              dump_return=_should_dump_return_value(args),
                              dump_backtrace=_should_dump_backtrace(args))

    runner.run_as_job(name='watch-java-method', args=args)
示例#9
0
def watch_class_methods_var_returns(args: list) -> None:
    """
        Starts an objection jon that hooks into a specific all classes
        and methods and reports on invocations when specifi
        args and return values reached.

        :param args:
        :return:
    """

    if len(clean_argument_flags(args)) <= 0:
        click.secho(('Usage: ios hooking watch var_and_returns <classPattern> <methodPattern> <argsPattern> <returnPattern> (eg: Controller login [email protected] false) '
                     '(optional: --dump-backtrace) '
                     '(optional: --dump-args) '
                     '(optional: --dump-return)'), bold=True)
        return

    classes_Pattern = args[0]
    methods_Pattern = args[1]
    args_Pattern = args[2]
    returns_Pattern = args[3]

    runner = FridaRunner()
    runner.set_hook_with_data(ios_hook('hooking/watch-class-methods-var-returns'),
                              classes_Pattern=classes_Pattern,
                              methods_Pattern=methods_Pattern,
                              args_Pattern=args_Pattern,
                              returns_Pattern=returns_Pattern,
                              dump_backtrace=_should_dump_backtrace(args),
                              dump_args=_should_dump_args(args),
                              dump_return=_should_dump_return_value(args))

    runner.run_as_job(name='watch-class-methods-var-returns', args=args)
示例#10
0
def cat(args: list = None) -> None:
    """
        Parses a plist on an iOS device and echoes it in a more human
        readable way.

        :param args:
        :return:
    """

    if len(args) <= 0:
        click.secho('Usage: ios plist cat <remote_plist>', bold=True)
        return

    plist = args[0]

    if not os.path.isabs(plist):
        pwd = filemanager.pwd()
        plist = os.path.join(pwd, plist)

    runner = FridaRunner()
    runner.set_hook_with_data(ios_hook('plist/get'), plist=plist)
    runner.run()

    response = runner.get_last_message()

    if not response.is_successful():
        click.secho('Failed to get plist with error: {0}'.format(
            response.error_reason),
                    fg='red')
        return

    click.secho(response.data, bold=True)
示例#11
0
def search_class(args: list) -> None:
    """
        Searches the current Android application for instances
        of a class.

        :param args:
        :return:
    """

    if len(args) < 1:
        click.secho('Usage: android hooking search classes <name>', bold=True)
        return

    search = args[0]

    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('hooking/search-class'), search=search)
    runner.run()

    response = runner.get_last_message()

    if not response.is_successful():
        click.secho('Failed to search for classes with error: {0}'.format(response.error_reason), fg='red')
        return None

    if response.data:

        # dump the classes to screen
        for classname in response.data:
            click.secho(classname)

        click.secho('\nFound {0} classes'.format(len(response.data)), bold=True)

    else:
        click.secho('No classes found')
示例#12
0
def watch_class_method(args: list) -> None:
    """
        Starts an objection jon that hooks into a specific class method
        and reports on invocations.

        :param args:
        :return:
    """

    if len(clean_argument_flags(args)) <= 0:
        click.secho(('Usage: ios hooking watch method <selector> (eg: -[ClassName methodName:]) '
                     '(optional: --dump-backtrace) '
                     '(optional: --dump-args) '
                     '(optional: --dump-return)'), bold=True)
        return

    selector = args[0]
    argument_count = selector.count(':')

    runner = FridaRunner()
    runner.set_hook_with_data(ios_hook('hooking/watch-method'), selector=selector,
                              argument_count=argument_count,
                              dump_backtrace=_should_dump_backtrace(args),
                              dump_args=_should_dump_args(args),
                              dump_return=_should_dump_return_value(args))

    runner.run_as_job(name='watch-method', args=args)
示例#13
0
def set_method_return_value(args: list = None) -> None:
    """
        Sets a Java methods return value to a specified boolean.

        :param args:
        :return:
    """

    if len(clean_argument_flags(args)) < 2:
        click.secho(
            ('Usage: android hooking set return_value '
             '"<fully qualified class>" (eg: "com.example.test") '
             '"<method (with overload if needed)>" (eg: see help for details) '
             '<true/false>'),
            bold=True)
        return

    class_name = args[0]
    method_name = args[1].replace('\'', '"')  # fun!
    retval = args[2]

    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('hooking/set-return'),
                              class_name=class_name,
                              method_name=method_name,
                              retval=retval)

    runner.run_as_job(name='set-return-value', args=args)
示例#14
0
def watch_class(args: list) -> None:
    """
        Watches for invocations of all methods in an Android
        Java class. All overloads for methods found are also watched.

        :param args:
        :return:
    """

    if len(clean_argument_flags(args)) < 1:
        click.secho(
            'Usage: android hooking watch class <class> '
            '(eg: com.example.test) '
            '(optional: --dump-args) '
            '(optional: --dump-backtrace) '
            '(optional: --dump-return)',
            bold=True)
        return

    target_class = args[0]

    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('hooking/watch-class-methods'),
                              target_class=target_class,
                              dump_args=_should_dump_args(args),
                              dump_return=_should_dump_return_value(args),
                              dump_backtrace=_should_dump_backtrace(args))

    runner.run_as_job(name='watch-java-class', args=args)
示例#15
0
def add(args: list) -> None:
    """
        Adds a new keychain entry to the keychain

        :param args:
        :return:
    """

    if not _has_minimum_flags_to_add_item(args):
        click.secho(
            'Usage: ios keychain add --key <key name> --data <entry data>',
            bold=True)
        return

    key = _get_flag_value(args, '--key')
    value = _get_flag_value(args, '--data')

    click.secho('Adding a new entry to the iOS keychain...', dim=True)
    click.secho('Key:       {0}'.format(key), dim=True)
    click.secho('Value:     {0}'.format(value), dim=True)

    runner = FridaRunner()
    runner.set_hook_with_data(ios_hook('keychain/add'))

    api = runner.rpc_exports()

    if api.add(key, value):
        click.secho('Successfully added the keychain item', fg='green')
        return

    click.secho('Failed to add the keychain item', fg='red')
示例#16
0
def show_ios_class_methods(args: list) -> None:
    """
        Displays the methods available in a class.

        :param args:
        :return:
    """

    if len(args) <= 0:
        click.secho('Usage: ios hooking list class_methods <class name> (--include-parents)', bold=True)
        return

    classname = args[0]

    runner = FridaRunner()
    runner.set_hook_with_data(
        ios_hook('hooking/list-class-methods'), classname=classname,
        include_parents=_should_include_parent_methods(args))

    runner.run()
    response = runner.get_last_message()

    if not response.is_successful():
        click.secho('Failed to list classes with error: {0}'.format(response.error_reason), fg='red')
        return None

    # dump the methods to screen
    for method in response.data:
        click.secho(method)
示例#17
0
def entries(args: list = None) -> None:
    """
        Lists entries in the Android KeyStore

        :param args:
        :return:
    """

    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('keystore/list'))
    runner.run()

    response = runner.get_last_message()

    if not response.is_successful():
        click.secho('Failed to list KeyStore items with error: {0}'.format(
            response.error_reason),
                    fg='red')
        return None

    if not response.data:
        click.secho('No keystore items were found', fg='yellow')
        return None

    output = [[x['alias'], x['is_key'], x['is_certificate']]
              for x in response.data]

    click.secho(tabulate(output, headers=['Alias', 'Is Key',
                                          'Is Certificate']))
示例#18
0
def disable(args: list = None) -> None:
    """
        Performs a generic anti root detection.

        :param args:
        :return:
    """

    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('root/disable'))

    runner.run_as_job(name='root-disable')
示例#19
0
def simulate(args: list = None) -> None:
    """
        Simulate a rooted environment.

        :param args:
        :return:
    """

    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('root/simulate'))

    runner.run_as_job(name='root-simulate')
示例#20
0
def android_disable(args: list = None) -> None:
    """
        Starts a new objection job that hooks common classes and functions,
        applying new logic in an attempt to bypass SSL pinning.

        :param args:
        :return:
    """

    hook = android_hook('pinning/disable')

    runner = FridaRunner()
    runner.set_hook_with_data(hook=hook, quiet=_should_be_quiet(args))
    runner.run_as_job(name='pinning-disable')
示例#21
0
def dump_ios_method_args(args: list) -> None:
    """
        Starts an objection job that hooks into a class method and
        dumps the argument values as the method is invoked.

        :param args:
        :return:
    """

    # small helper method to reduce copy/paste code for the usage info
    def usage():
        click.secho(
            'Usage: ios hooking dump method_args <+/-> <class_name> <method_name>',
            bold=True)

    if len(args) < 3:
        usage()
        return

    class_instance = args[0]
    class_name = args[1]
    method_name = args[2]

    if class_instance not in ['-', '+']:
        click.secho(
            'Specify a class method (+) or instance method (-) with either a "+" or a "-"',
            fg='red')
        usage()
        return

    full_method = '{0}[{1} {2}]'.format(class_instance, class_name,
                                        method_name)
    argument_count = full_method.count(':')
    click.secho('Full method: {0} ({1} arguments)'.format(
        full_method, argument_count))

    # prepare a runner for the arg dump hook
    runner = FridaRunner()
    runner.set_hook_with_data(ios_hook('hooking/dump-arguments'),
                              method=full_method,
                              argument_count=argument_count)

    runner.run_as_job(name='dump-arguments')
示例#22
0
def launch_service(args: list) -> None:
    """
        Launches an exported service using an Android Intent
        
        :param args:
        :return:
        """

    if len(args) < 1:
        click.secho('Usage: android intent launch_service <service_class>',
                    bold=True)
        return

    intent_class = args[0]

    click.secho('Launching Service: {0}...'.format(intent_class), dim=True)
    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('intent/start-service'),
                              intent_class=intent_class)
    runner.run()
示例#23
0
def launch_activity(args: list) -> None:
    """
        Launces an activity class using an Android Intent

        :param args:
        :return:
    """

    if len(args) < 1:
        click.secho('Usage: android intent launch_activity <activity_class>',
                    bold=True)
        return

    intent_class = args[0]

    click.secho('Launching Activity: {0}...'.format(intent_class), dim=True)
    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('intent/start-activity'),
                              intent_class=intent_class)
    runner.run()
示例#24
0
def watch_class(args: list) -> None:
    """
        Starts an objection job that hooks into all of the methods
        available in a class and reports on invocations.

        :param args:
        :return:
    """

    if len(args) <= 0:
        click.secho('Usage: ios hooking watch class <class_name> (--include-parents)', bold=True)
        return

    class_name = args[0]

    runner = FridaRunner()
    runner.set_hook_with_data(
        ios_hook('hooking/watch-class-methods'),
        class_name=class_name, include_parents=_should_include_parent_methods(args))

    runner.run_as_job(name='watch-class-methods')
示例#25
0
def clear(args: list = None) -> None:
    """
        Clears out an Android KeyStore

        :param args:
        :return:
    """

    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('keystore/clear'))
    runner.run()

    response = runner.get_last_message()

    if not response.is_successful():
        click.secho('Failed to clear the KeyStore error: {0}'.format(
            response.error_reason),
                    fg='red')
        return None

    click.secho('Cleared the KeyStore', fg='green')
示例#26
0
def set_method_return_value(args: list) -> None:
    """
        Make an Objective-C method return a specific boolean
        value, always.

        :param args:
        :return:
    """

    if len(args) < 2:
        click.secho('Usage: ios hooking set_method_return "<selector>" (eg: "-[ClassName methodName:]") <true/false>',
                    bold=True)
        return

    selector = args[0]
    retval = args[1]

    runner = FridaRunner()
    runner.set_hook_with_data(
        ios_hook('hooking/set-return'), selector=selector, retval=_string_is_true(retval))

    runner.run_as_job(name='set-return-value')
示例#27
0
def var_class(args: list) -> None:

    if len(clean_argument_flags(args)) < 2:
        click.secho(('Usage: android hooking watch var <class> <var> '
                     '(eg: com.example.test [email protected]) '
                     '(optional: --dump-args) '
                     '(optional: --dump-backtrace) '
                     '(optional: --dump-return)'),
                    bold=True)
        return

    search_class = args[0]
    search_var = args[1]
    runner = FridaRunner()

    runner.set_hook_with_data(android_hook('hooking/var-class'),
                              search_class=search_class,
                              search_var=search_var,
                              dump_args=_should_dump_args(args),
                              dump_return=_should_dump_return_value(args),
                              dump_backtrace=_should_dump_backtrace(args))

    runner.run_as_job(name='watch-java-var', args=args)
示例#28
0
def dump_android_method_args(args: list) -> None:
    """
        Starts an objection job that hooks into a class method and
        dumps the argument values as the method is invoked.

        :param args:
        :return:
    """

    if len(args) < 2:
        click.secho('Usage: android hooking dump_args <class> <method>',
                    bold=True)
        return

    target_class = args[0]
    target_method = args[1]

    # prepare a runner for the arg dump hook
    runner = FridaRunner()
    runner.set_hook_with_data(android_hook('hooking/dump-arguments'),
                              target_class=target_class,
                              target_method=target_method)

    runner.run_as_job(name='dump-arguments')
示例#29
0
class TestFridaRunner(unittest.TestCase):
    def setUp(self):
        self.runner = FridaRunner()

        self.successful_message = {
            'payload': {
                'type': 'send',
                'status': 'success',
                'error_reason': None,
                'data': 'data for unittest'
            }
        }

        self.error_message = {
            'payload': {
                'type': 'send',
                'status': 'error',
                'error_reason': 'error_message',
                'data': 'error data for unittest'
            }
        }

        self.sample_hook = """// this is a comment

var response = {
    status: 'success',
    error_reason: NaN,
    type: 'file-readable',
    data: { path: '{{ path }}', readable: Boolean(file.canRead()) }
};

send(response);"""

    def test_init_runner_without_hook(self):
        runner = FridaRunner()

        self.assertEqual(runner.messages, [])
        self.assertIsNone(runner.script)

    def test_init_runner_with_hook(self):
        runner = FridaRunner('test')

        self.assertEqual(runner.hook, 'test')

    def test_handles_incoming_success_message_and_adds_message(self):
        self.runner._on_message(self.successful_message, None)

        self.assertEqual(len(self.runner.messages), 1)

    def test_handles_incoming_error_message_and_warns_while_adding_message(
            self):
        with capture(self.runner._on_message, self.error_message, None) as o:
            output = o

        expected_output = '[hook failure] error_message\n'

        self.assertEqual(output, expected_output)
        self.assertEqual(len(self.runner.messages), 1)

    @mock.patch('objection.utils.frida_transport.app_state.should_debug_hooks')
    def test_handles_incoming_success_message_and_prints_debug_output(
            self, should_debug_hooks):
        should_debug_hooks.return_value = True

        with capture(self.runner._on_message, self.successful_message,
                     None) as o:
            output = o

        expected_output = """- [response] ------------------
{
  "payload": {
    "data": "data for unittest",
    "error_reason": null,
    "status": "success",
    "type": "send"
  }
}
- [./response] ----------------
"""

        self.assertEqual(output, expected_output)

    def test_hook_processor_beautifies_javascript_output_from_hook_property(
            self):
        self.runner.hook = self.sample_hook

        hook = self.runner._hook_processor()

        expected_outut = """var response = {
    status: 'success',
    error_reason: NaN,
    type: 'file-readable',
    data: { path: '', readable: Boolean(file.canRead()) }
};
send(response);"""

        self.assertEqual(hook, expected_outut)

    def test_can_fetch_last_message_with_multiple_messages_received(self):
        # ignore the output we get from the error message
        with capture(self.runner._on_message, self.error_message, None) as _:
            pass
        self.runner._on_message(self.successful_message, None)

        last_message = self.runner.get_last_message()

        self.assertEqual(last_message.data,
                         self.successful_message['payload']['data'])

    def test_sets_hook_with_data(self):
        self.runner.set_hook_with_data('{{ test }}', test='testing123')

        self.assertEqual(self.runner.hook, 'testing123')