angr 9.2.143__py3-none-macosx_11_0_arm64.whl → 9.2.145__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/calling_convention.py +13 -1
- angr/analyses/calling_convention/fact_collector.py +41 -5
- angr/analyses/cfg/cfg_base.py +7 -2
- angr/analyses/cfg/cfg_emulated.py +13 -4
- angr/analyses/cfg/cfg_fast.py +35 -61
- angr/analyses/cfg/indirect_jump_resolvers/__init__.py +2 -0
- angr/analyses/cfg/indirect_jump_resolvers/constant_value_manager.py +107 -0
- angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +2 -1
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +2 -101
- angr/analyses/cfg/indirect_jump_resolvers/syscall_resolver.py +92 -0
- angr/analyses/decompiler/ail_simplifier.py +5 -0
- angr/analyses/decompiler/clinic.py +163 -69
- angr/analyses/decompiler/decompiler.py +4 -4
- angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +5 -5
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +5 -0
- angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +58 -2
- angr/analyses/decompiler/peephole_optimizations/__init__.py +2 -0
- angr/analyses/decompiler/peephole_optimizations/a_sub_a_shr_const_shr_const.py +37 -0
- angr/analyses/decompiler/ssailification/rewriting_engine.py +2 -0
- angr/analyses/decompiler/ssailification/ssailification.py +10 -2
- angr/analyses/decompiler/ssailification/traversal_engine.py +17 -2
- angr/analyses/decompiler/structured_codegen/c.py +25 -4
- angr/analyses/disassembly.py +3 -3
- angr/analyses/fcp/fcp.py +1 -4
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +21 -22
- angr/analyses/stack_pointer_tracker.py +61 -25
- angr/analyses/typehoon/dfa.py +13 -3
- angr/analyses/typehoon/typehoon.py +60 -18
- angr/analyses/typehoon/typevars.py +11 -7
- angr/analyses/variable_recovery/engine_ail.py +13 -17
- angr/analyses/variable_recovery/engine_base.py +26 -30
- angr/analyses/variable_recovery/variable_recovery_fast.py +17 -21
- angr/knowledge_plugins/functions/function.py +29 -15
- angr/knowledge_plugins/key_definitions/constants.py +2 -2
- angr/knowledge_plugins/key_definitions/liveness.py +4 -4
- angr/lib/angr_native.dylib +0 -0
- angr/state_plugins/unicorn_engine.py +24 -8
- angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +1 -2
- angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +2 -2
- angr/utils/funcid.py +27 -2
- angr/utils/graph.py +26 -20
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/METADATA +11 -8
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/RECORD +49 -46
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/WHEEL +1 -1
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/LICENSE +0 -0
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/entry_points.txt +0 -0
- {angr-9.2.143.dist-info → angr-9.2.145.dist-info}/top_level.txt +0 -0
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
from collections import defaultdict
|
|
5
5
|
from collections.abc import Iterable
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Any, TYPE_CHECKING
|
|
7
7
|
|
|
8
8
|
import networkx
|
|
9
9
|
from cle import SymbolType
|
|
@@ -35,9 +35,9 @@ if TYPE_CHECKING:
|
|
|
35
35
|
|
|
36
36
|
l = logging.getLogger(name=__name__)
|
|
37
37
|
|
|
38
|
-
_PEEPHOLE_OPTIMIZATIONS_TYPE =
|
|
39
|
-
Iterable[
|
|
40
|
-
|
|
38
|
+
_PEEPHOLE_OPTIMIZATIONS_TYPE = (
|
|
39
|
+
Iterable[type["PeepholeOptimizationStmtBase"] | type["PeepholeOptimizationExprBase"]] | None
|
|
40
|
+
)
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
class Decompiler(Analysis):
|
|
@@ -16,7 +16,7 @@ class BasePointerSaveSimplifier(OptimizationPass):
|
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
ARCHES = ["X86", "AMD64", "ARMEL", "ARMHF", "ARMCortexM", "MIPS32", "MIPS64"]
|
|
19
|
-
PLATFORMS =
|
|
19
|
+
PLATFORMS = None
|
|
20
20
|
STAGE = OptimizationPassStage.AFTER_GLOBAL_SIMPLIFICATION
|
|
21
21
|
NAME = "Simplify base pointer saving"
|
|
22
22
|
DESCRIPTION = __doc__.strip()
|
|
@@ -443,6 +443,11 @@ class StructuringOptimizationPass(OptimizationPass):
|
|
|
443
443
|
"""
|
|
444
444
|
Wrapper for _analyze() that verifies the graph is structurable before and after the optimization.
|
|
445
445
|
"""
|
|
446
|
+
# replace the normal check in OptimizationPass.analyze()
|
|
447
|
+
ret, cache = self._check()
|
|
448
|
+
if not ret:
|
|
449
|
+
return
|
|
450
|
+
|
|
446
451
|
if not self._graph_is_structurable(self._graph, initial=True):
|
|
447
452
|
return
|
|
448
453
|
|
|
@@ -450,11 +455,6 @@ class StructuringOptimizationPass(OptimizationPass):
|
|
|
450
455
|
if self._require_gotos and not self._initial_gotos:
|
|
451
456
|
return
|
|
452
457
|
|
|
453
|
-
# replace the normal check in OptimizationPass.analyze()
|
|
454
|
-
ret, cache = self._check()
|
|
455
|
-
if not ret:
|
|
456
|
-
return
|
|
457
|
-
|
|
458
458
|
# setup for the very first analysis
|
|
459
459
|
self.out_graph = networkx.DiGraph(self._graph)
|
|
460
460
|
if self._max_opt_iters > 1:
|
|
@@ -95,6 +95,7 @@ class ReturnDuplicatorBase:
|
|
|
95
95
|
minimize_copies_for_regions: bool = True,
|
|
96
96
|
ri: RegionIdentifier | None = None,
|
|
97
97
|
scratch: dict[str, Any] | None = None,
|
|
98
|
+
max_func_blocks: int = 1500,
|
|
98
99
|
):
|
|
99
100
|
self._max_calls_in_region = max_calls_in_regions
|
|
100
101
|
self._minimize_copies_for_regions = minimize_copies_for_regions
|
|
@@ -105,6 +106,7 @@ class ReturnDuplicatorBase:
|
|
|
105
106
|
self._func = func
|
|
106
107
|
self._ri: RegionIdentifier | None = ri
|
|
107
108
|
self.vvar_id_start = vvar_id_start
|
|
109
|
+
self._max_func_blocks = max_func_blocks
|
|
108
110
|
|
|
109
111
|
def next_node_idx(self) -> int:
|
|
110
112
|
node_idx = self.scratch.get("returndup_node_idx", 0) + 1
|
|
@@ -123,6 +125,9 @@ class ReturnDuplicatorBase:
|
|
|
123
125
|
#
|
|
124
126
|
|
|
125
127
|
def _check(self):
|
|
128
|
+
# is this function too large?
|
|
129
|
+
if len(self._func.block_addrs_set) > self._max_func_blocks:
|
|
130
|
+
return False, None
|
|
126
131
|
# does this function have end points?
|
|
127
132
|
return bool(self._func.endpoints), None
|
|
128
133
|
|
|
@@ -110,8 +110,11 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
110
110
|
_l.debug("Cannot find the statement calling _security_check_cookie() in the predecessor.")
|
|
111
111
|
continue
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
canary_storing_stmt_idx = (
|
|
114
|
+
self._find_amd64_canary_storing_stmt(pred, store_offset)
|
|
115
|
+
if self.project.arch.name == "AMD64"
|
|
116
|
+
else self._find_x86_canary_storing_stmt(pred, store_offset)
|
|
117
|
+
)
|
|
115
118
|
if canary_storing_stmt_idx is None:
|
|
116
119
|
_l.debug("Cannot find the canary check statement in the predecessor.")
|
|
117
120
|
continue
|
|
@@ -270,6 +273,59 @@ class WinStackCanarySimplifier(OptimizationPass):
|
|
|
270
273
|
return idx
|
|
271
274
|
return None
|
|
272
275
|
|
|
276
|
+
def _find_x86_canary_storing_stmt(self, block, canary_value_stack_offset):
|
|
277
|
+
load_stmt_idx = None
|
|
278
|
+
|
|
279
|
+
for idx, stmt in enumerate(block.statements):
|
|
280
|
+
# when we are lucky, we have one instruction
|
|
281
|
+
if (
|
|
282
|
+
(
|
|
283
|
+
isinstance(stmt, ailment.Stmt.Assignment)
|
|
284
|
+
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
285
|
+
and stmt.dst.was_reg
|
|
286
|
+
and stmt.dst.reg_offset == self.project.arch.registers["eax"][0]
|
|
287
|
+
)
|
|
288
|
+
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
289
|
+
and stmt.src.op == "Xor"
|
|
290
|
+
):
|
|
291
|
+
op0, op1 = stmt.src.operands
|
|
292
|
+
if (
|
|
293
|
+
isinstance(op0, ailment.Expr.Load)
|
|
294
|
+
and isinstance(op0.addr, ailment.Expr.StackBaseOffset)
|
|
295
|
+
and op0.addr.offset == canary_value_stack_offset
|
|
296
|
+
) and isinstance(op1, ailment.Expr.StackBaseOffset):
|
|
297
|
+
# found it
|
|
298
|
+
return idx
|
|
299
|
+
# or when we are unlucky, we have two instructions...
|
|
300
|
+
if (
|
|
301
|
+
isinstance(stmt, ailment.Stmt.Assignment)
|
|
302
|
+
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
303
|
+
and stmt.dst.reg_offset == self.project.arch.registers["eax"][0]
|
|
304
|
+
and isinstance(stmt.src, ailment.Expr.Load)
|
|
305
|
+
and isinstance(stmt.src.addr, ailment.Expr.StackBaseOffset)
|
|
306
|
+
and stmt.src.addr.offset == canary_value_stack_offset
|
|
307
|
+
):
|
|
308
|
+
load_stmt_idx = idx
|
|
309
|
+
if (
|
|
310
|
+
load_stmt_idx is not None
|
|
311
|
+
and idx >= load_stmt_idx + 1
|
|
312
|
+
and (
|
|
313
|
+
isinstance(stmt, ailment.Stmt.Assignment)
|
|
314
|
+
and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
|
|
315
|
+
and stmt.dst.was_reg
|
|
316
|
+
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
317
|
+
and stmt.src.op == "Xor"
|
|
318
|
+
)
|
|
319
|
+
and (
|
|
320
|
+
isinstance(stmt.src.operands[0], ailment.Expr.VirtualVariable)
|
|
321
|
+
and stmt.src.operands[0].was_reg
|
|
322
|
+
and stmt.src.operands[0].reg_offset == self.project.arch.registers["eax"][0]
|
|
323
|
+
and isinstance(stmt.src.operands[1], ailment.Expr.StackBaseOffset)
|
|
324
|
+
)
|
|
325
|
+
):
|
|
326
|
+
return idx
|
|
327
|
+
return None
|
|
328
|
+
|
|
273
329
|
@staticmethod
|
|
274
330
|
def _find_return_addr_storing_stmt(block):
|
|
275
331
|
for idx, stmt in enumerate(block.statements):
|
|
@@ -5,6 +5,7 @@ from .a_mul_const_div_shr_const import AMulConstDivShrConst
|
|
|
5
5
|
from .a_shl_const_sub_a import AShlConstSubA
|
|
6
6
|
from .a_sub_a_div import ASubADiv
|
|
7
7
|
from .a_sub_a_div_const_mul_const import ASubADivConstMulConst
|
|
8
|
+
from .a_sub_a_shr_const_shr_const import ASubAShrConstShrConst
|
|
8
9
|
from .arm_cmpf import ARMCmpF
|
|
9
10
|
from .bswap import Bswap
|
|
10
11
|
from .coalesce_same_cascading_ifs import CoalesceSameCascadingIfs
|
|
@@ -57,6 +58,7 @@ ALL_PEEPHOLE_OPTS: list[type[PeepholeOptimizationExprBase]] = [
|
|
|
57
58
|
AMulConstSubA,
|
|
58
59
|
ASubADiv,
|
|
59
60
|
ASubADivConstMulConst,
|
|
61
|
+
ASubAShrConstShrConst,
|
|
60
62
|
ARMCmpF,
|
|
61
63
|
Bswap,
|
|
62
64
|
CoalesceSameCascadingIfs,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# pylint:disable=no-self-use,too-many-boolean-expressions
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from ailment.expression import BinaryOp, Const
|
|
4
|
+
|
|
5
|
+
from .base import PeepholeOptimizationExprBase
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ASubAShrConstShrConst(PeepholeOptimizationExprBase):
|
|
9
|
+
"""
|
|
10
|
+
Convert `cdq; sub eax, edx; sar eax, 1` to `eax /= 2`.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
__slots__ = ()
|
|
14
|
+
|
|
15
|
+
NAME = "(a - (a >> 31)) >> N => a / 2 ** N (signed)"
|
|
16
|
+
expr_classes = (BinaryOp,)
|
|
17
|
+
|
|
18
|
+
def optimize(self, expr: BinaryOp, **kwargs):
|
|
19
|
+
if (
|
|
20
|
+
expr.op == "Sar"
|
|
21
|
+
and len(expr.operands) == 2
|
|
22
|
+
and isinstance(expr.operands[1], Const)
|
|
23
|
+
and expr.operands[1].is_int
|
|
24
|
+
and isinstance(expr.operands[0], BinaryOp)
|
|
25
|
+
and expr.operands[0].op == "Sub"
|
|
26
|
+
):
|
|
27
|
+
a0, a1 = expr.operands[0].operands
|
|
28
|
+
if (
|
|
29
|
+
isinstance(a1, BinaryOp)
|
|
30
|
+
and a1.op == "Sar"
|
|
31
|
+
and isinstance(a1.operands[1], Const)
|
|
32
|
+
and a1.operands[1].value == 31
|
|
33
|
+
and a0.likes(a1.operands[0])
|
|
34
|
+
):
|
|
35
|
+
dividend = 2 ** expr.operands[1].value
|
|
36
|
+
return BinaryOp(a0.idx, "Div", [a0, Const(None, None, dividend, expr.bits)], True, **expr.tags)
|
|
37
|
+
return None
|
|
@@ -698,6 +698,8 @@ class SimEngineSSARewriting(
|
|
|
698
698
|
raise NotImplementedError("Store expressions are not supported in _replace_use_expr.")
|
|
699
699
|
if isinstance(thing, Tmp) and self.rewrite_tmps:
|
|
700
700
|
return self._replace_use_tmp(self.block.addr, self.block.idx, self.stmt_idx, thing)
|
|
701
|
+
if isinstance(thing, Load):
|
|
702
|
+
return self._replace_use_load(thing)
|
|
701
703
|
return None
|
|
702
704
|
|
|
703
705
|
def _replace_use_reg(self, reg_expr: Register) -> VirtualVariable | Expression:
|
|
@@ -5,7 +5,15 @@ from collections import defaultdict
|
|
|
5
5
|
from itertools import count
|
|
6
6
|
from bisect import bisect_left
|
|
7
7
|
|
|
8
|
-
from ailment.expression import
|
|
8
|
+
from ailment.expression import (
|
|
9
|
+
Expression,
|
|
10
|
+
Register,
|
|
11
|
+
StackBaseOffset,
|
|
12
|
+
Tmp,
|
|
13
|
+
VirtualVariable,
|
|
14
|
+
VirtualVariableCategory,
|
|
15
|
+
Load,
|
|
16
|
+
)
|
|
9
17
|
from ailment.statement import Statement, Store
|
|
10
18
|
|
|
11
19
|
from angr.knowledge_plugins.functions import Function
|
|
@@ -151,7 +159,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
|
|
|
151
159
|
reg_bits = def_.size * self.project.arch.byte_width
|
|
152
160
|
udef_to_defs[("reg", def_.reg_offset, reg_bits)].add(def_)
|
|
153
161
|
udef_to_blockkeys[("reg", def_.reg_offset, reg_bits)].add((loc.block_addr, loc.block_idx))
|
|
154
|
-
elif isinstance(def_, Store):
|
|
162
|
+
elif isinstance(def_, (Store, Load)):
|
|
155
163
|
if isinstance(def_.addr, StackBaseOffset) and isinstance(def_.addr.offset, int):
|
|
156
164
|
idx_begin = bisect_left(sorted_stackvar_offs, def_.addr.offset)
|
|
157
165
|
for i in range(idx_begin, len(sorted_stackvar_offs)):
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
from collections import OrderedDict
|
|
3
3
|
|
|
4
4
|
from ailment.statement import Call, Store, ConditionalJump
|
|
5
|
-
from ailment.expression import Register, BinaryOp, StackBaseOffset, ITE, VEXCCallExpression, Tmp, DirtyExpression
|
|
5
|
+
from ailment.expression import Register, BinaryOp, StackBaseOffset, ITE, VEXCCallExpression, Tmp, DirtyExpression, Load
|
|
6
6
|
|
|
7
7
|
from angr.engines.light import SimEngineLightAIL
|
|
8
8
|
from angr.project import Project
|
|
@@ -133,6 +133,22 @@ class SimEngineSSATraversal(SimEngineLightAIL[TraversalState, None, None, None])
|
|
|
133
133
|
|
|
134
134
|
self.state.live_registers.add(base_offset)
|
|
135
135
|
|
|
136
|
+
def _handle_expr_Load(self, expr: Load):
|
|
137
|
+
self._expr(expr.addr)
|
|
138
|
+
if (
|
|
139
|
+
self.stackvars
|
|
140
|
+
and isinstance(expr.addr, StackBaseOffset)
|
|
141
|
+
and isinstance(expr.addr.offset, int)
|
|
142
|
+
and (expr.addr.offset, expr.size) not in self.state.live_stackvars
|
|
143
|
+
):
|
|
144
|
+
# we must create this stack variable on the fly; we did not see its creation before it is first used
|
|
145
|
+
codeloc = self._codeloc()
|
|
146
|
+
self.def_to_loc.append((expr, codeloc))
|
|
147
|
+
if codeloc not in self.loc_to_defs:
|
|
148
|
+
self.loc_to_defs[codeloc] = OrderedSet()
|
|
149
|
+
self.loc_to_defs[codeloc].add(expr)
|
|
150
|
+
self.state.live_stackvars.add((expr.addr.offset, expr.size))
|
|
151
|
+
|
|
136
152
|
def _handle_expr_Tmp(self, expr: Tmp):
|
|
137
153
|
if self.use_tmps:
|
|
138
154
|
codeloc = self._codeloc()
|
|
@@ -251,7 +267,6 @@ class SimEngineSSATraversal(SimEngineLightAIL[TraversalState, None, None, None])
|
|
|
251
267
|
|
|
252
268
|
_handle_expr_VirtualVariable = _handle_Dummy
|
|
253
269
|
_handle_expr_Phi = _handle_Dummy
|
|
254
|
-
_handle_expr_Load = _handle_Dummy
|
|
255
270
|
_handle_expr_Const = _handle_Dummy
|
|
256
271
|
_handle_expr_MultiStatementExpression = _handle_Dummy
|
|
257
272
|
_handle_expr_StackBaseOffset = _handle_Dummy
|
|
@@ -3426,8 +3426,13 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3426
3426
|
return old_ty
|
|
3427
3427
|
|
|
3428
3428
|
if expr.variable is not None:
|
|
3429
|
-
|
|
3430
|
-
|
|
3429
|
+
if "struct_member_info" in expr.tags:
|
|
3430
|
+
offset, var, _ = expr.struct_member_info
|
|
3431
|
+
cvar = self._variable(var, var.size)
|
|
3432
|
+
else:
|
|
3433
|
+
cvar = self._variable(expr.variable, expr.size)
|
|
3434
|
+
offset = expr.variable_offset or 0
|
|
3435
|
+
|
|
3431
3436
|
assert type(offset) is int # I refuse to deal with the alternative
|
|
3432
3437
|
return self._access_constant_offset(CUnaryOp("Reference", cvar, codegen=self), offset, ty, False, negotiate)
|
|
3433
3438
|
|
|
@@ -3649,8 +3654,24 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3649
3654
|
return CMultiStatementExpression(cstmts, cexpr, tags=expr.tags, codegen=self)
|
|
3650
3655
|
|
|
3651
3656
|
def _handle_VirtualVariable(self, expr: Expr.VirtualVariable, **kwargs):
|
|
3652
|
-
|
|
3653
|
-
|
|
3657
|
+
def negotiate(old_ty: SimType, proposed_ty: SimType) -> SimType:
|
|
3658
|
+
# we do not allow returning a struct for a primitive type
|
|
3659
|
+
if old_ty.size == proposed_ty.size and (
|
|
3660
|
+
not isinstance(proposed_ty, SimStruct) or isinstance(old_ty, SimStruct)
|
|
3661
|
+
):
|
|
3662
|
+
return proposed_ty
|
|
3663
|
+
return old_ty
|
|
3664
|
+
|
|
3665
|
+
if expr.variable is not None:
|
|
3666
|
+
if "struct_member_info" in expr.tags:
|
|
3667
|
+
offset, var, _ = expr.struct_member_info
|
|
3668
|
+
cbasevar = self._variable(var, expr.size)
|
|
3669
|
+
cvar = self._access_constant_offset(
|
|
3670
|
+
self._get_variable_reference(cbasevar), offset, cbasevar.type, False, negotiate
|
|
3671
|
+
)
|
|
3672
|
+
else:
|
|
3673
|
+
cvar = self._variable(expr.variable, None, vvar_id=expr.varid)
|
|
3674
|
+
|
|
3654
3675
|
if expr.variable.size != expr.size:
|
|
3655
3676
|
l.warning(
|
|
3656
3677
|
"VirtualVariable size (%d) and variable size (%d) do not match. Force a type cast.",
|
angr/analyses/disassembly.py
CHANGED
|
@@ -4,7 +4,7 @@ import contextlib
|
|
|
4
4
|
import logging
|
|
5
5
|
from collections import defaultdict
|
|
6
6
|
from collections.abc import Sequence
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import Any
|
|
8
8
|
|
|
9
9
|
import pyvex
|
|
10
10
|
import archinfo
|
|
@@ -24,8 +24,8 @@ try:
|
|
|
24
24
|
from angr.engines import pcode
|
|
25
25
|
import pypcode
|
|
26
26
|
|
|
27
|
-
IRSBType =
|
|
28
|
-
IROpObjType =
|
|
27
|
+
IRSBType = pyvex.IRSB | pcode.lifter.IRSB
|
|
28
|
+
IROpObjType = pyvex.stmt.IRStmt | pypcode.PcodeOp
|
|
29
29
|
except ImportError:
|
|
30
30
|
pcode = None
|
|
31
31
|
IRSBType = pyvex.IRSB
|
angr/analyses/fcp/fcp.py
CHANGED
|
@@ -407,10 +407,7 @@ class FastConstantPropagation(Analysis):
|
|
|
407
407
|
except (TypeError, ValueError):
|
|
408
408
|
arg_locs = None
|
|
409
409
|
|
|
410
|
-
if None in arg_locs:
|
|
411
|
-
arg_locs = None
|
|
412
|
-
|
|
413
|
-
if arg_locs is not None:
|
|
410
|
+
if arg_locs is not None and None not in arg_locs:
|
|
414
411
|
for arg_loc in arg_locs:
|
|
415
412
|
for loc in arg_loc.get_footprint():
|
|
416
413
|
if isinstance(loc, SimStackArg):
|
|
@@ -131,28 +131,27 @@ class SReachingDefinitionsAnalysis(Analysis):
|
|
|
131
131
|
stmt if isinstance(stmt, Call) else stmt.src if isinstance(stmt, Assignment) else stmt.ret_exprs[0]
|
|
132
132
|
)
|
|
133
133
|
assert isinstance(call, Call)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
self.model.add_vvar_use(vvarid, None, codeloc)
|
|
134
|
+
|
|
135
|
+
# conservatively add uses to all registers that are potentially used here
|
|
136
|
+
if call.calling_convention is not None:
|
|
137
|
+
cc = call.calling_convention
|
|
138
|
+
else:
|
|
139
|
+
# just use all registers in the default calling convention because we don't know anything about
|
|
140
|
+
# the calling convention yet
|
|
141
|
+
cc_cls = default_cc(self.project.arch.name)
|
|
142
|
+
assert cc_cls is not None
|
|
143
|
+
cc = cc_cls(self.project.arch)
|
|
144
|
+
|
|
145
|
+
codeloc = CodeLocation(block_addr, stmt_idx, block_idx=block_idx, ins_addr=stmt.ins_addr)
|
|
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]
|
|
149
|
+
|
|
150
|
+
for arg_reg_name in arg_locs:
|
|
151
|
+
reg_offset = self.project.arch.registers[arg_reg_name][0]
|
|
152
|
+
if reg_offset in reg_to_vvarids:
|
|
153
|
+
vvarid = reg_to_vvarids[reg_offset]
|
|
154
|
+
self.model.add_vvar_use(vvarid, None, codeloc)
|
|
156
155
|
|
|
157
156
|
if self._track_tmps:
|
|
158
157
|
# track tmps
|
|
@@ -15,6 +15,7 @@ from angr.analyses import AnalysesHub
|
|
|
15
15
|
from angr.knowledge_plugins import Function
|
|
16
16
|
from angr.block import BlockNode
|
|
17
17
|
from angr.errors import SimTranslationError
|
|
18
|
+
from angr.calling_conventions import SimStackArg
|
|
18
19
|
from .analysis import Analysis
|
|
19
20
|
|
|
20
21
|
try:
|
|
@@ -554,7 +555,7 @@ class StackPointerTracker(Analysis, ForwardAnalysis):
|
|
|
554
555
|
if vex_block is not None:
|
|
555
556
|
if isinstance(vex_block, pyvex.IRSB):
|
|
556
557
|
curr_stmt_start_addr = self._process_vex_irsb(node, vex_block, state)
|
|
557
|
-
elif pypcode is not None and isinstance(vex_block, pcode.lifter.IRSB):
|
|
558
|
+
elif pypcode is not None and isinstance(vex_block, pcode.lifter.IRSB): # type: ignore
|
|
558
559
|
curr_stmt_start_addr = self._process_pcode_irsb(node, vex_block, state)
|
|
559
560
|
else:
|
|
560
561
|
raise NotImplementedError(f"Unsupported block type {type(vex_block)}")
|
|
@@ -587,7 +588,7 @@ class StackPointerTracker(Analysis, ForwardAnalysis):
|
|
|
587
588
|
raise CouldNotResolveException
|
|
588
589
|
if arg1_expr is BOTTOM:
|
|
589
590
|
return BOTTOM
|
|
590
|
-
return arg0_expr + arg1_expr
|
|
591
|
+
return arg0_expr + arg1_expr # type: ignore
|
|
591
592
|
if expr.op.startswith("Iop_Sub"):
|
|
592
593
|
arg0_expr = _resolve_expr(arg0)
|
|
593
594
|
if arg0_expr is None:
|
|
@@ -599,7 +600,7 @@ class StackPointerTracker(Analysis, ForwardAnalysis):
|
|
|
599
600
|
raise CouldNotResolveException
|
|
600
601
|
if arg1_expr is BOTTOM:
|
|
601
602
|
return BOTTOM
|
|
602
|
-
return arg0_expr - arg1_expr
|
|
603
|
+
return arg0_expr - arg1_expr # type: ignore
|
|
603
604
|
if expr.op.startswith("Iop_And"):
|
|
604
605
|
# handle stack pointer alignments
|
|
605
606
|
arg0_expr = _resolve_expr(arg0)
|
|
@@ -713,43 +714,78 @@ class StackPointerTracker(Analysis, ForwardAnalysis):
|
|
|
713
714
|
pass
|
|
714
715
|
# who are we calling?
|
|
715
716
|
callees = [] if self._func is None else self._find_callees(node)
|
|
717
|
+
sp_adjusted = False
|
|
716
718
|
if callees:
|
|
717
719
|
if len(callees) == 1:
|
|
720
|
+
|
|
718
721
|
callee = callees[0]
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
722
|
+
if callee.info.get("is_rust_probestack", False):
|
|
723
|
+
# sp = sp - rax/eax right after returning from the call
|
|
724
|
+
rust_probe_stack_rax_regname: str | None = None
|
|
725
|
+
if self.project.arch.name == "AMD64":
|
|
726
|
+
rust_probe_stack_rax_regname = "rax"
|
|
727
|
+
elif self.project.arch.name == "X86":
|
|
728
|
+
rust_probe_stack_rax_regname = "eax"
|
|
729
|
+
|
|
730
|
+
if rust_probe_stack_rax_regname is not None:
|
|
731
|
+
for stmt in reversed(vex_block.statements):
|
|
732
|
+
if (
|
|
733
|
+
isinstance(stmt, pyvex.IRStmt.Put)
|
|
734
|
+
and stmt.offset == self.project.arch.registers[rust_probe_stack_rax_regname][0]
|
|
735
|
+
and isinstance(stmt.data, pyvex.IRExpr.Const)
|
|
736
|
+
):
|
|
737
|
+
sp_adjusted = True
|
|
738
|
+
state.put(stmt.offset, Constant(stmt.data.con.value), force=True)
|
|
739
|
+
break
|
|
740
|
+
|
|
741
|
+
if not sp_adjusted and (callee.info.get("is_alloca_probe", False) or callee.name == "__chkstk"):
|
|
742
|
+
# sp = sp - rax, but it's adjusted within the callee
|
|
743
|
+
chkstk_stack_rax_regname: str | None = None
|
|
744
|
+
if self.project.arch.name == "AMD64":
|
|
745
|
+
chkstk_stack_rax_regname = "rax"
|
|
746
|
+
elif self.project.arch.name == "X86":
|
|
747
|
+
chkstk_stack_rax_regname = "eax"
|
|
748
|
+
|
|
749
|
+
if chkstk_stack_rax_regname is not None:
|
|
750
|
+
for stmt in reversed(vex_block.statements):
|
|
751
|
+
if (
|
|
752
|
+
isinstance(stmt, pyvex.IRStmt.Put)
|
|
753
|
+
and stmt.offset == self.project.arch.registers[chkstk_stack_rax_regname][0]
|
|
754
|
+
and isinstance(stmt.data, pyvex.IRExpr.Const)
|
|
755
|
+
and self.project.arch.sp_offset in state.regs
|
|
756
|
+
):
|
|
757
|
+
sp_adjusted = True
|
|
758
|
+
sp_v = state.regs[self.project.arch.sp_offset]
|
|
759
|
+
sp_v -= Constant(stmt.data.con.value)
|
|
760
|
+
state.put(self.project.arch.sp_offset, sp_v, force=True)
|
|
761
|
+
break
|
|
737
762
|
|
|
738
763
|
callee_cleanups = [
|
|
739
764
|
callee
|
|
740
765
|
for callee in callees
|
|
741
|
-
if callee.calling_convention is not None
|
|
766
|
+
if callee.calling_convention is not None
|
|
767
|
+
and callee.calling_convention.CALLEE_CLEANUP
|
|
768
|
+
and callee.prototype is not None
|
|
742
769
|
]
|
|
743
770
|
if callee_cleanups:
|
|
744
771
|
# found callee clean-up cases...
|
|
772
|
+
callee = callee_cleanups[0]
|
|
773
|
+
assert callee.calling_convention is not None # just to make pyright happy
|
|
745
774
|
try:
|
|
746
775
|
v = state.get(self.project.arch.sp_offset)
|
|
747
776
|
incremented = None
|
|
748
777
|
if v is BOTTOM:
|
|
749
778
|
incremented = BOTTOM
|
|
750
|
-
elif
|
|
751
|
-
|
|
752
|
-
|
|
779
|
+
elif callee.prototype is not None:
|
|
780
|
+
num_stack_args = len(
|
|
781
|
+
[
|
|
782
|
+
arg_loc
|
|
783
|
+
for arg_loc in callee.calling_convention.arg_locs(callee.prototype)
|
|
784
|
+
if isinstance(arg_loc, SimStackArg)
|
|
785
|
+
]
|
|
786
|
+
)
|
|
787
|
+
if num_stack_args > 0:
|
|
788
|
+
incremented = v + Constant(self.project.arch.bytes * num_stack_args)
|
|
753
789
|
if incremented is not None:
|
|
754
790
|
state.put(self.project.arch.sp_offset, incremented)
|
|
755
791
|
except CouldNotResolveException:
|
angr/analyses/typehoon/dfa.py
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
|
+
# pylint:disable=import-outside-toplevel
|
|
1
2
|
from __future__ import annotations
|
|
2
3
|
from typing import TYPE_CHECKING
|
|
3
4
|
|
|
4
5
|
import networkx
|
|
5
6
|
|
|
6
7
|
# FIXME: Remove the dependency on pyformlang
|
|
7
|
-
from pyformlang.finite_automaton import Epsilon, EpsilonNFA, State, Symbol
|
|
8
8
|
|
|
9
9
|
from angr.errors import AngrError
|
|
10
10
|
from .typevars import BaseLabel, Subtype
|
|
11
11
|
from .variance import Variance
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
|
+
from pyformlang.finite_automaton import EpsilonNFA
|
|
14
15
|
from pyformlang.finite_automaton import DeterministicFiniteAutomaton
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
START_STATE =
|
|
18
|
-
END_STATE =
|
|
18
|
+
START_STATE = None
|
|
19
|
+
END_STATE = None
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class EmptyEpsilonNFAError(AngrError):
|
|
@@ -31,6 +32,15 @@ class DFAConstraintSolver:
|
|
|
31
32
|
|
|
32
33
|
@staticmethod
|
|
33
34
|
def graph_to_epsilon_nfa(graph: networkx.DiGraph, starts: set, ends: set) -> EpsilonNFA:
|
|
35
|
+
from pyformlang.finite_automaton import Epsilon, EpsilonNFA, State, Symbol # delayed import
|
|
36
|
+
|
|
37
|
+
global START_STATE, END_STATE # pylint:disable=global-statement
|
|
38
|
+
|
|
39
|
+
if START_STATE is None:
|
|
40
|
+
START_STATE = State("START")
|
|
41
|
+
if END_STATE is None:
|
|
42
|
+
END_STATE = State("END")
|
|
43
|
+
|
|
34
44
|
enfa = EpsilonNFA()
|
|
35
45
|
|
|
36
46
|
# print("Converting graph to eNFA")
|