angr 9.2.152__py3-none-macosx_10_9_x86_64.whl → 9.2.154__py3-none-macosx_10_9_x86_64.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 (32) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/analysis.py +3 -3
  3. angr/analyses/calling_convention/fact_collector.py +8 -14
  4. angr/analyses/cfg/cfg_base.py +1 -1
  5. angr/analyses/cfg/cfg_fast.py +40 -1
  6. angr/analyses/decompiler/ail_simplifier.py +0 -1
  7. angr/analyses/decompiler/callsite_maker.py +17 -17
  8. angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +210 -1
  9. angr/analyses/decompiler/clinic.py +51 -13
  10. angr/analyses/decompiler/decompilation_cache.py +1 -1
  11. angr/analyses/decompiler/region_identifier.py +171 -119
  12. angr/analyses/decompiler/ssailification/ssailification.py +1 -1
  13. angr/analyses/decompiler/structured_codegen/c.py +15 -15
  14. angr/analyses/decompiler/structuring/phoenix.py +28 -0
  15. angr/analyses/decompiler/structuring/structurer_nodes.py +11 -0
  16. angr/analyses/reaching_definitions/function_handler.py +13 -19
  17. angr/analyses/smc.py +3 -1
  18. angr/analyses/stack_pointer_tracker.py +7 -1
  19. angr/analyses/typehoon/simple_solver.py +143 -81
  20. angr/analyses/typehoon/typehoon.py +2 -1
  21. angr/analyses/variable_recovery/engine_ail.py +14 -25
  22. angr/analyses/variable_recovery/engine_base.py +1 -1
  23. angr/knowledge_plugins/functions/function.py +10 -4
  24. angr/lib/angr_native.dylib +0 -0
  25. angr/sim_type.py +11 -70
  26. angr/utils/types.py +93 -1
  27. {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/METADATA +6 -6
  28. {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/RECORD +32 -32
  29. {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/WHEEL +1 -1
  30. {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/entry_points.txt +0 -0
  31. {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/licenses/LICENSE +0 -0
  32. {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/top_level.txt +0 -0
angr/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # pylint: disable=wrong-import-position
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "9.2.152"
5
+ __version__ = "9.2.154"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
angr/analyses/analysis.py CHANGED
@@ -5,7 +5,7 @@ import sys
5
5
  import contextlib
6
6
  from collections import defaultdict
7
7
  from inspect import Signature
8
- from typing import TYPE_CHECKING, TypeVar, Generic, cast
8
+ from typing import TYPE_CHECKING, TypeVar, Generic, cast, Any
9
9
  from collections.abc import Callable
10
10
  from types import NoneType
11
11
  from itertools import chain
@@ -111,7 +111,7 @@ class AnalysisLogEntry:
111
111
  A = TypeVar("A", bound="Analysis")
112
112
 
113
113
 
114
- class AnalysesHub(PluginVendor[A]):
114
+ class AnalysesHub(PluginVendor[Any]):
115
115
  """
116
116
  This class contains functions for all the registered and runnable analyses,
117
117
  """
@@ -123,7 +123,7 @@ class AnalysesHub(PluginVendor[A]):
123
123
  def _init_plugin(self, plugin_cls: type[A]) -> AnalysisFactory[A]:
124
124
  return AnalysisFactory(self.project, plugin_cls)
125
125
 
126
- def __getstate__(self):
126
+ def __getstate__(self): # type: ignore[reportIncompatibleMethodOverride]
127
127
  s = super().__getstate__()
128
128
  return (s, self.project)
129
129
 
@@ -6,7 +6,6 @@ from collections import defaultdict
6
6
  import pyvex
7
7
  import claripy
8
8
 
9
- from angr import SIM_LIBRARIES, SIM_TYPE_COLLECTIONS
10
9
  from angr.utils.bits import s2u, u2s
11
10
  from angr.block import Block
12
11
  from angr.analyses.analysis import Analysis
@@ -15,7 +14,8 @@ from angr.knowledge_plugins.functions import Function
15
14
  from angr.codenode import BlockNode, HookNode
16
15
  from angr.engines.light import SimEngineNostmtVEX, SimEngineLight, SpOffset, RegisterOffset
17
16
  from angr.calling_conventions import SimRegArg, SimStackArg, default_cc
18
- from angr.sim_type import SimTypeBottom, dereference_simtype, SimTypeFunction
17
+ from angr.sim_type import SimTypeBottom, SimTypeFunction
18
+ from angr.utils.types import dereference_simtype_by_lib
19
19
  from .utils import is_sane_register_variable
20
20
 
21
21
  if TYPE_CHECKING:
@@ -121,7 +121,7 @@ class SimEngineFactCollectorVEX(
121
121
  if self.block.vex.jumpkind == "Ijk_Call" and self.arch.ret_offset is not None:
122
122
  self.state.register_written(self.arch.ret_offset, self.arch.bytes)
123
123
 
124
- def _top(self, bits: int):
124
+ def _top(self, bits: int): # type: ignore
125
125
  return None
126
126
 
127
127
  def _is_top(self, expr: Any) -> bool:
@@ -190,13 +190,13 @@ class SimEngineFactCollectorVEX(
190
190
  self.state.register_read(expr.offset, bits // self.arch.byte_width)
191
191
  return RegisterOffset(bits, expr.offset, 0)
192
192
 
193
- def _handle_expr_GetI(self, expr):
193
+ def _handle_expr_GetI(self, expr): # type: ignore
194
194
  return None
195
195
 
196
- def _handle_expr_ITE(self, expr):
196
+ def _handle_expr_ITE(self, expr): # type: ignore
197
197
  return None
198
198
 
199
- def _handle_expr_Load(self, expr):
199
+ def _handle_expr_Load(self, expr): # type: ignore
200
200
  addr = self._expr(expr.addr)
201
201
  if isinstance(addr, SpOffset):
202
202
  self.state.stack_read(addr.offset, expr.result_size(self.tyenv) // self.arch.byte_width)
@@ -206,7 +206,7 @@ class SimEngineFactCollectorVEX(
206
206
  def _handle_expr_RdTmp(self, expr):
207
207
  return self.state.tmps.get(expr.tmp, None)
208
208
 
209
- def _handle_expr_VECRET(self, expr):
209
+ def _handle_expr_VECRET(self, expr): # type: ignore
210
210
  return None
211
211
 
212
212
  @binop_handler
@@ -444,13 +444,7 @@ class FactCollector(Analysis):
444
444
  proto = func_succ.prototype
445
445
  if func_succ.prototype_libname is not None:
446
446
  # we need to deref the prototype in case it uses SimTypeRef internally
447
- type_collections = []
448
- for prototype_lib in SIM_LIBRARIES[func_succ.prototype_libname]:
449
- if prototype_lib.type_collection_names:
450
- for typelib_name in prototype_lib.type_collection_names:
451
- type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
452
- if type_collections:
453
- proto = dereference_simtype(proto, type_collections)
447
+ proto = dereference_simtype_by_lib(proto, func_succ.prototype_libname)
454
448
 
455
449
  assert isinstance(proto, SimTypeFunction) and proto.returnty is not None
456
450
  returnty_size = proto.returnty.with_arch(self.project.arch).size
@@ -1515,7 +1515,7 @@ class CFGBase(Analysis):
1515
1515
  Revisit the entire control flow graph, create Function instances accordingly, and correctly put blocks into
1516
1516
  each function.
1517
1517
 
1518
- Although Function objects are crated during the CFG recovery, they are neither sound nor accurate. With a
1518
+ Although Function objects are created during the CFG recovery, they are neither sound nor accurate. With a
1519
1519
  pre-constructed CFG, this method rebuilds all functions bearing the following rules:
1520
1520
 
1521
1521
  - A block may only belong to one function.
@@ -1554,6 +1554,45 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1554
1554
  }:
1555
1555
  func.info["is_alloca_probe"] = True
1556
1556
 
1557
+ # determine if the function is _guard_xfg_dispatch_icall_nop or _guard_xfg_dispatch_icall_fptr
1558
+ if func is not None and not func.is_simprocedure and len(func.block_addrs_set) in {1, 2}:
1559
+ # _guard_xfg_dispatch_icall_nop jumps to _guard_xfg_dispatch_icall_fptr, but we may or may not identify
1560
+ # _guard_xfg_dispatch_icall_fptr as a separate function.
1561
+ # so, two possibilities:
1562
+ # - _guard_xfg_dispatch_icall_nop is a function with one block and jumps to
1563
+ # _guard_xfg_dispatch_icall_fptr.
1564
+ # - _guard_xfg_dispatch_icall_nop is a function with 2 blocks, and the second block is the body of
1565
+ # _guard_xfg_dispatch_icall_fptr.
1566
+ try:
1567
+ block = func.get_block(func.addr)
1568
+ except SimTranslationError:
1569
+ block = None
1570
+ if block is not None and block.instructions == 1 and len(block.capstone.insns) == 1:
1571
+ insn = block.capstone.insns[0]
1572
+ if block.bytes == b"\xff\xe0":
1573
+ func.info["jmp_rax"] = True
1574
+ elif (
1575
+ insn.mnemonic == "jmp"
1576
+ and insn.operands[0].type == capstone.x86.X86_OP_MEM
1577
+ and insn.operands[0].mem.base == capstone.x86.X86_REG_RIP
1578
+ and insn.operands[0].mem.disp > 0
1579
+ and insn.operands[0].mem.index == 0
1580
+ ):
1581
+ # where is it jumping to?
1582
+ jumpout_targets = list(self.graph.successors(self.model.get_any_node(func.addr)))
1583
+ if len(jumpout_targets) == 1:
1584
+ jumpout_target = jumpout_targets[0].addr
1585
+ if len(func.block_addrs_set) == 1 and len(func.jumpout_sites) == 1:
1586
+ if (
1587
+ self.kb.functions.contains_addr(jumpout_target)
1588
+ and self.kb.functions.get_by_addr(jumpout_target).get_block(jumpout_target).bytes
1589
+ == b"\xff\xe0"
1590
+ ):
1591
+ func.info["jmp_rax"] = True
1592
+ elif len(func.block_addrs_set) == 2 and func.get_block(jumpout_target).bytes == b"\xff\xe0":
1593
+ # check the second block and ensure it's jmp rax
1594
+ func.info["jmp_rax"] = True
1595
+
1557
1596
  elif self.project.arch.name == "X86":
1558
1597
  # determine if the function is __alloca_probe
1559
1598
  func = self.kb.functions.get_by_addr(func_addr) if self.kb.functions.contains_addr(func_addr) else None
@@ -1882,7 +1921,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1882
1921
  if cfg_node is None:
1883
1922
  continue
1884
1923
  func_addr = cfg_node.function_address
1885
- if func_addr not in tested_func_addrs:
1924
+ if func_addr not in tested_func_addrs and self.kb.functions.contains_addr(func_addr):
1886
1925
  func = self.kb.functions.get_by_addr(func_addr)
1887
1926
  if not security_check_cookie_found and is_function_security_check_cookie(
1888
1927
  func, self.project, security_cookie_addr
@@ -203,7 +203,6 @@ class AILSimplifier(Analysis):
203
203
  AILGraphWalker(self.func_graph, _handler, replace_nodes=True).walk()
204
204
  self.blocks = {}
205
205
 
206
- @timethis
207
206
  def _compute_reaching_definitions(self) -> SRDAModel:
208
207
  # Computing reaching definitions or return the cached one
209
208
  if self._reaching_definitions is not None:
@@ -1,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Any, TYPE_CHECKING
2
+ from typing import cast, Any, TYPE_CHECKING
3
3
  import copy
4
4
  import logging
5
5
 
@@ -14,7 +14,6 @@ from angr.sim_type import (
14
14
  SimTypeChar,
15
15
  SimTypeInt,
16
16
  SimTypeFloat,
17
- dereference_simtype,
18
17
  SimTypeFunction,
19
18
  SimTypeLongLong,
20
19
  )
@@ -22,7 +21,7 @@ from angr.calling_conventions import SimReferenceArgument, SimRegArg, SimStackAr
22
21
  from angr.knowledge_plugins.key_definitions.constants import OP_BEFORE
23
22
  from angr.analyses import Analysis, register_analysis
24
23
  from angr.analyses.s_reaching_definitions import SRDAView
25
- from angr import SIM_LIBRARIES, SIM_TYPE_COLLECTIONS
24
+ from angr.utils.types import dereference_simtype_by_lib
26
25
 
27
26
  if TYPE_CHECKING:
28
27
  from angr.knowledge_plugins.functions import Function
@@ -37,12 +36,14 @@ class CallSiteMaker(Analysis):
37
36
  Add calling convention, declaration, and args to a call site.
38
37
  """
39
38
 
40
- def __init__(self, block, reaching_definitions=None, stack_pointer_tracker=None, ail_manager: Manager = None):
39
+ def __init__(
40
+ self, block, reaching_definitions=None, stack_pointer_tracker=None, ail_manager: Manager | None = None
41
+ ):
41
42
  self.block = block
42
43
 
43
44
  self._reaching_definitions = reaching_definitions
44
45
  self._stack_pointer_tracker = stack_pointer_tracker
45
- self._ail_manager = ail_manager
46
+ self._ail_manager: Manager | None = ail_manager
46
47
 
47
48
  self.result_block = None
48
49
  self.stack_arg_offsets: set[tuple[int, int]] | None = None # call ins addr, stack_offset
@@ -109,16 +110,8 @@ class CallSiteMaker(Analysis):
109
110
  # make sure the function prototype is resolved.
110
111
  # TODO: Cache resolved function prototypes globally
111
112
  prototype_libname = func.prototype_libname
112
- type_collections = []
113
113
  if prototype_libname is not None:
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
- if type_collections:
119
- prototype = dereference_simtype(prototype, type_collections).with_arch( # type: ignore
120
- self.project.arch
121
- )
114
+ prototype = cast(SimTypeFunction, dereference_simtype_by_lib(prototype, prototype_libname))
122
115
 
123
116
  args = []
124
117
  arg_vvars = []
@@ -151,6 +144,10 @@ class CallSiteMaker(Analysis):
151
144
  # across registers). most importantly, a ComboArg represents one variable, not multiple, but we
152
145
  # have no way to know that until later down the pipeline.
153
146
  expanded_arg_locs += arg_loc.locations
147
+ elif isinstance(arg_loc, SimStructArg):
148
+ expanded_arg_locs += [ # type: ignore
149
+ arg_loc.locs[field_name] for field_name in arg_loc.struct.fields if field_name in arg_loc.locs
150
+ ]
154
151
  elif isinstance(arg_loc, (SimRegArg, SimStackArg, SimReferenceArgument)):
155
152
  expanded_arg_locs.append(arg_loc)
156
153
  else:
@@ -195,12 +192,15 @@ class CallSiteMaker(Analysis):
195
192
  if vvar_def_reg_offset is not None and offset > vvar_def_reg_offset:
196
193
  # we need to shift the value
197
194
  vvar_use = Expr.BinaryOp(
198
- self._ail_manager.next_atom(),
195
+ self._ail_manager.next_atom() if self._ail_manager is not None else None,
199
196
  "Shr",
200
197
  [
201
198
  vvar_use,
202
199
  Expr.Const(
203
- self._ail_manager.next_atom(), None, (offset - vvar_def_reg_offset) * 8, 8
200
+ self._ail_manager.next_atom() if self._ail_manager is not None else None,
201
+ None,
202
+ (offset - vvar_def_reg_offset) * 8,
203
+ 8,
204
204
  ),
205
205
  ],
206
206
  **vvar_use.tags,
@@ -208,7 +208,7 @@ class CallSiteMaker(Analysis):
208
208
  if vvar_def.size > arg_loc.size:
209
209
  # we need to narrow the value
210
210
  vvar_use = Expr.Convert(
211
- self._ail_manager.next_atom(),
211
+ self._ail_manager.next_atom() if self._ail_manager is not None else None,
212
212
  vvar_use.bits,
213
213
  arg_loc.size * self.project.arch.byte_width,
214
214
  False,
@@ -15,6 +15,23 @@ X86_CondBitOffsets = data["X86"]["CondBitOffsets"]
15
15
  class X86CCallRewriter(CCallRewriterBase):
16
16
  """
17
17
  Implements VEX ccall rewriter for X86.
18
+
19
+ From libVEX:
20
+
21
+ A summary of the field usages is:
22
+
23
+ Operation DEP1 DEP2 NDEP
24
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25
+
26
+ add/sub/mul first arg second arg unused
27
+ adc/sbb first arg (second arg)
28
+ XOR old_carry old_carry
29
+ and/or/xor result zero unused
30
+ inc/dec result zero old_carry
31
+ shl/shr/sar result subshifted- unused
32
+ result
33
+ rol/ror result zero old_flags
34
+ copy old_flags zero unused.
18
35
  """
19
36
 
20
37
  __slots__ = ()
@@ -28,7 +45,7 @@ class X86CCallRewriter(CCallRewriterBase):
28
45
  if isinstance(cond, Expr.Const) and isinstance(op, Expr.Const):
29
46
  cond_v = cond.value
30
47
  op_v = op.value
31
- if cond_v == X86_CondTypes["CondLE"]: # noqa: SIM102
48
+ if cond_v == X86_CondTypes["CondLE"]:
32
49
  if op_v in {
33
50
  X86_OpTypes["G_CC_OP_SUBB"],
34
51
  X86_OpTypes["G_CC_OP_SUBW"],
@@ -52,6 +69,198 @@ class X86CCallRewriter(CCallRewriterBase):
52
69
 
53
70
  r = Expr.BinaryOp(ccall.idx, "CmpLE", (dep_1, dep_2), True, **ccall.tags)
54
71
  return Expr.Convert(None, r.bits, ccall.bits, False, r, **ccall.tags)
72
+ elif cond_v == X86_CondTypes["CondO"]:
73
+ op_v = op.value
74
+ ret_cond = None
75
+ if op_v in {
76
+ X86_OpTypes["G_CC_OP_UMULB"],
77
+ X86_OpTypes["G_CC_OP_UMULW"],
78
+ X86_OpTypes["G_CC_OP_UMULL"],
79
+ }:
80
+ # dep_1 * dep_2 >= max_signed_byte/word/dword
81
+ ret = Expr.BinaryOp(
82
+ None,
83
+ "Mul",
84
+ (dep_1, dep_2),
85
+ bits=dep_1.bits * 2,
86
+ **ccall.tags,
87
+ )
88
+ max_signed = Expr.Const(
89
+ None,
90
+ None,
91
+ (1 << (dep_1.bits - 1)),
92
+ bits=dep_1.bits * 2,
93
+ **ccall.tags,
94
+ )
95
+ ret_cond = Expr.BinaryOp(None, "CmpGE", (ret, max_signed), signed=False, **ccall.tags)
96
+ if op_v in {
97
+ X86_OpTypes["G_CC_OP_INCB"],
98
+ X86_OpTypes["G_CC_OP_INCW"],
99
+ X86_OpTypes["G_CC_OP_INCL"],
100
+ }:
101
+ # dep_1 is the result
102
+ overflowed = Expr.Const(
103
+ None,
104
+ None,
105
+ 1 << (dep_1.bits - 1),
106
+ dep_1.bits,
107
+ **ccall.tags,
108
+ )
109
+ ret_cond = Expr.BinaryOp(None, "CmpEQ", (dep_1, overflowed), **ccall.tags)
110
+
111
+ if ret_cond is not None:
112
+ return Expr.ITE(
113
+ ccall.idx,
114
+ ret_cond,
115
+ Expr.Const(None, None, 0, 1, **ccall.tags),
116
+ Expr.Const(None, None, 1, 1, **ccall.tags),
117
+ **ccall.tags,
118
+ )
119
+ elif cond_v == X86_CondTypes["CondZ"]:
120
+ op_v = op.value
121
+ if op_v in {
122
+ X86_OpTypes["G_CC_OP_ADDB"],
123
+ X86_OpTypes["G_CC_OP_ADDW"],
124
+ X86_OpTypes["G_CC_OP_ADDL"],
125
+ }:
126
+ # dep_1 + dep_2 == 0
127
+ ret = Expr.BinaryOp(
128
+ None,
129
+ "Add",
130
+ (dep_1, dep_2),
131
+ bits=dep_1.bits,
132
+ **ccall.tags,
133
+ )
134
+ zero = Expr.Const(
135
+ None,
136
+ None,
137
+ 0,
138
+ dep_1.bits,
139
+ **ccall.tags,
140
+ )
141
+ return Expr.BinaryOp(
142
+ ccall.idx,
143
+ "CmpEQ",
144
+ (ret, zero),
145
+ True,
146
+ **ccall.tags,
147
+ )
148
+ if op_v in {
149
+ X86_OpTypes["G_CC_OP_SUBB"],
150
+ X86_OpTypes["G_CC_OP_SUBW"],
151
+ X86_OpTypes["G_CC_OP_SUBL"],
152
+ }:
153
+ # dep_1 - dep_2 == 0
154
+ return Expr.BinaryOp(
155
+ ccall.idx,
156
+ "CmpEQ",
157
+ (dep_1, dep_2),
158
+ True,
159
+ **ccall.tags,
160
+ )
161
+ if op_v in {
162
+ X86_OpTypes["G_CC_OP_LOGICB"],
163
+ X86_OpTypes["G_CC_OP_LOGICW"],
164
+ X86_OpTypes["G_CC_OP_LOGICL"],
165
+ }:
166
+ # dep_1 == 0
167
+ return Expr.BinaryOp(
168
+ ccall.idx,
169
+ "CmpEQ",
170
+ (dep_1, Expr.Const(None, None, 0, dep_1.bits, **ccall.tags)),
171
+ True,
172
+ **ccall.tags,
173
+ )
174
+ elif cond_v == X86_CondTypes["CondL"]:
175
+ op_v = op.value
176
+ if op_v in {
177
+ X86_OpTypes["G_CC_OP_SUBB"],
178
+ X86_OpTypes["G_CC_OP_SUBW"],
179
+ X86_OpTypes["G_CC_OP_SUBL"],
180
+ }:
181
+ # dep_1 - dep_2 < 0
182
+ return Expr.BinaryOp(
183
+ ccall.idx,
184
+ "CmpLT",
185
+ (dep_1, dep_2),
186
+ True,
187
+ **ccall.tags,
188
+ )
189
+ if op_v in {
190
+ X86_OpTypes["G_CC_OP_LOGICB"],
191
+ X86_OpTypes["G_CC_OP_LOGICW"],
192
+ X86_OpTypes["G_CC_OP_LOGICL"],
193
+ }:
194
+ # dep_1 < 0
195
+ return Expr.BinaryOp(
196
+ ccall.idx,
197
+ "CmpLT",
198
+ (dep_1, Expr.Const(None, None, 0, dep_1.bits, **ccall.tags)),
199
+ True,
200
+ **ccall.tags,
201
+ )
202
+ elif cond_v in {
203
+ X86_CondTypes["CondBE"],
204
+ X86_CondTypes["CondB"],
205
+ }:
206
+ op_v = op.value
207
+ if op_v in {
208
+ X86_OpTypes["G_CC_OP_ADDB"],
209
+ X86_OpTypes["G_CC_OP_ADDW"],
210
+ X86_OpTypes["G_CC_OP_ADDL"],
211
+ }:
212
+ # dep_1 + dep_2 <= 0 if CondBE
213
+ # dep_1 + dep_2 < 0 if CondB
214
+ ret = Expr.BinaryOp(
215
+ None,
216
+ "Add",
217
+ (dep_1, dep_2),
218
+ signed=False,
219
+ bits=dep_1.bits,
220
+ **ccall.tags,
221
+ )
222
+ zero = Expr.Const(
223
+ None,
224
+ None,
225
+ 0,
226
+ dep_1.bits,
227
+ **ccall.tags,
228
+ )
229
+ return Expr.BinaryOp(
230
+ ccall.idx,
231
+ "CmpLE" if cond_v == X86_CondTypes["CondBE"] else "CmpLT",
232
+ (ret, zero),
233
+ False,
234
+ **ccall.tags,
235
+ )
236
+ if op_v in {
237
+ X86_OpTypes["G_CC_OP_SUBB"],
238
+ X86_OpTypes["G_CC_OP_SUBW"],
239
+ X86_OpTypes["G_CC_OP_SUBL"],
240
+ }:
241
+ # dep_1 <= dep_2 if CondBE
242
+ # dep_1 < dep_2 if CondB
243
+ return Expr.BinaryOp(
244
+ ccall.idx,
245
+ "CmpLE" if cond_v == X86_CondTypes["CondBE"] else "CmpLT",
246
+ (dep_1, dep_2),
247
+ False,
248
+ **ccall.tags,
249
+ )
250
+ if op_v in {
251
+ X86_OpTypes["G_CC_OP_LOGICB"],
252
+ X86_OpTypes["G_CC_OP_LOGICW"],
253
+ X86_OpTypes["G_CC_OP_LOGICL"],
254
+ }:
255
+ # dep_1 <= 0 if CondBE
256
+ # dep_1 < 0 if CondB
257
+ return Expr.BinaryOp(
258
+ ccall.idx,
259
+ "CmpLE" if cond_v == X86_CondTypes["CondBE"] else "CmpLT",
260
+ (dep_1, Expr.Const(None, None, 0, dep_1.bits, **ccall.tags)),
261
+ False,
262
+ **ccall.tags,
263
+ )
55
264
  return None
56
265
 
57
266
  @staticmethod
@@ -12,7 +12,6 @@ import networkx
12
12
  import capstone
13
13
 
14
14
  import ailment
15
- from angr import SIM_LIBRARIES, SIM_TYPE_COLLECTIONS
16
15
 
17
16
  from angr.errors import AngrDecompilationError
18
17
  from angr.knowledge_base import KnowledgeBase
@@ -22,9 +21,9 @@ from angr.knowledge_plugins.key_definitions import atoms
22
21
  from angr.codenode import BlockNode
23
22
  from angr.utils import timethis
24
23
  from angr.utils.graph import GraphUtils
24
+ from angr.utils.types import dereference_simtype_by_lib
25
25
  from angr.calling_conventions import SimRegArg, SimStackArg, SimFunctionArgument
26
26
  from angr.sim_type import (
27
- dereference_simtype,
28
27
  SimTypeChar,
29
28
  SimTypeInt,
30
29
  SimTypeLongLong,
@@ -485,6 +484,8 @@ class Clinic(Analysis):
485
484
 
486
485
  # duplicate orphaned conditional jump blocks
487
486
  ail_graph = self._duplicate_orphaned_cond_jumps(ail_graph)
487
+ # rewrite jmp_rax function calls
488
+ ail_graph = self._rewrite_jump_rax_calls(ail_graph)
488
489
 
489
490
  # Transform the graph into partial SSA form
490
491
  self._update_progress(35.0, text="Transforming to partial-SSA form")
@@ -930,7 +931,7 @@ class Clinic(Analysis):
930
931
  self.kb.callsite_prototypes.set_prototype(callsite.addr, cc.cc, cc.prototype, manual=False)
931
932
  if func_graph is not None and cc.prototype.returnty is not None:
932
933
  # patch the AIL call statement if we can find one
933
- callsite_ail_block: ailment.Block = next(
934
+ callsite_ail_block: ailment.Block | None = next(
934
935
  iter(bb for bb in func_graph if bb.addr == callsite.addr), None
935
936
  )
936
937
  if callsite_ail_block is not None and callsite_ail_block.statements:
@@ -1005,6 +1006,7 @@ class Clinic(Analysis):
1005
1006
  :return: None
1006
1007
  """
1007
1008
  assert self._func_graph is not None
1009
+ assert self._blocks_by_addr_and_size is not None
1008
1010
 
1009
1011
  for block_node in self._func_graph.nodes():
1010
1012
  ail_block = self._convert(block_node)
@@ -1237,16 +1239,8 @@ class Clinic(Analysis):
1237
1239
  # make sure the function prototype is resolved.
1238
1240
  # TODO: Cache resolved function prototypes globally
1239
1241
  prototype_libname = func.prototype_libname
1240
- type_collections = []
1241
1242
  if prototype_libname is not None:
1242
- for prototype_lib in SIM_LIBRARIES[prototype_libname]:
1243
- if prototype_lib.type_collection_names:
1244
- for typelib_name in prototype_lib.type_collection_names:
1245
- type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
1246
- if type_collections:
1247
- prototype = dereference_simtype(prototype, type_collections).with_arch( # type: ignore
1248
- self.project.arch
1249
- )
1243
+ prototype = dereference_simtype_by_lib(prototype, prototype_libname)
1250
1244
 
1251
1245
  if cc is None:
1252
1246
  l.warning("Call site %#x (callee %s) has an unknown calling convention.", block.addr, repr(func))
@@ -1538,6 +1532,7 @@ class Clinic(Analysis):
1538
1532
  vvar_id_start=self.vvar_id_start,
1539
1533
  )
1540
1534
  self.vvar_id_start = ssailification.max_vvar_id + 1
1535
+ assert ssailification.out_graph is not None
1541
1536
  return ssailification.out_graph
1542
1537
 
1543
1538
  @timethis
@@ -1918,6 +1913,7 @@ class Clinic(Analysis):
1918
1913
  self._link_variables_on_call(variable_manager, global_variables, block, stmt_idx, stmt, is_expr=False)
1919
1914
 
1920
1915
  elif stmt_type is ailment.Stmt.Return:
1916
+ assert isinstance(stmt, ailment.Stmt.Return)
1921
1917
  self._link_variables_on_return(variable_manager, global_variables, block, stmt_idx, stmt)
1922
1918
 
1923
1919
  def _link_variables_on_return(
@@ -2178,6 +2174,47 @@ class Clinic(Analysis):
2178
2174
 
2179
2175
  return ail_graph
2180
2176
 
2177
+ def _rewrite_jump_rax_calls(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
2178
+ """
2179
+ Rewrite calls to special functions (e.g., guard_dispatch_icall_nop) into `call rax`.
2180
+ """
2181
+
2182
+ if self.project.arch.name != "AMD64":
2183
+ return ail_graph
2184
+ if self._cfg is None:
2185
+ return ail_graph
2186
+
2187
+ for block in ail_graph:
2188
+ if not block.statements:
2189
+ continue
2190
+ assert block.addr is not None
2191
+ last_stmt = block.statements[-1]
2192
+ if isinstance(last_stmt, ailment.Stmt.Call):
2193
+ # we can't examine the call target at this point because constant propagation hasn't run yet; we consult
2194
+ # the CFG instead
2195
+ callsite_node = self._cfg.get_any_node(block.addr, anyaddr=True)
2196
+ if callsite_node is None:
2197
+ break
2198
+ callees = self._cfg.get_successors(callsite_node, jumpkind="Ijk_Call")
2199
+ if len(callees) != 1:
2200
+ break
2201
+ callee = callees[0].addr
2202
+ if self.kb.functions.contains_addr(callee):
2203
+ callee_func = self.kb.functions.get_by_addr(callee)
2204
+ if callee_func.info.get("jmp_rax", False) is True:
2205
+ # rewrite this statement into Call(rax)
2206
+ call_stmt = last_stmt.copy()
2207
+ call_stmt.target = ailment.Expr.Register(
2208
+ self._ail_manager.next_atom(),
2209
+ None,
2210
+ self.project.arch.registers["rax"][0],
2211
+ 64,
2212
+ ins_addr=call_stmt.ins_addr,
2213
+ )
2214
+ block.statements[-1] = call_stmt
2215
+
2216
+ return ail_graph
2217
+
2181
2218
  def _rewrite_ite_expressions(self, ail_graph):
2182
2219
  cfg = self._cfg
2183
2220
  for block in list(ail_graph):
@@ -2208,6 +2245,7 @@ class Clinic(Analysis):
2208
2245
  def _create_triangle_for_ite_expression(self, ail_graph, block_addr: int, ite_ins_addr: int):
2209
2246
  ite_insn_only_block = self.project.factory.block(ite_ins_addr, num_inst=1)
2210
2247
  ite_insn_size = ite_insn_only_block.size
2248
+ assert ite_insn_size is not None
2211
2249
  if ite_insn_size <= 2: # we need an address for true_block and another address for false_block
2212
2250
  return None
2213
2251
  if ite_insn_only_block.vex.exit_statements:
@@ -3161,7 +3199,7 @@ class Clinic(Analysis):
3161
3199
  )
3162
3200
  break
3163
3201
 
3164
- if alloca_node is not None:
3202
+ if alloca_node is not None and sp_equal_to is not None:
3165
3203
  stmt0 = alloca_node.statements[1]
3166
3204
  statements = [ailment.Stmt.Call(stmt0.idx, "alloca", args=[sp_equal_to], **stmt0.tags)]
3167
3205
  new_node = ailment.Block(alloca_node.addr, alloca_node.original_size, statements=statements)
@@ -31,7 +31,7 @@ class DecompilationCache:
31
31
  self.parameters: dict[str, Any] = {}
32
32
  self.addr = addr
33
33
  self.type_constraints: dict[TypeVariable, set[TypeConstraint]] | None = None
34
- self.func_typevar = None
34
+ self.func_typevar: TypeVariable | None = None
35
35
  self.var_to_typevar: dict | None = None
36
36
  self.codegen: BaseStructuredCodeGenerator | None = None
37
37
  self.clinic: Clinic | None = None