class PDG(): def __init__(self, filename): """ :type self: object """ self.filename = filename try: self.a = APK(filename) self.d = DalvikVMFormat(self.a.get_dex()) self.d.create_python_export() self.dx = Analysis(self.d) except zipfile.BadZipfile: # if file is not an APK, may be a dex object _, self.d, self.dx = AnalyzeDex(self.filename) self.d.set_vmanalysis(self.dx) self.dx.create_xref() self.fcg = self.dx.get_call_graph() self.icfg = self.build_icfg() def get_graph(self): return self.icfg def build_icfg(self): icfg = nx.DiGraph() methods = self.d.get_methods() for method in methods: for bb in self.dx.get_method(method).basic_blocks.get(): children = [] label = self.get_bb_label(bb) children = self.get_children(bb, self.dx) icfg.add_node(label) icfg.add_edges_from([(label, child) for child in children]) return icfg def get_bb_label(self, bb): """ Return the descriptive name of a basic block """ return self.get_method_label(bb.method) + (bb.name, ) def get_method_label(self, method): """ Return the descriptive name of a method """ return (method.get_class_name(), method.get_name(), method.get_descriptor()) def get_children(self, bb, dx): """ Return the labels of the basic blocks that are children of the input basic block in and out of its method """ return self.get_bb_intra_method_children( bb) + self.get_bb_extra_method_children(bb, dx) def get_bb_intra_method_children(self, bb): """ Return the labels of the basic blocks that are children of the input basic block within a method """ child_labels = [] for c_in_bb in bb.get_next(): next_bb = c_in_bb[2] child_labels.append(self.get_bb_label(next_bb)) return child_labels def get_bb_extra_method_children(self, bb, dx): """ Given a basic block, find the calls to external methods and return the label of the first basic block in these methods """ call_labels = [] # iterate over calls from bb method to external methods try: xrefs = dx.get_method_analysis(bb.method).get_xref_to() except AttributeError: return call_labels for xref in xrefs: remote_method_offset = xref[2] if self.call_in_bb(bb, remote_method_offset): try: remote_method = dx.get_method( self.d.get_method_by_idx(remote_method_offset)) if remote_method: remote_bb = next(remote_method.basic_blocks.get()) call_labels.append(self.get_bb_label(remote_bb)) except StopIteration: pass return call_labels def call_in_bb(self, bb, idx): return bb.get_start() <= idx <= bb.get_end()