angr 9.2.161__cp310-abi3-manylinux2014_x86_64.whl → 9.2.163__cp310-abi3-manylinux2014_x86_64.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/ailment/expression.py +12 -0
- angr/analyses/analysis.py +0 -1
- angr/analyses/cfg/cfg_base.py +6 -2
- angr/analyses/decompiler/ail_simplifier.py +20 -1
- angr/analyses/decompiler/block_simplifier.py +6 -3
- angr/analyses/decompiler/clinic.py +6 -6
- 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/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 +119 -67
- 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/fcp/fcp.py +11 -10
- angr/analyses/flirt/flirt_sig.py +5 -2
- angr/analyses/reaching_definitions/function_handler.py +2 -1
- angr/analyses/reaching_definitions/function_handler_library/stdio.py +7 -6
- angr/analyses/reaching_definitions/function_handler_library/stdlib.py +10 -4
- angr/analyses/reaching_definitions/function_handler_library/string.py +13 -2
- angr/analyses/reaching_definitions/function_handler_library/unistd.py +7 -0
- angr/analyses/s_propagator.py +2 -2
- angr/analyses/variable_recovery/engine_base.py +8 -4
- angr/knowledge_plugins/functions/function.py +1 -1
- angr/knowledge_plugins/functions/function_manager.py +1 -2
- angr/project.py +5 -2
- angr/rustylib.abi3.so +0 -0
- angr/sim_type.py +2 -2
- angr/simos/javavm.py +1 -1
- angr/utils/graph.py +28 -12
- angr/utils/library.py +13 -12
- angr/utils/ssa/__init__.py +54 -2
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/METADATA +5 -5
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/RECORD +50 -50
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/WHEEL +0 -0
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/entry_points.txt +0 -0
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.161.dist-info → angr-9.2.163.dist-info}/top_level.txt +0 -0
angr/analyses/flirt/flirt_sig.py
CHANGED
|
@@ -146,7 +146,7 @@ class FlirtSignatureParsed:
|
|
|
146
146
|
name_lst = []
|
|
147
147
|
name_end = False # in case the function name is too long...
|
|
148
148
|
for _ in range(1024): # max length of a function name
|
|
149
|
-
if next_byte < 0x20:
|
|
149
|
+
if next_byte < 0x20 or next_byte >= 0x80:
|
|
150
150
|
name_end = True
|
|
151
151
|
break
|
|
152
152
|
name_lst.append(next_byte)
|
|
@@ -347,7 +347,10 @@ class FlirtSignatureParsed:
|
|
|
347
347
|
# is it compressed?
|
|
348
348
|
if obj.features & FlirtFeatureFlag.FEATURE_COMPRESSED:
|
|
349
349
|
data = file_obj.read()
|
|
350
|
-
|
|
350
|
+
try:
|
|
351
|
+
decompressed = BytesIO(zlib.decompress(data))
|
|
352
|
+
except zlib.error as ex:
|
|
353
|
+
raise FlirtSignatureError(f"Failed to decompress FLIRT signature: {ex}") from ex
|
|
351
354
|
file_obj = decompressed
|
|
352
355
|
|
|
353
356
|
root = obj.parse_tree(file_obj, root=True)
|
|
@@ -575,7 +575,7 @@ class FunctionHandler:
|
|
|
575
575
|
sub_rda = state.analysis.project.analyses.ReachingDefinitions(
|
|
576
576
|
data.function,
|
|
577
577
|
observe_all=state.analysis._observe_all,
|
|
578
|
-
observation_points=list(state.analysis._observation_points or [])
|
|
578
|
+
observation_points=list(state.analysis._observation_points or []) + return_observation_points,
|
|
579
579
|
observe_callback=state.analysis._observe_callback,
|
|
580
580
|
dep_graph=state.dep_graph,
|
|
581
581
|
function_handler=self,
|
|
@@ -584,6 +584,7 @@ class FunctionHandler:
|
|
|
584
584
|
# migrate data from sub_rda to its parent
|
|
585
585
|
state.analysis.function_calls.update(sub_rda.function_calls)
|
|
586
586
|
state.analysis.model.observed_results.update(sub_rda.model.observed_results)
|
|
587
|
+
state.all_definitions |= sub_rda.all_definitions
|
|
587
588
|
|
|
588
589
|
sub_ld = get_exit_livedefinitions(data.function, sub_rda.model)
|
|
589
590
|
if sub_ld is not None:
|
|
@@ -156,14 +156,14 @@ class LibcStdioHandlers(FunctionHandler):
|
|
|
156
156
|
@FunctionCallDataUnwrapped.decorate
|
|
157
157
|
def handle_impl_fread(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
158
158
|
size = state.get_concrete_value(data.args_atoms[1]) or 1
|
|
159
|
-
nmemb = state.get_concrete_value(data.args_atoms[
|
|
159
|
+
nmemb = state.get_concrete_value(data.args_atoms[2]) or 2
|
|
160
160
|
dst_atom = state.deref(data.args_atoms[0], size * nmemb)
|
|
161
161
|
data.depends(dst_atom, StdinAtom("fread", size * nmemb))
|
|
162
162
|
|
|
163
163
|
@FunctionCallDataUnwrapped.decorate
|
|
164
164
|
def handle_impl_fwrite(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
165
165
|
size = state.get_concrete_value(data.args_atoms[1]) or 1
|
|
166
|
-
nmemb = state.get_concrete_value(data.args_atoms[
|
|
166
|
+
nmemb = state.get_concrete_value(data.args_atoms[2]) or 2
|
|
167
167
|
src_atom = state.deref(data.args_atoms[0], size * nmemb)
|
|
168
168
|
data.depends(StdoutAtom("fwrite", size * nmemb), src_atom, value=state.get_values(src_atom))
|
|
169
169
|
|
|
@@ -206,8 +206,8 @@ def handle_printf(
|
|
|
206
206
|
elif fmt == "%u":
|
|
207
207
|
buf_atoms = atom
|
|
208
208
|
buf_data = state.get_concrete_value(buf_atoms)
|
|
209
|
-
if buf_data
|
|
210
|
-
|
|
209
|
+
buf_data = str(buf_data).encode() if buf_data else b"0"
|
|
210
|
+
|
|
211
211
|
elif fmt == "%d":
|
|
212
212
|
buf_atoms = atom
|
|
213
213
|
buf_data = state.get_concrete_value(buf_atoms)
|
|
@@ -215,11 +215,12 @@ def handle_printf(
|
|
|
215
215
|
if buf_data >= 2**31:
|
|
216
216
|
buf_data -= 2**32
|
|
217
217
|
buf_data = str(buf_data).encode()
|
|
218
|
+
else:
|
|
219
|
+
buf_data = b"0"
|
|
218
220
|
elif fmt == "%c":
|
|
219
221
|
buf_atoms = atom
|
|
220
222
|
buf_data = state.get_concrete_value(atom)
|
|
221
|
-
if buf_data
|
|
222
|
-
buf_data = chr(buf_data).encode()
|
|
223
|
+
buf_data = chr(buf_data).encode() if buf_data else b"0"
|
|
223
224
|
else:
|
|
224
225
|
_l.warning("Unimplemented printf format string %s", fmt)
|
|
225
226
|
buf_atoms = set()
|
|
@@ -64,21 +64,25 @@ class LibcStdlibHandlers(FunctionHandler):
|
|
|
64
64
|
buf_value = int(buf_value.decode().strip("\0"))
|
|
65
65
|
except ValueError:
|
|
66
66
|
buf_value = 0
|
|
67
|
+
else:
|
|
68
|
+
buf_value = 0 # Make the value concrete if it cannot be parsed
|
|
67
69
|
data.depends(data.ret_atoms, buf_atoms, value=buf_value)
|
|
68
70
|
|
|
69
71
|
@FunctionCallDataUnwrapped.decorate
|
|
70
72
|
def handle_impl_malloc(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
71
73
|
malloc_size = state.get_concrete_value(data.args_atoms[0]) or 48
|
|
72
74
|
heap_ptr = state.heap_allocator.allocate(malloc_size)
|
|
73
|
-
|
|
75
|
+
heap_addr = state.heap_address(heap_ptr)
|
|
76
|
+
data.depends(data.ret_atoms, value=heap_addr)
|
|
74
77
|
|
|
75
78
|
@FunctionCallDataUnwrapped.decorate
|
|
76
79
|
def handle_impl_calloc(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
77
80
|
nmemb = state.get_concrete_value(data.args_atoms[0]) or 48
|
|
78
81
|
size = state.get_concrete_value(data.args_atoms[1]) or 1
|
|
79
|
-
heap_ptr = state.
|
|
80
|
-
|
|
81
|
-
data.depends(
|
|
82
|
+
heap_ptr = state.heap_allocator.allocate(nmemb * size)
|
|
83
|
+
heap_addr = state.heap_address(heap_ptr)
|
|
84
|
+
data.depends(state.deref(heap_addr, nmemb * size), value=0)
|
|
85
|
+
data.depends(data.ret_atoms, value=heap_addr)
|
|
82
86
|
|
|
83
87
|
@FunctionCallDataUnwrapped.decorate
|
|
84
88
|
def handle_impl_getenv(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
@@ -156,6 +160,8 @@ class LibcStdlibHandlers(FunctionHandler):
|
|
|
156
160
|
def handle_impl_execve(self, state: ReachingDefinitionsState, data: FunctionCallDataUnwrapped):
|
|
157
161
|
argv_value = state.get_one_value(data.args_atoms[1])
|
|
158
162
|
if argv_value is None:
|
|
163
|
+
# Model execve with unknown argv - still has system effects
|
|
164
|
+
data.depends(SystemAtom(1), data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
|
|
159
165
|
return
|
|
160
166
|
|
|
161
167
|
nonce = random.randint(1, 999999999)
|
|
@@ -30,7 +30,7 @@ class LibcStringHandlers(FunctionHandler):
|
|
|
30
30
|
dest_value = src0_value.concat(MultiValues(top_val))
|
|
31
31
|
dest_atom = state.deref(data.args_atoms[0], len(dest_value) // 8, endness=archinfo.Endness.BE)
|
|
32
32
|
else:
|
|
33
|
-
dest_value =
|
|
33
|
+
dest_value = state.top(state.arch.bits)
|
|
34
34
|
dest_atom = src0_atom
|
|
35
35
|
if src0_atom is not None and src1_atom is not None:
|
|
36
36
|
data.depends(dest_atom, src0_atom, src1_atom, value=dest_value)
|
|
@@ -109,7 +109,8 @@ class LibcStringHandlers(FunctionHandler):
|
|
|
109
109
|
src_str = state.get_values(src_atom)
|
|
110
110
|
malloc_size = len(src_str) // 8 if src_str is not None else 1
|
|
111
111
|
heap_ptr = state.heap_allocator.allocate(malloc_size)
|
|
112
|
-
|
|
112
|
+
heap_addr = state.heap_address(heap_ptr)
|
|
113
|
+
dst_atom = state.deref(heap_addr, malloc_size)
|
|
113
114
|
data.depends(dst_atom, src_atom, value=src_str)
|
|
114
115
|
data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
|
|
115
116
|
|
|
@@ -121,6 +122,16 @@ class LibcStringHandlers(FunctionHandler):
|
|
|
121
122
|
dst_atom = state.deref(data.args_atoms[0], size)
|
|
122
123
|
if src_atom is not None:
|
|
123
124
|
data.depends(dst_atom, src_atom, value=state.get_values(src_atom))
|
|
125
|
+
else:
|
|
126
|
+
# When size is unknown, use default size and create TOP value
|
|
127
|
+
default_size = state.arch.bytes
|
|
128
|
+
src_atom = state.deref(data.args_atoms[1], default_size)
|
|
129
|
+
dst_atom = state.deref(data.args_atoms[0], default_size)
|
|
130
|
+
if src_atom is not None:
|
|
131
|
+
top_val = state.top(default_size * 8)
|
|
132
|
+
for defn in state.get_definitions(src_atom):
|
|
133
|
+
top_val = state.annotate_with_def(top_val, defn)
|
|
134
|
+
data.depends(dst_atom, src_atom, value=MultiValues(top_val))
|
|
124
135
|
data.depends(data.ret_atoms, data.args_atoms[0], value=state.get_values(data.args_atoms[0]))
|
|
125
136
|
|
|
126
137
|
@FunctionCallDataUnwrapped.decorate
|
|
@@ -32,6 +32,9 @@ class LibcUnistdHandlers(FunctionHandler):
|
|
|
32
32
|
buf_data = state.top(size * 8) if size is not None else state.top(state.arch.bits)
|
|
33
33
|
|
|
34
34
|
data.depends(dst_atom, fd_atom, value=buf_data)
|
|
35
|
+
# Model return value - number of bytes read (could be 0 to size, or -1 for error)
|
|
36
|
+
ret_val = state.top(state.arch.bits) # Unknown number of bytes read
|
|
37
|
+
data.depends(data.ret_atoms, fd_atom, value=ret_val)
|
|
35
38
|
|
|
36
39
|
handle_impl_recv = handle_impl_recvfrom = handle_impl_read
|
|
37
40
|
|
|
@@ -41,4 +44,8 @@ class LibcUnistdHandlers(FunctionHandler):
|
|
|
41
44
|
src_atom = state.deref(data.args_atoms[1], size)
|
|
42
45
|
data.depends(StdoutAtom(data.function.name, size), src_atom, value=state.get_values(src_atom))
|
|
43
46
|
|
|
47
|
+
# Model return value - number of bytes written (could be 0 to size, or -1 for error)
|
|
48
|
+
ret_val = state.top(state.arch.bits) # Unknown number of bytes written
|
|
49
|
+
data.depends(data.ret_atoms, value=ret_val)
|
|
50
|
+
|
|
44
51
|
handle_impl_send = handle_impl_write
|
angr/analyses/s_propagator.py
CHANGED
|
@@ -33,7 +33,7 @@ from angr.utils.ssa import (
|
|
|
33
33
|
is_const_vvar_load_assignment,
|
|
34
34
|
is_const_vvar_load_dirty_assignment,
|
|
35
35
|
is_const_vvar_tmp_assignment,
|
|
36
|
-
|
|
36
|
+
is_vvar_propagatable,
|
|
37
37
|
get_tmp_uselocs,
|
|
38
38
|
get_tmp_deflocs,
|
|
39
39
|
phi_assignment_get_src,
|
|
@@ -246,7 +246,7 @@ class SPropagatorAnalysis(Analysis):
|
|
|
246
246
|
self.model.dead_vvar_ids.add(vvar.varid)
|
|
247
247
|
continue
|
|
248
248
|
|
|
249
|
-
if
|
|
249
|
+
if is_vvar_propagatable(vvar, stmt):
|
|
250
250
|
if len(vvar_uselocs_set) == 1:
|
|
251
251
|
vvar_used, vvar_useloc = next(iter(vvar_uselocs_set))
|
|
252
252
|
if (
|
|
@@ -142,6 +142,8 @@ class SimEngineVRBase(
|
|
|
142
142
|
) -> list[tuple[SimVariable, int]]:
|
|
143
143
|
data = richr_addr.data
|
|
144
144
|
|
|
145
|
+
variable: SimVariable | None = None
|
|
146
|
+
|
|
145
147
|
if self.state.is_stack_address(data):
|
|
146
148
|
# this is a stack address
|
|
147
149
|
# extract stack offset
|
|
@@ -157,7 +159,6 @@ class SimEngineVRBase(
|
|
|
157
159
|
for candidate, offset in var_candidates:
|
|
158
160
|
if isinstance(candidate, SimStackVariable) and candidate.offset == stack_offset:
|
|
159
161
|
existing_vars.append((candidate, offset))
|
|
160
|
-
variable = None
|
|
161
162
|
if existing_vars:
|
|
162
163
|
variable, _ = existing_vars[0]
|
|
163
164
|
|
|
@@ -178,6 +179,9 @@ class SimEngineVRBase(
|
|
|
178
179
|
for var_stack_offset, var in self.state.extract_variables(v):
|
|
179
180
|
existing_vars.append((var, var_stack_offset))
|
|
180
181
|
|
|
182
|
+
if not existing_vars:
|
|
183
|
+
existing_vars = [(v, 0) for v in variable_manager.find_variables_by_stack_offset(stack_offset)]
|
|
184
|
+
|
|
181
185
|
if not existing_vars:
|
|
182
186
|
# no variables exist
|
|
183
187
|
lea_size = 1
|
|
@@ -185,10 +189,10 @@ class SimEngineVRBase(
|
|
|
185
189
|
stack_offset,
|
|
186
190
|
lea_size,
|
|
187
191
|
base="bp",
|
|
188
|
-
ident=
|
|
192
|
+
ident=variable_manager.next_variable_ident("stack"),
|
|
189
193
|
region=self.func_addr,
|
|
190
194
|
)
|
|
191
|
-
|
|
195
|
+
variable_manager.add_variable("stack", stack_offset, variable)
|
|
192
196
|
l.debug("Identified a new stack variable %s at %#x.", variable, self.ins_addr)
|
|
193
197
|
existing_vars.append((variable, 0))
|
|
194
198
|
|
|
@@ -555,7 +559,7 @@ class SimEngineVRBase(
|
|
|
555
559
|
|
|
556
560
|
if isinstance(stack_offset, int):
|
|
557
561
|
expr = data.data
|
|
558
|
-
if isinstance(expr, claripy.ast.
|
|
562
|
+
if isinstance(expr, claripy.ast.BV) and expr.size() > 1024:
|
|
559
563
|
# we don't write more than 256 bytes to the stack at a time for performance reasons
|
|
560
564
|
expr = expr[expr.size() - 1 : expr.size() - 1024]
|
|
561
565
|
expr = self.state.annotate_with_variables(expr, [(variable_offset, variable)])
|
|
@@ -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
|
@@ -32,7 +32,10 @@ def load_shellcode(shellcode: bytes | str, arch, start_offset=0, load_address=0,
|
|
|
32
32
|
if not isinstance(arch, archinfo.Arch):
|
|
33
33
|
arch = archinfo.arch_from_id(arch)
|
|
34
34
|
if isinstance(shellcode, str):
|
|
35
|
-
shellcode_bytes
|
|
35
|
+
shellcode_bytes = arch.asm(shellcode, load_address, thumb=thumb)
|
|
36
|
+
if shellcode_bytes is None:
|
|
37
|
+
raise ValueError("Could not assemble shellcode")
|
|
38
|
+
assert isinstance(shellcode_bytes, bytes)
|
|
36
39
|
else:
|
|
37
40
|
shellcode_bytes = shellcode
|
|
38
41
|
if thumb:
|
|
@@ -173,7 +176,7 @@ class Project:
|
|
|
173
176
|
|
|
174
177
|
# It doesn't make any sense to have auto_load_libs
|
|
175
178
|
# if you have the concrete target, let's warn the user about this.
|
|
176
|
-
if self.concrete_target and load_options.get("auto_load_libs"
|
|
179
|
+
if self.concrete_target and load_options.get("auto_load_libs"):
|
|
177
180
|
l.critical(
|
|
178
181
|
"Incompatible options selected for this project, please disable auto_load_libs if "
|
|
179
182
|
"you want to use a concrete target."
|
angr/rustylib.abi3.so
CHANGED
|
Binary file
|
angr/sim_type.py
CHANGED
|
@@ -3342,7 +3342,7 @@ def _decl_to_type(
|
|
|
3342
3342
|
|
|
3343
3343
|
if decl.name is not None:
|
|
3344
3344
|
key = "struct " + decl.name
|
|
3345
|
-
struct = extra_types.get(key
|
|
3345
|
+
struct = extra_types.get(key)
|
|
3346
3346
|
from_global = False
|
|
3347
3347
|
if struct is None:
|
|
3348
3348
|
struct = ALL_TYPES.get(key)
|
|
@@ -3378,7 +3378,7 @@ def _decl_to_type(
|
|
|
3378
3378
|
|
|
3379
3379
|
if decl.name is not None:
|
|
3380
3380
|
key = "union " + decl.name
|
|
3381
|
-
union = extra_types.get(key
|
|
3381
|
+
union = extra_types.get(key)
|
|
3382
3382
|
from_global = False
|
|
3383
3383
|
if union is None and key in ALL_TYPES:
|
|
3384
3384
|
union = ALL_TYPES[key]
|
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/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
|
|
|
@@ -672,6 +673,21 @@ class GraphUtils:
|
|
|
672
673
|
|
|
673
674
|
return list(widening_addrs)
|
|
674
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
|
+
|
|
675
691
|
@staticmethod
|
|
676
692
|
def reverse_post_order_sort_nodes(graph, nodes=None):
|
|
677
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
|
|
|
@@ -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.163
|
|
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.163
|
|
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.163
|
|
24
|
+
Requires-Dist: cle==9.2.163
|
|
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.163
|
|
34
34
|
Requires-Dist: rich>=13.1.0
|
|
35
35
|
Requires-Dist: sortedcontainers
|
|
36
36
|
Requires-Dist: sympy
|