Created
May 27, 2021 01:51
-
-
Save kiwidoggie/7c5287dbf50cf0e644de5850c18f75af to your computer and use it in GitHub Desktop.
fix_rtoc for ida 7.5
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
// | |
// This is a template for easy creation of IDA Plugins using | |
// MS Visual Studio 2005 or later (ie VS2008 or VS2010) | |
// | |
// This helps by setting all required settings, paths and preprocessor values | |
// required to create both 32bit and 64bit plugins from the same sourcecode. | |
// This will save work and time even for those with experience writing plugins, | |
// but it is of most use for those who are new to it and don't know where to start. | |
// | |
// All that you as a plugin writer needs to do now is fill in your own values | |
// for the strings and functions in the "plugin_t PLUGIN" structure below. | |
// | |
// Note: When building these samples, your plugin file will be created in the | |
// "idasdk/bin/plugins" directory. To install the plugin copy the created files | |
// to the "IDA/plugins" directory. | |
// | |
// Zak Stanborough - October 2009 | |
// | |
// A bunch of required headers from the IDA SDK | |
#include <ida.hpp> | |
#include <idp.hpp> | |
#include <name.hpp> | |
#include <bytes.hpp> | |
#include <loader.hpp> | |
#include <kernwin.hpp> | |
#include <allins.hpp> | |
#include <pro.h> | |
#include <ua.hpp> | |
// Determines whether this plugin will work with the current database. | |
// | |
// IDA will call this function only once. If this function returns PLUGIN_SKIP, | |
// IDA will never load it again. If it returns PLUGIN_OK, IDA will unload the plugin | |
// but remember that the plugin agreed to work with the database. The plugin will | |
// be loaded again if the user invokes it by pressing the hotkey or selecting it | |
// from the menu. After the second load, the plugin will stay in memory. | |
// | |
// returns: PLUGIN_OK if plugin is supported, PLUGIN_SKIP if plugin isn't supported | |
plugmod_t* idaapi PluginStartup(void) | |
{ | |
// Insert tests here to determine if this plugin can be used | |
// with the database being loaded. A common test is to check the | |
// processor type for plugins that only support particular processors. | |
msg("ph.id: (%d) (%d).\n", ph.id, PLFM_PPC); | |
// This tests if the processor type is PPC | |
// If it is PPC then return OK to show that the plugin is supported | |
if ( ph.id == PLFM_PPC ) | |
return PLUGIN_OK; | |
// If the processor isn't PPC then this plugin should not be used | |
// with the database being loaded, so return SKIP. | |
return PLUGIN_SKIP; | |
} | |
// IDA will call this function when the user asks to exit. This function is *not* | |
// called in the case of emergency exits. | |
void idaapi PluginShutdown(void) | |
{ | |
// Any cleanup code that needs to be done on exit goes here | |
} | |
#define GPR_COUNT 32 | |
#define FPR_COUNT 32 | |
#define G_STR_SIZE 256 | |
char g_insn[G_STR_SIZE]; | |
char g_mnem[G_STR_SIZE]; | |
char g_opnd[UA_MAXOP][G_STR_SIZE]; | |
typedef qvector<bool> coverage; | |
coverage func_map; | |
void ProcessFunction(func_t *p_func, ea_t rtoc_ea, const unsigned long *in_gpr = NULL, const bool *in_act = NULL); | |
void ProcessFunction(ea_t start_ea, ea_t end_ea, ea_t rtoc_ea, const unsigned long *in_gpr = NULL, const bool *in_act = NULL); | |
// This is the main plugin function. This code gets executed everytime | |
// your plugin is executed. | |
// | |
// args: param: Input argument specified in the "plugins.cfg" file. | |
bool idaapi PluginMain(size_t arg) | |
{ | |
// func_t* p_func = get_func(get_screen_ea()); | |
// if(p_func) | |
// { | |
// // process the current function | |
// ProcessFunction(p_func, 0xA90BF0); | |
// return; | |
// } | |
segment_t *p_seg = get_segm_by_name(".opd"); | |
if(p_seg == NULL) | |
{ | |
info(".opd segment not found, so can't run PS3 %rtoc Fixer!\n"); | |
return false; | |
} | |
ea_t seg_start = p_seg->start_ea; | |
ea_t seg_end = p_seg->end_ea; | |
ea_t seg_ea = seg_end; | |
do | |
//for(ea_t seg_ea=seg_start; seg_ea<seg_end; seg_ea+=8) | |
{ | |
seg_ea -= 8; | |
ea_t func_ea = get_dword(seg_ea); | |
ea_t rtoc_ea = get_dword(seg_ea + 4); | |
func_t* p_func = get_func(func_ea); | |
if (!p_func || | |
p_func->start_ea != func_ea) | |
{ | |
if (p_func) | |
{ | |
del_func(p_func->start_ea); | |
} | |
add_func(func_ea, BADADDR); | |
p_func = get_func(func_ea); | |
} | |
if(p_func) | |
{ | |
// resize function map | |
func_map.resize((p_func->end_ea - p_func->start_ea) >> 2); | |
// clear processed status | |
memset(&func_map.front(), 0, sizeof(bool) * func_map.size()); | |
// inform user of processing progress | |
msg("[%08d/%08d] Processing Function - [0x%08X - 0x%08X]...", get_func_num(func_ea), get_func_qty(), p_func->start_ea, p_func->end_ea); | |
// process the current function | |
ProcessFunction(p_func, rtoc_ea); | |
// function processing complete | |
msg("completed.\n"); | |
} | |
} | |
while (seg_ea > seg_start); | |
return true; | |
} | |
void ProcessFunction(func_t *p_func, ea_t rtoc_ea, const unsigned long *in_gpr, const bool *in_act) | |
{ | |
ProcessFunction(p_func->start_ea, p_func->end_ea, rtoc_ea, in_gpr, in_act); | |
} | |
void ProcessFunction(ea_t start_ea, ea_t end_ea, ea_t rtoc_ea, const unsigned long *in_gpr, const bool *in_act) | |
{ | |
unsigned long g_gpr[GPR_COUNT*8]; | |
bool g_act[GPR_COUNT*8]; | |
if (in_gpr) | |
memcpy(g_gpr, in_gpr, sizeof(g_gpr)); | |
else | |
memset(g_gpr, 0, sizeof(g_gpr)); | |
if (in_act) | |
memcpy(g_act, in_act, sizeof(g_act)); | |
else | |
memset(g_act, 0, sizeof(g_act)); | |
// special case for code relocated after end of function | |
// may cause false positives | |
if (!g_act[2]) | |
{ | |
// initialize %rtoc register | |
g_gpr[2] = rtoc_ea; | |
g_act[2] = true; | |
} | |
func_t *p_func = get_func(start_ea); | |
// scan entire function | |
ea_t start = start_ea; | |
ea_t end = end_ea; | |
for(ea_t ea=start; ea<end; ea+=4) | |
{ | |
ea_t ea_loc = (ea - p_func->start_ea) >> 2; | |
insn_t l_cmd; | |
if(!decode_insn(&l_cmd, ea) || func_map[ea_loc]) | |
continue; | |
func_map[ea_loc] = true; | |
// get mnemonic | |
//ua_mnem(ea, g_mnem, sizeof(g_mnem)); | |
bool reg_used = false; | |
int iOp = 0; | |
for (; iOp < UA_MAXOP; ++iOp) | |
{ | |
g_opnd[iOp][0] = 0; | |
if (l_cmd.ops[iOp].type == o_void) | |
break; | |
optype_t type = l_cmd.ops[iOp].type; | |
uint16 reg = l_cmd.ops[iOp].reg; | |
switch (type) | |
{ | |
case o_reg: | |
case o_phrase: | |
case o_displ: | |
{ | |
reg_used = (reg < (GPR_COUNT + FPR_COUNT)) && (reg_used || ((reg == 2) || g_act[reg])); | |
} | |
break; | |
case o_far: | |
case o_near: | |
{ | |
switch (l_cmd.itype) | |
{ | |
case PPC_b: // Branch | |
case PPC_bc: // Branch Conditional | |
case PPC_bdnz: // CTR--; branch if CTR non-zero | |
case PPC_bdz: // CTR--; branch if CTR zero | |
case PPC_blt: // Branch if less than | |
case PPC_ble: // Branch if less than or equal | |
case PPC_beq: // Branch if equal | |
case PPC_bge: // Branch if greater than or equal | |
case PPC_bgt: // Branch if greater than | |
case PPC_bne: // Branch if not equal | |
{ | |
reg_used = true; | |
} | |
break; | |
default: | |
{ | |
reg_used = reg_used || false; | |
} | |
break; | |
} | |
} | |
break; | |
} | |
// get operand string | |
//ua_outop2(ea, &g_opnd[iOp][0], sizeof(g_opnd[iOp]), iOp); | |
//tag_remove(&g_opnd[iOp][0], &g_opnd[iOp][0], sizeof(g_opnd[iOp])); | |
//ua_outop2(ea, g_insn, sizeof(g_insn), iOp); | |
//tag_remove(g_insn, g_insn, 0); | |
} | |
if (iOp > 0 && reg_used) | |
{ | |
switch (l_cmd.itype) | |
{ | |
case PPC_mr: | |
{ | |
if (g_act[l_cmd.Op2.reg]) | |
{ | |
g_gpr[l_cmd.Op1.reg] = g_gpr[l_cmd.Op2.reg]; | |
g_act[l_cmd.Op1.reg] = true; | |
} | |
} | |
break; | |
case PPC_addi: | |
{ | |
if (g_act[l_cmd.Op2.reg]) | |
{ | |
g_gpr[l_cmd.Op1.reg] = g_gpr[l_cmd.Op2.reg] + l_cmd.Op3.value; | |
g_act[l_cmd.Op1.reg] = true; | |
} | |
} | |
break; | |
case PPC_addis: | |
{ | |
if (g_act[l_cmd.Op2.reg]) | |
{ | |
g_gpr[l_cmd.Op1.reg] = g_gpr[l_cmd.Op2.reg] + (l_cmd.Op3.value << 16); | |
g_act[l_cmd.Op1.reg] = true; | |
} | |
} | |
break; | |
case PPC_bc: // Branch Conditional | |
{ | |
ea_t addr = l_cmd.Op3.addr; | |
func_t *p_branch = get_func(addr); | |
if ((p_func == p_branch) && (ea < addr)) | |
{ | |
ea_t addr_loc = (addr - p_func->start_ea) >> 2; | |
if (!func_map[addr_loc]) | |
{ | |
ProcessFunction(addr, end, (ea_t)g_gpr[2], g_gpr, g_act); | |
} | |
} | |
} | |
break; | |
case PPC_bdnz: // CTR--; branch if CTR non-zero | |
case PPC_bdz: // CTR--; branch if CTR zero | |
case PPC_blt: // Branch if less than | |
case PPC_ble: // Branch if less than or equal | |
case PPC_beq: // Branch if equal | |
case PPC_bge: // Branch if greater than or equal | |
case PPC_bgt: // Branch if greater than | |
case PPC_bne: // Branch if not equal | |
{ | |
ea_t addr = l_cmd.Op2.addr; | |
func_t *p_branch = get_func(addr); | |
if ((p_func == p_branch) && (ea < addr)) | |
{ | |
ea_t addr_loc = (addr - p_func->start_ea) >> 2; | |
if (!func_map[addr_loc]) | |
{ | |
ProcessFunction(addr, end, (ea_t)g_gpr[2], g_gpr, g_act); | |
} | |
} | |
} | |
break; | |
case PPC_b: // Branch | |
{ | |
ea_t addr = l_cmd.Op1.addr; | |
func_t *p_branch = get_func(addr); | |
if ((p_func == p_branch) && (ea < addr)) | |
{ | |
ea_t addr_loc = (addr - p_func->start_ea) >> 2; | |
if (!func_map[addr_loc]) | |
{ | |
ProcessFunction(addr, end, (ea_t)g_gpr[2], g_gpr, g_act); | |
} | |
} | |
} | |
break; | |
case PPC_or: | |
{ | |
if (g_act[l_cmd.Op1.reg] = (g_act[l_cmd.Op2.reg] && g_act[l_cmd.Op3.reg])) | |
g_gpr[l_cmd.Op1.reg] = g_gpr[l_cmd.Op2.reg] | g_gpr[l_cmd.Op3.reg]; | |
} | |
break; | |
case PPC_ori: | |
{ | |
if (g_act[l_cmd.Op1.reg] = g_act[l_cmd.Op2.reg]) | |
g_gpr[l_cmd.Op1.reg] = g_gpr[l_cmd.Op2.reg] | l_cmd.Op3.value; | |
} | |
break; | |
case PPC_oris: | |
{ | |
if (g_act[l_cmd.Op1.reg] = g_act[l_cmd.Op2.reg]) | |
g_gpr[l_cmd.Op1.reg] = g_gpr[l_cmd.Op2.reg] | (l_cmd.Op3.value << 16); | |
} | |
break; | |
case PPC_ld: | |
{ | |
if (l_cmd.Op1.reg == 2 && l_cmd.Op2.reg == 1) | |
{ | |
// assuming restoring from saved stack address | |
g_act[l_cmd.Op1.reg] = true; | |
} | |
else //if (g_act[l_cmd.Op2.reg]) | |
{ | |
// safety net, better to miss detection than falsely detect | |
// special consideration taken for %r30 | |
if (l_cmd.Op1.reg != 30) | |
{ | |
g_gpr[l_cmd.Op1.reg] = 0; | |
g_act[l_cmd.Op1.reg] = false; | |
} | |
} | |
} | |
break; | |
case PPC_li: | |
{ | |
g_gpr[l_cmd.Op1.reg] = l_cmd.Op2.value; | |
g_act[l_cmd.Op1.reg] = true; | |
} | |
break; | |
case PPC_lis: | |
{ | |
g_gpr[l_cmd.Op1.reg] = l_cmd.Op2.value << 16; | |
g_act[l_cmd.Op1.reg] = true; | |
} | |
break; | |
case PPC_lfs: | |
case PPC_lwz: | |
case PPC_lhz: | |
case PPC_lbz: | |
{ | |
if (l_cmd.Op1.reg == 2 && l_cmd.Op2.reg == 1) | |
; | |
else if ((l_cmd.Op2.reg == 2) || (g_act[l_cmd.Op2.reg])) | |
{ | |
ea_t addr = g_gpr[l_cmd.Op2.reg] + l_cmd.Op2.addr; | |
if (g_act[l_cmd.Op1.reg] = is_mapped(addr)) | |
{ | |
flags_t flags = get_flags(addr); | |
add_dref(ea, addr, (dref_t)(/*XREF_USER|*/dr_R)); | |
if (g_act[l_cmd.Op1.reg] = is_loaded(addr)) | |
{ | |
uint32 value = 0; //get_dword(addr); | |
switch (l_cmd.itype) | |
{ | |
case PPC_lfs: | |
case PPC_lwz: | |
value = get_dword(addr); | |
break; | |
case PPC_lhz: | |
value = get_word(addr); | |
break; | |
case PPC_lbz: | |
default: | |
value = get_byte(addr); | |
break; | |
} | |
g_gpr[l_cmd.Op1.reg] = value; | |
} | |
} | |
} | |
else | |
{ | |
// safety net, better to miss detection than falsely detect | |
// special consideration taken for %r30 | |
if (l_cmd.Op1.reg != 30) | |
{ | |
g_gpr[l_cmd.Op1.reg] = 0; | |
g_act[l_cmd.Op1.reg] = false; | |
} | |
} | |
} | |
break; | |
default: | |
{ | |
if (l_cmd.Op1.type == o_reg && g_act[l_cmd.Op1.reg]) | |
{ | |
// if not storing the value... | |
if (!((l_cmd.itype >= PPC_b && l_cmd.itype <= PPC_cmpli) || | |
( l_cmd.itype >= PPC_cmpwi && l_cmd.itype <= PPC_cmpld) || | |
( l_cmd.itype >= PPC_stb && l_cmd.itype <= PPC_stwx))) | |
{ | |
// safety net, better to miss detection than falsely detect | |
// special consideration taken for %r30 | |
if (l_cmd.Op1.reg != 30) | |
{ | |
g_gpr[l_cmd.Op1.reg] = 0; | |
g_act[l_cmd.Op1.reg] = false; | |
} | |
} | |
} | |
} | |
break; | |
} | |
// special case for code relocated after end of function | |
// may cause false positives | |
if (!g_act[2]) | |
{ | |
// initialize %rtoc register | |
g_gpr[2] = rtoc_ea; | |
//g_act[2] = true; | |
} | |
} | |
} | |
} | |
// | |
// Strings required for IDA Pro's PLUGIN descriptor block | |
// | |
const char G_PLUGIN_COMMENT[] = "IDA PS3 %rtoc Fixer Plugin"; | |
const char G_PLUGIN_HELP[] = "This is a plugin to help fix references to addresses" | |
"made through the PS3 %rtoc register.\n"; | |
const char G_PLUGIN_NAME[] = "PS3 %rtoc Fixer"; | |
const char G_PLUGIN_HOTKEY[] = "Ctrl-F11"; | |
// | |
// This 'PLUGIN' data block is how IDA Pro interfaces with this plugin. | |
// | |
plugin_t PLUGIN = | |
{ | |
// values | |
IDP_INTERFACE_VERSION, | |
0, // plugin flags | |
// functions | |
PluginStartup, // initialize and test if plugin is supported | |
PluginShutdown, // terminate. this pointer may be NULL. | |
PluginMain, // invoke plugin | |
// strings | |
(char*)G_PLUGIN_COMMENT,// long comment about the plugin (may appear on status line or as a hint) | |
(char*)G_PLUGIN_HELP, // multiline help about the plugin | |
(char*)G_PLUGIN_NAME, // the preferred short name of the plugin, used by menu system | |
(char*)G_PLUGIN_HOTKEY // the preferred hotkey to run the plugin | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment