angr 9.2.65__py3-none-win_amd64.whl → 9.2.66__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (45) hide show
  1. angr/__init__.py +55 -2
  2. angr/analyses/calling_convention.py +4 -3
  3. angr/analyses/cfg/cfg_base.py +2 -2
  4. angr/analyses/cfg/cfg_fast.py +128 -60
  5. angr/analyses/decompiler/ail_simplifier.py +1 -2
  6. angr/analyses/decompiler/block_simplifier.py +4 -3
  7. angr/analyses/decompiler/callsite_maker.py +1 -1
  8. angr/analyses/decompiler/condition_processor.py +5 -3
  9. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +51 -8
  10. angr/analyses/decompiler/peephole_optimizations/__init__.py +1 -1
  11. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +92 -0
  12. angr/analyses/decompiler/structured_codegen/c.py +59 -6
  13. angr/analyses/decompiler/utils.py +1 -1
  14. angr/analyses/find_objects_static.py +4 -4
  15. angr/analyses/propagator/engine_ail.py +2 -1
  16. angr/analyses/reaching_definitions/__init__.py +1 -3
  17. angr/analyses/reaching_definitions/dep_graph.py +33 -4
  18. angr/analyses/reaching_definitions/engine_ail.py +5 -6
  19. angr/analyses/reaching_definitions/engine_vex.py +6 -7
  20. angr/analyses/reaching_definitions/external_codeloc.py +0 -27
  21. angr/analyses/reaching_definitions/function_handler.py +145 -23
  22. angr/analyses/reaching_definitions/rd_initializer.py +221 -0
  23. angr/analyses/reaching_definitions/rd_state.py +95 -153
  24. angr/analyses/reaching_definitions/reaching_definitions.py +15 -3
  25. angr/calling_conventions.py +2 -2
  26. angr/code_location.py +24 -0
  27. angr/exploration_techniques/__init__.py +28 -0
  28. angr/knowledge_plugins/cfg/cfg_model.py +1 -1
  29. angr/knowledge_plugins/key_definitions/__init__.py +12 -1
  30. angr/knowledge_plugins/key_definitions/atoms.py +9 -0
  31. angr/knowledge_plugins/key_definitions/definition.py +13 -18
  32. angr/knowledge_plugins/key_definitions/live_definitions.py +350 -106
  33. angr/lib/angr_native.dll +0 -0
  34. angr/project.py +1 -1
  35. angr/sim_manager.py +15 -0
  36. angr/sim_state.py +3 -3
  37. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +56 -8
  38. angr/storage/memory_object.py +3 -1
  39. angr/utils/typing.py +16 -0
  40. {angr-9.2.65.dist-info → angr-9.2.66.dist-info}/METADATA +7 -7
  41. {angr-9.2.65.dist-info → angr-9.2.66.dist-info}/RECORD +44 -42
  42. angr/analyses/decompiler/peephole_optimizations/conv_const_mull_a_shift.py +0 -75
  43. {angr-9.2.65.dist-info → angr-9.2.66.dist-info}/LICENSE +0 -0
  44. {angr-9.2.65.dist-info → angr-9.2.66.dist-info}/WHEEL +0 -0
  45. {angr-9.2.65.dist-info → angr-9.2.66.dist-info}/top_level.txt +0 -0
@@ -892,7 +892,10 @@ class CIfElse(CStatement):
892
892
  omit_braces = (
893
893
  self.cstyle_ifs
894
894
  and first_node
895
+ and len(self.condition_and_nodes) == 1
896
+ # no else-if tree can exist
895
897
  and self._is_single_stmt_node(node)
898
+ # no else, else is also single-stmt, or else will not exist after pass
896
899
  and (self.else_node is None or self._is_single_stmt_node(self.else_node) or self.simplify_else_scope)
897
900
  )
898
901
 
@@ -1979,6 +1982,14 @@ class CConstant(CExpression):
1979
1982
  def fmt_neg(self, v):
1980
1983
  self._fmt_setter["neg"] = v
1981
1984
 
1985
+ @property
1986
+ def fmt_char(self):
1987
+ return self.fmt.get("char", False)
1988
+
1989
+ @fmt_char.setter
1990
+ def fmt_char(self, v: bool):
1991
+ self._fmt_setter["char"] = v
1992
+
1982
1993
  @property
1983
1994
  def type(self):
1984
1995
  return self._type
