mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
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:
parent
3e35b4d4a7
commit
b04d5335ca
5 changed files with 318 additions and 128 deletions
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue