angr 9.2.178__cp310-abi3-macosx_11_0_arm64.whl → 9.2.180__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/converter_vex.py +35 -4
- angr/analyses/cfg/cfb.py +11 -0
- angr/analyses/decompiler/ail_simplifier.py +1 -1
- angr/analyses/decompiler/clinic.py +9 -3
- angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +2 -2
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +38 -18
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +10 -4
- angr/analyses/decompiler/ssailification/rewriting.py +1 -1
- angr/analyses/decompiler/structured_codegen/c.py +54 -12
- angr/analyses/decompiler/structuring/phoenix.py +129 -64
- angr/analyses/decompiler/utils.py +26 -8
- angr/analyses/disassembly.py +108 -52
- angr/analyses/proximity_graph.py +20 -19
- angr/flirt/__init__.py +69 -42
- angr/knowledge_plugins/key_definitions/live_definitions.py +2 -1
- angr/rustylib.abi3.so +0 -0
- angr/unicornlib.dylib +0 -0
- angr/utils/types.py +2 -0
- {angr-9.2.178.dist-info → angr-9.2.180.dist-info}/METADATA +8 -8
- {angr-9.2.178.dist-info → angr-9.2.180.dist-info}/RECORD +25 -25
- {angr-9.2.178.dist-info → angr-9.2.180.dist-info}/WHEEL +0 -0
- {angr-9.2.178.dist-info → angr-9.2.180.dist-info}/entry_points.txt +0 -0
- {angr-9.2.178.dist-info → angr-9.2.180.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.178.dist-info → angr-9.2.180.dist-info}/top_level.txt +0 -0
|
@@ -1464,6 +1464,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1464
1464
|
switch_head_addr: int = 0
|
|
1465
1465
|
|
|
1466
1466
|
# case 1: the last block is a ConditionNode with two goto statements
|
|
1467
|
+
cond_expr_or_stmt = None
|
|
1468
|
+
cond_case = None
|
|
1467
1469
|
if isinstance(node, SequenceNode) and node.nodes and isinstance(node.nodes[-1], ConditionNode):
|
|
1468
1470
|
cond_node = node.nodes[-1]
|
|
1469
1471
|
assert isinstance(cond_node, ConditionNode)
|
|
@@ -1480,14 +1482,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1480
1482
|
if len(successor_addrs) != 2 or None in successor_addrs:
|
|
1481
1483
|
return False
|
|
1482
1484
|
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
self.cond_proc.convert_claripy_bool_ast(cond_node.condition)
|
|
1486
|
-
)
|
|
1487
|
-
if not cmp:
|
|
1488
|
-
return False
|
|
1489
|
-
cmp_expr, cmp_lb, _cmp_ub = cmp
|
|
1490
|
-
|
|
1485
|
+
cond_expr_or_stmt = cond_node.condition
|
|
1486
|
+
cond_case = 1
|
|
1491
1487
|
assert cond_node.addr is not None
|
|
1492
1488
|
switch_head_addr = cond_node.addr
|
|
1493
1489
|
|
|
@@ -1505,14 +1501,22 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1505
1501
|
if len(successor_addrs) != 2:
|
|
1506
1502
|
return False
|
|
1507
1503
|
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
if not cmp:
|
|
1511
|
-
return False
|
|
1512
|
-
cmp_expr, cmp_lb, _cmp_ub = cmp # pylint:disable=unused-variable
|
|
1513
|
-
|
|
1504
|
+
cond_expr_or_stmt = last_stmt
|
|
1505
|
+
cond_case = 2
|
|
1514
1506
|
switch_head_addr = last_stmt.ins_addr
|
|
1515
1507
|
|
|
1508
|
+
graph = _f(graph_raw)
|
|
1509
|
+
full_graph = _f(full_graph_raw)
|
|
1510
|
+
|
|
1511
|
+
# special fix
|
|
1512
|
+
if (
|
|
1513
|
+
len(successor_addrs) == 2
|
|
1514
|
+
and graph.out_degree[node] == 2
|
|
1515
|
+
and len(set(successor_addrs).intersection({succ.addr for succ in graph.successors(node)})) == 1
|
|
1516
|
+
):
|
|
1517
|
+
# there is an unmatched successor addr! fix it
|
|
1518
|
+
successor_addrs = [succ.addr for succ in graph.successors(node)]
|
|
1519
|
+
|
|
1516
1520
|
for t in successor_addrs:
|
|
1517
1521
|
if t in self.jump_tables:
|
|
1518
1522
|
# this is a candidate!
|
|
@@ -1521,13 +1525,29 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1521
1525
|
else:
|
|
1522
1526
|
return False
|
|
1523
1527
|
|
|
1528
|
+
# extract the comparison expression, lower-, and upper-bounds from the last statement
|
|
1529
|
+
match cond_case:
|
|
1530
|
+
case 1:
|
|
1531
|
+
cmp = switch_extract_cmp_bounds_from_condition(
|
|
1532
|
+
self.cond_proc.convert_claripy_bool_ast(cond_expr_or_stmt)
|
|
1533
|
+
)
|
|
1534
|
+
if not cmp:
|
|
1535
|
+
return False
|
|
1536
|
+
case 2:
|
|
1537
|
+
# extract the comparison expression, lower-, and upper-bounds from the last statement
|
|
1538
|
+
cmp = switch_extract_cmp_bounds(cond_expr_or_stmt)
|
|
1539
|
+
if not cmp:
|
|
1540
|
+
return False
|
|
1541
|
+
case _:
|
|
1542
|
+
# unreachable!
|
|
1543
|
+
return False
|
|
1544
|
+
|
|
1545
|
+
cmp_expr, cmp_lb, _cmp_ub = cmp # pylint:disable=unused-variable
|
|
1546
|
+
|
|
1524
1547
|
jump_table = self.jump_tables[target]
|
|
1525
1548
|
if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
|
|
1526
1549
|
return False
|
|
1527
1550
|
|
|
1528
|
-
graph = _f(graph_raw)
|
|
1529
|
-
full_graph = _f(full_graph_raw)
|
|
1530
|
-
|
|
1531
1551
|
node_a = next(iter(nn for nn in graph.nodes if nn.addr == target), None)
|
|
1532
1552
|
if node_a is None:
|
|
1533
1553
|
return False
|
|
@@ -1585,10 +1605,24 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1585
1605
|
# update node_a
|
|
1586
1606
|
node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
|
|
1587
1607
|
if isinstance(node_a, IncompleteSwitchCaseNode):
|
|
1588
|
-
|
|
1608
|
+
# special case: if node_default is None, node_a has a missing case, and node_a has a successor in the full
|
|
1609
|
+
# graph that is not the default node, then we know
|
|
1610
|
+
# 1. there is a default node (instead of the successor of the entire switch-case construct).
|
|
1611
|
+
# 2. the default node is in a parent region.
|
|
1612
|
+
# as a result, we cannot structure this switch-case right now
|
|
1613
|
+
if (
|
|
1614
|
+
len(node_a.cases) == len(set(jump_table.jumptable_entries)) - 1
|
|
1615
|
+
and node_default is None
|
|
1616
|
+
and len([succ for succ in full_graph.successors(node_a) if succ.addr != node_b_addr]) > 0
|
|
1617
|
+
):
|
|
1618
|
+
return False
|
|
1619
|
+
|
|
1620
|
+
r = self._unpack_incompleteswitchcasenode(graph_raw, node_a, jump_table.jumptable_entries)
|
|
1589
1621
|
if not r:
|
|
1590
1622
|
return False
|
|
1591
|
-
self._unpack_incompleteswitchcasenode(
|
|
1623
|
+
self._unpack_incompleteswitchcasenode(
|
|
1624
|
+
full_graph_raw, node_a, jump_table.jumptable_entries
|
|
1625
|
+
) # this shall not fail
|
|
1592
1626
|
# update node_a
|
|
1593
1627
|
node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
|
|
1594
1628
|
if self._node_order is not None:
|
|
@@ -1721,10 +1755,16 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1721
1755
|
|
|
1722
1756
|
# un-structure IncompleteSwitchCaseNode
|
|
1723
1757
|
if isinstance(node, IncompleteSwitchCaseNode):
|
|
1724
|
-
|
|
1758
|
+
if len(set(jump_table.jumptable_entries)) > len(node.cases):
|
|
1759
|
+
# it has a missing default case node! we cannot structure it as a no-default switch-case
|
|
1760
|
+
return False
|
|
1761
|
+
|
|
1762
|
+
r = self._unpack_incompleteswitchcasenode(graph_raw, node, jump_table.jumptable_entries)
|
|
1725
1763
|
if not r:
|
|
1726
1764
|
return False
|
|
1727
|
-
self._unpack_incompleteswitchcasenode(
|
|
1765
|
+
self._unpack_incompleteswitchcasenode(
|
|
1766
|
+
full_graph_raw, node, jump_table.jumptable_entries
|
|
1767
|
+
) # this shall not fail
|
|
1728
1768
|
# update node
|
|
1729
1769
|
node = next(iter(nn for nn in graph.nodes if nn.addr == jump_table.addr))
|
|
1730
1770
|
|
|
@@ -1934,31 +1974,35 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1934
1974
|
|
|
1935
1975
|
jump_table = self.jump_tables[node.addr]
|
|
1936
1976
|
assert jump_table.jumptable_entries is not None
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
self.
|
|
1961
|
-
|
|
1977
|
+
|
|
1978
|
+
if successors and all(graph.in_degree[succ] == 1 for succ in successors):
|
|
1979
|
+
succ_addrs = {succ.addr for succ in successors}
|
|
1980
|
+
expected_entry_addrs = set(jump_table.jumptable_entries)
|
|
1981
|
+
# test if we have found all entries or all but one entry (where the one missing entry is likely the default
|
|
1982
|
+
# case).
|
|
1983
|
+
if succ_addrs == expected_entry_addrs or (
|
|
1984
|
+
succ_addrs.issubset(expected_entry_addrs) and len(expected_entry_addrs - succ_addrs) == 1
|
|
1985
|
+
):
|
|
1986
|
+
out_nodes = set()
|
|
1987
|
+
for succ in successors:
|
|
1988
|
+
out_nodes |= {
|
|
1989
|
+
succ for succ in full_graph.successors(succ) if succ is not node and succ not in successors
|
|
1990
|
+
}
|
|
1991
|
+
out_nodes = list(out_nodes)
|
|
1992
|
+
if len(out_nodes) <= 1 and node.addr not in self._matched_incomplete_switch_case_addrs:
|
|
1993
|
+
self._matched_incomplete_switch_case_addrs.add(node.addr)
|
|
1994
|
+
new_node = IncompleteSwitchCaseNode(node.addr, node, successors)
|
|
1995
|
+
graph_raw.remove_nodes_from(successors)
|
|
1996
|
+
self.replace_nodes(graph_raw, node, new_node)
|
|
1997
|
+
if out_nodes and out_nodes[0] in graph:
|
|
1998
|
+
graph_raw.add_edge(new_node, out_nodes[0])
|
|
1999
|
+
full_graph_raw.remove_nodes_from(successors)
|
|
2000
|
+
self.replace_nodes(full_graph_raw, node, new_node, update_node_order=True)
|
|
2001
|
+
if out_nodes:
|
|
2002
|
+
full_graph_raw.add_edge(new_node, out_nodes[0])
|
|
2003
|
+
if self._node_order:
|
|
2004
|
+
self._node_order[new_node] = self._node_order[node]
|
|
2005
|
+
return True
|
|
1962
2006
|
return False
|
|
1963
2007
|
|
|
1964
2008
|
def _switch_build_cases(
|
|
@@ -2195,9 +2239,11 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2195
2239
|
out_dst_succs_fullgraph = []
|
|
2196
2240
|
for _, o in other_out_edges:
|
|
2197
2241
|
if o in graph:
|
|
2198
|
-
out_dst_succs
|
|
2242
|
+
if o not in out_dst_succs:
|
|
2243
|
+
out_dst_succs.append(o)
|
|
2199
2244
|
elif o in full_graph:
|
|
2200
|
-
out_dst_succs_fullgraph
|
|
2245
|
+
if o not in out_dst_succs_fullgraph:
|
|
2246
|
+
out_dst_succs_fullgraph.append(o)
|
|
2201
2247
|
out_dst_succ = sorted(out_dst_succs, key=lambda o: o.addr)[0] if out_dst_succs else None
|
|
2202
2248
|
out_dst_succ_fullgraph = (
|
|
2203
2249
|
sorted(out_dst_succs_fullgraph, key=lambda o: o.addr)[0] if out_dst_succs_fullgraph else None
|
|
@@ -2283,6 +2329,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2283
2329
|
def _is_switch_cases_address_loaded_from_memory_head_or_jumpnode(self, graph, node) -> bool:
|
|
2284
2330
|
if self._is_node_unstructured_switch_case_head(node):
|
|
2285
2331
|
return True
|
|
2332
|
+
if isinstance(node, IncompleteSwitchCaseNode):
|
|
2333
|
+
return True
|
|
2286
2334
|
for succ in graph.successors(node):
|
|
2287
2335
|
if self._is_node_unstructured_switch_case_head(succ):
|
|
2288
2336
|
return True
|
|
@@ -2299,20 +2347,31 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2299
2347
|
graph = _f(graph_raw)
|
|
2300
2348
|
|
|
2301
2349
|
succs = list(graph.successors(start_node))
|
|
2302
|
-
if len(succs)
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2350
|
+
if len(succs) != 1:
|
|
2351
|
+
return False
|
|
2352
|
+
end_node = succs[0]
|
|
2353
|
+
if (
|
|
2354
|
+
full_graph.out_degree[start_node] == 1
|
|
2355
|
+
and full_graph.in_degree[end_node] == 1
|
|
2356
|
+
and not full_graph.has_edge(end_node, start_node)
|
|
2357
|
+
and not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, start_node)
|
|
2358
|
+
and end_node not in self.dowhile_known_tail_nodes
|
|
2359
|
+
):
|
|
2360
|
+
new_seq = None
|
|
2361
|
+
if not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, end_node):
|
|
2313
2362
|
# merge two blocks
|
|
2314
2363
|
new_seq = self._merge_nodes(start_node, end_node)
|
|
2315
|
-
|
|
2364
|
+
elif isinstance(end_node, IncompleteSwitchCaseNode):
|
|
2365
|
+
# a special case where there is a node between the actual switch-case head and the jump table
|
|
2366
|
+
# head
|
|
2367
|
+
# binary 7995a0325b446c462bdb6ae10b692eee2ecadd8e888e9d7729befe4412007afb, function 0x1400326C0
|
|
2368
|
+
# keep the IncompleteSwitchCaseNode, and merge two blocks into the head of the IncompleteSwitchCaseNode.
|
|
2369
|
+
new_seq = self._merge_nodes(start_node, end_node.head)
|
|
2370
|
+
new_seq.addr = end_node.addr
|
|
2371
|
+
end_node.head = new_seq
|
|
2372
|
+
new_seq = end_node
|
|
2373
|
+
|
|
2374
|
+
if new_seq is not None:
|
|
2316
2375
|
# on the original graph
|
|
2317
2376
|
self.replace_nodes(graph_raw, start_node, new_seq, old_node_1=end_node if end_node in graph else None)
|
|
2318
2377
|
# on the graph with successors
|
|
@@ -3267,17 +3326,23 @@ class PhoenixStructurer(StructurerBase):
|
|
|
3267
3326
|
return True, new_seq
|
|
3268
3327
|
|
|
3269
3328
|
@staticmethod
|
|
3270
|
-
def _unpack_incompleteswitchcasenode(
|
|
3329
|
+
def _unpack_incompleteswitchcasenode(
|
|
3330
|
+
graph: networkx.DiGraph, incscnode: IncompleteSwitchCaseNode, jumptable_entries: list[int]
|
|
3331
|
+
) -> bool:
|
|
3271
3332
|
preds = list(graph.predecessors(incscnode))
|
|
3272
3333
|
succs = list(graph.successors(incscnode))
|
|
3273
|
-
|
|
3334
|
+
non_case_succs = [succ for succ in succs if succ.addr not in jumptable_entries]
|
|
3335
|
+
if len(non_case_succs) <= 1:
|
|
3274
3336
|
graph.remove_node(incscnode)
|
|
3275
3337
|
for pred in preds:
|
|
3276
3338
|
graph.add_edge(pred, incscnode.head)
|
|
3339
|
+
for succ in succs:
|
|
3340
|
+
if succ not in non_case_succs:
|
|
3341
|
+
graph.add_edge(incscnode.head, succ)
|
|
3277
3342
|
for case_node in incscnode.cases:
|
|
3278
3343
|
graph.add_edge(incscnode.head, case_node)
|
|
3279
|
-
if
|
|
3280
|
-
graph.add_edge(case_node,
|
|
3344
|
+
if non_case_succs:
|
|
3345
|
+
graph.add_edge(case_node, non_case_succs[0])
|
|
3281
3346
|
return True
|
|
3282
3347
|
return False
|
|
3283
3348
|
|
|
@@ -163,12 +163,30 @@ def switch_extract_cmp_bounds(
|
|
|
163
163
|
def switch_extract_cmp_bounds_from_condition(cond: ailment.Expr.Expression) -> tuple[Any, int, int] | None:
|
|
164
164
|
# TODO: Add more operations
|
|
165
165
|
if isinstance(cond, ailment.Expr.BinaryOp):
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
op = cond.op
|
|
167
|
+
op0, op1 = cond.operands
|
|
168
|
+
if not isinstance(op1, ailment.Expr.Const):
|
|
169
|
+
# swap them
|
|
170
|
+
match op:
|
|
171
|
+
case "CmpLE":
|
|
172
|
+
op = "CmpGE"
|
|
173
|
+
case "CmpLT":
|
|
174
|
+
op = "CmpGT"
|
|
175
|
+
case "CmpGE":
|
|
176
|
+
op = "CmpLE"
|
|
177
|
+
case "CmpGT":
|
|
178
|
+
op = "CmpLT"
|
|
179
|
+
case _:
|
|
180
|
+
# unsupported
|
|
181
|
+
return None
|
|
182
|
+
op0, op1 = op1, op0
|
|
183
|
+
|
|
184
|
+
if op in {"CmpLE", "CmpLT"}:
|
|
185
|
+
if not (isinstance(op1, ailment.Expr.Const) and isinstance(op1.value, int)):
|
|
168
186
|
return None
|
|
169
|
-
cmp_ub =
|
|
187
|
+
cmp_ub = op1.value if op == "CmpLE" else op1.value - 1
|
|
170
188
|
cmp_lb = 0
|
|
171
|
-
cmp =
|
|
189
|
+
cmp = op0
|
|
172
190
|
if (
|
|
173
191
|
isinstance(cmp, ailment.Expr.BinaryOp)
|
|
174
192
|
and cmp.op == "Sub"
|
|
@@ -180,15 +198,15 @@ def switch_extract_cmp_bounds_from_condition(cond: ailment.Expr.Expression) -> t
|
|
|
180
198
|
cmp = cmp.operands[0]
|
|
181
199
|
return cmp, cmp_lb, cmp_ub
|
|
182
200
|
|
|
183
|
-
if
|
|
201
|
+
if op in {"CmpGE", "CmpGT"}:
|
|
184
202
|
# We got the negated condition here
|
|
185
203
|
# CmpGE -> CmpLT
|
|
186
204
|
# CmpGT -> CmpLE
|
|
187
|
-
if not (isinstance(
|
|
205
|
+
if not (isinstance(op1, ailment.Expr.Const) and isinstance(op1.value, int)):
|
|
188
206
|
return None
|
|
189
|
-
cmp_ub =
|
|
207
|
+
cmp_ub = op1.value if op == "CmpGT" else op1.value - 1
|
|
190
208
|
cmp_lb = 0
|
|
191
|
-
cmp =
|
|
209
|
+
cmp = op0
|
|
192
210
|
if (
|
|
193
211
|
isinstance(cmp, ailment.Expr.BinaryOp)
|
|
194
212
|
and cmp.op == "Sub"
|
angr/analyses/disassembly.py
CHANGED
|
@@ -34,7 +34,7 @@ class DisassemblyPiece:
|
|
|
34
34
|
addr = None
|
|
35
35
|
ident = float("nan")
|
|
36
36
|
|
|
37
|
-
def render(self, formatting=None):
|
|
37
|
+
def render(self, formatting=None) -> list[str]:
|
|
38
38
|
x = self._render(formatting)
|
|
39
39
|
if len(x) == 1:
|
|
40
40
|
return [self.highlight(x[0], formatting)]
|
|
@@ -73,7 +73,7 @@ class DisassemblyPiece:
|
|
|
73
73
|
pass
|
|
74
74
|
return string
|
|
75
75
|
|
|
76
|
-
def __eq__(self, other):
|
|
76
|
+
def __eq__(self, other) -> bool:
|
|
77
77
|
return False
|
|
78
78
|
|
|
79
79
|
|
|
@@ -175,7 +175,7 @@ class Instruction(DisassemblyPiece):
|
|
|
175
175
|
self.arch = self.project.arch
|
|
176
176
|
self.format = ""
|
|
177
177
|
self.components = ()
|
|
178
|
-
self.opcode = None
|
|
178
|
+
self.opcode: Opcode = None # type:ignore
|
|
179
179
|
self.operands = []
|
|
180
180
|
|
|
181
181
|
# the following members will be filled in after dissecting the instruction
|
|
@@ -261,7 +261,11 @@ class Instruction(DisassemblyPiece):
|
|
|
261
261
|
if i > 0 and opr_pieces[i - 1] == "-":
|
|
262
262
|
v = -v
|
|
263
263
|
cur_operand.pop()
|
|
264
|
-
|
|
264
|
+
with_pound_sign = False
|
|
265
|
+
if i > 0 and opr_pieces[i - 1] == "#":
|
|
266
|
+
with_pound_sign = True
|
|
267
|
+
cur_operand.pop()
|
|
268
|
+
cur_operand.append(Value(v, with_sign, render_with_pound_sign=with_pound_sign))
|
|
265
269
|
elif p[0].isalpha() and p in self.arch.registers:
|
|
266
270
|
cur_operand.append(Register(p))
|
|
267
271
|
else:
|
|
@@ -592,7 +596,7 @@ class Opcode(DisassemblyPiece):
|
|
|
592
596
|
class Operand(DisassemblyPiece):
|
|
593
597
|
def __init__(self, op_num, children, parentinsn):
|
|
594
598
|
self.addr = parentinsn.addr
|
|
595
|
-
self.children = children
|
|
599
|
+
self.children: list = children
|
|
596
600
|
self.parentinsn = parentinsn
|
|
597
601
|
self.op_num = op_num
|
|
598
602
|
self.ident = (self.addr, "operand", self.op_num)
|
|
@@ -639,14 +643,33 @@ class Operand(DisassemblyPiece):
|
|
|
639
643
|
operand = cls(op_num, children, parentinsn)
|
|
640
644
|
|
|
641
645
|
# Post-processing
|
|
642
|
-
if cls is MemoryOperand
|
|
646
|
+
if cls is MemoryOperand:
|
|
647
|
+
operand = Operand._post_process_memory_operand(parentinsn, operand)
|
|
648
|
+
|
|
649
|
+
return operand
|
|
650
|
+
|
|
651
|
+
@staticmethod
|
|
652
|
+
def _post_process_memory_operand(parentinsn: Instruction, operand: MemoryOperand) -> Operand:
|
|
653
|
+
archname = parentinsn.arch.name
|
|
654
|
+
if archname in {"AMD64", "ARM", "ARMEL", "ARMHF", "ARMCortexM"} and len(operand.values) == 2:
|
|
643
655
|
op0, op1 = operand.values
|
|
644
656
|
if type(op0) is Register and op0.is_ip and type(op1) is Value:
|
|
645
|
-
# Indirect addressing in
|
|
657
|
+
# Indirect addressing in x86-64 and ARM
|
|
646
658
|
# 400520 push [rip+0x200782] ==> 400520 push [0x600ca8]
|
|
647
|
-
|
|
659
|
+
# 80410e6 ldr r1, [pc, #0x98] ==> 80410e6 ldr r1, [0x8041182]
|
|
660
|
+
match archname:
|
|
661
|
+
case "AMD64":
|
|
662
|
+
absolute_addr = parentinsn.addr + parentinsn.size + op1.val
|
|
663
|
+
case "ARM" | "ARMEL" | "ARMHF" | "ARMCortexM":
|
|
664
|
+
if parentinsn.addr & 1:
|
|
665
|
+
# thumb mode
|
|
666
|
+
absolute_addr = (parentinsn.addr & 0xFFFF_FFFE) + 4 + op1.val
|
|
667
|
+
else:
|
|
668
|
+
# arm mode
|
|
669
|
+
absolute_addr = parentinsn.addr + 8 + op1.val
|
|
670
|
+
case _:
|
|
671
|
+
raise AngrTypeError(f"Unsupported architecture {archname} for post-processing memory operand.")
|
|
648
672
|
return MemoryOperand(1, [*operand.prefix, "[", Value(absolute_addr, False), "]"], parentinsn)
|
|
649
|
-
|
|
650
673
|
return operand
|
|
651
674
|
|
|
652
675
|
|
|
@@ -680,13 +703,15 @@ class MemoryOperand(Operand):
|
|
|
680
703
|
# [ '[', Register, ']' ]
|
|
681
704
|
# or
|
|
682
705
|
# [ Value, '(', Register, ')' ]
|
|
706
|
+
# or
|
|
707
|
+
# [ Register, ",", Value]
|
|
683
708
|
|
|
684
709
|
# it will be converted into more meaningful and Pythonic properties
|
|
685
710
|
|
|
686
711
|
self.segment_selector = None
|
|
687
712
|
self.prefix = []
|
|
688
713
|
self.suffix_str = "" # could be arm pre index mark "!"
|
|
689
|
-
self.values = []
|
|
714
|
+
self.values: list[str | DisassemblyPiece] = []
|
|
690
715
|
self.offset = []
|
|
691
716
|
# offset_location
|
|
692
717
|
# - prefix: -0xff00($gp)
|
|
@@ -698,6 +723,10 @@ class MemoryOperand(Operand):
|
|
|
698
723
|
# - curly: {rax+0x10}
|
|
699
724
|
# - paren: (rax+0x10)
|
|
700
725
|
self.values_style = "square"
|
|
726
|
+
# separator (between register and value)
|
|
727
|
+
# - "": rax+0x10
|
|
728
|
+
# - "comma": r0, #0x10
|
|
729
|
+
self.separator = ""
|
|
701
730
|
|
|
702
731
|
try:
|
|
703
732
|
if "[" in self.children:
|
|
@@ -711,8 +740,8 @@ class MemoryOperand(Operand):
|
|
|
711
740
|
l.error("Failed to parse operand children %s. Please report to Fish.", self.children)
|
|
712
741
|
|
|
713
742
|
# setup all dummy properties
|
|
714
|
-
self.prefix =
|
|
715
|
-
self.values =
|
|
743
|
+
self.prefix = []
|
|
744
|
+
self.values = []
|
|
716
745
|
|
|
717
746
|
def _parse_memop_squarebracket(self):
|
|
718
747
|
if self.children[0] != "[":
|
|
@@ -746,6 +775,27 @@ class MemoryOperand(Operand):
|
|
|
746
775
|
raise ValueError
|
|
747
776
|
|
|
748
777
|
self.values = self.children[square_bracket_pos + 1 : close_square_pos]
|
|
778
|
+
if len(self.values) >= 3 and self.values[1] == ",":
|
|
779
|
+
# arm style memory operand: [r0, #0x10], or [pc, r3, lsl #1]
|
|
780
|
+
self.separator = "comma"
|
|
781
|
+
# remove all commas and consolidate all remaining strings
|
|
782
|
+
new_values = []
|
|
783
|
+
last_item = None
|
|
784
|
+
for v in self.values:
|
|
785
|
+
if v == ",":
|
|
786
|
+
if last_item is not None:
|
|
787
|
+
new_values.append(last_item)
|
|
788
|
+
last_item = None
|
|
789
|
+
elif isinstance(v, str):
|
|
790
|
+
last_item = v if last_item is None else last_item + v
|
|
791
|
+
else:
|
|
792
|
+
if last_item is not None:
|
|
793
|
+
new_values.append(last_item)
|
|
794
|
+
last_item = None
|
|
795
|
+
new_values.append(v)
|
|
796
|
+
if last_item is not None:
|
|
797
|
+
new_values.append(last_item)
|
|
798
|
+
self.values = new_values
|
|
749
799
|
|
|
750
800
|
def _parse_memop_paren(self):
|
|
751
801
|
offset = []
|
|
@@ -801,7 +851,8 @@ class MemoryOperand(Operand):
|
|
|
801
851
|
if custom_values_str is not None:
|
|
802
852
|
value_str = custom_values_str
|
|
803
853
|
else:
|
|
804
|
-
|
|
854
|
+
sep = "," if self.separator == "comma" else ""
|
|
855
|
+
value_str = sep.join(x.render(formatting)[0] if not isinstance(x, str) else x for x in self.values)
|
|
805
856
|
|
|
806
857
|
if values_style == "curly":
|
|
807
858
|
left_paren, right_paren = "{", "}"
|
|
@@ -811,7 +862,7 @@ class MemoryOperand(Operand):
|
|
|
811
862
|
left_paren, right_paren = "[", "]"
|
|
812
863
|
|
|
813
864
|
if self.offset:
|
|
814
|
-
offset_str = "".join(x.render(formatting)[0] if not isinstance(x,
|
|
865
|
+
offset_str = "".join(x.render(formatting)[0] if not isinstance(x, str) else x for x in self.offset)
|
|
815
866
|
|
|
816
867
|
# combine values and offsets according to self.offset_location
|
|
817
868
|
if self.offset_location == "prefix":
|
|
@@ -829,9 +880,7 @@ class MemoryOperand(Operand):
|
|
|
829
880
|
prefix_str += " "
|
|
830
881
|
|
|
831
882
|
if self.offset:
|
|
832
|
-
offset_str = "".join(
|
|
833
|
-
x.render(formatting)[0] if not isinstance(x, (bytes, str)) else x for x in self.offset
|
|
834
|
-
)
|
|
883
|
+
offset_str = "".join(x.render(formatting)[0] if not isinstance(x, str) else x for x in self.offset)
|
|
835
884
|
|
|
836
885
|
# combine values and offsets according to self.offset_location
|
|
837
886
|
if self.offset_location == "prefix":
|
|
@@ -873,12 +922,14 @@ class Register(OperandPiece):
|
|
|
873
922
|
|
|
874
923
|
|
|
875
924
|
class Value(OperandPiece):
|
|
876
|
-
def __init__(self, val, render_with_sign):
|
|
925
|
+
def __init__(self, val, render_with_sign, render_with_pound_sign: bool = False):
|
|
877
926
|
self.val = val
|
|
878
927
|
self.render_with_sign = render_with_sign
|
|
928
|
+
self.render_with_pound_sign = render_with_pound_sign
|
|
879
929
|
|
|
880
930
|
@property
|
|
881
931
|
def project(self):
|
|
932
|
+
assert self.parentop is not None
|
|
882
933
|
return self.parentop.parentinsn.project
|
|
883
934
|
|
|
884
935
|
def __eq__(self, other):
|
|
@@ -888,26 +939,26 @@ class Value(OperandPiece):
|
|
|
888
939
|
if formatting is not None:
|
|
889
940
|
try:
|
|
890
941
|
style = formatting["int_styles"][self.ident]
|
|
891
|
-
if style[0] == "hex":
|
|
892
|
-
if self.render_with_sign:
|
|
893
|
-
return [f"{self.val:+#x}"]
|
|
894
|
-
return [f"{self.val:#x}"]
|
|
895
|
-
if style[0] == "dec":
|
|
896
|
-
if self.render_with_sign:
|
|
897
|
-
return [f"{self.val:+d}"]
|
|
898
|
-
return [str(self.val)]
|
|
899
942
|
if style[0] == "label":
|
|
900
943
|
labeloffset = style[1]
|
|
901
944
|
if labeloffset == 0:
|
|
902
945
|
lbl = self.project.kb.labels[self.val]
|
|
903
946
|
return [lbl]
|
|
904
|
-
|
|
905
|
-
"
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
947
|
+
s = "{}{}{:#+x}".format(
|
|
948
|
+
"+" if self.render_with_sign else "",
|
|
949
|
+
self.project.kb.labels[self.val + labeloffset],
|
|
950
|
+
labeloffset,
|
|
951
|
+
)
|
|
952
|
+
elif style[0] == "hex":
|
|
953
|
+
s = f"{self.val:+#x}" if self.render_with_sign else f"{self.val:#x}"
|
|
954
|
+
if self.render_with_pound_sign:
|
|
955
|
+
s = "#" + s
|
|
956
|
+
else: # "dec"
|
|
957
|
+
s = f"{self.val:+d}" if self.render_with_sign else str(self.val)
|
|
958
|
+
|
|
959
|
+
if self.render_with_pound_sign:
|
|
960
|
+
s = "#" + s
|
|
961
|
+
return [s]
|
|
911
962
|
except KeyError:
|
|
912
963
|
pass
|
|
913
964
|
|
|
@@ -927,9 +978,10 @@ class Value(OperandPiece):
|
|
|
927
978
|
return [("+" if self.render_with_sign else "") + lbl]
|
|
928
979
|
if func is not None:
|
|
929
980
|
return [func.demangled_name]
|
|
930
|
-
if self.render_with_sign
|
|
931
|
-
|
|
932
|
-
|
|
981
|
+
s = f"{self.val:+#x}" if self.render_with_sign else f"{self.val:#x}"
|
|
982
|
+
if self.render_with_pound_sign:
|
|
983
|
+
s = "#" + s
|
|
984
|
+
return [s]
|
|
933
985
|
|
|
934
986
|
|
|
935
987
|
class Comment(DisassemblyPiece):
|
|
@@ -1079,10 +1131,11 @@ class Disassembly(Analysis):
|
|
|
1079
1131
|
if irsb.statements is not None:
|
|
1080
1132
|
if pcode is not None and isinstance(self.project.factory.default_engine, pcode.HeavyPcodeMixin):
|
|
1081
1133
|
addr = None
|
|
1082
|
-
for stmt_idx, op in enumerate(irsb._ops):
|
|
1134
|
+
for stmt_idx, op in enumerate(irsb._ops): # type:ignore
|
|
1083
1135
|
if op.opcode == pypcode.OpCode.IMARK:
|
|
1084
1136
|
addr = op.inputs[0].offset
|
|
1085
1137
|
else:
|
|
1138
|
+
assert addr is not None
|
|
1086
1139
|
addr_to_ops_map[addr].append(IROp(addr, stmt_idx, op, irsb))
|
|
1087
1140
|
else:
|
|
1088
1141
|
for seq, stmt in enumerate(irsb.statements):
|
|
@@ -1166,22 +1219,23 @@ class Disassembly(Analysis):
|
|
|
1166
1219
|
buf = []
|
|
1167
1220
|
|
|
1168
1221
|
if formatting is None:
|
|
1222
|
+
colors = (
|
|
1223
|
+
{
|
|
1224
|
+
"address": "gray",
|
|
1225
|
+
"bytes": "cyan",
|
|
1226
|
+
"edge": "yellow",
|
|
1227
|
+
Label: "bright_yellow",
|
|
1228
|
+
ConstantOperand: "cyan",
|
|
1229
|
+
MemoryOperand: "yellow",
|
|
1230
|
+
Comment: "gray",
|
|
1231
|
+
Hook: "green",
|
|
1232
|
+
}
|
|
1233
|
+
if ansi_color_enabled and color
|
|
1234
|
+
else {}
|
|
1235
|
+
)
|
|
1169
1236
|
formatting = {
|
|
1170
|
-
"colors":
|
|
1171
|
-
|
|
1172
|
-
"address": "gray",
|
|
1173
|
-
"bytes": "cyan",
|
|
1174
|
-
"edge": "yellow",
|
|
1175
|
-
Label: "bright_yellow",
|
|
1176
|
-
ConstantOperand: "cyan",
|
|
1177
|
-
MemoryOperand: "yellow",
|
|
1178
|
-
Comment: "gray",
|
|
1179
|
-
Hook: "green",
|
|
1180
|
-
}
|
|
1181
|
-
if ansi_color_enabled and color
|
|
1182
|
-
else {}
|
|
1183
|
-
),
|
|
1184
|
-
"format_callback": lambda item, s: ansi_color(s, formatting["colors"].get(type(item), None)),
|
|
1237
|
+
"colors": colors,
|
|
1238
|
+
"format_callback": lambda item, s: ansi_color(s, colors.get(type(item), None)),
|
|
1185
1239
|
}
|
|
1186
1240
|
|
|
1187
1241
|
def col(item: Any) -> str | None:
|
|
@@ -1234,12 +1288,14 @@ class Disassembly(Analysis):
|
|
|
1234
1288
|
# Format the instruction's address, bytes, disassembly, and comment
|
|
1235
1289
|
s_plain = format_address(item.addr, False)
|
|
1236
1290
|
s = format_address(item.addr)
|
|
1291
|
+
bytes_column = 0
|
|
1237
1292
|
if show_bytes:
|
|
1238
1293
|
bytes_column = len(s_plain)
|
|
1239
1294
|
s_plain += format_bytes(insn_bytes[0], False)
|
|
1240
1295
|
s += format_bytes(insn_bytes[0])
|
|
1241
1296
|
s_plain += item.render()[0]
|
|
1242
1297
|
s += item.render(formatting)[0]
|
|
1298
|
+
comment_column = 0
|
|
1243
1299
|
if comment is not None:
|
|
1244
1300
|
comment_column = len(s_plain)
|
|
1245
1301
|
s += format_comment(comment.text[0])
|