def query_report(args): """ all file type """ # default for vt resource = args.resource name = args.name # vendor's name : VT threatbook bsize = 65536 buff = None if input_file is not None and os.path.isfile(resource): sha256 = hashlib.sha256() while True: with open(resource, 'rb') as fp: buff = fp.read(bsize) if buff is None: break sha256.update(buff) resource = sha256.hexdigest() if resource is None or resource == '': echo("error", "query_report resouroce must be empty or None", color="red") return if name is not None and name == 'threatbook': ThreatbookSandbox(resource).analysis() return # query all online online_sandboxies = [ThreatbookSandbox(resource), VT(resource)] for sb in online_sandboxies: sb.analysis()
def extract_android_manifest_info(args): input_file = args.manifest entry = args.entry acs = args.activities rs = args.receivers ss = args.services ps = args.providers both = args.both exported = args.exported pm = args.permission if not os.path.isfile(input_file): echo("error", "need apk or AndroidManifest.xml as input file!!", 'red') return elif input_file.endswith('.xml'): axml = AndroidManifestXmlParser(input_file) axml.show_manifest(acs, rs, ss, ps, entry, both, exported, pm) elif input_file.endswith('apk') or input_file.endswith('bin'): # for some reason,we can alse check sample.bin apk_parser = ApkPaser(input_file) if apk_parser.ok(): apk_parser.show_manifest(acs, rs, ss, ps, entry, both, exported, pm) # echo("info", "\n"+str(apk_parser.mainifest_info())) else: echo("error", "unknow {} filtype ".format(input_file), 'red')
def __analyzer_string(self): """ need pattern or rule file """ for rule in self.rules: ss = self.apk_parser.all_strings(rule['patters']) if len(ss) > 0: echo("info", "rule: {} {} ".format(rule['name'], self.filename)) return True return False
def analyzer(self): if not self.ok: return False elif not os.path.isfile(self.rule): echo("error", "need rule file!!", 'red') return False elif not self.apk_parser.ok: echo("error", " {} is not a apk file !!!", 'red') return False self.rules = self.__read_rule() return self.__analyzer_string()\ or self.__analyzer_method()\ or self._online_sandbox()
def show_result(rsult, susp): for name in self.yara_namespace: if rsult.get(name, None) is None: continue for r in rsult[name]: tag = r['tags'][0] if r['matches']: echo( "rule", " %s/%s %s\t%s" % (tag, r['rule'], self.yara_namespace[name], susp), 'yellow') return
def dex_info(args): input_file = args.dex pattern = args.string method_name_arg = args.method pkg = args.pkgname clazz_name = args.clazz dump = args.print_ins if input_file is None: echo("error", "need a dex file!! ", "red") parser.print_help() sys.exit(1) patters = [] # if pkg is None: # echo("warning", "pkg is None and will retrive all methods in dex file.", 'yellow') # if pattern is None or pattern == '': # pattern = "://" patters.append(pattern) def scan_dex(dex): echo("dex", "analyzer " + dex) with open(dex, 'rb') as f: vm = DexFileVM(pkgname=pkg, buff=f.read()) if not vm.ok(): echo("error", "{} is not a dex format file.".format(input_file), 'red') return if pattern is not None: # if pattern is not None will show string for i, s in enumerate(vm.all_strings(patters)): echo("%d" % (i), "%s" % (s)) if method_name_arg is not None: echo("warning", " methodName is empty ,show all methods", 'yellow') vm.analysis_dex(clazz_name, method_name_arg, dump) if os.path.isdir(input_file): for root, _, fs in os.walk(input_file): for f in fs: if f.endswith('.dex'): dex = os.path.join(root, f) scan_dex(dex) elif os.path.isfile(input_file): scan_dex(input_file)
def get_export_components(self): """ Return All export components info """ # return self. exported_all = ["activity", 'service', 'provider', 'receiver'] # activities = list(self.get_all_attribute_value( # "activity", "name", {"exported": "true"})) # exported_all.append(activities) echo("exportedComponents", "**" * 20, "yellow") for k in exported_all: exported = list( self.get_all_attribute_value(k, "name", {"exported": "true"})) print("--" * 15 + " " + k + " " + "--" * 15) for i, item in enumerate(exported): echo("%d" % (i), item)
def is_target_clazz(self, pkg, clazz): # return True if pkg in clazz else False need_filter_classes = [ '.R$attr', '.R$drawable', '.R$id', '.R$layout', '.R$string', '.R', '.BuildConfig' ] android_s = [ "^(Landroid/support|Landroid/arch|Landroidx/versionedparcelable|Landroidx/core|Lkotlin/|Lkotlinx/).+", ".*(R\$.+)$", ".+(/BuildConfig;|/R;)$", ] if isinstance(clazz, bytes): # print(clazz) try: clazz = str(clazz, encoding="utf-8") except UnicodeDecodeError as e: echo("dexparse", "faile to decode string : {} ,error {} ".format(clazz, e), color="red") return False if clazz == '': return False for a in android_s: expr = re.compile(a) if expr.search(clazz): return False # print("--> check %s class: %s" % (pkg, clazz)) # return True if pkg is None or pkg == '': return True clazz = clazz.replace("L", "").replace("/", '.').replace(";", "") # filter thridpart class ,like google's code etc suffix = clazz[clazz.rfind('.'):] if suffix in need_filter_classes: return False # # # target = re.compile(pkg) # if target.match(clazz): # return True return True
def show_info(parser, dexname, vm): echo("dexname", "--> %s" % (dexname)) if save: f = os.getcwd() + os.sep + "%s.txt" % (dexname) files.append(f) if os.path.isfile(f): os.remove(f) if pattern is not None: # default all dex data for s in parser.all_strings([pattern], dex_vm=vm): if save: save_file(f, s) else: try: echo("string", "%s" % (s), 'yellow') except UnicodeDecodeError as e: print("--> Unicode error ,string type {}".format( type(s))) raise e if method_name_arg is not None: apk_parser.analysis_dex(clazz_name, method_name_arg, dump, dex_vm=dex_vm) if save: for f in files: echo("save", "data save at " + f)
def yara_scan(args): rule = args.rule f = args.file if rule is None or f is None: echo("error", "yara rule or apk file must be include", 'red') return if not os.path.isfile(rule) and not os.path.isdir(rule): echo("error", "yara rule file not exists", 'red') return if not os.path.isfile(f) and not os.path.isdir(f): echo("error", "apk file or apk directory need", 'red') return echo("yara_scan", "------YARA SCAN -------", 'yellow') if rule.startswith("."): rule = os.getcwd() + rule[1:] YaraMatcher(rule, f).yara_scan()
def scan_dex(dex): echo("dex", "analyzer " + dex) with open(dex, 'rb') as f: vm = DexFileVM(pkgname=pkg, buff=f.read()) if not vm.ok(): echo("error", "{} is not a dex format file.".format(input_file), 'red') return if pattern is not None: # if pattern is not None will show string for i, s in enumerate(vm.all_strings(patters)): echo("%d" % (i), "%s" % (s)) if method_name_arg is not None: echo("warning", " methodName is empty ,show all methods", 'yellow') vm.analysis_dex(clazz_name, method_name_arg, dump)
def print_ins(self, offset, show=True): ins_ = ' ' instrus = self.dex_header.read_code_instrs(offset) if show: echo("instructions", "--" * 20, 'yellow') # red, green, yellow, blue, magenta, cyan, white. echo("codeoff", " %s" % (hex(instrus[0])), "yellow") echo("codesize", " %s" % (hex(instrus[1])), "yellow") print("") # 格式化输出 print(" " + " 0 " + " " + "" + " 1 " + " " + " 2 " + " " + "" + " 3 " + " " + " 4 " + " " + "" + " 5 " + " " + " 6 " + " " + "" + " 7 " + " " + " 8 " + " " + "" + " 9 " + " " + " A " + " " + "" + " B " + " " + " C " + " " + "" + " D " + " " + " E " + " " + "" + " F ") print("-" * 16 + " " + "-" * 16 + " " + "-" * 16 + " " + "-" * 16) save = [] for i, ins in enumerate(instrus[2:]): if show: if i > 0 and i % 16 == 0: print("%s" % (ins_)) print("") ins_ = "" if i > 0 and i % 4 == 0: ins_ += " " ins_ += "%.2x " % (ins) + " " save.append("%.2x " % (ins)) if show: print(ins_) # print("") # echo("info", "all instructions ", ) echo("shellcode", "\n" + " ".join(save), 'yellow') return " ".join(save)
def show_manifest(self, ac, rs, ss, ps, entry, both, exported, pm): manifest = { "activities": self.get_all_activities, "receviers": self.get_receivers, "providers": self.get_providers, "services": self.get_all_services, "both": self.__str__, } def show(key): echo('%s' % (key), "--" * 15, 'yellow') cnt = 1 for a in manifest[key](): echo("%d" % (cnt), a) cnt += 1 if ac: show("activities") elif rs: show("receviers") elif ss: show("services") elif ps: show("providers") elif entry: # show("entry") echo("entryinfo", "**" * 20) self.entry_info() elif exported: self.get_export_components() elif pm: for p in self.permissions: echo("permission", p) elif both: echo("all", "\n" + manifest['both']())
def entry_info(self): echo("pkgname", self.get_package_name()) echo("application", self.get_application()) echo("MainActivity", self.get_main_activity())
def apk_info(args): """ 默认输出apk内的基本信息 apk的指纹信息 AndroidManifest.xml内的信息 使用-z --zipinfo 读取apk内的所有文件名信息 """ input_file = args.apk zip_info = args.zipinfo dex_num = args.dexnum info = args.info suffix = args.suffix # like .apk,.APK,.bin if suffix is None: suffix = ".apk,.APK" if input_file is None or not os.path.isfile(input_file): echo("error", "need a apk file as input.", "red") sys.exit(1) # 可以是任意文件,内部如果读取失败,则会直接异常 # found = False # for s in suffix.split(","): # if input_file.endswith(s): # #echo("error", "need a apk file", 'red') # # return # found = True # if found is False: # echo("error", "need a apk file", 'red') # return start = time.time() apk_parser = ApkPaser(input_file) if dex_num: for dexname in apk_parser.get_all_dexs(name=True): echo("dexname", dexname) print("costs: {}".format(time.time() - start)) if info: base_info = apk_parser.apk_base_info() print("") echo("AppName", base_info['app_name']) if base_info['packer_name'] != "N/A": echo("packer", "App may be packed by {}".format(base_info['packer_name']), color="red") print("") echo("apkInfo", "\n{}".format(json.dumps(base_info, indent=2)), "yellow") print("--" * 20) print("costs: {}".format(time.time() - start)) if zip_info: for f in apk_parser.get_file_names(): echo("info", "-> %s" % (f))
def show(key): echo('%s' % (key), "--" * 15, 'yellow') cnt = 1 for a in manifest[key](): echo("%d" % (cnt), a) cnt += 1
def scan(file): try: self.match(file) except Exception as e: echo("yara_scan", "error {} ".format(e), 'red')
def _online_sandbox(self): result = self.vt.analysis() if result is None: return False echo("info", "VT query reult:") echo("positives", result['positives'], "magenta") echo("virusName", result['virusName'], "red") echo("link", result['link'], "green") echo("scanDate", result['scanDate'], "yellow") echo("path", self.filename, "white") return True
def analysis_dex(self, clazz_name, method_name, show_ins=False): """ Analyzer dex .This function mainly is use to show dex class_defs eg. if clazz_name or method_name is None or empty, default show all class->method else if method_name is not none ,show the matched method info ,include class_name->method method_instructions else if class_name and method are not none or empty ,this means to specific class_def's method info """ if clazz_name is None: # class_name = "com.demo.Test" clazz_name = '' if method_name is None: method_name = '' marker = '.java -> ' query = clazz_name + marker + method_name for class_def in self.all_class_defs(): clzz_name = byte2str(class_def['class_name']) clzz_name = clzz_name.replace("L", '').replace("/", '.').replace(";", '') for method_ in class_def['code_item']: _method_name = byte2str(method_['method_name']) signature = byte2str(method_['signature']) _x = clzz_name + marker + _method_name + signature if clazz_name != marker and method_name != '' and query == _x: # show class.method(signature) print("**" * 20) echo("className", " %s" % (clzz_name), "yellow") echo("methodName", " %s" % (_method_name), "yellow") echo("signature", " %s" % (signature), "yellow") self.print_ins(method_['code_off'], show=show_ins) elif method_name == '': echo("info", "-> %s" % (_x), "blue") elif method_name == _method_name: print("**" * 20) echo("className", "%s" % (clzz_name), "blue") echo("methodName", "%s" % (_method_name), "blue") echo("signature", " %s" % (signature), "blue") self.print_ins(method_['code_off'], show=show_ins)
def extract_apk_info(args): input_file = args.apk pattern = args.string method_name_arg = args.method clazz_name = args.clazz dump = args.print_ins save = args.save # save strings or methods # if save: # echo("save", "date save at "+f) executor = ThreadPoolExecutor(max_workers=4) files = [] def show_info(parser, dexname, vm): echo("dexname", "--> %s" % (dexname)) if save: f = os.getcwd() + os.sep + "%s.txt" % (dexname) files.append(f) if os.path.isfile(f): os.remove(f) if pattern is not None: # default all dex data for s in parser.all_strings([pattern], dex_vm=vm): if save: save_file(f, s) else: try: echo("string", "%s" % (s), 'yellow') except UnicodeDecodeError as e: print("--> Unicode error ,string type {}".format( type(s))) raise e if method_name_arg is not None: apk_parser.analysis_dex(clazz_name, method_name_arg, dump, dex_vm=dex_vm) if save: for f in files: echo("save", "data save at " + f) if input_file is None or not os.path.isfile(input_file): echo("error", "need a apk file : %s" % (input_file), 'red') return if pattern is None: echo("warning", "no string specificed", 'yellow') # pattern = '' apk_parser = ApkPaser(input_file) if not apk_parser.ok(): return workers = [] for dexname, dex_vm in apk_parser.all_dex_vms(): workers.append( executor.submit(fn=show_info, parser=apk_parser, dexname=dexname, vm=dex_vm)) # pass for t in as_completed(workers): t.result()