Last active
October 3, 2024 14:51
-
-
Save jocopa3/7ac6842f9d625a0d326602e108013354 to your computer and use it in GitHub Desktop.
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 exports vtable entry symbols to a file. | |
Run this in the IDA Script Command window (File->Script Command...) | |
Each vtable segment in the output file is separated by an empty line. | |
*/ | |
#include <idc.idc> | |
static getPointerAddress(loc) | |
{ | |
auto ptrLoc, ptrSize; | |
ptrSize = ItemSize(loc); | |
auto i = 0, mul = 0x1; | |
while(i < ptrSize) | |
{ | |
ptrLoc = ptrLoc + Byte(loc+i)*mul; | |
mul = mul * 0x100; | |
i = i + 1; | |
} | |
return(ptrLoc-1); | |
} | |
static dumpVtables(fileName) | |
{ | |
SetStatus(IDA_STATUS_WORK); | |
Message("Saving vtable info to file...\n"); | |
// Open file with write permissions | |
auto file = fopen(fileName, "w"); | |
auto seg, loc, ptrLoc; | |
auto entries = 0, name; | |
// Get the segment storing vtables | |
seg = FirstSeg(); | |
while(SegName(seg) != ".data.rel.ro" && seg != BADADDR) | |
{ | |
seg = NextSeg(seg); | |
} | |
// Check if segment is valid address | |
if(seg == BADADDR) | |
{ | |
fclose(file); | |
Message("...Failed to dump vtable info: couldn't find the vtable segment\n"); | |
return; | |
} | |
// Loop over the segment and dump vtable info and entries | |
loc = SegStart(seg); | |
while(loc < SegEnd(seg)) | |
{ | |
// Get the name of the address | |
name = Name(loc); | |
// May need to change in the future | |
// This label marks the end of most of the game's vtables | |
if(name == "utf8proc_properties") | |
{ | |
break; | |
} | |
// Demangle the name to make it readable | |
name = Demangle(name, 0); | |
// Filter out typeinfo tables | |
if(substr(name, 1, 9) == "typeinfo") | |
{ | |
loc = loc + ItemSize(loc); | |
continue; | |
} | |
// Get just the name of the VTable | |
name = substr(name, 12, strlen(name)); | |
// Makeshift filter; don't care about std, boost, or pplx vtables | |
if(substr(name, 0, 5) == "std::" | |
|| substr(name, 0, 7) == "boost::" | |
|| substr(name, 0, 6) == "pplx::") | |
{ | |
loc = loc + ItemSize(loc); | |
continue; | |
} | |
if(strlen(name) != 0) | |
{ | |
// Save Vtable name | |
if(entries != 0) | |
{ | |
fprintf(file, "\n"); | |
} | |
fprintf(file, "%s\n", name); | |
entries = 0; | |
} else | |
{ | |
// Get the pointer address | |
ptrLoc = getPointerAddress(loc); | |
// Get the name of the function at the pointer location | |
name = GetFunctionName(ptrLoc); | |
if(strlen(name) != 0) | |
{ | |
// Save mangled function name | |
fprintf(file, "%s\n", name); | |
entries = entries + 1; | |
} | |
} | |
loc = loc + ItemSize(loc); | |
} | |
fclose(file); | |
Message("...Done!\n\n"); | |
} | |
static main() | |
{ | |
SetStatus(IDA_STATUS_WAITING); | |
auto promptResult = AskYN(1, "Would you like to save vtable info to a file?"); | |
if(promptResult == 1) | |
{ | |
auto file = AskFile(1, "*.txt", "Save Vtable Info to File"); | |
if(strlen(file) > 0) | |
{ | |
dumpVtables(file); | |
} | |
} | |
SetStatus(IDA_STATUS_READY); | |
} |
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 imports vtable entry symbols from a file. | |
This script requires you run ClassInformer first. | |
Run this in the IDA Script Command window (File->Script Command...) | |
WARNING: this script is very slow! I recommend you import only vtables | |
you need, as importing all vtables can take as long as an hour! I am | |
looking into ways to improve performance, but for now this script is slow. | |
Each new vtable segment in the input file must have a single empty line | |
between it and the previous vtable segment. | |
There are quite a few bugs currently. For example, some vtables like | |
the BrewingStandBlockEntity are split into two vtables of the same | |
name by class informer, which throws off this script. I am currently | |
trying to fix the issues I find. | |
*/ | |
#include <idc.idc> | |
static getPointerAddress(loc) | |
{ | |
auto ptrLoc, ptrSize; | |
ptrSize = ItemSize(loc); | |
auto i = 0, mul = 0x1; | |
while(i < ptrSize) | |
{ | |
ptrLoc = ptrLoc + Byte(loc+i)*mul; | |
mul = mul * 0x100; | |
i = i + 1; | |
} | |
return(ptrLoc); | |
} | |
// Names vtables using the input file | |
static nameTables(fileName) | |
{ | |
SetStatus(IDA_STATUS_WORK); | |
auto file = fopen(fileName, "r"); | |
auto seg, segLoc, loc, ptrLoc; | |
auto entries = 0, name, line; | |
// Get the segment storing vtables | |
seg = FirstSeg(); | |
while(SegName(seg) != ".data" && seg != BADADDR) | |
{ | |
seg = NextSeg(seg); | |
} | |
// Check if segment is valid address | |
if(seg == BADADDR) | |
{ | |
fclose(file); | |
Message("...Failed to import vtable info: couldn't find the data segment\n"); | |
return; | |
} | |
segLoc = SegStart(seg); | |
// Get the first vtable name | |
line = readstr(file); | |
line = substr(line, 0, strlen(line)-1); | |
// If the vtable struct already exists, delete it | |
auto structID = GetStrucIdByName(line); | |
if (structID != -1) | |
{ | |
Message("Deleted old vtable struct\n"); | |
DelStruc(structID); | |
} | |
// Create the struct to import vtable names into | |
structID = AddStrucEx(-1, line, 0); | |
// Search for the vtable address | |
loc = FindText(segLoc, SEARCH_DOWN, 0, 0, " "+line+":"); | |
Message("%s: 0x%x\n", line, loc); | |
// Skip the first entry in the vtable as it doesn't exist in the Windows version | |
line = readstr(file); | |
loc = loc + ItemSize(loc); | |
line = readstr(file); | |
auto ind = 0; | |
while(line != -1) | |
{ | |
line = substr(line, 0, strlen(line)-1); | |
if(line == "") | |
{ | |
line = readstr(file); | |
line = substr(line, 0, strlen(line)-1); | |
// If the vtable struct already exists, delete it | |
structID = GetStrucIdByName(line); | |
if (structID != -1) | |
{ | |
DelStruc(structID); | |
} | |
// Create the struct to import vtable names into | |
structID = AddStrucEx(-1, line, 0); | |
// Search for the vtable address | |
loc = FindText(segLoc, SEARCH_DOWN, 0, 0, " "+line+":"); | |
// If the vtable can't be found, skip its entries | |
if(loc == -1) { | |
Message("%s: not found\n", line); | |
while(line != "\n") | |
{ | |
line = readstr(file); | |
} | |
continue; | |
} | |
Message("%s: 0x%x\n", line, loc); | |
// Prepare to read the next vtable | |
line = readstr(file); | |
loc = loc + ItemSize(loc); | |
line = readstr(file); | |
line = substr(line, 0, strlen(line)-1); | |
ind = 0; | |
} | |
// Construct the vtable pointer (There's probably a better way to do this) | |
ptrLoc = getPointerAddress(loc); | |
//line = substr(line, 0, strlen(line)-1); | |
name = Name(ptrLoc); | |
//Message("[%d] 0x%x: %s : %s\n", ind, ptrLoc, line, name); | |
if(substr(name, 0, 7) == "nullsub") | |
{ | |
// Nullsub detected | |
//MakeComm(loc, Demangle(line, 0)); | |
} else if(CommentEx(ptrLoc, 0) == "reused") | |
{ | |
// Multi-purpose subroutine detected | |
//MakeComm(loc, Demangle(line, 0)); | |
}else if(substr(name, 0, 1) == "_" && name != line) | |
{ | |
// Same function has two different names | |
// Mark the function as being multi-purpose | |
MakeComm(ptrLoc, "reused"); | |
MakeNameEx(ptrLoc, "", SN_NOWARN); | |
//MakeComm(loc, Demangle(line, 0)); | |
} else | |
{ | |
// Function only has one name and can be safely renamed | |
MakeNameEx(ptrLoc, line, SN_NOWARN); | |
} | |
// Comment the vtable entry with the demangled function name | |
MakeComm(loc, Demangle(line, 0)); | |
// Add a member of type offset to the vtable struct using its mangled name | |
while (AddStrucMember(structID, line, ind*8, FF_0OFF, ptrLoc, 8) == STRUC_ERROR_MEMBER_NAME) | |
{ | |
line = line + "_"; | |
} | |
loc = loc + ItemSize(loc); | |
line = readstr(file); | |
ind = ind + 1; | |
} | |
fclose(file); | |
} | |
static main() | |
{ | |
SetStatus(IDA_STATUS_WAITING); | |
auto file = AskFile(0, "*.txt", "Import Vtable Info File"); | |
// Check if the user hasn't cancelled the file prompt | |
if(strlen(file) > 0) | |
{ | |
nameTables(file); | |
} | |
SetStatus(IDA_STATUS_READY); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Does this work in newer versions @jocopa3? I see no empty lines in the output file