angr 9.2.140__py3-none-manylinux2014_x86_64.whl → 9.2.141__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 +88 -32
- angr/analyses/calling_convention/fact_collector.py +44 -18
- angr/analyses/calling_convention/utils.py +3 -1
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
- angr/analyses/decompiler/ail_simplifier.py +48 -20
- angr/analyses/decompiler/callsite_maker.py +24 -11
- angr/analyses/decompiler/clinic.py +10 -0
- angr/analyses/decompiler/decompiler.py +1 -0
- 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 +84 -15
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +76 -1
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +51 -7
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
- angr/analyses/decompiler/region_identifier.py +6 -4
- 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 +23 -15
- angr/analyses/decompiler/ssailification/rewriting_engine.py +105 -24
- angr/analyses/decompiler/ssailification/ssailification.py +22 -14
- angr/analyses/decompiler/structured_codegen/c.py +73 -137
- angr/analyses/decompiler/structuring/dream.py +1 -1
- angr/analyses/decompiler/structuring/phoenix.py +6 -1
- angr/analyses/decompiler/structuring/structurer_base.py +2 -1
- angr/analyses/decompiler/utils.py +46 -20
- angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
- angr/analyses/variable_recovery/engine_ail.py +1 -1
- angr/analyses/variable_recovery/engine_vex.py +20 -4
- angr/calling_conventions.py +15 -10
- angr/factory.py +8 -3
- angr/knowledge_plugins/variables/variable_manager.py +7 -5
- angr/simos/simos.py +3 -1
- angr/utils/types.py +48 -0
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/RECORD +40 -39
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
- {angr-9.2.140.dist-info → angr-9.2.141.dist-info}/top_level.txt +0 -0
|
@@ -371,8 +371,8 @@ class StructurerBase(Analysis):
|
|
|
371
371
|
jump_stmt = this_node.nodes[-1].statements[-1]
|
|
372
372
|
this_node = this_node.nodes[-1]
|
|
373
373
|
|
|
374
|
-
assert isinstance(this_node, ailment.Block)
|
|
375
374
|
if isinstance(jump_stmt, ailment.Stmt.Jump):
|
|
375
|
+
assert isinstance(this_node, ailment.Block)
|
|
376
376
|
next_node = node.nodes[i + 1]
|
|
377
377
|
if (
|
|
378
378
|
isinstance(jump_stmt.target, ailment.Expr.Const)
|
|
@@ -381,6 +381,7 @@ class StructurerBase(Analysis):
|
|
|
381
381
|
# this goto is useless
|
|
382
382
|
this_node.statements = this_node.statements[:-1]
|
|
383
383
|
elif isinstance(jump_stmt, ailment.Stmt.ConditionalJump):
|
|
384
|
+
assert isinstance(this_node, ailment.Block)
|
|
384
385
|
next_node = node.nodes[i + 1]
|
|
385
386
|
if (
|
|
386
387
|
isinstance(jump_stmt.true_target, ailment.Expr.Const)
|
|
@@ -625,6 +625,48 @@ def update_labels(graph: networkx.DiGraph):
|
|
|
625
625
|
return add_labels(remove_labels(graph))
|
|
626
626
|
|
|
627
627
|
|
|
628
|
+
def _flatten_structured_node(packed_node: SequenceNode | MultiNode) -> list[ailment.Block]:
|
|
629
|
+
if not packed_node or not packed_node.nodes:
|
|
630
|
+
return []
|
|
631
|
+
|
|
632
|
+
blocks = []
|
|
633
|
+
if packed_node.nodes is not None:
|
|
634
|
+
for _node in packed_node.nodes:
|
|
635
|
+
if isinstance(_node, (SequenceNode, MultiNode)):
|
|
636
|
+
blocks += _flatten_structured_node(_node)
|
|
637
|
+
else:
|
|
638
|
+
blocks.append(_node)
|
|
639
|
+
|
|
640
|
+
return blocks
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def _find_node_in_graph(node: ailment.Block, graph: networkx.DiGraph) -> ailment.Block | None:
|
|
644
|
+
for bb in graph:
|
|
645
|
+
if bb.addr == node.addr and bb.idx == node.idx:
|
|
646
|
+
return bb
|
|
647
|
+
return None
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
def structured_node_has_multi_predecessors(node: SequenceNode | MultiNode, graph: networkx.DiGraph) -> bool:
|
|
651
|
+
if graph is None:
|
|
652
|
+
return False
|
|
653
|
+
|
|
654
|
+
first_block = None
|
|
655
|
+
if isinstance(node, (SequenceNode, MultiNode)) and node.nodes:
|
|
656
|
+
flat_blocks = _flatten_structured_node(node)
|
|
657
|
+
node = flat_blocks[0]
|
|
658
|
+
|
|
659
|
+
if isinstance(node, ailment.Block):
|
|
660
|
+
first_block = node
|
|
661
|
+
|
|
662
|
+
if first_block is not None:
|
|
663
|
+
graph_node = _find_node_in_graph(first_block, graph)
|
|
664
|
+
if graph_node is not None:
|
|
665
|
+
return len(list(graph.predecessors(graph_node))) > 1
|
|
666
|
+
|
|
667
|
+
return False
|
|
668
|
+
|
|
669
|
+
|
|
628
670
|
def structured_node_is_simple_return(
|
|
629
671
|
node: SequenceNode | MultiNode, graph: networkx.DiGraph, use_packed_successors=False
|
|
630
672
|
) -> bool:
|
|
@@ -639,21 +681,6 @@ def structured_node_is_simple_return(
|
|
|
639
681
|
|
|
640
682
|
Returns true on any block ending in linear statements and a return.
|
|
641
683
|
"""
|
|
642
|
-
|
|
643
|
-
def _flatten_structured_node(packed_node: SequenceNode | MultiNode) -> list[ailment.Block]:
|
|
644
|
-
if not packed_node or not packed_node.nodes:
|
|
645
|
-
return []
|
|
646
|
-
|
|
647
|
-
blocks = []
|
|
648
|
-
if packed_node.nodes is not None:
|
|
649
|
-
for _node in packed_node.nodes:
|
|
650
|
-
if isinstance(_node, (SequenceNode, MultiNode)):
|
|
651
|
-
blocks += _flatten_structured_node(_node)
|
|
652
|
-
else:
|
|
653
|
-
blocks.append(_node)
|
|
654
|
-
|
|
655
|
-
return blocks
|
|
656
|
-
|
|
657
684
|
# sanity check: we need a graph to understand returning blocks
|
|
658
685
|
if graph is None:
|
|
659
686
|
return False
|
|
@@ -676,11 +703,10 @@ def structured_node_is_simple_return(
|
|
|
676
703
|
if valid_last_stmt:
|
|
677
704
|
# note that the block may not be the same block in the AIL graph post dephication. we must find the block again
|
|
678
705
|
# in the graph.
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
return not succs or succs == [bb]
|
|
706
|
+
last_graph_block = _find_node_in_graph(last_block, graph)
|
|
707
|
+
if last_graph_block is not None:
|
|
708
|
+
succs = list(graph.successors(last_graph_block))
|
|
709
|
+
return not succs or succs == [last_graph_block]
|
|
684
710
|
return False
|
|
685
711
|
|
|
686
712
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
from collections.abc import Callable
|
|
4
5
|
from collections import defaultdict
|
|
5
6
|
|
|
7
|
+
from ailment import Block
|
|
6
8
|
from ailment.statement import Statement, Assignment, Call, Label
|
|
7
9
|
from ailment.expression import VirtualVariable, VirtualVariableCategory, Expression
|
|
8
10
|
|
|
@@ -22,7 +24,7 @@ class RegVVarPredicate:
|
|
|
22
24
|
Implements a predicate that is used in get_reg_vvar_by_stmt_idx and get_reg_vvar_by_insn.
|
|
23
25
|
"""
|
|
24
26
|
|
|
25
|
-
def __init__(self, reg_offset: int, vvars:
|
|
27
|
+
def __init__(self, reg_offset: int, vvars: list[VirtualVariable], arch):
|
|
26
28
|
self.reg_offset = reg_offset
|
|
27
29
|
self.vvars = vvars
|
|
28
30
|
self.arch = arch
|
|
@@ -47,7 +49,8 @@ class RegVVarPredicate:
|
|
|
47
49
|
and stmt.dst.was_reg
|
|
48
50
|
and stmt.dst.reg_offset == self.reg_offset
|
|
49
51
|
):
|
|
50
|
-
self.vvars
|
|
52
|
+
if stmt.dst not in self.vvars:
|
|
53
|
+
self.vvars.append(stmt.dst)
|
|
51
54
|
return True
|
|
52
55
|
if isinstance(stmt, Call):
|
|
53
56
|
if (
|
|
@@ -55,7 +58,8 @@ class RegVVarPredicate:
|
|
|
55
58
|
and stmt.ret_expr.was_reg
|
|
56
59
|
and stmt.ret_expr.reg_offset == self.reg_offset
|
|
57
60
|
):
|
|
58
|
-
self.vvars
|
|
61
|
+
if stmt.ret_expr not in self.vvars:
|
|
62
|
+
self.vvars.append(stmt.ret_expr)
|
|
59
63
|
return True
|
|
60
64
|
# is it clobbered maybe?
|
|
61
65
|
clobbered_regs = self._get_call_clobbered_regs(stmt)
|
|
@@ -69,7 +73,7 @@ class StackVVarPredicate:
|
|
|
69
73
|
Implements a predicate that is used in get_stack_vvar_by_stmt_idx and get_stack_vvar_by_insn.
|
|
70
74
|
"""
|
|
71
75
|
|
|
72
|
-
def __init__(self, stack_offset: int, size: int, vvars:
|
|
76
|
+
def __init__(self, stack_offset: int, size: int, vvars: list[VirtualVariable]):
|
|
73
77
|
self.stack_offset = stack_offset
|
|
74
78
|
self.size = size
|
|
75
79
|
self.vvars = vvars
|
|
@@ -82,7 +86,8 @@ class StackVVarPredicate:
|
|
|
82
86
|
and stmt.dst.stack_offset <= self.stack_offset < stmt.dst.stack_offset + stmt.dst.size
|
|
83
87
|
and stmt.dst.stack_offset <= self.stack_offset + self.size <= stmt.dst.stack_offset + stmt.dst.size
|
|
84
88
|
):
|
|
85
|
-
self.vvars
|
|
89
|
+
if stmt.dst not in self.vvars:
|
|
90
|
+
self.vvars.append(stmt.dst)
|
|
86
91
|
return True
|
|
87
92
|
return False
|
|
88
93
|
|
|
@@ -96,7 +101,13 @@ class SRDAView:
|
|
|
96
101
|
self.model = model
|
|
97
102
|
|
|
98
103
|
def _get_vvar_by_stmt(
|
|
99
|
-
self,
|
|
104
|
+
self,
|
|
105
|
+
block_addr: int,
|
|
106
|
+
block_idx: int | None,
|
|
107
|
+
stmt_idx: int,
|
|
108
|
+
op_type: ObservationPointType,
|
|
109
|
+
predicate: Callable,
|
|
110
|
+
consecutive: bool = False,
|
|
100
111
|
):
|
|
101
112
|
# find the starting block
|
|
102
113
|
for block in self.model.func_graph:
|
|
@@ -107,7 +118,10 @@ class SRDAView:
|
|
|
107
118
|
return
|
|
108
119
|
|
|
109
120
|
traversed = set()
|
|
110
|
-
queue
|
|
121
|
+
queue: list[tuple[Block, int | None]] = [
|
|
122
|
+
(the_block, stmt_idx if op_type == ObservationPointType.OP_BEFORE else stmt_idx + 1)
|
|
123
|
+
]
|
|
124
|
+
predicate_returned_true = False
|
|
111
125
|
while queue:
|
|
112
126
|
block, start_stmt_idx = queue.pop(0)
|
|
113
127
|
traversed.add(block)
|
|
@@ -115,7 +129,8 @@ class SRDAView:
|
|
|
115
129
|
stmts = block.statements[:start_stmt_idx] if start_stmt_idx is not None else block.statements
|
|
116
130
|
|
|
117
131
|
for stmt in reversed(stmts):
|
|
118
|
-
|
|
132
|
+
r = predicate(stmt)
|
|
133
|
+
should_break = (predicate_returned_true and r is False) if consecutive else r
|
|
119
134
|
if should_break:
|
|
120
135
|
break
|
|
121
136
|
else:
|
|
@@ -129,7 +144,7 @@ class SRDAView:
|
|
|
129
144
|
self, reg_offset: int, block_addr: int, block_idx: int | None, stmt_idx: int, op_type: ObservationPointType
|
|
130
145
|
) -> VirtualVariable | None:
|
|
131
146
|
reg_offset = get_reg_offset_base(reg_offset, self.model.arch)
|
|
132
|
-
vvars =
|
|
147
|
+
vvars = []
|
|
133
148
|
predicater = RegVVarPredicate(reg_offset, vvars, self.model.arch)
|
|
134
149
|
self._get_vvar_by_stmt(block_addr, block_idx, stmt_idx, op_type, predicater.predicate)
|
|
135
150
|
|
|
@@ -137,14 +152,14 @@ class SRDAView:
|
|
|
137
152
|
# not found - check function arguments
|
|
138
153
|
for func_arg in self.model.func_args:
|
|
139
154
|
if isinstance(func_arg, VirtualVariable):
|
|
140
|
-
func_arg_category = func_arg.
|
|
155
|
+
func_arg_category = func_arg.parameter_category
|
|
141
156
|
if func_arg_category == VirtualVariableCategory.REGISTER:
|
|
142
|
-
func_arg_regoff = func_arg.
|
|
157
|
+
func_arg_regoff = func_arg.parameter_reg_offset
|
|
143
158
|
if func_arg_regoff == reg_offset:
|
|
144
|
-
vvars.
|
|
159
|
+
vvars.append(func_arg)
|
|
145
160
|
|
|
146
161
|
assert len(vvars) <= 1
|
|
147
|
-
return
|
|
162
|
+
return vvars[0] if vvars else None
|
|
148
163
|
|
|
149
164
|
def get_stack_vvar_by_stmt( # pylint: disable=too-many-positional-arguments
|
|
150
165
|
self,
|
|
@@ -155,21 +170,24 @@ class SRDAView:
|
|
|
155
170
|
stmt_idx: int,
|
|
156
171
|
op_type: ObservationPointType,
|
|
157
172
|
) -> VirtualVariable | None:
|
|
158
|
-
vvars =
|
|
173
|
+
vvars = []
|
|
159
174
|
predicater = StackVVarPredicate(stack_offset, size, vvars)
|
|
160
|
-
self._get_vvar_by_stmt(block_addr, block_idx, stmt_idx, op_type, predicater.predicate)
|
|
175
|
+
self._get_vvar_by_stmt(block_addr, block_idx, stmt_idx, op_type, predicater.predicate, consecutive=True)
|
|
161
176
|
|
|
162
177
|
if not vvars:
|
|
163
178
|
# not found - check function arguments
|
|
164
179
|
for func_arg in self.model.func_args:
|
|
165
180
|
if isinstance(func_arg, VirtualVariable):
|
|
166
|
-
func_arg_category = func_arg.
|
|
181
|
+
func_arg_category = func_arg.parameter_category
|
|
167
182
|
if func_arg_category == VirtualVariableCategory.STACK:
|
|
168
|
-
func_arg_stackoff = func_arg.oident[1]
|
|
183
|
+
func_arg_stackoff = func_arg.oident[1] # type: ignore
|
|
169
184
|
if func_arg_stackoff == stack_offset and func_arg.size == size:
|
|
170
|
-
vvars.
|
|
171
|
-
|
|
172
|
-
|
|
185
|
+
vvars.append(func_arg)
|
|
186
|
+
# there might be multiple vvars; we prioritize the one whose size fits the best
|
|
187
|
+
for v in vvars:
|
|
188
|
+
if v.stack_offset == stack_offset and v.size == size:
|
|
189
|
+
return v
|
|
190
|
+
return vvars[0] if vvars else None
|
|
173
191
|
|
|
174
192
|
def _get_vvar_by_insn(self, addr: int, op_type: ObservationPointType, predicate, block_idx: int | None = None):
|
|
175
193
|
# find the starting block
|
|
@@ -202,23 +220,23 @@ class SRDAView:
|
|
|
202
220
|
self, reg_offset: int, addr: int, op_type: ObservationPointType, block_idx: int | None = None
|
|
203
221
|
) -> VirtualVariable | None:
|
|
204
222
|
reg_offset = get_reg_offset_base(reg_offset, self.model.arch)
|
|
205
|
-
vvars =
|
|
223
|
+
vvars = []
|
|
206
224
|
predicater = RegVVarPredicate(reg_offset, vvars, self.model.arch)
|
|
207
225
|
|
|
208
226
|
self._get_vvar_by_insn(addr, op_type, predicater.predicate, block_idx=block_idx)
|
|
209
227
|
|
|
210
228
|
assert len(vvars) <= 1
|
|
211
|
-
return
|
|
229
|
+
return vvars[0] if vvars else None
|
|
212
230
|
|
|
213
231
|
def get_stack_vvar_by_insn( # pylint: disable=too-many-positional-arguments
|
|
214
232
|
self, stack_offset: int, size: int, addr: int, op_type: ObservationPointType, block_idx: int | None = None
|
|
215
233
|
) -> VirtualVariable | None:
|
|
216
|
-
vvars =
|
|
234
|
+
vvars = []
|
|
217
235
|
predicater = StackVVarPredicate(stack_offset, size, vvars)
|
|
218
236
|
self._get_vvar_by_insn(addr, op_type, predicater.predicate, block_idx=block_idx)
|
|
219
237
|
|
|
220
238
|
assert len(vvars) <= 1
|
|
221
|
-
return
|
|
239
|
+
return vvars[0] if vvars else None
|
|
222
240
|
|
|
223
241
|
def get_vvar_value(self, vvar: VirtualVariable) -> Expression | None:
|
|
224
242
|
if vvar not in self.model.all_vvar_definitions:
|
|
@@ -227,7 +245,7 @@ class SRDAView:
|
|
|
227
245
|
|
|
228
246
|
for block in self.model.func_graph:
|
|
229
247
|
if block.addr == codeloc.block_addr and block.idx == codeloc.block_idx:
|
|
230
|
-
if codeloc.stmt_idx < len(block.statements):
|
|
248
|
+
if codeloc.stmt_idx is not None and codeloc.stmt_idx < len(block.statements):
|
|
231
249
|
stmt = block.statements[codeloc.stmt_idx]
|
|
232
250
|
if isinstance(stmt, Assignment) and stmt.dst.likes(vvar):
|
|
233
251
|
return stmt.src
|
|
@@ -333,7 +333,7 @@ class SimEngineVRAIL(
|
|
|
333
333
|
tvs = set()
|
|
334
334
|
for _, vvar in expr.src_and_vvars:
|
|
335
335
|
if vvar is not None:
|
|
336
|
-
r = self._read_from_vvar(vvar, expr=
|
|
336
|
+
r = self._read_from_vvar(vvar, expr=vvar, vvar_id=self._mapped_vvarid(vvar.varid))
|
|
337
337
|
if r.typevar is not None:
|
|
338
338
|
tvs.add(r.typevar)
|
|
339
339
|
|
|
@@ -8,12 +8,13 @@ from archinfo.arch_arm import is_arm_arch
|
|
|
8
8
|
|
|
9
9
|
from angr.block import Block
|
|
10
10
|
from angr.errors import SimMemoryMissingError
|
|
11
|
-
from angr.calling_conventions import SimRegArg, SimStackArg, default_cc
|
|
11
|
+
from angr.calling_conventions import SimRegArg, SimStackArg, SimTypeFunction, default_cc
|
|
12
12
|
from angr.engines.vex.claripy.datalayer import value as claripy_value
|
|
13
13
|
from angr.engines.light import SimEngineNostmtVEX
|
|
14
14
|
from angr.knowledge_plugins import Function
|
|
15
15
|
from angr.storage.memory_mixins.paged_memory.pages.multi_values import MultiValues
|
|
16
16
|
from angr.analyses.typehoon import typevars, typeconsts
|
|
17
|
+
from angr.sim_type import SimTypeBottom
|
|
17
18
|
from .engine_base import SimEngineVRBase, RichR
|
|
18
19
|
from .irsb_scanner import VEXIRSBScanner
|
|
19
20
|
|
|
@@ -222,24 +223,39 @@ class SimEngineVRVEX(
|
|
|
222
223
|
|
|
223
224
|
def _process_block_end(self, stmt_result, whitelist):
|
|
224
225
|
# handles block-end calls
|
|
226
|
+
has_call = False
|
|
225
227
|
current_addr = self.state.block_addr
|
|
226
228
|
for target_func in self.call_info.get(current_addr, []):
|
|
227
229
|
self._handle_function_concrete(target_func)
|
|
230
|
+
has_call = True
|
|
228
231
|
|
|
229
|
-
if self.block.vex.jumpkind == "Ijk_Call":
|
|
232
|
+
if has_call or self.block.vex.jumpkind == "Ijk_Call":
|
|
230
233
|
# emulates return values from calls
|
|
231
234
|
cc = None
|
|
235
|
+
proto: SimTypeFunction | None = None
|
|
232
236
|
for target_func in self.call_info.get(self.state.block_addr, []):
|
|
233
237
|
if target_func.calling_convention is not None:
|
|
234
238
|
cc = target_func.calling_convention
|
|
239
|
+
proto = target_func.prototype
|
|
235
240
|
break
|
|
236
241
|
if cc is None:
|
|
237
242
|
cc = default_cc(self.arch.name, platform=self.project.simos.name)(self.arch)
|
|
238
|
-
|
|
239
|
-
|
|
243
|
+
|
|
244
|
+
if proto is not None and not isinstance(proto.returnty, SimTypeBottom):
|
|
245
|
+
ret_reg = cc.return_val(proto.returnty)
|
|
246
|
+
else:
|
|
247
|
+
ret_reg = cc.RETURN_VAL
|
|
248
|
+
if isinstance(ret_reg, SimRegArg):
|
|
249
|
+
reg_offset, reg_size = self.arch.registers[ret_reg.reg_name]
|
|
240
250
|
data = self._top(reg_size * self.arch.byte_width)
|
|
241
251
|
self._assign_to_register(reg_offset, data, reg_size, create_variable=False)
|
|
242
252
|
|
|
253
|
+
# handle tail-call optimizations
|
|
254
|
+
if self.block.vex.jumpkind == "Ijk_Boring":
|
|
255
|
+
self.state.ret_val_size = (
|
|
256
|
+
reg_size if self.state.ret_val_size is None else max(self.state.ret_val_size, reg_size)
|
|
257
|
+
)
|
|
258
|
+
|
|
243
259
|
elif self.block.vex.jumpkind == "Ijk_Ret":
|
|
244
260
|
# handles return statements
|
|
245
261
|
|
angr/calling_conventions.py
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import logging
|
|
4
4
|
from typing import cast
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
from collections.abc import Iterable, Sequence
|
|
6
7
|
from collections import defaultdict
|
|
7
8
|
import contextlib
|
|
8
9
|
|
|
@@ -82,7 +83,8 @@ class AllocHelper:
|
|
|
82
83
|
|
|
83
84
|
def size(self):
|
|
84
85
|
val = self.translate(self.ptr, claripy.BVV(0, len(self.ptr)))
|
|
85
|
-
assert val.op == "BVV"
|
|
86
|
+
assert isinstance(val, claripy.ast.Base) and val.op == "BVV"
|
|
87
|
+
assert isinstance(val.args[0], int)
|
|
86
88
|
return abs(val.args[0])
|
|
87
89
|
|
|
88
90
|
@classmethod
|
|
@@ -130,6 +132,7 @@ def refine_locs_with_struct_type(
|
|
|
130
132
|
arg_type = SimTypeInt(label=arg_type.label).with_arch(arch)
|
|
131
133
|
|
|
132
134
|
if isinstance(arg_type, (SimTypeReg, SimTypeNum, SimTypeFloat)):
|
|
135
|
+
assert arg_type.size is not None
|
|
133
136
|
seen_bytes = 0
|
|
134
137
|
pieces = []
|
|
135
138
|
while seen_bytes < arg_type.size // arch.byte_width:
|
|
@@ -147,20 +150,21 @@ def refine_locs_with_struct_type(
|
|
|
147
150
|
piece.is_fp = True
|
|
148
151
|
return piece
|
|
149
152
|
if isinstance(arg_type, SimTypeFixedSizeArray):
|
|
153
|
+
assert arg_type.elem_type.size is not None and arg_type.length is not None
|
|
150
154
|
# TODO explicit stride
|
|
151
|
-
|
|
155
|
+
locs_list = [
|
|
152
156
|
refine_locs_with_struct_type(
|
|
153
157
|
arch, locs, arg_type.elem_type, offset=offset + i * arg_type.elem_type.size // arch.byte_width
|
|
154
158
|
)
|
|
155
159
|
for i in range(arg_type.length)
|
|
156
160
|
]
|
|
157
|
-
return SimArrayArg(
|
|
161
|
+
return SimArrayArg(locs_list)
|
|
158
162
|
if isinstance(arg_type, SimStruct):
|
|
159
|
-
|
|
163
|
+
locs_dict = {
|
|
160
164
|
field: refine_locs_with_struct_type(arch, locs, field_ty, offset=offset + arg_type.offsets[field])
|
|
161
165
|
for field, field_ty in arg_type.fields.items()
|
|
162
166
|
}
|
|
163
|
-
return SimStructArg(arg_type,
|
|
167
|
+
return SimStructArg(arg_type, locs_dict)
|
|
164
168
|
if isinstance(arg_type, SimUnion):
|
|
165
169
|
# Treat a SimUnion as functionality equivalent to its longest member
|
|
166
170
|
for member in arg_type.members.values():
|
|
@@ -574,8 +578,8 @@ class SimCC:
|
|
|
574
578
|
# (if applicable) and the arguments. Probably zero.
|
|
575
579
|
STACKARG_SP_DIFF = 0 # The amount of stack space reserved for the return address
|
|
576
580
|
CALLER_SAVED_REGS: list[str] = [] # Caller-saved registers
|
|
577
|
-
RETURN_ADDR: SimFunctionArgument
|
|
578
|
-
RETURN_VAL: SimFunctionArgument
|
|
581
|
+
RETURN_ADDR: SimFunctionArgument # The location where the return address is stored, as a SimFunctionArgument
|
|
582
|
+
RETURN_VAL: SimFunctionArgument # The location where the return value is stored, as a SimFunctionArgument
|
|
579
583
|
OVERFLOW_RETURN_VAL: SimFunctionArgument | None = (
|
|
580
584
|
None # The second half of the location where a double-length return value is stored
|
|
581
585
|
)
|
|
@@ -728,6 +732,7 @@ class SimCC:
|
|
|
728
732
|
l.warning("Function argument type cannot be BOT. Treating it as a 32-bit int.")
|
|
729
733
|
arg_type = SimTypeInt().with_arch(self.arch)
|
|
730
734
|
is_fp = isinstance(arg_type, SimTypeFloat)
|
|
735
|
+
assert arg_type.size is not None
|
|
731
736
|
size = arg_type.size // self.arch.byte_width
|
|
732
737
|
try:
|
|
733
738
|
arg = next(session.fp_iter) if is_fp else next(session.int_iter)
|
|
@@ -760,7 +765,7 @@ class SimCC:
|
|
|
760
765
|
def is_fp_value(val):
|
|
761
766
|
return (
|
|
762
767
|
isinstance(val, (float, claripy.ast.FP))
|
|
763
|
-
or (isinstance(val, claripy.ast.Base) and val.op.startswith("fp"))
|
|
768
|
+
or (isinstance(val, claripy.ast.Base) and val.op.startswith("fp")) # type: ignore
|
|
764
769
|
or (isinstance(val, claripy.ast.Base) and val.op == "Reverse" and val.args[0].op.startswith("fp"))
|
|
765
770
|
)
|
|
766
771
|
|
|
@@ -1130,7 +1135,7 @@ class SimCC:
|
|
|
1130
1135
|
|
|
1131
1136
|
@staticmethod
|
|
1132
1137
|
def find_cc(
|
|
1133
|
-
arch: archinfo.Arch, args:
|
|
1138
|
+
arch: archinfo.Arch, args: Sequence[SimFunctionArgument], sp_delta: int, platform: str = "Linux"
|
|
1134
1139
|
) -> SimCC | None:
|
|
1135
1140
|
"""
|
|
1136
1141
|
Pinpoint the best-fit calling convention and return the corresponding SimCC instance, or None if no fit is
|
angr/factory.py
CHANGED
|
@@ -7,6 +7,7 @@ from typing import overload, TYPE_CHECKING
|
|
|
7
7
|
import archinfo
|
|
8
8
|
from archinfo.arch_soot import ArchSoot, SootAddressDescriptor
|
|
9
9
|
|
|
10
|
+
from .knowledge_plugins.functions import Function
|
|
10
11
|
from .sim_state import SimState
|
|
11
12
|
from .calling_conventions import default_cc, SimRegArg, SimStackArg, PointerWrapper, SimCCUnknown
|
|
12
13
|
from .callable import Callable
|
|
@@ -236,7 +237,7 @@ class AngrObjectFactory:
|
|
|
236
237
|
|
|
237
238
|
def callable(
|
|
238
239
|
self,
|
|
239
|
-
addr,
|
|
240
|
+
addr: int | Function,
|
|
240
241
|
prototype=None,
|
|
241
242
|
concrete_only=False,
|
|
242
243
|
perform_merge=True,
|
|
@@ -251,8 +252,9 @@ class AngrObjectFactory:
|
|
|
251
252
|
A Callable is a representation of a function in the binary that can be interacted with like a native python
|
|
252
253
|
function.
|
|
253
254
|
|
|
254
|
-
:param addr: The address of the function to use
|
|
255
|
-
|
|
255
|
+
:param addr: The address of the function to use. If you pass in the function object, we will take
|
|
256
|
+
its addr.
|
|
257
|
+
:param prototype: The prototype of the call to use, as a string or a SimTypeFunction
|
|
256
258
|
:param concrete_only: Throw an exception if the execution splits into multiple states
|
|
257
259
|
:param perform_merge: Merge all result states into one at the end (only relevant if concrete_only=False)
|
|
258
260
|
:param base_state: The state from which to do these runs
|
|
@@ -263,6 +265,9 @@ class AngrObjectFactory:
|
|
|
263
265
|
python function.
|
|
264
266
|
:rtype: angr.callable.Callable
|
|
265
267
|
"""
|
|
268
|
+
if isinstance(addr, Function):
|
|
269
|
+
addr = addr.addr
|
|
270
|
+
|
|
266
271
|
return Callable(
|
|
267
272
|
self.project,
|
|
268
273
|
addr=addr,
|
|
@@ -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,
|
|
@@ -985,10 +985,12 @@ class VariableManagerInternal(Serializable):
|
|
|
985
985
|
if name not in self.types:
|
|
986
986
|
self.types[name] = TypeRef(name, ty).with_arch(self.manager._kb._project.arch)
|
|
987
987
|
ty = self.types[name]
|
|
988
|
-
elif
|
|
989
|
-
typeref = self._register_struct_type(
|
|
990
|
-
|
|
991
|
-
|
|
988
|
+
elif (inner_ty := unpack_pointer(ty, iterative=True)) and isinstance(inner_ty, SimStruct):
|
|
989
|
+
typeref = self._register_struct_type(inner_ty)
|
|
990
|
+
# rebuild the multi-layer pointer type
|
|
991
|
+
replaced_ty = replace_pointer_pts_to(ty, inner_ty, typeref)
|
|
992
|
+
assert replaced_ty is not None
|
|
993
|
+
ty = replaced_ty.with_arch(self.manager._kb._project.arch)
|
|
992
994
|
elif isinstance(ty, SimStruct):
|
|
993
995
|
ty = self._register_struct_type(ty, name=name)
|
|
994
996
|
|
angr/simos/simos.py
CHANGED
|
@@ -23,7 +23,9 @@ class SimOS:
|
|
|
23
23
|
A class describing OS/arch-level configuration.
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
name: str | None
|
|
27
|
+
|
|
28
|
+
def __init__(self, project: angr.Project, name: str | None = None):
|
|
27
29
|
self.arch = project.arch
|
|
28
30
|
self.project = project
|
|
29
31
|
self.name = name
|
angr/utils/types.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
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, 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 replace_pointer_pts_to(ty: SimType, old_pts_to: SimType, new_pts_to: SimType) -> SimTypePointer | None:
|
|
22
|
+
if isinstance(ty, SimTypePointer):
|
|
23
|
+
if ty.pts_to is old_pts_to:
|
|
24
|
+
inner = new_pts_to
|
|
25
|
+
elif isinstance(ty.pts_to, SimTypePointer):
|
|
26
|
+
# recursively replace pts_to inside
|
|
27
|
+
inner = replace_pointer_pts_to(ty.pts_to, old_pts_to, new_pts_to)
|
|
28
|
+
else:
|
|
29
|
+
return None
|
|
30
|
+
return SimTypePointer(inner, label=ty.label, offset=ty.offset)
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def unpack_array(ty) -> SimType | None:
|
|
35
|
+
if isinstance(ty, SimTypeArray):
|
|
36
|
+
return ty.elem_type
|
|
37
|
+
if isinstance(ty, SimTypeFixedSizeArray):
|
|
38
|
+
return ty.elem_type
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def squash_array_reference(ty):
|
|
43
|
+
pointed_to = unpack_pointer(ty)
|
|
44
|
+
if pointed_to:
|
|
45
|
+
array_of = unpack_array(pointed_to)
|
|
46
|
+
if array_of:
|
|
47
|
+
return SimTypePointer(array_of)
|
|
48
|
+
return ty
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: angr
|
|
3
|
-
Version: 9.2.
|
|
3
|
+
Version: 9.2.141
|
|
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,13 +16,13 @@ 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.141
|
|
20
|
+
Requires-Dist: archinfo==9.2.141
|
|
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.
|
|
24
|
+
Requires-Dist: claripy==9.2.141
|
|
25
|
+
Requires-Dist: cle==9.2.141
|
|
26
26
|
Requires-Dist: itanium-demangler
|
|
27
27
|
Requires-Dist: mulpyplexer
|
|
28
28
|
Requires-Dist: nampa
|
|
@@ -31,7 +31,7 @@ Requires-Dist: protobuf>=5.28.2
|
|
|
31
31
|
Requires-Dist: psutil
|
|
32
32
|
Requires-Dist: pycparser>=2.18
|
|
33
33
|
Requires-Dist: pyformlang
|
|
34
|
-
Requires-Dist: pyvex==9.2.
|
|
34
|
+
Requires-Dist: pyvex==9.2.141
|
|
35
35
|
Requires-Dist: rich>=13.1.0
|
|
36
36
|
Requires-Dist: sortedcontainers
|
|
37
37
|
Requires-Dist: sympy
|