Skip to content

Commit

Permalink
Fixed unicode bug in assemble.py
Browse files Browse the repository at this point in the history
Also fixed bug in call_table_enum.py by removing the long type carried
over from converting the code to python 2. Moreover, I've ran a linter
against the code and addressed recommendations.
  • Loading branch information
Brandon Miller committed Nov 28, 2020
1 parent f53d5b9 commit f198143
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 94 deletions.
14 changes: 9 additions & 5 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
from binaryninja import *
from genesis import *
from binaryninja import PluginCommand
from .genesis import GenesisChecksum, GenesisAssemble, GenesisCallTableEnum


def checksum(view):
checksum = GenesisChecksum(view)
checksum.start()


def assemble(view):
assemble = GenesisAssemble(view)
assemble.start()


def call_table_enum(view):
cte = GenesisCallTableEnum(view)
cte.start()


PluginCommand.register(
'genesis: Fixup ROM checksum',
'genesis: fixup ROM checksum',
'Fixup the SEGA Genesis ROM checksum',
checksum
)

PluginCommand.register(
'genesis: Assemble and patch',
'genesis: assemble and patch',
'Assemble M68K code and apply blob as patch',
assemble
)
PluginCommand.register(
'genesis: Enumerate call tables',
'genesis: enumerate call tables',
'Locate and disassemble call tables',
call_table_enum
)
46 changes: 20 additions & 26 deletions genesis/assemble.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
'''Assembles Motorola 68000 code and drops the blob at the specified offset
in the ROM
'''
"""Assembles M68K instructions and writes the opcode to the specified address
"""

from binaryninja import *
from binaryninja import (BackgroundTaskThread, AddressField,
MultilineTextField, get_form_input, show_message_box)
import tempfile
import shutil
import os
import subprocess

__author__ = 'zznop'
__copyright__ = 'Copyright 2019, zznop'
__license__ = 'GPL'
__version__ = '1.1'
__email__ = '[email protected]'

class GenesisAssemble(BackgroundTaskThread):
def __init__(self, bv):
Expand All @@ -23,10 +18,10 @@ def __init__(self, bv):
self.progress = 'genesis: Assembling code...'

