Last active
June 26, 2023 20:04
-
-
Save iMoD1998/bdfce617a6023412226de4f81937f541 to your computer and use it in GitHub Desktop.
FixGPLR - Xbox 360 GPLR remover for IDA decompiler
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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