summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/examples/newline_render_layer.py215
1 files changed, 215 insertions, 0 deletions
diff --git a/python/examples/newline_render_layer.py b/python/examples/newline_render_layer.py
new file mode 100644
index 00000000..c6119d2e
--- /dev/null
+++ b/python/examples/newline_render_layer.py
@@ -0,0 +1,215 @@
+from typing import List
+
+import binaryninja
+from binaryninja import RenderLayer, InstructionTextToken, \
+ InstructionTextTokenType, DisassemblyTextLine, log_info
+
+
+"""
+Render Layer that splits string literals containing newline characters into multiple lines.
+
+Before:
+ char* s = "Hi, guys!\nWe all love Binja!";
+
+After:
+ char* s = "Hi, guys!\n"
+ "We all love Binja!";
+"""
+
+
+class NewlineSplitRenderLayer(RenderLayer):
+ name = "Split Strings at Newlines"
+
+ def split_string_token(self, token: InstructionTextToken) -> List[InstructionTextToken]:
+ """
+ Split a string token containing \n into multiple tokens.
+ Returns a list of tokens representing the split string.
+ """
+ text = token.text
+ log_info(f"[NewlineSplitRenderLayer] Examining token: type={token.type}, text={repr(text)}")
+
+ # Check if this token contains \n (the token text doesn't include quotes)
+ if '\\n' not in text:
+ return [token]
+
+ log_info(f"[NewlineSplitRenderLayer] Found string with \\n: {repr(text)}")
+
+ # The text is the string content (no quotes in token.text)
+ content = text
+
+ # Split by \n but keep the \n with the preceding part
+ parts = []
+ current_part = ""
+ i = 0
+ while i < len(content):
+ if i < len(content) - 1 and content[i] == '\\' and content[i+1] == 'n':
+ current_part += '\\n'
+ parts.append(current_part)
+ current_part = ""
+ i += 2
+ else:
+ current_part += content[i]
+ i += 1
+
+ # Add any remaining content
+ if current_part:
+ parts.append(current_part)
+
+ # If we only have one part, return the original token
+ if len(parts) <= 1:
+ return [token]
+
+ log_info(f"[NewlineSplitRenderLayer] Splitting into {len(parts)} parts: {parts}")
+
+ # Create tokens for each part (token.text doesn't include quotes, so don't add them)
+ result = []
+ for i, part in enumerate(parts):
+ result.append(InstructionTextToken(token.type, part, token.value, token.size, token.operand, token.context, token.address, token.confidence))
+
+ return result
+
+ def apply_to_block(
+ self,
+ block: 'binaryninja.BasicBlock',
+ lines: List['binaryninja.DisassemblyTextLine']
+ ):
+ log_info(f"[NewlineSplitRenderLayer] apply_to_block called with {len(lines)} lines")
+ new_lines = []
+
+ for line in lines:
+ # Look for string tokens
+ has_split = False
+ split_info = None
+
+ for i, token in enumerate(line.tokens):
+ if token.type == InstructionTextTokenType.StringToken:
+ log_info(f"[NewlineSplitRenderLayer] Found StringToken at index {i}")
+ split_tokens = self.split_string_token(token)
+ if len(split_tokens) > 1:
+ has_split = True
+ split_info = (i, split_tokens)
+ log_info(f"[NewlineSplitRenderLayer] Will split this line into {len(split_tokens)} parts")
+ break
+
+ if not has_split:
+ new_lines.append(line)
+ continue
+
+ # Create multiple lines for the split string
+ token_idx, split_tokens = split_info
+
+ # Find the indentation level by looking at the position of the string token
+ indent_count = 0
+ for i in range(token_idx):
+ if line.tokens[i].type == InstructionTextTokenType.TextToken:
+ indent_count += len(line.tokens[i].text)
+ else:
+ indent_count += len(line.tokens[i].text)
+
+ # Create first line with original tokens up to and including first split token
+ first_line = DisassemblyTextLine()
+ first_line.address = line.address
+ first_line.il_instruction = line.il_instruction
+ first_line.highlight = line.highlight
+
+ first_line.tokens = line.tokens[:token_idx] + [split_tokens[0]]
+ new_lines.append(first_line)
+
+ # Create continuation lines for remaining split tokens
+ for j in range(1, len(split_tokens)):
+ cont_line = DisassemblyTextLine()
+ cont_line.address = line.address
+ cont_line.il_instruction = line.il_instruction
+ cont_line.highlight = line.highlight
+
+ # Add indentation to align with the start of the string
+ indent_token = InstructionTextToken(InstructionTextTokenType.TextToken, ' ' * indent_count)
+ cont_line.tokens = [indent_token, split_tokens[j]]
+
+ # If this is the last split token, add the rest of the original tokens
+ if j == len(split_tokens) - 1:
+ cont_line.tokens.extend(line.tokens[token_idx + 1:])
+
+ new_lines.append(cont_line)
+
+ return new_lines
+
+ def apply_to_high_level_il_body(
+ self,
+ function: 'binaryninja.Function',
+ lines: List['binaryninja.LinearDisassemblyLine']
+ ):
+ log_info(f"[NewlineSplitRenderLayer] apply_to_high_level_il_body called with {len(lines)} lines")
+ # Similar logic for HLIL linear view
+ new_lines = []
+
+ for line in lines:
+ # Look for string tokens
+ has_split = False
+ split_info = None
+
+ for i, token in enumerate(line.contents.tokens):
+ if token.type == InstructionTextTokenType.StringToken:
+ split_tokens = self.split_string_token(token)
+ if len(split_tokens) > 1:
+ has_split = True
+ split_info = (i, split_tokens)
+ break
+
+ if not has_split:
+ new_lines.append(line)
+ continue
+
+ # Create multiple lines for the split string
+ token_idx, split_tokens = split_info
+
+ # Find the indentation level
+ indent_count = 0
+ for i in range(token_idx):
+ indent_count += len(line.contents.tokens[i].text)
+
+ # Create first line - we need to create a new DisassemblyTextLine with modified tokens
+ first_tokens = line.contents.tokens[:token_idx] + [split_tokens[0]]
+ first_contents = DisassemblyTextLine(first_tokens, line.contents.address)
+ first_contents.highlight = line.contents.highlight
+ first_contents.il_instruction = line.contents.il_instruction
+
+ first_line = binaryninja.LinearDisassemblyLine(
+ line.type,
+ line.function,
+ line.block,
+ first_contents
+ )
+ new_lines.append(first_line)
+
+ # Create continuation lines
+ for j in range(1, len(split_tokens)):
+ # Add indentation
+ indent_token = InstructionTextToken(InstructionTextTokenType.TextToken, ' ' * indent_count)
+ cont_tokens = [indent_token, split_tokens[j]]
+
+ # If this is the last split token, add the rest of the original tokens
+ if j == len(split_tokens) - 1:
+ cont_tokens.extend(line.contents.tokens[token_idx + 1:])
+
+ cont_contents = DisassemblyTextLine(cont_tokens, line.contents.address)
+ cont_contents.highlight = line.contents.highlight
+ cont_contents.il_instruction = line.contents.il_instruction
+
+ cont_line = binaryninja.LinearDisassemblyLine(
+ line.type,
+ line.function,
+ line.block,
+ cont_contents
+ )
+
+ new_lines.append(cont_line)
+
+ return new_lines
+
+ def apply_to_flow_graph(self, graph: 'binaryninja.FlowGraph'):
+ # Don't modify flow graphs
+ pass
+
+
+NewlineSplitRenderLayer.register()