angr 9.2.125__py3-none-win_amd64.whl → 9.2.127__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/__init__.py +4 -0
- angr/analyses/analysis.py +8 -2
- angr/analyses/cfg/cfg_fast.py +12 -1
- angr/analyses/decompiler/ail_simplifier.py +1 -0
- angr/analyses/decompiler/callsite_maker.py +9 -1
- angr/analyses/decompiler/clinic.py +2 -1
- angr/analyses/decompiler/condition_processor.py +109 -73
- angr/analyses/decompiler/decompilation_cache.py +4 -0
- angr/analyses/decompiler/decompiler.py +21 -3
- angr/analyses/decompiler/dephication/graph_vvar_mapping.py +1 -2
- angr/analyses/decompiler/optimization_passes/__init__.py +15 -1
- angr/analyses/decompiler/return_maker.py +1 -0
- angr/analyses/decompiler/ssailification/rewriting.py +4 -0
- angr/analyses/decompiler/ssailification/rewriting_engine.py +10 -3
- angr/analyses/decompiler/ssailification/traversal.py +1 -0
- angr/analyses/decompiler/ssailification/traversal_engine.py +15 -0
- angr/analyses/decompiler/structured_codegen/c.py +18 -5
- angr/analyses/decompiler/structured_codegen/dwarf_import.py +4 -1
- angr/analyses/deobfuscator/__init__.py +18 -0
- angr/analyses/deobfuscator/api_obf_finder.py +313 -0
- angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +51 -0
- angr/analyses/deobfuscator/irsb_reg_collector.py +85 -0
- angr/analyses/deobfuscator/string_obf_finder.py +774 -0
- angr/analyses/deobfuscator/string_obf_opt_passes.py +133 -0
- angr/analyses/deobfuscator/string_obf_peephole_optimizer.py +47 -0
- angr/analyses/reaching_definitions/function_handler_library/stdio.py +8 -1
- angr/analyses/reaching_definitions/function_handler_library/string.py +2 -2
- angr/analyses/s_liveness.py +3 -3
- angr/analyses/s_propagator.py +74 -3
- angr/analyses/unpacker/__init__.py +6 -0
- angr/analyses/unpacker/obfuscation_detector.py +103 -0
- angr/analyses/unpacker/packing_detector.py +138 -0
- angr/angrdb/models.py +2 -1
- angr/angrdb/serializers/kb.py +3 -3
- angr/angrdb/serializers/structured_code.py +5 -3
- angr/calling_conventions.py +4 -2
- angr/engines/vex/claripy/irop.py +10 -5
- angr/knowledge_base.py +1 -1
- angr/knowledge_plugins/__init__.py +2 -2
- angr/knowledge_plugins/obfuscations.py +36 -0
- angr/knowledge_plugins/structured_code.py +1 -1
- angr/lib/angr_native.dll +0 -0
- angr/utils/ssa/__init__.py +8 -3
- {angr-9.2.125.dist-info → angr-9.2.127.dist-info}/METADATA +6 -6
- {angr-9.2.125.dist-info → angr-9.2.127.dist-info}/RECORD +50 -40
- {angr-9.2.125.dist-info → angr-9.2.127.dist-info}/WHEEL +1 -1
- angr/knowledge_plugins/decompilation.py +0 -45
- {angr-9.2.125.dist-info → angr-9.2.127.dist-info}/LICENSE +0 -0
- {angr-9.2.125.dist-info → angr-9.2.127.dist-info}/entry_points.txt +0 -0
- {angr-9.2.125.dist-info → angr-9.2.127.dist-info}/top_level.txt +0 -0
angr/__init__.py
CHANGED
angr/analyses/__init__.py
CHANGED
|
@@ -53,6 +53,8 @@ from .codecave import CodeCaveAnalysis
|
|
|
53
53
|
from .patchfinder import PatchFinderAnalysis
|
|
54
54
|
from .pathfinder import Pathfinder
|
|
55
55
|
from .smc import SelfModifyingCodeAnalysis
|
|
56
|
+
from .unpacker import PackingDetector
|
|
57
|
+
from . import deobfuscator
|
|
56
58
|
|
|
57
59
|
|
|
58
60
|
__all__ = (
|
|
@@ -109,4 +111,6 @@ __all__ = (
|
|
|
109
111
|
"PatchFinderAnalysis",
|
|
110
112
|
"Pathfinder",
|
|
111
113
|
"SelfModifyingCodeAnalysis",
|
|
114
|
+
"PackingDetector",
|
|
115
|
+
"deobfuscator",
|
|
112
116
|
)
|
angr/analyses/analysis.py
CHANGED
|
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, TypeVar, Generic, cast
|
|
|
9
9
|
from collections.abc import Callable
|
|
10
10
|
from types import NoneType
|
|
11
11
|
from itertools import chain
|
|
12
|
+
from traceback import format_exception
|
|
12
13
|
|
|
13
14
|
import logging
|
|
14
15
|
import time
|
|
@@ -75,6 +76,11 @@ class AnalysisLogEntry:
|
|
|
75
76
|
|
|
76
77
|
self.message = message
|
|
77
78
|
|
|
79
|
+
def format(self) -> str:
|
|
80
|
+
if self.exc_traceback is None:
|
|
81
|
+
return self.message
|
|
82
|
+
return "\n".join((*format_exception(self.exc_type, self.exc_value, self.exc_traceback), "", self.message))
|
|
83
|
+
|
|
78
84
|
def __getstate__(self):
|
|
79
85
|
return (
|
|
80
86
|
str(self.__dict__.get("exc_type")),
|
|
@@ -281,8 +287,8 @@ class Analysis:
|
|
|
281
287
|
kb: KnowledgeBase
|
|
282
288
|
_fail_fast: bool
|
|
283
289
|
_name: str
|
|
284
|
-
errors = []
|
|
285
|
-
named_errors = defaultdict(list)
|
|
290
|
+
errors: list[AnalysisLogEntry] = []
|
|
291
|
+
named_errors: defaultdict[str, list[AnalysisLogEntry]] = defaultdict(list)
|
|
286
292
|
_progress_callback = None
|
|
287
293
|
_show_progressbar = False
|
|
288
294
|
_progressbar = None
|
angr/analyses/cfg/cfg_fast.py
CHANGED
|
@@ -3309,7 +3309,18 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
|
|
|
3309
3309
|
# this is not a no-op block. Determine where nop instructions terminate.
|
|
3310
3310
|
insns = block.capstone.insns
|
|
3311
3311
|
if insns:
|
|
3312
|
-
|
|
3312
|
+
if (
|
|
3313
|
+
self.project.simos is not None
|
|
3314
|
+
and self.project.simos.name == "Win32"
|
|
3315
|
+
and insns[0].mnemonic == "mov"
|
|
3316
|
+
):
|
|
3317
|
+
op0, op1 = insns[0].operands
|
|
3318
|
+
if op0.type == 1 and op1.type == 1 and op0.reg == op1.reg:
|
|
3319
|
+
# hot-patch points on Windows DLLs
|
|
3320
|
+
# https://devblogs.microsoft.com/oldnewthing/20110921-00/?p=9583
|
|
3321
|
+
nop_length = None
|
|
3322
|
+
else:
|
|
3323
|
+
nop_length = self._get_nop_length(insns)
|
|
3313
3324
|
|
|
3314
3325
|
if nop_length is None or nop_length <= 0:
|
|
3315
3326
|
continue
|
|
@@ -872,6 +872,7 @@ class AILSimplifier(Analysis):
|
|
|
872
872
|
Const(None, None, eq.atom0.addr, self.project.arch.bits),
|
|
873
873
|
eq.atom0.size,
|
|
874
874
|
endness=self.project.arch.memory_endness,
|
|
875
|
+
**eq.atom1.tags,
|
|
875
876
|
)
|
|
876
877
|
elif isinstance(eq.atom0, VirtualVariable) and eq.atom0.was_reg:
|
|
877
878
|
if isinstance(eq.atom1, VirtualVariable) and eq.atom1.was_reg:
|
|
@@ -157,7 +157,15 @@ class CallSiteMaker(Analysis):
|
|
|
157
157
|
)
|
|
158
158
|
args.append(vvar_use)
|
|
159
159
|
else:
|
|
160
|
-
|
|
160
|
+
reg = Expr.Register(
|
|
161
|
+
self._atom_idx(),
|
|
162
|
+
None,
|
|
163
|
+
offset,
|
|
164
|
+
size * 8,
|
|
165
|
+
reg_name=arg_loc.reg_name,
|
|
166
|
+
ins_addr=last_stmt.ins_addr,
|
|
167
|
+
)
|
|
168
|
+
args.append(reg)
|
|
161
169
|
elif isinstance(arg_loc, SimStackArg):
|
|
162
170
|
stack_arg_locs.append(arg_loc)
|
|
163
171
|
_, the_arg = self._resolve_stack_argument(call_stmt, arg_loc)
|
|
@@ -1043,6 +1043,7 @@ class Clinic(Analysis):
|
|
|
1043
1043
|
reg_name=self.project.arch.translate_register_name(
|
|
1044
1044
|
ret_reg_offset, size=self.project.arch.bits
|
|
1045
1045
|
),
|
|
1046
|
+
**target.tags,
|
|
1046
1047
|
)
|
|
1047
1048
|
call_stmt = ailment.Stmt.Call(
|
|
1048
1049
|
None,
|
|
@@ -1812,7 +1813,7 @@ class Clinic(Analysis):
|
|
|
1812
1813
|
s = self.kb.custom_strings[expr.value]
|
|
1813
1814
|
expr.tags["reference_values"] = {
|
|
1814
1815
|
SimTypePointer(SimTypeChar().with_arch(self.project.arch)).with_arch(self.project.arch): s.decode(
|
|
1815
|
-
"
|
|
1816
|
+
"latin-1"
|
|
1816
1817
|
),
|
|
1817
1818
|
}
|
|
1818
1819
|
else:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
from collections import defaultdict, OrderedDict
|
|
3
3
|
from typing import Any
|
|
4
|
+
from collections.abc import Callable
|
|
4
5
|
from collections.abc import Generator
|
|
5
6
|
import operator
|
|
6
7
|
import logging
|
|
@@ -80,17 +81,17 @@ _INVERSE_OPERATIONS = {
|
|
|
80
81
|
#
|
|
81
82
|
|
|
82
83
|
|
|
83
|
-
def _op_with_unified_size(op, conv, operand0, operand1):
|
|
84
|
+
def _op_with_unified_size(op, conv: Callable, operand0, operand1, ins_addr: int):
|
|
84
85
|
# ensure operand1 is of the same size as operand0
|
|
85
86
|
if isinstance(operand1, ailment.Expr.Const):
|
|
86
87
|
# amazing - we do the easy thing here
|
|
87
|
-
return op(conv(operand0, nobool=True), operand1.value)
|
|
88
|
+
return op(conv(operand0, nobool=True, ins_addr=ins_addr), operand1.value)
|
|
88
89
|
if operand1.bits == operand0.bits:
|
|
89
|
-
return op(conv(operand0, nobool=True), conv(operand1))
|
|
90
|
+
return op(conv(operand0, nobool=True, ins_addr=ins_addr), conv(operand1, ins_addr=ins_addr))
|
|
90
91
|
# extension is required
|
|
91
92
|
assert operand1.bits < operand0.bits
|
|
92
93
|
operand1 = ailment.Expr.Convert(None, operand1.bits, operand0.bits, False, operand1)
|
|
93
|
-
return op(conv(operand0, nobool=True), conv(operand1, nobool=True))
|
|
94
|
+
return op(conv(operand0, nobool=True, ins_addr=ins_addr), conv(operand1, nobool=True, ins_addr=ins_addr))
|
|
94
95
|
|
|
95
96
|
|
|
96
97
|
def _dummy_bvs(condition, condition_mapping, name_suffix=""):
|
|
@@ -106,62 +107,94 @@ def _dummy_bools(condition, condition_mapping, name_suffix=""):
|
|
|
106
107
|
|
|
107
108
|
|
|
108
109
|
_ail2claripy_op_mapping = {
|
|
109
|
-
"LogicalAnd": lambda expr, conv, _: claripy.And(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
"
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
"
|
|
126
|
-
"
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
"
|
|
140
|
-
"
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"
|
|
144
|
-
"
|
|
145
|
-
|
|
110
|
+
"LogicalAnd": lambda expr, conv, _, ia: claripy.And(
|
|
111
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
112
|
+
),
|
|
113
|
+
"LogicalOr": lambda expr, conv, _, ia: claripy.Or(
|
|
114
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
115
|
+
),
|
|
116
|
+
"CmpEQ": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) == conv(expr.operands[1], ins_addr=ia),
|
|
117
|
+
"CmpNE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) != conv(expr.operands[1], ins_addr=ia),
|
|
118
|
+
"CmpLE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) <= conv(expr.operands[1], ins_addr=ia),
|
|
119
|
+
"CmpLEs": lambda expr, conv, _, ia: claripy.SLE(
|
|
120
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
121
|
+
),
|
|
122
|
+
"CmpLT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) < conv(expr.operands[1], ins_addr=ia),
|
|
123
|
+
"CmpLTs": lambda expr, conv, _, ia: claripy.SLT(
|
|
124
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
125
|
+
),
|
|
126
|
+
"CmpGE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) >= conv(expr.operands[1], ins_addr=ia),
|
|
127
|
+
"CmpGEs": lambda expr, conv, _, ia: claripy.SGE(
|
|
128
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
129
|
+
),
|
|
130
|
+
"CmpGT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) > conv(expr.operands[1], ins_addr=ia),
|
|
131
|
+
"CmpGTs": lambda expr, conv, _, ia: claripy.SGT(
|
|
132
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
133
|
+
),
|
|
134
|
+
"CasCmpEQ": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) == conv(expr.operands[1], ins_addr=ia),
|
|
135
|
+
"CasCmpNE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) != conv(expr.operands[1], ins_addr=ia),
|
|
136
|
+
"CasCmpLE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) <= conv(expr.operands[1], ins_addr=ia),
|
|
137
|
+
"CasCmpLEs": lambda expr, conv, _, ia: claripy.SLE(
|
|
138
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
139
|
+
),
|
|
140
|
+
"CasCmpLT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) < conv(expr.operands[1], ins_addr=ia),
|
|
141
|
+
"CasCmpLTs": lambda expr, conv, _, ia: claripy.SLT(
|
|
142
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
143
|
+
),
|
|
144
|
+
"CasCmpGE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) >= conv(expr.operands[1], ins_addr=ia),
|
|
145
|
+
"CasCmpGEs": lambda expr, conv, _, ia: claripy.SGE(
|
|
146
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
147
|
+
),
|
|
148
|
+
"CasCmpGT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) > conv(expr.operands[1], ins_addr=ia),
|
|
149
|
+
"CasCmpGTs": lambda expr, conv, _, ia: claripy.SGT(
|
|
150
|
+
conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
|
|
151
|
+
),
|
|
152
|
+
"Add": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
153
|
+
+ conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
154
|
+
"Sub": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
155
|
+
- conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
156
|
+
"Mul": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
157
|
+
* conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
158
|
+
"Div": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
159
|
+
/ conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
160
|
+
"Mod": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
161
|
+
% conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
162
|
+
"Not": lambda expr, conv, _, ia: claripy.Not(conv(expr.operand, ins_addr=ia)),
|
|
163
|
+
"Neg": lambda expr, conv, _, ia: -conv(expr.operand, ins_addr=ia),
|
|
164
|
+
"BitwiseNeg": lambda expr, conv, _, ia: ~conv(expr.operand, ins_addr=ia),
|
|
165
|
+
"Xor": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
166
|
+
^ conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
167
|
+
"And": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
168
|
+
& conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
169
|
+
"Or": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
|
|
170
|
+
| conv(expr.operands[1], nobool=True, ins_addr=ia),
|
|
171
|
+
"Shr": lambda expr, conv, _, ia: _op_with_unified_size(claripy.LShR, conv, expr.operands[0], expr.operands[1], ia),
|
|
172
|
+
"Shl": lambda expr, conv, _, ia: _op_with_unified_size(
|
|
173
|
+
operator.lshift, conv, expr.operands[0], expr.operands[1], ia
|
|
174
|
+
),
|
|
175
|
+
"Sar": lambda expr, conv, _, ia: _op_with_unified_size(
|
|
176
|
+
operator.rshift, conv, expr.operands[0], expr.operands[1], ia
|
|
177
|
+
),
|
|
178
|
+
"Concat": lambda expr, conv, _, ia: claripy.Concat(*[conv(operand, ins_addr=ia) for operand in expr.operands]),
|
|
146
179
|
# There are no corresponding claripy operations for the following operations
|
|
147
|
-
"DivMod": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
148
|
-
"CmpF": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
149
|
-
"Mull": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
150
|
-
"Mulls": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
151
|
-
"Reinterpret": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
152
|
-
"Rol": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
153
|
-
"Ror": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
154
|
-
"LogicalXor": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
155
|
-
"Carry": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
156
|
-
"SCarry": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
157
|
-
"SBorrow": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
158
|
-
"ExpCmpNE": lambda expr, _, m: _dummy_bools(expr, m),
|
|
159
|
-
"CmpORD": lambda expr, _, m: _dummy_bvs(expr, m), # in case CmpORDRewriter fails
|
|
160
|
-
"GetMSBs": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
161
|
-
"InterleaveLOV": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
162
|
-
"InterleaveHIV": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
180
|
+
"DivMod": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
181
|
+
"CmpF": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
182
|
+
"Mull": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
183
|
+
"Mulls": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
184
|
+
"Reinterpret": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
185
|
+
"Rol": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
186
|
+
"Ror": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
187
|
+
"LogicalXor": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
188
|
+
"Carry": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
189
|
+
"SCarry": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
190
|
+
"SBorrow": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
191
|
+
"ExpCmpNE": lambda expr, _, m, *args: _dummy_bools(expr, m),
|
|
192
|
+
"CmpORD": lambda expr, _, m, *args: _dummy_bvs(expr, m), # in case CmpORDRewriter fails
|
|
193
|
+
"GetMSBs": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
194
|
+
"InterleaveLOV": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
195
|
+
"InterleaveHIV": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
163
196
|
# catch-all
|
|
164
|
-
"_DUMMY_": lambda expr, _, m: _dummy_bvs(expr, m),
|
|
197
|
+
"_DUMMY_": lambda expr, _, m, *args: _dummy_bvs(expr, m),
|
|
165
198
|
}
|
|
166
199
|
|
|
167
200
|
#
|
|
@@ -610,6 +643,7 @@ class ConditionProcessor:
|
|
|
610
643
|
),
|
|
611
644
|
False,
|
|
612
645
|
),
|
|
646
|
+
ins_addr=dst_block.addr,
|
|
613
647
|
)
|
|
614
648
|
|
|
615
649
|
if type(src_block) is ConditionalBreakNode:
|
|
@@ -638,10 +672,10 @@ class ConditionProcessor:
|
|
|
638
672
|
if isinstance(last_stmt.target, ailment.Expr.Const):
|
|
639
673
|
return claripy.true()
|
|
640
674
|
# indirect jump
|
|
641
|
-
target_ast = self.claripy_ast_from_ail_condition(last_stmt.target)
|
|
675
|
+
target_ast = self.claripy_ast_from_ail_condition(last_stmt.target, ins_addr=last_stmt.ins_addr)
|
|
642
676
|
return target_ast == dst_block.addr
|
|
643
677
|
if type(last_stmt) is ailment.Stmt.ConditionalJump:
|
|
644
|
-
bool_var = self.claripy_ast_from_ail_condition(last_stmt.condition)
|
|
678
|
+
bool_var = self.claripy_ast_from_ail_condition(last_stmt.condition, ins_addr=last_stmt.ins_addr)
|
|
645
679
|
if isinstance(last_stmt.true_target, ailment.Expr.Const) and last_stmt.true_target.value == dst_block.addr:
|
|
646
680
|
return bool_var
|
|
647
681
|
return claripy.Not(bool_var)
|
|
@@ -766,7 +800,7 @@ class ConditionProcessor:
|
|
|
766
800
|
f"Condition variable {cond} has an unsupported operator {cond.op}. Consider implementing."
|
|
767
801
|
)
|
|
768
802
|
|
|
769
|
-
def claripy_ast_from_ail_condition(self, condition, nobool: bool = False) -> claripy.ast.Bool:
|
|
803
|
+
def claripy_ast_from_ail_condition(self, condition, nobool: bool = False, *, ins_addr: int = 0) -> claripy.ast.Bool:
|
|
770
804
|
# Unpack a condition all the way to the leaves
|
|
771
805
|
if isinstance(condition, claripy.ast.Base): # pylint:disable=isinstance-second-argument-not-valid-type
|
|
772
806
|
return condition
|
|
@@ -782,20 +816,24 @@ class ConditionProcessor:
|
|
|
782
816
|
# does it have a variable associated?
|
|
783
817
|
if condition.variable is not None:
|
|
784
818
|
var = claripy.BVS(
|
|
785
|
-
f"ailexpr_{condition!r}-{condition.variable.ident}",
|
|
819
|
+
f"ailexpr_{condition!r}-{condition.variable.ident}-{ins_addr:x}",
|
|
820
|
+
condition.bits,
|
|
821
|
+
explicit_name=True,
|
|
786
822
|
)
|
|
787
823
|
else:
|
|
788
|
-
var = claripy.BVS(
|
|
824
|
+
var = claripy.BVS(
|
|
825
|
+
f"ailexpr_{condition!r}-{condition.idx}-{ins_addr:x}", condition.bits, explicit_name=True
|
|
826
|
+
)
|
|
789
827
|
self._condition_mapping[var.args[0]] = condition
|
|
790
828
|
return var
|
|
791
829
|
if isinstance(condition, ailment.Expr.Convert):
|
|
792
830
|
# convert is special. if it generates a 1-bit variable, it should be treated as a BoolS
|
|
793
831
|
if condition.to_bits == 1:
|
|
794
|
-
var_ = self.claripy_ast_from_ail_condition(condition.operands[0])
|
|
832
|
+
var_ = self.claripy_ast_from_ail_condition(condition.operands[0], ins_addr=ins_addr)
|
|
795
833
|
name = "ailcond_Conv(%d->%d, %d)" % (condition.from_bits, condition.to_bits, hash(var_))
|
|
796
834
|
var = claripy.BoolS(name, explicit_name=True)
|
|
797
835
|
else:
|
|
798
|
-
var_ = self.claripy_ast_from_ail_condition(condition.operands[0])
|
|
836
|
+
var_ = self.claripy_ast_from_ail_condition(condition.operands[0], ins_addr=ins_addr)
|
|
799
837
|
name = "ailexpr_Conv(%d->%d, %d)" % (condition.from_bits, condition.to_bits, hash(var_))
|
|
800
838
|
var = claripy.BVS(name, condition.to_bits, explicit_name=True)
|
|
801
839
|
self._condition_mapping[var.args[0]] = condition
|
|
@@ -838,7 +876,7 @@ class ConditionProcessor:
|
|
|
838
876
|
condition.verbose_op,
|
|
839
877
|
)
|
|
840
878
|
lambda_expr = _ail2claripy_op_mapping["_DUMMY_"]
|
|
841
|
-
r = lambda_expr(condition, self.claripy_ast_from_ail_condition, self._condition_mapping)
|
|
879
|
+
r = lambda_expr(condition, self.claripy_ast_from_ail_condition, self._condition_mapping, ins_addr)
|
|
842
880
|
|
|
843
881
|
if isinstance(r, claripy.ast.Bool) and nobool:
|
|
844
882
|
r = claripy.BVS(f"ailexpr_from_bool_{r!r}", 1, explicit_name=True)
|
|
@@ -1075,17 +1113,15 @@ class ConditionProcessor:
|
|
|
1075
1113
|
r1_with: claripy.ast.Bool,
|
|
1076
1114
|
) -> claripy.ast.Bool:
|
|
1077
1115
|
if ast.op == "And":
|
|
1078
|
-
return
|
|
1079
|
-
|
|
1116
|
+
return claripy.And(
|
|
1117
|
+
*(ConditionProcessor._replace_term_in_ast(arg, r0, r0_with, r1, r1_with) for arg in ast.args)
|
|
1080
1118
|
)
|
|
1081
1119
|
if ast.op == "Or":
|
|
1082
|
-
return
|
|
1083
|
-
|
|
1120
|
+
return claripy.Or(
|
|
1121
|
+
*(ConditionProcessor._replace_term_in_ast(arg, r0, r0_with, r1, r1_with) for arg in ast.args)
|
|
1084
1122
|
)
|
|
1085
1123
|
if ast.op == "Not":
|
|
1086
|
-
return ast.
|
|
1087
|
-
"Not", (ConditionProcessor._replace_term_in_ast(ast.args[0], r0, r0_with, r1, r1_with),)
|
|
1088
|
-
)
|
|
1124
|
+
return claripy.Not(ConditionProcessor._replace_term_in_ast(ast.args[0], r0, r0_with, r1, r1_with))
|
|
1089
1125
|
if ast is r0:
|
|
1090
1126
|
return r0_with
|
|
1091
1127
|
if ast is r1:
|
|
@@ -23,6 +23,7 @@ class DecompilationCache:
|
|
|
23
23
|
"clinic",
|
|
24
24
|
"ite_exprs",
|
|
25
25
|
"binop_operators",
|
|
26
|
+
"errors",
|
|
26
27
|
)
|
|
27
28
|
|
|
28
29
|
def __init__(self, addr):
|
|
@@ -35,7 +36,10 @@ class DecompilationCache:
|
|
|
35
36
|
self.clinic: Clinic | None = None
|
|
36
37
|
self.ite_exprs: set[tuple[int, Any]] | None = None
|
|
37
38
|
self.binop_operators: dict[OpDescriptor, str] | None = None
|
|
39
|
+
self.errors: list[str] = []
|
|
38
40
|
|
|
39
41
|
@property
|
|
40
42
|
def local_types(self):
|
|
43
|
+
if self.clinic is None or self.clinic.variable_kb is None:
|
|
44
|
+
return None
|
|
41
45
|
return self.clinic.variable_kb.variables[self.addr].types
|
|
@@ -70,6 +70,7 @@ class Decompiler(Analysis):
|
|
|
70
70
|
update_memory_data: bool = True,
|
|
71
71
|
generate_code: bool = True,
|
|
72
72
|
use_cache: bool = True,
|
|
73
|
+
expr_collapse_depth: int = 16,
|
|
73
74
|
):
|
|
74
75
|
if not isinstance(func, Function):
|
|
75
76
|
func = self.kb.functions[func]
|
|
@@ -135,9 +136,25 @@ class Decompiler(Analysis):
|
|
|
135
136
|
self.ail_graph: networkx.DiGraph | None = None
|
|
136
137
|
self.vvar_id_start = None
|
|
137
138
|
self._optimization_scratch: dict[str, Any] = {}
|
|
139
|
+
self.expr_collapse_depth = expr_collapse_depth
|
|
138
140
|
|
|
139
141
|
if decompile:
|
|
140
|
-
self.
|
|
142
|
+
with self._resilience():
|
|
143
|
+
self._decompile()
|
|
144
|
+
if self.errors:
|
|
145
|
+
if (self.func.addr, self._flavor) not in self.kb.decompilations:
|
|
146
|
+
self.kb.decompilations[(self.func.addr, self._flavor)] = DecompilationCache(self.func.addr)
|
|
147
|
+
for error in self.errors:
|
|
148
|
+
self.kb.decompilations[(self.func.addr, self._flavor)].errors.append(error.format())
|
|
149
|
+
with self._resilience():
|
|
150
|
+
l.info("Decompilation failed for %s. Switching to basic preset and trying again.")
|
|
151
|
+
if preset != DECOMPILATION_PRESETS["basic"]:
|
|
152
|
+
self._optimization_passes = DECOMPILATION_PRESETS["basic"].get_optimization_passes(
|
|
153
|
+
self.project.arch, self.project.simos.name
|
|
154
|
+
)
|
|
155
|
+
self._decompile()
|
|
156
|
+
for error in self.errors:
|
|
157
|
+
self.kb.decompilations[(self.func.addr, self._flavor)].errors.append(error.format())
|
|
141
158
|
|
|
142
159
|
def _can_use_decompilation_cache(self, cache: DecompilationCache) -> bool:
|
|
143
160
|
a, b = self._cache_parameters, cache.parameters
|
|
@@ -153,7 +170,7 @@ class Decompiler(Analysis):
|
|
|
153
170
|
|
|
154
171
|
if self._cache_parameters is not None:
|
|
155
172
|
try:
|
|
156
|
-
cache = self.kb.decompilations[self.func.addr]
|
|
173
|
+
cache = self.kb.decompilations[(self.func.addr, self._flavor)]
|
|
157
174
|
if not self._can_use_decompilation_cache(cache):
|
|
158
175
|
cache = None
|
|
159
176
|
except KeyError:
|
|
@@ -333,6 +350,7 @@ class Decompiler(Analysis):
|
|
|
333
350
|
stmt_comments=old_codegen.stmt_comments if old_codegen is not None else None,
|
|
334
351
|
const_formats=old_codegen.const_formats if old_codegen is not None else None,
|
|
335
352
|
externs=clinic.externs,
|
|
353
|
+
binop_depth_cutoff=self.expr_collapse_depth,
|
|
336
354
|
**self.options_to_params(self.options_by_class["codegen"]),
|
|
337
355
|
)
|
|
338
356
|
|
|
@@ -344,7 +362,7 @@ class Decompiler(Analysis):
|
|
|
344
362
|
self.cache.codegen = codegen
|
|
345
363
|
self.cache.clinic = self.clinic
|
|
346
364
|
|
|
347
|
-
self.kb.decompilations[self.func.addr] = self.cache
|
|
365
|
+
self.kb.decompilations[(self.func.addr, self._flavor)] = self.cache
|
|
348
366
|
|
|
349
367
|
def _recover_regions(self, graph: networkx.DiGraph, condition_processor, update_graph: bool = True):
|
|
350
368
|
return self.project.analyses[RegionIdentifier].prep(kb=self.kb)(
|
|
@@ -290,8 +290,7 @@ class GraphDephicationVVarMapping(Analysis): # pylint:disable=abstract-method
|
|
|
290
290
|
for stmt in dst_block.statements:
|
|
291
291
|
if isinstance(stmt, Label):
|
|
292
292
|
continue
|
|
293
|
-
|
|
294
|
-
if r:
|
|
293
|
+
if is_phi_assignment(stmt):
|
|
295
294
|
for src_, vvar in stmt.src.src_and_vvars:
|
|
296
295
|
if src_ == src and vvar is not None and vvar.varid == vvar_id:
|
|
297
296
|
return True
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# pylint:disable=import-outside-toplevel
|
|
2
2
|
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
3
4
|
|
|
4
5
|
from archinfo import Arch
|
|
5
6
|
|
|
@@ -32,6 +33,10 @@ from .const_prop_reverter import ConstPropOptReverter
|
|
|
32
33
|
from .call_stmt_rewriter import CallStatementRewriter
|
|
33
34
|
from .duplication_reverter import DuplicationReverter
|
|
34
35
|
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from angr.analyses.decompiler.presets import DecompilationPreset
|
|
38
|
+
|
|
39
|
+
|
|
35
40
|
# order matters!
|
|
36
41
|
ALL_OPTIMIZATION_PASSES = [
|
|
37
42
|
RegisterSaveAreaSimplifier,
|
|
@@ -88,9 +93,18 @@ def get_optimization_passes(arch, platform):
|
|
|
88
93
|
return passes
|
|
89
94
|
|
|
90
95
|
|
|
91
|
-
def register_optimization_pass(opt_pass):
|
|
96
|
+
def register_optimization_pass(opt_pass, *, presets: list[str | DecompilationPreset] | None = None):
|
|
92
97
|
ALL_OPTIMIZATION_PASSES.append(opt_pass)
|
|
93
98
|
|
|
99
|
+
if presets:
|
|
100
|
+
from angr.analyses.decompiler.presets import DECOMPILATION_PRESETS
|
|
101
|
+
|
|
102
|
+
for preset in presets:
|
|
103
|
+
if isinstance(preset, str):
|
|
104
|
+
preset = DECOMPILATION_PRESETS[preset] # intentionally raise a KeyError if the preset is not found
|
|
105
|
+
if opt_pass not in preset.opt_passes:
|
|
106
|
+
preset.opt_passes.append(opt_pass)
|
|
107
|
+
|
|
94
108
|
|
|
95
109
|
__all__ = (
|
|
96
110
|
"OptimizationPassStage",
|
|
@@ -119,6 +119,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
119
119
|
self._ail_manager.next_atom(),
|
|
120
120
|
reg_bits,
|
|
121
121
|
src_and_vvars=[], # back patch later
|
|
122
|
+
ins_addr=node.addr,
|
|
122
123
|
)
|
|
123
124
|
phi_dst = VirtualVariable(
|
|
124
125
|
self._ail_manager.next_atom(),
|
|
@@ -126,6 +127,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
126
127
|
reg_bits,
|
|
127
128
|
VirtualVariableCategory.REGISTER,
|
|
128
129
|
oident=reg_offset,
|
|
130
|
+
ins_addr=node.addr,
|
|
129
131
|
)
|
|
130
132
|
|
|
131
133
|
case "stack":
|
|
@@ -135,6 +137,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
135
137
|
self._ail_manager.next_atom(),
|
|
136
138
|
stack_size * self.project.arch.byte_width,
|
|
137
139
|
src_and_vvars=[], # back patch later
|
|
140
|
+
ins_addr=node.addr,
|
|
138
141
|
)
|
|
139
142
|
phi_dst = VirtualVariable(
|
|
140
143
|
self._ail_manager.next_atom(),
|
|
@@ -142,6 +145,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
|
|
|
142
145
|
stack_size * self.project.arch.byte_width,
|
|
143
146
|
VirtualVariableCategory.STACK,
|
|
144
147
|
oident=stack_offset,
|
|
148
|
+
ins_addr=node.addr,
|
|
145
149
|
)
|
|
146
150
|
case _:
|
|
147
151
|
raise NotImplementedError
|
|
@@ -525,7 +525,7 @@ class SimEngineSSARewriting(
|
|
|
525
525
|
**expr.tags,
|
|
526
526
|
)
|
|
527
527
|
|
|
528
|
-
def _get_full_reg_vvar(self, reg_offset: int, size: int) -> VirtualVariable:
|
|
528
|
+
def _get_full_reg_vvar(self, reg_offset: int, size: int, ins_addr: int | None = None) -> VirtualVariable:
|
|
529
529
|
base_off, base_size = get_reg_offset_base_and_size(reg_offset, self.arch, size=size)
|
|
530
530
|
if (
|
|
531
531
|
base_off not in self.state.registers
|
|
@@ -534,13 +534,16 @@ class SimEngineSSARewriting(
|
|
|
534
534
|
):
|
|
535
535
|
# somehow it's never defined before...
|
|
536
536
|
_l.debug("Creating a new virtual variable for an undefined register (%d [%d]).", base_off, base_size)
|
|
537
|
+
tags = {}
|
|
538
|
+
if ins_addr is not None:
|
|
539
|
+
tags["ins_addr"] = ins_addr
|
|
537
540
|
vvar = VirtualVariable(
|
|
538
541
|
self.ail_manager.next_atom(),
|
|
539
542
|
self.next_vvar_id(),
|
|
540
543
|
base_size * self.arch.byte_width,
|
|
541
544
|
category=VirtualVariableCategory.REGISTER,
|
|
542
545
|
oident=base_off,
|
|
543
|
-
|
|
546
|
+
**tags,
|
|
544
547
|
)
|
|
545
548
|
self.state.registers[base_off][base_size] = vvar
|
|
546
549
|
return vvar
|
|
@@ -628,7 +631,11 @@ class SimEngineSSARewriting(
|
|
|
628
631
|
|
|
629
632
|
# no good size available
|
|
630
633
|
# get the full register, then extract from there
|
|
631
|
-
vvar = self._get_full_reg_vvar(
|
|
634
|
+
vvar = self._get_full_reg_vvar(
|
|
635
|
+
reg_expr.reg_offset,
|
|
636
|
+
reg_expr.size,
|
|
637
|
+
ins_addr=reg_expr.ins_addr,
|
|
638
|
+
)
|
|
632
639
|
# extract
|
|
633
640
|
shift_amount = Const(
|
|
634
641
|
self.ail_manager.next_atom(),
|