@@ -2034,10 +2045,18 @@ class CConstant(CExpression):
2034
2045
  elif value < 0:
2035
2046
  value = value + 2**self._type.size
2036
2047
 
2037
- if self.fmt_hex:
2038
- str_value = hex(value)
2039
- else:
2040
- str_value = str(value)
2048
+ str_value = None
2049
+ if self.fmt_char:
2050
+ try:
2051
+ str_value = f"'{chr(value)}'"
2052
+ except ValueError:
2053
+ str_value = None
2054
+
2055
+ if str_value is None:
2056
+ if self.fmt_hex:
2057
+ str_value = hex(value)
2058
+ else:
2059
+ str_value = str(value)
2041
2060
 
2042
2061
  yield str_value, self
2043
2062
  else:
@@ -3512,15 +3531,49 @@ class FieldReferenceCleanup(CStructuredCodeWalker):
3512
3531
 
3513
3532
 
3514
3533
  class PointerArithmeticFixer(CStructuredCodeWalker):
3534
+ """
3535
+ Before calling this fixer class, pointer arithmetics are purely integer-based and ignoring the pointer type.
3536
+
3537
+ For example, in the following case:
3538
+
3539
+ struct A* a_ptr; // assume struct A is 24 bytes in size
3540
+ a_ptr = a_ptr + 24;
3541
+
3542
+ It means adding 24 to the address of a_ptr, without considering the size of struct A. This fixer class will make
3543
+ pointer arithmetics aware of the pointer type. In this case, the fixer class will convert the code to
3544
+ a_ptr = a_ptr + 1.
3545
+ """
3546
+
3515
3547
  @classmethod
3516
3548
  def handle_CBinaryOp(cls, obj):
3517
- obj = super().handle_CBinaryOp(obj)
3549
+ obj: CBinaryOp = super().handle_CBinaryOp(obj)
3518
3550
  if (
3519
3551
  obj.op in ("Add", "Sub")
3520
3552
  and isinstance(obj.type, SimTypePointer)
3521
3553
  and not isinstance(obj.type.pts_to, SimTypeBottom)
3522
3554
  ):
3523
- obj = obj.codegen._access_reference(obj, obj.type.pts_to)
3555
+ out = obj.codegen._access_reference(obj, obj.type.pts_to)
3556
+ if (
3557
+ isinstance(out, CUnaryOp)
3558
+ and out.op == "Reference"
3559
+ and isinstance(out.operand, CIndexedVariable)
3560
+ and isinstance(out.operand.index, CConstant)
3561
+ ):
3562
+ # rewrite &a[1] to a + 1
3563
+ const = out.operand.index
3564
+ if isinstance(const.value, int) and const.value < 0:
3565
+ op = "Sub"
3566
+ const = CConstant(
3567
+ -const.value,
3568
+ const.type,
3569
+ reference_values=const.reference_values,
3570
+ tags=const.tags,
3571
+ codegen=const.codegen,
3572
+ )
3573
+ else:
3574
+ op = "Add"
3575
+ return CBinaryOp(op, out.operand.variable, const, out.operand.tags, codegen=out.codegen)
3576
+ return out
3524
3577
  return obj
3525
3578
 
3526
3579
 
@@ -368,7 +368,7 @@ def structured_node_is_simple_return(node: Union["SequenceNode", "MultiNode"], g
368
368
  }
369
369
  ...
370
370
 
