angr 9.2.65__py3-none-manylinux2014_x86_64.whl → 9.2.66__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 +55 -2
- angr/analyses/calling_convention.py +4 -3
- angr/analyses/cfg/cfg_base.py +2 -2
- angr/analyses/cfg/cfg_fast.py +128 -60
- angr/analyses/decompiler/ail_simplifier.py +1 -2
- angr/analyses/decompiler/block_simplifier.py +4 -3
- angr/analyses/decompiler/callsite_maker.py +1 -1
- angr/analyses/decompiler/condition_processor.py +5 -3
- angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +51 -8
- angr/analyses/decompiler/peephole_optimizations/__init__.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +92 -0
- angr/analyses/decompiler/structured_codegen/c.py +59 -6
- angr/analyses/decompiler/utils.py +1 -1
- angr/analyses/find_objects_static.py +4 -4
- angr/analyses/propagator/engine_ail.py +2 -1
- angr/analyses/reaching_definitions/__init__.py +1 -3
- angr/analyses/reaching_definitions/dep_graph.py +33 -4
- angr/analyses/reaching_definitions/engine_ail.py +5 -6
- angr/analyses/reaching_definitions/engine_vex.py +6 -7
- angr/analyses/reaching_definitions/external_codeloc.py +0 -27
- angr/analyses/reaching_definitions/function_handler.py +145 -23
- angr/analyses/reaching_definitions/rd_initializer.py +221 -0
- angr/analyses/reaching_definitions/rd_state.py +95 -153
- angr/analyses/reaching_definitions/reaching_definitions.py +15 -3
- angr/calling_conventions.py +2 -2
- angr/code_location.py +24 -0
- angr/exploration_techniques/__init__.py +28 -0
- angr/knowledge_plugins/cfg/cfg_model.py +1 -1
- angr/knowledge_plugins/key_definitions/__init__.py +12 -1
- angr/knowledge_plugins/key_definitions/atoms.py +9 -0
- angr/knowledge_plugins/key_definitions/definition.py +13 -18
- angr/knowledge_plugins/key_definitions/live_definitions.py +350 -106
- angr/project.py +1 -1
- angr/sim_manager.py +15 -0
- angr/sim_state.py +3 -3
- angr/storage/memory_mixins/paged_memory/pages/multi_values.py +56 -8
- angr/storage/memory_object.py +3 -1
- angr/utils/typing.py +16 -0
- {angr-9.2.65.dist-info → angr-9.2.66.dist-info}/METADATA +7 -7
- {angr-9.2.65.dist-info → angr-9.2.66.dist-info}/RECORD +43 -41
- angr/analyses/decompiler/peephole_optimizations/conv_const_mull_a_shift.py +0 -75
- {angr-9.2.65.dist-info → angr-9.2.66.dist-info}/LICENSE +0 -0
- {angr-9.2.65.dist-info → angr-9.2.66.dist-info}/WHEEL +0 -0
- {angr-9.2.65.dist-info → angr-9.2.66.dist-info}/top_level.txt +0 -0
|
@@ -892,7 +892,10 @@ class CIfElse(CStatement):
|
|
|
892
892
|
omit_braces = (
|
|
893
893
|
self.cstyle_ifs
|
|
894
894
|
and first_node
|
|
895
|
+
and len(self.condition_and_nodes) == 1
|
|
896
|
+
# no else-if tree can exist
|
|
895
897
|
and self._is_single_stmt_node(node)
|
|
898
|
+
# no else, else is also single-stmt, or else will not exist after pass
|
|
896
899
|
and (self.else_node is None or self._is_single_stmt_node(self.else_node) or self.simplify_else_scope)
|
|
897
900
|
)
|
|
898
901
|
|
|
@@ -1979,6 +1982,14 @@ class CConstant(CExpression):
|
|
|
1979
1982
|
def fmt_neg(self, v):
|
|
1980
1983
|
self._fmt_setter["neg"] = v
|
|
1981
1984
|
|
|
1985
|
+
@property
|
|
1986
|
+
def fmt_char(self):
|
|
1987
|
+
return self.fmt.get("char", False)
|
|
1988
|
+
|
|
1989
|
+
@fmt_char.setter
|
|
1990
|
+
def fmt_char(self, v: bool):
|
|
1991
|
+
self._fmt_setter["char"] = v
|
|
1992
|
+
|
|
1982
1993
|
@property
|
|
1983
1994
|
def type(self):
|
|
1984
1995
|
return self._type
|
|
@@ -2034,10 +2045,18 @@ class CConstant(CExpression):
|
|
|
2034
2045
|
elif value < 0:
|
|
2035
2046
|
value = value + 2**self._type.size
|
|
2036
2047
|
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2048
|
+
str_value = None
|
|
2049
|
+
if self.fmt_char:
|
|
2050
|
+
try:
|
|
2051
|
+
str_value = f"'{chr(value)}'"
|
|
2052
|
+
except ValueError:
|
|
2053
|
+
str_value = None
|
|
2054
|
+
|
|
2055
|
+
if str_value is None:
|
|
2056
|
+
if self.fmt_hex:
|
|
2057
|
+
str_value = hex(value)
|
|
2058
|
+
else:
|
|
2059
|
+
str_value = str(value)
|
|
2041
2060
|
|
|
2042
2061
|
yield str_value, self
|
|
2043
2062
|
else:
|
|
@@ -3512,15 +3531,49 @@ class FieldReferenceCleanup(CStructuredCodeWalker):
|
|
|
3512
3531
|
|
|
3513
3532
|
|
|
3514
3533
|
class PointerArithmeticFixer(CStructuredCodeWalker):
|
|
3534
|
+
"""
|
|
3535
|
+
Before calling this fixer class, pointer arithmetics are purely integer-based and ignoring the pointer type.
|
|
3536
|
+
|
|
3537
|
+
For example, in the following case:
|
|
3538
|
+
|
|
3539
|
+
struct A* a_ptr; // assume struct A is 24 bytes in size
|
|
3540
|
+
a_ptr = a_ptr + 24;
|
|
3541
|
+
|
|
3542
|
+
It means adding 24 to the address of a_ptr, without considering the size of struct A. This fixer class will make
|
|
3543
|
+
pointer arithmetics aware of the pointer type. In this case, the fixer class will convert the code to
|
|
3544
|
+
a_ptr = a_ptr + 1.
|
|
3545
|
+
"""
|
|
3546
|
+
|
|
3515
3547
|
@classmethod
|
|
3516
3548
|
def handle_CBinaryOp(cls, obj):
|
|
3517
|
-
obj = super().handle_CBinaryOp(obj)
|
|
3549
|
+
obj: CBinaryOp = super().handle_CBinaryOp(obj)
|
|
3518
3550
|
if (
|
|
3519
3551
|
obj.op in ("Add", "Sub")
|
|
3520
3552
|
and isinstance(obj.type, SimTypePointer)
|
|
3521
3553
|
and not isinstance(obj.type.pts_to, SimTypeBottom)
|
|
3522
3554
|
):
|
|
3523
|
-
|
|
3555
|
+
out = obj.codegen._access_reference(obj, obj.type.pts_to)
|
|
3556
|
+
if (
|
|
3557
|
+
isinstance(out, CUnaryOp)
|
|
3558
|
+
and out.op == "Reference"
|
|
3559
|
+
and isinstance(out.operand, CIndexedVariable)
|
|
3560
|
+
and isinstance(out.operand.index, CConstant)
|
|
3561
|
+
):
|
|
3562
|
+
# rewrite &a[1] to a + 1
|
|
3563
|
+
const = out.operand.index
|
|
3564
|
+
if isinstance(const.value, int) and const.value < 0:
|
|
3565
|
+
op = "Sub"
|
|
3566
|
+
const = CConstant(
|
|
3567
|
+
-const.value,
|
|
3568
|
+
const.type,
|
|
3569
|
+
reference_values=const.reference_values,
|
|
3570
|
+
tags=const.tags,
|
|
3571
|
+
codegen=const.codegen,
|
|
3572
|
+
)
|
|
3573
|
+
else:
|
|
3574
|
+
op = "Add"
|
|
3575
|
+
return CBinaryOp(op, out.operand.variable, const, out.operand.tags, codegen=out.codegen)
|
|
3576
|
+
return out
|
|
3524
3577
|
return obj
|
|
3525
3578
|
|
|
3526
3579
|
|
|
@@ -368,7 +368,7 @@ def structured_node_is_simple_return(node: Union["SequenceNode", "MultiNode"], g
|
|
|
368
368
|
}
|
|
369
369
|
...
|
|
370
370
|
|
|
371
|
-
Returns
|
|
371
|
+
Returns true on any block ending in linear statements and a return.
|
|
372
372
|
"""
|
|
373
373
|
|
|
374
374
|
def _flatten_structured_node(packed_node: Union["SequenceNode", "MultiNode"]) -> List[ailment.Block]:
|
|
@@ -74,7 +74,7 @@ class NewFunctionHandler(FunctionHandler):
|
|
|
74
74
|
else:
|
|
75
75
|
size_arg_reg_offset = self.project.arch.registers["rdi"][0]
|
|
76
76
|
size_arg_reg_size = word_size
|
|
77
|
-
v0 = state.
|
|
77
|
+
v0 = state.registers.load(size_arg_reg_offset, size_arg_reg_size).one_value()
|
|
78
78
|
size = v0._model_concrete.value if v0 is not None and v0.concrete else None
|
|
79
79
|
|
|
80
80
|
if size is not None:
|
|
@@ -108,7 +108,7 @@ class NewFunctionHandler(FunctionHandler):
|
|
|
108
108
|
# check if rdi has a possible this pointer/ object address, if so then we can assign this object this class
|
|
109
109
|
# also if the func is a constructor(not stripped binaries)
|
|
110
110
|
for addr, possible_object in self.possible_objects_dict.items():
|
|
111
|
-
v1 = state.
|
|
111
|
+
v1 = state.registers.load(72, state.arch.bits // state.arch.byte_width).one_value()
|
|
112
112
|
obj_addr = v1._model_concrete.value if v1 is not None and v1.concrete else None
|
|
113
113
|
if obj_addr is not None and addr == obj_addr:
|
|
114
114
|
col_ind = self.project.kb.functions[function_address].demangled_name.rfind("::")
|
|
@@ -172,7 +172,7 @@ class StaticObjectFinder(Analysis):
|
|
|
172
172
|
else:
|
|
173
173
|
ret_val_reg_offset = self.project.arch.registers["rax"][0]
|
|
174
174
|
ret_val_reg_size = word_size
|
|
175
|
-
v0 = rd_before_node.
|
|
175
|
+
v0 = rd_before_node.registers.load(ret_val_reg_offset, ret_val_reg_size).one_value()
|
|
176
176
|
addr_of_new_obj = v0._model_concrete.value if v0 is not None and v0.concrete else None
|
|
177
177
|
|
|
178
178
|
# we need the state right before the call
|
|
@@ -188,7 +188,7 @@ class StaticObjectFinder(Analysis):
|
|
|
188
188
|
else:
|
|
189
189
|
this_ptr_reg_offset = self.project.arch.registers["rdi"][0]
|
|
190
190
|
this_ptr_reg_size = word_size
|
|
191
|
-
v1 = rd_after_node.
|
|
191
|
+
v1 = rd_after_node.registers.load(this_ptr_reg_offset, this_ptr_reg_size).one_value()
|
|
192
192
|
addr_in_rdi = v1._model_concrete.value if v1 is not None and v1.concrete else None
|
|
193
193
|
|
|
194
194
|
if addr_of_new_obj is not None and addr_of_new_obj == addr_in_rdi:
|
|
@@ -6,8 +6,9 @@ import claripy
|
|
|
6
6
|
from ailment import Stmt, Expr
|
|
7
7
|
|
|
8
8
|
from angr.knowledge_plugins.propagations.prop_value import PropValue, Detail
|
|
9
|
-
from angr.analyses.reaching_definitions.external_codeloc import ExternalCodeLocation
|
|
10
9
|
from angr.knowledge_plugins.key_definitions.atoms import Register
|
|
10
|
+
|
|
11
|
+
from angr.code_location import ExternalCodeLocation
|
|
11
12
|
from ...utils.constants import is_alignment_mask
|
|
12
13
|
from ...engines.light import SimEngineLightAILMixin
|
|
13
14
|
from ...sim_variable import SimStackVariable, SimMemoryVariable
|
|
@@ -15,7 +15,6 @@ from ...knowledge_plugins.key_definitions.definition import Definition
|
|
|
15
15
|
from .. import register_analysis
|
|
16
16
|
from .reaching_definitions import ReachingDefinitionsAnalysis, ReachingDefinitionsModel
|
|
17
17
|
from .function_handler import FunctionHandler, FunctionCallData
|
|
18
|
-
from .external_codeloc import ExternalCodeLocation
|
|
19
18
|
from .rd_state import ReachingDefinitionsState
|
|
20
19
|
|
|
21
20
|
if TYPE_CHECKING:
|
|
@@ -40,7 +39,6 @@ __all__ = (
|
|
|
40
39
|
"FunctionHandler",
|
|
41
40
|
"FunctionCallData",
|
|
42
41
|
"get_all_definitions",
|
|
43
|
-
"ExternalCodeLocation",
|
|
44
42
|
)
|
|
45
43
|
|
|
46
44
|
|
|
@@ -55,7 +53,7 @@ def get_all_definitions(region: "MultiValuedMemory") -> Set["Definition"]:
|
|
|
55
53
|
cnt_set: Optional[Union["SimMemoryObject", Set["SimMemoryObject"]]] = page.content[idx]
|
|
56
54
|
if cnt_set is None:
|
|
57
55
|
continue
|
|
58
|
-
|
|
56
|
+
if type(cnt_set) is not set:
|
|
59
57
|
cnt_set = {cnt_set}
|
|
60
58
|
for cnt in cnt_set:
|
|
61
59
|
for def_ in LiveDefinitions.extract_defs(cnt.object):
|
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import (
|
|
2
|
+
Optional,
|
|
3
|
+
Dict,
|
|
4
|
+
Set,
|
|
5
|
+
Iterable,
|
|
6
|
+
Type,
|
|
7
|
+
Union,
|
|
8
|
+
List,
|
|
9
|
+
TYPE_CHECKING,
|
|
10
|
+
Tuple,
|
|
11
|
+
overload,
|
|
12
|
+
Literal,
|
|
13
|
+
Any,
|
|
14
|
+
Iterator,
|
|
15
|
+
)
|
|
2
16
|
from dataclasses import dataclass
|
|
3
17
|
|
|
4
18
|
import networkx
|
|
@@ -6,7 +20,7 @@ import networkx
|
|
|
6
20
|
import claripy
|
|
7
21
|
from cle.loader import Loader
|
|
8
22
|
|
|
9
|
-
from ...code_location import CodeLocation
|
|
23
|
+
from ...code_location import CodeLocation, ExternalCodeLocation
|
|
10
24
|
from ...knowledge_plugins.key_definitions.atoms import (
|
|
11
25
|
Atom,
|
|
12
26
|
MemoryLocation,
|
|
@@ -16,10 +30,9 @@ from ...knowledge_plugins.key_definitions.atoms import (
|
|
|
16
30
|
ConstantSrc,
|
|
17
31
|
GuardUse,
|
|
18
32
|
)
|
|
19
|
-
from ...knowledge_plugins.key_definitions.definition import Definition, DefinitionMatchPredicate
|
|
33
|
+
from ...knowledge_plugins.key_definitions.definition import A, Definition, DefinitionMatchPredicate
|
|
20
34
|
from ...knowledge_plugins.key_definitions.undefined import UNDEFINED
|
|
21
35
|
from ...knowledge_plugins.cfg import CFGModel
|
|
22
|
-
from .external_codeloc import ExternalCodeLocation
|
|
23
36
|
|
|
24
37
|
if TYPE_CHECKING:
|
|
25
38
|
pass
|
|
@@ -220,6 +233,16 @@ class DepGraph:
|
|
|
220
233
|
result.append(defn)
|
|
221
234
|
return result
|
|
222
235
|
|
|
236
|
+
@overload
|
|
237
|
+
def find_all_predecessors(
|
|
238
|
+
self,
|
|
239
|
+
starts: Union[Definition[Atom], Iterable[Definition[Atom]]],
|
|
240
|
+
*,
|
|
241
|
+
kind: Type[A],
|
|
242
|
+
**kwargs: Any,
|
|
243
|
+
) -> List[Definition[A]]:
|
|
244
|
+
...
|
|
245
|
+
|
|
223
246
|
@overload
|
|
224
247
|
def find_all_predecessors(
|
|
225
248
|
self,
|
|
@@ -292,6 +315,12 @@ class DepGraph:
|
|
|
292
315
|
) -> List[Definition[ConstantSrc]]:
|
|
293
316
|
...
|
|
294
317
|
|
|
318
|
+
@overload
|
|
319
|
+
def find_all_predecessors(
|
|
320
|
+
self, starts: Union[Definition[Atom], Iterable[Definition[Atom]]], **kwargs: Any
|
|
321
|
+
) -> List[Definition[Atom]]:
|
|
322
|
+
...
|
|
323
|
+
|
|
295
324
|
def find_all_predecessors(self, starts, **kwargs):
|
|
296
325
|
"""
|
|
297
326
|
Filter the ancestors of the given start node or nodes that match various criteria.
|
|
@@ -16,9 +16,8 @@ from ...storage.memory_mixins.paged_memory.pages.multi_values import MultiValues
|
|
|
16
16
|
from ...knowledge_plugins.key_definitions.atoms import Atom, Register, Tmp, MemoryLocation
|
|
17
17
|
from ...knowledge_plugins.key_definitions.constants import OP_BEFORE, OP_AFTER
|
|
18
18
|
from ...knowledge_plugins.key_definitions.live_definitions import Definition, LiveDefinitions
|
|
19
|
-
from ...code_location import CodeLocation
|
|
19
|
+
from ...code_location import CodeLocation, ExternalCodeLocation
|
|
20
20
|
from .subject import SubjectType
|
|
21
|
-
from .external_codeloc import ExternalCodeLocation
|
|
22
21
|
from .rd_state import ReachingDefinitionsState
|
|
23
22
|
from .function_handler import FunctionHandler, FunctionCallData
|
|
24
23
|
|
|
@@ -415,7 +414,7 @@ class SimEngineRDAIL(
|
|
|
415
414
|
|
|
416
415
|
# first check if it is ever defined
|
|
417
416
|
try:
|
|
418
|
-
value: MultiValues = self.state.
|
|
417
|
+
value: MultiValues = self.state.registers.load(reg_offset, size=size)
|
|
419
418
|
except SimMemoryMissingError as ex:
|
|
420
419
|
# the full value does not exist, but we handle partial existence, too
|
|
421
420
|
missing_defs = None
|
|
@@ -424,7 +423,7 @@ class SimEngineRDAIL(
|
|
|
424
423
|
i = 0
|
|
425
424
|
while i < size:
|
|
426
425
|
try:
|
|
427
|
-
value: MultiValues = self.state.
|
|
426
|
+
value: MultiValues = self.state.registers.load(reg_offset + i, size=1)
|
|
428
427
|
except SimMemoryMissingError as ex_:
|
|
429
428
|
i += ex_.missing_size
|
|
430
429
|
continue
|
|
@@ -498,7 +497,7 @@ class SimEngineRDAIL(
|
|
|
498
497
|
# a concrete address
|
|
499
498
|
concrete_addr: int = addr._model_concrete.value
|
|
500
499
|
try:
|
|
501
|
-
vs: MultiValues = self.state.
|
|
500
|
+
vs: MultiValues = self.state.memory.load(concrete_addr, size=size, endness=expr.endness)
|
|
502
501
|
defs = set(LiveDefinitions.extract_defs_from_mv(vs))
|
|
503
502
|
except SimMemoryMissingError:
|
|
504
503
|
continue
|
|
@@ -510,7 +509,7 @@ class SimEngineRDAIL(
|
|
|
510
509
|
if stack_offset is not None:
|
|
511
510
|
stack_addr = self.state.live_definitions.stack_offset_to_stack_addr(stack_offset)
|
|
512
511
|
try:
|
|
513
|
-
vs: MultiValues = self.state.
|
|
512
|
+
vs: MultiValues = self.state.stack.load(stack_addr, size=size, endness=expr.endness)
|
|
514
513
|
defs = set(LiveDefinitions.extract_defs_from_mv(vs))
|
|
515
514
|
except SimMemoryMissingError:
|
|
516
515
|
continue
|
|
@@ -16,9 +16,8 @@ from ...knowledge_plugins.key_definitions.tag import LocalVariableTag, Parameter
|
|
|
16
16
|
from ...knowledge_plugins.key_definitions.atoms import Atom, Register, MemoryLocation, Tmp
|
|
17
17
|
from ...knowledge_plugins.key_definitions.constants import OP_BEFORE, OP_AFTER
|
|
18
18
|
from ...knowledge_plugins.key_definitions.heap_address import HeapAddress
|
|
19
|
-
from ...code_location import CodeLocation
|
|
19
|
+
from ...code_location import CodeLocation, ExternalCodeLocation
|
|
20
20
|
from .rd_state import ReachingDefinitionsState
|
|
21
|
-
from .external_codeloc import ExternalCodeLocation
|
|
22
21
|
from .function_handler import FunctionCallData
|
|
23
22
|
|
|
24
23
|
if TYPE_CHECKING:
|
|
@@ -350,7 +349,7 @@ class SimEngineRDVEX(
|
|
|
350
349
|
|
|
351
350
|
reg_atom = Register(reg_offset, size, self.arch)
|
|
352
351
|
try:
|
|
353
|
-
values: MultiValues = self.state.
|
|
352
|
+
values: MultiValues = self.state.registers.load(reg_offset, size=size)
|
|
354
353
|
except SimMemoryMissingError:
|
|
355
354
|
top = self.state.top(size * self.arch.byte_width)
|
|
356
355
|
# annotate it
|
|
@@ -416,7 +415,7 @@ class SimEngineRDVEX(
|
|
|
416
415
|
loaded_stack_offsets.add(stack_offset)
|
|
417
416
|
stack_addr = self.state.live_definitions.stack_offset_to_stack_addr(stack_offset)
|
|
418
417
|
try:
|
|
419
|
-
vs: MultiValues = self.state.
|
|
418
|
+
vs: MultiValues = self.state.stack.load(stack_addr, size=size, endness=endness)
|
|
420
419
|
# extract definitions
|
|
421
420
|
defs = set(LiveDefinitions.extract_defs_from_mv(vs))
|
|
422
421
|
except SimMemoryMissingError:
|
|
@@ -429,7 +428,7 @@ class SimEngineRDVEX(
|
|
|
429
428
|
# Load data from the heap
|
|
430
429
|
heap_offset = self.state.get_heap_offset(addr)
|
|
431
430
|
try:
|
|
432
|
-
vs: MultiValues = self.state.
|
|
431
|
+
vs: MultiValues = self.state.heap.load(heap_offset, size=size, endness=endness)
|
|
433
432
|
defs = set(LiveDefinitions.extract_defs_from_mv(vs))
|
|
434
433
|
except SimMemoryMissingError:
|
|
435
434
|
continue
|
|
@@ -442,7 +441,7 @@ class SimEngineRDVEX(
|
|
|
442
441
|
|
|
443
442
|
# Load data from a global region
|
|
444
443
|
try:
|
|
445
|
-
vs: MultiValues = self.state.
|
|
444
|
+
vs: MultiValues = self.state.memory.load(addr_v, size=size, endness=endness)
|
|
446
445
|
defs = set(LiveDefinitions.extract_defs_from_mv(vs))
|
|
447
446
|
except SimMemoryMissingError:
|
|
448
447
|
try:
|
|
@@ -457,7 +456,7 @@ class SimEngineRDVEX(
|
|
|
457
456
|
v = self.state.annotate_with_def(claripy.BVV(val, size * self.arch.byte_width), missing_def)
|
|
458
457
|
vs = MultiValues(v)
|
|
459
458
|
if not section or section.is_writable:
|
|
460
|
-
self.state.
|
|
459
|
+
self.state.memory.store(addr_v, vs, size=size, endness=endness)
|
|
461
460
|
self.state.all_definitions.add(missing_def)
|
|
462
461
|
defs = {missing_def}
|
|
463
462
|
except KeyError:
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from typing import Tuple
|
|
2
|
-
|
|
3
|
-
from ...code_location import CodeLocation
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class ExternalCodeLocation(CodeLocation):
|
|
7
|
-
"""
|
|
8
|
-
Stands for a program point that originates from outside an analysis' scope.
|
|
9
|
-
i.e. a value loaded from rdi in a callee where the caller has not been analyzed.
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
__slots__ = ("call_string",)
|
|
13
|
-
|
|
14
|
-
def __init__(self, call_string: Tuple[int, ...] = None):
|
|
15
|
-
super().__init__(0, None)
|
|
16
|
-
self.call_string = call_string if call_string is not None else ()
|
|
17
|
-
|
|
18
|
-
def __repr__(self):
|
|
19
|
-
return f"[External {[hex(x) if isinstance(x, int) else x for x in self.call_string]}]"
|
|
20
|
-
|
|
21
|
-
def __hash__(self):
|
|
22
|
-
"""
|
|
23
|
-
returns the hash value of self.
|
|
24
|
-
"""
|
|
25
|
-
if self._hash is None:
|
|
26
|
-
self._hash = hash((self.call_string,))
|
|
27
|
-
return self._hash
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, List, Set, Optional, Union
|
|
1
|
+
from typing import TYPE_CHECKING, Iterable, List, Set, Optional, Union, Callable
|
|
2
2
|
from dataclasses import dataclass, field
|
|
3
3
|
import logging
|
|
4
|
+
from functools import wraps
|
|
4
5
|
from cle import Symbol
|
|
5
6
|
from cle.backends import ELF
|
|
7
|
+
import claripy
|
|
6
8
|
|
|
7
9
|
from angr.storage.memory_mixins.paged_memory.pages.multi_values import MultiValues
|
|
8
10
|
from angr.sim_type import SimTypeBottom
|
|
@@ -13,15 +15,35 @@ from angr.sim_type import SimTypeFunction
|
|
|
13
15
|
from angr.knowledge_plugins.key_definitions.definition import Definition
|
|
14
16
|
from angr.knowledge_plugins.functions import Function
|
|
15
17
|
from angr.analyses.reaching_definitions.dep_graph import FunctionCallRelationships
|
|
16
|
-
from angr.code_location import CodeLocation
|
|
18
|
+
from angr.code_location import CodeLocation, ExternalCodeLocation
|
|
19
|
+
from angr.knowledge_plugins.key_definitions.constants import ObservationPointType
|
|
20
|
+
|
|
17
21
|
|
|
18
22
|
if TYPE_CHECKING:
|
|
23
|
+
from angr.knowledge_plugins.key_definitions.rd_model import ReachingDefinitionsModel
|
|
19
24
|
from angr.analyses.reaching_definitions.rd_state import ReachingDefinitionsState
|
|
20
25
|
from angr.analyses.reaching_definitions.reaching_definitions import ReachingDefinitionsAnalysis
|
|
21
26
|
|
|
22
27
|
l = logging.getLogger(__name__)
|
|
23
28
|
|
|
24
29
|
|
|
30
|
+
def get_exit_livedefinitions(func: Function, rda_model: "ReachingDefinitionsModel"):
|
|
31
|
+
"""
|
|
32
|
+
Get LiveDefinitions at all exits of a function, merge them, and return.
|
|
33
|
+
"""
|
|
34
|
+
lds = []
|
|
35
|
+
for block in func.endpoints:
|
|
36
|
+
ld = rda_model.get_observation_by_node(block.addr, ObservationPointType.OP_AFTER)
|
|
37
|
+
if ld is None:
|
|
38
|
+
continue
|
|
39
|
+
lds.append(ld)
|
|
40
|
+
if len(lds) == 1:
|
|
41
|
+
return lds[0]
|
|
42
|
+
if len(lds) == 0:
|
|
43
|
+
return None
|
|
44
|
+
return lds[0].merge(*lds[1:])[0]
|
|
45
|
+
|
|
46
|
+
|
|
25
47
|
@dataclass
|
|
26
48
|
class FunctionEffect:
|
|
27
49
|
"""
|
|
@@ -113,9 +135,9 @@ class FunctionCallData:
|
|
|
113
135
|
|
|
114
136
|
def depends(
|
|
115
137
|
self,
|
|
116
|
-
dest:
|
|
117
|
-
*sources: Atom,
|
|
118
|
-
value:
|
|
138
|
+
dest: Union[Atom, Iterable[Atom], None],
|
|
139
|
+
*sources: Union[Atom, Iterable[Atom]],
|
|
140
|
+
value: Union[MultiValues, claripy.ast.BV, bytes, int, None] = None,
|
|
119
141
|
apply_at_callsite: bool = False,
|
|
120
142
|
tags: Optional[Set[Tag]] = None,
|
|
121
143
|
):
|
|
@@ -129,6 +151,22 @@ class FunctionCallData:
|
|
|
129
151
|
|
|
130
152
|
The atom being modified may be None to mark uses of the source atoms which do not have any explicit sinks.
|
|
131
153
|
"""
|
|
154
|
+
if dest is None and value is not None:
|
|
155
|
+
raise TypeError("Cannot provide value without a destination to write it to")
|
|
156
|
+
|
|
157
|
+
if dest is not None and not isinstance(dest, Atom):
|
|
158
|
+
for dest2 in dest:
|
|
159
|
+
self.depends(dest2, *sources, value=value, apply_at_callsite=apply_at_callsite, tags=tags)
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
if isinstance(value, int):
|
|
163
|
+
assert dest is not None
|
|
164
|
+
value = claripy.BVV(value, dest.size * 8)
|
|
165
|
+
elif isinstance(value, bytes):
|
|
166
|
+
value = claripy.BVV(value)
|
|
167
|
+
if isinstance(value, claripy.ast.BV):
|
|
168
|
+
value = MultiValues(value)
|
|
169
|
+
assert value is None or isinstance(value, MultiValues)
|
|
132
170
|
if dest is not None and self.has_clobbered(dest):
|
|
133
171
|
l.warning(
|
|
134
172
|
"Function handler for %s seems to be implemented incorrectly - "
|
|
@@ -139,13 +177,76 @@ class FunctionCallData:
|
|
|
139
177
|
self.effects.append(
|
|
140
178
|
FunctionEffect(
|
|
141
179
|
dest,
|
|
142
|
-
set(sources),
|
|
180
|
+
set().union(*({src} if isinstance(src, Atom) else set(src) for src in sources)),
|
|
143
181
|
value=value,
|
|
144
182
|
apply_at_callsite=apply_at_callsite,
|
|
145
183
|
tags=tags,
|
|
146
184
|
)
|
|
147
185
|
)
|
|
148
186
|
|
|
187
|
+
def reset_prototype(self, prototype: SimTypeFunction, state: "ReachingDefinitionsState") -> Set[Atom]:
|
|
188
|
+
self.prototype = prototype.with_arch(state.arch)
|
|
189
|
+
|
|
190
|
+
args_atoms_from_values = set()
|
|
191
|
+
if self.args_atoms is None and self.args_values is not None:
|
|
192
|
+
self.args_atoms = [
|
|
193
|
+
set().union(
|
|
194
|
+
*({defn.atom for defn in state.extract_defs(value)} for values in mv.values() for value in values)
|
|
195
|
+
)
|
|
196
|
+
for mv in self.args_values
|
|
197
|
+
]
|
|
198
|
+
for atoms_set in self.args_atoms:
|
|
199
|
+
args_atoms_from_values |= atoms_set
|
|
200
|
+
elif self.args_atoms is None and self.cc is not None and self.prototype is not None:
|
|
201
|
+
self.args_atoms = FunctionHandler.c_args_as_atoms(state, self.cc, self.prototype)
|
|
202
|
+
if self.ret_atoms is None and self.cc is not None and self.prototype is not None:
|
|
203
|
+
if self.prototype.returnty is not None:
|
|
204
|
+
self.ret_atoms = FunctionHandler.c_return_as_atoms(state, self.cc, self.prototype)
|
|
205
|
+
return args_atoms_from_values
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class FunctionCallDataUnwrapped(FunctionCallData):
|
|
209
|
+
"""
|
|
210
|
+
A subclass of FunctionCallData which asserts that many of its members are non-None at construction time.
|
|
211
|
+
Typechecks be gone!
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
address_multi: MultiValues
|
|
215
|
+
address: int
|
|
216
|
+
symbol: Symbol
|
|
217
|
+
function: Function
|
|
218
|
+
name: str
|
|
219
|
+
cc: SimCC
|
|
220
|
+
prototype: SimTypeFunction
|
|
221
|
+
args_atoms: List[Set[Atom]]
|
|
222
|
+
args_values: List[MultiValues]
|
|
223
|
+
ret_atoms: Set[Atom]
|
|
224
|
+
|
|
225
|
+
def __init__(self, inner: FunctionCallData):
|
|
226
|
+
d = dict(inner.__dict__)
|
|
227
|
+
annotations = type(self).__annotations__ # pylint: disable=no-member
|
|
228
|
+
for k, v in d.items():
|
|
229
|
+
assert v is not None or k not in annotations, (
|
|
230
|
+
"Failed to unwrap field %s - this function is more complicated than you're ready for!" % k
|
|
231
|
+
)
|
|
232
|
+
assert v is not None, "Members of FunctionCallDataUnwrapped may not be None"
|
|
233
|
+
super().__init__(**d)
|
|
234
|
+
|
|
235
|
+
@staticmethod
|
|
236
|
+
@wraps
|
|
237
|
+
def decorate(
|
|
238
|
+
f: Callable[["FunctionHandler", "ReachingDefinitionsState", "FunctionCallDataUnwrapped"], None]
|
|
239
|
+
) -> Callable[["FunctionHandler", "ReachingDefinitionsState", FunctionCallData], None]:
|
|
240
|
+
"""
|
|
241
|
+
Decorate a function handler method with this to make it take a FunctionCallDataUnwrapped instead of a
|
|
242
|
+
FunctionCallData.
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
def inner(self: "FunctionHandler", state: "ReachingDefinitionsState", data: FunctionCallData):
|
|
246
|
+
f(self, state, FunctionCallDataUnwrapped(data))
|
|
247
|
+
|
|
248
|
+
return inner
|
|
249
|
+
|
|
149
250
|
|
|
150
251
|
# pylint: disable=unused-argument, no-self-use
|
|
151
252
|
class FunctionHandler:
|
|
@@ -153,6 +254,9 @@ class FunctionHandler:
|
|
|
153
254
|
A mechanism for summarizing a function call's effect on a program for ReachingDefinitionsAnalysis.
|
|
154
255
|
"""
|
|
155
256
|
|
|
257
|
+
def __init__(self, interfunction_level: int = 0):
|
|
258
|
+
self.interfunction_level: int = interfunction_level
|
|
259
|
+
|
|
156
260
|
def hook(self, analysis: "ReachingDefinitionsAnalysis") -> "FunctionHandler":
|
|
157
261
|
"""
|
|
158
262
|
Attach this instance of the function handler to an instance of RDA.
|
|
@@ -249,21 +353,7 @@ class FunctionHandler:
|
|
|
249
353
|
data.prototype = state.analysis.project.factory.function_prototype()
|
|
250
354
|
data.guessed_prototype = True
|
|
251
355
|
|
|
252
|
-
args_atoms_from_values =
|
|
253
|
-
if data.args_atoms is None and data.args_values is not None:
|
|
254
|
-
data.args_atoms = [
|
|
255
|
-
set().union(
|
|
256
|
-
*({defn.atom for defn in state.extract_defs(value)} for values in mv.values() for value in values)
|
|
257
|
-
)
|
|
258
|
-
for mv in data.args_values
|
|
259
|
-
]
|
|
260
|
-
for atoms_set in data.args_atoms:
|
|
261
|
-
args_atoms_from_values |= atoms_set
|
|
262
|
-
elif data.args_atoms is None and data.cc is not None and data.prototype is not None:
|
|
263
|
-
data.args_atoms = self.c_args_as_atoms(state, data.cc, data.prototype)
|
|
264
|
-
if data.ret_atoms is None and data.cc is not None and data.prototype is not None:
|
|
265
|
-
if data.prototype.returnty is not None:
|
|
266
|
-
data.ret_atoms = self.c_return_as_atoms(state, data.cc, data.prototype)
|
|
356
|
+
args_atoms_from_values = data.reset_prototype(data.prototype, state)
|
|
267
357
|
|
|
268
358
|
# PROCESS
|
|
269
359
|
state.move_codelocs(data.function_codeloc)
|
|
@@ -316,6 +406,8 @@ class FunctionHandler:
|
|
|
316
406
|
for effect in data.effects:
|
|
317
407
|
if effect.sources_defns is None and effect.sources:
|
|
318
408
|
effect.sources_defns = set().union(*(set(state.get_definitions(atom)) for atom in effect.sources))
|
|
409
|
+
if not effect.sources_defns:
|
|
410
|
+
effect.sources_defns = {Definition(atom, ExternalCodeLocation()) for atom in effect.sources}
|
|
319
411
|
other_input_defns |= effect.sources_defns - all_args_defns
|
|
320
412
|
# apply the effects, with the ones marked with apply_at_callsite=False applied first
|
|
321
413
|
for effect in sorted(data.effects, key=lambda effect: effect.apply_at_callsite):
|
|
@@ -336,7 +428,7 @@ class FunctionHandler:
|
|
|
336
428
|
data.ret_values_deps = effect.sources_defns
|
|
337
429
|
else:
|
|
338
430
|
# mark definition
|
|
339
|
-
|
|
431
|
+
_, defs = state.kill_and_add_definition(
|
|
340
432
|
effect.dest,
|
|
341
433
|
value,
|
|
342
434
|
endness=None,
|
|
@@ -389,11 +481,41 @@ class FunctionHandler:
|
|
|
389
481
|
self.handle_generic_function(state, data)
|
|
390
482
|
|
|
391
483
|
def handle_local_function(self, state: "ReachingDefinitionsState", data: FunctionCallData) -> None:
|
|
392
|
-
self.
|
|
484
|
+
if self.interfunction_level > 0 and data.function is not None and state.analysis is not None:
|
|
485
|
+
self.interfunction_level -= 1
|
|
486
|
+
try:
|
|
487
|
+
self.recurse_analysis(state, data)
|
|
488
|
+
finally:
|
|
489
|
+
self.interfunction_level += 1
|
|
490
|
+
else:
|
|
491
|
+
self.handle_generic_function(state, data)
|
|
393
492
|
|
|
394
493
|
def handle_external_function(self, state: "ReachingDefinitionsState", data: FunctionCallData) -> None:
|
|
395
494
|
self.handle_generic_function(state, data)
|
|
396
495
|
|
|
496
|
+
def recurse_analysis(self, state: "ReachingDefinitionsState", data: FunctionCallData) -> None:
|
|
497
|
+
"""
|
|
498
|
+
Precondition: ``data.function`` MUST NOT BE NONE in order to call this method.
|
|
499
|
+
"""
|
|
500
|
+
assert state.analysis is not None
|
|
501
|
+
assert data.function is not None
|
|
502
|
+
sub_rda = state.analysis.project.analyses.ReachingDefinitions(
|
|
503
|
+
data.function,
|
|
504
|
+
observe_all=state.analysis._observe_all,
|
|
505
|
+
observation_points=state.analysis._observation_points,
|
|
506
|
+
observe_callback=state.analysis._observe_callback,
|
|
507
|
+
dep_graph=state.dep_graph,
|
|
508
|
+
function_handler=self,
|
|
509
|
+
init_state=state,
|
|
510
|
+
)
|
|
511
|
+
# migrate data from sub_rda to its parent
|
|
512
|
+
state.analysis.function_calls.update(sub_rda.function_calls)
|
|
513
|
+
state.analysis.model.observed_results.update(sub_rda.model.observed_results)
|
|
514
|
+
|
|
515
|
+
sub_ld = get_exit_livedefinitions(data.function, sub_rda.model)
|
|
516
|
+
if sub_ld is not None:
|
|
517
|
+
state.live_definitions = sub_ld
|
|
518
|
+
|
|
397
519
|
@staticmethod
|
|
398
520
|
def c_args_as_atoms(state: "ReachingDefinitionsState", cc: SimCC, prototype: SimTypeFunction) -> List[Set[Atom]]:
|
|
399
521
|
if not prototype.variadic:
|