Skip to content

Instantly share code, notes, and snippets.

@utkonos
Last active July 20, 2025 15:27
Show Gist options
  • Save utkonos/0aa6e1d491b703489359ca40f1c1cb4d to your computer and use it in GitHub Desktop.
Save utkonos/0aa6e1d491b703489359ca40f1c1cb4d to your computer and use it in GitHub Desktop.
Binary Ninja plugin for copying opcode bytes to the clipboard formatted to YARA best practice
"""Binary Ninja plugin for copying opcode bytes to the clipboard formatted to YARA best practice."""
import json
import re
from binaryninja.enums import DisassemblyOption, HighlightStandardColor, InstructionTextTokenType, LinearDisassemblyLineType
from binaryninja.function import DisassemblySettings
from binaryninja.interaction import get_text_line_input
from binaryninja.log import Logger
from binaryninja.plugin import PluginCommand
from binaryninja.settings import Settings
import PySide6
s = Settings()
s.register_group('c', 'Copy Bytes')
setting = {
'description': 'YARA format bytes copied as uppercase.',
'title': 'Uppercase Bytes',
'default': False,
'type': 'boolean'
}
s.register_setting('c.upper', json.dumps(setting))
log = Logger(0, 'CopyYARA')
def get_texts(bv, addr, end):
"""Get the opcode and disassembly texts in an address range."""
settings = DisassemblySettings.default_linear_settings()
settings.set_option(DisassemblyOption.ExpandLongOpcode)
pos = bv.get_linear_disassembly_position_at(addr, settings)
opcode_bytes = list()
disasm_text = list()
while True:
lines = bv.get_next_linear_disassembly_lines(pos)
exit = False
for line in lines:
if line.contents.address < addr:
continue
if line.contents.address >= end:
exit = True
break
if line.type != LinearDisassemblyLineType.CodeDisassemblyLineType:
continue
if InstructionTextTokenType.OpcodeToken not in {t.type for t in line.contents.tokens}:
continue
for token in line.contents.tokens:
if token.type is InstructionTextTokenType.OpcodeToken:
if s.get_bool('c.upper'):
out = token.text.upper()
else:
out = token.text
opcode_bytes.append(out)
blacklist = [InstructionTextTokenType.TagToken]
line_text = ''.join([t.text for t in line.contents.tokens if t.type not in blacklist])
no_auto_vars = re.sub(' {var_.+?}', '', line_text)
disasm_text.append(no_auto_vars)
if exit:
break
return opcode_bytes, disasm_text
def format_output(bv, addr, length, yara_string=False, named=False):
"""Copy properly formatted bytes to the clipboard."""
end = addr + length
opcode_bytes, disasm_text = get_texts(bv, addr, end)
# Copy decoded string to clipboard.
clip = PySide6.QtGui.QGuiApplication.clipboard()
opcode_output = ' '.join(opcode_bytes)
if named:
var_name = get_text_line_input('Variable Name', 'Name YARA Variable').decode()
else:
var_name = 'op'
if yara_string:
template = ' ${} = {{ {} }}\n // {}'
indent = '\n' + ' '*12 + '// '
disasm_output = indent.join(disasm_text)
output = template.format(var_name, opcode_output, disasm_output)
else:
output = opcode_output
clip.setText(output)
def format_opcode(bv, addr, length):
"""Output a YARA formatted opcode string."""
format_output(bv, addr, length)
def format_yara_string(bv, addr, length):
"""Output a formatted YARA string."""
format_output(bv, addr, length, yara_string=True)
def format_named_yara_string(bv, addr, length):
"""Output a formatted YARA string."""
format_output(bv, addr, length, yara_string=True, named=True)
def set_yara_target(bv, start, length):
"""Set tag and highlighting on a YARA rule target."""
tag_name = 'YARA Target'
if tag_name not in bv.tag_types:
bv.create_tag_type(tag_name, b'\xf0\x9f\x8e\xaf')
log.log_info(f'Tag created: {tag_name}')
f = next(iter(bv.get_functions_containing(start)))
clip = PySide6.QtGui.QGuiApplication.clipboard()
rule_name = clip.text()
f.add_tag(tag_name, rule_name, start)
cur = bv.get_linear_disassembly_position_at(start)
addr = 0
end = start + length
while addr <= end:
for line in cur.lines:
addr = line.contents.address
if addr < start or addr >= end:
continue
f.set_user_instr_highlight(addr, HighlightStandardColor.RedHighlightColor)
cur.next()
description = 'Copy YARA format opcodes to clipboard for selected instructions.'
PluginCommand.register_for_range('Copy YARA\\\U000E0102Opcodes', description, format_opcode)
description = 'Copy fully formatted YARA string to clipboard for selected instructions.'
PluginCommand.register_for_range('Copy YARA\\\U000E0100String', description, format_yara_string)
description = 'Copy fully formatted named YARA string to clipboard for selected instructions.'
PluginCommand.register_for_range('Copy YARA\\\U000E0101Named String', description, format_named_yara_string)
description = 'Paste the YARA rule name as a YARA target tag and highlight the selection.'
PluginCommand.register_for_range('Copy YARA\\\U000E0103Set Target', description, set_yara_target)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment