angr 9.2.140__py3-none-win_amd64.whl → 9.2.142__py3-none-win_amd64.whl

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

Potentially problematic release.


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

Files changed (76) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +105 -35
  3. angr/analyses/calling_convention/fact_collector.py +44 -18
  4. angr/analyses/calling_convention/utils.py +3 -1
  5. angr/analyses/cfg/cfg_base.py +38 -4
  6. angr/analyses/cfg/cfg_fast.py +23 -7
  7. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +13 -8
  8. angr/analyses/class_identifier.py +8 -7
  9. angr/analyses/complete_calling_conventions.py +1 -1
  10. angr/analyses/decompiler/ail_simplifier.py +105 -62
  11. angr/analyses/decompiler/callsite_maker.py +24 -11
  12. angr/analyses/decompiler/clinic.py +83 -5
  13. angr/analyses/decompiler/condition_processor.py +7 -7
  14. angr/analyses/decompiler/decompilation_cache.py +2 -1
  15. angr/analyses/decompiler/decompiler.py +11 -2
  16. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +4 -6
  17. angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +8 -2
  18. angr/analyses/decompiler/optimization_passes/condition_constprop.py +63 -34
  19. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +3 -1
  20. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
  21. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +85 -16
  22. angr/analyses/decompiler/optimization_passes/optimization_pass.py +78 -1
  23. angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +29 -7
  24. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +51 -7
  25. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +6 -0
  26. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +9 -1
  27. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
  28. angr/analyses/decompiler/region_identifier.py +76 -51
  29. angr/analyses/decompiler/region_simplifiers/expr_folding.py +32 -18
  30. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -1
  31. angr/analyses/decompiler/ssailification/rewriting.py +70 -32
  32. angr/analyses/decompiler/ssailification/rewriting_engine.py +118 -24
  33. angr/analyses/decompiler/ssailification/ssailification.py +22 -14
  34. angr/analyses/decompiler/stack_item.py +36 -0
  35. angr/analyses/decompiler/structured_codegen/c.py +86 -145
  36. angr/analyses/decompiler/structuring/dream.py +1 -1
  37. angr/analyses/decompiler/structuring/phoenix.py +9 -4
  38. angr/analyses/decompiler/structuring/structurer_base.py +2 -1
  39. angr/analyses/decompiler/utils.py +46 -20
  40. angr/analyses/find_objects_static.py +2 -1
  41. angr/analyses/reaching_definitions/engine_vex.py +13 -0
  42. angr/analyses/reaching_definitions/function_handler.py +24 -10
  43. angr/analyses/reaching_definitions/function_handler_library/stdio.py +1 -0
  44. angr/analyses/reaching_definitions/function_handler_library/stdlib.py +45 -12
  45. angr/analyses/reaching_definitions/function_handler_library/string.py +77 -21
  46. angr/analyses/reaching_definitions/function_handler_library/unistd.py +21 -1
  47. angr/analyses/reaching_definitions/rd_state.py +11 -7
  48. angr/analyses/s_liveness.py +44 -6
  49. angr/analyses/s_reaching_definitions/s_rda_model.py +4 -2
  50. angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
  51. angr/analyses/typehoon/simple_solver.py +35 -8
  52. angr/analyses/typehoon/typehoon.py +3 -1
  53. angr/analyses/variable_recovery/engine_ail.py +1 -1
  54. angr/analyses/variable_recovery/engine_vex.py +20 -4
  55. angr/calling_conventions.py +17 -12
  56. angr/factory.py +8 -3
  57. angr/knowledge_plugins/functions/function.py +5 -10
  58. angr/knowledge_plugins/variables/variable_manager.py +34 -5
  59. angr/lib/angr_native.dll +0 -0
  60. angr/procedures/definitions/__init__.py +3 -10
  61. angr/procedures/definitions/wdk_ntoskrnl.py +2 -0
  62. angr/procedures/win32_kernel/__fastfail.py +15 -0
  63. angr/sim_procedure.py +2 -2
  64. angr/simos/simos.py +17 -11
  65. angr/simos/windows.py +42 -1
  66. angr/utils/ail.py +41 -1
  67. angr/utils/cpp.py +17 -0
  68. angr/utils/doms.py +142 -0
  69. angr/utils/library.py +1 -1
  70. angr/utils/types.py +59 -0
  71. {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/METADATA +7 -7
  72. {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/RECORD +76 -71
  73. {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/LICENSE +0 -0
  74. {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/WHEEL +0 -0
  75. {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/entry_points.txt +0 -0
  76. {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/top_level.txt +0 -0
@@ -9,7 +9,7 @@ import contextlib
9
9
  from typing import overload
10
10
 
11
11
  import networkx
12
- from itanium_demangler import parse
12
+ import pydemumble
13
13
 
14
14
  from cle.backends.symbol import Symbol
15
15
  from archinfo.arch_arm import get_real_address_if_arm
@@ -202,7 +202,8 @@ class Function(Serializable):
202
202
  if is_plt is not None:
203
203
  self.is_plt = is_plt
204
204
  else:
205
- # Whether this function is a PLT entry or not is fully relying on the PLT detection in CLE
205
+ # Whether this function is a PLT entry or not is primarily relying on the PLT detection in CLE; it may also
206
+ # be updated (to True) during CFG recovery.
206
207
  if self.project is None:
207
208
  raise ValueError(
208
209
  "'is_plt' must be specified if you do not specify a function manager for this new function."
@@ -1568,14 +1569,8 @@ class Function(Serializable):
1568
1569
 
1569
1570
  @property
1570
1571
  def demangled_name(self):
1571
- if self.name[0:2] == "_Z":
1572
- try:
1573
- ast = parse(self.name)
1574
- except (NotImplementedError, KeyError): # itanium demangler is not the most robust package in the world
1575
- return self.name
1576
- if ast:
1577
- return ast.__str__()
1578
- return self.name
1572
+ ast = pydemumble.demangle(self.name)
1573
+ return ast if ast else self.name
1579
1574
 
1580
1575
  def get_unambiguous_name(self, display_name: str | None = None) -> str:
1581
1576
  """
@@ -12,6 +12,7 @@ from cle.backends.elf.variable import Variable
12
12
 
13
13
  from angr.utils.orderedset import OrderedSet
14
14
  from angr.utils.ail import is_phi_assignment
15
+ from angr.utils.types import unpack_pointer, replace_pointer_pts_to
15
16
  from angr.protos import variables_pb2
16
17
  from angr.serializable import Serializable
17
18
  from angr.sim_variable import SimVariable, SimStackVariable, SimMemoryVariable, SimRegisterVariable
@@ -19,7 +20,6 @@ from angr.sim_type import (
19
20
  TypeRef,
20
21
  SimType,
21
22
  SimStruct,
22
- SimTypePointer,
23
23
  SimTypeBottom,
24
24
  SimTypeChar,
25
25
  SimTypeShort,
@@ -32,6 +32,7 @@ from angr.knowledge_plugins.types import TypesStore
32
32
  from .variable_access import VariableAccess, VariableAccessSort
33
33
 
34
34
  if TYPE_CHECKING:
35
+ from angr.analyses.decompiler.stack_item import StackItem
35
36
  from angr.code_location import CodeLocation
36
37
 
37
38
  l = logging.getLogger(name=__name__)
@@ -985,10 +986,12 @@ class VariableManagerInternal(Serializable):
985
986
  if name not in self.types:
986
987
  self.types[name] = TypeRef(name, ty).with_arch(self.manager._kb._project.arch)
987
988
  ty = self.types[name]
988
- elif isinstance(ty, SimTypePointer) and isinstance(ty.pts_to, SimStruct):
989
- typeref = self._register_struct_type(ty.pts_to)
990
- ty = ty.copy().with_arch(self.manager._kb._project.arch)
991
- ty.pts_to = typeref
989
+ elif (inner_ty := unpack_pointer(ty, iterative=True)) and isinstance(inner_ty, SimStruct):
990
+ typeref = self._register_struct_type(inner_ty)
991
+ # rebuild the multi-layer pointer type
992
+ replaced_ty = replace_pointer_pts_to(ty, inner_ty, typeref)
993
+ assert replaced_ty is not None
994
+ ty = replaced_ty.with_arch(self.manager._kb._project.arch)
992
995
  elif isinstance(ty, SimStruct):
993
996
  ty = self._register_struct_type(ty, name=name)
994
997
 
@@ -1139,6 +1142,32 @@ class VariableManagerInternal(Serializable):
1139
1142
  return False
1140
1143
  return True
1141
1144
 
1145
+ def get_stackvar_max_sizes(self, stack_items: dict[int, StackItem]) -> dict[SimStackVariable, int]:
1146
+ """
1147
+ Get the maximum size of each stack variable regardless of the type of each stack variable, under the assumption
1148
+ that stack variables do not overlap.
1149
+
1150
+ :return: A dictionary from SimStackVariable to its maximum size.
1151
+ """
1152
+
1153
+ stackvars_by_offset = defaultdict(list)
1154
+ for v in self._variables:
1155
+ if isinstance(v, SimStackVariable):
1156
+ offset = v.offset
1157
+ stackvars_by_offset[offset].append(v)
1158
+
1159
+ max_sizes = {}
1160
+ offsets = sorted(list(stackvars_by_offset) + list(stack_items))
1161
+ for i, offset in enumerate(offsets):
1162
+ if i + 1 < len(offsets):
1163
+ next_off = offsets[i + 1]
1164
+ sz = next_off - offset
1165
+ if offset in stackvars_by_offset:
1166
+ for v in stackvars_by_offset[offset]:
1167
+ max_sizes[v] = max(v.size, sz)
1168
+
1169
+ return max_sizes
1170
+
1142
1171
 
1143
1172
  class VariableManager(KnowledgeBasePlugin):
1144
1173
  """
angr/lib/angr_native.dll CHANGED
Binary file
@@ -7,8 +7,7 @@ import inspect
7
7
  from collections import defaultdict
8
8
  from typing import TYPE_CHECKING
9
9
 
10
- import itanium_demangler
11
-
10
+ import pydemumble
12
11
  import archinfo
13
12
 
14
13
  from angr.errors import AngrMissingTypeError
@@ -345,14 +344,8 @@ class SimCppLibrary(SimLibrary):
345
344
 
346
345
  @staticmethod
347
346
  def _try_demangle(name):
348
- if name[0:2] == "_Z":
349
- try:
350
- ast = itanium_demangler.parse(name)
351
- except NotImplementedError:
352
- return name
353
- if ast:
354
- return str(ast)
355
- return name
347
+ ast = pydemumble.demangle(name)
348
+ return ast if ast else name
356
349
 
357
350
  @staticmethod
358
351
  def _proto_from_demangled_name(name: str) -> SimTypeFunction | None:
@@ -20,6 +20,8 @@ lib.add_all_from_dict(P["win32_kernel"])
20
20
  lib.set_library_names("ntoskrnl.exe")
21
21
  prototypes = \
22
22
  {
23
+ # int 29h
24
+ '__fastfail': SimTypeFunction([SimTypeInt(signed=False, label="Int")], SimTypeBottom(label="void"), arg_names=["code"]),
23
25
  #
24
26
  'NtQueryObject': SimTypeFunction([SimTypePointer(SimTypeInt(signed=True, label="Int"), label="IntPtr", offset=0), SimTypeInt(signed=False, label="OBJECT_INFORMATION_CLASS"), SimTypePointer(SimTypeBottom(label="Void"), offset=0), SimTypeInt(signed=False, label="UInt32"), SimTypePointer(SimTypeInt(signed=False, label="UInt32"), offset=0)], SimTypeInt(signed=True, label="Int32"), arg_names=["Handle", "ObjectInformationClass", "ObjectInformation", "ObjectInformationLength", "ReturnLength"]),
25
27
  #
@@ -0,0 +1,15 @@
1
+ from __future__ import annotations
2
+ import angr
3
+
4
+
5
+ class __fastfail(angr.SimProcedure):
6
+ """
7
+ Immediately terminates the calling process with minimum overhead.
8
+
9
+ https://learn.microsoft.com/en-us/cpp/intrinsics/fastfail?view=msvc-170
10
+ """
11
+
12
+ NO_RET = True
13
+
14
+ def run(self, _): # pylint:disable=arguments-differ
15
+ self.exit(0xC0000409)
angr/sim_procedure.py CHANGED
@@ -3,7 +3,7 @@ import inspect
3
3
  import copy
4
4
  import itertools
5
5
  import logging
6
- from typing import TYPE_CHECKING
6
+ from typing import Any, TYPE_CHECKING
7
7
 
8
8
  import claripy
9
9
  from cle import SymbolType
@@ -339,7 +339,7 @@ class SimProcedure:
339
339
  ALT_NAMES = None # alternative names
340
340
  local_vars: tuple[str, ...] = ()
341
341
 
342
- def run(self, *args, **kwargs): # pylint: disable=unused-argument
342
+ def run(self, *args, **kwargs) -> Any: # pylint: disable=unused-argument
343
343
  """
344
344
  Implement the actual procedure here!
345
345
  """
angr/simos/simos.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from __future__ import annotations
2
+ from typing import TYPE_CHECKING
2
3
  import logging
3
4
  import struct
4
5
 
@@ -14,6 +15,9 @@ from angr.procedures import SIM_PROCEDURES as P
14
15
  from angr import sim_options as o
15
16
  from angr.storage.file import SimFileStream, SimFileBase
16
17
 
18
+ if TYPE_CHECKING:
19
+ from angr.sim_procedure import SimProcedure
20
+
17
21
 
18
22
  _l = logging.getLogger(name=__name__)
19
23
 
@@ -23,7 +27,9 @@ class SimOS:
23
27
  A class describing OS/arch-level configuration.
24
28
  """
25
29
 
26
- def __init__(self, project: angr.Project, name=None):
30
+ name: str | None
31
+
32
+ def __init__(self, project: angr.Project, name: str | None = None):
27
33
  self.arch = project.arch
28
34
  self.project = project
29
35
  self.name = name
@@ -44,7 +50,7 @@ class SimOS:
44
50
  self.unresolvable_call_target = self.project.loader.extern_object.allocate()
45
51
  self.project.hook(self.unresolvable_call_target, P["stubs"]["UnresolvableCallTarget"]())
46
52
 
47
- def irelative_resolver(resolver_addr):
53
+ def irelative_resolver(resolver_addr: int) -> int | None:
48
54
  # autohooking runs before this does, might have provided this already
49
55
  # in that case, we want to advertise the _resolver_ address, since it is now
50
56
  # providing the behavior of the actual function
@@ -68,7 +74,7 @@ class SimOS:
68
74
  _l.error("Resolver at %#x failed to resolve!", resolver_addr)
69
75
  return None
70
76
 
71
- return val.concrete_value
77
+ return val.concrete_value if val is not None and val.concrete else None
72
78
 
73
79
  self.project.loader.perform_irelative_relocs(irelative_resolver)
74
80
 
@@ -77,7 +83,7 @@ class SimOS:
77
83
 
78
84
  if sym is not None:
79
85
  addr, _ = self.prepare_function_symbol(name, basic_addr=sym.rebased_addr)
80
- if self.project.is_hooked(addr) and not self.project.hooked_by(addr).is_stub:
86
+ if self.project.is_hooked(addr) and not self.project.hooked_by(addr).is_stub: # type: ignore
81
87
  return
82
88
  self.project.hook(addr, hook)
83
89
 
@@ -240,7 +246,7 @@ class SimOS:
240
246
  return self.state_entry(**kwargs)
241
247
 
242
248
  def state_call(self, addr, *args, **kwargs):
243
- cc = kwargs.pop("cc", default_cc(self.arch.name, platform=self.name)(self.project.arch))
249
+ cc = kwargs.pop("cc", default_cc(self.arch.name, platform=self.name)(self.project.arch)) # type: ignore
244
250
  state = kwargs.pop("base_state", None)
245
251
  toc = kwargs.pop("toc", None)
246
252
 
@@ -324,22 +330,22 @@ class SimOS:
324
330
  # Dummy stuff to allow this API to be used freely
325
331
 
326
332
  # pylint: disable=unused-argument, no-self-use
327
- def syscall(self, state, allow_unsupported=True):
333
+ def syscall(self, state: SimState, allow_unsupported: bool = True) -> SimProcedure | None:
328
334
  return None
329
335
 
330
- def syscall_abi(self, state) -> str:
336
+ def syscall_abi(self, state: SimState) -> str | None:
331
337
  return None
332
338
 
333
- def syscall_cc(self, state) -> angr.calling_conventions.SimCCSyscall | None:
339
+ def syscall_cc(self, state: SimState) -> angr.calling_conventions.SimCCSyscall | None:
334
340
  raise NotImplementedError
335
341
 
336
- def is_syscall_addr(self, addr):
342
+ def is_syscall_addr(self, addr) -> bool:
337
343
  return False
338
344
 
339
- def syscall_from_addr(self, addr, allow_unsupported=True):
345
+ def syscall_from_addr(self, addr, allow_unsupported=True) -> SimProcedure | None:
340
346
  return None
341
347
 
342
- def syscall_from_number(self, number, allow_unsupported=True, abi=None):
348
+ def syscall_from_number(self, number, allow_unsupported=True, abi=None) -> SimProcedure | None:
343
349
  return None
344
350
 
345
351
  def setup_gdt(self, state, gdt):
angr/simos/windows.py CHANGED
@@ -15,6 +15,7 @@ from angr import sim_options as o
15
15
  from angr.tablespecs import StringTableSpec
16
16
  from angr.procedures import SIM_LIBRARIES as L
17
17
  from angr.procedures.definitions import load_win32api_definitions
18
+ from angr.calling_conventions import SYSCALL_CC
18
19
  from .simos import SimOS
19
20
 
20
21
  _l = logging.getLogger(name=__name__)
@@ -26,6 +27,8 @@ VS_SECURITY_COOKIES = {"AMD64": _VS_Security_Cookie(0x2B992DDFA232, 48), "X86":
26
27
 
27
28
 
28
29
  class SecurityCookieInit(enum.Enum):
30
+ """Security cooke initialization value initialization method."""
31
+
29
32
  NONE = 0
30
33
  RANDOM = 1
31
34
  STATIC = 2
@@ -48,6 +51,11 @@ class SimWindows(SimOS):
48
51
  self.acmdln_ptr = None
49
52
  self.wcmdln_ptr = None
50
53
 
54
+ self.fastfail = L["ntoskrnl.exe"].get("__fastfail", self.arch)
55
+ self.fastfail.addr = self._find_or_make(self.fastfail.display_name)
56
+ self.fastfail.cc = SYSCALL_CC[self.arch.name]["Win32"](self.arch)
57
+ self._syscall_handlers = {self.fastfail.addr: self.fastfail}
58
+
51
59
  def configure_project(self):
52
60
  super().configure_project()
53
61
 
@@ -66,11 +74,15 @@ class SimWindows(SimOS):
66
74
  self.acmdln_ptr = self._find_or_make("_acmdln")
67
75
  self.wcmdln_ptr = self._find_or_make("_wcmdln")
68
76
 
69
- self.is_dump = isinstance(self.project.loader.main_object, cle.backends.Minidump)
77
+ self.project.hook(self.fastfail.addr, self.fastfail)
70
78
 
71
79
  if not self.is_dump:
72
80
  self.project.loader.tls.new_thread()
73
81
 
82
+ @property
83
+ def is_dump(self) -> bool:
84
+ return isinstance(self.project.loader.main_object, cle.backends.Minidump)
85
+
74
86
  def _find_or_make(self, name):
75
87
  sym = self.project.loader.find_symbol(name)
76
88
  if sym is None:
@@ -420,6 +432,35 @@ class SimWindows(SimOS):
420
432
  successors.add_successor(exc_state, self._exception_handler, claripy.true(), "Ijk_Exception")
421
433
  successors.processed = True
422
434
 
435
+ def syscall(self, state, allow_unsupported=True):
436
+ """
437
+ Given a state, return the procedure corresponding to the current syscall.
438
+ This procedure will have .syscall_number, .display_name, and .addr set.
439
+
440
+ :param state: The state to get the syscall number from
441
+ :param allow_unsupported: Whether to return a "dummy" sycall instead of raising an unsupported exception
442
+ """
443
+ if state.block(state.history.jump_source).bytes.hex() == "cd29": # int 29h
444
+ return self.fastfail
445
+ return None
446
+
447
+ def is_syscall_addr(self, addr):
448
+ """
449
+ Return whether or not the given address corresponds to a syscall implementation.
450
+ """
451
+ return addr in self._syscall_handlers
452
+
453
+ def syscall_from_addr(self, addr, allow_unsupported=True):
454
+ """
455
+ Get a syscall SimProcedure from an address.
456
+
457
+ :param addr: The address to convert to a syscall SimProcedure
458
+ :param allow_unsupported: Whether to return a dummy procedure for an unsupported syscall instead of raising an
459
+ exception.
460
+ :return: The SimProcedure for the syscall, or None if the address is not a syscall address.
461
+ """
462
+ return self._syscall_handlers.get(addr, None)
463
+
423
464
  # these two methods load and store register state from a struct CONTEXT
424
465
  # https://www.nirsoft.net/kernel_struct/vista/CONTEXT.html
425
466
  @staticmethod
angr/utils/ail.py CHANGED
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from ailment import AILBlockWalkerBase
4
4
  from ailment.block import Block
5
5
  from ailment.expression import Expression, VirtualVariable, Phi
6
- from ailment.statement import Assignment, Statement
6
+ from ailment.statement import Assignment, Statement, ConditionalJump
7
7
 
8
8
 
9
9
  def is_phi_assignment(stmt: Statement) -> bool:
@@ -28,3 +28,43 @@ class HasExprWalker(AILBlockWalkerBase):
28
28
  self.contains_exprs = True
29
29
  if not self.contains_exprs:
30
30
  super()._handle_expr(expr_idx, expr, stmt_idx, stmt, block)
31
+
32
+
33
+ def is_head_controlled_loop_block(block: Block) -> bool:
34
+ """
35
+ Determine if the block is a "head-controlled loop." A head-controlled loop (for the lack of a better term) is a
36
+ single-block that contains a conditional jump towards the beginning of the block. This conditional jump controls
37
+ whether the loop body (the remaining statements after the conditional jump) will be executed or not. It is usually
38
+ the result of lifting rep stosX instructions on x86 and amd64.
39
+
40
+ A head-controlled loop block looks like the following (lifted from rep stosq qword ptr [rdi], rax):
41
+
42
+ ## Block 4036df
43
+ 00 | 0x4036df | LABEL_4036df:
44
+ 01 | 0x4036df | vvar_27{reg 72} = 𝜙@64b []
45
+ 02 | 0x4036df | vvar_28{reg 24} = 𝜙@64b []
46
+ 03 | 0x4036df | t1 = rcx<8>
47
+ 04 | 0x4036df | t4 = (t1 == 0x0<64>)
48
+ 05 | 0x4036df | if (t4) { Goto 0x4036e2<64> } else { Goto 0x4036df<64> }
49
+ 06 | 0x4036df | t5 = (t1 - 0x1<64>)
50
+ 07 | 0x4036df | rcx<8> = t5
51
+ 08 | 0x4036df | t7 = d<8>
52
+ 09 | 0x4036df | t6 = (t7 << 0x3<8>)
53
+ 10 | 0x4036df | t2 = rax<8>
54
+ 11 | 0x4036df | t3 = rdi<8>
55
+ 12 | 0x4036df | STORE(addr=t3, data=t2, size=8, endness=Iend_LE, guard=None)
56
+ 13 | 0x4036df | t8 = (t3 + t6)
57
+ 14 | 0x4036df | rdi<8> = t8
58
+
59
+ Where statement 5 is the conditional jump that controls the execution of the remaining statements of this block.
60
+
61
+ :param block: An AIL block.
62
+ :return: True if the block represents a head-controlled loop block, False otherwise.
63
+ """
64
+
65
+ if not block.statements:
66
+ return False
67
+ last_stmt = block.statements[-1]
68
+ if isinstance(last_stmt, ConditionalJump):
69
+ return False
70
+ return any(isinstance(stmt, ConditionalJump) for stmt in block.statements[:-1])
angr/utils/cpp.py ADDED
@@ -0,0 +1,17 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ def is_cpp_funcname_ctor(name: str) -> bool:
5
+ """
6
+ Check if a demangled C++ function name is a constructor.
7
+
8
+ :param name: The demangled C++ function name.
9
+ :return: True if the function name is a constructor, False otherwise.
10
+ """
11
+
12
+ # With pydemumble, constructor names look like:
13
+ # A::A()
14
+ if "::" not in name:
15
+ return False
16
+ parts = name.split("::")
17
+ return bool(len(parts) == 2 and parts[0] and parts[0] + "()" == parts[1])
angr/utils/doms.py ADDED
@@ -0,0 +1,142 @@
1
+ # pylint:disable=consider-using-dict-items
2
+ from __future__ import annotations
3
+ from typing import Any
4
+ from collections import defaultdict
5
+
6
+ import networkx
7
+
8
+ from angr.utils.graph import shallow_reverse
9
+
10
+
11
+ class IncrementalDominators:
12
+ """
13
+ This class allows for incrementally updating dominators and post-dominators for graphs. The graph must only be
14
+ modified by replacing nodes, not adding nodes or edges.
15
+ """
16
+
17
+ def __init__(self, graph: networkx.DiGraph, start, post: bool = False):
18
+ self.graph = graph
19
+ self.start = start
20
+ self._post: bool = post # calculate post-dominators if True
21
+ self._pre: bool = not post # calculate dominators
22
+
23
+ self._doms: dict[Any, Any] = {}
24
+ self._inverted_dom_tree: dict[Any, Any] | None = None # initialized on demand
25
+
26
+ self._doms = self.init_doms()
27
+
28
+ def init_doms(self) -> dict[Any, Any]:
29
+ if self._post:
30
+ t = shallow_reverse(self.graph)
31
+ doms = networkx.immediate_dominators(t, self.start)
32
+ else:
33
+ doms = networkx.immediate_dominators(self.graph, self.start)
34
+ return doms
35
+
36
+ def _update_inverted_domtree(self):
37
+ # recalculate the dominators for dominatees of replaced nodes
38
+ if self._inverted_dom_tree is None:
39
+ self._inverted_dom_tree = defaultdict(list)
40
+ for dtee, dtor in self._doms.items():
41
+ self._inverted_dom_tree[dtor].append(dtee)
42
+
43
+ def graph_updated(self, new_node: Any, replaced_nodes: set[Any], replaced_head: Any):
44
+ self._update_inverted_domtree()
45
+ assert self._inverted_dom_tree is not None
46
+
47
+ # recalculate the dominators for impacted nodes
48
+ new_dom = self._doms[replaced_head]
49
+ while new_dom in replaced_nodes and new_dom is not self.start:
50
+ new_dom = self._doms[new_dom]
51
+
52
+ if self.start in replaced_nodes:
53
+ self.start = new_node
54
+ if new_dom in replaced_nodes:
55
+ new_dom = new_node
56
+
57
+ new_node_doms = []
58
+ for rn in replaced_nodes:
59
+ if rn not in self._inverted_dom_tree:
60
+ continue
61
+ for dtee in self._inverted_dom_tree[rn]:
62
+ self._doms[dtee] = new_node
63
+ new_node_doms.append(dtee)
64
+ self._doms[new_node] = new_dom
65
+
66
+ # keep inverted dom tree up-to-date
67
+ self._inverted_dom_tree[new_dom].append(new_node)
68
+ self._inverted_dom_tree[new_node] = new_node_doms
69
+ for rn in replaced_nodes:
70
+ if rn in self._doms:
71
+ d = self._doms[rn]
72
+ del self._doms[rn]
73
+ self._inverted_dom_tree[d].remove(rn)
74
+ if rn in self._inverted_dom_tree:
75
+ del self._inverted_dom_tree[rn]
76
+
77
+ def idom(self, node: Any) -> Any | None:
78
+ """
79
+ Get the immediate dominator of a given node.
80
+ """
81
+
82
+ return self._doms.get(node, None)
83
+
84
+ def df(self, node: Any) -> set[Any]:
85
+ """
86
+ Generate the dominance frontier of a node.
87
+ """
88
+
89
+ if node not in self.graph:
90
+ return set()
91
+
92
+ _pred = self.graph.predecessors if self._pre else self.graph.successors
93
+ _succ = self.graph.successors if self._pre else self.graph.predecessors
94
+ df = set()
95
+
96
+ visited = {node}
97
+ queue = [node]
98
+
99
+ while queue:
100
+ u = queue.pop(0)
101
+ preds = list(_pred(u)) # type: ignore
102
+ added = False
103
+ if len(preds) >= 2:
104
+ for v in preds:
105
+ if v in self._doms:
106
+ while v != self._doms[u]:
107
+ if v is node:
108
+ df.add(u)
109
+ added = True
110
+ break
111
+ v = self._doms[v]
112
+ if added:
113
+ break
114
+
115
+ if not added:
116
+ for v in _succ(u): # type: ignore
117
+ if v not in visited:
118
+ visited.add(v)
119
+ queue.append(v)
120
+ return df
121
+
122
+ def dominates(self, dominator_node: Any, node: Any) -> bool:
123
+ """
124
+ Tests if dominator_node dominates (or post-dominates) node.
125
+ """
126
+
127
+ n = node
128
+ while n:
129
+ if n is dominator_node:
130
+ return True
131
+ d = self.idom(n)
132
+ n = d if d is not None and n is not d else None
133
+ return False
134
+
135
+ def _debug_check(self):
136
+ true_doms = self.init_doms()
137
+ if len(true_doms) != len(self._doms):
138
+ raise ValueError("dominators do not match")
139
+ for k in true_doms:
140
+ if true_doms[k] != self._doms[k]:
141
+ print(f"{k!r}: {true_doms[k]!r} {self._doms[k]!r}")
142
+ raise ValueError("dominators do not match")
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[i] = SimTypeFd(label=arg.label)
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"
angr/utils/types.py ADDED
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+
3
+ from angr.sim_type import TypeRef, SimType, SimTypePointer, SimTypeArray, SimTypeFixedSizeArray
4
+
5
+
6
+ def unpack_typeref(ty):
7
+ if isinstance(ty, TypeRef):
8
+ return ty.type
9
+ return ty
10
+
11
+
12
+ def unpack_pointer(ty: SimType, iterative: bool = False) -> SimType | None:
13
+ if isinstance(ty, SimTypePointer):
14
+ if iterative:
15
+ inner = unpack_pointer(ty.pts_to, iterative=True)
16
+ return inner if inner is not None else ty.pts_to
17
+ return ty.pts_to
18
+ return None
19
+
20
+
21
+ def unpack_pointer_and_array(ty: SimType, iterative: bool = False) -> SimType | None:
22
+ if isinstance(ty, SimTypePointer):
23
+ if iterative:
24
+ inner = unpack_pointer(ty.pts_to, iterative=True)
25
+ return inner if inner is not None else ty.pts_to
26
+ return ty.pts_to
27
+ if isinstance(ty, SimTypeArray):
28
+ return ty.elem_type
29
+ return None
30
+
31
+
32
+ def replace_pointer_pts_to(ty: SimType, old_pts_to: SimType, new_pts_to: SimType) -> SimTypePointer | None:
33
+ if isinstance(ty, SimTypePointer):
34
+ if ty.pts_to is old_pts_to:
35
+ inner = new_pts_to
36
+ elif isinstance(ty.pts_to, SimTypePointer):
37
+ # recursively replace pts_to inside
38
+ inner = replace_pointer_pts_to(ty.pts_to, old_pts_to, new_pts_to)
39
+ else:
40
+ return None
41
+ return SimTypePointer(inner, label=ty.label, offset=ty.offset)
42
+ return None
43
+
44
+
45
+ def unpack_array(ty) -> SimType | None:
46
+ if isinstance(ty, SimTypeArray):
47
+ return ty.elem_type
48
+ if isinstance(ty, SimTypeFixedSizeArray):
49
+ return ty.elem_type
50
+ return None
51
+
52
+
53
+ def squash_array_reference(ty):
54
+ pointed_to = unpack_pointer(ty)
55
+ if pointed_to:
56
+ array_of = unpack_array(pointed_to)
57
+ if array_of:
58
+ return SimTypePointer(array_of)
59
+ return ty
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: angr
3
- Version: 9.2.140
3
+ Version: 9.2.142
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
  Home-page: https://github.com/angr/angr
6
6
  License: BSD-2-Clause
@@ -16,22 +16,22 @@ Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: CppHeaderParser
18
18
  Requires-Dist: GitPython
19
- Requires-Dist: ailment==9.2.140
20
- Requires-Dist: archinfo==9.2.140
19
+ Requires-Dist: ailment==9.2.142
20
+ Requires-Dist: archinfo==9.2.142
21
21
  Requires-Dist: cachetools
22
22
  Requires-Dist: capstone==5.0.3
23
23
  Requires-Dist: cffi>=1.14.0
24
- Requires-Dist: claripy==9.2.140
25
- Requires-Dist: cle==9.2.140
26
- Requires-Dist: itanium-demangler
24
+ Requires-Dist: claripy==9.2.142
25
+ Requires-Dist: cle==9.2.142
27
26
  Requires-Dist: mulpyplexer
28
27
  Requires-Dist: nampa
29
28
  Requires-Dist: networkx!=2.8.1,>=2.0
30
29
  Requires-Dist: protobuf>=5.28.2
31
30
  Requires-Dist: psutil
32
31
  Requires-Dist: pycparser>=2.18
32
+ Requires-Dist: pydemumble
33
33
  Requires-Dist: pyformlang
34
- Requires-Dist: pyvex==9.2.140
34
+ Requires-Dist: pyvex==9.2.142
35
35
  Requires-Dist: rich>=13.1.0
36
36
  Requires-Dist: sortedcontainers
37
37
  Requires-Dist: sympy