Exemplo n.º 1
0
    def analyzeAPK(self):
        """ Uses androguard to retrieve metadata about the app e.g. activities, permissions, intent filters, etc. """
        try:
            prettyPrint("Analyzing app")
            logEvent("Analyzing app: \"%s\"" % self.APKPath)
            # 1. Load the APK using androguard
            analysisSession = Session()
            analysisSession.add(self.APKPath, open(self.APKPath).read())
            # 2. Retrieve handles to APK and its dex code
            self.APK = analysisSession.analyzed_apk.values()[0]
            self.DEX = analysisSession.analyzed_dex.values()[0][0]
            self.VMAnalysis = analysisSession.analyzed_dex.values()[0][1]
            # 3. Retrieve information for each activity
            prettyPrint("Analyzing activities")
            self.activitiesInfo = analyzeActivities(self.APK, self.DEX)
            # 4. Do the same for services and broadcast receivers
            prettyPrint("Analyzing services")
            self.servicesInfo = analyzeServices(self.APK, self.DEX)
            prettyPrint("Analyzing broadcast receivers")
            self.receiversInfo = analyzeReceivers(self.APK, self.DEX)

        except Exception as e:
            prettyPrintError(e)
            return False

        prettyPrint("Success")
        return True
Exemplo n.º 2
0
    def testTypes(self):
        s = Session()
        with open(TEST_CASE, "rb") as fd:
            digest, d, dx = s.addDEX(TEST_CASE, fd.read())

        for method in d.get_methods():
            key = method.get_class_name() + " " + method.get_name(
            ) + " " + method.get_descriptor()

            if key not in VALUES:
                continue

            print("METHOD", method.get_class_name(), method.get_name(
            ), method.get_descriptor())

            code = method.get_code()
            bc = code.get_bc()

            idx = 0
            for i in bc.get_instructions():
                if "const" in i.get_name():
                    i.show(0)
                    formatted_operands = i.get_formatted_operands()
                    print(formatted_operands)
                    if not formatted_operands:
                        VALUES[key].pop(0)
                    else:
                        for f in formatted_operands:
                            self.assertAlmostEqual(f, VALUES[key].pop(0), places=4)

                idx += i.get_length()
Exemplo n.º 3
0
    def testTypes(self):
        s = Session()
        with open(TEST_CASE, "rb") as fd:
            digest, d, dx = s.addDEX(TEST_CASE, fd.read())

        for method in d.get_methods():
            key = method.get_class_name() + " " + method.get_name(
            ) + " " + method.get_descriptor()

            if key not in VALUES:
                continue

            print("METHOD", method.get_class_name(), method.get_name(),
                  method.get_descriptor())

            code = method.get_code()
            bc = code.get_bc()

            idx = 0
            for i in bc.get_instructions():
                if "const" in i.get_name():
                    i.show(0)
                    formatted_operands = i.get_formatted_operands()
                    print(formatted_operands)
                    if not formatted_operands:
                        VALUES[key].pop(0)
                    else:
                        for f in formatted_operands:
                            self.assertAlmostEqual(f,
                                                   VALUES[key].pop(0),
                                                   places=4)

                idx += i.get_length()
Exemplo n.º 4
0
    def setupSession(self):
        self.session = Session()

        self.fileLoadingThread = FileLoadingThread(self.session)
        self.connect(self.fileLoadingThread,
                     QtCore.SIGNAL("loadedFile(bool)"),
                     self.loadedFile)
Exemplo n.º 5
0
    def setupSession(self):
        self.session = Session()

        self.fileLoadingThread = FileLoadingThread(self.session)
        self.connect(self.fileLoadingThread,
                QtCore.SIGNAL("loadedFile(bool)"),
                self.loadedFile)
Exemplo n.º 6
0
def interact(session=False, apk=None):
    """
    Start an interactive shell
    :param session:
    :param apk:
    :return:
    """
    from androguard.core.androconf import ANDROGUARD_VERSION, CONF
    from IPython.terminal.embed import InteractiveShellEmbed
    from traitlets.config import Config

    from androguard.misc import init_print_colors, AnalyzeAPK
    from androguard.session import Session

    if session:
        CONF["SESSION"] = Session(export_ipython=True)

    if apk:
        print("Loading apk {}...".format(os.path.basename(apk)))
        print("Please be patient, this might take a while.")
        # TODO we can export fancy aliases for those as well...
        a, d, dx = AnalyzeAPK(apk)

    cfg = Config()
    _version_string = "Androguard version {}".format(ANDROGUARD_VERSION)
    ipshell = InteractiveShellEmbed(
        config=cfg, banner1="{} started".format(_version_string))
    init_print_colors()
    ipshell()
Exemplo n.º 7
0
def interact():
    CONF["SESSION"] = Session(True)
    cfg = Config()
    ipshell = InteractiveShellEmbed(config=cfg,
                                    banner1="Androguard version %s" %
                                    ANDROGUARD_VERSION)
    init_print_colors()
    ipshell()
Exemplo n.º 8
0
def get_default_session():
    """
    Return the default Session from the configuration
    or create a new one, if the session in the configuration is None.
    """
    if androconf.CONF["SESSION"] is None:
        androconf.CONF["SESSION"] = Session()
    return androconf.CONF["SESSION"]
Exemplo n.º 9
0
    def testTypes(self):
        s = Session()
        with open(TEST_CASE, "rb") as fd:
            digest, d, dx = s.addDEX(TEST_CASE, fd.read())

        for method in filter(lambda x: x.full_name in VALUES, d.get_methods()):
            print("METHOD", method.full_name)

            for i in filter(lambda x: 'const' in x.get_name(), method.get_instructions()):
                i.show(0)
                # ins should only have one literal
                self.assertEquals(len(i.get_literals()), 1)

                fmt, value = VALUES[method.full_name].pop(0)
                converted = format_value(i.get_literals()[0], i, fmt)
                print(i.get_literals(), fmt, value, converted)
                self.assertEqual(converted, value)
                print()
Exemplo n.º 10
0
    def testTypes(self):
        s = Session()
        with open(TEST_CASE, "rb") as fd:
            digest, d, dx = s.addDEX(TEST_CASE, fd.read())

        for method in filter(lambda x: x.full_name in VALUES, d.get_methods()):
            print("METHOD", method.full_name)

            for i in filter(lambda x: 'const' in x.get_name(),
                            method.get_instructions()):
                i.show(0)
                # ins should only have one literal
                self.assertEquals(len(i.get_literals()), 1)

                fmt, value = VALUES[method.full_name].pop(0)
                converted = format_value(i.get_literals()[0], i, fmt)
                print(i.get_literals(), fmt, value, converted)
                self.assertEqual(converted, value)
                print()
Exemplo n.º 11
0
def process_files(apk1, apk2, should_multiprocess=True):
    """
    Similar issues to load_androguard. Serialization issue prevents sending this object (multiple Gbs in RAM) through a
    multiprocessing mechanism such as Pipes (or anything build on top of it, i. e. Queues).
    """
    file_paths = (apk1, apk2)
    if not should_multiprocess:
        s = Session()
        return tuple(
            map(lambda f: load_androguard(f, True, False, s=s), file_paths))

    parent_conn, child_conn = multiprocessing.Pipe(False)

    def post_result(file_path, conn):
        value = load_androguard(file_path, True, False)
        conn.send((file_path, value))

    ps = [
        multiprocessing.Process(target=post_result, args=(f, child_conn))
        for f in file_paths
    ]

    def apply_map(f, i):
        for x in i:
            f(x)

    assert len(file_paths) == 2
    print("Starting multiprocessing Files")

    # Serialization with Pickle requires higher recursion limit
    import sys
    previous_recursion = sys.getrecursionlimit()
    sys.setrecursionlimit(50000)

    apply_map(multiprocessing.Process.start, ps)

    values = (parent_conn.recv(), parent_conn.recv())
    r = tuple(
        map(lambda x: x[1], sorted(values,
                                   key=lambda x: file_paths.index(x[0]))))

    apply_map(multiprocessing.Process.join, ps)

    print("Finished all processes")
    sys.setrecursionlimit(previous_recursion)
    return r
Exemplo n.º 12
0
def interact(session=False, apk=None):
    """
    Start an interactive shell
    :param session:
    :param apk:
    :return:
    """
    if session:
        CONF["SESSION"] = Session(export_ipython=True)

    if apk:
        print("Loading apk {}...".format(os.path.basename(apk)))
        print("Please be patient, this might take a while.")
        # TODO we can export fancy aliases for those as well...
        a, d, dx = AnalyzeAPK(apk)

    cfg = Config()
    ipshell = InteractiveShellEmbed(config=cfg, banner1="{} started".format(_version_string))
    init_print_colors()
    ipshell()
Exemplo n.º 13
0
def load_androguard(file_path,
                    force_reload=False,
                    write_session=True,
                    session_file=None,
                    s=None):
    """
    Should handle saving and loading sessions automatically.

    Writing and Loading sessions currently cause a Kernel Disconnect or an EOF Error (Pickle serialization issues)
    """
    if (not force_reload) and path.exists(session_file):
        print("Loading Existing Session")
        s = Load(session_file)
        a = d = dx = None
    else:
        print("Loading Session from Apk at", file_path)
        if s is None:
            s = Session()
        a, d, dx = AnalyzeAPK(file_path, s)
        if write_session:
            print("Saving Loaded Session to", session_file)
            s.add(file_path, dx=dx)
            Save(s, session_file)
    return a, d, dx
Exemplo n.º 14
0
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    fh.setFormatter(formatter)
    ch.setFormatter(formatter)
    # add the handlers to the logger
    logger.addHandler(fh)
    logger.addHandler(ch)

    if not os.path.isfile(path):
        logger.info('{path} is not a valid file'.format(path=path))
        sys.exit()

    if not output_file_path:
        output_file_path = os.path.splitext(os.path.basename(path))[0] + '.json'
        logger.info('output file defaulting to: {path}'.format(path=output_file_path))
    start = time.time()
    a, d, dx = AnalyzeAPK(path, session=Session())
    end = time.time()
    logger.info('androguard time: {t}'.format(t=(end-start)))

    #a = <class 'androguard.core.bytecodes.apk.APK'>
    #d = list <class 'androguard.core.bytecodes.dvm.DalvikVMFormat'> a list of DalvikVMFormat objects, each pertaining to a single classes.dex
    #dx = list <class 'androguard.core.analysis.analysis.Analysis'>  A list of Analyses objects, each pertaining to a single classes.dex

    logger.info(len(d))
    logger.info(len(dx))
    
    start = time.time()
    result = []
    for __d in d:#all dex
        print(len(__d.get_classes()))
        continue
Exemplo n.º 15
0
def extractStaticFeatures(apkPath):
    """Extracts static numerical features from APK using Androguard"""
    try:
        features = [[], [], [], []] # Tuples are immutable
        if os.path.exists(apkPath.replace(".apk",".static")):
            prettyPrint("Found a pre-computed static features file")
            bFeatures, pFeatures, aFeatures, allFeatures = [], [], [], []
            try:
                possibleExtensions = [".basic", ".perm", ".api", ".static"]
                for ext in possibleExtensions:
                    if os.path.exists(apkPath.replace(".apk", ext)):
                        content = open(apkPath.replace(".apk", ext)).read()
                        if len(content) > 0:
                            features[possibleExtensions.index(ext)] = [float(f) for f in content[1:-1].split(',') if len(f) > 0]

                return tuple(features)

            except Exception as e:
                prettyPrintError(e)
                prettyPrint("Could not extract features from \".static\" file. Continuing as usual", "warning")
        if verboseON():
            prettyPrint("Starting analysis on \"%s\"" % apkPath, "debug")
        analysisSession = Session()
        if not os.path.exists(apkPath):
            prettyPrint("Could not find the APK file \"%s\"" % apkPath, "warning")
            return [], [], [], []
        # 1. Analyze APK and retrieve its components
        #t = threading.Timer(300.0, returnEmptyFeatures) # Guarantees not being stuck on analyzing an APK
        #t.start()
        analysisSession.add(apkPath, open(apkPath).read())
        if type(analysisSession.analyzed_apk.values()) == list:
            apk = analysisSession.analyzed_apk.values()[0][0]
        else:
            apk = analysisSession.analyzed_apk.values()[0]
        dex = analysisSession.analyzed_dex.values()[0][0]
        vm = analysisSession.analyzed_dex.values()[0][1]
        # 2. Add features to the features vector
        basicFeatures, permissionFeatures, apiCallFeatures, allFeatures = [], [], [], []
        # 2.a. The APK-related features
        if verboseON():
            prettyPrint("Extracting basic features", "debug")
        minSDKVersion = 0.0 if not apk.get_min_sdk_version() else float(apk.get_min_sdk_version())
        maxSDKVersion = 0.0 if not apk.get_max_sdk_version() else float(apk.get_max_sdk_version())
        basicFeatures.append(minSDKVersion)
        basicFeatures.append(maxSDKVersion)
        basicFeatures.append(float(len(apk.get_activities()))) # No. of activities
        basicFeatures.append(float(len(apk.get_services()))) # No. of services
        basicFeatures.append(float(len(apk.get_receivers()))) # No. of broadcast receivers
        basicFeatures.append(float(len(apk.get_providers()))) # No. of providers
        # 2.b. Harvest permission-related features
        if verboseON():
            prettyPrint("Extracting permissions-related features", "debug")
        aospPermissions = float(len(apk.get_requested_aosp_permissions())) # Android permissions requested by the app
        declaredPermissions = float(len(apk.get_declared_permissions())) # Custom permissions declared by the app
        dangerousPermissions = float(len([p for p in apk.get_requested_aosp_permissions_details().values() if p["protectionLevel"] == "dangerous"]))
        totalPermissions = float(len(apk.get_permissions()))
        permissionFeatures.append(totalPermissions) # No. of permissions
        if totalPermissions > 0:
            permissionFeatures.append(aospPermissions/totalPermissions) # AOSP permissions : Total permissions
            permissionFeatures.append(declaredPermissions/totalPermissions) # Third-party permissions : Total permissions
            permissionFeatures.append(dangerousPermissions/totalPermissions) # Dangerous permissions : Total permissions
        else:
            permissionFeatures.append(0.0)
            permissionFeatures.append(0.0)
            permissionFeatures.append(0.0)
        # 2.c. The DEX-related features (API calls)
        if verboseON():
            prettyPrint("Extracting API calls from dex code", "debug")
        apiCallFeatures.append(float(len(dex.get_classes()))) # Total number of classes
        apiCallFeatures.append(float(len(dex.get_strings()))) # Total number of strings
        apiCategories = sensitiveAPICalls.keys()
        apiCategoryCount = [0.0] * len(apiCategories)
        for c in dex.classes.get_names():
            currentClass = dex.get_class(c)
            if not currentClass:
                continue
            code = currentClass.get_source()
            if len(code) < 1:
                continue
            for category in apiCategories:
                if code.find(category) != -1:
                    for call in sensitiveAPICalls[category]:
                        apiCategoryCount[apiCategories.index(category)] += float(len(re.findall(call, code)))

        apiCallFeatures += apiCategoryCount

    except Exception as e:
        prettyPrintError(e)
        return [], [], [], []
    
    allFeatures = basicFeatures + permissionFeatures + apiCallFeatures

    return basicFeatures, permissionFeatures, apiCallFeatures, allFeatures
Exemplo n.º 16
0
    def run(self):
        """
        Runs the Droidutan test against the [processTarget] for [processDuration]
        """
        try:
            # A timer to guarante the process exits
            if verboseON():
                prettyPrint(
                    "Setting timer for %s seconds" %
                    str(float(self.processDuration) * 5.0), "debug")
            t = threading.Timer(float(self.processDuration) * 5.0, self.stop)
            t.start()
            # Step 1. Analyze APK
            #APKType = "malware" if self.processTarget.find("malware") != -1 else "goodware"
            if verboseON():
                prettyPrint("Analyzing APK: \"%s\"" % self.processTarget,
                            "debug")
            s = Session()
            s.add(self.processTarget, open(self.processTarget).read())
            if len(s.analyzed_apk.values()) > 0:
                apk = s.analyzed_apk.values()[0]
                if type(apk) == list:
                    apk = s.analyzed_apk.values()[0][0]
            else:
                prettyPrint("Could not retrieve an APK to analyze. Skipping",
                            "warning")
                return False

            # Step 2. Get the Ip address assigned to the AVD
            getAVDIPCmd = [
                "VBoxManage", "guestproperty", "enumerate", self.processVM
            ]
            avdIP = ""
            result = subprocess.Popen(
                getAVDIPCmd, stderr=subprocess.STDOUT,
                stdout=subprocess.PIPE).communicate()[0].replace(' ', '')
            if result.lower().find("error") != -1:
                prettyPrint("Unable to retrieve the IP address of the AVD",
                            "error")
                print result
                return False
            index = result.find("androvm_ip_management,value:") + len(
                "androvm_ip_management,value:")
            while result[index] != ',':
                avdIP += result[index]
                index += 1
            adbID = "%s:5555" % avdIP

            # Step 3. Define frequently-used commands
            droidbotOut = self.processTarget.replace(".apk", "_droidbot")
            droidbotCmd = [
                "droidbot", "-d", adbID, "-a", self.processTarget, "-o",
                droidbotOut, "-timeout",
                str(self.processDuration), "-random", "-keep_env",
                "-grant_perm"
            ]

            # Step 4. Test the APK using Droidbot (Assuming machine is already on)
            prettyPrint("Testing the APK \"%s\" using Droidbot" % apk.package)
            # 4.a. Start Droidbot
            status = subprocess.Popen(droidbotCmd,
                                      stderr=subprocess.STDOUT,
                                      stdout=subprocess.PIPE).communicate()[0]

            # 4.b. Check for existence of output directory
            if not os.path.exists(droidbotOut):
                prettyPrint(
                    "No output folder found for \"%s\"" % self.processTarget,
                    "warning")
                return False

            # 4.c. Filter the logcat dumped by droidbot
            logFile = open("%s/logcat_filtered.log" % droidbotOut, "w")
            catlog = subprocess.Popen(("cat", "%s/logcat.txt" % droidbotOut),
                                      stdout=subprocess.PIPE)
            output = subprocess.check_output(
                ("grep", "-i", "droidmon-apimonitor-%s" % apk.package),
                stdin=catlog.stdout)
            logFile.write(output)
            logFile.close()

        except subprocess.CalledProcessError as cpe:
            prettyPrint(
                "Unable to find the tag \"Droidmon-apimonitor-%s\" in the log file"
                % apk.package, "warning")
        except Exception as e:
            prettyPrintError(e)
            return False

        return True
Exemplo n.º 17
0

def display_PERMISSION(a, x, classes):
    # Show methods used by permission
    perms_access = x.get_tainted_packages().get_permissions([])
    for perm in perms_access:
        print("PERM : ", perm)
        analysis.show_Paths(a, perms_access[perm])


def display_OBJECT_CREATED(a, x, class_name):
    print("Search object", class_name)
    analysis.show_Paths(a, x.get_tainted_packages().search_objects(class_name))


s = Session()
with open(TEST, "r") as fd:
    s.add(TEST, fd.read())

a, d, dx = s.get_objects_apk(TEST)

print(d.get_strings())
print(d.get_regex_strings("access"))
print(d.get_regex_strings("(long).*2"))
print(d.get_regex_strings(".*(t\_t).*"))

classes = d.get_classes_names()

display_CFG(d, dx, classes)
display_STRINGS(dx)
display_FIELDS(d, dx)
Exemplo n.º 18
0
        32768.5, 32767.5, 32766.5, -5, -65535, -65536,
        -123456789123456789.555555555, -123456789123456789.555555555,
        -606384730, -123456790519087104, -606384730, 3.5
    ],
}


def test(got, expected):
    if got == expected:
        prefix = ' OK '
    else:
        prefix = '  X '
    print('%s got: %s expected: %s' % (prefix, repr(got), repr(expected)))


s = Session()
with open(TEST_CASE, "r") as fd:
    digest, d, dx = s.addDEX(TEST_CASE, fd.read())

