GP-4376 Updated gcc class recovery to allow for special typeinfos that

are not in program memory or external block. Also updated to add a check
for unhandled relocations. Also updated to allow for non-mangled
typeinfo-name strings.
This commit is contained in:
ghidra007 2024-03-06 17:00:26 +00:00
parent 3e35b4d4a7
commit b04d5335ca
5 changed files with 318 additions and 128 deletions

View file

@ -80,6 +80,7 @@ import ghidra.program.model.data.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryBlock; import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.reloc.Relocation; import ghidra.program.model.reloc.Relocation;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationTable; import ghidra.program.model.reloc.RelocationTable;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.program.util.GhidraProgramUtilities; import ghidra.program.util.GhidraProgramUtilities;
@ -210,8 +211,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (!runGcc) { if (!runGcc) {
return; return;
} }
//run fixup old elf relocations script
runScript("FixElfExternalOffsetDataRelocationScript.java");
recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, state.getTool(), recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, state.getTool(),
this, BOOKMARK_FOUND_FUNCTIONS, USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, this, BOOKMARK_FOUND_FUNCTIONS, USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS,
nameVfunctions, hasDebugSymbols, monitor); nameVfunctions, hasDebugSymbols, monitor);
@ -232,9 +232,6 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
return; return;
} }
//run fixup old elf relocations script
runScript("FixElfExternalOffsetDataRelocationScript.java");
hasDebugSymbols = isDwarfLoadedInProgram(); hasDebugSymbols = isDwarfLoadedInProgram();
if (hasDwarf() && !hasDebugSymbols) { if (hasDwarf() && !hasDebugSymbols) {
println( println(
@ -379,7 +376,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
DWARFProgram.DWARF_ROOT_NAME) || options.getBoolean("DWARF Loaded", false)); DWARFProgram.DWARF_ROOT_NAME) || options.getBoolean("DWARF Loaded", false));
} }
public String validate() throws CancelledException { public String validate() throws Exception {
if (currentProgram == null) { if (currentProgram == null) {
return ("There is no open program"); return ("There is no open program");
@ -417,6 +414,8 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
// check that gcc loader or mingw analyzer has fixed the relocations correctly // check that gcc loader or mingw analyzer has fixed the relocations correctly
if (isGcc()) { if (isGcc()) {
runScript("FixElfExternalOffsetDataRelocationScript.java");
// first check that there is even rtti by searching the special string in memory // first check that there is even rtti by searching the special string in memory
if (!isStringInProgramMemory("class_type_info")) { if (!isStringInProgramMemory("class_type_info")) {
return ("This program does not contain RTTI."); return ("This program does not contain RTTI.");
@ -432,7 +431,7 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
"contact the Ghidra team so this issue can be fixed."); "contact the Ghidra team so this issue can be fixed.");
} }
if (hasUnhandledRelocations()) { if (hasRelocationIssue()) {
return ("This program has unhandled elf relocations so cannot continue. Please " + return ("This program has unhandled elf relocations so cannot continue. Please " +
"contact the Ghidra team for assistance."); "contact the Ghidra team for assistance.");
} }
@ -442,7 +441,13 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
} }
private boolean hasUnhandledRelocations() throws CancelledException { /**
* Method to determine if the gcc relocations needed to find the special typeinfos/vtables
* have any issues that would keep script from running correctly.
* @return true if there are any issues with the relocations, false otherwise
* @throws CancelledException if cancelled
*/
private boolean hasRelocationIssue() throws CancelledException {
RelocationTable relocationTable = currentProgram.getRelocationTable(); RelocationTable relocationTable = currentProgram.getRelocationTable();
@ -452,17 +457,39 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
monitor.checkCancelled(); monitor.checkCancelled();
Relocation r = relocations.next(); Relocation r = relocations.next();
if (r.getSymbolName().contains("class_type_info") && String symbolName = r.getSymbolName();
(r.getStatus() != Relocation.Status.APPLIED &&
r.getStatus() != Relocation.Status.APPLIED_OTHER &&
r.getStatus() != Relocation.Status.SKIPPED)) {
return true;
}
if (symbolName != null && symbolName.contains("class_type_info")) {
Status status = r.getStatus();
// if any relocations for special typeinfo class symbols have failed then there
// is an issue
if (status == Status.FAILURE) {
return true;
}
// if any relocations for special typeinfo class symbols are unsupported then
// determine where the symbol is located before determining if it is an issue
if(status == Status.UNSUPPORTED) {
//if relocation symbol is the same as the symbol at the relcation address
//then this situation is not an issue - it indicates a copy relocation at the
//location of the special typeinfo vtable which is a use case that can be handled
Address address = r.getAddress();
Symbol symbolAtAddress = currentProgram.getSymbolTable().getSymbol(symbolName, address, currentProgram.getGlobalNamespace());
if(symbolAtAddress != null) {
continue;
}
return true;
}
}
} }
return false; return false;
} }
private void analyzeProgramChanges(AddressSetView beforeChanges) throws Exception { private void analyzeProgramChanges(AddressSetView beforeChanges) throws Exception {
AddressSetView addressSet = currentProgram.getChanges().getAddressSet(); AddressSetView addressSet = currentProgram.getChanges().getAddressSet();

View file

@ -15,10 +15,7 @@
*/ */
package classrecovery; package classrecovery;
import java.util.ArrayList; import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Namespace;
@ -30,7 +27,7 @@ public class GccTypeinfo extends Typeinfo {
private static final String VMI_CLASS_TYPEINFO_NAMESPACE = "__vmi_class_type_info"; private static final String VMI_CLASS_TYPEINFO_NAMESPACE = "__vmi_class_type_info";
boolean isSpecialTypeinfo; boolean isSpecialTypeinfo;
GccTypeinfo inheritedSpecialTypeinfo = null; Namespace inheritedSpecialTypeinfoNamespace = null;
Address vtableAddress; Address vtableAddress;
boolean inProgramMemory; boolean inProgramMemory;
String mangledNamespaceString = null; String mangledNamespaceString = null;
@ -53,13 +50,13 @@ public class GccTypeinfo extends Typeinfo {
return inProgramMemory; return inProgramMemory;
} }
public void setInheritedSpecialTypeinfo(GccTypeinfo specialTypeinfo) { public void setInheritedSpecialTypeinfoNamespace(Namespace specialTypeinfoNamespace) {
inheritedSpecialTypeinfo = specialTypeinfo; inheritedSpecialTypeinfoNamespace = specialTypeinfoNamespace;
} }
public GccTypeinfo getInheritedSpecialTypeinfo() { public Namespace getInheritedSpecialTypeinfoNamespace() {
return inheritedSpecialTypeinfo; return inheritedSpecialTypeinfoNamespace;
} }
@ -156,21 +153,21 @@ public class GccTypeinfo extends Typeinfo {
} }
public boolean isClassTypeinfo() { public boolean isClassTypeinfo() {
if(inheritedSpecialTypeinfo.getNamespace().getName().equals(CLASS_TYPEINFO_NAMESPACE)) { if (inheritedSpecialTypeinfoNamespace.getName().equals(CLASS_TYPEINFO_NAMESPACE)) {
return true; return true;
} }
return false; return false;
} }
public boolean isSiClassTypeinfo() { public boolean isSiClassTypeinfo() {
if(inheritedSpecialTypeinfo.getNamespace().getName().equals(SI_CLASS_TYPEINFO_NAMESPACE)) { if (inheritedSpecialTypeinfoNamespace.getName().equals(SI_CLASS_TYPEINFO_NAMESPACE)) {
return true; return true;
} }
return false; return false;
} }
public boolean isVmiClassTypeinfo() { public boolean isVmiClassTypeinfo() {
if(inheritedSpecialTypeinfo.getNamespace().getName().equals(VMI_CLASS_TYPEINFO_NAMESPACE)) { if (inheritedSpecialTypeinfoNamespace.getName().equals(VMI_CLASS_TYPEINFO_NAMESPACE)) {
return true; return true;
} }
return false; return false;

View file

@ -19,6 +19,8 @@ package classrecovery;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.*; import java.util.*;
import org.apache.commons.lang3.StringUtils;
import ghidra.app.cmd.label.DemanglerCmd; import ghidra.app.cmd.label.DemanglerCmd;
import ghidra.app.plugin.core.analysis.ReferenceAddressPair; import ghidra.app.plugin.core.analysis.ReferenceAddressPair;
import ghidra.app.util.NamespaceUtils; import ghidra.app.util.NamespaceUtils;
@ -101,6 +103,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
protected final FunctionManager functionManager; protected final FunctionManager functionManager;
protected final Listing listing; protected final Listing listing;
protected final Memory memory;
public RTTIGccClassRecoverer(Program program, ServiceProvider serviceProvider, public RTTIGccClassRecoverer(Program program, ServiceProvider serviceProvider,
FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates, FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates,
@ -113,6 +116,9 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
functionManager = program.getFunctionManager(); functionManager = program.getFunctionManager();
listing = program.getListing(); listing = program.getListing();
memory = program.getMemory();
} }
@Override @Override
@ -156,7 +162,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
List<GccTypeinfo> specialTypeinfos = createSpecialTypeinfos(); List<GccTypeinfo> specialTypeinfos = createSpecialTypeinfos();
if (specialTypeinfos.isEmpty()) { if (specialTypeinfos.isEmpty()) {
Msg.debug(this, "Could not create special typeinfos"); Msg.debug(this, "Could not create special typeinfos");
return null;
} }
Msg.debug(this, "Creating Special Vtables"); Msg.debug(this, "Creating Special Vtables");
@ -169,7 +174,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
if (specialVtables.size() != specialTypeinfos.size()) { if (specialVtables.size() != specialTypeinfos.size()) {
Msg.debug(this, "Not equal number of special vtables and special typeinfos"); Msg.debug(this, "Not equal number of special vtables and special typeinfos");
return null;
} }
setComponentOffset(); setComponentOffset();
@ -355,7 +359,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
// in a non-loaded section that isn't real memory then it shouldn't be the case where the // in a non-loaded section that isn't real memory then it shouldn't be the case where the
// typeinfo is at the same location as the vtable since it should have enough memory and // typeinfo is at the same location as the vtable since it should have enough memory and
// real bytes that point to a real typeinfo in program memory // real bytes that point to a real typeinfo in program memory
if (hasAssociatedFileByes(vtableAddress)) { if (isLoadedAndInitializedMemory(vtableAddress)) {
return null; return null;
} }
@ -381,7 +385,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
private boolean hasAssociatedFileByes(Address address) { private boolean isLoadedAndInitializedMemory(Address address) {
if (inExternalBlock(address)) { if (inExternalBlock(address)) {
return false; return false;
@ -391,9 +395,9 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
Memory memory = program.getMemory(); Memory memory = program.getMemory();
long fileOffset = memory.getAddressSourceInfo(address).getFileOffset(); AddressSetView initMem = memory.getLoadedAndInitializedAddressSet();
if (fileOffset == -1) { if (initMem.contains(address)) {
return false; return true;
} }
return true; return true;
@ -451,7 +455,6 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
return null; return null;
} }
// TODO: can this be used for regular ones too?
private Symbol findTypeinfoSymbolUsingMangledNamespaceString(String mangledNamespace, private Symbol findTypeinfoSymbolUsingMangledNamespaceString(String mangledNamespace,
String namespaceName) throws CancelledException { String namespaceName) throws CancelledException {
@ -2220,6 +2223,19 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
SpecialVtable specialVtable = new SpecialVtable(program, vtableAddress, typeinfoRef, SpecialVtable specialVtable = new SpecialVtable(program, vtableAddress, typeinfoRef,
isExternal, vtableSymbol.getParentNamespace(), monitor); isExternal, vtableSymbol.getParentNamespace(), monitor);
if (specialTypeinfo != null) {
specialTypeinfo.setVtableAddress(vtableAddress);
}
if (!specialVtable.isExternal()) {
specialVtable.applyVtableData();
vtableToSizeMap.put(specialVtable.getAddress(), specialVtable.getLength());
createVtableLabel(specialVtable);
createVtableComment(specialVtable);
createVfunctionSymbol(specialVtable);
}
return specialVtable; return specialVtable;
} }
@ -2362,9 +2378,14 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
typeinfoSymbol = createDemangledTypeinfoSymbol(typeinfoAddress); typeinfoSymbol = createDemangledTypeinfoSymbol(typeinfoAddress);
if (typeinfoSymbol == null) { if (typeinfoSymbol == null) {
Msg.debug(this, "Could not create demangled typeinfo symbol at " + //If no mangled class name, check for non-mangled pascal type class name
typeinfoAddress.toString()); typeinfoSymbol =
continue; createTypeinfoSymbolFromNonMangledString(typeinfoAddress);
if (typeinfoSymbol == null) {
Msg.debug(this, "Could not create typeinfo symbol at " +
typeinfoAddress.toString());
continue;
}
} }
} }
@ -2374,9 +2395,9 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
if (specialTypeinfoNamespaceName == null) { if (specialTypeinfoNamespaceName == null) {
continue; continue;
} }
GccTypeinfo specialTypeinfo =
getTypeinfo(specialTypeinfoNamespaceName, specialTypeinfos); typeinfo.setInheritedSpecialTypeinfoNamespace(specialVtable.getNamespace());
typeinfo.setInheritedSpecialTypeinfo(specialTypeinfo);
typeinfos.add(typeinfo); typeinfos.add(typeinfo);
continue; continue;
} }
@ -2390,7 +2411,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
for (GccTypeinfo typeinfo : typeinfos) { for (GccTypeinfo typeinfo : typeinfos) {
monitor.checkCancelled(); monitor.checkCancelled();
Address typeinfoAddress = typeinfo.getAddress(); Address typeinfoAddress = typeinfo.getAddress();
if (typeinfo.getInheritedSpecialTypeinfo() == null) { if (typeinfo.getInheritedSpecialTypeinfoNamespace() == null) {
typeinfosToRemove.add(typeinfo); typeinfosToRemove.add(typeinfo);
continue; continue;
@ -2436,9 +2457,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
continue; continue;
} }
// TODO: update the typeinfo with the correct namespace based on the structure String namespaceName = typeinfo.getInheritedSpecialTypeinfoNamespace().getName();
String namespaceName = typeinfo.getInheritedSpecialTypeinfo().getNamespace().getName();
// if typeinfo inherits class_type_info then no Base to update // if typeinfo inherits class_type_info then no Base to update
if (namespaceName.equals(CLASS_TYPEINFO_NAMESPACE)) { if (namespaceName.equals(CLASS_TYPEINFO_NAMESPACE)) {
@ -2652,6 +2671,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
// ok if has symbol at the actual addr so don't check it // ok if has symbol at the actual addr so don't check it
if (offset == 0) { if (offset == 0) {
offset++;
continue; continue;
} }
@ -2848,6 +2868,103 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
return newSymbol; return newSymbol;
} }
private Symbol createTypeinfoSymbolFromNonMangledString(Address typeinfoAddress)
throws DuplicateNameException, InvalidInputException, CancelledException {
Address typeinfoNameAddress = getTypeinfoNameAddress(typeinfoAddress);
if (typeinfoNameAddress == null) {
Msg.debug(this,
"Could not get typeinfo-name address from " + typeinfoAddress.toString());
return null;
}
String typeinfoNameString = getStringFromMemory(typeinfoNameAddress);
if (typeinfoNameString == null) {
Msg.debug(this, "Could not get typeinfo string from " + typeinfoNameAddress.toString());
return null;
}
// get length from start of string
String lenString = getAsciiLengthString(typeinfoNameString);
if (lenString.isEmpty()) {
Msg.debug(this,
"Could not get typeinfo-name string len from " + typeinfoNameAddress.toString());
return null;
}
// convert lenString to int len
try {
int len = Integer.parseInt(lenString);
// get className from string - if not exactly the correct len then return null
String className = typeinfoNameString.substring(lenString.length());
if (className.length() != len) {
Msg.debug(this, "Expected typeinfo-name to be len " + len + " but it was " +
className.length());
return null;
}
program.getListing()
.clearCodeUnits(typeinfoNameAddress,
typeinfoNameAddress.add(typeinfoNameString.length()), true);
boolean created = createString(typeinfoNameAddress, typeinfoNameString.length());
if (!created) {
Msg.debug(this, "Could not create string at " + typeinfoNameAddress);
}
// create typeinfo name symbol
Namespace classNamespace =
symbolTable.getOrCreateNameSpace(globalNamespace, className, SourceType.ANALYSIS);
Symbol typeinfoNameSymbol = symbolTable.createLabel(typeinfoNameAddress,
"typeinfo-name", classNamespace, SourceType.ANALYSIS);
typeinfoNameSymbol.setPrimary();
// create the new typeinfo symbol in the demangled namespace
Symbol typeinfoSymbol = symbolTable.createLabel(typeinfoAddress, "typeinfo",
classNamespace, SourceType.ANALYSIS);
typeinfoSymbol.setPrimary();
api.setPlateComment(typeinfoAddress, "typeinfo for " + classNamespace.getName(true));
return typeinfoSymbol;
}
catch (NumberFormatException ex) {
return null;
}
}
private String getAsciiLengthString(String string) throws CancelledException {
boolean isDigit = true;
int len = 0;
String lenString = new String();
while (isDigit) {
monitor.checkCancelled();
String charString = string.substring(len, len);
if (!StringUtils.isNumeric(charString)) {
return lenString;
}
lenString.concat(charString);
len++;
}
return lenString;
}
private boolean isAllAscii(Address address, int len) throws CancelledException {
for (int i = 0; i < len; i++) {
monitor.checkCancelled();
if (!isAscii(address.add(i))) {
return false;
}
}
return true;
}
private boolean isTypeinfoNameString(String string) { private boolean isTypeinfoNameString(String string) {
DemangledObject demangledObject = DemanglerUtil.demangle(string); DemangledObject demangledObject = DemanglerUtil.demangle(string);
@ -2934,6 +3051,14 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
private String getStringFromMemory(Address address, int len) {
TerminatedStringDataType sdt = new TerminatedStringDataType();
MemBuffer buf = new DumbMemBufferImpl(program.getMemory(), address);
return (String) sdt.getValue(buf, sdt.getDefaultSettings(), len);
}
private int getStringLen(Address addr) { private int getStringLen(Address addr) {
int len = 0; int len = 0;
@ -3544,80 +3669,91 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
} }
private Address findSpecialVtable(GccTypeinfo specialTypeinfo,
List<GccTypeinfo> specialTypeinfos) throws CancelledException {
String namespaceName = specialTypeinfo.getNamespace().getName();
String mangledNamespaceString = specialTypeinfo.getMangledNamespaceString();
// try finding with normal symbol name and namespace
Symbol vtableSymbol =
getSymbolInNamespaces(SPECIAL_CLASS_NAMESPACE, namespaceName, VTABLE_LABEL);
if (vtableSymbol == null) {
// then try finding with mangled symbol
vtableSymbol =
findAndReturnDemangledSymbol(MANGLED_VTABLE_PREFIX + mangledNamespaceString,
SPECIAL_CLASS_NAMESPACE, namespaceName, VTABLE_LABEL);
// then try finding top of special vtable by finding ref to special typeinfo
if (vtableSymbol == null) {
Address vtableAddress = findSpecialVtableUsingSpecialTypeinfo(
specialTypeinfo.getAddress(), specialTypeinfos);
if (vtableAddress == null) {
return null;
}
try {
vtableSymbol = symbolTable.createLabel(vtableAddress, VTABLE_LABEL,
specialTypeinfo.getNamespace(), SourceType.ANALYSIS);
api.setPlateComment(vtableAddress,
"vtable for " + specialTypeinfo.getNamespace());
}
catch (InvalidInputException e) {
// ignore
}
}
}
if (vtableSymbol != null) {
return vtableSymbol.getAddress();
}
return null;
}
private List<SpecialVtable> findSpecialVtables(List<GccTypeinfo> specialTypeinfos) private List<SpecialVtable> findSpecialVtables(List<GccTypeinfo> specialTypeinfos)
throws Exception { throws Exception {
List<SpecialVtable> specialVtables = new ArrayList<SpecialVtable>(); List<SpecialVtable> specialVtables = new ArrayList<SpecialVtable>();
for (GccTypeinfo specialTypeinfo : specialTypeinfos) { Map<String, String> namespaceMap = new HashMap<String, String>();
monitor.checkCancelled(); namespaceMap.put(CLASS_TYPEINFO_NAMESPACE, MANGLED_CLASS_TYPEINFO_NAMESPACE);
namespaceMap.put(SI_CLASS_TYPEINFO_NAMESPACE, MANGLED_SI_CLASS_TYPEINFO_NAMESPACE);
namespaceMap.put(VMI_CLASS_TYPEINFO_NAMESPACE, MANGLED_VMI_CLASS_TYPEINFO_NAMESPACE);
Address vtableAddress = findSpecialVtable(specialTypeinfo, specialTypeinfos); for (String namespaceName : namespaceMap.keySet()) {
GccTypeinfo specTypeinfo = getSpecialTypeinfo(specialTypeinfos, namespaceName);
Address vtableAddress = findSpecialVtableAddress(namespaceName,
namespaceMap.get(namespaceName), specialTypeinfos);
if (vtableAddress == null) { if (vtableAddress == null) {
continue; continue;
} }
SpecialVtable specialVtable = createSpecialVtable(vtableAddress, specialTypeinfo); SpecialVtable specialVtable =
if (specialVtable != null) { createSpecialVtable(vtableAddress, specTypeinfo);
specialVtables.add(specialVtable); specialVtables.add(specialVtable);
specialTypeinfo.setVtableAddress(vtableAddress);
if (!specialVtable.isExternal()) {
specialVtable.applyVtableData();
vtableToSizeMap.put(specialVtable.getAddress(), specialVtable.getLength());
createVtableLabel(specialVtable);
createVtableComment(specialVtable);
createVfunctionSymbol(specialVtable);
}
}
} }
return specialVtables; return specialVtables;
} }
private Address findSpecialVtableAddress(String namespaceName, String mangledNamespace,
List<GccTypeinfo> specialTypeinfos) throws CancelledException {
//First try to find with special symbols
Symbol vtableSymbol = getSymbolInNamespaces(namespaceName, mangledNamespace, VTABLE_LABEL);
if (vtableSymbol != null) {
return vtableSymbol.getAddress();
}
// then try finding with mangled symbol
vtableSymbol = findAndReturnDemangledSymbol(MANGLED_VTABLE_PREFIX + mangledNamespace,
SPECIAL_CLASS_NAMESPACE, namespaceName, VTABLE_LABEL);
if (vtableSymbol != null) {
return vtableSymbol.getAddress();
}
//Then with special typeinfo if there is one
GccTypeinfo specTypeinfo = getSpecialTypeinfo(specialTypeinfos, namespaceName);
if (specTypeinfo != null) {
Address vtableAddress = findSpecialVtableUsingSpecialTypeinfo(specTypeinfo.getAddress(),
specialTypeinfos);
if (vtableAddress == null) {
return null;
}
try {
vtableSymbol = symbolTable.createLabel(vtableAddress, VTABLE_LABEL,
specTypeinfo.getNamespace(), SourceType.ANALYSIS);
api.setPlateComment(vtableAddress, "vtable for " + specTypeinfo.getNamespace());
return vtableSymbol.getAddress();
}
catch (InvalidInputException e) {
Msg.warn(this,
"Found vtable at " + vtableAddress + " but cannot create symbol" + e);
return null;
}
}
return null;
}
private GccTypeinfo getSpecialTypeinfo(List<GccTypeinfo> specialTypeinfos,
String namespaceName) {
for (GccTypeinfo specialTypeinfo : specialTypeinfos) {
if (specialTypeinfo.getNamespace().getName().equals(namespaceName)) {
return specialTypeinfo;
}
}
return null;
}
/* /*
* Method to find special vtable using special typeinfo references. This * Method to find special vtable using special typeinfo references. This
* assumption that vtable is defaultPtrSize above ref to single specialTypeinfo * assumption that vtable is defaultPtrSize above ref to single specialTypeinfo

View file

@ -15,9 +15,15 @@
*/ */
package classrecovery; package classrecovery;
import java.util.List;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.reloc.Relocation;
import ghidra.program.model.reloc.Relocation.Status;
import ghidra.program.model.reloc.RelocationTable;
import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -45,7 +51,20 @@ public class SpecialVtable extends Vtable {
return; return;
} }
typeinfoRefAddress = vtableAddress.add(defaultPointerSize); typeinfoRefAddress = vtableAddress.add(defaultPointerSize);
// check for vtable has memory but all zeros or has possible invalid values which in both
// cases would make the pointer to special typeinfo invalid
if (hasSpecialCopyUnhandledRelocation(vtableAddress)) {
isConstruction = false;
isPrimary = true;
typeinfoAddress = null;
length = 3 * defaultPointerSize; //actually prob 11*defPtr but are all zeros in this case
hasVfunctions = true; // they are null though so will count as num=0, need this to be true so check for refs to vfunction top will work
numVfunctions = 0;
vfunctionTop = vtableAddress.add(2 * defaultPointerSize);
return;
}
setTypeinfoAddress(); setTypeinfoAddress();
@ -71,7 +90,9 @@ public class SpecialVtable extends Vtable {
isConstruction = false; isConstruction = false;
classNamespace = typeinfoNamespace; if (classNamespace == null) {
classNamespace = typeinfoNamespace;
}
setLength(); setLength();
@ -82,4 +103,34 @@ public class SpecialVtable extends Vtable {
return refFromTypeinfos; return refFromTypeinfos;
} }
private boolean hasSpecialCopyUnhandledRelocation(Address address) {
RelocationTable relocationTable = program.getRelocationTable();
List<Relocation> relocations = relocationTable.getRelocations(address);
for (Relocation relocation : relocations) {
Status status = relocation.getStatus();
if (status == Status.UNSUPPORTED) {
String symbolName = relocation.getSymbolName();
if (symbolName == null || !symbolName.contains("class_type_info")) {
continue;
}
//if relocation symbol is the same as the symbol at the relcation address
//then this situation is not an issue - it indicates a copy relocation at the
//location of the special typeinfo vtable which is a use case that can be handled
Symbol symbolAtAddress = program.getSymbolTable()
.getSymbol(symbolName, address, program.getGlobalNamespace());
if (symbolAtAddress != null) {
return true;
}
}
}
return false;
}
} }

View file

@ -80,7 +80,9 @@ public class Vtable {
this.monitor = monitor; this.monitor = monitor;
this.typeinfoRefAddress = typeinfoRef.getAddress(); this.typeinfoRefAddress = typeinfoRef.getAddress();
this.typeinfo = (GccTypeinfo) typeinfoRef.getReferencedTypeinfo(); this.typeinfo = (GccTypeinfo) typeinfoRef.getReferencedTypeinfo();
this.typeinfoNamespace = typeinfo.getNamespace(); if (this.typeinfo != null) {
this.typeinfoNamespace = typeinfo.getNamespace();
}
AddressSpace addressSpace = vtableAddress.getAddressSpace(); AddressSpace addressSpace = vtableAddress.getAddressSpace();
defaultPointerSize = addressSpace.getPointerSize(); defaultPointerSize = addressSpace.getPointerSize();
@ -390,9 +392,6 @@ public class Vtable {
*/ */
private boolean isPossibleFunctionPointer(Address address) throws CancelledException { private boolean isPossibleFunctionPointer(Address address) throws CancelledException {
// TODO: make one that works for all casea in helper
// TODO: make sure it recognizes the external functions
long longValue = extendedFlatAPI.getLongValueAt(address); long longValue = extendedFlatAPI.getLongValueAt(address);
Register lowBitCodeMode = program.getRegister("LowBitCodeMode"); Register lowBitCodeMode = program.getRegister("LowBitCodeMode");
@ -605,26 +604,6 @@ public class Vtable {
public List<Vtable> getInternalVtables() { public List<Vtable> getInternalVtables() {
return internalVtables; return internalVtables;
} }
// TODO: put in helper or ext api
private boolean inExternalBlock(Address address) {
MemoryBlock externalBlock = getExternalBlock();
if (externalBlock == null) {
return false;
}
if (externalBlock.contains(address)) {
return true;
}
return false;
}
private MemoryBlock getExternalBlock() {
return program.getMemory().getBlock("EXTERNAL");
}
public void setIsConstructionVtable(Boolean setting) { public void setIsConstructionVtable(Boolean setting) {
isConstruction = setting; isConstruction = setting;