from typing import TYPE_CHECKING, List, Set, Optional, Tuple
import logging
from cle import Symbol
l = logging.getLogger(__name__)
if TYPE_CHECKING:
from angr.code_location import CodeLocation
from angr.analyses.reaching_definitions.dep_graph import DepGraph
from angr.analyses.reaching_definitions.rd_state import ReachingDefinitionsState
# pylint: disable=unused-argument, no-self-use
[docs]class FunctionHandler:
"""
An abstract base class for function handlers.
To work properly, we expect function handlers to:
- Be related to a <ReachingDefinitionsAnalysis>;
- Provide a `handle_local_function` method.
"""
[docs] def hook(self, analysis) -> "FunctionHandler":
"""
A <FunctionHandler> needs information about the context in which it is executed.
A <ReachingDefinitionsAnalysis> would "hook" into a handler by calling: `<FunctionHandler>.hook(self)`.
:param angr.analyses.ReachingDefinitionsAnalysis analysis: A RDA using this <FunctionHandler>.
:return FunctionHandler:
"""
return self
[docs] def handle_local_function(
self,
state: "ReachingDefinitionsState",
function_address: int,
call_stack: Optional[List],
maximum_local_call_depth: int,
visited_blocks: Set[int],
dep_graph: "DepGraph",
src_ins_addr: Optional[int] = None,
codeloc: Optional["CodeLocation"] = None,
) -> Tuple[bool, "ReachingDefinitionsState", "Set[int]", "DepGraph"]:
"""
:param state: The state at the entry of the function, i.e. the function's input state.
:param function_address: The address of the function to handle.
:param call_stack:
:param maximum_local_call_depth:
:param visited_blocks: A set of the addresses of the previously visited blocks.
:param dep_graph: A definition-use graph, where nodes represent definitions, and edges represent uses.
:param codeloc: The code location of the call to the analysed function.
"""
l.warning("Please implement the local function handler with your own logic.")
return False, state, visited_blocks, dep_graph
[docs] def handle_unknown_call(
self, state: "ReachingDefinitionsState", src_codeloc: Optional["CodeLocation"] = None
) -> Tuple[bool, "ReachingDefinitionsState"]:
"""
Called when the RDA encounters a function call to somewhere really weird.
E.g. the function address was invalid (not even TOP),
or the address of the function is outside of the main object, but also not a known symbol
:param state:
:param src_codeloc:
:return:
"""
l.error("Encountered unknown call. Implement the unknown function handler with your own logic.")
return False, state
[docs] def handle_indirect_call(
self, state: "ReachingDefinitionsState", src_codeloc: Optional["CodeLocation"] = None
) -> Tuple[bool, "ReachingDefinitionsState"]:
"""
The RDA encountered a function call with multiple possible values, or TOP as a target
:param state:
:param src_codeloc:
:return:
"""
l.warning("Please implement the indirect function handler with your own logic.")
return False, state
[docs] def handle_external_function_fallback(
self, state: "ReachingDefinitionsState", src_codeloc: Optional["CodeLocation"] = None
) -> Tuple[bool, "ReachingDefinitionsState"]:
"""
Fallback for a call to an external function, that has no specific implementation
:param state:
:param src_codeloc:
:return:
"""
return False, state
[docs] def handle_external_function_symbol(
self,
state: "ReachingDefinitionsState",
symbol: Symbol,
src_codeloc: Optional["CodeLocation"] = None,
) -> Tuple[bool, "ReachingDefinitionsState"]:
"""
The generic handler for external functions with a known symbol
This is different from
The default behavior using hasattr/getattr supports existing code,
but you can also implement the check if the external function is supported in another way,
e.g. similar to SimProcedures
:param state:
:param symbol:
:param src_codeloc:
:return:
"""
if symbol.name:
return self.handle_external_function_name(state, symbol.name, src_codeloc)
else:
l.warning("Symbol %s for external function has no name, falling back to generic handler", symbol)
return self.handle_external_function_fallback(state, src_codeloc)
[docs] def handle_external_function_name(
self,
state: "ReachingDefinitionsState",
ext_func_name: str,
src_codeloc: Optional["CodeLocation"] = None,
) -> Tuple[bool, "ReachingDefinitionsState"]:
handler_name = "handle_%s" % ext_func_name
if ext_func_name and hasattr(self, handler_name):
return getattr(self, handler_name)(state, src_codeloc)
else:
l.warning("No handler for external function %s(), falling back to generic handler", ext_func_name)
return self.handle_external_function_fallback(state, src_codeloc)