angr 9.2.136__py3-none-manylinux2014_aarch64.whl → 9.2.138__py3-none-manylinux2014_aarch64.whl

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

Potentially problematic release.


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

Files changed (64) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +2 -1
  3. angr/analyses/calling_convention/fact_collector.py +10 -2
  4. angr/analyses/cfg/cfg_base.py +3 -33
  5. angr/analyses/cfg/cfg_emulated.py +0 -103
  6. angr/analyses/cfg/cfg_fast.py +31 -12
  7. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +15 -0
  8. angr/analyses/class_identifier.py +1 -2
  9. angr/analyses/complete_calling_conventions.py +6 -3
  10. angr/analyses/decompiler/ail_simplifier.py +12 -1
  11. angr/analyses/decompiler/block_simplifier.py +2 -2
  12. angr/analyses/decompiler/ccall_rewriters/__init__.py +2 -0
  13. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +1 -1
  14. angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +69 -0
  15. angr/analyses/decompiler/clinic.py +77 -65
  16. angr/analyses/decompiler/condition_processor.py +2 -0
  17. angr/analyses/decompiler/decompilation_options.py +10 -0
  18. angr/analyses/decompiler/decompiler.py +1 -0
  19. angr/analyses/decompiler/dephication/dephication_base.py +2 -0
  20. angr/analyses/decompiler/dephication/rewriting_engine.py +8 -6
  21. angr/analyses/decompiler/dephication/seqnode_dephication.py +10 -1
  22. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +2 -2
  23. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +1 -2
  24. angr/analyses/decompiler/peephole_optimizations/remove_redundant_nots.py +21 -3
  25. angr/analyses/decompiler/sequence_walker.py +6 -2
  26. angr/analyses/decompiler/ssailification/rewriting.py +11 -1
  27. angr/analyses/decompiler/ssailification/rewriting_engine.py +56 -19
  28. angr/analyses/decompiler/ssailification/ssailification.py +13 -3
  29. angr/analyses/decompiler/ssailification/traversal.py +28 -2
  30. angr/analyses/decompiler/ssailification/traversal_state.py +6 -1
  31. angr/analyses/decompiler/structured_codegen/c.py +44 -21
  32. angr/analyses/decompiler/structuring/phoenix.py +117 -14
  33. angr/analyses/decompiler/utils.py +113 -8
  34. angr/analyses/reaching_definitions/function_handler.py +1 -1
  35. angr/analyses/s_liveness.py +5 -1
  36. angr/analyses/s_propagator.py +127 -28
  37. angr/analyses/s_reaching_definitions/s_rda_model.py +2 -1
  38. angr/analyses/s_reaching_definitions/s_rda_view.py +20 -1
  39. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +11 -1
  40. angr/analyses/stack_pointer_tracker.py +26 -16
  41. angr/analyses/variable_recovery/engine_ail.py +19 -7
  42. angr/analyses/variable_recovery/engine_base.py +16 -14
  43. angr/analyses/variable_recovery/engine_vex.py +2 -2
  44. angr/analyses/variable_recovery/variable_recovery_fast.py +22 -1
  45. angr/block.py +59 -20
  46. angr/engines/pcode/emulate.py +1 -1
  47. angr/engines/pcode/lifter.py +31 -18
  48. angr/engines/soot/expressions/__init__.py +2 -4
  49. angr/engines/soot/statements/__init__.py +1 -2
  50. angr/engines/soot/values/__init__.py +1 -2
  51. angr/engines/successors.py +11 -6
  52. angr/engines/vex/lifter.py +9 -6
  53. angr/flirt/build_sig.py +8 -15
  54. angr/knowledge_plugins/functions/function.py +0 -6
  55. angr/knowledge_plugins/functions/soot_function.py +5 -8
  56. angr/knowledge_plugins/variables/variable_manager.py +16 -10
  57. angr/procedures/glibc/__libc_start_main.py +10 -3
  58. angr/utils/ssa/__init__.py +14 -1
  59. {angr-9.2.136.dist-info → angr-9.2.138.dist-info}/METADATA +7 -7
  60. {angr-9.2.136.dist-info → angr-9.2.138.dist-info}/RECORD +64 -63
  61. {angr-9.2.136.dist-info → angr-9.2.138.dist-info}/WHEEL +1 -1
  62. {angr-9.2.136.dist-info → angr-9.2.138.dist-info}/LICENSE +0 -0
  63. {angr-9.2.136.dist-info → angr-9.2.138.dist-info}/entry_points.txt +0 -0
  64. {angr-9.2.136.dist-info → angr-9.2.138.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.136"
5
+ __version__ = "9.2.138"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
@@ -436,7 +436,8 @@ class CallingConventionAnalysis(Analysis):
436
436
  if caller.is_simprocedure or caller.is_alignment:
437
437
  # do not analyze SimProcedures or alignment stubs
438
438
  continue
439
- call_sites_by_function[caller].append((src.addr, src.instruction_addrs[-1]))
439
+ if src.instruction_addrs:
440
+ call_sites_by_function[caller].append((src.addr, src.instruction_addrs[-1]))
440
441
 
441
442
  call_sites_by_function_list = sorted(call_sites_by_function.items(), key=lambda x: x[0].addr)[
442
443
  :max_analyzing_callsites
@@ -276,13 +276,21 @@ class FactCollector(Analysis):
276
276
  call_succ, ret_succ = None, None
277
277
  for _, succ, data in func_graph.out_edges(node, data=True):
278
278
  edge_type = data.get("type")
279
+ outside = data.get("outside", False)
279
280
  if succ not in traversed and depth + 1 <= self._max_depth:
280
281
  if edge_type == "fake_return":
281
282
  ret_succ = succ
282
- elif edge_type == "transition":
283
+ elif edge_type == "transition" and not outside:
283
284
  successor_added = True
284
285
  queue.append((depth + 1, state.copy(), succ, None))
285
- elif edge_type == "call":
286
+ elif edge_type == "call" or (edge_type == "transition" and outside):
287
+ # a call or a tail-call
288
+ if not isinstance(succ, Function):
289
+ if self.kb.functions.contains_addr(succ.addr):
290
+ succ = self.kb.functions.get_by_addr(succ.addr)
291
+ else:
292
+ # not sure who we are calling
293
+ continue
286
294
  call_succ = succ
287
295
  if call_succ is not None:
288
296
  successor_added = True
@@ -2520,18 +2520,17 @@ class CFGBase(Analysis):
2520
2520
  #
2521
2521
 
2522
2522
  @staticmethod
2523
- def _is_noop_block(arch: archinfo.Arch, block):
2523
+ def _is_noop_block(arch: archinfo.Arch, block) -> bool:
2524
2524
  """
2525
2525
  Check if the block is a no-op block by checking VEX statements.
2526
2526
 
2527
2527
  :param arch: An architecture descriptor.
2528
2528
  :param block: The VEX block instance.
2529
2529
  :return: True if the entire block is a single-byte or multi-byte nop instruction, False otherwise.
2530
- :rtype: bool
2531
2530
  """
2532
2531
 
2533
2532
  if arch.name == "X86" or arch.name == "AMD64":
2534
- if set(block.bytes) == {"b\x90"}:
2533
+ if set(block.bytes) == {0x90}:
2535
2534
  return True
2536
2535
  elif arch.name == "MIPS32":
2537
2536
  if arch.memory_endness == "Iend_BE":
@@ -2577,36 +2576,7 @@ class CFGBase(Analysis):
2577
2576
  if THUMB_NOOPS.issuperset(insns):
2578
2577
  return True
2579
2578
 
2580
- # Fallback
2581
- # the block is a noop block if it only has IMark statements **and** it jumps to its immediate successor. VEX
2582
- # will generate such blocks when opt_level==1 and cross_insn_opt is True
2583
- fallthrough_addr = block.addr + block.size
2584
- next_ = block.vex.next
2585
- if (
2586
- isinstance(next_, pyvex.IRExpr.Const)
2587
- and next_.con.value == fallthrough_addr
2588
- and all((type(stmt) is pyvex.IRStmt.IMark) for stmt in block.vex.statements)
2589
- ):
2590
- return True
2591
-
2592
- # the block is a noop block if it only has IMark statements and IP-setting statements that set the IP to the
2593
- # next location. VEX will generate such blocks when opt_level==1 and cross_insn_opt is False
2594
- ip_offset = arch.ip_offset
2595
- if (
2596
- all(
2597
- (type(stmt) is pyvex.IRStmt.IMark or (type(stmt) is pyvex.IRStmt.Put and stmt.offset == ip_offset))
2598
- for stmt in block.vex.statements
2599
- )
2600
- and block.vex.statements
2601
- ):
2602
- last_stmt = block.vex.statements[-1]
2603
- if (
2604
- isinstance(last_stmt, pyvex.IRStmt.IMark)
2605
- and isinstance(next_, pyvex.IRExpr.Const)
2606
- and next_.con.value == fallthrough_addr
2607
- ):
2608
- return True
2609
- return False
2579
+ return block.vex_nostmt.is_noop_block
2610
2580
 
2611
2581
  @staticmethod
2612
2582
  def _is_noop_insn(insn):
@@ -3208,109 +3208,6 @@ class CFGEmulated(ForwardAnalysis, CFGBase): # pylint: disable=abstract-method
3208
3208
  # Update loop backedges and graph
3209
3209
  self._loop_back_edges = list(itertools.chain.from_iterable(loop.continue_edges for loop in loop_finder.loops))
3210
3210
 
3211
- # Private methods - function/procedure/subroutine analysis
3212
- # Including calling convention, function arguments, etc.
3213
-
3214
- def _refine_function_arguments(self, func, callsites):
3215
- """
3216
-
3217
- :param func:
3218
- :param callsites:
3219
- :return:
3220
- """
3221
-
3222
- for i, c in enumerate(callsites):
3223
- # Execute one block ahead of the callsite, and execute one basic block after the callsite
3224
- # In this process, the following tasks are performed:
3225
- # - Record registers/stack variables that are modified
3226
- # - Record the change of the stack pointer
3227
- # - Check if the return value is used immediately
3228
- # We assume that the stack is balanced before and after the call (you can have caller clean-up of course).
3229
- # Any abnormal behaviors will be logged.
3230
- # Hopefully this approach will allow us to have a better understanding of parameters of those function
3231
- # stubs and function proxies.
3232
-
3233
- if c.simprocedure_name is not None:
3234
- # Skip all SimProcedures
3235
- continue
3236
-
3237
- l.debug("Refining %s at 0x%x (%d/%d).", repr(func), c.addr, i, len(callsites))
3238
-
3239
- # Get a basic block ahead of the callsite
3240
- blocks_ahead = [c]
3241
-
3242
- # the block after
3243
- blocks_after = []
3244
- successors = self.get_successors_and_jumpkind(c, excluding_fakeret=False)
3245
- for s, jk in successors:
3246
- if jk == "Ijk_FakeRet":
3247
- blocks_after = [s]
3248
- break
3249
-
3250
- regs_overwritten = set()
3251
- stack_overwritten = set()
3252
- regs_read = set()
3253
- regs_written = set()
3254
-
3255
- try:
3256
- # Execute the predecessor
3257
- path = self.project.factory.path(
3258
- self.project.factory.blank_state(mode="fastpath", addr=blocks_ahead[0].addr)
3259
- )
3260
- path.step()
3261
- all_successors = path.next_run.successors + path.next_run.unsat_successors
3262
- if not all_successors:
3263
- continue
3264
-
3265
- suc = all_successors[0]
3266
- se = suc.solver
3267
- # Examine the path log
3268
- actions = suc.history.recent_actions
3269
- sp = se.eval_one(suc.regs.sp, default=0) + self.project.arch.call_sp_fix
3270
- for ac in actions:
3271
- if ac.type == "reg" and ac.action == "write":
3272
- regs_overwritten.add(ac.offset)
3273
- elif ac.type == "mem" and ac.action == "write":
3274
- addr = se.eval_one(ac.addr.ast, default=0)
3275
- if (self.project.arch.call_pushes_ret and addr >= sp + self.project.arch.bytes) or (
3276
- not self.project.arch.call_pushes_ret and addr >= sp
3277
- ):
3278
- offset = addr - sp
3279
- stack_overwritten.add(offset)
3280
-
3281
- func.prepared_registers.add(tuple(regs_overwritten))
3282
- func.prepared_stack_variables.add(tuple(stack_overwritten))
3283
-
3284
- except (SimError, AngrError):
3285
- pass
3286
-
3287
- try:
3288
- if blocks_after:
3289
- path = self.project.factory.path(
3290
- self.project.factory.blank_state(mode="fastpath", addr=blocks_after[0].addr)
3291
- )
3292
- path.step()
3293
- all_successors = path.next_run.successors + path.next_run.unsat_successors
3294
- if not all_successors:
3295
- continue
3296
-
3297
- suc = all_successors[0]
3298
- actions = suc.history.recent_actions
3299
- for ac in actions:
3300
- if ac.type == "reg" and ac.action == "read" and ac.offset not in regs_written:
3301
- regs_read.add(ac.offset)
3302
- elif ac.type == "reg" and ac.action == "write":
3303
- regs_written.add(ac.offset)
3304
-
3305
- # Filter registers, remove unnecessary registers from the set
3306
- # regs_overwritten = self.project.arch.argument_registers.intersection(regs_overwritten)
3307
- regs_read = self.project.arch.argument_registers.intersection(regs_read)
3308
-
3309
- func.registers_read_afterwards.add(tuple(regs_read))
3310
-
3311
- except (SimError, AngrError):
3312
- pass
3313
-
3314
3211
  # Private methods - dominators and post-dominators
3315
3212
 
3316
3213
  def _immediate_dominators(self, node, target_graph=None, reverse_graph=False):
@@ -1,5 +1,6 @@
1
1
  # pylint:disable=superfluous-parens,too-many-boolean-expressions,line-too-long
2
2
  from __future__ import annotations
3
+ from typing import TYPE_CHECKING
3
4
  import itertools
4
5
  import logging
5
6
  import math
@@ -52,6 +53,10 @@ from .cfg_arch_options import CFGArchOptions
52
53
  from .cfg_base import CFGBase
53
54
  from .indirect_jump_resolvers.jumptable import JumpTableResolver
54
55
 
56
+ if TYPE_CHECKING:
57
+ from angr.block import Block
58
+ from angr.engines.pcode.lifter import IRSB as PcodeIRSB
59
+
55
60
 
56
61
  VEX_IRSB_MAX_SIZE = 400
57
62
 
@@ -825,10 +830,10 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
825
830
  #
826
831
  # Variables used during analysis
827
832
  #
828
- self._pending_jobs = None
829
- self._traced_addresses = None
833
+ self._pending_jobs = None # type:ignore
834
+ self._traced_addresses = None # type:ignore
830
835
  self._function_returns = None
831
- self._function_exits = None
836
+ self._function_exits = None # type:ignore
832
837
  self._gp_value: int | None = None
833
838
  self._ro_region_cdata_cache: list | None = None
834
839
  self._job_ctr = 0
@@ -872,7 +877,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
872
877
  if size is None:
873
878
  size = len(data)
874
879
 
875
- data = bytes(pyvex.ffi.buffer(data, size))
880
+ data = bytes(pyvex.ffi.buffer(data, size)) # type:ignore
876
881
  for x in range(256):
877
882
  p_x = float(data.count(x)) / size
878
883
  if p_x > 0:
@@ -1229,7 +1234,9 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1229
1234
  # umm now it's probably code
1230
1235
  break
1231
1236
 
1232
- instr_alignment = self._initial_state.arch.instruction_alignment
1237
+ instr_alignment = self.project.arch.instruction_alignment
1238
+ if not instr_alignment:
1239
+ instr_alignment = 1
1233
1240
  if start_addr % instr_alignment > 0:
1234
1241
  # occupy those few bytes
1235
1242
  size = instr_alignment - (start_addr % instr_alignment)
@@ -1286,7 +1293,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1286
1293
  nodecode_bytes_ratio = (
1287
1294
  0.0 if self._next_addr is None else self._nodecode_bytes_ratio(self._next_addr, self._nodecode_window_size)
1288
1295
  )
1289
- if nodecode_bytes_ratio >= self._nodecode_threshold:
1296
+ if nodecode_bytes_ratio >= self._nodecode_threshold and self._next_addr is not None:
1290
1297
  next_allowed_addr = self._next_addr + self._nodecode_step
1291
1298
  else:
1292
1299
  next_allowed_addr = 0
@@ -1313,6 +1320,9 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1313
1320
  return job.addr
1314
1321
 
1315
1322
  def _pre_analysis(self):
1323
+ # Create a read-only memory view in loader for faster data loading
1324
+ self.project.loader.gen_ro_memview()
1325
+
1316
1326
  # Call _initialize_cfg() before self.functions is used.
1317
1327
  self._initialize_cfg()
1318
1328
 
@@ -1757,6 +1767,9 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
1757
1767
 
1758
1768
  CFGBase._post_analysis(self)
1759
1769
 
1770
+ # drop the read-only memory view in loader
1771
+ self.project.loader.discard_ro_memview()
1772
+
1760
1773
  # Clean up
1761
1774
  self._traced_addresses = None
1762
1775
  self._lifter_deregister_readonly_regions()
@@ -2090,6 +2103,9 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
2090
2103
  addr_ = addr_.concrete_value
2091
2104
  if not isinstance(addr_, int):
2092
2105
  continue
2106
+ # is it valid?
2107
+ if self.project.loader.find_object_containing(addr_) is None:
2108
+ continue
2093
2109
  entries += self._create_jobs(
2094
2110
  addr_,
2095
2111
  jumpkind,
@@ -2926,7 +2942,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
2926
2942
  if v is not None:
2927
2943
  tmps[stmt.tmp] = v
2928
2944
 
2929
- elif type(stmt.data) in (pyvex.IRExpr.Binop,): # pylint: disable=unidiomatic-typecheck
2945
+ elif type(stmt.data) is pyvex.IRExpr.Binop: # pylint: disable=unidiomatic-typecheck
2930
2946
  # rip-related addressing
2931
2947
  if stmt.data.op in ("Iop_Add32", "Iop_Add64"):
2932
2948
  if all(type(arg) is pyvex.expr.Const for arg in stmt.data.args):
@@ -4412,8 +4428,8 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4412
4428
 
4413
4429
  # Let's try to create the pyvex IRSB directly, since it's much faster
4414
4430
  nodecode = False
4415
- irsb = None
4416
- lifted_block = None
4431
+ irsb: pyvex.IRSB | PcodeIRSB | None = None
4432
+ lifted_block: Block = None # type:ignore
4417
4433
  try:
4418
4434
  lifted_block = self._lift(
4419
4435
  addr,
@@ -4427,10 +4443,13 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4427
4443
  except SimTranslationError:
4428
4444
  nodecode = True
4429
4445
 
4430
- irsb_string: bytes = lifted_block.bytes[: irsb.size] if irsb is not None else lifted_block.bytes
4446
+ irsb_string: bytes = b""
4447
+ lifted_block_bytes = lifted_block.bytes if lifted_block.bytes is not None else b""
4448
+ if lifted_block is not None:
4449
+ irsb_string = lifted_block_bytes[: irsb.size] if irsb is not None else lifted_block_bytes
4431
4450
 
4432
4451
  # special logic during the complete scanning phase
4433
- if cfg_job.job_type == CFGJobType.COMPLETE_SCANNING and is_arm_arch(self.project.arch):
4452
+ if cfg_job.job_type == CFGJobType.COMPLETE_SCANNING and is_arm_arch(self.project.arch) and irsb is not None:
4434
4453
  # it's way too easy to incorrectly disassemble THUMB code contains 0x4f as ARM code svc?? #????
4435
4454
  # if we get a single block that getting decoded to svc?? under ARM mode, we treat it as nodecode
4436
4455
  if (
@@ -4468,7 +4487,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
4468
4487
  except SimTranslationError:
4469
4488
  nodecode = True
4470
4489
 
4471
- irsb_string: bytes = lifted_block.bytes[: irsb.size] if irsb is not None else lifted_block.bytes
4490
+ irsb_string = lifted_block.bytes[: irsb.size] if irsb is not None else lifted_block.bytes
4472
4491
 
4473
4492
  if not (nodecode or irsb.size == 0 or irsb.jumpkind == "Ijk_NoDecode"):
4474
4493
  # it is decodeable
@@ -505,6 +505,21 @@ class JumpTableProcessor(
505
505
  return self._top(expr.result_size(self.tyenv))
506
506
  return v
507
507
 
508
+ @binop_handler
509
+ def _handle_binop_And(self, expr):
510
+ arg0 = self._expr(expr.args[0])
511
+ if (
512
+ isinstance(arg0, claripy.ast.BV)
513
+ and self._is_registeroffset(arg0)
514
+ and isinstance(expr.args[1], pyvex.IRExpr.Const)
515
+ ):
516
+ mask_value = expr.args[1].con.value
517
+ if mask_value in {1, 3, 7, 15, 31, 63, 127, 255}:
518
+ # 1cbbf108f44c8f4babde546d26425ca5340dccf878d306b90eb0fbec2f83ab51:0x40bd1b
519
+ self.state.is_jumptable = True
520
+ return arg0 & mask_value
521
+ return self._top(expr.result_size(self.tyenv))
522
+
508
523
  @binop_handler
509
524
  def _handle_binop_CmpLE(self, expr):
510
525
  return self._handle_Comparison(*expr.args)
@@ -30,8 +30,7 @@ class ClassIdentifier(Analysis):
30
30
  continue
31
31
  col_ind = func.demangled_name.rfind("::")
32
32
  class_name = func.demangled_name[:col_ind]
33
- if class_name.startswith("non-virtual thunk for "):
34
- class_name = class_name[len("non-virtual thunk for ") :]
33
+ class_name = class_name.removeprefix("non-virtual thunk for ")
35
34
  if col_ind != -1:
36
35
  if class_name not in self.classes:
37
36
  ctor = False
@@ -17,7 +17,7 @@ from angr.utils.graph import GraphUtils
17
17
  from angr.simos import SimWindows
18
18
  from angr.utils.mp import mp_context, Initializer
19
19
  from angr.knowledge_plugins.cfg import CFGModel
20
- from . import Analysis, register_analysis, VariableRecoveryFast, CallingConventionAnalysis, FactCollector
20
+ from . import Analysis, register_analysis, VariableRecoveryFast, CallingConventionAnalysis, FactCollector, CFGFast
21
21
 
22
22
  if TYPE_CHECKING:
23
23
  from angr.calling_conventions import SimCC
@@ -56,7 +56,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
56
56
  recover_variables=False,
57
57
  low_priority=False,
58
58
  force=False,
59
- cfg: CFGModel | None = None,
59
+ cfg: CFGFast | CFGModel | None = None,
60
60
  analyze_callsites: bool = False,
61
61
  skip_signature_matched_functions: bool = False,
62
62
  max_function_blocks: int | None = None,
@@ -89,7 +89,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
89
89
  self._recover_variables = recover_variables
90
90
  self._low_priority = low_priority
91
91
  self._force = force
92
- self._cfg = cfg
92
+ self._cfg: CFGModel | None = cfg.model if isinstance(cfg, CFGFast) else cfg
93
93
  self._analyze_callsites = analyze_callsites
94
94
  self._skip_signature_matched_functions = skip_signature_matched_functions
95
95
  self._max_function_blocks = max_function_blocks
@@ -208,6 +208,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
208
208
  self._update_progress(percentage, text=f"{idx + 1}/{total_funcs} - {func.demangled_name}")
209
209
  if self._low_priority:
210
210
  self._release_gil(idx + 1, 10, 0.000001)
211
+ self._finish_progress()
211
212
 
212
213
  else:
213
214
  self._remaining_funcs.value = len(self._func_addrs)
@@ -298,6 +299,8 @@ class CompleteCallingConventionsAnalysis(Analysis):
298
299
  for proc in procs:
299
300
  proc.join()
300
301
 
302
+ self._finish_progress()
303
+
301
304
  def _worker_routine(self, worker_id: int, initializer: Initializer):
302
305
  initializer.initialize()
303
306
  idx = 0
@@ -207,9 +207,11 @@ class AILSimplifier(Analysis):
207
207
  # Computing reaching definitions or return the cached one
208
208
  if self._reaching_definitions is not None:
209
209
  return self._reaching_definitions
210
+ func_args = {vvar for vvar, _ in self._arg_vvars.values()} if self._arg_vvars else set()
210
211
  rd = self.project.analyses.SReachingDefinitions(
211
212
  subject=self.func,
212
213
  func_graph=self.func_graph,
214
+ func_args=func_args,
213
215
  # use_callee_saved_regs_at_return=self._use_callee_saved_regs_at_return,
214
216
  # track_tmps=True,
215
217
  ).model
@@ -220,9 +222,11 @@ class AILSimplifier(Analysis):
220
222
  # Propagate expressions or return the existing result
221
223
  if self._propagator is not None:
222
224
  return self._propagator
223
- prop = self.project.analyses[SPropagatorAnalysis].prep()(
225
+ func_args = {vvar for vvar, _ in self._arg_vvars.values()} if self._arg_vvars else set()
226
+ prop = self.project.analyses[SPropagatorAnalysis].prep(fail_fast=self._fail_fast)(
224
227
  subject=self.func,
225
228
  func_graph=self.func_graph,
229
+ func_args=func_args,
226
230
  # gp=self._gp,
227
231
  only_consts=self._only_consts,
228
232
  )
@@ -898,6 +902,13 @@ class AILSimplifier(Analysis):
898
902
 
899
903
  to_replace_def = the_def
900
904
 
905
+ # check: the definition of expression being replaced should not be a phi variable
906
+ if (
907
+ isinstance(to_replace_def.atom, atoms.VirtualVariable)
908
+ and to_replace_def.atom.varid in rd.phi_vvar_ids
909
+ ):
910
+ continue
911
+
901
912
  # find all uses of this definition
902
913
  # we make a copy of the set since we may touch the set (uses) when replacing expressions
903
914
  all_uses: set[tuple[CodeLocation, Any]] = set(rd.get_vvar_uses_with_expr(to_replace_def.atom))
@@ -141,7 +141,7 @@ class BlockSimplifier(Analysis):
141
141
 
142
142
  def _compute_propagation(self, block) -> SPropagatorAnalysis:
143
143
  if self._propagator is None:
144
- self._propagator = self.project.analyses[SPropagatorAnalysis].prep()(
144
+ self._propagator = self.project.analyses[SPropagatorAnalysis].prep(fail_fast=self._fail_fast)(
145
145
  subject=block,
146
146
  func_addr=self.func_addr,
147
147
  stack_pointer_tracker=self._stack_pointer_tracker,
@@ -152,7 +152,7 @@ class BlockSimplifier(Analysis):
152
152
  if self._reaching_definitions is None:
153
153
  self._reaching_definitions = (
154
154
  self.project.analyses[SReachingDefinitionsAnalysis]
155
- .prep()(
155
+ .prep(fail_fast=self._fail_fast)(
156
156
  subject=block,
157
157
  track_tmps=True,
158
158
  func_addr=self.func_addr,
@@ -1,7 +1,9 @@
1
1
  from __future__ import annotations
2
2
  from .amd64_ccalls import AMD64CCallRewriter
3
+ from .x86_ccalls import X86CCallRewriter
3
4
 
4
5
 
5
6
  CCALL_REWRITERS = {
7
+ "X86": X86CCallRewriter,
6
8
  "AMD64": AMD64CCallRewriter,
7
9
  }
@@ -14,7 +14,7 @@ AMD64_CondBitOffsets = data["AMD64"]["CondBitOffsets"]
14
14
 
15
15
  class AMD64CCallRewriter(CCallRewriterBase):
16
16
  """
17
- Implements ccall rewriter for AMD64.
17
+ Implements VEX ccall rewriter for AMD64.
18
18
  """
19
19
 
20
20
  __slots__ = ()
@@ -0,0 +1,69 @@
1
+ from __future__ import annotations
2
+
3
+ from ailment import Expr
4
+
5
+ from angr.engines.vex.claripy.ccall import data
6
+ from .rewriter_base import CCallRewriterBase
7
+
8
+
9
+ X86_CondTypes = data["X86"]["CondTypes"]
10
+ X86_OpTypes = data["X86"]["OpTypes"]
11
+ X86_CondBitMasks = data["X86"]["CondBitMasks"]
12
+ X86_CondBitOffsets = data["X86"]["CondBitOffsets"]
13
+
14
+
15
+ class X86CCallRewriter(CCallRewriterBase):
16
+ """
17
+ Implements VEX ccall rewriter for X86.
18
+ """
19
+
20
+ __slots__ = ()
21
+
22
+ def _rewrite(self, ccall: Expr.VEXCCallExpression) -> Expr.Expression | None:
23
+ if ccall.callee == "x86g_calculate_condition":
24
+ cond = ccall.operands[0]
25
+ op = ccall.operands[1]
26
+ dep_1 = ccall.operands[2]
27
+ dep_2 = ccall.operands[3]
28
+ if isinstance(cond, Expr.Const) and isinstance(op, Expr.Const):
29
+ cond_v = cond.value
30
+ op_v = op.value
31
+ if cond_v == X86_CondTypes["CondLE"]: # noqa: SIM102
32
+ if op_v in {
33
+ X86_OpTypes["G_CC_OP_SUBB"],
34
+ X86_OpTypes["G_CC_OP_SUBW"],
35
+ X86_OpTypes["G_CC_OP_SUBL"],
36
+ }:
37
+ # dep_1 <=s dep_2
38
+ dep_1 = self._fix_size(
39
+ dep_1,
40
+ op_v,
41
+ X86_OpTypes["G_CC_OP_SUBB"],
42
+ X86_OpTypes["G_CC_OP_SUBW"],
43
+ ccall.tags,
44
+ )
45
+ dep_2 = self._fix_size(
46
+ dep_2,
47
+ op_v,
48
+ X86_OpTypes["G_CC_OP_SUBB"],
49
+ X86_OpTypes["G_CC_OP_SUBW"],
50
+ ccall.tags,
51
+ )
52
+
53
+ r = Expr.BinaryOp(ccall.idx, "CmpLE", (dep_1, dep_2), True, **ccall.tags)
54
+ return Expr.Convert(None, r.bits, ccall.bits, False, r, **ccall.tags)
55
+ return None
56
+
57
+ @staticmethod
58
+ def _fix_size(expr, op_v: int, type_8bit, type_16bit, tags):
59
+ if op_v == type_8bit:
60
+ bits = 8
61
+ elif op_v == type_16bit:
62
+ bits = 16
63
+ else:
64
+ bits = 32
65
+ if bits < 32:
66
+ if isinstance(expr, Expr.Const):
67
+ return Expr.Const(expr.idx, None, expr.value & ((1 << bits) - 1), bits, **tags)
68
+ return Expr.Convert(None, 32, bits, False, expr, **tags)
69
+ return expr