angr 9.2.141__py3-none-manylinux2014_x86_64.whl → 9.2.142__py3-none-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/analyses/calling_convention/calling_convention.py +17 -3
- angr/analyses/cfg/cfg_base.py +38 -4
- angr/analyses/cfg/cfg_fast.py +23 -7
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +4 -0
- angr/analyses/class_identifier.py +8 -7
- angr/analyses/complete_calling_conventions.py +1 -1
- angr/analyses/decompiler/ail_simplifier.py +61 -46
- angr/analyses/decompiler/clinic.py +73 -5
- angr/analyses/decompiler/condition_processor.py +7 -7
- angr/analyses/decompiler/decompilation_cache.py +2 -1
- angr/analyses/decompiler/decompiler.py +10 -2
- angr/analyses/decompiler/dephication/graph_vvar_mapping.py +4 -6
- angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +8 -2
- angr/analyses/decompiler/optimization_passes/condition_constprop.py +63 -34
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +2 -0
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +29 -7
- angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +6 -0
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +9 -1
- angr/analyses/decompiler/region_identifier.py +70 -47
- angr/analyses/decompiler/ssailification/rewriting.py +47 -17
- angr/analyses/decompiler/ssailification/rewriting_engine.py +13 -0
- angr/analyses/decompiler/stack_item.py +36 -0
- angr/analyses/decompiler/structured_codegen/c.py +14 -9
- angr/analyses/decompiler/structuring/phoenix.py +3 -3
- angr/analyses/find_objects_static.py +2 -1
- angr/analyses/reaching_definitions/engine_vex.py +13 -0
- angr/analyses/reaching_definitions/function_handler.py +24 -10
- angr/analyses/reaching_definitions/function_handler_library/stdio.py +1 -0
- angr/analyses/reaching_definitions/function_handler_library/stdlib.py +45 -12
- angr/analyses/reaching_definitions/function_handler_library/string.py +77 -21
- angr/analyses/reaching_definitions/function_handler_library/unistd.py +21 -1
- angr/analyses/reaching_definitions/rd_state.py +11 -7
- angr/analyses/s_liveness.py +44 -6
- angr/analyses/s_reaching_definitions/s_rda_model.py +4 -2
- angr/analyses/typehoon/simple_solver.py +35 -8
- angr/analyses/typehoon/typehoon.py +3 -1
- angr/calling_conventions.py +2 -2
- angr/knowledge_plugins/functions/function.py +5 -10
- angr/knowledge_plugins/variables/variable_manager.py +27 -0
- angr/procedures/definitions/__init__.py +3 -10
- angr/procedures/definitions/wdk_ntoskrnl.py +2 -0
- angr/procedures/win32_kernel/__fastfail.py +15 -0
- angr/sim_procedure.py +2 -2
- angr/simos/simos.py +14 -10
- angr/simos/windows.py +42 -1
- angr/utils/ail.py +41 -1
- angr/utils/cpp.py +17 -0
- angr/utils/doms.py +142 -0
- angr/utils/library.py +1 -1
- angr/utils/types.py +12 -1
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/METADATA +7 -7
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/RECORD +58 -54
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/LICENSE +0 -0
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/WHEEL +0 -0
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/entry_points.txt +0 -0
- {angr-9.2.141.dist-info → angr-9.2.142.dist-info}/top_level.txt +0 -0
|
@@ -32,6 +32,7 @@ from angr.knowledge_plugins.types import TypesStore
|
|
|
32
32
|
from .variable_access import VariableAccess, VariableAccessSort
|
|
33
33
|
|
|
34
34
|
if TYPE_CHECKING:
|
|
35
|
+
from angr.analyses.decompiler.stack_item import StackItem
|
|
35
36
|
from angr.code_location import CodeLocation
|
|
36
37
|
|
|
37
38
|
l = logging.getLogger(name=__name__)
|
|
@@ -1141,6 +1142,32 @@ class VariableManagerInternal(Serializable):
|
|
|
1141
1142
|
return False
|
|
1142
1143
|
return True
|
|
1143
1144
|
|
|
1145
|
+
def get_stackvar_max_sizes(self, stack_items: dict[int, StackItem]) -> dict[SimStackVariable, int]:
|
|
1146
|
+
"""
|
|
1147
|
+
Get the maximum size of each stack variable regardless of the type of each stack variable, under the assumption
|
|
1148
|
+
that stack variables do not overlap.
|
|
1149
|
+
|
|
1150
|
+
:return: A dictionary from SimStackVariable to its maximum size.
|
|
1151
|
+
"""
|
|
1152
|
+
|
|
1153
|
+
stackvars_by_offset = defaultdict(list)
|
|
1154
|
+
for v in self._variables:
|
|
1155
|
+
if isinstance(v, SimStackVariable):
|
|
1156
|
+
offset = v.offset
|
|
1157
|
+
stackvars_by_offset[offset].append(v)
|
|
1158
|
+
|
|
1159
|
+
max_sizes = {}
|
|
1160
|
+
offsets = sorted(list(stackvars_by_offset) + list(stack_items))
|
|
1161
|
+
for i, offset in enumerate(offsets):
|
|
1162
|
+
if i + 1 < len(offsets):
|
|
1163
|
+
next_off = offsets[i + 1]
|
|
1164
|
+
sz = next_off - offset
|
|
1165
|
+
if offset in stackvars_by_offset:
|
|
1166
|
+
for v in stackvars_by_offset[offset]:
|
|
1167
|
+
max_sizes[v] = max(v.size, sz)
|
|
1168
|
+
|
|
1169
|
+
return max_sizes
|
|
1170
|
+
|
|
1144
1171
|
|
|
1145
1172
|
class VariableManager(KnowledgeBasePlugin):
|
|
1146
1173
|
"""
|
|
@@ -7,8 +7,7 @@ import inspect
|
|
|
7
7
|
from collections import defaultdict
|
|
8
8
|
from typing import TYPE_CHECKING
|
|
9
9
|
|
|
10
|
-
import
|
|
11
|
-
|
|
10
|
+
import pydemumble
|
|
12
11
|
import archinfo
|
|
13
12
|
|
|
14
13
|
from angr.errors import AngrMissingTypeError
|
|
@@ -345,14 +344,8 @@ class SimCppLibrary(SimLibrary):
|
|
|
345
344
|
|
|
346
345
|
@staticmethod
|
|
347
346
|
def _try_demangle(name):
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
ast = itanium_demangler.parse(name)
|
|
351
|
-
except NotImplementedError:
|
|
352
|
-
return name
|
|
353
|
-
if ast:
|
|
354
|
-
return str(ast)
|
|
355
|
-
return name
|
|
347
|
+
ast = pydemumble.demangle(name)
|
|
348
|
+
return ast if ast else name
|
|
356
349
|
|
|
357
350
|
@staticmethod
|
|
358
351
|
def _proto_from_demangled_name(name: str) -> SimTypeFunction | None:
|
|
@@ -20,6 +20,8 @@ lib.add_all_from_dict(P["win32_kernel"])
|
|
|
20
20
|
lib.set_library_names("ntoskrnl.exe")
|
|
21
21
|
prototypes = \
|
|
22
22
|
{
|
|
23
|
+
# int 29h
|
|
24
|
+
'__fastfail': SimTypeFunction([SimTypeInt(signed=False, label="Int")], SimTypeBottom(label="void"), arg_names=["code"]),
|
|
23
25
|
#
|
|
24
26
|
'NtQueryObject': SimTypeFunction([SimTypePointer(SimTypeInt(signed=True, label="Int"), label="IntPtr", offset=0), SimTypeInt(signed=False, label="OBJECT_INFORMATION_CLASS"), SimTypePointer(SimTypeBottom(label="Void"), offset=0), SimTypeInt(signed=False, label="UInt32"), SimTypePointer(SimTypeInt(signed=False, label="UInt32"), offset=0)], SimTypeInt(signed=True, label="Int32"), arg_names=["Handle", "ObjectInformationClass", "ObjectInformation", "ObjectInformationLength", "ReturnLength"]),
|
|
25
27
|
#
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import angr
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class __fastfail(angr.SimProcedure):
|
|
6
|
+
"""
|
|
7
|
+
Immediately terminates the calling process with minimum overhead.
|
|
8
|
+
|
|
9
|
+
https://learn.microsoft.com/en-us/cpp/intrinsics/fastfail?view=msvc-170
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
NO_RET = True
|
|
13
|
+
|
|
14
|
+
def run(self, _): # pylint:disable=arguments-differ
|
|
15
|
+
self.exit(0xC0000409)
|
angr/sim_procedure.py
CHANGED
|
@@ -3,7 +3,7 @@ import inspect
|
|
|
3
3
|
import copy
|
|
4
4
|
import itertools
|
|
5
5
|
import logging
|
|
6
|
-
from typing import TYPE_CHECKING
|
|
6
|
+
from typing import Any, TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
import claripy
|
|
9
9
|
from cle import SymbolType
|
|
@@ -339,7 +339,7 @@ class SimProcedure:
|
|
|
339
339
|
ALT_NAMES = None # alternative names
|
|
340
340
|
local_vars: tuple[str, ...] = ()
|
|
341
341
|
|
|
342
|
-
def run(self, *args, **kwargs): # pylint: disable=unused-argument
|
|
342
|
+
def run(self, *args, **kwargs) -> Any: # pylint: disable=unused-argument
|
|
343
343
|
"""
|
|
344
344
|
Implement the actual procedure here!
|
|
345
345
|
"""
|
angr/simos/simos.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
2
3
|
import logging
|
|
3
4
|
import struct
|
|
4
5
|
|
|
@@ -14,6 +15,9 @@ from angr.procedures import SIM_PROCEDURES as P
|
|
|
14
15
|
from angr import sim_options as o
|
|
15
16
|
from angr.storage.file import SimFileStream, SimFileBase
|
|
16
17
|
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from angr.sim_procedure import SimProcedure
|
|
20
|
+
|
|
17
21
|
|
|
18
22
|
_l = logging.getLogger(name=__name__)
|
|
19
23
|
|
|
@@ -46,7 +50,7 @@ class SimOS:
|
|
|
46
50
|
self.unresolvable_call_target = self.project.loader.extern_object.allocate()
|
|
47
51
|
self.project.hook(self.unresolvable_call_target, P["stubs"]["UnresolvableCallTarget"]())
|
|
48
52
|
|
|
49
|
-
def irelative_resolver(resolver_addr):
|
|
53
|
+
def irelative_resolver(resolver_addr: int) -> int | None:
|
|
50
54
|
# autohooking runs before this does, might have provided this already
|
|
51
55
|
# in that case, we want to advertise the _resolver_ address, since it is now
|
|
52
56
|
# providing the behavior of the actual function
|
|
@@ -70,7 +74,7 @@ class SimOS:
|
|
|
70
74
|
_l.error("Resolver at %#x failed to resolve!", resolver_addr)
|
|
71
75
|
return None
|
|
72
76
|
|
|
73
|
-
return val.concrete_value
|
|
77
|
+
return val.concrete_value if val is not None and val.concrete else None
|
|
74
78
|
|
|
75
79
|
self.project.loader.perform_irelative_relocs(irelative_resolver)
|
|
76
80
|
|
|
@@ -79,7 +83,7 @@ class SimOS:
|
|
|
79
83
|
|
|
80
84
|
if sym is not None:
|
|
81
85
|
addr, _ = self.prepare_function_symbol(name, basic_addr=sym.rebased_addr)
|
|
82
|
-
if self.project.is_hooked(addr) and not self.project.hooked_by(addr).is_stub:
|
|
86
|
+
if self.project.is_hooked(addr) and not self.project.hooked_by(addr).is_stub: # type: ignore
|
|
83
87
|
return
|
|
84
88
|
self.project.hook(addr, hook)
|
|
85
89
|
|
|
@@ -242,7 +246,7 @@ class SimOS:
|
|
|
242
246
|
return self.state_entry(**kwargs)
|
|
243
247
|
|
|
244
248
|
def state_call(self, addr, *args, **kwargs):
|
|
245
|
-
cc = kwargs.pop("cc", default_cc(self.arch.name, platform=self.name)(self.project.arch))
|
|
249
|
+
cc = kwargs.pop("cc", default_cc(self.arch.name, platform=self.name)(self.project.arch)) # type: ignore
|
|
246
250
|
state = kwargs.pop("base_state", None)
|
|
247
251
|
toc = kwargs.pop("toc", None)
|
|
248
252
|
|
|
@@ -326,22 +330,22 @@ class SimOS:
|
|
|
326
330
|
# Dummy stuff to allow this API to be used freely
|
|
327
331
|
|
|
328
332
|
# pylint: disable=unused-argument, no-self-use
|
|
329
|
-
def syscall(self, state, allow_unsupported=True):
|
|
333
|
+
def syscall(self, state: SimState, allow_unsupported: bool = True) -> SimProcedure | None:
|
|
330
334
|
return None
|
|
331
335
|
|
|
332
|
-
def syscall_abi(self, state) -> str:
|
|
336
|
+
def syscall_abi(self, state: SimState) -> str | None:
|
|
333
337
|
return None
|
|
334
338
|
|
|
335
|
-
def syscall_cc(self, state) -> angr.calling_conventions.SimCCSyscall | None:
|
|
339
|
+
def syscall_cc(self, state: SimState) -> angr.calling_conventions.SimCCSyscall | None:
|
|
336
340
|
raise NotImplementedError
|
|
337
341
|
|
|
338
|
-
def is_syscall_addr(self, addr):
|
|
342
|
+
def is_syscall_addr(self, addr) -> bool:
|
|
339
343
|
return False
|
|
340
344
|
|
|
341
|
-
def syscall_from_addr(self, addr, allow_unsupported=True):
|
|
345
|
+
def syscall_from_addr(self, addr, allow_unsupported=True) -> SimProcedure | None:
|
|
342
346
|
return None
|
|
343
347
|
|
|
344
|
-
def syscall_from_number(self, number, allow_unsupported=True, abi=None):
|
|
348
|
+
def syscall_from_number(self, number, allow_unsupported=True, abi=None) -> SimProcedure | None:
|
|
345
349
|
return None
|
|
346
350
|
|
|
347
351
|
def setup_gdt(self, state, gdt):
|
angr/simos/windows.py
CHANGED
|
@@ -15,6 +15,7 @@ from angr import sim_options as o
|
|
|
15
15
|
from angr.tablespecs import StringTableSpec
|
|
16
16
|
from angr.procedures import SIM_LIBRARIES as L
|
|
17
17
|
from angr.procedures.definitions import load_win32api_definitions
|
|
18
|
+
from angr.calling_conventions import SYSCALL_CC
|
|
18
19
|
from .simos import SimOS
|
|
19
20
|
|
|
20
21
|
_l = logging.getLogger(name=__name__)
|
|
@@ -26,6 +27,8 @@ VS_SECURITY_COOKIES = {"AMD64": _VS_Security_Cookie(0x2B992DDFA232, 48), "X86":
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class SecurityCookieInit(enum.Enum):
|
|
30
|
+
"""Security cooke initialization value initialization method."""
|
|
31
|
+
|
|
29
32
|
NONE = 0
|
|
30
33
|
RANDOM = 1
|
|
31
34
|
STATIC = 2
|
|
@@ -48,6 +51,11 @@ class SimWindows(SimOS):
|
|
|
48
51
|
self.acmdln_ptr = None
|
|
49
52
|
self.wcmdln_ptr = None
|
|
50
53
|
|
|
54
|
+
self.fastfail = L["ntoskrnl.exe"].get("__fastfail", self.arch)
|
|
55
|
+
self.fastfail.addr = self._find_or_make(self.fastfail.display_name)
|
|
56
|
+
self.fastfail.cc = SYSCALL_CC[self.arch.name]["Win32"](self.arch)
|
|
57
|
+
self._syscall_handlers = {self.fastfail.addr: self.fastfail}
|
|
58
|
+
|
|
51
59
|
def configure_project(self):
|
|
52
60
|
super().configure_project()
|
|
53
61
|
|
|
@@ -66,11 +74,15 @@ class SimWindows(SimOS):
|
|
|
66
74
|
self.acmdln_ptr = self._find_or_make("_acmdln")
|
|
67
75
|
self.wcmdln_ptr = self._find_or_make("_wcmdln")
|
|
68
76
|
|
|
69
|
-
self.
|
|
77
|
+
self.project.hook(self.fastfail.addr, self.fastfail)
|
|
70
78
|
|
|
71
79
|
if not self.is_dump:
|
|
72
80
|
self.project.loader.tls.new_thread()
|
|
73
81
|
|
|
82
|
+
@property
|
|
83
|
+
def is_dump(self) -> bool:
|
|
84
|
+
return isinstance(self.project.loader.main_object, cle.backends.Minidump)
|
|
85
|
+
|
|
74
86
|
def _find_or_make(self, name):
|
|
75
87
|
sym = self.project.loader.find_symbol(name)
|
|
76
88
|
if sym is None:
|
|
@@ -420,6 +432,35 @@ class SimWindows(SimOS):
|
|
|
420
432
|
successors.add_successor(exc_state, self._exception_handler, claripy.true(), "Ijk_Exception")
|
|
421
433
|
successors.processed = True
|
|
422
434
|
|
|
435
|
+
def syscall(self, state, allow_unsupported=True):
|
|
436
|
+
"""
|
|
437
|
+
Given a state, return the procedure corresponding to the current syscall.
|
|
438
|
+
This procedure will have .syscall_number, .display_name, and .addr set.
|
|
439
|
+
|
|
440
|
+
:param state: The state to get the syscall number from
|
|
441
|
+
:param allow_unsupported: Whether to return a "dummy" sycall instead of raising an unsupported exception
|
|
442
|
+
"""
|
|
443
|
+
if state.block(state.history.jump_source).bytes.hex() == "cd29": # int 29h
|
|
444
|
+
return self.fastfail
|
|
445
|
+
return None
|
|
446
|
+
|
|
447
|
+
def is_syscall_addr(self, addr):
|
|
448
|
+
"""
|
|
449
|
+
Return whether or not the given address corresponds to a syscall implementation.
|
|
450
|
+
"""
|
|
451
|
+
return addr in self._syscall_handlers
|
|
452
|
+
|
|
453
|
+
def syscall_from_addr(self, addr, allow_unsupported=True):
|
|
454
|
+
"""
|
|
455
|
+
Get a syscall SimProcedure from an address.
|
|
456
|
+
|
|
457
|
+
:param addr: The address to convert to a syscall SimProcedure
|
|
458
|
+
:param allow_unsupported: Whether to return a dummy procedure for an unsupported syscall instead of raising an
|
|
459
|
+
exception.
|
|
460
|
+
:return: The SimProcedure for the syscall, or None if the address is not a syscall address.
|
|
461
|
+
"""
|
|
462
|
+
return self._syscall_handlers.get(addr, None)
|
|
463
|
+
|
|
423
464
|
# these two methods load and store register state from a struct CONTEXT
|
|
424
465
|
# https://www.nirsoft.net/kernel_struct/vista/CONTEXT.html
|
|
425
466
|
@staticmethod
|
angr/utils/ail.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from ailment import AILBlockWalkerBase
|
|
4
4
|
from ailment.block import Block
|
|
5
5
|
from ailment.expression import Expression, VirtualVariable, Phi
|
|
6
|
-
from ailment.statement import Assignment, Statement
|
|
6
|
+
from ailment.statement import Assignment, Statement, ConditionalJump
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def is_phi_assignment(stmt: Statement) -> bool:
|
|
@@ -28,3 +28,43 @@ class HasExprWalker(AILBlockWalkerBase):
|
|
|
28
28
|
self.contains_exprs = True
|
|
29
29
|
if not self.contains_exprs:
|
|
30
30
|
super()._handle_expr(expr_idx, expr, stmt_idx, stmt, block)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def is_head_controlled_loop_block(block: Block) -> bool:
|
|
34
|
+
"""
|
|
35
|
+
Determine if the block is a "head-controlled loop." A head-controlled loop (for the lack of a better term) is a
|
|
36
|
+
single-block that contains a conditional jump towards the beginning of the block. This conditional jump controls
|
|
37
|
+
whether the loop body (the remaining statements after the conditional jump) will be executed or not. It is usually
|
|
38
|
+
the result of lifting rep stosX instructions on x86 and amd64.
|
|
39
|
+
|
|
40
|
+
A head-controlled loop block looks like the following (lifted from rep stosq qword ptr [rdi], rax):
|
|
41
|
+
|
|
42
|
+
## Block 4036df
|
|
43
|
+
00 | 0x4036df | LABEL_4036df:
|
|
44
|
+
01 | 0x4036df | vvar_27{reg 72} = 𝜙@64b []
|
|
45
|
+
02 | 0x4036df | vvar_28{reg 24} = 𝜙@64b []
|
|
46
|
+
03 | 0x4036df | t1 = rcx<8>
|
|
47
|
+
04 | 0x4036df | t4 = (t1 == 0x0<64>)
|
|
48
|
+
05 | 0x4036df | if (t4) { Goto 0x4036e2<64> } else { Goto 0x4036df<64> }
|
|
49
|
+
06 | 0x4036df | t5 = (t1 - 0x1<64>)
|
|
50
|
+
07 | 0x4036df | rcx<8> = t5
|
|
51
|
+
08 | 0x4036df | t7 = d<8>
|
|
52
|
+
09 | 0x4036df | t6 = (t7 << 0x3<8>)
|
|
53
|
+
10 | 0x4036df | t2 = rax<8>
|
|
54
|
+
11 | 0x4036df | t3 = rdi<8>
|
|
55
|
+
12 | 0x4036df | STORE(addr=t3, data=t2, size=8, endness=Iend_LE, guard=None)
|
|
56
|
+
13 | 0x4036df | t8 = (t3 + t6)
|
|
57
|
+
14 | 0x4036df | rdi<8> = t8
|
|
58
|
+
|
|
59
|
+
Where statement 5 is the conditional jump that controls the execution of the remaining statements of this block.
|
|
60
|
+
|
|
61
|
+
:param block: An AIL block.
|
|
62
|
+
:return: True if the block represents a head-controlled loop block, False otherwise.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
if not block.statements:
|
|
66
|
+
return False
|
|
67
|
+
last_stmt = block.statements[-1]
|
|
68
|
+
if isinstance(last_stmt, ConditionalJump):
|
|
69
|
+
return False
|
|
70
|
+
return any(isinstance(stmt, ConditionalJump) for stmt in block.statements[:-1])
|
angr/utils/cpp.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def is_cpp_funcname_ctor(name: str) -> bool:
|
|
5
|
+
"""
|
|
6
|
+
Check if a demangled C++ function name is a constructor.
|
|
7
|
+
|
|
8
|
+
:param name: The demangled C++ function name.
|
|
9
|
+
:return: True if the function name is a constructor, False otherwise.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# With pydemumble, constructor names look like:
|
|
13
|
+
# A::A()
|
|
14
|
+
if "::" not in name:
|
|
15
|
+
return False
|
|
16
|
+
parts = name.split("::")
|
|
17
|
+
return bool(len(parts) == 2 and parts[0] and parts[0] + "()" == parts[1])
|
angr/utils/doms.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# pylint:disable=consider-using-dict-items
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import Any
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
|
|
6
|
+
import networkx
|
|
7
|
+
|
|
8
|
+
from angr.utils.graph import shallow_reverse
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class IncrementalDominators:
|
|
12
|
+
"""
|
|
13
|
+
This class allows for incrementally updating dominators and post-dominators for graphs. The graph must only be
|
|
14
|
+
modified by replacing nodes, not adding nodes or edges.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, graph: networkx.DiGraph, start, post: bool = False):
|
|
18
|
+
self.graph = graph
|
|
19
|
+
self.start = start
|
|
20
|
+
self._post: bool = post # calculate post-dominators if True
|
|
21
|
+
self._pre: bool = not post # calculate dominators
|
|
22
|
+
|
|
23
|
+
self._doms: dict[Any, Any] = {}
|
|
24
|
+
self._inverted_dom_tree: dict[Any, Any] | None = None # initialized on demand
|
|
25
|
+
|
|
26
|
+
self._doms = self.init_doms()
|
|
27
|
+
|
|
28
|
+
def init_doms(self) -> dict[Any, Any]:
|
|
29
|
+
if self._post:
|
|
30
|
+
t = shallow_reverse(self.graph)
|
|
31
|
+
doms = networkx.immediate_dominators(t, self.start)
|
|
32
|
+
else:
|
|
33
|
+
doms = networkx.immediate_dominators(self.graph, self.start)
|
|
34
|
+
return doms
|
|
35
|
+
|
|
36
|
+
def _update_inverted_domtree(self):
|
|
37
|
+
# recalculate the dominators for dominatees of replaced nodes
|
|
38
|
+
if self._inverted_dom_tree is None:
|
|
39
|
+
self._inverted_dom_tree = defaultdict(list)
|
|
40
|
+
for dtee, dtor in self._doms.items():
|
|
41
|
+
self._inverted_dom_tree[dtor].append(dtee)
|
|
42
|
+
|
|
43
|
+
def graph_updated(self, new_node: Any, replaced_nodes: set[Any], replaced_head: Any):
|
|
44
|
+
self._update_inverted_domtree()
|
|
45
|
+
assert self._inverted_dom_tree is not None
|
|
46
|
+
|
|
47
|
+
# recalculate the dominators for impacted nodes
|
|
48
|
+
new_dom = self._doms[replaced_head]
|
|
49
|
+
while new_dom in replaced_nodes and new_dom is not self.start:
|
|
50
|
+
new_dom = self._doms[new_dom]
|
|
51
|
+
|
|
52
|
+
if self.start in replaced_nodes:
|
|
53
|
+
self.start = new_node
|
|
54
|
+
if new_dom in replaced_nodes:
|
|
55
|
+
new_dom = new_node
|
|
56
|
+
|
|
57
|
+
new_node_doms = []
|
|
58
|
+
for rn in replaced_nodes:
|
|
59
|
+
if rn not in self._inverted_dom_tree:
|
|
60
|
+
continue
|
|
61
|
+
for dtee in self._inverted_dom_tree[rn]:
|
|
62
|
+
self._doms[dtee] = new_node
|
|
63
|
+
new_node_doms.append(dtee)
|
|
64
|
+
self._doms[new_node] = new_dom
|
|
65
|
+
|
|
66
|
+
# keep inverted dom tree up-to-date
|
|
67
|
+
self._inverted_dom_tree[new_dom].append(new_node)
|
|
68
|
+
self._inverted_dom_tree[new_node] = new_node_doms
|
|
69
|
+
for rn in replaced_nodes:
|
|
70
|
+
if rn in self._doms:
|
|
71
|
+
d = self._doms[rn]
|
|
72
|
+
del self._doms[rn]
|
|
73
|
+
self._inverted_dom_tree[d].remove(rn)
|
|
74
|
+
if rn in self._inverted_dom_tree:
|
|
75
|
+
del self._inverted_dom_tree[rn]
|
|
76
|
+
|
|
77
|
+
def idom(self, node: Any) -> Any | None:
|
|
78
|
+
"""
|
|
79
|
+
Get the immediate dominator of a given node.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
return self._doms.get(node, None)
|
|
83
|
+
|
|
84
|
+
def df(self, node: Any) -> set[Any]:
|
|
85
|
+
"""
|
|
86
|
+
Generate the dominance frontier of a node.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
if node not in self.graph:
|
|
90
|
+
return set()
|
|
91
|
+
|
|
92
|
+
_pred = self.graph.predecessors if self._pre else self.graph.successors
|
|
93
|
+
_succ = self.graph.successors if self._pre else self.graph.predecessors
|
|
94
|
+
df = set()
|
|
95
|
+
|
|
96
|
+
visited = {node}
|
|
97
|
+
queue = [node]
|
|
98
|
+
|
|
99
|
+
while queue:
|
|
100
|
+
u = queue.pop(0)
|
|
101
|
+
preds = list(_pred(u)) # type: ignore
|
|
102
|
+
added = False
|
|
103
|
+
if len(preds) >= 2:
|
|
104
|
+
for v in preds:
|
|
105
|
+
if v in self._doms:
|
|
106
|
+
while v != self._doms[u]:
|
|
107
|
+
if v is node:
|
|
108
|
+
df.add(u)
|
|
109
|
+
added = True
|
|
110
|
+
break
|
|
111
|
+
v = self._doms[v]
|
|
112
|
+
if added:
|
|
113
|
+
break
|
|
114
|
+
|
|
115
|
+
if not added:
|
|
116
|
+
for v in _succ(u): # type: ignore
|
|
117
|
+
if v not in visited:
|
|
118
|
+
visited.add(v)
|
|
119
|
+
queue.append(v)
|
|
120
|
+
return df
|
|
121
|
+
|
|
122
|
+
def dominates(self, dominator_node: Any, node: Any) -> bool:
|
|
123
|
+
"""
|
|
124
|
+
Tests if dominator_node dominates (or post-dominates) node.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
n = node
|
|
128
|
+
while n:
|
|
129
|
+
if n is dominator_node:
|
|
130
|
+
return True
|
|
131
|
+
d = self.idom(n)
|
|
132
|
+
n = d if d is not None and n is not d else None
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
def _debug_check(self):
|
|
136
|
+
true_doms = self.init_doms()
|
|
137
|
+
if len(true_doms) != len(self._doms):
|
|
138
|
+
raise ValueError("dominators do not match")
|
|
139
|
+
for k in true_doms:
|
|
140
|
+
if true_doms[k] != self._doms[k]:
|
|
141
|
+
print(f"{k!r}: {true_doms[k]!r} {self._doms[k]!r}")
|
|
142
|
+
raise ValueError("dominators do not match")
|
angr/utils/library.py
CHANGED
|
@@ -168,7 +168,7 @@ def parsedcprotos2py(
|
|
|
168
168
|
proto_.returnty = SimTypeFd(label=proto_.returnty.label)
|
|
169
169
|
for i, arg in enumerate(proto_.args):
|
|
170
170
|
if (func_name, i) in fd_spots:
|
|
171
|
-
proto_.args[i]
|
|
171
|
+
proto_.args = proto_.args[:i] + (SimTypeFd(label=arg.label),) + proto_.args[i + 1 :]
|
|
172
172
|
|
|
173
173
|
line1 = " " * 8 + "#" + ((" " + decl) if decl else "") + "\n"
|
|
174
174
|
line2 = " " * 8 + repr(func_name) + ": " + (proto_._init_str() if proto_ is not None else "None") + "," + "\n"
|
angr/utils/types.py
CHANGED
|
@@ -9,7 +9,7 @@ def unpack_typeref(ty):
|
|
|
9
9
|
return ty
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def unpack_pointer(ty, iterative: bool = False) -> SimType | None:
|
|
12
|
+
def unpack_pointer(ty: SimType, iterative: bool = False) -> SimType | None:
|
|
13
13
|
if isinstance(ty, SimTypePointer):
|
|
14
14
|
if iterative:
|
|
15
15
|
inner = unpack_pointer(ty.pts_to, iterative=True)
|
|
@@ -18,6 +18,17 @@ def unpack_pointer(ty, iterative: bool = False) -> SimType | None:
|
|
|
18
18
|
return None
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
def unpack_pointer_and_array(ty: SimType, iterative: bool = False) -> SimType | None:
|
|
22
|
+
if isinstance(ty, SimTypePointer):
|
|
23
|
+
if iterative:
|
|
24
|
+
inner = unpack_pointer(ty.pts_to, iterative=True)
|
|
25
|
+
return inner if inner is not None else ty.pts_to
|
|
26
|
+
return ty.pts_to
|
|
27
|
+
if isinstance(ty, SimTypeArray):
|
|
28
|
+
return ty.elem_type
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
|
|
21
32
|
def replace_pointer_pts_to(ty: SimType, old_pts_to: SimType, new_pts_to: SimType) -> SimTypePointer | None:
|
|
22
33
|
if isinstance(ty, SimTypePointer):
|
|
23
34
|
if ty.pts_to is old_pts_to:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: angr
|
|
3
|
-
Version: 9.2.
|
|
3
|
+
Version: 9.2.142
|
|
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
|
|
@@ -16,22 +16,22 @@ Description-Content-Type: text/markdown
|
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: CppHeaderParser
|
|
18
18
|
Requires-Dist: GitPython
|
|
19
|
-
Requires-Dist: ailment==9.2.
|
|
20
|
-
Requires-Dist: archinfo==9.2.
|
|
19
|
+
Requires-Dist: ailment==9.2.142
|
|
20
|
+
Requires-Dist: archinfo==9.2.142
|
|
21
21
|
Requires-Dist: cachetools
|
|
22
22
|
Requires-Dist: capstone==5.0.3
|
|
23
23
|
Requires-Dist: cffi>=1.14.0
|
|
24
|
-
Requires-Dist: claripy==9.2.
|
|
25
|
-
Requires-Dist: cle==9.2.
|
|
26
|
-
Requires-Dist: itanium-demangler
|
|
24
|
+
Requires-Dist: claripy==9.2.142
|
|
25
|
+
Requires-Dist: cle==9.2.142
|
|
27
26
|
Requires-Dist: mulpyplexer
|
|
28
27
|
Requires-Dist: nampa
|
|
29
28
|
Requires-Dist: networkx!=2.8.1,>=2.0
|
|
30
29
|
Requires-Dist: protobuf>=5.28.2
|
|
31
30
|
Requires-Dist: psutil
|
|
32
31
|
Requires-Dist: pycparser>=2.18
|
|
32
|
+
Requires-Dist: pydemumble
|
|
33
33
|
Requires-Dist: pyformlang
|
|
34
|
-
Requires-Dist: pyvex==9.2.
|
|
34
|
+
Requires-Dist: pyvex==9.2.142
|
|
35
35
|
Requires-Dist: rich>=13.1.0
|
|
36
36
|
Requires-Dist: sortedcontainers
|
|
37
37
|
Requires-Dist: sympy
|