def actionGoto(self): cursor = self.textCursor() start = cursor.selectionStart() end = cursor.selectionEnd() selection = cursor.selectedText() androconf.debug("Goto asked for '%s' (%d, %d)" % (selection, start, end)) if start not in self.doc.binding.keys(): self.mainwin.showStatus("Goto not available. No info for: '%s'." % selection) return t = self.doc.binding[start] if t[0] == 'NAME_METHOD_INVOKE': class_, method_ = t[2].split(' -> ') if class_ == 'this': class_ = self.path else: class_ = classdot2class(class_) else: self.mainwin.showStatus("Goto not available. Info ok: '%s' but object not supported." % selection) return androconf.debug("Found corresponding method: %s -> %s in source file: %s" % (class_, method_, self.path)) if not self.mainwin.doesClassExist(class_): self.mainwin.showStatus("Goto not available. Class: %s not in database." % class_) return self.mainwin.openSourceWindow(class_, method=method_)
def reload_java_sources(self): '''Reload completely the sources by asking Androguard to decompile it again. Useful when: - an element has been renamed to propagate the info - the current tab is changed because we do not know what user did since then, so we need to propagate previous changes as well ''' androconf.debug("Getting sources for %s" % self.current_class) lines = [] lines.append(("COMMENTS", [("COMMENT", "/*\n * filename:%s\n * digest:%s\n */\n" % (self.current_filename, self.current_digest))])) lines.extend(self.current_class.get_source_ext()) #TODO: delete doc when tab is closed? not deleted by "self" :( if hasattr(self, "doc"): del self.doc self.doc = SourceDocument(parent=self, lines=lines) self.setDocument(self.doc) #No need to save hightlighter. highlighBlock will automatically be called #because we passed the QTextDocument to QSyntaxHighlighter constructor if PYGMENTS: PygmentsHighlighter(self.doc, lexer=JavaLexer()) else: androconf.debug("Pygments is not present !")
def addAPK(self, filename, data): digest = hashlib.sha256(data).hexdigest() androconf.debug("add APK:%s" % digest) apk = APK(data, True) self.analyzed_apk[digest] = apk self.analyzed_files[filename].append(digest) self.analyzed_digest[digest] = filename androconf.debug("added APK:%s" % digest) return (digest, apk)
def itemDoubleClickedHandler(self, item, column): '''Signal sent by PySide when a tree element is clicked''' androconf.debug("item %s has been double clicked at column %s" % (str(item), str(column))) if item.childCount() != 0: self.mainwin.showStatus("Sources not available. %s is not a class" % path) return current_class, current_filename, current_digest = self._reverse_cache[item] self.mainwin.openSourceWindow(current_class)
def actionInfo(self): cursor = self.textCursor() start = cursor.selectionStart() end = cursor.selectionEnd() androconf.debug("actionInfo asked for (%d, %d)" % (start, end)) if start in self.doc.binding.keys(): self.mainwin.showStatus('%s at position: (%d, %d)' % (str(self.doc.binding[start]), start, end)) else: self.mainwin.showStatus("No info available.")
def get_xrefs_list(cls, class_item, method=None): '''Static method called before creating a XrefDialog to check if there are xrefs to display method (optional): method of the class we are looking xref from ''' androconf.debug("Getting XREF for %s" % class_item) item = class_item if method: item = method return XrefDialog.get_xrefs_list_from_element(item)
def browse_to_method(self, method): '''Scroll to the right place were the method is. TODO: implement it, because does not work for now. ''' #TODO: we need to find a way to scroll to the right place because # moving the cursor is not enough. Indeed if it is already in the window # it does not do nothing #TODO: idea, highlight the method in the screen so we do not have to search for it androconf.debug("Browsing to %s -> %s" % (self.current_class, method))
def cursor_position_changed(self): '''Used to detect when cursor change position and to auto select word underneath it''' androconf.debug("cursor_position_changed") cur = self.textCursor() androconf.debug(cur.position()) androconf.debug(cur.selectedText()) if len(cur.selectedText()) == 0: cur.select(QtGui.QTextCursor.SelectionType.WordUnderCursor) self.setTextCursor(cur) androconf.debug("cursor: %s" % cur.selectedText()) else: androconf.debug("cursor: no selection %s" % cur.selectedText())
def AnalyzeAPK(filename, raw=False, decompiler="dad"): """ Analyze an android application and setup all stuff for a more quickly analysis ! :param filename: the filename of the android application or a buffer which represents the application :type filename: string :param raw: True is you would like to use a buffer (optional) :type raw: boolean :param decompiler: ded, dex2jad, dad (optional) :type decompiler: string :rtype: return the :class:`APK`, :class:`DalvikVMFormat`, and :class:`VMAnalysis` objects """ androconf.debug("APK ...") a = APK(filename, raw) d, dx = AnalyzeDex(a.get_dex(), raw=True, decompiler=decompiler) return a, d, dx
def run(self): if self.incoming_file: try: file_path, file_type = self.incoming_file if file_type in ["APK", "DEX", "DEY"]: ret = self.session.add(file_path, open(file_path, 'r').read()) self.emit(QtCore.SIGNAL("loadedFile(bool)"), ret) elif file_type == "SESSION" : self.session.load(file_path) self.emit(QtCore.SIGNAL("loadedFile(bool)"), True) except Exception as e: androconf.debug(e) androconf.debug(traceback.format_exc()) self.emit(QtCore.SIGNAL("loadedFile(bool)"), False) self.incoming_file = [] else: self.emit(QtCore.SIGNAL("loadedFile(bool)"), False)
def __init__(self, parent=None, win=None, current_class=None, current_title=None, current_filename=None, current_digest=None, session=None): super(SourceWindow, self).__init__(parent) androconf.debug("New source tab for: %s" % current_class) self.mainwin = win self.session = session self.current_class = current_class self.current_title = current_title self.current_filename = current_filename self.current_digest = current_digest self.title = current_title self.setReadOnly(True) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.CustomContextMenuHandler) self.cursorPositionChanged.connect(self.cursor_position_changed)
def get_xrefs_list_from_element(cls, element): '''Helper for get_xrefs_list element is a ClassDefItem or MethodDefItem At the end of the function, we lost if we worked on a class or method but we do not care for now. ''' xref_items = element.XREFfrom.items androconf.debug("%d XREFs found" % len(xref_items)) # print xref_items xrefs = [] for xref_item in xref_items: class_ = xref_item[0].get_class_name() method_ = xref_item[0].get_name() descriptor_ = xref_item[0].get_descriptor() xrefs.append(classmethod2display(class_, method_, descriptor_)) # print xrefs return xrefs
def fill(self): '''Parse all the paths (['Lcom/example/myclass/MyActivity$1;', ...]) and build a tree using the QTreeWidgetItem insertion method.''' androconf.debug("Fill classes tree") for idx, filename, digest, classes in self.session.get_classes(): for c in sorted(classes, key=lambda c: c.name): sig = Signature(c) path_node = self.root_path_node path = None if sig.class_path == []: path = '.' if path not in path_node[0]: path_node[0][path] = ({}, QtGui.QTreeWidgetItem(path_node[1])) path_node[0][path][1].setText(0, path) path_node = path_node[0][path] else: # Namespaces for path in sig.class_path: if path not in path_node[0]: path_node[0][path] = ({}, QtGui.QTreeWidgetItem(path_node[1])) path_node[0][path][1].setText(0, path) path_node = path_node[0][path] # Class path_node[0][path] = ({}, QtGui.QTreeWidgetItem(path_node[1])) class_name = sig.class_name if idx > 0: class_name += "@%d" % idx c.current_title = class_name self._reverse_cache[path_node[0][path][1]] = (c, filename, digest) path_node[0][path][1].setText(0, class_name)
def actionRename(self): cursor = self.textCursor() start = cursor.selectionStart() end = cursor.selectionEnd() selection = cursor.selectedText() androconf.debug("Rename asked for '%s' (%d, %d)" % (selection, start, end)) if start not in self.doc.binding.keys(): self.mainwin.showStatus("Rename not available. No info for: '%s'." % selection) return # Double check if we support the renaming for the type of # object before poping a new window to the user t = self.doc.binding[start] if t[0] == 'NAME_METHOD_PROTOTYPE': class_ = self.current_class method_ = t[1] if method_ == self.title: method_ = 'init' androconf.debug("Found corresponding method: %s -> %s in source file: %s" % (class_, method_, self.current_filename)) elif t[0] == 'NAME_METHOD_INVOKE': class_, method_ = t[2].split(' -> ') if class_ == 'this': class_ = self.current_class androconf.debug("Found corresponding method: %s -> %s in source file: %s" % (class_, method_, self.current_filename)) elif t[0] == 'NAME_PROTOTYPE': class_ = t[2] + '.' + t[1] androconf.debug("Found corresponding class: %s in source file: %s" % (class_, self.current_filename)) elif t[0] == 'NAME_FIELD': field_ = t[1] androconf.debug("Found corresponding field: %s in source file: %s" % (field_, self.current_filename)) else: self.mainwin.showStatus("Rename not available. Info ok: '%s' but object not supported." % selection) return rwin = RenameDialog(parent=self, win=self.mainwin, element=selection, info=(start, end)) rwin.show()
def RunDecompiler(d, dx, decompiler): """ Run the decompiler on a specific analysis :param d: the DalvikVMFormat object :type d: :class:`DalvikVMFormat` object :param dx: the analysis of the format :type dx: :class:`VMAnalysis` object :param decompiler: the type of decompiler to use ("dad", "dex2jad", "ded") :type decompiler: string """ if decompiler != None: androconf.debug("Decompiler ...") decompiler = decompiler.lower() if decompiler == "dex2jad": d.set_decompiler(DecompilerDex2Jad(d, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR"], androconf.CONF["PATH_JAD"], androconf.CONF["BIN_JAD"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler == "dex2fernflower": d.set_decompiler(DecompilerDex2Fernflower(d, androconf.CONF["PATH_DEX2JAR"], androconf.CONF["BIN_DEX2JAR"], androconf.CONF["PATH_FERNFLOWER"], androconf.CONF["BIN_FERNFLOWER"], androconf.CONF["OPTIONS_FERNFLOWER"], androconf.CONF["TMP_DIRECTORY"])) elif decompiler == "ded": d.set_decompiler(DecompilerDed(d, androconf.CONF["PATH_DED"], androconf.CONF["BIN_DED"], androconf.CONF["TMP_DIRECTORY"])) else: d.set_decompiler(DecompilerDAD(d, dx))
def addDEX(self, filename, data): digest = hashlib.sha256(data).hexdigest() androconf.debug("add DEX:%s" % digest) d = DalvikVMFormat(data) androconf.debug("VMAnalysis ...") dx = newVMAnalysis(d) dx.create_xref() d.set_decompiler(DecompilerDAD(d, dx)) androconf.debug("added DEX:%s" % digest) self.analyzed_dex[digest] = (d, dx) self.analyzed_files[filename].append(digest) self.analyzed_digest[digest] = filename return (digest, d, dx)
def AnalyzeDex(filename, raw=False, decompiler="dad"): """ Analyze an android dex file and setup all stuff for a more quickly analysis ! :param filename: the filename of the android dex file or a buffer which represents the dex file :type filename: string :param raw: True is you would like to use a buffer (optional) :type raw: boolean :rtype: return the :class:`DalvikVMFormat`, and :class:`VMAnalysis` objects """ androconf.debug("DalvikVMFormat ...") d = None if raw == False: d = DalvikVMFormat(read(filename)) else: d = DalvikVMFormat(filename) androconf.debug("Export VM to python namespace") d.create_python_export() androconf.debug("VMAnalysis ...") dx = uVMAnalysis(d) androconf.debug("GVMAnalysis ...") gx = GVMAnalysis(dx, None) d.set_vmanalysis(dx) d.set_gvmanalysis(gx) RunDecompiler(d, dx, decompiler) androconf.debug("XREF ...") d.create_xref() androconf.debug("DREF ...") d.create_dref() return d, dx
def display_bytecodes(self): androconf.debug("Display bytecodes for %s" % self.current_class) self.mainwin.openBytecodeWindow(self.current_class)
def renameElement(self, oldname, newname, info): '''Called back after a user chose a new name for an element. ''' androconf.debug("Renaming %s into %s in %s" % (oldname, newname, self.current_filename)) start, end = info try: t = self.doc.binding[start] except: self.mainwin.showStatus("Unexpected error in renameElement") return # Determine type of the to-be-renamed element and Androguard internal objects type_ = None if t[0] == 'NAME_METHOD_PROTOTYPE': # method definition in a class method_ = t[1] if method_ == self.title: method_ = 'init' proto_ = t[2].method.proto androconf.debug("Found: class=%s, method=%s, proto=%s" % (self.current_class, method_, proto_)) type_ = "METHOD" elif t[0] == 'NAME_METHOD_INVOKE': # method call in a method class_, method_ = t[2].split(' -> ') class_ = classdot2class(class_) if class_ == 'this': class_ = self.path proto_ = proto2methodprotofunc("".join(t[3]) + t[4]) androconf.debug("Found: class=%s, method=%s, proto=%s" % (class_, method_, proto_)) type_ = "METHOD" elif t[0] == 'NAME_PROTOTYPE': # class definition on top of a class class_ = t[2] + '.' + t[1] package_ = t[2] androconf.debug("Found: package=%s, class=%s" % (package_, class_)) type_ = "CLASS" elif t[0] == 'NAME_FIELD': field_item = t[3] type_ = "FIELD" else: self.mainwin.showStatus("Rename not available. Info found: '%s' but object not supported." % selection) return # Do the actual renaming if type_ == "METHOD": if self.method_name_exist(newname): self.mainwin.showStatus("Method name already exist") return method_class_name = self.current_class.get_name() method_name = method_ method_proto = proto_ current_analysis = self.session.get_analysis(self.current_class) method_item = current_analysis.get_method_by_name(method_class_name, method_name, method_proto) if not method_item: self.mainwin.showStatus("Impossible to find the method") return method_item.set_name(str(newname)) #unicode to ascii elif type_ == "CLASS": newname_class = classdot2class(package_ + '.' + newname) self.mainwin.showStatus("New name: %s" % newname_class) class_item = self.current_class #getattr(self.mainwin.d, classdot2func(class_)) class_item.set_name(str(newname_class)) #unicode to ascii self.mainwin.updateDockWithTree() elif type_ == 'FIELD': if self.field_name_exist(newname): self.mainwin.showStatus("Field name already exist") return field_item.set_name(str(newname)) else: self.mainwin.showStatus("Unsupported type: %s" % str(type_)) return self.reload_java_sources()
def actionXref(self): cursor = self.textCursor() start = cursor.selectionStart() end = cursor.selectionEnd() selection = cursor.selectedText() androconf.debug("Xref asked for '%s' (%d, %d)" % (selection, start, end)) if start not in self.doc.binding.keys(): self.mainwin.showStatus("Xref not available. No info for: '%s'." % selection) return class_ = None method_ = None t = self.doc.binding[start] print t if t[0] == 'NAME_METHOD_PROTOTYPE': method_ = t[1] if method_ == self.title: method_ = 'init' proto_ = t[2].method.proto method_class_name = self.current_class.get_name() method_name = method_ method_proto = proto_ current_analysis = self.session.get_analysis(self.current_class) androconf.debug("Found corresponding method: %s %s %s in source file: %s" % (method_class_name, method_name, method_proto, self.current_filename)) class_analysis = current_analysis.get_class_analysis(self.current_class.get_name()) if not class_analysis: self.mainwin.showStatus("No xref returned (no class_analysis object).") return method_analysis = class_analysis.get_method_analysis(current_analysis.get_method_by_name(method_class_name, method_name, method_proto)) print method_analysis if not method_analysis: self.mainwin.showStatus("No xref returned (no method_analysis object).") return xwin = XrefDialogMethod(parent=self.mainwin, win=self.mainwin, current_class=self.current_class, class_analysis=class_analysis, method_analysis=method_analysis) xwin.show() elif t[0] == 'NAME_FIELD': field_ = t[3] current_analysis = self.session.get_analysis(self.current_class) class_analysis = current_analysis.get_class_analysis(self.current_class.get_name()) if not class_analysis: self.mainwin.showStatus("No xref returned (no class_analysis object).") return field_analysis = class_analysis.get_field_analysis(field_) print field_analysis if not field_analysis: self.mainwin.showStatus("No xref returned (no field_analysis object).") return xwin = XrefDialogField(parent=self.mainwin, win=self.mainwin, current_class=self.current_class, class_analysis=class_analysis, field_analysis=field_analysis) xwin.show() else: self.mainwin.showStatus("No xref returned.") return