371
- Returns tue on any block ending in linear statements and a return.
371
+ Returns true on any block ending in linear statements and a return.
372
372
  """
373
373
 
374
374
  def _flatten_structured_node(packed_node: Union["SequenceNode", "MultiNode"]) -> List[ailment.Block]:
@@ -74,7 +74,7 @@ class NewFunctionHandler(FunctionHandler):
74
74
  else:
75
75
  size_arg_reg_offset = self.project.arch.registers["rdi"][0]
76
76
  size_arg_reg_size = word_size
77
- v0 = state.register_definitions.load(size_arg_reg_offset, size_arg_reg_size).one_value()
77
+ v0 = state.registers.load(size_arg_reg_offset, size_arg_reg_size).one_value()
78
78
  size = v0._model_concrete.value if v0 is not None and v0.concrete else None
79
79
 
80
80
  if size is not None:
@@ -108,7 +108,7 @@ class NewFunctionHandler(FunctionHandler):
108
108
  # check if rdi has a possible this pointer/ object address, if so then we can assign this object this class
109
109
  # also if the func is a constructor(not stripped binaries)
110
110
  for addr, possible_object in self.possible_objects_dict.items():
111
- v1 = state.register_definitions.load(72, state.arch.bits // state.arch.byte_width).one_value()
111
+ v1 = state.registers.load(72, state.arch.bits // state.arch.byte_width).one_value()
112
112
  obj_addr = v1._model_concrete.value if v1 is not None and v1.concrete else None
113
113
  if obj_addr is not None and addr == obj_addr:
114
114
  col_ind = self.project.kb.functions[function_address].demangled_name.rfind("::")
@@ -172,7 +172,7 @@ class StaticObjectFinder(Analysis):
172
172
  else:
173
173
  ret_val_reg_offset = self.project.arch.registers["rax"][0]
174
174
  ret_val_reg_size = word_size
175
- v0 = rd_before_node.register_definitions.load(ret_val_reg_offset, ret_val_reg_size).one_value()
175
+ v0 = rd_before_node.registers.load(ret_val_reg_offset, ret_val_reg_size).one_value()
176
176
  addr_of_new_obj = v0._model_concrete.value if v0 is not None and v0.concrete else None
177
177
 
178
178
  # we need the state right before the call
@@ -188,7 +188,7 @@ class StaticObjectFinder(Analysis):
188
188
  else:
189
189
  this_ptr_reg_offset = self.project.arch.registers["rdi"][0]
190
190
  this_ptr_reg_size = word_size
191
- v1 = rd_after_node.register_definitions.load(this_ptr_reg_offset, this_ptr_reg_size).one_value()
191
+ v1 = rd_after_node.registers.load(this_ptr_reg_offset, this_ptr_reg_size).one_value()
192
192
  addr_in_rdi = v1._model_concrete.value if v1 is not None and v1.concrete else None
193
193
 
194
194
  if addr_of_new_obj is not None and addr_of_new_obj == addr_in_rdi:
@@ -6,8 +6,9 @@ import claripy
6
6
  from ailment import Stmt, Expr
7
7
 
8
8
  from angr.knowledge_plugins.propagations.prop_value import PropValue, Detail
9
- from angr.analyses.reaching_definitions.external_codeloc import ExternalCodeLocation
10
9
  from angr.knowledge_plugins.key_definitions.atoms import Register
10
+
11
+ from angr.code_location import ExternalCodeLocation
11
12
  from ...utils.constants import is_alignment_mask
12
13
  from ...engines.light import SimEngineLightAILMixin
13
14
  from ...sim_variable import SimStackVariable, SimMemoryVariable
@@ -15,7 +15,6 @@ from ...knowledge_plugins.key_definitions.definition import Definition
15
15
  from .. import register_analysis
16
16
  from .reaching_definitions import ReachingDefinitionsAnalysis, ReachingDefinitionsModel
17
17
  from .function_handler import FunctionHandler, FunctionCallData
18
- from .external_codeloc import ExternalCodeLocation
19
18
  from .rd_state import ReachingDefinitionsState
20
19
 
21
20
  if TYPE_CHECKING:
@@ -40,7 +39,6 @@ __all__ = (
40
39
  "FunctionHandler",
41
40
  "FunctionCallData",
42
41
  "get_all_definitions",
43
- "ExternalCodeLocation",
44
42
  )
45
43
 
46
44
 
@@ -55,7 +53,7 @@ def get_all_definitions(region: "MultiValuedMemory") -> Set["Definition"]:
55
53
  cnt_set: Optional[Union["SimMemoryObject", Set["SimMemoryObject"]]] = page.content[idx]
56
54
  if cnt_set is None:
57
55
  continue
58
- elif type(cnt_set) is not set:
56
+ if type(cnt_set) is not set:
59
57
  cnt_set = {cnt_set}
60
58
  for cnt in cnt_set:
61
59
  for def_ in LiveDefinitions.extract_defs(cnt.object):
@@ -1,4 +1,18 @@
1
- from typing import Optional, Dict, Set, Iterable, Union, List, TYPE_CHECKING, Tuple, overload, Literal, Any, Iterator
1
+ from typing import (
2
+ Optional,
3
+ Dict,
4
+ Set,
5
+ Iterable,
6
+ Type,
7
+ Union,
8
+ List,
9
+ TYPE_CHECKING,
10
+ Tuple,
11
+ overload,
12
+ Literal,
13
+ Any,
14
+ Iterator,
15
+ )
2
16
  from dataclasses import dataclass
3
17
 
4
18
  import networkx
@@ -6,7 +20,7 @@ import networkx
6
20
  import claripy
7
21
  from cle.loader import Loader
8
22
 
9
- from ...code_location import CodeLocation
23
+ from ...code_location import CodeLocation, ExternalCodeLocation
10
24
  from ...knowledge_plugins.key_definitions.atoms import (
11
25
  Atom,
12
26
  MemoryLocation,
@@ -16,10 +30,9 @@ from ...knowledge_plugins.key_definitions.atoms import (
16
30
  ConstantSrc,
17
31
  GuardUse,
18
32
  )
19
- from ...knowledge_plugins.key_definitions.definition import Definition, DefinitionMatchPredicate
33
+ from ...knowledge_plugins.key_definitions.definition import A, Definition, DefinitionMatchPredicate
20
34
  from ...knowledge_plugins.key_definitions.undefined import UNDEFINED
21
35
  from ...knowledge_plugins.cfg import CFGModel
22
- from .external_codeloc import ExternalCodeLocation
23
36
 
24
37
  if TYPE_CHECKING:
25
38
  pass
@@ -220,6 +233,16 @@ class DepGraph:
220
233
  result.append(defn)
221
234
  return result
222
235
 
236
+ @overload
237
+ def find_all_predecessors(
238
+ self,
239
+ starts: Union[Definition[Atom], Iterable[Definition[Atom]]],
240
+ *,
241
+ kind: Type[A],
242
+ **kwargs: Any,
243
+ ) -> List[Definition[A]]:
244
+ ...
245
+
223
246
  @overload
224
247
  def find_all_predecessors(
225
248
  self,
@@ -292,6 +315,12 @@ class DepGraph:
292
315
  ) -> List[Definition[ConstantSrc]]:
293
316
  ...
294
317
 
318
+ @overload
319
+ def find_all_predecessors(
320
+ self, starts: Union[Definition[Atom], Iterable[Definition[Atom]]], **kwargs: Any
321
+ ) -> List[Definition[Atom]]:
322
+ ...
323
+
295
324
  def find_all_predecessors(self, starts, **kwargs):
296
325
  """
