angr 9.2.163__cp310-abi3-manylinux2014_aarch64.whl → 9.2.164__cp310-abi3-manylinux2014_aarch64.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/ailment/converter_vex.py +1 -1
- angr/ailment/expression.py +5 -1
- angr/analyses/cfg/cfg_base.py +16 -13
- angr/analyses/cfg/cfg_emulated.py +5 -1
- angr/analyses/cfg/cfg_fast.py +27 -4
- angr/analyses/cfg/indirect_jump_resolvers/arm_elf_fast.py +11 -1
- angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +194 -41
- angr/analyses/decompiler/ail_simplifier.py +19 -5
- angr/analyses/decompiler/callsite_maker.py +33 -17
- angr/analyses/decompiler/graph_region.py +19 -0
- angr/analyses/decompiler/optimization_passes/deadblock_remover.py +1 -1
- angr/analyses/decompiler/region_identifier.py +22 -1
- angr/analyses/decompiler/structuring/phoenix.py +72 -20
- angr/analyses/decompiler/structuring/recursive_structurer.py +3 -4
- angr/analyses/decompiler/structuring/structurer_nodes.py +3 -0
- angr/analyses/decompiler/utils.py +17 -5
- angr/analyses/s_reaching_definitions/s_rda_view.py +2 -1
- angr/analyses/typehoon/typeconsts.py +3 -1
- angr/blade.py +20 -15
- angr/engines/icicle.py +7 -2
- angr/knowledge_plugins/propagations/propagation_model.py +7 -0
- angr/rustylib.abi3.so +0 -0
- angr/sim_type.py +16 -1
- angr/utils/constants.py +1 -1
- angr/utils/graph.py +1 -1
- angr/utils/vex.py +11 -0
- {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/METADATA +5 -5
- {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/RECORD +33 -32
- {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/WHEEL +0 -0
- {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/entry_points.txt +0 -0
- {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.163.dist-info → angr-9.2.164.dist-info}/top_level.txt +0 -0
|
@@ -99,7 +99,7 @@ class RegionIdentifier(Analysis):
|
|
|
99
99
|
|
|
100
100
|
def _analyze(self):
|
|
101
101
|
# make a copy of the graph
|
|
102
|
-
graph =
|
|
102
|
+
graph = self._pick_one_connected_component(self._graph, as_copy=True)
|
|
103
103
|
|
|
104
104
|
# preprocess: make it a super graph
|
|
105
105
|
self._make_supergraph(graph)
|
|
@@ -113,6 +113,27 @@ class RegionIdentifier(Analysis):
|
|
|
113
113
|
# make regions into block address lists
|
|
114
114
|
self.regions_by_block_addrs = self._make_regions_by_block_addrs()
|
|
115
115
|
|
|
116
|
+
def _pick_one_connected_component(self, digraph: networkx.DiGraph, as_copy: bool = False) -> networkx.DiGraph:
|
|
117
|
+
g = networkx.Graph(digraph)
|
|
118
|
+
components = list(networkx.connected_components(g))
|
|
119
|
+
if len(components) <= 1:
|
|
120
|
+
return networkx.DiGraph(digraph) if as_copy else digraph
|
|
121
|
+
|
|
122
|
+
the_component = None
|
|
123
|
+
largest_component = None
|
|
124
|
+
for component in components:
|
|
125
|
+
if largest_component is None or len(component) > len(largest_component):
|
|
126
|
+
largest_component = component
|
|
127
|
+
if any((block.addr, block.idx) == self.entry_node_addr for block in component):
|
|
128
|
+
the_component = component
|
|
129
|
+
break
|
|
130
|
+
|
|
131
|
+
if the_component is None:
|
|
132
|
+
the_component = largest_component
|
|
133
|
+
|
|
134
|
+
assert the_component is not None
|
|
135
|
+
return digraph.subgraph(the_component).to_directed()
|
|
136
|
+
|
|
116
137
|
@staticmethod
|
|
117
138
|
def _compute_node_order(graph: networkx.DiGraph) -> dict[Any, tuple[int, int]]:
|
|
118
139
|
sorted_nodes = GraphUtils.quasi_topological_sort_nodes(graph)
|
|
@@ -13,7 +13,7 @@ from angr.ailment.block import Block
|
|
|
13
13
|
from angr.ailment.statement import Statement, ConditionalJump, Jump, Label, Return
|
|
14
14
|
from angr.ailment.expression import Const, UnaryOp, MultiStatementExpression, BinaryOp
|
|
15
15
|
|
|
16
|
-
from angr.utils.graph import GraphUtils
|
|
16
|
+
from angr.utils.graph import GraphUtils, Dominators, compute_dominance_frontier
|
|
17
17
|
from angr.utils.ail import is_phi_assignment, is_head_controlled_loop_block
|
|
18
18
|
from angr.knowledge_plugins.cfg import IndirectJump, IndirectJumpType
|
|
19
19
|
from angr.utils.constants import SWITCH_MISSING_DEFAULT_NODE_ADDR
|
|
@@ -669,7 +669,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
669
669
|
continue_node = loop_head
|
|
670
670
|
|
|
671
671
|
is_while, result_while = self._refine_cyclic_is_while_loop(graph, fullgraph, loop_head, head_succs)
|
|
672
|
-
is_dowhile, result_dowhile = self._refine_cyclic_is_dowhile_loop(graph, fullgraph, loop_head
|
|
672
|
+
is_dowhile, result_dowhile = self._refine_cyclic_is_dowhile_loop(graph, fullgraph, loop_head)
|
|
673
673
|
|
|
674
674
|
continue_edges: list[tuple[BaseNode, BaseNode]] = []
|
|
675
675
|
outgoing_edges: list = []
|
|
@@ -702,22 +702,12 @@ class PhoenixStructurer(StructurerBase):
|
|
|
702
702
|
|
|
703
703
|
if loop_type is None:
|
|
704
704
|
# natural loop. select *any* exit edge to determine the successor
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
if loop_head is succ:
|
|
712
|
-
continue_edges.append((node, succ))
|
|
713
|
-
if successor_candidates:
|
|
714
|
-
successor_candidates = sorted(successor_candidates, key=lambda x: x.addr)
|
|
715
|
-
successor = successor_candidates[0]
|
|
716
|
-
# virtualize all other edges
|
|
717
|
-
for succ in successor_candidates:
|
|
718
|
-
for pred in fullgraph.predecessors(succ):
|
|
719
|
-
if pred in graph:
|
|
720
|
-
outgoing_edges.append((pred, succ))
|
|
705
|
+
is_natural, result_natural = self._refine_cyclic_make_natural_loop(graph, fullgraph, loop_head)
|
|
706
|
+
if not is_natural:
|
|
707
|
+
# cannot refine this loop
|
|
708
|
+
return False
|
|
709
|
+
assert result_natural is not None
|
|
710
|
+
continue_edges, outgoing_edges, successor = result_natural
|
|
721
711
|
|
|
722
712
|
if outgoing_edges:
|
|
723
713
|
# if there is a single successor, we convert all out-going edges into breaks;
|
|
@@ -963,8 +953,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
963
953
|
return True, (continue_edges, outgoing_edges, loop_head, successor)
|
|
964
954
|
return False, None
|
|
965
955
|
|
|
966
|
-
def _refine_cyclic_is_dowhile_loop(
|
|
967
|
-
self, graph, fullgraph, loop_head
|
|
956
|
+
def _refine_cyclic_is_dowhile_loop(
|
|
957
|
+
self, graph, fullgraph, loop_head
|
|
968
958
|
) -> tuple[bool, tuple[list, list, BaseNode, BaseNode] | None]:
|
|
969
959
|
# check if there is an out-going edge from the loop tail
|
|
970
960
|
head_preds = list(fullgraph.predecessors(loop_head))
|
|
@@ -996,6 +986,64 @@ class PhoenixStructurer(StructurerBase):
|
|
|
996
986
|
return True, (continue_edges, outgoing_edges, continue_node, successor)
|
|
997
987
|
return False, None
|
|
998
988
|
|
|
989
|
+
def _refine_cyclic_make_natural_loop(
|
|
990
|
+
self, graph, fullgraph, loop_head
|
|
991
|
+
) -> tuple[bool, tuple[list, list, Any] | None]:
|
|
992
|
+
continue_edges = []
|
|
993
|
+
outgoing_edges = []
|
|
994
|
+
|
|
995
|
+
# find dominance frontier
|
|
996
|
+
doms = Dominators(fullgraph, self._region.head)
|
|
997
|
+
dom_frontiers = compute_dominance_frontier(fullgraph, doms.dom)
|
|
998
|
+
|
|
999
|
+
if loop_head not in dom_frontiers:
|
|
1000
|
+
return False, None
|
|
1001
|
+
dom_frontier = dom_frontiers[loop_head]
|
|
1002
|
+
|
|
1003
|
+
# now this is a little complex
|
|
1004
|
+
dom_frontier = {node for node in dom_frontier if node is not loop_head}
|
|
1005
|
+
if len(dom_frontier) == 0:
|
|
1006
|
+
# the dominance frontier is empty (the loop head dominates all nodes in the full graph). however, this does
|
|
1007
|
+
# not mean that the loop head must dominate all the nodes, because we only have a limited view of the full
|
|
1008
|
+
# graph (e.g., some predecessors of the successor may not be in this full graph). as such, successors are
|
|
1009
|
+
# the ones that are in the fullgraph but not in the graph.
|
|
1010
|
+
successor_candidates = set()
|
|
1011
|
+
for node in networkx.descendants(graph, loop_head):
|
|
1012
|
+
for succ in fullgraph.successors(node):
|
|
1013
|
+
if succ not in graph:
|
|
1014
|
+
successor_candidates.add(succ)
|
|
1015
|
+
if loop_head is succ:
|
|
1016
|
+
continue_edges.append((node, succ))
|
|
1017
|
+
|
|
1018
|
+
else:
|
|
1019
|
+
# this loop has a single successor
|
|
1020
|
+
successor_candidates = dom_frontier
|
|
1021
|
+
# traverse the loop body to find all continue edges
|
|
1022
|
+
tmp_graph = networkx.DiGraph(graph)
|
|
1023
|
+
tmp_graph.remove_nodes_from(successor_candidates)
|
|
1024
|
+
for node in networkx.descendants(tmp_graph, loop_head):
|
|
1025
|
+
if tmp_graph.has_edge(node, loop_head):
|
|
1026
|
+
continue_edges.append((node, loop_head))
|
|
1027
|
+
|
|
1028
|
+
if len(successor_candidates) == 0:
|
|
1029
|
+
successor = None
|
|
1030
|
+
else:
|
|
1031
|
+
# one or multiple successors; try to pick a successor in graph, and prioritize the one with the lowest
|
|
1032
|
+
# address
|
|
1033
|
+
successor_candidates_in_graph = {nn for nn in successor_candidates if nn in graph}
|
|
1034
|
+
if successor_candidates_in_graph:
|
|
1035
|
+
# pick the one with the lowest address
|
|
1036
|
+
successor = next(iter(sorted(successor_candidates_in_graph, key=lambda x: x.addr)))
|
|
1037
|
+
else:
|
|
1038
|
+
successor = next(iter(sorted(successor_candidates, key=lambda x: x.addr)))
|
|
1039
|
+
# mark all edges as outgoing edges so they will be virtualized if they don't lead to the successor
|
|
1040
|
+
for node in successor_candidates:
|
|
1041
|
+
for pred in fullgraph.predecessors(node):
|
|
1042
|
+
if networkx.has_path(doms.dom, loop_head, pred):
|
|
1043
|
+
outgoing_edges.append((pred, node))
|
|
1044
|
+
|
|
1045
|
+
return True, (continue_edges, outgoing_edges, successor)
|
|
1046
|
+
|
|
999
1047
|
def _analyze_acyclic(self) -> bool:
|
|
1000
1048
|
# match against known schemas
|
|
1001
1049
|
l.debug("Matching acyclic schemas for region %r.", self._region)
|
|
@@ -1219,6 +1267,10 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1219
1267
|
node_a = next(iter(nn for nn in graph.nodes if nn.addr == target), None)
|
|
1220
1268
|
if node_a is None:
|
|
1221
1269
|
return False
|
|
1270
|
+
if node_a is self._region.head:
|
|
1271
|
+
# avoid structuring if node_a is the region head; this means the current node is a duplicated switch-case
|
|
1272
|
+
# head (instead of the original one), which is not something we want to structure
|
|
1273
|
+
return False
|
|
1222
1274
|
|
|
1223
1275
|
# the default case
|
|
1224
1276
|
node_b_addr = next(iter(t for t in successor_addrs if t != target), None)
|
|
@@ -3,8 +3,6 @@ import itertools
|
|
|
3
3
|
from typing import TYPE_CHECKING
|
|
4
4
|
import logging
|
|
5
5
|
|
|
6
|
-
import networkx
|
|
7
|
-
|
|
8
6
|
from angr.analyses import Analysis, register_analysis
|
|
9
7
|
from angr.analyses.decompiler.condition_processor import ConditionProcessor
|
|
10
8
|
from angr.analyses.decompiler.graph_region import GraphRegion
|
|
@@ -12,6 +10,7 @@ from angr.analyses.decompiler.jumptable_entry_condition_rewriter import JumpTabl
|
|
|
12
10
|
from angr.analyses.decompiler.empty_node_remover import EmptyNodeRemover
|
|
13
11
|
from angr.analyses.decompiler.jump_target_collector import JumpTargetCollector
|
|
14
12
|
from angr.analyses.decompiler.redundant_label_remover import RedundantLabelRemover
|
|
13
|
+
from angr.utils.graph import GraphUtils
|
|
15
14
|
from .structurer_nodes import BaseNode
|
|
16
15
|
from .structurer_base import StructurerBase
|
|
17
16
|
from .dream import DreamStructurer
|
|
@@ -61,7 +60,7 @@ class RecursiveStructurer(Analysis):
|
|
|
61
60
|
current_region = stack[-1]
|
|
62
61
|
|
|
63
62
|
has_region = False
|
|
64
|
-
for node in
|
|
63
|
+
for node in GraphUtils.dfs_postorder_nodes_deterministic(current_region.graph, current_region.head):
|
|
65
64
|
subnodes = []
|
|
66
65
|
if type(node) is GraphRegion:
|
|
67
66
|
if node.cyclic:
|
|
@@ -177,7 +176,7 @@ class RecursiveStructurer(Analysis):
|
|
|
177
176
|
for node in region.graph.nodes:
|
|
178
177
|
if not isinstance(node, BaseNode):
|
|
179
178
|
continue
|
|
180
|
-
if node.addr == self.function.addr:
|
|
179
|
+
if self.function is not None and node.addr == self.function.addr:
|
|
181
180
|
return node
|
|
182
181
|
if min_node is None or (min_node.addr is not None and node.addr is not None and min_node.addr < node.addr):
|
|
183
182
|
min_node = node
|
|
@@ -392,6 +392,9 @@ class IncompleteSwitchCaseNode(BaseNode):
|
|
|
392
392
|
self.head = head
|
|
393
393
|
self.cases: list = cases
|
|
394
394
|
|
|
395
|
+
def __repr__(self):
|
|
396
|
+
return f"<IncompleteSwitchCase {self.addr:#x} with {len(self.cases)} cases>"
|
|
397
|
+
|
|
395
398
|
|
|
396
399
|
#
|
|
397
400
|
# The following classes are custom AIL statements (not nodes, unfortunately)
|
|
@@ -158,10 +158,14 @@ def switch_extract_cmp_bounds(
|
|
|
158
158
|
return None
|
|
159
159
|
|
|
160
160
|
# TODO: Add more operations
|
|
161
|
-
if isinstance(last_stmt.condition, ailment.Expr.BinaryOp) and last_stmt.condition.op
|
|
161
|
+
if isinstance(last_stmt.condition, ailment.Expr.BinaryOp) and last_stmt.condition.op in {"CmpLE", "CmpLT"}:
|
|
162
162
|
if not isinstance(last_stmt.condition.operands[1], ailment.Expr.Const):
|
|
163
163
|
return None
|
|
164
|
-
cmp_ub =
|
|
164
|
+
cmp_ub = (
|
|
165
|
+
last_stmt.condition.operands[1].value
|
|
166
|
+
if last_stmt.condition.op == "CmpLE"
|
|
167
|
+
else last_stmt.condition.operands[1].value - 1
|
|
168
|
+
)
|
|
165
169
|
cmp_lb = 0
|
|
166
170
|
cmp = last_stmt.condition.operands[0]
|
|
167
171
|
if (
|
|
@@ -250,6 +254,10 @@ def switch_extract_bitwiseand_jumptable_info(last_stmt: ailment.Stmt.Jump) -> tu
|
|
|
250
254
|
size=4, endness=Iend_LE) + 0x4530e4<32>))
|
|
251
255
|
)
|
|
252
256
|
|
|
257
|
+
Another example:
|
|
258
|
+
|
|
259
|
+
Load(addr=(((vvar_9{reg 36} & 0x3<32>) * 0x4<32>) + 0x42cd28<32>), size=4, endness=Iend_LE)
|
|
260
|
+
|
|
253
261
|
:param last_stmt: The last statement of the switch-case header node.
|
|
254
262
|
:return: A tuple of (index expression, lower bound, upper bound), or None
|
|
255
263
|
"""
|
|
@@ -269,16 +277,20 @@ def switch_extract_bitwiseand_jumptable_info(last_stmt: ailment.Stmt.Jump) -> tu
|
|
|
269
277
|
continue
|
|
270
278
|
if isinstance(target, ailment.Expr.BinaryOp) and target.op == "Add":
|
|
271
279
|
if isinstance(target.operands[0], ailment.Expr.Const) and isinstance(target.operands[1], ailment.Expr.Load):
|
|
272
|
-
jump_addr_offset = target.operands[0]
|
|
280
|
+
jump_addr_offset = target.operands[0].value
|
|
273
281
|
jumptable_load_addr = target.operands[1].addr
|
|
274
282
|
break
|
|
275
283
|
if isinstance(target.operands[1], ailment.Expr.Const) and isinstance(target.operands[0], ailment.Expr.Load):
|
|
276
|
-
jump_addr_offset = target.operands[1]
|
|
284
|
+
jump_addr_offset = target.operands[1].value
|
|
277
285
|
jumptable_load_addr = target.operands[0].addr
|
|
278
286
|
break
|
|
279
287
|
return None
|
|
280
288
|
if isinstance(target, ailment.Expr.Const):
|
|
281
289
|
return None
|
|
290
|
+
if isinstance(target, ailment.Expr.Load):
|
|
291
|
+
jumptable_load_addr = target.addr
|
|
292
|
+
jump_addr_offset = 0
|
|
293
|
+
break
|
|
282
294
|
break
|
|
283
295
|
|
|
284
296
|
if jump_addr_offset is None or jumptable_load_addr is None:
|
|
@@ -655,7 +667,7 @@ def _flatten_structured_node(packed_node: SequenceNode | MultiNode) -> list[ailm
|
|
|
655
667
|
|
|
656
668
|
def _find_node_in_graph(node: ailment.Block, graph: networkx.DiGraph) -> ailment.Block | None:
|
|
657
669
|
for bb in graph:
|
|
658
|
-
if bb.addr == node.addr and bb.idx == node.idx:
|
|
670
|
+
if isinstance(bb, ailment.Block) and bb.addr == node.addr and bb.idx == node.idx:
|
|
659
671
|
return bb
|
|
660
672
|
return None
|
|
661
673
|
|
|
@@ -37,7 +37,8 @@ class RegVVarPredicate:
|
|
|
37
37
|
if cc is not None:
|
|
38
38
|
reg_list = cc.CALLER_SAVED_REGS
|
|
39
39
|
if isinstance(cc.RETURN_VAL, SimRegArg):
|
|
40
|
-
reg_list
|
|
40
|
+
# do not update reg_list directly, otherwise you may update cc.CALLER_SAVED_REGS!
|
|
41
|
+
reg_list = [*reg_list, cc.RETURN_VAL.reg_name]
|
|
41
42
|
return {self.arch.registers[reg_name][0] for reg_name in reg_list}
|
|
42
43
|
log.warning("Cannot determine registers that are clobbered by call statement %r.", stmt)
|
|
43
44
|
return set()
|
|
@@ -245,7 +245,9 @@ class Struct(TypeConstant):
|
|
|
245
245
|
if not self.fields:
|
|
246
246
|
return 0
|
|
247
247
|
max_field_off = max(self.fields.keys())
|
|
248
|
-
return max_field_off +
|
|
248
|
+
return max_field_off + (
|
|
249
|
+
self.fields[max_field_off].size if not isinstance(self.fields[max_field_off], BottomType) else 1
|
|
250
|
+
)
|
|
249
251
|
|
|
250
252
|
@memoize
|
|
251
253
|
def __repr__(self, memo=None):
|
angr/blade.py
CHANGED
|
@@ -40,6 +40,7 @@ class Blade:
|
|
|
40
40
|
cross_insn_opt=False,
|
|
41
41
|
max_predecessors: int = 10,
|
|
42
42
|
include_imarks: bool = True,
|
|
43
|
+
control_dependence: bool = True,
|
|
43
44
|
):
|
|
44
45
|
"""
|
|
45
46
|
:param graph: A graph representing the control flow graph. Note that it does not take
|
|
@@ -56,6 +57,8 @@ class Blade:
|
|
|
56
57
|
:param stop_at_calls: Limit slicing within a single function. Do not proceed when encounters a call
|
|
57
58
|
edge.
|
|
58
59
|
:param include_imarks: Should IMarks (instruction boundaries) be included in the slice.
|
|
60
|
+
:param control_dependence: Whether to consider control dependencies. If True, the temps controlling
|
|
61
|
+
conditional exits will be added to the tainting set.
|
|
59
62
|
:return: None
|
|
60
63
|
"""
|
|
61
64
|
|
|
@@ -70,6 +73,7 @@ class Blade:
|
|
|
70
73
|
self._cross_insn_opt = cross_insn_opt
|
|
71
74
|
self._max_predecessors = max_predecessors
|
|
72
75
|
self._include_imarks = include_imarks
|
|
76
|
+
self._control_dependence = control_dependence
|
|
73
77
|
|
|
74
78
|
self._slice = networkx.DiGraph()
|
|
75
79
|
|
|
@@ -347,7 +351,7 @@ class Blade:
|
|
|
347
351
|
except (SimTranslationError, BadJumpkindNotification):
|
|
348
352
|
return
|
|
349
353
|
|
|
350
|
-
if exit_stmt_idx is None or exit_stmt_idx == DEFAULT_STATEMENT:
|
|
354
|
+
if self._control_dependence and (exit_stmt_idx is None or exit_stmt_idx == DEFAULT_STATEMENT):
|
|
351
355
|
# Initialize the temps set with whatever in the `next` attribute of this irsb
|
|
352
356
|
next_expr = self._get_irsb(run).next
|
|
353
357
|
if type(next_expr) is pyvex.IRExpr.RdTmp:
|
|
@@ -357,20 +361,21 @@ class Blade:
|
|
|
357
361
|
self._inslice_callback(DEFAULT_STATEMENT, None, {"irsb_addr": irsb_addr, "prev": prev})
|
|
358
362
|
prev = irsb_addr, DEFAULT_STATEMENT
|
|
359
363
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
364
|
+
if self._control_dependence:
|
|
365
|
+
# if there are conditional exits, we *always* add them into the slice (so if they should not be taken, we
|
|
366
|
+
# do not lose the condition)
|
|
367
|
+
for stmt_idx_, s_ in enumerate(self._get_irsb(run).statements):
|
|
368
|
+
if type(s_) is not pyvex.IRStmt.Exit:
|
|
369
|
+
continue
|
|
370
|
+
if s_.jumpkind != "Ijk_Boring":
|
|
371
|
+
continue
|
|
372
|
+
|
|
373
|
+
if type(s_.guard) is pyvex.IRExpr.RdTmp:
|
|
374
|
+
temps.add(s_.guard.tmp)
|
|
375
|
+
|
|
376
|
+
# Put it in our slice
|
|
377
|
+
self._inslice_callback(stmt_idx_, s_, {"irsb_addr": irsb_addr, "prev": prev})
|
|
378
|
+
prev = (irsb_addr, stmt_idx_)
|
|
374
379
|
|
|
375
380
|
infodict = {"irsb_addr": irsb_addr, "prev": prev, "has_statement": False}
|
|
376
381
|
|
angr/engines/icicle.py
CHANGED
|
@@ -123,7 +123,7 @@ class IcicleEngine(ConcreteEngine):
|
|
|
123
123
|
if proj is None:
|
|
124
124
|
raise ValueError("IcicleEngine requires a project to be set")
|
|
125
125
|
|
|
126
|
-
emu = Icicle(icicle_arch, PROCESSORS_DIR)
|
|
126
|
+
emu = Icicle(icicle_arch, PROCESSORS_DIR, True)
|
|
127
127
|
|
|
128
128
|
copied_registers = set()
|
|
129
129
|
|
|
@@ -194,7 +194,8 @@ class IcicleEngine(ConcreteEngine):
|
|
|
194
194
|
addr = page_num * state.memory.page_size
|
|
195
195
|
state.memory.store(addr, emu.mem_read(addr, state.memory.page_size))
|
|
196
196
|
|
|
197
|
-
# 3. Set history
|
|
197
|
+
# 3. Set history
|
|
198
|
+
# 3.1 history.jumpkind
|
|
198
199
|
exc = emu.exception_code
|
|
199
200
|
if status == VmExit.UnhandledException:
|
|
200
201
|
if exc in (
|
|
@@ -216,6 +217,10 @@ class IcicleEngine(ConcreteEngine):
|
|
|
216
217
|
else:
|
|
217
218
|
state.history.jumpkind = "Ijk_Boring"
|
|
218
219
|
|
|
220
|
+
# 3.2 history.recent_bbl_addrs
|
|
221
|
+
# Skip the last block, because it will be added by Successors
|
|
222
|
+
state.history.recent_bbl_addrs.extend([b[0] for b in emu.recent_blocks][:-1])
|
|
223
|
+
|
|
219
224
|
# 4. Set history.recent_instruction_count
|
|
220
225
|
state.history.recent_instruction_count = emu.cpu_icount - translation_data.initial_cpu_icount
|
|
221
226
|
|
|
@@ -19,6 +19,7 @@ class PropagationModel(Serializable):
|
|
|
19
19
|
"_initial_state",
|
|
20
20
|
"block_initial_reg_values",
|
|
21
21
|
"equivalence",
|
|
22
|
+
"function_block_count",
|
|
22
23
|
"graph_visitor",
|
|
23
24
|
"input_states",
|
|
24
25
|
"key",
|
|
@@ -37,6 +38,7 @@ class PropagationModel(Serializable):
|
|
|
37
38
|
equivalence: set | None = None,
|
|
38
39
|
function: Function | None = None,
|
|
39
40
|
input_states: dict | None = None,
|
|
41
|
+
function_block_count: int | None = None,
|
|
40
42
|
):
|
|
41
43
|
self.key = prop_key
|
|
42
44
|
self.node_iterations = node_iterations if node_iterations is not None else defaultdict(int)
|
|
@@ -49,6 +51,11 @@ class PropagationModel(Serializable):
|
|
|
49
51
|
self.graph_visitor = None
|
|
50
52
|
self._initial_state = None
|
|
51
53
|
self._function = function
|
|
54
|
+
self.function_block_count = (
|
|
55
|
+
function_block_count
|
|
56
|
+
if function_block_count is not None
|
|
57
|
+
else len(function.block_addrs_set) if function is not None else None
|
|
58
|
+
)
|
|
52
59
|
|
|
53
60
|
def downsize(self):
|
|
54
61
|
self.node_iterations = None
|
angr/rustylib.abi3.so
CHANGED
|
Binary file
|
angr/sim_type.py
CHANGED
|
@@ -1789,6 +1789,7 @@ class SimCppClass(SimStruct):
|
|
|
1789
1789
|
vtable_ptrs=None,
|
|
1790
1790
|
pack: bool = False,
|
|
1791
1791
|
align=None,
|
|
1792
|
+
size: int | None = None,
|
|
1792
1793
|
):
|
|
1793
1794
|
super().__init__(members or {}, name=name, pack=pack, align=align)
|
|
1794
1795
|
self.unique_name = unique_name
|
|
@@ -1797,6 +1798,10 @@ class SimCppClass(SimStruct):
|
|
|
1797
1798
|
# this should also be added to the fields once we know the offsets of the members of this object
|
|
1798
1799
|
self.vtable_ptrs = [] if vtable_ptrs is None else vtable_ptrs
|
|
1799
1800
|
|
|
1801
|
+
# we can force the size (in bits) of a class because sometimes the class can be opaque and we don't know its
|
|
1802
|
+
# layout
|
|
1803
|
+
self._size = size
|
|
1804
|
+
|
|
1800
1805
|
@property
|
|
1801
1806
|
def members(self):
|
|
1802
1807
|
return self.fields
|
|
@@ -1805,6 +1810,12 @@ class SimCppClass(SimStruct):
|
|
|
1805
1810
|
def members(self, value):
|
|
1806
1811
|
self.fields = value
|
|
1807
1812
|
|
|
1813
|
+
@property
|
|
1814
|
+
def size(self):
|
|
1815
|
+
if self._size is not None:
|
|
1816
|
+
return self._size
|
|
1817
|
+
return super().size
|
|
1818
|
+
|
|
1808
1819
|
def __repr__(self):
|
|
1809
1820
|
return f"class {self.name}" if not self.name.startswith("class") else self.name
|
|
1810
1821
|
|
|
@@ -1848,6 +1859,7 @@ class SimCppClass(SimStruct):
|
|
|
1848
1859
|
vtable_ptrs=self.vtable_ptrs,
|
|
1849
1860
|
pack=self._pack,
|
|
1850
1861
|
align=self._align,
|
|
1862
|
+
size=self._size,
|
|
1851
1863
|
)
|
|
1852
1864
|
out._arch = arch
|
|
1853
1865
|
self._arch_memo[arch.name] = out
|
|
@@ -1877,6 +1889,7 @@ class SimCppClass(SimStruct):
|
|
|
1877
1889
|
align=self._align,
|
|
1878
1890
|
function_members=self.function_members,
|
|
1879
1891
|
vtable_ptrs=self.vtable_ptrs,
|
|
1892
|
+
size=self._size,
|
|
1880
1893
|
)
|
|
1881
1894
|
|
|
1882
1895
|
|
|
@@ -2029,6 +2042,8 @@ BASIC_TYPES: dict[str, SimType] = {
|
|
|
2029
2042
|
"long long int": SimTypeLongLong(True),
|
|
2030
2043
|
"signed long long int": SimTypeLongLong(True),
|
|
2031
2044
|
"unsigned long long int": SimTypeLongLong(False),
|
|
2045
|
+
"__int32": SimTypeInt(True),
|
|
2046
|
+
"__int64": SimTypeLongLong(True),
|
|
2032
2047
|
"__int128": SimTypeNum(128, True),
|
|
2033
2048
|
"unsigned __int128": SimTypeNum(128, False),
|
|
2034
2049
|
"__int256": SimTypeNum(256, True),
|
|
@@ -3563,7 +3578,7 @@ def _cpp_decl_to_type(
|
|
|
3563
3578
|
t = ALL_TYPES[lbl]
|
|
3564
3579
|
elif opaque_classes is True:
|
|
3565
3580
|
# create a class without knowing the internal members
|
|
3566
|
-
t = SimCppClass(unique_name=lbl, name=lbl, members={})
|
|
3581
|
+
t = SimCppClass(unique_name=lbl, name=lbl, members={}, size=32)
|
|
3567
3582
|
else:
|
|
3568
3583
|
raise TypeError(f'Unknown type "{lbl}"')
|
|
3569
3584
|
|
angr/utils/constants.py
CHANGED
angr/utils/graph.py
CHANGED
|
@@ -76,7 +76,7 @@ def to_acyclic_graph(
|
|
|
76
76
|
for src, dst in graph.edges():
|
|
77
77
|
src_order = node_order[src]
|
|
78
78
|
dst_order = node_order[dst]
|
|
79
|
-
if src_order
|
|
79
|
+
if src_order >= dst_order:
|
|
80
80
|
# this is a back edge, we need to remove it
|
|
81
81
|
edges_to_remove.append((src, dst))
|
|
82
82
|
|
angr/utils/vex.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pyvex import IRSB
|
|
4
|
+
from pyvex.stmt import WrTmp
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_tmp_def_stmt(vex_block: IRSB, tmp_idx: int) -> int | None:
|
|
8
|
+
for i, stmt in enumerate(vex_block.statements):
|
|
9
|
+
if isinstance(stmt, WrTmp) and stmt.tmp == tmp_idx:
|
|
10
|
+
return i
|
|
11
|
+
return None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: angr
|
|
3
|
-
Version: 9.2.
|
|
3
|
+
Version: 9.2.164
|
|
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
|
License: BSD-2-Clause
|
|
6
6
|
Project-URL: Homepage, https://angr.io/
|
|
@@ -16,12 +16,12 @@ Description-Content-Type: text/markdown
|
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: cxxheaderparser
|
|
18
18
|
Requires-Dist: GitPython
|
|
19
|
-
Requires-Dist: archinfo==9.2.
|
|
19
|
+
Requires-Dist: archinfo==9.2.164
|
|
20
20
|
Requires-Dist: cachetools
|
|
21
21
|
Requires-Dist: capstone==5.0.3
|
|
22
22
|
Requires-Dist: cffi>=1.14.0
|
|
23
|
-
Requires-Dist: claripy==9.2.
|
|
24
|
-
Requires-Dist: cle==9.2.
|
|
23
|
+
Requires-Dist: claripy==9.2.164
|
|
24
|
+
Requires-Dist: cle==9.2.164
|
|
25
25
|
Requires-Dist: mulpyplexer
|
|
26
26
|
Requires-Dist: networkx!=2.8.1,>=2.0
|
|
27
27
|
Requires-Dist: protobuf>=5.28.2
|
|
@@ -30,7 +30,7 @@ Requires-Dist: pycparser>=2.18
|
|
|
30
30
|
Requires-Dist: pydemumble
|
|
31
31
|
Requires-Dist: pyformlang
|
|
32
32
|
Requires-Dist: pypcode<4.0,>=3.2.1
|
|
33
|
-
Requires-Dist: pyvex==9.2.
|
|
33
|
+
Requires-Dist: pyvex==9.2.164
|
|
34
34
|
Requires-Dist: rich>=13.1.0
|
|
35
35
|
Requires-Dist: sortedcontainers
|
|
36
36
|
Requires-Dist: sympy
|