diff --git a/.pyup.yml b/.pyup.yml deleted file mode 100644 index c9cb339174..0000000000 --- a/.pyup.yml +++ /dev/null @@ -1,27 +0,0 @@ -# configure updates globally -# default: all -# allowed: all, insecure, False -update: all - -# configure dependency pinning globally -# default: True -# allowed: True, False -pin: True - -# set the default branch -# default: empty, the default branch on GitHub -branch: master - -# update schedule -# default: empty -# allowed: "every day", "every week", .. -schedule: "every week" - -# search for requirement files -# default: True -# allowed: True, False -search: True - -# allow to close stale PRs -# default: True -close_prs: True diff --git a/README.md b/README.md index c2b8b9218a..c2d4958351 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ Made with ![Love](https://cloud.githubusercontent.com/assets/4301109/16754758/82 [![Docker Pulls](https://img.shields.io/docker/pulls/opensecurity/mobile-security-framework-mobsf?style=social)](https://hub.docker.com/r/opensecurity/mobile-security-framework-mobsf/) [![MobSF tests](https://github.com/MobSF/Mobile-Security-Framework-MobSF/workflows/MobSF%20tests/badge.svg?branch=master)](https://github.com/MobSF/Mobile-Security-Framework-MobSF/actions) -[![Requirements Status](https://pyup.io/repos/github/MobSF/Mobile-Security-Framework-MobSF/shield.svg)](https://pyup.io/repos/github/MobSF/Mobile-Security-Framework-MobSF/) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=MobSF_Mobile-Security-Framework-MobSF&metric=alert_status)](https://sonarcloud.io/dashboard?id=MobSF_Mobile-Security-Framework-MobSF) ![GitHub closed issues](https://img.shields.io/github/issues-closed/MobSF/Mobile-Security-Framework-MobSF) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/6392/badge)](https://bestpractices.coreinfrastructure.org/projects/6392) diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/default/dump_clipboard.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/default/dump_clipboard.js new file mode 100644 index 0000000000..8cbd873806 --- /dev/null +++ b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/default/dump_clipboard.js @@ -0,0 +1,40 @@ +// Based on https://github.com/sensepost/objection/blob/f8e78d8a29574c6dadd2b953a63207b45a19b1cf/objection/hooks/android/clipboard/monitor.js +var ActivityThread = Java.use('android.app.ActivityThread'); +var ClipboardManager = Java.use('android.content.ClipboardManager'); +var CLIPBOARD_SERVICE = 'clipboard'; + +var currentApplication = ActivityThread.currentApplication(); +var context = currentApplication.getApplicationContext(); + +var clipboard_handle = context.getApplicationContext().getSystemService(CLIPBOARD_SERVICE); +var clipboard = Java.cast(clipboard_handle, ClipboardManager); + +// Variable used for the current string data +var string_data; + +function check_clipboard_data() { + + Java.perform(function () { + + var primary_clip = clipboard.getPrimaryClip(); + + // If we have managed to get the primary clipboard and there are + // items stored in it, process an update. + if (primary_clip != null && primary_clip.getItemCount() > 0) { + + var data = primary_clip.getItemAt(0).coerceToText(context).toString(); + + // If the data is the same, just stop. + if (string_data == data) { + return; + } + + // Update the data with the new string and report back. + string_data = data; + send('mobsf-android-clipboard:' + data); + } + }); +} + +// Poll every 5 seconds +setInterval(check_clipboard_data, 1000 * 5); \ No newline at end of file diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/default/root_bypass.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/default/root_bypass.js index b056e441a5..3ca264049f 100644 --- a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/default/root_bypass.js +++ b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/default/root_bypass.js @@ -130,12 +130,12 @@ Java.performNow(function () { } } if (shouldModifyCommand) { - send("[RootDetection Bypass] ProcessBuilder " + cmd); + send("[RootDetection Bypass] ProcessBuilder " + JSON.stringify(cmd)); this.command.call(this, ["grep"]); return this.start.call(this); } if (cmd.indexOf("su") != -1) { - send("[RootDetection Bypass] ProcessBuilder " + cmd); + send("[RootDetection Bypass] ProcessBuilder " + JSON.stringify(cmd)); this.command.call(this, ["justafakecommandthatcannotexistsusingthisshouldthowanexceptionwheneversuiscalled"]); return this.start.call(this); } diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/app-environment.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/app-environment.js new file mode 100644 index 0000000000..09d12e1d5a --- /dev/null +++ b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/app-environment.js @@ -0,0 +1,18 @@ +// Based on https://github.com/sensepost/objection/blob/f8e78d8a29574c6dadd2b953a63207b45a19b1cf/objection/hooks/android/filesystem/environment.js +var ActivityThread = Java.use('android.app.ActivityThread'); + +var currentApplication = ActivityThread.currentApplication(); +var context = currentApplication.getApplicationContext(); + +var data = { + + filesDirectory: context.getFilesDir().getAbsolutePath().toString(), + cacheDirectory: context.getCacheDir().getAbsolutePath().toString(), + externalCacheDirectory: context.getExternalCacheDir().getAbsolutePath().toString(), + codeCacheDirectory: 'getCodeCacheDir' in context ? context.getCodeCacheDir().getAbsolutePath().toString() : 'n/a', + obbDir: context.getObbDir().getAbsolutePath().toString(), + packageCodePath: context.getPackageCodePath().toString() +}; + + +send(JSON.stringify(data, null, 2)); diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/aes_key.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-aes-key.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/aes_key.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-aes-key.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-dump-keystore.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-dump-keystore.js new file mode 100644 index 0000000000..2dd7945156 --- /dev/null +++ b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-dump-keystore.js @@ -0,0 +1,42 @@ +// https://github.com/sensepost/objection/blob/f8e78d8a29574c6dadd2b953a63207b45a19b1cf/objection/hooks/android/keystore/list.js +// Dump entries in the Android Keystore, together with a flag +// indicating if its a key or a certificate. +// +// Ref: https://developer.android.com/reference/java/security/KeyStore.html + +var KeyStore = Java.use('java.security.KeyStore'); +var entries = []; + +// Prepare the AndroidKeyStore keystore provider and load it. +// Maybe at a later stage we should support adding other stores +// like from file or JKS. +var ks = KeyStore.getInstance('AndroidKeyStore'); +ks.load(null, null); + +// Get the aliases and loop through them. The aliases() method +// return an Enumeration type. +var aliases = ks.aliases(); + +while (aliases.hasMoreElements()) { + + var alias = aliases.nextElement(); + + entries.push({ + 'alias': alias.toString(), + 'is_key': ks.isKeyEntry(alias), + 'is_certificate': ks.isCertificateEntry(alias) + }) +} + + +send(JSON.stringify(entries, null, 2)); + +// - Sample Java +// +// KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); +// ks.load(null); +// Enumeration aliases = ks.aliases(); +// +// while(aliases.hasMoreElements()) { +// Log.e("E", "Aliases = " + aliases.nextElement()); +// } \ No newline at end of file diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/keyguard_credential_intent.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-keyguard-credential-intent.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/keyguard_credential_intent.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-keyguard-credential-intent.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/tracer_cipher.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-trace-cipher.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/tracer_cipher.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-trace-cipher.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/tracer_keygenparameterspec.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-trace-keygenparameterspec.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/tracer_keygenparameterspec.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-trace-keygenparameterspec.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/tracer_keystore.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-trace-keystore.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/tracer_keystore.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-trace-keystore.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/tracer_secretkeyfactory.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-trace-secretkeyfactory.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/tracer_secretkeyfactory.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/crypto-trace-secretkeyfactory.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/get_android_id.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/device-android-id.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/get_android_id.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/device-android-id.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/device-environment.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/device-environment.js new file mode 100644 index 0000000000..d3d477420f --- /dev/null +++ b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/device-environment.js @@ -0,0 +1,20 @@ +var Build = Java.use('android.os.Build'); + +var ActivityThread = Java.use('android.app.ActivityThread'); + +var currentApplication = ActivityThread.currentApplication(); +var context = currentApplication.getApplicationContext(); + +var data = { + application_name: context.getPackageName(), + model: Build.MODEL.value.toString(), + board: Build.BOARD.value.toString(), + brand: Build.BRAND.value.toString(), + device: Build.DEVICE.value.toString(), + host: Build.HOST.value.toString(), + id: Build.ID.value.toString(), + product: Build.PRODUCT.value.toString(), + user: Build.USER.value.toString(), + version: Java.androidVersion +} +send(JSON.stringify(data, null, 2)); \ No newline at end of file diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/inputstream_dump.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/dump-inputstream.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/inputstream_dump.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/dump-inputstream.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/intent_dumper.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/dump-intent.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/intent_dumper.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/dump-intent.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/bypass_method.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/helper-bypass-method.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/bypass_method.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/helper-bypass-method.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/helper.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/helper-convert.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/helper.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/helper-convert.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/jni_hook_by_address.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/helper-jni-hook-by-address.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/jni_hook_by_address.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/helper-jni-hook-by-address.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/jni_trace.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/helper-trace-jni.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/jni_trace.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/helper-trace-jni.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/hook_constructor.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/hook-constructor.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/hook_constructor.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/hook-constructor.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/hook_java_reflection.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/hook-java-reflection.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/hook_java_reflection.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/hook-java-reflection.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/file_trace.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/trace-file.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/file_trace.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/trace-file.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/bypass_flag_secure.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/ui-bypass-flag-secure.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/bypass_flag_secure.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/ui-bypass-flag-secure.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/fingerprint_bypass_via_exception_handling.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/ui-fingerprint-bypass-via-exception-handling.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/fingerprint_bypass_via_exception_handling.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/ui-fingerprint-bypass-via-exception-handling.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/fingerprint_bypass.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/ui-fingerprint-bypass.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/fingerprint_bypass.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/ui-fingerprint-bypass.js diff --git a/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/webview_enable_debugging.js b/mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/ui-webview-enable-debugging.js similarity index 100% rename from mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/webview_enable_debugging.js rename to mobsf/DynamicAnalyzer/tools/frida_scripts/android/others/ui-webview-enable-debugging.js diff --git a/mobsf/DynamicAnalyzer/tools/onDevice/mobsf_agents/ClipDump.apk b/mobsf/DynamicAnalyzer/tools/onDevice/xposed/ClipDump.apk similarity index 100% rename from mobsf/DynamicAnalyzer/tools/onDevice/mobsf_agents/ClipDump.apk rename to mobsf/DynamicAnalyzer/tools/onDevice/xposed/ClipDump.apk diff --git a/mobsf/DynamicAnalyzer/views/android/analysis.py b/mobsf/DynamicAnalyzer/views/android/analysis.py index 727b43b594..7b10630034 100644 --- a/mobsf/DynamicAnalyzer/views/android/analysis.py +++ b/mobsf/DynamicAnalyzer/views/android/analysis.py @@ -28,15 +28,21 @@ def run_analysis(apk_dir, md5_hash, package): clipboard = [] # Collect Log data data = get_log_data(apk_dir, package) - clip_tag = 'I/CLIPDUMP-INFO-LOG' - clip_tag2 = 'I CLIPDUMP-INFO-LOG' - # Collect Clipboard - for log_line in data['logcat']: - if clip_tag in log_line: - clipboard.append(log_line.replace(clip_tag, 'Process ID ')) - if clip_tag2 in log_line: - log_line = log_line.split(clip_tag2)[1] - clipboard.append(log_line) + clip = Path(apk_dir) / 'mobsf_app_clipboard.txt' + if clip.exists(): + clipboard = clip.read_text('utf-8', 'ignore').split('\n') + else: + # For Xposed + clip_tag = 'I/CLIPDUMP-INFO-LOG' + clip_tag2 = 'I CLIPDUMP-INFO-LOG' + # Collect Clipboard + for log_line in data['logcat']: + if clip_tag in log_line: + clipboard.append( + log_line.replace(clip_tag, 'Process ID ')) + if clip_tag2 in log_line: + log_line = log_line.split(clip_tag2)[1] + clipboard.append(log_line) urls, domains, emails = extract_urls_domains_emails( data['traffic'].lower()) # Tar dump and fetch files diff --git a/mobsf/DynamicAnalyzer/views/android/dynamic_analyzer.py b/mobsf/DynamicAnalyzer/views/android/dynamic_analyzer.py index d80a75298c..d753cbe05e 100755 --- a/mobsf/DynamicAnalyzer/views/android/dynamic_analyzer.py +++ b/mobsf/DynamicAnalyzer/views/android/dynamic_analyzer.py @@ -165,6 +165,8 @@ def dynamic_analyzer(request, checksum, api=False): 'Failed to MobSFy the instance', api) if version < 5: + # Start Clipboard monitor + env.start_clipmon() xposed_first_run = True if xposed_first_run: msg = ('Have you MobSFyed the instance before' @@ -182,10 +184,6 @@ def dynamic_analyzer(request, checksum, api=False): env.enable_adb_reverse_tcp(version) # Apply Global Proxy to device env.set_global_proxy(version) - # Start Clipboard monitor - env.start_clipmon() - # Get Screen Resolution - screen_width, screen_height = env.get_screen_res() if install == '1': # Install APK apk_path = Path(settings.UPLD_DIR) / checksum / f'{checksum}.apk' @@ -203,9 +201,7 @@ def dynamic_analyzer(request, checksum, api=False): msg, api) logger.info('Testing Environment is Ready!') - context = {'screen_width': screen_width, - 'screen_height': screen_height, - 'package': package, + context = {'package': package, 'hash': checksum, 'android_version': version, 'version': settings.MOBSF_VER, diff --git a/mobsf/DynamicAnalyzer/views/android/environment.py b/mobsf/DynamicAnalyzer/views/android/environment.py index 1840a3ca13..c7fcd3e7bf 100644 --- a/mobsf/DynamicAnalyzer/views/android/environment.py +++ b/mobsf/DynamicAnalyzer/views/android/environment.py @@ -2,12 +2,12 @@ """Dynamic Analyzer Helpers.""" import logging import os -import re import shutil import subprocess import tempfile import threading import time +from base64 import b64encode from hashlib import md5 from django.conf import settings @@ -83,7 +83,7 @@ def connect_n_mount(self): if not self.identifier: return False self.adb_command(['kill-server']) - self.adb_command(['start-server']) + self.adb_command(['start-server'], False, True) logger.info('ADB Restarted') self.wait(2) logger.info('Connecting to Android %s', self.identifier) @@ -305,47 +305,25 @@ def start_clipmon(self): 'opensecurity.clipdump/.ClipDumper'] self.adb_command(args, True) - def get_screen_res(self): - """Get Screen Resolution of Android Instance.""" - logger.info('Getting screen resolution') - try: - resp = self.adb_command(['dumpsys', 'window'], True) - scn_rgx = re.compile(r'mUnrestrictedScreen=\(0,0\) .*') - scn_rgx2 = re.compile(r'mUnrestricted=\[0,0\]\[.*\]') - match = scn_rgx.search(resp.decode('utf-8')) - if match: - screen_res = match.group().split(' ')[1] - width, height = screen_res.split('x', 1) - return width, height - match = scn_rgx2.search(resp.decode('utf-8')) - if match: - res = match.group().split('][')[1].replace(']', '') - width, height = res.split(',', 1) - return width, height - else: - logger.error('Error getting screen resolution') - except Exception: - logger.exception('Getting screen resolution') - return '1440', '2560' - def screen_shot(self, outfile): """Take Screenshot.""" self.adb_command(['screencap', '-p', - '/data/local/screen.png'], True) + '/data/local/screen.png'], True, True) self.adb_command(['pull', '/data/local/screen.png', - outfile]) + outfile], False, True) def screen_stream(self): """Screen Stream.""" self.adb_command(['screencap', '-p', '/data/local/stream.png'], - True) - self.adb_command(['pull', - '/data/local/stream.png', - '{}screen.png'.format(settings.SCREEN_DIR)]) + True, True) + out = self.adb_command(['cat', '/data/local/stream.png'], True, True) + if out: + return b64encode(out).decode('utf-8') + return '' def android_component(self, bin_hash, comp): """Get APK Components.""" @@ -371,18 +349,20 @@ def android_component(self, bin_hash, comp): def get_environment(self): """Identify the environment.""" out = self.adb_command(['getprop', - 'ro.boot.serialno'], True) + 'ro.boot.serialno'], True, False) out += self.adb_command(['getprop', - 'ro.serialno'], True) + 'ro.serialno'], True, False) out += self.adb_command(['getprop', - 'ro.build.user'], True) + 'ro.build.user'], True, False) out += self.adb_command(['getprop', - 'ro.manufacturer.geny-def'], True) + 'ro.manufacturer.geny-def'], + True, False) out += self.adb_command(['getprop', - 'ro.product.manufacturer.geny-def'], True) + 'ro.product.manufacturer.geny-def'], + True, False) ver = self.adb_command(['getprop', 'ro.genymotion.version'], - True).decode('utf-8', 'ignore') + True, False).decode('utf-8', 'ignore') if b'EMULATOR' in out: logger.info('Found Android Studio Emulator') return 'emulator' @@ -403,7 +383,8 @@ def get_environment(self): def get_android_version(self): """Get Android version.""" out = self.adb_command(['getprop', - 'ro.build.version.release'], True) + 'ro.build.version.release'], + True, False) and_version = out.decode('utf-8').rstrip() if and_version.count('.') > 1: and_version = and_version.rsplit('.', 1)[0] @@ -581,13 +562,6 @@ def mobsf_agents_setup(self, agent): create_ca() # Install MITM RootCA self.install_mobsf_ca('install') - # Install MobSF Agents - mobsf_agents = 'onDevice/mobsf_agents/' - clip_dump = os.path.join(self.tools_dir, - mobsf_agents, - 'ClipDump.apk') - logger.info('Installing MobSF Clipboard Dumper') - self.adb_command(['install', '-r', clip_dump]) if agent == 'frida': agent_file = '.mobsf-f' agent_str = self.frida_str @@ -604,6 +578,12 @@ def xposed_setup(self, android_version): """Setup Xposed.""" xposed_dir = 'onDevice/xposed/' xposed_modules = xposed_dir + 'modules/' + # Install MobSF Agents for Xposed + clip_dump_apk = os.path.join(self.tools_dir, + xposed_dir, + 'ClipDump.apk') + logger.info('Installing MobSF Clipboard Dumper') + self.adb_command(['install', '-r', clip_dump_apk]) if android_version < 5: logger.info('Installing Xposed for Kitkat and below') xposed_apk = os.path.join(self.tools_dir, diff --git a/mobsf/DynamicAnalyzer/views/android/frida_core.py b/mobsf/DynamicAnalyzer/views/android/frida_core.py index 1161144af0..0e4e442267 100644 --- a/mobsf/DynamicAnalyzer/views/android/frida_core.py +++ b/mobsf/DynamicAnalyzer/views/android/frida_core.py @@ -25,6 +25,7 @@ ) logger = logging.getLogger(__name__) +_FPID = None class Frida: @@ -42,6 +43,7 @@ def __init__(self, app_hash, package, defaults, auxiliary, extras, code): self.api_mon = os.path.join(self.apk_dir, 'mobsf_api_monitor.txt') self.frida_log = os.path.join(self.apk_dir, 'mobsf_frida_out.txt') self.deps = os.path.join(self.apk_dir, 'mobsf_app_deps.txt') + self.clipboard = os.path.join(self.apk_dir, 'mobsf_app_clipboard.txt') def get_default_scripts(self): """Get default Frida Scripts.""" @@ -101,10 +103,14 @@ def frida_response(self, message, data): api_mon = 'MobSF-API-Monitor: ' aux = '[AUXILIARY] ' deps = '[RUNTIME-DEPS] ' + clip = 'mobsf-android-clipboard:' if not isinstance(msg, str): msg = str(msg) if msg.startswith(api_mon): self.write_log(self.api_mon, msg.replace(api_mon, '')) + elif msg.startswith(clip): + msg = msg.replace(clip, '') + self.write_log(self.clipboard, f'{msg}\n') elif msg.startswith(deps): info = msg.replace(deps, '') + '\n' self.write_log(self.deps, info) @@ -118,22 +124,26 @@ def frida_response(self, message, data): else: logger.error('[Frida] %s', message) - def connect(self): - """Connect to Frida Server.""" - session = None - device = None + def spawn(self): + """Frida Spawn.""" + global _FPID try: env = Environment() self.clean_up() env.run_frida_server() - device = frida.get_device(get_device(), settings.FRIDA_TIMEOUT) - pid = device.spawn([self.package]) + device = frida.get_device( + get_device(), + settings.FRIDA_TIMEOUT) logger.info('Spawning %s', self.package) - session = device.attach(pid) - time.sleep(2) + _FPID = device.spawn([self.package]) + device.resume(_FPID) + time.sleep(1) + except frida.NotSupportedError: + logger.exception('Not Supported Error') + return except frida.ServerNotRunningError: logger.warning('Frida server is not running') - self.connect() + self.spawn() except frida.TimedOutError: logger.error('Timed out while waiting for device to appear') except (frida.ProcessNotFoundError, @@ -142,15 +152,37 @@ def connect(self): pass except Exception: logger.exception('Error Connecting to Frida') + + def session(self, pid, package): + """Use existing session to inject frida scripts.""" + global _FPID try: - if session: + try: + device = frida.get_device( + get_device(), + settings.FRIDA_TIMEOUT) + if pid and package: + _FPID = pid + self.package = package + session = device.attach(_FPID) + time.sleep(2) + except frida.NotSupportedError: + logger.exception('Not Supported Error') + return + except Exception: + logger.warning('Cannot attach to pid, spawning again') + self.spawn() + session = device.attach(_FPID) + time.sleep(2) + if session and device and _FPID: script = session.create_script(self.get_script()) script.on('message', self.frida_response) script.load() - device.resume(pid) sys.stdin.read() script.unload() session.detach() + except frida.NotSupportedError: + logger.exception('Not Supported Error') except (frida.ProcessNotFoundError, frida.TransportError, frida.InvalidOperationError): @@ -158,11 +190,33 @@ def connect(self): except Exception: logger.exception('Error Connecting to Frida') + def ps(self): + """Get running process pid.""" + ps_dict = [] + try: + device = frida.get_device( + get_device(), + settings.FRIDA_TIMEOUT) + processes = device.enumerate_applications(scope='minimal') + if device and processes: + for process in processes: + if process.pid != 0: + ps_dict.append({ + 'pid': process.pid, + 'name': process.name, + 'identifier': process.identifier, + }) + except Exception: + logger.exception('Failed to enumerate running applications') + return ps_dict + def clean_up(self): if is_file_exists(self.api_mon): os.remove(self.api_mon) if is_file_exists(self.frida_log): os.remove(self.frida_log) + if is_file_exists(self.clipboard): + os.remove(self.clipboard) def write_log(self, file_path, data): with io.open( diff --git a/mobsf/DynamicAnalyzer/views/android/operations.py b/mobsf/DynamicAnalyzer/views/android/operations.py index 2dde3739a4..4d263327f8 100644 --- a/mobsf/DynamicAnalyzer/views/android/operations.py +++ b/mobsf/DynamicAnalyzer/views/android/operations.py @@ -4,6 +4,7 @@ import logging import os import random +import shlex import subprocess import threading from pathlib import Path @@ -188,43 +189,74 @@ def take_screenshot(request, api=False): @require_http_methods(['POST']) def screen_cast(request): """ScreenCast.""" - data = {} + data = { + 'status': 'failed', + 'message': 'Failed to stream screen'} try: env = Environment() - trd = threading.Thread(target=env.screen_stream) - trd.daemon = True - trd.start() - data = {'status': 'ok'} + b64dat = env.screen_stream() + data = { + 'status': 'ok', + 'message': f'data:image/png;base64,{b64dat}'} except Exception as exp: logger.exception('Screen streaming') - data = {'status': 'failed', 'message': str(exp)} + data['message'] = str(exp) return send_response(data) # AJAX @require_http_methods(['POST']) def touch(request): - """Sending Touch Events.""" - data = {} + """Sending Touch/Swipe/Text Events.""" + data = { + 'status': 'failed', + 'message': '', + } try: env = Environment() - x_axis = request.POST['x'] - y_axis = request.POST['y'] - if not is_number(x_axis) and not is_number(y_axis): - logger.error('Axis parameters must be numbers') - return invalid_params() - args = ['input', - 'tap', - x_axis, - y_axis] - trd = threading.Thread(target=env.adb_command, - args=(args, True)) - trd.daemon = True - trd.start() + x = request.POST['x'] + y = request.POST['y'] + event = request.POST['event'] + max_x = request.POST.get('max_x', 0) + max_y = request.POST.get('max_y', 0) + + if event == 'text': + args = ['text', shlex.quote(x)] + else: + if (not is_number(x) + or not is_number(y) + or not is_number(max_x) + or not is_number(max_y)): + return + # Should not be greater than max screen size + swipe_x = str(min(int(float(x)) + 500, int(float(max_x)))) + swipe_y = str(min(int(float(y)) + 500, int(float(max_y)))) + + if event == 'enter': + args = ['keyevent', '66'] + elif event == 'backspace': + args = ['keyevent', '67'] + elif event == 'left': + args = ['keyevent', '21'] + elif event == 'right': + args = ['keyevent', '22'] + elif event == 'swipe_up': + args = ['swipe', x, y, x, swipe_y] + elif event == 'swipe_down': + args = ['swipe', x, swipe_y, x, y] + elif event == 'swipe_left': + args = ['swipe', x, y, swipe_x, y] + elif event == 'swipe_right': + args = ['swipe', swipe_x, y, x, y] + else: + args = ['tap', x, y] + threading.Thread(target=env.adb_command, + args=(['input'] + args, True), + daemon=True).start() data = {'status': 'ok'} except Exception as exp: - logger.exception('Sending Touch Events') - data = {'status': 'failed', 'message': str(exp)} + logger.exception('Sending Touchscreen Events') + data['message'] = str(exp) return send_response(data) # AJAX diff --git a/mobsf/DynamicAnalyzer/views/android/tests_frida.py b/mobsf/DynamicAnalyzer/views/android/tests_frida.py index b4cf8f4a1a..e45649079f 100644 --- a/mobsf/DynamicAnalyzer/views/android/tests_frida.py +++ b/mobsf/DynamicAnalyzer/views/android/tests_frida.py @@ -5,7 +5,7 @@ import re import json from pathlib import Path -import threading +from threading import Thread import logging from django.shortcuts import render @@ -25,6 +25,7 @@ is_file_exists, is_md5, print_n_send_error_response, + strict_package_check, ) logger = logging.getLogger(__name__) @@ -58,9 +59,13 @@ def get_runtime_dependencies(request, api=False): @require_http_methods(['POST']) def instrument(request, api=False): """Instrument app with frida.""" - data = {} + data = { + 'status': 'failed', + 'message': 'Failed to instrument app'} try: - logger.info('Starting Instrumentation') + action = request.POST.get('frida_action', 'spawn') + pid = request.POST.get('pid') + new_pkg = request.POST.get('new_package') md5_hash = request.POST['hash'] default_hooks = request.POST['default_hooks'] auxiliary_hooks = request.POST['auxiliary_hooks'] @@ -76,12 +81,14 @@ def instrument(request, api=False): cls_trace = request.POST.get('class_trace') if cls_trace: extras['class_trace'] = cls_trace.strip() + if (is_attack_pattern(default_hooks) or is_attack_pattern(auxiliary_hooks) - or not is_md5(md5_hash)): + or not is_md5(md5_hash) + or (new_pkg and not strict_package_check(new_pkg))): return invalid_params(api) package = get_package_name(md5_hash) - if not package: + if not package and not new_pkg: return invalid_params(api) frida_obj = Frida(md5_hash, package, @@ -89,10 +96,24 @@ def instrument(request, api=False): auxiliary_hooks.split(','), extras, code) - trd = threading.Thread(target=frida_obj.connect) - trd.daemon = True - trd.start() - data = {'status': 'ok'} + if action == 'spawn': + logger.info('Starting Instrumentation') + frida_obj.spawn() + elif action == 'ps': + logger.info('Enumerating running applications') + data['message'] = frida_obj.ps() + if action in ('spawn', 'session'): + if pid and pid.isdigit(): + # Attach to a different pid/bundle id + args = (int(pid), new_pkg) + logger.info('Attaching to %s [PID: %s]', new_pkg, pid) + else: + # Injecting to existing session/spawn + if action == 'session': + logger.info('Injecting to existing frida session') + args = (None, None) + Thread(target=frida_obj.session, args=args, daemon=True).start() + data['status'] = 'ok' except Exception as exp: logger.exception('Instrumentation failed') data = {'status': 'failed', 'message': str(exp)} @@ -271,9 +292,8 @@ def get_dependencies(package, checksum): location = Path(frd.deps) if location.exists(): location.write_text('') - trd = threading.Thread(target=frd.connect) - trd.daemon = True - trd.start() + frd.spawn() + Thread(target=frd.session, args=(None, None), daemon=True).start() def dependency_analysis(package, app_dir): diff --git a/mobsf/DynamicAnalyzer/views/android/tests_tls.py b/mobsf/DynamicAnalyzer/views/android/tests_tls.py index 73081ad3c8..e3669f880a 100644 --- a/mobsf/DynamicAnalyzer/views/android/tests_tls.py +++ b/mobsf/DynamicAnalyzer/views/android/tests_tls.py @@ -2,7 +2,7 @@ """Security tests on data in transit.""" import re import logging -import threading +from threading import Thread from json import dump from pathlib import Path @@ -90,9 +90,8 @@ def run_tls_tests(request, md5_hash, env, package, test_pkg, duration): None, None, ) - trd = threading.Thread(target=frd.connect) - trd.daemon = True - trd.start() + frd.spawn() + Thread(target=frd.session, args=(None, None), daemon=True).start() env.wait(duration) stop_httptools(get_http_tools_url(request)) traffic = get_traffic(test_pkg) diff --git a/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py b/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py index 6cb30eeb5d..d1a8a869a8 100644 --- a/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py +++ b/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py @@ -9,6 +9,9 @@ import requests +from mobsf.MobSF.utils import is_number + + SUCCESS_RESP = (200, 204) ERROR_RESP = (400, 403, 404, 409) OK = 'ok' @@ -322,74 +325,79 @@ def get_ssh_connection_string(self): 'Failed to get SSH connection string %s', r.json()['error']) return r.json()['error'] - def device_input(self, event, x, y): + def device_input(self, event, x, y, max_x, max_y): """Provide touch/button event to VM.""" - if event == 'home': - data = [{ - 'buttons': ['holdButton'], - }] - elif event == 'text': + if event == 'text': data = [{ 'text': x, }] - elif event == 'enter': - data = [{ - 'buttons': ['enter'], - }] - elif event == 'backspace': - data = [{ - 'buttons': ['backspace'], - }] - elif event == 'left': - data = [{ - 'buttons': ['left'], - }] - elif event == 'right': - data = [{ - 'buttons': ['right'], - }] - elif event == 'swipe_up': - data = [{ - 'startButtons': ['finger'], - 'start': [[300, 600]], - 'bezierPoints': [[[350, 700]], [[375, 850]]], - 'end': [[400, 950]], - 'endButtons': [], - 'duration': 200, - }] - elif event == 'swipe_down': - data = [{ - 'startButtons': ['finger'], - 'start': [[300, 600]], - 'bezierPoints': [[[700, 350]], [[850, 375]]], - 'end': [[950, 400]], - 'endButtons': [], - 'duration': 200, - }] - elif event == 'swipe_left': - data = [{ - 'startButtons': ['finger'], - 'start': [[200, 200]], - 'bezierPoints': [[[700, 350]], [[850, 375]]], - 'end': [[950, 400]], - 'endButtons': [], - 'duration': 200, - }] - elif event == 'swipe_right': - data = [{ - 'startButtons': ['finger'], - 'start': [[700, 100]], - 'bezierPoints': [[[350, 750]], [[375, 875]]], - 'end': [[300, 600]], - 'endButtons': [], - 'duration': 200, - }] else: - data = [ - {'buttons': ['finger'], - 'position': [[x, y]], - 'wait': 0}, - {'buttons': [], 'wait': 100}] + if (not is_number(x) + or not is_number(y) + or not is_number(max_x) + or not is_number(max_y)): + return + # Should not be greater than max screen size + swipe_x = min(int(x) + 200, int(max_x)) + swipe_y = min(int(y) + 400, int(max_y)) + if event == 'home': + data = [{ + 'buttons': ['holdButton'], + }] + elif event == 'enter': + data = [{ + 'buttons': ['enter'], + }] + elif event == 'backspace': + data = [{ + 'buttons': ['backspace'], + }] + elif event == 'left': + data = [{ + 'buttons': ['left'], + }] + elif event == 'right': + data = [{ + 'buttons': ['right'], + }] + elif event == 'swipe_up': + data = [{ + 'startButtons': ['finger'], + 'start': [[x, 600]], + 'end': [[400, y]], + 'duration': 200, + }] + elif event == 'swipe_down': + data = [{ + 'startButtons': ['finger'], + 'start': [[x, swipe_y]], + 'end': [[swipe_x, y]], + 'duration': 200, + }] + elif event == 'swipe_left': + data = [{ + 'startButtons': ['finger'], + 'start': [[100, 900]], + 'bezierPoints': [[[700, 350]], [[850, 375]]], + 'end': [[900, 100]], + 'duration': 200, + }] + elif event == 'swipe_right': + data = [{ + 'startButtons': ['finger'], + 'start': [[700, 100]], + 'bezierPoints': [[[350, 750]], [[375, 875]]], + 'end': [[100, 700]], + 'duration': 200, + }] + else: + if not is_number(x) or not is_number(y): + return + data = [ + {'buttons': ['finger'], + 'position': [[x, y]], + 'wait': 0}, + {'buttons': [], 'wait': 100}] r = requests.post( f'{self.api}/instances/{self.instance_id}/input', headers=self.headers, diff --git a/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py b/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py index 254f87fe5a..89f4277306 100644 --- a/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py +++ b/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py @@ -23,7 +23,6 @@ get_md5, id_generator, is_md5, - is_number, print_n_send_error_response, strict_package_check, ) @@ -33,7 +32,6 @@ stop_httptools, ) from mobsf.DynamicAnalyzer.views.common.shared import ( - invalid_params, send_response, ) from mobsf.DynamicAnalyzer.views.ios.corellium_ssh import ( @@ -715,7 +713,7 @@ def download_data(request, bundle_id, api=False): @require_http_methods(['POST']) def touch(request, api=False): - """Sending Touch Events.""" + """Sending Touch/Swipe/Text Events.""" data = { 'status': 'failed', 'message': '', @@ -724,18 +722,18 @@ def touch(request, api=False): x_axis = request.POST['x'] y_axis = request.POST['y'] event = request.POST['event'] + + max_x = request.POST.get('max_x', 0) + max_y = request.POST.get('max_y', 0) instance_id = request.POST['instance_id'] - if not is_number(x_axis) and not is_number(y_axis): - logger.error('Axis parameters must be numbers') - return invalid_params() failed = common_check(instance_id) if failed: return send_response(failed, api) ci = CorelliumInstanceAPI(instance_id) - ci.device_input(event, x_axis, y_axis) + ci.device_input(event, x_axis, y_axis, max_x, max_y) data = {'status': 'ok'} except Exception as exp: - logger.exception('Sending Touch Events') + logger.exception('Sending Touchscreen Events') data['message'] = str(exp) return send_response(data) # AJAX + HTML diff --git a/mobsf/DynamicAnalyzer/views/ios/frida_core.py b/mobsf/DynamicAnalyzer/views/ios/frida_core.py index a08b438f3b..025b15f305 100644 --- a/mobsf/DynamicAnalyzer/views/ios/frida_core.py +++ b/mobsf/DynamicAnalyzer/views/ios/frida_core.py @@ -38,8 +38,7 @@ def __init__( dump, auxiliary, extras, - code, - action): + code): self.ssh_connection_string = ssh_string self.app_container = None self.hash = app_hash @@ -49,7 +48,6 @@ def __init__( self.auxiliary = auxiliary self.extras = extras self.code = code - self.action = action self.frida_dir = Path(settings.TOOLS_DIR) / 'frida_scripts' / 'ios' self.ipa_dir = Path(settings.UPLD_DIR) / self.hash self.frida_log = self.ipa_dir / 'mobsf_frida_out.txt' @@ -157,8 +155,8 @@ def spawn(self): """Connect to Frida Server and spawn the app.""" global _PID try: - self.clean_up() try: + self.clean_up() _PID = frida.get_remote_device().spawn([self.bundle_id]) except frida.NotSupportedError: logger.exception('Not Supported Error') diff --git a/mobsf/DynamicAnalyzer/views/ios/tests_frida.py b/mobsf/DynamicAnalyzer/views/ios/tests_frida.py index 17bd0d9cd9..e0fd70a465 100644 --- a/mobsf/DynamicAnalyzer/views/ios/tests_frida.py +++ b/mobsf/DynamicAnalyzer/views/ios/tests_frida.py @@ -85,8 +85,7 @@ def ios_instrument(request, api=False): dump_hooks.split(','), auxiliary_hooks.split(','), extras, - code, - action) + code) if action == 'spawn': logger.info('Starting Instrumentation') frida_obj.spawn() @@ -107,5 +106,5 @@ def ios_instrument(request, api=False): data['status'] = OK except Exception as exp: logger.exception('Frida Instrumentation failed') - data = {'status': 'failed', 'message': str(exp)} + data['message'] = str(exp) return send_response(data, api) diff --git a/mobsf/MobSF/init.py b/mobsf/MobSF/init.py index fe780491c0..842c188fc4 100644 --- a/mobsf/MobSF/init.py +++ b/mobsf/MobSF/init.py @@ -10,7 +10,7 @@ logger = logging.getLogger(__name__) -VERSION = '3.8.0' +VERSION = '3.8.1' BANNER = """ __ __ _ ____ _____ _____ ___ | \/ | ___ | |__/ ___|| ___|_ _|___ / ( _ ) diff --git a/mobsf/MobSF/urls.py b/mobsf/MobSF/urls.py index 7db868bc84..da6c2593af 100755 --- a/mobsf/MobSF/urls.py +++ b/mobsf/MobSF/urls.py @@ -169,8 +169,12 @@ re_path(r'^mobsfy/$', operations.mobsfy, name='mobsfy'), re_path(r'^screenshot/$', operations.take_screenshot), re_path(r'^execute_adb/$', operations.execute_adb), - re_path(r'^screen_cast/$', operations.screen_cast), - re_path(r'^touch_events/$', operations.touch), + re_path(r'^screen_cast/$', + operations.screen_cast, + name='screen_cast'), + re_path(r'^touch_events/$', + operations.touch, + name='android_touch'), re_path(r'^get_component/$', operations.get_component), re_path(r'^mobsf_ca/$', operations.mobsf_ca), re_path(r'^global_proxy/$', operations.global_proxy), @@ -183,7 +187,9 @@ re_path(r'^collect_logs/$', tests_common.collect_logs), re_path(r'^tls_tests/$', tests_common.tls_tests), # Frida - re_path(r'^frida_instrument/$', tests_frida.instrument), + re_path(r'^frida_instrument/$', + tests_frida.instrument, + name='android_instrument'), re_path(r'^live_api/$', tests_frida.live_api, name='live_api'), diff --git a/mobsf/MobSF/utils.py b/mobsf/MobSF/utils.py index 190ceb42a3..7e104470b8 100755 --- a/mobsf/MobSF/utils.py +++ b/mobsf/MobSF/utils.py @@ -228,6 +228,8 @@ def find_between(s, first, last): def is_number(s): + if s == 'NaN': + return False try: float(s) return True diff --git a/mobsf/templates/dynamic_analysis/android/dynamic_analysis.html b/mobsf/templates/dynamic_analysis/android/dynamic_analysis.html index 624b97f85e..db3524b810 100644 --- a/mobsf/templates/dynamic_analysis/android/dynamic_analysis.html +++ b/mobsf/templates/dynamic_analysis/android/dynamic_analysis.html @@ -140,6 +140,43 @@

MobSF Dynamic Analysis

+

Apps in Device

+ + + + + + + + + + {% if device_packages %} + {% for hash, det in device_packages.items %} + + + + + + {% endfor %} + {% endif %} + +
APPLOCATION IN DEVICEACTION
+ +
{{ det.0 }} +
+ {{ det.1}} + +

+ Start Dynamic Analysis +

+

+ Pull & Static Analysis +

+

+ View Report +

+
+

Apps Available

@@ -169,7 +206,7 @@

Apps Available

Start Dynamic Analysis

- Start Dynamic Analysis (without Re-install) + Start Dynamic Analysis (No reinstall)

View Report @@ -180,43 +217,8 @@

Apps Available

{% endif %}
-
-

Apps in Device

- - - - - - - - - - {% if device_packages %} - {% for hash, det in device_packages.items %} - - - - - - {% endfor %} - {% endif %} - -
APPLOCATION IN DEVICEACTION
- -
{{ det.0 }} -
- {{ det.1}} - -

- Start Dynamic Analysis -

-

- Pull & Static Analysis -

-

- View Report -

-
+ +
diff --git a/mobsf/templates/dynamic_analysis/android/dynamic_analyzer.html b/mobsf/templates/dynamic_analysis/android/dynamic_analyzer.html index 773aec4534..124ca2b1ca 100644 --- a/mobsf/templates/dynamic_analysis/android/dynamic_analyzer.html +++ b/mobsf/templates/dynamic_analysis/android/dynamic_analyzer.html @@ -69,8 +69,6 @@

Dynamic Analyzer - {{ package }}

- -
@@ -118,11 +116,25 @@

Dynamic Analyzer - {{ package }}

-
+
+ +
+

+

+ +
+ +
+
+

+
+ @@ -141,9 +153,6 @@

Dynamic Analyzer - {{ package }}

- @@ -186,6 +195,10 @@

Default

Debugger Check Bypass +
@@ -225,12 +238,12 @@

Auxiliary

- + {% if activities or exported_activities %} @@ -255,12 +268,6 @@

Auxiliary

-
- -

Data refreshed in every 3 seconds.
-

-                  
-
@@ -336,6 +343,24 @@

Frida Code Editor

+ + +
+
+ +
+
+ +

Data refreshed in every 3 seconds.
+

+                  
+
+ +
+ +
+ + @@ -344,6 +369,39 @@

Frida Code Editor

+ + + + + + - - @@ -433,6 +485,7 @@ +