297
326
  Filter the ancestors of the given start node or nodes that match various criteria.
@@ -16,9 +16,8 @@ from ...storage.memory_mixins.paged_memory.pages.multi_values import MultiValues
16
16
  from ...knowledge_plugins.key_definitions.atoms import Atom, Register, Tmp, MemoryLocation
17
17
  from ...knowledge_plugins.key_definitions.constants import OP_BEFORE, OP_AFTER
18
18
  from ...knowledge_plugins.key_definitions.live_definitions import Definition, LiveDefinitions
19
- from ...code_location import CodeLocation
19
+ from ...code_location import CodeLocation, ExternalCodeLocation
20
20
  from .subject import SubjectType
21
- from .external_codeloc import ExternalCodeLocation
22
21
  from .rd_state import ReachingDefinitionsState
23
22
  from .function_handler import FunctionHandler, FunctionCallData
24
23
 
@@ -415,7 +414,7 @@ class SimEngineRDAIL(
415
414
 
416
415
  # first check if it is ever defined
417
416
  try:
418
- value: MultiValues = self.state.register_definitions.load(reg_offset, size=size)
417
+ value: MultiValues = self.state.registers.load(reg_offset, size=size)
419
418
  except SimMemoryMissingError as ex:
420
419
  # the full value does not exist, but we handle partial existence, too
421
420
  missing_defs = None
@@ -424,7 +423,7 @@ class SimEngineRDAIL(
424
423
  i = 0
425
424
  while i < size:
426
425
  try:
427
- value: MultiValues = self.state.register_definitions.load(reg_offset + i, size=1)
426
+ value: MultiValues = self.state.registers.load(reg_offset + i, size=1)
428
427
  except SimMemoryMissingError as ex_:
429
428
  i += ex_.missing_size
430
429
  continue
@@ -498,7 +497,7 @@ class SimEngineRDAIL(
498
497
  # a concrete address
499
498
  concrete_addr: int = addr._model_concrete.value
500
499
  try:
501
- vs: MultiValues = self.state.memory_definitions.load(concrete_addr, size=size, endness=expr.endness)
500
+ vs: MultiValues = self.state.memory.load(concrete_addr, size=size, endness=expr.endness)
502
501
  defs = set(LiveDefinitions.extract_defs_from_mv(vs))
503
502
  except SimMemoryMissingError:
504
503
  continue
@@ -510,7 +509,7 @@ class SimEngineRDAIL(
510
509
  if stack_offset is not None:
511
510
  stack_addr = self.state.live_definitions.stack_offset_to_stack_addr(stack_offset)
512
511
  try:
513
- vs: MultiValues = self.state.stack_definitions.load(stack_addr, size=size, endness=expr.endness)
512
+ vs: MultiValues = self.state.stack.load(stack_addr, size=size, endness=expr.endness)
514
513
  defs = set(LiveDefinitions.extract_defs_from_mv(vs))
515
514
  except SimMemoryMissingError:
516
515
  continue
@@ -16,9 +16,8 @@ from ...knowledge_plugins.key_definitions.tag import LocalVariableTag, Parameter
16
16
  from ...knowledge_plugins.key_definitions.atoms import Atom, Register, MemoryLocation, Tmp
17
17
  from ...knowledge_plugins.key_definitions.constants import OP_BEFORE, OP_AFTER
18
18
  from ...knowledge_plugins.key_definitions.heap_address import HeapAddress
19
- from ...code_location import CodeLocation
19
+ from ...code_location import CodeLocation, ExternalCodeLocation
20
20
  from .rd_state import ReachingDefinitionsState
21
- from .external_codeloc import ExternalCodeLocation
22
21
  from .function_handler import FunctionCallData
23
22
 
24
23
  if TYPE_CHECKING:
@@ -350,7 +349,7 @@ class SimEngineRDVEX(
350
349
 
351
350
  reg_atom = Register(reg_offset, size, self.arch)
352
351
  try:
353
- values: MultiValues = self.state.register_definitions.load(reg_offset, size=size)
352
+ values: MultiValues = self.state.registers.load(reg_offset, size=size)
354
353
  except SimMemoryMissingError:
355
354
  top = self.state.top(size * self.arch.byte_width)
356
355
  # annotate it
@@ -416,7 +415,7 @@ class SimEngineRDVEX(
416
415
  loaded_stack_offsets.add(stack_offset)
417
416
  stack_addr = self.state.live_definitions.stack_offset_to_stack_addr(stack_offset)
418
417
  try:
419
- vs: MultiValues = self.state.stack_definitions.load(stack_addr, size=size, endness=endness)
418
+ vs: MultiValues = self.state.stack.load(stack_addr, size=size, endness=endness)
420
419
  # extract definitions
421
420
  defs = set(LiveDefinitions.extract_defs_from_mv(vs))
422
421
  except SimMemoryMissingError:
@@ -429,7 +428,7 @@ class SimEngineRDVEX(
429
428
  # Load data from the heap
430
429
  heap_offset = self.state.get_heap_offset(addr)
431
430
  try:
432
- vs: MultiValues = self.state.heap_definitions.load(heap_offset, size=size, endness=endness)
431
+ vs: MultiValues = self.state.heap.load(heap_offset, size=size, endness=endness)
433
432
  defs = set(LiveDefinitions.extract_defs_from_mv(vs))
434
433
  except SimMemoryMissingError:
435
434
  continue
@@ -442,7 +441,7 @@ class SimEngineRDVEX(
442
441
 
443
442
  # Load data from a global region
444
443
  try:
445
- vs: MultiValues = self.state.memory_definitions.load(addr_v, size=size, endness=endness)
444
+ vs: MultiValues = self.state.memory.load(addr_v, size=size, endness=endness)
446
445
  defs = set(LiveDefinitions.extract_defs_from_mv(vs))
447
446
  except SimMemoryMissingError:
448
447
  try:
@@ -457,7 +456,7 @@ class SimEngineRDVEX(
457
456
  v = self.state.annotate_with_def(claripy.BVV(val, size * self.arch.byte_width), missing_def)
458
457
  vs = MultiValues(v)
459
458
  if not section or section.is_writable:
460
- self.state.memory_definitions.store(addr_v, vs, size=size, endness=endness)
459
+ self.state.memory.store(addr_v, vs, size=size, endness=endness)
461
460
  self.state.all_definitions.add(missing_def)
462
461
  defs = {missing_def}
463
462
  except KeyError:
@@ -1,27 +0,0 @@
1
- from typing import Tuple
2
-
3
- from ...code_location import CodeLocation
4
-
5
-
6
- class ExternalCodeLocation(CodeLocation):
7
- """
8
- Stands for a program point that originates from outside an analysis' scope.
9
- i.e. a value loaded from rdi in a callee where the caller has not been analyzed.
10
- """
11
-
12
- __slots__ = ("call_string",)
13
-
14
- def __init__(self, call_string: Tuple[int, ...] = None):
15
- super().__init__(0, None)
16
- self.call_string = call_string if call_string is not None else ()
17
-
18
- def __repr__(self):
19
- return f"[External {[hex(x) if isinstance(x, int) else x for x in self.call_string]}]"
20
-
21
- def __hash__(self):
22
- """
23
- returns the hash value of self.
24
- """
25
- if self._hash is None:
26
- self._hash = hash((self.call_string,))
27
- return self._hash
@@ -1,8 +1,10 @@
1
- from typing import TYPE_CHECKING, List, Set, Optional, Union
1
+ from typing import TYPE_CHECKING, Iterable, List, Set, Optional, Union, Callable
2
2
  from dataclasses import dataclass, field
3
3
  import logging
4
+ from functools import wraps
4
5
  from cle import Symbol
5
6
  from cle.backends import ELF
7
+ import claripy
6
8
 
7
9
  from angr.storage.memory_mixins.paged_memory.pages.multi_values import MultiValues
8
10
  from angr.sim_type import SimTypeBottom
@@ -13,15 +15,35 @@ from angr.sim_type import SimTypeFunction
13
15
  from angr.knowledge_plugins.key_definitions.definition import Definition
14
16
  from angr.knowledge_plugins.functions import Function
15
17
  from angr.analyses.reaching_definitions.dep_graph import FunctionCallRelationships
16
- from angr.code_location import CodeLocation
18
+ from angr.code_location import CodeLocation, ExternalCodeLocation
19
+ from angr.knowledge_plugins.key_definitions.constants import ObservationPointType
20
+
17
21
 
18
22
  if TYPE_CHECKING:
23
+ from angr.knowledge_plugins.key_definitions.rd_model import ReachingDefinitionsModel
19
24
  from angr.analyses.reaching_definitions.rd_state import ReachingDefinitionsState
20
25
  from angr.analyses.reaching_definitions.reaching_definitions import ReachingDefinitionsAnalysis
21
26
 
22
27
  l = logging.getLogger(__name__)
23
28
 
24
29
 
30
+ def get_exit_livedefinitions(func: Function, rda_model: "ReachingDefinitionsModel"):
31
+ """
32
+ Get LiveDefinitions at all exits of a function, merge them, and return.
33
+ """
34
+ lds = []
35
+ for block in func.endpoints:
36
+ ld = rda_model.get_observation_by_node(block.addr, ObservationPointType.OP_AFTER)
37
+ if ld is None:
38
+ continue
39
+ lds.append(ld)
40
+ if len(lds) == 1:
41
+ return lds[0]
42
+ if len(lds) == 0:
43
+ return None
44
+ return lds[0].merge(*lds[1:])[0]
45
+
46
+
25
47
  @dataclass
26
48
  class FunctionEffect:
27
49
  """
@@ -113,9 +135,9 @@ class FunctionCallData:
113
135
 
114
136
  def depends(
115
137
  self,
116
- dest: Optional[Atom],
117
- *sources: Atom,
118
- value: Optional[MultiValues] = None,
138
+ dest: Union[Atom, Iterable[Atom], None],
139
+ *sources: Union[Atom, Iterable[Atom]],
140
+ value: Union[MultiValues, claripy.ast.BV, bytes, int, None] = None,
119
141
  apply_at_callsite: bool = False,
120
142
  tags: Optional[Set[Tag]] = None,
121
143
  ):
@@ -129,6 +151,22 @@ class FunctionCallData:
129
151
 
130
152
  The atom being modified may be None to mark uses of the source atoms which do not have any explicit sinks.
131
153
  """
154
+ if dest is None and value is not None:
155
+ raise TypeError("Cannot provide value without a destination to write it to")
156
+
157
+ if dest is not None and not isinstance(dest, Atom):
158
+ for dest2 in dest:
159
+ self.depends(dest2, *sources, value=value, apply_at_callsite=apply_at_callsite, tags=tags)
160
+ return
161
+
162
+ if isinstance(value, int):
163
+ assert dest is not None
164
+ value = claripy.BVV(value, dest.size * 8)
165
+ elif isinstance(value, bytes):
166
+ value = claripy.BVV(value)
167
+ if isinstance(value, claripy.ast.BV):
168
+ value = MultiValues(value)
169
+ assert value is None or isinstance(value, MultiValues)
132
170
  if dest is not None and self.has_clobbered(dest):
133
171
  l.warning(
134
172
  "Function handler for %s seems to be implemented incorrectly - "
@@ -139,13 +177,76 @@ class FunctionCallData:
139
177
  self.effects.append(
140
178
  FunctionEffect(
141
179
  dest,
142
- set(sources),
180
+ set().union(*({src} if isinstance(src, Atom) else set(src) for src in sources)),
143
181
  value=value,
144
182
  apply_at_callsite=apply_at_callsite,
145
183
  tags=tags,
146
184
  )
147
185
  )
148
186
 
187
+ def reset_prototype(self, prototype: SimTypeFunction, state: "ReachingDefinitionsState") -> Set[Atom]:
188
+ self.prototype = prototype.with_arch(state.arch)
189
+
190
+ args_atoms_from_values = set()
191
+ if self.args_atoms is None and self.args_values is not None:
192
+ self.args_atoms = [
193
+ set().union(
194
+ *({defn.atom for defn in state.extract_defs(value)} for values in mv.values() for value in values)
195
+ )
196
+ for mv in self.args_values
197
+ ]
198
+ for atoms_set in self.args_atoms:
199
+ args_atoms_from_values |= atoms_set
200
+ elif self.args_atoms is None and self.cc is not None and self.prototype is not None:
201
+ self.args_atoms = FunctionHandler.c_args_as_atoms(state, self.cc, self.prototype)
202
+ if self.ret_atoms is None and self.cc is not None and self.prototype is not None:
203
+ if self.prototype.returnty is not None:
204
+ self.ret_atoms = FunctionHandler.c_return_as_atoms(state, self.cc, self.prototype)
205
+ return args_atoms_from_values
206
+
207
+
208
+ class FunctionCallDataUnwrapped(FunctionCallData):
209
+ """
210
+ A subclass of FunctionCallData which asserts that many of its members are non-None at construction time.
211
+ Typechecks be gone!
212
+ """
213
+
214
+ address_multi: MultiValues
215
+ address: int
216
+ symbol: Symbol
217
+ function: Function
218
+ name: str
219
+ cc: SimCC
220
+ prototype: SimTypeFunction
221
+ args_atoms: List[Set[Atom]]
222
+ args_values: List[MultiValues]
223
+ ret_atoms: Set[Atom]
224
+
225
+ def __init__(self, inner: FunctionCallData):
226
+ d = dict(inner.__dict__)
227
+ annotations = type(self).__annotations__ # pylint: disable=no-member
228
+ for k, v in d.items():
229
+ assert v is not None or k not in annotations, (
230
+ "Failed to unwrap field %s - this function is more complicated than you're ready for!" % k
231
+ )
232
+ assert v is not None, "Members of FunctionCallDataUnwrapped may not be None"
233
+ super().__init__(**d)
234
+
235
+ @staticmethod
236
+ @wraps
237
+ def decorate(
238
+ f: Callable[["FunctionHandler", "ReachingDefinitionsState", "FunctionCallDataUnwrapped"], None]
239
+ ) -> Callable[["FunctionHandler", "ReachingDefinitionsState", FunctionCallData], None]:
240
+ """
241
+ Decorate a function handler method with this to make it take a FunctionCallDataUnwrapped instead of a
242
+ FunctionCallData.
243
+ """
244
+
245
+ def inner(self: "FunctionHandler", state: "ReachingDefinitionsState", data: FunctionCallData):
246
+ f(self, state, FunctionCallDataUnwrapped(data))
247
+
248
+ return inner
249
+
149
250
 
150
251
  # pylint: disable=unused-argument, no-self-use
151
252
  class FunctionHandler:
@@ -153,6 +254,9 @@ class FunctionHandler:
153
254
  A mechanism for summarizing a function call's effect on a program for ReachingDefinitionsAnalysis.
154
255
  """
155
256
 
257
+ def __init__(self, interfunction_level: int = 0):
258
+ self.interfunction_level: int = interfunction_level
259
+
156
260
  def hook(self, analysis: "ReachingDefinitionsAnalysis") -> "FunctionHandler":
157
261
  """
158
262
  Attach this instance of the function handler to an instance of RDA.
@@ -249,21 +353,7 @@ class FunctionHandler:
249
353
  data.prototype = state.analysis.project.factory.function_prototype()
250
354
  data.guessed_prototype = True
251
355
 
252
- args_atoms_from_values = set()
253
- if data.args_atoms is None and data.args_values is not None:
254
- data.args_atoms = [
255
- set().union(
256
- *({defn.atom for defn in state.extract_defs(value)} for values in mv.values() for value in values)
257
- )
258
- for mv in data.args_values
259
- ]
260
- for atoms_set in data.args_atoms:
261
- args_atoms_from_values |= atoms_set
262
- elif data.args_atoms is None and data.cc is not None and data.prototype is not None:
263
- data.args_atoms = self.c_args_as_atoms(state, data.cc, data.prototype)
264
- if data.ret_atoms is None and data.cc is not None and data.prototype is not None:
265
- if data.prototype.returnty is not None:
266
- data.ret_atoms = self.c_return_as_atoms(state, data.cc, data.prototype)
356
+ args_atoms_from_values = data.reset_prototype(data.prototype, state)
267
357
 
268
358
  # PROCESS
269
359
  state.move_codelocs(data.function_codeloc)
@@ -316,6 +406,8 @@ class FunctionHandler:
316
406
  for effect in data.effects:
317
407
  if effect.sources_defns is None and effect.sources:
318
408
  effect.sources_defns = set().union(*(set(state.get_definitions(atom)) for atom in effect.sources))
409
+ if not effect.sources_defns:
410
+ effect.sources_defns = {Definition(atom, ExternalCodeLocation()) for atom in effect.sources}
319
411
  other_input_defns |= effect.sources_defns - all_args_defns
320
412
  # apply the effects, with the ones marked with apply_at_callsite=False applied first
321
413
  for effect in sorted(data.effects, key=lambda effect: effect.apply_at_callsite):
@@ -336,7 +428,7 @@ class FunctionHandler:
336
428
  data.ret_values_deps = effect.sources_defns
337
429
  else:
338
430
  # mark definition
339
- mv, defs = state.kill_and_add_definition(
431
+ _, defs = state.kill_and_add_definition(
340
432
  effect.dest,
341
433
  value,
342
434
  endness=None,
@@ -389,11 +481,41 @@ class FunctionHandler:
389
481
  self.handle_generic_function(state, data)
390
482
 
391
483
  def handle_local_function(self, state: "ReachingDefinitionsState", data: FunctionCallData) -> None:
392
- self.handle_generic_function(state, data)
484
+ if self.interfunction_level > 0 and data.function is not None and state.analysis is not None:
485
+ self.interfunction_level -= 1
486
+ try:
487
+ self.recurse_analysis(state, data)
488
+ finally:
489
+ self.interfunction_level += 1
490
+ else:
491
+ self.handle_generic_function(state, data)
393
492
 
394
493
  def handle_external_function(self, state: "ReachingDefinitionsState", data: FunctionCallData) -> None:
395
494
  self.handle_generic_function(state, data)
396
495
 
496
+ def recurse_analysis(self, state: "ReachingDefinitionsState", data: FunctionCallData) -> None:
497
+ """
498
+ Precondition: ``data.function`` MUST NOT BE NONE in order to call this method.
499
+ """
500
+ assert state.analysis is not None
501
+ assert data.function is not None
502
+ sub_rda = state.analysis.project.analyses.ReachingDefinitions(
503
+ data.function,
504
+ observe_all=state.analysis._observe_all,
505
+ observation_points=state.analysis._observation_points,
506
+ observe_callback=state.analysis._observe_callback,
507
+ dep_graph=state.dep_graph,
508
+ function_handler=self,
509
+ init_state=state,
510
+ )
511
+ # migrate data from sub_rda to its parent
512
+ state.analysis.function_calls.update(sub_rda.function_calls)
513
+ state.analysis.model.observed_results.update(sub_rda.model.observed_results)
514
+
515
+ sub_ld = get_exit_livedefinitions(data.function, sub_rda.model)
516
+ if sub_ld is not None:
517
+ state.live_definitions = sub_ld
518
+
397
519
  @staticmethod
398
520
  def c_args_as_atoms(state: "ReachingDefinitionsState", cc: SimCC, prototype: SimTypeFunction) -> List[Set[Atom]]:
399
521
  if not prototype.variadic: