GP-5505 Improved RecoverClassesFromRTTIScript abilty to use the LowCodeModeBit to find code references.

This commit is contained in:
ghidra007 2025-03-21 18:40:16 +00:00
parent fa554361d5
commit 2f83e26c0e
3 changed files with 217 additions and 246 deletions

View file

@ -16,52 +16,21 @@
//DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS.
package classrecovery;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.plugin.core.analysis.ReferenceAddressPair;
import ghidra.app.util.PseudoDisassembler;
import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.address.*;
import ghidra.program.model.block.CodeBlock;
import ghidra.program.model.block.IsolatedEntrySubModel;
import ghidra.program.model.data.CategoryPath;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.IBO32DataType;
import ghidra.program.model.data.LongDataType;
import ghidra.program.model.data.Pointer;
import ghidra.program.model.data.PointerDataType;
import ghidra.program.model.data.Structure;
import ghidra.program.model.data.Undefined4DataType;
import ghidra.program.model.data.Undefined8DataType;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.Data;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.listing.InstructionIterator;
import ghidra.program.model.listing.Listing;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.DumbMemBufferImpl;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.FlowType;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolIterator;
import ghidra.program.model.symbol.SymbolTable;
import ghidra.program.model.symbol.SymbolType;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
@ -133,6 +102,97 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
}
}
/**
* Method to determine if the given address contains a possible function pointer
* @param program the given program
*
* @param address the given address
* @return true if the given address contains a possible function pointer or
* false otherwise
* @throws CancelledException if cancelled
*/
public boolean isPossibleFunctionPointer(Program program, Address address)
throws CancelledException {
Address referencedAddress = getPointer(address);
if (referencedAddress == null) {
return false;
}
Address normalizedReferencedAddress =
PseudoDisassembler.getNormalizedDisassemblyAddress(program, referencedAddress);
if (normalizedReferencedAddress == null) {
return false;
}
Function function = getFunctionAt(normalizedReferencedAddress);
if (function != null) {
return true;
}
AddressSetView executeSet = program.getMemory().getExecuteSet();
if (!executeSet.contains(normalizedReferencedAddress)) {
return false;
}
Instruction instruction = getInstructionAt(normalizedReferencedAddress);
if (instruction != null) {
createFunction(normalizedReferencedAddress, null);
return true;
}
boolean disassemble = disassemble(normalizedReferencedAddress);
if (disassemble) {
Listing listing = program.getListing();
// check for the case where there is conflicting data at the thumb offset function
// pointer and if so clear the data and redisassemble and remove the bad bookmark
// long originalLongValue = extendedFlatAPI.getLongValueAt(address);
if (!referencedAddress.equals(normalizedReferencedAddress)) {
Data dataAt = listing.getDataAt(referencedAddress);
if (dataAt != null && dataAt.isDefined()) {
clearListing(referencedAddress);
disassemble = disassemble(address);
Bookmark bookmark =
getBookmarkAt(program, normalizedReferencedAddress, BookmarkType.ERROR,
"Bad Instruction", "conflicting data");
if (bookmark != null) {
removeBookmark(bookmark);
}
}
}
createFunction(normalizedReferencedAddress, null);
return true;
}
return false;
}
public Bookmark getBookmarkAt(Program program, Address address, String bookmarkType,
String category,
String commentContains) throws CancelledException {
Bookmark[] bookmarks = program.getBookmarkManager().getBookmarks(address);
for (Bookmark bookmark : bookmarks) {
monitor.checkCancelled();
if (bookmark.getType().getTypeString().equals(bookmarkType) &&
bookmark.getCategory().equals(category) &&
bookmark.getComment().contains(commentContains)) {
return bookmark;
}
}
return null;
}
/**
* Method to check to see if there is a valid function pointer at the given address. If it is
* valid but not created, create it
@ -255,12 +315,10 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
return null;
}
Address functionAddress = referencesFrom.get(0);
Register lowBitCodeMode = currentProgram.getRegister("LowBitCodeMode");
if(lowBitCodeMode != null) {
if (lowBitCodeMode != null) {
long longValue = functionAddress.getOffset();
longValue = longValue & ~0x1;
functionAddress = functionAddress.getNewAddress(longValue);
@ -581,7 +639,6 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
return subroutineAddresses;
}
/**
* Method to get a list of symbols either matching exactly (if exact flag is true) or containing (if exact flag is false) the given symbol name
* @param addressSet the address set to find matching symbols in
@ -641,7 +698,7 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
if (addressSize == 32) {
long offset32 = getInt(address);
Address newAddr = address.getNewAddress(offset32);
if(currentProgram.getMemory().contains(newAddr)) {
if (currentProgram.getMemory().contains(newAddr)) {
return newAddr;
}
return null;
@ -651,7 +708,7 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
long offset64 = getLong(address);
Address newAddr = address.getNewAddress(offset64);
if(currentProgram.getMemory().contains(newAddr)) {
if (currentProgram.getMemory().contains(newAddr)) {
return newAddr;
}
return null;
@ -856,7 +913,6 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
return false;
}
/**
* Method to retrieve a single referenced address from the given address
* @param address the given address to look for a single referenced address
@ -1363,5 +1419,4 @@ public class ExtendedFlatProgramAPI extends FlatProgramAPI {
return buffer.toString();
}
}

View file

@ -24,6 +24,7 @@ import org.apache.commons.lang3.StringUtils;
import ghidra.app.cmd.label.DemanglerCmd;
import ghidra.app.plugin.core.analysis.ReferenceAddressPair;
import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.PseudoDisassembler;
import ghidra.app.util.demangler.DemangledObject;
import ghidra.app.util.demangler.DemanglerUtil;
import ghidra.framework.plugintool.ServiceProvider;
@ -31,7 +32,6 @@ import ghidra.program.flatapi.FlatProgramAPI;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.data.DataUtilities.ClearDataMode;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.scalar.Scalar;
@ -4395,60 +4395,51 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
*/
private boolean isPossibleFunctionPointer(Address address) throws CancelledException {
// TODO: make one that works for all casea in helper
long longValue = extendedFlatAPI.getLongValueAt(address);
Register lowBitCodeMode = program.getRegister("LowBitCodeMode");
if (lowBitCodeMode != null) {
longValue = longValue & ~0x1;
}
Address possibleFunctionPointer = null;
try {
possibleFunctionPointer = address.getNewAddress(longValue);
}
catch (AddressOutOfBoundsException e) {
Address referencedAddress = extendedFlatAPI.getSingleReferencedAddress(address);
if (referencedAddress == null) {
return false;
}
if (possibleFunctionPointer == null) {
Address normalizedReferencedAddress =
PseudoDisassembler.getNormalizedDisassemblyAddress(program, referencedAddress);
if (normalizedReferencedAddress == null) {
return false;
}
Function function = api.getFunctionAt(possibleFunctionPointer);
Function function = api.getFunctionAt(normalizedReferencedAddress);
if (function != null) {
return true;
}
AddressSetView executeSet = program.getMemory().getExecuteSet();
if (!executeSet.contains(possibleFunctionPointer)) {
if (!executeSet.contains(normalizedReferencedAddress)) {
return false;
}
Instruction instruction = api.getInstructionAt(possibleFunctionPointer);
Instruction instruction = api.getInstructionAt(normalizedReferencedAddress);
if (instruction != null) {
api.createFunction(possibleFunctionPointer, null);
api.createFunction(normalizedReferencedAddress, null);
return true;
}
boolean disassemble = api.disassemble(possibleFunctionPointer);
boolean disassemble = api.disassemble(normalizedReferencedAddress);
if (disassemble) {
// check for the case where there is conflicting data at the thumb offset function
// pointer and if so clear the data and redisassemble and remove the bad bookmark
long originalLongValue = extendedFlatAPI.getLongValueAt(address);
if (originalLongValue != longValue) {
Address offsetPointer = address.getNewAddress(originalLongValue);
Data dataAt = listing.getDataAt(offsetPointer);
// long originalLongValue = extendedFlatAPI.getLongValueAt(address);
if (!referencedAddress.equals(normalizedReferencedAddress)) {
Data dataAt = listing.getDataAt(referencedAddress);
if (dataAt != null && dataAt.isDefined()) {
api.clearListing(offsetPointer);
api.clearListing(referencedAddress);
disassemble = api.disassemble(address);
Bookmark bookmark = getBookmarkAt(possibleFunctionPointer, BookmarkType.ERROR,
Bookmark bookmark =
getBookmarkAt(normalizedReferencedAddress, BookmarkType.ERROR,
"Bad Instruction", "conflicting data");
if (bookmark != null) {
api.removeBookmark(bookmark);
@ -4456,7 +4447,7 @@ public class RTTIGccClassRecoverer extends RTTIClassRecoverer {
}
}
api.createFunction(possibleFunctionPointer, null);
api.createFunction(normalizedReferencedAddress, null);
return true;
}
return false;

View file

@ -20,7 +20,6 @@ import java.util.List;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.*;
import ghidra.program.model.symbol.*;
@ -64,9 +63,8 @@ public class Vtable {
DataTypeManager dataTypeManager;
Listing listing;
public Vtable(Program program, Address vtableAddress, GccTypeinfoRef typeinfoRef, boolean isSpecial, boolean inExternalMemory,
public Vtable(Program program, Address vtableAddress, GccTypeinfoRef typeinfoRef,
boolean isSpecial, boolean inExternalMemory,
Vtable primaryVtable, Boolean isConstruction, TaskMonitor monitor)
throws CancelledException {
@ -96,14 +94,17 @@ public class Vtable {
setup();
}
public Vtable(Program program, Address vtableAddress, GccTypeinfoRef typeinfoRef, boolean isSpecial, boolean inExternalMemory, TaskMonitor monitor)
public Vtable(Program program, Address vtableAddress, GccTypeinfoRef typeinfoRef,
boolean isSpecial, boolean inExternalMemory, TaskMonitor monitor)
throws CancelledException {
this(program, vtableAddress, typeinfoRef, isSpecial, inExternalMemory, null, null, monitor);
}
public Vtable(Program program, Address vtableAddress, GccTypeinfoRef typeinfoRef, boolean isSpecial, boolean inExternalMemory, boolean isConstruction,
public Vtable(Program program, Address vtableAddress, GccTypeinfoRef typeinfoRef,
boolean isSpecial, boolean inExternalMemory, boolean isConstruction,
TaskMonitor monitor) throws CancelledException {
this(program, vtableAddress, typeinfoRef, isSpecial, inExternalMemory, null, isConstruction, monitor);
this(program, vtableAddress, typeinfoRef, isSpecial, inExternalMemory, null, isConstruction,
monitor);
}
protected void setup() throws CancelledException {
@ -146,7 +147,8 @@ public class Vtable {
try {
applyVtableData();
} catch (Exception e) {
}
catch (Exception e) {
isValid = false;
}
@ -196,7 +198,6 @@ public class Vtable {
return isValid;
}
public Address getTypeinfoRefAddress() {
return typeinfoRef.getAddress();
}
@ -221,13 +222,14 @@ public class Vtable {
try {
Address topOffset = typeinfoRefAddress.subtract(defaultPointerSize);
if (topOffset.getOffset() < vtableAddress.getOffset()) {
Msg.debug(this,"No offset field in vtable at " + vtableAddress.toString());
Msg.debug(this, "No offset field in vtable at " + vtableAddress.toString());
isValid = false;
return;
}
topOffsetValue = extendedFlatAPI.getLongValueAt(topOffset);
} catch (IllegalArgumentException e) {
}
catch (IllegalArgumentException e) {
Msg.debug(this, "Invalid vtable: " + vtableAddress.toString() + " No offset field");
isValid = false;
}
@ -252,7 +254,8 @@ public class Vtable {
// otherwise, use the topOffsetValue to figure it out
if (topOffsetValue == 0L) {
isPrimary = true;
} else {
}
else {
isPrimary = false;
}
}
@ -293,11 +296,13 @@ public class Vtable {
numVfunctions = getNumFunctionPointers(possVfunctionTop, true, false);
if (numVfunctions == 0) {
hasVfunctions = false;
} else {
}
else {
hasVfunctions = true;
vfunctionTop = possVfunctionTop;
}
} catch (AddressOutOfBoundsException e) {
}
catch (AddressOutOfBoundsException e) {
hasVfunctions = false;
}
@ -311,14 +316,16 @@ public class Vtable {
// if it has a primary non-default symbol and it isn't "vftable" then it isn't a vftable
Symbol primarySymbol = symbolTable.getPrimarySymbol(topAddress);
if(primarySymbol != null && primarySymbol.getSource() != SourceType.DEFAULT && !primarySymbol.getName().contains("vftable")) {
if (primarySymbol != null && primarySymbol.getSource() != SourceType.DEFAULT &&
!primarySymbol.getName().contains("vftable")) {
return numFunctionPointers;
}
MemoryBlock currentBlock = program.getMemory().getBlock(topAddress);
boolean stillInCurrentTable = true;
while (address != null && currentBlock.contains(address) && stillInCurrentTable
&& (isPossibleFunctionPointer(address) || (allowNullFunctionPtrs && isPossibleNullPointer(address)))) {
while (address != null && currentBlock.contains(address) && stillInCurrentTable &&
(extendedFlatAPI.isPossibleFunctionPointer(program, address) ||
(allowNullFunctionPtrs && isPossibleNullPointer(address)))) {
numFunctionPointers++;
address = address.add(defaultPointerSize);
@ -344,8 +351,9 @@ public class Vtable {
// }
// check to see if last is null ptr and next addr after that is typeinfo ref - indicating the null is really top of next vtable
Address lastAddress = topAddress.add((numFunctionPointers-1)*defaultPointerSize);
if(isPossibleNullPointer(lastAddress) && (isTypeinfoRef(lastAddress.add(defaultPointerSize)))){
Address lastAddress = topAddress.add((numFunctionPointers - 1) * defaultPointerSize);
if (isPossibleNullPointer(lastAddress) &&
(isTypeinfoRef(lastAddress.add(defaultPointerSize)))) {
numFunctionPointers--;
}
return numFunctionPointers;
@ -354,15 +362,15 @@ public class Vtable {
private boolean isTypeinfoRef(Address addr) {
Address referencedAddress = getReferencedAddress(addr);
if(referencedAddress == null) {
if (referencedAddress == null) {
return false;
}
Data data = program.getListing().getDataAt(referencedAddress);
if(data == null) {
if (data == null) {
return false;
}
if(data.getBaseDataType().getName().contains("ClassTypeInfoStructure")) {
if (data.getBaseDataType().getName().contains("ClassTypeInfoStructure")) {
return true;
}
return false;
@ -382,97 +390,6 @@ public class Vtable {
return true;
}
/**
* Method to determine if the given address contains a possible function pointer
*
* @param address the given address
* @return true if the given address contains a possible function pointer or
* false otherwise
* @throws CancelledException if cancelled
*/
private boolean isPossibleFunctionPointer(Address address) throws CancelledException {
long longValue = extendedFlatAPI.getLongValueAt(address);
Register lowBitCodeMode = program.getRegister("LowBitCodeMode");
if (lowBitCodeMode != null) {
longValue = longValue & ~0x1;
}
Address possibleFunctionPointer = null;
try {
possibleFunctionPointer = address.getNewAddress(longValue);
} catch (AddressOutOfBoundsException e) {
return false;
}
if (possibleFunctionPointer == null) {
return false;
}
Function function = extendedFlatAPI.getFunctionAt(possibleFunctionPointer);
if (function != null) {
return true;
}
AddressSetView executeSet = program.getMemory().getExecuteSet();
if (!executeSet.contains(possibleFunctionPointer)) {
return false;
}
Instruction instruction = extendedFlatAPI.getInstructionAt(possibleFunctionPointer);
if (instruction != null) {
extendedFlatAPI.createFunction(possibleFunctionPointer, null);
return true;
}
boolean disassemble = extendedFlatAPI.disassemble(possibleFunctionPointer);
if (disassemble) {
// check for the case where there is conflicting data at the thumb offset
// function
// pointer and if so clear the data and redisassemble and remove the bad
// bookmark
long originalLongValue = extendedFlatAPI.getLongValueAt(address);
if (originalLongValue != longValue) {
Address offsetPointer = address.getNewAddress(originalLongValue);
if (extendedFlatAPI.getDataAt(offsetPointer) != null) {
extendedFlatAPI.clearListing(offsetPointer);
disassemble = extendedFlatAPI.disassemble(address);
Bookmark bookmark = getBookmarkAt(possibleFunctionPointer, BookmarkType.ERROR, "Bad Instruction",
"conflicting data");
if (bookmark != null) {
extendedFlatAPI.removeBookmark(bookmark);
}
}
}
extendedFlatAPI.createFunction(possibleFunctionPointer, null);
return true;
}
return false;
}
private Bookmark getBookmarkAt(Address address, String bookmarkType, String category, String commentContains)
throws CancelledException {
Bookmark[] bookmarks = program.getBookmarkManager().getBookmarks(address);
for (Bookmark bookmark : bookmarks) {
monitor.checkCancelled();
if (bookmark.getType().getTypeString().equals(bookmarkType) && bookmark.getCategory().equals(category)
&& bookmark.getComment().contains(commentContains)) {
return bookmark;
}
}
return null;
}
public void setHasVfunctions(boolean flag) {
hasVfunctions = flag;
}
@ -492,7 +409,8 @@ public class Vtable {
return;
}
if (!hasVfunctions) {
length = (int) (typeinfoRefAddress.getOffset() + defaultPointerSize - vtableAddress.getOffset());
length = (int) (typeinfoRefAddress.getOffset() + defaultPointerSize -
vtableAddress.getOffset());
return;
}
@ -548,19 +466,21 @@ public class Vtable {
Address nextAddr = vtableAddress.add(length);
Address typeinfoAddr = typeinfo.getAddress();
int alignment = nextAddr.getSize()/8;
Address nextTypeinfoRefAddr = getNextReferenceTo(nextAddr, typeinfoAddr, alignment, limit);
if(nextTypeinfoRefAddr == null) {
int alignment = nextAddr.getSize() / 8;
Address nextTypeinfoRefAddr =
getNextReferenceTo(nextAddr, typeinfoAddr, alignment, limit);
if (nextTypeinfoRefAddr == null) {
keepChecking = false;
continue;
}
GccTypeinfoRef internalTypenfoRef = new GccTypeinfoRef(nextTypeinfoRefAddr, typeinfo, true);
GccTypeinfoRef internalTypenfoRef =
new GccTypeinfoRef(nextTypeinfoRefAddr, typeinfo, true);
Vtable possibleInternalVtable = new Vtable(program, nextAddr,internalTypenfoRef, isSpecial, inExternalMemory,
Vtable possibleInternalVtable =
new Vtable(program, nextAddr, internalTypenfoRef, isSpecial, inExternalMemory,
this, isConstruction, monitor);
if (!possibleInternalVtable.isValid()) {
keepChecking = false;
@ -575,20 +495,22 @@ public class Vtable {
Namespace internalVtableNamespace = possibleInternalVtable.getNamespace();
if (internalVtableNamespace != null && internalVtableNamespace.equals(classNamespace)) {
addInternalVtable(possibleInternalVtable);
} else {
}
else {
keepChecking = false;
}
}
}
private Address getNextReferenceTo(Address startAddress, Address refdAddress, int alignment, int limit) {
private Address getNextReferenceTo(Address startAddress, Address refdAddress, int alignment,
int limit) {
int offset = alignment;
while(offset < limit) {
while (offset < limit) {
Address addr = startAddress.add(offset);
Address referencedAddress = getReferencedAddress(addr);
if(referencedAddress != null && referencedAddress.equals(refdAddress)) {
if (referencedAddress != null && referencedAddress.equals(refdAddress)) {
return addr;
}
offset += alignment;
@ -615,7 +537,7 @@ public class Vtable {
private void figureOutNamespace() {
if(isConstruction == null) {
if (isConstruction == null) {
setNamespace(globalNamespace);
return;
}
@ -635,7 +557,8 @@ public class Vtable {
// if not primary and the primary has same namespace then it is an internal
// vtable and can
// set the namespace to the typeinfo namespace
if (!primaryVtable.getNamespace().isGlobal() && primaryVtable.getNamespace().equals(typeinfoNamespace)) {
if (!primaryVtable.getNamespace().isGlobal() &&
primaryVtable.getNamespace().equals(typeinfoNamespace)) {
setNamespace(typeinfoNamespace);
return;
}
@ -647,7 +570,7 @@ public class Vtable {
public void setNamespace(Namespace namespace) {
classNamespace = namespace;
for(Vtable internalVtable : internalVtables) {
for (Vtable internalVtable : internalVtables) {
internalVtable.setNamespace(namespace);
}
}
@ -707,7 +630,8 @@ public class Vtable {
try {
Data vftableArrayData = listing.createData(vftableAddress, vftableArrayDataType);
return vftableArrayData;
} catch (Exception e) {
}
catch (Exception e) {
return null;
}
@ -732,7 +656,7 @@ public class Vtable {
Address address = start;
while (address != null && !address.equals(end)) {
listing.clearCodeUnits(address, address.add(defaultPointerSize - 1),false);
listing.clearCodeUnits(address, address.add(defaultPointerSize - 1), false);
listing.createData(address, longDT);
offset += defaultPointerSize;
address = getAddress(start, offset);
@ -751,7 +675,8 @@ public class Vtable {
try {
Address newAddress = address.add(offset);
return newAddress;
} catch (AddressOutOfBoundsException e) {
}
catch (AddressOutOfBoundsException e) {
return null;
}
}
@ -765,7 +690,7 @@ public class Vtable {
if (addressSize == 32) {
long offset32 = memory.getInt(address);
Address newAddr = address.getNewAddress(offset32);
if(memory.contains(newAddr)) {
if (memory.contains(newAddr)) {
return newAddr;
}
return null;
@ -775,7 +700,7 @@ public class Vtable {
long offset64 = memory.getLong(address);
Address newAddr = address.getNewAddress(offset64);
if(memory.contains(newAddr)) {
if (memory.contains(newAddr)) {
return newAddr;
}
return null;