def extraction(apk_path, file): t = "<sep>" apkf = APK(apk_path) f = open("train.csv", 'a') f.write(file + t) f.write(str(apkf.cert_text) + t) f.write(str(apkf.file_md5) + t) f.write(str(apkf.cert_md5) + t) f.write(str(apkf.file_size) + t) f.write(str(apkf.androidversion) + t) f.write(str(apkf.package) + t) f.write(str(apkf.get_android_manifest_xml()) + t) f.write(str(apkf.get_android_manifest_axml()) + t) f.write(str(apkf.is_valid_APK()) + t) f.write(str(apkf.get_filename()) + t) f.write(str(apkf.get_package()) + t) f.write(str(apkf.get_androidversion_code()) + t) f.write(str(apkf.get_androidversion_name()) + t) f.write(str(apkf.get_max_sdk_version()) + t) f.write(str(apkf.get_min_sdk_version()) + t) f.write(str(apkf.get_target_sdk_version()) + t) f.write(str(apkf.get_libraries()) + t) f.write(str(apkf.get_files()) + t) f.write(str(apkf.get_files_types()) + t) f.write(str(apkf.get_main_activity()) + t) f.write(str(apkf.get_activities()) + t) f.write(str(apkf.get_services()) + t) f.write(str(apkf.get_receivers()) + t) f.write(str(apkf.get_providers()) + t) f.write(str(apkf.get_permissions())) f.write("<new>")
def get_feature(apk_path): try: apkf = APK(apk_path) x_feature = "" x_feature = x_feature + str(apkf.file_size) + sep x_feature = x_feature + str(apkf.get_max_sdk_version()) + sep x_feature = x_feature + str(apkf.get_min_sdk_version()) + sep x_feature = x_feature + str(apkf.get_target_sdk_version()) + sep x_feature = x_feature + str(apkf.androidversion) + sep x_feature = x_feature + str(apkf.get_activities()) + sep x_feature = x_feature + str(apkf.get_services()) + sep x_feature = x_feature + str(apkf.get_receivers()) + sep x_feature = x_feature + str(apkf.get_providers()) + sep x_feature = x_feature + str(apkf.get_permissions()) + "\n" f.write(x_feature) except: pass
def test(): if len(sys.argv) == 1: print('Usage: %s app.apk' % sys.argv[0]) sys.exit(1) apk_path = sys.argv[1] apkf = APK(apk_path) print(apkf.cert_text) print(apkf.cert_pem) print(apkf.file_md5) print(apkf.cert_md5) print(apkf.file_size) print(apkf.androidversion) print(apkf.package) print(apkf.get_android_manifest_xml()) print(apkf.get_android_manifest_axml()) print(apkf.is_valid_APK()) print(apkf.get_filename()) print(apkf.get_package()) print(apkf.get_androidversion_code()) print(apkf.get_androidversion_name()) print(apkf.get_max_sdk_version()) print(apkf.get_min_sdk_version()) print(apkf.get_target_sdk_version()) print(apkf.get_libraries()) print(apkf.get_files()) # pip install python-magic print(apkf.get_files_types()) # print(apkf.get_dex()) print(apkf.get_main_activity()) print(apkf.get_activities()) print(apkf.get_services()) print(apkf.get_receivers()) print(apkf.get_providers()) print(apkf.get_permissions()) print(binascii.hexlify(apkf.get_signature())) print(apkf.get_signature_name()) print apkf.show()
def get_permission_info(PATH): filenames = os.listdir(PATH) get_permission = {} my_list = [0 for i in range(len(permission_v2))] for filename in filenames: # add permission list 11/12 my_list = [0 for i in range(len(permission_v2))] full_path = PATH + "/" + filename apkf = APK(full_path) permission_list = [] count = 0 for i in apkf.get_permissions(): permission_list.append(i.split(".")[-1]) for i in permission_list: if i in permission_v2: my_list[permission_v2.index(i)]=1 count += 1 my_list.append(count) get_permission[filename[:-4]]= my_list return get_permission
def main(): path = sys.argv[1] malicious = sys.argv[2] result = {} if not os.path.exists(path): return False, "File is not exists" apk = APK(path) if not apk.is_valid_APK(): return False, "APK file is wrong" result = {} ### APK File Info result['Apk'] = {} result['Apk']['path'] = path result['Apk']['malicious'] = malicious result['Apk']['md5'] = apk.file_md5 result['Apk']['sha256'] = apk.file_sha256 result['Apk']['size'] = apk.file_size result['Apk']['magic'] = magic.Magic().from_file(path) result['Apk']['icon_files'] = apk.get_icon_files() ### Certificate Information result['Certificate'] = {} result['Certificate']['md5'] = apk.cert_md5 result['Certificate']['text'] = apk.cert_text ### AndroidManifiest.xml Information result['AndroidManifest'] = {} result['AndroidManifest'][ 'androidversion_code'] = apk.get_androidversion_code() result['AndroidManifest'][ 'androidversion_name'] = apk.get_androidversion_name() result['AndroidManifest']['min_sdk_version'] = apk.get_min_sdk_version() result['AndroidManifest'][ 'target_sdk_version'] = apk.get_target_sdk_version() result['AndroidManifest']['libraries'] = apk.get_libraries() result['AndroidManifest']['main_activitiy'] = apk.get_main_activity() result['AndroidManifest']['activities'] = {} for activity in apk.get_activities(): result['AndroidManifest']['activities'][ activity] = apk.get_intent_filters('activity', activity) result['AndroidManifest']['services'] = {} for service in apk.get_services(): result['AndroidManifest']['services'][ service] = apk.get_intent_filters('service', service) result['AndroidManifest']['receivers'] = {} for receiver in apk.get_receivers(): result['AndroidManifest']['receivers'][ receiver] = apk.get_intent_filters('receiver', receiver) result['AndroidManifest']['permissions'] = {} for permission in apk.get_permissions(): result['AndroidManifest']['permissions'][ permission] = apk.get_intent_filters('permission', permission) result['AndroidManifest']['providers'] = apk.get_providers() ### APK File Information and File Magic Data result['Files'] = {} image_extension_list = ['png', 'jpeg', 'jpg', 'gif'] image_magic_list = ['PNG image data', 'JPEG image data'] for file in apk.get_files(): result['Files'][file] = {} fileData = apk.get_file(filename=file) result['Files'][file]['icon'] = False result['Files'][file]['size'] = len(fileData) result['Files'][file]['md5'] = hashlib.md5(fileData).hexdigest() result['Files'][file]['sha256'] = hashlib.sha256(fileData).hexdigest() result['Files'][file]['magic'] = magic.Magic().from_buffer(fileData) result['Files'][file]['file_name'] = None result['Files'][file]['file_extension'] = None result['Files'][file]['image_resource'] = False if '/' in file: result['Files'][file]['file_name'] = file.split('/')[-1] else: result['Files'][file]['file_name'] = file if '.' in result['Files'][file]['file_name']: result['Files'][file]['file_extension'] = result['Files'][file][ 'file_name'].split('.')[-1].lower() if file in apk.get_icon_files(): result['Files'][file]['icon'] = True if result['Files'][file]['file_extension'] in image_extension_list: result['Files'][file]['image_resource'] = get_image_resource( file_magic=result['Files'][file]['magic'], image_magic_list=image_magic_list) if get_image_resource( file_magic=result['Files'][file]['magic'], image_magic_list=image_magic_list) and result['Files'][file][ 'file_extension'] not in image_extension_list: continue #print "[+] This file is strange %s %s" % (file, result['Files'][file]['magic']) fd = open('./temp/' + file.replace('/', '_'), 'wb') fd.write(apk.get_file(file)) fd.close() ### decompile proc = subprocess.Popen(['java', '-jar', 'apktool_2.3.0.jar', 'd', path], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = proc.communicate() if '/' in path: path = path.split('/')[-1] result['Class'] = {} for root, dirs, files in os.walk('./' + path + '.out' + os.sep + 'smali'): for file in files: filepath = root + os.sep + file className = filepath.split('/smali/')[-1].split( '.smali')[0].replace('/', '.') result['Class'][className] = {} result['Class'][className]['Method'] = {} result['Class'][className]['interfaces'] = [] result['Class'][className]['Fields'] = [] fd = open(filepath, 'rb') data = fd.read() fd.close() index = 0 for line in data.split('\n'): if not line: continue line = line.strip() if line.startswith('.super '): result['Class'][className]['super_class'] = line[ 8:-1].replace('/', '.') #print result['Class'][className]['super_class'] elif line.startswith('.method '): ### Method Info try: methodName, parameters, returnType = re.search( '\.method\s.+\s(.+)\((.*)\)(.+)', line).groups() except AttributeError: methodName, parameters, returnType = re.search( '\.method\s(.+)\((.*)\)(.+)', line).groups() result['Class'][className]['Method'][methodName] = {} result['Class'][className]['Method'][methodName][ 'parameters'] = [] result['Class'][className]['Method'][methodName][ 'strings'] = [] result['Class'][className]['Method'][methodName][ 'call-api'] = [] result['Class'][className]['Method'][methodName][ 'returnType'] = None result['Class'][className]['Method'][methodName][ 'flags'] = None #if returnType not in ['Z','B','C','D','F','I','J','V']: # print line, returnType # raw_input() ### Method Parameter for parameter in parameters.split(';'): result['Class'][className]['Method'][methodName][ 'parameters'].append(parameter[1:].replace( '/', '.')) if returnType == 'Z': returnType = 'boolean' elif returnType == 'B': returnType = 'byte' elif returnType == 'C': returnType = 'char' elif returnType == 'D': returnType = 'double' elif returnType == 'F': returnType = 'float' elif returnType == 'I': returnType = 'int' elif returnType == 'J': returnType == 'long' elif returnType == 'V': returnType = 'void' elif returnType.startswith('L') and returnType.endswith( ';'): returnType = returnType[1:-1].replace('/', '.') result['Class'][className]['Method'][methodName][ 'returnType'] = returnType #if '[' in returnType: elif line.startswith('const-string'): result['Class'][className]['Method'][methodName][ 'strings'].append( re.search('\"(.*)\"', line).groups()[0]) ### target = {'class', 'method', 'parameters' = []} elif line.startswith('invoke-virtual') or line.startswith( 'invoke-static') or line.startswith( 'invoke-interfaces'): targetClass, targetMethod, targetMethodParameters, targetMethodReturnType = re.search( 'invoke\-.+\s\{.*\}\,\sL(.+)\-\>(.+)\((.*)\)(.*)', line).groups() target = {} target['class'] = targetClass target['mehtod'] = targetMethod target['parameters'] = [] if targetMethodParameters.count(';') > 1: for parameter in targetMethodParameters.split(';'): target['parameters'].append(parameter[1:].replace( '/', '.')) else: target['parameters'].append( targetMethodParameters.replace('/', '.')) result['Class'][className]['Method'][methodName][ 'call-api'].append(target) index += 1 shutil.rmtree('./' + path + '.out') return True, ""
if apk_file is not None: # generate info print("[*] Collecting app info") apk_info = APK(apk_file) report["app_info"] = { "md5": apk_info.file_md5, "cert_md5": apk_info.cert_md5, "file_size": apk_info.file_size, "version_name": apk_info.get_androidversion_name(), "version_code": apk_info.get_androidversion_code(), "main_activity": apk_info.get_main_activity(), "activities": apk_info.get_activities(), "services": apk_info.get_services(), "receivers": apk_info.get_receivers(), "providers": apk_info.get_providers(), "permissions": apk_info.get_permissions(), "certificates": [] } report["app_info"]["certificates"].append(apk_info.cert_text) if cli is not None: store_info = cli.get_package_info(package_name) report["store_info"] = { "downloads": store_info["numDownloads"], "date": store_info["uploadDate"], "ads": store_info["containsAds"] == 'Contains ads', "app_apptype": store_info["category"]["appType"], "app_category": store_info["category"]["appCategory"] } for image in store_info["images"]:
def main(): banner() for i in range(start, end): target = "bot/" + str(i) try: apkf = APK(target) md5 = apkf.file_md5 package = apkf.get_package() file_size = apkf.file_size andro_version = apkf.get_androidversion_name() libraries = apkf.get_libraries() main_activity = apkf.get_main_activity() activities = apkf.get_activities() services = apkf.get_services() files = apkf.get_files() permissions = apkf.get_permissions() counter_emails = 0 counter_urls = 0 counter_ftps = 0 all_emails = [] all_urls = [] all_ftps = [] print "----------------------------------------------" print colores.header + colores.underline + "[+][TARGET][>]" + package + " [" + str( i) + "]" + colores.normal print colores.header + "[-][md5][>] " + md5 print colores.header + "[-][Android version][>] " + andro_version print colores.green + "|----[>] " + "Searching emails and links in strings ..." + colores.normal strings = os.popen("strings " + target) for word in strings: #To found emails in strings if "@" in word: if word.find(".com") > 0 or word.find( ".es") > 0 or word.find(".eu") > 0 or word.find( ".net") > 0 or word.find( ".gob") > 0 or word.find( ".info") > 0 or word.find(".org") > 0: words = word.split(" ") for w in words: if word.find(".com") > 0 or word.find( ".es" ) > 0 or word.find(".eu") > 0 or word.find( ".net" ) > 0 or word.find(".gob") > 0 or word.find( ".info") > 0 or word.find(".org") > 0: counter_emails += 1 email = re.findall(r'[\w\.-]+@[\w\.-]+', word) if not email in all_emails: print colores.green + "|----[EMAIL][>] " + str( email) + colores.normal all_emails.append(email) #To found urls in strings if "http" in word or "wwww." in word: url = re.findall( 'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', word) if not url in all_urls or url == "": all_urls.append(url) print colores.green + "|----[URL][>] " + str( url) + colores.normal #To found FTP in strings if "ftp" in word: ftp = re.findall( 'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', word) if not ftp in all_ftps or ftp == "": all_ftps.append(ftp) print colores.green + "|----[FTP][>] " + str( ftp) + colores.normal except: continue insert_mongodb(i, package, md5, file_size, andro_version, main_activity, activities, services, permissions, all_urls, all_emails, all_ftps)
class ChecklistBerker(object): is_apk_created = False apk_location = "/app/build/outputs/apk/app-external-release.apk" test_results = [] def __init__(self, project_dir, apk_dir, config_location): self.project_dir = project_dir self.apk_dir = apk_dir self.apkf = APK(apk_dir) self.manifestDict = extractXML(apk_dir, config_location) self.gradleDict = gr.GradleParserNew(self.project_dir + "/app").parse(False) def execute_test_batch(self, config_location): config = ConfigParser.ConfigParser() config.read(config_location) resCongList = config.get('B1', 'resConfigs') self.test_results.append(self.b1(resCongList)) self.test_results.append(self.b4()) targetSdkVersion = config.get('B6', 'targetSdkVersion') self.test_results.append(self.b6(targetSdkVersion)) self.test_results.append(self.b7()) self.test_results.append(self.b9()) self.test_results.append(self.man2()) self.test_results.append(self.man5()) self.test_results.append(self.sign1()) self.test_results.append(self.sign3()) self.test_results.append(self.perm2()) self.test_results.append(self.prg1()) minifyEnabled = config.get('PRG2', 'minifyEnabled') shrinkResources = config.get('PRG2', 'shrinkResources') self.test_results.append(self.prg2(shrinkResources, minifyEnabled)) allowBackup = config.get('SEC1', 'allowBackup') self.test_results.append(self.sec1(allowBackup)) return self.test_results def b1(self, configResConfig_list): testId = "B1" found = False resConf_filtered = configResConfig_list.split(",") resConf_filtered = [x.strip(" ") for x in resConf_filtered] if "resConfigs" in self.gradleDict['android']["defaultConfig"][0]: for i in self.gradleDict['android']["defaultConfig"][0][ "resConfigs"][0]: for conf in resConf_filtered: if i.lower() == conf.lower(): found = True break # found in the config if not found: result = "FAILED." additional = " In your resConfigs, you have: " + i + " but not in config file." return (testId, self.b1_descp(), (result, additional)) else: found = False for conf in resConf_filtered: for i in self.gradleDict['android']["defaultConfig"][0][ "resConfigs"][0]: if i.lower() == conf.lower(): found = True break # found in the config if not found: result = "FAILED." additional = " In your config file, you have: " + conf + " but not in manifest." return (testId, self.b1_descp(), (result, additional)) else: found = False else: result = "CONFIRM:" additional = " You dont have resConfigs in your project." return (testId, self.b1_descp(), (result, additional)) result = "SUCCEED." additional = " Your resConfigs in config file match with the ones in the manifest." return (testId, self.b1_descp(), (result, additional)) def b1_descp(self): return "Make sure to minimize res configs by only including necessary resources (localization etc.)" def b4(self): testId = "B4" appId = self.apkf.get_package() startingName = "com.monitise.mea." if appId.startswith(startingName): result = "SUCCEED." additional = "Your project name starts with \"com.monitise.mea\"." else: result = "FAILED." additional = "Your project name does not start with \"com.monitise.mea\" It starts with " + appId return (testId, self.b4_descp(), (result, additional)) def b4_descp(self): return "Make sure that applicationId respects com.monitise.mea.<product> convention unless other indicated." def b6(self, configTargetSdk): testId = "B6" configTargetSdk targetSDK = self.apkf.get_target_sdk_version() if configTargetSdk == targetSDK: result = "SUCCEED." additional = "Your targetSdkVersion is: " + targetSDK + "." else: result = "FAILED." additional = "Your targetSdkVersion should be " + configTargetSdk + " but it is " + targetSDK + "." return (testId, self.b6_descp(), (result, additional)) def b6_descp(self): return "Make sure that targetSdkVersion is set to most recent api version that app is tested against." def b7(self): testId = "B7 Test" for dep in self.gradleDict["dependencies"]["compile"]: if "com.google.android.gms:play-services:" in dep: result = "FAILED." additional = "Google Play Services API should be included as separate dependencies." return (testId, self.b7_descp(), (result, additional)) result = "SUCCEED." additional = "Google Play Services API is not included with just one line. (or not included at all)" return (testId, self.b7_descp(), (result, additional)) def b7_descp(self): return "Make sure that any Google Play Services API is included as separate dependencies." def b9(self): testId = "B9" if '@android:debuggable' in self.manifestDict['manifest'][ 'application']: deb = self.manifestDict['manifest']['application'][ '@android:debuggable'] deb = deb.lower() if deb == "true": result = "FAILED." additional = "debuggable should not be set to true." return (testId, self.b9_descp(), (result, additional)) result = "SUCCEED." additional = "debuggable is not set to true." return (testId, self.b9_descp(), (result, additional)) def b9_descp(self): return "Make sure that release build type in gradle build file doesn't have debuggable set to true." def man2(self): testId = "MAN2" if "@android:versionName" in self.manifestDict['manifest']: version = self.manifestDict['manifest']['@android:versionName'] result = "CONFIRM:" additional = "Dismiss if you updated your version. android:versionName is set to: " + version + "." else: result = "FAILED." additional = "You need to update android:versionName." return (testId, self.man2_descp(), (result, additional)) def man2_descp(self): return "Make sure that android:versionName attribute is updated." def man5(self): testId = "MAN5" if "@android:installLocation" in self.manifestDict['manifest']: location = self.manifestDict['manifest'][ '@android:installLocation'] if location == "externalOnly": result = "FAILED." additional = "You cannot set android:installLocation to externalOnly." return (testId, self.man5_descp(), (result, additional)) result = "SUCCEED." additional = " android:installLocation is not set to externalOnly." return (testId, self.man5_descp(), (result, additional)) def man5_descp(self): return "Make sure that android:installLocation attributes is not set to externalOnly." def perm2(self): testId = "PERM2" result = "CONFIRM:" additional = "Check if all the permissions are necessary:" counter = 0 for i in self.apkf.get_permissions(): additional = additional + "\n\t- " + self.apkf.get_permissions( )[counter] counter += 1 return (testId, self.perm2_descp(), (result, additional)) def perm2_descp(self): return "Make sure that app is NOT requesting any unnecessary permissions." def sec1(self, configAllowBackup): testId = "SEC1" configAllowBackup = configAllowBackup.lower() if "@android:allowBackup" in self.manifestDict['manifest'][ 'application']: backup = self.manifestDict['manifest']['application'][ '@android:allowBackup'] backup = backup.lower() configAllowBackup = configAllowBackup.lower() if backup == configAllowBackup: result = "SUCCEED." additional = "android:allowBackup is set to " + backup else: result = "FAILED." additional = "android:allowBackup is set to " + backup + ". But it must be " + configAllowBackup + "." elif configAllowBackup == "true": result = "FAILED." additional = "You need to specify android:allowBackup as true." else: result = "SUCCEED." additional = "Your android:allowBackup is set to false by default." return (testId, self.sec1_descp(), (result, additional)) def sec1_descp(self): return "Make sure to set android:allowBackup to false unless otherwise indicated." def sign1(self): testId = "SIGN1" if not os.path.exists(self.project_dir + "/release.keystore.jks"): result = "FAILED." additional = " release.keystore.jks does not exist in your project path." return (testId, self.sign1_descp(), (result, additional)) def sign1_descp(self): return "Make sure that a release keystore is created and used to sign any release configuration " \ "(prod-release, internal-release, external-release etc.) of the app" def sign3(self): testId = "SIGN3" keyPath = '' try: keyPath = self.gradle['android']['signingConfigs'][0]['release'][ 0]['storeFile'][0][0] except: result = "FAILED." additional = "There is no given path for release keystore file" return (testId, self.sign3_descp(), (result, additional)) if path.exists(keyPath): if "/build/" in keyPath: result = "FAILED" additional = " Your release keystore is in build classpath." else: result = "SUCCEED." additional = "Your release keystore is not in build classpath." return (testId, self.sign3_descp(), (result, additional)) else: result = "FAILED." additional = " There is no release keystore in the project." return (testId, self.sign3_descp(), (result, additional)) def sign3_descp(self): return "Make sure that the release keystore is NOT included in build classpath i.e. Apk should never expose this file" def prg1(self): testId = "PRG1" if 'monitise' in self.gradleDict: result = "SUCCEED." additional = "Your gradle has \"monitise\" block." else: result = "FAILED." additional = "Your gradle file does not have \"monitise\" block. You forgot deleting logs." return (testId, self.prg1_descp(), (result, additional)) def prg1_descp(self): return "Make sure that logging is disabled on release builds." def prg2(self, configMinifyEn, configShrinkRes): testId = "PRG2" configMinifyEn = configMinifyEn.lower() configShrinkRes = configShrinkRes.lower() if "minifyEnabled" in self.gradleDict['android']["buildTypes"][0]["release"][0] and \ "shrinkResources" in self.gradleDict['android']["buildTypes"][0]["release"][0]: minifyEnabled = self.gradleDict["android"]["buildTypes"][0][ "release"][0]["minifyEnabled"][0] shrinkResources = self.gradleDict["android"]["buildTypes"][0][ "release"][0]["shrinkResources"][0] minifyEnabled = minifyEnabled[0].lower() shrinkResources = shrinkResources[0].lower() if minifyEnabled == configMinifyEn and shrinkResources == configShrinkRes: result = "SUCCEED." additional = "minifyEnabled and shrinkResources are set to true." return (testId, self.prg2_descp(), (result, additional)) result = "FAILED." additional = "minifyEnabled and shrinkResources must be true." return (testId, self.prg2_descp(), (result, additional)) def prg2_descp(self): return "Make sure that minifyEnabled and shrinkResoureces are set to true for release build type"