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 , vmx , jadx = "jadx" , keepfiles = False ) : self . vm = vm self . vmx = vmx self . classes = { } tmpfolder = tempfile . mkdtemp ( ) 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 ( ) 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 ) ) ) andr_class_names = { x . get_name ( ) [ 1 : - 1 ] : x for x in vm . get_classes ( ) } 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 path = os . path . join ( root , f ) [ len ( tmpfolder ) + 1 : - 5 ] path = path . replace ( os . sep , "/" ) if path . startswith ( "defpackage" ) : path = path [ len ( "defpackage/" ) : ] if path in andr_class_names : with open ( os . path . join ( root , f ) , "rb" ) as fp : 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 ) ) 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 : self . classes [ "L{};" . format ( cl ) ] = fp . read ( ) . decode ( "UTF-8" ) else : pass else : log . warning ( "Found a class called {} which is not decompiled by jadx" . format ( cl ) ) 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 )
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