Skip to content

Instantly share code, notes, and snippets.

@iMoD1998
Last active June 26, 2023 20:04
Show Gist options
  • Save iMoD1998/bdfce617a6023412226de4f81937f541 to your computer and use it in GitHub Desktop.
Save iMoD1998/bdfce617a6023412226de4f81937f541 to your computer and use it in GitHub Desktop.
FixGPLR - Xbox 360 GPLR remover for IDA decompiler
import idautils
import ida_bytes
import ida_funcs
import ida_ida
import ida_kernwin
import ida_search
import ida_idp
import idaapi
import idc
import pprint
import json
def is_ppc_idb():
return ida_idp.ph.id == ida_idp.PLFM_PPC
def get_reg_number(address):
register_num = idc.get_operand_value( address, 0 )
if register_num >= 35:
return register_num - 35
return register_num
def gplr_chunk_name(address):
register_num = get_reg_number( address )
if idc.print_insn_mnem( address ) == "std":
return "__savegprlr_%i" % ( register_num )
elif idc.print_insn_mnem( address ) == "ld":
return "__restgprlr_%i" % ( register_num )
elif idc.print_insn_mnem( address ) == "stfd":
return "__savefpr_%i" % ( register_num )
elif idc.print_insn_mnem( address ) == "lfd":
return "__restfpr_%i" % ( register_num )
def nop_xrefs(address):
for xref in list( idautils.XrefsTo( address, 0 ) ):
if idc.print_insn_mnem( xref.frm ) == "bl":
idc.set_cmt(xref.frm, "bl " + gplr_chunk_name( address ), 0)
ida_bytes.patch_dword( xref.frm, 0x60000000 )
elif idc.print_insn_mnem( xref.frm ) == "b":
idc.set_cmt(xref.frm, "b " + gplr_chunk_name( address ), 0)
ida_bytes.patch_dword( xref.frm, 0x4E800020 )
def process_gplr_chunk(address, length, remove_xrefs):
ida_funcs.del_func( address )
ida_bytes.del_items( address, 0, length )
idc.create_insn( address )
ida_funcs.add_func( address, address + length )
idc.apply_type( address, idc.parse_decl( "void __fastcall __spoils<> _savegprlr()", idc.PT_SILENT ) )
idc.set_name( address, gplr_chunk_name( address ), idaapi.SN_CHECK )
if remove_xrefs:
nop_xrefs( address )
def process_gplr(address, removexrefs):
current_address = address
while True:
# if we hit blr which shouldnt happen
if ida_bytes.get_dword( current_address ) == 0x4E800020:
break
current_instruction = ida_bytes.get_dword( current_address )
if current_instruction == 0xFBE1FFF0: # if we hit the last part of the savegprlr stub
process_gplr_chunk( current_address, 12, removexrefs )
print( "end %s -> 0x%X" % ( gplr_chunk_name( current_address ), current_address ) )
break
elif current_instruction == 0xEBE1FFF0: # if we hit the last part of the resgplr stub
process_gplr_chunk( current_address, 16, removexrefs )
print( "end %s -> 0x%X" % ( gplr_chunk_name( current_address ), current_address ) )
break
elif current_instruction == 0xDBECFFF8: # if we hit the last part of the savefpr stub
process_gplr_chunk( current_address, 8, removexrefs )
print( "end %s -> 0x%X" % ( gplr_chunk_name( current_address ), current_address ) )
break
elif current_instruction == 0xCBECFFF8: # if we hit the last part of the restfpr stub
process_gplr_chunk( current_address, 8, removexrefs )
print( "end %s -> 0x%X" % ( gplr_chunk_name( current_address ), current_address ) )
break
else:
process_gplr_chunk( current_address, 4, removexrefs )
print( "%s -> 0x%X" % ( gplr_chunk_name( current_address ), current_address ) )
current_address = idc.next_head( current_address )
def fix_gplr_handler():
save_gplr = ida_search.find_binary( ida_ida.inf_get_min_ea(), ida_ida.inf_get_max_ea(), "F9 C1 FF 68 F9 E1 FF 70 FA 01 FF 78 FA 21 FF 80", 16, ida_search.SEARCH_DOWN )
save_fpr = ida_search.find_binary( ida_ida.inf_get_min_ea(), ida_ida.inf_get_max_ea(), "D9 CC FF 70 D9 EC FF 78 DA 0C FF 80 DA 2C FF 88", 16, ida_search.SEARCH_DOWN )
restore_gplr = ida_search.find_binary( ida_ida.inf_get_min_ea(), ida_ida.inf_get_max_ea(), "E9 C1 FF 68 E9 E1 FF 70 EA 01 FF 78 EA 21 FF 80", 16, ida_search.SEARCH_DOWN )
restore_fpr = ida_search.find_binary( ida_ida.inf_get_min_ea(), ida_ida.inf_get_max_ea(), "C9 CC FF 70 C9 EC FF 78 CA 0C FF 80 CA 2C FF 88", 16, ida_search.SEARCH_DOWN )
if save_gplr == idaapi.BADADDR:
print("Failed to find save_gplr")
return 1
if restore_gplr == idaapi.BADADDR:
print("Failed to find restore_gplr")
return 1
dialog_result = ida_kernwin.ask_yn( 0, "Would you like to NOP branches to save_gplr and restore_gplr?\nYes: Replace branches to save stubs with NOPs\nNo: Only set the name and prototype of GPLR stubs\nCancel: Do nothing" )
if dialog_result != ida_kernwin.ASKBTN_CANCEL:
process_gplr( save_gplr, dialog_result == ida_kernwin.ASKBTN_YES )
process_gplr( restore_gplr, dialog_result == ida_kernwin.ASKBTN_YES )
if save_fpr != idaapi.BADADDR:
print("Detected save_fpr")
process_gplr( save_fpr, dialog_result == ida_kernwin.ASKBTN_YES )
if restore_fpr != idaapi.BADADDR:
print("Detected restore_fpr")
process_gplr( restore_fpr, dialog_result == ida_kernwin.ASKBTN_YES )
if dialog_result == ida_kernwin.ASKBTN_YES:
ida_kernwin.msg( "NOP'd xrefs\nThese can be reverted by going to patched bytes and clicking revert\n" )
class ActionHandler(idaapi.action_handler_t):
def __init__(self, callback):
idaapi.action_handler_t.__init__(self)
self.callback = callback
def activate(self, ctx):
self.callback()
return 1
def update(self, ctx):
return idaapi.AST_ENABLE_FOR_IDB
class Xbox360HelperPlugin(idaapi.plugin_t):
flags = idaapi.PLUGIN_UNL
comment = "Xbox 360 Helper Plugin"
help = "This is help"
wanted_name = "Xbox 360 Helper Plugin"
wanted_hotkey = ""
def init(self):
if not is_ppc_idb():
return idaapi.PLUGIN_SKIP
idaapi.register_action( idaapi.action_desc_t( "xbox_360_helper_plugin_gplrfixer", # The action name. This acts like an ID and must be unique
"Fix GPLR", # The action text.
ActionHandler( fix_gplr_handler ), # The action handler.
"", # Optional: the action shortcut
"Fixes GPLR stubs from corrupting decompiler output", # Optional: the action tooltip (available in menus/toolbar)
0 ) ) # Optional: the action icon (shows when in menus/toolbars)
idaapi.attach_action_to_menu( "Edit/Xbox 360 Helper/Fix GPLR",
"xbox_360_helper_plugin_gplrfixer", idaapi.SETMENU_APP )
return idaapi.PLUGIN_OK
def run(self, arg):
pass
def term(self):
pass
def PLUGIN_ENTRY():
return Xbox360HelperPlugin()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment