Repository: angr/angr

Issue:
Printing basic block instructions after running CFGEmulated

```python
proj = angr.Project('hello', load_options={"auto_load_libs": False})

cfg = proj.analyses.CFGEmulated(keep_state=True,
                              state_add_options=angr.sim_options.refs,
                              context_sensitivity_level=2)

functions_found = [ func for addr,func in cfg.kb.functions.items() ]
for t_fun in functions_found:
    for t_blk in t_fun.blocks:
        print(t_blk.addr)
        print(t_blk.pp())
```

Results in:

```python
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_52745/2216003323.py in <module>
     16     for t_blk in t_fun.blocks:
     17         print(t_blk.addr)
---> 18         print(t_blk.pp())
     19 block = proj.factory.block(addr=int(4198496))
     20 block.pp()

~/venv/lib/python3.8/site-packages/angr/block.py in pp(self, **kwargs)
    242     def pp(self, **kwargs):
    243         if self._project is not None:
--> 244             print(self._project.analyses.Disassembly(ranges=[(self.addr, self.addr + self.size)]).render(**kwargs))
    245         else:
    246             self.disassembly.pp()

~/venv/lib/python3.8/site-packages/angr/analyses/analysis.py in __call__(self, *args, **kwargs)
    113 
    114         oself._show_progressbar = show_progressbar
--> 115         oself.__init__(*args, **kwargs)
    116         return oself
    117 

~/venv/lib/python3.8/site-packages/angr/analyses/disassembly.py in __init__(self, function, ranges, include_ir)
    847                     assert(start < end)
    848                     # Grab all blocks that intersect target range
--> 849                     blocks = sorted([n.block.codenode
    850                                      for n in self._graph.nodes() if not (n.addr + n.size <= start or n.addr >= end)],
    851                                     key=lambda node: (node.addr, not node.is_hook))

~/venv/lib/python3.8/site-packages/angr/analyses/disassembly.py in <listcomp>(.0)
    848                     # Grab all blocks that intersect target range
    849                     blocks = sorted([n.block.codenode
--> 850                                      for n in self._graph.nodes() if not (n.addr + n.size <= start or n.addr >= end)],
    851                                     key=lambda node: (node.addr, not node.is_hook))
    852 

TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
```

Binary file:
[hello.zip](https://github.com/angr/angr/files/7678527/hello.zip)



Discussion:

Resolving patch:
diff --git a/angr/analyses/disassembly.py b/angr/analyses/disassembly.py
--- a/angr/analyses/disassembly.py
+++ b/angr/analyses/disassembly.py
@@ -149,13 +149,9 @@ def _render(self, formatting):
 
 
 class Hook(DisassemblyPiece):
-    def __init__(self, addr, parentblock):
-        self.addr = addr
-        self.parentblock = parentblock
-        if parentblock and parentblock.parentfunc:
-            simproc_name = str(parentblock.parentfunc.sim_procedure)
-        else:
-            simproc_name = "Unknown"
+    def __init__(self, block):
+        self.addr = block.addr
+        simproc_name = str(block.sim_procedure)
         self.name = simproc_name.split()[-1].strip("'<>")
         self.short_name = simproc_name.strip("'<>").split('.')[-1]
 
@@ -844,21 +840,27 @@ def __init__(self, function: Optional[Function] = None, ranges: Optional[Sequenc
             else:
                 self._graph = cfg.graph
                 for start, end in ranges:
-                    assert(start < end)
+                    if start == end:
+                        continue
+                    assert start < end
+
                     # Grab all blocks that intersect target range
-                    blocks = sorted([n.block.codenode
-                                     for n in self._graph.nodes() if not (n.addr + n.size <= start or n.addr >= end)],
+                    blocks = sorted([n.to_codenode()
+                                     for n in self._graph.nodes() if not (n.addr + (n.size or 1) <= start or
+                                                                          n.addr >= end)],
                                     key=lambda node: (node.addr, not node.is_hook))
 
                     # Trim blocks that are not within range
                     for i, block in enumerate(blocks):
-                        if block.addr < start:
+                        if block.size and block.addr < start:
                             delta = start - block.addr
-                            blocks[i] = BlockNode(block.addr + delta, block.size - delta, block.bytestr[delta:])
+                            block_bytes = block.bytestr[delta:] if block.bytestr else None
+                            blocks[i] = BlockNode(block.addr + delta, block.size - delta, block_bytes)
                     for i, block in enumerate(blocks):
-                        if block.addr + block.size > end:
+                        if block.size and block.addr + block.size > end:
                             delta = block.addr + block.size - end
-                            blocks[i] = BlockNode(block.addr, block.size - delta, block.bytestr[0:-delta])
+                            block_bytes = block.bytestr[0:-delta] if block.bytestr else None
+                            blocks[i] = BlockNode(block.addr, block.size - delta, block_bytes)
 
                     for block in blocks:
                         self.parse_block(block)
@@ -925,7 +927,7 @@ def parse_block(self, block: BlockNode) -> None:
         self.raw_result.append(bs)
 
         if block.is_hook:
-            hook = Hook(block.addr, bs)
+            hook = Hook(block)
             self.raw_result.append(hook)
             self.raw_result_map['hooks'][block.addr] = hook
         elif self.project.arch.capstone_support:
@@ -986,6 +988,7 @@ def render(self, formatting=None, show_edges: bool = True, show_addresses: bool
                     ConstantOperand: 'cyan',
                     MemoryOperand:   'yellow',
                     Comment:         'gray',
+                    Hook:            'green',
                 } if ansi_color_enabled else {},
                 'format_callback': lambda item, s: ansi_color(s, formatting['colors'].get(type(item), None))
             }
@@ -1023,6 +1026,9 @@ def format_comment(text: str, color: bool = True) -> str:
                 buf.append(pad + item.render(formatting)[0])
             elif isinstance(item, Comment):
                 comment = item
+            elif isinstance(item, Hook):
+                a2ln[item.addr].append(len(buf))
+                buf.append(format_address(item.addr) + item.render(formatting)[0])
             elif isinstance(item, Instruction):
                 a2ln[item.addr].append(len(buf))
                 lines = []
@@ -1062,12 +1068,14 @@ def format_comment(text: str, color: bool = True) -> str:
 
                 buf.extend(lines)
             else:
-                buf.append(item.render(formatting))
+                buf.append(''.join(item.render(formatting)))
 
         if self._graph is not None and show_edges and buf:
             edges_by_line = set()
             for edge in self._graph.edges.items():
                 from_block, to_block = edge[0]
+                if from_block.size is None:
+                    continue
                 if to_block.addr != from_block.addr + from_block.size:
                     from_addr = edge[1]['ins_addr']
                     to_addr = to_block.addr


Test patch: