[docs]classExplorer(ExplorationTechnique):""" Search for up to "num_find" paths that satisfy condition "find", avoiding condition "avoid". Stashes found paths into "find_stash' and avoided paths into "avoid_stash". The "find" and "avoid" parameters may be any of: - An address to find - A set or list of addresses to find - A function that takes a path and returns whether or not it matches. If an angr CFG is passed in as the "cfg" parameter and "find" is either a number or a list or a set, then any paths which cannot possibly reach a success state without going through a failure state will be preemptively avoided. If either the "find" or "avoid" parameter is a function returning a boolean, and a path triggers both conditions, it will be added to the find stash, unless "avoid_priority" is set to True. """
[docs]def__init__(self,find=None,avoid=None,find_stash="found",avoid_stash="avoid",cfg=None,num_find=1,avoid_priority=False):super().__init__()self.find,static_find=condition_to_lambda(find)self.avoid,static_avoid=condition_to_lambda(avoid)self.find_stash=find_stashself.avoid_stash=avoid_stashself.cfg=cfgself.ok_blocks=set()self.num_find=num_findself.avoid_priority=avoid_priority# even if avoid or find addresses are not statically known, stop on those that we do knowself._extra_stop_points=(static_findorset())|(static_avoidorset())self._unknown_stop_points=static_findisNoneorstatic_avoidisNoneself._warned_unicorn=False# TODO: This is a hack for while CFGFast doesn't handle procedure continuationsfrom..importanalyses# pylint: disable=import-outside-toplevelifisinstance(cfg,analyses.CFGFast):l.error("CFGFast is currently inappropriate for use with Explorer.")l.error("Usage of the CFG has been disabled for this explorer.")self.cfg=Noneifself.cfgisnotNone:avoid=static_avoidorset()# we need the find addresses to be determined staticallyifnotstatic_find:l.error("You must provide at least one numeric 'find' address if you provide a CFG.")l.error("Usage of the CFG has been disabled for this explorer.")self.cfg=Nonereturnforainavoid:ifcfg.model.get_any_node(a)isNone:l.warning("'Avoid' address %#x not present in CFG...",a)# not a queue but a stack... it's just a worklist!queue=[]forfinstatic_find:nodes=cfg.model.get_all_nodes(f)iflen(nodes)==0:l.warning("'Find' address %#x not present in CFG...",f)else:queue.extend(nodes)seen_nodes=set()whilelen(queue)>0:n=queue.pop()ifid(n)inseen_nodes:continueifn.addrinavoid:continueself.ok_blocks.add(n.addr)seen_nodes.add(id(n))queue.extend(n.predecessors)iflen(self.ok_blocks)==0:l.error("No addresses could be validated by the provided CFG!")l.error("Usage of the CFG has been disabled for this explorer.")self.cfg=Nonereturnl.warning("Please be sure that the CFG you have passed in is complete.")l.warning("Providing an incomplete CFG can cause viable paths to be discarded!")
def_filter_inner(self,state):ifself._unknown_stop_pointsandsim_options.UNICORNinstate.optionsandnotself._warned_unicorn:l.warning("Using unicorn with find/avoid conditions that are a lambda (not a number, set, tuple or list)")l.warning("Unicorn may step over states that match the condition (find or avoid) without stopping.")self._warned_unicorn=Truetry:ifself.avoid_priority:avoidable=self.avoid(state)ifavoidableand(avoidableisTrueorstate.addrinavoidable):returnself.avoid_stashfindable=self.find(state)iffindableand(findableisTrueorstate.addrinfindable):returnself.find_stashifnotself.avoid_priority:avoidable=self.avoid(state)ifavoidableand(avoidableisTrueorstate.addrinavoidable):returnself.avoid_stashexceptclaripy.errors.ClaripySolverInterruptErrorase:resource_event(state,e)return"interrupted"ifself.cfgisnotNoneandself.cfg.model.get_any_node(state.addr)isnotNone:ifstate.addrnotinself.ok_blocks:returnself.avoid_stashreturnNone