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')
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)
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)
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']))
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')
def test_finds_and_compiles_android_hooks_without_an_exception_handler( self): hook = android_hook('filesystem/pwd', skip_trycatch=True) expected_output = """// Determines the current working directory, based // on the applications filesDir var ActivityThread = Java.use('android.app.ActivityThread'); var currentApplication = ActivityThread.currentApplication(); var context = currentApplication.getApplicationContext(); var response = { status: 'success', error_reason: NaN, type: 'current-working-directory', data: { cwd: context.getFilesDir().getAbsolutePath().toString() } }; send(response); // -- Sample Java // // getApplicationContext().getFilesDir().getAbsolutePath() """ self.assertEqual(hook, expected_output)
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')
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)
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)
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)
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')
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)
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')
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')
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(hook=hook) runner.run_as_job(name='pinning-disable')
def monitor(args: list = None) -> None: """ Starts a new objection job that monitors the Android clipboard and reports on new strings found. :param args: :return: """ hook = android_hook('clipboard/monitor') runner = FridaRunner(hook=hook) runner.run_as_job(name='clipboard-monitor')
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()
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()
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')
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)
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')
def test_finds_and_compiles_android_hooks_with_an_exception_handler(self): hook = android_hook('filesystem/pwd') expected_output = """if (Java.available) { try { // From Frida documentation: // "ensure that the current thread is attached to the VM and call fn" // // We also handle the exception that could happen within the callback as // it does not seem to bubble outside of it. Java.perform(function () { try { // Determines the current working directory, based // on the applications filesDir var ActivityThread = Java.use('android.app.ActivityThread'); var currentApplication = ActivityThread.currentApplication(); var context = currentApplication.getApplicationContext(); var response = { status: 'success', error_reason: NaN, type: 'current-working-directory', data: { cwd: context.getFilesDir().getAbsolutePath().toString() } }; send(response); // -- Sample Java // // getApplicationContext().getFilesDir().getAbsolutePath() } catch (err) { var response = { status: 'error', error_reason: err.message, type: 'java-perform-exception', data: {} }; send(response); } }); } catch (err) { var response = { status: 'error', error_reason: err.message, type: 'global-exception', data: {} }; send(response); } } else { var response = { status: 'error', error_reason: 'Java runtime is not available.', type: 'global-exception', data: {} }; send(response); } """ self.assertEqual(hook, expected_output)