[HOTFIX] RPC hook suggestions + Bug Fix (#2301)

* String compare script improvements
* Fix iOS Frida script bugs
* Added RPC helpers for hook suggestion (TODO:Expose to UI)
* Code QA
This commit is contained in:
Ajin Abraham
2023-12-06 18:14:45 -08:00
committed by GitHub
parent b668ee89c0
commit d3b02143fe
24 changed files with 440 additions and 356 deletions
@@ -1,15 +1,37 @@
//https://github.com/iddoeldor/frida-snippets#reveal-native-methods
//String comparison
Java.perform(function () {
send('[AUXILIARY] [String Compare] capturing all string comparisons')
var str = Java.use('java.lang.String'), objectClass = 'java.lang.Object';
str.equals.overload(objectClass).implementation = function (obj) {
var response = str.equals.overload(objectClass).call(this, obj);
if (obj) {
if (obj.toString().length > 5) {
send('[AUXILIARY] [String Compare] ' + str.toString.call(this) + ' == ' + obj.toString() + ' ? ' + response);
}
let Exception = Java.use('java.lang.Exception');
let javaString = Java.use('java.lang.String')
let objectClass = 'java.lang.Object';
var skiplist = ['android.app.SystemServiceRegistry.getSystemService']
javaString.equals.overload(objectClass).implementation = function (obj) {
var response = javaString.equals.overload(objectClass).call(this, obj);
if (obj && obj.toString().length > 5) {
var stack = [];
var calledFrom = Exception.$new().getStackTrace().toString().split(',');
// Otherwise capture string comparisons
let i = 0;
do {
i = i + 1;
stack.push(calledFrom[i]);
} while (i <= 5);
var skipClass, skipMethod = false;
skiplist.forEach(function (toSkip) {
if (calledFrom[4].includes(toSkip))
skipClass = true;
});
if (!skipClass) {
var data = {
caller: stack,
string1: javaString.toString.call(this),
string2: obj.toString(),
return: response,
}
send('[AUXILIARY] [String Compare] ' + JSON.stringify(data, null, 2));
}
}
return response;
}
});
});
@@ -1,40 +1,42 @@
// 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 ActivityThread = Java.use('android.app.ActivityThread');
var ClipboardManager = Java.use('android.content.ClipboardManager');
var CLIPBOARD_SERVICE = 'clipboard';
var primary_clip = clipboard.getPrimaryClip();
var currentApplication = ActivityThread.currentApplication();
var context = currentApplication.getApplicationContext();
// 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 clipboard_handle = context.getApplicationContext().getSystemService(CLIPBOARD_SERVICE);
var clipboard = Java.cast(clipboard_handle, ClipboardManager);
var data = primary_clip.getItemAt(0).coerceToText(context).toString();
setInterval(function(){
// If the data is the same, just stop.
if (string_data == data) {
return;
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);
}
// Update the data with the new string and report back.
string_data = data;
send('mobsf-android-clipboard:' + data);
}
// Poll every 5 seconds
}, 1000 * 5);
});
}
// Poll every 5 seconds
setInterval(check_clipboard_data, 1000 * 5);
check_clipboard_data();
@@ -132,38 +132,46 @@ Java.perform(function() {
console.error(e);
}
})
Interceptor.attach(Module.findExportByName(null, "exit"), {
onEnter: function(args) {
console.warn("Native Exit() Called :-->:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n") + "\n");
},
onLeave: function(retval) {}
});
Interceptor.attach(Module.findExportByName(null, "abort"), {
onEnter: function(args) {
console.warn("Native Abort() Called :-->:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n") + "\n");
},
onLeave: function(retval) {}
});
var fork = Module.findExportByName(null, "fork")
Interceptor.attach(fork, {
onEnter: function(args) {},
onLeave: function(retval) {
var pid = parseInt(retval.toString(16), 16)
console.log("Second Process PID : ", pid)
}
})
Interceptor.attach(Module.findExportByName("libc.so", "system"), {
onEnter: function(args) {
var cmd = Memory.readCString(args[0]);
if (cmd.indexOf("kill") != -1) {
console.log("Bypass native system: " + cmd);
var NewKill = args[0].writeUtf8String("bypassed");
args[0] = ptr(NewKill);
try {
Interceptor.attach(Module.findExportByName(null, "exit"), {
onEnter: function(args) {
console.warn("Native Exit() Called :-->:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n") + "\n");
},
onLeave: function(retval) {}
});
} catch (e) {}
try {
Interceptor.attach(Module.findExportByName(null, "abort"), {
onEnter: function(args) {
console.warn("Native Abort() Called :-->:\n" + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join("\n") + "\n");
},
onLeave: function(retval) {}
});
} catch (e) {}
try {
var fork = Module.findExportByName(null, "fork")
Interceptor.attach(fork, {
onEnter: function(args) {},
onLeave: function(retval) {
var pid = parseInt(retval.toString(16), 16)
console.log("Second Process PID : ", pid)
}
},
onLeave: function(retval) {}
});
})
} catch (e) {}
try {
Interceptor.attach(Module.findExportByName("libc.so", "system"), {
onEnter: function(args) {
var cmd = Memory.readCString(args[0]);
if (cmd.indexOf("kill") != -1) {
console.log("Bypass native system: " + cmd);
var NewKill = args[0].writeUtf8String("bypassed");
args[0] = ptr(NewKill);
}
},
onLeave: function(retval) {}
});
} catch (e) {}
try {
var abortPtr = Module.getExportByName('libc.so', 'abort');
var abort = new NativeFunction(abortPtr, 'int', ['int']);
var exitPtr = Module.getExportByName('libc.so', 'exit');
@@ -200,4 +208,4 @@ Interceptor.attach(Module.findExportByName("libc.so", "system"), {
console.log('Shutdown Replaced');
return 0;
}, 'int', ['int', 'int']));
} catch (e) {}
@@ -1,90 +1,89 @@
var paths = [
"/Applications/blackra1n.app",
"/Applications/Cydia.app",
"/Applications/FakeCarrier.app",
"/Applications/Icy.app",
"/Applications/IntelliScreen.app",
"/Applications/MxTube.app",
"/Applications/RockApp.app",
"/Applications/SBSetttings.app",
"/Applications/WinterBoard.app",
"/bin/bash",
"/bin/sh",
"/bin/su",
"/etc/apt",
"/etc/ssh/sshd_config",
"/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
"/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/pguntether",
"/private/var/lib/cydia",
"/private/var/mobile/Library/SBSettings/Themes",
"/private/var/stash",
"/private/var/tmp/cydia.log",
"/System/Library/LaunchDaemons/com.ikey.bbot.plist",
"/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
"/usr/bin/cycript",
"/usr/bin/ssh",
"/usr/bin/sshd",
"/usr/libexec/sftp-server",
"/usr/libexec/ssh-keysign",
"/usr/sbin/frida-server",
"/usr/sbin/sshd",
"/var/cache/apt",
"/var/lib/cydia",
"/var/log/syslog",
"/var/mobile/Media/.evasi0n7_installed",
"/var/tmp/cydia.log",
"/etc/apt",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/Applications/Cydia.app",
"/Applications/blackra1n.app",
"/Applications/FakeCarrier.app",
"/Applications/Icy.app",
"/Applications/IntelliScreen.app",
"/Applications/MxTube.app",
"/Applications/RockApp.app",
"/Applications/SBSetttings.app",
"/private/var/lib/apt/",
"/Applications/WinterBoard.app",
"/usr/sbin/sshd",
"/private/var/tmp/cydia.log",
"/usr/binsshd",
"/usr/libexec/sftp-server",
"/Systetem/Library/LaunchDaemons/com.ikey.bbot.plist",
"/System/Library/LaunchDaemons/[email protected]",
"/var/log/syslog",
"/bin/bash",
"/bin/sh",
"/etc/ssh/sshd_config",
"/usr/libexec/ssh-keysign",
"/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
"/System/Library/LaunchDaemons/com.ikey.bbot.plist",
"/private/var/stash",
"/usr/bin/cycript",
"/usr/bin/ssh",
"/usr/bin/sshd",
"/var/cache/apt",
"/var/lib/cydia",
"/var/tmp/cydia.log",
"/Applications/SBSettings.app",
"/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
"/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
"/private/var/lib/apt",
"/private/var/lib/cydia",
"/private/var/mobile/Library/SBSettings/Themes",
"/var/lib/apt",
"/private/jailbreak.txt",
"/bin/su",
"/pguntether",
"/usr/sbin/frida-server",
"/private/Jailbreaktest.txt",
"/var/mobile/Media/.evasi0n7_installed",
"cydia://package/com.example.package"
];
function bypassJailbreakDetection(){
var paths = [
"/Applications/blackra1n.app",
"/Applications/Cydia.app",
"/Applications/FakeCarrier.app",
"/Applications/Icy.app",
"/Applications/IntelliScreen.app",
"/Applications/MxTube.app",
"/Applications/RockApp.app",
"/Applications/SBSetttings.app",
"/Applications/WinterBoard.app",
"/bin/bash",
"/bin/sh",
"/bin/su",
"/etc/apt",
"/etc/ssh/sshd_config",
"/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
"/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/pguntether",
"/private/var/lib/cydia",
"/private/var/mobile/Library/SBSettings/Themes",
"/private/var/stash",
"/private/var/tmp/cydia.log",
"/System/Library/LaunchDaemons/com.ikey.bbot.plist",
"/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
"/usr/bin/cycript",
"/usr/bin/ssh",
"/usr/bin/sshd",
"/usr/libexec/sftp-server",
"/usr/libexec/ssh-keysign",
"/usr/sbin/frida-server",
"/usr/sbin/sshd",
"/var/cache/apt",
"/var/lib/cydia",
"/var/log/syslog",
"/var/mobile/Media/.evasi0n7_installed",
"/var/tmp/cydia.log",
"/etc/apt",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/Applications/Cydia.app",
"/Applications/blackra1n.app",
"/Applications/FakeCarrier.app",
"/Applications/Icy.app",
"/Applications/IntelliScreen.app",
"/Applications/MxTube.app",
"/Applications/RockApp.app",
"/Applications/SBSetttings.app",
"/private/var/lib/apt/",
"/Applications/WinterBoard.app",
"/usr/sbin/sshd",
"/private/var/tmp/cydia.log",
"/usr/binsshd",
"/usr/libexec/sftp-server",
"/Systetem/Library/LaunchDaemons/com.ikey.bbot.plist",
"/System/Library/LaunchDaemons/[email protected]",
"/var/log/syslog",
"/bin/bash",
"/bin/sh",
"/etc/ssh/sshd_config",
"/usr/libexec/ssh-keysign",
"/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
"/System/Library/LaunchDaemons/com.ikey.bbot.plist",
"/private/var/stash",
"/usr/bin/cycript",
"/usr/bin/ssh",
"/usr/bin/sshd",
"/var/cache/apt",
"/var/lib/cydia",
"/var/tmp/cydia.log",
"/Applications/SBSettings.app",
"/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
"/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
"/private/var/lib/apt",
"/private/var/lib/cydia",
"/private/var/mobile/Library/SBSettings/Themes",
"/var/lib/apt",
"/private/jailbreak.txt",
"/bin/su",
"/pguntether",
"/usr/sbin/frida-server",
"/private/Jailbreaktest.txt",
"/var/mobile/Media/.evasi0n7_installed",
"cydia://package/com.example.package"
];
try {
var f = Module.findExportByName("libSystem.B.dylib", "stat64");
@@ -196,20 +195,21 @@ function bypassJailbreakDetection2() {
},
onComplete: function() {}
});
send("[Jailbreak Detection Bypass 2] success");
send("[Jailbreak Detection Bypass] success");
}
catch(e) {
send('[Jailbreak Detection Bypass 2] script error:' + e.toString());
send('[Jailbreak Detection Bypass] script error:' + e.toString());
}
}
try {
if (ObjC.available) {
send('Bypassing Jailbreak detection checks');
bypassJailbreakDetection();
// Disable the below if the app is crashing
setTimeout(() => {
bypassJailbreakDetection2();
}, "1000");
}, 1000);
} else {
send('[Jailbreak Detection Bypass] error: Objective-C Runtime is not available!');
}
@@ -61,7 +61,7 @@ send('Dumping Application Directory file information');
try {
setTimeout(() => {
send(JSON.stringify({'[MBSFDUMP] datadir': getDataProtectionKeysForAllPaths()}));
}, "2000");
}, 2000);
} catch(err) {}
// /********************************************************************************
@@ -26,8 +26,13 @@ function NSLogv(){
}
try {
NSlog();
setTimeout(() => {
NSlog();
}, 1000);
} catch(err) {}
try {
NSLogv();
setTimeout(() => {
NSLogv();
}, 1000);
} catch(err) {}
@@ -30,6 +30,6 @@ function ns_userdefaults() {
try{
setTimeout(() => {
ns_userdefaults();
}, "2000");
}, 2000);
} catch(err) {}
@@ -1,6 +1,3 @@
import io
import os
import glob
import logging
from pathlib import Path
import sys
@@ -21,7 +18,6 @@ from mobsf.DynamicAnalyzer.views.android.frida_scripts import (
)
from mobsf.MobSF.utils import (
get_device,
is_file_exists,
)
logger = logging.getLogger(__name__)
@@ -37,28 +33,26 @@ class Frida:
self.auxiliary = auxiliary
self.extras = extras
self.code = code
self.frida_dir = os.path.join(settings.TOOLS_DIR,
'frida_scripts', 'android')
self.apk_dir = os.path.join(settings.UPLD_DIR, self.hash + '/')
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')
self.frida_dir = Path(settings.TOOLS_DIR) / 'frida_scripts' / 'android'
self.apk_dir = Path(settings.UPLD_DIR) / self.hash
self.api_mon = self.apk_dir / 'mobsf_api_monitor.txt'
self.frida_log = self.apk_dir / 'mobsf_frida_out.txt'
self.deps = self.apk_dir / 'mobsf_app_deps.txt'
self.clipboard = self.apk_dir / 'mobsf_app_clipboard.txt'
def get_default_scripts(self):
"""Get default Frida Scripts."""
def get_scripts(self, script_type, selected_scripts):
"""Get Frida Scripts."""
combined_script = []
header = []
if not self.defaults:
if not selected_scripts:
return header
def_scripts = os.path.join(self.frida_dir, 'default')
files = glob.glob(def_scripts + '**/*.js', recursive=True)
for item in files:
script = Path(item)
if script.stem in self.defaults:
header.append('send("Loaded Frida Script - {}");'.format(
script.stem))
combined_script.append(script.read_text())
all_scripts = self.frida_dir / script_type
for script in all_scripts.rglob('*.js'):
if '*' in selected_scripts:
combined_script.append(script.read_text('utf-8', 'ignore'))
if script.stem in selected_scripts:
header.append(f'send("Loaded Frida Script - {script.stem}");')
combined_script.append(script.read_text('utf-8', 'ignore'))
return header + combined_script
def get_auxiliary(self):
@@ -88,12 +82,16 @@ class Frida:
"""Get final script."""
if not self.code:
self.code = ''
rpc_list = []
# Load custom code first
scripts = [self.code]
scripts.extend(self.get_default_scripts())
scripts.extend(self.get_scripts('default', self.defaults))
rpc_list.extend(self.get_scripts('rpc', ['*']))
scripts.extend(self.get_auxiliary())
rpc_script = ','.join(rpc_list)
rpc = f'rpc.exports = {{ \n{rpc_script}\n }};'
combined = '\n'.join(scripts)
final = f'setTimeout(function() {{ \n{combined}\n}}, 1000)'
final = f'{rpc}\n setTimeout(function() {{ \n{combined}\n }}, 1000)'
return final
def frida_response(self, message, data):
@@ -112,15 +110,15 @@ class Frida:
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)
self.write_log(self.frida_log, info)
info = msg.replace(deps, '')
self.write_log(self.deps, f'{info}\n')
self.write_log(self.frida_log, f'{info}\n')
elif msg.startswith(aux):
self.write_log(self.frida_log,
msg.replace(aux, '[*] ') + '\n')
msg = msg.replace(aux, '[*] ')
self.write_log(self.frida_log, f'{msg}\n')
else:
logger.debug('[Frida] %s', msg)
self.write_log(self.frida_log, msg + '\n')
self.write_log(self.frida_log, f'{msg}\n')
else:
logger.error('[Frida] %s', message)
@@ -165,13 +163,23 @@ class Frida:
if pid and package:
_FPID = pid
self.package = package
try:
front = device.get_frontmost_application()
if front and front.pid != _FPID:
# Not the front most app.
# Get the pid of the front most app
logger.warning('Front most app has PID %s', front.pid)
_FPID = front.pid
except Exception:
pass
# pid is the fornt most app
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')
logger.exception('Cannot attach to pid, spawning again')
self.spawn()
session = device.attach(_FPID)
time.sleep(2)
@@ -179,6 +187,8 @@ class Frida:
script = session.create_script(self.get_script())
script.on('message', self.frida_response)
script.load()
api = script.exports_sync
self.api_handler(api)
sys.stdin.read()
script.unload()
session.detach()
@@ -212,18 +222,57 @@ class Frida:
logger.exception('Failed to enumerate running applications')
return ps_dict
def api_handler(self, api):
"""Call Frida rpc functions."""
loaded_classes = []
loaded_class_methods = []
implementations = []
try:
raction = self.extras.get('rclass_action')
rclass = self.extras.get('rclass_name')
rclass_pattern = self.extras.get('rclass_pattern')
rmethod = self.extras.get('rmethod_name')
rmethod_pattern = self.extras.get('rmethod_pattern')
if raction == 'raction':
loaded_classes = api.getLoadedClasses()
elif raction == 'getclasses' and rclass_pattern:
loaded_classes = api.getLoadedClasses(f'/{rclass_pattern}/i')
elif raction == 'getmethods' and rclass and rmethod:
loaded_class_methods = api.getMethods(rclass)
elif raction == 'getmethods' and rclass and rmethod_pattern:
loaded_class_methods = api.getMethods(
rclass,
f'/{rmethod_pattern}/i')
elif raction == 'getimplementations' and rclass and rmethod:
implementations = api.getImplementations(rclass, rmethod)
except Exception:
logger.exception('Error while calling Frida RPC functions')
if loaded_classes:
rpc_classes = self.apk_dir / 'mobsf_rpc_classes.txt'
loaded_classes = sorted(loaded_classes)
rpc_classes.write_text('\n'.join(
loaded_classes), 'utf-8')
if loaded_class_methods:
rpc_methods = self.apk_dir / 'mobsf_rpc_methods.txt'
loaded_class_methods = sorted(loaded_class_methods)
rpc_methods.write_text('\n'.join(
loaded_class_methods), 'utf-8')
if implementations:
implementations = sorted(implementations)
rpc_impl = self.apk_dir / 'mobsf_rpc_impl.txt'
rpc_impl.write_text('\n'.join(
implementations), 'utf-8')
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)
if self.api_mon.exists():
self.api_mon.unlink()
if self.frida_log.exists():
self.frida_log.unlink()
if self.clipboard.exists():
self.clipboard.unlink()
def write_log(self, file_path, data):
with io.open(
file_path,
'a',
encoding='utf-8',
errors='replace') as flip:
with file_path.open('a',
encoding='utf-8',
errors='replace') as flip:
flip.write(data)
@@ -163,44 +163,6 @@ def live_api(request, api=False):
return print_n_send_error_response(request, err, api)
def frida_logs(request, api=False):
try:
if api:
apphash = request.POST['hash']
stream = True
else:
apphash = request.GET.get('hash', '')
stream = request.GET.get('stream', '')
if not is_md5(apphash):
return invalid_params(api)
if stream:
apk_dir = os.path.join(settings.UPLD_DIR, apphash + '/')
frida_logs = os.path.join(apk_dir, 'mobsf_frida_out.txt')
data = {}
if not is_file_exists(frida_logs):
data = {
'status': 'failed',
'message': 'Data does not exist.'}
return send_response(data, api)
with open(frida_logs, 'r',
encoding='utf8',
errors='ignore') as flip:
data = {'data': flip.read()}
return send_response(data, api)
logger.info('Frida Logs live streaming')
template = 'dynamic_analysis/android/frida_logs.html'
return render(request,
template,
{'hash': apphash,
'package': request.GET.get('package', ''),
'version': settings.MOBSF_VER,
'title': 'Live Frida logs'})
except Exception:
logger.exception('Frida log streaming')
err = 'Error in Frida log streaming'
return print_n_send_error_response(request, err, api)
def decode_base64(data, altchars=b'+/'):
"""Decode base64, padding being optional.
@@ -1,9 +1,11 @@
"""Shared Frida Views."""
import logging
import glob
import os
from pathlib import Path
from django.conf import settings
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
from mobsf.DynamicAnalyzer.views.common.shared import (
@@ -11,8 +13,15 @@ from mobsf.DynamicAnalyzer.views.common.shared import (
)
from mobsf.MobSF.utils import (
is_file_exists,
is_md5,
is_safe_path,
print_n_send_error_response,
)
logger = logging.getLogger(__name__)
# AJAX
@@ -65,3 +74,45 @@ def get_script(request, api=False):
except Exception:
pass
return send_response(data, api)
# AJAX + HTML
def frida_logs(request, api=False):
try:
data = {
'status': 'failed',
'message': 'Data does not exist.'}
if api:
apphash = request.POST['hash']
stream = True
else:
apphash = request.GET.get('hash', '')
stream = request.GET.get('stream', '')
if not is_md5(apphash):
data['message'] = 'Invalid hash'
return send_response(data, api)
if stream:
apk_dir = os.path.join(settings.UPLD_DIR, apphash + '/')
frida_logs = os.path.join(apk_dir, 'mobsf_frida_out.txt')
if not is_file_exists(frida_logs):
return send_response(data, api)
with open(frida_logs, 'r',
encoding='utf8',
errors='ignore') as flip:
data = {
'status': 'ok',
'message': flip.read(),
}
return send_response(data, api)
logger.info('Frida Logs live streaming')
template = 'dynamic_analysis/android/frida_logs.html'
return render(request,
template,
{'hash': apphash,
'package': request.GET.get('package', ''),
'version': settings.MOBSF_VER,
'title': 'Live Frida logs'})
except Exception:
logger.exception('Frida log streaming')
err = 'Error in Frida log streaming'
return print_n_send_error_response(request, err, api)
@@ -262,13 +262,20 @@ class CorelliumInstanceAPI:
def screenshot(self):
"""Take screenshot inside VM."""
r = requests.get(
f'{self.api}/instances/{self.instance_id}/screenshot.png?scale=1',
headers=self.headers,
stream=True)
if r.status_code == 200:
return r.content
logger.error('Failed to take a screenshot. %s', r.json()['error'])
r = None
err = ''
try:
r = requests.get(
(f'{self.api}/instances/{self.instance_id}'
'/screenshot.png?scale=1'),
headers=self.headers,
stream=True)
if r.status_code == 200:
return r.content
except Exception:
if r and r.json().get('error'):
err = r.json()['error']
logger.error('Failed to take a screenshot. %s', err)
return False
def start_network_capture(self):
@@ -719,9 +719,9 @@ def touch(request, api=False):
'message': '',
}
try:
x_axis = request.POST['x']
y_axis = request.POST['y']
event = request.POST['event']
x_axis = request.POST.get('x')
y_axis = request.POST.get('y')
event = request.POST.get('event')
max_x = request.POST.get('max_x', 0)
max_y = request.POST.get('max_y', 0)
+60 -10
View File
@@ -168,12 +168,15 @@ class Frida:
return
except frida.ServerNotRunningError:
self.frida_ssh_forward()
_PID = None
if not _PID:
_PID = frida.get_remote_device().spawn([self.bundle_id])
logger.info('Spawning %s', self.bundle_id)
time.sleep(2)
except frida.TimedOutError:
logger.error('Timed out while waiting for device to appear')
except frida.ServerNotRunningError:
logger.error('Frida Server is not running')
except frida.NotSupportedError:
logger.error(self.not_supported_text)
return
@@ -194,19 +197,21 @@ class Frida:
if pid and bundle_id:
_PID = pid
self.bundle_id = bundle_id
front = device.get_frontmost_application()
if not front or front.pid != _PID:
# No front most app, spawn the app or
# pid is not the front most app
_PID = device.spawn([self.bundle_id])
logger.info('Spawning %s', self.bundle_id)
# pid is the fornt most app
try:
front = device.get_frontmost_application()
if front or front.pid != _PID:
# Not the front most app.
# Get the pid of the front most app
logger.warning('Front most app has PID %s', front.pid)
_PID = front.pid
except Exception:
pass
session = device.attach(_PID)
except frida.NotSupportedError:
logger.error(self.not_supported_text)
return
except Exception:
logger.warning('Cannot attach to pid, spawning again')
logger.warning('Cannot attach to pid, spawning again.')
self.spawn()
session = device.attach(_PID)
if session and device and _PID:
@@ -215,8 +220,7 @@ class Frida:
script.load()
api = script.exports_sync
device.resume(_PID)
self.app_container = api.get_container()
self.container_file.write_text(self.app_container)
self.api_handler(api)
sys.stdin.read()
script.unload()
session.detach()
@@ -253,6 +257,52 @@ class Frida:
logger.exception('Failed to enumerate running applications')
return ps_dict
def api_handler(self, api):
"""Call Frida rpc functions."""
loaded_classes = []
loaded_class_methods = []
implementations = []
try:
try:
self.app_container = api.get_container()
self.container_file.write_text(self.app_container)
except frida.InvalidOperationError:
pass
raction = self.extras.get('rclass_action')
rclass = self.extras.get('rclass_name')
rclass_pattern = self.extras.get('rclass_pattern')
rmethod = self.extras.get('rmethod_name')
rmethod_pattern = self.extras.get('rmethod_pattern')
if raction == 'raction':
loaded_classes = api.getLoadedClasses()
elif raction == 'getclasses' and rclass_pattern:
loaded_classes = api.getLoadedClasses(f'/{rclass_pattern}/i')
elif raction == 'getmethods' and rclass and rmethod:
loaded_class_methods = api.getMethods(rclass)
elif raction == 'getmethods' and rclass and rmethod_pattern:
loaded_class_methods = api.getMethods(
rclass,
f'/{rmethod_pattern}/i')
elif raction == 'getimplementations' and rclass and rmethod:
implementations = api.getImplementations(rclass, rmethod)
except Exception:
logger.exception('Error while calling Frida RPC functions')
if loaded_classes:
loaded_classes = sorted(loaded_classes)
rpc_classes = self.ipa_dir / 'mobsf_rpc_classes.txt'
rpc_classes.write_text('\n'.join(
loaded_classes), 'utf-8')
if loaded_class_methods:
loaded_class_methods = sorted(loaded_class_methods)
rpc_methods = self.ipa_dir / 'mobsf_rpc_methods.txt'
rpc_methods.write_text('\n'.join(
loaded_class_methods), 'utf-8')
if implementations:
implementations = sorted(implementations)
rpc_impl = self.ipa_dir / 'mobsf_rpc_impl.txt'
rpc_impl.write_text('\n'.join(
implementations), 'utf-8')
def clean_up(self):
if self.frida_log.exists():
self.frida_log.unlink()
+1 -1
View File
@@ -10,7 +10,7 @@ from mobsf.install.windows.setup import windows_config_local
logger = logging.getLogger(__name__)
VERSION = '3.8.4'
VERSION = '3.8.5'
BANNER = """
__ __ _ ____ _____ _____ ___
| \/ | ___ | |__/ ___|| ___|_ _|___ / ( _ )
+1 -1
View File
@@ -194,7 +194,7 @@ if settings.API_ONLY == '0':
tests_frida.live_api,
name='live_api'),
re_path(r'^frida_logs/$',
tests_frida.frida_logs,
frida.frida_logs,
name='frida_logs'),
re_path(r'^get_dependencies/$', tests_frida.get_runtime_dependencies),
# Report
+2
View File
@@ -228,6 +228,8 @@ def find_between(s, first, last):
def is_number(s):
if not s:
return False
if s == 'NaN':
return False
try:
@@ -222,7 +222,7 @@ def api_frida_logs(request):
if 'hash' not in request.POST:
return make_api_response(
{'error': 'Missing Parameters'}, 422)
resp = tests_frida.frida_logs(request, True)
resp = frida.frida_logs(request, True)
# frida logs can be json or html
if resp.get('data'):
return make_api_response(resp, 200)
-4
View File
@@ -1,7 +1,3 @@
::selection {
background: #FF5E99;
}
output {
color: #d2d2d2;
}
-79
View File
@@ -1,79 +0,0 @@
::selection {
background: #FF5E99;
}
output {
color: #d2d2d2;
}
#shell {
padding: .1em 1.5em 1em 1em;
color: #d2d2d2;
background: black;
border-radius: 12px;
}
#shell output {
clear: both;
width: 100%;
}
#shell output h3 {
margin: 0;
}
#shell output pre {
margin: 0;
}
.input-line {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-align: stretch;
display: -moz-box;
-moz-box-orient: horizontal;
-moz-box-align: stretch;
display: box;
box-orient: horizontal;
box-align: stretch;
clear: both;
}
.input-line > div:nth-child(2) {
-webkit-box-flex: 1;
-moz-box-flex: 1;
box-flex: 1;
}
.prompt {
white-space: nowrap;
color: #96b38a;
margin-right: 7px;
display: -webkit-box;
-webkit-box-pack: center;
-webkit-box-orient: vertical;
display: -moz-box;
-moz-box-pack: center;
-moz-box-orient: vertical;
display: box;
box-pack: center;
box-orient: vertical;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.cmdline {
outline: none;
background-color: transparent;
margin: 0;
width: 100%;
font: inherit;
border: none;
color: inherit;
}
.ls-files {
-webkit-column-width: 100px;
-moz-column-width: 100px;
-o-column-width: 100px;
column-width: 100px;
}
.clean-text {
background-color: none;
background: none;
color: #ccc;
}
@@ -241,7 +241,7 @@
<!-- /.box-body -->
<div class="box-footer">
<button id="frida_spawn" type="submit" class="btn btn-success btn-sm"><i class="far fa-play-circle"></i> Spawn & Inject</button>
<button id="frida_session" type="submit" class="btn btn-success btn-sm"><i class="fas fa-redo"></i> Inject </button>
<button id="frida_session" type="submit" class="btn btn-success btn-sm" disabled><i class="fas fa-redo"></i> Inject </button>
<button id="frida_attach" type="submit" class="btn btn-success btn-sm"><i class="fas fa-bug"></i> Attach </button>
<button id="frida_get" class="btn btn-primary btn-sm" hidden><i class="fas fa-code"></i> Injected Code</button>
</div>
@@ -847,11 +847,15 @@ print_status("Start Instrumentation or Run the application and navigate through
function get_logs(){
$.ajax({
url : '/frida_logs/?hash={{ hash }}&stream=1',
url : '{% url 'frida_logs' %}?hash={{ hash }}&stream=1',
type : "GET",
dataType: "json",
success : function(json){
$('#frida-logs').text(json.data);
if (json.status==="ok"){
$('#frida-logs').text(json.message);
} else {
$('#frida-logs').text('')
}
},
error : function(xhr, ajaxOptions, thrownError) {
console.log(xhr.responseText);
@@ -1232,6 +1236,7 @@ function frida_instrument(frida_action, pid=null, new_pkg=null){
});
if (frida_action === 'spawn'){
document.getElementById('frida_session').disabled = false;
print_status("Instrumenting the application with Frida.");
} else if (frida_action === 'session' && pid === null){
print_status("Injecting Frida script to existing session.");
@@ -31,7 +31,7 @@ $(document).ready( function () {
type : "GET",
dataType: "json",
success : function(json){
$('#logs').text(json.data);
$('#logs').text(json.message);
},
error : function(xhr, ajaxOptions, thrownError) {
console.log(xhr.responseText);
@@ -802,9 +802,8 @@ function upload_and_install(url){
'IPA Installed',
'The IPA is successfully installed',
'success'
).then(function () {
location.reload();
})
);
location.reload();
}
},
error : function(xhr,errmsg,err) {
@@ -280,7 +280,7 @@
<div class="box-footer">
<button id="run" type="submit" class="btn btn-success btn-sm"><i class="fas fa-play"></i> Run</button>
<button id="frida_spawn" type="submit" class="btn btn-success btn-sm"><i class="far fa-play-circle"></i> Spawn & Inject</button>
<button id="frida_session" type="submit" class="btn btn-success btn-sm"><i class="fas fa-redo"></i> Inject </button>
<button id="frida_session" type="submit" class="btn btn-success btn-sm" disabled><i class="fas fa-redo"></i> Inject </button>
<button id="frida_attach" type="submit" class="btn btn-success btn-sm"><i class="fas fa-bug"></i> Attach </button>
<button id="frida_get" class="btn btn-primary btn-sm" hidden><i class="fas fa-code"></i> Injected Code</button>
</div>
@@ -774,11 +774,15 @@ print_status("Environment is ready for Dynamic Analysis.");
// Frida Logs Tab
setInterval( function () {
$.ajax({
url : '/frida_logs/?hash={{ hash }}&stream=1',
url : '{% url 'frida_logs' %}?hash={{ hash }}&stream=1',
type : "GET",
dataType: "json",
success : function(json){
$('#frida-logs').text(json.data);
if (json.status==="ok"){
$('#frida-logs').text(json.message);
} else {
$('#frida-logs').text('')
}
},
error : function(xhr, ajaxOptions, thrownError) {
console.log(xhr.responseText);
@@ -1077,6 +1081,7 @@ function frida_instrument(frida_action, pid=null, new_bundle_id=null){
});
if (frida_action === 'spawn'){
document.getElementById('frida_session').disabled = false;
print_status("Instrumenting the application with Frida.");
} else if (frida_action === 'session' && pid === null){
print_status("Injecting Frida script to existing session.");
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry]
name = "mobsf"
version = "3.8.4"
version = "3.8.5"
description = "Mobile Security Framework (MobSF) is an automated, all-in-one mobile application (Android/iOS/Windows) pen-testing, malware analysis and security assessment framework capable of performing static and dynamic analysis."
keywords = ["mobsf", "mobile security framework", "mobile security", "security tool", "static analysis", "dynamic analysis", "malware analysis"]
authors = ["Ajin Abraham <[email protected]>"]