angr 9.2.131__py3-none-win_amd64.whl → 9.2.132__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 (112) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/analysis.py +6 -2
  3. angr/analyses/cfg/cfg_emulated.py +5 -5
  4. angr/analyses/cfg/cfg_fast.py +2 -2
  5. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +139 -94
  6. angr/analyses/cfg/indirect_jump_resolvers/x86_elf_pic_plt.py +1 -1
  7. angr/analyses/ddg.py +14 -11
  8. angr/analyses/decompiler/ail_simplifier.py +3 -2
  9. angr/analyses/decompiler/block_simplifier.py +10 -21
  10. angr/analyses/decompiler/clinic.py +108 -34
  11. angr/analyses/decompiler/condition_processor.py +12 -10
  12. angr/analyses/decompiler/dephication/graph_rewriting.py +1 -1
  13. angr/analyses/decompiler/dephication/rewriting_engine.py +169 -45
  14. angr/analyses/decompiler/dephication/seqnode_dephication.py +5 -4
  15. angr/analyses/decompiler/optimization_passes/const_derefs.py +1 -0
  16. angr/analyses/decompiler/optimization_passes/div_simplifier.py +41 -16
  17. angr/analyses/decompiler/optimization_passes/engine_base.py +261 -83
  18. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +173 -35
  19. angr/analyses/decompiler/optimization_passes/mod_simplifier.py +5 -2
  20. angr/analyses/decompiler/optimization_passes/optimization_pass.py +39 -19
  21. angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +2 -0
  22. angr/analyses/decompiler/ssailification/rewriting.py +1 -2
  23. angr/analyses/decompiler/ssailification/rewriting_engine.py +138 -55
  24. angr/analyses/decompiler/ssailification/ssailification.py +2 -1
  25. angr/analyses/decompiler/ssailification/traversal.py +4 -6
  26. angr/analyses/decompiler/ssailification/traversal_engine.py +125 -42
  27. angr/analyses/decompiler/structured_codegen/c.py +5 -3
  28. angr/analyses/decompiler/structuring/phoenix.py +26 -9
  29. angr/analyses/decompiler/structuring/structurer_nodes.py +9 -0
  30. angr/analyses/deobfuscator/irsb_reg_collector.py +29 -60
  31. angr/analyses/deobfuscator/string_obf_finder.py +2 -2
  32. angr/analyses/init_finder.py +47 -22
  33. angr/analyses/propagator/engine_base.py +21 -14
  34. angr/analyses/propagator/engine_vex.py +149 -179
  35. angr/analyses/propagator/propagator.py +10 -28
  36. angr/analyses/propagator/top_checker_mixin.py +211 -5
  37. angr/analyses/propagator/vex_vars.py +1 -1
  38. angr/analyses/reaching_definitions/dep_graph.py +1 -1
  39. angr/analyses/reaching_definitions/engine_ail.py +304 -329
  40. angr/analyses/reaching_definitions/engine_vex.py +243 -229
  41. angr/analyses/reaching_definitions/function_handler.py +3 -3
  42. angr/analyses/reaching_definitions/rd_state.py +37 -32
  43. angr/analyses/s_propagator.py +18 -3
  44. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +9 -5
  45. angr/analyses/typehoon/simple_solver.py +7 -5
  46. angr/analyses/typehoon/translator.py +8 -0
  47. angr/analyses/typehoon/typeconsts.py +10 -2
  48. angr/analyses/typehoon/typevars.py +9 -7
  49. angr/analyses/variable_recovery/engine_ail.py +299 -259
  50. angr/analyses/variable_recovery/engine_base.py +135 -117
  51. angr/analyses/variable_recovery/engine_vex.py +175 -185
  52. angr/analyses/variable_recovery/irsb_scanner.py +49 -38
  53. angr/analyses/variable_recovery/variable_recovery.py +28 -5
  54. angr/analyses/variable_recovery/variable_recovery_base.py +32 -33
  55. angr/analyses/variable_recovery/variable_recovery_fast.py +2 -2
  56. angr/analyses/xrefs.py +46 -19
  57. angr/annocfg.py +19 -14
  58. angr/block.py +4 -9
  59. angr/calling_conventions.py +1 -1
  60. angr/engines/engine.py +30 -14
  61. angr/engines/light/__init__.py +11 -3
  62. angr/engines/light/engine.py +1003 -1185
  63. angr/engines/pcode/cc.py +2 -0
  64. angr/engines/successors.py +13 -9
  65. angr/engines/vex/claripy/datalayer.py +1 -1
  66. angr/engines/vex/claripy/irop.py +1 -1
  67. angr/engines/vex/light/slicing.py +2 -2
  68. angr/exploration_techniques/__init__.py +1 -124
  69. angr/exploration_techniques/base.py +126 -0
  70. angr/exploration_techniques/bucketizer.py +1 -1
  71. angr/exploration_techniques/dfs.py +3 -1
  72. angr/exploration_techniques/director.py +2 -3
  73. angr/exploration_techniques/driller_core.py +1 -1
  74. angr/exploration_techniques/explorer.py +4 -2
  75. angr/exploration_techniques/lengthlimiter.py +2 -1
  76. angr/exploration_techniques/local_loop_seer.py +2 -1
  77. angr/exploration_techniques/loop_seer.py +5 -5
  78. angr/exploration_techniques/manual_mergepoint.py +2 -1
  79. angr/exploration_techniques/memory_watcher.py +3 -1
  80. angr/exploration_techniques/oppologist.py +4 -5
  81. angr/exploration_techniques/slicecutor.py +4 -2
  82. angr/exploration_techniques/spiller.py +1 -1
  83. angr/exploration_techniques/stochastic.py +2 -1
  84. angr/exploration_techniques/stub_stasher.py +2 -1
  85. angr/exploration_techniques/suggestions.py +3 -1
  86. angr/exploration_techniques/symbion.py +3 -1
  87. angr/exploration_techniques/tech_builder.py +2 -1
  88. angr/exploration_techniques/threading.py +4 -7
  89. angr/exploration_techniques/timeout.py +4 -2
  90. angr/exploration_techniques/tracer.py +4 -3
  91. angr/exploration_techniques/unique.py +3 -2
  92. angr/exploration_techniques/veritesting.py +1 -1
  93. angr/knowledge_plugins/key_definitions/atoms.py +2 -2
  94. angr/knowledge_plugins/key_definitions/live_definitions.py +16 -13
  95. angr/knowledge_plugins/propagations/states.py +13 -8
  96. angr/knowledge_plugins/variables/variable_manager.py +23 -9
  97. angr/lib/angr_native.dll +0 -0
  98. angr/sim_manager.py +1 -3
  99. angr/sim_state.py +39 -41
  100. angr/sim_type.py +5 -0
  101. angr/sim_variable.py +29 -28
  102. angr/utils/bits.py +12 -0
  103. angr/utils/orderedset.py +4 -1
  104. angr/utils/ssa/__init__.py +21 -3
  105. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/METADATA +6 -6
  106. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/RECORD +110 -111
  107. angr/analyses/propagator/engine_ail.py +0 -1562
  108. angr/storage/memory_mixins/__init__.pyi +0 -48
  109. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/LICENSE +0 -0
  110. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/WHEEL +0 -0
  111. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/entry_points.txt +0 -0
  112. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/top_level.txt +0 -0
