def generate_download(apk_dir, md5_hash, download_dir, package): """Generating Downloads.""" logger.info('Generating Downloads') try: httptools = os.path.join(str(Path.home()), '.httptools') logcat = os.path.join(apk_dir, 'logcat.txt') xlogcat = os.path.join(apk_dir, 'x_logcat.txt') apimon = os.path.join(apk_dir, 'kensa_api_monitor.txt') fd_logs = os.path.join(apk_dir, 'kensa_frida_out.txt') dumpsys = os.path.join(apk_dir, 'dump.txt') sshot = os.path.join(apk_dir, 'screenshots-apk/') web = os.path.join(httptools, 'flows', package + '.flow.txt') star = os.path.join(apk_dir, package + '.tar') dlogcat = os.path.join(download_dir, md5_hash + '-logcat.txt') dxlogcat = os.path.join(download_dir, md5_hash + '-x_logcat.txt') dapimon = os.path.join(download_dir, md5_hash + '-api_monitor.txt') dfd_logs = os.path.join(download_dir, md5_hash + '-frida_out.txt') ddumpsys = os.path.join(download_dir, md5_hash + '-dump.txt') dsshot = os.path.join(download_dir, md5_hash + '-screenshots-apk/') dweb = os.path.join(download_dir, md5_hash + '-web_traffic.txt') dstar = os.path.join(download_dir, md5_hash + '-app_data.tar') # Delete existing data dellist = [ dlogcat, dxlogcat, dapimon, dfd_logs, ddumpsys, dsshot, dweb, dstar ] for item in dellist: if os.path.isdir(item): shutil.rmtree(item) elif os.path.isfile(item): os.remove(item) # Copy new data shutil.copyfile(logcat, dlogcat) shutil.copyfile(dumpsys, ddumpsys) if is_file_exists(xlogcat): shutil.copyfile(xlogcat, dxlogcat) if is_file_exists(apimon): shutil.copyfile(apimon, dapimon) if is_file_exists(fd_logs): shutil.copyfile(fd_logs, dfd_logs) try: shutil.copytree(sshot, dsshot) except Exception: pass if is_file_exists(web): shutil.copyfile(web, dweb) if is_file_exists(star): shutil.copyfile(star, dstar) except Exception: logger.exception('Generating Downloads')
def view_report(request): """Dynamic Analysis Report Generation.""" logger.info('Dynamic Analysis Report Generation') try: md5_hash = request.GET['hash'] package = request.GET['package'] droidmon = {} apimon = {} if (is_attack_pattern(package) or not is_md5(md5_hash)): return print_n_send_error_response(request, 'Invalid Parameters') app_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/') download_dir = settings.DWD_DIR if not is_file_exists(os.path.join(app_dir, 'logcat.txt')): msg = ('Dynamic Analysis report is not available ' 'for this app. Perform Dynamic Analysis ' 'and generate the report.') return print_n_send_error_response(request, msg) fd_log = os.path.join(app_dir, 'kensa_frida_out.txt') droidmon = droidmon_api_analysis(app_dir, package) apimon = apimon_analysis(app_dir) analysis_result = run_analysis(app_dir, md5_hash, package) generate_download(app_dir, md5_hash, download_dir, package) images = get_screenshots(md5_hash, download_dir) context = { 'md5': md5_hash, 'emails': analysis_result['emails'], 'urls': analysis_result['urls'], 'domains': analysis_result['domains'], 'clipboard': analysis_result['clipboard'], 'xml': analysis_result['xml'], 'sqlite': analysis_result['sqlite'], 'others': analysis_result['other_files'], 'screenshots': images['screenshots'], 'acttest': images['activities'], 'expacttest': images['exported_activities'], 'droidmon': droidmon, 'apimon': apimon, 'fdlog': is_file_exists(fd_log), 'package': package, 'version': settings.KENSA_VER, 'title': 'Dynamic Analysis' } template = 'dynamic_analysis/android/dynamic_report.html' return render(request, template, context) except Exception as exp: logger.exception('Dynamic Analysis Report Generation') err = 'Error Geneating Dynamic Analysis Report. ' + str(exp) return print_n_send_error_response(request, err)
def get_embedded_classes(self): """ Get the list of Java classes from all DEX files. :return: list of Java classes """ if self.classes is not None: return self.classes for dex_file in glob.iglob(os.path.join(self.apk_dir, '*.dex')): if (len(settings.BACKSMALI_BINARY) > 0 and is_file_exists(settings.BACKSMALI_BINARY)): bs_path = settings.BACKSMALI_BINARY else: bs_path = os.path.join(self.tools_dir, 'baksmali-2.4.0.jar') args = [ settings.JAVA_BINARY, '-jar', bs_path, 'list', 'classes', dex_file ] classes = subprocess.check_output( args, universal_newlines=True).splitlines() if self.classes is not None: self.classes = self.classes + classes else: self.classes = classes return self.classes
def dex_2_smali(app_dir, tools_dir): """Run dex2smali.""" try: logger.info('DEX -> SMALI') dexes = get_dex_files(app_dir) for dex_path in dexes: logger.info('Converting %s to Smali Code', filename_from_path(dex_path)) if (len(settings.BACKSMALI_BINARY) > 0 and is_file_exists(settings.BACKSMALI_BINARY)): bs_path = settings.BACKSMALI_BINARY else: bs_path = os.path.join(tools_dir, 'baksmali-2.4.0.jar') output = os.path.join(app_dir, 'smali_source/') smali = [ settings.JAVA_BINARY, '-jar', bs_path, 'd', dex_path, '-o', output, ] trd = threading.Thread(target=subprocess.call, args=(smali,)) trd.daemon = True trd.start() except Exception: logger.exception('Converting DEX to SMALI')
def apimon_analysis(app_dir): """API Analysis.""" api_details = {} try: location = os.path.join(app_dir, 'kensa_api_monitor.txt') if not is_file_exists(location): return {} logger.info('Frida API Monitor Analysis') with open(location, 'r') as flip: apis = json.loads('[{}]'.format( flip.read()[:-1])) for api in apis: to_decode = None if (api['class'] == 'android.util.Base64'and (api['method'] == 'encodeToString')): to_decode = api['returnValue'].replace('"', '') elif (api['class'] == 'android.util.Base64' and api['method'] == 'decode'): to_decode = api['arguments'][0] try: if to_decode: api['decoded'] = decode_base64(to_decode) except Exception: pass api['icon'] = get_icon_map(api['name']) if api['name'] in api_details: api_details[api['name']].append(api) else: api_details[api['name']] = [api] except Exception: logger.exception('API Monitor Analysis') return api_details
def frida_logs(request): try: apphash = request.GET.get('hash', '') stream = request.GET.get('stream', '') if not is_md5(apphash): return invalid_params() if stream: apk_dir = os.path.join(settings.UPLD_DIR, apphash + '/') frida_logs = os.path.join(apk_dir, 'kensa_frida_out.txt') data = {} if is_file_exists(frida_logs): with open(frida_logs, 'r') as flip: data = {'data': flip.read()} return json_response(data) 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.KENSA_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)
def live_api(request): try: apphash = request.GET.get('hash', '') stream = request.GET.get('stream', '') if not is_md5(apphash): return invalid_params() if stream: apk_dir = os.path.join(settings.UPLD_DIR, apphash + '/') apimon_file = os.path.join(apk_dir, 'kensa_api_monitor.txt') data = {} if is_file_exists(apimon_file): with open(apimon_file, 'r') as flip: api_list = json.loads('[{}]'.format( flip.read()[:-1])) data = {'data': api_list} return json_response(data) logger.info('Starting API monitor streaming') template = 'dynamic_analysis/android/live_api.html' return render(request, template, {'hash': apphash, 'package': request.GET.get('package', ''), 'version': settings.KENSA_VER, 'title': 'Live API Monitor'}) except Exception: logger.exception('API monitor streaming') err = 'Error in API monitor streaming' return print_n_send_error_response(request, err)
def binary_analysis(src, tools_dir, app_dir, executable_name): """Binary Analysis of IPA.""" try: binary_analysis_dict = {} binary_findings = {} logger.info('Starting Binary Analysis') dirs = os.listdir(src) dot_app_dir = '' for dir_ in dirs: if dir_.endswith('.app'): dot_app_dir = dir_ break # Bin Dir - Dir/Payload/x.app/ bin_dir = os.path.join(src, dot_app_dir) if (executable_name and is_file_exists(os.path.join(bin_dir, executable_name))): bin_name = executable_name else: bin_name = dot_app_dir.replace('.app', '') # Bin Path - Dir/Payload/x.app/x bin_path = os.path.join(bin_dir, bin_name) binary_analysis_dict['libs'] = [] binary_analysis_dict['bin_res'] = {} binary_analysis_dict['strings'] = [] if not is_file_exists(bin_path): logger.warning('Kensa Cannot find binary in %s', bin_path) logger.warning('Skipping Otool, Classdump and Strings') else: bin_info = get_bin_info(bin_path) object_data = otool_analysis(tools_dir, bin_name, bin_path, bin_dir) bin_type = detect_bin_type(object_data['libs']) cdump = get_class_dump(tools_dir, bin_path, app_dir, bin_type) binary_rule_matcher(binary_findings, object_data['bindata'] + cdump, ipa_rules.IPA_RULES) strings_in_ipa = strings_on_ipa(bin_path) binary_analysis_dict['libs'] = object_data['libs'] binary_analysis_dict['bin_res'] = binary_findings binary_analysis_dict['strings'] = strings_in_ipa binary_analysis_dict['macho'] = bin_info binary_analysis_dict['bin_type'] = bin_type return binary_analysis_dict except Exception: logger.exception('iOS Binary Analysis')
def get_app_files(apk_dir, md5_hash, package): """Get files from device.""" logger.info('Getting app files') all_files = {'xml': [], 'sqlite': [], 'others': []} # Extract Device Data tar_loc = os.path.join(apk_dir, package + '.tar') untar_dir = os.path.join(apk_dir, 'DYNAMIC_DeviceData/') if not is_file_exists(tar_loc): return all_files if os.path.exists(untar_dir): # fix for permission errors shutil.rmtree(untar_dir) try: with tarfile.open(tar_loc, errorlevel=1) as tar: tar.extractall(untar_dir) except FileExistsError: pass except Exception: logger.exception('Tar extraction failed') # Do Static Analysis on Data from Device try: if not os.path.exists(untar_dir): os.makedirs(untar_dir) for dir_name, _, files in os.walk(untar_dir): for jfile in files: file_path = os.path.join(untar_dir, dir_name, jfile) fileparam = file_path.replace(untar_dir, '') if is_pipe_or_link(file_path): continue if jfile == 'lib': pass else: if jfile.endswith('.xml'): all_files['xml'].append({ 'type': 'xml', 'file': fileparam }) else: with open(file_path, 'r', encoding='ISO-8859-1') as flip: file_cnt_sig = flip.read(6) if file_cnt_sig == 'SQLite': all_files['sqlite'].append({ 'type': 'db', 'file': fileparam }) elif not jfile.endswith('.DS_Store'): all_files['others'].append({ 'type': 'others', 'file': fileparam }) except Exception: logger.exception('Getting app files') return all_files
def droidmon_api_analysis(app_dir, package): """API Analysis.""" try: dat = '' hooked_apis = get_hooked_apis() api_details = {} hooks = [] location = os.path.join(app_dir, 'x_logcat.txt') if not is_file_exists(location): return {} logger.info('Xposed Droidmon API Analysis') with open(location, 'r', encoding='utf-8') as flip: dat = flip.readlines() res_id = 'Droidmon-apimonitor-' + package + ':' for line in dat: if res_id not in line: continue _, value = line.split(res_id, 1) try: apis = json.loads(value, strict=False) call_data = {} call_data['class'] = apis['class'] call_data['method'] = apis['method'] if apis.get('return'): call_data['return'] = apis['return'] if apis.get('args'): call_data['args'] = apis['args'] for api, details in hooked_apis.items(): if re.findall(details['regex'], apis['class']): call_data['api'] = api # Decode Base64 if ('decode' in apis['method'] and api == 'api_base64'): call_data['decoded'] = base64_decode( call_data['args']) hooks.append(call_data) except Exception: pass for hook in hooks: iden = hook['api'] api_details[iden] = { 'name': hooked_apis[iden]['name'], 'icon': hooked_apis[iden]['icon'], 'calls': [], } for hook in hooks: iden = hook['api'] if hook not in api_details[iden]['calls']: api_details[iden]['calls'].append(hook) except Exception: logger.exception('Droidmon API Analysis') return api_details
def get_log_data(apk_dir, package): """Get Data for analysis.""" logcat_data = [] droidmon_data = '' apimon_data = '' frida_logs = '' web_data = '' traffic = '' httptools = os.path.join(str(Path.home()), '.httptools') web = os.path.join(httptools, 'flows', package + '.flow.txt') logcat = os.path.join(apk_dir, 'logcat.txt') xlogcat = os.path.join(apk_dir, 'x_logcat.txt') apimon = os.path.join(apk_dir, 'kensa_api_monitor.txt') fd_logs = os.path.join(apk_dir, 'kensa_frida_out.txt') if is_file_exists(web): with io.open(web, mode='r', encoding='utf8', errors='ignore') as flip: web_data = flip.read() if is_file_exists(logcat): with io.open(logcat, mode='r', encoding='utf8', errors='ignore') as flip: logcat_data = flip.readlines() traffic = ''.join(logcat_data) if is_file_exists(xlogcat): with io.open(xlogcat, mode='r', encoding='utf8', errors='ignore') as flip: droidmon_data = flip.read() if is_file_exists(apimon): with io.open(apimon, mode='r', encoding='utf8', errors='ignore') as flip: apimon_data = flip.read() if is_file_exists(fd_logs): with io.open(fd_logs, mode='r', encoding='utf8', errors='ignore') as flip: frida_logs = flip.read() traffic = (web_data + traffic + droidmon_data + apimon_data + frida_logs) return {'logcat': logcat_data, 'traffic': traffic}
def classdump_mac(clsdmp_bin, tools_dir, ipa_bin): """Run Classdump for Objective-C/Swift.""" if clsdmp_bin == 'class-dump-swift': logger.info('Running class-dump-swift against binary') external = settings.CLASSDUMP_SWIFT_BINARY else: logger.info('Running class-dump against binary') external = settings.CLASSDUMP_BINARY if (len(external) > 0 and is_file_exists(external)): class_dump_bin = external else: class_dump_bin = os.path.join(tools_dir, clsdmp_bin) # Execute permission check if not os.access(class_dump_bin, os.X_OK): os.chmod(class_dump_bin, stat.S_IEXEC) return subprocess.check_output([class_dump_bin, ipa_bin])
def delete_scan(request, api=False): """Delete Scan from DB and remove the scan related files.""" try: if request.method == 'POST': if api: md5_hash = request.POST['hash'] else: md5_hash = request.POST['md5'] data = {'deleted': 'scan hash not found'} if re.match('[0-9a-f]{32}', md5_hash): # Delete DB Entries scan = RecentScansDB.objects.filter(MD5=md5_hash) if scan.exists(): RecentScansDB.objects.filter(MD5=md5_hash).delete() StaticAnalyzerAndroid.objects.filter(MD5=md5_hash).delete() StaticAnalyzerIOS.objects.filter(MD5=md5_hash).delete() StaticAnalyzerWindows.objects.filter(MD5=md5_hash).delete() # Delete Upload Dir Contents app_upload_dir = os.path.join(settings.UPLD_DIR, md5_hash) if is_dir_exists(app_upload_dir): shutil.rmtree(app_upload_dir) # Delete Download Dir Contents dw_dir = settings.DWD_DIR for item in os.listdir(dw_dir): item_path = os.path.join(dw_dir, item) valid_item = item.startswith(md5_hash + '-') # Delete all related files if is_file_exists(item_path) and valid_item: os.remove(item_path) # Delete related directories if is_dir_exists(item_path) and valid_item: shutil.rmtree(item_path) data = {'deleted': 'yes'} if api: return data else: ctype = 'application/json; charset=utf-8' return HttpResponse(json.dumps(data), content_type=ctype) except Exception as exp: msg = str(exp) exp_doc = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp_doc) else: return print_n_send_error_response(request, msg, False, exp_doc)
def get_script(request): """Get frida scripts from others.""" data = {'status': 'ok', 'content': ''} try: scripts = request.POST.getlist('scripts[]') others = os.path.join(settings.TOOLS_DIR, 'frida_scripts', 'others') script_ct = [] for script in scripts: script_file = os.path.join(others, script + '.js') if not is_safe_path(others, script_file): return json_response(data) if is_file_exists(script_file): script_ct.append(Path(script_file).read_text()) data['content'] = '\n'.join(script_ct) except Exception: pass return json_response(data)
def apk_2_java(app_path, app_dir, tools_dir): """Run jadx.""" try: logger.info('APK -> JAVA') args = [] output = os.path.join(app_dir, 'java_source/') logger.info('Decompiling to Java with jadx') if os.path.exists(output): shutil.rmtree(output) if (len(settings.JADX_BINARY) > 0 and is_file_exists(settings.JADX_BINARY)): jadx = settings.JADX_BINARY else: if platform.system() == 'Windows': jadx = os.path.join(tools_dir, 'jadx/bin/jadx.bat') else: jadx = os.path.join(tools_dir, 'jadx/bin/jadx') # Set write permission, if JADX is not executable if not os.access(jadx, os.X_OK): os.chmod(jadx, stat.S_IEXEC) args = [ jadx, '-ds', output, '-q', '-r', '--show-bad-code', app_path, ] fnull = open(os.devnull, 'w') subprocess.call(args, stdout=fnull, stderr=subprocess.STDOUT) except Exception: logger.exception('Decompiling to JAVA')
def run(request, api=False): """View iOS Files.""" try: logger.info('View iOS Source File') exp = 'Error Description' file_format = 'cpp' if api: fil = request.POST['file'] md5_hash = request.POST['hash'] mode = request.POST['type'] viewsource_form = ViewSourceIOSApiForm(request.POST) else: fil = request.GET['file'] md5_hash = request.GET['md5'] mode = request.GET['type'] viewsource_form = ViewSourceIOSForm(request.GET) typ = set_ext_api(fil) if not viewsource_form.is_valid(): err = FormUtil.errors_message(viewsource_form) if api: return err return print_n_send_error_response(request, err, False, exp) if mode == 'ipa': src = os.path.join(settings.UPLD_DIR, md5_hash + '/Payload/') elif mode == 'ios': src = os.path.join(settings.UPLD_DIR, md5_hash + '/') sfile = os.path.join(src, fil) if not is_safe_path(src, sfile): msg = 'Path Traversal Detected!' if api: return {'error': 'Path Traversal Detected!'} return print_n_send_error_response(request, msg, False, exp) dat = '' sql_dump = {} if typ == 'm': file_format = 'cpp' with io.open(sfile, mode='r', encoding='utf8', errors='ignore') as flip: dat = flip.read() elif typ == 'xml': file_format = 'xml' with io.open(sfile, mode='r', encoding='utf8', errors='ignore') as flip: dat = flip.read() elif typ == 'plist': file_format = 'json' dat = biplist.readPlist(sfile) try: dat = json.dumps(dat, indent=4, sort_keys=True) except Exception: pass elif typ == 'db': file_format = 'asciidoc' sql_dump = read_sqlite(sfile) elif typ == 'txt' and fil == 'classdump.txt': file_format = 'cpp' app_dir = os.path.join(settings.UPLD_DIR, md5_hash + '/') cls_dump_file = os.path.join(app_dir, 'classdump.txt') if is_file_exists(cls_dump_file): with io.open(cls_dump_file, mode='r', encoding='utf8', errors='ignore') as flip: dat = flip.read() else: dat = 'Class Dump result not Found' elif typ == 'txt': file_format = 'text' with io.open(sfile, mode='r', encoding='utf8', errors='ignore') as flip: dat = flip.read() else: if api: return {'error': 'Invalid Parameters'} return HttpResponseRedirect('/error/') context = { 'title': escape(ntpath.basename(fil)), 'file': escape(ntpath.basename(fil)), 'type': file_format, 'dat': dat, 'sql': sql_dump, 'version': settings.KENSA_VER, } template = 'general/view.html' if api: return context return render(request, template, context) except Exception as exp: logger.exception('Error Viewing Source') msg = str(exp) exp = exp.__doc__ if api: return print_n_send_error_response(request, msg, True, exp) return print_n_send_error_response(request, msg, False, exp)
def plist_analysis(src, is_source): """Plist Analysis.""" try: logger.info('iOS Info.plist Analysis Started') plist_info = { 'bin_name': '', 'bin': '', 'id': '', 'version': '', 'build': '', 'sdk': '', 'pltfm': '', 'min': '', 'plist_xml': '', 'permissions': [], 'inseccon': [], 'bundle_name': '', 'build_version_name': '', 'bundle_url_types': [], 'bundle_supported_platforms': [], } plist_file = None if is_source: logger.info('Finding Info.plist in iOS Source') for dirpath, _dirnames, files in os.walk(src): for name in files: if (not any(x in dirpath for x in ['__MACOSX', 'Pods']) and name == 'Info.plist'): plist_file = os.path.join(dirpath, name) break else: logger.info('Finding Info.plist in iOS Binary') dirs = os.listdir(src) dot_app_dir = '' for dir_ in dirs: if dir_.endswith('.app'): dot_app_dir = dir_ break bin_dir = os.path.join(src, dot_app_dir) # Full Dir/Payload/x.app plist_file = os.path.join(bin_dir, 'Info.plist') if not is_file_exists(plist_file): logger.warning( 'Cannot find Info.plist file. Skipping Plist Analysis.') else: # Generic Plist Analysis plist_obj = plistlib.readPlist(plist_file) plist_info['plist_xml'] = plistlib.writePlistToBytes( plist_obj).decode('utf-8', 'ignore') plist_info['bin_name'] = (plist_obj.get('CFBundleDisplayName', '') or plist_obj.get('CFBundleName', '')) if not plist_info['bin_name'] and not is_source: # For iOS IPA plist_info['bin_name'] = dot_app_dir.replace('.app', '') plist_info['bin'] = plist_obj.get('CFBundleExecutable', '') plist_info['id'] = plist_obj.get('CFBundleIdentifier', '') plist_info['build'] = plist_obj.get('CFBundleVersion', '') plist_info['sdk'] = plist_obj.get('DTSDKName', '') plist_info['pltfm'] = plist_obj.get('DTPlatformVersion', '') plist_info['min'] = plist_obj.get('MinimumOSVersion', '') plist_info['bundle_name'] = plist_obj.get('CFBundleName', '') plist_info['bundle_version_name'] = plist_obj.get( 'CFBundleShortVersionString', '') plist_info['bundle_url_types'] = plist_obj.get( 'CFBundleURLTypes', []) plist_info['bundle_supported_platforms'] = plist_obj.get( 'CFBundleSupportedPlatforms', []) # Check for app-permissions plist_info['permissions'] = check_permissions(plist_obj) # Check for ats misconfigurations plist_info['inseccon'] = check_transport_security(plist_obj) return plist_info except Exception: logger.exception('Reading from Info.plist')
def get_otool_out(tools_dir, cmd_type, bin_path, bin_dir): """Get otool args by OS and type.""" if (len(settings.OTOOL_BINARY) > 0 and is_file_exists(settings.OTOOL_BINARY)): otool_bin = settings.OTOOL_BINARY else: otool_bin = 'otool' if (len(settings.JTOOL_BINARY) > 0 and is_file_exists(settings.JTOOL_BINARY)): jtool_bin = settings.JTOOL_BINARY else: jtool_bin = os.path.join(tools_dir, 'jtool.ELF64') jtool2_bin = os.path.join(tools_dir, 'jtool2.ELF64') # jtool execute permission check for toolbin in [jtool_bin, jtool2_bin]: if not os.access(toolbin, os.X_OK): os.chmod(toolbin, stat.S_IEXEC) plat = platform.system() if cmd_type == 'libs': if plat == 'Darwin': args = [otool_bin, '-L', bin_path] args2 = args elif plat == 'Linux': args = [jtool_bin, '-arch', 'arm', '-L', '-v', bin_path] args2 = [jtool2_bin, '-L', '-v', '-q', bin_path] else: # Platform Not Supported return None try: libs = subprocess.check_output(args2).decode('utf-8', 'ignore') except Exception: libs = subprocess.check_output(args).decode('utf-8', 'ignore') libs = smart_text(escape(libs.replace(bin_dir + '/', ''))) return libs.split('\n') elif cmd_type == 'header': if plat == 'Darwin': args = [otool_bin, '-hv', bin_path] args2 = args elif plat == 'Linux': args = [jtool_bin, '-arch', 'arm', '-h', '-v', bin_path] args2 = [jtool2_bin, '-h', '-v', '-q', bin_path] else: # Platform Not Supported return None try: return subprocess.check_output(args2) except Exception: return subprocess.check_output(args) elif cmd_type == 'symbols': if plat == 'Darwin': args = [otool_bin, '-Iv', bin_path] args2 = args return subprocess.check_output(args) elif plat == 'Linux': args = [jtool_bin, '-arch', 'arm', '-S', bin_path] arg2 = [jtool2_bin, '-S', bin_path] try: with open(os.devnull, 'w') as devnull: return subprocess.check_output(arg2, stderr=devnull) except Exception: return subprocess.check_output(args) else: # Platform Not Supported return None elif cmd_type == 'classdump': # Handle Classdump in Linux # Add timeout to handle ULEB128 malformed return [jtool_bin, '-arch', 'arm', '-d', 'objc', '-v', bin_path] return None
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)