angr 9.2.147__py3-none-manylinux2014_x86_64.whl → 9.2.149__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 -11
- angr/analyses/calling_convention/calling_convention.py +42 -2
- angr/analyses/calling_convention/fact_collector.py +5 -4
- angr/analyses/calling_convention/utils.py +1 -0
- angr/analyses/cfg/cfg_base.py +3 -59
- angr/analyses/cfg/cfg_emulated.py +17 -14
- angr/analyses/cfg/cfg_fast.py +68 -63
- angr/analyses/cfg/cfg_fast_soot.py +3 -3
- angr/analyses/decompiler/ail_simplifier.py +65 -32
- angr/analyses/decompiler/block_simplifier.py +20 -6
- angr/analyses/decompiler/callsite_maker.py +28 -18
- angr/analyses/decompiler/clinic.py +84 -17
- angr/analyses/decompiler/condition_processor.py +0 -21
- angr/analyses/decompiler/counters/call_counter.py +3 -0
- angr/analyses/decompiler/dephication/rewriting_engine.py +24 -2
- angr/analyses/decompiler/optimization_passes/__init__.py +5 -0
- angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +15 -13
- angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +1 -1
- angr/analyses/decompiler/optimization_passes/determine_load_sizes.py +64 -0
- angr/analyses/decompiler/optimization_passes/eager_std_string_concatenation.py +165 -0
- angr/analyses/decompiler/optimization_passes/engine_base.py +11 -2
- angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +17 -2
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +10 -6
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +99 -30
- angr/analyses/decompiler/peephole_optimizations/__init__.py +6 -0
- angr/analyses/decompiler/peephole_optimizations/base.py +43 -3
- angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +1 -1
- angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +3 -0
- angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +4 -1
- angr/analyses/decompiler/peephole_optimizations/remove_cxx_destructor_calls.py +32 -0
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +69 -2
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +14 -0
- angr/analyses/decompiler/peephole_optimizations/rewrite_conv_mul.py +40 -0
- angr/analyses/decompiler/peephole_optimizations/rewrite_cxx_operator_calls.py +90 -0
- angr/analyses/decompiler/presets/fast.py +2 -0
- angr/analyses/decompiler/presets/full.py +2 -0
- angr/analyses/decompiler/ssailification/rewriting_engine.py +51 -4
- angr/analyses/decompiler/ssailification/ssailification.py +23 -3
- angr/analyses/decompiler/ssailification/traversal_engine.py +15 -1
- angr/analyses/decompiler/structured_codegen/c.py +146 -15
- angr/analyses/decompiler/structuring/phoenix.py +11 -3
- angr/analyses/decompiler/utils.py +6 -1
- angr/analyses/deobfuscator/api_obf_finder.py +5 -1
- angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +1 -1
- angr/analyses/forward_analysis/visitors/graph.py +0 -8
- angr/analyses/identifier/runner.py +1 -1
- angr/analyses/reaching_definitions/function_handler.py +4 -4
- angr/analyses/reassembler.py +1 -1
- angr/analyses/s_reaching_definitions/s_rda_view.py +1 -0
- angr/analyses/stack_pointer_tracker.py +1 -1
- angr/analyses/static_hooker.py +11 -9
- angr/analyses/typehoon/lifter.py +20 -0
- angr/analyses/typehoon/simple_solver.py +42 -9
- angr/analyses/typehoon/translator.py +4 -1
- angr/analyses/typehoon/typeconsts.py +17 -6
- angr/analyses/typehoon/typehoon.py +21 -5
- angr/analyses/variable_recovery/engine_ail.py +52 -13
- angr/analyses/variable_recovery/engine_base.py +37 -12
- angr/analyses/variable_recovery/variable_recovery_fast.py +33 -2
- angr/calling_conventions.py +96 -27
- angr/engines/light/engine.py +7 -0
- angr/exploration_techniques/director.py +1 -1
- angr/knowledge_plugins/functions/function.py +109 -38
- angr/knowledge_plugins/functions/function_manager.py +9 -0
- angr/knowledge_plugins/functions/function_parser.py +9 -1
- angr/knowledge_plugins/functions/soot_function.py +1 -1
- angr/knowledge_plugins/key_definitions/key_definition_manager.py +1 -1
- angr/knowledge_plugins/propagations/states.py +5 -2
- angr/knowledge_plugins/variables/variable_manager.py +3 -3
- angr/procedures/definitions/__init__.py +15 -12
- angr/procedures/definitions/types_stl.py +22 -0
- angr/procedures/stubs/format_parser.py +1 -1
- angr/project.py +23 -29
- angr/protos/cfg_pb2.py +14 -25
- angr/protos/function_pb2.py +11 -22
- angr/protos/primitives_pb2.py +36 -47
- angr/protos/variables_pb2.py +28 -39
- angr/protos/xrefs_pb2.py +8 -19
- angr/sim_type.py +251 -146
- angr/simos/cgc.py +1 -1
- angr/simos/linux.py +5 -5
- angr/simos/windows.py +5 -5
- angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +1 -1
- {angr-9.2.147.dist-info → angr-9.2.149.dist-info}/METADATA +9 -8
- {angr-9.2.147.dist-info → angr-9.2.149.dist-info}/RECORD +90 -84
- {angr-9.2.147.dist-info → angr-9.2.149.dist-info}/WHEEL +1 -1
- {angr-9.2.147.dist-info → angr-9.2.149.dist-info/licenses}/LICENSE +3 -0
- {angr-9.2.147.dist-info → angr-9.2.149.dist-info}/entry_points.txt +0 -0
- {angr-9.2.147.dist-info → angr-9.2.149.dist-info}/top_level.txt +0 -0
|
@@ -29,7 +29,7 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
29
29
|
PLATFORMS = ["windows"]
|
|
30
30
|
STAGE = OptimizationPassStage.AFTER_SINGLE_BLOCK_SIMPLIFICATION
|
|
31
31
|
NAME = "Simplify stack canaries in Windows PE files"
|
|
32
|
-
DESCRIPTION = __doc__.strip()
|
|
32
|
+
DESCRIPTION = __doc__.strip() # type:ignore
|
|
33
33
|
|
|
34
34
|
def __init__(self, func, **kwargs):
|
|
35
35
|
super().__init__(func, **kwargs)
|
|
@@ -63,19 +63,16 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
63
63
|
first_block, canary_init_stmt_ids = init_stmts
|
|
64
64
|
canary_init_stmt = first_block.statements[canary_init_stmt_ids[-1]]
|
|
65
65
|
# where is the stack canary stored?
|
|
66
|
-
if not isinstance(canary_init_stmt, ailment.Stmt.Store)
|
|
67
|
-
|
|
68
|
-
)
|
|
66
|
+
if not isinstance(canary_init_stmt, ailment.Stmt.Store):
|
|
67
|
+
return
|
|
68
|
+
store_offset = self._get_bp_offset(canary_init_stmt.addr, canary_init_stmt.ins_addr)
|
|
69
|
+
if store_offset is None:
|
|
69
70
|
_l.debug(
|
|
70
|
-
"Unsupported canary storing location %s. Expects
|
|
71
|
+
"Unsupported canary storing location %s. Expects a StackBaseOffset or (bp - Const).",
|
|
71
72
|
canary_init_stmt.addr,
|
|
72
73
|
)
|
|
73
74
|
return
|
|
74
75
|
|
|
75
|
-
store_offset = canary_init_stmt.addr.offset
|
|
76
|
-
if not isinstance(store_offset, int):
|
|
77
|
-
_l.debug("Unsupported canary storing offset %s. Expects an int.", store_offset)
|
|
78
|
-
|
|
79
76
|
# The function should have at least one end point calling _security_check_cookie
|
|
80
77
|
# note that (at least for now) we rely on FLIRT to identify the _security_check_cookie function inside the
|
|
81
78
|
# binary.
|
|
@@ -154,17 +151,42 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
154
151
|
store_offset, canary_init_stmt.size, "canary", StackItemType.STACK_CANARY
|
|
155
152
|
)
|
|
156
153
|
|
|
157
|
-
def _find_canary_init_stmt(self):
|
|
154
|
+
def _find_canary_init_stmt(self) -> tuple[ailment.Block, list[int]] | None:
|
|
158
155
|
first_block = self._get_block(self._func.addr)
|
|
159
156
|
if first_block is None:
|
|
160
157
|
return None
|
|
161
158
|
|
|
159
|
+
r = self._extract_canary_init_stmt_from_block(first_block)
|
|
160
|
+
if r is not None:
|
|
161
|
+
return first_block, r
|
|
162
|
+
|
|
163
|
+
# if the first block is calling alloca_probe, we may want to take the second block instead
|
|
164
|
+
if (
|
|
165
|
+
first_block.statements
|
|
166
|
+
and first_block.original_size > 0
|
|
167
|
+
and isinstance(first_block.statements[-1], ailment.statement.Call)
|
|
168
|
+
and isinstance(first_block.statements[-1].target, ailment.expression.Const)
|
|
169
|
+
):
|
|
170
|
+
# check if the target is alloca_probe
|
|
171
|
+
callee_addr = first_block.statements[-1].target.value
|
|
172
|
+
if (
|
|
173
|
+
self.kb.functions.contains_addr(callee_addr)
|
|
174
|
+
and self.kb.functions.get_by_addr(callee_addr).info.get("is_alloca_probe", False) is True
|
|
175
|
+
):
|
|
176
|
+
second_block = self._get_block(first_block.addr + first_block.original_size)
|
|
177
|
+
if second_block is not None:
|
|
178
|
+
r = self._extract_canary_init_stmt_from_block(second_block)
|
|
179
|
+
if r is not None:
|
|
180
|
+
return second_block, r
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
def _extract_canary_init_stmt_from_block(self, block: ailment.Block) -> list[int] | None:
|
|
162
184
|
load_stmt_idx = None
|
|
163
185
|
load_reg = None
|
|
164
186
|
xor_stmt_idx = None
|
|
165
187
|
xored_reg = None
|
|
166
188
|
|
|
167
|
-
for idx, stmt in enumerate(
|
|
189
|
+
for idx, stmt in enumerate(block.statements):
|
|
168
190
|
# if we are lucky and things get folded into one statement:
|
|
169
191
|
if (
|
|
170
192
|
isinstance(stmt, ailment.Stmt.Store)
|
|
@@ -178,7 +200,7 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
178
200
|
# Check addr: must be __security_cookie
|
|
179
201
|
load_addr = stmt.data.operands[0].addr.value
|
|
180
202
|
if load_addr == self._security_cookie_addr:
|
|
181
|
-
return
|
|
203
|
+
return [idx]
|
|
182
204
|
# or if we are unlucky and the load and the xor are two different statements
|
|
183
205
|
if (
|
|
184
206
|
isinstance(stmt, ailment.Stmt.Assignment)
|
|
@@ -191,33 +213,48 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
191
213
|
if load_addr == self._security_cookie_addr:
|
|
192
214
|
load_stmt_idx = idx
|
|
193
215
|
load_reg = stmt.dst.reg_offset
|
|
194
|
-
if load_stmt_idx is not None and idx
|
|
216
|
+
if load_stmt_idx is not None and xor_stmt_idx is None and idx >= load_stmt_idx + 1: # noqa:SIM102
|
|
195
217
|
if (
|
|
196
218
|
isinstance(stmt, ailment.Stmt.Assignment)
|
|
197
219
|
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
198
220
|
and stmt.dst.was_reg
|
|
221
|
+
and not self.project.arch.is_artificial_register(stmt.dst.reg_offset, stmt.dst.size)
|
|
199
222
|
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
200
223
|
and stmt.src.op == "Xor"
|
|
201
224
|
and isinstance(stmt.src.operands[0], ailment.Expr.VirtualVariable)
|
|
202
225
|
and stmt.src.operands[0].was_reg
|
|
203
226
|
and stmt.src.operands[0].reg_offset == load_reg
|
|
204
|
-
and
|
|
227
|
+
and (
|
|
228
|
+
isinstance(stmt.src.operands[1], ailment.Expr.StackBaseOffset)
|
|
229
|
+
or (
|
|
230
|
+
isinstance(stmt.src.operands[1], ailment.Expr.VirtualVariable)
|
|
231
|
+
and stmt.src.operands[1].was_reg
|
|
232
|
+
and stmt.src.operands[1].reg_offset == self.project.arch.registers["ebp"][0]
|
|
233
|
+
)
|
|
234
|
+
)
|
|
205
235
|
):
|
|
206
236
|
xor_stmt_idx = idx
|
|
207
237
|
xored_reg = stmt.dst.reg_offset
|
|
208
|
-
|
|
209
|
-
break
|
|
210
|
-
if xor_stmt_idx is not None and idx == xor_stmt_idx + 1:
|
|
238
|
+
if load_stmt_idx is not None and xor_stmt_idx is not None and idx == xor_stmt_idx + 1:
|
|
211
239
|
if (
|
|
212
240
|
isinstance(stmt, ailment.Stmt.Store)
|
|
213
|
-
and
|
|
241
|
+
and (
|
|
242
|
+
isinstance(stmt.addr, ailment.Expr.StackBaseOffset)
|
|
243
|
+
or (
|
|
244
|
+
isinstance(stmt.addr, ailment.Expr.BinaryOp)
|
|
245
|
+
and stmt.addr.op == "Sub"
|
|
246
|
+
and isinstance(stmt.addr.operands[0], ailment.Expr.VirtualVariable)
|
|
247
|
+
and stmt.addr.operands[0].was_reg
|
|
248
|
+
and stmt.addr.operands[0].reg_offset == self.project.arch.registers["ebp"][0]
|
|
249
|
+
and isinstance(stmt.addr.operands[1], ailment.Expr.Const)
|
|
250
|
+
)
|
|
251
|
+
)
|
|
214
252
|
and isinstance(stmt.data, ailment.Expr.VirtualVariable)
|
|
215
253
|
and stmt.data.was_reg
|
|
216
254
|
and stmt.data.reg_offset == xored_reg
|
|
217
255
|
):
|
|
218
|
-
return
|
|
256
|
+
return [load_stmt_idx, xor_stmt_idx, idx]
|
|
219
257
|
break
|
|
220
|
-
|
|
221
258
|
return None
|
|
222
259
|
|
|
223
260
|
def _find_amd64_canary_storing_stmt(self, block, canary_value_stack_offset):
|
|
@@ -275,6 +312,7 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
275
312
|
|
|
276
313
|
def _find_x86_canary_storing_stmt(self, block, canary_value_stack_offset):
|
|
277
314
|
load_stmt_idx = None
|
|
315
|
+
canary_reg_dst_offset = None
|
|
278
316
|
|
|
279
317
|
for idx, stmt in enumerate(block.statements):
|
|
280
318
|
# when we are lucky, we have one instruction
|
|
@@ -283,7 +321,7 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
283
321
|
isinstance(stmt, ailment.Stmt.Assignment)
|
|
284
322
|
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
285
323
|
and stmt.dst.was_reg
|
|
286
|
-
and stmt.dst.reg_offset
|
|
324
|
+
and not self.project.arch.is_artificial_register(stmt.dst.reg_offset, stmt.dst.size)
|
|
287
325
|
)
|
|
288
326
|
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
289
327
|
and stmt.src.op == "Xor"
|
|
@@ -291,8 +329,7 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
291
329
|
op0, op1 = stmt.src.operands
|
|
292
330
|
if (
|
|
293
331
|
isinstance(op0, ailment.Expr.Load)
|
|
294
|
-
and
|
|
295
|
-
and op0.addr.offset == canary_value_stack_offset
|
|
332
|
+
and self._get_bp_offset(op0.addr, stmt.ins_addr) == canary_value_stack_offset
|
|
296
333
|
) and isinstance(op1, ailment.Expr.StackBaseOffset):
|
|
297
334
|
# found it
|
|
298
335
|
return idx
|
|
@@ -300,12 +337,12 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
300
337
|
if (
|
|
301
338
|
isinstance(stmt, ailment.Stmt.Assignment)
|
|
302
339
|
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
303
|
-
and stmt.dst.reg_offset
|
|
340
|
+
and not self.project.arch.is_artificial_register(stmt.dst.reg_offset, stmt.dst.size)
|
|
304
341
|
and isinstance(stmt.src, ailment.Expr.Load)
|
|
305
|
-
and
|
|
306
|
-
and stmt.src.addr.offset == canary_value_stack_offset
|
|
342
|
+
and self._get_bp_offset(stmt.src.addr, stmt.ins_addr) == canary_value_stack_offset
|
|
307
343
|
):
|
|
308
344
|
load_stmt_idx = idx
|
|
345
|
+
canary_reg_dst_offset = stmt.dst.reg_offset
|
|
309
346
|
if (
|
|
310
347
|
load_stmt_idx is not None
|
|
311
348
|
and idx >= load_stmt_idx + 1
|
|
@@ -313,19 +350,49 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
313
350
|
isinstance(stmt, ailment.Stmt.Assignment)
|
|
314
351
|
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
315
352
|
and stmt.dst.was_reg
|
|
353
|
+
and not self.project.arch.is_artificial_register(stmt.dst.reg_offset, stmt.dst.size)
|
|
316
354
|
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
317
355
|
and stmt.src.op == "Xor"
|
|
318
356
|
)
|
|
319
357
|
and (
|
|
320
358
|
isinstance(stmt.src.operands[0], ailment.Expr.VirtualVariable)
|
|
321
359
|
and stmt.src.operands[0].was_reg
|
|
322
|
-
and stmt.src.operands[0].reg_offset ==
|
|
323
|
-
and
|
|
360
|
+
and stmt.src.operands[0].reg_offset == canary_reg_dst_offset
|
|
361
|
+
and self._get_bp_offset(stmt.src.operands[1], stmt.ins_addr) is not None
|
|
324
362
|
)
|
|
325
363
|
):
|
|
326
364
|
return idx
|
|
327
365
|
return None
|
|
328
366
|
|
|
367
|
+
def _get_bp_offset(self, expr: ailment.Expr.Expression, ins_addr: int) -> int | None:
|
|
368
|
+
if isinstance(expr, ailment.Expr.StackBaseOffset):
|
|
369
|
+
return expr.offset
|
|
370
|
+
if (
|
|
371
|
+
isinstance(expr, ailment.Expr.VirtualVariable)
|
|
372
|
+
and expr.was_reg
|
|
373
|
+
and expr.reg_offset == self.project.arch.bp_offset
|
|
374
|
+
):
|
|
375
|
+
if self._stack_pointer_tracker is None:
|
|
376
|
+
return None
|
|
377
|
+
return self._stack_pointer_tracker.offset_before(ins_addr, self.project.arch.bp_offset)
|
|
378
|
+
if (
|
|
379
|
+
isinstance(expr, ailment.Expr.BinaryOp)
|
|
380
|
+
and expr.op == "Sub"
|
|
381
|
+
and isinstance(expr.operands[0], ailment.Expr.VirtualVariable)
|
|
382
|
+
and expr.operands[0].was_reg
|
|
383
|
+
and expr.operands[0].reg_offset == self.project.arch.bp_offset
|
|
384
|
+
and isinstance(expr.operands[1], ailment.Expr.Const)
|
|
385
|
+
):
|
|
386
|
+
if self._stack_pointer_tracker is None:
|
|
387
|
+
return None
|
|
388
|
+
base_bp_offset = self._stack_pointer_tracker.offset_before(ins_addr, self.project.arch.bp_offset)
|
|
389
|
+
if base_bp_offset is None:
|
|
390
|
+
return None
|
|
391
|
+
bp_off = base_bp_offset - expr.operands[1].value
|
|
392
|
+
mask = 0xFFFF_FFFF if self.project.arch.bits == 32 else 0xFFFF_FFFF_FFFF_FFFF
|
|
393
|
+
return bp_off & mask
|
|
394
|
+
return None
|
|
395
|
+
|
|
329
396
|
@staticmethod
|
|
330
397
|
def _find_return_addr_storing_stmt(block):
|
|
331
398
|
for idx, stmt in enumerate(block.statements):
|
|
@@ -339,11 +406,13 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
339
406
|
return None
|
|
340
407
|
|
|
341
408
|
def _find_stmt_calling_security_check_cookie(self, node):
|
|
409
|
+
assert self._security_cookie_addr is not None
|
|
342
410
|
for idx, stmt in enumerate(node.statements):
|
|
343
411
|
if isinstance(stmt, ailment.Stmt.Call) and isinstance(stmt.target, ailment.Expr.Const):
|
|
344
412
|
const_target = stmt.target.value
|
|
345
|
-
if
|
|
346
|
-
func = self.kb.functions.
|
|
413
|
+
if self.kb.functions.contains_addr(const_target):
|
|
414
|
+
func = self.kb.functions.get_by_addr(const_target)
|
|
415
|
+
assert func is not None
|
|
347
416
|
if func.name == "_security_check_cookie" or is_function_security_check_cookie(
|
|
348
417
|
func, self.project, self._security_cookie_addr
|
|
349
418
|
):
|
|
@@ -48,6 +48,9 @@ from .inlined_wstrcpy import InlinedWstrcpy
|
|
|
48
48
|
from .cmpord_rewriter import CmpORDRewriter
|
|
49
49
|
from .coalesce_adjacent_shrs import CoalesceAdjacentShiftRights
|
|
50
50
|
from .a_mul_const_sub_a import AMulConstSubA
|
|
51
|
+
from .rewrite_cxx_operator_calls import RewriteCxxOperatorCalls
|
|
52
|
+
from .remove_cxx_destructor_calls import RemoveCxxDestructorCalls
|
|
53
|
+
from .rewrite_conv_mul import RewriteConvMul
|
|
51
54
|
from .base import PeepholeOptimizationExprBase, PeepholeOptimizationStmtBase, PeepholeOptimizationMultiStmtBase
|
|
52
55
|
|
|
53
56
|
|
|
@@ -100,6 +103,9 @@ ALL_PEEPHOLE_OPTS: list[type[PeepholeOptimizationExprBase]] = [
|
|
|
100
103
|
CmpORDRewriter,
|
|
101
104
|
CoalesceAdjacentShiftRights,
|
|
102
105
|
ShlToMul,
|
|
106
|
+
RewriteCxxOperatorCalls,
|
|
107
|
+
RemoveCxxDestructorCalls,
|
|
108
|
+
RewriteConvMul,
|
|
103
109
|
]
|
|
104
110
|
|
|
105
111
|
MULTI_STMT_OPTS: list[type[PeepholeOptimizationMultiStmtBase]] = [
|
|
@@ -4,6 +4,7 @@ from ailment.statement import Statement, Assignment
|
|
|
4
4
|
from ailment import Block
|
|
5
5
|
from angr.project import Project
|
|
6
6
|
from angr.knowledge_base import KnowledgeBase
|
|
7
|
+
from angr.knowledge_plugins.key_definitions import atoms
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class PeepholeOptimizationStmtBase:
|
|
@@ -14,20 +15,33 @@ class PeepholeOptimizationStmtBase:
|
|
|
14
15
|
__slots__ = (
|
|
15
16
|
"func_addr",
|
|
16
17
|
"kb",
|
|
18
|
+
"preserve_vvar_ids",
|
|
17
19
|
"project",
|
|
20
|
+
"type_hints",
|
|
18
21
|
)
|
|
19
22
|
project: Project | None
|
|
20
23
|
kb: KnowledgeBase | None
|
|
21
24
|
func_addr: int | None
|
|
25
|
+
preserve_vvar_ids: set[int]
|
|
26
|
+
type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]]
|
|
22
27
|
|
|
23
28
|
NAME = "Peephole Optimization - Statement"
|
|
24
29
|
DESCRIPTION = "Peephole Optimization - Statement"
|
|
25
30
|
stmt_classes = None
|
|
26
31
|
|
|
27
|
-
def __init__(
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
project: Project | None,
|
|
35
|
+
kb: KnowledgeBase | None,
|
|
36
|
+
func_addr: int | None = None,
|
|
37
|
+
preserve_vvar_ids: set[int] | None = None,
|
|
38
|
+
type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]] | None = None,
|
|
39
|
+
):
|
|
28
40
|
self.project = project
|
|
29
41
|
self.kb = kb
|
|
30
42
|
self.func_addr = func_addr
|
|
43
|
+
self.preserve_vvar_ids = set() if preserve_vvar_ids is None else preserve_vvar_ids
|
|
44
|
+
self.type_hints = [] if type_hints is None else type_hints
|
|
31
45
|
|
|
32
46
|
def optimize(self, stmt, stmt_idx: int | None = None, block=None, **kwargs):
|
|
33
47
|
raise NotImplementedError("_optimize() is not implemented.")
|
|
@@ -41,20 +55,33 @@ class PeepholeOptimizationMultiStmtBase:
|
|
|
41
55
|
__slots__ = (
|
|
42
56
|
"func_addr",
|
|
43
57
|
"kb",
|
|
58
|
+
"preserve_vvar_ids",
|
|
44
59
|
"project",
|
|
60
|
+
"type_hints",
|
|
45
61
|
)
|
|
46
62
|
project: Project | None
|
|
47
63
|
kb: KnowledgeBase | None
|
|
48
64
|
func_addr: int | None
|
|
65
|
+
preserve_vvar_ids: set[int]
|
|
66
|
+
type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]]
|
|
49
67
|
|
|
50
68
|
NAME = "Peephole Optimization - Multi-statement"
|
|
51
69
|
DESCRIPTION = "Peephole Optimization - Multi-statement"
|
|
52
70
|
stmt_classes = None
|
|
53
71
|
|
|
54
|
-
def __init__(
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
project: Project | None,
|
|
75
|
+
kb: KnowledgeBase | None,
|
|
76
|
+
func_addr: int | None = None,
|
|
77
|
+
preserve_vvar_ids: set[int] | None = None,
|
|
78
|
+
type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]] | None = None,
|
|
79
|
+
):
|
|
55
80
|
self.project = project
|
|
56
81
|
self.kb = kb
|
|
57
82
|
self.func_addr = func_addr
|
|
83
|
+
self.preserve_vvar_ids = set() if preserve_vvar_ids is None else preserve_vvar_ids
|
|
84
|
+
self.type_hints = [] if type_hints is None else type_hints
|
|
58
85
|
|
|
59
86
|
def optimize(self, stmts: list[Statement], stmt_idx: int | None = None, block=None, **kwargs):
|
|
60
87
|
raise NotImplementedError("_optimize() is not implemented.")
|
|
@@ -68,20 +95,33 @@ class PeepholeOptimizationExprBase:
|
|
|
68
95
|
__slots__ = (
|
|
69
96
|
"func_addr",
|
|
70
97
|
"kb",
|
|
98
|
+
"preserve_vvar_ids",
|
|
71
99
|
"project",
|
|
100
|
+
"type_hints",
|
|
72
101
|
)
|
|
73
102
|
project: Project | None
|
|
74
103
|
kb: KnowledgeBase | None
|
|
75
104
|
func_addr: int | None
|
|
105
|
+
preserve_vvar_ids: set[int]
|
|
106
|
+
type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]]
|
|
76
107
|
|
|
77
108
|
NAME = "Peephole Optimization - Expression"
|
|
78
109
|
DESCRIPTION = "Peephole Optimization - Expression"
|
|
79
110
|
expr_classes = None
|
|
80
111
|
|
|
81
|
-
def __init__(
|
|
112
|
+
def __init__(
|
|
113
|
+
self,
|
|
114
|
+
project: Project | None,
|
|
115
|
+
kb: KnowledgeBase | None,
|
|
116
|
+
func_addr: int | None = None,
|
|
117
|
+
preserve_vvar_ids: set[int] | None = None,
|
|
118
|
+
type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]] | None = None,
|
|
119
|
+
):
|
|
82
120
|
self.project = project
|
|
83
121
|
self.kb = kb
|
|
84
122
|
self.func_addr = func_addr
|
|
123
|
+
self.preserve_vvar_ids = set() if preserve_vvar_ids is None else preserve_vvar_ids
|
|
124
|
+
self.type_hints = [] if type_hints is None else type_hints
|
|
85
125
|
|
|
86
126
|
def optimize(self, expr, **kwargs):
|
|
87
127
|
raise NotImplementedError("_optimize() is not implemented.")
|
|
@@ -16,7 +16,7 @@ class ConstantDereferences(PeepholeOptimizationExprBase):
|
|
|
16
16
|
expr_classes = (Load,)
|
|
17
17
|
|
|
18
18
|
def optimize(self, expr: Load, **kwargs):
|
|
19
|
-
if isinstance(expr.addr, Const):
|
|
19
|
+
if isinstance(expr.addr, Const) and expr.size in {1, 2, 4, 8, 10, 16, 32, 64, 128, 256}:
|
|
20
20
|
# is it loading from a read-only section?
|
|
21
21
|
sec = self.project.loader.find_section_containing(expr.addr.value)
|
|
22
22
|
if sec is not None and sec.is_readable and (not sec.is_writable or "got" in sec.name):
|
|
@@ -7,6 +7,7 @@ from archinfo import Endness
|
|
|
7
7
|
from ailment.expression import Const, StackBaseOffset, VirtualVariable
|
|
8
8
|
from ailment.statement import Call, Assignment
|
|
9
9
|
|
|
10
|
+
from angr import SIM_LIBRARIES
|
|
10
11
|
from angr.utils.endness import ail_const_to_be
|
|
11
12
|
from .base import PeepholeOptimizationStmtBase
|
|
12
13
|
|
|
@@ -44,6 +45,7 @@ class InlinedStrcpy(PeepholeOptimizationStmtBase):
|
|
|
44
45
|
Const(None, None, str_id, self.project.arch.bits, custom_string=True),
|
|
45
46
|
Const(None, None, len(s), self.project.arch.bits),
|
|
46
47
|
],
|
|
48
|
+
prototype=SIM_LIBRARIES["libc.so"][0].get_prototype("strncpy"),
|
|
47
49
|
**stmt.tags,
|
|
48
50
|
)
|
|
49
51
|
|
|
@@ -85,6 +87,7 @@ class InlinedStrcpy(PeepholeOptimizationStmtBase):
|
|
|
85
87
|
Const(None, None, str_id, self.project.arch.bits, custom_string=True),
|
|
86
88
|
Const(None, None, len(s), self.project.arch.bits),
|
|
87
89
|
],
|
|
90
|
+
prototype=SIM_LIBRARIES["libc.so"][0].get_prototype("strncpy"),
|
|
88
91
|
**stmt.tags,
|
|
89
92
|
)
|
|
90
93
|
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
from ailment.expression import Expression, BinaryOp, Const, Register, StackBaseOffset
|
|
5
5
|
from ailment.statement import Call, Store
|
|
6
6
|
|
|
7
|
+
from angr import SIM_LIBRARIES
|
|
7
8
|
from .base import PeepholeOptimizationMultiStmtBase
|
|
8
9
|
from .inlined_strcpy import InlinedStrcpy
|
|
9
10
|
|
|
@@ -58,6 +59,7 @@ class InlinedStrcpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
|
58
59
|
last_stmt.args[0],
|
|
59
60
|
Const(None, None, new_str_idx, last_stmt.args[0].bits, custom_string=True),
|
|
60
61
|
]
|
|
62
|
+
prototype = SIM_LIBRARIES["libc.so"][0].get_prototype("strcpy")
|
|
61
63
|
else:
|
|
62
64
|
call_name = "strncpy"
|
|
63
65
|
new_str_idx = self.kb.custom_strings.allocate(new_str)
|
|
@@ -66,8 +68,9 @@ class InlinedStrcpyConsolidation(PeepholeOptimizationMultiStmtBase):
|
|
|
66
68
|
Const(None, None, new_str_idx, last_stmt.args[0].bits, custom_string=True),
|
|
67
69
|
Const(None, None, len(new_str), self.project.arch.bits),
|
|
68
70
|
]
|
|
71
|
+
prototype = SIM_LIBRARIES["libc.so"][0].get_prototype("strncpy")
|
|
69
72
|
|
|
70
|
-
return [Call(stmt.idx, call_name, args=args, **stmt.tags)]
|
|
73
|
+
return [Call(stmt.idx, call_name, args=args, prototype=prototype, **stmt.tags)]
|
|
71
74
|
|
|
72
75
|
return None
|
|
73
76
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# pylint:disable=arguments-differ
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from ailment.expression import Const
|
|
5
|
+
from ailment.statement import Call
|
|
6
|
+
|
|
7
|
+
from .base import PeepholeOptimizationStmtBase
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RemoveCxxDestructorCalls(PeepholeOptimizationStmtBase):
|
|
11
|
+
"""
|
|
12
|
+
Rewrite C++ operator function calls into operations.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
__slots__ = ()
|
|
16
|
+
|
|
17
|
+
NAME = "Remove C++ destructor function calls"
|
|
18
|
+
stmt_classes = (Call,)
|
|
19
|
+
|
|
20
|
+
def optimize(self, stmt: Call, **kwargs) -> tuple | None: # type:ignore
|
|
21
|
+
# are we calling a function that we deem as a C++ destructor?
|
|
22
|
+
assert self.project is not None
|
|
23
|
+
|
|
24
|
+
if isinstance(stmt.target, Const):
|
|
25
|
+
func_addr = stmt.target.value
|
|
26
|
+
if not self.project.kb.functions.contains_addr(func_addr):
|
|
27
|
+
return None
|
|
28
|
+
func = self.project.kb.functions[func_addr]
|
|
29
|
+
if "::~" in func.demangled_name and stmt.args is not None:
|
|
30
|
+
# yes it is!
|
|
31
|
+
return ()
|
|
32
|
+
return None
|
|
@@ -20,10 +20,19 @@ class RemoveRedundantBitmasks(PeepholeOptimizationExprBase):
|
|
|
20
20
|
__slots__ = ()
|
|
21
21
|
|
|
22
22
|
NAME = "Remove redundant bitmasks"
|
|
23
|
-
expr_classes = (BinaryOp,)
|
|
23
|
+
expr_classes = (BinaryOp, Convert)
|
|
24
24
|
|
|
25
|
-
def optimize(self, expr: BinaryOp, **kwargs):
|
|
25
|
+
def optimize(self, expr: BinaryOp | Convert, **kwargs):
|
|
26
|
+
|
|
27
|
+
if isinstance(expr, BinaryOp):
|
|
28
|
+
return self._optimize_BinaryOp(expr)
|
|
29
|
+
if isinstance(expr, Convert):
|
|
30
|
+
return RemoveRedundantBitmasks._optimize_Convert(expr)
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
def _optimize_BinaryOp(self, expr: BinaryOp):
|
|
26
34
|
# And(expr, full_N_bitmask) ==> expr
|
|
35
|
+
# And(SHR(expr, N), bitmask)) ==> SHR(expr, N)
|
|
27
36
|
# And(Conv(1->N, expr), bitmask) ==> Conv(1->N, expr)
|
|
28
37
|
# And(Conv(1->N, bool_expr), bitmask) ==> Conv(1->N, bool_expr)
|
|
29
38
|
# And(ITE(?, const_expr, const_expr), bitmask) ==> ITE(?, const_expr, const_expr)
|
|
@@ -31,6 +40,17 @@ class RemoveRedundantBitmasks(PeepholeOptimizationExprBase):
|
|
|
31
40
|
inner_expr = expr.operands[0]
|
|
32
41
|
if expr.operands[1].value == _MASKS.get(inner_expr.bits, None):
|
|
33
42
|
return inner_expr
|
|
43
|
+
|
|
44
|
+
if isinstance(inner_expr, BinaryOp) and inner_expr.op == "Shr":
|
|
45
|
+
mask = expr.operands[1]
|
|
46
|
+
shift_val = inner_expr.operands[1]
|
|
47
|
+
if (
|
|
48
|
+
isinstance(shift_val, Const)
|
|
49
|
+
and shift_val.value in _MASKS
|
|
50
|
+
and mask.value == _MASKS.get(int(64 - shift_val.value), None)
|
|
51
|
+
):
|
|
52
|
+
return inner_expr
|
|
53
|
+
|
|
34
54
|
if isinstance(inner_expr, Convert) and self.is_bool_expr(inner_expr.operand):
|
|
35
55
|
# useless masking
|
|
36
56
|
return inner_expr
|
|
@@ -47,3 +67,50 @@ class RemoveRedundantBitmasks(PeepholeOptimizationExprBase):
|
|
|
47
67
|
return ite
|
|
48
68
|
|
|
49
69
|
return None
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def _optimize_Convert(expr: Convert):
|
|
73
|
+
# Conv(64->32, (expr & bitmask) + expr)
|
|
74
|
+
# => Conv(64->32, (expr + expr))
|
|
75
|
+
if (
|
|
76
|
+
expr.op == "Convert"
|
|
77
|
+
and expr.from_bits > expr.to_bits
|
|
78
|
+
and isinstance(expr.operand, BinaryOp)
|
|
79
|
+
and expr.operand.op == "Add"
|
|
80
|
+
):
|
|
81
|
+
operand_expr = expr.operand
|
|
82
|
+
op0, op1 = operand_expr.operands
|
|
83
|
+
if (
|
|
84
|
+
isinstance(op0, BinaryOp)
|
|
85
|
+
and op0.op == "And"
|
|
86
|
+
and isinstance(op0.operands[1], Const)
|
|
87
|
+
and op0.operands[1].value == _MASKS.get(expr.to_bits, None)
|
|
88
|
+
):
|
|
89
|
+
new_op0 = op0.operands[0]
|
|
90
|
+
replaced, new_operand_expr = operand_expr.replace(op0, new_op0)
|
|
91
|
+
if replaced:
|
|
92
|
+
expr.operand = new_operand_expr
|
|
93
|
+
return expr
|
|
94
|
+
# Conv(64->32, (expr) - (expr) & 0xffffffff<64>)))
|
|
95
|
+
# => Conv(64->32, (expr - expr))
|
|
96
|
+
elif (
|
|
97
|
+
expr.op == "Convert"
|
|
98
|
+
and expr.from_bits > expr.to_bits
|
|
99
|
+
and isinstance(expr.operand, BinaryOp)
|
|
100
|
+
and expr.operand.op == "Sub"
|
|
101
|
+
):
|
|
102
|
+
operand_expr = expr.operand
|
|
103
|
+
op0, op1 = operand_expr.operands
|
|
104
|
+
if (
|
|
105
|
+
isinstance(op1, BinaryOp)
|
|
106
|
+
and op1.op == "And"
|
|
107
|
+
and isinstance(op1.operands[1], Const)
|
|
108
|
+
and op1.operands[1].value == _MASKS.get(expr.to_bits, None)
|
|
109
|
+
):
|
|
110
|
+
new_op1 = op1.operands[0]
|
|
111
|
+
replaced, new_operand_expr = operand_expr.replace(op1, new_op1)
|
|
112
|
+
if replaced:
|
|
113
|
+
expr.operand = new_operand_expr
|
|
114
|
+
return expr
|
|
115
|
+
|
|
116
|
+
return None
|
|
@@ -164,6 +164,20 @@ class RemoveRedundantConversions(PeepholeOptimizationExprBase):
|
|
|
164
164
|
**expr.tags,
|
|
165
165
|
)
|
|
166
166
|
|
|
167
|
+
# simpler cases
|
|
168
|
+
# (A & mask) & mask ==> A & mask
|
|
169
|
+
if (
|
|
170
|
+
expr.op == "And"
|
|
171
|
+
and isinstance(expr.operands[1], Const)
|
|
172
|
+
and isinstance(expr.operands[0], BinaryOp)
|
|
173
|
+
and expr.operands[0].op == "And"
|
|
174
|
+
):
|
|
175
|
+
inner_op0, inner_op1 = expr.operands[0].operands
|
|
176
|
+
if (isinstance(inner_op0, Const) and inner_op0.value == expr.operands[1].value) or (
|
|
177
|
+
isinstance(inner_op1, Const) and inner_op1.value == expr.operands[1].value
|
|
178
|
+
):
|
|
179
|
+
return expr.operands[0]
|
|
180
|
+
|
|
167
181
|
return None
|
|
168
182
|
|
|
169
183
|
@staticmethod
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from ailment.expression import BinaryOp, Const, Convert
|
|
3
|
+
|
|
4
|
+
from .base import PeepholeOptimizationExprBase
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RewriteConvMul(PeepholeOptimizationExprBase):
|
|
8
|
+
"""
|
|
9
|
+
Rewrites multiplication to be inside conversion.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
__slots__ = ()
|
|
13
|
+
|
|
14
|
+
NAME = "Rewrite Conv Mul"
|
|
15
|
+
expr_classes = (BinaryOp,)
|
|
16
|
+
|
|
17
|
+
# Conv(64->32, (Conv(32->64, expr) * N<64>)) * N<32>)
|
|
18
|
+
# => Conv(64->32, (Conv(32->64, expr) * N<64>) * Conv(32->64,N<32>))
|
|
19
|
+
def optimize(self, expr: BinaryOp, **kwargs):
|
|
20
|
+
if (
|
|
21
|
+
expr.op == "Mul"
|
|
22
|
+
and isinstance(expr.operands[1], Const)
|
|
23
|
+
and expr.operands[1].bits == 32
|
|
24
|
+
and isinstance(expr.operands[0], Convert)
|
|
25
|
+
and expr.operands[0].from_bits > expr.operands[0].to_bits
|
|
26
|
+
):
|
|
27
|
+
op0, op1 = expr.operands
|
|
28
|
+
operand_expr = op0.operand
|
|
29
|
+
if (
|
|
30
|
+
isinstance(operand_expr, BinaryOp)
|
|
31
|
+
and operand_expr.op == "Mul"
|
|
32
|
+
and isinstance(operand_expr.operands[1], Const)
|
|
33
|
+
and operand_expr.operands[1].bits == 64
|
|
34
|
+
):
|
|
35
|
+
new_op1 = Convert(op1.idx, op1.bits, op0.from_bits, False, op1, **op1.tags)
|
|
36
|
+
new_op0 = op0.operand
|
|
37
|
+
new_expr = BinaryOp(expr.idx, "Mul", [new_op0, new_op1], expr.signed, **expr.tags)
|
|
38
|
+
return Convert(new_expr.idx, op0.from_bits, op0.to_bits, False, new_expr, **expr.tags)
|
|
39
|
+
|
|
40
|
+
return None
|