angr 9.2.174__cp310-abi3-macosx_11_0_arm64.whl → 9.2.176__cp310-abi3-macosx_11_0_arm64.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/__main__.py +32 -2
- angr/analyses/calling_convention/calling_convention.py +12 -0
- angr/analyses/cfg/cfg_base.py +1 -1
- angr/analyses/cfg/cfg_fast.py +27 -8
- angr/analyses/complete_calling_conventions.py +39 -26
- angr/analyses/decompiler/ail_simplifier.py +13 -11
- angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +5 -1
- angr/analyses/decompiler/clinic.py +54 -40
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +3 -3
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +2 -2
- angr/analyses/decompiler/peephole_optimizations/__init__.py +4 -4
- angr/analyses/decompiler/peephole_optimizations/{inlined_wstrcpy.py → inlined_wcscpy.py} +16 -8
- angr/analyses/decompiler/peephole_optimizations/{inlined_wstrcpy_consolidation.py → inlined_wcscpy_consolidation.py} +13 -13
- angr/analyses/decompiler/ssailification/rewriting_engine.py +14 -1
- angr/analyses/decompiler/structured_codegen/c.py +6 -5
- angr/analyses/decompiler/structuring/dream.py +2 -2
- angr/analyses/decompiler/structuring/phoenix.py +101 -23
- angr/analyses/decompiler/utils.py +1 -1
- angr/analyses/smc.py +1 -1
- angr/analyses/stack_pointer_tracker.py +4 -3
- angr/analyses/typehoon/lifter.py +29 -18
- angr/analyses/typehoon/simple_solver.py +157 -50
- angr/analyses/typehoon/translator.py +34 -34
- angr/analyses/typehoon/typeconsts.py +33 -15
- angr/analyses/typehoon/typevars.py +9 -2
- angr/analyses/variable_recovery/engine_ail.py +4 -2
- angr/analyses/variable_recovery/engine_base.py +4 -1
- angr/analyses/variable_recovery/variable_recovery_fast.py +3 -1
- angr/calling_conventions.py +2 -1
- angr/engines/icicle.py +4 -4
- angr/engines/vex/claripy/ccall.py +3 -3
- angr/knowledge_plugins/functions/function.py +18 -1
- angr/misc/bug_report.py +11 -2
- angr/procedures/definitions/__init__.py +88 -20
- angr/procedures/definitions/common/glibc.json +3516 -0
- angr/procedures/definitions/parse_glibc.py +78 -0
- angr/procedures/libc/fgets.py +2 -1
- angr/procedures/posix/pthread.py +4 -4
- angr/procedures/stubs/format_parser.py +3 -3
- angr/rustylib.abi3.so +0 -0
- angr/sim_type.py +73 -11
- angr/simos/windows.py +1 -1
- angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +1 -1
- angr/unicornlib.dylib +0 -0
- angr/utils/constants.py +1 -1
- angr/utils/library.py +1 -0
- angr/utils/strings.py +20 -0
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/METADATA +5 -5
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/RECORD +54 -52
- angr/procedures/definitions/glibc.py +0 -8372
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/WHEEL +0 -0
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/entry_points.txt +0 -0
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.174.dist-info → angr-9.2.176.dist-info}/top_level.txt +0 -0
|
@@ -46,8 +46,8 @@ from .rol_ror import RolRorRewriter
|
|
|
46
46
|
from .inlined_memcpy import InlinedMemcpy
|
|
47
47
|
from .inlined_strcpy import InlinedStrcpy
|
|
48
48
|
from .inlined_strcpy_consolidation import InlinedStrcpyConsolidation
|
|
49
|
-
from .
|
|
50
|
-
from .
|
|
49
|
+
from .inlined_wcscpy import InlinedWcscpy
|
|
50
|
+
from .inlined_wcscpy_consolidation import InlinedWcscpyConsolidation
|
|
51
51
|
from .cmpord_rewriter import CmpORDRewriter
|
|
52
52
|
from .coalesce_adjacent_shrs import CoalesceAdjacentShiftRights
|
|
53
53
|
from .a_mul_const_sub_a import AMulConstSubA
|
|
@@ -104,8 +104,8 @@ ALL_PEEPHOLE_OPTS: list[type[PeepholeOptimizationExprBase]] = [
|
|
|
104
104
|
InlinedMemcpy,
|
|
105
105
|
InlinedStrcpy,
|
|
106
106
|
InlinedStrcpyConsolidation,
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
InlinedWcscpy,
|
|
108
|
+
InlinedWcscpyConsolidation,
|
|
109
109
|
CmpORDRewriter,
|
|
110
110
|
CoalesceAdjacentShiftRights,
|
|
111
111
|
ShlToMul,
|
|
@@ -17,17 +17,20 @@ ASCII_PRINTABLES = {ord(x) for x in string.printable}
|
|
|
17
17
|
ASCII_DIGITS = {ord(x) for x in string.digits}
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class
|
|
20
|
+
class InlinedWcscpy(PeepholeOptimizationStmtBase):
|
|
21
21
|
"""
|
|
22
|
-
Simplifies inlined wide string copying logic into calls to
|
|
22
|
+
Simplifies inlined wide string copying logic into calls to wcscpy.
|
|
23
23
|
"""
|
|
24
24
|
|
|
25
25
|
__slots__ = ()
|
|
26
26
|
|
|
27
|
-
NAME = "Simplifying inlined
|
|
27
|
+
NAME = "Simplifying inlined wcscpy"
|
|
28
28
|
stmt_classes = (Assignment, Store)
|
|
29
29
|
|
|
30
30
|
def optimize(self, stmt: Assignment | Store, stmt_idx: int | None = None, block=None, **kwargs):
|
|
31
|
+
assert self.project is not None
|
|
32
|
+
assert self.kb is not None
|
|
33
|
+
|
|
31
34
|
if (
|
|
32
35
|
isinstance(stmt, Assignment)
|
|
33
36
|
and isinstance(stmt.dst, VirtualVariable)
|
|
@@ -48,11 +51,12 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
48
51
|
r, s = self.is_integer_likely_a_wide_string(value, value_size, self.project.arch.memory_endness)
|
|
49
52
|
if r:
|
|
50
53
|
# replace it with a call to strncpy
|
|
54
|
+
assert s is not None
|
|
51
55
|
str_id = self.kb.custom_strings.allocate(s)
|
|
52
56
|
wstr_type = SimTypePointer(SimTypeWideChar()).with_arch(self.project.arch)
|
|
53
57
|
return Call(
|
|
54
58
|
stmt.idx,
|
|
55
|
-
"
|
|
59
|
+
"wcsncpy",
|
|
56
60
|
args=[
|
|
57
61
|
dst,
|
|
58
62
|
Const(None, None, str_id, self.project.arch.bits, custom_string=True, type=wstr_type),
|
|
@@ -83,6 +87,7 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
83
87
|
integer, size = self.stride_to_int(stride)
|
|
84
88
|
r, s = self.is_integer_likely_a_wide_string(integer, size, Endness.BE, min_length=3)
|
|
85
89
|
if r:
|
|
90
|
+
assert s is not None
|
|
86
91
|
# we remove all involved statements whose statement IDs are greater than the current one
|
|
87
92
|
for _, stmt_idx_, _ in reversed(stride):
|
|
88
93
|
if stmt_idx_ <= stmt_idx:
|
|
@@ -94,7 +99,7 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
94
99
|
wstr_type = SimTypePointer(SimTypeWideChar()).with_arch(self.project.arch)
|
|
95
100
|
return Call(
|
|
96
101
|
stmt.idx,
|
|
97
|
-
"
|
|
102
|
+
"wcsncpy",
|
|
98
103
|
args=[
|
|
99
104
|
dst,
|
|
100
105
|
Const(None, None, str_id, self.project.arch.bits, custom_string=True, type=wstr_type),
|
|
@@ -112,11 +117,14 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
112
117
|
size = 0
|
|
113
118
|
for _, _, v in stride:
|
|
114
119
|
size += v.size
|
|
120
|
+
assert isinstance(v.value, int)
|
|
115
121
|
n <<= v.bits
|
|
116
122
|
n |= v.value
|
|
117
123
|
return n, size
|
|
118
124
|
|
|
119
125
|
def collect_constant_stores(self, block, starting_stmt_idx: int) -> dict[int, tuple[int, Const | None]]:
|
|
126
|
+
assert self.project is not None
|
|
127
|
+
|
|
120
128
|
r = {}
|
|
121
129
|
expected_store_varid: int | None = None
|
|
122
130
|
starting_stmt = block.statements[starting_stmt_idx]
|
|
@@ -224,7 +232,7 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
224
232
|
# unsupported endness
|
|
225
233
|
return False, None
|
|
226
234
|
|
|
227
|
-
if not (
|
|
235
|
+
if not (InlinedWcscpy.even_offsets_are_zero(chars) or InlinedWcscpy.odd_offsets_are_zero(chars)):
|
|
228
236
|
return False, None
|
|
229
237
|
|
|
230
238
|
if chars and len(chars) >= 2 and chars[-1] == 0 and chars[-2] == 0:
|
|
@@ -236,11 +244,11 @@ class InlinedWstrcpy(PeepholeOptimizationStmtBase):
|
|
|
236
244
|
return False, None
|
|
237
245
|
|
|
238
246
|
@staticmethod
|
|
239
|
-
def
|
|
247
|
+
def is_inlined_wcsncpy(stmt: Statement) -> bool:
|
|
240
248
|
return (
|
|
241
249
|
isinstance(stmt, Call)
|
|
242
250
|
and isinstance(stmt.target, str)
|
|
243
|
-
and stmt.target == "
|
|
251
|
+
and stmt.target == "wcsncpy"
|
|
244
252
|
and stmt.args is not None
|
|
245
253
|
and len(stmt.args) == 3
|
|
246
254
|
and isinstance(stmt.args[1], Const)
|
|
@@ -6,31 +6,31 @@ from angr.ailment.statement import Call, Store
|
|
|
6
6
|
|
|
7
7
|
from angr.sim_type import SimTypePointer, SimTypeWideChar
|
|
8
8
|
from .base import PeepholeOptimizationMultiStmtBase
|
|
9
|
-
from .
|
|
9
|
+
from .inlined_wcscpy import InlinedWcscpy
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class
|
|
12
|
+
class InlinedWcscpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
13
13
|
"""
|
|
14
|
-
Consolidate multiple inlined
|
|
14
|
+
Consolidate multiple inlined wcscpy/wcsncpy calls.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
__slots__ = ()
|
|
18
18
|
|
|
19
|
-
NAME = "Consolidate multiple inlined
|
|
19
|
+
NAME = "Consolidate multiple inlined wcsncpy calls"
|
|
20
20
|
stmt_classes = ((Call, Call), (Call, Store))
|
|
21
21
|
|
|
22
22
|
def optimize( # type:ignore
|
|
23
23
|
self, stmts: list[Call], stmt_idx: int | None = None, block=None, **kwargs
|
|
24
24
|
): # pylint:disable=unused-argument
|
|
25
25
|
last_stmt, stmt = stmts
|
|
26
|
-
if
|
|
26
|
+
if InlinedWcscpy.is_inlined_wcsncpy(last_stmt):
|
|
27
27
|
assert last_stmt.args is not None
|
|
28
28
|
assert self.kb is not None
|
|
29
29
|
s_last: bytes = self.kb.custom_strings[last_stmt.args[1].value]
|
|
30
30
|
addr_last = last_stmt.args[0]
|
|
31
31
|
new_str = None # will be set if consolidation should happen
|
|
32
32
|
|
|
33
|
-
if isinstance(stmt, Call) and
|
|
33
|
+
if isinstance(stmt, Call) and InlinedWcscpy.is_inlined_wcsncpy(stmt):
|
|
34
34
|
assert stmt.args is not None
|
|
35
35
|
# consolidating two calls
|
|
36
36
|
s_curr: bytes = self.kb.custom_strings[stmt.args[1].value]
|
|
@@ -50,7 +50,7 @@ class InlinedWstrcpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
|
50
50
|
# it's probably the terminating null byte
|
|
51
51
|
r, s = True, b"\x00\x00"
|
|
52
52
|
else:
|
|
53
|
-
r, s =
|
|
53
|
+
r, s = InlinedWcscpy.is_integer_likely_a_wide_string(
|
|
54
54
|
stmt.data.value, stmt.size, stmt.endness, min_length=1 # type:ignore
|
|
55
55
|
)
|
|
56
56
|
if r and s is not None:
|
|
@@ -60,7 +60,7 @@ class InlinedWstrcpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
|
60
60
|
assert self.project is not None
|
|
61
61
|
wstr_type = SimTypePointer(SimTypeWideChar()).with_arch(self.project.arch)
|
|
62
62
|
if new_str.endswith(b"\x00\x00"):
|
|
63
|
-
call_name = "
|
|
63
|
+
call_name = "wcsncpy"
|
|
64
64
|
new_str_idx = self.kb.custom_strings.allocate(new_str[:-2])
|
|
65
65
|
args = [
|
|
66
66
|
last_stmt.args[0],
|
|
@@ -68,7 +68,7 @@ class InlinedWstrcpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
|
68
68
|
]
|
|
69
69
|
prototype = None
|
|
70
70
|
else:
|
|
71
|
-
call_name = "
|
|
71
|
+
call_name = "wcsncpy"
|
|
72
72
|
new_str_idx = self.kb.custom_strings.allocate(new_str)
|
|
73
73
|
args = [
|
|
74
74
|
last_stmt.args[0],
|
|
@@ -96,18 +96,18 @@ class InlinedWstrcpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
|
96
96
|
return StackBaseOffset(None, addr.bits, 0), addr.operand.stack_offset
|
|
97
97
|
if isinstance(addr, BinaryOp):
|
|
98
98
|
if addr.op == "Add" and isinstance(addr.operands[1], Const) and isinstance(addr.operands[1].value, int):
|
|
99
|
-
base_0, offset_0 =
|
|
99
|
+
base_0, offset_0 = InlinedWcscpyConsolidation._parse_addr(addr.operands[0])
|
|
100
100
|
return base_0, offset_0 + addr.operands[1].value
|
|
101
101
|
if addr.op == "Sub" and isinstance(addr.operands[1], Const) and isinstance(addr.operands[1].value, int):
|
|
102
|
-
base_0, offset_0 =
|
|
102
|
+
base_0, offset_0 = InlinedWcscpyConsolidation._parse_addr(addr.operands[0])
|
|
103
103
|
return base_0, offset_0 - addr.operands[1].value
|
|
104
104
|
|
|
105
105
|
return addr, 0
|
|
106
106
|
|
|
107
107
|
@staticmethod
|
|
108
108
|
def _get_delta(addr_0: Expression, addr_1: Expression) -> int | None:
|
|
109
|
-
base_0, offset_0 =
|
|
110
|
-
base_1, offset_1 =
|
|
109
|
+
base_0, offset_0 = InlinedWcscpyConsolidation._parse_addr(addr_0)
|
|
110
|
+
base_1, offset_1 = InlinedWcscpyConsolidation._parse_addr(addr_1)
|
|
111
111
|
if base_0.likes(base_1):
|
|
112
112
|
return offset_1 - offset_0
|
|
113
113
|
return None
|
|
@@ -97,6 +97,19 @@ class SimEngineSSARewriting(
|
|
|
97
97
|
self._current_vvar_id += 1
|
|
98
98
|
return self._current_vvar_id
|
|
99
99
|
|
|
100
|
+
#
|
|
101
|
+
# Util functions
|
|
102
|
+
#
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def _is_head_controlled_loop_jump(block, jump_stmt: ConditionalJump) -> bool:
|
|
106
|
+
concrete_targets = []
|
|
107
|
+
if isinstance(jump_stmt.true_target, Const):
|
|
108
|
+
concrete_targets.append(jump_stmt.true_target.value)
|
|
109
|
+
if isinstance(jump_stmt.false_target, Const):
|
|
110
|
+
concrete_targets.append(jump_stmt.false_target.value)
|
|
111
|
+
return not all(block.addr <= t < block.addr + block.original_size for t in concrete_targets)
|
|
112
|
+
|
|
100
113
|
#
|
|
101
114
|
# Handlers
|
|
102
115
|
#
|
|
@@ -303,7 +316,7 @@ class SimEngineSSARewriting(
|
|
|
303
316
|
new_true_target = self._expr(stmt.true_target) if stmt.true_target is not None else None
|
|
304
317
|
new_false_target = self._expr(stmt.false_target) if stmt.false_target is not None else None
|
|
305
318
|
|
|
306
|
-
if self.stmt_idx != len(self.block.statements) - 1:
|
|
319
|
+
if self.stmt_idx != len(self.block.statements) - 1 and self._is_head_controlled_loop_jump(self.block, stmt):
|
|
307
320
|
# the conditional jump is in the middle of the block (e.g., the block generated from lifting rep stosq).
|
|
308
321
|
# we need to make a copy of the state and use the state of this point in its successor
|
|
309
322
|
self.head_controlled_loop_outstate = self.state.copy()
|
|
@@ -42,6 +42,7 @@ from angr.utils.constants import is_alignment_mask
|
|
|
42
42
|
from angr.utils.library import get_cpp_function_name
|
|
43
43
|
from angr.utils.loader import is_in_readonly_segment, is_in_readonly_section
|
|
44
44
|
from angr.utils.types import unpack_typeref, unpack_pointer_and_array, dereference_simtype_by_lib
|
|
45
|
+
from angr.utils.strings import decode_utf16_string
|
|
45
46
|
from angr.analyses.decompiler.utils import structured_node_is_simple_return
|
|
46
47
|
from angr.analyses.decompiler.notes.deobfuscated_strings import DeobfuscatedStringsNote
|
|
47
48
|
from angr.errors import UnsupportedNodeTypeError, AngrRuntimeError
|
|
@@ -2269,9 +2270,9 @@ class CConstant(CExpression):
|
|
|
2269
2270
|
elif isinstance(self._type, SimTypePointer) and isinstance(self._type.pts_to, SimTypeWideChar):
|
|
2270
2271
|
refval = self.reference_values[self._type]
|
|
2271
2272
|
v = (
|
|
2272
|
-
refval.content
|
|
2273
|
+
decode_utf16_string(refval.content)
|
|
2273
2274
|
if isinstance(refval, MemoryData)
|
|
2274
|
-
else refval
|
|
2275
|
+
else decode_utf16_string(refval)
|
|
2275
2276
|
) # it's a string
|
|
2276
2277
|
yield CConstant.str_to_c_str(v, prefix="L"), self
|
|
2277
2278
|
return
|
|
@@ -4076,9 +4077,9 @@ class MakeTypecastsImplicit(CStructuredCodeWalker):
|
|
|
4076
4077
|
class FieldReferenceCleanup(CStructuredCodeWalker):
|
|
4077
4078
|
def handle_CTypeCast(self, obj):
|
|
4078
4079
|
if isinstance(obj.dst_type, SimTypePointer) and not isinstance(obj.dst_type.pts_to, SimTypeBottom):
|
|
4079
|
-
|
|
4080
|
-
if not isinstance(
|
|
4081
|
-
return self.handle(
|
|
4080
|
+
new_obj = obj.codegen._access_reference(obj.expr, obj.dst_type.pts_to)
|
|
4081
|
+
if not isinstance(new_obj, CTypeCast):
|
|
4082
|
+
return self.handle(new_obj)
|
|
4082
4083
|
return super().handle_CTypeCast(obj)
|
|
4083
4084
|
|
|
4084
4085
|
|
|
@@ -548,7 +548,7 @@ class DreamStructurer(StructurerBase):
|
|
|
548
548
|
cmp = switch_extract_cmp_bounds(last_stmt)
|
|
549
549
|
if not cmp:
|
|
550
550
|
return False
|
|
551
|
-
cmp_expr, cmp_lb,
|
|
551
|
+
cmp_expr, cmp_lb, _cmp_ub = cmp # pylint:disable=unused-variable
|
|
552
552
|
|
|
553
553
|
# the real indirect jump
|
|
554
554
|
if len(addr2nodes[target]) != 1:
|
|
@@ -619,7 +619,7 @@ class DreamStructurer(StructurerBase):
|
|
|
619
619
|
cmp = switch_extract_cmp_bounds(last_stmt)
|
|
620
620
|
if not cmp:
|
|
621
621
|
return False
|
|
622
|
-
cmp_expr, cmp_lb,
|
|
622
|
+
cmp_expr, cmp_lb, _cmp_ub = cmp # pylint:disable=unused-variable
|
|
623
623
|
|
|
624
624
|
jumptable_entries = jump_table.jumptable_entries
|
|
625
625
|
assert jumptable_entries is not None
|
|
@@ -929,23 +929,55 @@ class PhoenixStructurer(StructurerBase):
|
|
|
929
929
|
)
|
|
930
930
|
break_node = Block(last_src_stmt.ins_addr, None, statements=[break_stmt])
|
|
931
931
|
else:
|
|
932
|
-
|
|
933
|
-
None
|
|
934
|
-
Const(None, None, successor.addr, self.project.arch.bits),
|
|
935
|
-
target_idx=successor.idx if isinstance(successor, Block) else None,
|
|
936
|
-
ins_addr=last_src_stmt.ins_addr,
|
|
937
|
-
)
|
|
938
|
-
break_node_inner = Block(last_src_stmt.ins_addr, None, statements=[break_stmt])
|
|
939
|
-
fallthrough_node = next(iter(succ for succ in fullgraph.successors(src) if succ is not dst))
|
|
940
|
-
fallthrough_stmt = Jump(
|
|
941
|
-
None,
|
|
942
|
-
Const(None, None, fallthrough_node.addr, self.project.arch.bits),
|
|
943
|
-
target_idx=successor.idx if isinstance(successor, Block) else None,
|
|
944
|
-
ins_addr=last_src_stmt.ins_addr,
|
|
945
|
-
)
|
|
946
|
-
break_node_inner_fallthrough = Block(
|
|
947
|
-
last_src_stmt.ins_addr, None, statements=[fallthrough_stmt]
|
|
932
|
+
fallthrough_node = next(
|
|
933
|
+
iter(succ for succ in fullgraph.successors(src) if succ is not dst), None
|
|
948
934
|
)
|
|
935
|
+
if fallthrough_node is not None:
|
|
936
|
+
# we create a conditional jump that will be converted to a conditional break later
|
|
937
|
+
break_stmt = Jump(
|
|
938
|
+
None,
|
|
939
|
+
Const(None, None, successor.addr, self.project.arch.bits),
|
|
940
|
+
target_idx=successor.idx if isinstance(successor, Block) else None,
|
|
941
|
+
ins_addr=last_src_stmt.ins_addr,
|
|
942
|
+
)
|
|
943
|
+
break_node_inner = Block(last_src_stmt.ins_addr, None, statements=[break_stmt])
|
|
944
|
+
fallthrough_stmt = Jump(
|
|
945
|
+
None,
|
|
946
|
+
Const(None, None, fallthrough_node.addr, self.project.arch.bits),
|
|
947
|
+
target_idx=successor.idx if isinstance(successor, Block) else None,
|
|
948
|
+
ins_addr=last_src_stmt.ins_addr,
|
|
949
|
+
)
|
|
950
|
+
break_node_inner_fallthrough = Block(
|
|
951
|
+
last_src_stmt.ins_addr, None, statements=[fallthrough_stmt]
|
|
952
|
+
)
|
|
953
|
+
else:
|
|
954
|
+
# the fallthrough node does not exist in the graph. we create a conditional jump that
|
|
955
|
+
# jumps to an address
|
|
956
|
+
if not isinstance(last_src_stmt, ConditionalJump):
|
|
957
|
+
raise TypeError(f"Unexpected last_src_stmt type {type(last_src_stmt)}")
|
|
958
|
+
other_target = (
|
|
959
|
+
last_src_stmt.true_target
|
|
960
|
+
if isinstance(last_src_stmt.false_target, Const)
|
|
961
|
+
and last_src_stmt.false_target.value == successor.addr
|
|
962
|
+
else last_src_stmt.false_target
|
|
963
|
+
)
|
|
964
|
+
assert other_target is not None
|
|
965
|
+
break_stmt = Jump(
|
|
966
|
+
None,
|
|
967
|
+
Const(None, None, successor.addr, self.project.arch.bits),
|
|
968
|
+
target_idx=successor.idx if isinstance(successor, Block) else None,
|
|
969
|
+
ins_addr=last_src_stmt.ins_addr,
|
|
970
|
+
)
|
|
971
|
+
break_node_inner = Block(last_src_stmt.ins_addr, None, statements=[break_stmt])
|
|
972
|
+
fallthrough_stmt = Jump(
|
|
973
|
+
None,
|
|
974
|
+
other_target,
|
|
975
|
+
target_idx=successor.idx if isinstance(successor, Block) else None,
|
|
976
|
+
ins_addr=last_src_stmt.ins_addr,
|
|
977
|
+
)
|
|
978
|
+
break_node_inner_fallthrough = Block(
|
|
979
|
+
last_src_stmt.ins_addr, None, statements=[fallthrough_stmt]
|
|
980
|
+
)
|
|
949
981
|
break_node = ConditionNode(
|
|
950
982
|
last_src_stmt.ins_addr,
|
|
951
983
|
None,
|
|
@@ -1454,7 +1486,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1454
1486
|
)
|
|
1455
1487
|
if not cmp:
|
|
1456
1488
|
return False
|
|
1457
|
-
cmp_expr, cmp_lb,
|
|
1489
|
+
cmp_expr, cmp_lb, _cmp_ub = cmp
|
|
1458
1490
|
|
|
1459
1491
|
assert cond_node.addr is not None
|
|
1460
1492
|
switch_head_addr = cond_node.addr
|
|
@@ -1477,7 +1509,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1477
1509
|
cmp = switch_extract_cmp_bounds(last_stmt)
|
|
1478
1510
|
if not cmp:
|
|
1479
1511
|
return False
|
|
1480
|
-
cmp_expr, cmp_lb,
|
|
1512
|
+
cmp_expr, cmp_lb, _cmp_ub = cmp # pylint:disable=unused-variable
|
|
1481
1513
|
|
|
1482
1514
|
switch_head_addr = last_stmt.ins_addr
|
|
1483
1515
|
|
|
@@ -1849,7 +1881,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1849
1881
|
cmp = switch_extract_cmp_bounds(last_stmt)
|
|
1850
1882
|
if not cmp:
|
|
1851
1883
|
return False
|
|
1852
|
-
cmp_expr, cmp_lb,
|
|
1884
|
+
cmp_expr, cmp_lb, _cmp_ub = cmp # pylint:disable=unused-variable
|
|
1853
1885
|
|
|
1854
1886
|
if isinstance(last_stmt.false_target, Const):
|
|
1855
1887
|
default_addr = last_stmt.false_target.value
|
|
@@ -2148,19 +2180,54 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2148
2180
|
jump_node = Block(out_src.addr, 0, statements=[jump_stmt])
|
|
2149
2181
|
case_node.nodes.append(jump_node)
|
|
2150
2182
|
|
|
2151
|
-
|
|
2183
|
+
# out_dst_succ is the successor within the current region
|
|
2184
|
+
# out_dst_succ_fullgraph is the successor outside the current region
|
|
2185
|
+
if out_edges_to_head:
|
|
2152
2186
|
# add an edge from SwitchCaseNode to head so that a loop will be structured later
|
|
2153
2187
|
out_dst_succ = head
|
|
2188
|
+
out_dst_succ_fullgraph = None
|
|
2154
2189
|
else:
|
|
2155
2190
|
# add an edge from SwitchCaseNode to its most immediate successor (if there is one)
|
|
2156
|
-
|
|
2191
|
+
# there might be an in-region successor and an out-of-region successor (especially due to the
|
|
2192
|
+
# introduction of self.dowhile_known_tail_nodes)!
|
|
2193
|
+
# example: 7995a0325b446c462bdb6ae10b692eee2ecadd8e888e9d7729befe4412007afb, function 1400EF820
|
|
2194
|
+
out_dst_succs = []
|
|
2195
|
+
out_dst_succs_fullgraph = []
|
|
2196
|
+
for _, o in other_out_edges:
|
|
2197
|
+
if o in graph:
|
|
2198
|
+
out_dst_succs.append(o)
|
|
2199
|
+
elif o in full_graph:
|
|
2200
|
+
out_dst_succs_fullgraph.append(o)
|
|
2201
|
+
out_dst_succ = sorted(out_dst_succs, key=lambda o: o.addr)[0] if out_dst_succs else None
|
|
2202
|
+
out_dst_succ_fullgraph = (
|
|
2203
|
+
sorted(out_dst_succs_fullgraph, key=lambda o: o.addr)[0] if out_dst_succs_fullgraph else None
|
|
2204
|
+
)
|
|
2205
|
+
if len(out_dst_succs) > 1:
|
|
2206
|
+
assert out_dst_succ is not None
|
|
2207
|
+
l.warning(
|
|
2208
|
+
"Multiple in-region successors detected for switch-case node at %#x. Picking %#x as the "
|
|
2209
|
+
"successor and dropping others.",
|
|
2210
|
+
scnode.addr,
|
|
2211
|
+
out_dst_succ.addr,
|
|
2212
|
+
)
|
|
2213
|
+
if len(out_dst_succs_fullgraph) > 1:
|
|
2214
|
+
assert out_dst_succ_fullgraph is not None
|
|
2215
|
+
l.warning(
|
|
2216
|
+
"Multiple out-of-region successors detected for switch-case node at %#x. Picking %#x as the "
|
|
2217
|
+
"successor and dropping others.",
|
|
2218
|
+
scnode.addr,
|
|
2219
|
+
out_dst_succ_fullgraph.addr,
|
|
2220
|
+
)
|
|
2157
2221
|
|
|
2158
2222
|
if out_dst_succ is not None:
|
|
2159
|
-
|
|
2160
|
-
graph.add_edge(scnode, out_dst_succ)
|
|
2223
|
+
graph.add_edge(scnode, out_dst_succ)
|
|
2161
2224
|
full_graph.add_edge(scnode, out_dst_succ)
|
|
2162
2225
|
if full_graph.has_edge(head, out_dst_succ):
|
|
2163
2226
|
full_graph.remove_edge(head, out_dst_succ)
|
|
2227
|
+
if out_dst_succ_fullgraph is not None:
|
|
2228
|
+
full_graph.add_edge(scnode, out_dst_succ_fullgraph)
|
|
2229
|
+
if full_graph.has_edge(head, out_dst_succ_fullgraph):
|
|
2230
|
+
full_graph.remove_edge(head, out_dst_succ_fullgraph)
|
|
2164
2231
|
|
|
2165
2232
|
# fix full_graph if needed: remove successors that are no longer needed
|
|
2166
2233
|
for _out_src, out_dst in other_out_edges:
|
|
@@ -2925,6 +2992,17 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2925
2992
|
ordered_nodes.remove(postorder_head)
|
|
2926
2993
|
acyclic_graph.remove_node(postorder_head)
|
|
2927
2994
|
node_seq = {nn: (len(ordered_nodes) - idx) for (idx, nn) in enumerate(ordered_nodes)} # post-order
|
|
2995
|
+
if len(node_seq) < len(acyclic_graph):
|
|
2996
|
+
# some nodes are not reachable from head - add them to node_seq as well
|
|
2997
|
+
# but this is usually the result of incorrect structuring, so we may still fail at a later point
|
|
2998
|
+
l.warning("Adding %d unreachable nodes to node_seq", len(acyclic_graph) - len(node_seq))
|
|
2999
|
+
unreachable_nodes = sorted(
|
|
3000
|
+
(nn for nn in acyclic_graph if nn not in node_seq),
|
|
3001
|
+
key=lambda n: (n.addr, (-1 if n.idx is None else n.idx) if hasattr(n, "idx") else 0),
|
|
3002
|
+
)
|
|
3003
|
+
max_seq = max(node_seq.values(), default=0)
|
|
3004
|
+
for i, nn in enumerate(unreachable_nodes):
|
|
3005
|
+
node_seq[nn] = max_seq + i
|
|
2928
3006
|
|
|
2929
3007
|
if all_edges_wo_dominance:
|
|
2930
3008
|
all_edges_wo_dominance = self._order_virtualizable_edges(full_graph, all_edges_wo_dominance, node_seq)
|
angr/analyses/smc.py
CHANGED
|
@@ -787,9 +787,10 @@ class StackPointerTracker(Analysis, ForwardAnalysis):
|
|
|
787
787
|
):
|
|
788
788
|
sp_adjusted = True
|
|
789
789
|
sp_v = state.regs[self.project.arch.sp_offset]
|
|
790
|
-
sp_v
|
|
791
|
-
|
|
792
|
-
|
|
790
|
+
if sp_v is not None:
|
|
791
|
+
sp_v -= Constant(stmt.data.con.value)
|
|
792
|
+
state.put(self.project.arch.sp_offset, sp_v, force=True) # sp -= OFFSET
|
|
793
|
+
state.put(stmt.offset, Constant(0), force=True) # rax = 0
|
|
793
794
|
break
|
|
794
795
|
|
|
795
796
|
callee_cleanups = [
|
angr/analyses/typehoon/lifter.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
import itertools
|
|
2
3
|
from typing import TYPE_CHECKING
|
|
3
4
|
|
|
4
5
|
from angr.sim_type import (
|
|
@@ -26,45 +27,53 @@ class TypeLifter:
|
|
|
26
27
|
Lift SimTypes to type constants.
|
|
27
28
|
"""
|
|
28
29
|
|
|
29
|
-
__slots__ = ("bits", "memo")
|
|
30
|
+
__slots__ = ("bits", "memo", "named_struct_id_counter", "struct_name_to_idx")
|
|
30
31
|
|
|
31
32
|
def __init__(self, bits: int):
|
|
32
33
|
if bits not in (32, 64):
|
|
33
34
|
raise ValueError("TypeLifter only supports 32-bit or 64-bit pointers.")
|
|
34
35
|
self.bits = bits
|
|
35
36
|
self.memo = {}
|
|
37
|
+
self.named_struct_id_counter = itertools.count(133337)
|
|
38
|
+
self.struct_name_to_idx = {}
|
|
36
39
|
|
|
37
40
|
def lift(self, ty: SimType):
|
|
38
41
|
handler = _mapping.get(type(ty), None)
|
|
39
42
|
if handler is None:
|
|
40
|
-
return BottomType()
|
|
43
|
+
return BottomType(name=ty.label)
|
|
41
44
|
|
|
42
45
|
return handler(self, ty)
|
|
43
46
|
|
|
44
|
-
def _lift_SimTypeChar(self, ty): # pylint:disable=
|
|
45
|
-
return Int8()
|
|
47
|
+
def _lift_SimTypeChar(self, ty): # pylint:disable=no-self-use
|
|
48
|
+
return Int8(name=ty.label)
|
|
46
49
|
|
|
47
|
-
def _lift_SimTypeShort(self, ty): # pylint:disable=
|
|
48
|
-
return Int16()
|
|
50
|
+
def _lift_SimTypeShort(self, ty): # pylint:disable=no-self-use
|
|
51
|
+
return Int16(name=ty.label)
|
|
49
52
|
|
|
50
|
-
def _lift_SimTypeInt(self, ty): # pylint:disable=
|
|
51
|
-
return Int32()
|
|
53
|
+
def _lift_SimTypeInt(self, ty): # pylint:disable=no-self-use
|
|
54
|
+
return Int32(name=ty.label)
|
|
52
55
|
|
|
53
|
-
def _lift_SimTypeLongLong(self, ty): # pylint:disable=
|
|
54
|
-
return Int64()
|
|
56
|
+
def _lift_SimTypeLongLong(self, ty): # pylint:disable=no-self-use
|
|
57
|
+
return Int64(name=ty.label)
|
|
55
58
|
|
|
56
59
|
def _lift_SimTypePointer(self, ty: SimTypePointer):
|
|
57
60
|
if self.bits == 32:
|
|
58
|
-
return Pointer32(self.lift(ty.pts_to))
|
|
61
|
+
return Pointer32(self.lift(ty.pts_to), name=ty.label)
|
|
59
62
|
if self.bits == 64:
|
|
60
|
-
return Pointer64(self.lift(ty.pts_to))
|
|
63
|
+
return Pointer64(self.lift(ty.pts_to), name=ty.label)
|
|
61
64
|
raise ValueError(f"Unsupported bits {self.bits}.")
|
|
62
65
|
|
|
63
66
|
def _lift_SimStruct(self, ty: SimStruct) -> TypeConstant | BottomType:
|
|
64
67
|
if ty in self.memo:
|
|
65
68
|
return BottomType()
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
struct_idx = {}
|
|
71
|
+
if ty.name:
|
|
72
|
+
if ty.name not in self.struct_name_to_idx:
|
|
73
|
+
self.struct_name_to_idx[ty.name] = next(self.named_struct_id_counter)
|
|
74
|
+
struct_idx["idx"] = self.struct_name_to_idx[ty.name]
|
|
75
|
+
|
|
76
|
+
obj = Struct(fields={}, name=ty.name, **struct_idx)
|
|
68
77
|
self.memo[ty] = obj
|
|
69
78
|
converted_fields = {}
|
|
70
79
|
field_names = {}
|
|
@@ -76,6 +85,7 @@ class TypeLifter:
|
|
|
76
85
|
field_names[ty_offsets[field_name]] = field_name
|
|
77
86
|
obj.fields = converted_fields
|
|
78
87
|
obj.field_names = field_names
|
|
88
|
+
del self.memo[ty]
|
|
79
89
|
return obj
|
|
80
90
|
|
|
81
91
|
def _lift_SimCppClass(self, ty: SimCppClass) -> TypeConstant | BottomType:
|
|
@@ -94,17 +104,18 @@ class TypeLifter:
|
|
|
94
104
|
field_names[ty_offsets[field_name]] = field_name
|
|
95
105
|
obj.fields = converted_fields
|
|
96
106
|
obj.field_names = field_names
|
|
107
|
+
del self.memo[ty]
|
|
97
108
|
return obj
|
|
98
109
|
|
|
99
110
|
def _lift_SimTypeArray(self, ty: SimTypeArray) -> Array:
|
|
100
111
|
elem_type = self.lift(ty.elem_type)
|
|
101
|
-
return Array(elem_type, count=ty.length)
|
|
112
|
+
return Array(elem_type, count=ty.length, name=ty.label)
|
|
102
113
|
|
|
103
|
-
def _lift_SimTypeFloat(self, ty: SimTypeFloat) -> Float32: # pylint:disable=
|
|
104
|
-
return Float32()
|
|
114
|
+
def _lift_SimTypeFloat(self, ty: SimTypeFloat) -> Float32: # pylint:disable=no-self-use
|
|
115
|
+
return Float32(name=ty.label)
|
|
105
116
|
|
|
106
|
-
def _lift_SimTypeDouble(self, ty: SimTypeDouble) -> Float64: # pylint:disable=
|
|
107
|
-
return Float64()
|
|
117
|
+
def _lift_SimTypeDouble(self, ty: SimTypeDouble) -> Float64: # pylint:disable=no-self-use
|
|
118
|
+
return Float64(name=ty.label)
|
|
108
119
|
|
|
109
120
|
|
|
110
121
|
_mapping = {
|