Source code for angr.analyses.soot_class_hierarchy

import logging

from . import Analysis

l = logging.getLogger(name=__name__)


[docs]class SootClassHierarchyError(Exception):
[docs] def __init__(self, msg): self.msg = msg
def __str__(self): return self.msg
[docs]class NoConcreteDispatch(SootClassHierarchyError):
[docs] def __init__(self, msg): self.msg = msg
[docs]class SootClassHierarchy(Analysis): """ Generate complete hierarchy. """
[docs] def __init__(self): if self.project.arch.name != "Soot": raise SootClassHierarchyError("SootClassHierarchyError only supports analyzing Soot programs.") self.interface_implementers = {} self.sub_interfaces = {} self.dir_sub_interfaces = {} self.sub_classes = {} self.dir_sub_classes = {} # init data self.init_hierarchy()
[docs] def init_hierarchy(self): for class_name, cls in self.project.loader.main_object.classes.items(): if "INTERFACE" in cls.attrs: self.interface_implementers[cls] = [] self.dir_sub_interfaces[cls] = [] else: self.dir_sub_classes[cls] = [] for class_name, cls in self.project.loader.main_object.classes.items(): if self.has_super_class(cls): if "INTERFACE" in cls.attrs: # TODO # super_interfaces pass else: super_class = self.project.loader.main_object.classes[cls.super_class] self.dir_sub_classes[super_class].append(cls) for i_name in cls.interfaces: # get interface if i_name not in self.project.loader.main_object.classes: continue i = self.project.loader.main_object.classes[i_name] self.interface_implementers[i].append(cls) # fill direct implementers with subclasses for class_name, cls in self.project.loader.main_object.classes.items(): if "INTERFACE" in cls.attrs: implementers = self.interface_implementers[cls] s = set() for c in implementers: s |= set(self.get_sub_classes_including(c)) self.interface_implementers[cls] = list(s)
[docs] def has_super_class(self, cls): if cls.super_class: try: self.project.loader.main_object.classes[cls.super_class] return True except KeyError: pass return False
[docs] def is_subclass_including(self, cls_child, cls_parent): parent_classes = self.get_super_classes_including(cls_child) if cls_parent in parent_classes: return True return False
[docs] def is_subclass(self, cls_child, cls_parent): parent_classes = self.get_super_classes(cls_child) if cls_parent in parent_classes: return True return False
[docs] def is_visible_method(self, cls, method): method_cls = self.project.loader.main_object.classes[method.class_name] if not self.is_visible_class(cls, method_cls): return False if "PUBLIC" in method.attrs: return True if "PRIVATE" in method.attrs: return cls == method_cls package_from = cls.name.split(".")[:-1] package_to = method_cls.name.split(".")[:-1] if "PROTECTED" in method.attrs: is_sub = self.is_subclass_including(cls, method_cls) is_same_package = package_from == package_to return is_sub or is_same_package return package_from == package_to
[docs] def is_visible_class(self, cls_from, cls_to): if "PUBLIC" in cls_to.attrs: return True if "PROTECTED" in cls_to.attrs or "PRIVATE" in cls_to.attrs: return False package_from = cls_from.name.split(".")[:-1] package_to = cls_to.name.split(".")[:-1] return package_from == package_to
[docs] def get_super_classes(self, cls): if "INTERFACE" in cls.attrs: raise SootClassHierarchyError("This is an Interface") super_classes = [] current = cls try: while True: current = self.project.loader.main_object.classes[current.super_class] super_classes.append(current) except KeyError: return super_classes
[docs] def get_super_classes_including(self, cls): super_classes = self.get_super_classes(cls) res = [] res.append(cls) res.extend(super_classes) return res
[docs] def get_implementers(self, interface): if "INTERFACE" not in interface.attrs: raise SootClassHierarchyError("This is not an interface") res_set = set() for i in self.get_sub_interfaces_including(interface): res_set |= set(self.interface_implementers[i]) return list(res_set)
[docs] def get_sub_interfaces_including(self, interface): res = self.get_sub_interfaces(interface) res.append(interface) return res
[docs] def get_sub_interfaces(self, interface): if "INTERFACE" not in interface.attrs: raise SootClassHierarchyError("This is not an interface") if interface in self.sub_interfaces: return self.sub_interfaces[interface] # Otherwise res = [] for i in self.dir_sub_interfaces[interface]: res.extend(self.get_sub_interfaces_including(i)) self.sub_interfaces[interface] = res return res
[docs] def get_sub_classes(self, cls): if "INTERFACE" in cls.attrs: raise SootClassHierarchyError("This is an Interface. Class needed") if cls in self.sub_classes: return self.sub_classes[cls] res = [] for c in self.dir_sub_classes[cls]: res.extend(self.get_sub_classes_including(c)) self.sub_classes[cls] = res return res
[docs] def get_sub_classes_including(self, cls): if "INTERFACE" in cls.attrs: raise SootClassHierarchyError("This is an Interface. Class needed") res = [] res.extend(self.get_sub_classes(cls)) res.append(cls) return res
[docs] def resolve_abstract_dispatch(self, cls, method): if "INTERFACE" in cls.attrs: classes_set = set() for i in self.get_implementers(cls): classes_set |= set(self.get_sub_classes_including(i)) classes = list(classes_set) else: classes = self.get_sub_classes_including(cls) res_set = set() for c in classes: if "ABSTRACT" not in c.attrs: res_set.add(self.resolve_concrete_dispatch(c, method)) return list(res_set)
[docs] def resolve_concrete_dispatch(self, cls, method): if "INTERFACE" in cls.attrs: raise SootClassHierarchyError("class needed!") for c in self.get_super_classes_including(cls): for m in c.methods: if m.name == method.name and m.params == method.params: if self.is_visible_method(c, method): return m raise NoConcreteDispatch("Could not resolve concrete dispatch!")
[docs] def resolve_special_dispatch(self, method, container): # container is the method that contains the invoke method_cls = self.project.loader.main_object.classes[method.class_name] container_cls = self.project.loader.main_object.classes[container.class_name] if method.name == "<init>" or "PRIVATE" in method.attrs: return method elif self.is_subclass(method_cls, container_cls): return self.resolve_concrete_dispatch(container_cls, method) else: return method
[docs] def resolve_invoke(self, invoke_expr, method, container): # Generic method to resolve invoke # Given an invoke expression it figures out which "technique" should be applied invoke_type = str(type(invoke_expr)) cls = self.project.loader.main_object.classes[method.class_name] if "VirtualInvokeExpr" in invoke_type: targets = self.resolve_abstract_dispatch(cls, method) elif "DynamicInvokeExpr" in invoke_type: targets = self.resolve_abstract_dispatch(cls, method) elif "InterfaceInvokeExpr" in invoke_type: targets = self.resolve_abstract_dispatch(cls, method) elif "SpecialInvokeExpr" in invoke_type: t = self.resolve_special_dispatch(method, container) targets = [t] elif "StaticInvokeExpr" in invoke_type: targets = [method] return targets
from angr.analyses import AnalysesHub AnalysesHub.register_default("SootClassHierarchy", SootClassHierarchy)