angr 9.2.78__py3-none-win_amd64.whl → 9.2.80__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 (55) hide show
  1. angr/__init__.py +1 -1
  2. angr/__main__.py +59 -0
  3. angr/analyses/cfg/cfg_fast.py +140 -3
  4. angr/analyses/decompiler/ail_simplifier.py +8 -0
  5. angr/analyses/decompiler/block_simplifier.py +25 -5
  6. angr/analyses/decompiler/clinic.py +33 -19
  7. angr/analyses/decompiler/decompilation_options.py +9 -0
  8. angr/analyses/decompiler/optimization_passes/__init__.py +6 -0
  9. angr/analyses/decompiler/optimization_passes/engine_base.py +2 -2
  10. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +2 -2
  11. angr/analyses/decompiler/optimization_passes/multi_simplifier.py +0 -12
  12. angr/analyses/decompiler/optimization_passes/optimization_pass.py +8 -5
  13. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +82 -12
  14. angr/analyses/decompiler/peephole_optimizations/__init__.py +11 -2
  15. angr/analyses/decompiler/peephole_optimizations/base.py +29 -2
  16. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +1 -1
  17. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +14 -2
  18. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +83 -0
  19. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +103 -0
  20. angr/analyses/decompiler/region_simplifiers/ifelse.py +19 -10
  21. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -2
  22. angr/analyses/decompiler/structured_codegen/c.py +20 -4
  23. angr/analyses/decompiler/utils.py +131 -2
  24. angr/analyses/propagator/engine_ail.py +3 -1
  25. angr/analyses/propagator/engine_vex.py +45 -0
  26. angr/analyses/propagator/propagator.py +24 -15
  27. angr/analyses/proximity_graph.py +30 -0
  28. angr/analyses/reaching_definitions/engine_ail.py +1 -1
  29. angr/analyses/stack_pointer_tracker.py +55 -0
  30. angr/callable.py +4 -4
  31. angr/engines/light/engine.py +30 -18
  32. angr/knowledge_plugins/__init__.py +1 -0
  33. angr/knowledge_plugins/custom_strings.py +40 -0
  34. angr/knowledge_plugins/functions/function.py +29 -0
  35. angr/knowledge_plugins/propagations/propagation_model.py +4 -0
  36. angr/knowledge_plugins/propagations/states.py +54 -4
  37. angr/lib/angr_native.dll +0 -0
  38. angr/procedures/definitions/__init__.py +2 -1
  39. angr/procedures/definitions/msvcr.py +0 -3
  40. angr/procedures/definitions/ntoskrnl.py +9 -0
  41. angr/procedures/win32_kernel/ExAllocatePool.py +12 -0
  42. angr/procedures/win32_kernel/ExFreePoolWithTag.py +7 -0
  43. angr/procedures/win32_kernel/__init__.py +3 -0
  44. angr/sim_type.py +3 -0
  45. angr/storage/memory_mixins/__init__.py +1 -1
  46. angr/utils/funcid.py +128 -0
  47. {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/METADATA +6 -6
  48. {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/RECORD +55 -45
  49. {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/WHEEL +1 -1
  50. angr-9.2.80.dist-info/entry_points.txt +2 -0
  51. tests/analyses/cfg/test_cfgfast.py +24 -0
  52. tests/analyses/decompiler/test_decompiler.py +128 -0
  53. tests/analyses/test_constantpropagation.py +34 -0
  54. {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/LICENSE +0 -0
  55. {angr-9.2.78.dist-info → angr-9.2.80.dist-info}/top_level.txt +0 -0
@@ -80,6 +80,7 @@ class Function(Serializable):
80
80
  "is_alignment",
81
81
  "is_prototype_guessed",
82
82
  "ran_cca",
83
+ "_cyclomatic_complexity",
83
84
  )
84
85
 
85
86
  def __init__(
@@ -161,6 +162,9 @@ class Function(Serializable):
161
162
  self.info = {} # storing special information, like $gp values for MIPS32
162
163
  self.tags = () # store function tags. can be set manually by performing CodeTagging analysis.
163
164
 
165
+ # Initialize _cyclomatic_complexity to None
166
+ self._cyclomatic_complexity = None
167
+
164
168
  # TODO: Can we remove the following two members?
165
169
  # Register offsets of those arguments passed in registers
166
170
  self._argument_registers = []
@@ -302,6 +306,30 @@ class Function(Serializable):
302
306
  except (SimEngineError, SimMemoryError):
303
307
  pass
304
308
 
309
+ @property
310
+ def cyclomatic_complexity(self):
311
+ """
312
+ The cyclomatic complexity of the function.
313
+
314
+ Cyclomatic complexity is a software metric used to indicate the complexity of a program.
315
+ It is a quantitative measure of the number of linearly independent paths through a program's source code.
316
+ It is computed using the formula: M = E - N + 2P, where
317
+ E = the number of edges in the graph,
318
+ N = the number of nodes in the graph,
319
+ P = the number of connected components.
320
+
321
+ The cyclomatic complexity value is lazily computed and cached for future use.
322
+ Initially this value is None until it is computed for the first time
323
+
324
+ :return: The cyclomatic complexity of the function.
325
+ :rtype: int
326
+ """
327
+ if self._cyclomatic_complexity is None:
328
+ self._cyclomatic_complexity = (
329
+ self.transition_graph.number_of_edges() - self.transition_graph.number_of_nodes() + 2
330
+ )
331
+ return self._cyclomatic_complexity
332
+
305
333
  @property
306
334
  def xrefs(self):
307
335
  """
@@ -565,6 +593,7 @@ class Function(Serializable):
565
593
  s += " Alignment: %s\n" % (self.alignment)
566
594
  s += f" Arguments: reg: {self._argument_registers}, stack: {self._argument_stack_variables}\n"
567
595
  s += " Blocks: [%s]\n" % ", ".join(["%#x" % i for i in self.block_addrs])
596
+ s += " Cyclomatic Complexity: %s\n" % self.cyclomatic_complexity
568
597
  s += " Calling convention: %s" % self.calling_convention
569
598
  return s
570
599
 
@@ -17,6 +17,7 @@ class PropagationModel(Serializable):
17
17
  "key",
18
18
  "node_iterations",
19
19
  "states",
20
+ "input_states",
20
21
  "block_initial_reg_values",
21
22
  "replacements",
22
23
  "equivalence",
@@ -35,9 +36,11 @@ class PropagationModel(Serializable):
35
36
  replacements: Optional[DefaultDict[Any, Dict]] = None,
36
37
  equivalence: Optional[Set] = None,
37
38
  function: Optional[Function] = None,
39
+ input_states: Optional[Dict] = None,
38
40
  ):
39
41
  self.key = prop_key
40
42
  self.node_iterations = node_iterations if node_iterations is not None else defaultdict(int)
43
+ self.input_states = input_states if input_states is not None else {}
41
44
  self.states = states if states is not None else {}
42
45
  self.block_initial_reg_values = block_initial_reg_values if block_initial_reg_values is not None else {}
43
46
  self.replacements = replacements
@@ -51,6 +54,7 @@ class PropagationModel(Serializable):
51
54
  self.node_iterations = None
52
55
  self.block_initial_reg_values = None
53
56
  self.states = None
57
+ self.input_states = None
54
58
  self.graph_visitor = None
55
59
 
56
60
  def block_beginning_state(self, block_addr) -> PropagatorState:
@@ -1,3 +1,4 @@
1
+ # pylint:disable=too-many-boolean-expressions
1
2
  from typing import Set, Optional, Union, Tuple, DefaultDict, List, Any, Dict, TYPE_CHECKING
2
3
  from collections import defaultdict
3
4
  import weakref
@@ -256,7 +257,9 @@ class PropagatorState:
256
257
  def init_replacements(self):
257
258
  self._replacements = defaultdict(dict)
258
259
 
259
- def add_replacement(self, codeloc: CodeLocation, old, new, force_replace: bool = False) -> bool:
260
+ def add_replacement(
261
+ self, codeloc: CodeLocation, old, new, force_replace: bool = False
262
+ ) -> bool: # pylint:disable=unused-argument
260
263
  """
261
264
  Add a replacement record: Replacing expression `old` with `new` at program location `codeloc`.
262
265
  If the self._only_consts flag is set to true, only constant values will be set.
@@ -296,6 +299,44 @@ class PropagatorState:
296
299
  # VEX state
297
300
 
298
301
 
302
+ class RegisterAnnotation(claripy.Annotation):
303
+ """
304
+ Annotates TOP values that are coming from registers.
305
+ """
306
+
307
+ def __init__(self, offset, size):
308
+ self.offset = offset
309
+ self.size = size
310
+
311
+ @property
312
+ def eliminatable(self) -> bool:
313
+ return True
314
+
315
+ @property
316
+ def relocatable(self) -> bool:
317
+ return True
318
+
319
+
320
+ class RegisterComparisonAnnotation(claripy.Annotation):
321
+ """
322
+ Annotate TOP values that are the result of register values comparing against constant values.
323
+ """
324
+
325
+ def __init__(self, offset, size, cmp_op, value):
326
+ self.offset = offset
327
+ self.size = size
328
+ self.cmp_op = cmp_op
329
+ self.value = value
330
+
331
+ @property
332
+ def eliminatable(self) -> bool:
333
+ return True
334
+
335
+ @property
336
+ def relocatable(self) -> bool:
337
+ return True
338
+
339
+
299
340
  class PropagatorVEXState(PropagatorState):
300
341
  """
301
342
  Describes the state used in the VEX engine of Propagator.
@@ -305,6 +346,7 @@ class PropagatorVEXState(PropagatorState):
305
346
  "_registers",
306
347
  "_stack_variables",
307
348
  "do_binops",
349
+ "block_initial_reg_values",
308
350
  )
309
351
 
310
352
  def __init__(
@@ -319,6 +361,7 @@ class PropagatorVEXState(PropagatorState):
319
361
  expr_used_locs=None,
320
362
  do_binops=True,
321
363
  store_tops=True,
364
+ block_initial_reg_values=None,
322
365
  gp=None,
323
366
  max_prop_expr_occurrence: int = 1,
324
367
  model=None,
@@ -349,6 +392,9 @@ class PropagatorVEXState(PropagatorState):
349
392
 
350
393
  self._registers.set_state(self)
351
394
  self._stack_variables.set_state(self)
395
+ self.block_initial_reg_values = (
396
+ defaultdict(list) if block_initial_reg_values is None else block_initial_reg_values
397
+ )
352
398
 
353
399
  def __repr__(self):
354
400
  return "<PropagatorVEXState>"
@@ -418,6 +464,7 @@ class PropagatorVEXState(PropagatorState):
418
464
  only_consts=self._only_consts,
419
465
  do_binops=self.do_binops,
420
466
  store_tops=self._store_tops,
467
+ block_initial_reg_values=self.block_initial_reg_values.copy(),
421
468
  gp=self._gp,
422
469
  max_prop_expr_occurrence=self._max_prop_expr_occurrence,
423
470
  model=self.model,
@@ -448,12 +495,15 @@ class PropagatorVEXState(PropagatorState):
448
495
  def load_register(self, offset, size):
449
496
  # TODO: Fix me
450
497
  if size != self.gpr_size:
451
- return self.top(size * self.arch.byte_width)
498
+ return self.top(size * self.arch.byte_width).annotate(RegisterAnnotation(offset, size))
452
499
 
453
500
  try:
454
- return self._registers.load(offset, size=size)
501
+ v = self._registers.load(offset, size=size)
502
+ if self.is_top(v):
503
+ v = v.annotate(RegisterAnnotation(offset, size))
504
+ return v
455
505
  except SimMemoryMissingError:
456
- return self.top(size * self.arch.byte_width)
506
+ return self.top(size * self.arch.byte_width).annotate(RegisterAnnotation(offset, size))
457
507
 
458
508
  def register_results(self) -> Dict[str, claripy.ast.BV]:
459
509
  result = {}
angr/lib/angr_native.dll CHANGED
Binary file
@@ -632,7 +632,8 @@ def load_win32api_definitions():
632
632
  for _ in autoimport.auto_import_modules(
633
633
  "angr.procedures.definitions",
634
634
  _DEFINITIONS_BASEDIR,
635
- filter_func=lambda module_name: module_name.startswith("win32_"),
635
+ filter_func=lambda module_name: module_name.startswith("win32_")
636
+ or module_name in {"ntoskrnl", "ntdll", "user32"},
636
637
  ):
637
638
  pass
638
639
 
@@ -13,6 +13,3 @@ libc.set_non_returning('_exit', 'abort', 'exit', '_invoke_watson')
13
13
  libc.add_alias('_initterm', '_initterm_e')
14
14
 
15
15
  libc.set_default_cc('AMD64', SimCCMicrosoftAMD64)
16
-
17
- for name, procedure in libc.procedures.items():
18
- libc.set_prototype(name, procedure.prototype)
@@ -0,0 +1,9 @@
1
+ from . import SimLibrary
2
+ from .. import SIM_PROCEDURES as P
3
+ from ...calling_conventions import SimCCStdcall, SimCCMicrosoftAMD64
4
+
5
+ lib = SimLibrary()
6
+ lib.set_library_names("ntoskrnl.exe")
7
+ lib.add_all_from_dict(P["win32_kernel"])
8
+ lib.set_default_cc("X86", SimCCStdcall)
9
+ lib.set_default_cc("AMD64", SimCCMicrosoftAMD64)
@@ -0,0 +1,12 @@
1
+ # pylint: disable=missing-class-docstring
2
+ import claripy
3
+
4
+ import angr
5
+
6
+
7
+ class ExAllocatePool(angr.SimProcedure):
8
+ def run(self, PoolType, NumberOfBytes): # pylint:disable=arguments-differ, unused-argument
9
+ addr = self.state.heap._malloc(NumberOfBytes)
10
+ memset = angr.SIM_PROCEDURES["libc"]["memset"]
11
+ self.inline_call(memset, addr, claripy.BVV(0, 8), NumberOfBytes) # zerofill
12
+ return addr
@@ -0,0 +1,7 @@
1
+ # pylint: disable=missing-class-docstring
2
+ from angr import SimProcedure
3
+
4
+
5
+ class ExFreePoolWithTag(SimProcedure):
6
+ def run(self, P, Tag): # pylint:disable=arguments-differ, unused-argument
7
+ self.state.heap._free(P)
@@ -0,0 +1,3 @@
1
+ """
2
+ These procedures implement functions from the Windows kernel.
3
+ """
angr/sim_type.py CHANGED
@@ -908,6 +908,9 @@ class SimTypeFunction(SimType):
908
908
  self.arg_names = arg_names if arg_names else ()
909
909
  self.variadic = variadic
910
910
 
911
+ def __hash__(self):
912
+ return hash(type(self)) ^ hash(tuple(self.args)) ^ hash(self.returnty)
913
+
911
914
  def __repr__(self):
912
915
  argstrs = [str(a) for a in self.args]
913
916
  if self.variadic:
@@ -58,7 +58,7 @@ class MemoryMixin(SimStatePlugin):
58
58
  to_add = c
59
59
  self.state.add_constraints(to_add)
60
60
 
61
- def load(self, addr, **kwargs):
61
+ def load(self, addr, size=None, **kwargs):
62
62
  pass
63
63
 
64
64
  def store(self, addr, data, **kwargs):
angr/utils/funcid.py ADDED
@@ -0,0 +1,128 @@
1
+ # pylint:disable=too-many-boolean-expressions
2
+ from typing import Optional
3
+
4
+ import capstone
5
+
6
+ from angr.knowledge_plugins.functions import Function
7
+
8
+
9
+ def is_function_security_check_cookie(func, project, security_cookie_addr: int) -> bool:
10
+ # disassemble the first instruction
11
+ if func.is_plt or func.is_syscall or func.is_simprocedure:
12
+ return False
13
+ block = project.factory.block(func.addr)
14
+ if block.instructions != 2:
15
+ return False
16
+ ins0 = block.capstone.insns[0]
17
+ if (
18
+ ins0.mnemonic == "cmp"
19
+ and len(ins0.operands) == 2
20
+ and ins0.operands[0].type == capstone.x86.X86_OP_REG
21
+ and ins0.operands[0].reg == capstone.x86.X86_REG_RCX
22
+ and ins0.operands[1].type == capstone.x86.X86_OP_MEM
23
+ and ins0.operands[1].mem.base == capstone.x86.X86_REG_RIP
24
+ and ins0.operands[1].mem.index == 0
25
+ and ins0.operands[1].mem.disp + ins0.address + ins0.size == security_cookie_addr
26
+ ):
27
+ ins1 = block.capstone.insns[1]
28
+ if ins1.mnemonic == "jne":
29
+ return True
30
+ return False
31
+
32
+
33
+ def is_function_security_init_cookie(func: "Function", project, security_cookie_addr: Optional[int]) -> bool:
34
+ if func.is_plt or func.is_syscall or func.is_simprocedure:
35
+ return False
36
+ # the function should have only one return point
37
+ if len(func.endpoints) == 1 and len(func.ret_sites) == 1:
38
+ # the function is normalized
39
+ ret_block = next(iter(func.ret_sites))
40
+ preds = [(pred.addr, pred.size) for pred in func.graph.predecessors(ret_block)]
41
+ if len(preds) != 2:
42
+ return False
43
+ elif len(func.endpoints) == 2 and len(func.ret_sites) == 2:
44
+ # the function is not normalized
45
+ ep0, ep1 = func.endpoints
46
+ if ep0.addr > ep1.addr:
47
+ ep0, ep1 = ep1, ep0
48
+ if ep0.addr + ep0.size == ep1.addr + ep1.size and ep0.addr < ep1.addr:
49
+ # overlapping block
50
+ preds = [(ep0.addr, ep1.addr - ep0.addr)]
51
+ else:
52
+ return False
53
+ else:
54
+ return False
55
+ for node_addr, node_size in preds:
56
+ # lift the block and check the last instruction
57
+ block = project.factory.block(node_addr, size=node_size)
58
+ if not block.instructions:
59
+ continue
60
+ last_insn = block.capstone.insns[-1]
61
+ if (
62
+ last_insn.mnemonic == "mov"
63
+ and len(last_insn.operands) == 2
64
+ and last_insn.operands[0].type == capstone.x86.X86_OP_MEM
65
+ and last_insn.operands[0].mem.base == capstone.x86.X86_REG_RIP
66
+ and last_insn.operands[0].mem.index == 0
67
+ and last_insn.operands[0].mem.disp + last_insn.address + last_insn.size == security_cookie_addr
68
+ and last_insn.operands[1].type == capstone.x86.X86_OP_REG
69
+ ):
70
+ return True
71
+ return False
72
+
73
+
74
+ def is_function_security_init_cookie_win8(func: "Function", project, security_cookie_addr: int) -> bool:
75
+ # disassemble the first instruction
76
+ if func.is_plt or func.is_syscall or func.is_simprocedure:
77
+ return False
78
+ block = project.factory.block(func.addr)
79
+ if block.instructions != 3:
80
+ return False
81
+ ins0 = block.capstone.insns[0]
82
+ if (
83
+ ins0.mnemonic == "mov"
84
+ and len(ins0.operands) == 2
85
+ and ins0.operands[0].type == capstone.x86.X86_OP_REG
86
+ and ins0.operands[0].reg == capstone.x86.X86_REG_RAX
87
+ and ins0.operands[1].type == capstone.x86.X86_OP_MEM
88
+ and ins0.operands[1].mem.base == capstone.x86.X86_REG_RIP
89
+ and ins0.operands[1].mem.index == 0
90
+ and ins0.operands[1].mem.disp + ins0.address + ins0.size == security_cookie_addr
91
+ ):
92
+ ins1 = block.capstone.insns[-1]
93
+ if ins1.mnemonic == "je":
94
+ succs = list(func.graph.successors(func.get_node(block.addr)))
95
+ if len(succs) > 2:
96
+ return False
97
+ for succ in succs:
98
+ succ_block = project.factory.block(succ.addr)
99
+ if succ_block.instructions:
100
+ first_insn = succ_block.capstone.insns[0]
101
+ if (
102
+ first_insn.mnemonic == "movabs"
103
+ and len(first_insn.operands) == 2
104
+ and first_insn.operands[1].type == capstone.x86.X86_OP_IMM
105
+ and first_insn.operands[1].imm == 0x2B992DDFA232
106
+ ):
107
+ return True
108
+ return False
109
+
110
+
111
+ def is_function_likely_security_init_cookie(func: "Function") -> bool:
112
+ """
113
+ Conducts a fuzzy match for security_init_cookie function.
114
+ """
115
+
116
+ callees = [node for node in func.transition_graph if isinstance(node, Function)]
117
+ callee_names = {callee.name for callee in callees}
118
+ if callee_names.issuperset(
119
+ {
120
+ "GetSystemTimeAsFileTime",
121
+ "GetCurrentProcessId",
122
+ "GetCurrentThreadId",
123
+ "GetTickCount",
124
+ "QueryPerformanceCounter",
125
+ }
126
+ ):
127
+ return True
128
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: angr
3
- Version: 9.2.78
3
+ Version: 9.2.80
4
4
  Summary: A multi-architecture binary analysis toolkit, with the ability to perform dynamic symbolic execution and various static analyses on binaries
5
5
  Home-page: https://github.com/angr/angr
6
6
  License: BSD-2-Clause
@@ -17,13 +17,13 @@ Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
18
  Requires-Dist: CppHeaderParser
19
19
  Requires-Dist: GitPython
20
- Requires-Dist: ailment ==9.2.78
21
- Requires-Dist: archinfo ==9.2.78
20
+ Requires-Dist: ailment ==9.2.80
21
+ Requires-Dist: archinfo ==9.2.80
22
22
  Requires-Dist: cachetools
23
23
  Requires-Dist: capstone ==5.0.0.post1
24
24
  Requires-Dist: cffi >=1.14.0
25
- Requires-Dist: claripy ==9.2.78
26
- Requires-Dist: cle ==9.2.78
25
+ Requires-Dist: claripy ==9.2.80
26
+ Requires-Dist: cle ==9.2.80
27
27
  Requires-Dist: dpkt
28
28
  Requires-Dist: itanium-demangler
29
29
  Requires-Dist: mulpyplexer
@@ -32,7 +32,7 @@ Requires-Dist: networkx !=2.8.1,>=2.0
32
32
  Requires-Dist: protobuf >=3.19.0
33
33
  Requires-Dist: psutil
34
34
  Requires-Dist: pycparser >=2.18
35
- Requires-Dist: pyvex ==9.2.78
35
+ Requires-Dist: pyvex ==9.2.80
36
36
  Requires-Dist: rich >=13.1.0
37
37
  Requires-Dist: rpyc
38
38
  Requires-Dist: sortedcontainers