angr 9.2.140__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 +105 -35
- angr/analyses/calling_convention/fact_collector.py +44 -18
- angr/analyses/calling_convention/utils.py +3 -1
- angr/analyses/cfg/cfg_base.py +38 -4
- angr/analyses/cfg/cfg_fast.py +23 -7
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +13 -8
- angr/analyses/class_identifier.py +8 -7
- angr/analyses/complete_calling_conventions.py +1 -1
- angr/analyses/decompiler/ail_simplifier.py +105 -62
- angr/analyses/decompiler/callsite_maker.py +24 -11
- angr/analyses/decompiler/clinic.py +83 -5
- angr/analyses/decompiler/condition_processor.py +7 -7
- angr/analyses/decompiler/decompilation_cache.py +2 -1
- angr/analyses/decompiler/decompiler.py +11 -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/duplication_reverter/duplication_reverter.py +3 -1
- angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +85 -16
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +78 -1
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +29 -7
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +51 -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/peephole_optimizations/eager_eval.py +44 -7
- angr/analyses/decompiler/region_identifier.py +76 -51
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +32 -18
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -1
- angr/analyses/decompiler/ssailification/rewriting.py +70 -32
- angr/analyses/decompiler/ssailification/rewriting_engine.py +118 -24
- angr/analyses/decompiler/ssailification/ssailification.py +22 -14
- angr/analyses/decompiler/stack_item.py +36 -0
- angr/analyses/decompiler/structured_codegen/c.py +86 -145
- angr/analyses/decompiler/structuring/dream.py +1 -1
- angr/analyses/decompiler/structuring/phoenix.py +9 -4
- angr/analyses/decompiler/structuring/structurer_base.py +2 -1
- angr/analyses/decompiler/utils.py +46 -20
- 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/s_reaching_definitions/s_rda_view.py +43 -25
- angr/analyses/typehoon/simple_solver.py +35 -8
- angr/analyses/typehoon/typehoon.py +3 -1
- angr/analyses/variable_recovery/engine_ail.py +1 -1
- angr/analyses/variable_recovery/engine_vex.py +20 -4
- angr/calling_conventions.py +17 -12
- angr/factory.py +8 -3
- angr/knowledge_plugins/functions/function.py +5 -10
- angr/knowledge_plugins/variables/variable_manager.py +34 -5
- 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 +17 -11
- 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 +59 -0
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/METADATA +7 -7
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/RECORD +75 -70
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/LICENSE +0 -0
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/WHEEL +0 -0
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/entry_points.txt +0 -0
- {angr-9.2.140.dist-info → angr-9.2.142.dist-info}/top_level.txt +0 -0
|
@@ -9,7 +9,7 @@ import contextlib
|
|
|
9
9
|
from typing import overload
|
|
10
10
|
|
|
11
11
|
import networkx
|
|
12
|
-
|
|
12
|
+
import pydemumble
|
|
13
13
|
|
|
14
14
|
from cle.backends.symbol import Symbol
|
|
15
15
|
from archinfo.arch_arm import get_real_address_if_arm
|
|
@@ -202,7 +202,8 @@ class Function(Serializable):
|
|
|
202
202
|
if is_plt is not None:
|
|
203
203
|
self.is_plt = is_plt
|
|
204
204
|
else:
|
|
205
|
-
# Whether this function is a PLT entry or not is
|
|
205
|
+
# Whether this function is a PLT entry or not is primarily relying on the PLT detection in CLE; it may also
|
|
206
|
+
# be updated (to True) during CFG recovery.
|
|
206
207
|
if self.project is None:
|
|
207
208
|
raise ValueError(
|
|
208
209
|
"'is_plt' must be specified if you do not specify a function manager for this new function."
|
|
@@ -1568,14 +1569,8 @@ class Function(Serializable):
|
|
|
1568
1569
|
|
|
1569
1570
|
@property
|
|
1570
1571
|
def demangled_name(self):
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
ast = parse(self.name)
|
|
1574
|
-
except (NotImplementedError, KeyError): # itanium demangler is not the most robust package in the world
|
|
1575
|
-
return self.name
|
|
1576
|
-
if ast:
|
|
1577
|
-
return ast.__str__()
|
|
1578
|
-
return self.name
|
|
1572
|
+
ast = pydemumble.demangle(self.name)
|
|
1573
|
+
return ast if ast else self.name
|
|
1579
1574
|
|
|
1580
1575
|
def get_unambiguous_name(self, display_name: str | None = None) -> str:
|
|
1581
1576
|
"""
|
|
@@ -12,6 +12,7 @@ from cle.backends.elf.variable import Variable
|
|
|
12
12
|
|
|
13
13
|
from angr.utils.orderedset import OrderedSet
|
|
14
14
|
from angr.utils.ail import is_phi_assignment
|
|
15
|
+
from angr.utils.types import unpack_pointer, replace_pointer_pts_to
|
|
15
16
|
from angr.protos import variables_pb2
|
|
16
17
|
from angr.serializable import Serializable
|
|
17
18
|
from angr.sim_variable import SimVariable, SimStackVariable, SimMemoryVariable, SimRegisterVariable
|
|
@@ -19,7 +20,6 @@ from angr.sim_type import (
|
|
|
19
20
|
TypeRef,
|
|
20
21
|
SimType,
|
|
21
22
|
SimStruct,
|
|
22
|
-
SimTypePointer,
|
|
23
23
|
SimTypeBottom,
|
|
24
24
|
SimTypeChar,
|
|
25
25
|
SimTypeShort,
|
|
@@ -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__)
|
|
@@ -985,10 +986,12 @@ class VariableManagerInternal(Serializable):
|
|
|
985
986
|
if name not in self.types:
|
|
986
987
|
self.types[name] = TypeRef(name, ty).with_arch(self.manager._kb._project.arch)
|
|
987
988
|
ty = self.types[name]
|
|
988
|
-
elif
|
|
989
|
-
typeref = self._register_struct_type(
|
|
990
|
-
|
|
991
|
-
|
|
989
|
+
elif (inner_ty := unpack_pointer(ty, iterative=True)) and isinstance(inner_ty, SimStruct):
|
|
990
|
+
typeref = self._register_struct_type(inner_ty)
|
|
991
|
+
# rebuild the multi-layer pointer type
|
|
992
|
+
replaced_ty = replace_pointer_pts_to(ty, inner_ty, typeref)
|
|
993
|
+
assert replaced_ty is not None
|
|
994
|
+
ty = replaced_ty.with_arch(self.manager._kb._project.arch)
|
|
992
995
|
elif isinstance(ty, SimStruct):
|
|
993
996
|
ty = self._register_struct_type(ty, name=name)
|
|
994
997
|
|
|
@@ -1139,6 +1142,32 @@ class VariableManagerInternal(Serializable):
|
|
|
1139
1142
|
return False
|
|
1140
1143
|
return True
|
|
1141
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
|
+
|
|
1142
1171
|
|
|
1143
1172
|
class VariableManager(KnowledgeBasePlugin):
|
|
1144
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
|
|
|
@@ -23,7 +27,9 @@ class SimOS:
|
|
|
23
27
|
A class describing OS/arch-level configuration.
|
|
24
28
|
"""
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
name: str | None
|
|
31
|
+
|
|
32
|
+
def __init__(self, project: angr.Project, name: str | None = None):
|
|
27
33
|
self.arch = project.arch
|
|
28
34
|
self.project = project
|
|
29
35
|
self.name = name
|
|
@@ -44,7 +50,7 @@ class SimOS:
|
|
|
44
50
|
self.unresolvable_call_target = self.project.loader.extern_object.allocate()
|
|
45
51
|
self.project.hook(self.unresolvable_call_target, P["stubs"]["UnresolvableCallTarget"]())
|
|
46
52
|
|
|
47
|
-
def irelative_resolver(resolver_addr):
|
|
53
|
+
def irelative_resolver(resolver_addr: int) -> int | None:
|
|
48
54
|
# autohooking runs before this does, might have provided this already
|
|
49
55
|
# in that case, we want to advertise the _resolver_ address, since it is now
|
|
50
56
|
# providing the behavior of the actual function
|
|
@@ -68,7 +74,7 @@ class SimOS:
|
|
|
68
74
|
_l.error("Resolver at %#x failed to resolve!", resolver_addr)
|
|
69
75
|
return None
|
|
70
76
|
|
|
71
|
-
return val.concrete_value
|
|
77
|
+
return val.concrete_value if val is not None and val.concrete else None
|
|
72
78
|
|
|
73
79
|
self.project.loader.perform_irelative_relocs(irelative_resolver)
|
|
74
80
|
|
|
@@ -77,7 +83,7 @@ class SimOS:
|
|
|
77
83
|
|
|
78
84
|
if sym is not None:
|
|
79
85
|
addr, _ = self.prepare_function_symbol(name, basic_addr=sym.rebased_addr)
|
|
80
|
-
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
|
|
81
87
|
return
|
|
82
88
|
self.project.hook(addr, hook)
|
|
83
89
|
|
|
@@ -240,7 +246,7 @@ class SimOS:
|
|
|
240
246
|
return self.state_entry(**kwargs)
|
|
241
247
|
|
|
242
248
|
def state_call(self, addr, *args, **kwargs):
|
|
243
|
-
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
|
|
244
250
|
state = kwargs.pop("base_state", None)
|
|
245
251
|
toc = kwargs.pop("toc", None)
|
|
246
252
|
|
|
@@ -324,22 +330,22 @@ class SimOS:
|
|
|
324
330
|
# Dummy stuff to allow this API to be used freely
|
|
325
331
|
|
|
326
332
|
# pylint: disable=unused-argument, no-self-use
|
|
327
|
-
def syscall(self, state, allow_unsupported=True):
|
|
333
|
+
def syscall(self, state: SimState, allow_unsupported: bool = True) -> SimProcedure | None:
|
|
328
334
|
return None
|
|
329
335
|
|
|
330
|
-
def syscall_abi(self, state) -> str:
|
|
336
|
+
def syscall_abi(self, state: SimState) -> str | None:
|
|
331
337
|
return None
|
|
332
338
|
|
|
333
|
-
def syscall_cc(self, state) -> angr.calling_conventions.SimCCSyscall | None:
|
|
339
|
+
def syscall_cc(self, state: SimState) -> angr.calling_conventions.SimCCSyscall | None:
|
|
334
340
|
raise NotImplementedError
|
|
335
341
|
|
|
336
|
-
def is_syscall_addr(self, addr):
|
|
342
|
+
def is_syscall_addr(self, addr) -> bool:
|
|
337
343
|
return False
|
|
338
344
|
|
|
339
|
-
def syscall_from_addr(self, addr, allow_unsupported=True):
|
|
345
|
+
def syscall_from_addr(self, addr, allow_unsupported=True) -> SimProcedure | None:
|
|
340
346
|
return None
|
|
341
347
|
|
|
342
|
-
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:
|
|
343
349
|
return None
|
|
344
350
|
|
|
345
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
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from angr.sim_type import TypeRef, SimType, SimTypePointer, SimTypeArray, SimTypeFixedSizeArray
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def unpack_typeref(ty):
|
|
7
|
+
if isinstance(ty, TypeRef):
|
|
8
|
+
return ty.type
|
|
9
|
+
return ty
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def unpack_pointer(ty: SimType, iterative: bool = False) -> SimType | None:
|
|
13
|
+
if isinstance(ty, SimTypePointer):
|
|
14
|
+
if iterative:
|
|
15
|
+
inner = unpack_pointer(ty.pts_to, iterative=True)
|
|
16
|
+
return inner if inner is not None else ty.pts_to
|
|
17
|
+
return ty.pts_to
|
|
18
|
+
return None
|
|
19
|
+
|
|
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
|
+
|
|
32
|
+
def replace_pointer_pts_to(ty: SimType, old_pts_to: SimType, new_pts_to: SimType) -> SimTypePointer | None:
|
|
33
|
+
if isinstance(ty, SimTypePointer):
|
|
34
|
+
if ty.pts_to is old_pts_to:
|
|
35
|
+
inner = new_pts_to
|
|
36
|
+
elif isinstance(ty.pts_to, SimTypePointer):
|
|
37
|
+
# recursively replace pts_to inside
|
|
38
|
+
inner = replace_pointer_pts_to(ty.pts_to, old_pts_to, new_pts_to)
|
|
39
|
+
else:
|
|
40
|
+
return None
|
|
41
|
+
return SimTypePointer(inner, label=ty.label, offset=ty.offset)
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def unpack_array(ty) -> SimType | None:
|
|
46
|
+
if isinstance(ty, SimTypeArray):
|
|
47
|
+
return ty.elem_type
|
|
48
|
+
if isinstance(ty, SimTypeFixedSizeArray):
|
|
49
|
+
return ty.elem_type
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def squash_array_reference(ty):
|
|
54
|
+
pointed_to = unpack_pointer(ty)
|
|
55
|
+
if pointed_to:
|
|
56
|
+
array_of = unpack_array(pointed_to)
|
|
57
|
+
if array_of:
|
|
58
|
+
return SimTypePointer(array_of)
|
|
59
|
+
return ty
|
|
@@ -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
|