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.

@@ -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
- # extract the comparison expression, lower-, and upper-bounds from the last statement
1484
- cmp = switch_extract_cmp_bounds_from_condition(
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
- # extract the comparison expression, lower-, and upper-bounds from the last statement
1509
- cmp = switch_extract_cmp_bounds(last_stmt)
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
- r = self._unpack_incompleteswitchcasenode(graph_raw, node_a)
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(full_graph_raw, node_a) # this shall not fail
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
- r = self._unpack_incompleteswitchcasenode(graph_raw, node)
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(full_graph_raw, node) # this shall not fail
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
- if (
1938
- successors
1939
- and {succ.addr for succ in successors} == set(jump_table.jumptable_entries)
1940
- and all(graph.in_degree[succ] == 1 for succ in successors)
1941
- ):
1942
- out_nodes = set()
1943
- for succ in successors:
1944
- out_nodes |= {
1945
- succ for succ in full_graph.successors(succ) if succ is not node and succ not in successors
1946
- }
1947
- out_nodes = list(out_nodes)
1948
- if len(out_nodes) <= 1 and node.addr not in self._matched_incomplete_switch_case_addrs:
1949
- self._matched_incomplete_switch_case_addrs.add(node.addr)
1950
- new_node = IncompleteSwitchCaseNode(node.addr, node, successors)
1951
- graph_raw.remove_nodes_from(successors)
1952
- self.replace_nodes(graph_raw, node, new_node)
1953
- if out_nodes and out_nodes[0] in graph:
1954
- graph_raw.add_edge(new_node, out_nodes[0])
1955
- full_graph_raw.remove_nodes_from(successors)
1956
- self.replace_nodes(full_graph_raw, node, new_node, update_node_order=True)
1957
- if out_nodes:
1958
- full_graph_raw.add_edge(new_node, out_nodes[0])
1959
- if self._node_order:
1960
- self._node_order[new_node] = self._node_order[node]
1961
- return True
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.append(o)
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.append(o)
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) == 1:
2303
- end_node = succs[0]
2304
- if (
2305
- full_graph.out_degree[start_node] == 1
2306
- and full_graph.in_degree[end_node] == 1
2307
- and not full_graph.has_edge(end_node, start_node)
2308
- and not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, end_node)
2309
- and not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, start_node)
2310
- and end_node not in self.dowhile_known_tail_nodes
2311
- and not isinstance(end_node, IncompleteSwitchCaseNode)
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(graph: networkx.DiGraph, incscnode: IncompleteSwitchCaseNode) -> bool:
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
- if len(succs) <= 1:
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 succs:
3280
- graph.add_edge(case_node, succs[0])
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
- if cond.op in {"CmpLE", "CmpLT"}:
167
- if not (isinstance(cond.operands[1], ailment.Expr.Const) and isinstance(cond.operands[1].value, int)):
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 = cond.operands[1].value if cond.op == "CmpLE" else cond.operands[1].value - 1
187
+ cmp_ub = op1.value if op == "CmpLE" else op1.value - 1
170
188
  cmp_lb = 0
171
- cmp = cond.operands[0]
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 cond.op in {"CmpGE", "CmpGT"}:
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(cond.operands[1], ailment.Expr.Const) and isinstance(cond.operands[1].value, int)):
205
+ if not (isinstance(op1, ailment.Expr.Const) and isinstance(op1.value, int)):
188
206
  return None
189
- cmp_ub = cond.operands[1].value if cond.op == "CmpGT" else cond.operands[1].value - 1
207
+ cmp_ub = op1.value if op == "CmpGT" else op1.value - 1
190
208
  cmp_lb = 0
191
- cmp = cond.operands[0]
209
+ cmp = op0
192
210
  if (
193
211
  isinstance(cmp, ailment.Expr.BinaryOp)
194
212
  and cmp.op == "Sub"
@@ -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
- cur_operand.append(Value(v, with_sign))
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 and parentinsn.arch.name in {"AMD64"} and len(operand.values) == 2:
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 x86_64
657
+ # Indirect addressing in x86-64 and ARM
646
658
  # 400520 push [rip+0x200782] ==> 400520 push [0x600ca8]
647
- absolute_addr = parentinsn.addr + parentinsn.size + op1.val
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 = None
715
- self.values = None
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
- value_str = "".join(x.render(formatting)[0] if not isinstance(x, (bytes, str)) else x for x in self.values)
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, (bytes, str)) else x for x in self.offset)
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
- return [
905
- "{}{}{:#+x}".format(
906
- "+" if self.render_with_sign else "",
907
- self.project.kb.labels[self.val + labeloffset],
908
- labeloffset,
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
- return [f"{self.val:+#x}"]
932
- return [f"{self.val:#x}"]
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])