def testPermissions(self): """Test the get_permissions and get_permission_usage methods""" a, _, dx = AnalyzeAPK("examples/android/TestsAndroguard/bin/TestActivity.apk") api_level = a.get_effective_target_sdk_version() used_permissions = ['android.permission.BROADCAST_STICKY', 'android.permission.ACCESS_NETWORK_STATE'] sticky_meths = ['onMenuItemSelected', 'navigateUpTo'] network_meths = ['getNetworkInfo', 'getActiveNetworkInfo', 'isActiveNetworkMetered'] for _, perm in dx.get_permissions(api_level): for p in perm: self.assertIn(p, used_permissions) meths = [x.name for x in dx.get_permission_usage('android.permission.BROADCAST_STICKY', api_level)] self.assertListEqual(sorted(meths), sorted(sticky_meths)) meths = [x.name for x in dx.get_permission_usage('android.permission.ACCESS_NETWORK_STATE', api_level)] self.assertListEqual(sorted(meths), sorted(network_meths)) # Should give same result if no API level is given for _, perm in dx.get_permissions(): for p in perm: self.assertIn(p, used_permissions) meths = [x.name for x in dx.get_permission_usage('android.permission.BROADCAST_STICKY')] self.assertListEqual(sorted(meths), sorted(sticky_meths)) meths = [x.name for x in dx.get_permission_usage('android.permission.ACCESS_NETWORK_STATE')] self.assertListEqual(sorted(meths), sorted(network_meths))
def main(): parser = ArgumentParser(description="Create a call graph based on the data" "of Analysis and export it into a graph format.") parser.add_argument("APK", nargs=1, help="The APK to analyze") parser.add_argument("--output", "-o", default="callgraph.gml", help="Filename of the output file, the extension is used to decide which format to use (default callgraph.gml)") parser.add_argument("--show", "-s", action="store_true", default=False, help="instead of saving the graph, print it with mathplotlib (you might not see anything!") parser.add_argument("--verbose", "-v", action="store_true", default=False, help="Print more output") parser.add_argument("--classname", default=".*", help="Regex to filter by classname") parser.add_argument("--methodname", default=".*", help="Regex to filter by methodname") parser.add_argument("--descriptor", default=".*", help="Regex to filter by descriptor") parser.add_argument("--accessflag", default=".*", help="Regex to filter by accessflags") parser.add_argument("--no-isolated", default=False, action="store_true", help="Do not store methods which has no xrefs") args = parser.parse_args() if args.verbose: show_logging(logging.INFO) a, d, dx = AnalyzeAPK(args.APK[0]) entry_points = map(FormatClassToJava, a.get_activities() + a.get_providers() + a.get_services() + a.get_receivers()) entry_points = list(entry_points) log.info("Found The following entry points by search AndroidManifest.xml: {}".format(entry_points)) CG = dx.get_call_graph(args.classname, args.methodname, args.descriptor, args.accessflag, args.no_isolated, entry_points, ) write_methods = dict(gml=_write_gml, gexf=nx.write_gexf, gpickle=nx.write_gpickle, graphml=nx.write_graphml, yaml=nx.write_yaml, net=nx.write_pajek, ) if args.show: plot(CG) else: writer = args.output.rsplit(".", 1)[1] if writer in ["bz2", "gz"]: writer = args.output.rsplit(".", 2)[1] if writer not in write_methods: print("Could not find a method to export files to {}!".format(writer)) sys.exit(1) write_methods[writer](CG, args.output)
def androcg_main(verbose, APK, classname, methodname, descriptor, accessflag, no_isolated, show, output): from androguard.core.androconf import show_logging from androguard.core.bytecode import FormatClassToJava from androguard.misc import AnalyzeAPK import networkx as nx import logging log = logging.getLogger("androcfg") if verbose: show_logging(logging.INFO) a, d, dx = AnalyzeAPK(APK) entry_points = map(FormatClassToJava, a.get_activities() + a.get_providers() + a.get_services() + a.get_receivers()) entry_points = list(entry_points) log.info("Found The following entry points by search AndroidManifest.xml: " "{}".format(entry_points)) CG = dx.get_call_graph(classname, methodname, descriptor, accessflag, no_isolated, entry_points, ) write_methods = dict(gml=_write_gml, gexf=nx.write_gexf, gpickle=nx.write_gpickle, graphml=nx.write_graphml, yaml=nx.write_yaml, net=nx.write_pajek, ) if show: plot(CG) else: writer = output.rsplit(".", 1)[1] if writer in ["bz2", "gz"]: writer = output.rsplit(".", 2)[1] if writer not in write_methods: print("Could not find a method to export files to {}!" .format(writer)) sys.exit(1) write_methods[writer](CG, output)
def testAPKWrapperUnsigned(self): from androguard.misc import AnalyzeAPK from androguard.core.bytecodes.apk import APK from androguard.core.bytecodes.dvm import DalvikVMFormat from androguard.core.analysis.analysis import Analysis a, d, dx = AnalyzeAPK("examples/android/TestsAndroguard/bin/TestActivity_unsigned.apk") self.assertIsInstance(a, APK) self.assertIsInstance(d, DalvikVMFormat) self.assertIsInstance(dx, Analysis) self.assertEqual(a.get_signature_name(), None) self.assertEqual(a.get_signature_names(), [])
def testAPKWrapper(self): from androguard.misc import AnalyzeAPK from androguard.core.bytecodes.apk import APK from androguard.core.bytecodes.dvm import DalvikVMFormat from androguard.core.analysis.analysis import Analysis a, d, dx = AnalyzeAPK("examples/android/TestsAndroguard/bin/TestActivity.apk") self.assertIsInstance(a, APK) self.assertIsInstance(d, DalvikVMFormat) self.assertIsInstance(dx, Analysis) self.assertEqual(a.get_signature_name(), "META-INF/CERT.RSA") self.assertEqual(a.get_signature_names(), ["META-INF/CERT.RSA"]) self.assertIsNotNone(a.get_certificate(a.get_signature_name()))
def testAPKWrapperRaw(self): from androguard.misc import AnalyzeAPK from androguard.core.bytecodes.apk import APK from androguard.core.bytecodes.dvm import DalvikVMFormat from androguard.core.analysis.analysis import Analysis with open( "examples/android/TestsAndroguard/bin/TestActivity.apk", 'rb') \ as file_obj: file_contents = file_obj.read() a, d, dx = AnalyzeAPK(file_contents, raw=True) self.assertIsInstance(a, APK) self.assertIsInstance(d[0], DalvikVMFormat) self.assertIsInstance(dx, Analysis) self.assertEqual(a.get_signature_name(), "META-INF/CERT.RSA") self.assertEqual(a.get_signature_names(), ["META-INF/CERT.RSA"]) self.assertIsNotNone(a.get_certificate(a.get_signature_name()))
def testMultidex(self): a, d, dx = AnalyzeAPK("examples/tests/multidex/multidex.apk") cls = list(map(lambda x: x.get_vm_class().get_name(), dx.get_classes())) self.assertIn('Lcom/foobar/foo/Foobar;', cls) self.assertIn('Lcom/blafoo/bar/Blafoo;', cls)
def get_permissions(apk_file: AnalyzeAPK) -> List[str]: return apk_file.get_permissions()
class Apkinfo: def __init__(self, apk_filepath): self.a, self.d, self.dx = AnalyzeAPK(apk_filepath) # Create Class, Method, String and Field # crossreferences for all classes in the Analysis. # self.dx.create_xref() @property def permissions(self): """ :returns: A list of permissions :rtype: list """ return self.a.get_permissions() def find_method(self, class_name=".*", method_name=".*"): """ Find method from given class_name and method_name, default is find all. :returns: an generator of MethodClassAnalysis :rtype: generator """ result = self.dx.find_methods(class_name, method_name) if len(list(result)) > 0: return self.dx.find_methods(class_name, method_name) else: # Method Not Found return None def upperfunc(self, class_name, method_name): """ Return the upper level method from given class name and method name. :param class_name: :param method_name: :return: list """ result = [] method_set = self.find_method(class_name, method_name) if method_set is not None: for md in method_set: for _, call, _ in md.get_xref_from(): # Get class name and method name: # call.class_name, call.name result.append((call.class_name, call.name)) return tools.remove_dup_list(result) else: return None def get_method_bytecode(self, class_name, method_name): """ Return the corresponding bytecode according to the given class name and method name. :param class_name: :param method_name: :return: generator """ result = self.dx.find_methods(class_name, method_name) if len(list(result)) > 0: for m in self.dx.find_methods(class_name, method_name): for idx, ins in m.get_method().get_instructions_idx(): bytecode_obj = None reg_list = [] # count the number of the registers. length_operands = len(ins.get_operands()) if length_operands == 0: # No register, no parm bytecode_obj = BytecodeObject(ins.get_name(), None, None) elif length_operands == 1: # Only one register reg_list.append( "v" + str(ins.get_operands()[length_operands - 1][1]) ) bytecode_obj = BytecodeObject(ins.get_name(), reg_list, None, ) elif length_operands >= 2: # the last one is parm, the other are registers. parameter = ins.get_operands()[length_operands - 1] for i in range(0, length_operands - 1): reg_list.append("v" + str(ins.get_operands()[i][1])) if len(parameter) == 3: # method or value parameter = parameter[2] else: # Operand.OFFSET parameter = parameter[1] bytecode_obj = BytecodeObject( ins.get_name(), reg_list, parameter, ) yield bytecode_obj else: return None
for feature in features: f.write("feature::%s\n" % feature) num = 0 for path, subdirs, files in os.walk(rootdir): for name in files: if name.endswith(".apk"): num = num + 1 filepath = os.path.join(path, name) appName = name[:-4] #print("File %d: %s"%(num,name)) print(num) try: a, d, dx = AnalyzeAPK(filepath) permission_file_path = os.path.join(permission_dir, appName) write_permissions(a, permission_file_path) icc_file_path = os.path.join(icc_dir, appName) write_icc(a, icc_file_path) api_file_path = os.path.join(api_dir, appName) write_api(dx, api_file_path) except Exception as e: with open('exception.txt', 'a') as exceptionfile: exceptionfile.write('{}\n'.format(appName)) continue
def get_package_name(filename): a, d, dx = AnalyzeAPK(filename) return a.get_package()
def get_detailed_analysis(self, app_path): from androguard.misc import AnalyzeAPK self.a, self.d, self.dx = AnalyzeAPK(app_path)
action="store_true") parser.add_argument("--hex", "-H", help="Give hex string of the bytecode", action="store_true") args = parser.parse_args() if not os.path.isfile(args.APK): print("This file does not exist") sys.exit(-1) if androconf.is_android(args.APK) != 'APK': print("This is not an APK file :o") sys.exit(-1) else: a, d, dx = AnalyzeAPK(args.APK) class_name = args.CLASS.replace('.', '/') if args.verbose: print("Searching for {}".format(class_name)) cc = [d for d in dx.get_classes() if class_name in d.name] if len(cc) == 0: print("Class not found") else: for c in cc: methods = [ m for m in c.get_methods() if m.get_method().name == args.METHOD ] print("{} methods found in {}".format(len(methods), c.name)) for m in methods: m.get_method().show_info()
workbook = xlsxwriter.Workbook("APK1funcallapi.xlsx") worksheet = workbook.add_worksheet() exapiname = api_classes funcname = [] funcnameapi = [] for i in range(len(exapiname)): for meth in dx.classes[exapiname[i]].get_methods(): for _, call, _ in meth.get_xref_from(): funcname.append(call.name) funcnameapi.append(([call.name, exapiname[i]])) funcname = list(set(funcname)) k = 0 funcallapi = [] for i in range(len(funcname)): for j in range(len(funcnameapi)): if funcname[i] == funcnameapi[j][0]: funcallapi.append(funcnameapi[j][1]) funcallapi = list(set(funcallapi)) worksheet.write(i, 0, funcname[i]) for k in range(len(funcallapi)): worksheet.write(i, k + 1, funcallapi[k]) funcallapi = [] workbook.close() if __name__ == "__main__": a, d, dx = AnalyzeAPK("E:/Code/APK1.apk") exclassname = get_source_call(a, d, dx) apiClass = select_api(exclassname) build_methodnameapi_relationship(a, d, dx, apiClass)
def get_detailed_analysis(self): self.a, self.ds, self.dx = AnalyzeAPK(self.app_path) print("decompiler done.")
class androidguard_decompiler(object): """ analysis result of androguard """ def __init__(self, app_path): """ :param app_path: local file path of app, should not be None analyse app specified by app_path """ self.app_path = app_path try: self.a = APK(self.app_path) except: raise self.ds = None self.dx = None def get_detailed_analysis(self): self.a, self.ds, self.dx = AnalyzeAPK(self.app_path) print("decompiler done.") # # def get_fast_analysis(self): # self.ds = DalvikVMFormat( self.a.get_dex() ) def save_session(self, outputpath): save_session([self.a, self.ds, self.dx], "{}/session.json".format(outputpath)) def process_and_savesession_multiplefolder(self, outputdirs): print("filename: ", os.path.basename(self.app_path)) filename = os.path.basename(self.app_path) filename = filename.replace(".apk", "") for outputdir in outputdirs: outputpath = "{}{}".format(outputdir, filename) if os.path.exists(outputpath): print("skip based on filename: {}.".format(filename)) return self.process_and_savesession(outputdirs[-1]) def process_and_savesession(self, outputdir): packagename = self.a.get_package() outputpath = "{}{}".format(outputdir, packagename) if os.path.exists(outputpath): print("skip based on package name: {}.".format(packagename)) return os.mkdir(outputpath) try: if self.ds == None: self.get_detailed_analysis() self.save_session(outputpath) # writeDexToFile(self.ds, packagename, outputpath) except Exception as e: print(e) os.rmdir(outputpath) def output_sorce(self, outputdir): packagename = self.a.get_package() outputpath = "{}{}".format(outputdir, packagename) if os.path.exists(outputpath): print("skip {}.".format(packagename)) return os.mkdir(outputpath) try: if self.ds == None: self.get_detailed_analysis() writeDexToFile(self.ds, packagename, outputpath) except Exception as e: print(e) os.rmdir(outputpath)
class APKStringResourceExtractor: def __init__(self, file, print_func=print): self.file = file self.print = print_func self.apk = None self.apk_analysis = None self.resources = None self.package_names = [] self.locales_priority = [] self.strings_in_resources = set() self.strings_in_code = set() self.id_names = set() def run(self): start_time = datetime.datetime.now() self.load_apk() self.select_locales() self.create_string_resources_list() self.create_resource_ids_list() def create_resource_ids_list(self): for package in self.package_names: for locale in self.locales_priority: if locale not in self.resources.values[package]: continue for res_type, values in self.resources.values[package][ locale].items(): for value in values: if len(value) == 1 or len(value) == 2: self.id_names.add(value[0]) elif len(value) == 3: self.id_names.add(value[1]) def load_apk(self): self.apk, _, self.apk_analysis = AnalyzeAPK(self.file) self.resources = self.apk.get_android_resources() self.package_names = self.resources.get_packages_names() def select_locales(self): all_locales = set() for apk_package_name in self.package_names: all_locales.update(self.resources.get_locales(apk_package_name)) all_locales = list(all_locales) all_locales = [ l if l != "\x00\x00" else "DEFAULT" for l in all_locales ] if len(all_locales) == 1: self.locales_priority = all_locales return # prioritized locales # first: pure english if "en" in all_locales: self.locales_priority.append("en") # then: country-specific english self.locales_priority += [ l for l in all_locales if l.startswith("en-") ] # then: default language if "DEFAULT" in all_locales: self.locales_priority.append("DEFAULT") self.locales_priority.append("\x00\x00") # if still no locale found, raise error if len(self.locales_priority) == 0: raise Exception("no locale found") def create_string_resources_list(self): strings = {} for pkg, locales_res in self.resources.get_resolved_strings().items(): for locale in self.locales_priority: if locale in locales_res: for id, value in locales_res[locale].items(): value = value.strip() if len(value) == 0: continue if id not in strings: strings[id] = value self.strings_in_resources = set(strings.values()) self.strings_in_code = set( [s.value.strip() for s in self.apk_analysis.get_strings()]) def get_all_strings(self): return list(self.strings_in_resources) + list(self.strings_in_code) def get_code_strings(self): return list(self.strings_in_code) def get_resource_strings(self): return list(self.strings_in_resources) def get_id_names(self): return list(self.id_names) def save(self, out_file): with open(out_file, "wb") as fp: data = (self.get_id_names(), self.get_code_strings(), self.get_resource_strings()) pickle.dump(data, fp)
def load_apk(self): self.apk, _, self.apk_analysis = AnalyzeAPK(self.file) self.resources = self.apk.get_android_resources() self.package_names = self.resources.get_packages_names()
0xf05368c0: 'SIGNv3', 0x2146444e: 'Google Metadata', 0x42726577: 'Padding' } if __name__ == '__main__': parser = argparse.ArgumentParser(description='Process some apks') parser.add_argument('APK', help='APK') args = parser.parse_args() if os.path.isdir(args.APK): for f in os.listdir(args.APK): apk_path = os.path.join(args.APK, f) if os.path.isfile(apk_path): if androconf.is_android(apk_path) == 'APK': a, d, dx = AnalyzeAPK(apk_path) a.parse_v2_v3_signature() if 0x2146444e in a._v2_blocks: print("{} : FROSTING".format(f)) else: print("{} : NOPE".format(f)) else: print("{} not an APK".format(f)) else: if androconf.is_android(args.APK) == 'APK': a, d, dx = AnalyzeAPK(args.APK) if a.is_signed_v1(): print("V1 Signature") if a.is_signed_v2(): print("V2 Signature") if a.is_signed_v3():
def run_application_checks(PATH_TO_FILES,results_file): files=[] for i in range(0,5): P=PATH_TO_FILES.replace("_i","_"+str(i)) for path, subdirs, files_w in os.walk(P): for name in files_w: n=os.path.join(path, name) if not n.endswith(".apk"): continue files.append(n) results=open(results_file,"a",buffering=0)#open the results file #loop the files for f in files: subprocess.Popen("adb logcat -b all -c", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: a, d, dx = AnalyzeAPK(f) package_name=a.get_package() os.system("adb install "+f)#install the app p = subprocess.Popen("adb shell pm dump "+package_name+" | grep -A 1 MAIN",shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]#+" | grep -A 1 MAIN", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] lines=p.split("\n") #print lines intent_main="android.intent.action.MAIN:" activity_name="" activity_check=0 #get the main activity for line in lines: if intent_main in line: index=lines.index(line) opts=lines[index+1].split(" ") for option in opts: if option.startswith(package_name):#found the activity name activity_name=option activity_check=1 break break if activity_check==1: try: os.system("adb shell am start -a android.intent.action.MAIN -n "+activity_name)#run the app time.sleep(1.5) out = subprocess.Popen("adb logcat -d", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] lines=out.split("\n") dictOfLines = { l : 0 for l in lines} if "--------- beginning of crash" in dictOfLines: results.write(package_name+" 1\n") else: results.write(package_name+" 0\n") os.system("adb shell am force-stop "+package_name)#stop the app except: results.write(package_name+" 3\n") else: results.write(package_name+" 2\n") subprocess.Popen("adb logcat -b all -c", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) os.system("adb shell pm uninstall "+package_name) except: subprocess.Popen("adb logcat -b all -c", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) os.system("adb shell pm uninstall "+package_name) continue
class Apkinfo: """Information about apk based on androguard analysis""" def __init__(self, apk_filepath): """Information about apk based on androguard analysis""" # return the APK, list of DalvikVMFormat, and Analysis objects self.apk, self.dalvikvmformat, self.analysis = AnalyzeAPK(apk_filepath) self.apk_filename = os.path.basename(apk_filepath) self.apk_filepath = apk_filepath def __repr__(self): return f"<Apkinfo-APK:{self.apk_filename}>" @property def filename(self): """ Return the filename of apk. :return: a string of apk filename """ return os.path.basename(self.apk_filepath) @property def filesize(self): """ Return the file size of apk file by bytes. :return: a number of size bytes """ return os.path.getsize(self.apk_filepath) @property def md5(self): """ Return the md5 checksum of the apk file. :return: a string of md5 checksum of the apk file """ md5 = hashlib.md5() with open(self.apk_filepath, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): md5.update(chunk) return md5.hexdigest() @property def permissions(self): """ Return all permissions from given APK. :return: a list of all permissions """ return self.apk.get_permissions() def find_method(self, class_name=".*", method_name=".*"): """ Find method from given class_name and method_name, default is find all method. :param class_name: the class name of the Android API :param method_name: the method name of the Android API :return: a generator of MethodClassAnalysis """ result = self.analysis.find_methods(class_name, method_name) if list(result): return self.analysis.find_methods(class_name, method_name) return None def upperfunc(self, class_name, method_name): """ Return the upper level method from given class name and method name. :param class_name: the class name of the Android API :param method_name: the method name of the Android API :return: a list of all upper functions """ upperfunc_result = [] method_set = self.find_method(class_name, method_name) if method_set is not None: for method in method_set: for _, call, _ in method.get_xref_from(): # Get class name and method name: # call.class_name, call.name upperfunc_result.append((call.class_name, call.name)) return tools.remove_dup_list(upperfunc_result) return None def get_method_bytecode(self, class_name, method_name): """ Return the corresponding bytecode according to the given class name and method name. :param class_name: the class name of the Android API :param method_name: the method name of the Android API :return: a generator of all bytecode instructions """ result = self.analysis.find_methods(class_name, method_name) if list(result): for method in self.analysis.find_methods(class_name, method_name): try: for _, ins in method.get_method().get_instructions_idx(): bytecode_obj = None reg_list = [] # count the number of the registers. length_operands = len(ins.get_operands()) if length_operands == 0: # No register, no parameter bytecode_obj = BytecodeObject( ins.get_name(), None, None, ) elif length_operands == 1: # Only one register reg_list.append( f"v{ins.get_operands()[length_operands - 1][1]}", ) bytecode_obj = BytecodeObject( ins.get_name(), reg_list, None, ) elif length_operands >= 2: # the last one is parameter, the other are registers. parameter = ins.get_operands()[length_operands - 1] for i in range(0, length_operands - 1): reg_list.append( "v" + str(ins.get_operands()[i][1]), ) if len(parameter) == 3: # method or value parameter = parameter[2] else: # Operand.OFFSET parameter = parameter[1] bytecode_obj = BytecodeObject( ins.get_name(), reg_list, parameter, ) yield bytecode_obj except AttributeError as error: # TODO Log the rule here continue
def __init__(self, apk_filepath): self.a, self.d, self.dx = AnalyzeAPK(apk_filepath)
def __init__(self, apk_filepath): """Information about apk based on androguard analysis""" # return the APK, list of DalvikVMFormat, and Analysis objects self.apk, self.dalvikvmformat, self.analysis = AnalyzeAPK(apk_filepath) self.apk_filename = os.path.basename(apk_filepath) self.apk_filepath = apk_filepath
def extract_features_task(self, args): apk_info = {} apk_analysis = {} apk_features = {} drebin_apk_file_list = dict(args[0]) analysis_mode = args[1] url_patterns = ['http://', 'https://', 'www\.', '\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}\b'] regex_patterns = [] apks_to_ignore = [] # apks_to_ignore = ['7a513817c9eac7f249c939252b48572824c9bb423ca31b3c5d84b242661d74bb', # '0899e04e972faded4f22138e6c1289f1095e9bfe5281d10c2866c7a5a9eec0b6', # '8994684817eb83cf360822238925c0025682a9ee804edbf40693a0320a444ed7', # '7bbd566f2f3abb78b3ffcc23ba4ad84e06a00f758d245c660c61b21814a850a5', # 'd53e4087beeee9323f86c7c65944cd10b8eb7bb78ca50de5a01e048b659aecb7', # 'ea153a9dc50f5f9dbaaa29d764b54a819e6b8c0917b000f1c78a32be77f2f804', # 'b7c760781b8d3cfbf7b6657b8ec5f232405038ff399f78f8d05c391de54401b9', # '6337b2ed626ae2ff6f9bd7e09a90abf78cc61c301d5dd18fa936a893766c819e'] for url_pattern in url_patterns: regex_patterns.append(re.compile('^' + url_pattern + '[a-zA-Z]+')) for drebin_apk_name, drebin_apk_path in drebin_apk_file_list.items(): if drebin_apk_name not in apks_to_ignore: try: apk_features[drebin_apk_name] = [] url_features = [] calls = [] api_calls = [] if analysis_mode is False: apk_info[drebin_apk_name] = apk.APK(drebin_apk_path, raw=False, skip_analysis=False, testzip=False) else: apk_object, dalvik_vmf, analysis_obj = AnalyzeAPK(drebin_apk_path) apk_info[drebin_apk_name] = apk_object strings = analysis_obj.get_strings_analysis() for s in strings.keys(): if s != '': for url_pattern in regex_patterns: if re.search(url_pattern, s): if s not in url_features: url_features.append(s) for c in analysis_obj.get_classes(): for meth in c.get_methods(): for _, call, _ in meth.get_xref_to(): calls.append(str(call.class_name) + "->" + str(call.name)) for _, call, _ in meth.get_xref_from(): api_calls.append(str(call.class_name) + "->" + str(call.name)) features_dict = { 'permission': apk_info[drebin_apk_name].get_permissions(), 'activity': apk_info[drebin_apk_name].get_activities(), 'real_permission': apk_info[drebin_apk_name].get_requested_permissions(), 'feature': apk_object.get_features(), 'service_receiver': apk_info[drebin_apk_name].get_services() + apk_info[ drebin_apk_name].get_receivers(), 'provider': apk_info[drebin_apk_name].get_providers(), 'url': url_features, } possible_feature_names = ['permission', 'activity', 'provider', 'feature', 'url', 'network', 'feature', 'call', 'api_call' 'real_permission', 'service_receiver'] intents = [] for item_type in possible_feature_names: for key in features_dict.keys(): for val in features_dict[key]: intent_filters = apk_info[drebin_apk_name].get_intent_filters(item_type, val) if len(intent_filters) > 0: for intent in intent_filters.values(): if intent not in intents: intents.append(intent) features_dict['intent'] = intents features_dict['call'] = calls features_dict['api_call'] = api_calls apk_features[drebin_apk_name].append(features_dict) except OSError as exception: Log.log_message(log_level="ERROR", log_message='cannot process file:' + drebin_apk_name + ' path:' + drebin_apk_path, exception=exception) return apk_features
class Apkinfo: """Information about apk based on androguard analysis""" __slots__ = [ "ret_type", "apk", "dalvikvmformat", "analysis", "apk_filename", "apk_filepath" ] def __init__(self, apk_filepath): """Information about apk based on androguard analysis""" self.ret_type = androconf.is_android(apk_filepath) if self.ret_type == "APK": # return the APK, list of DalvikVMFormat, and Analysis objects self.apk, self.dalvikvmformat, self.analysis = AnalyzeAPK( apk_filepath) if self.ret_type == "DEX": # return the sha256hash, DalvikVMFormat, and Analysis objects _, _, self.analysis = AnalyzeDex(apk_filepath) self.apk_filename = os.path.basename(apk_filepath) self.apk_filepath = apk_filepath def __repr__(self): return f"<Apkinfo-APK:{self.apk_filename}>" @property def filename(self): """ Return the filename of apk. :return: a string of apk filename """ return os.path.basename(self.apk_filepath) @property def filesize(self): """ Return the file size of apk file by bytes. :return: a number of size bytes """ return os.path.getsize(self.apk_filepath) @property def md5(self): """ Return the md5 checksum of the apk file. :return: a string of md5 checksum of the apk file """ md5 = hashlib.md5() with open(self.apk_filepath, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): md5.update(chunk) return md5.hexdigest() @property def permissions(self): """ Return all permissions from given APK. :return: a list of all permissions """ if self.ret_type == "APK": return self.apk.get_permissions() if self.ret_type == "DEX": return [] @functools.lru_cache() def find_method(self, class_name=".*", method_name=".*", descriptor=".*"): """ Find method from given class_name, method_name and the descriptor. default is find all method. :param class_name: the class name of the Android API :param method_name: the method name of the Android API :param descriptor: the descriptor of the Android API :return: a generator of MethodClassAnalysis """ regex_class_name = re.escape(class_name) regex_method_name = f"^{re.escape(method_name)}$" regex_descriptor = re.escape(descriptor) method_result = self.analysis.find_methods( classname=regex_class_name, methodname=regex_method_name, descriptor=regex_descriptor) if list(method_result): result, = list( self.analysis.find_methods(classname=regex_class_name, methodname=regex_method_name, descriptor=regex_descriptor)) return result else: return None @functools.lru_cache() def upperfunc(self, method_analysis): """ Return the xref from method from given method analysis instance. :param method_analysis: the method analysis in androguard :return: a set of all xref from functions """ upperfunc_result = set() for _, call, _ in method_analysis.get_xref_from(): # Call is the MethodAnalysis in the androguard # call.class_name, call.name, call.descriptor upperfunc_result.add(call) return upperfunc_result def get_method_bytecode(self, method_analysis): """ Return the corresponding bytecode according to the given class name and method name. :param method_analysis: the method analysis in androguard :return: a generator of all bytecode instructions """ try: for _, ins in method_analysis.get_method().get_instructions_idx(): bytecode_obj = None reg_list = [] # count the number of the registers. length_operands = len(ins.get_operands()) if length_operands == 0: # No register, no parameter bytecode_obj = BytecodeObject( ins.get_name(), None, None, ) elif length_operands == 1: # Only one register reg_list.append( f"v{ins.get_operands()[length_operands - 1][1]}", ) bytecode_obj = BytecodeObject( ins.get_name(), reg_list, None, ) elif length_operands >= 2: # the last one is parameter, the other are registers. parameter = ins.get_operands()[length_operands - 1] for i in range(0, length_operands - 1): reg_list.append("v" + str(ins.get_operands()[i][1]), ) if len(parameter) == 3: # method or value parameter = parameter[2] else: # Operand.OFFSET parameter = parameter[1] bytecode_obj = BytecodeObject( ins.get_name(), reg_list, parameter, ) yield bytecode_obj except AttributeError as error: # TODO Log the rule here pass
def generate_facts(app_folder,result_prefix,rules,storage=None): files = get_all_in_dir(app_folder,"*") send_intent_actions_stats = Counter() recv_intent_actions_stats = Counter() len_files = 0 is_apk = None for file in files: logging.info("Analyzing file %s",file) try: a,d, dx = AnalyzeAPK(file) is_apk = True # Create package to file relations except: is_apk = None print "Not valid APK file: "+file try: if is_apk: with open(result_prefix+"_packages.txt", 'a') as f: f.write("package('"+a.get_package()+"','"+ntpath.basename(file)+"').\n") # Permissions permissions = [] permissions.extend([(str(a.get_package()), permission) for permission in a.get_permissions()]) with open(result_prefix+"_uses_aux.txt", 'a') as f: for permission in permissions: f.write("uses('"+permission[0]+"','"+permission[1]+"').\n") # Intents logging.info("Looking for Intent Sends") sends = Set() sends.update([(str(a.get_package()),"i_"+intent.action) for intent in get_implicit_intents(a,d,dx)]) send_intent_actions_stats.update([send[1] for send in sends]) # Shared Prefs logging.info("Looking for Shared Prefs Sends") sends.update([(str(a.get_package()),"sp_"+shared.package+"_"+shared.preference_file) for shared in get_shared_preferences_writes(a,d,dx)]) with open(result_prefix+"_trans_aux.txt", 'a') as f: for send in sends: f.write("trans('"+send[0]+"','"+escape_quotes(send[1])+"').\n") # Receivers logging.info("Looking for Dynamic Receivers") receives = Set() receives.update([(str(a.get_package()),"i_"+receiver.get_action()) for receiver in get_dynamic_receivers(a,d,dx)]) logging.info("Looking for Static Receivers") receives.update([(str(a.get_package()),"i_"+receiver.get_action()) for receiver in get_static_receivers(a)]) recv_intent_actions_stats.update([receive[1] for receive in receives]) # Shared Prefs logging.info("Looking for Shared Prefs Receives") receives.update([(str(a.get_package()),"sp_"+shared.package+"_"+shared.preference_file) for shared in get_shared_preferences_reads(a,d,dx)]) with open(result_prefix+"_recv_aux.txt", 'a') as f: for receive in receives: f.write("recv('"+receive[0]+"','"+escape_quotes(receive[1])+"').\n") len_files += 1 utils.remove_duplicate_lines(result_prefix+"_uses_aux.txt",result_prefix+"_uses.txt",True) utils.remove_duplicate_lines(result_prefix+"_trans_aux.txt",result_prefix+"_trans.txt",True) utils.remove_duplicate_lines(result_prefix+"_recv_aux.txt",result_prefix+"_recv.txt",True) except: print "Error during analysis: "+file traceback.print_exc() if rules != "": with open(os.path.splitext(rules)[0]+"_program.pl", 'w') as f: #write packages with open(result_prefix+"_packages.txt", 'r') as to_read: f.writelines(to_read.readlines()) #write uses with open(result_prefix+"_uses.txt", 'r') as to_read: f.writelines(to_read.readlines()) #write trans with open(result_prefix+"_trans.txt", 'r') as to_read: f.writelines(to_read.readlines()) if storage: f.write("trans(A,'external_storage'):- uses(A,'android.permission.WRITE_EXTERNAL_STORAGE').\n") #write receives with open(result_prefix+"_recv.txt", 'r') as to_read: f.writelines(to_read.readlines()) if storage: f.write("recv(A,'external_storage'):- uses(A,'android.permission.WRITE_EXTERNAL_STORAGE').\n") f.write("recv(A,'external_storage'):- uses(A,'android.permission.READ_EXTERNAL_STORAGE').\n") with open(rules, 'r') as to_read: f.writelines(to_read.readlines()) with open(result_prefix+"_intent_send_stats",'w') as send_stats_file: send_stats_file.write("**** Results for send intent analysis ****\n") send_stats_file.write("Files analized: ") send_stats_file.write(str(len_files)) send_stats_file.write("\n") for send_stat in send_intent_actions_stats.most_common(): freq = send_stat[1]/len_files send_stats_file.write(send_stat[0]+", "+"{0:.2f}".format(round(freq,2))+", "+str(send_stat[1])+"\n") with open(result_prefix+"_intent_recv_stats",'w') as recv_stats_file: recv_stats_file.write("**** Results for send intent analysis ****\n") recv_stats_file.write("Files analized: ") recv_stats_file.write(str(len_files)) recv_stats_file.write("\n") for recv_stat in recv_intent_actions_stats.most_common(): freq = recv_stat[1]/len_files recv_stats_file.write(recv_stat[0]+", "+"{0:.2f}".format(round(freq,2))+", "+str(recv_stat[1])+"\n") logging.info("Results saved in %s files",result_prefix) return os.path.splitext(rules)[0]+"_program.pl"
def main(): a, d, dx = AnalyzeAPK('demo.apk') subclasses = extendsApplication(d) #print(subclasses) fields = callsGAC(d, subclasses)
def analyze_apk_androguard(apk_file: str, md5_app: str = None, dict_analysis_apk: dict = None): """ Parameters ---------- apk_file Returns ------- """ tracker_name_package = {} # package name analytics to monitoring logger.info("Start App Analyzer") start = time.time() application, dalvik, analysis = AnalyzeAPK(apk_file) # read all trackers package name inside app with open( os.path.join(os.getcwd(), "resources", "package_name_trackers_most_used.txt"), "r") as file: tracker_list = file.readlines() tracker_list = [x.strip() for x in tracker_list] # creation of regular expression for searching methods inside apps for tracker in tracker_list: name = tracker.split(",")[0] packages = tracker.split(",")[1].split("|") packages_new = [] for package in packages: package_new = "L" + package.replace(".", "/") if package_new.endswith("/"): package_new = package_new + ".*" else: package_new = package_new + "/.*" packages_new.append(package_new) tracker_name_package[name] = packages_new # creation list of API for monitoring n_method = 0 list_tracker_inside_app = [] trackers_api_to_monitoring = { } # dict api to monitoring during dynamic analysis for key, list_package_name in tracker_name_package.items(): for package_name in list_package_name: methods = list( analysis.find_methods(package_name) ) # find all methods that satisfy regular expression if len(methods) > 0: trackers_api_to_monitoring[package_name] = [] list_tracker_inside_app.append(key) # add each method to list for monitoring during dynamic analysis for method in methods: n_method = n_method + 1 trackers_api_to_monitoring[package_name].append( (method.get_method().get_class_name().replace( "L", "", 1).replace("/", ".").replace(";", ""), method.get_method().get_name())) if len(methods) > 0: # remove duplicate trackers_api_to_monitoring[package_name] = list( set(trackers_api_to_monitoring[package_name])) list_tracker_inside_app = list(set(list_tracker_inside_app)) list_permissions_app = application.get_permissions() end = time.time() logger.info( "Permission requested by the app {}".format(list_permissions_app)) logger.info("Tracker inside the app {}".format(list_tracker_inside_app)) logger.info("Execution time App Analyzer {}".format(end - start)) # logger.info("API to Monitoring (Trackers) {}".format(n_method)) dict_analysis_apk["permission_requested"] = list_permissions_app dict_analysis_apk["trackers_inside"] = list_tracker_inside_app dict_analysis_apk["execution_time_app_analyzer"] = end - start # dict_analysis_apk["api_to_monitoring_trackers"] = n_method write_result_md5_app(md5_app, list_tracker_inside_app, list_permissions_app, end - start, dict_analysis_apk) logger.info("End App Analyzer") return list_tracker_inside_app, list_permissions_app, trackers_api_to_monitoring, application, dict_analysis_apk
from androguard.core.bytecodes import apk from androguard.core.bytecodes import dvm from androguard.core.analysis import analysis from androguard.misc import AnalyzeAPK b = apk.APK("C://xampp//htdocs//Upload//sample.apk") app_name=b.get_app_name() pk_name = b.get_package() android_version=b.get_androidversion_name() permissions=b.get_permissions() print(app_name) print (pk_name) print(android_version) print(permissions) a,d,dx = AnalyzeAPK("C://xampp//htdocs//Upload//sample.apk") apis=dx.get_classes() print(apis)