def analyze(self): self.packed_files = dict() self.malware_detect() for file in self.a.get_files(): file_type = check_header(self.a.get_file(file)[0:4].hex()) if file_type == "JAR": write_file_to_dir(self.output_dir, file.split("/")[-1], a.get_file(file)) try: a, d, dx = AnalyzeAPK(self.output_dir + file.split("/")[-1]) if a.get_package(): self.packed_files[self.a.get_package()] = {file: {}} else: continue except Exception as e: # not an APK file continue with open(PERMISSIONS_FILE) as json_file: permissions = json.load(json_file) perms_desc = {} dangerous_perms = {} if a.get_permissions(): for perm in a.get_permissions(): try: perms_desc[perm] = { "description": permissions[perm]["description"], "level": permissions[perm]["protection_lvl"] } if any( re.findall( r'dangerous', permissions[perm]["protection_lvl"], re.IGNORECASE)): # Permission is flagged as dangerous dangerous_perms[permissions[perm] ["permission"]] = permissions[ perm]["description"] except Exception as e: continue self.packed_files[self.a.get_package()][file] = dangerous_perms return { "packed_file": self.packed_files, "detected_malware": self.detected_malware }
def each(self, target): self.results = dict() try: apk, vm, vm_analysis = AnalyzeAPK(target) # First, get basic information about the APK self.results['name'] = apk.get_app_name() self.results['package'] = apk.get_package() self.results['permissions'] = apk.get_permissions() self.results['main_activity'] = apk.get_main_activity() self.results['receivers'] = apk.get_receivers() self.results['services'] = apk.get_services() self.results['main_activity_content'] = vm.get_class("L{};".format( self.results['main_activity']).replace('.', '/')).get_source() except: apk = None vm, vm_analysis = AnalyzeDex(target) self.results['dex'] = True # Then, run all the APK Plugins in order to see if this is a known malware for plugin in APKPlugin.__subclasses__(): plugin = plugin(target, apk, vm, vm_analysis) plugin.apply(self) return True
def analyse(self): self.packed_files = dict() self.malware_detect() for file in self.a.get_files(): file_type = check_header(self.a.get_file(file)[0:4].hex()) if file_type == "JAR": if not os.path.isdir(self.output_dir): os.makedirs(self.output_dir) f = open(self.output_dir + file.split("/")[-1], 'wb') f.write(self.a.get_file(file)) f.close() try: a, d, dx = AnalyzeAPK(self.output_dir + file.split("/")[-1]) if a.get_package(): self.packed_files[self.a.get_package()] = {file: {}} else: continue except Exception as e: # not an APK file continue with open(PERMISSIONS_FILE) as json_file: permissions = json.load(json_file) perms_desc = {} dangerous_perms = {} if a.get_permissions(): for perm in a.get_permissions(): try: mapped = list(filter(lambda x: x["permission"] == perm, permissions)) perms_desc[perm] = {"desc": mapped[0]["desc"], "level": mapped[0]["protection_lvl"]} if any(re.findall(r'dangerous', mapped[0]["protection_lvl"], re.IGNORECASE)): # Permission is flagged as dangerous dangerous_perms[mapped[0]["permission"]] = mapped[0]["desc"] except Exception as e: continue self.packed_files[self.a.get_package()][file] = dangerous_perms return {"packed_file": self.packed_files, "detected_malware": self.detected_malware}
def main(args=None, stdout_suppress=False, stderr_suppress=False): with open(os.devnull, "w") as devnull: old_stdout = sys.stdout old_stderr = sys.stderr sys.stdout = devnull if stdout_suppress else sys.stdout sys.stderr = devnull if stderr_suppress else sys.stderr results = None try: if args and not isinstance(args, list): raise TypeError(f"Args are {type(args)}, which is not a list.") _args = _parseargs(args) _a, _vm, _vmx = AnalyzeAPK(_args.file) print(("Analyse file: {:s}".format(_args.file))) print(("Package name: {:s}".format(_a.get_package()))) if "android.permission.INTERNET" in _a.get_permissions(): print("App requires INTERNET permission. Continue analysis...") _result = { "trustmanager": [], "hostnameverifier": [], "onreceivedsslerror": [], } _result = _check_all(_vm, _vmx) results = _result if not _args.xml and not _args.output: _print_result(_result, _java=_args.java) else: _xml_result( _a, _result, printed=True if _args.xml else False, file=_args.output if _args.output else None, ) if _args.dir: print("Store decompiled Java code in {:s}".format(_args.dir)) _store_java(_vmx, _vm, _args) else: print( "App does not require INTERNET permission. No need to worry about SSL misuse... Abort!" ) except: import traceback # printing stack trace traceback.print_exc() finally: sys.stdout = old_stdout sys.stderr = old_stderr return results
def GetFromInstructions(apkfile, PMap): SuspiciousApiSet = set() URLDomainSet = set() UsedPermissionSet = set() restrictedApiSet = set() apiList = getSentitiveApiList() try: apkfile = os.path.abspath(apkfile) a, d, dx = AnalyzeAPK(apkfile) except Exception as e: print(e) return requestedPermission = a.get_permissions() print(requestedPermission) for method in d[0].get_methods(): g = dx.get_method(method) for BasicBlock in g.get_basic_blocks().get(): instructions = BasicBlockAttrBuilder.GetBasicBlockDalvikCode( BasicBlock) calledApis = getApiCalls(instructions) for apiCall in calledApis: for sApi in apiList: if apiCall in sApi: SuspiciousApiSet.add(sApi) break perm = pmap.GetPermFromFullApi(apiCall) if (perm != None): UsedPermissionSet.add(perm) if perm not in requestedPermission: restrictedApiSet.add(apiCall) for Instruction in instructions: URLSearch = re.search( r"https?://([\da-z\.-]+\.[a-z\.]{2, 6}|[\d.]+)[^'\"]*", Instruction, re.IGNORECASE) if (URLSearch): URL = URLSearch.group() Domain = re.sub( "https?://(.*)", "\g<1>", re.search(r"https?://([^/:\\\\]*)", URL, re.IGNORECASE).group(), 0, re.IGNORECASE) URLDomainSet.add(Domain) return SuspiciousApiSet, URLDomainSet, UsedPermissionSet, restrictedApiSet
def sigpidclass(path): # import pandas as pd # app.config['SFILE']=r"C:\Users\moham\drebinsayo\scripts\srd" # ds=app.config["SFILE"] # ps=os.listdir(ds) # for fffs in ps: # bbbs=os.path.join(app.config["SFILE"], fffs) # os.remove(bbbs) # files = request.files["file"] # apple=app.config['SFILE'] # files.save(os.path.join(app.config["SFILE"], files.filename)) # # import GetApkData as ge # # import importlib # # importlib.reload(ge) # sapple=files.filename # fsapple=ds+"\\"+sapple from androguard.misc import AnalyzeAPK a, d, dx = AnalyzeAPK(path) ttapp = a.get_permissions() #ttapp= android.permission.send_sms for t in ttapp: if t[0] != 'a': ttapp.remove(t) el = [] for i in ttapp: k = i.split('.')[-1] el.append(k) dpermf = pd.read_csv('onlydangsigt.csv') malapp = {i: 0 for i in dpermf.columns} del malapp['malware'] for i in el: for j in malapp: if (i == j): malapp[j] = 1 y_testap = list(malapp.values()) y_testwork = np.array([np.array(y_testap)]) model = pickle.load(open('rfc_sig.pkl', 'rb')) bans = model.predict(y_testwork) if (bans == False): band = "benign" else: band = "malware" return band
def analyze(app): result = {} # We open the APK apk_path = c.get_apk_path(app) # Here we check if the APK is actually there, otherwise we skip the analysis if (not os.path.exists(apk_path)): return a, d, dx = AnalyzeAPK(apk_path) # Get all the permissions requested by the app requested_permissions = a.get_permissions() # Get all the Android activities of the app activities = a.get_activities() # Get all String constants in the app presumably containing a URL urls = list() for u in dx.find_strings("http[s]?://."): urls.append(u.get_value()) # We pack together all the partial results result['permissions'] = requested_permissions result['activities'] = activities result['urls'] = urls # We save the result into a JSON file app_suffix_path = app['id'] + c.SEPARATOR + app['latest_crawled_version'] result_path = c.DATA_PATH + app_suffix_path + c.SEPARATOR + 'androguard.json' c.save(result_path, result) # Now we run also the Androwarn analysis (with no Play Store look up) data = perform_analysis(apk_path, a, d, dx, False) # We generate the JSON report with the following parameters # Verbosity level: 3 (advanced) # Report type: json # Output path: same pattern as all the other JSON files produced so far androwarn_report_path = c.DATA_PATH + app_suffix_path + c.SEPARATOR + 'androwarn.json' generate_report(app['id'], data, 3, 'json', androwarn_report_path)
def get_prediction(): f_rep = [0] * 900 dat = flask.request.get_json() name = dat['file_name'] with open(name, 'rb') as f: b = f.read() a, d, dx = AnalyzeAPK(b, raw = True) perms = str(a.get_permissions()) perm_count = len(perms) app_name = a.get_app_name() perms = re.findall(r"(?=.)[A-Z|_]+(?=')", perms) for p in perms: if p in features: f_rep[features.index(p)] = 1 xml = apk.APK(name).get_android_manifest_xml() xml = tostring(xml, encoding = 'unicode') intents = re.findall(r'(?<=<intent\-filter>)(.*?)(?=</intent\-filter>)',xml,re.DOTALL) for intent in intents: ints= (re.findall(r'(?<=\<action android:name=\")([^"]*)"',intent,re.DOTALL)) for a in ints: if a in features: f_rep[features.index(a)] = 1 classes = dx.get_external_classes() class_count = len(classes) for classObj in classes: vmClass = str(classObj.get_vm_class()) if vmClass not in api_dang: continue vmMeth = classObj.get_methods() for vmMethIter in vmMeth: met = str(vmMethIter.method) if met in features: f_rep[features.index(met)] = 1 f_rep = np.array(f_rep)[np.newaxis, :] pred = model.predict(f_rep)[0] response = {"appName": app_name, "class": pred, "permCount": perm_count, "fileName": name, "classCount": class_count} response = flask.jsonify(response) return response
def analyseAPK(apk_file): sha256 = get_sha256(apk_file) a, d, dx = AnalyzeAPK(apk_file) # ============== extract permissions =============== permissions = a.get_permissions() # ============== extract sensitiveApis =============== sensitiveApis = set() for dd in d: for method in dd.get_methods(): g = dx.get_method(method) for BasicBlock in g.get_basic_blocks().get(): instructions = BasicBlockAttrBuilder.GetBasicBlockDalvikCode( BasicBlock) PscoutApis = BasicBlockAttrBuilder.GetInvokedPscoutApis( instructions) sensitiveApis = sensitiveApis.union(PscoutApis) # ============== extract third-party-libraries =========== tpls = getThirdPartyLibrary(apk_file, sha256) return sha256, permissions, list(sensitiveApis), tpls
ret_type = androconf.is_android(args.APK) if ret_type != "APK": print("Not an APK file") sys.exit(1) apk, dex, dexes = AnalyzeAPK(args.APK) res = { 'app_name': apk.get_app_name(), 'package_name': apk.get_package(), 'providers': apk.get_providers(), 'new_permissions': extract_new_permissions(apk.get_permissions()), 'filters': get_intent_filers(apk), 'certificate': {}, 'wearable': apk.is_wearable(), 'max_sdk_version': (apk.get_max_sdk_version()), 'min_sdk_version': int(apk.get_min_sdk_version()), 'version_code': apk.xml['AndroidManifest.xml'].get( '{http://schemas.android.com/apk/res/android}versionCode'), 'libraries': list(apk.get_libraries()), 'androidtv': apk.is_androidtv(),
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"
class XRule: def __init__(self, apk): self.a, self.d, self.dx = AnalyzeAPK(apk) # Create Class, Method, String and Field # crossreferences for all classes in the Analysis. # self.dx.create_xref() self.pre_method0 = [] self.pre_method1 = [] self.same_sequence_show_up = [] self.same_operation = [] self.check_item = [False, False, False, False, False] # Pretty Table Output self.tb = PrettyTable() self.tb.field_names = ["Rule", "Confidence", "Score", "Weight"] self.tb.align = "l" # Sum of the each weight self.weight_sum = 0 # Sum of the each rule self.score_sum = 0 @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 (result is not None) and 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 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 result is not None: 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: raise ValueError("Method Not Found") def find_f_previous_method(self, base, top): """ Find the previous method based on base method before top method. This will append the method into self.pre_method0 :param base: :param top: :return: None """ method_set = self.upperFunc(base[0], base[1]) if method_set is not None: if top in method_set: self.pre_method0.append(base) else: for item in method_set: self.find_f_previous_method(item, top) def find_s_previous_method(self, base, top): """ Find the previous method based on base method before top method. This will append the method into self.pre_method1 :param base: :param top: :return: None """ method_set = self.upperFunc(base[0], base[1]) if method_set is not None: if top in method_set: self.pre_method1.append(base) else: for item in method_set: self.find_s_previous_method(item, top) def find_intersection(self, list1, list2, depth=1): """ Find the list1 ∩ list2. list1 & list2 are list within tuple, for example, [("class_name","method_name"),...] :param list1: :param list2: :param depth: MAX recursion :return: """ # Check both lists are not null if len(list1) > 0 and len(list2) > 0: # Limit up to three layers of the recursions. if depth == MAX_SEARCH_LAYER: return None # find ∩ result = set(list1).intersection(list2) if len(result) > 0: return result else: # Not found same method usage, try to find the next layer. next_list1 = [] next_list2 = [] for item in list1: if self.upperFunc(item[0], item[1]) is not None: next_list1 = self.upperFunc(item[0], item[1]) for item in list2: if self.upperFunc(item[0], item[1]) is not None: next_list2.extend(self.upperFunc(item[0], item[1])) # Append first layer into next layer for pre_list in list1: next_list1.append(pre_list) for pre_list in list2: next_list2.append(pre_list) depth += 1 # To find the same method, push the previous two # methods into the stack here. Once it found there # is same method, pop the previous method from stack. self.pre_method0.append(list1) self.pre_method1.append(list2) return self.find_intersection(next_list1, next_list2, depth) else: raise ValueError("List is Null") def check_sequence(self, same_method, f_func, s_func): """ Check if the first function appeared before the second function. :param same_method: the tuple with (class_name, method_name) :param f_func: the first show up function, which is (class_name, method_name) :param s_func: the tuple with (class_name, method_name) :return: boolean """ method_set = self.find_method(same_method[0], same_method[1]) seq_table = [] if method_set is not None: for md in method_set: for _, call, number in md.get_xref_to(): to_md_name = str(call.name) if (to_md_name == f_func[1]) or (to_md_name == s_func[1]): seq_table.append((call.name, number)) # sorting based on the value of the number if len(seq_table) < 2: # Not Found sequence in same_method return False seq_table.sort(key=operator.itemgetter(1)) idx = 0 length = len(seq_table) f_func_val = None s_func_val = None while idx < length: if seq_table[idx][0] == f_func[1]: f_func_val = idx break idx += 1 while length > 0: if seq_table[length - 1][0] == s_func[1]: s_func_val = length - 1 break length -= 1 if s_func_val > f_func_val: # print("Found sequence in :" + repr(same_method)) return True else: return False def check_parameter(self, common_method, fist_method_name, second_method_name): """ check the usage of the same parameter between two method. :param common_method: ("class_name", "method_name") :param fist_method_name: :param second_method_name: :return: """ pyeval = PyEval() # Check if there is an operation of the same register state = False for bytecode_obj in self.get_method_bytecode(common_method[0], common_method[1]): # ['new-instance', 'v4', Lcom/google/progress/SMSHelper;] instruction = [] instruction.append(bytecode_obj.mnemonic) if bytecode_obj.registers is not None: instruction.extend(bytecode_obj.registers) if bytecode_obj.parameter is not None: instruction.append(bytecode_obj.parameter) # for the case of MUTF8String instruction = [str(x) for x in instruction] if instruction[0] in pyeval.eval.keys(): pyeval.eval[instruction[0]](instruction) for table in pyeval.show_table(): for val_obj in table: matchers = [fist_method_name, second_method_name] matching = [ s for s in val_obj.called_by_func if all(xs in s for xs in matchers) ] if len(matching) > 0: state = True break return state def run(self, rule_obj): """ Run the five levels check to get the y_score. :param rule_obj: :return: """ # Level 1 if set(rule_obj.x1_permission).issubset(set(self.permissions)): self.check_item[0] = True # Level 2 test_md0 = rule_obj.x2n3n4_comb[0]["method"] test_cls0 = rule_obj.x2n3n4_comb[0]["class"] if self.find_method(test_cls0, test_md0) is not None: self.check_item[1] = True # Level 3 test_md1 = rule_checker.x2n3n4_comb[1]["method"] test_cls1 = rule_checker.x2n3n4_comb[1]["class"] if self.find_method(test_cls1, test_md1) is not None: self.check_item[2] = True # Level 4 # [('class_a','method_a'),('class_b','method_b')] # Looking for the first layer of the upperfunction upperfunc0 = self.upperFunc(test_cls0, test_md0) upperfunc1 = self.upperFunc(test_cls1, test_md1) same = self.find_intersection(upperfunc0, upperfunc1) if same is not None: for common_method in same: base_method_0 = (test_cls0, test_md0) base_method_1 = (test_cls1, test_md1) self.pre_method0.clear() self.pre_method1.clear() self.find_f_previous_method(base_method_0, common_method) self.find_s_previous_method(base_method_1, common_method) # TODO It may have many previous method in self.pre_method pre_0 = self.pre_method0[0] pre_1 = self.pre_method1[0] if self.check_sequence(common_method, pre_0, pre_1): self.check_item[3] = True self.same_sequence_show_up.append(common_method) # Level 5 if self.check_parameter(common_method, str(pre_0[1]), str(pre_1[1])): self.check_item[4] = True self.same_operation.append(common_method) def show_easy_report(self, rule_obj): """ Show the summary report. :param rule_obj: :return: """ # Count the confidence confidence = str(self.check_item.count(True) * 20) + "%" conf = self.check_item.count(True) weight = rule_obj.get_score(conf) score = rule_obj.yscore self.tb.add_row( [green(rule_obj.crime), yellow(confidence), score, red(weight)]) # add the weight self.weight_sum += weight # add the score self.score_sum += score def show_detail_report(self, rule_obj): """ Show the detail report :param rule_obj: :return: """ # Count the confidence print("") print("Confidence:" + str(self.check_item.count(True) * 20) + "%") print("") if self.check_item[0]: COLOR_OUTPUT_RED("\t[" + u"\u2713" + "]") COLOR_OUTPUT_GREEN(bold("1.Permission Request")) print("") for permission in rule_obj.x1_permission: print("\t\t" + permission) if self.check_item[1]: COLOR_OUTPUT_RED("\t[" + u"\u2713" + "]") COLOR_OUTPUT_GREEN(bold("2.Native API Usage")) print("") print("\t\t" + rule_obj.x2n3n4_comb[0]["method"]) if self.check_item[2]: COLOR_OUTPUT_RED("\t[" + u"\u2713" + "]") COLOR_OUTPUT_GREEN(bold("3.Native API Combination")) print("") print("\t\t" + rule_obj.x2n3n4_comb[0]["method"]) print("\t\t" + rule_obj.x2n3n4_comb[1]["method"]) if self.check_item[3]: COLOR_OUTPUT_RED("\t[" + u"\u2713" + "]") COLOR_OUTPUT_GREEN(bold("4.Native API Sequence")) print("") print("\t\t" + "Sequence show up in:") for seq_methon in self.same_sequence_show_up: print("\t\t" + repr(seq_methon)) if self.check_item[4]: COLOR_OUTPUT_RED("\t[" + u"\u2713" + "]") COLOR_OUTPUT_GREEN(bold("5.Native API Use Same Parameter")) print("") for seq_operation in self.same_operation: print("\t\t" + repr(seq_operation))
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=".*", descriptor=None): """ Find method from given class_name and method_name, default is find all method. :param descriptor: :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 """ regex_method_name = f"^{method_name}$" if descriptor is not None: des = descriptor.replace(")", "\)").replace("(", "\(") result = self.analysis.find_methods(class_name, regex_method_name, descriptor=des) if list(result): return self.analysis.find_methods(class_name, regex_method_name, descriptor=des) else: return None else: result = self.analysis.find_methods(class_name, regex_method_name) if list(result): return self.analysis.find_methods(class_name, regex_method_name) else: 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(): # Call is the MethodAnalysis in the androguard # call.class_name, call.name, call.descriptor upperfunc_result.append(call) 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 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
def main(): input_dir = './recv' file_num = 1 tic = time.time() # user_input = raw_input("Masukkan Lokasi File: ") user_input = os.path.join(input_dir, '%06d.apk' % file_num) assert os.path.exists( user_input), "file tidak ditemukan pada, " + str(user_input) print("File Ditemukan !!") a, d, dx = AnalyzeAPK(user_input) dx.create_xref() package_name = a.get_package() permissions = a.get_permissions() val = [ "android.permission.READ_PHONE_STATE", "android.permission.RECEIVE_BOOT_COMPLETED", "android.permission.READ_SMS", "android.permission.WRITE_SMS", "android.permission.SEND_SMS", "android.permission.RECEIVE_SMS", "android.permission.CALL_PHONE", "android.permission.CHANGE_WIFI_STATE", "android.permission.RESTART_PACKAGES", "android.permission.GET_TASKS" ] # valApi = ['Landroid/content/Intent;->getAction()','Ldalvik/system/DexClassLoader;->loadClass','Landroid/telephony/TelephonyManager;->getDeviceId()','Landroid/telephony/TelephonyManager;->getLine1Number()','Landroid/telephony/TelephonyManager;->getNetworkOperator()','Landroid/telephony/TelephonyManager;->getSimSerialNumber()','Landroid/telephony/TelephonyManager;->getSimOperator()','Landroid/telephony/TelephonyManager;->getSubscriberId()','Landroid/telephony/SmsManager;->sendTextMessage','Landroid/location/LocationManager;->getLastKnownLocation'] ps = [] API_freq = dict() frequency(dx, API_freq) fs = open( os.path.join('./Documents/API-Kurang/coba', package_name + '.txt'), "w") #datas = str(sorted(API_freq.items(), key=lambda b:b[1], reverse=True)) datas = str(sorted(API_freq.items(), key=lambda b: b[1], reverse=True)) #print(sorted(API_freq.items(), key=lambda b:b[1], reverse=True)) print(permissions) for i in range(0, len(val)): if (check(permissions, val[i])): ps.append(1) else: ps.append(0) if 'Landroid/content/Intent;->getAction()' in datas: ps.append(1) else: ps.append(0) if 'Ldalvik/system/DexClassLoader;->loadClass' in datas: ps.append(1) else: ps.append(0) if 'Landroid/telephony/TelephonyManager;->getDeviceId()' in datas: ps.append(1) else: ps.append(0) if 'Landroid/telephony/TelephonyManager;->getLine1Number()' in datas: ps.append(1) else: ps.append(0) if 'Landroid/telephony/TelephonyManager;->getNetworkOperator()' in datas: ps.append(1) else: ps.append(0) if 'Landroid/telephony/TelephonyManager;->getSimSerialNumber()' in datas: ps.append(1) else: ps.append(0) if 'Landroid/telephony/TelephonyManager;->getSimOperator()' in datas: ps.append(1) else: ps.append(0) if 'Landroid/telephony/TelephonyManager;->getSubscriberId()' in datas: ps.append(1) else: ps.append(0) if 'Landroid/telephony/SmsManager;->sendTextMessage' in datas: ps.append(1) else: ps.append(0) if 'Landroid/location/LocationManager;->getLastKnownLocation' in datas: ps.append(1) else: ps.append(0) ps.append(package_name) print(ps) # fs.write(datas) # #print(time.time()-tic) # fs.close() #print(sorted(API_freq.items(), key=lambda b:b[1], reverse=True)) print(time.time() - tic) print("Done !!!") return ps file_num += 1
from androguard.misc import AnalyzeAPK a, d, dx = AnalyzeAPK('Virus0e69af88dcbb469e30f16609b10c926c.apk') activity = a.get_activities() service = a.get_services() provider = a.get_providers() receiver = a.get_receivers() permission = a.get_permissions() print(activity) print(service) print(provider) print(receiver) print(permission) ''' ['com.security.service.MainActivity'] [] [] ['com.security.service.receiver.ActionReceiver', 'com.security.service.receiver.SmsReceiver', 'com.security.service.receiver.RebootReceiver'] ['android.permission.RECEIVE_SMS', 'android.permission.SEND_SMS'] '''
class AndroguardImp(BaseApkinfo): """Information about apk based on androguard analysis""" __slots__ = ("apk", "dalvikvmformat", "analysis") def __init__(self, apk_filepath: Union[str, PathLike]): super().__init__(apk_filepath, "androguard") if self.ret_type == "APK": # return the APK, list of DalvikVMFormat, and Analysis objects self.apk, self.dalvikvmformat, self.analysis = AnalyzeAPK( apk_filepath) elif self.ret_type == "DEX": # return the sha256hash, DalvikVMFormat, and Analysis objects _, _, self.analysis = AnalyzeDex(apk_filepath) else: raise ValueError("Unsupported File type.") @property def permissions(self) -> List[str]: if self.ret_type == "APK": return self.apk.get_permissions() if self.ret_type == "DEX": return [] @property def android_apis(self) -> Set[MethodObject]: apis = set() for external_cls in self.analysis.get_external_classes(): for meth_analysis in external_cls.get_methods(): if meth_analysis.is_android_api(): apis.add(meth_analysis) return {self._convert_to_method_object(api) for api in apis} @property def custom_methods(self) -> Set[MethodObject]: return { self._convert_to_method_object(meth_analysis) for meth_analysis in self.analysis.get_methods() if not meth_analysis.is_external() } @property def all_methods(self) -> Set[MethodObject]: return { self._convert_to_method_object(meth_analysis) for meth_analysis in self.analysis.get_methods() } @functools.lru_cache() def find_method( self, class_name: Optional[str] = ".*", method_name: Optional[str] = ".*", descriptor: Optional[str] = ".*", ) -> MethodObject: 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, ) result = next(method_result, None) return self._convert_to_method_object(result) if result else None @functools.lru_cache() def upperfunc(self, method_object: MethodObject) -> Set[MethodObject]: method_analysis = method_object.cache return { self._convert_to_method_object(call) for _, call, _ in method_analysis.get_xref_from() } def lowerfunc(self, method_object: MethodObject) -> Set[MethodObject]: method_analysis = method_object.cache return {(self._convert_to_method_object(call), offset) for _, call, offset in method_analysis.get_xref_to()} def get_method_bytecode(self, method_object: MethodObject) -> Set[MethodObject]: method_analysis = method_object.cache 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, ) else: index_of_parameter_starts = None for i in range(length_operands - 1, -1, -1): if not isinstance(ins.get_operands()[i][0], Operand): index_of_parameter_starts = i break if index_of_parameter_starts is not None: parameter = ins.get_operands( )[index_of_parameter_starts] parameter = (parameter[2] if len(parameter) == 3 else parameter[1]) for i in range(index_of_parameter_starts): reg_list.append( "v" + str(ins.get_operands()[i][1]), ) else: parameter = None for i in range(length_operands): reg_list.append( "v" + str(ins.get_operands()[i][1]), ) bytecode_obj = BytecodeObject(ins.get_name(), reg_list, parameter) yield bytecode_obj except AttributeError: # TODO Log the rule here pass def get_strings(self) -> str: return { str(string_analysis.get_orig_value()) for string_analysis in self.analysis.get_strings() } @functools.lru_cache() def _construct_bytecode_instruction(self, instruction): """ Construct a list of strings from the given bytecode instructions. :param instruction: instruction instance from androguard :return: a list with bytecode instructions strings """ instruction_list = [instruction.get_name()] reg_list = [] # count the number of the registers. length_operands = len(instruction.get_operands()) if length_operands == 0: # No register, no parameter return instruction_list elif length_operands == 1: # Only one register reg_list.append( f"v{instruction.get_operands()[length_operands - 1][1]}") instruction_list.extend(reg_list) return instruction_list elif length_operands >= 2: # the last one is parameter, the other are registers. parameter = instruction.get_operands()[length_operands - 1] for i in range(length_operands - 1): reg_list.append("v" + str(instruction.get_operands()[i][1]), ) parameter = parameter[2] if len(parameter) == 3 else parameter[1] instruction_list.extend(reg_list) instruction_list.append(parameter) return instruction_list @functools.lru_cache() def get_wrapper_smali( self, parent_method: MethodObject, first_method: MethodObject, second_method: MethodObject, ) -> Dict[str, Union[BytecodeObject, str]]: method_analysis = parent_method.cache result = { "first": None, "first_hex": None, "second": None, "second_hex": None, } first_method_pattern = ( f"{first_method.class_name}" f"->{first_method.name}{first_method.descriptor}") second_method_pattern = ( f"{second_method.class_name}" f"->{second_method.name}{second_method.descriptor}") for _, ins in method_analysis.get_method().get_instructions_idx(): if first_method_pattern in str(ins): result["first"] = self._construct_bytecode_instruction(ins) result["first_hex"] = ins.get_hex() if second_method_pattern in str(ins): result["second"] = self._construct_bytecode_instruction(ins) result["second_hex"] = ins.get_hex() return result @property def superclass_relationships(self) -> Dict[str, Set[str]]: hierarchy_dict = defaultdict(set) for _class in self.analysis.get_classes(): hierarchy_dict[str(_class.name)].add(str(_class.extends)) hierarchy_dict[str(_class.name)].union( str(implements) for implements in _class.implements) return hierarchy_dict @property def subclass_relationships(self) -> Dict[str, Set[str]]: hierarchy_dict = defaultdict(set) for _class in self.analysis.get_classes(): class_name = str(_class.name) hierarchy_dict[str(_class.extends)].add(class_name) for implements in _class.implements: hierarchy_dict[str(implements)].add(class_name) return hierarchy_dict @staticmethod @functools.lru_cache def _convert_to_method_object( method_analysis: MethodAnalysis, ) -> MethodObject: return MethodObject( access_flags=method_analysis.access, class_name=str(method_analysis.class_name), name=str(method_analysis.name), descriptor=str(method_analysis.descriptor), cache=method_analysis, )
def api_check(folder, APKname): if os.path.exists("result/" + folder + APKname + 'data/'): print(APKname + " Already scanned") return print("Starting apk:" + APKname) apk_start_time = time.time() RESULTdict = dict.fromkeys(RESULT_PARAMS, 0) ##отдельные словари для фич OtherDict = dict.fromkeys(('obfuscation', 'database'), 0) APIdict = dict.fromkeys((API_CALLS + API_ClASS), 0) permission_dict = dict.fromkeys(PERMISSIONS, 0) strings_dict = dict.fromkeys(API_SYSTEM_COMMANDS, 0) groupAPI_dict = dict.fromkeys(APIGROUPS, 0) ##№№№ #a-APK d[0]-DalvikVMFormat dx-Analysis try: a, d, dx = AnalyzeAPK(folder + APKname) except: print(" ERROR: Androguard parse error, skipping file") return ### temp = a.get_details_permissions() temp2 = a.get_declared_permissions_details() temp3 = a.get_uses_implied_permission_list() # ########TODO почитать про использование пермишинсов без запросов #### RESULTdict["APP_Name"] = APKname RESULTdict['folder'] = folder #methods = [] #подозрительные строки RESULTdict["warn_strings"] = [] strings = dx.get_strings_analysis() #w=d[0].get_strings() list_system_commands = read_system_commands(strings, API_SYSTEM_COMMANDS) for i in list_system_commands: #print(i) RESULTdict["warn_strings"].append(i) for i in list_system_commands: strings_dict[i] += 1 ### общая информация RESULTdict['permissions'] = a.get_permissions() RESULTdict['activities'] = a.get_activities() RESULTdict['providers'] = a.get_providers() RESULTdict['services'] = a.get_services() RESULTdict['libraries'] = a.get_libraries() RESULTdict['is_obfuscation'] = 1 if is_ascii_obfuscation(d[0]) else 0 RESULTdict['is_database'] = 1 if d[0].get_regex_strings(DB_REGEX) else 0 #TODO intents_analysis from new.py OtherDict['obfuscation'] = RESULTdict['is_obfuscation'] OtherDict['database'] = RESULTdict['is_database'] #permissions RESULTdict['warn_permissions'] = [] #RESULTdict['feature_vectors']['permissions'] = [] for permission in PERMISSIONS: if permission in RESULTdict['permissions']: RESULTdict['warn_permissions'].append(permission) permission_dict[permission] = 1 ########################################################################### #TODO подсчет групп АПИ и системных команд для вектора фич ########################################################################### #API RESULTdict['API_groups'] = [] external_classes = dx.get_external_classes() for i in external_classes: class_name = i.get_vm_class() methods_list = class_name.get_methods() for method in methods_list: a = '%s' % method.get_class_name().replace(';', '') b = '%s' % method.get_name() c = '%s' % method.get_descriptor() #TODO permission_api_name https://androguard.readthedocs.io/en/latest/api/androguard.core.analysis.html?highlight=permission#androguard.core.analysis.analysis.ExternalMethod.permission_api_name if b in API_CALLS: APIdict[b] += 1 ###TODO !!!нужна нормализация данных if a in API_ClASS: APIdict[a] += 1 temp = GroupAPI_Checker.checkAPIGroup(a.replace('/', '.')[1:], b) if (temp != None): groupAPI_dict[temp] += 1 RESULTdict['API_groups'].append(temp) ##запись общих параметров with open("result/" + 'API_CALLS.csv', 'a', encoding='utf8') as csvfile: fieldnames = (('APP_Name', 'folder') + API_CALLS + API_ClASS) writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") #writer.writeheader() tempDict = APIdict.copy() tempDict['APP_Name'] = APKname tempDict['folder'] = folder writer.writerow(tempDict) with open("result/" + 'OtherDict.csv', 'a', encoding='utf8') as csvfile: fieldnames = 'APP_Name', 'folder', 'obfuscation', 'database' writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") #writer.writeheader() tempDict = OtherDict.copy() tempDict['APP_Name'] = APKname tempDict['folder'] = folder writer.writerow(tempDict) with open("result/" + 'permission_dict.csv', 'a', encoding='utf8') as csvfile: fieldnames = ('APP_Name', 'folder') + PERMISSIONS writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") #writer.writeheader() tempDict = permission_dict.copy() tempDict['APP_Name'] = APKname tempDict['folder'] = folder writer.writerow(tempDict) with open("result/" + 'strings_dict.csv', 'a', encoding='utf8') as csvfile: fieldnames = ('APP_Name', 'folder') + API_SYSTEM_COMMANDS writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") #writer.writeheader() tempDict = strings_dict.copy() tempDict['APP_Name'] = APKname tempDict['folder'] = folder writer.writerow(tempDict) with open("result/" + 'groupAPI_dict.csv', 'a', encoding='utf8') as csvfile: fieldnames = ('APP_Name', 'folder') + APIGROUPS writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") #writer.writeheader() tempDict = groupAPI_dict.copy() tempDict['APP_Name'] = APKname tempDict['folder'] = folder writer.writerow(tempDict) with open("result/" + 'RESULTdict.csv', 'a', encoding='utf8') as csvfile: fieldnames = RESULT_PARAMS writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") #writer.writeheader() writer.writerow(RESULTdict) ##запись параметров данного приложения try: if os.path.exists("result/" + folder): os.mkdir('result/' + folder + APKname + 'data') else: os.mkdir('result/' + folder) os.mkdir('result/' + folder + APKname + 'data') except OSError: print("Создать директорию %s не удалось" % ('result/' + folder + APKname + 'data')) else: with open("result/" + folder + APKname + 'data/RESULT.csv', 'w', encoding='utf8') as csvfile: fieldnames = RESULT_PARAMS writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") writer.writeheader() writer.writerow(RESULTdict) with open("result/" + folder + APKname + 'data/OtherDict.csv', 'w', encoding='utf8') as csvfile: fieldnames = 'obfuscation', 'database' writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") writer.writeheader() writer.writerow(OtherDict) with open("result/" + folder + APKname + 'data/APIdict.csv', 'w', encoding='utf8') as csvfile: fieldnames = API_CALLS + API_ClASS writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") writer.writeheader() writer.writerow(APIdict) with open("result/" + folder + APKname + 'data/permission_dict.csv', 'w', encoding='utf8') as csvfile: fieldnames = PERMISSIONS writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") writer.writeheader() writer.writerow(permission_dict) with open("result/" + folder + APKname + 'data/strings_dict.csv', 'w', encoding='utf8') as csvfile: fieldnames = API_SYSTEM_COMMANDS writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") writer.writeheader() writer.writerow(strings_dict) with open("result/" + folder + APKname + 'data/groupAPI_dict.csv', 'w', encoding='utf8') as csvfile: fieldnames = APIGROUPS writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=";", lineterminator="\n") writer.writeheader() writer.writerow(groupAPI_dict) print("APK done:{} ".format(time.time() - apk_start_time))
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 get_strings(self): all_strings = set() for string_analysis in self.analysis.get_strings(): all_strings.add(str(string_analysis.get_orig_value())) return all_strings @functools.lru_cache() def construct_bytecode_instruction(self, instruction): """ Construct a list of strings from the given bytecode instructions. :param instruction: instruction instance from androguard :return: a list with bytecode instructions strings """ instruction_list = [instruction.get_name()] reg_list = [] # count the number of the registers. length_operands = len(instruction.get_operands()) if length_operands == 0: # No register, no parameter return instruction_list elif length_operands == 1: # Only one register reg_list.append(f"v{instruction.get_operands()[length_operands - 1][1]}") instruction_list.extend(reg_list) return instruction_list elif length_operands >= 2: # the last one is parameter, the other are registers. parameter = instruction.get_operands()[length_operands - 1] for i in range(0, length_operands - 1): reg_list.append( "v" + str(instruction.get_operands()[i][1]), ) if len(parameter) == 3: # method or value parameter = parameter[2] else: # Operand.OFFSET parameter = parameter[1] instruction_list.extend(reg_list) instruction_list.append(parameter) return instruction_list @functools.lru_cache() def get_wrapper_smali(self, method_analysis, first_method, second_method): """ Return the dict of two method smali code from given method analysis object, only for self-defined method. :param method_analysis: :return: { "first": "invoke-virtual v5, Lcom/google/progress/Locate;->getLocation()Ljava/lang/String;", "second": "invoke-virtual v3, v0, v4, Lcom/google/progress/SMSHelper;->sendSms(Ljava/lang/String; Ljava/lang/String;)I" } """ result = {"first": None, "first_hex": None, "second": None, "second_hex": None} first_method_pattern = f"{first_method.class_name}->{first_method.name}{first_method.descriptor}" second_method_pattern = f"{second_method.class_name}->{second_method.name}{second_method.descriptor}" for _, ins in method_analysis.get_method().get_instructions_idx(): if first_method_pattern in str(ins): result["first"] = self.construct_bytecode_instruction(ins) result["first_hex"] = ins.get_hex() if second_method_pattern in str(ins): result["second"] = self.construct_bytecode_instruction(ins) result["second_hex"] = ins.get_hex() return result
def get_permissions(apk_file: AnalyzeAPK) -> List[str]: return apk_file.get_permissions()
for name in obj: for action,intent_name in a.get_intent_filters(itemtype, name).items(): for intent in intent_name: alista.append(intent) return alista i = 0 while i < 1200: file = (data.iloc[i][0]) numero = (data.iloc[i][1]) try: a, d, dx = AnalyzeAPK(file) print ("===> "+ file + " <===") perms = (a.get_permissions()) prov = (a.get_providers()) serv = (a.get_services()) sint = helper(serv,'service') receivers = a.get_receivers() r = [] for x in receivers: r.append(x) rint = helper(receivers,'receiver') writer.writerow((numero,file,perms,prov,serv,sint,r,rint)) csv_base.flush() except zipfile.BadZipFile: # WiNDOWS FILE print ('THEY TRY 2 GET ME' + str(numero) ) except KeyError: # DIC ERROR print('F**K ====>' + str(numero)) except TypeError: print ('Really ====>' + str(numero))
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
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) def __repr__(self): return f"<Apkinfo-APK:{self.apk_filename}>" @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): 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
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