angr/analyses/ddg.py CHANGED
@@ -2,8 +2,10 @@ from __future__ import annotations
2
2
  import logging
3
3
  from collections import defaultdict
4
4
 
5
+ import claripy
5
6
  import networkx
6
7
  import pyvex
8
+
7
9
  from . import Analysis
8
10
 
9
11
  from angr.code_location import CodeLocation
@@ -1030,9 +1032,9 @@ class DDG(Analysis):
1030
1032
 
1031
1033
  if not action.data.reg_deps and not action.data.tmp_deps:
1032
1034
  # might be a constant assignment
1033
- v = action.data.ast
1035
+ v: claripy.ast.BV = action.data.ast
1034
1036
  if not v.symbolic:
1035
- const_var = SimConstantVariable(v.concrete_value)
1037
+ const_var = SimConstantVariable(value=v.concrete_value, size=v.size())
1036
1038
  const_progvar = ProgramVariable(const_var, prog_var.location)
1037
1039
  self._data_graph_add_edge(const_progvar, prog_var, type="mem_data")
1038
1040
 
@@ -1109,7 +1111,8 @@ class DDG(Analysis):
1109
1111
  elif isinstance(statement.data, pyvex.IRExpr.Const):
1110
1112
  # assignment
1111
1113
  const = statement.data.con.value
1112
- self._ast_graph.add_edge(ProgramVariable(SimConstantVariable(const), location), pv)
1114
+ size = statement.data.con.size
1115
+ self._ast_graph.add_edge(ProgramVariable(SimConstantVariable(value=const, size=size), location), pv)
1113
1116
 
1114
1117
  def _handle_reg_read(self, action, location, state, statement): # pylint:disable=unused-argument
1115
1118
  reg_offset = action.offset
@@ -1140,7 +1143,7 @@ class DDG(Analysis):
1140
1143
  elif reg_offset == self.project.arch.bp_offset:
1141
1144
  self._custom_data_per_statement = ("bp", 0)
1142
1145
 
