angr 9.2.161__cp310-abi3-macosx_11_0_arm64.whl → 9.2.163__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 +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/unicornlib.dylib +0 -0
- 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 +51 -51
- {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
|
@@ -126,6 +126,12 @@ class PhoenixStructurer(StructurerBase):
|
|
|
126
126
|
self._improve_algorithm = improve_algorithm
|
|
127
127
|
self._edge_virtualization_hints = []
|
|
128
128
|
|
|
129
|
+
# node_order keeps a dictionary of nodes and their order in a quasi-topological sort of the region full graph
|
|
130
|
+
# (graph_with_successors). _generate_node_order() initializes this dictionary. we then update this dictionary
|
|
131
|
+
# when new nodes are created. we do not populate this dictionary when working on acyclic graphs because it's
|
|
132
|
+
# not used for acyclic graphs.
|
|
133
|
+
self._node_order: dict[Any, int] | None = None
|
|
134
|
+
|
|
129
135
|
self._use_multistmtexprs = use_multistmtexprs
|
|
130
136
|
self._multistmtexpr_stmt_threshold = multistmtexpr_stmt_threshold
|
|
131
137
|
self._analyze()
|
|
@@ -221,8 +227,11 @@ class PhoenixStructurer(StructurerBase):
|
|
|
221
227
|
|
|
222
228
|
def _analyze_cyclic(self) -> bool:
|
|
223
229
|
any_matches = False
|
|
224
|
-
|
|
225
|
-
|
|
230
|
+
|
|
231
|
+
if self._node_order is None:
|
|
232
|
+
self._generate_node_order()
|
|
233
|
+
acyclic_graph = to_acyclic_graph(self._region.graph, node_order=self._node_order)
|
|
234
|
+
for node in list(GraphUtils.dfs_postorder_nodes_deterministic(acyclic_graph, self._region.head)):
|
|
226
235
|
if node not in self._region.graph:
|
|
227
236
|
continue
|
|
228
237
|
matched = self._match_cyclic_schemas(
|
|
@@ -323,10 +332,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
323
332
|
# it's a while loop if the conditional jump (or the head block) is at the beginning of node
|
|
324
333
|
loop_type = "while" if head_block_idx == 0 else "do-while"
|
|
325
334
|
# otherwise it's a do-while loop
|
|
326
|
-
|
|
327
|
-
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
|
|
328
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
335
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, head_block, left, right):
|
|
329
336
|
# c = !c
|
|
337
|
+
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
|
|
330
338
|
if head_block_idx == 0:
|
|
331
339
|
self._remove_first_statement_if_jump(head_block)
|
|
332
340
|
else:
|
|
@@ -351,13 +359,12 @@ class PhoenixStructurer(StructurerBase):
|
|
|
351
359
|
# possible candidate
|
|
352
360
|
_, _, head_block = self._find_node_going_to_dst(node, left, condjump_only=True)
|
|
353
361
|
if head_block is not None:
|
|
354
|
-
|
|
355
|
-
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
|
|
356
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
362
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, head_block, left, right):
|
|
357
363
|
# c = !c
|
|
358
364
|
if PhoenixStructurer._is_single_statement_block(node):
|
|
359
365
|
# the single-statement-block check is to ensure we don't execute any code before the
|
|
360
366
|
# conditional jump. this way the entire node can be dropped.
|
|
367
|
+
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
|
|
361
368
|
new_node = SequenceNode(node.addr, nodes=[left])
|
|
362
369
|
loop_node = LoopNode("while", edge_cond_left, new_node, addr=node.addr)
|
|
363
370
|
|
|
@@ -372,6 +379,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
372
379
|
|
|
373
380
|
return True, loop_node, right
|
|
374
381
|
# we generate a while-true loop instead
|
|
382
|
+
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
|
|
375
383
|
last_stmt = self._remove_last_statement_if_jump(head_block)
|
|
376
384
|
assert last_stmt is not None
|
|
377
385
|
cond_jump = Jump(
|
|
@@ -400,10 +408,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
400
408
|
# while (true) { ...; if (...) break; }
|
|
401
409
|
_, _, head_block = self._find_node_going_to_dst(node, left, condjump_only=True)
|
|
402
410
|
if head_block is not None:
|
|
403
|
-
|
|
404
|
-
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
|
|
405
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
411
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, head_block, left, right):
|
|
406
412
|
# c = !c
|
|
413
|
+
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
|
|
407
414
|
self._remove_last_statement_if_jump(head_block)
|
|
408
415
|
cond_break = ConditionalBreakNode(node.addr, edge_cond_right, right.addr)
|
|
409
416
|
new_node = SequenceNode(node.addr, nodes=[node, cond_break, left])
|
|
@@ -501,6 +508,10 @@ class PhoenixStructurer(StructurerBase):
|
|
|
501
508
|
self.replace_nodes(full_graph, node, loop_node, self_loop=False)
|
|
502
509
|
full_graph.add_edge(loop_node, successor_node)
|
|
503
510
|
|
|
511
|
+
if self._node_order is not None:
|
|
512
|
+
self._node_order[loop_node] = self._node_order[node]
|
|
513
|
+
self._node_order[successor_node] = self._node_order[loop_node]
|
|
514
|
+
|
|
504
515
|
return True, loop_node, successor_node
|
|
505
516
|
|
|
506
517
|
def _cyclic_while_with_single_successor_must_return(self, successor_node: SequenceNode) -> bool:
|
|
@@ -527,10 +538,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
527
538
|
# possible candidate
|
|
528
539
|
_, _, succ_block = self._find_node_going_to_dst(succ, out_node, condjump_only=True)
|
|
529
540
|
if succ_block is not None:
|
|
530
|
-
|
|
531
|
-
edge_cond_succout = self.cond_proc.recover_edge_condition(full_graph, succ_block, out_node)
|
|
532
|
-
if claripy.is_true(claripy.Not(edge_cond_succhead) == edge_cond_succout): # type: ignore
|
|
541
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, succ_block, node, out_node):
|
|
533
542
|
# c = !c
|
|
543
|
+
edge_cond_succhead = self.cond_proc.recover_edge_condition(full_graph, succ_block, node)
|
|
534
544
|
self._remove_last_statement_if_jump(succ)
|
|
535
545
|
drop_succ = False
|
|
536
546
|
|
|
@@ -570,10 +580,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
570
580
|
succ = succs[0]
|
|
571
581
|
if not full_graph.has_edge(succ, node):
|
|
572
582
|
# possible candidate
|
|
573
|
-
|
|
574
|
-
edge_cond_head_succ = self.cond_proc.recover_edge_condition(full_graph, node, succ)
|
|
575
|
-
if claripy.is_true(claripy.Not(edge_cond_head) == edge_cond_head_succ): # type: ignore
|
|
583
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, node, node, succ):
|
|
576
584
|
# c = !c
|
|
585
|
+
edge_cond_head = self.cond_proc.recover_edge_condition(full_graph, node, node)
|
|
577
586
|
self._remove_last_statement_if_jump(node)
|
|
578
587
|
seq_node = SequenceNode(node.addr, nodes=[node]) if not isinstance(node, SequenceNode) else node
|
|
579
588
|
loop_node = LoopNode("do-while", edge_cond_head, seq_node, addr=seq_node.addr)
|
|
@@ -1039,7 +1048,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1039
1048
|
|
|
1040
1049
|
self._assert_graph_ok(acyclic_graph, "Removed wrong edges")
|
|
1041
1050
|
|
|
1042
|
-
for node in list(
|
|
1051
|
+
for node in list(GraphUtils.dfs_postorder_nodes_deterministic(acyclic_graph, head)):
|
|
1043
1052
|
if node not in graph:
|
|
1044
1053
|
continue
|
|
1045
1054
|
if graph.has_edge(node, head):
|
|
@@ -1145,6 +1154,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1145
1154
|
node_default = Block(SWITCH_MISSING_DEFAULT_NODE_ADDR, 0, statements=[jmp_to_default_node])
|
|
1146
1155
|
graph.add_edge(node, node_default)
|
|
1147
1156
|
full_graph.add_edge(node, node_default)
|
|
1157
|
+
if self._node_order is not None:
|
|
1158
|
+
self._node_order[node_default] = self._node_order[node]
|
|
1148
1159
|
r = self._make_switch_cases_core(
|
|
1149
1160
|
node,
|
|
1150
1161
|
self.cond_proc.claripy_ast_from_ail_condition(last_stmt.switch_variable),
|
|
@@ -1250,6 +1261,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1250
1261
|
# un-structure IncompleteSwitchCaseNode
|
|
1251
1262
|
if isinstance(node_a, SequenceNode) and node_a.nodes and isinstance(node_a.nodes[0], IncompleteSwitchCaseNode):
|
|
1252
1263
|
_, new_seq_node = self._unpack_sequencenode_head(graph, node_a)
|
|
1264
|
+
if new_seq_node is not None and self._node_order is not None:
|
|
1265
|
+
self._node_order[new_seq_node] = self._node_order[node_a]
|
|
1253
1266
|
self._unpack_sequencenode_head(full_graph, node_a, new_seq=new_seq_node)
|
|
1254
1267
|
# update node_a
|
|
1255
1268
|
node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
|
|
@@ -1260,6 +1273,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1260
1273
|
self._unpack_incompleteswitchcasenode(full_graph, node_a) # this shall not fail
|
|
1261
1274
|
# update node_a
|
|
1262
1275
|
node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
|
|
1276
|
+
if self._node_order is not None:
|
|
1277
|
+
self._generate_node_order()
|
|
1263
1278
|
|
|
1264
1279
|
better_node_a = node_a
|
|
1265
1280
|
if isinstance(node_a, SequenceNode) and is_empty_or_label_only_node(node_a.nodes[0]) and len(node_a.nodes) == 2:
|
|
@@ -1604,6 +1619,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1604
1619
|
self.replace_nodes(full_graph, node, new_node)
|
|
1605
1620
|
if out_nodes:
|
|
1606
1621
|
full_graph.add_edge(new_node, out_nodes[0])
|
|
1622
|
+
if self._node_order:
|
|
1623
|
+
self._node_order[new_node] = self._node_order[node]
|
|
1607
1624
|
return True
|
|
1608
1625
|
return False
|
|
1609
1626
|
|
|
@@ -1785,6 +1802,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1785
1802
|
|
|
1786
1803
|
graph.add_edge(head, scnode)
|
|
1787
1804
|
full_graph.add_edge(head, scnode)
|
|
1805
|
+
if self._node_order is not None:
|
|
1806
|
+
self._node_order[scnode] = self._node_order[head]
|
|
1788
1807
|
|
|
1789
1808
|
if out_edges:
|
|
1790
1809
|
# for all out edges going to head, we ensure there is a goto at the end of each corresponding case node
|
|
@@ -1934,10 +1953,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1934
1953
|
and not self._is_node_unstructured_switch_case_head(left)
|
|
1935
1954
|
and not self._is_node_unstructured_switch_case_head(right)
|
|
1936
1955
|
):
|
|
1937
|
-
|
|
1938
|
-
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
1939
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
1956
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
|
|
1940
1957
|
# c = !c
|
|
1958
|
+
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
1941
1959
|
last_if_jump = self._remove_last_statement_if_jump(start_node)
|
|
1942
1960
|
new_cond_node = ConditionNode(
|
|
1943
1961
|
last_if_jump.ins_addr if last_if_jump is not None else start_node.addr,
|
|
@@ -1976,10 +1994,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1976
1994
|
if not self._is_node_unstructured_switch_case_head(
|
|
1977
1995
|
left
|
|
1978
1996
|
) and not self._is_node_unstructured_switch_case_head(right):
|
|
1979
|
-
|
|
1980
|
-
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
1981
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
1997
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
|
|
1982
1998
|
# c = !c
|
|
1999
|
+
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
1983
2000
|
last_if_jump = self._remove_last_statement_if_jump(start_node)
|
|
1984
2001
|
new_cond_node = ConditionNode(
|
|
1985
2002
|
last_if_jump.ins_addr if last_if_jump is not None else start_node.addr,
|
|
@@ -2009,10 +2026,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2009
2026
|
and full_graph.in_degree[left] == 1
|
|
2010
2027
|
and full_graph.in_degree[right] >= 2
|
|
2011
2028
|
):
|
|
2012
|
-
|
|
2013
|
-
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
2014
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
2029
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
|
|
2015
2030
|
# c = !c
|
|
2031
|
+
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2016
2032
|
last_if_jump = self._remove_last_statement_if_jump(start_node)
|
|
2017
2033
|
new_cond_node = ConditionNode(
|
|
2018
2034
|
last_if_jump.ins_addr if last_if_jump is not None else start_node.addr,
|
|
@@ -2044,10 +2060,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2044
2060
|
or (full_graph.in_degree[right] == 1 and not left_succs)
|
|
2045
2061
|
)
|
|
2046
2062
|
):
|
|
2047
|
-
|
|
2048
|
-
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
2049
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
2063
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
|
|
2050
2064
|
# c = !c
|
|
2065
|
+
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2051
2066
|
try:
|
|
2052
2067
|
last_stmt = self.cond_proc.get_last_statement(start_node)
|
|
2053
2068
|
except EmptyBlockNotice:
|
|
@@ -2169,6 +2184,23 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2169
2184
|
#
|
|
2170
2185
|
# We reduce it into if (cond && next_cond) { body } else { else }
|
|
2171
2186
|
|
|
2187
|
+
# fast-path check to reject nodes that definitely do not work
|
|
2188
|
+
if full_graph.out_degree[start_node] != 2:
|
|
2189
|
+
return False
|
|
2190
|
+
next_cond_candidates = list(full_graph.successors(start_node))
|
|
2191
|
+
check_passed = False
|
|
2192
|
+
for next_cond in next_cond_candidates:
|
|
2193
|
+
if full_graph.out_degree[next_cond] != 2:
|
|
2194
|
+
continue
|
|
2195
|
+
for next_cond_succ in full_graph.successors(next_cond):
|
|
2196
|
+
if full_graph.has_edge(start_node, next_cond_succ):
|
|
2197
|
+
check_passed = True
|
|
2198
|
+
break
|
|
2199
|
+
if check_passed:
|
|
2200
|
+
break
|
|
2201
|
+
if not check_passed:
|
|
2202
|
+
return False
|
|
2203
|
+
|
|
2172
2204
|
r = self._match_acyclic_short_circuit_conditions_type_a(graph, full_graph, start_node)
|
|
2173
2205
|
|
|
2174
2206
|
if r is not None:
|
|
@@ -2336,21 +2368,17 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2336
2368
|
and full_graph.in_degree[left] == 1
|
|
2337
2369
|
and full_graph.in_degree[right] >= 1
|
|
2338
2370
|
):
|
|
2339
|
-
|
|
2340
|
-
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
2341
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
2371
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
|
|
2342
2372
|
# c0 = !c0
|
|
2343
2373
|
left_succs = list(full_graph.successors(left))
|
|
2344
2374
|
if len(left_succs) == 2 and right in left_succs:
|
|
2345
2375
|
other_succ = next(iter(succ for succ in left_succs if succ is not right))
|
|
2346
2376
|
if full_graph.out_degree[right] == 1 and full_graph.has_edge(right, other_succ):
|
|
2347
2377
|
# there must be an edge between right and other_succ
|
|
2348
|
-
|
|
2349
|
-
edge_cond_left_other = self.cond_proc.recover_edge_condition(full_graph, left, other_succ)
|
|
2350
|
-
if claripy.is_true(
|
|
2351
|
-
claripy.Not(edge_cond_left_right) == edge_cond_left_other # type: ignore
|
|
2352
|
-
):
|
|
2378
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, left, right, other_succ):
|
|
2353
2379
|
# c1 = !c1
|
|
2380
|
+
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2381
|
+
edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
|
|
2354
2382
|
return left, edge_cond_left, right, edge_cond_left_right, other_succ
|
|
2355
2383
|
return None
|
|
2356
2384
|
|
|
@@ -2373,7 +2401,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2373
2401
|
if len(succs) == 2:
|
|
2374
2402
|
left, right = succs
|
|
2375
2403
|
|
|
2376
|
-
if full_graph.in_degree[left] == 1 and full_graph.in_degree[right]
|
|
2404
|
+
if full_graph.in_degree[left] == 1 and full_graph.in_degree[right] >= 2:
|
|
2377
2405
|
left, right = right, left
|
|
2378
2406
|
|
|
2379
2407
|
# ensure left and right nodes are not the head of a switch-case construct
|
|
@@ -2384,24 +2412,20 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2384
2412
|
|
|
2385
2413
|
if (
|
|
2386
2414
|
self._is_sequential_statement_block(right)
|
|
2387
|
-
and full_graph.in_degree[left]
|
|
2415
|
+
and full_graph.in_degree[left] >= 2
|
|
2388
2416
|
and full_graph.in_degree[right] == 1
|
|
2389
2417
|
):
|
|
2390
|
-
|
|
2391
|
-
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
2392
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
2418
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
|
|
2393
2419
|
# c0 = !c0
|
|
2394
2420
|
right_succs = list(full_graph.successors(right))
|
|
2395
2421
|
left_succs = list(full_graph.successors(left))
|
|
2396
2422
|
if len(right_succs) == 2 and left in right_succs:
|
|
2397
2423
|
else_node = next(iter(succ for succ in right_succs if succ is not left))
|
|
2398
2424
|
if len([succ for succ in left_succs if succ is not else_node]) == 1:
|
|
2399
|
-
|
|
2400
|
-
edge_cond_right_else = self.cond_proc.recover_edge_condition(full_graph, right, else_node)
|
|
2401
|
-
if claripy.is_true(
|
|
2402
|
-
claripy.Not(edge_cond_right_left) == edge_cond_right_else # type: ignore
|
|
2403
|
-
):
|
|
2425
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, right, left, else_node):
|
|
2404
2426
|
# c1 = !c1
|
|
2427
|
+
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2428
|
+
edge_cond_right_left = self.cond_proc.recover_edge_condition(full_graph, right, left)
|
|
2405
2429
|
return left, edge_cond_left, right, edge_cond_right_left, else_node
|
|
2406
2430
|
return None
|
|
2407
2431
|
|
|
@@ -2432,23 +2456,19 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2432
2456
|
and full_graph.in_degree[left] == 1
|
|
2433
2457
|
and full_graph.in_degree[successor] >= 1
|
|
2434
2458
|
):
|
|
2435
|
-
|
|
2436
|
-
edge_cond_successor = self.cond_proc.recover_edge_condition(full_graph, start_node, successor)
|
|
2437
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_successor): # type: ignore
|
|
2459
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, successor):
|
|
2438
2460
|
# c0 = !c0
|
|
2439
2461
|
left_succs = list(full_graph.successors(left))
|
|
2440
2462
|
if len(left_succs) == 2 and successor in left_succs:
|
|
2441
2463
|
right = next(iter(succ for succ in left_succs if succ is not successor))
|
|
2442
2464
|
if full_graph.out_degree[right] == 1 and full_graph.has_edge(right, successor):
|
|
2443
2465
|
# there must be an edge from right to successor
|
|
2444
|
-
|
|
2445
|
-
edge_cond_left_successor = self.cond_proc.recover_edge_condition(
|
|
2446
|
-
full_graph, left, successor
|
|
2447
|
-
)
|
|
2448
|
-
if claripy.is_true(
|
|
2449
|
-
claripy.Not(edge_cond_left_right) == edge_cond_left_successor # type: ignore
|
|
2450
|
-
):
|
|
2466
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, left, right, successor):
|
|
2451
2467
|
# c1 = !c1
|
|
2468
|
+
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2469
|
+
edge_cond_left_successor = self.cond_proc.recover_edge_condition(
|
|
2470
|
+
full_graph, left, successor
|
|
2471
|
+
)
|
|
2452
2472
|
return left, edge_cond_left, successor, edge_cond_left_successor, right
|
|
2453
2473
|
return None
|
|
2454
2474
|
|
|
@@ -2484,17 +2504,15 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2484
2504
|
and full_graph.in_degree[left] == 1
|
|
2485
2505
|
and full_graph.in_degree[else_node] >= 1
|
|
2486
2506
|
):
|
|
2487
|
-
|
|
2488
|
-
edge_cond_else = self.cond_proc.recover_edge_condition(full_graph, start_node, else_node)
|
|
2489
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_else): # type: ignore
|
|
2507
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, else_node):
|
|
2490
2508
|
# c0 = !c0
|
|
2491
2509
|
left_succs = list(full_graph.successors(left))
|
|
2492
2510
|
if len(left_succs) == 2 and else_node in left_succs:
|
|
2493
2511
|
right = next(iter(succ for succ in left_succs if succ is not else_node))
|
|
2494
|
-
|
|
2495
|
-
edge_cond_left_else = self.cond_proc.recover_edge_condition(full_graph, left, else_node)
|
|
2496
|
-
if claripy.is_true(claripy.Not(edge_cond_left_right) == edge_cond_left_else): # type: ignore
|
|
2512
|
+
if self.cond_proc.have_opposite_edge_conditions(full_graph, left, right, else_node):
|
|
2497
2513
|
# c1 = !c1
|
|
2514
|
+
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2515
|
+
edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
|
|
2498
2516
|
return left, edge_cond_left, right, edge_cond_left_right, else_node
|
|
2499
2517
|
return None
|
|
2500
2518
|
|
|
@@ -2516,7 +2534,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2516
2534
|
if networkx.is_directed_acyclic_graph(full_graph):
|
|
2517
2535
|
acyclic_graph = full_graph
|
|
2518
2536
|
else:
|
|
2519
|
-
acyclic_graph = to_acyclic_graph(full_graph,
|
|
2537
|
+
acyclic_graph = to_acyclic_graph(full_graph, node_order=self._node_order)
|
|
2520
2538
|
for src, dst in acyclic_graph.edges:
|
|
2521
2539
|
if src is dst:
|
|
2522
2540
|
continue
|
|
@@ -2532,7 +2550,20 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2532
2550
|
if (src.addr, dst.addr) not in self.whitelist_edges:
|
|
2533
2551
|
other_edges.append((src, dst))
|
|
2534
2552
|
|
|
2535
|
-
|
|
2553
|
+
# acyclic graph may contain more than one entry node, so we may add a temporary head node to ensure all nodes
|
|
2554
|
+
# are accounted for in node_seq
|
|
2555
|
+
graph_entries = [nn for nn in acyclic_graph if acyclic_graph.in_degree[nn] == 0]
|
|
2556
|
+
postorder_head = head
|
|
2557
|
+
if len(graph_entries) > 1:
|
|
2558
|
+
postorder_head = Block(0, 0)
|
|
2559
|
+
for nn in graph_entries:
|
|
2560
|
+
acyclic_graph.add_edge(postorder_head, nn)
|
|
2561
|
+
ordered_nodes = list(
|
|
2562
|
+
reversed(list(GraphUtils.dfs_postorder_nodes_deterministic(acyclic_graph, postorder_head)))
|
|
2563
|
+
)
|
|
2564
|
+
if len(graph_entries) > 1:
|
|
2565
|
+
ordered_nodes.remove(postorder_head)
|
|
2566
|
+
acyclic_graph.remove_node(postorder_head)
|
|
2536
2567
|
node_seq = {nn: (len(ordered_nodes) - idx) for (idx, nn) in enumerate(ordered_nodes)} # post-order
|
|
2537
2568
|
|
|
2538
2569
|
if all_edges_wo_dominance:
|
|
@@ -2612,6 +2643,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2612
2643
|
self.virtualized_edges.add((src, dst))
|
|
2613
2644
|
if new_src is not None:
|
|
2614
2645
|
self.replace_nodes(graph, src, new_src)
|
|
2646
|
+
if self._node_order is not None:
|
|
2647
|
+
self._node_order[new_src] = self._node_order[src]
|
|
2615
2648
|
if full_graph is not None:
|
|
2616
2649
|
self.virtualized_edges.add((src, dst))
|
|
2617
2650
|
full_graph.remove_edge(src, dst)
|
|
@@ -2921,10 +2954,29 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2921
2954
|
src, dst = edge_
|
|
2922
2955
|
dst_in_degree = graph.in_degree[dst]
|
|
2923
2956
|
src_out_degree = graph.out_degree[src]
|
|
2924
|
-
return -node_seq
|
|
2957
|
+
return -node_seq[dst], dst_in_degree, src_out_degree, -src.addr, -dst.addr # type: ignore
|
|
2925
2958
|
|
|
2926
2959
|
return sorted(edges, key=_sort_edge, reverse=True)
|
|
2927
2960
|
|
|
2961
|
+
def _generate_node_order(self):
|
|
2962
|
+
the_graph = (
|
|
2963
|
+
self._region.graph_with_successors if self._region.graph_with_successors is not None else self._region.graph
|
|
2964
|
+
)
|
|
2965
|
+
the_head = self._region.head
|
|
2966
|
+
ordered_nodes = GraphUtils.quasi_topological_sort_nodes(
|
|
2967
|
+
the_graph,
|
|
2968
|
+
loop_heads=[the_head],
|
|
2969
|
+
)
|
|
2970
|
+
self._node_order = {n: i for i, n in enumerate(ordered_nodes)}
|
|
2971
|
+
|
|
2972
|
+
def replace_nodes(self, graph, old_node_0, new_node, old_node_1=None, self_loop=True):
|
|
2973
|
+
super().replace_nodes(graph, old_node_0, new_node, old_node_1=old_node_1, self_loop=self_loop)
|
|
2974
|
+
if self._node_order is not None and graph is self._region.graph_with_successors:
|
|
2975
|
+
if old_node_1 is not None:
|
|
2976
|
+
self._node_order[new_node] = min(self._node_order[old_node_0], self._node_order[old_node_1])
|
|
2977
|
+
else:
|
|
2978
|
+
self._node_order[new_node] = self._node_order[old_node_0]
|
|
2979
|
+
|
|
2928
2980
|
@staticmethod
|
|
2929
2981
|
def _replace_node_in_edge_list(edge_list: list[tuple], old_node, new_node) -> None:
|
|
2930
2982
|
for idx in range(len(edge_list)): # pylint:disable=consider-using-enumerate
|
|
@@ -43,7 +43,7 @@ class RecursiveStructurer(Analysis):
|
|
|
43
43
|
self.structurer_cls = structurer_cls if structurer_cls is not None else DreamStructurer
|
|
44
44
|
self.structurer_options = kwargs
|
|
45
45
|
|
|
46
|
-
self.result = None
|
|
46
|
+
self.result: BaseNode | None = None
|
|
47
47
|
self.result_incomplete: bool = False
|
|
48
48
|
|
|
49
49
|
self._analyze()
|
|
@@ -161,6 +161,7 @@ class RecursiveStructurer(Analysis):
|
|
|
161
161
|
for jump_table_head_addr, jumptable in jump_tables.items():
|
|
162
162
|
if jump_table_head_addr not in func_block_addrs:
|
|
163
163
|
continue
|
|
164
|
+
assert jumptable.jumptable_entries is not None
|
|
164
165
|
for entry_addr in jumptable.jumptable_entries:
|
|
165
166
|
entries[entry_addr] = jump_table_head_addr
|
|
166
167
|
|
|
@@ -178,7 +179,7 @@ class RecursiveStructurer(Analysis):
|
|
|
178
179
|
continue
|
|
179
180
|
if node.addr == self.function.addr:
|
|
180
181
|
return node
|
|
181
|
-
if min_node is None or min_node.addr < node.addr:
|
|
182
|
+
if min_node is None or (min_node.addr is not None and node.addr is not None and min_node.addr < node.addr):
|
|
182
183
|
min_node = node
|
|
183
184
|
|
|
184
185
|
return min_node
|
|
@@ -26,7 +26,9 @@ class SAILRStructurer(PhoenixStructurer):
|
|
|
26
26
|
|
|
27
27
|
NAME = "sailr"
|
|
28
28
|
|
|
29
|
-
def __init__(self, region, improve_phoenix=True, **kwargs):
|
|
29
|
+
def __init__(self, region, improve_phoenix=True, postdom_max_edges=10, postdom_max_graph_size=50, **kwargs):
|
|
30
|
+
self._postdom_max_edges = postdom_max_edges
|
|
31
|
+
self._postdom_max_graph_size = postdom_max_graph_size
|
|
30
32
|
super().__init__(
|
|
31
33
|
region,
|
|
32
34
|
improve_algorithm=improve_phoenix,
|
|
@@ -46,44 +48,50 @@ class SAILRStructurer(PhoenixStructurer):
|
|
|
46
48
|
except StopIteration:
|
|
47
49
|
entry_node = None
|
|
48
50
|
|
|
51
|
+
edges = sorted(edges, key=lambda edge: (edge[0].addr, edge[1].addr))
|
|
49
52
|
best_edges = edges
|
|
50
53
|
if entry_node is not None:
|
|
51
|
-
#
|
|
52
|
-
# so we collect them for each candidate edge
|
|
53
|
-
edge_postdom_count = {}
|
|
54
|
+
# collect sibling counts for edges
|
|
54
55
|
edge_sibling_count = {}
|
|
55
56
|
for edge in edges:
|
|
56
57
|
_, dst = edge
|
|
57
|
-
|
|
58
|
-
graph_copy.remove_edge(*edge)
|
|
59
|
-
sibling_cnt = graph_copy.in_degree(dst)
|
|
58
|
+
sibling_cnt = graph.in_degree[dst] - 1
|
|
60
59
|
if sibling_cnt == 0:
|
|
61
60
|
continue
|
|
62
|
-
|
|
63
61
|
edge_sibling_count[edge] = sibling_cnt
|
|
64
|
-
post_dom_graph = PostDominators(graph_copy, entry_node).post_dom
|
|
65
|
-
post_doms = set()
|
|
66
|
-
for postdom_node, dominatee in post_dom_graph.edges():
|
|
67
|
-
if not isinstance(postdom_node, TemporaryNode) and not isinstance(dominatee, TemporaryNode):
|
|
68
|
-
post_doms.add((postdom_node, dominatee))
|
|
69
|
-
edge_postdom_count[edge] = len(post_doms)
|
|
70
|
-
|
|
71
|
-
# H1: the edge that has the least amount of sibling edges should be virtualized first
|
|
72
|
-
# this is believed to reduce the amount of virtualization needed in future rounds and increase
|
|
73
|
-
# the edges that enter a single outer-scope if-stmt
|
|
74
|
-
if edge_sibling_count:
|
|
75
|
-
min_sibling_count = min(edge_sibling_count.values())
|
|
76
|
-
best_edges = [edge for edge, cnt in edge_sibling_count.items() if cnt == min_sibling_count]
|
|
77
|
-
if len(best_edges) == 1:
|
|
78
|
-
return best_edges
|
|
79
62
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
63
|
+
# H1: the edge that has the least amount of sibling edges should be virtualized first
|
|
64
|
+
# this is believed to reduce the amount of virtualization needed in future rounds and increase
|
|
65
|
+
# the edges that enter a single outer-scope if-stmt
|
|
66
|
+
if edge_sibling_count:
|
|
67
|
+
min_sibling_count = min(edge_sibling_count.values())
|
|
68
|
+
best_edges = [edge for edge, cnt in edge_sibling_count.items() if cnt == min_sibling_count]
|
|
69
|
+
if len(best_edges) == 1:
|
|
70
|
+
return best_edges
|
|
71
|
+
|
|
72
|
+
if len(edges) <= self._postdom_max_edges and len(graph) <= self._postdom_max_graph_size:
|
|
73
|
+
# dominator analysis is expensive, so we only do it for small graphs
|
|
74
|
+
edge_postdom_count = {}
|
|
75
|
+
graph_copy = networkx.DiGraph(graph)
|
|
76
|
+
for edge in edges:
|
|
77
|
+
_, dst = edge
|
|
78
|
+
graph_copy.remove_edge(*edge)
|
|
79
|
+
post_dom_graph = PostDominators(graph_copy, entry_node).post_dom
|
|
80
|
+
post_doms = set()
|
|
81
|
+
for postdom_node, dominatee in post_dom_graph.edges():
|
|
82
|
+
if not isinstance(postdom_node, TemporaryNode) and not isinstance(dominatee, TemporaryNode):
|
|
83
|
+
post_doms.add((postdom_node, dominatee))
|
|
84
|
+
edge_postdom_count[edge] = len(post_doms)
|
|
85
|
+
# add back the edge
|
|
86
|
+
graph_copy.add_edge(*edge)
|
|
87
|
+
|
|
88
|
+
# create the next heuristic based on the best edges from the previous heuristic
|
|
89
|
+
filtered_edge_postdom_count = edge_postdom_count.copy()
|
|
90
|
+
for edge in list(edge_postdom_count.keys()):
|
|
91
|
+
if edge not in best_edges:
|
|
92
|
+
del filtered_edge_postdom_count[edge]
|
|
93
|
+
if filtered_edge_postdom_count:
|
|
94
|
+
edge_postdom_count = filtered_edge_postdom_count
|
|
87
95
|
|
|
88
96
|
# H2: the edge, when removed, that causes the most post-dominators of the graph should be virtualized
|
|
89
97
|
# first. this is believed to make the code more linear looking be reducing the amount of scopes.
|
|
@@ -94,19 +102,19 @@ class SAILRStructurer(PhoenixStructurer):
|
|
|
94
102
|
if len(best_edges) == 1:
|
|
95
103
|
return best_edges
|
|
96
104
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
105
|
+
# H3: the edge that goes directly to a return statement should be virtualized first
|
|
106
|
+
# this is believed to be good because it can be corrected in later optimization by duplicating
|
|
107
|
+
# the return
|
|
108
|
+
candidate_edges = best_edges
|
|
109
|
+
best_edges = []
|
|
110
|
+
for src, dst in candidate_edges:
|
|
111
|
+
if graph.has_node(dst) and structured_node_is_simple_return(dst, graph):
|
|
112
|
+
best_edges.append((src, dst))
|
|
113
|
+
|
|
114
|
+
if len(best_edges) == 1:
|
|
115
|
+
return best_edges
|
|
116
|
+
if not best_edges:
|
|
117
|
+
best_edges = candidate_edges
|
|
110
118
|
|
|
111
119
|
# if we have another tie, or we never used improved heuristics, then we do the default ordering.
|
|
112
120
|
return super()._order_virtualizable_edges(graph, best_edges, node_seq)
|
|
@@ -6,9 +6,9 @@ import logging
|
|
|
6
6
|
|
|
7
7
|
import networkx
|
|
8
8
|
|
|
9
|
-
import angr.ailment as ailment
|
|
10
9
|
import claripy
|
|
11
10
|
|
|
11
|
+
from angr import ailment
|
|
12
12
|
from angr.analyses import Analysis
|
|
13
13
|
from angr.analyses.decompiler.condition_processor import ConditionProcessor
|
|
14
14
|
from angr.analyses.decompiler.sequence_walker import SequenceWalker
|
|
@@ -971,8 +971,7 @@ class StructurerBase(Analysis):
|
|
|
971
971
|
new_sequences.append(new_seq_)
|
|
972
972
|
self._new_sequences = new_sequences
|
|
973
973
|
|
|
974
|
-
|
|
975
|
-
def replace_nodes(graph, old_node_0, new_node, old_node_1=None, self_loop=True):
|
|
974
|
+
def replace_nodes(self, graph, old_node_0, new_node, old_node_1=None, self_loop=True): # pylint:disable=no-self-use
|
|
976
975
|
in_edges = list(graph.in_edges(old_node_0, data=True))
|
|
977
976
|
out_edges = list(graph.out_edges(old_node_0, data=True))
|
|
978
977
|
if old_node_1 is not None:
|
|
@@ -90,7 +90,7 @@ class StringObfType3Rewriter(OptimizationPass):
|
|
|
90
90
|
else:
|
|
91
91
|
new_stmt = new_call
|
|
92
92
|
|
|
93
|
-
statements = block.statements[:-1]
|
|
93
|
+
statements = [*block.statements[:-1], new_stmt]
|
|
94
94
|
|
|
95
95
|
# remove N-2 continuous stack assignment
|
|
96
96
|
if len(deobf_content) > 2:
|
angr/analyses/disassembly.py
CHANGED
|
@@ -922,7 +922,7 @@ class Value(OperandPiece):
|
|
|
922
922
|
if func is not None and lbl == func.name and func.name != func.demangled_name:
|
|
923
923
|
# see if lbl == func.name and func.demangled_name != func.name. if so, we prioritize the
|
|
924
924
|
# demangled name
|
|
925
|
-
normalized_name = get_cpp_function_name(func.demangled_name
|
|
925
|
+
normalized_name = get_cpp_function_name(func.demangled_name)
|
|
926
926
|
return [normalized_name]
|
|
927
927
|
return [("+" if self.render_with_sign else "") + lbl]
|
|
928
928
|
if func is not None:
|
angr/analyses/fcp/fcp.py
CHANGED
|
@@ -343,16 +343,17 @@ class FastConstantPropagation(Analysis):
|
|
|
343
343
|
engine.process(state, block=block)
|
|
344
344
|
|
|
345
345
|
# if the node ends with a function call, call _handle_function
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
346
|
+
if node in func_graph_with_callees:
|
|
347
|
+
succs = list(func_graph_with_callees.successors(node))
|
|
348
|
+
if any(isinstance(succ, (Function, HookNode)) for succ in succs):
|
|
349
|
+
callee = next(succ for succ in succs if isinstance(succ, (Function, HookNode)))
|
|
350
|
+
if isinstance(callee, HookNode):
|
|
351
|
+
# attempt to convert it into a function
|
|
352
|
+
if self.kb.functions.contains_addr(callee.addr):
|
|
353
|
+
callee = self.kb.functions.get_by_addr(callee.addr)
|
|
354
|
+
else:
|
|
355
|
+
callee = None
|
|
356
|
+
state = self._handle_function(state, callee)
|
|
356
357
|
|
|
357
358
|
states[node] = state
|
|
358
359
|
|