angr 9.2.141__py3-none-manylinux2014_aarch64.whl → 9.2.143__py3-none-manylinux2014_aarch64.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 (71) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +26 -12
  3. angr/analyses/calling_convention/fact_collector.py +31 -9
  4. angr/analyses/cfg/cfg_base.py +38 -4
  5. angr/analyses/cfg/cfg_fast.py +23 -7
  6. angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +12 -1
  7. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +8 -1
  8. angr/analyses/class_identifier.py +8 -7
  9. angr/analyses/complete_calling_conventions.py +19 -6
  10. angr/analyses/decompiler/ail_simplifier.py +138 -98
  11. angr/analyses/decompiler/clinic.py +73 -5
  12. angr/analyses/decompiler/condition_processor.py +7 -7
  13. angr/analyses/decompiler/decompilation_cache.py +2 -1
  14. angr/analyses/decompiler/decompiler.py +10 -2
  15. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +4 -6
  16. angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +8 -2
  17. angr/analyses/decompiler/optimization_passes/condition_constprop.py +110 -46
  18. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +8 -0
  19. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -1
  20. angr/analyses/decompiler/optimization_passes/optimization_pass.py +2 -0
  21. angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +29 -7
  22. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +6 -0
  23. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +9 -1
  24. angr/analyses/decompiler/peephole_optimizations/simplify_pc_relative_loads.py +15 -1
  25. angr/analyses/decompiler/region_identifier.py +70 -47
  26. angr/analyses/decompiler/sequence_walker.py +8 -0
  27. angr/analyses/decompiler/ssailification/rewriting.py +47 -17
  28. angr/analyses/decompiler/ssailification/rewriting_engine.py +13 -0
  29. angr/analyses/decompiler/stack_item.py +36 -0
  30. angr/analyses/decompiler/structured_codegen/c.py +14 -9
  31. angr/analyses/decompiler/structuring/phoenix.py +3 -3
  32. angr/analyses/decompiler/utils.py +13 -0
  33. angr/analyses/find_objects_static.py +2 -1
  34. angr/analyses/reaching_definitions/engine_vex.py +13 -0
  35. angr/analyses/reaching_definitions/function_handler.py +24 -10
  36. angr/analyses/reaching_definitions/function_handler_library/stdio.py +1 -0
  37. angr/analyses/reaching_definitions/function_handler_library/stdlib.py +45 -12
  38. angr/analyses/reaching_definitions/function_handler_library/string.py +77 -21
  39. angr/analyses/reaching_definitions/function_handler_library/unistd.py +21 -1
  40. angr/analyses/reaching_definitions/rd_state.py +11 -7
  41. angr/analyses/s_liveness.py +44 -6
  42. angr/analyses/s_propagator.py +40 -29
  43. angr/analyses/s_reaching_definitions/s_rda_model.py +48 -37
  44. angr/analyses/s_reaching_definitions/s_rda_view.py +6 -3
  45. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +21 -21
  46. angr/analyses/typehoon/simple_solver.py +35 -8
  47. angr/analyses/typehoon/typehoon.py +3 -1
  48. angr/analyses/variable_recovery/engine_ail.py +6 -6
  49. angr/calling_conventions.py +20 -10
  50. angr/knowledge_plugins/functions/function.py +5 -10
  51. angr/knowledge_plugins/variables/variable_manager.py +27 -0
  52. angr/procedures/definitions/__init__.py +3 -10
  53. angr/procedures/definitions/linux_kernel.py +5 -0
  54. angr/procedures/definitions/wdk_ntoskrnl.py +2 -0
  55. angr/procedures/win32_kernel/__fastfail.py +15 -0
  56. angr/sim_procedure.py +2 -2
  57. angr/simos/simos.py +14 -10
  58. angr/simos/windows.py +42 -1
  59. angr/utils/ail.py +41 -1
  60. angr/utils/cpp.py +17 -0
  61. angr/utils/doms.py +149 -0
  62. angr/utils/library.py +1 -1
  63. angr/utils/ssa/__init__.py +21 -14
  64. angr/utils/ssa/vvar_uses_collector.py +2 -2
  65. angr/utils/types.py +12 -1
  66. {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/METADATA +7 -7
  67. {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/RECORD +71 -67
  68. {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/LICENSE +0 -0
  69. {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/WHEEL +0 -0
  70. {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/entry_points.txt +0 -0
  71. {angr-9.2.141.dist-info → angr-9.2.143.dist-info}/top_level.txt +0 -0
@@ -14,7 +14,7 @@ from ailment.statement import Statement, ConditionalJump, Jump, Label, Return
14
14
  from ailment.expression import Const, UnaryOp, MultiStatementExpression
15
15
 
16
16
  from angr.utils.graph import GraphUtils
17
- from angr.utils.ail import is_phi_assignment
17
+ from angr.utils.ail import is_phi_assignment, is_head_controlled_loop_block
18
18
  from angr.knowledge_plugins.cfg import IndirectJump, IndirectJumpType
19
19
  from angr.utils.constants import SWITCH_MISSING_DEFAULT_NODE_ADDR
20
20
  from angr.utils.graph import dominates, to_acyclic_graph, dfs_back_edges
@@ -312,11 +312,11 @@ class PhoenixStructurer(StructurerBase):
312
312
  and head_block.nodes
313
313
  and isinstance(head_block.nodes[0], Block)
314
314
  and head_block.nodes[0].statements
315
- and isinstance(first_nonlabel_nonphi_statement(head_block.nodes[0]), ConditionalJump)
315
+ and is_head_controlled_loop_block(head_block.nodes[0])
316
316
  ) or (
317
317
  isinstance(head_block, Block)
318
318
  and head_block.statements
319
- and isinstance(first_nonlabel_nonphi_statement(head_block), ConditionalJump)
319
+ and is_head_controlled_loop_block(head_block)
320
320
  ):
321
321
  # it's a while loop if the conditional jump (or the head block) is at the beginning of node
322
322
  loop_type = "while" if head_block_idx == 0 else "do-while"
@@ -214,6 +214,19 @@ def switch_extract_switch_expr_from_jump_target(target: ailment.Expr.Expression)
214
214
  target = target.operands[0]
215
215
  else:
216
216
  return None
217
+ elif target.op == "And":
218
+ # it must be and-ing the target expr with a constant
219
+ if (
220
+ isinstance(target.operands[1], ailment.Expr.VirtualVariable)
221
+ and isinstance(target.operands[0], ailment.Expr.Const)
222
+ ) or (
223
+ isinstance(target.operands[0], ailment.Expr.VirtualVariable)
224
+ and isinstance(target.operands[1], ailment.Expr.Const)
225
+ ):
226
+ break
227
+ return None
228
+ else:
229
+ return None
217
230
  elif isinstance(target, ailment.Expr.Load):
218
231
  # we want the address!
219
232
  found_load = True
@@ -9,6 +9,7 @@ from angr.analyses.reaching_definitions.function_handler import FunctionHandler
9
9
  from angr.knowledge_plugins.key_definitions.atoms import Register, MemoryLocation
10
10
  from angr.storage.memory_mixins.paged_memory.pages.multi_values import MultiValues
11
11
  from angr.knowledge_plugins.key_definitions.constants import OP_BEFORE, OP_AFTER
12
+ from angr.utils.cpp import is_cpp_funcname_ctor
12
13
  from . import Analysis, VtableFinder, CFGFast, ReachingDefinitionsAnalysis
13
14
 
14
15
  if TYPE_CHECKING:
@@ -109,7 +110,7 @@ class NewFunctionHandler(FunctionHandler):
109
110
  else:
110
111
  if self.project.kb.functions.contains_addr(function_address):
111
112
  func = self.project.kb.functions.get_by_addr(function_address)
112
- if func is not None and "ctor" in func.demangled_name:
113
+ if func is not None and is_cpp_funcname_ctor(func.demangled_name):
113
114
  # check if rdi has a possible this pointer/ object address, if so then we can assign this object
114
115
  # this class
115
116
  # also if the func is a constructor(not stripped binaries)
@@ -77,12 +77,15 @@ class SimEngineRDVEX(
77
77
  def _process_block_end(self, stmt_result, whitelist):
78
78
  self.stmt_idx = DEFAULT_STATEMENT
79
79
  self._set_codeloc()
80
+
81
+ function_handled = False
80
82
  if self.block.vex.jumpkind == "Ijk_Call":
81
83
  # it has to be a function
82
84
  block_next = self.block.vex.next
83
85
  assert isinstance(block_next, pyvex.expr.IRExpr)
84
86
  addr = self._expr_bv(block_next)
85
87
  self._handle_function(addr)
88
+ function_handled = True
86
89
  elif self.block.vex.jumpkind == "Ijk_Boring":
87
90
  # test if the target addr is a function or not
88
91
  block_next = self.block.vex.next
@@ -94,6 +97,16 @@ class SimEngineRDVEX(
94
97
  if addr_int in self.functions:
95
98
  # yes it's a jump to a function
96
99
  self._handle_function(addr)
100
+ function_handled = True
101
+
102
+ # take care of OP_AFTER during statement processing for function calls in a block
103
+ if self.state.analysis and function_handled:
104
+ self.state.analysis.stmt_observe(
105
+ self.stmt_idx, self.block.vex.statements[-1], self.block, self.state, OP_AFTER
106
+ )
107
+ self.state.analysis.insn_observe(
108
+ self.ins_addr, self.block.vex.statements[-1], self.block, self.state, OP_AFTER
109
+ )
97
110
 
98
111
  return self.state
99
112
 
@@ -121,9 +121,9 @@ class FunctionCallData:
121
121
  return False
122
122
  if isinstance(dest, MemoryLocation) and isinstance(dest.addr, SpOffset):
123
123
  for effect in self.effects:
124
- if not isinstance(effect.dest, MemoryLocation) or not isinstance(effect.dest.addr, SpOffset):
125
- continue
126
124
  stkarg = effect.dest
125
+ if not isinstance(stkarg, MemoryLocation) or not isinstance(stkarg.addr, SpOffset):
126
+ continue
127
127
  if (
128
128
  dest.addr.offset + dest.size <= stkarg.addr.offset
129
129
  or stkarg.addr.offset + stkarg.size <= dest.addr.offset
@@ -282,12 +282,20 @@ class FunctionHandler:
282
282
  A mechanism for summarizing a function call's effect on a program for ReachingDefinitionsAnalysis.
283
283
  """
284
284
 
285
- def __init__(self, interfunction_level: int = 0, extra_impls: Iterable[FunctionHandler] | None = None):
285
+ def __init__(self, interfunction_level: int = 0, extra_impls: Iterable[type[FunctionHandler]] | None = None):
286
+ """
287
+ :param interfunction_level: Maximum depth in to continue local function exploration
288
+ :param extra_impls: FunctionHandler classes to implement beyond what's implemented in function_handler_library
289
+ """
290
+
286
291
  self.interfunction_level: int = interfunction_level
287
292
 
288
- if extra_impls is not None:
289
- for extra_handler in extra_impls:
290
- for name, func in vars(extra_handler).items():
293
+ if extra_impls is None:
294
+ return
295
+
296
+ for extra_handler in extra_impls:
297
+ for cls in extra_handler.__mro__:
298
+ for name, func in vars(cls).items():
291
299
  if name.startswith("handle_impl_"):
292
300
  setattr(self, name, _mk_wrapper(func, self))
293
301
 
@@ -398,9 +406,13 @@ class FunctionHandler:
398
406
  for typelib_name in prototype_lib.type_collection_names:
399
407
  type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
400
408
  if type_collections:
401
- data.prototype = dereference_simtype(data.prototype, type_collections).with_arch(state.arch)
409
+ prototype = dereference_simtype(data.prototype, type_collections).with_arch(state.arch)
410
+ data.prototype = cast(SimTypeFunction, prototype)
402
411
 
403
- args_atoms_from_values = data.reset_prototype(data.prototype, state, soft_reset=True)
412
+ if isinstance(data.prototype, SimTypeFunction):
413
+ args_atoms_from_values = data.reset_prototype(data.prototype, state, soft_reset=True)
414
+ else:
415
+ args_atoms_from_values = set()
404
416
 
405
417
  # PROCESS
406
418
  state.move_codelocs(data.function_codeloc)
@@ -506,7 +518,9 @@ class FunctionHandler:
506
518
  assert data.prototype is not None
507
519
  if data.prototype.returnty is not None:
508
520
  if not isinstance(data.prototype.returnty, SimTypeBottom):
509
- data.ret_values = MultiValues(state.top(data.prototype.returnty.with_arch(state.arch).size))
521
+ data.ret_values = MultiValues(
522
+ state.top(data.prototype.returnty.with_arch(state.arch).size or state.arch.bits)
523
+ )
510
524
  else:
511
525
  data.ret_values = MultiValues(state.top(state.arch.bits))
512
526
  if data.guessed_prototype:
@@ -567,7 +581,7 @@ class FunctionHandler:
567
581
  sub_rda = state.analysis.project.analyses.ReachingDefinitions(
568
582
  data.function,
569
583
  observe_all=state.analysis._observe_all,
570
- observation_points=(state.analysis._observation_points or []) + return_observation_points,
584
+ observation_points=list(state.analysis._observation_points or []).extend(return_observation_points),
571
585
  observe_callback=state.analysis._observe_callback,
572
586
  dep_graph=state.dep_graph,
573
587
  function_handler=self,
@@ -202,6 +202,7 @@ def handle_printf(
202
202
  for defn in state.get_definitions(atom):
203
203
  top_val = state.annotate_with_def(top_val, defn)
204
204
  buf_data = MultiValues(top_val)
205
+ buf_atoms = atom
205
206
  elif fmt == "%u":
206
207
  buf_atoms = atom
207
208
  buf_data = state.get_concrete_value(buf_atoms)
@@ -7,7 +7,7 @@ import claripy
7
7
  from angr.analyses.reaching_definitions.function_handler import FunctionCallDataUnwrapped, FunctionHandler
8
8
  from angr.knowledge_plugins.key_definitions.atoms import Atom
9
9
  from angr.knowledge_plugins.key_definitions.live_definitions import DerefSize
10
-
10
+ from angr.knowledge_plugins.key_definitions.definition import Definition
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from angr.analyses.reaching_definitions.rd_state import ReachingDefinitionsState
@@ -75,7 +75,7 @@ class LibcStdlibHandlers(FunctionHandler):
75
75
  @FunctionCallDataUnwrapped.decorate
76
76
  def handle_impl_calloc(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
77
77
  nmemb = state.get_concrete_value(data.args_atoms[0]) or 48
78
- size = state.get_concrete_value(data.args_atoms[0]) or 1
78
+ size = state.get_concrete_value(data.args_atoms[1]) or 1
79
79
  heap_ptr = state.heap_address(state.heap_allocator.allocate(nmemb * size))
80
80
  data.depends(state.deref(heap_ptr, nmemb * size), value=0)
81
81
  data.depends(data.ret_atoms, value=heap_ptr)
@@ -84,18 +84,51 @@ class LibcStdlibHandlers(FunctionHandler):
84
84
  def handle_impl_getenv(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
85
85
  name_atom = state.deref(data.args_atoms[0], DerefSize.NULL_TERMINATE)
86
86
  name_value = state.get_concrete_value(name_atom, cast_to=bytes)
87
- if name_value is not None:
88
- name_value = name_value.strip(b"\0").decode()
87
+ length = 2
88
+ heap_value = None
89
+
89
90
  data.depends(None, name_atom)
90
91
 
91
92
  # store a buffer, registering it as an output of this function
92
93
  # we store this two-byte mixed value because we don't want the value to be picked up by get_concrete_value()
93
94
  # but also it should be able to be picked up by NULL_TERMINATE reads
94
- heap_ptr = state.heap_allocator.allocate(2)
95
- heap_atom = state.deref(heap_ptr, 2)
96
- heap_value = claripy.BVS("weh", 8).concat(claripy.BVV(0, 8))
97
- data.depends(heap_atom, EnvironAtom(2, name_value), value=heap_value)
98
- data.depends(data.ret_atoms, value=state.heap_address(heap_ptr))
95
+ heap_atom = None
96
+ env_atom = None
97
+ heap_ptr = None
98
+ sources = []
99
+ if name_value is not None:
100
+ name_value = name_value.strip(b"\0").decode()
101
+ for env_atom, env_value in state.others.items():
102
+ if not isinstance(env_atom, EnvironAtom) or env_atom.name != name_value:
103
+ continue
104
+
105
+ # There exists an environment variable with this name
106
+ heap_value = env_value
107
+ length = env_atom.size
108
+ heap_ptr = state.heap_allocator.allocate(length)
109
+ heap_atom = state.deref(heap_ptr, length)
110
+ break
111
+
112
+ else:
113
+ heap_value = None
114
+
115
+ if name_value is None or heap_value is None or heap_atom is None or env_atom is None:
116
+ heap_ptr = state.heap_allocator.allocate(length)
117
+ heap_atom = state.deref(heap_ptr, length)
118
+ heap_value = claripy.BVS("weh", 8)
119
+ env_atom = EnvironAtom(length, name_value)
120
+ if heap_atom is not None:
121
+ heap_value = state.annotate_with_def(heap_value, Definition(heap_atom, state.codeloc))
122
+ heap_value = heap_value.concat(claripy.BVV(0, 8))
123
+ data.depends(env_atom, value=heap_value) # Puts the env_atom in the others dict
124
+
125
+ data.depends(heap_atom, env_atom, value=heap_value)
126
+ sources = [heap_atom, env_atom]
127
+ if name_atom is not None:
128
+ sources.append(name_atom)
129
+
130
+ value = state.heap_address(heap_ptr) if heap_ptr is not None else state.top(state.arch.bits)
131
+ data.depends(data.ret_atoms, *sources, value=value)
99
132
 
100
133
  @FunctionCallDataUnwrapped.decorate
101
134
  def handle_impl_setenv(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
@@ -107,9 +140,9 @@ class LibcStdlibHandlers(FunctionHandler):
107
140
 
108
141
  src_atom = state.deref(data.args_atoms[1], DerefSize.NULL_TERMINATE)
109
142
  src_value = state.get_values(src_atom)
110
- data.depends(
111
- EnvironAtom(len(src_value) // 8 if src_value is not None else 1, name_value), src_atom, value=src_value
112
- )
143
+
144
+ env_atom = EnvironAtom(len(src_value) // 8 if src_value is not None else 1, name_value)
145
+ data.depends(env_atom, src_atom, value=src_value)
113
146
 
114
147
  @FunctionCallDataUnwrapped.decorate
115
148
  def handle_impl_system(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
@@ -1,8 +1,10 @@
1
1
  from __future__ import annotations
2
2
  import archinfo
3
+ import claripy
3
4
  from angr.analyses.reaching_definitions.function_handler import FunctionCallDataUnwrapped, FunctionHandler
4
5
  from angr.analyses.reaching_definitions.rd_state import ReachingDefinitionsState
5
6
  from angr.knowledge_plugins.key_definitions.live_definitions import DerefSize
7
+ from angr.knowledge_plugins.key_definitions.live_definitions import MultiValues
6
8
 
7
9
  # pylint: disable=no-self-use,missing-class-docstring,unused-argument
8
10
 
@@ -12,16 +14,26 @@ class LibcStringHandlers(FunctionHandler):
12
14
  def handle_impl_strcat(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
13
15
  src0_atom = state.deref(data.args_atoms[0], DerefSize.NULL_TERMINATE)
14
16
  src1_atom = state.deref(data.args_atoms[1], DerefSize.NULL_TERMINATE)
15
- src0_value = state.get_values(src0_atom)
16
- src1_value = state.get_values(src1_atom)
17
+ src0_value = state.get_values(src0_atom) if src0_atom is not None else None
18
+ src1_value = state.get_values(src1_atom) if src1_atom is not None else None
19
+
17
20
  if src0_value is not None and src1_value is not None:
18
21
  src0_value = src0_value.extract(0, len(src0_value) // 8 - 1, archinfo.Endness.BE)
19
22
  dest_value = src0_value.concat(src1_value)
20
23
  dest_atom = state.deref(data.args_atoms[0], len(dest_value) // 8, endness=archinfo.Endness.BE)
24
+ elif src0_value is not None:
25
+ src0_value = src0_value.extract(0, len(src0_value) // 8 - 1, archinfo.Endness.BE)
26
+ top_val = state.top(state.arch.bits)
27
+ if src1_atom is not None:
28
+ for defn in state.get_definitions(src1_atom):
29
+ top_val = state.annotate_with_def(top_val, defn)
30
+ dest_value = src0_value.concat(MultiValues(top_val))
31
+ dest_atom = state.deref(data.args_atoms[0], len(dest_value) // 8, endness=archinfo.Endness.BE)
21
32
  else:
22
33
  dest_value = None
23
34
  dest_atom = src0_atom
24
- data.depends(dest_atom, src0_atom, src1_atom, value=dest_value)
35
+ if src0_atom is not None and src1_atom is not None:
36
+ data.depends(dest_atom, src0_atom, src1_atom, value=dest_value)
25
37
  data.depends(data.ret_atoms, data.args_atoms[0], value=src0_value)
26
38
 
27
39
  handle_impl_strncat = handle_impl_strcat
@@ -29,39 +41,76 @@ class LibcStringHandlers(FunctionHandler):
29
41
  @FunctionCallDataUnwrapped.decorate
30
42
  def handle_impl_strlen(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
31
43
  src_atom = state.deref(data.args_atoms[0], DerefSize.NULL_TERMINATE)
32
- src_str = state.get_values(src_atom)
33
- if src_str is not None:
34
- data.depends(data.ret_atoms, src_atom, value=len(src_str) // 8 - 1)
44
+ if src_atom is not None:
45
+ src_str = state.get_values(src_atom) if src_atom is not None else None
46
+ if src_str is not None:
47
+ data.depends(data.ret_atoms, src_atom, value=len(src_str) // 8 - 1)
48
+ else:
49
+ data.depends(data.ret_atoms, src_atom)
35
50
  else:
36
- data.depends(data.ret_atoms, src_atom)
51
+ data.depends(data.ret_atoms, data.args_atoms[0])
37
52
 
38
53
  @FunctionCallDataUnwrapped.decorate
39
54
  def handle_impl_strcpy(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
40
55
  src_atom = state.deref(data.args_atoms[1], DerefSize.NULL_TERMINATE)
41
- src_str = state.get_values(src_atom)
42
- if src_str is not None:
43
- dst_atom = state.deref(data.args_atoms[0], len(src_str) // 8)
56
+ src_str = state.get_values(src_atom) if src_atom is not None else None
57
+ if src_str is None:
58
+ src_str = state.top(state.arch.bits)
59
+ if src_atom is not None:
60
+ for defn in state.get_definitions(src_atom):
61
+ src_str = state.annotate_with_def(src_str, defn)
62
+ src_str = MultiValues(src_str)
63
+
64
+ dst_atom = state.deref(data.args_atoms[0], len(src_str) // 8)
65
+ if src_atom is not None:
44
66
  data.depends(dst_atom, src_atom, value=src_str)
45
67
  data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
46
68
 
47
69
  @FunctionCallDataUnwrapped.decorate
48
70
  def handle_impl_strncpy(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
49
71
  n = state.get_concrete_value(data.args_atoms[2])
50
- src_atom = state.deref(data.args_atoms[1], DerefSize.NULL_TERMINATE if n is None else n)
51
- src_str = state.get_values(src_atom)
52
- if src_str is not None:
72
+ src_atom = state.deref(data.args_atoms[1], DerefSize.NULL_TERMINATE)
73
+ src_str = state.get_values(src_atom) if src_atom is not None else None
74
+ if src_str is None and src_atom is not None:
75
+ tmp_atom = state.deref(data.args_atoms[1], 1)
76
+ if tmp_atom is not None:
77
+ tmp_str = state.get_values(tmp_atom)
78
+ val_defns = None if tmp_str is None else state.get_definitions(tmp_str)
79
+ if tmp_str is None or not val_defns: # There's no data at all or no valid definitions
80
+ src_str = state.top(state.arch.bits if n is None or n > state.arch.bytes else n * 8)
81
+ defns = state.get_definitions(src_atom) if src_atom is not None else []
82
+ for defn in defns:
83
+ src_str = state.annotate_with_def(src_str, defn)
84
+ src_str = MultiValues(src_str)
85
+ else: # We found some data, but it's not NULL_TERIMINATED or of size n
86
+ src_atoms = set()
87
+ for defn in val_defns:
88
+ a = defn.atom
89
+ a.size = a.size if n is None or a.size < n else n
90
+ src_atoms.add(a)
91
+ src_str = state.get_values(src_atoms)
92
+
93
+ elif n is not None and src_str is not None and n < len(src_str) // 8:
94
+ # We have a src_str, but need to truncate it if n is not None and less than the size of src_str
95
+ src_atom = state.deref(data.args_atoms[1], n)
96
+ if src_atom is not None:
97
+ src_str = state.get_values(src_atom)
98
+
99
+ if src_str is not None and src_atom is not None:
53
100
  dst_atom = state.deref(data.args_atoms[0], len(src_str) // 8)
54
101
  data.depends(dst_atom, src_atom, value=src_str)
102
+
55
103
  data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
56
104
 
57
105
  @FunctionCallDataUnwrapped.decorate
58
106
  def handle_impl_strdup(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
59
- src_atom = state.deref(data.args_atoms[1], DerefSize.NULL_TERMINATE)
60
- src_str = state.get_values(src_atom)
61
- malloc_size = len(src_str) // 8 if src_str is not None else 1
62
- heap_ptr = state.heap_allocator.allocate(malloc_size)
63
- dst_atom = state.deref(heap_ptr, malloc_size)
64
- data.depends(dst_atom, src_atom, value=src_str)
107
+ src_atom = state.deref(data.args_atoms[0], DerefSize.NULL_TERMINATE)
108
+ if src_atom is not None:
109
+ src_str = state.get_values(src_atom)
110
+ malloc_size = len(src_str) // 8 if src_str is not None else 1
111
+ heap_ptr = state.heap_allocator.allocate(malloc_size)
112
+ dst_atom = state.deref(heap_ptr, malloc_size)
113
+ data.depends(dst_atom, src_atom, value=src_str)
65
114
  data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
66
115
 
67
116
  @FunctionCallDataUnwrapped.decorate
@@ -70,15 +119,22 @@ class LibcStringHandlers(FunctionHandler):
70
119
  if size is not None:
71
120
  src_atom = state.deref(data.args_atoms[1], size)
72
121
  dst_atom = state.deref(data.args_atoms[0], size)
73
- data.depends(dst_atom, src_atom, value=state.get_values(src_atom))
122
+ if src_atom is not None:
123
+ data.depends(dst_atom, src_atom, value=state.get_values(src_atom))
74
124
  data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
75
125
 
76
126
  @FunctionCallDataUnwrapped.decorate
77
127
  def handle_impl_memset(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
78
128
  size = state.get_concrete_value(data.args_atoms[2])
129
+ c = state.get_concrete_value(data.args_atoms[1])
79
130
  if size is not None:
80
131
  dst_atom = state.deref(data.args_atoms[0], size)
81
- data.depends(dst_atom, data.args_atoms[1])
132
+ if c is not None:
133
+ value = MultiValues(claripy.BVV(chr(c) * size, size * 8))
134
+ data.depends(dst_atom, data.args_atoms[1], value=value)
135
+ else:
136
+ data.depends(dst_atom, data.args_atoms[1], value=state.get_values(data.args_atoms[1]))
137
+
82
138
  data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
83
139
 
84
140
  @FunctionCallDataUnwrapped.decorate
@@ -1,17 +1,37 @@
1
1
  from __future__ import annotations
2
+ import random
2
3
  from angr.analyses.reaching_definitions.function_handler import FunctionCallDataUnwrapped, FunctionHandler
3
4
  from angr.analyses.reaching_definitions.function_handler_library.stdio import StdinAtom, StdoutAtom
4
5
  from angr.analyses.reaching_definitions.rd_state import ReachingDefinitionsState
6
+ from angr.knowledge_plugins.key_definitions.atoms import Atom
5
7
 
6
8
  # pylint: disable=no-self-use,missing-class-docstring,unused-argument
7
9
 
8
10
 
11
+ class FDAtom(Atom):
12
+ def __init__(self, fd: int | None, source: str, size: int = 1):
13
+ self.source = source
14
+ self.fd = fd
15
+ self.nonce = random.randint(0, 999999999999)
16
+ super().__init__(size)
17
+
18
+ def _identity(self):
19
+ if self.fd is not None:
20
+ return (self.fd,)
21
+ return (self.nonce,)
22
+
23
+
9
24
  class LibcUnistdHandlers(FunctionHandler):
10
25
  @FunctionCallDataUnwrapped.decorate
11
26
  def handle_impl_read(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
12
27
  size = state.get_concrete_value(data.args_atoms[2]) or 1
13
28
  dst_atom = state.deref(data.args_atoms[1], size)
14
- data.depends(dst_atom, StdinAtom(data.function.name, size))
29
+ real_fd = state.get_concrete_value(data.args_atoms[0])
30
+
31
+ fd_atom = StdinAtom(data.function.name, size) if real_fd == 0 else FDAtom(real_fd, data.function.name, size)
32
+ buf_data = state.top(size * 8) if size is not None else state.top(state.arch.bits)
33
+
34
+ data.depends(dst_atom, fd_atom, value=buf_data)
15
35
 
16
36
  handle_impl_recv = handle_impl_recvfrom = handle_impl_read
17
37
 
@@ -215,14 +215,14 @@ class ReachingDefinitionsState:
215
215
  def tmp_uses(self):
216
216
  return self.live_definitions.tmp_uses
217
217
 
218
- @property
219
- def register_uses(self):
220
- return self.live_definitions.register_uses
221
-
222
218
  @property
223
219
  def registers(self) -> MultiValuedMemory:
224
220
  return self.live_definitions.registers
225
221
 
222
+ @property
223
+ def register_uses(self):
224
+ return self.live_definitions.register_uses
225
+
226
226
  @property
227
227
  def stack(self) -> MultiValuedMemory:
228
228
  return self.live_definitions.stack
@@ -239,13 +239,17 @@ class ReachingDefinitionsState:
239
239
  def heap_uses(self):
240
240
  return self.live_definitions.heap_uses
241
241
 
242
+ @property
243
+ def memory(self) -> MultiValuedMemory:
244
+ return self.live_definitions.memory
245
+
242
246
  @property
243
247
  def memory_uses(self):
244
248
  return self.live_definitions.memory_uses
245
249
 
246
250
  @property
247
- def memory(self) -> MultiValuedMemory:
248
- return self.live_definitions.memory
251
+ def others(self) -> dict[Atom, MultiValues]:
252
+ return self.live_definitions.others
249
253
 
250
254
  @property
251
255
  def uses_by_codeloc(self):
@@ -493,7 +497,7 @@ class ReachingDefinitionsState:
493
497
  self.live_definitions.add_memory_use_by_def(definition, self.codeloc, expr=expr)
494
498
 
495
499
  def get_definitions(
496
- self, atom: Atom | Definition[Atom] | Iterable[Atom] | Iterable[Definition[Atom]]
500
+ self, atom: Atom | Definition[Atom] | Iterable[Atom] | Iterable[Definition[Atom]] | MultiValues
497
501
  ) -> set[Definition[Atom]]:
498
502
  return self.live_definitions.get_definitions(atom)
499
503
 
@@ -2,9 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  import networkx
4
4
  from ailment.expression import VirtualVariable
5
- from ailment.statement import Assignment, Call
5
+ from ailment.statement import Assignment, Call, ConditionalJump
6
6
 
7
7
  from angr.analyses import Analysis, register_analysis
8
+ from angr.utils.ail import is_head_controlled_loop_block, is_phi_assignment
8
9
  from angr.utils.ssa import VVarUsesCollector, phi_assignment_get_src
9
10
 
10
11
 
@@ -69,8 +70,14 @@ class SLivenessAnalysis(Analysis):
69
70
  block_key = block.addr, block.idx
70
71
  changed = False
71
72
 
73
+ head_controlled_loop = is_head_controlled_loop_block(block)
74
+
72
75
  live = set()
73
76
  for succ in graph.successors(block):
77
+ if head_controlled_loop and (block.addr, block.idx) == (succ.addr, succ.idx):
78
+ # this is a head-controlled loop block; we ignore the self-loop edge because all variables defined
79
+ # in the block after the conditional jump will be dead after leaving the current block
80
+ continue
74
81
  edge = (block.addr, block.idx), (succ.addr, succ.idx)
75
82
  if edge in live_on_edges:
76
83
  live |= live_on_edges[edge]
@@ -81,8 +88,18 @@ class SLivenessAnalysis(Analysis):
81
88
  changed = True
82
89
  live_outs[block_key] = live.copy()
83
90
 
91
+ if head_controlled_loop:
92
+ # this is a head-controlled loop block; we start scanning from the first condition jump backwards
93
+ condjump_idx = next(
94
+ iter(i for i, stmt in enumerate(block.statements) if isinstance(stmt, ConditionalJump)), None
95
+ )
96
+ assert condjump_idx is not None
97
+ stmts = block.statements[: condjump_idx + 1]
98
+ else:
99
+ stmts = block.statements
100
+
84
101
  live_in_by_pred = {}
85
- for stmt in reversed(block.statements):
102
+ for stmt in reversed(stmts):
86
103
  # handle assignments: a defined vvar is not live before the assignment
87
104
  if isinstance(stmt, Assignment) and isinstance(stmt.dst, VirtualVariable):
88
105
  live.discard(stmt.dst.varid)
@@ -92,6 +109,10 @@ class SLivenessAnalysis(Analysis):
92
109
  phi_expr = phi_assignment_get_src(stmt)
93
110
  if phi_expr is not None:
94
111
  for src, vvar in phi_expr.src_and_vvars:
112
+ if head_controlled_loop and src == (block.addr, block.idx):
113
+ # this is a head-controlled loop block; we ignore the self-loop edge
114
+ continue
115
+
95
116
  if src not in live_in_by_pred:
96
117
  live_in_by_pred[src] = live.copy()
97
118
  if vvar is not None:
@@ -99,9 +120,15 @@ class SLivenessAnalysis(Analysis):
99
120
  live_in_by_pred[src].discard(stmt.dst.varid)
100
121
 
101
122
  # handle the statement: add used vvars to the live set
102
- vvar_use_collector = VVarUsesCollector()
103
- vvar_use_collector.walk_statement(stmt)
104
- live |= vvar_use_collector.vvars
123
+ if head_controlled_loop and is_phi_assignment(stmt):
124
+ for src, vvar in stmt.src.src_and_vvars:
125
+ # this is a head-controlled loop block; we ignore the self-loop edge
126
+ if src != (block.addr, block.idx) and vvar is not None:
127
+ live |= {vvar.varid}
128
+ else:
129
+ vvar_use_collector = VVarUsesCollector()
130
+ vvar_use_collector.walk_statement(stmt)
131
+ live |= vvar_use_collector.vvars
105
132
 
106
133
  if live_ins[block_key] != live:
107
134
  live_ins[block_key] = live
@@ -135,7 +162,18 @@ class SLivenessAnalysis(Analysis):
135
162
 
136
163
  for block in self.func_graph.nodes():
137
164
  live = self.model.live_outs[(block.addr, block.idx)].copy()
138
- for stmt in reversed(block.statements):
165
+
166
+ if is_head_controlled_loop_block(block):
167
+ # this is a head-controlled loop block; we start scanning from the first condition jump backwards
168
+ condjump_idx = next(
169
+ iter(i for i, stmt in enumerate(block.statements) if isinstance(stmt, ConditionalJump)), None
170
+ )
171
+ assert condjump_idx is not None
172
+ stmts = block.statements[: condjump_idx + 1]
173
+ else:
174
+ stmts = block.statements
175
+
176
+ for stmt in reversed(stmts):
139
177
  if isinstance(stmt, Assignment) and isinstance(stmt.dst, VirtualVariable):
140
178
  def_vvar = stmt.dst.varid
141
179
  elif isinstance(stmt, Call) and isinstance(stmt.ret_expr, VirtualVariable):