for method in d.get_methods():
    key = method.get_class_name() + " " + method.get_name(
    ) + " " + method.get_descriptor()

    if key not in VALUES:
        continue

    print("METHOD", method.get_class_name(), method.get_name(
    ), method.get_descriptor())

    code = method.get_code()
    bc = code.get_bc()
Exemplo n.º 19
0
def androlyze_main(session, filename):
    """
    Start an interactive shell

    :param session: Session file to load
    :param filename: File to analyze, can be APK or DEX (or ODEX)
    """
    from androguard.core.androconf import ANDROGUARD_VERSION, CONF
    from IPython.terminal.embed import InteractiveShellEmbed
    from traitlets.config import Config
    from androguard.session import Session, Load
    from colorama import Fore
    import colorama
    import atexit

    # Import commonly used classes, for further usage...
    from androguard.core.bytecodes.apk import APK
    from androguard.core.bytecodes.dvm import DalvikVMFormat
    from androguard.core.analysis.analysis import Analysis
    from androguard.misc import AnalyzeAPK

    colorama.init()

    if session:
        print("Restoring session '{}'...".format(session))
        s = CONF['SESSION'] = Load(session)
        print("Successfully restored {}".format(s))
        # TODO Restore a, d, dx etc...
    else:
        s = CONF["SESSION"] = Session(export_ipython=True)

    if filename:
        ("Loading apk {}...".format(os.path.basename(filename)))
        print("Please be patient, this might take a while.")

        filetype = androconf.is_android(filename)

        print("Found the provided file is of type '{}'".format(filetype))

        if filetype not in ['DEX', 'DEY', 'APK']:
            print(Fore.RED + "This file type is not supported by androlyze for auto loading right now!" + Fore.RESET, file=sys.stderr)
            print("But your file is still available:")
            print(">>> filename")
            print(repr(filename))
            print()

        else:
            with open(filename, "rb") as fp:
                raw = fp.read()

            h = s.add(apk, raw)
            print("Added file to session: SHA256::{}".format(h))

            if filetype == 'APK':
                print("Loaded APK file...")
                a, d, dx = s.get_objects_apk(digest=h)

                print(">>> a")
                print(a)
                print(">>> d")
                print(d)
                print(">>> dx")
                print(dx)
                print()
            elif filetype in ['DEX', 'DEY']:
                print("Loaded DEX file...")
                for h_, d, dx in s.get_objects_dex():
                    if h == h_:
                        break
                print(">>> d")
                print(d)
                print(">>> dx")
                print(dx)
                print()

    def shutdown_hook():
        """Save the session on exit, if wanted"""
        if not s.isOpen():
            return

        try:
            res = input("Do you want to save the session? (y/[n])?").lower()
        except (EOFError, KeyboardInterrupt):
            pass
        else:
            if res == "y":
                # TODO: if we already started from a session, probably we want to save it under the same name...
                # TODO: be able to take any filename you want
                fname = s.save()
                print("Saved Session to file: '{}'".format(fname))

    cfg = Config()
    _version_string = "Androguard version {}".format(ANDROGUARD_VERSION)
    ipshell = InteractiveShellEmbed(config=cfg, banner1="{} started"
                                    .format(_version_string))
    atexit.register(shutdown_hook)
    ipshell()
