Beispiel #1
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']))
Beispiel #2
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)
Beispiel #3
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')
Beispiel #4
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')
Beispiel #5
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')
Beispiel #6
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')
Beispiel #7
0
def show_registered_activities(args: list = None) -> None:
    """
        Enumerate all registered Activities

        :param args:
        :return:
    """

    hook = android_hook('hooking/list-activities')
    runner = FridaRunner(hook=hook)
    runner.run()

    response = runner.get_last_message()

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

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

    for class_name in sorted(response.data):
        click.secho(class_name)

    click.secho('\nFound {0} classes'.format(len(response.data)), bold=True)
Beispiel #8
0
def show_android_classes(args: list = None) -> None:
    """
        Show the currently loaded classes.

        :param args:
        :return:
    """

    hook = android_hook('hooking/list-classes')
    runner = FridaRunner(hook=hook)
    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

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

    click.secho('\nFound {0} classes'.format(len(response.data)), bold=True)
Beispiel #9
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')
Beispiel #10
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)
Beispiel #11
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)
Beispiel #12
0
def dump(args: list = None) -> None:
    """
        Dump the iOS keychain

        :param args:
        :return:
    """

    if _should_output_json(args) and len(args) < 2:
        click.secho('Usage: ios keychain dump (--json <local destination>)', bold=True)
        return

    click.secho('Note: You may be asked to authenticate using the devices passcode or TouchID')

    if not _should_output_json(args):
        click.secho('Get all of the attributes by adding `--json keychain.json` to this command', dim=True)

    click.secho('Reading the iOS keychain...', dim=True)
    hook = ios_hook('keychain/dump')
    runner = FridaRunner(hook=hook)
    runner.run()

    response = runner.get_last_message()

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

    if _should_output_json(args):
        destination = args[1]

        click.secho('Writing full keychain as json to {0}...'.format(destination), dim=True)

        with open(destination, 'w') as f:
            f.write(json.dumps(response.data, indent=2))

        click.secho('Dumped full keychain to: {0}'.format(destination), fg='green')
        return

    # refer to hooks/ios/keychain/dump.js for a key,value reference

    data = []

    if response.data:
        for entry in response.data:
            data.append([entry['item_class'], entry['account'], entry['service'], entry['generic'], entry['data'], ])

        click.secho('')
        click.secho(tabulate(data, headers=['Class', 'Account', 'Service', 'Generic', 'Data']))

    else:
        click.secho('No keychain data could be found', fg='yellow')
Beispiel #13
0
def get(args: list) -> None:
    """
        Gets cookies using the iOS NSHTTPCookieStorage sharedHTTPCookieStorage
        and prints them to the screen.

        :param args:
        :return:
    """

    hook = ios_hook('binarycookie/get')

    runner = FridaRunner(hook=hook)
    runner.run()

    response = runner.get_last_message()

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

    if not response.data:
        click.secho('No cookies found')
        return

    if _should_dump_json(args):
        print(json.dumps(response.data, indent=4))
        return

    data = []

    for cookie in response.data:
        data.append([
            cookie['name'], cookie['value'], cookie['expiresDate'],
            cookie['domain'], cookie['path'], cookie['isSecure'],
            cookie['isHTTPOnly']
        ])

    click.secho(tabulate(data,
                         headers=[
                             'Name', 'Value', 'Expires', 'Domain', 'Path',
                             'Secure', 'HTTPOnly'
                         ]),
                bold=True)
Beispiel #14
0
def _get_ios_classes() -> list:
    """
        Gets a list of all of the classes available in the current
        Objective-C runtime.

        :return:
    """

    hook = ios_hook('hooking/list-classes')
    runner = FridaRunner(hook=hook)
    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

    return response.data
Beispiel #15
0
def clear(args: list = None) -> None:
    """
        Clear the iOS keychain.

        :param args:
        :return:
    """

    click.secho('Clearing the keychain...', dim=True)
    hook = ios_hook('keychain/clear')
    runner = FridaRunner(hook=hook)
    runner.run()

    response = runner.get_last_message()

    if not response.is_successful():
        click.secho('Failed to clear keychain items with error: {0}'.format(response.error_message), fg='red')
        return

    click.secho('Keychain cleared', fg='green')
Beispiel #16
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')
Beispiel #17
0
def get(args: list = None) -> None:
    """
        Gets all of the values stored in NSUserDefaults and prints
        them to screen.

        :param args:
        :return:
    """

    hook = ios_hook('nsuserdefaults/get')

    runner = FridaRunner(hook=hook)
    runner.run()

    response = runner.get_last_message()

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

    click.secho(response.data, bold=True)
Beispiel #18
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')