angr 9.2.160__cp310-abi3-macosx_11_0_arm64.whl → 9.2.162__cp310-abi3-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +4 -1
- angr/analyses/analysis.py +0 -1
- angr/analyses/cfg/cfg_base.py +5 -1
- angr/analyses/decompiler/ail_simplifier.py +101 -2
- angr/analyses/decompiler/block_simplifier.py +13 -8
- angr/analyses/decompiler/clinic.py +1 -0
- angr/analyses/decompiler/condition_processor.py +24 -0
- angr/analyses/decompiler/counters/call_counter.py +11 -1
- angr/analyses/decompiler/decompiler.py +3 -1
- angr/analyses/decompiler/graph_region.py +11 -2
- angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +1 -1
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -0
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +31 -11
- angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +2 -0
- angr/analyses/decompiler/peephole_optimizations/__init__.py +4 -4
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +53 -0
- angr/analyses/decompiler/peephole_optimizations/modulo_simplifier.py +89 -0
- angr/analyses/decompiler/peephole_optimizations/{const_mull_a_shift.py → optimized_div_simplifier.py} +139 -25
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +18 -9
- angr/analyses/decompiler/region_simplifiers/goto.py +3 -3
- angr/analyses/decompiler/region_simplifiers/if_.py +2 -2
- angr/analyses/decompiler/region_simplifiers/loop.py +2 -2
- angr/analyses/decompiler/structured_codegen/c.py +3 -3
- angr/analyses/decompiler/structuring/dream.py +1 -1
- angr/analyses/decompiler/structuring/phoenix.py +138 -99
- angr/analyses/decompiler/structuring/recursive_structurer.py +3 -2
- angr/analyses/decompiler/structuring/sailr.py +51 -43
- angr/analyses/decompiler/structuring/structurer_base.py +2 -3
- angr/analyses/deobfuscator/string_obf_opt_passes.py +1 -1
- angr/analyses/disassembly.py +1 -1
- angr/analyses/reaching_definitions/function_handler.py +1 -0
- angr/analyses/s_propagator.py +2 -2
- angr/analyses/s_reaching_definitions/s_rda_model.py +1 -0
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +5 -2
- angr/analyses/variable_recovery/engine_base.py +17 -1
- angr/analyses/variable_recovery/variable_recovery_base.py +30 -2
- angr/analyses/variable_recovery/variable_recovery_fast.py +11 -2
- angr/emulator.py +143 -0
- angr/engines/concrete.py +66 -0
- angr/engines/icicle.py +66 -30
- angr/exploration_techniques/driller_core.py +2 -2
- angr/knowledge_plugins/functions/function.py +1 -1
- angr/knowledge_plugins/functions/function_manager.py +1 -2
- angr/project.py +7 -0
- angr/rustylib.abi3.so +0 -0
- angr/sim_type.py +16 -8
- angr/simos/javavm.py +1 -1
- angr/unicornlib.dylib +0 -0
- angr/utils/graph.py +48 -13
- angr/utils/library.py +13 -12
- angr/utils/ssa/__init__.py +57 -5
- {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/METADATA +5 -5
- {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/RECORD +57 -55
- angr/analyses/decompiler/peephole_optimizations/a_sub_a_div_const_mul_const.py +0 -57
- {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/WHEEL +0 -0
- {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/entry_points.txt +0 -0
- {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/top_level.txt +0 -0
angr/engines/icicle.py
CHANGED
|
@@ -5,17 +5,16 @@ from __future__ import annotations
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
7
|
from dataclasses import dataclass
|
|
8
|
+
from typing_extensions import override
|
|
8
9
|
|
|
9
|
-
import claripy
|
|
10
10
|
import pypcode
|
|
11
11
|
from archinfo import Arch, Endness
|
|
12
12
|
|
|
13
|
+
from angr.engines.concrete import ConcreteEngine, HeavyConcreteState
|
|
13
14
|
from angr.engines.failure import SimEngineFailure
|
|
14
15
|
from angr.engines.hook import HooksMixin
|
|
15
|
-
from angr.engines.successors import SuccessorsEngine
|
|
16
16
|
from angr.engines.syscall import SimEngineSyscall
|
|
17
17
|
from angr.rustylib.icicle import Icicle, VmExit, ExceptionCode
|
|
18
|
-
from angr.sim_state import SimState
|
|
19
18
|
|
|
20
19
|
log = logging.getLogger(__name__)
|
|
21
20
|
|
|
@@ -30,12 +29,13 @@ class IcicleStateTranslationData:
|
|
|
30
29
|
to an angr state.
|
|
31
30
|
"""
|
|
32
31
|
|
|
33
|
-
base_state:
|
|
32
|
+
base_state: HeavyConcreteState
|
|
34
33
|
registers: set[str]
|
|
35
34
|
writable_pages: set[int]
|
|
35
|
+
initial_cpu_icount: int
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class IcicleEngine(
|
|
38
|
+
class IcicleEngine(ConcreteEngine):
|
|
39
39
|
"""
|
|
40
40
|
An angr engine that uses Icicle to execute concrete states. The purpose of
|
|
41
41
|
this implementation is to provide a high-performance concrete execution
|
|
@@ -55,6 +55,16 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
55
55
|
intends to provide a more complete set of features, such as hooks and syscalls.
|
|
56
56
|
"""
|
|
57
57
|
|
|
58
|
+
breakpoints: set[int]
|
|
59
|
+
|
|
60
|
+
def __init__(self, *args, **kwargs):
|
|
61
|
+
"""
|
|
62
|
+
Initialize the IcicleEngine. This sets up the breakpoints set and
|
|
63
|
+
initializes the parent class.
|
|
64
|
+
"""
|
|
65
|
+
super().__init__(*args, **kwargs)
|
|
66
|
+
self.breakpoints = set()
|
|
67
|
+
|
|
58
68
|
@staticmethod
|
|
59
69
|
def __make_icicle_arch(arch: Arch) -> str | None:
|
|
60
70
|
"""
|
|
@@ -81,7 +91,7 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
81
91
|
return IcicleEngine.__is_arm(icicle_arch) and addr & 1 == 1
|
|
82
92
|
|
|
83
93
|
@staticmethod
|
|
84
|
-
def __get_pages(state:
|
|
94
|
+
def __get_pages(state: HeavyConcreteState) -> set[int]:
|
|
85
95
|
"""
|
|
86
96
|
Unfortunately, the memory model doesn't have a way to get all pages.
|
|
87
97
|
Instead, we can get all of the backers from the loader, then all of the
|
|
@@ -104,7 +114,7 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
104
114
|
return pages
|
|
105
115
|
|
|
106
116
|
@staticmethod
|
|
107
|
-
def __convert_angr_state_to_icicle(state:
|
|
117
|
+
def __convert_angr_state_to_icicle(state: HeavyConcreteState) -> tuple[Icicle, IcicleStateTranslationData]:
|
|
108
118
|
icicle_arch = IcicleEngine.__make_icicle_arch(state.arch)
|
|
109
119
|
if icicle_arch is None:
|
|
110
120
|
raise ValueError("Unsupported architecture")
|
|
@@ -161,12 +171,15 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
161
171
|
base_state=state,
|
|
162
172
|
registers=copied_registers,
|
|
163
173
|
writable_pages=writable_pages,
|
|
174
|
+
initial_cpu_icount=emu.cpu_icount,
|
|
164
175
|
)
|
|
165
176
|
|
|
166
177
|
return (emu, translation_data)
|
|
167
178
|
|
|
168
179
|
@staticmethod
|
|
169
|
-
def __convert_icicle_state_to_angr(
|
|
180
|
+
def __convert_icicle_state_to_angr(
|
|
181
|
+
emu: Icicle, translation_data: IcicleStateTranslationData, status: VmExit
|
|
182
|
+
) -> HeavyConcreteState:
|
|
170
183
|
state = translation_data.base_state.copy()
|
|
171
184
|
|
|
172
185
|
# 1. Copy the register values
|
|
@@ -181,20 +194,8 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
181
194
|
addr = page_num * state.memory.page_size
|
|
182
195
|
state.memory.store(addr, emu.mem_read(addr, state.memory.page_size))
|
|
183
196
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def process_successors(self, successors, *, num_inst=0, **kwargs):
|
|
187
|
-
if len(kwargs) > 0:
|
|
188
|
-
log.warning("IcicleEngine.process_successors received unknown kwargs: %s", kwargs)
|
|
189
|
-
|
|
190
|
-
emu, translation_data = self.__convert_angr_state_to_icicle(self.state)
|
|
191
|
-
|
|
192
|
-
if num_inst > 0:
|
|
193
|
-
emu.icount_limit = num_inst
|
|
194
|
-
|
|
195
|
-
status = emu.run() # pylint: ignore=assignment-from-no-return (pylint bug)
|
|
197
|
+
# 3. Set history.jumpkind
|
|
196
198
|
exc = emu.exception_code
|
|
197
|
-
|
|
198
199
|
if status == VmExit.UnhandledException:
|
|
199
200
|
if exc in (
|
|
200
201
|
ExceptionCode.ReadUnmapped,
|
|
@@ -203,22 +204,57 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
203
204
|
ExceptionCode.WritePerm,
|
|
204
205
|
ExceptionCode.ExecViolation,
|
|
205
206
|
):
|
|
206
|
-
jumpkind = "Ijk_SigSEGV"
|
|
207
|
+
state.history.jumpkind = "Ijk_SigSEGV"
|
|
207
208
|
elif exc == ExceptionCode.Syscall:
|
|
208
|
-
jumpkind = "Ijk_Syscall"
|
|
209
|
+
state.history.jumpkind = "Ijk_Syscall"
|
|
209
210
|
elif exc == ExceptionCode.Halt:
|
|
210
|
-
jumpkind = "Ijk_Exit"
|
|
211
|
+
state.history.jumpkind = "Ijk_Exit"
|
|
211
212
|
elif exc == ExceptionCode.InvalidInstruction:
|
|
212
|
-
jumpkind = "Ijk_NoDecode"
|
|
213
|
+
state.history.jumpkind = "Ijk_NoDecode"
|
|
213
214
|
else:
|
|
214
|
-
jumpkind = "Ijk_EmFail"
|
|
215
|
+
state.history.jumpkind = "Ijk_EmFail"
|
|
215
216
|
else:
|
|
216
|
-
jumpkind = "Ijk_Boring"
|
|
217
|
+
state.history.jumpkind = "Ijk_Boring"
|
|
218
|
+
|
|
219
|
+
# 4. Set history.recent_instruction_count
|
|
220
|
+
state.history.recent_instruction_count = emu.cpu_icount - translation_data.initial_cpu_icount
|
|
221
|
+
|
|
222
|
+
return state
|
|
223
|
+
|
|
224
|
+
@override
|
|
225
|
+
def get_breakpoints(self) -> set[int]:
|
|
226
|
+
"""Return the set of currently set breakpoints."""
|
|
227
|
+
return self.breakpoints
|
|
228
|
+
|
|
229
|
+
@override
|
|
230
|
+
def add_breakpoint(self, addr: int) -> None:
|
|
231
|
+
"""Add a breakpoint at the given address."""
|
|
232
|
+
self.breakpoints.add(addr)
|
|
233
|
+
|
|
234
|
+
@override
|
|
235
|
+
def remove_breakpoint(self, addr: int) -> None:
|
|
236
|
+
"""Remove a breakpoint at the given address, if present."""
|
|
237
|
+
self.breakpoints.discard(addr)
|
|
238
|
+
|
|
239
|
+
@override
|
|
240
|
+
def process_concrete(self, state: HeavyConcreteState, num_inst: int | None = None) -> HeavyConcreteState:
|
|
241
|
+
emu, translation_data = self.__convert_angr_state_to_icicle(state)
|
|
242
|
+
|
|
243
|
+
# Set breakpoints, skip the current PC. This assumes that if running
|
|
244
|
+
# with a breakpoint at the current PC, then the user has already done
|
|
245
|
+
# the necessary handling and is resuming execution.
|
|
246
|
+
for addr in self.breakpoints:
|
|
247
|
+
if emu.pc != addr:
|
|
248
|
+
emu.add_breakpoint(addr)
|
|
249
|
+
|
|
250
|
+
# Set the instruction count limit
|
|
251
|
+
if num_inst is not None and num_inst > 0:
|
|
252
|
+
emu.icount_limit = num_inst
|
|
217
253
|
|
|
218
|
-
|
|
219
|
-
|
|
254
|
+
# Run it
|
|
255
|
+
status = emu.run()
|
|
220
256
|
|
|
221
|
-
|
|
257
|
+
return IcicleEngine.__convert_icicle_state_to_angr(emu, translation_data, status)
|
|
222
258
|
|
|
223
259
|
|
|
224
260
|
class UberIcicleEngine(SimEngineFailure, SimEngineSyscall, HooksMixin, IcicleEngine):
|
|
@@ -94,7 +94,7 @@ class DrillerCore(ExplorationTechnique):
|
|
|
94
94
|
@staticmethod
|
|
95
95
|
def _has_false(state):
|
|
96
96
|
# Check if the state is unsat even if we remove preconstraints.
|
|
97
|
-
if state.scratch.guard
|
|
97
|
+
if claripy.is_false(state.scratch.guard):
|
|
98
98
|
return True
|
|
99
99
|
|
|
100
|
-
return any(
|
|
100
|
+
return any(claripy.is_false(c) for c in state.solver.constraints)
|
|
@@ -1666,7 +1666,7 @@ class Function(Serializable):
|
|
|
1666
1666
|
if self.is_rust_function():
|
|
1667
1667
|
ast = pydemumble.demangle(self.name)
|
|
1668
1668
|
return Function._rust_fmt_node(ast.split("::")[-2])
|
|
1669
|
-
func_name = get_cpp_function_name(self.demangled_name
|
|
1669
|
+
func_name = get_cpp_function_name(self.demangled_name)
|
|
1670
1670
|
return func_name.split("::")[-1]
|
|
1671
1671
|
|
|
1672
1672
|
def get_unambiguous_name(self, display_name: str | None = None) -> str:
|
|
@@ -154,8 +154,7 @@ class FunctionManager(KnowledgeBasePlugin, collections.abc.Mapping):
|
|
|
154
154
|
:return: None
|
|
155
155
|
"""
|
|
156
156
|
with open(filepath, "w", encoding="utf-8") as f:
|
|
157
|
-
for src, dst in self.callgraph.edges()
|
|
158
|
-
f.write(f"{src:#x}\tDirectEdge\t{dst:#x}\n")
|
|
157
|
+
f.writelines(f"{src:#x}\tDirectEdge\t{dst:#x}\n" for src, dst in self.callgraph.edges())
|
|
159
158
|
|
|
160
159
|
def _addr_in_plt_cached_ranges(self, addr: int) -> bool:
|
|
161
160
|
if self._rplt_cache_ranges is None:
|
angr/project.py
CHANGED
|
@@ -297,11 +297,18 @@ class Project:
|
|
|
297
297
|
|
|
298
298
|
# Step 1: get the set of libraries we are allowed to use to resolve unresolved symbols
|
|
299
299
|
missing_libs = []
|
|
300
|
+
missing_wincore_dlls = False
|
|
300
301
|
for lib_name in self.loader.missing_dependencies:
|
|
301
302
|
try:
|
|
302
303
|
missing_libs.extend(SIM_LIBRARIES[lib_name])
|
|
303
304
|
except KeyError:
|
|
304
305
|
l.info("There are no simprocedures for missing library %s :(", lib_name)
|
|
306
|
+
if lib_name.startswith("api-ms-win-"):
|
|
307
|
+
missing_wincore_dlls = True
|
|
308
|
+
if missing_wincore_dlls and "kernel32.dll" not in self.loader.missing_dependencies:
|
|
309
|
+
# some of the missing api-ms-win-*.dll libraries are actually provided by kernel32.dll
|
|
310
|
+
missing_libs.extend(SIM_LIBRARIES["kernel32.dll"])
|
|
311
|
+
|
|
305
312
|
# additionally provide libraries we _have_ loaded as a fallback fallback
|
|
306
313
|
# this helps in the case that e.g. CLE picked up a linux arm libc to satisfy an android arm binary
|
|
307
314
|
for lib in self.loader.all_objects:
|
angr/rustylib.abi3.so
CHANGED
|
Binary file
|
angr/sim_type.py
CHANGED
|
@@ -609,33 +609,41 @@ class SimTypeWideChar(SimTypeReg):
|
|
|
609
609
|
|
|
610
610
|
_base_name = "char"
|
|
611
611
|
|
|
612
|
-
def __init__(self, signed=True, label=None):
|
|
612
|
+
def __init__(self, signed=True, label=None, endness: Endness = Endness.BE):
|
|
613
613
|
"""
|
|
614
614
|
:param label: the type label.
|
|
615
615
|
"""
|
|
616
616
|
SimTypeReg.__init__(self, 16, label=label)
|
|
617
617
|
self.signed = signed
|
|
618
|
+
self.endness = endness
|
|
618
619
|
|
|
619
620
|
def __repr__(self):
|
|
620
621
|
return "wchar"
|
|
621
622
|
|
|
622
623
|
def store(self, state, addr, value: StoreType):
|
|
623
|
-
self._size = state.arch.byte_width
|
|
624
624
|
try:
|
|
625
625
|
super().store(state, addr, value)
|
|
626
626
|
except TypeError:
|
|
627
627
|
if isinstance(value, bytes) and len(value) == 2:
|
|
628
|
-
|
|
628
|
+
inner = (
|
|
629
|
+
((value[0] << state.arch.byte_width) | value[1])
|
|
630
|
+
if self.endness == Endness.BE
|
|
631
|
+
else ((value[1] << state.arch.byte_width) | value[0])
|
|
632
|
+
)
|
|
633
|
+
value = claripy.BVV(inner, state.arch.byte_width * 2)
|
|
629
634
|
super().store(state, addr, value)
|
|
630
635
|
else:
|
|
631
636
|
raise
|
|
632
637
|
|
|
633
638
|
def extract(self, state, addr, concrete=False) -> Any:
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
out = super().extract(state, addr, concrete)
|
|
639
|
+
out = state.memory.load(addr, 2)
|
|
637
640
|
if concrete:
|
|
638
|
-
|
|
641
|
+
data = state.solver.eval(out, cast_to=bytes)
|
|
642
|
+
fmt_str = "utf-16be" if self.endness == Endness.BE else "utf-16le"
|
|
643
|
+
try:
|
|
644
|
+
return data.decode(fmt_str)
|
|
645
|
+
except UnicodeDecodeError:
|
|
646
|
+
return data
|
|
639
647
|
return out
|
|
640
648
|
|
|
641
649
|
def _init_str(self):
|
|
@@ -645,7 +653,7 @@ class SimTypeWideChar(SimTypeReg):
|
|
|
645
653
|
)
|
|
646
654
|
|
|
647
655
|
def copy(self):
|
|
648
|
-
return self.__class__(signed=self.signed, label=self.label)
|
|
656
|
+
return self.__class__(signed=self.signed, label=self.label, endness=self.endness)
|
|
649
657
|
|
|
650
658
|
|
|
651
659
|
class SimTypeBool(SimTypeReg):
|
angr/simos/javavm.py
CHANGED
|
@@ -255,7 +255,7 @@ class SimJavaVM(SimOS):
|
|
|
255
255
|
upper = native_arg_value.get_bytes(0, 4)
|
|
256
256
|
lower = native_arg_value.get_bytes(4, 4)
|
|
257
257
|
idx = args.index(arg)
|
|
258
|
-
args = args[:idx]
|
|
258
|
+
args = (*args[:idx], SootArgument(upper, "int"), SootArgument(lower, "int"), *args[idx + 1 :])
|
|
259
259
|
native_arg_values += [upper, lower]
|
|
260
260
|
continue
|
|
261
261
|
if type(arg.value) is BV and len(arg.value) > arg_ty.size:
|
angr/unicornlib.dylib
CHANGED
|
Binary file
|
angr/utils/graph.py
CHANGED
|
@@ -55,7 +55,7 @@ def inverted_idoms(graph: networkx.DiGraph) -> tuple[networkx.DiGraph, dict | No
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def to_acyclic_graph(
|
|
58
|
-
graph: networkx.DiGraph,
|
|
58
|
+
graph: networkx.DiGraph, node_order: dict[Any, int] | None = None, loop_heads: list | None = None
|
|
59
59
|
) -> networkx.DiGraph:
|
|
60
60
|
"""
|
|
61
61
|
Convert a given DiGraph into an acyclic graph.
|
|
@@ -66,21 +66,22 @@ def to_acyclic_graph(
|
|
|
66
66
|
:return: The converted acyclic graph.
|
|
67
67
|
"""
|
|
68
68
|
|
|
69
|
-
if
|
|
69
|
+
if node_order is None:
|
|
70
70
|
# take the quasi-topological order of the graph
|
|
71
71
|
ordered_nodes = GraphUtils.quasi_topological_sort_nodes(graph, loop_heads=loop_heads)
|
|
72
|
-
|
|
73
|
-
acyclic_graph = networkx.DiGraph()
|
|
72
|
+
node_order = {n: i for i, n in enumerate(ordered_nodes)}
|
|
74
73
|
|
|
75
74
|
# add each node and its edge into the graph
|
|
76
|
-
|
|
77
|
-
for
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
edges_to_remove = []
|
|
76
|
+
for src, dst in graph.edges():
|
|
77
|
+
src_order = node_order[src]
|
|
78
|
+
dst_order = node_order[dst]
|
|
79
|
+
if src_order > dst_order:
|
|
80
|
+
# this is a back edge, we need to remove it
|
|
81
|
+
edges_to_remove.append((src, dst))
|
|
82
|
+
|
|
83
|
+
acyclic_graph = graph.copy()
|
|
84
|
+
acyclic_graph.remove_edges_from(edges_to_remove)
|
|
84
85
|
return acyclic_graph
|
|
85
86
|
|
|
86
87
|
|
|
@@ -534,11 +535,30 @@ class Dominators:
|
|
|
534
535
|
|
|
535
536
|
def _pd_eval(self, v):
|
|
536
537
|
assert self._ancestor is not None
|
|
538
|
+
assert self._semi is not None
|
|
537
539
|
assert self._label is not None
|
|
538
540
|
|
|
539
541
|
if self._ancestor[v.index] is None:
|
|
540
542
|
return v
|
|
541
|
-
|
|
543
|
+
|
|
544
|
+
# pd_compress without recursion
|
|
545
|
+
queue = []
|
|
546
|
+
current = v
|
|
547
|
+
ancestor = self._ancestor[current.index]
|
|
548
|
+
greater_ancestor = self._ancestor[ancestor.index]
|
|
549
|
+
while greater_ancestor is not None:
|
|
550
|
+
queue.append(current)
|
|
551
|
+
current, ancestor = ancestor, greater_ancestor
|
|
552
|
+
greater_ancestor = self._ancestor[ancestor.index]
|
|
553
|
+
|
|
554
|
+
for vv in reversed(queue):
|
|
555
|
+
if (
|
|
556
|
+
self._semi[self._label[self._ancestor[vv.index].index].index].index
|
|
557
|
+
< self._semi[self._label[vv.index].index].index
|
|
558
|
+
):
|
|
559
|
+
self._label[vv.index] = self._label[self._ancestor[vv.index].index]
|
|
560
|
+
self._ancestor[vv.index] = self._ancestor[self._ancestor[vv.index].index]
|
|
561
|
+
|
|
542
562
|
return self._label[v.index]
|
|
543
563
|
|
|
544
564
|
def _pd_compress(self, v):
|
|
@@ -653,6 +673,21 @@ class GraphUtils:
|
|
|
653
673
|
|
|
654
674
|
return list(widening_addrs)
|
|
655
675
|
|
|
676
|
+
@staticmethod
|
|
677
|
+
def dfs_postorder_nodes_deterministic(graph: networkx.DiGraph, source):
|
|
678
|
+
visited = set()
|
|
679
|
+
stack = [source]
|
|
680
|
+
while stack:
|
|
681
|
+
node = stack[-1]
|
|
682
|
+
if node not in visited:
|
|
683
|
+
visited.add(node)
|
|
684
|
+
for succ in sorted(graph.successors(node), key=GraphUtils._sort_node):
|
|
685
|
+
if succ not in visited:
|
|
686
|
+
stack.append(succ)
|
|
687
|
+
else:
|
|
688
|
+
yield node
|
|
689
|
+
stack.pop()
|
|
690
|
+
|
|
656
691
|
@staticmethod
|
|
657
692
|
def reverse_post_order_sort_nodes(graph, nodes=None):
|
|
658
693
|
"""
|
angr/utils/library.py
CHANGED
|
@@ -168,7 +168,7 @@ def parsedcprotos2py(
|
|
|
168
168
|
proto_.returnty = SimTypeFd(label=proto_.returnty.label)
|
|
169
169
|
for i, arg in enumerate(proto_.args):
|
|
170
170
|
if (func_name, i) in fd_spots:
|
|
171
|
-
proto_.args = proto_.args[:i]
|
|
171
|
+
proto_.args = (*proto_.args[:i], SimTypeFd(label=arg.label), *proto_.args[i + 1 :])
|
|
172
172
|
|
|
173
173
|
line1 = " " * 8 + "#" + ((" " + decl) if decl else "") + "\n"
|
|
174
174
|
line2 = " " * 8 + repr(func_name) + ": " + (proto_._init_str() if proto_ is not None else "None") + "," + "\n"
|
|
@@ -195,17 +195,18 @@ def cprotos2py(cprotos: list[str], fd_spots=frozenset(), remove_sys_prefix=False
|
|
|
195
195
|
return parsedcprotos2py(parsed_cprotos, fd_spots=fd_spots, remove_sys_prefix=remove_sys_prefix)
|
|
196
196
|
|
|
197
197
|
|
|
198
|
-
def get_cpp_function_name(demangled_name
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
def get_cpp_function_name(demangled_name: str) -> str:
|
|
199
|
+
"""
|
|
200
|
+
Parse a demangled C++ declaration into a function name.
|
|
201
201
|
|
|
202
|
-
|
|
203
|
-
# remove leading namespaces
|
|
204
|
-
chunks = name.split("::")
|
|
205
|
-
name = "::".join(chunks[2:])
|
|
202
|
+
Note that the extracted name may include template instantiation, for example:
|
|
206
203
|
|
|
207
|
-
|
|
208
|
-
if "(" in name:
|
|
209
|
-
name = name[: name.find("(")]
|
|
204
|
+
example_func<int>
|
|
210
205
|
|
|
211
|
-
|
|
206
|
+
:param demangled_name: The demangled C++ function name.
|
|
207
|
+
:return: The qualified function name, excluding return type and parameters.
|
|
208
|
+
"""
|
|
209
|
+
func_decls, _ = parse_cpp_file(demangled_name)
|
|
210
|
+
if func_decls and len(func_decls) == 1:
|
|
211
|
+
return next(iter(func_decls))
|
|
212
|
+
return normalize_cpp_function_name(demangled_name)
|
angr/utils/ssa/__init__.py
CHANGED
|
@@ -6,7 +6,7 @@ from typing import Any, Literal, overload
|
|
|
6
6
|
import networkx
|
|
7
7
|
|
|
8
8
|
import archinfo
|
|
9
|
-
from angr.ailment import Expression, Block
|
|
9
|
+
from angr.ailment import Expression, Block, UnaryOp
|
|
10
10
|
from angr.ailment.expression import (
|
|
11
11
|
VirtualVariable,
|
|
12
12
|
Const,
|
|
@@ -42,7 +42,7 @@ def get_reg_offset_base_and_size(
|
|
|
42
42
|
|
|
43
43
|
def get_reg_offset_base_and_size(
|
|
44
44
|
reg_offset: int, arch: archinfo.Arch, size: int | None = None, resilient: bool = True
|
|
45
|
-
) -> tuple[int, int] | None:
|
|
45
|
+
) -> tuple[int, int | None] | None:
|
|
46
46
|
"""
|
|
47
47
|
Translate a given register offset into the offset of its full register and obtain the size of the full register.
|
|
48
48
|
|
|
@@ -89,7 +89,7 @@ def get_reg_offset_base(reg_offset, arch, size=None, resilient=True):
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
def get_vvar_deflocs(
|
|
92
|
-
blocks, phi_vvars: dict[int, set[int]] | None = None
|
|
92
|
+
blocks, phi_vvars: dict[int, set[int | None]] | None = None
|
|
93
93
|
) -> dict[int, tuple[VirtualVariable, CodeLocation]]:
|
|
94
94
|
vvar_to_loc: dict[int, tuple[VirtualVariable, CodeLocation]] = {}
|
|
95
95
|
for block in blocks:
|
|
@@ -100,7 +100,7 @@ def get_vvar_deflocs(
|
|
|
100
100
|
)
|
|
101
101
|
if phi_vvars is not None and isinstance(stmt.src, Phi):
|
|
102
102
|
phi_vvars[stmt.dst.varid] = {
|
|
103
|
-
vvar_.varid for src, vvar_ in stmt.src.src_and_vvars
|
|
103
|
+
vvar_.varid if vvar_ is not None else None for src, vvar_ in stmt.src.src_and_vvars
|
|
104
104
|
}
|
|
105
105
|
elif isinstance(stmt, Call):
|
|
106
106
|
if isinstance(stmt.ret_expr, VirtualVariable):
|
|
@@ -161,7 +161,7 @@ def get_tmp_uselocs(blocks) -> dict[CodeLocation, dict[atoms.Tmp, set[tuple[Tmp,
|
|
|
161
161
|
return tmp_to_loc
|
|
162
162
|
|
|
163
163
|
|
|
164
|
-
def is_const_assignment(stmt: Statement) -> tuple[bool, Const | None]:
|
|
164
|
+
def is_const_assignment(stmt: Statement) -> tuple[bool, Const | StackBaseOffset | None]:
|
|
165
165
|
if isinstance(stmt, Assignment) and isinstance(stmt.src, (Const, StackBaseOffset)):
|
|
166
166
|
return True, stmt.src
|
|
167
167
|
return False, None
|
|
@@ -278,6 +278,31 @@ def has_tmp_expr(expr: Expression) -> bool:
|
|
|
278
278
|
return walker.has_blacklisted_exprs
|
|
279
279
|
|
|
280
280
|
|
|
281
|
+
class AILReferenceFinder(AILBlockWalkerBase):
|
|
282
|
+
"""
|
|
283
|
+
Walks an AIL expression or statement and finds if it contains references to certain expressions.
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
def __init__(self, vvar_id: int):
|
|
287
|
+
super().__init__()
|
|
288
|
+
self.vvar_id = vvar_id
|
|
289
|
+
self.has_references_to_vvar = False
|
|
290
|
+
|
|
291
|
+
def _handle_UnaryOp(
|
|
292
|
+
self, expr_idx: int, expr: UnaryOp, stmt_idx: int, stmt: Statement | None, block: Block | None
|
|
293
|
+
) -> Any:
|
|
294
|
+
if expr.op == "Reference" and isinstance(expr.operand, VirtualVariable) and expr.operand.varid == self.vvar_id:
|
|
295
|
+
self.has_references_to_vvar = True
|
|
296
|
+
return None
|
|
297
|
+
return super()._handle_UnaryOp(expr_idx, expr, stmt_idx, stmt, block)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def has_reference_to_vvar(stmt: Statement, vvar_id: int) -> bool:
|
|
301
|
+
walker = AILReferenceFinder(vvar_id)
|
|
302
|
+
walker.walk_statement(stmt)
|
|
303
|
+
return walker.has_references_to_vvar
|
|
304
|
+
|
|
305
|
+
|
|
281
306
|
def check_in_between_stmts(
|
|
282
307
|
graph: networkx.DiGraph,
|
|
283
308
|
blocks: dict[tuple[int, int | None], Block],
|
|
@@ -358,6 +383,33 @@ def has_load_expr_in_between_stmts(
|
|
|
358
383
|
)
|
|
359
384
|
|
|
360
385
|
|
|
386
|
+
def is_vvar_propagatable(vvar: VirtualVariable, def_stmt: Statement | None) -> bool:
|
|
387
|
+
if vvar.was_tmp or vvar.was_reg or vvar.was_parameter:
|
|
388
|
+
return True
|
|
389
|
+
if vvar.was_stack and isinstance(def_stmt, Assignment):
|
|
390
|
+
if isinstance(def_stmt.src, Const):
|
|
391
|
+
return True
|
|
392
|
+
if (
|
|
393
|
+
isinstance(def_stmt.src, VirtualVariable)
|
|
394
|
+
and def_stmt.src.was_stack
|
|
395
|
+
and def_stmt.src.stack_offset == vvar.stack_offset
|
|
396
|
+
):
|
|
397
|
+
# special case: the following block
|
|
398
|
+
# ## Block 401e98
|
|
399
|
+
# 00 | 0x401e98 | LABEL_401e98:
|
|
400
|
+
# 01 | 0x401e98 | vvar_227{stack -12} = 𝜙@32b [((4202088, None), vvar_277{stack -12}), ((4202076, None),
|
|
401
|
+
# vvar_278{stack -12})]
|
|
402
|
+
# 02 | 0x401ea0 | return Conv(32->64, vvar_227{stack -12});
|
|
403
|
+
# might be simplified to the following block after return duplication
|
|
404
|
+
# ## Block 401e98.1
|
|
405
|
+
# 00 | 0x401e98 | LABEL_401e98__1:
|
|
406
|
+
# 01 | 0x401e98 | vvar_279{stack -12} = vvar_277{stack -12}
|
|
407
|
+
# 02 | 0x401ea0 | return Conv(32->64, vvar_279{stack -12});
|
|
408
|
+
# in this case, vvar_279 is eliminatable.
|
|
409
|
+
return True
|
|
410
|
+
return False
|
|
411
|
+
|
|
412
|
+
|
|
361
413
|
def is_vvar_eliminatable(vvar: VirtualVariable, def_stmt: Statement | None) -> bool:
|
|
362
414
|
if vvar.was_tmp or vvar.was_reg or vvar.was_parameter:
|
|
363
415
|
return True
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: angr
|
|
3
|
-
Version: 9.2.
|
|
3
|
+
Version: 9.2.162
|
|
4
4
|
Summary: A multi-architecture binary analysis toolkit, with the ability to perform dynamic symbolic execution and various static analyses on binaries
|
|
5
5
|
License: BSD-2-Clause
|
|
6
6
|
Project-URL: Homepage, https://angr.io/
|
|
@@ -16,12 +16,12 @@ Description-Content-Type: text/markdown
|
|
|
16
16
|
License-File: LICENSE
|
|
17
17
|
Requires-Dist: cxxheaderparser
|
|
18
18
|
Requires-Dist: GitPython
|
|
19
|
-
Requires-Dist: archinfo==9.2.
|
|
19
|
+
Requires-Dist: archinfo==9.2.162
|
|
20
20
|
Requires-Dist: cachetools
|
|
21
21
|
Requires-Dist: capstone==5.0.3
|
|
22
22
|
Requires-Dist: cffi>=1.14.0
|
|
23
|
-
Requires-Dist: claripy==9.2.
|
|
24
|
-
Requires-Dist: cle==9.2.
|
|
23
|
+
Requires-Dist: claripy==9.2.162
|
|
24
|
+
Requires-Dist: cle==9.2.162
|
|
25
25
|
Requires-Dist: mulpyplexer
|
|
26
26
|
Requires-Dist: networkx!=2.8.1,>=2.0
|
|
27
27
|
Requires-Dist: protobuf>=5.28.2
|
|
@@ -30,7 +30,7 @@ Requires-Dist: pycparser>=2.18
|
|
|
30
30
|
Requires-Dist: pydemumble
|
|
31
31
|
Requires-Dist: pyformlang
|
|
32
32
|
Requires-Dist: pypcode<4.0,>=3.2.1
|
|
33
|
-
Requires-Dist: pyvex==9.2.
|
|
33
|
+
Requires-Dist: pyvex==9.2.162
|
|
34
34
|
Requires-Dist: rich>=13.1.0
|
|
35
35
|
Requires-Dist: sortedcontainers
|
|
36
36
|
Requires-Dist: sympy
|