Exemplo n.º 20
0
class MainView(QtGui.QMainWindow):

    def __init__(self, parent=None):
        super(MainView, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.__logger = Logger(self.ui.logArea)
        self.__logger.log(Logger.INFO, '')
        self.init_actions()
        self.setup_fileloading()
        self.apk_path = ''
        self.apk = None
        self.x = self.d = self.a = None
        self.manifest = None
        self.show_sample_question()

    def setup_fileloading(self):
        self.session = Session()
        self.fileLoadingThread = FileLoadingThread(self.session)
        self.connect(self.fileLoadingThread,QtCore.SIGNAL("loadedFile(bool)"),self.loadedApk)

    def show_sample_question(self):
        self.sample_dialog = SampleDialog(self)
        self.sample_dialog.exec_()

    def get_logger(self):
        return self.__logger;

    def loadedApk(self, success):
        self.sample_dialog.close()
        if not success:
            self.__logger.log(Logger.ERROR,"Analysis of %s failed :(" % str(self.fileLoadingThread.file_path))
            self.set_loading_progressbar_disabled()
            return

        self.d = self.session.get_DalvikForm()
        self.d.create_python_export()
        self.x = uVMAnalysis(self.d)

        self.setupTree()
        self.setupResourceController()
        self.load_app_info_table()
        self.load_permissions()
        try:
            self.__logger.log(Logger.INFO,"Analysis of %s done!" % str(self.apk.get_app_name()))
            self.ui.loadedAPK_label.setText("Loaded: "+str(self.apk.get_app_name()))
        except UnicodeEncodeError:
            self.ui.loadedAPK_label.setText("APK Loaded! (non-ascii chars detected)")
            self.__logger.log(Logger.WARNING,"Non ascii code characters detected, some strings are possibly not displayed properly.")
        self.set_loading_progressbar_disabled()

    def setupResourceController(self):
        self.resourcecontroller = ResourceFileController(self.apk,
                                                         self.ui.resourceFileTable,
                                                         self.ui.graphicview,
                                                         self.ui.fileResourceInfo)

    def get_android_manifest_xml(self):
        self.set_loading_progressbar_text("Decompiling AndroidManifest.xml")
        buff = self.apk.get_android_manifest_xml().toprettyxml(encoding="utf-8")
        doc = QtGui.QTextEdit()
        doc.setWindowTitle("AndroidManifest.xml")
        hl = XMLHighlighter(doc.document())
        doc.setPlainText(str(buff).strip())
        return doc

    def setupTree(self):
        try:
            self.ui.tree_area.layout().deleteLater()
        except AttributeError:
            pass
        self.tree = TreeWindow(self,self,session=self.session)
        self.tree.setWindowTitle("Tree model")
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.tree)
        self.ui.tree_area.setLayout(layout)
        self.tree.fill()
        self.setupCentral()

    def setupCentral(self):
        self.central = QtGui.QTabWidget()
        self.central.setTabBar(CustomTabBar())
        self.central.setTabsClosable(True)
        self.central.tabCloseRequested.connect(self.tabCloseRequestedHandler)
        self.central.currentChanged.connect(self.currentTabChanged)
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.central)
        self.ui.sourceTextWidget.setLayout(layout)

    def tabCloseRequestedHandler(self, index):
        self.central.removeTab(index)

    def currentTabChanged(self, index):
        if index == -1:
            return # all tab closed

    def openSourceWindow(self, current_class, method=None):

        sourcewin = self.getMeSourceWindowIfExists(current_class)
        if not sourcewin:
            current_filename = self.session.get_filename_by_class(current_class)
            current_digest = self.session.get_digest_by_class(current_class)

            sourcewin = SourceWindow(win=self,
                                    current_class=current_class,
                                    current_title=current_class.current_title,
                                    current_filename=current_filename,
                                    current_digest=current_digest,
                                    session=self.session)
            sourcewin.reload_java_sources()
            self.central.addTab(sourcewin, sourcewin.title)
            self.central.setTabToolTip(self.central.indexOf(sourcewin), current_class.get_name())

        if method:
            sourcewin.browse_to_method(method)

        self.central.setCurrentWidget(sourcewin)

    def openManifestWindow(self):
        manifest_tab = self.get_android_manifest_xml()
        self.central.addTab(manifest_tab,"AndroidManifest.xml")
        self.central.setCurrentWidget(manifest_tab)

    def openStringsWindow(self):
        stringswin = StringsWindow(win=self, session=self.session)
        self.central.addTab(stringswin, stringswin.title)
        self.central.setTabToolTip(self.central.indexOf(stringswin), stringswin.title)
        self.central.setCurrentWidget(stringswin)

    def openBytecodeWindow(self, current_class, method=None):
        byte_code_str = ''
        for clazz in self.d.get_classes():
            if clazz.get_name() == current_class.get_name():
                for method in clazz.get_methods():
                    byte_code_str += "# "+method.get_name()+" "+method.get_descriptor()+"\n"
                    byte_code = method.get_code()
                    if byte_code != None:
                        byte_code = byte_code.get_bc()
                        idx = 0
                        for i in byte_code.get_instructions():
                            byte_code_str += "\t, %x " % (idx)+i.get_name()+" "+i.get_output()+"\n"
                            idx += i.get_length()

                    bytecode_tab = self.get_bytecode_window(byte_code_str)
                    self.central.addTab(bytecode_tab,"Bytecode: "+current_class.get_name())
                    self.central.setCurrentWidget(bytecode_tab)

    def showStatus(self,text):
        QtGui.QMessageBox.information(self, "Info", text)

    def get_bytecode_window(self,byte_code):
        return BytecodeWindow(byte_code,self)

    def getMeSourceWindowIfExists(self, path):
        '''Helper for openSourceWindow'''
        for idx in range(self.central.count()):
            if path == self.central.tabToolTip(idx):
                return self.central.widget(idx)
        return None

    def load_app_info_table(self):
        self.info = {}
        self.info["Application Name"]            = self.apk.get_app_name()
        self.info["Application Size"]            = util.sizeof_fmt(os.path.getsize(self.apk_path))
        self.info["Android Version Name"]        = self.apk.get_androidversion_name()
        self.info["Android Version Code"]        = self.apk.get_androidversion_code()
        self.info["Android Package Name"]        = self.apk.get_package()
        self.info["Signature Name"]              = self.apk.get_signature_name()
        self.info["Uses Dynamic Code Loading"]   = str(analysis.is_dyn_code(self.x))
        self.info["Uses Reflection"]             = str(analysis.is_reflection_code(self.x))
        self.info["Uses Crypto"]                 = str(analysis.is_crypto_code(self.x))
        self.info["Privacy Leaks"]               = str(len(self.get_privacy_leaks()))
        self.info["Number of Providers"]         = str(len(self.apk.get_providers()))
        self.info["Number of Activities"]        = str(len(self.apk.get_activities()))
        self.info["Number of Services"]          = str(len(self.apk.get_services()))
        self.info["Number of Libraries"]         = str(len(self.apk.get_libraries()))
        self.info["Number of Permissions"]       = str(len(self.get_uses_permissions()))

        self.info_actions = {}
        self.info_actions["Application Name"]            = None
        self.info_actions["Application Size"]            = None
        self.info_actions["Android Version Name"]        = None
        self.info_actions["Android Version Code"]        = None
        self.info_actions["Android Package Name"]        = None
        self.info_actions["Signature Name"]              = self.show_signature
        self.info_actions["Uses Dynamic Code Loading"]   = self.show_dyncode
        self.info_actions["Uses Reflection"]             = self.show_reflection
        self.info_actions["Uses Crypto"]                 = self.show_cryptocode
        self.info_actions["Privacy Leaks"]               = self.show_privacy_leaks
        self.info_actions["Number of Providers"]         = self.show_providers
        self.info_actions["Number of Activities"]        = self.show_activities
        self.info_actions["Number of Services"]          = self.show_services
        self.info_actions["Number of Libraries"]         = self.show_libraries
        self.info_actions["Number of Permissions"]       = self.show_permissions
        info_table = self.ui.appInfoTable
        info_table.setRowCount(len(self.info))
        info_table.setColumnWidth(1, 200)
        info_table.horizontalHeader().setResizeMode(0, QtGui.QHeaderView.Stretch)
        row = 0
        for key in sorted(self.info):
            action = self.info_actions[key]
            action_button = None
            if action is not None:
                action_button = QtGui.QPushButton()
                action_button.setText("Show")
                action_button.clicked.connect(action)
            key_item = QtGui.QTableWidgetItem(key)
            value_item = QtGui.QTableWidgetItem(self.info[key])
            info_table.setItem(row,0,key_item)
            info_table.setItem(row,1,value_item)
            if action_button is not None:
                info_table.setCellWidget(row,2,action_button)
            row += 1

    def show_providers(self):
        self.__logger.log_with_title("Providers",self.apk.get_providers())

    def show_signature(self):
        certname = ""
        signature_expr = re.compile("^(META-INF/)(.*)(\.RSA|\.DSA)$")
        for i in self.apk.get_files():
            if signature_expr.search(i):
                f = tempfile.NamedTemporaryFile(delete=False)
                f.write(self.apk.get_file(i))
                f.close()
                certname = f.name

        cmd = ["keytool","-printcert","-file",certname]
        proc = subprocess.Popen(cmd,stdout=subprocess.PIPE)
        (out,err) = proc.communicate()
        self.__logger.log_with_title("Signature",out)

    def show_services(self):
        self.__logger.log_with_title("Services",'\n\t -'+'\n\t -'.join(self.apk.get_services()))

    def show_permissions(self):
        self.__logger.log(Logger.INFO,"Searching for permission usage, this can take a while depending on the size of the app.")
        p = self.x.get_permissions( [] )
        self.__logger.log_with_title("Permissions Usage","")
        for i in p:
            self.__logger.log_with_color(Logger.WARNING,"\n\t======="+i+"=======\n")
            for j in p[i]:
                self.__logger.log_with_color(Logger.INFO,"\t\t -"+self.show_Path(self.x.get_vm(), j )+"\n")

    def show_cryptocode(self):
        if analysis.is_crypto_code(self.x) is False:
            self.__logger.log(Logger.WARNING,"No crypto code was found! Maybe it uses adapted or obfuscated encryption code.")
            return

        paths = []

        paths.extend(self.x.get_tainted_packages().search_methods("Ljavax/crypto/.",
                                                ".",
                                                "."))

        paths.extend(self.x.get_tainted_packages().search_methods("Ljava/security/spec/.",
                                                    ".",
                                                    "."))

        str_info_dyn="\t"
        for path in paths:
            str_info_dyn += (self.show_Path(self.x.get_vm(), path )+"\n\n\t")
        self.__logger.log_with_title("Usage of Crypto Code",str_info_dyn)

    def show_dyncode(self):
        if analysis.is_dyn_code(self.x) is False:
            self.__logger.log(Logger.WARNING,"No dynamic code was found!")
            return

        paths = []
        paths.extend(self.x.get_tainted_packages().search_methods("Ldalvik/system/BaseDexClassLoader;",
                                                    "<init>",
                                                    "."))

        paths.extend(self.x.get_tainted_packages().search_methods("Ldalvik/system/PathClassLoader;",
                                                    "<init>",
                                                    "."))

        paths.extend(self.x.get_tainted_packages().search_methods("Ldalvik/system/DexClassLoader;",
                                                    "<init>",
                                                    "."))

        paths.extend(self.x.get_tainted_packages().search_methods("Ldalvik/system/DexFile;",
                                                    "<init>",
                                                    "."))

        paths.extend(self.x.get_tainted_packages().search_methods("Ldalvik/system/DexFile;",
                                                    "loadDex",
                                                    "."))
        str_info_dyn="\t"
        for path in paths:
            str_info_dyn += (self.show_Path(self.x.get_vm(), path )+"\n\n\t")
        self.__logger.log_with_title("Usage of Dynamic Code",str_info_dyn)

    def get_privacy_leaks(self):
        paths = []
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getDeviceId", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getSubscriberId", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getSimSerialNumber", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getLine1Number", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getAddress", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getMacAddress", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getSSID", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getCid", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getAccounts", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getTimeZone", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getAllBookmarks", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(".", "getAllVisitedUrls", "."))
        return paths

    def show_privacy_leaks(self):
        paths = self.get_privacy_leaks()
        if len(paths) == 0:
            self.__logger.log(Logger.WARNING,"No privacy leaks were found!")
            return
        str_info_privacy="\t"
        for path in paths:
            str_info_privacy += (self.show_Path(self.x.get_vm(), path )+"\n\n\t")
        self.__logger.log_with_title("Possible Privacy Leaks",str_info_privacy)

    def show_reflection(self):
        if analysis.is_reflection_code(self.x) is False:
            self.__logger.log(Logger.WARNING,"No reflection code was found!")
            return
        paths = self.x.get_tainted_packages().search_methods("Ljava/lang/reflect/Method;", ".", ".")
        str_info_reflection=""
        for path in paths:
            str_info_reflection += (self.show_Path(self.x.get_vm(), path )+"\n\n\t")
        self.__logger.log_with_title("Usage of Reflection",str_info_reflection)

    def show_activities(self):
        self.__logger.log_with_title("Activities",'\n\t -'+'\n\t -'.join(self.apk.get_activities()))

    def show_libraries(self):
        if len(self.apk.get_libraries()) == 0:
            self.__logger.log(Logger.WARNING,"No libraries were found.")
            return
        self.__logger.log(Logger.INFO,"Libraries Used: "+str(self.apk.get_libraries()))

    def init_actions(self):
        self.ui.saveLogBtn.clicked.connect(self.__logger.saveLog)
        self.ui.clearLogBtn.clicked.connect(self.__logger.clearLog)
        self.ui.showStringsBtn.clicked.connect(self.openStringsWindow)
        self.ui.showManifestBtn.clicked.connect(self.openManifestWindow)

    def load_apk_from_device(self):
        table = DeviceTable(self,parent=self)
        self.sample_dialog.close()
        table.show_data()
        table.exec_()

    def load_apk(self,path=None):
        if not path:
            path = QtGui.QFileDialog.getOpenFileName(self, "Open File",'', "APK Files (*.apk);;Androguard Session (*.ag)")
            path = str(path[0])
        self.apk_path = path
        if path:
            self.set_loading_progressbar("Analyzing APK","Please wait while the APK is being dissected")
            self.__logger.log(Logger.INFO,"Analyzing %s..." % str(path))
            self.apk = apk.APK(path)
            self.manifest = self.apk.get_AndroidManifest().getElementsByTagName("manifest")[0]
            self.fileLoadingThread.load(path)

    def load_permissions(self):
        perms = self.get_uses_permissions()
        self.ui.permsTable.setRowCount(len(perms))
        self.ui.permsTable.horizontalHeader().setStretchLastSection(True)
        for i, uses_permission in enumerate(perms):
            newitem = QtGui.QTableWidgetItem(uses_permission)
            self.ui.permsTable.setItem(0, i, newitem)

    def get_uses_permissions(self):
        permissions = []
        for uses_permission in self.manifest.getElementsByTagName("uses-permission"):
            permissions.append(uses_permission.attributes["android:name"].value)
        return permissions

    def set_loading_progressbar(self,title,text):
        self.dialog = QtGui.QProgressDialog(self)
        self.dialog.setMinimum(0)
        self.dialog.setLabelText(text)
        self.dialog.setMaximum(0)
        self.dialog.setWindowTitle(title)
        self.dialog.setCancelButton(None)
        self.dialog.setModal(True)
        self.dialog.show()

    def set_loading_progressbar_text(self,text):
        if self.dialog is not None:
            self.dialog.setLabelText(text);

    def set_loading_progressbar_disabled(self):
        self.dialog.hide()

    def show_Path(self,vm, path):
      cm = vm.get_class_manager()

      if isinstance(path, analysis.PathVar):
        dst_class_name, dst_method_name, dst_descriptor =  path.get_dst( cm )
        info_var = path.get_var_info()
        return  "%s %s (0x%x) ---> %s->%s%s " % (path.get_access_flag(),
                                              info_var,
                                              path.get_idx(),
                                              dst_class_name,
                                              dst_method_name,
                                              dst_descriptor)
      else:
        if path.get_access_flag() == analysis.TAINTED_PACKAGE_CALL:
          src_class_name, src_method_name, src_descriptor =  path.get_src( cm )
          dst_class_name, dst_method_name, dst_descriptor =  path.get_dst( cm )

          return "%d %s->%s%s (0x%x) ---> %s->%s%s" % (path.get_access_flag(),
                                                      src_class_name,
                                                      src_method_name,
                                                      src_descriptor,
                                                      path.get_idx(),
                                                      dst_class_name,
                                                      dst_method_name,
                                                      dst_descriptor)
        else:
          src_class_name, src_method_name, src_descriptor =  path.get_src( cm )
          return "%d %s->%s%s (0x%x)" % (path.get_access_flag(),
                                        src_class_name,
                                        src_method_name,
                                        src_descriptor,
                                        path.get_idx())
Exemplo n.º 21
0
    def run(self):
        app.logger.info('new analysis')
        s = Session()
        self.status = 'Analyzing APK'
        a, d, dx = AnalyzeAPK(self.target_file,
                              session=s)  #APK,list[DalvikVMFormat],Analysis
        print(type(a), type(d[0]), type(dx))
        #cache activities, receivers, services, and providers, because for some reason, saving the Session causes a bug, breaking getters
        """i.e. bytecodes/apk.py", line 582, in get_elements
                for item in self.xml[i].findall('.//' + tag_name):
            TypeError: string indices must be integers """
        activities = a.get_activities()
        receivers = a.get_receivers()
        services = a.get_services()
        providers = a.get_providers()
        self.main_activity = a.get_main_activity()
        if self.session_save_file:
            sys.setrecursionlimit(100000000)
            self.status = 'Saving session file'
            Save(s, self.session_save_file)

        cached_analyses.append({'md5': self.md5, 'analysis': (a, d, dx)})

        #gather all classes from dexs 'd'
        #classes = get_all_classes_from_dexs(d)
        classes = dx.classes
        total_num = len(classes)
        done = 0  #num of done classes
        #result_classes contains the completed analysis info for each class run through the ClassAnalysis object
        result_classes = []
        analysis_start_time = time.time()

        self.status = 'Getting all classes'
        for c_name, c_analysis in classes.items():
            ca = ClassAnalysis(c_name, c_analysis, activities, receivers,
                               services, providers)
            ca_result = ca.run()
            result_classes.append(ca_result)
            done += 1
            if done % ceil(total_num / 100) == 0:
                self.progress += 1
                #app.logger.info(self.progress)
                # with app.test_request_context('/'):
                #     socketio.emit('newstatus', {'data':self.progress}, namespace='/status')
        analysis_end_time = time.time()
        analysis_total_time = analysis_end_time - analysis_start_time

        #debugging:
        self.status = 'Writing beforenetworkx debugging JSON'
        with open(self.graph_out_path + '.beforenetworkx', 'w') as f:
            json.dump(result_classes,
                      f,
                      indent=4,
                      separators=(',', ': '),
                      sort_keys=True)

        #create a networkx graph given the completed analyses in result_classess
        create_graph_start_time = time.time()
        self.status = 'Creating graph out of {} classes analyzed'.format(
            len(result_classes))
        graph = create_graph(classes=result_classes)
        create_graph_end_time = time.time()
        create_graph_total_time = create_graph_end_time - create_graph_start_time

        #write graph to file: graph_out_path
        write_graph_start_time = time.time()
        self.status = 'Writing graph to disk'
        write_graph(graph, self.graph_out_path)
        write_graph_end_time = time.time()
        write_graph_total_time = write_graph_end_time - write_graph_start_time

        #build and write another graph that contains only providers,receivers,activities, and services
        if self.component_subgraph_out_path:
            component_names = []
            self.status = 'Getting component nodes from graph'
            for node in graph:
                node_tmp = graph.node[node]
                if node_tmp[
                        'component_type'] != NonComponentType.EXTERNAL and node_tmp[
                            'component_type'] != NonComponentType.INTERNAL:
                    component_names.append(node_tmp['name'])
            self.status = 'Creating subgraph containing only components'
            subgraph = get_class_subgraph(graph, class_names=component_names)
            self.status = 'Writing subgraph to disk'
            write_graph(subgraph, self.component_subgraph_out_path)

        #app metadata for misc/debugging
        apk_size = os.path.getsize(self.target_file)
        self.status = 'Writing metadata'
        self.write_app_metadata(result_classes, a, analysis_total_time,
                                apk_size, create_graph_total_time,
                                write_graph_total_time)
        #debugging
        # with open(self.graph_out_path+'.runmetrics', 'w') as f:
        #     json.dump()

        self.progress = 100
        self.status = 'Done'
        self.paused.wait(
        )  #wait for caller to collect last status and reset event before finishing

        app.logger.info('done')
Exemplo n.º 22
0
from pprint import pprint

from androguard.session import Session
from androguard.misc import AnalyzeAPK



a, d, dx = AnalyzeAPK('/home/branden/apks/dendroid.apk', session=Session())
print('here')
acts = a.get_activities()
print('here2')
print(acts)

Exemplo n.º 23
0
    parser = argparse.ArgumentParser(description="Androguard GUI")
    parser.add_argument("-d", "--debug", action="store_true", default=False)
    parser.add_argument("-i", "--input_file", default=None)
    parser.add_argument("-c", "--console", action="store_true", default=False)

    args = parser.parse_args()

    if args.debug:
        androconf.set_debug()

    # We need that to save huge sessions when leaving and avoid
    # RuntimeError: maximum recursion depth exceeded while pickling an object
    # or
    # RuntimeError: maximum recursion depth exceeded in cmp
    # http://stackoverflow.com/questions/2134706/hitting-maximum-recursion-depth-using-pythons-pickle-cpickle
    sys.setrecursionlimit(50000)

    session = Session(export_ipython=args.console)
    console = None
    if args.console:
        console = IpythonConsole()
        console.start()

    app = QtGui.QApplication(sys.argv)
    window = MainWindow(session=session, input_file=args.input_file)
    window.resize(1024, 768)
    window.show()

    sys.exit(app.exec_())
Exemplo n.º 24
0

def display_PERMISSION(a, x, classes):
    # Show methods used by permission
    perms_access = x.get_tainted_packages().get_permissions([])
    for perm in perms_access:
        print("PERM : ", perm)
        analysis.show_Paths(a, perms_access[perm])


def display_OBJECT_CREATED(a, x, class_name):
    print("Search object", class_name)
    analysis.show_Paths(a, x.get_tainted_packages().search_objects(class_name))


s = Session()
with open(TEST, "r") as fd:
    s.add(TEST, fd.read())

a, d, dx = s.get_objects_apk(TEST)

print(d.get_strings())
print(d.get_regex_strings("access"))
print(d.get_regex_strings("(long).*2"))
print(d.get_regex_strings(".*(t\_t).*"))

classes = d.get_classes_names()

display_CFG(d, dx, classes)
display_STRINGS(dx)
display_FIELDS(d, dx)
Exemplo n.º 25
0
#!/usr/bin/env python

from __future__ import print_function
import sys, hashlib

PATH_INSTALL = "./"
sys.path.append(PATH_INSTALL)

from androguard.session import Session
from androguard.core.bytecodes import dvm

TEST = 'examples/android/TestsAndroguard/bin/classes.dex'

s = Session()
with open(TEST, "r") as fd:
    digest, d, dx = s.addDEX(TEST, fd.read())

# CFG
for method in d.get_methods():
    g = dx.get_method(method)

    # Display only methods with exceptions
    if method.get_code() == None:
        continue

    if method.get_code().tries_size <= 0:
        continue

    print(method.get_class_name(), method.get_name(), method.get_descriptor(
    ), method.get_code().get_length(), method.get_code().registers_size)
Exemplo n.º 26
0
        32768.5, 32767.5, 32766.5, -5, -65535, -65536,
        -123456789123456789.555555555, -123456789123456789.555555555,
        -606384730, -123456790519087104, -606384730, 3.5
    ],
}


def _test(got, expected):
    if got == expected:
        prefix = ' OK '
    else:
        prefix = '  X '
    print('%s got: %s expected: %s' % (prefix, repr(got), repr(expected)))


s = Session()
with open(TEST_CASE, "rb") as fd:
    digest, d, dx = s.addDEX(TEST_CASE, fd.read())

for method in d.get_methods():
    key = method.get_class_name() + " " + method.get_name(
    ) + " " + method.get_descriptor()

    if key not in VALUES:
        continue

    print("METHOD", method.get_class_name(), method.get_name(),
          method.get_descriptor())

    code = method.get_code()
    bc = code.get_bc()
