angr 9.2.130__py3-none-manylinux2014_aarch64.whl → 9.2.132__py3-none-manylinux2014_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/analysis.py +6 -2
- angr/analyses/cfg/cfg_emulated.py +5 -5
- angr/analyses/cfg/cfg_fast.py +2 -2
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +139 -94
- angr/analyses/cfg/indirect_jump_resolvers/x86_elf_pic_plt.py +1 -1
- angr/analyses/ddg.py +14 -11
- angr/analyses/decompiler/ail_simplifier.py +3 -2
- angr/analyses/decompiler/block_simplifier.py +10 -21
- angr/analyses/decompiler/clinic.py +361 -8
- angr/analyses/decompiler/condition_processor.py +12 -10
- angr/analyses/decompiler/dephication/graph_rewriting.py +1 -1
- angr/analyses/decompiler/dephication/rewriting_engine.py +169 -45
- angr/analyses/decompiler/dephication/seqnode_dephication.py +5 -4
- angr/analyses/decompiler/optimization_passes/__init__.py +0 -3
- angr/analyses/decompiler/optimization_passes/const_derefs.py +1 -0
- angr/analyses/decompiler/optimization_passes/div_simplifier.py +41 -16
- angr/analyses/decompiler/optimization_passes/engine_base.py +261 -83
- angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +173 -35
- angr/analyses/decompiler/optimization_passes/mod_simplifier.py +5 -2
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +39 -19
- angr/analyses/decompiler/peephole_optimizations/__init__.py +5 -1
- angr/analyses/decompiler/peephole_optimizations/a_mul_const_sub_a.py +34 -0
- angr/analyses/decompiler/peephole_optimizations/a_shl_const_sub_a.py +3 -1
- angr/analyses/decompiler/peephole_optimizations/bswap.py +10 -6
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +100 -19
- angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +17 -0
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +42 -3
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +4 -2
- angr/analyses/decompiler/peephole_optimizations/rol_ror.py +37 -10
- angr/analyses/decompiler/peephole_optimizations/shl_to_mul.py +25 -0
- angr/analyses/decompiler/peephole_optimizations/utils.py +18 -0
- angr/analyses/decompiler/presets/fast.py +0 -2
- angr/analyses/decompiler/presets/full.py +0 -2
- angr/analyses/decompiler/ssailification/rewriting.py +1 -2
- angr/analyses/decompiler/ssailification/rewriting_engine.py +140 -57
- angr/analyses/decompiler/ssailification/ssailification.py +2 -1
- angr/analyses/decompiler/ssailification/traversal.py +4 -6
- angr/analyses/decompiler/ssailification/traversal_engine.py +125 -42
- angr/analyses/decompiler/structured_codegen/c.py +79 -16
- angr/analyses/decompiler/structuring/phoenix.py +40 -14
- angr/analyses/decompiler/structuring/structurer_nodes.py +9 -0
- angr/analyses/deobfuscator/irsb_reg_collector.py +29 -60
- angr/analyses/deobfuscator/string_obf_finder.py +2 -2
- angr/analyses/init_finder.py +47 -22
- angr/analyses/propagator/engine_base.py +21 -14
- angr/analyses/propagator/engine_vex.py +149 -179
- angr/analyses/propagator/propagator.py +10 -28
- angr/analyses/propagator/top_checker_mixin.py +211 -5
- angr/analyses/propagator/vex_vars.py +1 -1
- angr/analyses/reaching_definitions/dep_graph.py +1 -1
- angr/analyses/reaching_definitions/engine_ail.py +304 -329
- angr/analyses/reaching_definitions/engine_vex.py +243 -229
- angr/analyses/reaching_definitions/function_handler.py +3 -3
- angr/analyses/reaching_definitions/rd_state.py +37 -32
- angr/analyses/s_propagator.py +38 -5
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +9 -5
- angr/analyses/typehoon/simple_solver.py +16 -7
- angr/analyses/typehoon/translator.py +8 -0
- angr/analyses/typehoon/typeconsts.py +10 -2
- angr/analyses/typehoon/typehoon.py +4 -1
- angr/analyses/typehoon/typevars.py +9 -7
- angr/analyses/variable_recovery/engine_ail.py +296 -256
- angr/analyses/variable_recovery/engine_base.py +137 -116
- angr/analyses/variable_recovery/engine_vex.py +175 -185
- angr/analyses/variable_recovery/irsb_scanner.py +49 -38
- angr/analyses/variable_recovery/variable_recovery.py +28 -5
- angr/analyses/variable_recovery/variable_recovery_base.py +32 -33
- angr/analyses/variable_recovery/variable_recovery_fast.py +2 -2
- angr/analyses/xrefs.py +46 -19
- angr/annocfg.py +19 -14
- angr/block.py +4 -9
- angr/calling_conventions.py +1 -1
- angr/engines/engine.py +30 -14
- angr/engines/light/__init__.py +11 -3
- angr/engines/light/engine.py +1003 -1185
- angr/engines/pcode/cc.py +2 -0
- angr/engines/successors.py +13 -9
- angr/engines/vex/claripy/datalayer.py +1 -1
- angr/engines/vex/claripy/irop.py +14 -3
- angr/engines/vex/light/slicing.py +2 -2
- angr/exploration_techniques/__init__.py +1 -124
- angr/exploration_techniques/base.py +126 -0
- angr/exploration_techniques/bucketizer.py +1 -1
- angr/exploration_techniques/dfs.py +3 -1
- angr/exploration_techniques/director.py +2 -3
- angr/exploration_techniques/driller_core.py +1 -1
- angr/exploration_techniques/explorer.py +4 -2
- angr/exploration_techniques/lengthlimiter.py +2 -1
- angr/exploration_techniques/local_loop_seer.py +2 -1
- angr/exploration_techniques/loop_seer.py +5 -5
- angr/exploration_techniques/manual_mergepoint.py +2 -1
- angr/exploration_techniques/memory_watcher.py +3 -1
- angr/exploration_techniques/oppologist.py +4 -5
- angr/exploration_techniques/slicecutor.py +4 -2
- angr/exploration_techniques/spiller.py +1 -1
- angr/exploration_techniques/stochastic.py +2 -1
- angr/exploration_techniques/stub_stasher.py +2 -1
- angr/exploration_techniques/suggestions.py +3 -1
- angr/exploration_techniques/symbion.py +3 -1
- angr/exploration_techniques/tech_builder.py +2 -1
- angr/exploration_techniques/threading.py +4 -7
- angr/exploration_techniques/timeout.py +4 -2
- angr/exploration_techniques/tracer.py +4 -3
- angr/exploration_techniques/unique.py +3 -2
- angr/exploration_techniques/veritesting.py +1 -1
- angr/knowledge_plugins/key_definitions/atoms.py +2 -2
- angr/knowledge_plugins/key_definitions/live_definitions.py +16 -13
- angr/knowledge_plugins/propagations/states.py +13 -8
- angr/knowledge_plugins/variables/variable_manager.py +23 -9
- angr/sim_manager.py +1 -3
- angr/sim_state.py +39 -41
- angr/sim_type.py +5 -0
- angr/sim_variable.py +29 -28
- angr/utils/bits.py +17 -0
- angr/utils/formatting.py +4 -1
- angr/utils/orderedset.py +4 -1
- angr/utils/ssa/__init__.py +21 -3
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/METADATA +6 -6
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/RECORD +124 -123
- angr/analyses/decompiler/optimization_passes/multi_simplifier.py +0 -223
- angr/analyses/propagator/engine_ail.py +0 -1562
- angr/storage/memory_mixins/__init__.pyi +0 -48
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/LICENSE +0 -0
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/WHEEL +0 -0
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/entry_points.txt +0 -0
- {angr-9.2.130.dist-info → angr-9.2.132.dist-info}/top_level.txt +0 -0
angr/analyses/ddg.py
CHANGED
|
@@ -2,8 +2,10 @@ from __future__ import annotations
|
|
|
2
2
|
import logging
|
|
3
3
|
from collections import defaultdict
|
|
4
4
|
|
|
5
|
+
import claripy
|
|
5
6
|
import networkx
|
|
6
7
|
import pyvex
|
|
8
|
+
|
|
7
9
|
from . import Analysis
|
|
8
10
|
|
|
9
11
|
from angr.code_location import CodeLocation
|
|
@@ -1030,9 +1032,9 @@ class DDG(Analysis):
|
|
|
1030
1032
|
|
|
1031
1033
|
if not action.data.reg_deps and not action.data.tmp_deps:
|
|
1032
1034
|
# might be a constant assignment
|
|
1033
|
-
v = action.data.ast
|
|
1035
|
+
v: claripy.ast.BV = action.data.ast
|
|
1034
1036
|
if not v.symbolic:
|
|
1035
|
-
const_var = SimConstantVariable(v.concrete_value)
|
|
1037
|
+
const_var = SimConstantVariable(value=v.concrete_value, size=v.size())
|
|
1036
1038
|
const_progvar = ProgramVariable(const_var, prog_var.location)
|
|
1037
1039
|
self._data_graph_add_edge(const_progvar, prog_var, type="mem_data")
|
|
1038
1040
|
|
|
@@ -1109,7 +1111,8 @@ class DDG(Analysis):
|
|
|
1109
1111
|
elif isinstance(statement.data, pyvex.IRExpr.Const):
|
|
1110
1112
|
# assignment
|
|
1111
1113
|
const = statement.data.con.value
|
|
1112
|
-
|
|
1114
|
+
size = statement.data.con.size
|
|
1115
|
+
self._ast_graph.add_edge(ProgramVariable(SimConstantVariable(value=const, size=size), location), pv)
|
|
1113
1116
|
|
|
1114
1117
|
def _handle_reg_read(self, action, location, state, statement): # pylint:disable=unused-argument
|
|
1115
1118
|
reg_offset = action.offset
|
|
@@ -1140,7 +1143,7 @@ class DDG(Analysis):
|
|
|
1140
1143
|
elif reg_offset == self.project.arch.bp_offset:
|
|
1141
1144
|
self._custom_data_per_statement = ("bp", 0)
|
|
1142
1145
|
|
|
1143
|
-
def _handle_reg_write(self, action, location, state, statement): # pylint:disable=unused-argument
|
|
1146
|
+
def _handle_reg_write(self, action, location, state, statement: pyvex.stmt.Put): # pylint:disable=unused-argument
|
|
1144
1147
|
reg_offset = action.offset
|
|
1145
1148
|
variable = SimRegisterVariable(reg_offset, action.data.ast.size() // 8)
|
|
1146
1149
|
|
|
@@ -1157,9 +1160,9 @@ class DDG(Analysis):
|
|
|
1157
1160
|
if not action.reg_deps and not action.tmp_deps:
|
|
1158
1161
|
# moving a constant into the register
|
|
1159
1162
|
# try to parse out the constant from statement
|
|
1160
|
-
const_variable = SimConstantVariable()
|
|
1163
|
+
const_variable = SimConstantVariable(size=1)
|
|
1161
1164
|
if statement is not None and isinstance(statement.data, pyvex.IRExpr.Const):
|
|
1162
|
-
const_variable = SimConstantVariable(value=statement.data.con.value)
|
|
1165
|
+
const_variable = SimConstantVariable(value=statement.data.con.value, size=statement.data.con.size)
|
|
1163
1166
|
const_pv = ProgramVariable(const_variable, location, arch=self.project.arch)
|
|
1164
1167
|
self._data_graph_add_edge(const_pv, pv)
|
|
1165
1168
|
|
|
@@ -1187,7 +1190,7 @@ class DDG(Analysis):
|
|
|
1187
1190
|
ast = None
|
|
1188
1191
|
|
|
1189
1192
|
tmp = action.tmp
|
|
1190
|
-
pv = ProgramVariable(SimTemporaryVariable(tmp), location, arch=self.project.arch)
|
|
1193
|
+
pv = ProgramVariable(SimTemporaryVariable(tmp, len(action.data)), location, arch=self.project.arch)
|
|
1191
1194
|
|
|
1192
1195
|
if ast is not None:
|
|
1193
1196
|
for operand in ast.operands:
|
|
@@ -1230,12 +1233,12 @@ class DDG(Analysis):
|
|
|
1230
1233
|
if not action.tmp_deps and not self._variables_per_statement and not ast:
|
|
1231
1234
|
# read in a constant
|
|
1232
1235
|
# try to parse out the constant from statement
|
|
1233
|
-
const_variable = SimConstantVariable()
|
|
1236
|
+
const_variable = SimConstantVariable(size=1)
|
|
1234
1237
|
if statement is not None:
|
|
1235
1238
|
if isinstance(statement, pyvex.IRStmt.Dirty):
|
|
1236
1239
|
l.warning("Dirty statements are not supported in DDG for now.")
|
|
1237
1240
|
elif isinstance(statement.data, pyvex.IRExpr.Const):
|
|
1238
|
-
const_variable = SimConstantVariable(value=statement.data.con.value)
|
|
1241
|
+
const_variable = SimConstantVariable(value=statement.data.con.value, size=statement.data.con.size)
|
|
1239
1242
|
const_pv = ProgramVariable(const_variable, location, arch=self.project.arch)
|
|
1240
1243
|
self._data_graph_add_edge(const_pv, pv)
|
|
1241
1244
|
|
|
@@ -1296,7 +1299,7 @@ class DDG(Analysis):
|
|
|
1296
1299
|
const_value = expr_1.ast.args[0]
|
|
1297
1300
|
tmp = next(iter(expr_0.tmp_deps))
|
|
1298
1301
|
|
|
1299
|
-
const_def = ProgramVariable(SimConstantVariable(const_value), location)
|
|
1302
|
+
const_def = ProgramVariable(SimConstantVariable(value=const_value, size=len(expr_1.ast)), location)
|
|
1300
1303
|
tmp_def = self._temp_variables[tmp]
|
|
1301
1304
|
return AST("-", tmp_def, const_def)
|
|
1302
1305
|
|
|
@@ -1310,7 +1313,7 @@ class DDG(Analysis):
|
|
|
1310
1313
|
const_value = expr_1.ast.args[0]
|
|
1311
1314
|
tmp = next(iter(expr_0.tmp_deps))
|
|
1312
1315
|
|
|
1313
|
-
const_def = ProgramVariable(SimConstantVariable(const_value), location)
|
|
1316
|
+
const_def = ProgramVariable(SimConstantVariable(value=const_value, size=len(expr_1.ast)), location)
|
|
1314
1317
|
tmp_def = self._temp_variables[tmp]
|
|
1315
1318
|
return AST("+", tmp_def, const_def)
|
|
1316
1319
|
|
|
@@ -24,6 +24,7 @@ from ailment.expression import (
|
|
|
24
24
|
VirtualVariable,
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
+
from angr.analyses.s_propagator import SPropagatorAnalysis
|
|
27
28
|
from angr.analyses.s_reaching_definitions import SRDAModel
|
|
28
29
|
from angr.utils.ail import is_phi_assignment, HasExprWalker
|
|
29
30
|
from angr.code_location import CodeLocation, ExternalCodeLocation
|
|
@@ -213,11 +214,11 @@ class AILSimplifier(Analysis):
|
|
|
213
214
|
self._reaching_definitions = rd
|
|
214
215
|
return rd
|
|
215
216
|
|
|
216
|
-
def _compute_propagation(self, immediate_stmt_removal: bool = False):
|
|
217
|
+
def _compute_propagation(self, immediate_stmt_removal: bool = False) -> SPropagatorAnalysis:
|
|
217
218
|
# Propagate expressions or return the existing result
|
|
218
219
|
if self._propagator is not None:
|
|
219
220
|
return self._propagator
|
|
220
|
-
prop = self.project.analyses.
|
|
221
|
+
prop = self.project.analyses[SPropagatorAnalysis].prep()(
|
|
221
222
|
subject=self.func,
|
|
222
223
|
func_graph=self.func_graph,
|
|
223
224
|
# gp=self._gp,
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import logging
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
|
-
from collections.abc import Iterable
|
|
5
|
+
from collections.abc import Iterable, Mapping
|
|
6
6
|
|
|
7
7
|
from ailment.statement import Statement, Assignment, Call, Store, Jump
|
|
8
|
-
from ailment.expression import Tmp, Load, Const, Register, Convert
|
|
8
|
+
from ailment.expression import Tmp, Load, Const, Register, Convert, Expression
|
|
9
9
|
from ailment import AILBlockWalkerBase
|
|
10
10
|
|
|
11
11
|
from angr.code_location import ExternalCodeLocation, CodeLocation
|
|
@@ -139,7 +139,7 @@ class BlockSimplifier(Analysis):
|
|
|
139
139
|
|
|
140
140
|
self.result_block = block
|
|
141
141
|
|
|
142
|
-
def _compute_propagation(self, block):
|
|
142
|
+
def _compute_propagation(self, block) -> SPropagatorAnalysis:
|
|
143
143
|
if self._propagator is None:
|
|
144
144
|
self._propagator = self.project.analyses[SPropagatorAnalysis].prep()(
|
|
145
145
|
subject=block,
|
|
@@ -155,7 +155,6 @@ class BlockSimplifier(Analysis):
|
|
|
155
155
|
.prep()(
|
|
156
156
|
subject=block,
|
|
157
157
|
track_tmps=True,
|
|
158
|
-
stack_pointer_tracker=self._stack_pointer_tracker,
|
|
159
158
|
func_addr=self.func_addr,
|
|
160
159
|
)
|
|
161
160
|
.model
|
|
@@ -201,8 +200,8 @@ class BlockSimplifier(Analysis):
|
|
|
201
200
|
|
|
202
201
|
@staticmethod
|
|
203
202
|
def _replace_and_build(
|
|
204
|
-
block,
|
|
205
|
-
replacements,
|
|
203
|
+
block: Block,
|
|
204
|
+
replacements: Mapping[CodeLocation, Mapping[Expression, Expression]],
|
|
206
205
|
replace_assignment_dsts: bool = False,
|
|
207
206
|
replace_loads: bool = False,
|
|
208
207
|
gp: int | None = None,
|
|
@@ -211,14 +210,9 @@ class BlockSimplifier(Analysis):
|
|
|
211
210
|
new_statements = block.statements[::]
|
|
212
211
|
replaced = False
|
|
213
212
|
|
|
214
|
-
stmts_to_remove = set()
|
|
215
213
|
for codeloc, repls in replacements.items():
|
|
216
214
|
for old, new in repls.items():
|
|
217
|
-
|
|
218
|
-
if isinstance(new, dict):
|
|
219
|
-
stmt_to_remove = new["stmt_to_remove"]
|
|
220
|
-
new = new["expr"]
|
|
221
|
-
|
|
215
|
+
assert codeloc.stmt_idx is not None
|
|
222
216
|
stmt = new_statements[codeloc.stmt_idx]
|
|
223
217
|
if (
|
|
224
218
|
not replace_loads
|
|
@@ -229,7 +223,9 @@ class BlockSimplifier(Analysis):
|
|
|
229
223
|
# skip memory-based replacement for non-Call and non-gp-loading statements
|
|
230
224
|
continue
|
|
231
225
|
if stmt == old:
|
|
232
|
-
#
|
|
226
|
+
# the replacement must be a call, since replacements can only be expressions
|
|
227
|
+
# and call is the only thing which is both a statement and an expression
|
|
228
|
+
assert isinstance(new, Call)
|
|
233
229
|
r = True
|
|
234
230
|
new_stmt = new
|
|
235
231
|
else:
|
|
@@ -257,20 +253,13 @@ class BlockSimplifier(Analysis):
|
|
|
257
253
|
r, new_stmt = stmt.replace(old, new)
|
|
258
254
|
|
|
259
255
|
if r:
|
|
256
|
+
assert new_stmt is not None
|
|
260
257
|
replaced = True
|
|
261
258
|
new_statements[codeloc.stmt_idx] = new_stmt
|
|
262
|
-
if stmt_to_remove is not None:
|
|
263
|
-
stmts_to_remove.add(stmt_to_remove)
|
|
264
259
|
|
|
265
260
|
if not replaced:
|
|
266
261
|
return False, block
|
|
267
262
|
|
|
268
|
-
if stmts_to_remove:
|
|
269
|
-
stmt_ids_to_remove = {a.stmt_idx for a in stmts_to_remove}
|
|
270
|
-
all_stmts = {idx: stmt for idx, stmt in enumerate(new_statements) if idx not in stmt_ids_to_remove}
|
|
271
|
-
filtered_stmts = sorted(all_stmts.items(), key=lambda x: x[0])
|
|
272
|
-
new_statements = [stmt for _, stmt in filtered_stmts]
|
|
273
|
-
|
|
274
263
|
new_block = block.copy()
|
|
275
264
|
new_block.statements = new_statements
|
|
276
265
|
return True, new_block
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# pylint:disable=too-many-boolean-expressions
|
|
1
2
|
from __future__ import annotations
|
|
2
3
|
from typing import Any, NamedTuple, TYPE_CHECKING
|
|
3
4
|
import copy
|
|
@@ -12,12 +13,14 @@ import capstone
|
|
|
12
13
|
|
|
13
14
|
import ailment
|
|
14
15
|
|
|
16
|
+
from angr.analyses.decompiler.ssailification.ssailification import Ssailification
|
|
15
17
|
from angr.errors import AngrDecompilationError
|
|
16
18
|
from angr.knowledge_base import KnowledgeBase
|
|
17
19
|
from angr.knowledge_plugins.functions import Function
|
|
18
20
|
from angr.knowledge_plugins.cfg.memory_data import MemoryDataSort
|
|
19
21
|
from angr.codenode import BlockNode
|
|
20
22
|
from angr.utils import timethis
|
|
23
|
+
from angr.utils.graph import GraphUtils
|
|
21
24
|
from angr.calling_conventions import SimRegArg, SimStackArg, SimFunctionArgument
|
|
22
25
|
from angr.sim_type import (
|
|
23
26
|
SimTypeChar,
|
|
@@ -115,6 +118,7 @@ class Clinic(Analysis):
|
|
|
115
118
|
desired_variables: set[str] | None = None,
|
|
116
119
|
force_loop_single_exit: bool = True,
|
|
117
120
|
complete_successors: bool = False,
|
|
121
|
+
unsound_fix_abnormal_switches: bool = True,
|
|
118
122
|
):
|
|
119
123
|
if not func.normalized and mode == ClinicMode.DECOMPILE:
|
|
120
124
|
raise ValueError("Decompilation must work on normalized function graphs.")
|
|
@@ -146,6 +150,7 @@ class Clinic(Analysis):
|
|
|
146
150
|
self._must_struct = must_struct
|
|
147
151
|
self._reset_variable_names = reset_variable_names
|
|
148
152
|
self._rewrite_ites_to_diamonds = rewrite_ites_to_diamonds
|
|
153
|
+
self._unsound_fix_abnormal_switches = unsound_fix_abnormal_switches
|
|
149
154
|
self.reaching_definitions: ReachingDefinitionsAnalysis | None = None
|
|
150
155
|
self._cache = cache
|
|
151
156
|
self._mode = mode
|
|
@@ -265,6 +270,9 @@ class Clinic(Analysis):
|
|
|
265
270
|
def _decompilation_fixups(self, ail_graph):
|
|
266
271
|
is_pcode_arch = ":" in self.project.arch.name
|
|
267
272
|
|
|
273
|
+
self._remove_redundant_jump_blocks(ail_graph)
|
|
274
|
+
# _fix_abnormal_switch_case_heads may re-lift from VEX blocks, so it should be placed as high up as possible
|
|
275
|
+
self._fix_abnormal_switch_case_heads(ail_graph)
|
|
268
276
|
if self._rewrite_ites_to_diamonds:
|
|
269
277
|
self._rewrite_ite_expressions(ail_graph)
|
|
270
278
|
self._remove_redundant_jump_blocks(ail_graph)
|
|
@@ -941,7 +949,7 @@ class Clinic(Analysis):
|
|
|
941
949
|
|
|
942
950
|
def _convert(self, block_node):
|
|
943
951
|
"""
|
|
944
|
-
Convert a
|
|
952
|
+
Convert a BlockNode to an AIL block.
|
|
945
953
|
|
|
946
954
|
:param block_node: A BlockNode instance.
|
|
947
955
|
:return: A converted AIL block.
|
|
@@ -955,13 +963,16 @@ class Clinic(Analysis):
|
|
|
955
963
|
return ailment.Block(block_node.addr, 0, statements=[])
|
|
956
964
|
|
|
957
965
|
block = self.project.factory.block(block_node.addr, block_node.size, cross_insn_opt=False)
|
|
966
|
+
return self._convert_vex(block)
|
|
967
|
+
|
|
968
|
+
def _convert_vex(self, block):
|
|
958
969
|
if block.vex.jumpkind not in {"Ijk_Call", "Ijk_Boring", "Ijk_Ret"} and not block.vex.jumpkind.startswith(
|
|
959
970
|
"Ijk_Sys"
|
|
960
971
|
):
|
|
961
972
|
# we don't support lifting this block. use a dummy block instead
|
|
962
973
|
dirty_expr = ailment.Expr.DirtyExpression(
|
|
963
974
|
self._ail_manager.next_atom,
|
|
964
|
-
f"Unsupported jumpkind {block.vex.jumpkind} at address {
|
|
975
|
+
f"Unsupported jumpkind {block.vex.jumpkind} at address {block.addr}",
|
|
965
976
|
[],
|
|
966
977
|
bits=0,
|
|
967
978
|
)
|
|
@@ -969,10 +980,10 @@ class Clinic(Analysis):
|
|
|
969
980
|
ailment.Stmt.DirtyStatement(
|
|
970
981
|
self._ail_manager.next_atom(),
|
|
971
982
|
dirty_expr,
|
|
972
|
-
ins_addr=
|
|
983
|
+
ins_addr=block.addr,
|
|
973
984
|
)
|
|
974
985
|
]
|
|
975
|
-
return ailment.Block(
|
|
986
|
+
return ailment.Block(block.addr, block.size, statements=statements)
|
|
976
987
|
|
|
977
988
|
return ailment.IRSBConverter.convert(block.vex, self._ail_manager)
|
|
978
989
|
|
|
@@ -1351,10 +1362,9 @@ class Clinic(Analysis):
|
|
|
1351
1362
|
|
|
1352
1363
|
@timethis
|
|
1353
1364
|
def _transform_to_ssa_level0(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
1354
|
-
ssailification = self.project.analyses.
|
|
1365
|
+
ssailification = self.project.analyses[Ssailification].prep(fail_fast=self._fail_fast)(
|
|
1355
1366
|
self.function,
|
|
1356
1367
|
ail_graph,
|
|
1357
|
-
fail_fast=self._fail_fast,
|
|
1358
1368
|
entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
|
|
1359
1369
|
ail_manager=self._ail_manager,
|
|
1360
1370
|
ssa_stackvars=False,
|
|
@@ -1924,10 +1934,12 @@ class Clinic(Analysis):
|
|
|
1924
1934
|
break
|
|
1925
1935
|
|
|
1926
1936
|
def _create_triangle_for_ite_expression(self, ail_graph, block_addr: int, ite_ins_addr: int):
|
|
1927
|
-
|
|
1928
|
-
ite_insn_size =
|
|
1937
|
+
ite_insn_only_block = self.project.factory.block(ite_ins_addr, num_inst=1)
|
|
1938
|
+
ite_insn_size = ite_insn_only_block.size
|
|
1929
1939
|
if ite_insn_size <= 2: # we need an address for true_block and another address for false_block
|
|
1930
1940
|
return None
|
|
1941
|
+
if ite_insn_only_block.vex.exit_statements:
|
|
1942
|
+
return None
|
|
1931
1943
|
|
|
1932
1944
|
# relift the head and the ITE instruction
|
|
1933
1945
|
new_head = self.project.factory.block(
|
|
@@ -2029,6 +2041,10 @@ class Clinic(Analysis):
|
|
|
2029
2041
|
):
|
|
2030
2042
|
return None
|
|
2031
2043
|
|
|
2044
|
+
# corner-case: the last statement of original_block might have been patched by _remove_redundant_jump_blocks.
|
|
2045
|
+
# we detect such case and fix it in new_head_ail
|
|
2046
|
+
self._remove_redundant_jump_blocks_repatch_relifted_block(original_block, end_block_ail)
|
|
2047
|
+
|
|
2032
2048
|
ail_graph.remove_node(original_block)
|
|
2033
2049
|
|
|
2034
2050
|
if end_block_ail not in ail_graph:
|
|
@@ -2055,6 +2071,310 @@ class Clinic(Analysis):
|
|
|
2055
2071
|
|
|
2056
2072
|
return end_block_ail.addr
|
|
2057
2073
|
|
|
2074
|
+
def _fix_abnormal_switch_case_heads(self, ail_graph: networkx.DiGraph) -> None:
|
|
2075
|
+
"""
|
|
2076
|
+
Detect the existence of switch-case heads whose indirect jump node has more than one predecessor, and attempt
|
|
2077
|
+
to fix those cases by altering the graph.
|
|
2078
|
+
"""
|
|
2079
|
+
|
|
2080
|
+
if self._cfg is None:
|
|
2081
|
+
return
|
|
2082
|
+
|
|
2083
|
+
if not self._cfg.jump_tables:
|
|
2084
|
+
return
|
|
2085
|
+
|
|
2086
|
+
node_dict: defaultdict[int, list[ailment.Block]] = defaultdict(list)
|
|
2087
|
+
for node in ail_graph:
|
|
2088
|
+
node_dict[node.addr].append(node)
|
|
2089
|
+
|
|
2090
|
+
candidates = []
|
|
2091
|
+
for block_addr in self._cfg.jump_tables:
|
|
2092
|
+
block_nodes = node_dict[block_addr]
|
|
2093
|
+
for block_node in block_nodes:
|
|
2094
|
+
if ail_graph.in_degree[block_node] > 1:
|
|
2095
|
+
# found it
|
|
2096
|
+
candidates.append(block_node)
|
|
2097
|
+
|
|
2098
|
+
if not candidates:
|
|
2099
|
+
return
|
|
2100
|
+
|
|
2101
|
+
sorted_nodes = GraphUtils.quasi_topological_sort_nodes(ail_graph)
|
|
2102
|
+
node_to_rank = {node: rank for rank, node in enumerate(sorted_nodes)}
|
|
2103
|
+
for candidate in candidates:
|
|
2104
|
+
# determine the "intended" switch-case head using topological order
|
|
2105
|
+
preds = list(ail_graph.predecessors(candidate))
|
|
2106
|
+
preds = sorted(preds, key=lambda n_: node_to_rank[n_])
|
|
2107
|
+
intended_head = preds[0]
|
|
2108
|
+
other_heads = preds[1:]
|
|
2109
|
+
|
|
2110
|
+
# now here is the tricky part. there are two cases:
|
|
2111
|
+
# Case 1: the intended head and the other heads share the same suffix (of instructions)
|
|
2112
|
+
# Example:
|
|
2113
|
+
# ; binary 736cb27201273f6c4f83da362c9595b50d12333362e02bc7a77dd327cc6b045a
|
|
2114
|
+
# 0041DA97 mov ecx, [esp+2Ch+var_18] ; this is the intended head
|
|
2115
|
+
# 0041DA9B mov ecx, [ecx]
|
|
2116
|
+
# 0041DA9D cmp ecx, 9
|
|
2117
|
+
# 0041DAA0 jbe loc_41D5A8
|
|
2118
|
+
#
|
|
2119
|
+
# 0041D599 mov ecx, [ecx] ; this is the other head
|
|
2120
|
+
# 0041D59B mov [esp+2Ch+var_10], eax
|
|
2121
|
+
# 0041D59F cmp ecx, 9
|
|
2122
|
+
# 0041D5A2 ja loc_41DAA6 ; fallthrough to 0x41d5a8
|
|
2123
|
+
# given the overlap of two instructions at the end of both blocks, we will alter the second block to remove
|
|
2124
|
+
# the overlapped instructions and add an unconditional jump so that it jumps to 0x41da9d.
|
|
2125
|
+
# this is the most common case created by jump threading optimization in compilers. it's easy to handle.
|
|
2126
|
+
|
|
2127
|
+
# Case 2: the intended head and the other heads do not share the same suffix of instructions. in this case,
|
|
2128
|
+
# we cannot reliably convert the blocks into a properly structured switch-case construct. we will alter the
|
|
2129
|
+
# last instruction of all other heads to jump to the cmp instruction in the intended head, but do not remove
|
|
2130
|
+
# any other instructions in these other heads. this is unsound, but is the best we can do in this case.
|
|
2131
|
+
|
|
2132
|
+
overlaps = [self._get_overlapping_suffix_instructions(intended_head, head) for head in other_heads]
|
|
2133
|
+
if overlaps and (overlap := min(overlaps)) > 0:
|
|
2134
|
+
# Case 1
|
|
2135
|
+
self._fix_abnormal_switch_case_heads_case1(ail_graph, candidate, intended_head, other_heads, overlap)
|
|
2136
|
+
else:
|
|
2137
|
+
if self._unsound_fix_abnormal_switches:
|
|
2138
|
+
# Case 2
|
|
2139
|
+
l.warning("Switch-case at %#x has multiple head nodes but cannot be fixed soundly.", candidate.addr)
|
|
2140
|
+
# find the comparison instruction in the intended head
|
|
2141
|
+
comparison_stmt = None
|
|
2142
|
+
if "cc_op" in self.project.arch.registers:
|
|
2143
|
+
comparison_stmt = next(
|
|
2144
|
+
iter(
|
|
2145
|
+
stmt
|
|
2146
|
+
for stmt in intended_head.statements
|
|
2147
|
+
if isinstance(stmt, ailment.Stmt.Assignment)
|
|
2148
|
+
and isinstance(stmt.dst, ailment.Expr.Register)
|
|
2149
|
+
and stmt.dst.reg_offset == self.project.arch.registers["cc_op"][0]
|
|
2150
|
+
),
|
|
2151
|
+
None,
|
|
2152
|
+
)
|
|
2153
|
+
intended_head_block = self.project.factory.block(
|
|
2154
|
+
intended_head.addr, size=intended_head.original_size
|
|
2155
|
+
)
|
|
2156
|
+
if comparison_stmt is not None:
|
|
2157
|
+
cmp_rpos = len(
|
|
2158
|
+
intended_head_block.instruction_addrs
|
|
2159
|
+
) - intended_head_block.instruction_addrs.index(comparison_stmt.ins_addr)
|
|
2160
|
+
else:
|
|
2161
|
+
cmp_rpos = min(len(intended_head_block.instruction_addrs), 2)
|
|
2162
|
+
self._fix_abnormal_switch_case_heads_case2(
|
|
2163
|
+
ail_graph,
|
|
2164
|
+
candidate,
|
|
2165
|
+
intended_head,
|
|
2166
|
+
other_heads,
|
|
2167
|
+
intended_head_split_insns=cmp_rpos,
|
|
2168
|
+
other_head_split_insns=0,
|
|
2169
|
+
)
|
|
2170
|
+
|
|
2171
|
+
def _get_overlapping_suffix_instructions(self, ailblock_0: ailment.Block, ailblock_1: ailment.Block) -> int:
|
|
2172
|
+
# we first compare their ending conditional jumps
|
|
2173
|
+
if not self._get_overlapping_suffix_instructions_compare_conditional_jumps(ailblock_0, ailblock_1):
|
|
2174
|
+
return 0
|
|
2175
|
+
|
|
2176
|
+
# we re-lift the blocks and compare the instructions
|
|
2177
|
+
block_0 = self.project.factory.block(ailblock_0.addr, size=ailblock_0.original_size)
|
|
2178
|
+
block_1 = self.project.factory.block(ailblock_1.addr, size=ailblock_1.original_size)
|
|
2179
|
+
|
|
2180
|
+
i0 = len(block_0.capstone.insns) - 2
|
|
2181
|
+
i1 = len(block_1.capstone.insns) - 2
|
|
2182
|
+
overlap = 1
|
|
2183
|
+
while i0 >= 0 and i1 >= 0:
|
|
2184
|
+
same = self._get_overlapping_suffix_instructions_compare_instructions(
|
|
2185
|
+
block_0.capstone.insns[i0], block_1.capstone.insns[i1]
|
|
2186
|
+
)
|
|
2187
|
+
if not same:
|
|
2188
|
+
break
|
|
2189
|
+
overlap += 1
|
|
2190
|
+
i0 -= 1
|
|
2191
|
+
i1 -= 1
|
|
2192
|
+
|
|
2193
|
+
return overlap
|
|
2194
|
+
|
|
2195
|
+
@staticmethod
|
|
2196
|
+
def _get_overlapping_suffix_instructions_compare_instructions(insn_0, insn_1) -> bool:
|
|
2197
|
+
return insn_0.mnemonic == insn_1.mnemonic and insn_0.op_str == insn_1.op_str
|
|
2198
|
+
|
|
2199
|
+
@staticmethod
|
|
2200
|
+
def _get_overlapping_suffix_instructions_compare_conditional_jumps(
|
|
2201
|
+
ailblock_0: ailment.Block, ailblock_1: ailment.Block
|
|
2202
|
+
) -> bool:
|
|
2203
|
+
# TODO: The logic here is naive and highly customized to the only example I can access. Expand this method
|
|
2204
|
+
# later to handle more cases if needed.
|
|
2205
|
+
if len(ailblock_0.statements) == 0 or len(ailblock_1.statements) == 0:
|
|
2206
|
+
return False
|
|
2207
|
+
|
|
2208
|
+
# 12 | 0x41d5a2 | t17 = (t4 <= 0x9<32>)
|
|
2209
|
+
# 13 | 0x41d5a2 | t16 = Conv(1->32, t17)
|
|
2210
|
+
# 14 | 0x41d5a2 | t14 = t16
|
|
2211
|
+
# 15 | 0x41d5a2 | t18 = Conv(32->1, t14)
|
|
2212
|
+
# 16 | 0x41d5a2 | t9 = t18
|
|
2213
|
+
# 17 | 0x41d5a2 | if (t9) { Goto 0x41d5a8<32> } else { Goto 0x41daa6<32> }
|
|
2214
|
+
|
|
2215
|
+
last_stmt_0 = ailblock_0.statements[-1]
|
|
2216
|
+
last_stmt_1 = ailblock_1.statements[-1]
|
|
2217
|
+
if not (isinstance(last_stmt_0, ailment.Stmt.ConditionalJump) and last_stmt_0.likes(last_stmt_1)):
|
|
2218
|
+
return False
|
|
2219
|
+
|
|
2220
|
+
last_cmp_stmt_0 = next(
|
|
2221
|
+
iter(
|
|
2222
|
+
stmt
|
|
2223
|
+
for stmt in reversed(ailblock_0.statements)
|
|
2224
|
+
if isinstance(stmt, ailment.Stmt.Assignment)
|
|
2225
|
+
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
2226
|
+
and stmt.src.op in ailment.Expr.BinaryOp.COMPARISON_NEGATION
|
|
2227
|
+
and stmt.ins_addr == last_stmt_0.ins_addr
|
|
2228
|
+
),
|
|
2229
|
+
None,
|
|
2230
|
+
)
|
|
2231
|
+
last_cmp_stmt_1 = next(
|
|
2232
|
+
iter(
|
|
2233
|
+
stmt
|
|
2234
|
+
for stmt in reversed(ailblock_1.statements)
|
|
2235
|
+
if isinstance(stmt, ailment.Stmt.Assignment)
|
|
2236
|
+
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
2237
|
+
and stmt.src.op in ailment.Expr.BinaryOp.COMPARISON_NEGATION
|
|
2238
|
+
and stmt.ins_addr == last_stmt_1.ins_addr
|
|
2239
|
+
),
|
|
2240
|
+
None,
|
|
2241
|
+
)
|
|
2242
|
+
return (
|
|
2243
|
+
last_cmp_stmt_0 is not None
|
|
2244
|
+
and last_cmp_stmt_1 is not None
|
|
2245
|
+
and last_cmp_stmt_0.src.op == last_cmp_stmt_1.src.op
|
|
2246
|
+
and last_cmp_stmt_0.src.operands[1].likes(last_cmp_stmt_1.src.operands[1])
|
|
2247
|
+
)
|
|
2248
|
+
|
|
2249
|
+
def _fix_abnormal_switch_case_heads_case1(
|
|
2250
|
+
self,
|
|
2251
|
+
ail_graph: networkx.DiGraph,
|
|
2252
|
+
indirect_jump_node: ailment.Block,
|
|
2253
|
+
intended_head: ailment.Block,
|
|
2254
|
+
other_heads: list[ailment.Block],
|
|
2255
|
+
overlap: int,
|
|
2256
|
+
) -> None:
|
|
2257
|
+
self._fix_abnormal_switch_case_heads_case2(
|
|
2258
|
+
ail_graph,
|
|
2259
|
+
indirect_jump_node,
|
|
2260
|
+
intended_head,
|
|
2261
|
+
other_heads,
|
|
2262
|
+
intended_head_split_insns=overlap,
|
|
2263
|
+
other_head_split_insns=overlap,
|
|
2264
|
+
)
|
|
2265
|
+
|
|
2266
|
+
def _fix_abnormal_switch_case_heads_case2(
|
|
2267
|
+
self,
|
|
2268
|
+
ail_graph: networkx.DiGraph,
|
|
2269
|
+
indirect_jump_node: ailment.Block,
|
|
2270
|
+
intended_head: ailment.Block,
|
|
2271
|
+
other_heads: list[ailment.Block],
|
|
2272
|
+
intended_head_split_insns: int = 1,
|
|
2273
|
+
other_head_split_insns: int = 0,
|
|
2274
|
+
) -> None:
|
|
2275
|
+
|
|
2276
|
+
# split the intended head into two
|
|
2277
|
+
intended_head_block = self.project.factory.block(intended_head.addr, size=intended_head.original_size)
|
|
2278
|
+
split_ins_addr = intended_head_block.instruction_addrs[-intended_head_split_insns]
|
|
2279
|
+
# note that the two blocks can be fully overlapping, so block_0 will be empty...
|
|
2280
|
+
intended_head_block_0 = (
|
|
2281
|
+
self.project.factory.block(intended_head.addr, size=split_ins_addr - intended_head.addr)
|
|
2282
|
+
if split_ins_addr != intended_head.addr
|
|
2283
|
+
else None
|
|
2284
|
+
)
|
|
2285
|
+
intended_head_block_1 = self.project.factory.block(
|
|
2286
|
+
split_ins_addr, size=intended_head.addr + intended_head.original_size - split_ins_addr
|
|
2287
|
+
)
|
|
2288
|
+
intended_head_0 = self._convert_vex(intended_head_block_0) if intended_head_block_0 is not None else None
|
|
2289
|
+
intended_head_1 = self._convert_vex(intended_head_block_1)
|
|
2290
|
+
|
|
2291
|
+
# corner-case: the last statement of intended_head might have been patched by _remove_redundant_jump_blocks. we
|
|
2292
|
+
# detect such case and fix it in intended_head_1
|
|
2293
|
+
self._remove_redundant_jump_blocks_repatch_relifted_block(intended_head, intended_head_1)
|
|
2294
|
+
|
|
2295
|
+
# adjust the graph accordingly
|
|
2296
|
+
preds = list(ail_graph.predecessors(intended_head))
|
|
2297
|
+
succs = list(ail_graph.successors(intended_head))
|
|
2298
|
+
ail_graph.remove_node(intended_head)
|
|
2299
|
+
|
|
2300
|
+
if intended_head_0 is None:
|
|
2301
|
+
# perfect overlap; the first block is empty
|
|
2302
|
+
for pred in preds:
|
|
2303
|
+
if pred is intended_head:
|
|
2304
|
+
ail_graph.add_edge(intended_head_1, intended_head_1)
|
|
2305
|
+
else:
|
|
2306
|
+
ail_graph.add_edge(pred, intended_head_1)
|
|
2307
|
+
for succ in succs:
|
|
2308
|
+
if succ is intended_head:
|
|
2309
|
+
ail_graph.add_edge(intended_head_1, intended_head_1)
|
|
2310
|
+
else:
|
|
2311
|
+
ail_graph.add_edge(intended_head_1, succ)
|
|
2312
|
+
else:
|
|
2313
|
+
ail_graph.add_edge(intended_head_0, intended_head_1)
|
|
2314
|
+
for pred in preds:
|
|
2315
|
+
if pred is intended_head:
|
|
2316
|
+
ail_graph.add_edge(intended_head_1, intended_head_0)
|
|
2317
|
+
else:
|
|
2318
|
+
ail_graph.add_edge(pred, intended_head_0)
|
|
2319
|
+
for succ in succs:
|
|
2320
|
+
if succ is intended_head:
|
|
2321
|
+
ail_graph.add_edge(intended_head_1, intended_head_0)
|
|
2322
|
+
else:
|
|
2323
|
+
ail_graph.add_edge(intended_head_1, succ)
|
|
2324
|
+
|
|
2325
|
+
# split other heads
|
|
2326
|
+
for o in other_heads:
|
|
2327
|
+
if other_head_split_insns > 0:
|
|
2328
|
+
o_block = self.project.factory.block(o.addr, size=o.original_size)
|
|
2329
|
+
o_split_addr = o_block.instruction_addrs[-other_head_split_insns]
|
|
2330
|
+
new_o_block = (
|
|
2331
|
+
self.project.factory.block(o.addr, size=o_split_addr - o.addr) if o_split_addr != o.addr else None
|
|
2332
|
+
)
|
|
2333
|
+
new_head = self._convert_vex(new_o_block) if new_o_block is not None else None
|
|
2334
|
+
else:
|
|
2335
|
+
new_head = o
|
|
2336
|
+
|
|
2337
|
+
if new_head is None:
|
|
2338
|
+
# the head is removed - let's replace it with a jump to the target
|
|
2339
|
+
jump_stmt = ailment.Stmt.Jump(
|
|
2340
|
+
None,
|
|
2341
|
+
ailment.Expr.Const(None, None, intended_head_1.addr, self.project.arch.bits),
|
|
2342
|
+
target_idx=intended_head_1.idx,
|
|
2343
|
+
ins_addr=o.addr,
|
|
2344
|
+
)
|
|
2345
|
+
new_head = ailment.Block(o.addr, 1, statements=[jump_stmt], idx=o.idx)
|
|
2346
|
+
else:
|
|
2347
|
+
if (
|
|
2348
|
+
new_head.statements
|
|
2349
|
+
and isinstance(new_head.statements[-1], ailment.Stmt.Jump)
|
|
2350
|
+
and isinstance(new_head.statements[-1].target, ailment.Expr.Const)
|
|
2351
|
+
):
|
|
2352
|
+
# update the jump target
|
|
2353
|
+
new_head.statements[-1] = ailment.Stmt.Jump(
|
|
2354
|
+
new_head.statements[-1].idx,
|
|
2355
|
+
ailment.Expr.Const(None, None, intended_head_1.addr, self.project.arch.bits),
|
|
2356
|
+
target_idx=intended_head_1.idx,
|
|
2357
|
+
**new_head.statements[-1].tags,
|
|
2358
|
+
)
|
|
2359
|
+
|
|
2360
|
+
# adjust the graph accordingly
|
|
2361
|
+
preds = list(ail_graph.predecessors(o))
|
|
2362
|
+
succs = list(ail_graph.successors(o))
|
|
2363
|
+
ail_graph.remove_node(o)
|
|
2364
|
+
for pred in preds:
|
|
2365
|
+
if pred is o:
|
|
2366
|
+
ail_graph.add_edge(new_head, new_head)
|
|
2367
|
+
else:
|
|
2368
|
+
ail_graph.add_edge(pred, new_head)
|
|
2369
|
+
for succ in succs:
|
|
2370
|
+
if succ is o:
|
|
2371
|
+
ail_graph.add_edge(new_head, new_head)
|
|
2372
|
+
elif succ is indirect_jump_node:
|
|
2373
|
+
ail_graph.add_edge(new_head, intended_head_1)
|
|
2374
|
+
else:
|
|
2375
|
+
# it should be going to the default node. ignore it
|
|
2376
|
+
pass
|
|
2377
|
+
|
|
2058
2378
|
@staticmethod
|
|
2059
2379
|
def _remove_redundant_jump_blocks(ail_graph):
|
|
2060
2380
|
def first_conditional_jump(block: ailment.Block) -> ailment.Stmt.ConditionalJump | None:
|
|
@@ -2105,6 +2425,39 @@ class Clinic(Analysis):
|
|
|
2105
2425
|
ail_graph.add_edge(pred, succs[0])
|
|
2106
2426
|
ail_graph.remove_node(node)
|
|
2107
2427
|
|
|
2428
|
+
@staticmethod
|
|
2429
|
+
def _remove_redundant_jump_blocks_repatch_relifted_block(
|
|
2430
|
+
patched_block: ailment.Block, new_block: ailment.Block
|
|
2431
|
+
) -> None:
|
|
2432
|
+
"""
|
|
2433
|
+
The last statement of patched_block might have been patched by _remove_redundant_jump_blocks. In this case, we
|
|
2434
|
+
fix the last instruction for new_block, which is a newly lifted (from VEX) block that ends at the same address
|
|
2435
|
+
as patched_block.
|
|
2436
|
+
|
|
2437
|
+
:param patched_block: Previously patched block.
|
|
2438
|
+
:param new_block: Newly lifted block.
|
|
2439
|
+
"""
|
|
2440
|
+
|
|
2441
|
+
if (
|
|
2442
|
+
isinstance(patched_block.statements[-1], ailment.Stmt.Jump)
|
|
2443
|
+
and isinstance(patched_block.statements[-1].target, ailment.Expr.Const)
|
|
2444
|
+
and isinstance(new_block.statements[-1], ailment.Stmt.Jump)
|
|
2445
|
+
and isinstance(new_block.statements[-1].target, ailment.Expr.Const)
|
|
2446
|
+
and not patched_block.statements[-1].likes(new_block.statements[-1])
|
|
2447
|
+
):
|
|
2448
|
+
new_block.statements[-1].target = patched_block.statements[-1].target
|
|
2449
|
+
if (
|
|
2450
|
+
isinstance(patched_block.statements[-1], ailment.Stmt.ConditionalJump)
|
|
2451
|
+
and isinstance(patched_block.statements[-1].true_target, ailment.Expr.Const)
|
|
2452
|
+
and isinstance(patched_block.statements[-1].false_target, ailment.Expr.Const)
|
|
2453
|
+
and isinstance(new_block.statements[-1], ailment.Stmt.ConditionalJump)
|
|
2454
|
+
and isinstance(new_block.statements[-1].true_target, ailment.Expr.Const)
|
|
2455
|
+
and isinstance(new_block.statements[-1].false_target, ailment.Expr.Const)
|
|
2456
|
+
and not patched_block.statements[-1].likes(new_block.statements[-1])
|
|
2457
|
+
):
|
|
2458
|
+
new_block.statements[-1].true_target = patched_block.statements[-1].true_target
|
|
2459
|
+
new_block.statements[-1].false_target = patched_block.statements[-1].false_target
|
|
2460
|
+
|
|
2108
2461
|
@staticmethod
|
|
2109
2462
|
def _insert_block_labels(ail_graph):
|
|
2110
2463
|
for node in ail_graph.nodes:
|