angr 9.2.138__py3-none-macosx_11_0_arm64.whl → 9.2.139__py3-none-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/analyses/calling_convention/fact_collector.py +59 -12
- angr/analyses/calling_convention/utils.py +2 -2
- angr/analyses/cfg/cfg_fast.py +12 -4
- angr/analyses/decompiler/ail_simplifier.py +14 -3
- angr/analyses/decompiler/block_simplifier.py +0 -2
- angr/analyses/decompiler/callsite_maker.py +80 -14
- angr/analyses/decompiler/clinic.py +31 -37
- angr/analyses/decompiler/condition_processor.py +2 -2
- angr/analyses/decompiler/decompiler.py +2 -0
- angr/analyses/decompiler/dephication/rewriting_engine.py +16 -7
- angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
- angr/analyses/decompiler/optimization_passes/condition_constprop.py +149 -0
- angr/analyses/decompiler/optimization_passes/deadblock_remover.py +12 -3
- angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +5 -2
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +15 -7
- angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +7 -10
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +12 -1
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +61 -25
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +50 -1
- angr/analyses/decompiler/presets/fast.py +2 -0
- angr/analyses/decompiler/presets/full.py +2 -0
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +4 -0
- angr/analyses/decompiler/ssailification/rewriting_engine.py +20 -2
- angr/analyses/decompiler/ssailification/traversal_engine.py +4 -3
- angr/analyses/decompiler/structured_codegen/c.py +10 -3
- angr/analyses/decompiler/structuring/dream.py +7 -2
- angr/analyses/decompiler/structuring/phoenix.py +101 -49
- angr/analyses/decompiler/structuring/structurer_base.py +85 -36
- angr/analyses/decompiler/structuring/structurer_nodes.py +3 -1
- angr/analyses/deobfuscator/api_obf_finder.py +6 -1
- angr/analyses/deobfuscator/api_obf_type2_finder.py +158 -0
- angr/analyses/s_propagator.py +127 -50
- angr/analyses/s_reaching_definitions/s_rda_view.py +2 -2
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -1
- angr/analyses/variable_recovery/engine_ail.py +1 -1
- angr/analyses/variable_recovery/engine_base.py +55 -62
- angr/analyses/variable_recovery/engine_vex.py +1 -1
- angr/analyses/variable_recovery/irsb_scanner.py +2 -2
- angr/calling_conventions.py +66 -9
- angr/engines/engine.py +2 -18
- angr/engines/light/engine.py +3 -8
- angr/engines/pcode/emulate.py +2 -2
- angr/engines/pcode/lifter.py +2 -2
- angr/engines/successors.py +1 -8
- angr/engines/vex/lifter.py +2 -2
- angr/engines/vex/light/light.py +2 -2
- angr/knowledge_plugins/cfg/cfg_model.py +3 -2
- angr/knowledge_plugins/labels.py +2 -2
- angr/knowledge_plugins/obfuscations.py +1 -0
- angr/knowledge_plugins/xrefs/xref_manager.py +4 -0
- angr/lib/angr_native.dylib +0 -0
- {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/METADATA +6 -6
- {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/RECORD +59 -57
- {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/LICENSE +0 -0
- {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/WHEEL +0 -0
- {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/entry_points.txt +0 -0
- {angr-9.2.138.dist-info → angr-9.2.139.dist-info}/top_level.txt +0 -0
angr/analyses/s_propagator.py
CHANGED
|
@@ -16,7 +16,7 @@ from ailment.expression import (
|
|
|
16
16
|
Convert,
|
|
17
17
|
Expression,
|
|
18
18
|
)
|
|
19
|
-
from ailment.statement import Assignment, Store, Return, Jump
|
|
19
|
+
from ailment.statement import Assignment, Store, Return, Jump, ConditionalJump
|
|
20
20
|
|
|
21
21
|
from angr.knowledge_plugins.functions import Function
|
|
22
22
|
from angr.code_location import CodeLocation, ExternalCodeLocation
|
|
@@ -45,6 +45,8 @@ class SPropagatorModel:
|
|
|
45
45
|
|
|
46
46
|
def __init__(self):
|
|
47
47
|
self.replacements: Mapping[CodeLocation, Mapping[Expression, Expression]] = {}
|
|
48
|
+
# store vvars that are definitely dead (but usually not removed by default because they are stack variables)
|
|
49
|
+
self.dead_vvar_ids: set[int] = set()
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
class SPropagatorAnalysis(Analysis):
|
|
@@ -90,6 +92,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
90
92
|
bp_as_gpr = the_func.info.get("bp_as_gpr", False)
|
|
91
93
|
self._bp_as_gpr = bp_as_gpr
|
|
92
94
|
|
|
95
|
+
# output
|
|
93
96
|
self.model = SPropagatorModel()
|
|
94
97
|
|
|
95
98
|
self._analyze()
|
|
@@ -98,6 +101,10 @@ class SPropagatorAnalysis(Analysis):
|
|
|
98
101
|
def replacements(self):
|
|
99
102
|
return self.model.replacements
|
|
100
103
|
|
|
104
|
+
@property
|
|
105
|
+
def dead_vvar_ids(self):
|
|
106
|
+
return self.model.dead_vvar_ids
|
|
107
|
+
|
|
101
108
|
def _analyze(self):
|
|
102
109
|
blocks: dict[tuple[int, int | None], Block]
|
|
103
110
|
match self.mode:
|
|
@@ -132,7 +139,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
132
139
|
|
|
133
140
|
replacements = defaultdict(dict)
|
|
134
141
|
|
|
135
|
-
# find constant assignments
|
|
142
|
+
# find constant and other propagatable assignments
|
|
136
143
|
vvarid_to_vvar = {}
|
|
137
144
|
const_vvars: dict[int, Const] = {}
|
|
138
145
|
for vvar, defloc in vvar_deflocs.items():
|
|
@@ -140,7 +147,6 @@ class SPropagatorAnalysis(Analysis):
|
|
|
140
147
|
continue
|
|
141
148
|
|
|
142
149
|
vvarid_to_vvar[vvar.varid] = vvar
|
|
143
|
-
defloc = vvar_deflocs[vvar]
|
|
144
150
|
if isinstance(defloc, ExternalCodeLocation):
|
|
145
151
|
continue
|
|
146
152
|
|
|
@@ -178,8 +184,27 @@ class SPropagatorAnalysis(Analysis):
|
|
|
178
184
|
for vvar_at_use, useloc in vvar_uselocs[vvar.varid]:
|
|
179
185
|
replacements[useloc][vvar_at_use] = const_value
|
|
180
186
|
|
|
181
|
-
|
|
182
|
-
|
|
187
|
+
# function mode only
|
|
188
|
+
if self.mode == "function":
|
|
189
|
+
for vvar, defloc in vvar_deflocs.items():
|
|
190
|
+
if vvar.varid not in vvar_uselocs:
|
|
191
|
+
continue
|
|
192
|
+
if vvar.varid in const_vvars:
|
|
193
|
+
continue
|
|
194
|
+
if isinstance(defloc, ExternalCodeLocation):
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
assert defloc.block_addr is not None
|
|
198
|
+
assert defloc.stmt_idx is not None
|
|
199
|
+
|
|
200
|
+
block = blocks[(defloc.block_addr, defloc.block_idx)]
|
|
201
|
+
stmt = block.statements[defloc.stmt_idx]
|
|
202
|
+
if (
|
|
203
|
+
(vvar.was_reg or vvar.was_parameter)
|
|
204
|
+
and len(vvar_uselocs[vvar.varid]) <= 2
|
|
205
|
+
and isinstance(stmt, Assignment)
|
|
206
|
+
and isinstance(stmt.src, Load)
|
|
207
|
+
):
|
|
183
208
|
# do we want to propagate this Load expression if it's used for less than twice?
|
|
184
209
|
# it's often seen in the following pattern, where propagation will be beneficial:
|
|
185
210
|
# v0 = Load(...)
|
|
@@ -197,63 +222,80 @@ class SPropagatorAnalysis(Analysis):
|
|
|
197
222
|
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
198
223
|
continue
|
|
199
224
|
|
|
200
|
-
if
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
225
|
+
if (
|
|
226
|
+
(vvar.was_reg or vvar.was_stack)
|
|
227
|
+
and len(vvar_uselocs[vvar.varid]) == 2
|
|
228
|
+
and not is_phi_assignment(stmt)
|
|
229
|
+
):
|
|
230
|
+
# a special case: in a typical switch-case construct, a variable may be used once for comparison
|
|
231
|
+
# for the default case and then used again for constructing the jump target. we can propagate this
|
|
232
|
+
# variable for such cases.
|
|
233
|
+
uselocs = {loc for _, loc in vvar_uselocs[vvar.varid]}
|
|
234
|
+
if self.is_vvar_used_for_addr_loading_switch_case(uselocs, blocks):
|
|
235
|
+
for vvar_used, vvar_useloc in vvar_uselocs[vvar.varid]:
|
|
236
|
+
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
237
|
+
# mark the vvar as dead and should be removed
|
|
238
|
+
self.model.dead_vvar_ids.add(vvar.varid)
|
|
207
239
|
continue
|
|
208
240
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
]
|
|
217
|
-
if is_phi_assignment(useloc_stmt):
|
|
218
|
-
if (
|
|
219
|
-
isinstance(stmt.src, VirtualVariable)
|
|
220
|
-
and stmt.src.oident == useloc_stmt.dst.oident
|
|
221
|
-
and stmt.src.category == useloc_stmt.dst.category
|
|
222
|
-
):
|
|
223
|
-
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
224
|
-
else:
|
|
241
|
+
if vvar.was_reg or vvar.was_parameter:
|
|
242
|
+
if len(vvar_uselocs[vvar.varid]) == 1:
|
|
243
|
+
vvar_used, vvar_useloc = next(iter(vvar_uselocs[vvar.varid]))
|
|
244
|
+
if is_const_vvar_load_assignment(stmt) and not self.has_store_stmt_in_between(
|
|
245
|
+
blocks, defloc, vvar_useloc
|
|
246
|
+
):
|
|
247
|
+
# we can propagate this load because there is no store between its def and use
|
|
225
248
|
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
226
|
-
|
|
249
|
+
continue
|
|
227
250
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
251
|
+
if is_const_and_vvar_assignment(stmt):
|
|
252
|
+
# if the useloc is a phi assignment statement, ensure that stmt.src is the same as the phi
|
|
253
|
+
# variable
|
|
254
|
+
assert vvar_useloc.block_addr is not None
|
|
255
|
+
assert vvar_useloc.stmt_idx is not None
|
|
256
|
+
useloc_stmt = blocks[(vvar_useloc.block_addr, vvar_useloc.block_idx)].statements[
|
|
257
|
+
vvar_useloc.stmt_idx
|
|
258
|
+
]
|
|
259
|
+
if is_phi_assignment(useloc_stmt):
|
|
260
|
+
if (
|
|
261
|
+
isinstance(stmt.src, VirtualVariable)
|
|
262
|
+
and stmt.src.oident == useloc_stmt.dst.oident
|
|
263
|
+
and stmt.src.category == useloc_stmt.dst.category
|
|
264
|
+
):
|
|
265
|
+
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
266
|
+
else:
|
|
239
267
|
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
240
268
|
continue
|
|
241
269
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
270
|
+
else:
|
|
271
|
+
non_exitsite_uselocs = [
|
|
272
|
+
loc
|
|
273
|
+
for _, loc in vvar_uselocs[vvar.varid]
|
|
274
|
+
if (loc.block_addr, loc.block_idx, loc.stmt_idx) not in (retsites | jumpsites)
|
|
275
|
+
]
|
|
276
|
+
if is_const_and_vvar_assignment(stmt):
|
|
277
|
+
if len(non_exitsite_uselocs) == 1:
|
|
278
|
+
# this vvar is used once if we exclude its uses at ret sites or jump sites. we can
|
|
279
|
+
# propagate it
|
|
250
280
|
for vvar_used, vvar_useloc in vvar_uselocs[vvar.varid]:
|
|
251
281
|
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
252
282
|
continue
|
|
253
283
|
|
|
284
|
+
if len(set(non_exitsite_uselocs)) == 1 and not has_ite_expr(stmt.src):
|
|
285
|
+
useloc = non_exitsite_uselocs[0]
|
|
286
|
+
assert useloc.block_addr is not None
|
|
287
|
+
assert useloc.stmt_idx is not None
|
|
288
|
+
useloc_stmt = blocks[(useloc.block_addr, useloc.block_idx)].statements[useloc.stmt_idx]
|
|
289
|
+
if stmt.src.depth <= 3 and not has_ite_stmt(useloc_stmt):
|
|
290
|
+
# remove duplicate use locs (e.g., if the variable is used multiple times by the
|
|
291
|
+
# same statement) - but ensure stmt is simple enough
|
|
292
|
+
for vvar_used, vvar_useloc in vvar_uselocs[vvar.varid]:
|
|
293
|
+
replacements[vvar_useloc][vvar_used] = stmt.src
|
|
294
|
+
continue
|
|
295
|
+
|
|
254
296
|
# special logic for global variables: if it's used once or multiple times, and the variable is never
|
|
255
297
|
# updated before it's used, we will propagate the load
|
|
256
|
-
if isinstance(stmt, Assignment):
|
|
298
|
+
if (vvar.was_reg or vvar.was_parameter) and isinstance(stmt, Assignment):
|
|
257
299
|
stmt_src = stmt.src
|
|
258
300
|
# unpack conversions
|
|
259
301
|
while isinstance(stmt_src, Convert):
|
|
@@ -442,7 +484,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
442
484
|
if block is def_block:
|
|
443
485
|
starting_stmt_idx = defloc.stmt_idx + 1
|
|
444
486
|
if block is use_block:
|
|
445
|
-
ending_stmt_idx = useloc.stmt_idx
|
|
487
|
+
ending_stmt_idx = useloc.stmt_idx
|
|
446
488
|
|
|
447
489
|
for i in range(starting_stmt_idx, ending_stmt_idx):
|
|
448
490
|
if isinstance(block.statements[i], Store):
|
|
@@ -458,5 +500,40 @@ class SPropagatorAnalysis(Analysis):
|
|
|
458
500
|
|
|
459
501
|
return False
|
|
460
502
|
|
|
503
|
+
@staticmethod
|
|
504
|
+
def is_vvar_used_for_addr_loading_switch_case(uselocs: set[CodeLocation], blocks) -> bool:
|
|
505
|
+
"""
|
|
506
|
+
Check if a virtual variable is used for loading an address in a switch-case construct.
|
|
507
|
+
|
|
508
|
+
:param uselocs: The use locations of the virtual variable.
|
|
509
|
+
:param blocks: All blocks of the current function.
|
|
510
|
+
:return: True if the virtual variable is used for loading an address in a switch-case construct, False
|
|
511
|
+
otherwise.
|
|
512
|
+
"""
|
|
513
|
+
|
|
514
|
+
if len(uselocs) != 2:
|
|
515
|
+
return False
|
|
516
|
+
|
|
517
|
+
useloc_0, useloc_1 = list(uselocs)
|
|
518
|
+
block_0 = blocks[(useloc_0.block_addr, useloc_0.block_idx)]
|
|
519
|
+
stmt_0 = block_0.statements[useloc_0.stmt_idx]
|
|
520
|
+
block_1 = blocks[(useloc_1.block_addr, useloc_1.block_idx)]
|
|
521
|
+
stmt_1 = block_1.statements[useloc_1.stmt_idx]
|
|
522
|
+
|
|
523
|
+
if isinstance(stmt_0, Jump):
|
|
524
|
+
stmt_0, stmt_1 = stmt_1, stmt_0
|
|
525
|
+
block_0, block_1 = block_1, block_0
|
|
526
|
+
if not isinstance(stmt_0, ConditionalJump) or not isinstance(stmt_1, Jump):
|
|
527
|
+
return False
|
|
528
|
+
|
|
529
|
+
# check if stmt_0 jumps to block_1
|
|
530
|
+
if not isinstance(stmt_0.true_target, Const) or not isinstance(stmt_0.false_target, Const):
|
|
531
|
+
return False
|
|
532
|
+
stmt_0_targets = {
|
|
533
|
+
(stmt_0.true_target.value, stmt_0.true_target_idx),
|
|
534
|
+
(stmt_0.false_target.value, stmt_0.false_target_idx),
|
|
535
|
+
}
|
|
536
|
+
return (block_1.addr, block_1.idx) in stmt_0_targets
|
|
537
|
+
|
|
461
538
|
|
|
462
539
|
register_analysis(SPropagatorAnalysis, "SPropagator")
|
|
@@ -79,8 +79,8 @@ class StackVVarPredicate:
|
|
|
79
79
|
isinstance(stmt, Assignment)
|
|
80
80
|
and isinstance(stmt.dst, VirtualVariable)
|
|
81
81
|
and stmt.dst.was_stack
|
|
82
|
-
and stmt.dst.stack_offset
|
|
83
|
-
and stmt.dst.
|
|
82
|
+
and stmt.dst.stack_offset <= self.stack_offset < stmt.dst.stack_offset + stmt.dst.size
|
|
83
|
+
and stmt.dst.stack_offset <= self.stack_offset + self.size <= stmt.dst.stack_offset + stmt.dst.size
|
|
84
84
|
):
|
|
85
85
|
self.vvars.add(stmt.dst)
|
|
86
86
|
return True
|
|
@@ -143,7 +143,9 @@ class SReachingDefinitionsAnalysis(Analysis):
|
|
|
143
143
|
cc = cc_cls(self.project.arch)
|
|
144
144
|
|
|
145
145
|
codeloc = CodeLocation(block_addr, stmt_idx, block_idx=block_idx, ins_addr=stmt.ins_addr)
|
|
146
|
-
arg_locs = cc.ARG_REGS
|
|
146
|
+
arg_locs = list(cc.ARG_REGS)
|
|
147
|
+
if cc.FP_ARG_REGS:
|
|
148
|
+
arg_locs += [r_name for r_name in cc.FP_ARG_REGS if r_name not in arg_locs]
|
|
147
149
|
|
|
148
150
|
for arg_reg_name in arg_locs:
|
|
149
151
|
reg_offset = self.project.arch.registers[arg_reg_name][0]
|
|
@@ -84,7 +84,7 @@ class SimEngineVRAIL(
|
|
|
84
84
|
if vvar is not None:
|
|
85
85
|
r = self._read_from_vvar(vvar, expr=stmt.src, vvar_id=self._mapped_vvarid(vvar.varid))
|
|
86
86
|
if r.variable is not None:
|
|
87
|
-
pv = self.variable_manager[self.func_addr]._phi_variables
|
|
87
|
+
pv = self.state.variable_manager[self.func_addr]._phi_variables
|
|
88
88
|
if variable not in pv:
|
|
89
89
|
pv[variable] = set()
|
|
90
90
|
pv[variable].add(r.variable)
|