Exemplo n.º 27
0
class MainView(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainView, self).__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.__logger = Logger(self.ui.logArea)
        self.__logger.log(Logger.INFO, '')
        self.init_actions()
        self.setup_fileloading()
        self.apk_path = ''
        self.apk = None
        self.x = self.d = self.a = None
        self.manifest = None
        self.show_sample_question()

    def setup_fileloading(self):
        self.session = Session()
        self.fileLoadingThread = FileLoadingThread(self.session)
        self.connect(self.fileLoadingThread, QtCore.SIGNAL("loadedFile(bool)"),
                     self.loadedApk)

    def show_sample_question(self):
        self.sample_dialog = SampleDialog(self)
        self.sample_dialog.exec_()

    def get_logger(self):
        return self.__logger

    def loadedApk(self, success):
        self.sample_dialog.close()
        if not success:
            self.__logger.log(
                Logger.ERROR, "Analysis of %s failed :(" %
                str(self.fileLoadingThread.file_path))
            self.set_loading_progressbar_disabled()
            return

        self.d = self.session.get_DalvikForm()
        self.d.create_python_export()
        self.x = uVMAnalysis(self.d)

        self.setupTree()
        self.setupResourceController()
        self.load_app_info_table()
        self.load_permissions()
        try:
            self.__logger.log(
                Logger.INFO,
                "Analysis of %s done!" % str(self.apk.get_app_name()))
            self.ui.loadedAPK_label.setText("Loaded: " +
                                            str(self.apk.get_app_name()))
        except UnicodeEncodeError:
            self.ui.loadedAPK_label.setText(
                "APK Loaded! (non-ascii chars detected)")
            self.__logger.log(
                Logger.WARNING,
                "Non ascii code characters detected, some strings are possibly not displayed properly."
            )
        self.set_loading_progressbar_disabled()

    def setupResourceController(self):
        self.resourcecontroller = ResourceFileController(
            self.apk, self.ui.resourceFileTable, self.ui.graphicview,
            self.ui.fileResourceInfo)

    def get_android_manifest_xml(self):
        self.set_loading_progressbar_text("Decompiling AndroidManifest.xml")
        buff = self.apk.get_android_manifest_xml().toprettyxml(
            encoding="utf-8")
        doc = QtGui.QTextEdit()
        doc.setWindowTitle("AndroidManifest.xml")
        hl = XMLHighlighter(doc.document())
        doc.setPlainText(str(buff).strip())
        return doc

    def setupTree(self):
        try:
            self.ui.tree_area.layout().deleteLater()
        except AttributeError:
            pass
        self.tree = TreeWindow(self, self, session=self.session)
        self.tree.setWindowTitle("Tree model")
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.tree)
        self.ui.tree_area.setLayout(layout)
        self.tree.fill()
        self.setupCentral()

    def setupCentral(self):
        self.central = QtGui.QTabWidget()
        self.central.setTabBar(CustomTabBar())
        self.central.setTabsClosable(True)
        self.central.tabCloseRequested.connect(self.tabCloseRequestedHandler)
        self.central.currentChanged.connect(self.currentTabChanged)
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.central)
        self.ui.sourceTextWidget.setLayout(layout)

    def tabCloseRequestedHandler(self, index):
        self.central.removeTab(index)

    def currentTabChanged(self, index):
        if index == -1:
            return  # all tab closed

    def openSourceWindow(self, current_class, method=None):

        sourcewin = self.getMeSourceWindowIfExists(current_class)
        if not sourcewin:
            current_filename = self.session.get_filename_by_class(
                current_class)
            current_digest = self.session.get_digest_by_class(current_class)

            sourcewin = SourceWindow(win=self,
                                     current_class=current_class,
                                     current_title=current_class.current_title,
                                     current_filename=current_filename,
                                     current_digest=current_digest,
                                     session=self.session)
            sourcewin.reload_java_sources()
            self.central.addTab(sourcewin, sourcewin.title)
            self.central.setTabToolTip(self.central.indexOf(sourcewin),
                                       current_class.get_name())

        if method:
            sourcewin.browse_to_method(method)

        self.central.setCurrentWidget(sourcewin)

    def openManifestWindow(self):
        manifest_tab = self.get_android_manifest_xml()
        self.central.addTab(manifest_tab, "AndroidManifest.xml")
        self.central.setCurrentWidget(manifest_tab)

    def openStringsWindow(self):
        stringswin = StringsWindow(win=self, session=self.session)
        self.central.addTab(stringswin, stringswin.title)
        self.central.setTabToolTip(self.central.indexOf(stringswin),
                                   stringswin.title)
        self.central.setCurrentWidget(stringswin)

    def openBytecodeWindow(self, current_class, method=None):
        byte_code_str = ''
        for clazz in self.d.get_classes():
            if clazz.get_name() == current_class.get_name():
                for method in clazz.get_methods():
                    byte_code_str += "# " + method.get_name(
                    ) + " " + method.get_descriptor() + "\n"
                    byte_code = method.get_code()
                    if byte_code != None:
                        byte_code = byte_code.get_bc()
                        idx = 0
                        for i in byte_code.get_instructions():
                            byte_code_str += "\t, %x " % (idx) + i.get_name(
                            ) + " " + i.get_output() + "\n"
                            idx += i.get_length()

                    bytecode_tab = self.get_bytecode_window(byte_code_str)
                    self.central.addTab(
                        bytecode_tab, "Bytecode: " + current_class.get_name())
                    self.central.setCurrentWidget(bytecode_tab)

    def showStatus(self, text):
        QtGui.QMessageBox.information(self, "Info", text)

    def get_bytecode_window(self, byte_code):
        return BytecodeWindow(byte_code, self)

    def getMeSourceWindowIfExists(self, path):
        '''Helper for openSourceWindow'''
        for idx in range(self.central.count()):
            if path == self.central.tabToolTip(idx):
                return self.central.widget(idx)
        return None

    def load_app_info_table(self):
        self.info = {}
        self.info["Application Name"] = self.apk.get_app_name()
        self.info["Application Size"] = util.sizeof_fmt(
            os.path.getsize(self.apk_path))
        self.info["Android Version Name"] = self.apk.get_androidversion_name()
        self.info["Android Version Code"] = self.apk.get_androidversion_code()
        self.info["Android Package Name"] = self.apk.get_package()
        self.info["Signature Name"] = self.apk.get_signature_name()
        self.info["Uses Dynamic Code Loading"] = str(
            analysis.is_dyn_code(self.x))
        self.info["Uses Reflection"] = str(analysis.is_reflection_code(self.x))
        self.info["Uses Crypto"] = str(analysis.is_crypto_code(self.x))
        self.info["Privacy Leaks"] = str(len(self.get_privacy_leaks()))
        self.info["Number of Providers"] = str(len(self.apk.get_providers()))
        self.info["Number of Activities"] = str(len(self.apk.get_activities()))
        self.info["Number of Services"] = str(len(self.apk.get_services()))
        self.info["Number of Libraries"] = str(len(self.apk.get_libraries()))
        self.info["Number of Permissions"] = str(
            len(self.get_uses_permissions()))

        self.info_actions = {}
        self.info_actions["Application Name"] = None
        self.info_actions["Application Size"] = None
        self.info_actions["Android Version Name"] = None
        self.info_actions["Android Version Code"] = None
        self.info_actions["Android Package Name"] = None
        self.info_actions["Signature Name"] = self.show_signature
        self.info_actions["Uses Dynamic Code Loading"] = self.show_dyncode
        self.info_actions["Uses Reflection"] = self.show_reflection
        self.info_actions["Uses Crypto"] = self.show_cryptocode
        self.info_actions["Privacy Leaks"] = self.show_privacy_leaks
        self.info_actions["Number of Providers"] = self.show_providers
        self.info_actions["Number of Activities"] = self.show_activities
        self.info_actions["Number of Services"] = self.show_services
        self.info_actions["Number of Libraries"] = self.show_libraries
        self.info_actions["Number of Permissions"] = self.show_permissions
        info_table = self.ui.appInfoTable
        info_table.setRowCount(len(self.info))
        info_table.setColumnWidth(1, 200)
        info_table.horizontalHeader().setResizeMode(0,
                                                    QtGui.QHeaderView.Stretch)
        row = 0
        for key in sorted(self.info):
            action = self.info_actions[key]
            action_button = None
            if action is not None:
                action_button = QtGui.QPushButton()
                action_button.setText("Show")
                action_button.clicked.connect(action)
            key_item = QtGui.QTableWidgetItem(key)
            value_item = QtGui.QTableWidgetItem(self.info[key])
            info_table.setItem(row, 0, key_item)
            info_table.setItem(row, 1, value_item)
            if action_button is not None:
                info_table.setCellWidget(row, 2, action_button)
            row += 1

    def show_providers(self):
        self.__logger.log_with_title("Providers", self.apk.get_providers())

    def show_signature(self):
        certname = ""
        signature_expr = re.compile("^(META-INF/)(.*)(\.RSA|\.DSA)$")
        for i in self.apk.get_files():
            if signature_expr.search(i):
                f = tempfile.NamedTemporaryFile(delete=False)
                f.write(self.apk.get_file(i))
                f.close()
                certname = f.name

        cmd = ["keytool", "-printcert", "-file", certname]
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
        (out, err) = proc.communicate()
        self.__logger.log_with_title("Signature", out)

    def show_services(self):
        self.__logger.log_with_title(
            "Services", '\n\t -' + '\n\t -'.join(self.apk.get_services()))

    def show_permissions(self):
        self.__logger.log(
            Logger.INFO,
            "Searching for permission usage, this can take a while depending on the size of the app."
        )
        p = self.x.get_permissions([])
        self.__logger.log_with_title("Permissions Usage", "")
        for i in p:
            self.__logger.log_with_color(Logger.WARNING,
                                         "\n\t=======" + i + "=======\n")
            for j in p[i]:
                self.__logger.log_with_color(
                    Logger.INFO,
                    "\t\t -" + self.show_Path(self.x.get_vm(), j) + "\n")

    def show_cryptocode(self):
        if analysis.is_crypto_code(self.x) is False:
            self.__logger.log(
                Logger.WARNING,
                "No crypto code was found! Maybe it uses adapted or obfuscated encryption code."
            )
            return

        paths = []

        paths.extend(self.x.get_tainted_packages().search_methods(
            "Ljavax/crypto/.", ".", "."))

        paths.extend(self.x.get_tainted_packages().search_methods(
            "Ljava/security/spec/.", ".", "."))

        str_info_dyn = "\t"
        for path in paths:
            str_info_dyn += (self.show_Path(self.x.get_vm(), path) + "\n\n\t")
        self.__logger.log_with_title("Usage of Crypto Code", str_info_dyn)

    def show_dyncode(self):
        if analysis.is_dyn_code(self.x) is False:
            self.__logger.log(Logger.WARNING, "No dynamic code was found!")
            return

        paths = []
        paths.extend(self.x.get_tainted_packages().search_methods(
            "Ldalvik/system/BaseDexClassLoader;", "<init>", "."))

        paths.extend(self.x.get_tainted_packages().search_methods(
            "Ldalvik/system/PathClassLoader;", "<init>", "."))

        paths.extend(self.x.get_tainted_packages().search_methods(
            "Ldalvik/system/DexClassLoader;", "<init>", "."))

        paths.extend(self.x.get_tainted_packages().search_methods(
            "Ldalvik/system/DexFile;", "<init>", "."))

        paths.extend(self.x.get_tainted_packages().search_methods(
            "Ldalvik/system/DexFile;", "loadDex", "."))
        str_info_dyn = "\t"
        for path in paths:
            str_info_dyn += (self.show_Path(self.x.get_vm(), path) + "\n\n\t")
        self.__logger.log_with_title("Usage of Dynamic Code", str_info_dyn)

    def get_privacy_leaks(self):
        paths = []
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getDeviceId", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getSubscriberId", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getSimSerialNumber", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getLine1Number", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getAddress", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getMacAddress", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getSSID", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getCid", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getAccounts", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getTimeZone", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getAllBookmarks", "."))
        paths.extend(self.x.get_tainted_packages().search_methods(
            ".", "getAllVisitedUrls", "."))
        return paths

    def show_privacy_leaks(self):
        paths = self.get_privacy_leaks()
        if len(paths) == 0:
            self.__logger.log(Logger.WARNING, "No privacy leaks were found!")
            return
        str_info_privacy = "\t"
        for path in paths:
            str_info_privacy += (self.show_Path(self.x.get_vm(), path) +
                                 "\n\n\t")
        self.__logger.log_with_title("Possible Privacy Leaks",
                                     str_info_privacy)

    def show_reflection(self):
        if analysis.is_reflection_code(self.x) is False:
            self.__logger.log(Logger.WARNING, "No reflection code was found!")
            return
        paths = self.x.get_tainted_packages().search_methods(
            "Ljava/lang/reflect/Method;", ".", ".")
        str_info_reflection = ""
        for path in paths:
            str_info_reflection += (self.show_Path(self.x.get_vm(), path) +
                                    "\n\n\t")
        self.__logger.log_with_title("Usage of Reflection",
                                     str_info_reflection)

    def show_activities(self):
        self.__logger.log_with_title(
            "Activities", '\n\t -' + '\n\t -'.join(self.apk.get_activities()))

    def show_libraries(self):
        if len(self.apk.get_libraries()) == 0:
            self.__logger.log(Logger.WARNING, "No libraries were found.")
            return
        self.__logger.log(Logger.INFO,
                          "Libraries Used: " + str(self.apk.get_libraries()))

    def init_actions(self):
        self.ui.saveLogBtn.clicked.connect(self.__logger.saveLog)
        self.ui.clearLogBtn.clicked.connect(self.__logger.clearLog)
        self.ui.showStringsBtn.clicked.connect(self.openStringsWindow)
        self.ui.showManifestBtn.clicked.connect(self.openManifestWindow)

    def load_apk_from_device(self):
        table = DeviceTable(self, parent=self)
        self.sample_dialog.close()
        table.show_data()
        table.exec_()

    def load_apk(self, path=None):
        if not path:
            path = QtGui.QFileDialog.getOpenFileName(
                self, "Open File", '',
                "APK Files (*.apk);;Androguard Session (*.ag)")
        self.apk_path = path
        self.set_loading_progressbar(
            "Analyzing APK", "Please wait while the APK is being dissected")
        self.__logger.log(Logger.INFO, "Analyzing %s..." % str(path))
        self.apk = apk.APK(self.apk_path)
        self.manifest = self.apk.get_AndroidManifest().getElementsByTagName(
            "manifest")[0]
        self.fileLoadingThread.load(str(path))

    def load_permissions(self):
        perms = self.get_uses_permissions()
        self.ui.permsTable.setRowCount(len(perms))
        self.ui.permsTable.horizontalHeader().setStretchLastSection(True)
        for i, uses_permission in enumerate(perms):
            newitem = QtGui.QTableWidgetItem(uses_permission)
            self.ui.permsTable.setItem(0, i, newitem)

    def get_uses_permissions(self):
        permissions = []
        for uses_permission in self.manifest.getElementsByTagName(
                "uses-permission"):
            permissions.append(
                uses_permission.attributes["android:name"].value)
        return permissions

    def set_loading_progressbar(self, title, text):
        self.dialog = QtGui.QProgressDialog(self)
        self.dialog.setMinimum(0)
        self.dialog.setLabelText(text)
        self.dialog.setMaximum(0)
        self.dialog.setWindowTitle(title)
        self.dialog.setCancelButton(None)
        self.dialog.setModal(True)
        self.dialog.show()

    def set_loading_progressbar_text(self, text):
        if self.dialog is not None:
            self.dialog.setLabelText(text)

    def set_loading_progressbar_disabled(self):
        self.dialog.hide()

    def show_Path(self, vm, path):
        cm = vm.get_class_manager()

        if isinstance(path, analysis.PathVar):
            dst_class_name, dst_method_name, dst_descriptor = path.get_dst(cm)
            info_var = path.get_var_info()
            return "%s %s (0x%x) ---> %s->%s%s " % (
                path.get_access_flag(), info_var, path.get_idx(),
                dst_class_name, dst_method_name, dst_descriptor)
        else:
            if path.get_access_flag() == analysis.TAINTED_PACKAGE_CALL:
                src_class_name, src_method_name, src_descriptor = path.get_src(
                    cm)
                dst_class_name, dst_method_name, dst_descriptor = path.get_dst(
                    cm)

                return "%d %s->%s%s (0x%x) ---> %s->%s%s" % (
                    path.get_access_flag(), src_class_name, src_method_name,
                    src_descriptor, path.get_idx(), dst_class_name,
                    dst_method_name, dst_descriptor)
            else:
                src_class_name, src_method_name, src_descriptor = path.get_src(
                    cm)
                return "%d %s->%s%s (0x%x)" % (path.get_access_flag(),
                                               src_class_name, src_method_name,
                                               src_descriptor, path.get_idx())
