angr 9.2.152__py3-none-win_amd64.whl → 9.2.154__py3-none-win_amd64.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 -3
- angr/analyses/calling_convention/fact_collector.py +8 -14
- angr/analyses/cfg/cfg_base.py +1 -1
- angr/analyses/cfg/cfg_fast.py +40 -1
- angr/analyses/decompiler/ail_simplifier.py +0 -1
- angr/analyses/decompiler/callsite_maker.py +17 -17
- angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +210 -1
- angr/analyses/decompiler/clinic.py +51 -13
- angr/analyses/decompiler/decompilation_cache.py +1 -1
- angr/analyses/decompiler/region_identifier.py +171 -119
- angr/analyses/decompiler/ssailification/ssailification.py +1 -1
- angr/analyses/decompiler/structured_codegen/c.py +15 -15
- angr/analyses/decompiler/structuring/phoenix.py +28 -0
- angr/analyses/decompiler/structuring/structurer_nodes.py +11 -0
- angr/analyses/reaching_definitions/function_handler.py +13 -19
- angr/analyses/smc.py +3 -1
- angr/analyses/stack_pointer_tracker.py +7 -1
- angr/analyses/typehoon/simple_solver.py +143 -81
- angr/analyses/typehoon/typehoon.py +2 -1
- angr/analyses/variable_recovery/engine_ail.py +14 -25
- angr/analyses/variable_recovery/engine_base.py +1 -1
- angr/knowledge_plugins/functions/function.py +10 -4
- angr/lib/angr_native.dll +0 -0
- angr/sim_type.py +11 -70
- angr/utils/types.py +93 -1
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/METADATA +6 -6
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/RECORD +32 -32
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/WHEEL +1 -1
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/entry_points.txt +0 -0
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.152.dist-info → angr-9.2.154.dist-info}/top_level.txt +0 -0
angr/__init__.py
CHANGED
angr/analyses/analysis.py
CHANGED
|
@@ -5,7 +5,7 @@ import sys
|
|
|
5
5
|
import contextlib
|
|
6
6
|
from collections import defaultdict
|
|
7
7
|
from inspect import Signature
|
|
8
|
-
from typing import TYPE_CHECKING, TypeVar, Generic, cast
|
|
8
|
+
from typing import TYPE_CHECKING, TypeVar, Generic, cast, Any
|
|
9
9
|
from collections.abc import Callable
|
|
10
10
|
from types import NoneType
|
|
11
11
|
from itertools import chain
|
|
@@ -111,7 +111,7 @@ class AnalysisLogEntry:
|
|
|
111
111
|
A = TypeVar("A", bound="Analysis")
|
|
112
112
|
|
|
113
113
|
|
|
114
|
-
class AnalysesHub(PluginVendor[
|
|
114
|
+
class AnalysesHub(PluginVendor[Any]):
|
|
115
115
|
"""
|
|
116
116
|
This class contains functions for all the registered and runnable analyses,
|
|
117
117
|
"""
|
|
@@ -123,7 +123,7 @@ class AnalysesHub(PluginVendor[A]):
|
|
|
123
123
|
def _init_plugin(self, plugin_cls: type[A]) -> AnalysisFactory[A]:
|
|
124
124
|
return AnalysisFactory(self.project, plugin_cls)
|
|
125
125
|
|
|
126
|
-
def __getstate__(self):
|
|
126
|
+
def __getstate__(self): # type: ignore[reportIncompatibleMethodOverride]
|
|
127
127
|
s = super().__getstate__()
|
|
128
128
|
return (s, self.project)
|
|
129
129
|
|
|
@@ -6,7 +6,6 @@ from collections import defaultdict
|
|
|
6
6
|
import pyvex
|
|
7
7
|
import claripy
|
|
8
8
|
|
|
9
|
-
from angr import SIM_LIBRARIES, SIM_TYPE_COLLECTIONS
|
|
10
9
|
from angr.utils.bits import s2u, u2s
|
|
11
10
|
from angr.block import Block
|
|
12
11
|
from angr.analyses.analysis import Analysis
|
|
@@ -15,7 +14,8 @@ from angr.knowledge_plugins.functions import Function
|
|
|
15
14
|
from angr.codenode import BlockNode, HookNode
|
|
16
15
|
from angr.engines.light import SimEngineNostmtVEX, SimEngineLight, SpOffset, RegisterOffset
|
|
17
16
|
from angr.calling_conventions import SimRegArg, SimStackArg, default_cc
|
|
18
|
-
from angr.sim_type import SimTypeBottom,
|
|
17
|
+
from angr.sim_type import SimTypeBottom, SimTypeFunction
|
|
18
|
+
from angr.utils.types import dereference_simtype_by_lib
|
|
19
19
|
from .utils import is_sane_register_variable
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
@@ -121,7 +121,7 @@ class SimEngineFactCollectorVEX(
|
|
|
121
121
|
if self.block.vex.jumpkind == "Ijk_Call" and self.arch.ret_offset is not None:
|
|
122
122
|
self.state.register_written(self.arch.ret_offset, self.arch.bytes)
|
|
123
123
|
|
|
124
|
-
def _top(self, bits: int):
|
|
124
|
+
def _top(self, bits: int): # type: ignore
|
|
125
125
|
return None
|
|
126
126
|
|
|
127
127
|
def _is_top(self, expr: Any) -> bool:
|
|
@@ -190,13 +190,13 @@ class SimEngineFactCollectorVEX(
|
|
|
190
190
|
self.state.register_read(expr.offset, bits // self.arch.byte_width)
|
|
191
191
|
return RegisterOffset(bits, expr.offset, 0)
|
|
192
192
|
|
|
193
|
-
def _handle_expr_GetI(self, expr):
|
|
193
|
+
def _handle_expr_GetI(self, expr): # type: ignore
|
|
194
194
|
return None
|
|
195
195
|
|
|
196
|
-
def _handle_expr_ITE(self, expr):
|
|
196
|
+
def _handle_expr_ITE(self, expr): # type: ignore
|
|
197
197
|
return None
|
|
198
198
|
|
|
199
|
-
def _handle_expr_Load(self, expr):
|
|
199
|
+
def _handle_expr_Load(self, expr): # type: ignore
|
|
200
200
|
addr = self._expr(expr.addr)
|
|
201
201
|
if isinstance(addr, SpOffset):
|
|
202
202
|
self.state.stack_read(addr.offset, expr.result_size(self.tyenv) // self.arch.byte_width)
|
|
@@ -206,7 +206,7 @@ class SimEngineFactCollectorVEX(
|
|
|
206
206
|
def _handle_expr_RdTmp(self, expr):
|
|
207
207
|
return self.state.tmps.get(expr.tmp, None)
|
|
208
208
|
|
|
209
|
-
def _handle_expr_VECRET(self, expr):
|
|
209
|
+
def _handle_expr_VECRET(self, expr): # type: ignore
|
|
210
210
|
return None
|
|
211
211
|
|
|
212
212
|
@binop_handler
|
|
@@ -444,13 +444,7 @@ class FactCollector(Analysis):
|
|
|
444
444
|
proto = func_succ.prototype
|
|
445
445
|
if func_succ.prototype_libname is not None:
|
|
446
446
|
# we need to deref the prototype in case it uses SimTypeRef internally
|
|
447
|
-
|
|
448
|
-
for prototype_lib in SIM_LIBRARIES[func_succ.prototype_libname]:
|
|
449
|
-
if prototype_lib.type_collection_names:
|
|
450
|
-
for typelib_name in prototype_lib.type_collection_names:
|
|
451
|
-
type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
|
|
452
|
-
if type_collections:
|
|
453
|
-
proto = dereference_simtype(proto, type_collections)
|
|
447
|
+
proto = dereference_simtype_by_lib(proto, func_succ.prototype_libname)
|
|
454
448
|
|
|
455
449
|
assert isinstance(proto, SimTypeFunction) and proto.returnty is not None
|
|
456
450
|
returnty_size = proto.returnty.with_arch(self.project.arch).size
|
angr/analyses/cfg/cfg_base.py
CHANGED
|
@@ -1515,7 +1515,7 @@ class CFGBase(Analysis):
|
|
|
1515
1515
|
Revisit the entire control flow graph, create Function instances accordingly, and correctly put blocks into
|
|
1516
1516
|
each function.
|
|
1517
1517
|
|
|
1518
|
-
Although Function objects are
|
|
1518
|
+
Although Function objects are created during the CFG recovery, they are neither sound nor accurate. With a
|
|
1519
1519
|
pre-constructed CFG, this method rebuilds all functions bearing the following rules:
|
|
1520
1520
|
|
|
1521
1521
|
- A block may only belong to one function.
|
angr/analyses/cfg/cfg_fast.py
CHANGED
|
@@ -1554,6 +1554,45 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1554
1554
|
}:
|
|
1555
1555
|
func.info["is_alloca_probe"] = True
|
|
1556
1556
|
|
|
1557
|
+
# determine if the function is _guard_xfg_dispatch_icall_nop or _guard_xfg_dispatch_icall_fptr
|
|
1558
|
+
if func is not None and not func.is_simprocedure and len(func.block_addrs_set) in {1, 2}:
|
|
1559
|
+
# _guard_xfg_dispatch_icall_nop jumps to _guard_xfg_dispatch_icall_fptr, but we may or may not identify
|
|
1560
|
+
# _guard_xfg_dispatch_icall_fptr as a separate function.
|
|
1561
|
+
# so, two possibilities:
|
|
1562
|
+
# - _guard_xfg_dispatch_icall_nop is a function with one block and jumps to
|
|
1563
|
+
# _guard_xfg_dispatch_icall_fptr.
|
|
1564
|
+
# - _guard_xfg_dispatch_icall_nop is a function with 2 blocks, and the second block is the body of
|
|
1565
|
+
# _guard_xfg_dispatch_icall_fptr.
|
|
1566
|
+
try:
|
|
1567
|
+
block = func.get_block(func.addr)
|
|
1568
|
+
except SimTranslationError:
|
|
1569
|
+
block = None
|
|
1570
|
+
if block is not None and block.instructions == 1 and len(block.capstone.insns) == 1:
|
|
1571
|
+
insn = block.capstone.insns[0]
|
|
1572
|
+
if block.bytes == b"\xff\xe0":
|
|
1573
|
+
func.info["jmp_rax"] = True
|
|
1574
|
+
elif (
|
|
1575
|
+
insn.mnemonic == "jmp"
|
|
1576
|
+
and insn.operands[0].type == capstone.x86.X86_OP_MEM
|
|
1577
|
+
and insn.operands[0].mem.base == capstone.x86.X86_REG_RIP
|
|
1578
|
+
and insn.operands[0].mem.disp > 0
|
|
1579
|
+
and insn.operands[0].mem.index == 0
|
|
1580
|
+
):
|
|
1581
|
+
# where is it jumping to?
|
|
1582
|
+
jumpout_targets = list(self.graph.successors(self.model.get_any_node(func.addr)))
|
|
1583
|
+
if len(jumpout_targets) == 1:
|
|
1584
|
+
jumpout_target = jumpout_targets[0].addr
|
|
1585
|
+
if len(func.block_addrs_set) == 1 and len(func.jumpout_sites) == 1:
|
|
1586
|
+
if (
|
|
1587
|
+
self.kb.functions.contains_addr(jumpout_target)
|
|
1588
|
+
and self.kb.functions.get_by_addr(jumpout_target).get_block(jumpout_target).bytes
|
|
1589
|
+
== b"\xff\xe0"
|
|
1590
|
+
):
|
|
1591
|
+
func.info["jmp_rax"] = True
|
|
1592
|
+
elif len(func.block_addrs_set) == 2 and func.get_block(jumpout_target).bytes == b"\xff\xe0":
|
|
1593
|
+
# check the second block and ensure it's jmp rax
|
|
1594
|
+
func.info["jmp_rax"] = True
|
|
1595
|
+
|
|
1557
1596
|
elif self.project.arch.name == "X86":
|
|
1558
1597
|
# determine if the function is __alloca_probe
|
|
1559
1598
|
func = self.kb.functions.get_by_addr(func_addr) if self.kb.functions.contains_addr(func_addr) else None
|
|
@@ -1882,7 +1921,7 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
1882
1921
|
if cfg_node is None:
|
|
1883
1922
|
continue
|
|
1884
1923
|
func_addr = cfg_node.function_address
|
|
1885
|
-
if func_addr not in tested_func_addrs:
|
|
1924
|
+
if func_addr not in tested_func_addrs and self.kb.functions.contains_addr(func_addr):
|
|
1886
1925
|
func = self.kb.functions.get_by_addr(func_addr)
|
|
1887
1926
|
if not security_check_cookie_found and is_function_security_check_cookie(
|
|
1888
1927
|
func, self.project, security_cookie_addr
|
|
@@ -203,7 +203,6 @@ class AILSimplifier(Analysis):
|
|
|
203
203
|
AILGraphWalker(self.func_graph, _handler, replace_nodes=True).walk()
|
|
204
204
|
self.blocks = {}
|
|
205
205
|
|
|
206
|
-
@timethis
|
|
207
206
|
def _compute_reaching_definitions(self) -> SRDAModel:
|
|
208
207
|
# Computing reaching definitions or return the cached one
|
|
209
208
|
if self._reaching_definitions is not None:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import Any, TYPE_CHECKING
|
|
2
|
+
from typing import cast, Any, TYPE_CHECKING
|
|
3
3
|
import copy
|
|
4
4
|
import logging
|
|
5
5
|
|
|
@@ -14,7 +14,6 @@ from angr.sim_type import (
|
|
|
14
14
|
SimTypeChar,
|
|
15
15
|
SimTypeInt,
|
|
16
16
|
SimTypeFloat,
|
|
17
|
-
dereference_simtype,
|
|
18
17
|
SimTypeFunction,
|
|
19
18
|
SimTypeLongLong,
|
|
20
19
|
)
|
|
@@ -22,7 +21,7 @@ from angr.calling_conventions import SimReferenceArgument, SimRegArg, SimStackAr
|
|
|
22
21
|
from angr.knowledge_plugins.key_definitions.constants import OP_BEFORE
|
|
23
22
|
from angr.analyses import Analysis, register_analysis
|
|
24
23
|
from angr.analyses.s_reaching_definitions import SRDAView
|
|
25
|
-
from angr import
|
|
24
|
+
from angr.utils.types import dereference_simtype_by_lib
|
|
26
25
|
|
|
27
26
|
if TYPE_CHECKING:
|
|
28
27
|
from angr.knowledge_plugins.functions import Function
|
|
@@ -37,12 +36,14 @@ class CallSiteMaker(Analysis):
|
|
|
37
36
|
Add calling convention, declaration, and args to a call site.
|
|
38
37
|
"""
|
|
39
38
|
|
|
40
|
-
def __init__(
|
|
39
|
+
def __init__(
|
|
40
|
+
self, block, reaching_definitions=None, stack_pointer_tracker=None, ail_manager: Manager | None = None
|
|
41
|
+
):
|
|
41
42
|
self.block = block
|
|
42
43
|
|
|
43
44
|
self._reaching_definitions = reaching_definitions
|
|
44
45
|
self._stack_pointer_tracker = stack_pointer_tracker
|
|
45
|
-
self._ail_manager = ail_manager
|
|
46
|
+
self._ail_manager: Manager | None = ail_manager
|
|
46
47
|
|
|
47
48
|
self.result_block = None
|
|
48
49
|
self.stack_arg_offsets: set[tuple[int, int]] | None = None # call ins addr, stack_offset
|
|
@@ -109,16 +110,8 @@ class CallSiteMaker(Analysis):
|
|
|
109
110
|
# make sure the function prototype is resolved.
|
|
110
111
|
# TODO: Cache resolved function prototypes globally
|
|
111
112
|
prototype_libname = func.prototype_libname
|
|
112
|
-
type_collections = []
|
|
113
113
|
if prototype_libname is not None:
|
|
114
|
-
|
|
115
|
-
if prototype_lib.type_collection_names:
|
|
116
|
-
for typelib_name in prototype_lib.type_collection_names:
|
|
117
|
-
type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
|
|
118
|
-
if type_collections:
|
|
119
|
-
prototype = dereference_simtype(prototype, type_collections).with_arch( # type: ignore
|
|
120
|
-
self.project.arch
|
|
121
|
-
)
|
|
114
|
+
prototype = cast(SimTypeFunction, dereference_simtype_by_lib(prototype, prototype_libname))
|
|
122
115
|
|
|
123
116
|
args = []
|
|
124
117
|
arg_vvars = []
|
|
@@ -151,6 +144,10 @@ class CallSiteMaker(Analysis):
|
|
|
151
144
|
# across registers). most importantly, a ComboArg represents one variable, not multiple, but we
|
|
152
145
|
# have no way to know that until later down the pipeline.
|
|
153
146
|
expanded_arg_locs += arg_loc.locations
|
|
147
|
+
elif isinstance(arg_loc, SimStructArg):
|
|
148
|
+
expanded_arg_locs += [ # type: ignore
|
|
149
|
+
arg_loc.locs[field_name] for field_name in arg_loc.struct.fields if field_name in arg_loc.locs
|
|
150
|
+
]
|
|
154
151
|
elif isinstance(arg_loc, (SimRegArg, SimStackArg, SimReferenceArgument)):
|
|
155
152
|
expanded_arg_locs.append(arg_loc)
|
|
156
153
|
else:
|
|
@@ -195,12 +192,15 @@ class CallSiteMaker(Analysis):
|
|
|
195
192
|
if vvar_def_reg_offset is not None and offset > vvar_def_reg_offset:
|
|
196
193
|
# we need to shift the value
|
|
197
194
|
vvar_use = Expr.BinaryOp(
|
|
198
|
-
self._ail_manager.next_atom(),
|
|
195
|
+
self._ail_manager.next_atom() if self._ail_manager is not None else None,
|
|
199
196
|
"Shr",
|
|
200
197
|
[
|
|
201
198
|
vvar_use,
|
|
202
199
|
Expr.Const(
|
|
203
|
-
self._ail_manager.next_atom()
|
|
200
|
+
self._ail_manager.next_atom() if self._ail_manager is not None else None,
|
|
201
|
+
None,
|
|
202
|
+
(offset - vvar_def_reg_offset) * 8,
|
|
203
|
+
8,
|
|
204
204
|
),
|
|
205
205
|
],
|
|
206
206
|
**vvar_use.tags,
|
|
@@ -208,7 +208,7 @@ class CallSiteMaker(Analysis):
|
|
|
208
208
|
if vvar_def.size > arg_loc.size:
|
|
209
209
|
# we need to narrow the value
|
|
210
210
|
vvar_use = Expr.Convert(
|
|
211
|
-
self._ail_manager.next_atom(),
|
|
211
|
+
self._ail_manager.next_atom() if self._ail_manager is not None else None,
|
|
212
212
|
vvar_use.bits,
|
|
213
213
|
arg_loc.size * self.project.arch.byte_width,
|
|
214
214
|
False,
|
|
@@ -15,6 +15,23 @@ X86_CondBitOffsets = data["X86"]["CondBitOffsets"]
|
|
|
15
15
|
class X86CCallRewriter(CCallRewriterBase):
|
|
16
16
|
"""
|
|
17
17
|
Implements VEX ccall rewriter for X86.
|
|
18
|
+
|
|
19
|
+
From libVEX:
|
|
20
|
+
|
|
21
|
+
A summary of the field usages is:
|
|
22
|
+
|
|
23
|
+
Operation DEP1 DEP2 NDEP
|
|
24
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
25
|
+
|
|
26
|
+
add/sub/mul first arg second arg unused
|
|
27
|
+
adc/sbb first arg (second arg)
|
|
28
|
+
XOR old_carry old_carry
|
|
29
|
+
and/or/xor result zero unused
|
|
30
|
+
inc/dec result zero old_carry
|
|
31
|
+
shl/shr/sar result subshifted- unused
|
|
32
|
+
result
|
|
33
|
+
rol/ror result zero old_flags
|
|
34
|
+
copy old_flags zero unused.
|
|
18
35
|
"""
|
|
19
36
|
|
|
20
37
|
__slots__ = ()
|
|
@@ -28,7 +45,7 @@ class X86CCallRewriter(CCallRewriterBase):
|
|
|
28
45
|
if isinstance(cond, Expr.Const) and isinstance(op, Expr.Const):
|
|
29
46
|
cond_v = cond.value
|
|
30
47
|
op_v = op.value
|
|
31
|
-
if cond_v == X86_CondTypes["CondLE"]:
|
|
48
|
+
if cond_v == X86_CondTypes["CondLE"]:
|
|
32
49
|
if op_v in {
|
|
33
50
|
X86_OpTypes["G_CC_OP_SUBB"],
|
|
34
51
|
X86_OpTypes["G_CC_OP_SUBW"],
|
|
@@ -52,6 +69,198 @@ class X86CCallRewriter(CCallRewriterBase):
|
|
|
52
69
|
|
|
53
70
|
r = Expr.BinaryOp(ccall.idx, "CmpLE", (dep_1, dep_2), True, **ccall.tags)
|
|
54
71
|
return Expr.Convert(None, r.bits, ccall.bits, False, r, **ccall.tags)
|
|
72
|
+
elif cond_v == X86_CondTypes["CondO"]:
|
|
73
|
+
op_v = op.value
|
|
74
|
+
ret_cond = None
|
|
75
|
+
if op_v in {
|
|
76
|
+
X86_OpTypes["G_CC_OP_UMULB"],
|
|
77
|
+
X86_OpTypes["G_CC_OP_UMULW"],
|
|
78
|
+
X86_OpTypes["G_CC_OP_UMULL"],
|
|
79
|
+
}:
|
|
80
|
+
# dep_1 * dep_2 >= max_signed_byte/word/dword
|
|
81
|
+
ret = Expr.BinaryOp(
|
|
82
|
+
None,
|
|
83
|
+
"Mul",
|
|
84
|
+
(dep_1, dep_2),
|
|
85
|
+
bits=dep_1.bits * 2,
|
|
86
|
+
**ccall.tags,
|
|
87
|
+
)
|
|
88
|
+
max_signed = Expr.Const(
|
|
89
|
+
None,
|
|
90
|
+
None,
|
|
91
|
+
(1 << (dep_1.bits - 1)),
|
|
92
|
+
bits=dep_1.bits * 2,
|
|
93
|
+
**ccall.tags,
|
|
94
|
+
)
|
|
95
|
+
ret_cond = Expr.BinaryOp(None, "CmpGE", (ret, max_signed), signed=False, **ccall.tags)
|
|
96
|
+
if op_v in {
|
|
97
|
+
X86_OpTypes["G_CC_OP_INCB"],
|
|
98
|
+
X86_OpTypes["G_CC_OP_INCW"],
|
|
99
|
+
X86_OpTypes["G_CC_OP_INCL"],
|
|
100
|
+
}:
|
|
101
|
+
# dep_1 is the result
|
|
102
|
+
overflowed = Expr.Const(
|
|
103
|
+
None,
|
|
104
|
+
None,
|
|
105
|
+
1 << (dep_1.bits - 1),
|
|
106
|
+
dep_1.bits,
|
|
107
|
+
**ccall.tags,
|
|
108
|
+
)
|
|
109
|
+
ret_cond = Expr.BinaryOp(None, "CmpEQ", (dep_1, overflowed), **ccall.tags)
|
|
110
|
+
|
|
111
|
+
if ret_cond is not None:
|
|
112
|
+
return Expr.ITE(
|
|
113
|
+
ccall.idx,
|
|
114
|
+
ret_cond,
|
|
115
|
+
Expr.Const(None, None, 0, 1, **ccall.tags),
|
|
116
|
+
Expr.Const(None, None, 1, 1, **ccall.tags),
|
|
117
|
+
**ccall.tags,
|
|
118
|
+
)
|
|
119
|
+
elif cond_v == X86_CondTypes["CondZ"]:
|
|
120
|
+
op_v = op.value
|
|
121
|
+
if op_v in {
|
|
122
|
+
X86_OpTypes["G_CC_OP_ADDB"],
|
|
123
|
+
X86_OpTypes["G_CC_OP_ADDW"],
|
|
124
|
+
X86_OpTypes["G_CC_OP_ADDL"],
|
|
125
|
+
}:
|
|
126
|
+
# dep_1 + dep_2 == 0
|
|
127
|
+
ret = Expr.BinaryOp(
|
|
128
|
+
None,
|
|
129
|
+
"Add",
|
|
130
|
+
(dep_1, dep_2),
|
|
131
|
+
bits=dep_1.bits,
|
|
132
|
+
**ccall.tags,
|
|
133
|
+
)
|
|
134
|
+
zero = Expr.Const(
|
|
135
|
+
None,
|
|
136
|
+
None,
|
|
137
|
+
0,
|
|
138
|
+
dep_1.bits,
|
|
139
|
+
**ccall.tags,
|
|
140
|
+
)
|
|
141
|
+
return Expr.BinaryOp(
|
|
142
|
+
ccall.idx,
|
|
143
|
+
"CmpEQ",
|
|
144
|
+
(ret, zero),
|
|
145
|
+
True,
|
|
146
|
+
**ccall.tags,
|
|
147
|
+
)
|
|
148
|
+
if op_v in {
|
|
149
|
+
X86_OpTypes["G_CC_OP_SUBB"],
|
|
150
|
+
X86_OpTypes["G_CC_OP_SUBW"],
|
|
151
|
+
X86_OpTypes["G_CC_OP_SUBL"],
|
|
152
|
+
}:
|
|
153
|
+
# dep_1 - dep_2 == 0
|
|
154
|
+
return Expr.BinaryOp(
|
|
155
|
+
ccall.idx,
|
|
156
|
+
"CmpEQ",
|
|
157
|
+
(dep_1, dep_2),
|
|
158
|
+
True,
|
|
159
|
+
**ccall.tags,
|
|
160
|
+
)
|
|
161
|
+
if op_v in {
|
|
162
|
+
X86_OpTypes["G_CC_OP_LOGICB"],
|
|
163
|
+
X86_OpTypes["G_CC_OP_LOGICW"],
|
|
164
|
+
X86_OpTypes["G_CC_OP_LOGICL"],
|
|
165
|
+
}:
|
|
166
|
+
# dep_1 == 0
|
|
167
|
+
return Expr.BinaryOp(
|
|
168
|
+
ccall.idx,
|
|
169
|
+
"CmpEQ",
|
|
170
|
+
(dep_1, Expr.Const(None, None, 0, dep_1.bits, **ccall.tags)),
|
|
171
|
+
True,
|
|
172
|
+
**ccall.tags,
|
|
173
|
+
)
|
|
174
|
+
elif cond_v == X86_CondTypes["CondL"]:
|
|
175
|
+
op_v = op.value
|
|
176
|
+
if op_v in {
|
|
177
|
+
X86_OpTypes["G_CC_OP_SUBB"],
|
|
178
|
+
X86_OpTypes["G_CC_OP_SUBW"],
|
|
179
|
+
X86_OpTypes["G_CC_OP_SUBL"],
|
|
180
|
+
}:
|
|
181
|
+
# dep_1 - dep_2 < 0
|
|
182
|
+
return Expr.BinaryOp(
|
|
183
|
+
ccall.idx,
|
|
184
|
+
"CmpLT",
|
|
185
|
+
(dep_1, dep_2),
|
|
186
|
+
True,
|
|
187
|
+
**ccall.tags,
|
|
188
|
+
)
|
|
189
|
+
if op_v in {
|
|
190
|
+
X86_OpTypes["G_CC_OP_LOGICB"],
|
|
191
|
+
X86_OpTypes["G_CC_OP_LOGICW"],
|
|
192
|
+
X86_OpTypes["G_CC_OP_LOGICL"],
|
|
193
|
+
}:
|
|
194
|
+
# dep_1 < 0
|
|
195
|
+
return Expr.BinaryOp(
|
|
196
|
+
ccall.idx,
|
|
197
|
+
"CmpLT",
|
|
198
|
+
(dep_1, Expr.Const(None, None, 0, dep_1.bits, **ccall.tags)),
|
|
199
|
+
True,
|
|
200
|
+
**ccall.tags,
|
|
201
|
+
)
|
|
202
|
+
elif cond_v in {
|
|
203
|
+
X86_CondTypes["CondBE"],
|
|
204
|
+
X86_CondTypes["CondB"],
|
|
205
|
+
}:
|
|
206
|
+
op_v = op.value
|
|
207
|
+
if op_v in {
|
|
208
|
+
X86_OpTypes["G_CC_OP_ADDB"],
|
|
209
|
+
X86_OpTypes["G_CC_OP_ADDW"],
|
|
210
|
+
X86_OpTypes["G_CC_OP_ADDL"],
|
|
211
|
+
}:
|
|
212
|
+
# dep_1 + dep_2 <= 0 if CondBE
|
|
213
|
+
# dep_1 + dep_2 < 0 if CondB
|
|
214
|
+
ret = Expr.BinaryOp(
|
|
215
|
+
None,
|
|
216
|
+
"Add",
|
|
217
|
+
(dep_1, dep_2),
|
|
218
|
+
signed=False,
|
|
219
|
+
bits=dep_1.bits,
|
|
220
|
+
**ccall.tags,
|
|
221
|
+
)
|
|
222
|
+
zero = Expr.Const(
|
|
223
|
+
None,
|
|
224
|
+
None,
|
|
225
|
+
0,
|
|
226
|
+
dep_1.bits,
|
|
227
|
+
**ccall.tags,
|
|
228
|
+
)
|
|
229
|
+
return Expr.BinaryOp(
|
|
230
|
+
ccall.idx,
|
|
231
|
+
"CmpLE" if cond_v == X86_CondTypes["CondBE"] else "CmpLT",
|
|
232
|
+
(ret, zero),
|
|
233
|
+
False,
|
|
234
|
+
**ccall.tags,
|
|
235
|
+
)
|
|
236
|
+
if op_v in {
|
|
237
|
+
X86_OpTypes["G_CC_OP_SUBB"],
|
|
238
|
+
X86_OpTypes["G_CC_OP_SUBW"],
|
|
239
|
+
X86_OpTypes["G_CC_OP_SUBL"],
|
|
240
|
+
}:
|
|
241
|
+
# dep_1 <= dep_2 if CondBE
|
|
242
|
+
# dep_1 < dep_2 if CondB
|
|
243
|
+
return Expr.BinaryOp(
|
|
244
|
+
ccall.idx,
|
|
245
|
+
"CmpLE" if cond_v == X86_CondTypes["CondBE"] else "CmpLT",
|
|
246
|
+
(dep_1, dep_2),
|
|
247
|
+
False,
|
|
248
|
+
**ccall.tags,
|
|
249
|
+
)
|
|
250
|
+
if op_v in {
|
|
251
|
+
X86_OpTypes["G_CC_OP_LOGICB"],
|
|
252
|
+
X86_OpTypes["G_CC_OP_LOGICW"],
|
|
253
|
+
X86_OpTypes["G_CC_OP_LOGICL"],
|
|
254
|
+
}:
|
|
255
|
+
# dep_1 <= 0 if CondBE
|
|
256
|
+
# dep_1 < 0 if CondB
|
|
257
|
+
return Expr.BinaryOp(
|
|
258
|
+
ccall.idx,
|
|
259
|
+
"CmpLE" if cond_v == X86_CondTypes["CondBE"] else "CmpLT",
|
|
260
|
+
(dep_1, Expr.Const(None, None, 0, dep_1.bits, **ccall.tags)),
|
|
261
|
+
False,
|
|
262
|
+
**ccall.tags,
|
|
263
|
+
)
|
|
55
264
|
return None
|
|
56
265
|
|
|
57
266
|
@staticmethod
|
|
@@ -12,7 +12,6 @@ import networkx
|
|
|
12
12
|
import capstone
|
|
13
13
|
|
|
14
14
|
import ailment
|
|
15
|
-
from angr import SIM_LIBRARIES, SIM_TYPE_COLLECTIONS
|
|
16
15
|
|
|
17
16
|
from angr.errors import AngrDecompilationError
|
|
18
17
|
from angr.knowledge_base import KnowledgeBase
|
|
@@ -22,9 +21,9 @@ from angr.knowledge_plugins.key_definitions import atoms
|
|
|
22
21
|
from angr.codenode import BlockNode
|
|
23
22
|
from angr.utils import timethis
|
|
24
23
|
from angr.utils.graph import GraphUtils
|
|
24
|
+
from angr.utils.types import dereference_simtype_by_lib
|
|
25
25
|
from angr.calling_conventions import SimRegArg, SimStackArg, SimFunctionArgument
|
|
26
26
|
from angr.sim_type import (
|
|
27
|
-
dereference_simtype,
|
|
28
27
|
SimTypeChar,
|
|
29
28
|
SimTypeInt,
|
|
30
29
|
SimTypeLongLong,
|
|
@@ -485,6 +484,8 @@ class Clinic(Analysis):
|
|
|
485
484
|
|
|
486
485
|
# duplicate orphaned conditional jump blocks
|
|
487
486
|
ail_graph = self._duplicate_orphaned_cond_jumps(ail_graph)
|
|
487
|
+
# rewrite jmp_rax function calls
|
|
488
|
+
ail_graph = self._rewrite_jump_rax_calls(ail_graph)
|
|
488
489
|
|
|
489
490
|
# Transform the graph into partial SSA form
|
|
490
491
|
self._update_progress(35.0, text="Transforming to partial-SSA form")
|
|
@@ -930,7 +931,7 @@ class Clinic(Analysis):
|
|
|
930
931
|
self.kb.callsite_prototypes.set_prototype(callsite.addr, cc.cc, cc.prototype, manual=False)
|
|
931
932
|
if func_graph is not None and cc.prototype.returnty is not None:
|
|
932
933
|
# patch the AIL call statement if we can find one
|
|
933
|
-
callsite_ail_block: ailment.Block = next(
|
|
934
|
+
callsite_ail_block: ailment.Block | None = next(
|
|
934
935
|
iter(bb for bb in func_graph if bb.addr == callsite.addr), None
|
|
935
936
|
)
|
|
936
937
|
if callsite_ail_block is not None and callsite_ail_block.statements:
|
|
@@ -1005,6 +1006,7 @@ class Clinic(Analysis):
|
|
|
1005
1006
|
:return: None
|
|
1006
1007
|
"""
|
|
1007
1008
|
assert self._func_graph is not None
|
|
1009
|
+
assert self._blocks_by_addr_and_size is not None
|
|
1008
1010
|
|
|
1009
1011
|
for block_node in self._func_graph.nodes():
|
|
1010
1012
|
ail_block = self._convert(block_node)
|
|
@@ -1237,16 +1239,8 @@ class Clinic(Analysis):
|
|
|
1237
1239
|
# make sure the function prototype is resolved.
|
|
1238
1240
|
# TODO: Cache resolved function prototypes globally
|
|
1239
1241
|
prototype_libname = func.prototype_libname
|
|
1240
|
-
type_collections = []
|
|
1241
1242
|
if prototype_libname is not None:
|
|
1242
|
-
|
|
1243
|
-
if prototype_lib.type_collection_names:
|
|
1244
|
-
for typelib_name in prototype_lib.type_collection_names:
|
|
1245
|
-
type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
|
|
1246
|
-
if type_collections:
|
|
1247
|
-
prototype = dereference_simtype(prototype, type_collections).with_arch( # type: ignore
|
|
1248
|
-
self.project.arch
|
|
1249
|
-
)
|
|
1243
|
+
prototype = dereference_simtype_by_lib(prototype, prototype_libname)
|
|
1250
1244
|
|
|
1251
1245
|
if cc is None:
|
|
1252
1246
|
l.warning("Call site %#x (callee %s) has an unknown calling convention.", block.addr, repr(func))
|
|
@@ -1538,6 +1532,7 @@ class Clinic(Analysis):
|
|
|
1538
1532
|
vvar_id_start=self.vvar_id_start,
|
|
1539
1533
|
)
|
|
1540
1534
|
self.vvar_id_start = ssailification.max_vvar_id + 1
|
|
1535
|
+
assert ssailification.out_graph is not None
|
|
1541
1536
|
return ssailification.out_graph
|
|
1542
1537
|
|
|
1543
1538
|
@timethis
|
|
@@ -1918,6 +1913,7 @@ class Clinic(Analysis):
|
|
|
1918
1913
|
self._link_variables_on_call(variable_manager, global_variables, block, stmt_idx, stmt, is_expr=False)
|
|
1919
1914
|
|
|
1920
1915
|
elif stmt_type is ailment.Stmt.Return:
|
|
1916
|
+
assert isinstance(stmt, ailment.Stmt.Return)
|
|
1921
1917
|
self._link_variables_on_return(variable_manager, global_variables, block, stmt_idx, stmt)
|
|
1922
1918
|
|
|
1923
1919
|
def _link_variables_on_return(
|
|
@@ -2178,6 +2174,47 @@ class Clinic(Analysis):
|
|
|
2178
2174
|
|
|
2179
2175
|
return ail_graph
|
|
2180
2176
|
|
|
2177
|
+
def _rewrite_jump_rax_calls(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
2178
|
+
"""
|
|
2179
|
+
Rewrite calls to special functions (e.g., guard_dispatch_icall_nop) into `call rax`.
|
|
2180
|
+
"""
|
|
2181
|
+
|
|
2182
|
+
if self.project.arch.name != "AMD64":
|
|
2183
|
+
return ail_graph
|
|
2184
|
+
if self._cfg is None:
|
|
2185
|
+
return ail_graph
|
|
2186
|
+
|
|
2187
|
+
for block in ail_graph:
|
|
2188
|
+
if not block.statements:
|
|
2189
|
+
continue
|
|
2190
|
+
assert block.addr is not None
|
|
2191
|
+
last_stmt = block.statements[-1]
|
|
2192
|
+
if isinstance(last_stmt, ailment.Stmt.Call):
|
|
2193
|
+
# we can't examine the call target at this point because constant propagation hasn't run yet; we consult
|
|
2194
|
+
# the CFG instead
|
|
2195
|
+
callsite_node = self._cfg.get_any_node(block.addr, anyaddr=True)
|
|
2196
|
+
if callsite_node is None:
|
|
2197
|
+
break
|
|
2198
|
+
callees = self._cfg.get_successors(callsite_node, jumpkind="Ijk_Call")
|
|
2199
|
+
if len(callees) != 1:
|
|
2200
|
+
break
|
|
2201
|
+
callee = callees[0].addr
|
|
2202
|
+
if self.kb.functions.contains_addr(callee):
|
|
2203
|
+
callee_func = self.kb.functions.get_by_addr(callee)
|
|
2204
|
+
if callee_func.info.get("jmp_rax", False) is True:
|
|
2205
|
+
# rewrite this statement into Call(rax)
|
|
2206
|
+
call_stmt = last_stmt.copy()
|
|
2207
|
+
call_stmt.target = ailment.Expr.Register(
|
|
2208
|
+
self._ail_manager.next_atom(),
|
|
2209
|
+
None,
|
|
2210
|
+
self.project.arch.registers["rax"][0],
|
|
2211
|
+
64,
|
|
2212
|
+
ins_addr=call_stmt.ins_addr,
|
|
2213
|
+
)
|
|
2214
|
+
block.statements[-1] = call_stmt
|
|
2215
|
+
|
|
2216
|
+
return ail_graph
|
|
2217
|
+
|
|
2181
2218
|
def _rewrite_ite_expressions(self, ail_graph):
|
|
2182
2219
|
cfg = self._cfg
|
|
2183
2220
|
for block in list(ail_graph):
|
|
@@ -2208,6 +2245,7 @@ class Clinic(Analysis):
|
|
|
2208
2245
|
def _create_triangle_for_ite_expression(self, ail_graph, block_addr: int, ite_ins_addr: int):
|
|
2209
2246
|
ite_insn_only_block = self.project.factory.block(ite_ins_addr, num_inst=1)
|
|
2210
2247
|
ite_insn_size = ite_insn_only_block.size
|
|
2248
|
+
assert ite_insn_size is not None
|
|
2211
2249
|
if ite_insn_size <= 2: # we need an address for true_block and another address for false_block
|
|
2212
2250
|
return None
|
|
2213
2251
|
if ite_insn_only_block.vex.exit_statements:
|
|
@@ -3161,7 +3199,7 @@ class Clinic(Analysis):
|
|
|
3161
3199
|
)
|
|
3162
3200
|
break
|
|
3163
3201
|
|
|
3164
|
-
if alloca_node is not None:
|
|
3202
|
+
if alloca_node is not None and sp_equal_to is not None:
|
|
3165
3203
|
stmt0 = alloca_node.statements[1]
|
|
3166
3204
|
statements = [ailment.Stmt.Call(stmt0.idx, "alloca", args=[sp_equal_to], **stmt0.tags)]
|
|
3167
3205
|
new_node = ailment.Block(alloca_node.addr, alloca_node.original_size, statements=statements)
|
|
@@ -31,7 +31,7 @@ class DecompilationCache:
|
|
|
31
31
|
self.parameters: dict[str, Any] = {}
|
|
32
32
|
self.addr = addr
|
|
33
33
|
self.type_constraints: dict[TypeVariable, set[TypeConstraint]] | None = None
|
|
34
|
-
self.func_typevar = None
|
|
34
|
+
self.func_typevar: TypeVariable | None = None
|
|
35
35
|
self.var_to_typevar: dict | None = None
|
|
36
36
|
self.codegen: BaseStructuredCodeGenerator | None = None
|
|
37
37
|
self.clinic: Clinic | None = None
|