angr 9.2.152__py3-none-manylinux2014_x86_64.whl → 9.2.154__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/analysis.py +3 -3
- angr/analyses/calling_convention/fact_collector.py +8 -14
- angr/analyses/cfg/cfg_base.py +1 -1
- angr/analyses/cfg/cfg_fast.py +40 -1
- angr/analyses/decompiler/ail_simplifier.py +0 -1
- angr/analyses/decompiler/callsite_maker.py +17 -17
- angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +210 -1
- angr/analyses/decompiler/clinic.py +51 -13
- angr/analyses/decompiler/decompilation_cache.py +1 -1
- angr/analyses/decompiler/region_identifier.py +171 -119
- angr/analyses/decompiler/ssailification/ssailification.py +1 -1
- angr/analyses/decompiler/structured_codegen/c.py +15 -15
- angr/analyses/decompiler/structuring/phoenix.py +28 -0
- angr/analyses/decompiler/structuring/structurer_nodes.py +11 -0
- angr/analyses/reaching_definitions/function_handler.py +13 -19
- angr/analyses/smc.py +3 -1
- angr/analyses/stack_pointer_tracker.py +7 -1
- angr/analyses/typehoon/simple_solver.py +143 -81
- angr/analyses/typehoon/typehoon.py +2 -1
- angr/analyses/variable_recovery/engine_ail.py +14 -25
- angr/analyses/variable_recovery/engine_base.py +1 -1
- angr/knowledge_plugins/functions/function.py +10 -4
- angr/sim_type.py +11 -70
- angr/utils/types.py +93 -1
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/METADATA +6 -6
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/RECORD +31 -31
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/WHEEL +1 -1
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/entry_points.txt +0 -0
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/top_level.txt +0 -0
|
@@ -17,6 +17,7 @@ from angr.knowledge_plugins import Function
|
|
|
17
17
|
from angr.block import BlockNode
|
|
18
18
|
from angr.errors import SimTranslationError
|
|
19
19
|
from angr.calling_conventions import SimStackArg
|
|
20
|
+
from angr.utils.types import dereference_simtype_by_lib
|
|
20
21
|
|
|
21
22
|
from .analysis import Analysis
|
|
22
23
|
|
|
@@ -812,10 +813,15 @@ class StackPointerTracker(Analysis, ForwardAnalysis):
|
|
|
812
813
|
if v is BOTTOM:
|
|
813
814
|
incremented = BOTTOM
|
|
814
815
|
elif callee.prototype is not None:
|
|
816
|
+
proto = (
|
|
817
|
+
dereference_simtype_by_lib(callee.prototype, callee.prototype_libname)
|
|
818
|
+
if callee.prototype_libname
|
|
819
|
+
else callee.prototype
|
|
820
|
+
)
|
|
815
821
|
num_stack_args = len(
|
|
816
822
|
[
|
|
817
823
|
arg_loc
|
|
818
|
-
for arg_loc in callee.calling_convention.arg_locs(
|
|
824
|
+
for arg_loc in callee.calling_convention.arg_locs(proto)
|
|
819
825
|
if isinstance(arg_loc, SimStackArg)
|
|
820
826
|
]
|
|
821
827
|
)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import enum
|
|
4
4
|
from collections import defaultdict
|
|
5
|
+
from contextlib import suppress
|
|
5
6
|
import logging
|
|
6
7
|
|
|
7
8
|
import networkx
|
|
@@ -51,23 +52,6 @@ from .dfa import DFAConstraintSolver, EmptyEpsilonNFAError
|
|
|
51
52
|
_l = logging.getLogger(__name__)
|
|
52
53
|
|
|
53
54
|
|
|
54
|
-
PRIMITIVE_TYPES = {
|
|
55
|
-
TopType(),
|
|
56
|
-
Int(),
|
|
57
|
-
Int8(),
|
|
58
|
-
Int16(),
|
|
59
|
-
Int32(),
|
|
60
|
-
Int64(),
|
|
61
|
-
Pointer32(),
|
|
62
|
-
Pointer64(),
|
|
63
|
-
BottomType(),
|
|
64
|
-
Struct(),
|
|
65
|
-
Array(),
|
|
66
|
-
Float(),
|
|
67
|
-
Float32(),
|
|
68
|
-
Float64(),
|
|
69
|
-
}
|
|
70
|
-
|
|
71
55
|
Top_ = TopType()
|
|
72
56
|
Int_ = Int()
|
|
73
57
|
Int64_ = Int64()
|
|
@@ -83,6 +67,25 @@ Float_ = Float()
|
|
|
83
67
|
Float32_ = Float32()
|
|
84
68
|
Float64_ = Float64()
|
|
85
69
|
|
|
70
|
+
|
|
71
|
+
PRIMITIVE_TYPES = {
|
|
72
|
+
Top_,
|
|
73
|
+
Int_,
|
|
74
|
+
Int8_,
|
|
75
|
+
Int16_,
|
|
76
|
+
Int32_,
|
|
77
|
+
Int64_,
|
|
78
|
+
Pointer32_,
|
|
79
|
+
Pointer64_,
|
|
80
|
+
Bottom_,
|
|
81
|
+
Struct_,
|
|
82
|
+
Array_,
|
|
83
|
+
Float_,
|
|
84
|
+
Float32_,
|
|
85
|
+
Float64_,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
86
89
|
# lattice for 64-bit binaries
|
|
87
90
|
BASE_LATTICE_64 = networkx.DiGraph()
|
|
88
91
|
BASE_LATTICE_64.add_edge(Top_, Int_)
|
|
@@ -149,6 +152,24 @@ class SketchNode(SketchNodeBase):
|
|
|
149
152
|
def __hash__(self):
|
|
150
153
|
return hash((SketchNode, self.typevar))
|
|
151
154
|
|
|
155
|
+
@property
|
|
156
|
+
def size(self) -> int | None:
|
|
157
|
+
"""
|
|
158
|
+
Best-effort estimation of the size of the typevar (in bits). Returns None if we cannot determine.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
if isinstance(self.typevar, DerivedTypeVariable):
|
|
162
|
+
last_label = self.typevar.labels[-1]
|
|
163
|
+
if isinstance(last_label, HasField) and last_label.bits != MAX_POINTSTO_BITS:
|
|
164
|
+
return last_label.bits
|
|
165
|
+
if isinstance(self.lower_bound, TypeConstant) and not isinstance(self.lower_bound, (TopType, BottomType)):
|
|
166
|
+
with suppress(NotImplementedError):
|
|
167
|
+
return self.lower_bound.size * 8
|
|
168
|
+
if isinstance(self.upper_bound, TypeConstant) and not isinstance(self.upper_bound, (TopType, BottomType)):
|
|
169
|
+
with suppress(NotImplementedError):
|
|
170
|
+
return self.upper_bound.size * 8
|
|
171
|
+
return None
|
|
172
|
+
|
|
152
173
|
|
|
153
174
|
class RecursiveRefNode(SketchNodeBase):
|
|
154
175
|
"""
|
|
@@ -433,9 +454,8 @@ class SimpleSolver:
|
|
|
433
454
|
if isinstance(tv, TypeVariable) and isinstance(sol, TypeConstant):
|
|
434
455
|
self.solution[tv] = sol
|
|
435
456
|
|
|
436
|
-
equ_classes, sketches, _ = self.solve()
|
|
437
457
|
self._solution_cache = {}
|
|
438
|
-
self.
|
|
458
|
+
self.solve()
|
|
439
459
|
for typevar in list(self._constraints):
|
|
440
460
|
self._convert_arrays(self._constraints[typevar])
|
|
441
461
|
|
|
@@ -448,6 +468,11 @@ class SimpleSolver:
|
|
|
448
468
|
- Build the constraint graph
|
|
449
469
|
- Collect all constraints
|
|
450
470
|
- Apply constraints to derive the lower and upper bounds
|
|
471
|
+
- Determine a solution for type variables with constraints
|
|
472
|
+
- Rewrite the constraint graph by replacing determined type variables with their solutions
|
|
473
|
+
- Solve repeatedly until all interesting type variables have solutions
|
|
474
|
+
|
|
475
|
+
By repeatedly solving until exhausting interesting type variables, we ensure the S-Trans rule is applied.
|
|
451
476
|
"""
|
|
452
477
|
|
|
453
478
|
prem_typevars = set(self._constraints) | self._typevars
|
|
@@ -476,11 +501,7 @@ class SimpleSolver:
|
|
|
476
501
|
elif isinstance(t, TypeVariable) and t in typevars:
|
|
477
502
|
constrained_typevars.add(t)
|
|
478
503
|
|
|
479
|
-
|
|
480
|
-
# TODO: Handle global variables
|
|
481
|
-
|
|
482
|
-
type_schemes = constraints
|
|
483
|
-
|
|
504
|
+
_, sketches = self.infer_shapes(typevars, constraints)
|
|
484
505
|
constraintset2tvs = defaultdict(set)
|
|
485
506
|
for idx, tv in enumerate(constrained_typevars):
|
|
486
507
|
_l.debug("Collecting constraints for type variable %r (%d/%d)", tv, idx + 1, len(constrained_typevars))
|
|
@@ -490,20 +511,43 @@ class SimpleSolver:
|
|
|
490
511
|
|
|
491
512
|
for idx, (constraint_subset, tvs) in enumerate(constraintset2tvs.items()):
|
|
492
513
|
_l.debug(
|
|
493
|
-
"Solving %d constraints for type variables %r (%d/%d)",
|
|
514
|
+
"Solving %d constraints for %d type variables %r (%d/%d)",
|
|
494
515
|
len(constraint_subset),
|
|
516
|
+
len(tvs),
|
|
495
517
|
tvs,
|
|
496
518
|
idx + 1,
|
|
497
519
|
len(constraintset2tvs),
|
|
498
520
|
)
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
primitive_constraints = self._generate_primitive_constraints(
|
|
521
|
+
|
|
522
|
+
while True:
|
|
523
|
+
base_constraint_graph = self._generate_constraint_graph(constraint_subset, tvs | PRIMITIVE_TYPES)
|
|
524
|
+
primitive_constraints = self._generate_primitive_constraints(tvs, base_constraint_graph)
|
|
525
|
+
tvs_with_primitive_constraints = set()
|
|
503
526
|
for primitive_constraint in primitive_constraints:
|
|
527
|
+
tv = self._typevar_from_primitive_constraint(primitive_constraint)
|
|
528
|
+
tvs_with_primitive_constraints.add(tv)
|
|
529
|
+
assert tv is not None, f"Cannot find type variable in primitive constraint {primitive_constraint}"
|
|
504
530
|
sketches[tv].add_constraint(primitive_constraint)
|
|
505
|
-
|
|
506
|
-
|
|
531
|
+
solutions = {}
|
|
532
|
+
self.determine(sketches, tvs_with_primitive_constraints, solutions)
|
|
533
|
+
_l.debug("Determined solutions for %d type variable(s).", len(tvs_with_primitive_constraints))
|
|
534
|
+
if not solutions:
|
|
535
|
+
break
|
|
536
|
+
|
|
537
|
+
self.solution |= solutions
|
|
538
|
+
|
|
539
|
+
tvs = {tv for tv in tvs if tv not in tvs_with_primitive_constraints}
|
|
540
|
+
if not tvs:
|
|
541
|
+
break
|
|
542
|
+
# rewrite existing constraints
|
|
543
|
+
new_constraint_subset = set()
|
|
544
|
+
for constraint in constraint_subset:
|
|
545
|
+
rewritten = self._rewrite_constraint(constraint, solutions)
|
|
546
|
+
new_constraint_subset.add(rewritten)
|
|
547
|
+
constraint_subset = new_constraint_subset
|
|
548
|
+
|
|
549
|
+
# set the solution for missing type vars to TOP
|
|
550
|
+
self.determine(sketches, set(sketches).difference(set(self.solution)), self.solution)
|
|
507
551
|
|
|
508
552
|
def infer_shapes(
|
|
509
553
|
self, typevars: set[TypeVariable], constraints: set[TypeConstraint]
|
|
@@ -593,7 +637,6 @@ class SimpleSolver:
|
|
|
593
637
|
non_primitive_endpoints: set[TypeVariable | DerivedTypeVariable],
|
|
594
638
|
constraint_graph,
|
|
595
639
|
) -> set[TypeConstraint]:
|
|
596
|
-
# FIXME: Extract interesting variables
|
|
597
640
|
constraints_0 = self._solve_constraints_between(constraint_graph, non_primitive_endpoints, PRIMITIVE_TYPES)
|
|
598
641
|
constraints_1 = self._solve_constraints_between(constraint_graph, PRIMITIVE_TYPES, non_primitive_endpoints)
|
|
599
642
|
return constraints_0 | constraints_1
|
|
@@ -612,6 +655,25 @@ class SimpleSolver:
|
|
|
612
655
|
# TODO: Other types of constraints?
|
|
613
656
|
return typevars
|
|
614
657
|
|
|
658
|
+
@staticmethod
|
|
659
|
+
def _typevar_from_primitive_constraint(constraint: TypeConstraint) -> TypeVariable | None:
|
|
660
|
+
if isinstance(constraint, Subtype):
|
|
661
|
+
if (
|
|
662
|
+
isinstance(constraint.sub_type, DerivedTypeVariable)
|
|
663
|
+
and type(constraint.sub_type.type_var) is TypeVariable
|
|
664
|
+
):
|
|
665
|
+
return constraint.sub_type.type_var
|
|
666
|
+
if type(constraint.sub_type) is TypeVariable:
|
|
667
|
+
return constraint.sub_type
|
|
668
|
+
if (
|
|
669
|
+
isinstance(constraint.super_type, DerivedTypeVariable)
|
|
670
|
+
and type(constraint.super_type.type_var) is TypeVariable
|
|
671
|
+
):
|
|
672
|
+
return constraint.super_type.type_var
|
|
673
|
+
if type(constraint.super_type) is TypeVariable:
|
|
674
|
+
return constraint.super_type
|
|
675
|
+
return None
|
|
676
|
+
|
|
615
677
|
@staticmethod
|
|
616
678
|
def _get_all_paths(
|
|
617
679
|
graph: networkx.DiGraph,
|
|
@@ -825,7 +887,6 @@ class SimpleSolver:
|
|
|
825
887
|
"""
|
|
826
888
|
|
|
827
889
|
graph = networkx.DiGraph()
|
|
828
|
-
constraints = self._get_transitive_subtype_constraints(constraints)
|
|
829
890
|
for constraint in constraints:
|
|
830
891
|
if isinstance(constraint, Subtype):
|
|
831
892
|
self._constraint_graph_add_edges(
|
|
@@ -836,33 +897,6 @@ class SimpleSolver:
|
|
|
836
897
|
self._constraint_graph_recall_forget_split(graph)
|
|
837
898
|
return graph
|
|
838
899
|
|
|
839
|
-
@staticmethod
|
|
840
|
-
def _get_transitive_subtype_constraints(constraints: set[TypeConstraint]) -> set[TypeConstraint]:
|
|
841
|
-
"""
|
|
842
|
-
Apply the S-Trans rule: a <: b, b <: c => a <: c
|
|
843
|
-
"""
|
|
844
|
-
tv2supertypes = defaultdict(set)
|
|
845
|
-
for constraint in constraints:
|
|
846
|
-
if isinstance(constraint, Subtype):
|
|
847
|
-
tv2supertypes[constraint.sub_type].add(constraint.super_type)
|
|
848
|
-
|
|
849
|
-
new_constraints = set()
|
|
850
|
-
while True:
|
|
851
|
-
changed = False
|
|
852
|
-
for subtype, supertypes in tv2supertypes.items():
|
|
853
|
-
supertypes_copy = set(supertypes)
|
|
854
|
-
for supertype in supertypes_copy:
|
|
855
|
-
if supertype in tv2supertypes:
|
|
856
|
-
for supertype_ in tv2supertypes[supertype]:
|
|
857
|
-
if supertype_ not in supertypes_copy:
|
|
858
|
-
changed = True
|
|
859
|
-
supertypes.add(supertype_)
|
|
860
|
-
new_constraints.add(Subtype(subtype, supertype_))
|
|
861
|
-
if not changed:
|
|
862
|
-
break
|
|
863
|
-
|
|
864
|
-
return constraints | new_constraints
|
|
865
|
-
|
|
866
900
|
@staticmethod
|
|
867
901
|
def _constraint_graph_add_recall_edges(graph: networkx.DiGraph, node: ConstraintGraphNode) -> None:
|
|
868
902
|
while True:
|
|
@@ -1004,12 +1038,7 @@ class SimpleSolver:
|
|
|
1004
1038
|
if typevar in typevar_set:
|
|
1005
1039
|
return True
|
|
1006
1040
|
if isinstance(typevar, Struct) and Struct_ in typevar_set:
|
|
1007
|
-
|
|
1008
|
-
return True
|
|
1009
|
-
return all(
|
|
1010
|
-
SimpleSolver._typevar_inside_set(field_typevar, typevar_set)
|
|
1011
|
-
for field_typevar in typevar.fields.values()
|
|
1012
|
-
)
|
|
1041
|
+
return True
|
|
1013
1042
|
if isinstance(typevar, Array) and Array_ in typevar_set:
|
|
1014
1043
|
return SimpleSolver._typevar_inside_set(typevar.element, typevar_set)
|
|
1015
1044
|
if isinstance(typevar, Pointer) and (Pointer32_ in typevar_set or Pointer64_ in typevar_set):
|
|
@@ -1110,31 +1139,47 @@ class SimpleSolver:
|
|
|
1110
1139
|
return Pointer64()
|
|
1111
1140
|
return t
|
|
1112
1141
|
|
|
1142
|
+
@staticmethod
|
|
1143
|
+
def _rewrite_constraint(constraint: TypeConstraint, solutions: dict) -> TypeConstraint:
|
|
1144
|
+
if isinstance(constraint, Subtype):
|
|
1145
|
+
replaced = False
|
|
1146
|
+
if isinstance(constraint.sub_type, TypeVariable) and constraint.sub_type in solutions:
|
|
1147
|
+
sub_type = solutions[constraint.sub_type]
|
|
1148
|
+
replaced = True
|
|
1149
|
+
else:
|
|
1150
|
+
sub_type = constraint.sub_type
|
|
1151
|
+
if isinstance(constraint.super_type, TypeVariable) and constraint.super_type in solutions:
|
|
1152
|
+
super_type = solutions[constraint.super_type]
|
|
1153
|
+
replaced = True
|
|
1154
|
+
else:
|
|
1155
|
+
super_type = constraint.super_type
|
|
1156
|
+
return Subtype(sub_type, super_type) if replaced else constraint
|
|
1157
|
+
return constraint
|
|
1158
|
+
|
|
1113
1159
|
def determine(
|
|
1114
1160
|
self,
|
|
1115
|
-
equivalent_classes: dict[TypeVariable, TypeVariable],
|
|
1116
1161
|
sketches,
|
|
1162
|
+
tvs,
|
|
1117
1163
|
solution: dict,
|
|
1118
1164
|
nodes: set[SketchNode] | None = None,
|
|
1119
1165
|
) -> None:
|
|
1120
1166
|
"""
|
|
1121
1167
|
Determine C-like types from sketches.
|
|
1122
1168
|
|
|
1123
|
-
:param equivalent_classes: A dictionary mapping each type variable from its representative in the equivalence
|
|
1124
|
-
class over ~.
|
|
1125
1169
|
:param sketches: A dictionary storing sketches for each type variable.
|
|
1126
1170
|
:param solution: The dictionary storing C-like types for each type variable. Output.
|
|
1127
1171
|
:param nodes: Optional. Nodes that should be considered in the sketch.
|
|
1128
1172
|
:return: None
|
|
1129
1173
|
"""
|
|
1130
|
-
for typevar, sketch in sketches.items():
|
|
1131
|
-
self._determine(equivalent_classes, typevar, sketch, solution, nodes=nodes)
|
|
1132
1174
|
|
|
1133
|
-
for
|
|
1134
|
-
|
|
1135
|
-
|
|
1175
|
+
for typevar in tvs:
|
|
1176
|
+
self._determine(typevar, sketches[typevar], solution, nodes=nodes)
|
|
1177
|
+
|
|
1178
|
+
for v, eq in self._equivalence.items():
|
|
1179
|
+
if v not in solution and eq in solution:
|
|
1180
|
+
solution[v] = solution[eq]
|
|
1136
1181
|
|
|
1137
|
-
def _determine(self,
|
|
1182
|
+
def _determine(self, the_typevar, sketch, solution: dict, nodes: set[SketchNode] | None = None):
|
|
1138
1183
|
"""
|
|
1139
1184
|
Return the solution from sketches
|
|
1140
1185
|
"""
|
|
@@ -1194,7 +1239,7 @@ class SimpleSolver:
|
|
|
1194
1239
|
for vals, out in [(func_inputs, input_args), (func_outputs, output_values)]:
|
|
1195
1240
|
for idx in range(max(vals) + 1):
|
|
1196
1241
|
if idx in vals:
|
|
1197
|
-
sol = self._determine(
|
|
1242
|
+
sol = self._determine(the_typevar, sketch, solution, nodes=vals[idx])
|
|
1198
1243
|
out.append(sol)
|
|
1199
1244
|
else:
|
|
1200
1245
|
out.append(None)
|
|
@@ -1290,7 +1335,7 @@ class SimpleSolver:
|
|
|
1290
1335
|
offset = sorted_offsets[i]
|
|
1291
1336
|
|
|
1292
1337
|
child_nodes = node_by_offset[offset]
|
|
1293
|
-
sol = self._determine(
|
|
1338
|
+
sol = self._determine(the_typevar, sketch, solution, nodes=child_nodes)
|
|
1294
1339
|
if isinstance(sol, TopType):
|
|
1295
1340
|
# make it an array if possible
|
|
1296
1341
|
elem_size = min(offset_to_sizes[offset])
|
|
@@ -1323,12 +1368,20 @@ class SimpleSolver:
|
|
|
1323
1368
|
lower_bound = Bottom_
|
|
1324
1369
|
upper_bound = Top_
|
|
1325
1370
|
|
|
1371
|
+
node_sizes = set()
|
|
1326
1372
|
for node in nodes:
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1373
|
+
node_size = node.size
|
|
1374
|
+
if node_size is not None:
|
|
1375
|
+
node_sizes.add(node_size)
|
|
1376
|
+
if len(node_sizes) > 1:
|
|
1377
|
+
# multi-sized reads - cannot converge to a reasonable type
|
|
1378
|
+
result = Bottom_
|
|
1379
|
+
else:
|
|
1380
|
+
for node in nodes:
|
|
1381
|
+
lower_bound = self.join(lower_bound, node.lower_bound)
|
|
1382
|
+
upper_bound = self.meet(upper_bound, node.upper_bound)
|
|
1383
|
+
result = lower_bound if not isinstance(lower_bound, BottomType) else upper_bound
|
|
1330
1384
|
|
|
1331
|
-
result = lower_bound if not isinstance(lower_bound, BottomType) else upper_bound
|
|
1332
1385
|
for node in nodes:
|
|
1333
1386
|
solution[node.typevar] = result
|
|
1334
1387
|
self._solution_cache[node.typevar] = result
|
|
@@ -1386,3 +1439,12 @@ class SimpleSolver:
|
|
|
1386
1439
|
if self.bits == 64:
|
|
1387
1440
|
return Pointer64
|
|
1388
1441
|
raise NotImplementedError(f"Unsupported bits {self.bits}")
|
|
1442
|
+
|
|
1443
|
+
@staticmethod
|
|
1444
|
+
def dump_constraint_graph(graph: networkx.DiGraph, filename: str) -> None:
|
|
1445
|
+
"""
|
|
1446
|
+
Dump the constraint graph to a file.
|
|
1447
|
+
"""
|
|
1448
|
+
from networkx.drawing.nx_agraph import write_dot # pylint:disable=import-outside-toplevel
|
|
1449
|
+
|
|
1450
|
+
write_dot(graph, filename)
|
|
@@ -100,7 +100,8 @@ class Typehoon(Analysis):
|
|
|
100
100
|
and not isinstance(type_.pts_to, SimTypeArray)
|
|
101
101
|
):
|
|
102
102
|
type_ = type_.pts_to
|
|
103
|
-
|
|
103
|
+
if type_ is not None:
|
|
104
|
+
type_candidates.append(type_)
|
|
104
105
|
|
|
105
106
|
# determine the best type - this logic can be made better!
|
|
106
107
|
if not type_candidates:
|
|
@@ -9,10 +9,10 @@ import claripy
|
|
|
9
9
|
from unique_log_filter import UniqueLogFilter
|
|
10
10
|
|
|
11
11
|
from angr.engines.light.engine import SimEngineNostmtAIL
|
|
12
|
-
from angr.
|
|
13
|
-
from angr.sim_type import SimTypeFunction, dereference_simtype
|
|
12
|
+
from angr.sim_type import SimTypeFunction
|
|
14
13
|
from angr.analyses.typehoon import typeconsts, typevars
|
|
15
14
|
from angr.analyses.typehoon.lifter import TypeLifter
|
|
15
|
+
from angr.utils.types import dereference_simtype_by_lib
|
|
16
16
|
from .engine_base import SimEngineVRBase, RichR
|
|
17
17
|
|
|
18
18
|
if TYPE_CHECKING:
|
|
@@ -190,17 +190,11 @@ class SimEngineVRAIL(
|
|
|
190
190
|
|
|
191
191
|
if prototype is not None and args:
|
|
192
192
|
# add type constraints
|
|
193
|
-
|
|
194
|
-
type_collections = []
|
|
195
|
-
if prototype_libname is not None:
|
|
196
|
-
for prototype_lib in SIM_LIBRARIES[prototype_libname]:
|
|
197
|
-
if prototype_lib.type_collection_names:
|
|
198
|
-
for typelib_name in prototype_lib.type_collection_names:
|
|
199
|
-
type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
|
|
200
|
-
|
|
201
193
|
for arg, arg_type in zip(args, prototype.args):
|
|
202
194
|
if arg.typevar is not None:
|
|
203
|
-
arg_type =
|
|
195
|
+
arg_type = (
|
|
196
|
+
dereference_simtype_by_lib(arg_type, prototype_libname) if prototype_libname else arg_type
|
|
197
|
+
)
|
|
204
198
|
arg_ty = TypeLifter(self.arch.bits).lift(arg_type)
|
|
205
199
|
type_constraint = typevars.Subtype(arg.typevar, arg_ty)
|
|
206
200
|
self.state.add_type_constraint(type_constraint)
|
|
@@ -209,7 +203,7 @@ class SimEngineVRAIL(
|
|
|
209
203
|
|
|
210
204
|
def _handle_stmt_Call(self, stmt):
|
|
211
205
|
target = stmt.target
|
|
212
|
-
args = []
|
|
206
|
+
args: list[RichR] = []
|
|
213
207
|
if stmt.args:
|
|
214
208
|
for arg in stmt.args:
|
|
215
209
|
self._reference_spoffset = True
|
|
@@ -283,22 +277,17 @@ class SimEngineVRAIL(
|
|
|
283
277
|
|
|
284
278
|
if prototype is not None and args:
|
|
285
279
|
# add type constraints
|
|
286
|
-
|
|
287
|
-
type_collections = []
|
|
288
|
-
if prototype_libname is not None:
|
|
289
|
-
for prototype_lib in SIM_LIBRARIES[prototype_libname]:
|
|
290
|
-
if prototype_lib.type_collection_names:
|
|
291
|
-
for typelib_name in prototype_lib.type_collection_names:
|
|
292
|
-
type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
|
|
293
|
-
|
|
294
280
|
for arg, arg_type in zip(args, prototype.args):
|
|
295
281
|
if arg.typevar is not None:
|
|
296
|
-
arg_type =
|
|
282
|
+
arg_type = (
|
|
283
|
+
dereference_simtype_by_lib(arg_type, prototype_libname) if prototype_libname else arg_type
|
|
284
|
+
)
|
|
297
285
|
arg_ty = TypeLifter(self.arch.bits).lift(arg_type)
|
|
298
|
-
if
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
286
|
+
if arg.typevar is not None and isinstance(
|
|
287
|
+
arg_ty, (typeconsts.TypeConstant, typevars.TypeVariable, typevars.DerivedTypeVariable)
|
|
288
|
+
):
|
|
289
|
+
type_constraint = typevars.Subtype(arg.typevar, arg_ty)
|
|
290
|
+
self.state.add_type_constraint(type_constraint)
|
|
302
291
|
|
|
303
292
|
def _handle_stmt_Return(self, stmt):
|
|
304
293
|
if stmt.ret_exprs:
|
|
@@ -127,7 +127,7 @@ class Function(Serializable):
|
|
|
127
127
|
self._retout_sites: set[BlockNode] = set()
|
|
128
128
|
# block nodes (basic block nodes) at whose ends the function terminates
|
|
129
129
|
# in theory, if everything works fine, endpoints == ret_sites | jumpout_sites | callout_sites
|
|
130
|
-
self._endpoints = defaultdict(set)
|
|
130
|
+
self._endpoints: defaultdict[str, set[BlockNode]] = defaultdict(set)
|
|
131
131
|
|
|
132
132
|
self._call_sites = {}
|
|
133
133
|
self.addr = addr
|
|
@@ -1358,7 +1358,7 @@ class Function(Serializable):
|
|
|
1358
1358
|
return
|
|
1359
1359
|
|
|
1360
1360
|
graph = self.transition_graph
|
|
1361
|
-
end_addresses = defaultdict(list)
|
|
1361
|
+
end_addresses: defaultdict[int, list[BlockNode]] = defaultdict(list)
|
|
1362
1362
|
|
|
1363
1363
|
for block in self.nodes:
|
|
1364
1364
|
if isinstance(block, BlockNode):
|
|
@@ -1456,7 +1456,13 @@ class Function(Serializable):
|
|
|
1456
1456
|
)
|
|
1457
1457
|
else:
|
|
1458
1458
|
# We gotta create a new one
|
|
1459
|
-
l.error("normalize(): Please report it to Fish
|
|
1459
|
+
l.error("normalize(): Please report it to Fish.")
|
|
1460
|
+
|
|
1461
|
+
# update endpoints
|
|
1462
|
+
for sortset in self._endpoints.values():
|
|
1463
|
+
if n in sortset:
|
|
1464
|
+
sortset.remove(n)
|
|
1465
|
+
sortset.add(smallest_node)
|
|
1460
1466
|
|
|
1461
1467
|
end_addresses[end_addr] = [smallest_node]
|
|
1462
1468
|
|
|
@@ -1680,7 +1686,7 @@ class Function(Serializable):
|
|
|
1680
1686
|
n = separator
|
|
1681
1687
|
if must_disambiguate_by_addr:
|
|
1682
1688
|
n += hex(self.addr) + separator
|
|
1683
|
-
elif self.binary is not self.project.loader.main_object:
|
|
1689
|
+
elif self.binary is not self.project.loader.main_object and self.binary_name is not None:
|
|
1684
1690
|
n += self.binary_name + separator
|
|
1685
1691
|
return n + (display_name or self.name)
|
|
1686
1692
|
|
angr/sim_type.py
CHANGED
|
@@ -7,7 +7,7 @@ import re
|
|
|
7
7
|
import logging
|
|
8
8
|
from collections import OrderedDict, defaultdict, ChainMap
|
|
9
9
|
from collections.abc import Iterable
|
|
10
|
-
from typing import Literal, Any,
|
|
10
|
+
from typing import Literal, Any, cast, overload
|
|
11
11
|
|
|
12
12
|
from archinfo import Endness, Arch
|
|
13
13
|
import claripy
|
|
@@ -17,12 +17,9 @@ import cxxheaderparser.types
|
|
|
17
17
|
import pycparser
|
|
18
18
|
from pycparser import c_ast
|
|
19
19
|
|
|
20
|
-
from angr.errors import
|
|
20
|
+
from angr.errors import AngrTypeError
|
|
21
21
|
from angr.sim_state import SimState
|
|
22
22
|
|
|
23
|
-
if TYPE_CHECKING:
|
|
24
|
-
from angr.procedures.definitions import SimTypeCollection
|
|
25
|
-
|
|
26
23
|
StoreType = int | claripy.ast.BV
|
|
27
24
|
|
|
28
25
|
l = logging.getLogger(name=__name__)
|
|
@@ -324,7 +321,7 @@ class SimTypeReg(SimType):
|
|
|
324
321
|
with contextlib.suppress(AttributeError):
|
|
325
322
|
value = value.ast # type: ignore
|
|
326
323
|
if isinstance(value, claripy.ast.Bits): # pylint:disable=isinstance-second-argument-not-valid-type
|
|
327
|
-
if value.size() != self.size:
|
|
324
|
+
if value.size() != self.size: # type: ignore
|
|
328
325
|
raise ValueError("size of expression is wrong size for type")
|
|
329
326
|
elif isinstance(value, int):
|
|
330
327
|
value = claripy.BVV(value, self.size)
|
|
@@ -383,7 +380,7 @@ class SimTypeNum(SimType):
|
|
|
383
380
|
store_endness = state.arch.memory_endness
|
|
384
381
|
|
|
385
382
|
if isinstance(value, claripy.ast.Bits): # pylint:disable=isinstance-second-argument-not-valid-type
|
|
386
|
-
if value.size() != self.size:
|
|
383
|
+
if value.size() != self.size: # type: ignore
|
|
387
384
|
raise ValueError("size of expression is wrong size for type")
|
|
388
385
|
elif isinstance(value, int) and self.size is not None:
|
|
389
386
|
value = claripy.BVV(value, self.size)
|
|
@@ -505,7 +502,12 @@ class SimTypeFixedSizeInt(SimTypeInt):
|
|
|
505
502
|
_fixed_size: int = 32
|
|
506
503
|
|
|
507
504
|
def c_repr(
|
|
508
|
-
self,
|
|
505
|
+
self,
|
|
506
|
+
name=None,
|
|
507
|
+
full=0,
|
|
508
|
+
memo=None,
|
|
509
|
+
indent: int | None = 0,
|
|
510
|
+
name_parens: bool = True, # pylint:disable=unused-argument
|
|
509
511
|
):
|
|
510
512
|
out = self._base_name
|
|
511
513
|
if not self.signed:
|
|
@@ -1653,7 +1655,7 @@ class SimUnion(NamedTypeMixin, SimType):
|
|
|
1653
1655
|
def size(self):
|
|
1654
1656
|
if self._arch is None:
|
|
1655
1657
|
raise ValueError("Can't tell my size without an arch!")
|
|
1656
|
-
member_sizes = [ty.size for ty in self.members.values() if not isinstance(ty, SimTypeBottom)]
|
|
1658
|
+
member_sizes: list[int] = [ty.size for ty in self.members.values() if not isinstance(ty, SimTypeBottom)]
|
|
1657
1659
|
# fall back to word size in case all members are SimTypeBottom
|
|
1658
1660
|
return max(member_sizes) if member_sizes else self._arch.bytes
|
|
1659
1661
|
|
|
@@ -3652,67 +3654,6 @@ def parse_cpp_file(cpp_decl, with_param_names: bool = False):
|
|
|
3652
3654
|
return func_decls, {}
|
|
3653
3655
|
|
|
3654
3656
|
|
|
3655
|
-
def dereference_simtype(
|
|
3656
|
-
t: SimType, type_collections: list[SimTypeCollection], memo: dict[str, SimType] | None = None
|
|
3657
|
-
) -> SimType:
|
|
3658
|
-
if memo is None:
|
|
3659
|
-
memo = {}
|
|
3660
|
-
|
|
3661
|
-
if isinstance(t, SimTypeRef):
|
|
3662
|
-
real_type = None
|
|
3663
|
-
|
|
3664
|
-
if t.name in memo:
|
|
3665
|
-
return memo[t.name]
|
|
3666
|
-
|
|
3667
|
-
if type_collections and t.name is not None:
|
|
3668
|
-
for tc in type_collections:
|
|
3669
|
-
try:
|
|
3670
|
-
real_type = tc.get(t.name)
|
|
3671
|
-
break
|
|
3672
|
-
except AngrMissingTypeError:
|
|
3673
|
-
continue
|
|
3674
|
-
if real_type is None:
|
|
3675
|
-
raise AngrMissingTypeError(f"Missing type {t.name}")
|
|
3676
|
-
return dereference_simtype(real_type, type_collections, memo=memo)
|
|
3677
|
-
|
|
3678
|
-
# the following code prepares a real_type SimType object that will be returned at the end of this method
|
|
3679
|
-
if isinstance(t, SimStruct):
|
|
3680
|
-
if t.name in memo:
|
|
3681
|
-
return memo[t.name]
|
|
3682
|
-
|
|
3683
|
-
real_type = t.copy()
|
|
3684
|
-
if not t.anonymous:
|
|
3685
|
-
memo[t.name] = real_type
|
|
3686
|
-
fields = OrderedDict((k, dereference_simtype(v, type_collections, memo=memo)) for k, v in t.fields.items())
|
|
3687
|
-
real_type.fields = fields
|
|
3688
|
-
elif isinstance(t, SimTypePointer):
|
|
3689
|
-
real_pts_to = dereference_simtype(t.pts_to, type_collections, memo=memo)
|
|
3690
|
-
real_type = t.copy()
|
|
3691
|
-
real_type.pts_to = real_pts_to
|
|
3692
|
-
elif isinstance(t, SimTypeArray):
|
|
3693
|
-
real_elem_type = dereference_simtype(t.elem_type, type_collections, memo=memo)
|
|
3694
|
-
real_type = t.copy()
|
|
3695
|
-
real_type.elem_type = real_elem_type
|
|
3696
|
-
elif isinstance(t, SimUnion):
|
|
3697
|
-
real_members = {k: dereference_simtype(v, type_collections, memo=memo) for k, v in t.members.items()}
|
|
3698
|
-
real_type = t.copy()
|
|
3699
|
-
real_type.members = real_members
|
|
3700
|
-
elif isinstance(t, SimTypeFunction):
|
|
3701
|
-
real_args = [dereference_simtype(arg, type_collections, memo=memo) for arg in t.args]
|
|
3702
|
-
real_return_type = (
|
|
3703
|
-
dereference_simtype(t.returnty, type_collections, memo=memo) if t.returnty is not None else None
|
|
3704
|
-
)
|
|
3705
|
-
real_type = t.copy()
|
|
3706
|
-
real_type.args = tuple(real_args)
|
|
3707
|
-
real_type.returnty = real_return_type
|
|
3708
|
-
else:
|
|
3709
|
-
return t
|
|
3710
|
-
|
|
3711
|
-
if t._arch is not None:
|
|
3712
|
-
real_type = real_type.with_arch(t._arch)
|
|
3713
|
-
return real_type
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
3657
|
if pycparser is not None:
|
|
3717
3658
|
_accepts_scope_stack()
|
|
3718
3659
|
|