Exemplo n.º 28
0
class MainWindow(QtGui.QMainWindow):

    '''Main window:
       self.central: QTabWidget in center area
       self.dock: QDockWidget in left area
       self.tree: TreeWindow(QTreeWidget) in self.dock
    '''

    def __init__(self, parent=None, input_file=None):
        super(MainWindow, self).__init__(parent)
        self.session = None

        self.setupSession()

        self.setupFileMenu()
        self.setupViewMenu()
        self.setupHelpMenu()

        self.setupCentral()
        self.setupEmptyTree()
        self.setupDock()
        self.setWindowTitle("Androguard GUI")

        self.showStatus("Androguard GUI")

        if input_file != None:
            self.openFile(input_file)

    def showStatus(self, msg):
        '''Helper function called by any window to display a message
           in status bar.
        '''
        androconf.debug(msg)
        self.statusBar().showMessage(msg)

    def about(self):
        '''User clicked About menu. Display a Message box.'''
        QtGui.QMessageBox.about(self, "About Androguard GUI",
                "<p><b>Androguard GUI</b> is basically a GUI for Androguard :)." \
                "<br>Have fun !</p>")

    def setupSession(self):
        self.session = Session()

        self.fileLoadingThread = FileLoadingThread(self.session)
        self.connect(self.fileLoadingThread,
                QtCore.SIGNAL("loadedFile(bool)"),
                self.loadedFile)

    def loadedFile(self, success):
        if not success:
            self.showStatus("Analysis of %s failed :(" %
                    str(self.fileLoadingThread.file_path))
            return

        self.updateDockWithTree()
        self.cleanCentral()

        self.showStatus("Analysis of %s done!" %
                str(self.fileLoadingThread.file_path))

    def openFile(self, path=None):
        '''User clicked Open menu. Display a Dialog to ask which file to open.'''
        self.session.reset()

        if not path:
            path = QtGui.QFileDialog.getOpenFileName(self, "Open File",
                    '', "Android Files (*.apk *.jar *.dex *.odex *.dey);;Androguard Session (*.ag)")
            path = str(path[0])

        if path:
            self.setupTree()
            self.showStatus("Analyzing %s..." % str(path))
            self.fileLoadingThread.load(path)

    def addFile(self, path=None):
        '''User clicked Open menu. Display a Dialog to ask which APK to open.'''
        if not self.session.isOpen():
            return

        if not path:
            path = QtGui.QFileDialog.getOpenFileName(self, "Add File",
                    '', "Android Files (*.apk *.jar *.dex *.odex *.dey)")
            path = str(path[0])

        if path:
            self.showStatus("Analyzing %s..." % str(path))
            self.fileLoadingThread.load(path)

    def saveFile(self, path=None):
        '''User clicked Save menu. Display a Dialog to ask whwre to save.'''
        if not path:
            path = QtGui.QFileDialog.getSaveFileName(self, "Save File",
                    '', "Androguard Session (*.ag)")
            path = str(path[0])

        if path:
            self.showStatus("Saving %s..." % str(path))
            self.saveSession(path)

    def saveSession(self, path):
        '''Save androguard session.'''
        try:
            self.session.save(path)
        except RuntimeError as e:
            androconf.error(str(e))
            # http://stackoverflow.com/questions/2134706/hitting-maximum-recursion-depth-using-pythons-pickle-cpickle
            androconf.error("Try increasing sys.recursionlimit")
            os.remove(path)
            androconf.warning("Session not saved")

    def quit(self):
        '''Clicked in File menu to exit or CTRL+Q to close main window'''
        QtGui.qApp.quit()

    def closeEvent(self, event):
        '''Clicked [x] to close main window'''
        event.accept()

    def setupEmptyTree(self):
        '''Setup empty Tree at startup. '''
        if hasattr(self, "tree"):
            del self.tree
        self.tree = QtGui.QTreeWidget(self)
        self.tree.header().close()

    def setupDock(self):
        '''Setup empty Dock at startup. '''
        self.dock = QtGui.QDockWidget("Classes", self)
        self.dock.setWidget(self.tree)
        self.dock.setFeatures(QtGui.QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.dock)

    def setupTree(self):
        androconf.debug("Setup Tree")
        self.tree = TreeWindow(win=self, session=self.session)
        self.tree.setWindowTitle("Tree model")
        self.dock.setWidget(self.tree)

    def setupCentral(self):
        '''Setup empty window supporting tabs at startup. '''
        self.central = QtGui.QTabWidget()
        self.central.setTabsClosable(True)
        self.central.tabCloseRequested.connect(self.tabCloseRequestedHandler)
        self.central.currentChanged.connect(self.currentTabChanged)
        self.setCentralWidget(self.central)

    def tabCloseRequestedHandler(self, index):
        self.central.removeTab(index)

    def currentTabChanged(self, index):
        androconf.debug("curentTabChanged -> %d" % index)
        if index == -1:
            return # all tab closed

    def cleanCentral(self):
        #TOFIX: Removes all the pages, but does not delete them.
        self.central.clear()

    def setupFileMenu(self):
        fileMenu = QtGui.QMenu("&File", self)
        self.menuBar().addMenu(fileMenu)

        fileMenu.addAction("&Open...", self.openFile, "Ctrl+O")
        fileMenu.addAction("&Add...", self.addFile, "Ctrl+A")
        fileMenu.addAction("&Save...", self.saveFile, "Ctrl+S")
        fileMenu.addAction("E&xit", self.quit, "Ctrl+Q")

    def setupViewMenu(self):
        viewMenu = QtGui.QMenu("&View", self)
        self.menuBar().addMenu(viewMenu)

        viewMenu.addAction("&Strings...", self.openStringsWindow)

    def setupHelpMenu(self):
        helpMenu = QtGui.QMenu("&Help", self)
        self.menuBar().addMenu(helpMenu)

        helpMenu.addAction("&About", self.about)
        helpMenu.addAction("About &Qt", QtGui.qApp.aboutQt)

    def updateDockWithTree(self, empty=False):
        '''Update the classes tree. Called when
            - a new APK has been imported
            - a classe has been renamed (displayed in the tree)
        '''
        self.setupTree()
        self.tree.fill()


    def openStringsWindow(self):
        stringswin = StringsWindow(win=self, session=self.session)
        self.central.addTab(stringswin, stringswin.title)
        self.central.setTabToolTip(self.central.indexOf(stringswin), stringswin.title)
        self.central.setCurrentWidget(stringswin)

    def openBytecodeWindow(self, current_class, method=None):
        pass#self.central.setCurrentWidget(sourcewin)

    def openSourceWindow(self, current_class, method=None):
        '''Main function to open a .java source window
           It checks if it already opened and open that tab,
           otherwise, initialize a new window.
        '''
        androconf.debug("openSourceWindow for %s" % current_class)

        sourcewin = self.getMeSourceWindowIfExists(current_class)
        if not sourcewin:
            current_filename = self.session.get_filename_by_class(current_class)
            current_digest = self.session.get_digest_by_class(current_class)

            sourcewin = SourceWindow(win=self,
                                    current_class=current_class,
                                    current_title=current_class.current_title,
                                    current_filename=current_filename,
                                    current_digest=current_digest,
                                    session=self.session)
            sourcewin.reload_java_sources()
            self.central.addTab(sourcewin, sourcewin.title)
            self.central.setTabToolTip(self.central.indexOf(sourcewin), current_class.get_name())

        if method:
            sourcewin.browse_to_method(method)

        self.central.setCurrentWidget(sourcewin)

    def getMeSourceWindowIfExists(self, current_class):
        '''Helper for openSourceWindow'''
        for idx in range(self.central.count()):
            if current_class.get_name() == self.central.tabToolTip(idx):
                androconf.debug("Tab %s already opened at: %d" % (current_class.get_name(), idx))
                return self.central.widget(idx)
        return None

    def doesClassExist(self, path):
        arg = class2func(path)
        try:
            getattr(self.d, arg)
        except AttributeError:
            return False
        return True
