angr 9.2.161__cp310-abi3-win_amd64.whl → 9.2.163__cp310-abi3-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 (51) hide show
  1. angr/__init__.py +1 -1
  2. angr/ailment/expression.py +12 -0
  3. angr/analyses/analysis.py +0 -1
  4. angr/analyses/cfg/cfg_base.py +6 -2
  5. angr/analyses/decompiler/ail_simplifier.py +20 -1
  6. angr/analyses/decompiler/block_simplifier.py +6 -3
  7. angr/analyses/decompiler/clinic.py +6 -6
  8. angr/analyses/decompiler/condition_processor.py +24 -0
  9. angr/analyses/decompiler/counters/call_counter.py +11 -1
  10. angr/analyses/decompiler/decompiler.py +3 -1
  11. angr/analyses/decompiler/graph_region.py +11 -2
  12. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +1 -1
  13. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -0
  14. angr/analyses/decompiler/optimization_passes/optimization_pass.py +31 -11
  15. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +2 -0
  16. angr/analyses/decompiler/region_simplifiers/goto.py +3 -3
  17. angr/analyses/decompiler/region_simplifiers/if_.py +2 -2
  18. angr/analyses/decompiler/region_simplifiers/loop.py +2 -2
  19. angr/analyses/decompiler/structured_codegen/c.py +3 -3
  20. angr/analyses/decompiler/structuring/dream.py +1 -1
  21. angr/analyses/decompiler/structuring/phoenix.py +119 -67
  22. angr/analyses/decompiler/structuring/recursive_structurer.py +3 -2
  23. angr/analyses/decompiler/structuring/sailr.py +51 -43
  24. angr/analyses/decompiler/structuring/structurer_base.py +2 -3
  25. angr/analyses/deobfuscator/string_obf_opt_passes.py +1 -1
  26. angr/analyses/disassembly.py +1 -1
  27. angr/analyses/fcp/fcp.py +11 -10
  28. angr/analyses/flirt/flirt_sig.py +5 -2
  29. angr/analyses/reaching_definitions/function_handler.py +2 -1
  30. angr/analyses/reaching_definitions/function_handler_library/stdio.py +7 -6
  31. angr/analyses/reaching_definitions/function_handler_library/stdlib.py +10 -4
  32. angr/analyses/reaching_definitions/function_handler_library/string.py +13 -2
  33. angr/analyses/reaching_definitions/function_handler_library/unistd.py +7 -0
  34. angr/analyses/s_propagator.py +2 -2
  35. angr/analyses/variable_recovery/engine_base.py +8 -4
  36. angr/knowledge_plugins/functions/function.py +1 -1
  37. angr/knowledge_plugins/functions/function_manager.py +1 -2
  38. angr/project.py +5 -2
  39. angr/rustylib.pyd +0 -0
  40. angr/sim_type.py +2 -2
  41. angr/simos/javavm.py +1 -1
  42. angr/unicornlib.dll +0 -0
  43. angr/utils/graph.py +28 -12
  44. angr/utils/library.py +13 -12
  45. angr/utils/ssa/__init__.py +54 -2
  46. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/METADATA +5 -5
  47. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/RECORD +51 -51
  48. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/WHEEL +0 -0
  49. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/entry_points.txt +0 -0
  50. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/licenses/LICENSE +0 -0
  51. {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/top_level.txt +0 -0
@@ -146,7 +146,7 @@ class FlirtSignatureParsed:
146
146
  name_lst = []
147
147
  name_end = False # in case the function name is too long...
148
148
  for _ in range(1024): # max length of a function name
149
- if next_byte < 0x20:
149
+ if next_byte < 0x20 or next_byte >= 0x80:
150
150
  name_end = True
151
151
  break
152
152
  name_lst.append(next_byte)
@@ -347,7 +347,10 @@ class FlirtSignatureParsed:
347
347
  # is it compressed?
348
348
  if obj.features & FlirtFeatureFlag.FEATURE_COMPRESSED:
349
349
  data = file_obj.read()
350
- decompressed = BytesIO(zlib.decompress(data))
350
+ try:
351
+ decompressed = BytesIO(zlib.decompress(data))
352
+ except zlib.error as ex:
353
+ raise FlirtSignatureError(f"Failed to decompress FLIRT signature: {ex}") from ex
351
354
  file_obj = decompressed
352
355
 
353
356
  root = obj.parse_tree(file_obj, root=True)
@@ -575,7 +575,7 @@ class FunctionHandler:
575
575
  sub_rda = state.analysis.project.analyses.ReachingDefinitions(
576
576
  data.function,
577
577
  observe_all=state.analysis._observe_all,
578
- observation_points=list(state.analysis._observation_points or []).extend(return_observation_points),
578
+ observation_points=list(state.analysis._observation_points or []) + return_observation_points,
579
579
  observe_callback=state.analysis._observe_callback,
580
580
  dep_graph=state.dep_graph,
581
581
  function_handler=self,
@@ -584,6 +584,7 @@ class FunctionHandler:
584
584
  # migrate data from sub_rda to its parent
585
585
  state.analysis.function_calls.update(sub_rda.function_calls)
586
586
  state.analysis.model.observed_results.update(sub_rda.model.observed_results)
587
+ state.all_definitions |= sub_rda.all_definitions
587
588
 
588
589
  sub_ld = get_exit_livedefinitions(data.function, sub_rda.model)
589
590
  if sub_ld is not None:
@@ -156,14 +156,14 @@ class LibcStdioHandlers(FunctionHandler):
156
156
  @FunctionCallDataUnwrapped.decorate
157
157
  def handle_impl_fread(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
158
158
  size = state.get_concrete_value(data.args_atoms[1]) or 1
159
- nmemb = state.get_concrete_value(data.args_atoms[1]) or 2
159
+ nmemb = state.get_concrete_value(data.args_atoms[2]) or 2
160
160
  dst_atom = state.deref(data.args_atoms[0], size * nmemb)
161
161
  data.depends(dst_atom, StdinAtom("fread", size * nmemb))
162
162
 
163
163
  @FunctionCallDataUnwrapped.decorate
164
164
  def handle_impl_fwrite(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
165
165
  size = state.get_concrete_value(data.args_atoms[1]) or 1
166
- nmemb = state.get_concrete_value(data.args_atoms[1]) or 2
166
+ nmemb = state.get_concrete_value(data.args_atoms[2]) or 2
167
167
  src_atom = state.deref(data.args_atoms[0], size * nmemb)
168
168
  data.depends(StdoutAtom("fwrite", size * nmemb), src_atom, value=state.get_values(src_atom))
169
169
 
@@ -206,8 +206,8 @@ def handle_printf(
206
206
  elif fmt == "%u":
207
207
  buf_atoms = atom
208
208
  buf_data = state.get_concrete_value(buf_atoms)
209
- if buf_data is not None:
210
- buf_data = str(buf_data).encode()
209
+ buf_data = str(buf_data).encode() if buf_data else b"0"
210
+
211
211
  elif fmt == "%d":
212
212
  buf_atoms = atom
213
213
  buf_data = state.get_concrete_value(buf_atoms)
@@ -215,11 +215,12 @@ def handle_printf(
215
215
  if buf_data >= 2**31:
216
216
  buf_data -= 2**32
217
217
  buf_data = str(buf_data).encode()
218
+ else:
219
+ buf_data = b"0"
218
220
  elif fmt == "%c":
219
221
  buf_atoms = atom
220
222
  buf_data = state.get_concrete_value(atom)
221
- if buf_data is not None:
222
- buf_data = chr(buf_data).encode()
223
+ buf_data = chr(buf_data).encode() if buf_data else b"0"
223
224
  else:
224
225
  _l.warning("Unimplemented printf format string %s", fmt)
225
226
  buf_atoms = set()
@@ -64,21 +64,25 @@ class LibcStdlibHandlers(FunctionHandler):
64
64
  buf_value = int(buf_value.decode().strip("\0"))
65
65
  except ValueError:
66
66
  buf_value = 0
67
+ else:
68
+ buf_value = 0 # Make the value concrete if it cannot be parsed
67
69
  data.depends(data.ret_atoms, buf_atoms, value=buf_value)
68
70
 
69
71
  @FunctionCallDataUnwrapped.decorate
70
72
  def handle_impl_malloc(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
71
73
  malloc_size = state.get_concrete_value(data.args_atoms[0]) or 48
72
74
  heap_ptr = state.heap_allocator.allocate(malloc_size)
73
- data.depends(data.ret_atoms, value=state.heap_address(heap_ptr))
75
+ heap_addr = state.heap_address(heap_ptr)
76
+ data.depends(data.ret_atoms, value=heap_addr)
74
77
 
75
78
  @FunctionCallDataUnwrapped.decorate
76
79
  def handle_impl_calloc(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
77
80
  nmemb = state.get_concrete_value(data.args_atoms[0]) or 48
78
81
  size = state.get_concrete_value(data.args_atoms[1]) or 1
79
- heap_ptr = state.heap_address(state.heap_allocator.allocate(nmemb * size))
80
- data.depends(state.deref(heap_ptr, nmemb * size), value=0)
81
- data.depends(data.ret_atoms, value=heap_ptr)
82
+ heap_ptr = state.heap_allocator.allocate(nmemb * size)
83
+ heap_addr = state.heap_address(heap_ptr)
84
+ data.depends(state.deref(heap_addr, nmemb * size), value=0)
85
+ data.depends(data.ret_atoms, value=heap_addr)
82
86
 
83
87
  @FunctionCallDataUnwrapped.decorate
84
88
  def handle_impl_getenv(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
@@ -156,6 +160,8 @@ class LibcStdlibHandlers(FunctionHandler):
156
160
  def handle_impl_execve(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
157
161
  argv_value = state.get_one_value(data.args_atoms[1])
158
162
  if argv_value is None:
163
+ # Model execve with unknown argv - still has system effects
164
+ data.depends(SystemAtom(1), data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
159
165
  return
160
166
 
161
167
  nonce = random.randint(1, 999999999)
@@ -30,7 +30,7 @@ class LibcStringHandlers(FunctionHandler):
30
30
  dest_value = src0_value.concat(MultiValues(top_val))
31
31
  dest_atom = state.deref(data.args_atoms[0], len(dest_value) // 8, endness=archinfo.Endness.BE)
32
32
  else:
33
- dest_value = None
33
+ dest_value = state.top(state.arch.bits)
34
34
  dest_atom = src0_atom
35
35
  if src0_atom is not None and src1_atom is not None:
36
36
  data.depends(dest_atom, src0_atom, src1_atom, value=dest_value)
@@ -109,7 +109,8 @@ class LibcStringHandlers(FunctionHandler):
109
109
  src_str = state.get_values(src_atom)
110
110
  malloc_size = len(src_str) // 8 if src_str is not None else 1
111
111
  heap_ptr = state.heap_allocator.allocate(malloc_size)
112
- dst_atom = state.deref(heap_ptr, malloc_size)
112
+ heap_addr = state.heap_address(heap_ptr)
113
+ dst_atom = state.deref(heap_addr, malloc_size)
113
114
  data.depends(dst_atom, src_atom, value=src_str)
114
115
  data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
115
116
 
@@ -121,6 +122,16 @@ class LibcStringHandlers(FunctionHandler):
121
122
  dst_atom = state.deref(data.args_atoms[0], size)
122
123
  if src_atom is not None:
123
124
  data.depends(dst_atom, src_atom, value=state.get_values(src_atom))
125
+ else:
126
+ # When size is unknown, use default size and create TOP value
127
+ default_size = state.arch.bytes
128
+ src_atom = state.deref(data.args_atoms[1], default_size)
129
+ dst_atom = state.deref(data.args_atoms[0], default_size)
130
+ if src_atom is not None:
131
+ top_val = state.top(default_size * 8)
132
+ for defn in state.get_definitions(src_atom):
133
+ top_val = state.annotate_with_def(top_val, defn)
134
+ data.depends(dst_atom, src_atom, value=MultiValues(top_val))
124
135
  data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
125
136
 
126
137
  @FunctionCallDataUnwrapped.decorate
@@ -32,6 +32,9 @@ class LibcUnistdHandlers(FunctionHandler):
32
32
  buf_data = state.top(size * 8) if size is not None else state.top(state.arch.bits)
33
33
 
34
34
  data.depends(dst_atom, fd_atom, value=buf_data)
35
+ # Model return value - number of bytes read (could be 0 to size, or -1 for error)
36
+ ret_val = state.top(state.arch.bits) # Unknown number of bytes read
37
+ data.depends(data.ret_atoms, fd_atom, value=ret_val)
35
38
 
36
39
  handle_impl_recv = handle_impl_recvfrom = handle_impl_read
37
40
 
@@ -41,4 +44,8 @@ class LibcUnistdHandlers(FunctionHandler):
41
44
  src_atom = state.deref(data.args_atoms[1], size)
42
45
  data.depends(StdoutAtom(data.function.name, size), src_atom, value=state.get_values(src_atom))
43
46
 
47
+ # Model return value - number of bytes written (could be 0 to size, or -1 for error)
48
+ ret_val = state.top(state.arch.bits) # Unknown number of bytes written
49
+ data.depends(data.ret_atoms, value=ret_val)
50
+
44
51
  handle_impl_send = handle_impl_write
@@ -33,7 +33,7 @@ from angr.utils.ssa import (
33
33
  is_const_vvar_load_assignment,
34
34
  is_const_vvar_load_dirty_assignment,
35
35
  is_const_vvar_tmp_assignment,
36
- is_vvar_eliminatable,
36
+ is_vvar_propagatable,
37
37
  get_tmp_uselocs,
38
38
  get_tmp_deflocs,
39
39
  phi_assignment_get_src,
@@ -246,7 +246,7 @@ class SPropagatorAnalysis(Analysis):
246
246
  self.model.dead_vvar_ids.add(vvar.varid)
247
247
  continue
248
248
 
249
- if is_vvar_eliminatable(vvar, stmt):
249
+ if is_vvar_propagatable(vvar, stmt):
250
250
  if len(vvar_uselocs_set) == 1:
251
251
  vvar_used, vvar_useloc = next(iter(vvar_uselocs_set))
252
252
  if (
@@ -142,6 +142,8 @@ class SimEngineVRBase(
142
142
  ) -> list[tuple[SimVariable, int]]:
143
143
  data = richr_addr.data
144
144
 
145
+ variable: SimVariable | None = None
146
+
145
147
  if self.state.is_stack_address(data):
146
148
  # this is a stack address
147
149
  # extract stack offset
@@ -157,7 +159,6 @@ class SimEngineVRBase(
157
159
  for candidate, offset in var_candidates:
158
160
  if isinstance(candidate, SimStackVariable) and candidate.offset == stack_offset:
159
161
  existing_vars.append((candidate, offset))
160
- variable = None
161
162
  if existing_vars:
162
163
  variable, _ = existing_vars[0]
163
164
 
@@ -178,6 +179,9 @@ class SimEngineVRBase(
178
179
  for var_stack_offset, var in self.state.extract_variables(v):
179
180
  existing_vars.append((var, var_stack_offset))
180
181
 
182
+ if not existing_vars:
183
+ existing_vars = [(v, 0) for v in variable_manager.find_variables_by_stack_offset(stack_offset)]
184
+
181
185
  if not existing_vars:
182
186
  # no variables exist
183
187
  lea_size = 1
@@ -185,10 +189,10 @@ class SimEngineVRBase(
185
189
  stack_offset,
186
190
  lea_size,
187
191
  base="bp",
188
- ident=self.state.variable_manager[self.func_addr].next_variable_ident("stack"),
192
+ ident=variable_manager.next_variable_ident("stack"),
189
193
  region=self.func_addr,
190
194
  )
191
- self.state.variable_manager[self.func_addr].add_variable("stack", stack_offset, variable)
195
+ variable_manager.add_variable("stack", stack_offset, variable)
192
196
  l.debug("Identified a new stack variable %s at %#x.", variable, self.ins_addr)
193
197
  existing_vars.append((variable, 0))
194
198
 
@@ -555,7 +559,7 @@ class SimEngineVRBase(
555
559
 
556
560
  if isinstance(stack_offset, int):
557
561
  expr = data.data
558
- if isinstance(expr, claripy.ast.Bits) and expr.size() > 1024:
562
+ if isinstance(expr, claripy.ast.BV) and expr.size() > 1024:
559
563
  # we don't write more than 256 bytes to the stack at a time for performance reasons
560
564
  expr = expr[expr.size() - 1 : expr.size() - 1024]
561
565
  expr = self.state.annotate_with_variables(expr, [(variable_offset, variable)])
@@ -1666,7 +1666,7 @@ class Function(Serializable):
1666
1666
  if self.is_rust_function():
1667
1667
  ast = pydemumble.demangle(self.name)
1668
1668
  return Function._rust_fmt_node(ast.split("::")[-2])
1669
- func_name = get_cpp_function_name(self.demangled_name, specialized=False, qualified=True)
1669
+ func_name = get_cpp_function_name(self.demangled_name)
1670
1670
  return func_name.split("::")[-1]
1671
1671
 
1672
1672
  def get_unambiguous_name(self, display_name: str | None = None) -> str:
@@ -154,8 +154,7 @@ class FunctionManager(KnowledgeBasePlugin, collections.abc.Mapping):
154
154
  :return: None
155
155
  """
156
156
  with open(filepath, "w", encoding="utf-8") as f:
157
- for src, dst in self.callgraph.edges():
158
- f.write(f"{src:#x}\tDirectEdge\t{dst:#x}\n")
157
+ f.writelines(f"{src:#x}\tDirectEdge\t{dst:#x}\n" for src, dst in self.callgraph.edges())
159
158
 
160
159
  def _addr_in_plt_cached_ranges(self, addr: int) -> bool:
161
160
  if self._rplt_cache_ranges is None:
angr/project.py CHANGED
@@ -32,7 +32,10 @@ def load_shellcode(shellcode: bytes | str, arch, start_offset=0, load_address=0,
32
32
  if not isinstance(arch, archinfo.Arch):
33
33
  arch = archinfo.arch_from_id(arch)
34
34
  if isinstance(shellcode, str):
35
- shellcode_bytes: bytes = arch.asm(shellcode, load_address, thumb=thumb)
35
+ shellcode_bytes = arch.asm(shellcode, load_address, thumb=thumb)
36
+ if shellcode_bytes is None:
37
+ raise ValueError("Could not assemble shellcode")
38
+ assert isinstance(shellcode_bytes, bytes)
36
39
  else:
37
40
  shellcode_bytes = shellcode
38
41
  if thumb:
@@ -173,7 +176,7 @@ class Project:
173
176
 
174
177
  # It doesn't make any sense to have auto_load_libs
175
178
  # if you have the concrete target, let's warn the user about this.
176
- if self.concrete_target and load_options.get("auto_load_libs", None):
179
+ if self.concrete_target and load_options.get("auto_load_libs"):
177
180
  l.critical(
178
181
  "Incompatible options selected for this project, please disable auto_load_libs if "
179
182
  "you want to use a concrete target."
angr/rustylib.pyd CHANGED
Binary file
angr/sim_type.py CHANGED
@@ -3342,7 +3342,7 @@ def _decl_to_type(
3342
3342
 
3343
3343
  if decl.name is not None:
3344
3344
  key = "struct " + decl.name
3345
- struct = extra_types.get(key, None)
3345
+ struct = extra_types.get(key)
3346
3346
  from_global = False
3347
3347
  if struct is None:
3348
3348
  struct = ALL_TYPES.get(key)
@@ -3378,7 +3378,7 @@ def _decl_to_type(
3378
3378
 
3379
3379
  if decl.name is not None:
3380
3380
  key = "union " + decl.name
3381
- union = extra_types.get(key, None)
3381
+ union = extra_types.get(key)
3382
3382
  from_global = False
3383
3383
  if union is None and key in ALL_TYPES:
3384
3384
  union = ALL_TYPES[key]
angr/simos/javavm.py CHANGED
@@ -255,7 +255,7 @@ class SimJavaVM(SimOS):
255
255
  upper = native_arg_value.get_bytes(0, 4)
256
256
  lower = native_arg_value.get_bytes(4, 4)
257
257
  idx = args.index(arg)
258
- args = args[:idx] + (SootArgument(upper, "int"), SootArgument(lower, "int")) + args[idx + 1 :]
258
+ args = (*args[:idx], SootArgument(upper, "int"), SootArgument(lower, "int"), *args[idx + 1 :])
259
259
  native_arg_values += [upper, lower]
260
260
  continue
261
261
  if type(arg.value) is BV and len(arg.value) > arg_ty.size:
angr/unicornlib.dll CHANGED
Binary file
angr/utils/graph.py CHANGED
@@ -55,7 +55,7 @@ def inverted_idoms(graph: networkx.DiGraph) -> tuple[networkx.DiGraph, dict | No
55
55
 
56
56
 
57
57
  def to_acyclic_graph(
58
- graph: networkx.DiGraph, ordered_nodes: list | None = None, loop_heads: list | None = None
58
+ graph: networkx.DiGraph, node_order: dict[Any, int] | None = None, loop_heads: list | None = None
59
59
  ) -> networkx.DiGraph:
60
60
  """
61
61
  Convert a given DiGraph into an acyclic graph.
@@ -66,21 +66,22 @@ def to_acyclic_graph(
66
66
  :return: The converted acyclic graph.
67
67
  """
68
68
 
69
- if ordered_nodes is None:
69
+ if node_order is None:
70
70
  # take the quasi-topological order of the graph
71
71
  ordered_nodes = GraphUtils.quasi_topological_sort_nodes(graph, loop_heads=loop_heads)
72
-
73
- acyclic_graph = networkx.DiGraph()
72
+ node_order = {n: i for i, n in enumerate(ordered_nodes)}
74
73
 
75
74
  # add each node and its edge into the graph
76
- visited = set()
77
- for node in ordered_nodes:
78
- visited.add(node)
79
- acyclic_graph.add_node(node)
80
- for successor in graph.successors(node):
81
- if successor not in visited:
82
- acyclic_graph.add_edge(node, successor)
83
-
75
+ edges_to_remove = []
76
+ for src, dst in graph.edges():
77
+ src_order = node_order[src]
78
+ dst_order = node_order[dst]
79
+ if src_order > dst_order:
80
+ # this is a back edge, we need to remove it
81
+ edges_to_remove.append((src, dst))
82
+
83
+ acyclic_graph = graph.copy()
84
+ acyclic_graph.remove_edges_from(edges_to_remove)
84
85
  return acyclic_graph
85
86
 
86
87
 
@@ -672,6 +673,21 @@ class GraphUtils:
672
673
 
673
674
  return list(widening_addrs)
674
675
 
676
+ @staticmethod
677
+ def dfs_postorder_nodes_deterministic(graph: networkx.DiGraph, source):
678
+ visited = set()
679
+ stack = [source]
680
+ while stack:
681
+ node = stack[-1]
682
+ if node not in visited:
683
+ visited.add(node)
684
+ for succ in sorted(graph.successors(node), key=GraphUtils._sort_node):
685
+ if succ not in visited:
686
+ stack.append(succ)
687
+ else:
688
+ yield node
689
+ stack.pop()
690
+
675
691
  @staticmethod
676
692
  def reverse_post_order_sort_nodes(graph, nodes=None):
677
693
  """
angr/utils/library.py CHANGED
@@ -168,7 +168,7 @@ def parsedcprotos2py(
168
168
  proto_.returnty = SimTypeFd(label=proto_.returnty.label)
169
169
  for i, arg in enumerate(proto_.args):
170
170
  if (func_name, i) in fd_spots:
171
- proto_.args = proto_.args[:i] + (SimTypeFd(label=arg.label),) + proto_.args[i + 1 :]
171
+ proto_.args = (*proto_.args[:i], SimTypeFd(label=arg.label), *proto_.args[i + 1 :])
172
172
 
173
173
  line1 = " " * 8 + "#" + ((" " + decl) if decl else "") + "\n"
174
174
  line2 = " " * 8 + repr(func_name) + ": " + (proto_._init_str() if proto_ is not None else "None") + "," + "\n"
@@ -195,17 +195,18 @@ def cprotos2py(cprotos: list[str], fd_spots=frozenset(), remove_sys_prefix=False
195
195
  return parsedcprotos2py(parsed_cprotos, fd_spots=fd_spots, remove_sys_prefix=remove_sys_prefix)
196
196
 
197
197
 
198
- def get_cpp_function_name(demangled_name, specialized=True, qualified=True):
199
- # remove "<???>"s
200
- name = normalize_cpp_function_name(demangled_name) if not specialized else demangled_name
198
+ def get_cpp_function_name(demangled_name: str) -> str:
199
+ """
200
+ Parse a demangled C++ declaration into a function name.
201
201
 
202
- if not qualified:
203
- # remove leading namespaces
204
- chunks = name.split("::")
205
- name = "::".join(chunks[2:])
202
+ Note that the extracted name may include template instantiation, for example:
206
203
 
207
- # remove arguments
208
- if "(" in name:
209
- name = name[: name.find("(")]
204
+ example_func<int>
210
205
 
211
- return name
206
+ :param demangled_name: The demangled C++ function name.
207
+ :return: The qualified function name, excluding return type and parameters.
208
+ """
209
+ func_decls, _ = parse_cpp_file(demangled_name)
210
+ if func_decls and len(func_decls) == 1:
211
+ return next(iter(func_decls))
212
+ return normalize_cpp_function_name(demangled_name)
@@ -6,7 +6,7 @@ from typing import Any, Literal, overload
6
6
  import networkx
7
7
 
8
8
  import archinfo
9
- from angr.ailment import Expression, Block
9
+ from angr.ailment import Expression, Block, UnaryOp
10
10
  from angr.ailment.expression import (
11
11
  VirtualVariable,
12
12
  Const,
@@ -42,7 +42,7 @@ def get_reg_offset_base_and_size(
42
42
 
43
43
  def get_reg_offset_base_and_size(
44
44
  reg_offset: int, arch: archinfo.Arch, size: int | None = None, resilient: bool = True
45
- ) -> tuple[int, int] | None:
45
+ ) -> tuple[int, int | None] | None:
46
46
  """
47
47
  Translate a given register offset into the offset of its full register and obtain the size of the full register.
48
48
 
@@ -278,6 +278,31 @@ def has_tmp_expr(expr: Expression) -> bool:
278
278
  return walker.has_blacklisted_exprs
279
279
 
280
280
 
281
+ class AILReferenceFinder(AILBlockWalkerBase):
282
+ """
283
+ Walks an AIL expression or statement and finds if it contains references to certain expressions.
284
+ """
285
+
286
+ def __init__(self, vvar_id: int):
287
+ super().__init__()
288
+ self.vvar_id = vvar_id
289
+ self.has_references_to_vvar = False
290
+
291
+ def _handle_UnaryOp(
292
+ self, expr_idx: int, expr: UnaryOp, stmt_idx: int, stmt: Statement | None, block: Block | None
293
+ ) -> Any:
294
+ if expr.op == "Reference" and isinstance(expr.operand, VirtualVariable) and expr.operand.varid == self.vvar_id:
295
+ self.has_references_to_vvar = True
296
+ return None
297
+ return super()._handle_UnaryOp(expr_idx, expr, stmt_idx, stmt, block)
298
+
299
+
300
+ def has_reference_to_vvar(stmt: Statement, vvar_id: int) -> bool:
301
+ walker = AILReferenceFinder(vvar_id)
302
+ walker.walk_statement(stmt)
303
+ return walker.has_references_to_vvar
304
+
305
+
281
306
  def check_in_between_stmts(
282
307
  graph: networkx.DiGraph,
283
308
  blocks: dict[tuple[int, int | None], Block],
@@ -358,6 +383,33 @@ def has_load_expr_in_between_stmts(
358
383
  )
359
384
 
360
385
 
386
+ def is_vvar_propagatable(vvar: VirtualVariable, def_stmt: Statement | None) -> bool:
387
+ if vvar.was_tmp or vvar.was_reg or vvar.was_parameter:
388
+ return True
389
+ if vvar.was_stack and isinstance(def_stmt, Assignment):
390
+ if isinstance(def_stmt.src, Const):
391
+ return True
392
+ if (
393
+ isinstance(def_stmt.src, VirtualVariable)
394
+ and def_stmt.src.was_stack
395
+ and def_stmt.src.stack_offset == vvar.stack_offset
396
+ ):
397
+ # special case: the following block
398
+ # ## Block 401e98
399
+ # 00 | 0x401e98 | LABEL_401e98:
400
+ # 01 | 0x401e98 | vvar_227{stack -12} = 𝜙@32b [((4202088, None), vvar_277{stack -12}), ((4202076, None),
401
+ # vvar_278{stack -12})]
402
+ # 02 | 0x401ea0 | return Conv(32->64, vvar_227{stack -12});
403
+ # might be simplified to the following block after return duplication
404
+ # ## Block 401e98.1
405
+ # 00 | 0x401e98 | LABEL_401e98__1:
406
+ # 01 | 0x401e98 | vvar_279{stack -12} = vvar_277{stack -12}
407
+ # 02 | 0x401ea0 | return Conv(32->64, vvar_279{stack -12});
408
+ # in this case, vvar_279 is eliminatable.
409
+ return True
410
+ return False
411
+
412
+
361
413
  def is_vvar_eliminatable(vvar: VirtualVariable, def_stmt: Statement | None) -> bool:
362
414
  if vvar.was_tmp or vvar.was_reg or vvar.was_parameter:
363
415
  return True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: angr
3
- Version: 9.2.161
3
+ Version: 9.2.163
4
4
  Summary: A multi-architecture binary analysis toolkit, with the ability to perform dynamic symbolic execution and various static analyses on binaries
5
5
  License: BSD-2-Clause
6
6
  Project-URL: Homepage, https://angr.io/
@@ -16,12 +16,12 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: cxxheaderparser
18
18
  Requires-Dist: GitPython
19
- Requires-Dist: archinfo==9.2.161
19
+ Requires-Dist: archinfo==9.2.163
20
20
  Requires-Dist: cachetools
21
21
  Requires-Dist: capstone==5.0.3
22
22
  Requires-Dist: cffi>=1.14.0
23
- Requires-Dist: claripy==9.2.161
24
- Requires-Dist: cle==9.2.161
23
+ Requires-Dist: claripy==9.2.163
24
+ Requires-Dist: cle==9.2.163
25
25
  Requires-Dist: mulpyplexer
26
26
  Requires-Dist: networkx!=2.8.1,>=2.0
27
27
  Requires-Dist: protobuf>=5.28.2
@@ -30,7 +30,7 @@ Requires-Dist: pycparser>=2.18
30
30
  Requires-Dist: pydemumble
31
31
  Requires-Dist: pyformlang
32
32
  Requires-Dist: pypcode<4.0,>=3.2.1
33
- Requires-Dist: pyvex==9.2.161
33
+ Requires-Dist: pyvex==9.2.163
34
34
  Requires-Dist: rich>=13.1.0
35
35
  Requires-Dist: sortedcontainers
36
36
  Requires-Dist: sympy