angr 9.2.146__py3-none-manylinux2014_aarch64.whl → 9.2.148__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 (66) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/analysis.py +3 -11
  3. angr/analyses/bindiff.py +343 -68
  4. angr/analyses/calling_convention/fact_collector.py +5 -4
  5. angr/analyses/calling_convention/utils.py +1 -0
  6. angr/analyses/cfg/cfg_arch_options.py +10 -0
  7. angr/analyses/cfg/cfg_base.py +42 -74
  8. angr/analyses/cfg/cfg_emulated.py +12 -12
  9. angr/analyses/cfg/cfg_fast.py +39 -20
  10. angr/analyses/cfg/cfg_fast_soot.py +3 -3
  11. angr/analyses/decompiler/callsite_maker.py +28 -18
  12. angr/analyses/decompiler/clinic.py +4 -4
  13. angr/analyses/decompiler/condition_processor.py +0 -21
  14. angr/analyses/decompiler/counters/call_counter.py +3 -0
  15. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +1 -1
  16. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +14 -0
  17. angr/analyses/decompiler/structured_codegen/c.py +5 -5
  18. angr/analyses/decompiler/structuring/phoenix.py +11 -3
  19. angr/analyses/deobfuscator/api_obf_finder.py +5 -1
  20. angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +1 -1
  21. angr/analyses/flirt/__init__.py +47 -0
  22. angr/analyses/flirt/consts.py +160 -0
  23. angr/analyses/{flirt.py → flirt/flirt.py} +99 -38
  24. angr/analyses/flirt/flirt_function.py +20 -0
  25. angr/analyses/flirt/flirt_matcher.py +351 -0
  26. angr/analyses/flirt/flirt_module.py +32 -0
  27. angr/analyses/flirt/flirt_node.py +23 -0
  28. angr/analyses/flirt/flirt_sig.py +356 -0
  29. angr/analyses/flirt/flirt_utils.py +31 -0
  30. angr/analyses/forward_analysis/visitors/graph.py +0 -8
  31. angr/analyses/identifier/runner.py +1 -1
  32. angr/analyses/reaching_definitions/function_handler.py +4 -4
  33. angr/analyses/reassembler.py +1 -1
  34. angr/analyses/stack_pointer_tracker.py +35 -1
  35. angr/analyses/static_hooker.py +11 -9
  36. angr/analyses/variable_recovery/engine_ail.py +8 -8
  37. angr/analyses/variable_recovery/engine_base.py +2 -0
  38. angr/block.py +6 -6
  39. angr/calling_conventions.py +74 -23
  40. angr/engines/vex/heavy/concretizers.py +10 -0
  41. angr/exploration_techniques/director.py +1 -1
  42. angr/flirt/__init__.py +15 -44
  43. angr/knowledge_plugins/functions/function.py +42 -39
  44. angr/knowledge_plugins/functions/function_manager.py +9 -0
  45. angr/knowledge_plugins/functions/function_parser.py +9 -1
  46. angr/knowledge_plugins/functions/soot_function.py +1 -1
  47. angr/knowledge_plugins/key_definitions/key_definition_manager.py +1 -1
  48. angr/procedures/definitions/__init__.py +14 -11
  49. angr/procedures/stubs/format_parser.py +1 -1
  50. angr/project.py +23 -29
  51. angr/protos/cfg_pb2.py +14 -25
  52. angr/protos/function_pb2.py +11 -22
  53. angr/protos/primitives_pb2.py +36 -47
  54. angr/protos/variables_pb2.py +28 -39
  55. angr/protos/xrefs_pb2.py +8 -19
  56. angr/sim_type.py +0 -16
  57. angr/simos/cgc.py +1 -1
  58. angr/simos/linux.py +5 -5
  59. angr/simos/windows.py +5 -5
  60. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +1 -1
  61. {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/METADATA +8 -8
  62. {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/RECORD +66 -58
  63. {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/WHEEL +1 -1
  64. {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/entry_points.txt +0 -0
  65. {angr-9.2.146.dist-info → angr-9.2.148.dist-info/licenses}/LICENSE +0 -0
  66. {angr-9.2.146.dist-info → angr-9.2.148.dist-info}/top_level.txt +0 -0
@@ -17,7 +17,6 @@ from archinfo.arch_arm import is_arm_arch, get_real_address_if_arm
17
17
  from angr.knowledge_plugins.functions.function_manager import FunctionManager
18
18
  from angr.knowledge_plugins.functions.function import Function
19
19
  from angr.knowledge_plugins.cfg import IndirectJump, CFGNode, CFGENode, CFGModel # pylint:disable=unused-import
20
- from angr.misc.ux import deprecated
21
20
  from angr.procedures.stubs.UnresolvableJumpTarget import UnresolvableJumpTarget
22
21
  from angr.utils.constants import DEFAULT_STATEMENT
23
22
  from angr.procedures.procedure_dict import SIM_PROCEDURES
@@ -354,64 +353,9 @@ class CFGBase(Analysis):
354
353
 
355
354
  raise NotImplementedError("I'm too lazy to implement it right now")
356
355
 
357
- @deprecated(replacement="self.model.get_predecessors()")
358
- def get_predecessors(self, cfgnode, excluding_fakeret=True, jumpkind=None):
359
- return self._model.get_predecessors(cfgnode, excluding_fakeret=excluding_fakeret, jumpkind=jumpkind)
360
-
361
- @deprecated(replacement="self.model.get_successors()")
362
- def get_successors(self, node, excluding_fakeret=True, jumpkind=None):
363
- return self._model.get_successors(node, excluding_fakeret=excluding_fakeret, jumpkind=jumpkind)
364
-
365
- @deprecated(replacement="self.model.get_successors_and_jumpkind")
366
- def get_successors_and_jumpkind(self, node, excluding_fakeret=True):
367
- return self._model.get_successors_and_jumpkind(node, excluding_fakeret=excluding_fakeret)
368
-
369
- @deprecated(replacement="self.model.get_all_predecessors()")
370
- def get_all_predecessors(self, cfgnode, depth_limit=None):
371
- return self._model.get_all_predecessors(cfgnode, depth_limit)
372
-
373
- @deprecated(replacement="self.model.get_all_successors()")
374
- def get_all_successors(self, cfgnode, depth_limit=None):
375
- return self._model.get_all_successors(cfgnode, depth_limit)
376
-
377
- @deprecated(replacement="self.model.get_node()")
378
- def get_node(self, block_id):
379
- return self._model.get_node(block_id)
380
-
381
- @deprecated(replacement="self.model.get_any_node()")
382
- def get_any_node(self, addr, is_syscall=None, anyaddr=False, force_fastpath=False):
383
- return self._model.get_any_node(addr, is_syscall=is_syscall, anyaddr=anyaddr, force_fastpath=force_fastpath)
384
-
385
- @deprecated(replacement="self.model.get_all_nodes()")
386
- def get_all_nodes(self, addr, is_syscall=None, anyaddr=False):
387
- return self._model.get_all_nodes(addr, is_syscall=is_syscall, anyaddr=anyaddr)
388
-
389
- @deprecated(replacement="self.model.nodes()")
390
- def nodes(self):
391
- return self._model.nodes()
392
-
393
- @deprecated(replacement="nodes")
394
- def nodes_iter(self):
395
- """
396
- (Decrepated) An iterator of all nodes in the graph. Will be removed in the future.
397
-
398
- :return: The iterator.
399
- :rtype: iterator
400
- """
401
-
402
- return self.nodes()
403
-
404
356
  def get_loop_back_edges(self):
405
357
  return self._loop_back_edges
406
358
 
407
- @deprecated(replacement="self.model.get_branching_nodes()")
408
- def get_branching_nodes(self):
409
- return self._model.get_branching_nodes()
410
-
411
- @deprecated(replacement="self.model.get_exit_stmt_idx")
412
- def get_exit_stmt_idx(self, src_block, dst_block):
413
- return self._model.get_exit_stmt_idx(src_block, dst_block)
414
-
415
359
  @property
416
360
  def graph(self) -> networkx.DiGraph[CFGNode]:
417
361
  raise NotImplementedError
@@ -1550,10 +1494,12 @@ class CFGBase(Analysis):
1550
1494
  block = next((b for b in function.blocks), None)
1551
1495
  if block is None:
1552
1496
  continue
1553
- if all(self._is_noop_insn(insn) for insn in block.capstone.insns):
1497
+ if self._is_noop_block(self.project.arch, block) or all(
1498
+ self._is_noop_insn(insn) for insn in block.capstone.insns
1499
+ ):
1554
1500
  # all nops. mark this function as a function alignment
1555
1501
  l.debug("Function chunk %#x is probably used as a function alignment (all nops).", func_addr)
1556
- self.kb.functions[func_addr].alignment = True
1502
+ self.kb.functions[func_addr].is_alignment = True
1557
1503
  continue
1558
1504
  node = function.get_node(block.addr)
1559
1505
  assert node is not None
@@ -1561,7 +1507,7 @@ class CFGBase(Analysis):
1561
1507
  if len(successors) == 1 and successors[0].addr == node.addr:
1562
1508
  # self loop. mark this function as a function alignment
1563
1509
  l.debug("Function chunk %#x is probably used as a function alignment (self-loop).", func_addr)
1564
- self.kb.functions[func_addr].alignment = True
1510
+ self.kb.functions[func_addr].is_alignment = True
1565
1511
  continue
1566
1512
 
1567
1513
  def make_functions(self):
@@ -2108,7 +2054,7 @@ class CFGBase(Analysis):
2108
2054
  continue
2109
2055
  if func_addr in jumptable_entries:
2110
2056
  # is there any call edge pointing to it?
2111
- func_node = self.get_any_node(func_addr, force_fastpath=True)
2057
+ func_node = self.model.get_any_node(func_addr, force_fastpath=True)
2112
2058
  if func_node is not None:
2113
2059
  in_edges = self.graph.in_edges(func_node, data=True)
2114
2060
  has_transition_pred = None
@@ -2205,10 +2151,6 @@ class CFGBase(Analysis):
2205
2151
  out_edges = [e for e in g.out_edges(node_) if g.get_edge_data(*e)["jumpkind"] != "Ijk_FakeRet"]
2206
2152
  return len(out_edges) > 1
2207
2153
 
2208
- if len(src_function.block_addrs_set) > 10:
2209
- # ignore functions unless they are extremely small
2210
- return False
2211
-
2212
2154
  if len(all_edges) == 1 and dst_addr != src_addr:
2213
2155
  the_edge = next(iter(all_edges))
2214
2156
  _, dst, data = the_edge
@@ -2251,15 +2193,41 @@ class CFGBase(Analysis):
2251
2193
  candidate = True
2252
2194
 
2253
2195
  if candidate:
2254
- regs = {self.project.arch.sp_offset}
2255
- if hasattr(self.project.arch, "bp_offset") and self.project.arch.bp_offset is not None:
2256
- regs.add(self.project.arch.bp_offset)
2257
- sptracker = self.project.analyses[StackPointerTracker].prep()(
2258
- src_function, regs, track_memory=self._sp_tracking_track_memory
2259
- )
2260
- sp_delta = sptracker.offset_after_block(src_addr, self.project.arch.sp_offset)
2261
- if sp_delta == 0:
2262
- return True
2196
+ # we have two strategies; for small functions, we run SPTracker on the entire function and see if the
2197
+ # stack pointer changes or not; for large functions, we simply detect how far away we jump as well as
2198
+ # if there are any other functions identified between the source and the destination.
2199
+ if len(src_function.block_addrs_set) <= 10:
2200
+ regs = {self.project.arch.sp_offset}
2201
+ if hasattr(self.project.arch, "bp_offset") and self.project.arch.bp_offset is not None:
2202
+ regs.add(self.project.arch.bp_offset)
2203
+ sptracker = self.project.analyses[StackPointerTracker].prep()(
2204
+ src_function, regs, track_memory=self._sp_tracking_track_memory
2205
+ )
2206
+ sp_delta = sptracker.offset_after_block(src_addr, self.project.arch.sp_offset)
2207
+ if sp_delta == 0:
2208
+ return True
2209
+ else:
2210
+ # large function; to speed things up, we don't track sp
2211
+ minaddr, maxaddr = None, None
2212
+ if dst_addr - src_addr >= 0x100:
2213
+ minaddr = src_addr
2214
+ maxaddr = dst_addr
2215
+ elif dst_addr < src_addr:
2216
+ # jumping back; is it jumping beyond the function header?
2217
+ src_func = blockaddr_to_function[src_addr]
2218
+ if dst_addr < src_func.addr and src_func.addr - dst_addr >= 0x100:
2219
+ minaddr = dst_addr
2220
+ maxaddr = src_func.addr
2221
+
2222
+ if minaddr is not None and maxaddr is not None:
2223
+ # are there other function in between?
2224
+ funcaddrs_in_between = list(
2225
+ known_functions._function_map.irange(minimum=minaddr + 1, maximum=maxaddr - 1)
2226
+ )
2227
+ funcs_in_between = [known_functions.get_by_addr(a) for a in funcaddrs_in_between]
2228
+ funcs_in_between = [func for func in funcs_in_between if not func.is_alignment]
2229
+ if len(funcs_in_between) >= 3:
2230
+ return True
2263
2231
 
2264
2232
  return False
2265
2233
 
@@ -2639,7 +2607,7 @@ class CFGBase(Analysis):
2639
2607
  :return: True if the instruction does no-op, False otherwise.
2640
2608
  """
2641
2609
 
2642
- insn_name = insn.insn_name()
2610
+ insn_name = insn.mnemonic
2643
2611
 
2644
2612
  if insn_name == "nop":
2645
2613
  # nops
@@ -472,7 +472,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
472
472
  src_blocknode: BlockNode = back_edge[0]
473
473
  dst_blocknode: BlockNode = back_edge[1]
474
474
 
475
- for src in self.get_all_nodes(src_blocknode.addr):
475
+ for src in self.model.get_all_nodes(src_blocknode.addr):
476
476
  for dst in graph.successors(src):
477
477
  if dst.addr != dst_blocknode.addr:
478
478
  continue
@@ -524,7 +524,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
524
524
  start = self._starts[0]
525
525
  if isinstance(start, tuple):
526
526
  start, _ = start # pylint: disable=unpacking-non-sequence
527
- start_node = self.get_any_node(start)
527
+ start_node = self.model.get_any_node(start)
528
528
  if start_node is None:
529
529
  raise AngrCFGError("Cannot find start node when trying to unroll loops. The CFG might be empty.")
530
530
 
@@ -720,7 +720,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
720
720
  # FIXME: syscalls are not supported
721
721
  # FIXME: start should also take a CFGNode instance
722
722
 
723
- start_node = self.get_any_node(start)
723
+ start_node = self.model.get_any_node(start)
724
724
  assert start_node is not None
725
725
 
726
726
  node_wrapper = (start_node, 0)
@@ -2286,9 +2286,9 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
2286
2286
  # Remove that edge!
2287
2287
  graph.remove_edge(call_func_addr, return_to_addr)
2288
2288
  # Remove the edge in CFG
2289
- nodes = self.get_all_nodes(callsite_block_addr)
2289
+ nodes = self.model.get_all_nodes(callsite_block_addr)
2290
2290
  for n in nodes:
2291
- successors = self.get_successors_and_jumpkind(n, excluding_fakeret=False)
2291
+ successors = self.model.get_successors_and_jumpkind(n, excluding_fakeret=False)
2292
2292
  for successor, jumpkind in successors:
2293
2293
  if jumpkind == "Ijk_FakeRet" and successor.addr == return_to_addr:
2294
2294
  self.remove_edge(n, successor)
@@ -2548,7 +2548,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
2548
2548
  for start in starts:
2549
2549
  l.debug("Start symbolic execution at 0x%x on program slice.", start)
2550
2550
  # Get the state from our CFG
2551
- node = self.get_any_node(start)
2551
+ node = self.model.get_any_node(start)
2552
2552
  if node is None:
2553
2553
  # Well, we have to live with an empty state
2554
2554
  base_state = self.project.factory.blank_state(addr=start)
@@ -2561,7 +2561,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
2561
2561
  initial_nodes = [n for n in bc.taint_graph.nodes() if bc.taint_graph.in_degree(n) == 0]
2562
2562
  for cl in initial_nodes:
2563
2563
  # Iterate in all actions of this node, and pick corresponding actions
2564
- cfg_nodes = self.get_all_nodes(cl.block_addr)
2564
+ cfg_nodes = self.model.get_all_nodes(cl.block_addr)
2565
2565
  for n in cfg_nodes:
2566
2566
  if not n.final_states:
2567
2567
  continue
@@ -3376,9 +3376,9 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
3376
3376
 
3377
3377
  all_predecessors = []
3378
3378
 
3379
- nodes = self.get_all_nodes(function_address)
3379
+ nodes = self.model.get_all_nodes(function_address)
3380
3380
  for n in nodes:
3381
- predecessors = list(self.get_predecessors(n))
3381
+ predecessors = list(self.model.get_predecessors(n))
3382
3382
  all_predecessors.extend(predecessors)
3383
3383
 
3384
3384
  return all_predecessors
@@ -3391,8 +3391,8 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
3391
3391
  Return: a list of lists of nodes representing paths.
3392
3392
  """
3393
3393
  if isinstance(begin, int) and isinstance(end, int):
3394
- n_begin = self.get_any_node(begin)
3395
- n_end = self.get_any_node(end)
3394
+ n_begin = self.model.get_any_node(begin)
3395
+ n_end = self.model.get_any_node(end)
3396
3396
 
3397
3397
  elif isinstance(begin, CFGENode) and isinstance(end, CFGENode):
3398
3398
  n_begin = begin
@@ -3417,7 +3417,7 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
3417
3417
 
3418
3418
  for ep in self._entry_points:
3419
3419
  # FIXME: This is not always correct. We'd better store CFGNodes in self._entry_points
3420
- ep_node = self.get_any_node(ep)
3420
+ ep_node = self.model.get_any_node(ep)
3421
3421
 
3422
3422
  if not ep_node:
3423
3423
  continue
@@ -22,10 +22,10 @@ from archinfo.arch_soot import SootAddressDescriptor
22
22
  from archinfo.arch_arm import is_arm_arch, get_real_address_if_arm
23
23
 
24
24
  from angr.analyses import AnalysesHub
25
+ from angr.misc.ux import once
25
26
  from angr.knowledge_plugins.cfg import CFGNode, MemoryDataSort, MemoryData, IndirectJump, IndirectJumpType
26
27
  from angr.knowledge_plugins.xrefs import XRef, XRefType
27
28
  from angr.knowledge_plugins.functions import Function
28
- from angr.misc.ux import deprecated
29
29
  from angr.codenode import HookNode
30
30
  from angr import sim_options as o
31
31
  from angr.errors import (
@@ -589,10 +589,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
589
589
  regions=None,
590
590
  pickle_intermediate_results=False,
591
591
  symbols=True,
592
- function_prologues=True,
592
+ function_prologues: bool | None = None,
593
593
  resolve_indirect_jumps=True,
594
594
  force_segment=False,
595
- force_smart_scan=True,
595
+ force_smart_scan: bool | None = None,
596
596
  force_complete_scan=False,
597
597
  indirect_jump_target_limit=100000,
598
598
  data_references=True,
@@ -712,6 +712,20 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
712
712
  if binary is not None and not objects:
713
713
  objects = [binary]
714
714
 
715
+ is_dotnet = (
716
+ isinstance(self.project.loader.main_object, cle.backends.pe.PE)
717
+ and self.project.loader.main_object.is_dotnet
718
+ )
719
+
720
+ if function_prologues is None:
721
+ function_prologues = not is_dotnet
722
+ if is_dotnet and once("dotnet_native"):
723
+ l.warning("You're trying to analyze a .NET binary as native code. Are you sure?")
724
+ if force_smart_scan is None:
725
+ force_smart_scan = not is_dotnet
726
+ if is_dotnet and once("dotnet_native"):
727
+ l.warning("You're trying to analyze a .NET binary as native code. Are you sure?")
728
+
715
729
  CFGBase.__init__(
716
730
  self,
717
731
  "fast",
@@ -1649,6 +1663,13 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1649
1663
  if addr is not None:
1650
1664
  # if this is ARM and addr % 4 != 0, it has to be THUMB
1651
1665
  if is_arm_arch(self.project.arch):
1666
+ if (
1667
+ "has_arm_code" in self._arch_options
1668
+ and self._arch_options["has_arm_code"] is False
1669
+ and addr % 2 == 0
1670
+ ):
1671
+ addr |= 1
1672
+
1652
1673
  if addr % 2 == 0 and addr % 4 != 0:
1653
1674
  # it's not aligned by 4, so it's definitely not ARM mode
1654
1675
  addr |= 1
@@ -1968,9 +1989,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1968
1989
 
1969
1990
  # Pre-compile all regexes
1970
1991
  regexes = []
1971
- for ins_regex in self.project.arch.function_prologs:
1972
- r = re.compile(ins_regex)
1973
- regexes.append(r)
1992
+ if "has_arm_code" not in self._arch_options or self._arch_options["has_arm_code"]:
1993
+ for ins_regex in self.project.arch.function_prologs:
1994
+ r = re.compile(ins_regex)
1995
+ regexes.append(r)
1974
1996
  # EDG says: I challenge anyone bothering to read this to come up with a better
1975
1997
  # way to handle CPU modes that affect instruction decoding.
1976
1998
  # Since the only one we care about is ARM/Thumb right now
@@ -2109,6 +2131,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
2109
2131
  src_func = self.functions.function(addr=cfg_job.src_node.addr, create=True)
2110
2132
  else:
2111
2133
  src_func = self.functions.get_by_addr(cfg_job.src_node.addr)
2134
+ assert src_func is not None
2112
2135
  if len(src_func.block_addrs_set) <= 1 and src_func.is_default_name:
2113
2136
  # assign a name to the caller function that jumps to this procedure
2114
2137
  src_func.name = procedure.display_name
@@ -2832,6 +2855,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
2832
2855
  and not self._seg_list.is_occupied(v)
2833
2856
  and v % self.project.arch.instruction_alignment == 0
2834
2857
  ):
2858
+ if is_arm_arch(self.project.arch) and not self._arch_options.has_arm_code and v % 2 != 1:
2859
+ # no ARM code in this binary!
2860
+ return
2861
+
2835
2862
  # create a new CFG job
2836
2863
  ce = CFGJob(
2837
2864
  v,
@@ -3831,6 +3858,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
3831
3858
 
3832
3859
  for ep in endpoints:
3833
3860
  src = self.model.get_any_node(ep.addr)
3861
+ assert src is not None
3834
3862
  for rt in return_targets:
3835
3863
  if not src.instruction_addrs:
3836
3864
  ins_addr = None
@@ -4321,7 +4349,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4321
4349
 
4322
4350
  # extra check for ARM
4323
4351
  if is_arm_arch(self.project.arch) and self._seg_list.occupied_by_sort(addr) == "code":
4324
- existing_node = self.get_any_node(addr, anyaddr=True)
4352
+ existing_node = self.model.get_any_node(addr, anyaddr=True)
4325
4353
  if existing_node is not None and (addr & 1) != (existing_node.addr & 1):
4326
4354
  # we are trying to break an existing ARM node with a THUMB node, or vice versa
4327
4355
  # this is probably because our current node is unexpected
@@ -4430,6 +4458,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4430
4458
  self._cascading_remove_lifted_blocks(cfg_job.src_node.addr & 0xFFFF_FFFE)
4431
4459
  return None, None, None, None
4432
4460
 
4461
+ if not self._arch_options.has_arm_code and addr % 2 == 0:
4462
+ # No ARM code for this architecture!
4463
+ return None, None, None, None
4464
+
4433
4465
  initial_regs = self._get_initial_registers(addr, cfg_job, current_function_addr)
4434
4466
 
4435
4467
  # Let's try to create the pyvex IRSB directly, since it's much faster
@@ -5196,18 +5228,5 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
5196
5228
  def output(self):
5197
5229
  return f"{self._graph.edges(data=True)}"
5198
5230
 
5199
- @deprecated(replacement="angr.analyses.CFB")
5200
- def generate_code_cover(self):
5201
- """
5202
- Generate a list of all recovered basic blocks.
5203
- """
5204
-
5205
- lst = []
5206
- for cfg_node in self.graph.nodes():
5207
- size = cfg_node.size
5208
- lst.append((cfg_node.addr, size))
5209
-
5210
- return sorted(lst, key=lambda x: x[0])
5211
-
5212
5231
 
5213
5232
  AnalysesHub.register_default("CFGFast", CFGFast)
@@ -487,7 +487,7 @@ class CFGFastSoot(CFGFast):
487
487
  # it might be a jumpout
488
488
  target_func_addr = None
489
489
  if target_addr in self._traced_addresses:
490
- node = self.get_any_node(target_addr)
490
+ node = self.model.get_any_node(target_addr)
491
491
  if node is not None:
492
492
  target_func_addr = node.function_address
493
493
  if target_func_addr is None:
@@ -578,7 +578,7 @@ class CFGFastSoot(CFGFast):
578
578
  if jumpkind == "Ijk_Call" or jumpkind.startswith("Ijk_Sys"):
579
579
  function_nodes.add(dst)
580
580
 
581
- entry_node = self.get_any_node(self._binary.entry)
581
+ entry_node = self.model.get_any_node(self._binary.entry)
582
582
  if entry_node is not None:
583
583
  function_nodes.add(entry_node)
584
584
 
@@ -616,7 +616,7 @@ class CFGFastSoot(CFGFast):
616
616
  secondary_function_nodes = set()
617
617
  # add all function chunks ("functions" that are not called from anywhere)
618
618
  for func_addr in tmp_functions:
619
- node = self.get_any_node(func_addr)
619
+ node = self.model.get_any_node(func_addr)
620
620
  if node is None:
621
621
  continue
622
622
  if node.addr not in blockaddr_to_function:
@@ -18,7 +18,7 @@ from angr.sim_type import (
18
18
  SimTypeFunction,
19
19
  SimTypeLongLong,
20
20
  )
21
- from angr.calling_conventions import SimRegArg, SimStackArg, SimCC, SimStructArg, SimComboArg
21
+ from angr.calling_conventions import SimReferenceArgument, SimRegArg, SimStackArg, SimCC, SimStructArg, SimComboArg
22
22
  from angr.knowledge_plugins.key_definitions.constants import OP_BEFORE
23
23
  from angr.analyses import Analysis, register_analysis
24
24
  from angr.analyses.s_reaching_definitions import SRDAView
@@ -111,10 +111,10 @@ class CallSiteMaker(Analysis):
111
111
  prototype_libname = func.prototype_libname
112
112
  type_collections = []
113
113
  if prototype_libname is not None:
114
- prototype_lib = SIM_LIBRARIES[prototype_libname]
115
- if prototype_lib.type_collection_names:
116
- for typelib_name in prototype_lib.type_collection_names:
117
- type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
114
+ for prototype_lib in SIM_LIBRARIES[prototype_libname]:
115
+ if prototype_lib.type_collection_names:
116
+ for typelib_name in prototype_lib.type_collection_names:
117
+ type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
118
118
  if type_collections:
119
119
  prototype = dereference_simtype(prototype, type_collections).with_arch( # type: ignore
120
120
  self.project.arch
@@ -144,17 +144,30 @@ class CallSiteMaker(Analysis):
144
144
  arg_locs = cc.arg_locs(callsite_ty)
145
145
 
146
146
  if arg_locs is not None and cc is not None:
147
- expanded_arg_locs = []
147
+ expanded_arg_locs: list[SimStackArg | SimRegArg | SimReferenceArgument] = []
148
148
  for arg_loc in arg_locs:
149
149
  if isinstance(arg_loc, SimComboArg):
150
150
  # a ComboArg spans across multiple locations (mostly stack but *in theory* can also be spanning
151
151
  # across registers). most importantly, a ComboArg represents one variable, not multiple, but we
152
152
  # have no way to know that until later down the pipeline.
153
153
  expanded_arg_locs += arg_loc.locations
154
- else:
154
+ elif isinstance(arg_loc, (SimRegArg, SimStackArg, SimReferenceArgument)):
155
155
  expanded_arg_locs.append(arg_loc)
156
+ else:
157
+ raise NotImplementedError("Not implemented yet.")
156
158
 
157
159
  for arg_loc in expanded_arg_locs:
160
+ if isinstance(arg_loc, SimReferenceArgument):
161
+ if not isinstance(arg_loc.ptr_loc, (SimRegArg, SimStackArg)):
162
+ raise NotImplementedError("Why would a calling convention produce this?")
163
+ if isinstance(arg_loc.main_loc, SimStructArg):
164
+ dereference_size = arg_loc.main_loc.struct.size // self.project.arch.byte_width
165
+ else:
166
+ dereference_size = arg_loc.main_loc.size
167
+ arg_loc = arg_loc.ptr_loc
168
+ else:
169
+ dereference_size = None
170
+
158
171
  if isinstance(arg_loc, SimRegArg):
159
172
  size = arg_loc.size
160
173
  offset = arg_loc.check_offset(cc.arch)
@@ -202,7 +215,7 @@ class CallSiteMaker(Analysis):
202
215
  vvar_use,
203
216
  **vvar_use.tags,
204
217
  )
205
- args.append(vvar_use)
218
+ arg_expr = vvar_use
206
219
  else:
207
220
  reg = Expr.Register(
208
221
  self._atom_idx(),
@@ -212,20 +225,17 @@ class CallSiteMaker(Analysis):
212
225
  reg_name=arg_loc.reg_name,
213
226
  ins_addr=last_stmt.ins_addr,
214
227
  )
215
- args.append(reg)
228
+ arg_expr = reg
216
229
  elif isinstance(arg_loc, SimStackArg):
217
230
  stack_arg_locs.append(arg_loc)
218
231
  _, the_arg = self._resolve_stack_argument(call_stmt, arg_loc)
219
-
220
- if the_arg is not None:
221
- args.append(the_arg)
222
- else:
223
- args.append(None)
224
- elif isinstance(arg_loc, SimStructArg):
225
- l.warning("SimStructArg is not yet supported")
226
-
232
+ arg_expr = the_arg if the_arg is not None else None
227
233
  else:
228
- raise NotImplementedError("Not implemented yet.")
234
+ assert False, "Unreachable"
235
+
236
+ if arg_expr is not None and dereference_size is not None:
237
+ arg_expr = Expr.Load(self._atom_idx(), arg_expr, dereference_size, endness=archinfo.Endness.BE)
238
+ args.append(arg_expr)
229
239
 
230
240
  # Remove the old call statement
231
241
  new_stmts = self.block.statements[:-1]
@@ -1197,10 +1197,10 @@ class Clinic(Analysis):
1197
1197
  prototype_libname = func.prototype_libname
1198
1198
  type_collections = []
1199
1199
  if prototype_libname is not None:
1200
- prototype_lib = SIM_LIBRARIES[prototype_libname]
1201
- if prototype_lib.type_collection_names:
1202
- for typelib_name in prototype_lib.type_collection_names:
1203
- type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
1200
+ for prototype_lib in SIM_LIBRARIES[prototype_libname]:
1201
+ if prototype_lib.type_collection_names:
1202
+ for typelib_name in prototype_lib.type_collection_names:
1203
+ type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
1204
1204
  if type_collections:
1205
1205
  prototype = dereference_simtype(prototype, type_collections).with_arch( # type: ignore
1206
1206
  self.project.arch
@@ -961,27 +961,6 @@ class ConditionProcessor:
961
961
  sympy_expr = ConditionProcessor.claripy_ast_to_sympy_expr(cond, memo=memo)
962
962
  return ConditionProcessor.sympy_expr_to_claripy_ast(sympy.simplify_logic(sympy_expr, deep=False), memo)
963
963
 
964
- @staticmethod
965
- def simplify_condition_deprecated(cond):
966
- # Z3's simplification may yield weird and unreadable results
967
- # hence we mostly rely on our own simplification. we only use Z3's simplification results when it returns a
968
- # concrete value.
969
- claripy_simplified = claripy.simplify(cond)
970
- if not claripy_simplified.symbolic:
971
- return claripy_simplified
972
-
973
- simplified = ConditionProcessor._fold_double_negations(cond)
974
- cond = simplified if simplified is not None else cond
975
- simplified = ConditionProcessor._revert_short_circuit_conditions(cond)
976
- cond = simplified if simplified is not None else cond
977
- simplified = ConditionProcessor._extract_common_subexpressions(cond)
978
- cond = simplified if simplified is not None else cond
979
- # simplified = ConditionProcessor._remove_redundant_terms(cond)
980
- # cond = simplified if simplified is not None else cond
981
- # in the end, use claripy's simplification to handle really easy cases again
982
- simplified = ConditionProcessor._simplify_trivial_cases(cond)
983
- return simplified if simplified is not None else cond
984
-
985
964
  @staticmethod
986
965
  def _simplify_trivial_cases(cond):
987
966
  if cond.op == "And":
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
  from typing import TYPE_CHECKING
3
3
 
4
4
  from ailment import Block
5
+ from ailment.statement import Label
5
6
  from ailment.block_walker import AILBlockWalkerBase
6
7
 
7
8
  from angr.analyses.decompiler.sequence_walker import SequenceWalker
@@ -37,8 +38,10 @@ class AILCallCounter(SequenceWalker):
37
38
  }
38
39
  super().__init__(handlers)
39
40
  self.calls = 0
41
+ self.non_label_stmts = 0
40
42
 
41
43
  def _handle_Block(self, node: Block, **kwargs): # pylint:disable=unused-argument
42
44
  ctr = AILBlockCallCounter()
43
45
  ctr.walk(node)
44
46
  self.calls += ctr.calls
47
+ self.non_label_stmts += sum(1 for stmt in node.statements if not isinstance(stmt, Label))
@@ -66,7 +66,7 @@ class PairAILBlockWalker:
66
66
  def _handle_call_expr(expr_idx: int, expr: Call, stmt_idx: int, stmt: Statement, block_):
67
67
  walked_objs[Call].add(expr)
68
68
 
69
- _stmt_handlers = {typ: _handle_ail_obj for typ in walked_objs}
69
+ _stmt_handlers = dict.fromkeys(walked_objs, _handle_ail_obj)
70
70
  walker.stmt_handlers = _stmt_handlers
71
71
  walker.expr_handlers[Call] = _handle_call_expr
72
72
 
@@ -164,6 +164,20 @@ class RemoveRedundantConversions(PeepholeOptimizationExprBase):
164
164
  **expr.tags,
165
165
  )
166
166
 
167
+ # simpler cases
168
+ # (A & mask) & mask ==> A & mask
169
+ if (
170
+ expr.op == "And"
171
+ and isinstance(expr.operands[1], Const)
172
+ and isinstance(expr.operands[0], BinaryOp)
173
+ and expr.operands[0].op == "And"
174
+ ):
175
+ inner_op0, inner_op1 = expr.operands[0].operands
176
+ if (isinstance(inner_op0, Const) and inner_op0.value == expr.operands[1].value) or (
177
+ isinstance(inner_op1, Const) and inner_op1.value == expr.operands[1].value
178
+ ):
179
+ return expr.operands[0]
180
+
167
181
  return None
168
182
 
169
183
  @staticmethod
@@ -1281,11 +1281,11 @@ class CFunctionCall(CStatement, CExpression):
1281
1281
  if self.callee_func.prototype_libname is not None:
1282
1282
  # we need to deref the prototype in case it uses SimTypeRef internally
1283
1283
  type_collections = []
1284
- prototype_lib = SIM_LIBRARIES[self.callee_func.prototype_libname]
1285
- if prototype_lib.type_collection_names:
1286
- for typelib_name in prototype_lib.type_collection_names:
1287
- type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
1288
- proto = dereference_simtype(proto, type_collections)
1284
+ for prototype_lib in SIM_LIBRARIES[self.callee_func.prototype_libname]:
1285
+ if prototype_lib.type_collection_names:
1286
+ for typelib_name in prototype_lib.type_collection_names:
1287
+ type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
1288
+ proto = dereference_simtype(proto, type_collections)
1289
1289
  return proto
1290
1290
  returnty = SimTypeInt(signed=False)
1291
1291
  return SimTypeFunction([arg.type for arg in self.args], returnty).with_arch(self.codegen.project.arch)