1143
- def _handle_reg_write(self, action, location, state, statement): # pylint:disable=unused-argument
1146
+ def _handle_reg_write(self, action, location, state, statement: pyvex.stmt.Put): # pylint:disable=unused-argument
1144
1147
  reg_offset = action.offset
1145
1148
  variable = SimRegisterVariable(reg_offset, action.data.ast.size() // 8)
1146
1149
 
@@ -1157,9 +1160,9 @@ class DDG(Analysis):
1157
1160
  if not action.reg_deps and not action.tmp_deps:
1158
1161
  # moving a constant into the register
1159
1162
  # try to parse out the constant from statement
1160
- const_variable = SimConstantVariable()
1163
+ const_variable = SimConstantVariable(size=1)
1161
1164
  if statement is not None and isinstance(statement.data, pyvex.IRExpr.Const):
1162
- const_variable = SimConstantVariable(value=statement.data.con.value)
1165
+ const_variable = SimConstantVariable(value=statement.data.con.value, size=statement.data.con.size)
1163
1166
  const_pv = ProgramVariable(const_variable, location, arch=self.project.arch)
1164
1167
  self._data_graph_add_edge(const_pv, pv)
1165
1168
 
@@ -1187,7 +1190,7 @@ class DDG(Analysis):
1187
1190
  ast = None
1188
1191
 
1189
1192
  tmp = action.tmp
1190
- pv = ProgramVariable(SimTemporaryVariable(tmp), location, arch=self.project.arch)
1193
+ pv = ProgramVariable(SimTemporaryVariable(tmp, len(action.data)), location, arch=self.project.arch)
1191
1194
 
1192
1195
  if ast is not None:
1193
1196
  for operand in ast.operands:
@@ -1230,12 +1233,12 @@ class DDG(Analysis):
1230
1233
  if not action.tmp_deps and not self._variables_per_statement and not ast:
1231
1234
  # read in a constant
1232
1235
  # try to parse out the constant from statement
1233
- const_variable = SimConstantVariable()
1236
+ const_variable = SimConstantVariable(size=1)
1234
1237
  if statement is not None:
1235
1238
  if isinstance(statement, pyvex.IRStmt.Dirty):
1236
1239
  l.warning("Dirty statements are not supported in DDG for now.")
1237
1240
  elif isinstance(statement.data, pyvex.IRExpr.Const):
1238
- const_variable = SimConstantVariable(value=statement.data.con.value)
1241
+ const_variable = SimConstantVariable(value=statement.data.con.value, size=statement.data.con.size)
1239
1242
  const_pv = ProgramVariable(const_variable, location, arch=self.project.arch)
1240
1243
  self._data_graph_add_edge(const_pv, pv)
1241
1244
 
@@ -1296,7 +1299,7 @@ class DDG(Analysis):
1296
1299
  const_value = expr_1.ast.args[0]
1297
1300
  tmp = next(iter(expr_0.tmp_deps))
1298
1301
 
1299
- const_def = ProgramVariable(SimConstantVariable(const_value), location)
1302
+ const_def = ProgramVariable(SimConstantVariable(value=const_value, size=len(expr_1.ast)), location)
1300
1303
  tmp_def = self._temp_variables[tmp]
1301
1304
  return AST("-", tmp_def, const_def)
1302
1305
 
@@ -1310,7 +1313,7 @@ class DDG(Analysis):
1310
1313
  const_value = expr_1.ast.args[0]
1311
1314
  tmp = next(iter(expr_0.tmp_deps))
1312
1315
 
1313
- const_def = ProgramVariable(SimConstantVariable(const_value), location)
1316
+ const_def = ProgramVariable(SimConstantVariable(value=const_value, size=len(expr_1.ast)), location)
1314
1317
  tmp_def = self._temp_variables[tmp]
1315
1318
  return AST("+", tmp_def, const_def)
1316
1319
 
@@ -24,6 +24,7 @@ from ailment.expression import (
24
24
  VirtualVariable,
25
25
  )
26
26
 
27
+ from angr.analyses.s_propagator import SPropagatorAnalysis
27
28
  from angr.analyses.s_reaching_definitions import SRDAModel
28
29
  from angr.utils.ail import is_phi_assignment, HasExprWalker
29
30
  from angr.code_location import CodeLocation, ExternalCodeLocation
@@ -213,11 +214,11 @@ class AILSimplifier(Analysis):
213
214
  self._reaching_definitions = rd
214
215
  return rd
215
216
 
216
- def _compute_propagation(self, immediate_stmt_removal: bool = False):
217
+ def _compute_propagation(self, immediate_stmt_removal: bool = False) -> SPropagatorAnalysis:
217
218
  # Propagate expressions or return the existing result
218
219
  if self._propagator is not None:
219
220
  return self._propagator
220
- prop = self.project.analyses.SPropagator(
221
+ prop = self.project.analyses[SPropagatorAnalysis].prep()(
221
222
  subject=self.func,
222
223
  func_graph=self.func_graph,
223
224
  # gp=self._gp,
@@ -2,10 +2,10 @@
2
2
  from __future__ import annotations
3
3
  import logging
4
4
  from typing import TYPE_CHECKING
5
- from collections.abc import Iterable
5
+ from collections.abc import Iterable, Mapping
6
6
 
7
7
  from ailment.statement import Statement, Assignment, Call, Store, Jump
8
- from ailment.expression import Tmp, Load, Const, Register, Convert
8
+ from ailment.expression import Tmp, Load, Const, Register, Convert, Expression
9
9
  from ailment import AILBlockWalkerBase
10
10
 
11
11
  from angr.code_location import ExternalCodeLocation, CodeLocation
@@ -139,7 +139,7 @@ class BlockSimplifier(Analysis):
139
139
 
140
140
  self.result_block = block
141
141
 
142
- def _compute_propagation(self, block):
142
+ def _compute_propagation(self, block) -> SPropagatorAnalysis:
143
143
  if self._propagator is None:
144
144
  self._propagator = self.project.analyses[SPropagatorAnalysis].prep()(
145
145
  subject=block,
@@ -155,7 +155,6 @@ class BlockSimplifier(Analysis):
155
155
  .prep()(
156
156
  subject=block,
157
157
  track_tmps=True,
158
- stack_pointer_tracker=self._stack_pointer_tracker,
159
158
  func_addr=self.func_addr,
160
159
  )
161
160
  .model
@@ -201,8 +200,8 @@ class BlockSimplifier(Analysis):
201
200
 
202
201
  @staticmethod
203
202
  def _replace_and_build(
204
- block,
205
- replacements,
203
+ block: Block,
204
+ replacements: Mapping[CodeLocation, Mapping[Expression, Expression]],
206
205
  replace_assignment_dsts: bool = False,
207
206
  replace_loads: bool = False,
208
207
  gp: int | None = None,
@@ -211,14 +210,9 @@ class BlockSimplifier(Analysis):
211
210
  new_statements = block.statements[::]
212
211
  replaced = False
213
212
 
214
- stmts_to_remove = set()
215
213
  for codeloc, repls in replacements.items():
216
214
  for old, new in repls.items():
217
- stmt_to_remove = None
218
- if isinstance(new, dict):
219
- stmt_to_remove = new["stmt_to_remove"]
220
- new = new["expr"]
221
-
215
+ assert codeloc.stmt_idx is not None
222
216
  stmt = new_statements[codeloc.stmt_idx]
223
217
  if (
224
218
  not replace_loads
@@ -229,7 +223,9 @@ class BlockSimplifier(Analysis):
229
223
  # skip memory-based replacement for non-Call and non-gp-loading statements
230
224
  continue
231
225
  if stmt == old:
232
- # replace this statement
226
+ # the replacement must be a call, since replacements can only be expressions
227
+ # and call is the only thing which is both a statement and an expression
228
+ assert isinstance(new, Call)
233
229
  r = True
234
230
  new_stmt = new
235
231
  else:
@@ -257,20 +253,13 @@ class BlockSimplifier(Analysis):
257
253
  r, new_stmt = stmt.replace(old, new)
258
254
 
259
255
  if r:
256
+ assert new_stmt is not None
260
257
  replaced = True
261
258
  new_statements[codeloc.stmt_idx] = new_stmt
262
- if stmt_to_remove is not None:
263
- stmts_to_remove.add(stmt_to_remove)
264
259
 
265
260
  if not replaced:
266
261
  return False, block
267
262
 
268
- if stmts_to_remove:
269
- stmt_ids_to_remove = {a.stmt_idx for a in stmts_to_remove}
270
- all_stmts = {idx: stmt for idx, stmt in enumerate(new_statements) if idx not in stmt_ids_to_remove}
271
- filtered_stmts = sorted(all_stmts.items(), key=lambda x: x[0])
272
- new_statements = [stmt for _, stmt in filtered_stmts]
273
-
274
263
  new_block = block.copy()
275
264
  new_block.statements = new_statements
276
265
  return True, new_block
@@ -1,3 +1,4 @@
1
+ # pylint:disable=too-many-boolean-expressions
1
2
  from __future__ import annotations
2
3
  from typing import Any, NamedTuple, TYPE_CHECKING
3
4
  import copy
@@ -12,6 +13,7 @@ import capstone
12
13
 
13
14
  import ailment
14
15
 
16
+ from angr.analyses.decompiler.ssailification.ssailification import Ssailification
15
17
  from angr.errors import AngrDecompilationError
16
18
  from angr.knowledge_base import KnowledgeBase
17
19
  from angr.knowledge_plugins.functions import Function
@@ -268,6 +270,7 @@ class Clinic(Analysis):
268
270
  def _decompilation_fixups(self, ail_graph):
269
271
  is_pcode_arch = ":" in self.project.arch.name
270
272
 
273
+ self._remove_redundant_jump_blocks(ail_graph)
271
274
  # _fix_abnormal_switch_case_heads may re-lift from VEX blocks, so it should be placed as high up as possible
272
275
  self._fix_abnormal_switch_case_heads(ail_graph)
273
276
  if self._rewrite_ites_to_diamonds:
@@ -1359,10 +1362,9 @@ class Clinic(Analysis):
1359
1362
 
1360
1363
  @timethis
1361
1364
  def _transform_to_ssa_level0(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
1362
- ssailification = self.project.analyses.Ssailification(
1365
+ ssailification = self.project.analyses[Ssailification].prep(fail_fast=self._fail_fast)(
1363
1366
  self.function,
1364
1367
  ail_graph,
1365
- fail_fast=self._fail_fast,
1366
1368
  entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
1367
1369
  ail_manager=self._ail_manager,
1368
1370
  ssa_stackvars=False,
@@ -1932,10 +1934,12 @@ class Clinic(Analysis):
1932
1934
  break
1933
1935
 
1934
1936
  def _create_triangle_for_ite_expression(self, ail_graph, block_addr: int, ite_ins_addr: int):
1935
- # lift the ite instruction to get its size
1936
- ite_insn_size = self.project.factory.block(ite_ins_addr, num_inst=1).size
1937
+ ite_insn_only_block = self.project.factory.block(ite_ins_addr, num_inst=1)
1938
+ ite_insn_size = ite_insn_only_block.size
1937
1939
  if ite_insn_size <= 2: # we need an address for true_block and another address for false_block
1938
1940
  return None
1941
+ if ite_insn_only_block.vex.exit_statements:
1942
+ return None
1939
1943
 
1940
1944
  # relift the head and the ITE instruction
1941
1945
  new_head = self.project.factory.block(
@@ -2037,6 +2041,10 @@ class Clinic(Analysis):
2037
2041
  ):
2038
2042
  return None
2039
2043
 
2044
+ # corner-case: the last statement of original_block might have been patched by _remove_redundant_jump_blocks.
2045
+ # we detect such case and fix it in new_head_ail
2046
+ self._remove_redundant_jump_blocks_repatch_relifted_block(original_block, end_block_ail)
2047
+
2040
2048
  ail_graph.remove_node(original_block)
2041
2049
 
2042
2050
  if end_block_ail not in ail_graph:
@@ -2142,15 +2150,15 @@ class Clinic(Analysis):
2142
2150
  ),
2143
2151
  None,
2144
2152
  )
2153
+ intended_head_block = self.project.factory.block(
2154
+ intended_head.addr, size=intended_head.original_size
2155
+ )
2145
2156
  if comparison_stmt is not None:
2146
- intended_head_block = self.project.factory.block(
2147
- intended_head.addr, size=intended_head.original_size
2148
- )
2149
2157
  cmp_rpos = len(
2150
2158
  intended_head_block.instruction_addrs
2151
2159
  ) - intended_head_block.instruction_addrs.index(comparison_stmt.ins_addr)
2152
2160
  else:
2153
- cmp_rpos = 2
2161
+ cmp_rpos = min(len(intended_head_block.instruction_addrs), 2)
2154
2162
  self._fix_abnormal_switch_case_heads_case2(
2155
2163
  ail_graph,
2156
2164
  candidate,
@@ -2216,7 +2224,6 @@ class Clinic(Analysis):
2216
2224
  if isinstance(stmt, ailment.Stmt.Assignment)
2217
2225
  and isinstance(stmt.src, ailment.Expr.BinaryOp)
2218
2226
  and stmt.src.op in ailment.Expr.BinaryOp.COMPARISON_NEGATION
2219
- and isinstance(stmt.src.operands[1], ailment.Expr.Const)
2220
2227
  and stmt.ins_addr == last_stmt_0.ins_addr
2221
2228
  ),
2222
2229
  None,
@@ -2228,7 +2235,6 @@ class Clinic(Analysis):
2228
2235
  if isinstance(stmt, ailment.Stmt.Assignment)
2229
2236
  and isinstance(stmt.src, ailment.Expr.BinaryOp)
2230
2237
  and stmt.src.op in ailment.Expr.BinaryOp.COMPARISON_NEGATION
2231
- and isinstance(stmt.src.operands[1], ailment.Expr.Const)
2232
2238
  and stmt.ins_addr == last_stmt_1.ins_addr
2233
2239
  ),
2234
2240
  None,
@@ -2270,51 +2276,86 @@ class Clinic(Analysis):
2270
2276
  # split the intended head into two
2271
2277
  intended_head_block = self.project.factory.block(intended_head.addr, size=intended_head.original_size)
2272
2278
  split_ins_addr = intended_head_block.instruction_addrs[-intended_head_split_insns]
2273
- intended_head_block_0 = self.project.factory.block(intended_head.addr, size=split_ins_addr - intended_head.addr)
2279
+ # note that the two blocks can be fully overlapping, so block_0 will be empty...
2280
+ intended_head_block_0 = (
2281
+ self.project.factory.block(intended_head.addr, size=split_ins_addr - intended_head.addr)
2282
+ if split_ins_addr != intended_head.addr
2283
+ else None
2284
+ )
2274
2285
  intended_head_block_1 = self.project.factory.block(
2275
2286
  split_ins_addr, size=intended_head.addr + intended_head.original_size - split_ins_addr
2276
2287
  )
2277
- intended_head_0 = self._convert_vex(intended_head_block_0)
2288
+ intended_head_0 = self._convert_vex(intended_head_block_0) if intended_head_block_0 is not None else None
2278
2289
  intended_head_1 = self._convert_vex(intended_head_block_1)
2279
2290
 
2291
+ # corner-case: the last statement of intended_head might have been patched by _remove_redundant_jump_blocks. we
2292
+ # detect such case and fix it in intended_head_1
2293
+ self._remove_redundant_jump_blocks_repatch_relifted_block(intended_head, intended_head_1)
2294
+
2280
2295
  # adjust the graph accordingly
2281
2296
  preds = list(ail_graph.predecessors(intended_head))
2282
2297
  succs = list(ail_graph.successors(intended_head))
2283
2298
  ail_graph.remove_node(intended_head)
2284
- ail_graph.add_edge(intended_head_0, intended_head_1)
2285
- for pred in preds:
2286
- if pred is intended_head:
2287
- ail_graph.add_edge(intended_head_1, intended_head_0)
2288
- else:
2289
- ail_graph.add_edge(pred, intended_head_0)
2290
- for succ in succs:
2291
- if succ is intended_head:
2292
- ail_graph.add_edge(intended_head_1, intended_head_0)
2293
- else:
2294
- ail_graph.add_edge(intended_head_1, succ)
2299
+
2300
+ if intended_head_0 is None:
2301
+ # perfect overlap; the first block is empty
2302
+ for pred in preds:
2303
+ if pred is intended_head:
2304
+ ail_graph.add_edge(intended_head_1, intended_head_1)
2305
+ else:
2306
+ ail_graph.add_edge(pred, intended_head_1)
2307
+ for succ in succs:
2308
+ if succ is intended_head:
2309
+ ail_graph.add_edge(intended_head_1, intended_head_1)
2310
+ else:
2311
+ ail_graph.add_edge(intended_head_1, succ)
2312
+ else:
2313
+ ail_graph.add_edge(intended_head_0, intended_head_1)
2314
+ for pred in preds:
2315
+ if pred is intended_head:
2316
+ ail_graph.add_edge(intended_head_1, intended_head_0)
2317
+ else:
2318
+ ail_graph.add_edge(pred, intended_head_0)
2319
+ for succ in succs:
2320
+ if succ is intended_head:
2321
+ ail_graph.add_edge(intended_head_1, intended_head_0)
2322
+ else:
2323
+ ail_graph.add_edge(intended_head_1, succ)
2295
2324
 
2296
2325
  # split other heads
2297
2326
  for o in other_heads:
2298
2327
  if other_head_split_insns > 0:
2299
2328
  o_block = self.project.factory.block(o.addr, size=o.original_size)
2300
2329
  o_split_addr = o_block.instruction_addrs[-other_head_split_insns]
2301
- new_o_block = self.project.factory.block(o.addr, size=o_split_addr - o.addr)
2302
- new_head = self._convert_vex(new_o_block)
2330
+ new_o_block = (
2331
+ self.project.factory.block(o.addr, size=o_split_addr - o.addr) if o_split_addr != o.addr else None
2332
+ )
2333
+ new_head = self._convert_vex(new_o_block) if new_o_block is not None else None
2303
2334
  else:
2304
2335
  new_head = o
2305
2336
 
2306
- if (
2307
- new_head.statements
2308
- and isinstance(new_head.statements[-1], ailment.Stmt.Jump)
2309
- and isinstance(new_head.statements[-1].target, ailment.Expr.Const)
2310
- ):
2311
- # update the jump target
2312
- new_head.statements[-1] = ailment.Stmt.Jump(
2313
- new_head.statements[-1].idx,
2337
+ if new_head is None:
2338
+ # the head is removed - let's replace it with a jump to the target
2339
+ jump_stmt = ailment.Stmt.Jump(
2340
+ None,
2314
2341
  ailment.Expr.Const(None, None, intended_head_1.addr, self.project.arch.bits),
2315
2342
  target_idx=intended_head_1.idx,
2316
- **new_head.statements[-1].tags,
2343
+ ins_addr=o.addr,
2317
2344
  )
2345
+ new_head = ailment.Block(o.addr, 1, statements=[jump_stmt], idx=o.idx)
2346
+ else:
2347
+ if (
2348
+ new_head.statements
2349
+ and isinstance(new_head.statements[-1], ailment.Stmt.Jump)
2350
+ and isinstance(new_head.statements[-1].target, ailment.Expr.Const)
2351
+ ):
2352
+ # update the jump target
2353
+ new_head.statements[-1] = ailment.Stmt.Jump(
2354
+ new_head.statements[-1].idx,
2355
+ ailment.Expr.Const(None, None, intended_head_1.addr, self.project.arch.bits),
2356
+ target_idx=intended_head_1.idx,
2357
+ **new_head.statements[-1].tags,
2358
+ )
2318
2359
 
2319
2360
  # adjust the graph accordingly
2320
2361
  preds = list(ail_graph.predecessors(o))
@@ -2384,6 +2425,39 @@ class Clinic(Analysis):
2384
2425
  ail_graph.add_edge(pred, succs[0])
2385
2426
  ail_graph.remove_node(node)
2386
2427
 
2428
+ @staticmethod
2429
+ def _remove_redundant_jump_blocks_repatch_relifted_block(
2430
+ patched_block: ailment.Block, new_block: ailment.Block
2431
+ ) -> None:
2432
+ """
2433
+ The last statement of patched_block might have been patched by _remove_redundant_jump_blocks. In this case, we
2434
+ fix the last instruction for new_block, which is a newly lifted (from VEX) block that ends at the same address
2435
+ as patched_block.
2436
+
2437
+ :param patched_block: Previously patched block.
2438
+ :param new_block: Newly lifted block.
2439
+ """
2440
+
2441
+ if (
2442
+ isinstance(patched_block.statements[-1], ailment.Stmt.Jump)
2443
+ and isinstance(patched_block.statements[-1].target, ailment.Expr.Const)
2444
+ and isinstance(new_block.statements[-1], ailment.Stmt.Jump)
2445
+ and isinstance(new_block.statements[-1].target, ailment.Expr.Const)
2446
+ and not patched_block.statements[-1].likes(new_block.statements[-1])
2447
+ ):
2448
+ new_block.statements[-1].target = patched_block.statements[-1].target
2449
+ if (
2450
+ isinstance(patched_block.statements[-1], ailment.Stmt.ConditionalJump)
2451
+ and isinstance(patched_block.statements[-1].true_target, ailment.Expr.Const)
2452
+ and isinstance(patched_block.statements[-1].false_target, ailment.Expr.Const)
2453
+ and isinstance(new_block.statements[-1], ailment.Stmt.ConditionalJump)
2454
+ and isinstance(new_block.statements[-1].true_target, ailment.Expr.Const)
2455
+ and isinstance(new_block.statements[-1].false_target, ailment.Expr.Const)
2456
+ and not patched_block.statements[-1].likes(new_block.statements[-1])
2457
+ ):
2458
+ new_block.statements[-1].true_target = patched_block.statements[-1].true_target
2459
+ new_block.statements[-1].false_target = patched_block.statements[-1].false_target
2460
+
2387
2461
  @staticmethod
2388
2462
  def _insert_block_labels(ail_graph):
2389
2463
  for node in ail_graph.nodes:
@@ -116,37 +116,37 @@ _ail2claripy_op_mapping = {
116
116
  "CmpEQ": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) == conv(expr.operands[1], ins_addr=ia),
117
117
  "CmpNE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) != conv(expr.operands[1], ins_addr=ia),
118
118
  "CmpLE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) <= conv(expr.operands[1], ins_addr=ia),
119
- "CmpLEs": lambda expr, conv, _, ia: claripy.SLE(
119
+ "CmpLE (signed)": lambda expr, conv, _, ia: claripy.SLE(
120
120
  conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
121
121
  ),
122
122
  "CmpLT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) < conv(expr.operands[1], ins_addr=ia),
123
- "CmpLTs": lambda expr, conv, _, ia: claripy.SLT(
123
+ "CmpLT (signed)": lambda expr, conv, _, ia: claripy.SLT(
124
124
  conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
125
125
  ),
126
126
  "CmpGE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) >= conv(expr.operands[1], ins_addr=ia),
127
- "CmpGEs": lambda expr, conv, _, ia: claripy.SGE(
127
+ "CmpGE (signed)": lambda expr, conv, _, ia: claripy.SGE(
128
128
  conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
129
129
  ),
130
130
  "CmpGT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) > conv(expr.operands[1], ins_addr=ia),
131
- "CmpGTs": lambda expr, conv, _, ia: claripy.SGT(
131
+ "CmpGT (signed)": lambda expr, conv, _, ia: claripy.SGT(
132
132
  conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
133
133
  ),
134
134
  "CasCmpEQ": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) == conv(expr.operands[1], ins_addr=ia),
135
135
  "CasCmpNE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) != conv(expr.operands[1], ins_addr=ia),
136
136
  "CasCmpLE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) <= conv(expr.operands[1], ins_addr=ia),
137
- "CasCmpLEs": lambda expr, conv, _, ia: claripy.SLE(
137
+ "CasCmpLE (signed)": lambda expr, conv, _, ia: claripy.SLE(
138
138
  conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
139
139
  ),
140
140
  "CasCmpLT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) < conv(expr.operands[1], ins_addr=ia),
141
- "CasCmpLTs": lambda expr, conv, _, ia: claripy.SLT(
141
+ "CasCmpLT (signed)": lambda expr, conv, _, ia: claripy.SLT(
142
142
  conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
143
143
  ),
144
144
  "CasCmpGE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) >= conv(expr.operands[1], ins_addr=ia),
145
- "CasCmpGEs": lambda expr, conv, _, ia: claripy.SGE(
145
+ "CasCmpGE (signed)": lambda expr, conv, _, ia: claripy.SGE(
146
146
  conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
147
147
  ),
148
148
  "CasCmpGT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) > conv(expr.operands[1], ins_addr=ia),
149
- "CasCmpGTs": lambda expr, conv, _, ia: claripy.SGT(
149
+ "CasCmpGT (signed)": lambda expr, conv, _, ia: claripy.SGT(
150
150
  conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
151
151
  ),
152
152
  "Add": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
@@ -177,10 +177,9 @@ _ail2claripy_op_mapping = {
177
177
  ),
178
178
  "Concat": lambda expr, conv, _, ia: claripy.Concat(*[conv(operand, ins_addr=ia) for operand in expr.operands]),
179
179
  # There are no corresponding claripy operations for the following operations
180
- "DivMod": lambda expr, _, m, *args: _dummy_bvs(expr, m),
181
180
  "CmpF": lambda expr, _, m, *args: _dummy_bvs(expr, m),
182
181
  "Mull": lambda expr, _, m, *args: _dummy_bvs(expr, m),
183
- "Mulls": lambda expr, _, m, *args: _dummy_bvs(expr, m),
182
+ "Mull (signed)": lambda expr, _, m, *args: _dummy_bvs(expr, m),
184
183
  "Reinterpret": lambda expr, _, m, *args: _dummy_bvs(expr, m),
185
184
  "Rol": lambda expr, _, m, *args: _dummy_bvs(expr, m),
186
185
  "Ror": lambda expr, _, m, *args: _dummy_bvs(expr, m),
@@ -190,7 +189,10 @@ _ail2claripy_op_mapping = {
190
189
  "SBorrow": lambda expr, _, m, *args: _dummy_bvs(expr, m),
191
190
  "ExpCmpNE": lambda expr, _, m, *args: _dummy_bools(expr, m),
192
191
  "CmpORD": lambda expr, _, m, *args: _dummy_bvs(expr, m), # in case CmpORDRewriter fails
192
+ "CmpEQV": lambda expr, _, m, *args: _dummy_bvs(expr, m),
193
193
  "GetMSBs": lambda expr, _, m, *args: _dummy_bvs(expr, m),
194
+ "ShlNV": lambda expr, _, m, *args: _dummy_bvs(expr, m),
195
+ "ShrNV": lambda expr, _, m, *args: _dummy_bvs(expr, m),
194
196
  "InterleaveLOV": lambda expr, _, m, *args: _dummy_bvs(expr, m),
195
197
  "InterleaveHIV": lambda expr, _, m, *args: _dummy_bvs(expr, m),
196
198
  # catch-all
@@ -37,7 +37,7 @@ class GraphRewritingAnalysis(ForwardAnalysis[None, NodeType, object, object]):
37
37
  )
38
38
  self._graph = ail_graph
39
39
  self._vvar_to_vvar = vvar_to_vvar
40
- self._engine_ail = SimEngineDephiRewriting(self.project.arch, self._vvar_to_vvar)
40
+ self._engine_ail = SimEngineDephiRewriting(self.project, self._vvar_to_vvar)
41
41
 
42
42
  self._visited_blocks: set[Any] = set()
43
43
  self.out_blocks = {}