def _get_params(self):
'''Launch an input box to get start offset for patch and code
'''
params = {}
start_offset_field = AddressField('Start offset for patch (current offset: 0x{:08x})'.format(self.bv.offset),
start_offset_field = AddressField(
'Start offset for patch (current offset: 0x{:08x})'.format(
self.bv.offset),
view=self.bv, current_address=self.bv.offset)
code_field = MultilineTextField('Code')
get_form_input([start_offset_field, code_field], 'Patch Parameters')
Expand All @@ -35,10 +30,9 @@ def _get_params(self):
return params

def _assemble_code(self, dirpath):
'''Assemble patch.S
'''
p = subprocess.Popen([self.as_path,'-m68000', '-c', '-a={}/patch.lst'.format(dirpath),
'{}/patch.S'.format(dirpath), '-o', '{}/patch.o'.format(dirpath)],
p = subprocess.Popen(
[self.as_path, '-m68000', '-c', '-a={}/patch.lst'.format(dirpath),
'{}/patch.S'.format(dirpath), '-o', '{}/patch.o'.format(dirpath)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)

(out, err) = p.communicate()
Expand All @@ -49,10 +43,9 @@ def _assemble_code(self, dirpath):
return True

def _link_code(self, dirpath):
'''Link patch.o object
'''
p = subprocess.Popen([self.ld_path, '-Ttext', '0', '--oformat', 'binary', '-o',
'{}/patch.bin'.format(dirpath), '{}/patch.o'.format(dirpath)],
p = subprocess.Popen(
[self.ld_path, '-Ttext', '0', '--oformat', 'binary', '-o',
'{}/patch.bin'.format(dirpath), '{}/patch.o'.format(dirpath)],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)

(out, err) = p.communicate()
Expand All @@ -63,8 +56,6 @@ def _link_code(self, dirpath):
return True

def _assemble_link_extract(self, code):
'''Write code to tempdir/patch.S and assemble it
'''
blob = None
try:
template = '.section .text\n' \
Expand All @@ -75,7 +66,7 @@ def _assemble_link_extract(self, code):
dirpath = tempfile.mkdtemp()
print(dirpath)
with open(dirpath + '/patch.S', 'w+b') as f:
f.write(template)
f.write(template.encode('utf-8'))

if not self._assemble_code(dirpath):
raise OSError('Failed to assemble code')
Expand All @@ -91,8 +82,6 @@ def _assemble_link_extract(self, code):
return blob

def run(self):
'''Assemble code and patch offset
'''
params = self._get_params()
blob = self._assemble_link_extract(params['code'])
if blob is None:
Expand All @@ -101,9 +90,14 @@ def run(self):
blob_len = len(blob)
if blob_len > 0:
self.bv.write(params['start_offset'], blob)
show_message_box('genesis', 'Wrote {} bytes beginning at {:08x}'.format(blob_len, params['start_offset']))
show_message_box(
'genesis',
'Wrote {} bytes beginning at {:08x}'.format(
blob_len, params['start_offset'])
)
else:
show_message_box('genesis', 'Patch is 0 bytes in size')


if __name__ == '__main__':
print('! this plugin does not run headless')
47 changes: 24 additions & 23 deletions genesis/call_table_enum.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,43 @@
'''Locates and disassembles common call tables present in certain ROM's
'''
"""Locates and disassembles common call tables present in certain ROM's
"""

from binaryninja import *
import struct
from binaryninja import (BackgroundTaskThread, BinaryReader,
MediumLevelILOperation, Platform, show_message_box)

__author__ = 'zznop'
__copyright__ = 'Copyright 2019, zznop'
__license__ = 'GPL'
__version__ = '1.1'
__email__ = '[email protected]'

class GenesisCallTableEnum(BackgroundTaskThread):
def __init__(self, bv):
BackgroundTaskThread.__init__(self, "", True)
self.progress = 'gensis: Enumerating call tables...'
self.progress = 'genesis: Enumerating call tables...'
self.bv = bv
self.br = BinaryReader(self.bv)

def find_call_tables(self):
'''Find call table base addresses using MLIL
'''
base_addrs = []
for func in self.bv:
if not func.medium_level_il.ssa_form:
continue

for block in func.medium_level_il.ssa_form:
for instr in block:
branch_operations = [
MediumLevelILOperation.MLIL_CALL_UNTYPED_SSA,
MediumLevelILOperation.MLIL_JUMP,
MediumLevelILOperation.MLIL_GOTO
]
if instr.operation in branch_operations:
if type(instr.dest) == long:
continue

if instr.dest.operation == MediumLevelILOperation.MLIL_ADD:
if instr.dest.operands[0].operation == MediumLevelILOperation.MLIL_CONST:
base_addrs.append(instr.dest.operands[0].constant)
if instr.operation not in branch_operations:
continue

if type(instr.dest) == int:
continue

if instr.dest.operation == MediumLevelILOperation.MLIL_ADD:
if instr.dest.operands[0].operation == MediumLevelILOperation.MLIL_CONST:
base_addrs.append(instr.dest.operands[0].constant)
return base_addrs

def disas_call_tables(self, base_addrs):
'''Disassemble the instructions in the call table
'''
count = 0
for addr in base_addrs:
i = addr
Expand All @@ -58,12 +55,16 @@ def disas_call_tables(self, base_addrs):
return count

def run(self):
'''Locate and disassemble call tables
'''
self.bv.platform = Platform['M68000']
call_table_addrs = self.find_call_tables()
if not call_table_addrs:
return
count = self.disas_call_tables(call_table_addrs)
show_message_box('genesis', 'Disassembled {} call table instructions'.format(count))
show_message_box(
'genesis',
'Disassembled {} call table instructions'.format(count)
)


if __name__ == '__main__':
pass
16 changes: 4 additions & 12 deletions genesis/checksum.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
'''Calculates the new ROM checksum and writes it back to the binary
'''
"""Calculates the new ROM checksum and writes it back to the binary
"""

from binaryninja import *
from binaryninja import BackgroundTaskThread, BinaryReader, show_message_box
import struct

__author__ = 'zznop'
__copyright__ = 'Copyright 2019, zznop'
__license__ = 'GPL'
__version__ = '1.1'
__email__ = '[email protected]'

class GenesisChecksum(BackgroundTaskThread):
def __init__(self, bv):
Expand All @@ -20,8 +15,6 @@ def __init__(self, bv):
self.br = BinaryReader(self.bv)

def _calculate_checksum(self):
'''Calculate the ROM checksum
'''
self.br.seek(self.rom_start)
checksum = self.br.read16be()
while True:
Expand All @@ -32,11 +25,10 @@ def _calculate_checksum(self):
return checksum

def run(self):
'''Calculate new checksum and overwrite the existing
'''
checksum = self._calculate_checksum()
self.bv.write(self.checksum_off, struct.pack('>H', checksum))
show_message_box('genesis', 'ROM checksum has been updated')


if __name__ == '__main__':
print('! this plugin does not run headless')
63 changes: 37 additions & 26 deletions genesis/loader.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
from binaryninja import *
from binaryninja import (binaryview, Architecture, core, SegmentFlag,
SectionSemantics, Symbol, SymbolType, log)
import struct
import traceback

__author__ = 'zznop'
__copyright__ = 'Copyright 2019, zznop'
__license__ = 'GPL'
__version__ = '1.1'
__email__ = '[email protected]'

class GenesisView(binaryview.BinaryView):
name = 'SG/SMD'
long_name = 'SEGA Genesis/Megadrive ROM'

def __init__(self, data):
binaryview.BinaryView.__init__(self, parent_view=data, file_metadata=data.file)
binaryview.BinaryView.__init__(self, parent_view=data,
file_metadata=data.file)
self.platform = Architecture['M68000'].standalone_platform
self.raw = data

Expand All @@ -39,41 +36,49 @@ def get_load_settings_for_data(self, data):
return core.BNAllocString(load_settings_id)

def create_segments(self):
self.add_auto_segment(0, len(self.raw), 0,
len(self.raw), SegmentFlag.SegmentReadable|SegmentFlag.SegmentExecutable)
self.add_auto_segment(
0, len(self.raw), 0, len(self.raw),
SegmentFlag.SegmentReadable | SegmentFlag.SegmentExecutable
)

# RAM Segment
self.add_auto_segment(0xff0000, 0xffff, 0,
0, SegmentFlag.SegmentReadable|SegmentFlag.SegmentWritable)
self.add_auto_segment(
0xff0000, 0xffff, 0, 0,
SegmentFlag.SegmentReadable | SegmentFlag.SegmentWritable
)

# Z80 Segment
self.add_auto_segment(0xa00000, 0x1ffff, 0,
0, SegmentFlag.SegmentReadable|SegmentFlag.SegmentWritable)
self.add_auto_segment(
0xa00000, 0x1ffff, 0, 0,
SegmentFlag.SegmentReadable | SegmentFlag.SegmentWritable
)

# VDP Segment
self.add_auto_segment(0xc00000, 0x20, 0,
0, SegmentFlag.SegmentReadable|SegmentFlag.SegmentWritable)
self.add_auto_segment(
0xc00000, 0x20, 0, 0,
SegmentFlag.SegmentReadable | SegmentFlag.SegmentWritable
)

def create_sections(self):
self.add_auto_section(
self.add_auto_section(
"header", 0, 8,
SectionSemantics.ReadOnlyDataSectionSemantics)
self.add_auto_section(
self.add_auto_section(
"ivt", 8, 248,
SectionSemantics.ReadOnlyDataSectionSemantics)
self.add_auto_section(
self.add_auto_section(
"info", 256, 256,
SectionSemantics.ReadOnlyDataSectionSemantics)
self.add_auto_section(
self.add_auto_section(
"code", 512, len(self.raw)-512,
SectionSemantics.ReadOnlyCodeSectionSemantics)
self.add_auto_section(
self.add_auto_section(
"ram", 0xff0000, 0xffff,
SectionSemantics.ReadWriteDataSectionSemantics)
self.add_auto_section(
self.add_auto_section(
"z80", 0xa00000, 0x1ffff,
SectionSemantics.ReadWriteDataSectionSemantics)
self.add_auto_section(
self.add_auto_section(
"vdp", 0xc00000, 0x20,
SectionSemantics.ReadWriteDataSectionSemantics)

Expand All @@ -83,14 +88,20 @@ def create_functions(self):
self.add_function(addr)
if idx == 4:
self.add_entry_point(addr)
self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, addr, "_start"))
self.define_auto_symbol(
Symbol(SymbolType.FunctionSymbol, addr, "_start")
)
elif idx == 112:
self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, addr, "hblank"))
self.define_auto_symbol(
Symbol(SymbolType.FunctionSymbol, addr, "hblank")
)
elif idx == 120:
self.define_auto_symbol(Symbol(SymbolType.FunctionSymbol, addr, "vblank"))
self.define_auto_symbol(
Symbol(SymbolType.FunctionSymbol, addr, "vblank")
)

def create_datatype_and_name(self, addr, name, _type):
self.define_user_data_var(addr, _type)
self.define_user_data_var(addr, _type)
self.define_auto_symbol(Symbol(SymbolType.DataSymbol, addr, name))

def create_vector_table(self):
Expand Down
Loading

0 comments on commit f198143

Please sign in to comment.