def __init__(self, vm, bin_dex2jar="dex2jar.sh", bin_jad="jad", tmp_dir="/tmp/"): """ Use JAD on wine :param vm: :param bin_dex2jar: :param bin_jad: :param tmp_dir: """ self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) with os.fdopen(fd, "w+b") as fd: fd.write(vm.get_buff()) fd.flush() cmd = Popen([bin_dex2jar, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname) pathclasses = fdname + "dex2jar/" cmd = Popen(["unzip", fdname + "_dex2jar.jar", "-d", pathclasses], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname + "_dex2jar.jar") for root, dirs, files in os.walk(pathclasses, followlinks=True): if files: for f in files: real_filename = root if real_filename[-1] != "/": real_filename += "/" real_filename += f cmd = Popen(["wine", bin_jad, "-o", "-d", root, real_filename], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() for i in vm.get_classes(): fname = pathclasses + "/" + i.get_name()[1:-1] + ".jad" if os.path.isfile(fname): self.classes[i.get_name()] = read(fname, binary=False) else: self.classes_failed.append(i.get_name()) rrmdir(pathclasses)
def __init__(self, vm, bin_ded="ded.sh", tmp_dir="/tmp/"): """ DED is an old, probably deprecated, decompiler http://siis.cse.psu.edu/ded/ .. deprecated:: 3.3.5 DED is not supported by androguard anymore! It is now replaced by DARE. :param vm: `DalvikVMFormat` object :param bin_ded: :param tmp_dir: """ warnings.warn("DED is deprecated since 3.3.5.", DeprecationWarning) self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) with os.fdopen(fd, "w+b") as fd: fd.write(vm.get_buff()) fd.flush() dirname = tempfile.mkdtemp(prefix=fdname + "-src") cmd = Popen([bin_ded, "-c", "-o", "-d", dirname, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname) findsrc = None for root, dirs, files in os.walk(dirname + "/optimized-decompiled/"): if dirs: for f in dirs: if f == "src": findsrc = root if findsrc[-1] != "/": findsrc += "/" findsrc += f break if findsrc is not None: break for i in vm.get_classes(): fname = findsrc + "/" + i.get_name()[1:-1] + ".java" # print fname if os.path.isfile(fname): self.classes[i.get_name()] = read(fname, binary=False) else: self.classes_failed.append(i.get_name()) rrmdir(dirname)
def __init__(self, vm, bin_ded="ded.sh", tmp_dir="/tmp/"): """ DED is an old, probably deprecated, decompiler http://siis.cse.psu.edu/ded/ .. deprecated:: 3.3.5 DED is not supported by androguard anymore! It is now replaced by DARE. :param vm: `DalvikVMFormat` object :param bin_ded: :param tmp_dir: """ warnings.warn("DED is deprecated since 3.3.5.", DeprecationWarning) self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) with os.fdopen(fd, "w+b") as fd: fd.write(vm.get_buff()) fd.flush() dirname = tempfile.mkdtemp(prefix=fdname + "-src") cmd = Popen([bin_ded, "-c", "-o", "-d", dirname, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname) findsrc = None for root, dirs, files in os.walk(dirname + "/optimized-decompiled/"): if dirs: for f in dirs: if f == "src": findsrc = root if findsrc[-1] != "/": findsrc += "/" findsrc += f break if findsrc is not None: break for i in vm.get_classes(): fname = findsrc + "/" + i.get_name()[1:-1] + ".java" # print fname if os.path.isfile(fname): self.classes[i.get_name()] = read(fname, binary=False) else: self.classes_failed.append(i.get_name()) rrmdir(dirname)
def __init__(self, vm, path_dex2jar="./decompiler/dex2jar/", bin_dex2jar="dex2jar.sh", path_jad="./decompiler/jad/", bin_jad="jad", tmp_dir="/tmp/"): self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) fd = os.fdopen(fd, "w+b") fd.write(vm.get_buff()) fd.flush() fd.close() compile = Popen([path_dex2jar + bin_dex2jar, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() os.unlink(fdname) pathclasses = fdname + "dex2jar/" compile = Popen(["unzip", fdname + "_dex2jar.jar", "-d", pathclasses], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() os.unlink(fdname + "_dex2jar.jar") for root, dirs, files in os.walk(pathclasses, followlinks=True): if files != []: for f in files: real_filename = root if real_filename[-1] != "/": real_filename += "/" real_filename += f compile = Popen([ "wine", path_jad + bin_jad, "-o", "-d", root, real_filename ], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() for i in vm.get_classes(): fname = pathclasses + "/" + i.get_name()[1:-1] + ".jad" if os.path.isfile(fname) == True: fd = open(fname, "r") self.classes[i.get_name()] = fd.read() fd.close() else: self.classes_failed.append(i.get_name()) rrmdir(pathclasses)
def __init__(self, vm, bin_dex2jar="dex2jar.sh", bin_jad="jad", tmp_dir="/tmp/"): """ Use JAD on wine :param vm: :param bin_dex2jar: :param bin_jad: :param tmp_dir: """ self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) with os.fdopen(fd, "w+b") as fd: fd.write(vm.get_buff()) fd.flush() cmd = Popen([bin_dex2jar, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname) pathclasses = fdname + "dex2jar/" cmd = Popen(["unzip", fdname + "_dex2jar.jar", "-d", pathclasses], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname + "_dex2jar.jar") for root, dirs, files in os.walk(pathclasses, followlinks=True): if files: for f in files: real_filename = root if real_filename[-1] != "/": real_filename += "/" real_filename += f cmd = Popen( ["wine", bin_jad, "-o", "-d", root, real_filename], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() for i in vm.get_classes(): fname = pathclasses + "/" + i.get_name()[1:-1] + ".jad" if os.path.isfile(fname): self.classes[i.get_name()] = read(fname, binary=False) else: self.classes_failed.append(i.get_name()) rrmdir(pathclasses)
def __init__(self, vm, path_dex2jar="./decompiler/dex2jar/", bin_dex2jar="dex2jar.sh", path_fernflower="./decompiler/fernflower/", bin_fernflower="fernflower.jar", options_fernflower={"dgs": '1', "asc": '1'}, tmp_dir="/tmp/"): self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) with os.fdopen(fd, "w+b") as fd: fd.write(vm.get_buff()) fd.flush() compile = Popen([path_dex2jar + bin_dex2jar, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() os.unlink(fdname) pathclasses = fdname + "dex2jar/" compile = Popen(["unzip", fdname + "_dex2jar.jar", "-d", pathclasses], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() os.unlink(fdname + "_dex2jar.jar") for root, dirs, files in os.walk(pathclasses, followlinks=True): if files != []: for f in files: real_filename = root if real_filename[-1] != "/": real_filename += "/" real_filename += f l = ["java", "-jar", path_fernflower + bin_fernflower] for option in options_fernflower: l.append("-%s:%s" % (option, options_fernflower[option])) l.append(real_filename) l.append(root) compile = Popen(l, stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() for i in vm.get_classes(): fname = pathclasses + "/" + i.get_name()[1:-1] + ".java" if os.path.isfile(fname) == True: self.classes[i.get_name()] = read(fname, binary=False) else: self.classes_failed.append(i.get_name()) rrmdir(pathclasses)
def __init__(self, vm, path_dex2jar="./decompiler/dex2jar/", bin_dex2jar="dex2jar.sh", path_fernflower="./decompiler/fernflower/", bin_fernflower="fernflower.jar", options_fernflower={"dgs": '1', "asc": '1'}, tmp_dir="/tmp/"): self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) fd = os.fdopen(fd, "w+b") fd.write(vm.get_buff()) fd.flush() fd.close() compile = Popen([path_dex2jar + bin_dex2jar, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() os.unlink(fdname) pathclasses = fdname + "dex2jar/" compile = Popen(["unzip", fdname + "_dex2jar.jar", "-d", pathclasses], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() os.unlink(fdname + "_dex2jar.jar") for root, dirs, files in os.walk(pathclasses, followlinks=True): if files != []: for f in files: real_filename = root if real_filename[-1] != "/": real_filename += "/" real_filename += f l = ["java", "-jar", path_fernflower + bin_fernflower] for option in options_fernflower: l.append("-%s:%s" % (option, options_fernflower[option])) l.append(real_filename) l.append(root) compile = Popen(l, stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() for i in vm.get_classes(): fname = pathclasses + "/" + i.get_name()[1:-1] + ".java" if os.path.isfile(fname) == True: fd = open(fname, "r") self.classes[i.get_name()] = fd.read() fd.close() else: self.classes_failed.append(i.get_name()) rrmdir(pathclasses)
def __init__(self, vm, path="./decompiler/ded/", bin_ded="ded.sh", tmp_dir="/tmp/"): self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) fd = os.fdopen(fd, "w+b") fd.write(vm.get_buff()) fd.flush() fd.close() dirname = tempfile.mkdtemp(prefix=fdname + "-src") compile = Popen([path + bin_ded, "-c", "-o", "-d", dirname, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() os.unlink(fdname) findsrc = None for root, dirs, files in os.walk(dirname + "/optimized-decompiled/"): if dirs != []: for f in dirs: if f == "src": findsrc = root if findsrc[-1] != "/": findsrc += "/" findsrc += f break if findsrc != None: break for i in vm.get_classes(): fname = findsrc + "/" + i.get_name()[1:-1] + ".java" #print fname if os.path.isfile(fname) == True: fd = open(fname, "r") self.classes[i.get_name()] = fd.read() fd.close() else: self.classes_failed.append(i.get_name()) rrmdir(dirname)
def __init__(self, vm, path="./decompiler/ded/", bin_ded="ded.sh", tmp_dir="/tmp/"): self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) with os.fdopen(fd, "w+b") as fd: fd.write(vm.get_buff()) fd.flush() dirname = tempfile.mkdtemp(prefix=fdname + "-src") compile = Popen([path + bin_ded, "-c", "-o", "-d", dirname, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() os.unlink(fdname) findsrc = None for root, dirs, files in os.walk(dirname + "/optimized-decompiled/"): if dirs != []: for f in dirs: if f == "src": findsrc = root if findsrc[-1] != "/": findsrc += "/" findsrc += f break if findsrc != None: break for i in vm.get_classes(): fname = findsrc + "/" + i.get_name()[1:-1] + ".java" #print fname if os.path.isfile(fname) == True: self.classes[i.get_name()] = read(fname, binary=False) else: self.classes_failed.append(i.get_name()) rrmdir(dirname)
def __init__(self, vm, path_dex2jar = "./decompiler/dex2jar/", bin_dex2jar = "dex2jar.sh", path_jad="./decompiler/jad/", bin_jad="jad", tmp_dir="/tmp/") : self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp) : os.makedirs( pathtmp ) fd, fdname = tempfile.mkstemp( dir=pathtmp ) fd = os.fdopen(fd, "w+b") fd.write( vm.get_buff() ) fd.flush() fd.close() compile = Popen([ path_dex2jar + bin_dex2jar, fdname ], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() os.unlink( fdname ) pathclasses = fdname + "dex2jar/" compile = Popen([ "unzip", fdname + "_dex2jar.jar", "-d", pathclasses ], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() os.unlink( fdname + "_dex2jar.jar" ) for root, dirs, files in os.walk( pathclasses, followlinks=True ) : if files != [] : for f in files : real_filename = root if real_filename[-1] != "/" : real_filename += "/" real_filename += f compile = Popen([ path_jad + bin_jad, "-o", "-d", root, real_filename ], stdout=PIPE, stderr=STDOUT) stdout, stderr = compile.communicate() for i in vm.get_classes() : fname = pathclasses + "/" + i.get_name()[1:-1] + ".jad" if os.path.isfile(fname) == True : fd = open(fname, "r") self.classes[ i.get_name() ] = fd.read() fd.close() else : self.classes_failed.append( i.get_name() ) rrmdir( pathclasses )
def export_apps_to_format(filename, a, output, methods_filter=None, jar=None, decompiler_type=None, format=None): print "Dump information %s in %s" % (filename, output) if not os.path.exists(output): print "Create directory %s" % output os.makedirs(output) else: print "Clean directory %s" % output androconf.rrmdir(output) os.makedirs(output) methods_filter_expr = None if methods_filter: methods_filter_expr = re.compile(methods_filter) output_name = output if output_name[-1] != "/": output_name = output_name + "/" dump_classes = [] for vm in a.get_vms(): print "Analysis ...", sys.stdout.flush() vmx = analysis.VMAnalysis(vm) print "End" print "Decompilation ...", sys.stdout.flush() if not decompiler_type: vm.set_decompiler(decompiler.DecompilerDAD(vm, vmx)) elif decompiler_type == "dex2jad": vm.set_decompiler( decompiler.DecompilerDex2Jad(vm, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR"], androconf.CONF["PATH_JAD"], androconf.CONF["BIN_JAD"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "dex2winejad": vm.set_decompiler( decompiler.DecompilerDex2WineJad( vm, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR"], androconf.CONF["PATH_JAD"], androconf.CONF["BIN_WINEJAD"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "ded": vm.set_decompiler( decompiler.DecompilerDed(vm, androconf.CONF["PATH_DED"], androconf.CONF["BIN_DED"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "dex2fernflower": vm.set_decompiler( decompiler.DecompilerDex2Fernflower( vm, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR"], androconf.CONF["PATH_FERNFLOWER"], androconf.CONF["BIN_FERNFLOWER"], androconf.CONF["OPTIONS_FERNFLOWER"], androconf.CONF["TMP_DIRECTORY"])) else: raise ("invalid decompiler !") print "End" if options.jar: print "jar ...", filenamejar = decompiler.Dex2Jar( vm, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR"], androconf.CONF["TMP_DIRECTORY"]).get_jar() shutil.move(filenamejar, output + "classes.jar") print "End" for method in vm.get_methods(): if methods_filter_expr: msig = "%s%s%s" % (method.get_class_name(), method.get_name(), method.get_descriptor()) if not methods_filter_expr.search(msig): continue filename_class = valid_class_name(method.get_class_name()) create_directory(filename_class, output) print "Dump %s %s %s ..." % (method.get_class_name(), method.get_name(), method.get_descriptor()), filename_class = output_name + filename_class if filename_class[-1] != "/": filename_class = filename_class + "/" descriptor = method.get_descriptor() descriptor = descriptor.replace(";", "") descriptor = descriptor.replace(" ", "") descriptor = descriptor.replace("(", "-") descriptor = descriptor.replace(")", "-") descriptor = descriptor.replace("/", "_") filename = filename_class + method.get_name() + descriptor if len(method.get_name() + descriptor) > 250: all_identical_name_methods = vm.get_methods_descriptor( method.get_class_name(), method.get_name()) pos = 0 for i in all_identical_name_methods: if i.get_descriptor() == method.get_descriptor(): break pos += 1 filename = filename_class + method.get_name() + "_%d" % pos buff = method2dot(vmx.get_method(method)) if format: print "%s ..." % format, method2format(filename + "." + format, format, None, buff) if method.get_class_name() not in dump_classes: print "source codes ...", current_class = vm.get_class(method.get_class_name()) current_filename_class = valid_class_name( current_class.get_name()) create_directory(filename_class, output) current_filename_class = output_name + current_filename_class + ".java" fd = open(current_filename_class, "w") fd.write(current_class.get_source()) fd.close() dump_classes.append(method.get_class_name()) print "bytecodes ...", bytecode_buff = dvm.get_bytecodes_method(vm, vmx, method) fd = open(filename + ".ag", "w") fd.write(bytecode_buff) fd.close() print
def export_apps_to_format(filename, s, output, methods_filter=None, jar=None, decompiler_type=None, form=None): print("Dump information %s in %s" % (filename, output)) if not os.path.exists(output): print("Create directory %s" % output) os.makedirs(output) else: print("Clean directory %s" % output) androconf.rrmdir(output) os.makedirs(output) methods_filter_expr = None if methods_filter: methods_filter_expr = re.compile(methods_filter) dump_classes = [] for _, vm, vmx in s.get_objects_dex(): print("Decompilation ...", end=' ') sys.stdout.flush() if decompiler_type == "dex2jad": vm.set_decompiler(decompiler.DecompilerDex2Jad(vm, androconf.CONF["BIN_DEX2JAR"], androconf.CONF["BIN_JAD"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "dex2winejad": vm.set_decompiler(decompiler.DecompilerDex2WineJad(vm, androconf.CONF["BIN_DEX2JAR"], androconf.CONF["BIN_WINEJAD"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "ded": vm.set_decompiler(decompiler.DecompilerDed(vm, androconf.CONF["BIN_DED"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "dex2fernflower": vm.set_decompiler(decompiler.DecompilerDex2Fernflower(vm, androconf.CONF["BIN_DEX2JAR"], androconf.CONF["BIN_FERNFLOWER"], androconf.CONF["OPTIONS_FERNFLOWER"], androconf.CONF["TMP_DIRECTORY"])) print("End") if jar: print("jar ...", end=' ') filenamejar = decompiler.Dex2Jar(vm, androconf.CONF["BIN_DEX2JAR"], androconf.CONF["TMP_DIRECTORY"]).get_jar() shutil.move(filenamejar, os.path.join(output, "classes.jar")) print("End") for method in vm.get_methods(): if methods_filter_expr: msig = "%s%s%s" % (method.get_class_name(), method.get_name(), method.get_descriptor()) if not methods_filter_expr.search(msig): continue # Current Folder to write to filename_class = valid_class_name(method.get_class_name()) filename_class = os.path.join(output, filename_class) create_directory(filename_class) print("Dump %s %s %s ..." % (method.get_class_name(), method.get_name(), method.get_descriptor()), end=' ') filename = clean_file_name(os.path.join(filename_class, method.get_short_string())) buff = method2dot(vmx.get_method(method)) # Write Graph of method if form: print("%s ..." % form, end=' ') method2format(filename + "." + form, form, None, buff) # Write the Java file for the whole class if method.get_class_name() not in dump_classes: print("source codes ...", end=' ') current_class = vm.get_class(method.get_class_name()) current_filename_class = valid_class_name(current_class.get_name()) current_filename_class = os.path.join(output, current_filename_class + ".java") with open(current_filename_class, "w") as fd: fd.write(current_class.get_source()) dump_classes.append(method.get_class_name()) # Write SMALI like code print("bytecodes ...", end=' ') bytecode_buff = dvm.get_bytecodes_method(vm, vmx, method) with open(filename + ".ag", "w") as fd: fd.write(bytecode_buff) print()
def __init__(self, vm, vmx, jadx="jadx", keepfiles=False): """ DecompilerJADX is a wrapper for the jadx decompiler: https://github.com/skylot/jadx Note, that jadx need to write files to your local disk. :param vm: `DalvikVMFormat` object :param vmx: `Analysis` object :param jadx: path to the jadx executable :param keepfiles: set to True, if you like to keep temporary files """ self.vm = vm self.vmx = vmx # Dictionary to store classnames: sourcecode self.classes = {} # Result directory: # TODO need to remove the folder correctly! tmpfolder = tempfile.mkdtemp() # We need to decompile the whole dex file, as we do not have an API... # dump the dex file into a temp file # THIS WILL NOT WORK ON WINDOWS!!! # See https://stackoverflow.com/q/15169101/446140 # Files can not be read, only if they specify temp file. But jadx does not do that... # # We need to trick jadx by setting the suffix, otherwise the file will not be loaded with tempfile.NamedTemporaryFile(suffix=".dex") as tf: tf.write(vm.get_buff()) cmd = [ jadx, "-d", tmpfolder, "--escape-unicode", "--no-res", tf.name ] log.debug("Call JADX with the following cmdline: {}".format( " ".join(cmd))) x = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout, _ = x.communicate() # Looks like jadx does not use stderr log.info("Output of JADX during decompilation") for line in stdout.decode("UTF-8").splitlines(): log.info(line) if x.returncode != 0: rrmdir(tmpfolder) raise JADXDecompilerError( "Could not decompile file. Args: {}".format(" ".join(cmd))) # Next we parse the folder structure for later lookup # We read the content of each file here, so we can later delete the folder # We check here two ways, first we iterate all files and see if the class exists # in androguard # then, we iterate all classes in androguard and check if the file exists. andr_class_names = {x.get_name()[1:-1]: x for x in vm.get_classes()} # First, try to find classes for the files we have for root, dirs, files in os.walk(tmpfolder): for f in files: if not f.endswith(".java"): log.warning( "found a file in jadx folder which is not a java file: {}" .format(f)) continue # as the path begins always with `self.res` (hopefully), we remove that length # also, all files should end with .java path = os.path.join(root, f)[len(tmpfolder) + 1:-5] path = path.replace(os.sep, "/") # Special care for files without package # All files that have no package set, will get the # package `defpackage` automatically if path.startswith("defpackage"): path = path[len("defpackage/"):] if path in andr_class_names: with open(os.path.join(root, f), "rb") as fp: # Need to convert back to the "full" classname self.classes["L{};".format(path)] = fp.read().decode( "UTF-8") else: log.warning( "Found a class called {}, which is not found by androguard!" .format(path)) # Next, try to find files for the classes we have for cl in andr_class_names: fname = self._find_class(cl, tmpfolder) if fname: if "L{};".format(cl) not in self.classes: with open(fname, "rb") as fp: # TODO need to snip inner class from file self.classes["L{};".format(cl)] = fp.read().decode( "UTF-8") else: # Class was already found... pass else: log.warning( "Found a class called {} which is not decompiled by jadx". format(cl)) # check if we have good matching if len(self.classes) == len(andr_class_names): log.debug( "JADX looks good, we have the same number of classes: {}". format(len(self.classes))) else: log.info( "Looks like JADX is missing some classes or " "we decompiled too much: decompiled: {} vs androguard: {}". format(len(self.classes), len(andr_class_names))) if not keepfiles: rrmdir(tmpfolder)
def __init__(self, vm, bin_dex2jar="dex2jar.sh", bin_fernflower="fernflower.jar", options_fernflower={"dgs": '1', "asc": '1'}, tmp_dir="/tmp/"): """ Decompiler interface for Fernflower Fernflower is a java decompiler by IntelliJ: https://github.com/JetBrains/intellij-community/tree/master/plugins/java-decompiler/engine As it can not decompile Dalvik code directly, the DEX is first decompiled as a JAR file. :param vm: `DalvikVMFormtat` object :param bin_dex2jar: :param bin_fernflower: :param options_fernflower: :param tmp_dir: """ self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) with os.fdopen(fd, "w+b") as fd: fd.write(vm.get_buff()) fd.flush() cmd = Popen([bin_dex2jar, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname) pathclasses = fdname + "dex2jar/" cmd = Popen(["unzip", fdname + "_dex2jar.jar", "-d", pathclasses], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname + "_dex2jar.jar") for root, dirs, files in os.walk(pathclasses, followlinks=True): if files: for f in files: real_filename = root if real_filename[-1] != "/": real_filename += "/" real_filename += f l = ["java", "-jar", bin_fernflower] for option in options_fernflower: l.append("-%s:%s" % (option, options_fernflower[option])) l.append(real_filename) l.append(root) cmd = Popen(l, stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() for i in vm.get_classes(): fname = pathclasses + "/" + i.get_name()[1:-1] + ".java" if os.path.isfile(fname): self.classes[i.get_name()] = read(fname, binary=False) else: self.classes_failed.append(i.get_name()) rrmdir(pathclasses)
def __init__(self, vm, bin_dex2jar="dex2jar.sh", bin_fernflower="fernflower.jar", options_fernflower={ "dgs": '1', "asc": '1' }, tmp_dir="/tmp/"): """ Decompiler interface for Fernflower Fernflower is a java decompiler by IntelliJ: https://github.com/JetBrains/intellij-community/tree/master/plugins/java-decompiler/engine As it can not decompile Dalvik code directly, the DEX is first decompiled as a JAR file. :param vm: `DalvikVMFormtat` object :param bin_dex2jar: :param bin_fernflower: :param options_fernflower: :param tmp_dir: """ self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) with os.fdopen(fd, "w+b") as fd: fd.write(vm.get_buff()) fd.flush() cmd = Popen([bin_dex2jar, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname) pathclasses = fdname + "dex2jar/" cmd = Popen(["unzip", fdname + "_dex2jar.jar", "-d", pathclasses], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname + "_dex2jar.jar") for root, dirs, files in os.walk(pathclasses, followlinks=True): if files: for f in files: real_filename = root if real_filename[-1] != "/": real_filename += "/" real_filename += f l = ["java", "-jar", bin_fernflower] for option in options_fernflower: l.append("-%s:%s" % (option, options_fernflower[option])) l.append(real_filename) l.append(root) cmd = Popen(l, stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() for i in vm.get_classes(): fname = pathclasses + "/" + i.get_name()[1:-1] + ".java" if os.path.isfile(fname): self.classes[i.get_name()] = read(fname, binary=False) else: self.classes_failed.append(i.get_name()) rrmdir(pathclasses)
def __init__(self, vm, vmx, jadx="jadx", keepfiles=False): """ DecompilerJADX is a wrapper for the jadx decompiler: https://github.com/skylot/jadx Note, that jadx need to write files to your local disk. :param vm: `DalvikVMFormat` object :param vmx: `Analysis` object :param jadx: path to the jadx executable :param keepfiles: set to True, if you like to keep temporary files """ self.vm = vm self.vmx = vmx # Dictionary to store classnames: sourcecode self.classes = {} # Result directory: # TODO need to remove the folder correctly! tmpfolder = tempfile.mkdtemp() # We need to decompile the whole dex file, as we do not have an API... # dump the dex file into a temp file # THIS WILL NOT WORK ON WINDOWS!!! # See https://stackoverflow.com/q/15169101/446140 # Files can not be read, only if they specify temp file. But jadx does not do that... # # We need to trick jadx by setting the suffix, otherwise the file will not be loaded with tempfile.NamedTemporaryFile(suffix=".dex") as tf: tf.write(vm.get_buff()) cmd = [jadx, "-d", tmpfolder, "--escape-unicode", "--no-res", tf.name] log.debug("Call JADX with the following cmdline: {}".format(" ".join(cmd))) x = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout, _ = x.communicate() # Looks like jadx does not use stderr log.info("Output of JADX during decompilation") for line in stdout.decode("UTF-8").splitlines(): log.info(line) if x.returncode != 0: rrmdir(tmpfolder) raise JADXDecompilerError("Could not decompile file. Args: {}".format(" ".join(cmd))) # Next we parse the folder structure for later lookup # We read the content of each file here, so we can later delete the folder # We check here two ways, first we iterate all files and see if the class exists # in androguard # then, we iterate all classes in androguard and check if the file exists. andr_class_names = {x.get_name()[1:-1]: x for x in vm.get_classes()} # First, try to find classes for the files we have for root, dirs, files in os.walk(tmpfolder): for f in files: if not f.endswith(".java"): log.warning("found a file in jadx folder which is not a java file: {}".format(f)) continue # as the path begins always with `self.res` (hopefully), we remove that length # also, all files should end with .java path = os.path.join(root, f)[len(tmpfolder) + 1:-5] path = path.replace(os.sep, "/") # Special care for files without package # All files that have no package set, will get the # package `defpackage` automatically if path.startswith("defpackage"): path = path[len("defpackage/"):] if path in andr_class_names: with open(os.path.join(root, f), "rb") as fp: # Need to convert back to the "full" classname self.classes["L{};".format(path)] = fp.read().decode("UTF-8") else: log.warning("Found a class called {}, which is not found by androguard!".format(path)) # Next, try to find files for the classes we have for cl in andr_class_names: fname = self._find_class(cl, tmpfolder) if fname: if "L{};".format(cl) not in self.classes: with open(fname, "rb") as fp: # TODO need to snip inner class from file self.classes["L{};".format(cl)] = fp.read().decode("UTF-8") else: # Class was already found... pass else: log.warning("Found a class called {} which is not decompiled by jadx".format(cl)) # check if we have good matching if len(self.classes) == len(andr_class_names): log.debug("JADX looks good, we have the same number of classes: {}".format(len(self.classes))) else: log.info("Looks like JADX is missing some classes or " "we decompiled too much: decompiled: {} vs androguard: {}".format(len(self.classes), len(andr_class_names))) if not keepfiles: rrmdir(tmpfolder)
def __init__(self, vm, bin_dex2jar="dex2jar.sh", bin_jad="jad", tmp_dir="/tmp/"): """ Decompiler interface for JAD JAD is not a native Dalvik decompiler, therefore dex2jar is required. .. deprecated:: 3.3.5 JAD is not supported anymore in androguard! :param vm: :param bin_dex2jar: :param bin_jad: :param tmp_dir: """ warnings.warn("JAD is deprecated since 3.3.5.", DeprecationWarning) self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) with os.fdopen(fd, "w+b") as fd: fd.write(vm.get_buff()) fd.flush() cmd = Popen([bin_dex2jar, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname) pathclasses = fdname + "dex2jar/" cmd = Popen(["unzip", fdname + "_dex2jar.jar", "-d", pathclasses], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname + "_dex2jar.jar") for root, dirs, files in os.walk(pathclasses, followlinks=True): if files: for f in files: real_filename = root if real_filename[-1] != "/": real_filename += "/" real_filename += f cmd = Popen([bin_jad, "-o", "-d", root, real_filename], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() for i in vm.get_classes(): fname = pathclasses + "/" + i.get_name()[1:-1] + ".jad" if os.path.isfile(fname): self.classes[i.get_name()] = read(fname, binary=False) else: self.classes_failed.append(i.get_name()) rrmdir(pathclasses)
def __init__(self, vm, bin_dex2jar="dex2jar.sh", bin_jad="jad", tmp_dir="/tmp/"): """ Decompiler interface for JAD JAD is not a native Dalvik decompiler, therefore dex2jar is required. .. deprecated:: 3.3.5 JAD is not supported anymore in androguard! :param vm: :param bin_dex2jar: :param bin_jad: :param tmp_dir: """ warnings.warn("JAD is deprecated since 3.3.5.", DeprecationWarning) self.classes = {} self.classes_failed = [] pathtmp = tmp_dir if not os.path.exists(pathtmp): os.makedirs(pathtmp) fd, fdname = tempfile.mkstemp(dir=pathtmp) with os.fdopen(fd, "w+b") as fd: fd.write(vm.get_buff()) fd.flush() cmd = Popen([bin_dex2jar, fdname], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname) pathclasses = fdname + "dex2jar/" cmd = Popen(["unzip", fdname + "_dex2jar.jar", "-d", pathclasses], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() os.unlink(fdname + "_dex2jar.jar") for root, dirs, files in os.walk(pathclasses, followlinks=True): if files: for f in files: real_filename = root if real_filename[-1] != "/": real_filename += "/" real_filename += f cmd = Popen([bin_jad, "-o", "-d", root, real_filename], stdout=PIPE, stderr=STDOUT) stdout, stderr = cmd.communicate() for i in vm.get_classes(): fname = pathclasses + "/" + i.get_name()[1:-1] + ".jad" if os.path.isfile(fname): self.classes[i.get_name()] = read(fname, binary=False) else: self.classes_failed.append(i.get_name()) rrmdir(pathclasses)
def export_apps_to_format(filename, s, output, methods_filter=None, jar=None, decompiler_type=None, form=None): from androguard.misc import clean_file_name from androguard.core.bytecodes import dvm from androguard.core.bytecode import method2dot, method2format from androguard.decompiler import decompiler print("Dump information {} in {}".format(filename, output)) if not os.path.exists(output): print("Create directory %s" % output) os.makedirs(output) else: print("Clean directory %s" % output) androconf.rrmdir(output) os.makedirs(output) methods_filter_expr = None if methods_filter: methods_filter_expr = re.compile(methods_filter) dump_classes = [] for _, vm, vmx in s.get_objects_dex(): print("Decompilation ...", end=' ') sys.stdout.flush() if decompiler_type == "dex2jad": vm.set_decompiler(decompiler.DecompilerDex2Jad(vm, androconf.CONF["BIN_DEX2JAR"], androconf.CONF["BIN_JAD"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "dex2winejad": vm.set_decompiler(decompiler.DecompilerDex2WineJad(vm, androconf.CONF["BIN_DEX2JAR"], androconf.CONF["BIN_WINEJAD"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "ded": vm.set_decompiler(decompiler.DecompilerDed(vm, androconf.CONF["BIN_DED"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "dex2fernflower": vm.set_decompiler(decompiler.DecompilerDex2Fernflower(vm, androconf.CONF["BIN_DEX2JAR"], androconf.CONF["BIN_FERNFLOWER"], androconf.CONF["OPTIONS_FERNFLOWER"], androconf.CONF["TMP_DIRECTORY"])) print("End") if jar: print("jar ...", end=' ') filenamejar = decompiler.Dex2Jar(vm, androconf.CONF["BIN_DEX2JAR"], androconf.CONF["TMP_DIRECTORY"]).get_jar() shutil.move(filenamejar, os.path.join(output, "classes.jar")) print("End") for method in vm.get_methods(): if methods_filter_expr: msig = "{}{}{}".format(method.get_class_name(), method.get_name(), method.get_descriptor()) if not methods_filter_expr.search(msig): continue # Current Folder to write to filename_class = valid_class_name(str(method.get_class_name())) filename_class = os.path.join(output, filename_class) create_directory(filename_class) print("Dump {} {} {} ...".format(method.get_class_name(), method.get_name(), method.get_descriptor()), end=' ') filename = clean_file_name(os.path.join(filename_class, method.get_short_string())) buff = method2dot(vmx.get_method(method)) # Write Graph of method if form: print("%s ..." % form, end=' ') method2format(filename + "." + form, form, None, buff) # Write the Java file for the whole class if str(method.get_class_name()) not in dump_classes: print("source codes ...", end=' ') current_class = vm.get_class(method.get_class_name()) current_filename_class = valid_class_name(str(current_class.get_name())) current_filename_class = os.path.join(output, current_filename_class + ".java") with open(current_filename_class, "w") as fd: fd.write(current_class.get_source()) dump_classes.append(method.get_class_name()) # Write SMALI like code print("bytecodes ...", end=' ') bytecode_buff = dvm.get_bytecodes_method(vm, vmx, method) with open(filename + ".ag", "w") as fd: fd.write(bytecode_buff) print()
def export_apps_to_format(filename, a, output, methods_filter=None, jar=None, decompiler_type=None, format=None): print "Dump information %s in %s" % (filename, output) if not os.path.exists(output): print "Create directory %s" % output os.makedirs(output) else: print "Clean directory %s" % output androconf.rrmdir(output) os.makedirs(output) methods_filter_expr = None if methods_filter: methods_filter_expr = re.compile(methods_filter) output_name = output if output_name[-1] != "/": output_name = output_name + "/" dump_classes = [] for vm in a.get_vms(): print "Analysis ...", sys.stdout.flush() vmx = analysis.VMAnalysis(vm) print "End" print "Decompilation ...", sys.stdout.flush() if not decompiler_type: vm.set_decompiler(decompiler.DecompilerDAD(vm, vmx)) elif decompiler_type == "dex2jad": vm.set_decompiler(decompiler.DecompilerDex2Jad( vm, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR" ], androconf.CONF["PATH_JAD"], androconf.CONF["BIN_JAD"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "dex2winejad": vm.set_decompiler(decompiler.DecompilerDex2WineJad( vm, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR" ], androconf.CONF["PATH_JAD"], androconf.CONF["BIN_WINEJAD"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "ded": vm.set_decompiler(decompiler.DecompilerDed( vm, androconf.CONF["PATH_DED"], androconf.CONF["BIN_DED"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "dex2fernflower": vm.set_decompiler(decompiler.DecompilerDex2Fernflower( vm, androconf.CONF["PATH_DEX2JAR"], androconf.CONF[ "BIN_DEX2JAR" ], androconf.CONF["PATH_FERNFLOWER"], androconf.CONF[ "BIN_FERNFLOWER" ], androconf.CONF["OPTIONS_FERNFLOWER" ], androconf.CONF["TMP_DIRECTORY"])) else: raise ("invalid decompiler !") print "End" if options.jar: print "jar ...", filenamejar = decompiler.Dex2Jar( vm, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR"], androconf.CONF["TMP_DIRECTORY"]).get_jar() shutil.move(filenamejar, output + "classes.jar") print "End" for method in vm.get_methods(): if methods_filter_expr: msig = "%s%s%s" % (method.get_class_name(), method.get_name(), method.get_descriptor()) if not methods_filter_expr.search(msig): continue filename_class = valid_class_name(method.get_class_name()) create_directory(filename_class, output) print "Dump %s %s %s ..." % (method.get_class_name(), method.get_name(), method.get_descriptor()), filename_class = output_name + filename_class if filename_class[-1] != "/": filename_class = filename_class + "/" descriptor = method.get_descriptor() descriptor = descriptor.replace(";", "") descriptor = descriptor.replace(" ", "") descriptor = descriptor.replace("(", "-") descriptor = descriptor.replace(")", "-") descriptor = descriptor.replace("/", "_") filename = filename_class + method.get_name() + descriptor if len(method.get_name() + descriptor) > 250: all_identical_name_methods = vm.get_methods_descriptor( method.get_class_name(), method.get_name()) pos = 0 for i in all_identical_name_methods: if i.get_descriptor() == method.get_descriptor(): break pos += 1 filename = filename_class + method.get_name() + "_%d" % pos buff = method2dot(vmx.get_method(method)) if format: print "%s ..." % format, method2format(filename + "." + format, format, None, buff) if method.get_class_name() not in dump_classes: print "source codes ...", current_class = vm.get_class(method.get_class_name()) current_filename_class = valid_class_name( current_class.get_name()) create_directory(filename_class, output) current_filename_class = output_name + current_filename_class + ".java" with open(current_filename_class, "w") as fd: fd.write(current_class.get_source()) dump_classes.append(method.get_class_name()) print "bytecodes ...", bytecode_buff = dvm.get_bytecodes_method(vm, vmx, method) with open(filename + ".ag", "w") as fd: fd.write(bytecode_buff) print
def decompile(apkname, output): print "Dump information %s in %s" % (apkname, output) apk_vm_serial = [] a = Androguard([apkname]) decompiler_type = None if not os.path.exists(output): print "Create directory %s" % output os.makedirs(output) else: print "Clean directory %s" % output androconf.rrmdir(output) os.makedirs(output) output_dir = output if output_dir[-1] != "/": output_name = output_dir + "/" print "Output dir: %s" % output_dir for vm in a.get_vms(): vm_list = [] #vm_list = [vm, vmx] print "Analysis ...", sys.stdout.flush() vmx = analysis.VMAnalysis(vm) vm_list.append(vm) vm_list.append(vmx) print "End" print "Decompilation ...", sys.stdout.flush() if not decompiler_type: vm.set_decompiler(decompiler.DecompilerDAD(vm, vmx)) elif decompiler_type == "dex2jad": vm.set_decompiler( decompiler.DecompilerDex2Jad(vm, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR"], androconf.CONF["PATH_JAD"], androconf.CONF["BIN_JAD"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "dex2winejad": vm.set_decompiler( decompiler.DecompilerDex2WineJad( vm, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR"], androconf.CONF["PATH_JAD"], androconf.CONF["BIN_WINEJAD"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "ded": vm.set_decompiler( decompiler.DecompilerDed(vm, androconf.CONF["PATH_DED"], androconf.CONF["BIN_DED"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler_type == "dex2fernflower": vm.set_decompiler( decompiler.DecompilerDex2Fernflower( vm, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR"], androconf.CONF["PATH_FERNFLOWER"], androconf.CONF["BIN_FERNFLOWER"], androconf.CONF["OPTIONS_FERNFLOWER"], androconf.CONF["TMP_DIRECTORY"])) else: raise ("invalid decompiler !") apk_vm_serial.append(vm_list) print "End" return apk_vm_serial