angr 9.2.176__cp310-abi3-manylinux_2_28_aarch64.whl → 9.2.178__cp310-abi3-manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/analyses/cfg/cfg_fast.py +15 -0
- angr/analyses/decompiler/ail_simplifier.py +69 -1
- angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +45 -7
- angr/analyses/decompiler/clinic.py +15 -7
- angr/analyses/decompiler/dirty_rewriters/__init__.py +7 -0
- angr/analyses/decompiler/dirty_rewriters/amd64_dirty.py +69 -0
- angr/analyses/decompiler/dirty_rewriters/rewriter_base.py +27 -0
- angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +10 -8
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +44 -6
- angr/analyses/decompiler/optimization_passes/register_save_area_simplifier_adv.py +198 -0
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +111 -55
- angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +69 -12
- angr/analyses/decompiler/peephole_optimizations/inlined_wcscpy_consolidation.py +189 -6
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts_around_comparators.py +72 -1
- angr/analyses/decompiler/presets/basic.py +2 -0
- angr/analyses/decompiler/presets/fast.py +2 -0
- angr/analyses/decompiler/presets/full.py +2 -0
- angr/analyses/decompiler/utils.py +10 -3
- angr/analyses/flirt/flirt.py +5 -4
- angr/analyses/s_propagator.py +23 -21
- angr/analyses/smc.py +2 -3
- angr/analyses/variable_recovery/engine_ail.py +39 -0
- angr/emulator.py +2 -1
- angr/engines/hook.py +1 -1
- angr/engines/icicle.py +19 -3
- angr/knowledge_plugins/functions/function.py +2 -2
- angr/knowledge_plugins/labels.py +4 -4
- angr/procedures/definitions/__init__.py +9 -0
- angr/procedures/definitions/parse_win32json.py +11 -0
- angr/procedures/definitions/wdk/ntoskrnl.json +4 -0
- angr/rustylib.abi3.so +0 -0
- angr/utils/funcid.py +85 -0
- angr/utils/ssa/__init__.py +2 -6
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/METADATA +6 -5
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/RECORD +41 -37
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/WHEEL +0 -0
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/entry_points.txt +0 -0
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.176.dist-info → angr-9.2.178.dist-info}/top_level.txt +0 -0
angr/__init__.py
CHANGED
angr/analyses/cfg/cfg_fast.py
CHANGED
|
@@ -40,6 +40,7 @@ from angr.errors import (
|
|
|
40
40
|
from angr.utils.constants import DEFAULT_STATEMENT
|
|
41
41
|
from angr.utils.funcid import (
|
|
42
42
|
is_function_security_check_cookie,
|
|
43
|
+
is_function_security_check_cookie_strict,
|
|
43
44
|
is_function_security_init_cookie,
|
|
44
45
|
is_function_security_init_cookie_win8,
|
|
45
46
|
is_function_likely_security_init_cookie,
|
|
@@ -2029,6 +2030,20 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
2029
2030
|
# both are found. exit from the loop
|
|
2030
2031
|
break
|
|
2031
2032
|
|
|
2033
|
+
else:
|
|
2034
|
+
# security_cookie_addr is None; let's invoke the stricter version to find _security_check_cookie
|
|
2035
|
+
for func in self.kb.functions.values():
|
|
2036
|
+
if len(func.block_addrs_set) in {5, 6}:
|
|
2037
|
+
r, cookie_addr = is_function_security_check_cookie_strict(func, self.project)
|
|
2038
|
+
if r:
|
|
2039
|
+
security_cookie_addr = cookie_addr
|
|
2040
|
+
if security_cookie_addr not in self.kb.labels:
|
|
2041
|
+
self.kb.labels[security_cookie_addr] = "_security_cookie"
|
|
2042
|
+
security_check_cookie_found = True
|
|
2043
|
+
func.is_default_name = False
|
|
2044
|
+
func.name = "_security_check_cookie"
|
|
2045
|
+
break
|
|
2046
|
+
|
|
2032
2047
|
# special handling: some binaries do not have SecurityCookie set, but still contain _security_init_cookie
|
|
2033
2048
|
if security_init_cookie_found is False and self.functions.contains_addr(self.project.entry):
|
|
2034
2049
|
start_func = self.functions.get_by_addr(self.project.entry)
|
|
@@ -57,6 +57,7 @@ from .ailgraph_walker import AILGraphWalker
|
|
|
57
57
|
from .expression_narrower import ExprNarrowingInfo, NarrowingInfoExtractor, ExpressionNarrower
|
|
58
58
|
from .block_simplifier import BlockSimplifier
|
|
59
59
|
from .ccall_rewriters import CCALL_REWRITERS
|
|
60
|
+
from .dirty_rewriters import DIRTY_REWRITERS
|
|
60
61
|
from .counters.expression_counters import SingleExpressionCounter
|
|
61
62
|
|
|
62
63
|
if TYPE_CHECKING:
|
|
@@ -169,6 +170,7 @@ class AILSimplifier(Analysis):
|
|
|
169
170
|
use_callee_saved_regs_at_return=True,
|
|
170
171
|
rewrite_ccalls=True,
|
|
171
172
|
rename_ccalls=True,
|
|
173
|
+
rewrite_dirty=True,
|
|
172
174
|
removed_vvar_ids: set[int] | None = None,
|
|
173
175
|
arg_vvars: dict[int, tuple[VirtualVariable, SimVariable]] | None = None,
|
|
174
176
|
avoid_vvar_ids: set[int] | None = None,
|
|
@@ -190,6 +192,7 @@ class AILSimplifier(Analysis):
|
|
|
190
192
|
self._use_callee_saved_regs_at_return = use_callee_saved_regs_at_return
|
|
191
193
|
self._should_rewrite_ccalls = rewrite_ccalls
|
|
192
194
|
self._should_rename_ccalls = rename_ccalls
|
|
195
|
+
self._should_rewrite_dirty = rewrite_dirty
|
|
193
196
|
self._removed_vvar_ids = removed_vvar_ids if removed_vvar_ids is not None else set()
|
|
194
197
|
self._arg_vvars = arg_vvars
|
|
195
198
|
self._avoid_vvar_ids = avoid_vvar_ids if avoid_vvar_ids is not None else set()
|
|
@@ -258,6 +261,15 @@ class AILSimplifier(Analysis):
|
|
|
258
261
|
self._rebuild_func_graph()
|
|
259
262
|
self._clear_cache()
|
|
260
263
|
|
|
264
|
+
if self._should_rewrite_dirty:
|
|
265
|
+
_l.debug("Rewriting dirty expressions/statements")
|
|
266
|
+
dirty_rewritten = self._rewrite_dirty_calls()
|
|
267
|
+
self.simplified |= dirty_rewritten
|
|
268
|
+
if dirty_rewritten:
|
|
269
|
+
_l.debug("... dirty expressions/statements rewritten")
|
|
270
|
+
self._rebuild_func_graph()
|
|
271
|
+
self._clear_cache()
|
|
272
|
+
|
|
261
273
|
if self._unify_vars:
|
|
262
274
|
_l.debug("Removing dead assignments")
|
|
263
275
|
r = self._iteratively_remove_dead_assignments()
|
|
@@ -841,6 +853,7 @@ class AILSimplifier(Analysis):
|
|
|
841
853
|
if (
|
|
842
854
|
isinstance(stmt, Assignment)
|
|
843
855
|
and isinstance(stmt.dst, VirtualVariable)
|
|
856
|
+
and stmt.dst.was_reg # values of stack variables might be updated in callees or via pointers
|
|
844
857
|
and isinstance(stmt.src, Const)
|
|
845
858
|
and isinstance(stmt.src.value, int)
|
|
846
859
|
):
|
|
@@ -1426,7 +1439,7 @@ class AILSimplifier(Analysis):
|
|
|
1426
1439
|
|
|
1427
1440
|
for eq in equivalence:
|
|
1428
1441
|
# register variable == Call
|
|
1429
|
-
if isinstance(eq.atom0, VirtualVariable) and eq.atom0.was_reg:
|
|
1442
|
+
if isinstance(eq.atom0, VirtualVariable) and (eq.atom0.was_reg or eq.atom0.was_tmp):
|
|
1430
1443
|
if isinstance(eq.atom1, Call):
|
|
1431
1444
|
# register variable = Call
|
|
1432
1445
|
call: Expression = eq.atom1
|
|
@@ -2020,6 +2033,61 @@ class AILSimplifier(Analysis):
|
|
|
2020
2033
|
|
|
2021
2034
|
return updated
|
|
2022
2035
|
|
|
2036
|
+
#
|
|
2037
|
+
# Rewriting dirty calls
|
|
2038
|
+
#
|
|
2039
|
+
|
|
2040
|
+
def _rewrite_dirty_calls(self):
|
|
2041
|
+
rewriter_cls = DIRTY_REWRITERS.get(self.project.arch.name, None)
|
|
2042
|
+
if rewriter_cls is None:
|
|
2043
|
+
return False
|
|
2044
|
+
|
|
2045
|
+
walker = AILBlockWalker()
|
|
2046
|
+
|
|
2047
|
+
class _any_update:
|
|
2048
|
+
"""
|
|
2049
|
+
Dummy class for storing if any result has been updated.
|
|
2050
|
+
"""
|
|
2051
|
+
|
|
2052
|
+
v = False
|
|
2053
|
+
|
|
2054
|
+
def _handle_DirtyStatement( # pylint:disable=unused-argument
|
|
2055
|
+
stmt_idx: int, stmt: DirtyStatement, block: Block | None
|
|
2056
|
+
) -> Expression | None:
|
|
2057
|
+
# we do not want to trigger _handle_DirtyExpression, which is why we do not call the superclass method
|
|
2058
|
+
rewriter = rewriter_cls(stmt, self.project.arch)
|
|
2059
|
+
if rewriter.result is not None:
|
|
2060
|
+
_any_update.v = True
|
|
2061
|
+
return rewriter.result # type:ignore
|
|
2062
|
+
return None
|
|
2063
|
+
|
|
2064
|
+
def _handle_DirtyExpression(
|
|
2065
|
+
expr_idx: int, expr: DirtyExpression, stmt_idx: int, stmt: Statement, block: Block | None
|
|
2066
|
+
):
|
|
2067
|
+
r_expr = AILBlockWalker._handle_DirtyExpression(walker, expr_idx, expr, stmt_idx, stmt, block)
|
|
2068
|
+
if r_expr is None:
|
|
2069
|
+
r_expr = expr
|
|
2070
|
+
rewriter = rewriter_cls(r_expr, self.project.arch)
|
|
2071
|
+
if rewriter.result is not None:
|
|
2072
|
+
_any_update.v = True
|
|
2073
|
+
return rewriter.result
|
|
2074
|
+
return r_expr if r_expr is not expr else None
|
|
2075
|
+
|
|
2076
|
+
blocks_by_addr_and_idx = {(node.addr, node.idx): node for node in self.func_graph.nodes()}
|
|
2077
|
+
walker.expr_handlers[DirtyExpression] = _handle_DirtyExpression
|
|
2078
|
+
walker.stmt_handlers[DirtyStatement] = _handle_DirtyStatement
|
|
2079
|
+
|
|
2080
|
+
updated = False
|
|
2081
|
+
for block in blocks_by_addr_and_idx.values():
|
|
2082
|
+
_any_update.v = False
|
|
2083
|
+
old_block = block.copy()
|
|
2084
|
+
walker.walk(block)
|
|
2085
|
+
if _any_update.v:
|
|
2086
|
+
self.blocks[old_block] = block
|
|
2087
|
+
updated = True
|
|
2088
|
+
|
|
2089
|
+
return updated
|
|
2090
|
+
|
|
2023
2091
|
#
|
|
2024
2092
|
# Util functions
|
|
2025
2093
|
#
|
|
@@ -26,8 +26,8 @@ class AMD64CCallRewriter(CCallRewriterBase):
|
|
|
26
26
|
dep_1 = ccall.operands[2]
|
|
27
27
|
dep_2 = ccall.operands[3]
|
|
28
28
|
if isinstance(cond, Expr.Const) and isinstance(op, Expr.Const):
|
|
29
|
-
cond_v = cond.
|
|
30
|
-
op_v = op.
|
|
29
|
+
cond_v = cond.value_int
|
|
30
|
+
op_v = op.value_int
|
|
31
31
|
if cond_v == AMD64_CondTypes["CondLE"]:
|
|
32
32
|
if op_v in {
|
|
33
33
|
AMD64_OpTypes["G_CC_OP_SUBB"],
|
|
@@ -233,7 +233,9 @@ class AMD64CCallRewriter(CCallRewriterBase):
|
|
|
233
233
|
if op_v == AMD64_OpTypes["G_CC_OP_COPY"]:
|
|
234
234
|
# dep_1 & G_CC_MASK_Z == 0 or dep_1 & G_CC_MASK_Z != 0
|
|
235
235
|
|
|
236
|
-
|
|
236
|
+
bitmask = AMD64_CondBitMasks["G_CC_MASK_Z"]
|
|
237
|
+
assert isinstance(bitmask, int)
|
|
238
|
+
flag = Expr.Const(None, None, bitmask, dep_1.bits)
|
|
237
239
|
masked_dep = Expr.BinaryOp(None, "And", [dep_1, flag], False, **ccall.tags)
|
|
238
240
|
zero = Expr.Const(None, None, 0, dep_1.bits)
|
|
239
241
|
expr_op = "CmpEQ" if cond_v == AMD64_CondTypes["CondZ"] else "CmpNE"
|
|
@@ -372,6 +374,39 @@ class AMD64CCallRewriter(CCallRewriterBase):
|
|
|
372
374
|
bits=ccall.bits,
|
|
373
375
|
**ccall.tags,
|
|
374
376
|
)
|
|
377
|
+
if op_v in {
|
|
378
|
+
AMD64_OpTypes["G_CC_OP_SUBB"],
|
|
379
|
+
AMD64_OpTypes["G_CC_OP_SUBW"],
|
|
380
|
+
AMD64_OpTypes["G_CC_OP_SUBL"],
|
|
381
|
+
AMD64_OpTypes["G_CC_OP_SUBQ"],
|
|
382
|
+
}:
|
|
383
|
+
# dep_1 <u dep_2
|
|
384
|
+
|
|
385
|
+
dep_1 = self._fix_size(
|
|
386
|
+
dep_1,
|
|
387
|
+
op_v,
|
|
388
|
+
AMD64_OpTypes["G_CC_OP_SUBB"],
|
|
389
|
+
AMD64_OpTypes["G_CC_OP_SUBW"],
|
|
390
|
+
AMD64_OpTypes["G_CC_OP_SUBL"],
|
|
391
|
+
ccall.tags,
|
|
392
|
+
)
|
|
393
|
+
dep_2 = self._fix_size(
|
|
394
|
+
dep_2,
|
|
395
|
+
op_v,
|
|
396
|
+
AMD64_OpTypes["G_CC_OP_SUBB"],
|
|
397
|
+
AMD64_OpTypes["G_CC_OP_SUBW"],
|
|
398
|
+
AMD64_OpTypes["G_CC_OP_SUBL"],
|
|
399
|
+
ccall.tags,
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
r = Expr.BinaryOp(
|
|
403
|
+
ccall.idx,
|
|
404
|
+
"CmpLT",
|
|
405
|
+
(dep_1, dep_2),
|
|
406
|
+
False,
|
|
407
|
+
**ccall.tags,
|
|
408
|
+
)
|
|
409
|
+
return Expr.Convert(None, r.bits, ccall.bits, False, r, **ccall.tags)
|
|
375
410
|
elif (
|
|
376
411
|
cond_v == AMD64_CondTypes["CondS"]
|
|
377
412
|
and op_v
|
|
@@ -458,7 +493,7 @@ class AMD64CCallRewriter(CCallRewriterBase):
|
|
|
458
493
|
dep_2 = ccall.operands[2]
|
|
459
494
|
ndep = ccall.operands[3]
|
|
460
495
|
if isinstance(op, Expr.Const):
|
|
461
|
-
op_v = op.
|
|
496
|
+
op_v = op.value_int
|
|
462
497
|
if op_v in {
|
|
463
498
|
AMD64_OpTypes["G_CC_OP_ADDB"],
|
|
464
499
|
AMD64_OpTypes["G_CC_OP_ADDW"],
|
|
@@ -545,6 +580,9 @@ class AMD64CCallRewriter(CCallRewriterBase):
|
|
|
545
580
|
AMD64_OpTypes["G_CC_OP_DECQ"],
|
|
546
581
|
}:
|
|
547
582
|
# pc_actions_DEC
|
|
583
|
+
bitmask = AMD64_CondBitMasks["G_CC_MASK_C"]
|
|
584
|
+
bitmask_1 = AMD64_CondBitOffsets["G_CC_SHIFT_C"]
|
|
585
|
+
assert isinstance(bitmask, int) and isinstance(bitmask_1, int)
|
|
548
586
|
return Expr.BinaryOp(
|
|
549
587
|
None,
|
|
550
588
|
"Shr",
|
|
@@ -552,10 +590,10 @@ class AMD64CCallRewriter(CCallRewriterBase):
|
|
|
552
590
|
Expr.BinaryOp(
|
|
553
591
|
None,
|
|
554
592
|
"And",
|
|
555
|
-
[ndep, Expr.Const(None, None,
|
|
593
|
+
[ndep, Expr.Const(None, None, bitmask, 64)],
|
|
556
594
|
False,
|
|
557
595
|
),
|
|
558
|
-
Expr.Const(None, None,
|
|
596
|
+
Expr.Const(None, None, bitmask_1, 64),
|
|
559
597
|
],
|
|
560
598
|
False,
|
|
561
599
|
**ccall.tags,
|
|
@@ -575,6 +613,6 @@ class AMD64CCallRewriter(CCallRewriterBase):
|
|
|
575
613
|
bits = 64
|
|
576
614
|
if bits < 64:
|
|
577
615
|
if isinstance(expr, Expr.Const):
|
|
578
|
-
return Expr.Const(expr.idx, None, expr.
|
|
616
|
+
return Expr.Const(expr.idx, None, expr.value_int & ((1 << bits) - 1), bits, **tags)
|
|
579
617
|
return Expr.Convert(None, 64, bits, False, expr, **tags)
|
|
580
618
|
return expr
|
|
@@ -46,7 +46,6 @@ from .return_maker import ReturnMaker
|
|
|
46
46
|
from .ailgraph_walker import AILGraphWalker, RemoveNodeNotice
|
|
47
47
|
from .optimization_passes import (
|
|
48
48
|
OptimizationPassStage,
|
|
49
|
-
RegisterSaveAreaSimplifier,
|
|
50
49
|
StackCanarySimplifier,
|
|
51
50
|
TagSlicer,
|
|
52
51
|
DUPLICATING_OPTS,
|
|
@@ -598,6 +597,17 @@ class Clinic(Analysis):
|
|
|
598
597
|
assert self.func_args is not None
|
|
599
598
|
self._ail_graph = self._transform_to_ssa_level1(self._ail_graph, self.func_args)
|
|
600
599
|
|
|
600
|
+
# Run simplification passes
|
|
601
|
+
self._update_progress(49.0, text="Running simplifications 1.5")
|
|
602
|
+
self._ail_graph = self._run_simplification_passes(
|
|
603
|
+
self._ail_graph, stage=OptimizationPassStage.AFTER_SSA_LEVEL1_TRANSFORMATION
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
# register save area has been removed at this point - we should no longer use callee-saved registers in RDA
|
|
607
|
+
self._register_save_areas_removed = True
|
|
608
|
+
# clear the cached RDA result
|
|
609
|
+
self.reaching_definitions = None
|
|
610
|
+
|
|
601
611
|
def _stage_pre_ssa_level1_simplifications(self) -> None:
|
|
602
612
|
# Simplify blocks
|
|
603
613
|
# we never remove dead memory definitions before making callsites. otherwise stack arguments may go missing
|
|
@@ -698,7 +708,10 @@ class Clinic(Analysis):
|
|
|
698
708
|
# Run simplification passes
|
|
699
709
|
self._update_progress(65.0, text="Running simplifications 3")
|
|
700
710
|
self._ail_graph = self._run_simplification_passes(
|
|
701
|
-
self._ail_graph,
|
|
711
|
+
self._ail_graph,
|
|
712
|
+
stack_items=self.stack_items,
|
|
713
|
+
stage=OptimizationPassStage.AFTER_GLOBAL_SIMPLIFICATION,
|
|
714
|
+
arg_vvars=self.arg_vvars,
|
|
702
715
|
)
|
|
703
716
|
|
|
704
717
|
# Simplify the entire function for the third time
|
|
@@ -1579,11 +1592,6 @@ class Clinic(Analysis):
|
|
|
1579
1592
|
if a.out_graph:
|
|
1580
1593
|
# use the new graph
|
|
1581
1594
|
ail_graph = a.out_graph
|
|
1582
|
-
if isinstance(a, RegisterSaveAreaSimplifier):
|
|
1583
|
-
# register save area has been removed - we should no longer use callee-saved registers in RDA
|
|
1584
|
-
self._register_save_areas_removed = True
|
|
1585
|
-
# clear the cached RDA result
|
|
1586
|
-
self.reaching_definitions = None
|
|
1587
1595
|
self.vvar_id_start = a.vvar_id_start
|
|
1588
1596
|
if stack_items is not None and a.stack_items:
|
|
1589
1597
|
stack_items.update(a.stack_items)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from angr.ailment import Const
|
|
4
|
+
from angr.ailment.statement import DirtyStatement, Statement, Call
|
|
5
|
+
from angr.ailment.expression import DirtyExpression, Expression
|
|
6
|
+
from .rewriter_base import DirtyRewriterBase
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AMD64DirtyRewriter(DirtyRewriterBase):
|
|
10
|
+
"""
|
|
11
|
+
Rewrites AMD64 DirtyStatement and DirtyExpression.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__slots__ = ()
|
|
15
|
+
|
|
16
|
+
def _rewrite_stmt(self, dirty: DirtyStatement) -> Statement | None:
|
|
17
|
+
# TODO: Rewrite more dirty statements
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
def _rewrite_expr(self, dirty: DirtyExpression) -> Expression | None:
|
|
21
|
+
match dirty.callee:
|
|
22
|
+
case "amd64g_dirtyhelper_IN":
|
|
23
|
+
# in
|
|
24
|
+
bits = (
|
|
25
|
+
dirty.operands[1].value * self.arch.byte_width
|
|
26
|
+
if len(dirty.operands) > 1 and isinstance(dirty.operands[1], Const)
|
|
27
|
+
else None
|
|
28
|
+
)
|
|
29
|
+
func_name = "__in"
|
|
30
|
+
suffix = self._inout_intrinsic_suffix(bits) if bits is not None else None
|
|
31
|
+
if suffix is not None:
|
|
32
|
+
func_name += f"{suffix}"
|
|
33
|
+
else:
|
|
34
|
+
func_name += f"_{bits}"
|
|
35
|
+
return Call(
|
|
36
|
+
dirty.idx, func_name, None, None, args=(dirty.operands[0],), ret_expr=None, bits=bits, **dirty.tags
|
|
37
|
+
)
|
|
38
|
+
case "amd64g_dirtyhelper_OUT":
|
|
39
|
+
# out
|
|
40
|
+
bits = (
|
|
41
|
+
dirty.operands[1].value * self.arch.byte_width
|
|
42
|
+
if len(dirty.operands) > 1 and isinstance(dirty.operands[1], Const)
|
|
43
|
+
else None
|
|
44
|
+
)
|
|
45
|
+
func_name = "__out"
|
|
46
|
+
suffix = self._inout_intrinsic_suffix(bits) if bits is not None else None
|
|
47
|
+
if suffix is not None:
|
|
48
|
+
func_name += f"{suffix}"
|
|
49
|
+
else:
|
|
50
|
+
func_name += f"_{bits}"
|
|
51
|
+
return Call(
|
|
52
|
+
dirty.idx, func_name, None, None, args=(dirty.operands[0],), ret_expr=None, bits=bits, **dirty.tags
|
|
53
|
+
)
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
#
|
|
57
|
+
# in, out
|
|
58
|
+
#
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def _inout_intrinsic_suffix(bits: int) -> str | None:
|
|
62
|
+
match bits:
|
|
63
|
+
case 8:
|
|
64
|
+
return "byte"
|
|
65
|
+
case 16:
|
|
66
|
+
return "word"
|
|
67
|
+
case 32:
|
|
68
|
+
return "dword"
|
|
69
|
+
return None
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from angr.ailment.statement import DirtyStatement, Statement
|
|
4
|
+
from angr.ailment.expression import DirtyExpression, Expression
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DirtyRewriterBase:
|
|
8
|
+
"""
|
|
9
|
+
The base class for DirtyStatement and DirtyExpression rewriters.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
__slots__ = (
|
|
13
|
+
"arch",
|
|
14
|
+
"result",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
def __init__(self, dirty: DirtyExpression | DirtyStatement, arch):
|
|
18
|
+
self.arch = arch
|
|
19
|
+
self.result: Expression | Statement | None = (
|
|
20
|
+
self._rewrite_expr(dirty) if isinstance(dirty, DirtyExpression) else self._rewrite_stmt(dirty)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def _rewrite_stmt(self, dirty: DirtyStatement) -> Statement | None:
|
|
24
|
+
raise NotImplementedError
|
|
25
|
+
|
|
26
|
+
def _rewrite_expr(self, dirty: DirtyExpression) -> Expression | None:
|
|
27
|
+
raise NotImplementedError
|
|
@@ -36,6 +36,7 @@ from .condition_constprop import ConditionConstantPropagation
|
|
|
36
36
|
from .determine_load_sizes import DetermineLoadSizes
|
|
37
37
|
from .eager_std_string_concatenation import EagerStdStringConcatenationPass
|
|
38
38
|
from .peephole_simplifier import PostStructuringPeepholeOptimizationPass
|
|
39
|
+
from .register_save_area_simplifier_adv import RegisterSaveAreaSimplifierAdvanced
|
|
39
40
|
|
|
40
41
|
if TYPE_CHECKING:
|
|
41
42
|
from angr.analyses.decompiler.presets import DecompilationPreset
|
|
@@ -74,6 +75,7 @@ ALL_OPTIMIZATION_PASSES = [
|
|
|
74
75
|
DetermineLoadSizes,
|
|
75
76
|
EagerStdStringConcatenationPass,
|
|
76
77
|
PostStructuringPeepholeOptimizationPass,
|
|
78
|
+
RegisterSaveAreaSimplifierAdvanced,
|
|
77
79
|
]
|
|
78
80
|
|
|
79
81
|
# these passes may duplicate code to remove gotos or improve the structure of the graph
|
|
@@ -138,6 +140,7 @@ __all__ = (
|
|
|
138
140
|
"ModSimplifier",
|
|
139
141
|
"OptimizationPassStage",
|
|
140
142
|
"RegisterSaveAreaSimplifier",
|
|
143
|
+
"RegisterSaveAreaSimplifierAdvanced",
|
|
141
144
|
"RetAddrSaveSimplifier",
|
|
142
145
|
"ReturnDeduplicator",
|
|
143
146
|
"ReturnDuplicatorHigh",
|
|
@@ -21,6 +21,7 @@ from angr.project import Project
|
|
|
21
21
|
|
|
22
22
|
if TYPE_CHECKING:
|
|
23
23
|
from angr.knowledge_plugins.functions import Function
|
|
24
|
+
from angr.sim_variable import SimVariable
|
|
24
25
|
from angr.analyses.decompiler.stack_item import StackItem
|
|
25
26
|
|
|
26
27
|
|
|
@@ -54,13 +55,14 @@ class OptimizationPassStage(Enum):
|
|
|
54
55
|
BEFORE_SSA_LEVEL0_TRANSFORMATION = 1
|
|
55
56
|
AFTER_SINGLE_BLOCK_SIMPLIFICATION = 2
|
|
56
57
|
BEFORE_SSA_LEVEL1_TRANSFORMATION = 3
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
AFTER_SSA_LEVEL1_TRANSFORMATION = 4
|
|
59
|
+
AFTER_MAKING_CALLSITES = 5
|
|
60
|
+
AFTER_GLOBAL_SIMPLIFICATION = 6
|
|
61
|
+
BEFORE_VARIABLE_RECOVERY = 7
|
|
62
|
+
AFTER_VARIABLE_RECOVERY = 8
|
|
63
|
+
BEFORE_REGION_IDENTIFICATION = 9
|
|
64
|
+
DURING_REGION_IDENTIFICATION = 10
|
|
65
|
+
AFTER_STRUCTURING = 11
|
|
64
66
|
|
|
65
67
|
|
|
66
68
|
class BaseOptimizationPass:
|
|
@@ -138,7 +140,7 @@ class OptimizationPass(BaseOptimizationPass):
|
|
|
138
140
|
refine_loops_with_single_successor: bool = False,
|
|
139
141
|
complete_successors: bool = False,
|
|
140
142
|
avoid_vvar_ids: set[int] | None = None,
|
|
141
|
-
arg_vvars:
|
|
143
|
+
arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]] | None = None,
|
|
142
144
|
peephole_optimizations=None,
|
|
143
145
|
stack_pointer_tracker=None,
|
|
144
146
|
notes: dict | None = None,
|
|
@@ -99,18 +99,39 @@ class RegisterSaveAreaSimplifier(OptimizationPass):
|
|
|
99
99
|
|
|
100
100
|
results = []
|
|
101
101
|
|
|
102
|
+
# there are cases where sp is moved to another register at the beginning of the function before it is
|
|
103
|
+
# subtracted, then it is used for stack accesses.
|
|
104
|
+
# for example:
|
|
105
|
+
# 132B0 mov r11, rsp
|
|
106
|
+
# 132B3 sub rsp, 88h
|
|
107
|
+
# 132BA ...
|
|
108
|
+
# 132C1 ...
|
|
109
|
+
# 132C6 mov [r11-18h], r13
|
|
110
|
+
# 132CA mov [r11-20h], r14
|
|
111
|
+
# we support such cases because we will have rewritten r11-18h to SpOffset(-N) when this optimization runs.
|
|
112
|
+
|
|
113
|
+
# identify which registers have been updated in this function; they are no longer saved
|
|
114
|
+
ignored_regs: set[int] = set()
|
|
115
|
+
|
|
102
116
|
for idx, stmt in enumerate(first_block.statements):
|
|
117
|
+
if (
|
|
118
|
+
isinstance(stmt, ailment.Stmt.Assignment)
|
|
119
|
+
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
120
|
+
and stmt.dst.was_reg
|
|
121
|
+
):
|
|
122
|
+
ignored_regs.add(stmt.dst.reg_offset)
|
|
103
123
|
if (
|
|
104
124
|
isinstance(stmt, ailment.Stmt.Store)
|
|
105
125
|
and isinstance(stmt.addr, ailment.Expr.StackBaseOffset)
|
|
106
126
|
and isinstance(stmt.addr.offset, int)
|
|
107
127
|
):
|
|
108
128
|
if isinstance(stmt.data, ailment.Expr.VirtualVariable) and stmt.data.was_reg:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
129
|
+
if stmt.data.reg_offset not in ignored_regs:
|
|
130
|
+
# it's storing registers to the stack!
|
|
131
|
+
stack_offset = stmt.addr.offset
|
|
132
|
+
reg_offset = stmt.data.reg_offset
|
|
133
|
+
codeloc = CodeLocation(first_block.addr, idx, block_idx=first_block.idx, ins_addr=stmt.ins_addr)
|
|
134
|
+
results.append((reg_offset, stack_offset, codeloc))
|
|
114
135
|
elif (
|
|
115
136
|
self.project.arch.name == "AMD64"
|
|
116
137
|
and isinstance(stmt.data, ailment.Expr.Convert)
|
|
@@ -130,7 +151,24 @@ class RegisterSaveAreaSimplifier(OptimizationPass):
|
|
|
130
151
|
def _find_registers_restored_from_stack(self) -> list[list[tuple[int, int, CodeLocation]]]:
|
|
131
152
|
all_results = []
|
|
132
153
|
for ret_site in self._func.ret_sites + self._func.jumpout_sites:
|
|
133
|
-
|
|
154
|
+
|
|
155
|
+
ret_blocks = list(self._get_blocks(ret_site.addr))
|
|
156
|
+
if len(ret_blocks) == 1 and self.project.simos is not None and self.project.simos.name == "Win32":
|
|
157
|
+
# PE files may call __security_check_cookie (which terminates the program if the stack canary is
|
|
158
|
+
# corrupted) before returning.
|
|
159
|
+
preds = list(self._graph.predecessors(ret_blocks[0]))
|
|
160
|
+
if len(preds) == 1:
|
|
161
|
+
pred = preds[0]
|
|
162
|
+
if pred.statements and isinstance(pred.statements[-1], ailment.Stmt.Call):
|
|
163
|
+
last_stmt = pred.statements[-1]
|
|
164
|
+
if isinstance(last_stmt.target, ailment.Expr.Const):
|
|
165
|
+
callee_addr = last_stmt.target.value
|
|
166
|
+
if self.project.kb.functions.contains_addr(callee_addr):
|
|
167
|
+
callee_func = self.project.kb.functions.get_by_addr(callee_addr)
|
|
168
|
+
if callee_func.name == "_security_check_cookie":
|
|
169
|
+
ret_blocks.append(pred)
|
|
170
|
+
|
|
171
|
+
for block in ret_blocks:
|
|
134
172
|
results = []
|
|
135
173
|
for idx, stmt in enumerate(block.statements):
|
|
136
174
|
if (
|