angr 9.2.138__py3-none-macosx_11_0_arm64.whl → 9.2.139__py3-none-macosx_11_0_arm64.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 (59) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/fact_collector.py +59 -12
  3. angr/analyses/calling_convention/utils.py +2 -2
  4. angr/analyses/cfg/cfg_fast.py +12 -4
  5. angr/analyses/decompiler/ail_simplifier.py +14 -3
  6. angr/analyses/decompiler/block_simplifier.py +0 -2
  7. angr/analyses/decompiler/callsite_maker.py +80 -14
  8. angr/analyses/decompiler/clinic.py +31 -37
  9. angr/analyses/decompiler/condition_processor.py +2 -2
  10. angr/analyses/decompiler/decompiler.py +2 -0
  11. angr/analyses/decompiler/dephication/rewriting_engine.py +16 -7
  12. angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
  13. angr/analyses/decompiler/optimization_passes/condition_constprop.py +149 -0
  14. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +12 -3
  15. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +1 -1
  16. angr/analyses/decompiler/optimization_passes/optimization_pass.py +5 -2
  17. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +15 -7
  18. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +7 -10
  19. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +12 -1
  20. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +61 -25
  21. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +50 -1
  22. angr/analyses/decompiler/presets/fast.py +2 -0
  23. angr/analyses/decompiler/presets/full.py +2 -0
  24. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -0
  25. angr/analyses/decompiler/ssailification/rewriting_engine.py +20 -2
  26. angr/analyses/decompiler/ssailification/traversal_engine.py +4 -3
  27. angr/analyses/decompiler/structured_codegen/c.py +10 -3
  28. angr/analyses/decompiler/structuring/dream.py +7 -2
  29. angr/analyses/decompiler/structuring/phoenix.py +101 -49
  30. angr/analyses/decompiler/structuring/structurer_base.py +85 -36
  31. angr/analyses/decompiler/structuring/structurer_nodes.py +3 -1
  32. angr/analyses/deobfuscator/api_obf_finder.py +6 -1
  33. angr/analyses/deobfuscator/api_obf_type2_finder.py +158 -0
  34. angr/analyses/s_propagator.py +127 -50
  35. angr/analyses/s_reaching_definitions/s_rda_view.py +2 -2
  36. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -1
  37. angr/analyses/variable_recovery/engine_ail.py +1 -1
  38. angr/analyses/variable_recovery/engine_base.py +55 -62
  39. angr/analyses/variable_recovery/engine_vex.py +1 -1
  40. angr/analyses/variable_recovery/irsb_scanner.py +2 -2
  41. angr/calling_conventions.py +66 -9
  42. angr/engines/engine.py +2 -18
  43. angr/engines/light/engine.py +3 -8
  44. angr/engines/pcode/emulate.py +2 -2
  45. angr/engines/pcode/lifter.py +2 -2
  46. angr/engines/successors.py +1 -8
  47. angr/engines/vex/lifter.py +2 -2
  48. angr/engines/vex/light/light.py +2 -2
  49. angr/knowledge_plugins/cfg/cfg_model.py +3 -2
  50. angr/knowledge_plugins/labels.py +2 -2
  51. angr/knowledge_plugins/obfuscations.py +1 -0
  52. angr/knowledge_plugins/xrefs/xref_manager.py +4 -0
  53. angr/lib/angr_native.dylib +0 -0
  54. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/METADATA +6 -6
  55. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/RECORD +59 -57
  56. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/LICENSE +0 -0
  57. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/WHEEL +0 -0
  58. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/entry_points.txt +0 -0
  59. {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/top_level.txt +0 -0
@@ -16,7 +16,7 @@ from ailment.expression import (
16
16
  Convert,
17
17
  Expression,
18
18
  )
19
- from ailment.statement import Assignment, Store, Return, Jump
19
+ from ailment.statement import Assignment, Store, Return, Jump, ConditionalJump
20
20
 
21
21
  from angr.knowledge_plugins.functions import Function
22
22
  from angr.code_location import CodeLocation, ExternalCodeLocation
@@ -45,6 +45,8 @@ class SPropagatorModel:
45
45
 
46
46
  def __init__(self):
47
47
  self.replacements: Mapping[CodeLocation, Mapping[Expression, Expression]] = {}
48
+ # store vvars that are definitely dead (but usually not removed by default because they are stack variables)
49
+ self.dead_vvar_ids: set[int] = set()
48
50
 
49
51
 
50
52
  class SPropagatorAnalysis(Analysis):
@@ -90,6 +92,7 @@ class SPropagatorAnalysis(Analysis):
90
92
  bp_as_gpr = the_func.info.get("bp_as_gpr", False)
91
93
  self._bp_as_gpr = bp_as_gpr
92
94
 
95
+ # output
93
96
  self.model = SPropagatorModel()
94
97
 
95
98
  self._analyze()
@@ -98,6 +101,10 @@ class SPropagatorAnalysis(Analysis):
98
101
  def replacements(self):
99
102
  return self.model.replacements
100
103
 
104
+ @property
105
+ def dead_vvar_ids(self):
106
+ return self.model.dead_vvar_ids
107
+
101
108
  def _analyze(self):
102
109
  blocks: dict[tuple[int, int | None], Block]
103
110
  match self.mode:
@@ -132,7 +139,7 @@ class SPropagatorAnalysis(Analysis):
132
139
 
133
140
  replacements = defaultdict(dict)
134
141
 
135
- # find constant assignments
142
+ # find constant and other propagatable assignments
136
143
  vvarid_to_vvar = {}
137
144
  const_vvars: dict[int, Const] = {}
138
145
  for vvar, defloc in vvar_deflocs.items():
@@ -140,7 +147,6 @@ class SPropagatorAnalysis(Analysis):
140
147
  continue
141
148
 
142
149
  vvarid_to_vvar[vvar.varid] = vvar
143
- defloc = vvar_deflocs[vvar]
144
150
  if isinstance(defloc, ExternalCodeLocation):
145
151
  continue
146
152
 
@@ -178,8 +184,27 @@ class SPropagatorAnalysis(Analysis):
178
184
  for vvar_at_use, useloc in vvar_uselocs[vvar.varid]:
179
185
  replacements[useloc][vvar_at_use] = const_value
180
186
 
181
- if self.mode == "function" and vvar.varid in vvar_uselocs:
182
- if len(vvar_uselocs[vvar.varid]) <= 2 and isinstance(stmt, Assignment) and isinstance(stmt.src, Load):
187
+ # function mode only
188
+ if self.mode == "function":
189
+ for vvar, defloc in vvar_deflocs.items():
190
+ if vvar.varid not in vvar_uselocs:
191
+ continue
192
+ if vvar.varid in const_vvars:
193
+ continue
194
+ if isinstance(defloc, ExternalCodeLocation):
195
+ continue
196
+
197
+ assert defloc.block_addr is not None
198
+ assert defloc.stmt_idx is not None
199
+
200
+ block = blocks[(defloc.block_addr, defloc.block_idx)]
201
+ stmt = block.statements[defloc.stmt_idx]
202
+ if (
203
+ (vvar.was_reg or vvar.was_parameter)
204
+ and len(vvar_uselocs[vvar.varid]) <= 2
205
+ and isinstance(stmt, Assignment)
206
+ and isinstance(stmt.src, Load)
207
+ ):
183
208
  # do we want to propagate this Load expression if it's used for less than twice?
184
209
  # it's often seen in the following pattern, where propagation will be beneficial:
185
210
  # v0 = Load(...)
@@ -197,63 +222,80 @@ class SPropagatorAnalysis(Analysis):
197
222
  replacements[vvar_useloc][vvar_used] = stmt.src
198
223
  continue
199
224
 
200
- if len(vvar_uselocs[vvar.varid]) == 1:
201
- vvar_used, vvar_useloc = next(iter(vvar_uselocs[vvar.varid]))
202
- if is_const_vvar_load_assignment(stmt) and not self.has_store_stmt_in_between(
203
- blocks, defloc, vvar_useloc
204
- ):
205
- # we can propagate this load because there is no store between its def and use
206
- replacements[vvar_useloc][vvar_used] = stmt.src
225
+ if (
226
+ (vvar.was_reg or vvar.was_stack)
227
+ and len(vvar_uselocs[vvar.varid]) == 2
228
+ and not is_phi_assignment(stmt)
229
+ ):
230
+ # a special case: in a typical switch-case construct, a variable may be used once for comparison
231
+ # for the default case and then used again for constructing the jump target. we can propagate this
232
+ # variable for such cases.
233
+ uselocs = {loc for _, loc in vvar_uselocs[vvar.varid]}
234
+ if self.is_vvar_used_for_addr_loading_switch_case(uselocs, blocks):
235
+ for vvar_used, vvar_useloc in vvar_uselocs[vvar.varid]:
236
+ replacements[vvar_useloc][vvar_used] = stmt.src
237
+ # mark the vvar as dead and should be removed
238
+ self.model.dead_vvar_ids.add(vvar.varid)
207
239
  continue
208
240
 
209
- if is_const_and_vvar_assignment(stmt):
210
- # if the useloc is a phi assignment statement, ensure that stmt.src is the same as the phi
211
- # variable
212
- assert vvar_useloc.block_addr is not None
213
- assert vvar_useloc.stmt_idx is not None
214
- useloc_stmt = blocks[(vvar_useloc.block_addr, vvar_useloc.block_idx)].statements[
215
- vvar_useloc.stmt_idx
216
- ]
217
- if is_phi_assignment(useloc_stmt):
218
- if (
219
- isinstance(stmt.src, VirtualVariable)
220
- and stmt.src.oident == useloc_stmt.dst.oident
221
- and stmt.src.category == useloc_stmt.dst.category
222
- ):
223
- replacements[vvar_useloc][vvar_used] = stmt.src
224
- else:
241
+ if vvar.was_reg or vvar.was_parameter:
242
+ if len(vvar_uselocs[vvar.varid]) == 1:
243
+ vvar_used, vvar_useloc = next(iter(vvar_uselocs[vvar.varid]))
244
+ if is_const_vvar_load_assignment(stmt) and not self.has_store_stmt_in_between(
245
+ blocks, defloc, vvar_useloc
246
+ ):
247
+ # we can propagate this load because there is no store between its def and use
225
248
  replacements[vvar_useloc][vvar_used] = stmt.src
226
- continue
249
+ continue
227
250
 
228
- else:
229
- non_exitsite_uselocs = [
230
- loc
231
- for _, loc in vvar_uselocs[vvar.varid]
232
- if (loc.block_addr, loc.block_idx, loc.stmt_idx) not in (retsites | jumpsites)
233
- ]
234
- if is_const_and_vvar_assignment(stmt):
235
- if len(non_exitsite_uselocs) == 1:
236
- # this vvar is used once if we exclude its uses at ret sites or jump sites. we can
237
- # propagate it
238
- for vvar_used, vvar_useloc in vvar_uselocs[vvar.varid]:
251
+ if is_const_and_vvar_assignment(stmt):
252
+ # if the useloc is a phi assignment statement, ensure that stmt.src is the same as the phi
253
+ # variable
254
+ assert vvar_useloc.block_addr is not None
255
+ assert vvar_useloc.stmt_idx is not None
256
+ useloc_stmt = blocks[(vvar_useloc.block_addr, vvar_useloc.block_idx)].statements[
257
+ vvar_useloc.stmt_idx
258
+ ]
259
+ if is_phi_assignment(useloc_stmt):
260
+ if (
261
+ isinstance(stmt.src, VirtualVariable)
262
+ and stmt.src.oident == useloc_stmt.dst.oident
263
+ and stmt.src.category == useloc_stmt.dst.category
264
+ ):
265
+ replacements[vvar_useloc][vvar_used] = stmt.src
266
+ else:
239
267
  replacements[vvar_useloc][vvar_used] = stmt.src
240
268
  continue
241
269
 
242
- if len(set(non_exitsite_uselocs)) == 1 and not has_ite_expr(stmt.src):
243
- useloc = non_exitsite_uselocs[0]
244
- assert useloc.block_addr is not None
245
- assert useloc.stmt_idx is not None
246
- useloc_stmt = blocks[(useloc.block_addr, useloc.block_idx)].statements[useloc.stmt_idx]
247
- if stmt.src.depth <= 3 and not has_ite_stmt(useloc_stmt):
248
- # remove duplicate use locs (e.g., if the variable is used multiple times by the same
249
- # statement) - but ensure stmt is simple enough
270
+ else:
271
+ non_exitsite_uselocs = [
272
+ loc
273
+ for _, loc in vvar_uselocs[vvar.varid]
274
+ if (loc.block_addr, loc.block_idx, loc.stmt_idx) not in (retsites | jumpsites)
275
+ ]
276
+ if is_const_and_vvar_assignment(stmt):
277
+ if len(non_exitsite_uselocs) == 1:
278
+ # this vvar is used once if we exclude its uses at ret sites or jump sites. we can
279
+ # propagate it
250
280
  for vvar_used, vvar_useloc in vvar_uselocs[vvar.varid]:
251
281
  replacements[vvar_useloc][vvar_used] = stmt.src
252
282
  continue
253
283
 
284
+ if len(set(non_exitsite_uselocs)) == 1 and not has_ite_expr(stmt.src):
285
+ useloc = non_exitsite_uselocs[0]
286
+ assert useloc.block_addr is not None
287
+ assert useloc.stmt_idx is not None
288
+ useloc_stmt = blocks[(useloc.block_addr, useloc.block_idx)].statements[useloc.stmt_idx]
289
+ if stmt.src.depth <= 3 and not has_ite_stmt(useloc_stmt):
290
+ # remove duplicate use locs (e.g., if the variable is used multiple times by the
291
+ # same statement) - but ensure stmt is simple enough
292
+ for vvar_used, vvar_useloc in vvar_uselocs[vvar.varid]:
293
+ replacements[vvar_useloc][vvar_used] = stmt.src
294
+ continue
295
+
254
296
  # special logic for global variables: if it's used once or multiple times, and the variable is never
255
297
  # updated before it's used, we will propagate the load
256
- if isinstance(stmt, Assignment):
298
+ if (vvar.was_reg or vvar.was_parameter) and isinstance(stmt, Assignment):
257
299
  stmt_src = stmt.src
258
300
  # unpack conversions
259
301
  while isinstance(stmt_src, Convert):
@@ -442,7 +484,7 @@ class SPropagatorAnalysis(Analysis):
442
484
  if block is def_block:
443
485
  starting_stmt_idx = defloc.stmt_idx + 1
444
486
  if block is use_block:
445
- ending_stmt_idx = useloc.stmt_idx + 1
487
+ ending_stmt_idx = useloc.stmt_idx
446
488
 
447
489
  for i in range(starting_stmt_idx, ending_stmt_idx):
448
490
  if isinstance(block.statements[i], Store):
@@ -458,5 +500,40 @@ class SPropagatorAnalysis(Analysis):
458
500
 
459
501
  return False
460
502
 
503
+ @staticmethod
504
+ def is_vvar_used_for_addr_loading_switch_case(uselocs: set[CodeLocation], blocks) -> bool:
505
+ """
506
+ Check if a virtual variable is used for loading an address in a switch-case construct.
507
+
508
+ :param uselocs: The use locations of the virtual variable.
509
+ :param blocks: All blocks of the current function.
510
+ :return: True if the virtual variable is used for loading an address in a switch-case construct, False
511
+ otherwise.
512
+ """
513
+
514
+ if len(uselocs) != 2:
515
+ return False
516
+
517
+ useloc_0, useloc_1 = list(uselocs)
518
+ block_0 = blocks[(useloc_0.block_addr, useloc_0.block_idx)]
519
+ stmt_0 = block_0.statements[useloc_0.stmt_idx]
520
+ block_1 = blocks[(useloc_1.block_addr, useloc_1.block_idx)]
521
+ stmt_1 = block_1.statements[useloc_1.stmt_idx]
522
+
523
+ if isinstance(stmt_0, Jump):
524
+ stmt_0, stmt_1 = stmt_1, stmt_0
525
+ block_0, block_1 = block_1, block_0
526
+ if not isinstance(stmt_0, ConditionalJump) or not isinstance(stmt_1, Jump):
527
+ return False
528
+
529
+ # check if stmt_0 jumps to block_1
530
+ if not isinstance(stmt_0.true_target, Const) or not isinstance(stmt_0.false_target, Const):
531
+ return False
532
+ stmt_0_targets = {
533
+ (stmt_0.true_target.value, stmt_0.true_target_idx),
534
+ (stmt_0.false_target.value, stmt_0.false_target_idx),
535
+ }
536
+ return (block_1.addr, block_1.idx) in stmt_0_targets
537
+
461
538
 
462
539
  register_analysis(SPropagatorAnalysis, "SPropagator")
@@ -79,8 +79,8 @@ class StackVVarPredicate:
79
79
  isinstance(stmt, Assignment)
80
80
  and isinstance(stmt.dst, VirtualVariable)
81
81
  and stmt.dst.was_stack
82
- and stmt.dst.stack_offset == self.stack_offset
83
- and stmt.dst.size == self.size
82
+ and stmt.dst.stack_offset <= self.stack_offset < stmt.dst.stack_offset + stmt.dst.size
83
+ and stmt.dst.stack_offset <= self.stack_offset + self.size <= stmt.dst.stack_offset + stmt.dst.size
84
84
  ):
