Recently picked up Binary Ninja and been playing around with it. Here are a few scripts/plugins I've made that others may find useful.
You can find all of them in plugin form here: https://github.com/SlidyBat/binja-plugins
For makesig/rtti you can also add them as snippets rather than plugins since they are single-file.
makesig
Spoiler
Pretty much a port of asherkin's makesig.idc for IDA. May generate slightly different signatures; this plugin only wildcards addresses that may be relocated rather than all addresses.
Also allows searching for AM style signatures since there's no equivalent functionality in Binja.
Code:
from binaryninja import *
def decode_sig(sig):
return sig.decode('unicode_escape')
def encode_sig(sig):
t = ''
for b in sig:
t += '\\x%02X' % ord(b)
return t
def get_function_end(func):
end = func.start
for bb in func:
if bb.end > end:
end = bb.end
return end
def find_sig(bv, sig, max_results=1):
br = BinaryReader(bv)
results = []
# Store non-wildcarded start of signature
sig_start = ''
for c in sig:
if c == '*': break
sig_start += c
curr_search = bv.start
while True:
curr_search = bv.find_next_data(curr_search + 1, sig_start, FindFlag.FindCaseSensitive)
if curr_search == None:
break
br.offset = curr_search
for i in range(len(sig)):
byte = br.read8()
if byte == None:
break
if sig[i] != '*' and ord(sig[i]) != byte:
break
else:
results += [curr_search]
if len(results) >= max_results:
return results
if byte == None:
break
return results
def is_good_sig(bv, sig):
if len(sig) < 5:
return False
addrs = find_sig(bv, sig, max_results=2)
return len(addrs) == 1
def find_sig_from_input(bv):
sig = interaction.get_text_line_input('Signature:', 'Enter signature to search for...')
addrs = find_sig(bv, decode_sig(sig.strip()), max_results=100)
if len(addrs) == 0:
print('No matches found for signature')
else:
print('Found match at 0x%x' % addrs[0])
def sig_for_function(bv, func):
func_start = func.start
func_end = get_function_end(func)
sig = ''
br = BinaryReader(bv)
br.seek(func_start)
while br.offset < func_end:
curr_instr = br.offset
instr_len = bv.get_instruction_length(curr_instr)
for i in range(instr_len):
relocations = bv.relocation_ranges_at(br.offset)
# Wildcard bytes that will be relocated
if len(relocations) == 0:
sig += chr(br.read8())
else:
sig += '*'
if is_good_sig(bv, sig):
print(encode_sig(sig))
return
br.offset = curr_instr + instr_len
print('Function too short to generate unique signature')
PluginCommand.register("Find signature", "Searches the current binary for the given signature", find_sig_from_input)
PluginCommand.register_for_function("Make signature for current function", "Generates a unique signature for the current selected function", sig_for_function)
from binaryninja import *
def read_ptr(bv, br):
if bv.address_size == 4: return br.read32()
if bv.address_size == 8: return br.read64()
raise NotImplementedError()
# From https://github.com/0x1F9F1/binja-msvc/blob/master/rtti.py
def demangle_vtable_name(bv, name):
try:
if name[:4] in [ '.?AU', '.?AV' ]:
# demangle_ms doesn't support flags (UNDNAME_32_BIT_DECODE | UNDNAME_NAME_ONLY | UNDNAME_NO_ARGUMENTS | UNDNAME_NO_MS_KEYWORDS)
demangle_type, demangle_name = demangle.demangle_ms(bv.arch, '??_7%s6B@' % name[4:])
if demangle_type is not None:
return demangle.get_qualified_name(demangle_name)
except:
pass
return 'vtable_{0}'.format(name)
def get_class_name(bv, name):
try:
if name[:4] in [ '.?AU', '.?AV' ]:
# demangle_ms doesn't support flags (UNDNAME_32_BIT_DECODE | UNDNAME_NAME_ONLY | UNDNAME_NO_ARGUMENTS | UNDNAME_NO_MS_KEYWORDS)
demangle_type, demangle_name = demangle.demangle_ms(bv.arch, '??_7%s6B@' % name[4:])
if demangle_type is not None:
return demangle_name[0]
except:
pass
return name
def is_vtable_func(bv, addr):
segment = bv.get_segment_at(addr)
return segment and segment.executable
def is_complete_object_locator(bv, addr):
rdata = bv.sections['.rdata']
if rdata.start < addr and addr < rdata.end:
br = BinaryReader(bv)
br.seek(addr)
signature = br.read32()
return signature == 0
return False
def vtable_t(bv, count):
return Type.array(Type.pointer(bv.arch, Type.void()), count)
def type_descriptor_t(bv, str_size):
type_descriptor_struct = Structure()
type_descriptor_struct.append(Type.pointer(bv.arch, Type.void()), 'vtable')
type_descriptor_struct.append(Type.pointer(bv.arch, Type.void()), 'runtime_ref')
type_descriptor_struct.append(Type.array(Type.int(1), str_size), 'name')
return Type.structure_type(type_descriptor_struct)
def base_class_desc_t(bv):
base_class_desc_struct = Structure()
base_class_desc_struct.append(Type.pointer(bv.arch, Type.void()), 'pTypeDescriptor')
base_class_desc_struct.append(Type.int(4, False), 'numContainedBases')
base_class_desc_struct.append(Type.int(4), 'mdisp')
base_class_desc_struct.append(Type.int(4), 'pdisp')
base_class_desc_struct.append(Type.int(4), 'vdisp')
base_class_desc_struct.append(Type.int(4, False), 'attributes')
return Type.structure_type(base_class_desc_struct)
def class_hierarchy_desc_t(bv):
class_hierarchy_desc_struct = Structure()
class_hierarchy_desc_struct.append(Type.int(4, False), 'signature')
class_hierarchy_desc_struct.append(Type.int(4, False), 'attributes')
class_hierarchy_desc_struct.append(Type.int(4, False), 'numBaseClasses')
class_hierarchy_desc_struct.append(Type.pointer(bv.arch, base_class_desc_t(bv)), 'pBaseClassArray')
return Type.structure_type(class_hierarchy_desc_struct)
def complete_object_locator_t(bv):
complete_object_locator_struct = Structure()
complete_object_locator_struct.append(Type.int(4, False), 'signature')
complete_object_locator_struct.append(Type.int(4, False), 'offset')
complete_object_locator_struct.append(Type.int(4, False), 'cdOffset')
complete_object_locator_struct.append(Type.pointer(bv.arch, Type.void()), 'pTypeDescriptor')
complete_object_locator_struct.append(Type.pointer(bv.arch, class_hierarchy_desc_t(bv)), 'pClassDescriptor')
return Type.structure_type(complete_object_locator_struct)
def find_rtti(bv):
rdata = bv.sections['.rdata']
br = BinaryReader(bv)
br.seek(rdata.start)
vtable_count = 0
while br.offset < rdata.end:
p_col = br.offset
col = read_ptr(bv, br)
if col == None:
break
p_vtable = br.offset
vtable_func = read_ptr(bv, br)
if vtable_func == None:
break
if is_complete_object_locator(bv, col) and is_vtable_func(bv, vtable_func):
vtable_count += 1
# Define complete object locator
bv.define_data_var(p_col, Type.pointer(bv.arch, complete_object_locator_t(bv)))
bv.define_data_var(col, complete_object_locator_t(bv))
# Define type descriptor
br.seek(col + 0xC)
typedesc = read_ptr(bv, br)
mangled_name = bv.get_string_at(typedesc + 8)
vtable_name = demangle_vtable_name(bv, mangled_name.value)
class_name = get_class_name(bv, mangled_name.value)
bv.define_data_var(typedesc, type_descriptor_t(bv, mangled_name.length))
bv.define_user_symbol(Symbol(SymbolType.DataSymbol, typedesc, '%s::`RTTI Type Descriptor\'' % class_name))
print('[%i] vtable at : 0x%0x (%s)' % (vtable_count, p_vtable, vtable_name))
# Now that we have the class name, rename complete object locator to something more suitable
bv.define_user_symbol(Symbol(SymbolType.DataSymbol, p_col, 'locator_%s' % class_name))
bv.define_user_symbol(Symbol(SymbolType.DataSymbol, col, '%s::`RTTI Complete Object Locator\'' % class_name))
# Define class hierarchy desc
br.seek(col + 0x10)
classhier = read_ptr(bv, br)
bv.define_data_var(classhier, class_hierarchy_desc_t(bv))
bv.define_user_symbol(Symbol(SymbolType.DataSymbol, classhier, '%s::`RTTI Class Hierarchy Descriptor\'' % class_name))
# Define base class array
br.seek(classhier + 8)
num_baseclasses = br.read32()
baseclasses = read_ptr(bv, br)
bv.define_data_var(baseclasses, Type.array(Type.pointer(bv.arch, base_class_desc_t(bv)), num_baseclasses))
bv.define_user_symbol(Symbol(SymbolType.DataSymbol, baseclasses, '%s::`RTTI Base Class Array\'' % class_name))
# Count number of functions in vtable and mark as array
br.seek(p_vtable)
vfunc_count = 0
while True:
vtable_func = read_ptr(bv, br)
if not is_vtable_func(bv, vtable_func):
break
vfunc_count += 1
bv.add_function(vtable_func)
bv.define_data_var(p_vtable, vtable_t(bv, vfunc_count))
bv.define_user_symbol(Symbol(SymbolType.DataSymbol, p_vtable, vtable_name))
# The last vtable func check failed, back up in case this is a locator
br.offset -= 4
print('Found %i vtables' % vtable_count)\
class ScanRtti(BackgroundTaskThread):
def __init__(self, msg, bv):
BackgroundTaskThread.__init__(self, msg, True)
self.bv = bv
def run(self):
find_rtti(self.bv)
def command_scan_rtti(bv):
task = ScanRtti('Scanning RTTI', bv)
task.start()
PluginCommand.register('Scan RTTI', 'Scans for MSVC RTTI', command_scan_rtti)
SMX
Spoiler
This is more of a fun project than a practical one. It adds support for analysing .smx files, including defining the internal structures, decompressing/unpacking code/data sections, disassembling, and lifting to LLIL.