def get_cert(self): try: pinfo("开始获取签名文件") files = [ f for f in os.listdir(self.cert_file) if os.path.isfile(os.path.join(self.cert_file, f)) ] for file_name in files: if file_name.lower().split('.')[-1] in ['rsa', 'dsa']: args = [ ToolSettings.JAVA_PATH, '-jar', ToolSettings.TOOL_CERTPRINT, os.path.join(self.cert_file, file_name) ] issued = 'good' dat = subprocess.check_output(args) if re.findall( r"Issuer: CN=Android Debug|Subject: CN=Android Debug", dat): issued = 'bad' if re.findall(r"\[SHA1withRSA\]", dat): issued = 'bad hash' result = { 'cert_file': file_name, 'cert_info': dat, 'issued': issued } return result except: einfo("签名获取异常")
def elf_analysis(app_dir, typ): """Perform the elf analysis.""" try: print "[INFO] Static Android Binary Analysis Started" elf_desc = { 'elf_no_pi': (u'Found elf built without Position Independent Executable (PIE) flag', u'high', u'In order to prevent an attacker from reliably jumping to, for example, a particular' u' exploited function in memory, Address space layout randomization (ASLR) randomly ' u'arranges the address space positions of key data areas of a process, including the ' u'base of the executable and the positions of the stack, heap and libraries. Built with' u' option <strong>-pie</strong>.'), 'elf_no_sp': (u'Found elf built without Stack Protection', u'high', u'Stack canaries can greatly increase the difficulty of exploiting a stack buffer ' u'overflow because it forces the attacker to gain control of the instruction pointer' u' by some non-traditional means such as corrupting other important variables on the' u' stack. Built with option <strong>-fstack-protector</strong>.'), } elf_an_dic = {} for k in elf_desc.keys(): elf_an_dic[k] = [] libdir = os.path.join(app_dir, "lib") if os.path.exists(libdir): for pdir, dirl, filel in os.walk(libdir): for filename in filel: if filename.endswith(".so"): try: filepath = os.path.join(pdir, filename) f = io.open(filepath, mode='rb') has_pie, has_sg = check_elf_built(f) f.close() if has_pie == False: if "nopie" in filename or "nonpie" in filename or "no-pie" in filename: pass else: elf_an_dic['elf_no_pi'].append( filepath.replace(libdir, "lib")) if has_sg == False: elf_an_dic['elf_no_sp'].append( filepath.replace(libdir, "lib")) except Exception as e: pass res = [] for k, filelist in elf_an_dic.items(): if len(filelist): descs = elf_desc.get(k) res.append({ 'title': descs[0], 'stat': descs[1], 'desc': descs[2], 'file': " ".join(filelist), }) return res except: pinfo("[ERROR] Performing Binary Analysis")
def dex_to_smali(self): pinfo("DEX -> SMALI") output = os.path.join(self.extract_path, 'smali_source') for dex in self.dex_path: args = [ ToolSettings.JAVA_PATH, '-jar', ToolSettings.TOOL_BAKSAMLI, dex, '-o', output ] exec_command_silence(args)
def jar_to_java(self): pinfo("JAR -> JAVA") jar_path = [dex.replace('dex', 'jar') for dex in self.dex_path] output = os.path.join(self.extract_path, 'java_source') for jar in jar_path: args = [ ToolSettings.JAVA_PATH, '-jar', ToolSettings.TOOL_JDCORE, jar, output ] exec_command_silence(args)
def dex_to_jar(self): pinfo("DEX -> JAR") # files = os.walk(self.extract_path).next()[2] self.dex_path = [ os.path.join(self.extract_path, f) for f in self.files if '.' in f and f.split('.')[-1] == 'dex' ] for dex in self.dex_path: args = [ ToolSettings.TOOL_DEX2JAR, dex, '-o', dex.replace('dex', 'jar') ] exec_command_silence(args)
def find_redundancy_permissions(self): redundancy_permissions = [] pinfo("Now finding the redundancy permissions...") try: apk, d, dx = AnalyzeAPK(self.APP_PATH, decompiler="dad") used_permissions = show_Permissions(dx) for perm in self.PERMISSIONS: if perm not in used_permissions.keys(): redundancy_permissions.append(perm) except Exception as e: einfo("Find redundancy permissions occured error: " + str(e)) finally: return redundancy_permissions
def res_analysis(app_dir, typ): """Perform the elf analysis.""" try: print "[INFO] Static Android Resourse Analysis Started" elf_desc = { 'html_infected': (u'Found html files infected by malware.', u'high', u'The built environment was probably infected by malware, The html file ' u'used in this APK is infected.'), } html_an_dic = {} for k in elf_desc.keys(): html_an_dic[k] = [] resraw = os.path.join(app_dir, "res", "raw") assets = os.path.join(app_dir, "assets") for resdir in (resraw, assets): if os.path.exists(resdir) and os.path.isdir(resdir): for pdir, dirl, filel in os.walk(resdir): for filename in filel: if filename.endswith(".htm") or filename.endswith( ".html"): try: filepath = os.path.join(pdir, filename) buf = "" with io.open(filepath, mode='rb') as filp: buf = filp.read() if "svchost.exe" in buf: html_an_dic['html_infected'].append( filepath.replace(app_dir, "")) except Exception as e: pass res = [] for k, filelist in html_an_dic.items(): if len(filelist): descs = elf_desc.get(k) res.append({ 'title': descs[0], 'stat': descs[1], 'desc': descs[2], 'file': " ".join(filelist), }) return res except: pinfo("[ERROR] Performing Resourse Analysis")
def is_protected(self): # 获得加固状态 app = open(self.apk_path, 'rb') data = app.read() app.close() protected = False enterprise = 'None' for key, value in AndroidFeature.protect_dic.items(): if key in data: protected = True enterprise = value break if protected: pinfo('%s被%s加固了' % (self.base_context[2], enterprise)) else: pinfo('%s没有被加固!' % self.base_context[2]) context = {'protected': protected, 'enterprise': enterprise} self.static_context.update(context)
def calc_score(self): score_weight = { 'Bug': 0.8, 'Threat': 0.3, 'Sensitive': 0.2, } score = 100.0 check_result = self.aobj.checkresult_set.all() for result in check_result: if result.number == 0: continue else: vunl_db = result.vulnerability weight = vunl_db.knowledge.rank v_type = getattr(vunl_db, 'v_type') # 如果是敏感信息,则按个数计算分数 if v_type == 'Sensitive': weight = weight * result.number if weight >= 100: weight = 100 # 若判断条件为‘存在则安全’,则表示其为加分项目 # 此时判断检测结果表中该项对应的漏洞数目是否为0 # 若为0,则表示通过检查,应该加分。否则减分 if vunl_db.judge_reverse and result.number == 0: score += weight * score_weight.get(v_type) elif result.number != 0: pinfo('subtract %s cause %s' % (weight, str(vunl_db.name))) score -= weight * score_weight.get(v_type) # 控制分数在10-90的区间内 if score < 10: score = 10 elif score > 90: score = 90 if self.aobj is not None: self.aobj.Score = score self.aobj.save() return score
def get_hardcoded_cert_keystore(self): pinfo('开始获取硬编码Certificates/Keystores') certz = [] key_store = [] try: for file_name in self.files: ext = file_name.split('.')[-1] if re.search("cer|pem|cert|crt|pub|key|pfx|p12", ext): pinfo('获取到硬编码证书文件') certz.append(file_name) if re.search("jks|bks", ext): pinfo('获取到硬编码keystore文件') key_store.append(file_name) except: einfo("获取硬编码Certificates/Keystores错误") result = {'certz': certz, 'key_store': key_store} return result
def get_info_from_xml(self): context = {} if self.AAPT_XML: pinfo("应用XML数据获取成功!") a_xml = self.AAPT_XML['manifest'] version_code = None version_name = None package_name = None min_sdk = None target_sdk = None mainact = None permissions = [] activities = [] receivers = [] providers = [] services = [] exp_activities = [] exp_receivers = [] exp_services = [] exp_providers = [] version_code_regx = '\)(?P<version>0x\w+)' version_name_regx = '\(Raw: "(?P<vname>.+)"\)' package_name_regx = '\(Raw: "(?P<package>.+)"\)' min_sdk_regx = '\)(?P<min_sdk>0x\w+)' target_sdk_regx = '\)(?P<target_sdk>0x\w+)' permission_regx = '\(Raw: "(?P<permission>.+)"\)' component_regx = '\(Raw: "(?P<comp>.+)"\)' for node in a_xml: if 'android:versionCode' in node['line'] and not version_code: version_re = re.compile(version_code_regx) version_code = version_re.findall(node['line']) if len(version_code) > 0: version_code = version_code[0] print "Found version code : %s" % version_code else: version_code = 'unknown' # a_xml.remove(node) if 'android:versionName' in node['line'] and not version_name: # (Raw: "5.5.365") version_name_re = re.compile(version_name_regx) version_name = version_name_re.findall(node['line']) if len(version_name) > 0: version_name = version_name[0] print "Found version name : %s" % version_name else: version_name = 'unknown' # a_xml.remove(node) if 'package="' in node['line'] and not package_name: # package="com.SvADK8.s6lFYYB" (Raw: "com.SvADK8.s6lFYYB") package_name_re = re.compile(package_name_regx) package_name = package_name_re.findall(node['line']) if len(package_name) > 0: package_name = package_name[0] print "Found package name : %s" % package_name else: package_name = 'unknown' # a_xml.remove(node) if 'uses-sdk' in node['line']: for node_attr in node['attr']: if 'android:minSdkVersion' in node_attr[ 'line'] and not min_sdk: # android:minSdkVersion(0x0101020c)=(type 0x10)0x8 min_sdk_re = re.compile(min_sdk_regx) min_sdk = min_sdk_re.findall(node_attr['line']) if len(min_sdk) > 0: min_sdk = min_sdk[0] print "Found min_sdk version : %s" % min_sdk else: min_sdk = 'unknown' # node['attr'].remove(node_attr) if 'android:targetSdkVersion' in node_attr[ 'line'] and not target_sdk: # android:targetSdkVersion(0x01010270)=(type 0x10)0x13 target_sdk_re = re.compile(target_sdk_regx) target_sdk = target_sdk_re.findall(node_attr['line']) if len(target_sdk) > 0: target_sdk = target_sdk[0] print "Found target_sdk version : %s" % target_sdk else: target_sdk = 'unknown' # node['attr'].remove(node_attr) if 'uses-permission' in node['line']: for node_attr in node['attr']: permission_re = re.compile(permission_regx) t_perm = permission_re.findall(node_attr['line']) if len(t_perm) > 0: print "Found permission : %s" % t_perm[0] permissions = permissions.__add__(t_perm) if 'application' in node['line']: component_re = re.compile(component_regx) for app_node in node['attr']: if app_node['feature'].lower() == 'activity': exported = True is_intent = False for app_node_node in app_node['attr']: if 'android:name' in app_node_node['line']: activity = component_re.findall( app_node_node['line']) if len(activity) > 0: # print "Found activity : %s" % activity[0] activities = activities.__add__(activity) if 'android:exported' in app_node_node[ 'line'] and '0xffffffff' in app_node_node[ 'line'].lower(): exported = False if 'intent-filter' in app_node_node[ 'line'] and exported: is_intent = True for compo_attr in app_node_node['attr']: if 'action' in compo_attr['line']: # exclude main activity for action in compo_attr['attr']: if 'android.intent.action.MAIN' in action[ 'line']: exported = False mainact = activity[0] if exported and is_intent: exp_activities.append(activity[0]) # print "<--!-->Found exported activity : %s" % # activity[0] if app_node['feature'].lower() == 'receiver': exported = True is_intent = False for app_node_node in app_node['attr']: if 'android:name' in app_node_node['line']: receiver = component_re.findall( app_node_node['line']) if len(receiver) > 0: print "Found receiver : %s" % receiver[0] receivers.__add__(receiver) if 'android:exported' in app_node_node[ 'line'] and '0xffffffff' in app_node_node[ 'line'].lower(): exported = False if 'intent-filter' in app_node_node['line']: is_intent = True if exported and is_intent: exp_receivers.append(receiver[0]) # print "<--!-->Found exported receiver : %s" % # receiver[0] if app_node['feature'].lower() == 'service': exported = True is_intent = False for app_node_node in app_node['attr']: if 'android:name' in app_node_node['line']: service = component_re.findall( app_node_node['line']) if len(service) > 0: # print "Found service : %s" % service[0] services.__add__(service) if 'android:exported' in app_node_node[ 'line'] and '0xffffffff' in app_node_node[ 'line'].lower(): exported = False if 'intent-filter' in app_node_node['line']: is_intent = True if exported and is_intent: exp_services.append(service[0]) # print "<--!-->Found exported service : %s" % # service[0] if app_node['feature'].lower() == 'provider': exported = True is_intent = False for app_node_node in app_node['attr']: if 'android:name' in app_node_node['line']: provider = component_re.findall( app_node_node['line']) if len(provider) > 0: # print "Found provider : %s" % provider[0] providers.__add__(provider) if 'android:exported' in app_node_node[ 'line'] and '0xffffffff' in app_node_node[ 'line'].lower(): exported = False if 'intent-filter' in app_node_node['line']: is_intent = True if exported and is_intent: exp_providers.append(provider[0]) # print "<--!-->Found exported provider : %s" % # provider[0] if not mainact: mainact = activities[0] context.update({ 'Permissions': permissions, 'Package_Name': package_name, 'Version_Code': version_code, 'Version': version_name, 'Min_SDK': min_sdk, 'Target_SDK': target_sdk, 'Main_Activity': mainact, 'Activitys': activities, 'Services': services, 'Receivers': receivers, 'Providers': providers, 'Act_count': len(activities), 'Ser_count': len(services), 'Rec_count': len(receivers), 'Pro_count': len(providers), }) return context
def analysis_code(self): column = [] code_vul_db = [] for vul_item in code_vul_db: column.append(vul_item.flag) context = {key: [] for key in column} urllist = [] emaillist = [] JS = os.path.join(self.extract_path, 'java_source') url_reg = re.compile( ur'((?:https?://|s?ftps?://|file://|javascript:|data:|www\d{0,3}[.])[\w().=/;,#:@?&~*+!$%\'{}-]+)', re.UNICODE) email_reg = re.compile("[\w.-]+@[\w-]+\.[\w.]+") pinfo("Code Analysis Started on - " + JS) for dirName, subDir, files in os.walk(JS): for jfile in files: jfile_path = os.path.join(JS, dirName, jfile) if "+" in jfile: p2 = os.path.join(JS, dirName, jfile.replace("+", "x")) shutil.move(jfile_path, p2) jfile_path = p2 repath = jfile_path.replace(JS, '') # 排除分析默认框架的源码 及 第三方SDK源码 if not self.init_blacklist(repath): with io.open(jfile_path, mode='r', encoding="utf8", errors="ignore") as f: dat = f.read() f.close() for vul in code_vul_db: result = False features = getattr(vul, 'check_feature') for feature in features: is_found = True if isinstance(feature, list): # pinfo('Now checking %s' % feature) for i in feature: try: t_reg = re.compile(i) if not t_reg.findall(dat): is_found = False except Exception as e: einfo('Code analysis error! ' + str(e)) else: try: t_reg = re.compile(feature) if not t_reg.findall(dat): is_found = False except Exception as e: einfo('Code analysis error! ' + str(e)) result = result or is_found if result: t_column = getattr(vul, 'flag') context[t_column].append(jfile_path.replace( JS, '')) # URLs My Custom regex for url in re.findall(url_reg, dat.lower()): urllist.append(url) # Email Etraction Regex for email in email_reg.findall(dat.lower()): emaillist.append(email) context['URLs'] = list(set(urllist)) context['Emails'] = [] for email in list(set(emaillist)): if not email.startswith('//'): context['Emails'].append(email) print "[INFO] Finished Code Analysis, Email and URL Extraction" return context
def read_manifest(self, manifest): # 要求安装Androguard的androaxml.py pinfo('Now Getting AndroidManifest.xml from Androguard module') ap = apk.AXMLPrinter(read(manifest)) buff = ap.get_buff() return buff