85
85
  self.vvars.add(stmt.dst)
86
86
  return True
@@ -143,7 +143,9 @@ class SReachingDefinitionsAnalysis(Analysis):
143
143
  cc = cc_cls(self.project.arch)
144
144
 
145
145
  codeloc = CodeLocation(block_addr, stmt_idx, block_idx=block_idx, ins_addr=stmt.ins_addr)
146
- arg_locs = cc.ARG_REGS
146
+ arg_locs = list(cc.ARG_REGS)
147
+ if cc.FP_ARG_REGS:
148
+ arg_locs += [r_name for r_name in cc.FP_ARG_REGS if r_name not in arg_locs]
147
149
 
148
150
  for arg_reg_name in arg_locs:
149
151
  reg_offset = self.project.arch.registers[arg_reg_name][0]
@@ -84,7 +84,7 @@ class SimEngineVRAIL(
84
84
  if vvar is not None:
85
85
  r = self._read_from_vvar(vvar, expr=stmt.src, vvar_id=self._mapped_vvarid(vvar.varid))
86
86
  if r.variable is not None:
87
- pv = self.variable_manager[self.func_addr]._phi_variables
87
+ pv = self.state.variable_manager[self.func_addr]._phi_variables
88
88
  if variable not in pv:
89
89
  pv[variable] = set()
90
90
  pv[variable].add(r.variable)