angr 9.2.95__py3-none-manylinux2014_x86_64.whl → 9.2.97__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/cfg/cfg_fast.py +9 -6
- angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +6 -1
- angr/analyses/complete_calling_conventions.py +27 -11
- angr/analyses/decompiler/ail_simplifier.py +30 -8
- angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +20 -7
- angr/analyses/decompiler/clinic.py +21 -5
- angr/analyses/decompiler/condition_processor.py +11 -0
- angr/analyses/decompiler/decompiler.py +58 -46
- angr/analyses/decompiler/optimization_passes/__init__.py +11 -5
- angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +13 -7
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +31 -11
- angr/analyses/decompiler/optimization_passes/{return_duplicator.py → return_duplicator_base.py} +54 -102
- angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +57 -0
- angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +121 -0
- angr/analyses/decompiler/region_identifier.py +13 -0
- angr/analyses/decompiler/seq_to_blocks.py +19 -0
- angr/analyses/decompiler/structured_codegen/c.py +21 -0
- angr/analyses/decompiler/structuring/phoenix.py +28 -4
- angr/analyses/decompiler/structuring/recursive_structurer.py +35 -1
- angr/analyses/decompiler/structuring/structurer_base.py +3 -0
- angr/analyses/decompiler/utils.py +41 -6
- angr/analyses/disassembly.py +4 -1
- angr/analyses/find_objects_static.py +15 -10
- angr/analyses/forward_analysis/forward_analysis.py +15 -1
- angr/analyses/propagator/engine_ail.py +40 -0
- angr/analyses/propagator/propagator.py +6 -3
- angr/analyses/reaching_definitions/engine_ail.py +16 -24
- angr/analyses/reaching_definitions/rd_state.py +14 -1
- angr/analyses/reaching_definitions/reaching_definitions.py +19 -2
- angr/analyses/variable_recovery/engine_ail.py +6 -6
- angr/analyses/variable_recovery/engine_base.py +22 -4
- angr/analyses/variable_recovery/variable_recovery_base.py +4 -1
- angr/engines/light/engine.py +8 -1
- angr/knowledge_plugins/key_definitions/atoms.py +4 -2
- angr/knowledge_plugins/key_definitions/environment.py +11 -0
- angr/knowledge_plugins/key_definitions/live_definitions.py +41 -8
- angr/knowledge_plugins/key_definitions/uses.py +18 -4
- angr/knowledge_plugins/propagations/states.py +22 -3
- angr/knowledge_plugins/types.py +6 -0
- angr/knowledge_plugins/variables/variable_manager.py +54 -5
- angr/simos/simos.py +2 -0
- angr/storage/memory_mixins/__init__.py +3 -0
- angr/storage/memory_mixins/multi_value_merger_mixin.py +22 -11
- angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +20 -2
- angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +81 -44
- angr/utils/cowdict.py +4 -2
- angr/utils/funcid.py +6 -0
- angr/utils/mp.py +1 -1
- {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/METADATA +6 -6
- {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/RECORD +55 -52
- {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/LICENSE +0 -0
- {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/WHEEL +0 -0
- {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/entry_points.txt +0 -0
- {angr-9.2.95.dist-info → angr-9.2.97.dist-info}/top_level.txt +0 -0
|
@@ -55,7 +55,7 @@ class ReachingDefinitionsAnalysis(
|
|
|
55
55
|
self,
|
|
56
56
|
subject: Union[Subject, ailment.Block, Block, Function, str] = None,
|
|
57
57
|
func_graph=None,
|
|
58
|
-
max_iterations=
|
|
58
|
+
max_iterations=30,
|
|
59
59
|
track_tmps=False,
|
|
60
60
|
track_consts=True,
|
|
61
61
|
observation_points: "Iterable[ObservationPoint]" = None,
|
|
@@ -74,6 +74,7 @@ class ReachingDefinitionsAnalysis(
|
|
|
74
74
|
interfunction_level: int = 0,
|
|
75
75
|
track_liveness: bool = True,
|
|
76
76
|
func_addr: Optional[int] = None,
|
|
77
|
+
element_limit: int = 5,
|
|
77
78
|
):
|
|
78
79
|
"""
|
|
79
80
|
:param subject: The subject of the analysis: a function, or a single basic block
|
|
@@ -131,6 +132,7 @@ class ReachingDefinitionsAnalysis(
|
|
|
131
132
|
self._canonical_size = canonical_size
|
|
132
133
|
self._use_callee_saved_regs_at_return = use_callee_saved_regs_at_return
|
|
133
134
|
self._func_addr = func_addr
|
|
135
|
+
self._element_limit = element_limit
|
|
134
136
|
|
|
135
137
|
if dep_graph is None or dep_graph is False:
|
|
136
138
|
self._dep_graph = None
|
|
@@ -469,13 +471,28 @@ class ReachingDefinitionsAnalysis(
|
|
|
469
471
|
analysis=self,
|
|
470
472
|
canonical_size=self._canonical_size,
|
|
471
473
|
initializer=self._state_initializer,
|
|
474
|
+
element_limit=self._element_limit,
|
|
472
475
|
)
|
|
473
476
|
|
|
474
477
|
# pylint: disable=no-self-use,arguments-differ
|
|
475
478
|
def _merge_states(self, _node, *states: ReachingDefinitionsState):
|
|
479
|
+
assert len(states) >= 2
|
|
476
480
|
merged_state, merge_occurred = states[0].merge(*states[1:])
|
|
477
481
|
return merged_state, not merge_occurred
|
|
478
482
|
|
|
483
|
+
def _compare_states(self, node, old_state: ReachingDefinitionsState, new_state: ReachingDefinitionsState) -> bool:
|
|
484
|
+
"""
|
|
485
|
+
Return True if new_state >= old_state in the lattice.
|
|
486
|
+
|
|
487
|
+
:param node:
|
|
488
|
+
:param old_state:
|
|
489
|
+
:param new_state:
|
|
490
|
+
:return:
|
|
491
|
+
"""
|
|
492
|
+
|
|
493
|
+
reached_fixedpoint = new_state.compare(old_state)
|
|
494
|
+
return reached_fixedpoint
|
|
495
|
+
|
|
479
496
|
def _run_on_node(self, node, state: ReachingDefinitionsState):
|
|
480
497
|
"""
|
|
481
498
|
|
|
@@ -550,7 +567,7 @@ class ReachingDefinitionsAnalysis(
|
|
|
550
567
|
state.downsize()
|
|
551
568
|
|
|
552
569
|
if self._node_iterations[block_key] < self._max_iterations:
|
|
553
|
-
return
|
|
570
|
+
return None, state
|
|
554
571
|
else:
|
|
555
572
|
return False, state
|
|
556
573
|
|
|
@@ -469,20 +469,20 @@ class SimEngineVRAIL(
|
|
|
469
469
|
|
|
470
470
|
r0 = self._expr(arg0)
|
|
471
471
|
r1 = self._expr(arg1)
|
|
472
|
-
from_size =
|
|
473
|
-
to_size =
|
|
472
|
+
from_size = expr.from_bits
|
|
473
|
+
to_size = expr.to_bits
|
|
474
474
|
|
|
475
475
|
if expr.signed:
|
|
476
|
-
quotient = r0.data.SDiv(claripy.SignExt(
|
|
477
|
-
remainder = r0.data.SMod(claripy.SignExt(
|
|
476
|
+
quotient = r0.data.SDiv(claripy.SignExt(from_size - to_size, r1.data))
|
|
477
|
+
remainder = r0.data.SMod(claripy.SignExt(from_size - to_size, r1.data))
|
|
478
478
|
quotient_size = to_size
|
|
479
479
|
remainder_size = to_size
|
|
480
480
|
r = claripy.Concat(
|
|
481
481
|
claripy.Extract(remainder_size - 1, 0, remainder), claripy.Extract(quotient_size - 1, 0, quotient)
|
|
482
482
|
)
|
|
483
483
|
else:
|
|
484
|
-
quotient = r0.data // claripy.ZeroExt(
|
|
485
|
-
remainder = r0.data % claripy.ZeroExt(
|
|
484
|
+
quotient = r0.data // claripy.ZeroExt(from_size - to_size, r1.data)
|
|
485
|
+
remainder = r0.data % claripy.ZeroExt(from_size - to_size, r1.data)
|
|
486
486
|
quotient_size = to_size
|
|
487
487
|
remainder_size = to_size
|
|
488
488
|
r = claripy.Concat(
|
|
@@ -270,10 +270,8 @@ class SimEngineVRBase(SimEngineLight):
|
|
|
270
270
|
return
|
|
271
271
|
|
|
272
272
|
if not existing_vars:
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
"_ensure_variable_existence() first?"
|
|
276
|
-
)
|
|
273
|
+
# no associated variables. it's usually because _ensure_variable_existence() is not called, or the address
|
|
274
|
+
# is a TOP. we ignore this case.
|
|
277
275
|
return
|
|
278
276
|
else:
|
|
279
277
|
variable, _ = existing_vars[0]
|
|
@@ -392,6 +390,15 @@ class SimEngineVRBase(SimEngineLight):
|
|
|
392
390
|
stored = True
|
|
393
391
|
|
|
394
392
|
if not stored:
|
|
393
|
+
# remove existing variables linked to this statement
|
|
394
|
+
existing_vars = self.variable_manager[self.func_addr].find_variables_by_stmt(
|
|
395
|
+
self.block.addr, self.stmt_idx, "memory"
|
|
396
|
+
)
|
|
397
|
+
codeloc = self._codeloc()
|
|
398
|
+
if existing_vars:
|
|
399
|
+
for existing_var, _ in list(existing_vars):
|
|
400
|
+
self.variable_manager[self.func_addr].remove_variable_by_atom(codeloc, existing_var, stmt)
|
|
401
|
+
|
|
395
402
|
# storing to a location specified by a pointer whose value cannot be determined at this point
|
|
396
403
|
self._store_to_variable(richr_addr, size, stmt=stmt)
|
|
397
404
|
|
|
@@ -602,6 +609,7 @@ class SimEngineVRBase(SimEngineLight):
|
|
|
602
609
|
self.block.addr, self.stmt_idx, ins_addr=self.ins_addr, block_idx=getattr(self.block, "idx", None)
|
|
603
610
|
)
|
|
604
611
|
typevar = None
|
|
612
|
+
v = None
|
|
605
613
|
|
|
606
614
|
if self.state.is_stack_address(addr):
|
|
607
615
|
stack_offset = self.state.get_stack_offset(addr)
|
|
@@ -743,6 +751,16 @@ class SimEngineVRBase(SimEngineLight):
|
|
|
743
751
|
v = self._load_from_global(base_addr.concrete_value, size, expr=expr, offset=offset, elem_size=elem_size)
|
|
744
752
|
typevar = v.typevar
|
|
745
753
|
|
|
754
|
+
if v is None and expr is not None:
|
|
755
|
+
# failed to map the address to a known variable
|
|
756
|
+
# remove existing variables linked to this variable
|
|
757
|
+
existing_vars = self.variable_manager[self.func_addr].find_variables_by_atom(
|
|
758
|
+
self.block.addr, self.stmt_idx, expr
|
|
759
|
+
)
|
|
760
|
+
if existing_vars:
|
|
761
|
+
for existing_var, _ in list(existing_vars):
|
|
762
|
+
self.variable_manager[self.func_addr].remove_variable_by_atom(codeloc, existing_var, expr)
|
|
763
|
+
|
|
746
764
|
# Loading data from a pointer
|
|
747
765
|
if richr_addr.type_constraints:
|
|
748
766
|
for tc in richr_addr.type_constraints:
|
|
@@ -175,6 +175,7 @@ class VariableRecoveryStateBase:
|
|
|
175
175
|
self.stack_region: MultiValuedMemory = MultiValuedMemory(
|
|
176
176
|
memory_id="mem",
|
|
177
177
|
top_func=self.top,
|
|
178
|
+
is_top_func=self.is_top,
|
|
178
179
|
phi_maker=self._make_phi_variable,
|
|
179
180
|
skip_missing_values_during_merging=True,
|
|
180
181
|
page_kwargs={"mo_cmp": self._mo_cmp},
|
|
@@ -188,6 +189,7 @@ class VariableRecoveryStateBase:
|
|
|
188
189
|
self.register_region: MultiValuedMemory = MultiValuedMemory(
|
|
189
190
|
memory_id="reg",
|
|
190
191
|
top_func=self.top,
|
|
192
|
+
is_top_func=self.is_top,
|
|
191
193
|
phi_maker=self._make_phi_variable,
|
|
192
194
|
skip_missing_values_during_merging=True,
|
|
193
195
|
page_kwargs={"mo_cmp": self._mo_cmp},
|
|
@@ -201,6 +203,7 @@ class VariableRecoveryStateBase:
|
|
|
201
203
|
self.global_region: MultiValuedMemory = MultiValuedMemory(
|
|
202
204
|
memory_id="mem",
|
|
203
205
|
top_func=self.top,
|
|
206
|
+
is_top_func=self.is_top,
|
|
204
207
|
phi_maker=self._make_phi_variable,
|
|
205
208
|
skip_missing_values_during_merging=True,
|
|
206
209
|
page_kwargs={"mo_cmp": self._mo_cmp},
|
|
@@ -215,7 +218,7 @@ class VariableRecoveryStateBase:
|
|
|
215
218
|
self.type_constraints = defaultdict(set) if type_constraints is None else type_constraints
|
|
216
219
|
self.func_typevar = func_typevar
|
|
217
220
|
self.delayed_type_constraints = (
|
|
218
|
-
DefaultChainMapCOW(set, collapse_threshold=25)
|
|
221
|
+
DefaultChainMapCOW(default_factory=set, collapse_threshold=25)
|
|
219
222
|
if delayed_type_constraints is None
|
|
220
223
|
else delayed_type_constraints
|
|
221
224
|
)
|
angr/engines/light/engine.py
CHANGED
|
@@ -1078,7 +1078,14 @@ class SimEngineLightAILMixin(SimEngineLightMixin):
|
|
|
1078
1078
|
expr_1 = arg1
|
|
1079
1079
|
|
|
1080
1080
|
return ailment.Expr.BinaryOp(
|
|
1081
|
-
expr.idx,
|
|
1081
|
+
expr.idx,
|
|
1082
|
+
"DivMod",
|
|
1083
|
+
[expr_0, expr_1],
|
|
1084
|
+
expr.signed,
|
|
1085
|
+
bits=expr.bits,
|
|
1086
|
+
from_bits=expr.from_bits,
|
|
1087
|
+
to_bits=expr.to_bits,
|
|
1088
|
+
**expr.tags,
|
|
1082
1089
|
)
|
|
1083
1090
|
|
|
1084
1091
|
def _ail_handle_Mod(self, expr):
|
|
@@ -71,7 +71,7 @@ class Atom:
|
|
|
71
71
|
Instanciate an `Atom` from a given argument.
|
|
72
72
|
|
|
73
73
|
:param argument: The argument to create a new atom from.
|
|
74
|
-
:param
|
|
74
|
+
:param arch: The argument representing archinfo architecture for argument.
|
|
75
75
|
:param full_reg: Whether to return an atom indicating the entire register if the argument only specifies a
|
|
76
76
|
slice of the register.
|
|
77
77
|
:param sp: The current stack offset. Optional. Only used when argument is a SimStackArg.
|
|
@@ -84,7 +84,9 @@ class Atom:
|
|
|
84
84
|
elif isinstance(argument, SimStackArg):
|
|
85
85
|
if sp is None:
|
|
86
86
|
raise ValueError("You must provide a stack pointer to translate a SimStackArg")
|
|
87
|
-
return MemoryLocation(
|
|
87
|
+
return MemoryLocation(
|
|
88
|
+
SpOffset(arch.bits, argument.stack_offset + sp), argument.size, endness=arch.memory_endness
|
|
89
|
+
)
|
|
88
90
|
else:
|
|
89
91
|
raise TypeError("Argument type %s is not yet supported." % type(argument))
|
|
90
92
|
|
|
@@ -14,6 +14,8 @@ class Environment:
|
|
|
14
14
|
**Note**: The <Environment> object does not store the values associated with variables themselves.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
+
__slots__ = ("_environment",)
|
|
18
|
+
|
|
17
19
|
def __init__(self, environment: Dict[Union[str, Undefined], Set[claripy.ast.Base]] = None):
|
|
18
20
|
self._environment: Dict[Union[str, Undefined], Set[claripy.ast.Base]] = environment or {}
|
|
19
21
|
|
|
@@ -81,3 +83,12 @@ class Environment:
|
|
|
81
83
|
|
|
82
84
|
merge_occurred = new_env != self._environment
|
|
83
85
|
return Environment(environment=new_env), merge_occurred
|
|
86
|
+
|
|
87
|
+
def compare(self, other: "Environment") -> bool:
|
|
88
|
+
for k in set(self._environment.keys()).union(set(other._environment.keys())):
|
|
89
|
+
if k not in self._environment:
|
|
90
|
+
return False
|
|
91
|
+
if k in self._environment and k in other._environment:
|
|
92
|
+
if not self._environment[k].issuperset(other._environment[k]):
|
|
93
|
+
return False
|
|
94
|
+
return True
|
|
@@ -49,11 +49,12 @@ class DefinitionAnnotation(Annotation):
|
|
|
49
49
|
An annotation that attaches a `Definition` to an AST.
|
|
50
50
|
"""
|
|
51
51
|
|
|
52
|
-
__slots__ = ("definition",)
|
|
52
|
+
__slots__ = ("definition", "_hash")
|
|
53
53
|
|
|
54
54
|
def __init__(self, definition):
|
|
55
55
|
super().__init__()
|
|
56
56
|
self.definition = definition
|
|
57
|
+
self._hash = hash((DefinitionAnnotation, self.definition))
|
|
57
58
|
|
|
58
59
|
@property
|
|
59
60
|
def relocatable(self):
|
|
@@ -64,15 +65,11 @@ class DefinitionAnnotation(Annotation):
|
|
|
64
65
|
return False
|
|
65
66
|
|
|
66
67
|
def __hash__(self):
|
|
67
|
-
return
|
|
68
|
+
return self._hash
|
|
68
69
|
|
|
69
70
|
def __eq__(self, other: "object"):
|
|
70
|
-
if
|
|
71
|
-
return
|
|
72
|
-
self.definition == other.definition
|
|
73
|
-
and self.relocatable == other.relocatable
|
|
74
|
-
and self.eliminatable == other.eliminatable
|
|
75
|
-
)
|
|
71
|
+
if type(other) is DefinitionAnnotation:
|
|
72
|
+
return self.definition == other.definition
|
|
76
73
|
else:
|
|
77
74
|
return False
|
|
78
75
|
|
|
@@ -129,6 +126,7 @@ class LiveDefinitions:
|
|
|
129
126
|
memory_uses=None,
|
|
130
127
|
tmp_uses=None,
|
|
131
128
|
other_uses=None,
|
|
129
|
+
element_limit=5,
|
|
132
130
|
):
|
|
133
131
|
self.project: Optional["Project"] = None
|
|
134
132
|
self.arch = arch
|
|
@@ -139,9 +137,11 @@ class LiveDefinitions:
|
|
|
139
137
|
MultiValuedMemory(
|
|
140
138
|
memory_id="reg",
|
|
141
139
|
top_func=self.top,
|
|
140
|
+
is_top_func=self.is_top,
|
|
142
141
|
skip_missing_values_during_merging=False,
|
|
143
142
|
page_kwargs={"mo_cmp": self._mo_cmp},
|
|
144
143
|
endness=self.arch.register_endness,
|
|
144
|
+
element_limit=element_limit,
|
|
145
145
|
)
|
|
146
146
|
if registers is None
|
|
147
147
|
else registers
|
|
@@ -150,8 +150,10 @@ class LiveDefinitions:
|
|
|
150
150
|
MultiValuedMemory(
|
|
151
151
|
memory_id="mem",
|
|
152
152
|
top_func=self.top,
|
|
153
|
+
is_top_func=self.is_top,
|
|
153
154
|
skip_missing_values_during_merging=False,
|
|
154
155
|
page_kwargs={"mo_cmp": self._mo_cmp},
|
|
156
|
+
element_limit=element_limit,
|
|
155
157
|
)
|
|
156
158
|
if stack is None
|
|
157
159
|
else stack
|
|
@@ -160,8 +162,10 @@ class LiveDefinitions:
|
|
|
160
162
|
MultiValuedMemory(
|
|
161
163
|
memory_id="mem",
|
|
162
164
|
top_func=self.top,
|
|
165
|
+
is_top_func=self.is_top,
|
|
163
166
|
skip_missing_values_during_merging=False,
|
|
164
167
|
page_kwargs={"mo_cmp": self._mo_cmp},
|
|
168
|
+
element_limit=element_limit,
|
|
165
169
|
)
|
|
166
170
|
if memory is None
|
|
167
171
|
else memory
|
|
@@ -170,8 +174,10 @@ class LiveDefinitions:
|
|
|
170
174
|
MultiValuedMemory(
|
|
171
175
|
memory_id="mem",
|
|
172
176
|
top_func=self.top,
|
|
177
|
+
is_top_func=self.is_top,
|
|
173
178
|
skip_missing_values_during_merging=False,
|
|
174
179
|
page_kwargs={"mo_cmp": self._mo_cmp},
|
|
180
|
+
element_limit=element_limit,
|
|
175
181
|
)
|
|
176
182
|
if heap is None
|
|
177
183
|
else heap
|
|
@@ -464,6 +470,33 @@ class LiveDefinitions:
|
|
|
464
470
|
|
|
465
471
|
return state, merge_occurred
|
|
466
472
|
|
|
473
|
+
def compare(self, other: "LiveDefinitions") -> bool:
|
|
474
|
+
r0 = self.registers.compare(other.registers)
|
|
475
|
+
if r0 is False:
|
|
476
|
+
return False
|
|
477
|
+
r1 = self.heap.compare(other.heap)
|
|
478
|
+
if r1 is False:
|
|
479
|
+
return False
|
|
480
|
+
r2 = self.memory.compare(other.memory)
|
|
481
|
+
if r2 is False:
|
|
482
|
+
return False
|
|
483
|
+
r3 = self.stack.compare(other.stack)
|
|
484
|
+
if r3 is False:
|
|
485
|
+
return False
|
|
486
|
+
|
|
487
|
+
r4 = True
|
|
488
|
+
for k in other.others:
|
|
489
|
+
if k in self.others:
|
|
490
|
+
thing = self.others[k].merge(other.others[k])
|
|
491
|
+
if thing != self.others[k]:
|
|
492
|
+
r4 = False
|
|
493
|
+
break
|
|
494
|
+
else:
|
|
495
|
+
r4 = False
|
|
496
|
+
break
|
|
497
|
+
|
|
498
|
+
return r0 and r1 and r2 and r3 and r4
|
|
499
|
+
|
|
467
500
|
def kill_definitions(self, atom: Atom) -> None:
|
|
468
501
|
"""
|
|
469
502
|
Overwrite existing definitions w.r.t 'atom' with a dummy definition instance. A dummy definition will not be
|
|
@@ -21,10 +21,14 @@ class Uses:
|
|
|
21
21
|
uses_by_location: Optional[DefaultChainMapCOW] = None,
|
|
22
22
|
):
|
|
23
23
|
self._uses_by_definition: DefaultChainMapCOW["Definition", Set[Tuple[CodeLocation, Optional[Any]]]] = (
|
|
24
|
-
DefaultChainMapCOW(set, collapse_threshold=25)
|
|
24
|
+
DefaultChainMapCOW(default_factory=set, collapse_threshold=25)
|
|
25
|
+
if uses_by_definition is None
|
|
26
|
+
else uses_by_definition
|
|
25
27
|
)
|
|
26
28
|
self._uses_by_location: DefaultChainMapCOW[CodeLocation, Set[Tuple["Definition", Optional[Any]]]] = (
|
|
27
|
-
DefaultChainMapCOW(set, collapse_threshold=25)
|
|
29
|
+
DefaultChainMapCOW(default_factory=set, collapse_threshold=25)
|
|
30
|
+
if uses_by_location is None
|
|
31
|
+
else uses_by_location
|
|
28
32
|
)
|
|
29
33
|
|
|
30
34
|
def add_use(self, definition: "Definition", codeloc: CodeLocation, expr: Optional[Any] = None):
|
|
@@ -35,7 +39,9 @@ class Uses:
|
|
|
35
39
|
:param codeloc: The code location where the use occurs.
|
|
36
40
|
:param expr: The expression that uses the specified definition at this location.
|
|
37
41
|
"""
|
|
42
|
+
self._uses_by_definition = self._uses_by_definition.clean()
|
|
38
43
|
self._uses_by_definition[definition].add((codeloc, expr))
|
|
44
|
+
self._uses_by_location = self._uses_by_location.clean()
|
|
39
45
|
self._uses_by_location[codeloc].add((definition, expr))
|
|
40
46
|
|
|
41
47
|
def get_uses(self, definition: "Definition") -> Set[CodeLocation]:
|
|
@@ -65,6 +71,7 @@ class Uses:
|
|
|
65
71
|
"""
|
|
66
72
|
if definition in self._uses_by_definition:
|
|
67
73
|
if codeloc in self._uses_by_definition[definition]:
|
|
74
|
+
self._uses_by_definition = self._uses_by_definition.clean()
|
|
68
75
|
if expr is None:
|
|
69
76
|
for codeloc_, expr_ in list(self._uses_by_definition[definition]):
|
|
70
77
|
if codeloc_ == codeloc:
|
|
@@ -73,6 +80,7 @@ class Uses:
|
|
|
73
80
|
self._uses_by_definition[definition].remove((codeloc, expr))
|
|
74
81
|
|
|
75
82
|
if codeloc in self._uses_by_location:
|
|
83
|
+
self._uses_by_location = self._uses_by_location.clean()
|
|
76
84
|
for item in list(self._uses_by_location[codeloc]):
|
|
77
85
|
if item[0] == definition:
|
|
78
86
|
self._uses_by_location[codeloc].remove(item)
|
|
@@ -85,9 +93,11 @@ class Uses:
|
|
|
85
93
|
:return: None
|
|
86
94
|
"""
|
|
87
95
|
if definition in self._uses_by_definition:
|
|
96
|
+
self._uses_by_definition = self._uses_by_definition.clean()
|
|
88
97
|
codeloc_and_ids = self._uses_by_definition[definition]
|
|
89
98
|
del self._uses_by_definition[definition]
|
|
90
99
|
|
|
100
|
+
self._uses_by_location = self._uses_by_location.clean()
|
|
91
101
|
for codeloc, _ in codeloc_and_ids:
|
|
92
102
|
for item in list(self._uses_by_location[codeloc]):
|
|
93
103
|
if item[0] == definition:
|
|
@@ -149,18 +159,22 @@ class Uses:
|
|
|
149
159
|
|
|
150
160
|
for k, v in other._uses_by_definition.items():
|
|
151
161
|
if k not in self._uses_by_definition:
|
|
162
|
+
self._uses_by_definition = self._uses_by_definition.clean()
|
|
152
163
|
self._uses_by_definition[k] = v
|
|
153
164
|
merge_occurred = True
|
|
154
165
|
elif not v.issubset(self._uses_by_definition[k]):
|
|
155
166
|
merge_occurred = True
|
|
156
|
-
self._uses_by_definition
|
|
167
|
+
self._uses_by_definition = self._uses_by_definition.clean()
|
|
168
|
+
self._uses_by_definition[k] = self._uses_by_definition[k] | v
|
|
157
169
|
|
|
158
170
|
for k, v in other._uses_by_location.items():
|
|
159
171
|
if k not in self._uses_by_location:
|
|
172
|
+
self._uses_by_location = self._uses_by_location.clean()
|
|
160
173
|
self._uses_by_location[k] = v
|
|
161
174
|
merge_occurred = True
|
|
162
175
|
elif not v.issubset(self._uses_by_location[k]):
|
|
163
176
|
merge_occurred = True
|
|
164
|
-
self._uses_by_location
|
|
177
|
+
self._uses_by_location = self._uses_by_location.clean()
|
|
178
|
+
self._uses_by_location[k] = self._uses_by_location[k] | v
|
|
165
179
|
|
|
166
180
|
return merge_occurred
|
|
@@ -221,9 +221,10 @@ class PropagatorState:
|
|
|
221
221
|
merge_occurred = True
|
|
222
222
|
else:
|
|
223
223
|
if PropagatorState.is_top(repl) or PropagatorState.is_top(replacements_0[loc][var]):
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
if not PropagatorState.is_top(replacements_0[loc][var]):
|
|
225
|
+
t = PropagatorState.top(_get_repl_size(repl))
|
|
226
|
+
replacements_0[loc][var] = t
|
|
227
|
+
merge_occurred = True
|
|
227
228
|
elif (
|
|
228
229
|
isinstance(replacements_0[loc][var], claripy.ast.Base) or isinstance(repl, claripy.ast.Base)
|
|
229
230
|
) and replacements_0[loc][var] is not repl:
|
|
@@ -316,6 +317,12 @@ class RegisterAnnotation(claripy.Annotation):
|
|
|
316
317
|
def relocatable(self) -> bool:
|
|
317
318
|
return True
|
|
318
319
|
|
|
320
|
+
def __hash__(self):
|
|
321
|
+
return hash((RegisterAnnotation, self.offset, self.size))
|
|
322
|
+
|
|
323
|
+
def __eq__(self, other):
|
|
324
|
+
return type(other) is RegisterAnnotation and self.offset == other.offset and self.size == other.size
|
|
325
|
+
|
|
319
326
|
|
|
320
327
|
class RegisterComparisonAnnotation(claripy.Annotation):
|
|
321
328
|
"""
|
|
@@ -336,6 +343,18 @@ class RegisterComparisonAnnotation(claripy.Annotation):
|
|
|
336
343
|
def relocatable(self) -> bool:
|
|
337
344
|
return True
|
|
338
345
|
|
|
346
|
+
def __hash__(self):
|
|
347
|
+
return hash((RegisterComparisonAnnotation, self.offset, self.size, self.cmp_op, self.value))
|
|
348
|
+
|
|
349
|
+
def __eq__(self, other):
|
|
350
|
+
return (
|
|
351
|
+
type(other) is RegisterAnnotation
|
|
352
|
+
and self.offset == other.offset
|
|
353
|
+
and self.size == other.size
|
|
354
|
+
and self.cmp_op == other.cmp_op
|
|
355
|
+
and self.value == other.value
|
|
356
|
+
)
|
|
357
|
+
|
|
339
358
|
|
|
340
359
|
class PropagatorVEXState(PropagatorState):
|
|
341
360
|
"""
|
angr/knowledge_plugins/types.py
CHANGED
|
@@ -56,6 +56,12 @@ class TypesStore(KnowledgeBasePlugin, UserDict):
|
|
|
56
56
|
yield from super().__iter__()
|
|
57
57
|
yield from iter(ALL_TYPES)
|
|
58
58
|
|
|
59
|
+
def __getstate__(self):
|
|
60
|
+
return self.data # do not pickle self.kb
|
|
61
|
+
|
|
62
|
+
def __setstate__(self, state):
|
|
63
|
+
self.data = state
|
|
64
|
+
|
|
59
65
|
def iter_own(self):
|
|
60
66
|
"""
|
|
61
67
|
Iterate over all the names which are stored in this object - i.e. ``values()`` without ``ALL_TYPES``
|
|
@@ -122,7 +122,32 @@ class VariableManagerInternal(Serializable):
|
|
|
122
122
|
self.__dict__.update(state)
|
|
123
123
|
|
|
124
124
|
def __getstate__(self):
|
|
125
|
-
|
|
125
|
+
attributes = [
|
|
126
|
+
"func_addr",
|
|
127
|
+
"_variables",
|
|
128
|
+
"_global_region",
|
|
129
|
+
"_stack_region",
|
|
130
|
+
"_register_region",
|
|
131
|
+
"_live_variables",
|
|
132
|
+
"_variable_accesses",
|
|
133
|
+
"_insn_to_variable",
|
|
134
|
+
"_stmt_to_variable",
|
|
135
|
+
"_variable_to_stmt",
|
|
136
|
+
"_atom_to_variable",
|
|
137
|
+
"_ident_to_variable",
|
|
138
|
+
"_variable_counters",
|
|
139
|
+
"_unified_variables",
|
|
140
|
+
"_variables_to_unified_variables",
|
|
141
|
+
"_phi_variables",
|
|
142
|
+
"_variables_to_phivars",
|
|
143
|
+
"_phi_variables_by_block",
|
|
144
|
+
"types",
|
|
145
|
+
"variable_to_types",
|
|
146
|
+
"variables_with_manual_types",
|
|
147
|
+
"_variables_without_writes",
|
|
148
|
+
"ret_val_size",
|
|
149
|
+
]
|
|
150
|
+
d = {k: getattr(self, k) for k in attributes}
|
|
126
151
|
d["manager"] = None
|
|
127
152
|
d["types"].kb = None
|
|
128
153
|
return d
|
|
@@ -441,6 +466,29 @@ class VariableManagerInternal(Serializable):
|
|
|
441
466
|
if atom_hash is not None:
|
|
442
467
|
self._atom_to_variable[key][atom_hash].add(var_and_offset)
|
|
443
468
|
|
|
469
|
+
def remove_variable_by_atom(self, location: "CodeLocation", variable: SimVariable, atom):
|
|
470
|
+
key = (
|
|
471
|
+
(location.block_addr, location.stmt_idx)
|
|
472
|
+
if location.block_idx is None
|
|
473
|
+
else (location.block_addr, location.block_idx, location.stmt_idx)
|
|
474
|
+
)
|
|
475
|
+
if key in self._stmt_to_variable:
|
|
476
|
+
for var_and_offset in list(self._stmt_to_variable[key]):
|
|
477
|
+
if var_and_offset[0] == variable:
|
|
478
|
+
self._stmt_to_variable[key].remove(var_and_offset)
|
|
479
|
+
if not self._stmt_to_variable[key]:
|
|
480
|
+
del self._stmt_to_variable[key]
|
|
481
|
+
|
|
482
|
+
atom_hash = (hash(atom) & 0xFFFF_FFFF) if atom is not None else None
|
|
483
|
+
if key in self._atom_to_variable and atom_hash is not None and atom_hash in self._atom_to_variable[key]:
|
|
484
|
+
for var_and_offset in list(self._atom_to_variable[key][atom_hash]):
|
|
485
|
+
if var_and_offset[0] == variable:
|
|
486
|
+
self._atom_to_variable[key][atom_hash].discard(var_and_offset)
|
|
487
|
+
if not self._atom_to_variable[key][atom_hash]:
|
|
488
|
+
del self._atom_to_variable[key][atom_hash]
|
|
489
|
+
if not self._atom_to_variable[key]:
|
|
490
|
+
del self._atom_to_variable[key]
|
|
491
|
+
|
|
444
492
|
def make_phi_node(self, block_addr, *variables):
|
|
445
493
|
"""
|
|
446
494
|
Create a phi variable for variables at block `block_addr`.
|
|
@@ -736,10 +784,11 @@ class VariableManagerInternal(Serializable):
|
|
|
736
784
|
if variable in self._phi_variables:
|
|
737
785
|
# a phi variable is definitely not an input variable
|
|
738
786
|
continue
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
if
|
|
742
|
-
|
|
787
|
+
if variable in self._variable_accesses:
|
|
788
|
+
accesses = self._variable_accesses[variable]
|
|
789
|
+
if has_read_access(accesses):
|
|
790
|
+
if not exclude_specials or not variable.category:
|
|
791
|
+
input_variables.append(variable)
|
|
743
792
|
|
|
744
793
|
return input_variables
|
|
745
794
|
|
angr/simos/simos.py
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
# pylint:disable=missing-class-docstring
|
|
1
2
|
from typing import Iterable, Tuple, Any, Callable, Optional
|
|
2
3
|
|
|
3
4
|
from . import MemoryMixin
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class MultiValueMergerMixin(MemoryMixin):
|
|
7
|
-
def __init__(
|
|
8
|
+
def __init__(
|
|
9
|
+
self, *args, element_limit=5, annotation_limit=256, top_func=None, is_top_func=None, phi_maker=None, **kwargs
|
|
10
|
+
):
|
|
8
11
|
self._element_limit = element_limit
|
|
9
12
|
self._annotation_limit = annotation_limit
|
|
10
13
|
self._top_func: Callable = top_func
|
|
14
|
+
self._is_top_func: Callable = is_top_func
|
|
11
15
|
self._phi_maker: Optional[Callable] = phi_maker
|
|
12
16
|
|
|
13
17
|
super().__init__(*args, **kwargs)
|
|
@@ -20,18 +24,23 @@ class MultiValueMergerMixin(MemoryMixin):
|
|
|
20
24
|
return {phi_var}
|
|
21
25
|
|
|
22
26
|
# try to merge it in the traditional way
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# python implicitly calls __eq__ to determine if the two objects are actually the same
|
|
27
|
-
# and that just results in a new AST for a BV. Python then tries to convert that AST to a bool
|
|
28
|
-
# which fails with the safeguard in claripy.ast.bool.Bool.__bool__.
|
|
29
|
-
stripped_values_set = {v._apply_to_annotations(lambda alist: None).cache_key for v in values_set}
|
|
30
|
-
if len(stripped_values_set) > 1:
|
|
27
|
+
has_top = any(self._is_top_func(v) for v in values_set)
|
|
28
|
+
if has_top or len(values_set) > self._element_limit:
|
|
29
|
+
if has_top:
|
|
31
30
|
ret_val = self._top_func(merged_size * self.state.arch.byte_width)
|
|
32
31
|
else:
|
|
33
|
-
#
|
|
34
|
-
|
|
32
|
+
# strip annotations from each value and see how many raw values there are in total
|
|
33
|
+
# We have to use cache_key to determine uniqueness here, because if __hash__ collides,
|
|
34
|
+
# python implicitly calls __eq__ to determine if the two objects are actually the same
|
|
35
|
+
# and that just results in a new AST for a BV. Python then tries to convert that AST to a bool
|
|
36
|
+
# which fails with the safeguard in claripy.ast.bool.Bool.__bool__.
|
|
37
|
+
stripped_values_set = {v._apply_to_annotations(lambda alist: None).cache_key for v in values_set}
|
|
38
|
+
if len(stripped_values_set) > 1:
|
|
39
|
+
ret_val = self._top_func(merged_size * self.state.arch.byte_width)
|
|
40
|
+
else:
|
|
41
|
+
# Get the AST back from the cache_key
|
|
42
|
+
ret_val = next(iter(stripped_values_set)).ast
|
|
43
|
+
|
|
35
44
|
# migrate annotations
|
|
36
45
|
annotations = []
|
|
37
46
|
annotations_set = set()
|
|
@@ -41,6 +50,7 @@ class MultiValueMergerMixin(MemoryMixin):
|
|
|
41
50
|
annotations.append(anno)
|
|
42
51
|
annotations_set.add(anno)
|
|
43
52
|
if annotations:
|
|
53
|
+
annotations = sorted(annotations, key=str)
|
|
44
54
|
ret_val = ret_val.annotate(*annotations[: self._annotation_limit])
|
|
45
55
|
merged_val = {ret_val}
|
|
46
56
|
else:
|
|
@@ -52,5 +62,6 @@ class MultiValueMergerMixin(MemoryMixin):
|
|
|
52
62
|
copied._element_limit = self._element_limit
|
|
53
63
|
copied._annotation_limit = self._annotation_limit
|
|
54
64
|
copied._top_func = self._top_func
|
|
65
|
+
copied._is_top_func = self._is_top_func
|
|
55
66
|
copied._phi_maker = self._phi_maker
|
|
56
67
|
return copied
|