angr 9.2.149__py3-none-manylinux2014_aarch64.whl → 9.2.152__py3-none-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/__main__.py +100 -37
- angr/analyses/calling_convention/calling_convention.py +17 -9
- angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +39 -0
- angr/analyses/decompiler/clinic.py +73 -1
- angr/analyses/decompiler/dephication/rewriting_engine.py +38 -1
- angr/analyses/decompiler/optimization_passes/condition_constprop.py +6 -0
- angr/analyses/decompiler/optimization_passes/engine_base.py +5 -0
- angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +2 -1
- angr/analyses/decompiler/peephole_optimizations/__init__.py +2 -0
- angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +115 -0
- angr/analyses/decompiler/ssailification/rewriting_engine.py +37 -1
- angr/analyses/decompiler/ssailification/traversal_engine.py +10 -1
- angr/analyses/decompiler/utils.py +17 -0
- angr/analyses/disassembly.py +2 -1
- angr/analyses/patchfinder.py +1 -1
- angr/analyses/reaching_definitions/engine_ail.py +20 -0
- angr/analyses/s_propagator.py +28 -0
- angr/analyses/stack_pointer_tracker.py +2 -1
- angr/analyses/typehoon/typehoon.py +4 -1
- angr/analyses/variable_recovery/engine_ail.py +9 -0
- angr/engines/light/engine.py +7 -0
- angr/engines/pcode/lifter.py +7 -0
- angr/storage/memory_mixins/clouseau_mixin.py +7 -1
- angr/utils/graph.py +61 -39
- angr/utils/ssa/__init__.py +6 -1
- {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/METADATA +6 -6
- {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/RECORD +32 -31
- {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/WHEEL +1 -1
- {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/entry_points.txt +0 -0
- {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.149.dist-info → angr-9.2.152.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# pylint:disable=no-self-use,unused-argument
|
|
1
|
+
# pylint:disable=no-self-use,unused-argument,too-many-boolean-expressions
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
from typing import Literal
|
|
4
4
|
import logging
|
|
@@ -9,6 +9,7 @@ from ailment.manager import Manager
|
|
|
9
9
|
from ailment.statement import (
|
|
10
10
|
Statement,
|
|
11
11
|
Assignment,
|
|
12
|
+
CAS,
|
|
12
13
|
Store,
|
|
13
14
|
Call,
|
|
14
15
|
Return,
|
|
@@ -18,6 +19,7 @@ from ailment.statement import (
|
|
|
18
19
|
WeakAssignment,
|
|
19
20
|
)
|
|
20
21
|
from ailment.expression import (
|
|
22
|
+
Atom,
|
|
21
23
|
Expression,
|
|
22
24
|
Register,
|
|
23
25
|
VirtualVariable,
|
|
@@ -204,6 +206,40 @@ class SimEngineSSARewriting(
|
|
|
204
206
|
)
|
|
205
207
|
return None
|
|
206
208
|
|
|
209
|
+
def _handle_stmt_CAS(self, stmt: CAS) -> CAS | None:
|
|
210
|
+
new_addr = self._expr(stmt.addr)
|
|
211
|
+
new_data_lo = self._expr(stmt.data_lo)
|
|
212
|
+
new_data_hi = self._expr(stmt.data_hi) if stmt.data_hi is not None else None
|
|
213
|
+
new_expd_lo = self._expr(stmt.expd_lo)
|
|
214
|
+
new_expd_hi = self._expr(stmt.expd_hi) if stmt.expd_hi is not None else None
|
|
215
|
+
new_old_lo = self._expr(stmt.old_lo)
|
|
216
|
+
new_old_hi = self._expr(stmt.old_hi) if stmt.old_hi is not None else None
|
|
217
|
+
assert new_old_lo is None or isinstance(new_old_lo, Atom)
|
|
218
|
+
assert new_old_hi is None or isinstance(new_old_hi, Atom)
|
|
219
|
+
|
|
220
|
+
if (
|
|
221
|
+
new_addr is not None
|
|
222
|
+
or new_old_lo is not None
|
|
223
|
+
or new_old_hi is not None
|
|
224
|
+
or new_data_lo is not None
|
|
225
|
+
or new_data_hi is not None
|
|
226
|
+
or new_expd_lo is not None
|
|
227
|
+
or new_expd_hi is not None
|
|
228
|
+
):
|
|
229
|
+
return CAS(
|
|
230
|
+
stmt.idx,
|
|
231
|
+
stmt.addr if new_addr is None else new_addr,
|
|
232
|
+
stmt.data_lo if new_data_lo is None else new_data_lo,
|
|
233
|
+
stmt.data_hi if new_data_hi is None else new_data_hi,
|
|
234
|
+
stmt.expd_lo if new_expd_lo is None else new_expd_lo,
|
|
235
|
+
stmt.expd_hi if new_expd_hi is None else new_expd_hi,
|
|
236
|
+
stmt.old_lo if new_old_lo is None else new_old_lo,
|
|
237
|
+
stmt.old_hi if new_old_hi is None else new_old_hi,
|
|
238
|
+
stmt.endness,
|
|
239
|
+
**stmt.tags,
|
|
240
|
+
)
|
|
241
|
+
return None
|
|
242
|
+
|
|
207
243
|
def _handle_stmt_Store(self, stmt: Store) -> Store | Assignment | tuple[Assignment, ...] | None:
|
|
208
244
|
new_data = self._expr(stmt.data)
|
|
209
245
|
if stmt.guard is None:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from collections import OrderedDict
|
|
3
3
|
|
|
4
|
-
from ailment.statement import Call, Store, ConditionalJump
|
|
4
|
+
from ailment.statement import Call, Store, ConditionalJump, CAS
|
|
5
5
|
from ailment.expression import Register, BinaryOp, StackBaseOffset, ITE, VEXCCallExpression, Tmp, DirtyExpression, Load
|
|
6
6
|
|
|
7
7
|
from angr.engines.light import SimEngineLightAIL
|
|
@@ -64,6 +64,15 @@ class SimEngineSSATraversal(SimEngineLightAIL[TraversalState, None, None, None])
|
|
|
64
64
|
self._expr(stmt.src)
|
|
65
65
|
self._expr(stmt.dst)
|
|
66
66
|
|
|
67
|
+
def _handle_stmt_CAS(self, stmt: CAS):
|
|
68
|
+
self._expr(stmt.addr)
|
|
69
|
+
self._expr(stmt.data_lo)
|
|
70
|
+
if stmt.data_hi is not None:
|
|
71
|
+
self._expr(stmt.data_hi)
|
|
72
|
+
self._expr(stmt.expd_lo)
|
|
73
|
+
if stmt.expd_hi is not None:
|
|
74
|
+
self._expr(stmt.expd_hi)
|
|
75
|
+
|
|
67
76
|
def _handle_stmt_Store(self, stmt: Store):
|
|
68
77
|
self._expr(stmt.addr)
|
|
69
78
|
self._expr(stmt.data)
|
|
@@ -723,6 +723,23 @@ def structured_node_is_simple_return(
|
|
|
723
723
|
return False
|
|
724
724
|
|
|
725
725
|
|
|
726
|
+
def structured_node_is_simple_return_strict(node: BaseNode | SequenceNode | MultiNode | ailment.Block) -> bool:
|
|
727
|
+
"""
|
|
728
|
+
Returns True iff the node exclusively contains a return statement.
|
|
729
|
+
"""
|
|
730
|
+
if isinstance(node, (SequenceNode, MultiNode)) and node.nodes:
|
|
731
|
+
flat_blocks = _flatten_structured_node(node)
|
|
732
|
+
if len(flat_blocks) != 1:
|
|
733
|
+
return False
|
|
734
|
+
node = flat_blocks[-1]
|
|
735
|
+
|
|
736
|
+
return (
|
|
737
|
+
isinstance(node, ailment.Block)
|
|
738
|
+
and len(node.statements) == 1
|
|
739
|
+
and isinstance(node.statements[0], ailment.Stmt.Return)
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
|
|
726
743
|
def is_statement_terminating(stmt: ailment.statement.Statement, functions) -> bool:
|
|
727
744
|
if isinstance(stmt, ailment.Stmt.Return):
|
|
728
745
|
return True
|
angr/analyses/disassembly.py
CHANGED
|
@@ -1159,6 +1159,7 @@ class Disassembly(Analysis):
|
|
|
1159
1159
|
show_bytes: bool = False,
|
|
1160
1160
|
ascii_only: bool | None = None,
|
|
1161
1161
|
color: bool = True,
|
|
1162
|
+
min_edge_depth: int = 0,
|
|
1162
1163
|
) -> str:
|
|
1163
1164
|
"""
|
|
1164
1165
|
Render the disassembly to a string, with optional edges and addresses.
|
|
@@ -1288,7 +1289,7 @@ class Disassembly(Analysis):
|
|
|
1288
1289
|
for f, t in sorted(edges_by_line, key=lambda e: abs(e[0] - e[1])):
|
|
1289
1290
|
add_edge_to_buffer(edge_buf, ref_buf, f, t, lambda s: ansi_color(s, edge_col), ascii_only=ascii_only)
|
|
1290
1291
|
add_edge_to_buffer(ref_buf, ref_buf, f, t, ascii_only=ascii_only)
|
|
1291
|
-
max_edge_depth = max(map(len, ref_buf))
|
|
1292
|
+
max_edge_depth = max(*map(len, ref_buf), min_edge_depth)
|
|
1292
1293
|
|
|
1293
1294
|
# Justify edge and combine with disassembly
|
|
1294
1295
|
for i, line in enumerate(buf):
|
angr/analyses/patchfinder.py
CHANGED
|
@@ -97,7 +97,7 @@ class PatchFinderAnalysis(Analysis):
|
|
|
97
97
|
# - Looking for instruction partials broken by a patch (nodecode)
|
|
98
98
|
# - Unusual stack manipulation
|
|
99
99
|
|
|
100
|
-
atypical_alignments: list[
|
|
100
|
+
atypical_alignments: list[AtypicallyAlignedFunction]
|
|
101
101
|
possibly_patched_out: list[PatchedOutFunctionality]
|
|
102
102
|
|
|
103
103
|
def __init__(self):
|
|
@@ -143,6 +143,26 @@ class SimEngineRDAIL(
|
|
|
143
143
|
else:
|
|
144
144
|
l.warning("Unsupported type of Assignment dst %s.", type(dst).__name__)
|
|
145
145
|
|
|
146
|
+
def _handle_stmt_CAS(self, stmt: ailment.statement.CAS):
|
|
147
|
+
addr = self._expr(stmt.addr)
|
|
148
|
+
old_lo = stmt.old_lo
|
|
149
|
+
old_hi = stmt.old_hi
|
|
150
|
+
|
|
151
|
+
self._expr(stmt.data_lo)
|
|
152
|
+
if stmt.data_hi is not None:
|
|
153
|
+
self._expr(stmt.data_hi)
|
|
154
|
+
self._expr(stmt.expd_lo)
|
|
155
|
+
if stmt.expd_hi is not None:
|
|
156
|
+
self._expr(stmt.expd_hi)
|
|
157
|
+
|
|
158
|
+
if isinstance(old_lo, ailment.Tmp):
|
|
159
|
+
self.state.kill_and_add_definition(Tmp(old_lo.tmp_idx, old_lo.size), addr)
|
|
160
|
+
self.tmps[old_lo.tmp_idx] = self._top(old_lo.size)
|
|
161
|
+
|
|
162
|
+
if isinstance(old_hi, ailment.Tmp):
|
|
163
|
+
self.state.kill_and_add_definition(Tmp(old_hi.tmp_idx, old_hi.size), addr)
|
|
164
|
+
self.tmps[old_hi.tmp_idx] = self._top(old_hi.size)
|
|
165
|
+
|
|
146
166
|
def _handle_stmt_Store(self, stmt: ailment.Stmt.Store) -> None:
|
|
147
167
|
data = self._expr(stmt.data)
|
|
148
168
|
addr = self._expr_bv(stmt.addr)
|
angr/analyses/s_propagator.py
CHANGED
|
@@ -511,5 +511,33 @@ class SPropagatorAnalysis(Analysis):
|
|
|
511
511
|
}
|
|
512
512
|
return (block_1.addr, block_1.idx) in stmt_0_targets
|
|
513
513
|
|
|
514
|
+
@staticmethod
|
|
515
|
+
def vvar_dep_graph(blocks, vvar_def_locs, vvar_use_locs) -> networkx.DiGraph:
|
|
516
|
+
g = networkx.DiGraph()
|
|
517
|
+
|
|
518
|
+
for var_id in vvar_def_locs:
|
|
519
|
+
# where is it used?
|
|
520
|
+
for _, use_loc in vvar_use_locs[var_id]:
|
|
521
|
+
if isinstance(use_loc, ExternalCodeLocation):
|
|
522
|
+
g.add_edge(var_id, "ExternalCodeLocation")
|
|
523
|
+
continue
|
|
524
|
+
assert use_loc.block_addr is not None
|
|
525
|
+
assert use_loc.stmt_idx is not None
|
|
526
|
+
block = blocks[(use_loc.block_addr, use_loc.block_idx)]
|
|
527
|
+
stmt = block.statements[use_loc.stmt_idx]
|
|
528
|
+
if isinstance(stmt, Assignment):
|
|
529
|
+
if isinstance(stmt.dst, VirtualVariable):
|
|
530
|
+
g.add_edge(var_id, stmt.dst.varid)
|
|
531
|
+
else:
|
|
532
|
+
g.add_edge(var_id, f"Assignment@{stmt.ins_addr:#x}")
|
|
533
|
+
elif isinstance(stmt, Store):
|
|
534
|
+
# store to memory
|
|
535
|
+
g.add_edge(var_id, f"Store@{stmt.ins_addr:#x}")
|
|
536
|
+
else:
|
|
537
|
+
# other statements
|
|
538
|
+
g.add_edge(var_id, f"{stmt.__class__.__name__}@{stmt.ins_addr:#x}")
|
|
539
|
+
|
|
540
|
+
return g
|
|
541
|
+
|
|
514
542
|
|
|
515
543
|
register_analysis(SPropagatorAnalysis, "SPropagator")
|
|
@@ -791,7 +791,8 @@ class StackPointerTracker(Analysis, ForwardAnalysis):
|
|
|
791
791
|
sp_adjusted = True
|
|
792
792
|
sp_v = state.regs[self.project.arch.sp_offset]
|
|
793
793
|
sp_v -= Constant(stmt.data.con.value)
|
|
794
|
-
state.put(self.project.arch.sp_offset, sp_v, force=True)
|
|
794
|
+
state.put(self.project.arch.sp_offset, sp_v, force=True) # sp -= OFFSET
|
|
795
|
+
state.put(stmt.offset, Constant(0), force=True) # rax = 0
|
|
795
796
|
break
|
|
796
797
|
|
|
797
798
|
callee_cleanups = [
|
|
@@ -263,7 +263,10 @@ class Typehoon(Analysis):
|
|
|
263
263
|
max_offset = offsets[-1]
|
|
264
264
|
field0_size = 1
|
|
265
265
|
if not isinstance(field0, TopType):
|
|
266
|
-
|
|
266
|
+
try:
|
|
267
|
+
field0_size = field0.size
|
|
268
|
+
except NotImplementedError:
|
|
269
|
+
field0_size = 1
|
|
267
270
|
count = (max_offset + field0_size) // alignment
|
|
268
271
|
return Array(field0, count=count)
|
|
269
272
|
|
|
@@ -115,6 +115,15 @@ class SimEngineVRAIL(
|
|
|
115
115
|
tc = typevars.Subtype(src.typevar, dst.typevar)
|
|
116
116
|
self.state.add_type_constraint(tc)
|
|
117
117
|
|
|
118
|
+
def _handle_stmt_CAS(self, stmt) -> None:
|
|
119
|
+
self._expr(stmt.addr)
|
|
120
|
+
self._expr(stmt.data_lo)
|
|
121
|
+
if stmt.data_hi is not None:
|
|
122
|
+
self._expr(stmt.data_hi)
|
|
123
|
+
self._expr(stmt.expd_lo)
|
|
124
|
+
if stmt.expd_hi is not None:
|
|
125
|
+
self._expr(stmt.expd_hi)
|
|
126
|
+
|
|
118
127
|
def _handle_stmt_Store(self, stmt: ailment.Stmt.Store):
|
|
119
128
|
addr_r = self._expr_bv(stmt.addr)
|
|
120
129
|
data = self._expr(stmt.data)
|
angr/engines/light/engine.py
CHANGED
|
@@ -533,6 +533,7 @@ class SimEngineLightAIL(
|
|
|
533
533
|
def __init__(self, *args, **kwargs):
|
|
534
534
|
self._stmt_handlers: dict[str, Callable[[Any], StmtDataType]] = {
|
|
535
535
|
"Assignment": self._handle_stmt_Assignment,
|
|
536
|
+
"CAS": self._handle_stmt_CAS,
|
|
536
537
|
"WeakAssignment": self._handle_stmt_WeakAssignment,
|
|
537
538
|
"Store": self._handle_stmt_Store,
|
|
538
539
|
"Jump": self._handle_stmt_Jump,
|
|
@@ -698,6 +699,9 @@ class SimEngineLightAIL(
|
|
|
698
699
|
@abstractmethod
|
|
699
700
|
def _handle_stmt_Assignment(self, stmt: ailment.statement.Assignment) -> StmtDataType: ...
|
|
700
701
|
|
|
702
|
+
@abstractmethod
|
|
703
|
+
def _handle_stmt_CAS(self, stmt: ailment.statement.CAS) -> StmtDataType: ...
|
|
704
|
+
|
|
701
705
|
@abstractmethod
|
|
702
706
|
def _handle_stmt_WeakAssignment(self, stmt: ailment.statement.WeakAssignment) -> StmtDataType: ...
|
|
703
707
|
|
|
@@ -1013,6 +1017,9 @@ class SimEngineNostmtAIL(
|
|
|
1013
1017
|
def _handle_stmt_WeakAssignment(self, stmt) -> StmtDataType | None:
|
|
1014
1018
|
pass
|
|
1015
1019
|
|
|
1020
|
+
def _handle_stmt_CAS(self, stmt) -> StmtDataType | None:
|
|
1021
|
+
pass
|
|
1022
|
+
|
|
1016
1023
|
def _handle_stmt_Store(self, stmt) -> StmtDataType | None:
|
|
1017
1024
|
pass
|
|
1018
1025
|
|
angr/engines/pcode/lifter.py
CHANGED
|
@@ -427,6 +427,13 @@ class IRSB:
|
|
|
427
427
|
|
|
428
428
|
return exits
|
|
429
429
|
|
|
430
|
+
@property
|
|
431
|
+
def is_noop_block(self) -> bool:
|
|
432
|
+
"""
|
|
433
|
+
Returns True if this block is a no-op block (i.e. it has no instructions and no jumps).
|
|
434
|
+
"""
|
|
435
|
+
return not any(op.opcode != pypcode.OpCode.IMARK for op in self._ops)
|
|
436
|
+
|
|
430
437
|
#
|
|
431
438
|
# private methods
|
|
432
439
|
#
|
|
@@ -68,6 +68,7 @@ class InspectMixinHigh(MemoryMixin):
|
|
|
68
68
|
if not inspect or not self.state.supports_inspect:
|
|
69
69
|
return super().load(addr, size=size, condition=condition, endness=endness, inspect=inspect, **kwargs)
|
|
70
70
|
|
|
71
|
+
r = None
|
|
71
72
|
if self.category == "reg":
|
|
72
73
|
self.state._inspect(
|
|
73
74
|
"reg_read",
|
|
@@ -76,7 +77,9 @@ class InspectMixinHigh(MemoryMixin):
|
|
|
76
77
|
reg_read_length=size,
|
|
77
78
|
reg_read_condition=condition,
|
|
78
79
|
reg_read_endness=endness,
|
|
80
|
+
reg_read_expr=None,
|
|
79
81
|
)
|
|
82
|
+
r = self.state._inspect_getattr("reg_read_expr", None)
|
|
80
83
|
addr = self.state._inspect_getattr("reg_read_offset", addr)
|
|
81
84
|
size = self.state._inspect_getattr("reg_read_length", size)
|
|
82
85
|
condition = self.state._inspect_getattr("reg_read_condition", condition)
|
|
@@ -89,13 +92,16 @@ class InspectMixinHigh(MemoryMixin):
|
|
|
89
92
|
mem_read_length=size,
|
|
90
93
|
mem_read_condition=condition,
|
|
91
94
|
mem_read_endness=endness,
|
|
95
|
+
mem_read_expr=None,
|
|
92
96
|
)
|
|
97
|
+
r = self.state._inspect_getattr("mem_read_expr", None)
|
|
93
98
|
addr = self.state._inspect_getattr("mem_read_address", addr)
|
|
94
99
|
size = self.state._inspect_getattr("mem_read_length", size)
|
|
95
100
|
condition = self.state._inspect_getattr("mem_read_condition", condition)
|
|
96
101
|
endness = self.state._inspect_getattr("mem_read_endness", endness)
|
|
97
102
|
|
|
98
|
-
r
|
|
103
|
+
if r is None:
|
|
104
|
+
r = super().load(addr, size=size, condition=condition, endness=endness, inspect=inspect, **kwargs)
|
|
99
105
|
|
|
100
106
|
if self.category == "mem":
|
|
101
107
|
self.state._inspect(
|
angr/utils/graph.py
CHANGED
|
@@ -672,9 +672,40 @@ class GraphUtils:
|
|
|
672
672
|
addrs_to_index = {n.addr: i for (i, n) in enumerate(post_order)}
|
|
673
673
|
return sorted(nodes, key=lambda n: addrs_to_index[n.addr], reverse=True)
|
|
674
674
|
|
|
675
|
+
@staticmethod
|
|
676
|
+
def _sort_node(node):
|
|
677
|
+
"""
|
|
678
|
+
A sorter to make a deterministic order of nodes.
|
|
679
|
+
"""
|
|
680
|
+
if hasattr(node, "addr"):
|
|
681
|
+
return node.addr
|
|
682
|
+
return node
|
|
683
|
+
|
|
684
|
+
@staticmethod
|
|
685
|
+
def _sort_edge(edge):
|
|
686
|
+
"""
|
|
687
|
+
A sorter to make a deterministic order of edges.
|
|
688
|
+
"""
|
|
689
|
+
_src, _dst = edge
|
|
690
|
+
src_addr, dst_addr = 0, 0
|
|
691
|
+
if hasattr(_src, "addr"):
|
|
692
|
+
src_addr = _src.addr
|
|
693
|
+
elif isinstance(_src, int):
|
|
694
|
+
src_addr = _src
|
|
695
|
+
|
|
696
|
+
if hasattr(_dst, "addr"):
|
|
697
|
+
dst_addr = _dst.addr
|
|
698
|
+
elif isinstance(_dst, int):
|
|
699
|
+
dst_addr = _dst
|
|
700
|
+
|
|
701
|
+
return src_addr + dst_addr
|
|
702
|
+
|
|
675
703
|
@staticmethod
|
|
676
704
|
def quasi_topological_sort_nodes(
|
|
677
|
-
graph: networkx.DiGraph,
|
|
705
|
+
graph: networkx.DiGraph,
|
|
706
|
+
nodes: list | None = None,
|
|
707
|
+
loop_heads: list | None = None,
|
|
708
|
+
panic_mode_threshold: int = 3000,
|
|
678
709
|
) -> list:
|
|
679
710
|
"""
|
|
680
711
|
Sort a given set of nodes from a graph based on the following rules:
|
|
@@ -688,6 +719,7 @@ class GraphUtils:
|
|
|
688
719
|
:param graph: A local transition graph of the function.
|
|
689
720
|
:param nodes: A list of nodes to sort. None if you want to sort all nodes inside the graph.
|
|
690
721
|
:param loop_heads: A list of nodes that should be treated loop heads.
|
|
722
|
+
:param panic_mode_threshold: Threshold of nodes in an SCC to begin aggressively removing edges.
|
|
691
723
|
:return: A list of ordered nodes.
|
|
692
724
|
"""
|
|
693
725
|
|
|
@@ -702,32 +734,18 @@ class GraphUtils:
|
|
|
702
734
|
|
|
703
735
|
# find all strongly connected components in the graph
|
|
704
736
|
sccs = [scc for scc in networkx.strongly_connected_components(graph) if len(scc) > 1]
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
_src, _dst = edge
|
|
711
|
-
src_addr, dst_addr = 0, 0
|
|
712
|
-
if hasattr(_src, "addr"):
|
|
713
|
-
src_addr = _src.addr
|
|
714
|
-
elif isinstance(_src, int):
|
|
715
|
-
src_addr = _src
|
|
716
|
-
|
|
717
|
-
if hasattr(_dst, "addr"):
|
|
718
|
-
dst_addr = _dst.addr
|
|
719
|
-
elif isinstance(_dst, int):
|
|
720
|
-
dst_addr = _dst
|
|
721
|
-
|
|
722
|
-
return src_addr + dst_addr
|
|
737
|
+
comp_indices = {}
|
|
738
|
+
for i, scc in enumerate(sccs):
|
|
739
|
+
for node in scc:
|
|
740
|
+
if node not in comp_indices:
|
|
741
|
+
comp_indices[node] = i
|
|
723
742
|
|
|
724
743
|
# collapse all strongly connected components
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
scc_index = GraphUtils._components_index_node(sccs, src)
|
|
744
|
+
for src, dst in sorted(graph.edges(), key=GraphUtils._sort_edge):
|
|
745
|
+
scc_index = comp_indices.get(src)
|
|
728
746
|
if scc_index is not None:
|
|
729
747
|
src = SCCPlaceholder(scc_index)
|
|
730
|
-
scc_index =
|
|
748
|
+
scc_index = comp_indices.get(dst)
|
|
731
749
|
if scc_index is not None:
|
|
732
750
|
dst = SCCPlaceholder(scc_index)
|
|
733
751
|
|
|
@@ -754,7 +772,13 @@ class GraphUtils:
|
|
|
754
772
|
ordered_nodes = []
|
|
755
773
|
for n in tmp_nodes:
|
|
756
774
|
if isinstance(n, SCCPlaceholder):
|
|
757
|
-
GraphUtils._append_scc(
|
|
775
|
+
GraphUtils._append_scc(
|
|
776
|
+
graph,
|
|
777
|
+
ordered_nodes,
|
|
778
|
+
sccs[n.scc_id],
|
|
779
|
+
loop_head_candidates=loop_heads,
|
|
780
|
+
panic_mode_threshold=panic_mode_threshold,
|
|
781
|
+
)
|
|
758
782
|
else:
|
|
759
783
|
ordered_nodes.append(n)
|
|
760
784
|
|
|
@@ -762,16 +786,13 @@ class GraphUtils:
|
|
|
762
786
|
return ordered_nodes
|
|
763
787
|
return [n for n in ordered_nodes if n in set(nodes)]
|
|
764
788
|
|
|
765
|
-
@staticmethod
|
|
766
|
-
def _components_index_node(components, node):
|
|
767
|
-
for i, comp in enumerate(components):
|
|
768
|
-
if node in comp:
|
|
769
|
-
return i
|
|
770
|
-
return None
|
|
771
|
-
|
|
772
789
|
@staticmethod
|
|
773
790
|
def _append_scc(
|
|
774
|
-
graph: networkx.DiGraph,
|
|
791
|
+
graph: networkx.DiGraph,
|
|
792
|
+
ordered_nodes: list,
|
|
793
|
+
scc: set,
|
|
794
|
+
loop_head_candidates: list | None = None,
|
|
795
|
+
panic_mode_threshold: int = 3000,
|
|
775
796
|
) -> None:
|
|
776
797
|
"""
|
|
777
798
|
Append all nodes from a strongly connected component to a list of ordered nodes and ensure the topological
|
|
@@ -780,15 +801,16 @@ class GraphUtils:
|
|
|
780
801
|
:param graph: The graph where all nodes belong to.
|
|
781
802
|
:param ordered_nodes: Ordered nodes.
|
|
782
803
|
:param scc: A set of nodes that forms a strongly connected component in the graph.
|
|
804
|
+
:param panic_mode_threshold: Threshold of nodes in an SCC to begin aggressively removing edges.
|
|
783
805
|
"""
|
|
784
806
|
|
|
785
807
|
loop_head = None
|
|
786
808
|
|
|
787
809
|
if loop_head_candidates is not None:
|
|
788
810
|
# find the first node that appears in loop_heads
|
|
789
|
-
|
|
811
|
+
loop_head_candidates_set = set(loop_head_candidates)
|
|
790
812
|
for n in scc:
|
|
791
|
-
if n in
|
|
813
|
+
if n in loop_head_candidates_set:
|
|
792
814
|
loop_head = n
|
|
793
815
|
break
|
|
794
816
|
|
|
@@ -817,10 +839,10 @@ class GraphUtils:
|
|
|
817
839
|
break
|
|
818
840
|
|
|
819
841
|
if loop_head is None:
|
|
820
|
-
#
|
|
821
|
-
loop_head =
|
|
842
|
+
# pick the first one
|
|
843
|
+
loop_head = sorted(scc, key=GraphUtils._sort_node)[0]
|
|
822
844
|
|
|
823
|
-
subgraph: networkx.DiGraph = graph.subgraph(scc).copy()
|
|
845
|
+
subgraph: networkx.DiGraph = graph.subgraph(scc).copy() # type: ignore
|
|
824
846
|
for src, _ in list(subgraph.in_edges(loop_head)):
|
|
825
847
|
subgraph.remove_edge(src, loop_head)
|
|
826
848
|
|
|
@@ -828,8 +850,8 @@ class GraphUtils:
|
|
|
828
850
|
# will take too long to converge if we only remove one node out of the component each time. we introduce a
|
|
829
851
|
# panic mode that will aggressively remove edges
|
|
830
852
|
|
|
831
|
-
if len(subgraph) >
|
|
832
|
-
for n0, n1 in sorted(dfs_back_edges(subgraph, loop_head), key=
|
|
853
|
+
if len(subgraph) > panic_mode_threshold and len(subgraph.edges) > len(subgraph) * 1.4:
|
|
854
|
+
for n0, n1 in sorted(dfs_back_edges(subgraph, loop_head), key=GraphUtils._sort_edge):
|
|
833
855
|
subgraph.remove_edge(n0, n1)
|
|
834
856
|
if len(subgraph.edges) <= len(subgraph) * 1.4:
|
|
835
857
|
break
|
angr/utils/ssa/__init__.py
CHANGED
|
@@ -8,7 +8,7 @@ import networkx
|
|
|
8
8
|
import archinfo
|
|
9
9
|
from ailment import Expression, Block
|
|
10
10
|
from ailment.expression import VirtualVariable, Const, Phi, Tmp, Load, Register, StackBaseOffset, DirtyExpression, ITE
|
|
11
|
-
from ailment.statement import Statement, Assignment, Call, Store
|
|
11
|
+
from ailment.statement import Statement, Assignment, Call, Store, CAS
|
|
12
12
|
from ailment.block_walker import AILBlockWalkerBase
|
|
13
13
|
|
|
14
14
|
from angr.knowledge_plugins.key_definitions import atoms
|
|
@@ -126,6 +126,11 @@ def get_tmp_deflocs(blocks) -> dict[CodeLocation, dict[atoms.Tmp, int]]:
|
|
|
126
126
|
for stmt_idx, stmt in enumerate(block.statements):
|
|
127
127
|
if isinstance(stmt, Assignment) and isinstance(stmt.dst, Tmp):
|
|
128
128
|
tmp_to_loc[codeloc][atoms.Tmp(stmt.dst.tmp_idx, stmt.dst.bits)] = stmt_idx
|
|
129
|
+
if isinstance(stmt, CAS):
|
|
130
|
+
if isinstance(stmt.old_lo, Tmp):
|
|
131
|
+
tmp_to_loc[codeloc][atoms.Tmp(stmt.old_lo.tmp_idx, stmt.old_lo.bits)] = stmt_idx
|
|
132
|
+
if stmt.old_hi is not None and isinstance(stmt.old_hi, Tmp):
|
|
133
|
+
tmp_to_loc[codeloc][atoms.Tmp(stmt.old_hi.tmp_idx, stmt.old_hi.bits)] = stmt_idx
|
|
129
134
|
|
|
130
135
|
return tmp_to_loc
|
|
131
136
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: angr
|
|
3
|
-
Version: 9.2.
|
|
3
|
+
Version: 9.2.152
|
|
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/
|
|
@@ -17,13 +17,13 @@ Description-Content-Type: text/markdown
|
|
|
17
17
|
License-File: LICENSE
|
|
18
18
|
Requires-Dist: cxxheaderparser
|
|
19
19
|
Requires-Dist: GitPython
|
|
20
|
-
Requires-Dist: ailment==9.2.
|
|
21
|
-
Requires-Dist: archinfo==9.2.
|
|
20
|
+
Requires-Dist: ailment==9.2.152
|
|
21
|
+
Requires-Dist: archinfo==9.2.152
|
|
22
22
|
Requires-Dist: cachetools
|
|
23
23
|
Requires-Dist: capstone==5.0.3
|
|
24
24
|
Requires-Dist: cffi>=1.14.0
|
|
25
|
-
Requires-Dist: claripy==9.2.
|
|
26
|
-
Requires-Dist: cle==9.2.
|
|
25
|
+
Requires-Dist: claripy==9.2.152
|
|
26
|
+
Requires-Dist: cle==9.2.152
|
|
27
27
|
Requires-Dist: mulpyplexer
|
|
28
28
|
Requires-Dist: networkx!=2.8.1,>=2.0
|
|
29
29
|
Requires-Dist: protobuf>=5.28.2
|
|
@@ -31,7 +31,7 @@ Requires-Dist: psutil
|
|
|
31
31
|
Requires-Dist: pycparser>=2.18
|
|
32
32
|
Requires-Dist: pydemumble
|
|
33
33
|
Requires-Dist: pyformlang
|
|
34
|
-
Requires-Dist: pyvex==9.2.
|
|
34
|
+
Requires-Dist: pyvex==9.2.152
|
|
35
35
|
Requires-Dist: rich>=13.1.0
|
|
36
36
|
Requires-Dist: sortedcontainers
|
|
37
37
|
Requires-Dist: sympy
|