class MainWindow(QtWidgets.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, session=session_module.Session(), input_file=None, input_plugin=None): super(MainWindow, self).__init__(parent) self.session = session self.bin_windows = {} self.setupFileMenu() self.setupViewMenu() self.setupPluginsMenu() self.setupHelpMenu() self.setupCentral() self.setupEmptyTree() self.setupDock() self.setupSession() self.setWindowTitle("Androguard GUI") self.showStatus("Androguard GUI") self.installEventFilter(self) self.input_plugin = input_plugin if input_file: self._openFile(input_file) def eventFilter(self, watched, event): for bin_window in self.bin_windows.values(): bin_window.eventFilter(watched, event) return False 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.""" QtWidgets.QMessageBox.about( self, "About Androguard GUI", "<p><b>Androguard GUI</b> is basically a GUI for Androguard :)." "<br>Have fun !</p>", ) def setupSession(self): androconf.debug("Setup Session") self.fileLoadingThread = FileLoadingThread(self) self.fileLoadingThread.file_loaded.connect(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)) if self.input_plugin: self._runPlugin(self.input_plugin) def openFile(self): self.session.reset() filepath, _ = QtWidgets.QFileDialog.getOpenFileName( self, "Open File", ".", "Android Files (*.apk *.jar *.dex *.odex *.dey);;Androguard Session (*.ag)" ) self._openFile(filepath) def _openFile(self, filepath=None): if filepath: self.setupTree() self.showStatus("Analyzing %s..." % str(filepath)) self.fileLoadingThread.load(filepath) def addFile(self): if not self.session.isOpen(): return filepath, _ = QtWidgets.QFileDialog.getOpenFileName( self, "Add File", "", "Android Files (*.apk *.jar *.dex *.odex *.dey)" ) if filepath: self.showStatus("Analyzing %s..." % str(filepath)) self.fileLoadingThread.load(filepath) def saveFile(self): """User clicked Save menu. Display a Dialog to ask whwre to save.""" filepath, _ = QtWidgets.QFileDialog.getSaveFileName(self, "Save File", "", "Androguard Session (*.ag)") if filepath: self.showStatus("Saving %s..." % str(filepath)) self.saveSession(filepath) def saveSession(self, filepath): """Save androguard session.""" try: session_module.Save(self.session, filepath) except RuntimeError as e: androconf.error(str(e)) os.remove(filepath) androconf.warning("Session not saved") def _runPlugin(self, filepath): androconf.debug("RUN plugin from %s" % filepath) module_name = os.path.splitext(os.path.basename(filepath))[0] f, filename, description = imp.find_module(module_name, [os.path.dirname(filepath)]) print f, filename, description mod = imp.load_module(module_name, f, filename, description) mod.PluginEntry(self.session) def openRunPluginWindow(self): filepath, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Open File", "", "Python Files (*.py);;") if filepath: self._runPlugin(filepath) 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 = QtWidgets.QTreeWidget(self) self.tree.header().close() def setupDock(self): """Setup empty Dock at startup. """ self.dock = QtWidgets.QDockWidget("Classes", self) self.dock.setWidget(self.tree) self.dock.setFeatures(QtWidgets.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 = TabsWindow(self.bin_windows, self) self.setCentralWidget(self.central) def cleanCentral(self): self.central.actioncloseAllTabs() def setupFileMenu(self): androconf.debug("Setup File Menu") self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction("&Open...", self.openFile, "Ctrl+O") self.fileMenu.addAction("&Add...", self.addFile, "Ctrl+A") self.fileMenu.addAction("&Save...", self.saveFile, "Ctrl+S") self.fileMenu.addAction("E&xit", self.close, "Ctrl+Q") def setupViewMenu(self): androconf.debug("Setup View Menu") self.viewMenu = self.menuBar().addMenu("&View") self.viewMenu.addAction("&Strings...", self.openStringsWindow) self.viewMenu.addAction("&Methods...", self.openMethodsWindow) self.viewMenu.addAction("&API...", self.openAPIWindow) self.viewMenu.addAction("&APK...", self.openApkWindow) self.viewMenu.addAction("&Resources...", self.openResourcesWindow) def setupPluginsMenu(self): androconf.debug("Setup Plugins Menu") self.pluginsMenu = self.menuBar().addMenu("&Plugins") self.pluginsMenu.addAction("&Run...", self.openRunPluginWindow) def setupHelpMenu(self): androconf.debug("Setup Help Menu") self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction("&About", self.about) self.helpMenu.addAction("About &Qt", QtWidgets.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 openMethodsWindow(self): methodswin = MethodsWindow(win=self, session=self.session) self.central.addTab(methodswin, methodswin.title) self.central.setTabToolTip(self.central.indexOf(methodswin), methodswin.title) self.central.setCurrentWidget(methodswin) def openResourcesWindow(self): resourceswin = ResourcesWindow(win=self, session=self.session) self.central.addTab(resourceswin, resourceswin.title) self.central.setTabToolTip(self.central.indexOf(resourceswin), resourceswin.title) self.central.setCurrentWidget(resourceswin) def openAPIWindow(self): apiwin = APIWindow(win=self, session=self.session) self.central.addTab(apiwin, apiwin.title) self.central.setTabToolTip(self.central.indexOf(apiwin), apiwin.title) self.central.setCurrentWidget(apiwin) def openApkWindow(self): androconf.debug("openApkWindow for %s" % self.session.analyzed_apk) bin_window = binWidget(self, ApkModel(self.session.get_objects_apk(self.fileLoadingThread.file_path)[0]), "APK") bin_window.activateWindow() self.central.addTab(bin_window, bin_window.title) self.central.setCurrentWidget(bin_window) self.bin_windows[bin_window.title] = bin_window def openBinWindow(self, current_class): androconf.debug("openBinWindow for %s" % current_class) bin_window = self.getMeOpenedWindowIfExists(current_class.current_title) if not bin_window: bin_window = binWidget(self, DexClassModel(current_class), current_class.get_name()) bin_window.activateWindow() self.central.addTab(bin_window, current_class.current_title) self.central.setTabToolTip(self.central.indexOf(bin_window), current_class.current_title) self.bin_windows[current_class.current_title] = bin_window bin_window.enable() self.central.setCurrentWidget(bin_window) def openSourceWindow(self, current_class, method=None): """Main function to open a decompile 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.getMeOpenedWindowIfExists(current_class.current_title + "(S)") 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 + "(S)", 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), sourcewin.title) if method: sourcewin.browse_to_method(method) self.central.setCurrentWidget(sourcewin) def getMeOpenedWindowIfExists(self, name): for idx in range(self.central.count()): if name == self.central.tabToolTip(idx): androconf.debug("Tab %s already opened at: %d" % (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
class MainWindow(QtWidgets.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, session=session_module.Session(), input_file=None, input_plugin=None): super(MainWindow, self).__init__(parent) self.session = session self.bin_windows = {} self.setupFileMenu() self.setupViewMenu() self.setupPluginsMenu() self.setupHelpMenu() self.setupCentral() self.setupEmptyTree() self.setupDock() self.setupSession() self.setWindowTitle("Androguard GUI") self.showStatus("Androguard GUI") self.installEventFilter(self) self.input_plugin = input_plugin if input_file: self._openFile(input_file) root = os.path.dirname(os.path.realpath(__file__)) self.setWindowIcon(QtGui.QIcon(os.path.join(root, "androguard.ico"))) def eventFilter(self, watched, event): for bin_window in list(self.bin_windows.values()): bin_window.eventFilter(watched, event) return False def showStatus(self, msg): """Helper function called by any window to display a message in status bar. """ log.debug(msg) self.statusBar().showMessage(msg) def about(self): """User clicked About menu. Display a Message box.""" QtWidgets.QMessageBox.about(self, "About Androguard GUI", "<p><b>Androguard GUI</b> is basically a GUI for Androguard :)." \ "<br>Have fun !</p>") def _no_apk_loaded(self): """Show a message if no APK was loaded yet...""" QtWidgets.QMessageBox.information( self, "No APK loaded yet!", "<p>There was no APK loaded yet. Please load one using File->Open.</p>" ) def setupSession(self): log.debug("Setup Session") self.fileLoadingThread = FileLoadingThread(self) self.fileLoadingThread.file_loaded.connect(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)) if self.input_plugin: self._runPlugin(self.input_plugin) def openFile(self): self.session.reset() filepath, _ = QtWidgets.QFileDialog.getOpenFileName( self, "Open File", '.', "Android Files (*.apk *.jar *.dex *.odex *.dey);;Androguard Session (*.ag)" ) self._openFile(filepath) def _openFile(self, filepath=None): if filepath: self.setupTree() self.showStatus("Analyzing %s..." % str(filepath)) self.fileLoadingThread.load(filepath) def addFile(self): if not self.session.isOpen(): log.debug(self.session.analyzed_digest) self._no_apk_loaded() return filepath, _ = QtWidgets.QFileDialog.getOpenFileName( self, "Add File", '', "Android Files (*.apk *.jar *.dex *.odex *.dey)") if filepath: self.showStatus("Analyzing %s..." % str(filepath)) self.fileLoadingThread.load(filepath) def saveFile(self): """User clicked Save menu. Display a Dialog to ask whwre to save.""" filepath, _ = QtWidgets.QFileDialog.getSaveFileName( self, "Save File", '', "Androguard Session (*.ag)") if filepath: # Ensure .ag as file ending if not filepath.endswith(".ag"): filepath = "{}.ag".format(filepath) self.showStatus("Saving %s..." % str(filepath)) self.saveSession(filepath) self.showStatus("Saved Session to %s!" % str(filepath)) def saveSession(self, filepath): """Save androguard session.""" try: session_module.Save(self.session, filepath) except RuntimeError as e: log.exception(e) os.remove(filepath) log.warning("Session not saved") def _runPlugin(self, filepath): module_name = os.path.splitext(os.path.basename(filepath))[0] log.debug("RUN plugin '{}' from {}".format(module_name, filepath)) mod = load_module(module_name, filepath) log.debug("Loaded %s", mod) if not mod or not hasattr(mod, 'PluginEntry'): QtWidgets.QMessageBox.warning( self, "Not a valid Plugin", "<p>This python file does not look like a valid plugin.</p>") return mod.PluginEntry(self.session) def openRunPluginWindow(self): filepath, _ = QtWidgets.QFileDialog.getOpenFileName( self, "Open File", '', "Python Files (*.py);;") if filepath: self._runPlugin(filepath) 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 = QtWidgets.QTreeWidget(self) self.tree.header().close() def setupDock(self): """Setup empty Dock at startup. """ self.dock = QtWidgets.QDockWidget("Classes", self) self.dock.setWidget(self.tree) self.dock.setFeatures(QtWidgets.QDockWidget.NoDockWidgetFeatures) self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.dock) def setupTree(self): log.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 = TabsWindow(self.bin_windows, self) self.setCentralWidget(self.central) def cleanCentral(self): self.central.actioncloseAllTabs() def setupFileMenu(self): log.debug("Setup File Menu") self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction("&Open...", self.openFile, "Ctrl+O") self.fileMenu.addAction("&Add...", self.addFile, "Ctrl+A") self.fileMenu.addAction("&Save...", self.saveFile, "Ctrl+S") self.fileMenu.addAction("E&xit", self.close, "Ctrl+Q") def setupViewMenu(self): log.debug("Setup View Menu") self.viewMenu = self.menuBar().addMenu("&View") self.viewMenu.addAction("&Strings...", self.openStringsWindow) self.viewMenu.addAction("&Methods...", self.openMethodsWindow) self.viewMenu.addAction("&API...", self.openAPIWindow) self.viewMenu.addAction("&APK...", self.openApkWindow) self.viewMenu.addAction("&Resources...", self.openResourcesWindow) def setupPluginsMenu(self): log.debug("Setup Plugins Menu") self.pluginsMenu = self.menuBar().addMenu("&Plugins") self.pluginsMenu.addAction("&Run...", self.openRunPluginWindow) def setupHelpMenu(self): log.debug("Setup Help Menu") self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction("&About", self.about) self.helpMenu.addAction("About &Qt", QtWidgets.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 openMethodsWindow(self): methodswin = MethodsWindow(win=self, session=self.session) self.central.addTab(methodswin, methodswin.title) self.central.setTabToolTip(self.central.indexOf(methodswin), methodswin.title) self.central.setCurrentWidget(methodswin) def openResourcesWindow(self): resourceswin = ResourcesWindow(win=self, session=self.session) self.central.addTab(resourceswin, resourceswin.title) self.central.setTabToolTip(self.central.indexOf(resourceswin), resourceswin.title) self.central.setCurrentWidget(resourceswin) def openAPIWindow(self): apiwin = APIWindow(win=self, session=self.session) self.central.addTab(apiwin, apiwin.title) self.central.setTabToolTip(self.central.indexOf(apiwin), apiwin.title) self.central.setCurrentWidget(apiwin) def openApkWindow(self): log.debug("openApkWindow for %s" % self.session.analyzed_apk) if not self.fileLoadingThread.file_path: self._no_apk_loaded() return bin_window = binWidget( self, ApkModel( self.session.get_objects_apk( self.fileLoadingThread.file_path)[0]), "APK") bin_window.activateWindow() self.central.addTab(bin_window, bin_window.title) self.central.setCurrentWidget(bin_window) self.bin_windows[bin_window.title] = bin_window def openBinWindow(self, current_class): log.debug("openBinWindow for %s" % current_class) dx = self.session.get_analysis(current_class) bin_window = self.getMeOpenedWindowIfExists( current_class.current_title) if not bin_window: bin_window = binWidget(self, DexClassModel(current_class, dx), current_class.get_name()) bin_window.activateWindow() self.central.addTab(bin_window, current_class.current_title) self.central.setTabToolTip(self.central.indexOf(bin_window), current_class.current_title) self.bin_windows[current_class.current_title] = bin_window bin_window.enable() self.central.setCurrentWidget(bin_window) def openSourceWindow(self, current_class, method=None): """Main function to open a decompile source window It checks if it already opened and open that tab, otherwise, initialize a new window. """ log.debug("openSourceWindow for %s" % current_class) sourcewin = self.getMeOpenedWindowIfExists( current_class.current_title + "(S)") 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 + "(S)", 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), sourcewin.title) if method: sourcewin.browse_to_method(method) self.central.setCurrentWidget(sourcewin) def getMeOpenedWindowIfExists(self, name): for idx in range(self.central.count()): if name == self.central.tabToolTip(idx): log.debug("Tab %s already opened at: %d" % (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
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())
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())
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
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, session=Session(), input_file=None): super(MainWindow, self).__init__(parent) self.session = session 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.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