Exemplo n.º 29
0
class MainWindow(QtGui.QMainWindow):

    '''Main window:
       self.central: QTabWidget in center area
       self.dock: QDockWidget in left area
       self.tree: TreeWindow(QTreeWidget) in self.dock
    '''

    def __init__(self, parent=None, input_file=None):
        super(MainWindow, self).__init__(parent)
        self.session = None

        self.setupSession()

        self.setupFileMenu()
        self.setupViewMenu()
        self.setupHelpMenu()

        self.setupCentral()
        self.setupEmptyTree()
        self.setupDock()
        self.setWindowTitle("Androguard GUI")

        self.showStatus("Androguard GUI")

        if input_file != None:
            self.openFile(input_file)

    def showStatus(self, msg):
        '''Helper function called by any window to display a message
           in status bar.
        '''
        androconf.debug(msg)
        self.statusBar().showMessage(msg)

    def about(self):
        '''User clicked About menu. Display a Message box.'''
        QtGui.QMessageBox.about(self, "About Androguard GUI",
                                "<p><b>Androguard GUI</b> is basically a GUI for Androguard :)."
                                "<br>Have fun !</p>")

    def setupSession(self):
        self.session = Session()

        self.fileLoadingThread = FileLoadingThread(self.session)
        self.connect(self.fileLoadingThread,
                     QtCore.SIGNAL("loadedFile(bool)"),
                     self.loadedFile)

    def loadedFile(self, success):
        if not success:
            self.showStatus("Analysis of %s failed :(" %
                            str(self.fileLoadingThread.file_path))
            return

        self.updateDockWithTree()
        self.cleanCentral()

        self.showStatus("Analysis of %s done!" %
                        str(self.fileLoadingThread.file_path))

    def openFile(self, path=None):
        '''User clicked Open menu. Display a Dialog to ask which file to open.'''
        self.session.reset()

        if not path:
            path = QtGui.QFileDialog.getOpenFileName(self, "Open File",
                                                     '', "Android Files (*.apk *.jar *.dex *.odex *.dey);;Androguard Session (*.ag)")
            path = str(path[0])

        if path:
            self.setupTree()
            self.showStatus("Analyzing %s..." % str(path))
            self.fileLoadingThread.load(path)

    def addFile(self, path=None):
        '''User clicked Open menu. Display a Dialog to ask which APK to open.'''
        if not self.session.isOpen():
            return

        if not path:
            path = QtGui.QFileDialog.getOpenFileName(self, "Add File",
                                                     '', "Android Files (*.apk *.jar *.dex *.odex *.dey)")
            path = str(path[0])

        if path:
            self.showStatus("Analyzing %s..." % str(path))
            self.fileLoadingThread.load(path)

    def saveFile(self, path=None):
        '''User clicked Save menu. Display a Dialog to ask whwre to save.'''
        if not path:
            path = QtGui.QFileDialog.getSaveFileName(self, "Save File",
                                                     '', "Androguard Session (*.ag)")
            path = str(path[0])

        if path:
            self.showStatus("Saving %s..." % str(path))
            self.saveSession(path)

    def saveSession(self, path):
        '''Save androguard session.'''
        try:
            self.session.save(path)
        except RuntimeError as e:
            androconf.error(str(e))
            # http://stackoverflow.com/questions/2134706/hitting-maximum-recursion-depth-using-pythons-pickle-cpickle
            androconf.error("Try increasing sys.recursionlimit")
            os.remove(path)
            androconf.warning("Session not saved")

    def quit(self):
        '''Clicked in File menu to exit or CTRL+Q to close main window'''
        QtGui.qApp.quit()

    def closeEvent(self, event):
        '''Clicked [x] to close main window'''
        event.accept()

    def setupEmptyTree(self):
        '''Setup empty Tree at startup. '''
        if hasattr(self, "tree"):
            del self.tree
        self.tree = QtGui.QTreeWidget(self)
        self.tree.header().close()

    def setupDock(self):
        '''Setup empty Dock at startup. '''
        self.dock = QtGui.QDockWidget("Classes", self)
        self.dock.setWidget(self.tree)
        self.dock.setFeatures(QtGui.QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.dock)

    def setupTree(self):
        androconf.debug("Setup Tree")
        self.tree = TreeWindow(win=self, session=self.session)
        self.tree.setWindowTitle("Tree model")
        self.dock.setWidget(self.tree)

    def setupCentral(self):
        '''Setup empty window supporting tabs at startup. '''
        self.central = QtGui.QTabWidget()
        self.central.setTabsClosable(True)
        self.central.tabCloseRequested.connect(self.tabCloseRequestedHandler)
        self.central.currentChanged.connect(self.currentTabChanged)
        self.setCentralWidget(self.central)

    def tabCloseRequestedHandler(self, index):
        self.central.removeTab(index)

    def currentTabChanged(self, index):
        androconf.debug("curentTabChanged -> %d" % index)
        if index == -1:
            return  # all tab closed

    def cleanCentral(self):
        # TOFIX: Removes all the pages, but does not delete them.
        self.central.clear()

    def setupFileMenu(self):
        fileMenu = QtGui.QMenu("&File", self)
        self.menuBar().addMenu(fileMenu)

        fileMenu.addAction("&Open...", self.openFile, "Ctrl+O")
        fileMenu.addAction("&Add...", self.addFile, "Ctrl+A")
        fileMenu.addAction("&Save...", self.saveFile, "Ctrl+S")
        fileMenu.addAction("E&xit", self.quit, "Ctrl+Q")

    def setupViewMenu(self):
        viewMenu = QtGui.QMenu("&View", self)
        self.menuBar().addMenu(viewMenu)

        viewMenu.addAction("&Strings...", self.openStringsWindow)

    def setupHelpMenu(self):
        helpMenu = QtGui.QMenu("&Help", self)
        self.menuBar().addMenu(helpMenu)

        helpMenu.addAction("&About", self.about)
        helpMenu.addAction("About &Qt", QtGui.qApp.aboutQt)

    def updateDockWithTree(self, empty=False):
        '''Update the classes tree. Called when
            - a new APK has been imported
            - a classe has been renamed (displayed in the tree)
        '''
        self.setupTree()
        self.tree.fill()

    def openStringsWindow(self):
        stringswin = StringsWindow(win=self, session=self.session)
        self.central.addTab(stringswin, stringswin.title)
        self.central.setTabToolTip(
            self.central.indexOf(stringswin), stringswin.title)
        self.central.setCurrentWidget(stringswin)

    def openBytecodeWindow(self, current_class, method=None):
        pass  # self.central.setCurrentWidget(sourcewin)

    def openSourceWindow(self, current_class, method=None):
        '''Main function to open a .java source window
           It checks if it already opened and open that tab,
           otherwise, initialize a new window.
        '''
        androconf.debug("openSourceWindow for %s" % current_class)

        sourcewin = self.getMeSourceWindowIfExists(current_class)
        if not sourcewin:
            current_filename = self.session.get_filename_by_class(
                current_class)
            current_digest = self.session.get_digest_by_class(current_class)

            sourcewin = SourceWindow(win=self,
                                     current_class=current_class,
                                     current_title=current_class.current_title,
                                     current_filename=current_filename,
                                     current_digest=current_digest,
                                     session=self.session)
            sourcewin.reload_java_sources()
            self.central.addTab(sourcewin, sourcewin.title)
            self.central.setTabToolTip(self.central.indexOf(
                sourcewin), current_class.get_name())

        if method:
            sourcewin.browse_to_method(method)

        self.central.setCurrentWidget(sourcewin)

    def getMeSourceWindowIfExists(self, current_class):
        '''Helper for openSourceWindow'''
        for idx in range(self.central.count()):
            if current_class.get_name() == self.central.tabToolTip(idx):
                androconf.debug("Tab %s already opened at: %d" %
                                (current_class.get_name(), idx))
                return self.central.widget(idx)
        return None

    def doesClassExist(self, path):
        arg = class2func(path)
        try:
            getattr(self.d, arg)
        except AttributeError:
            return False
        return True