GP-973 Added ApplyClassFunctionSignatureUpdatesScript and ApplyClassFunctionDefinitionUpdatesScript fix-up scripts for when users update RecoveredClass virtual function signatures or definitions.

This commit is contained in:
ghidra007 2021-06-09 17:08:44 -04:00 committed by ghidra1
parent 8c488aaacf
commit b6a5ce659b
7 changed files with 1306 additions and 299 deletions

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
//Script to graph class hierarchies given metadata found in class structure description that //Script to graph class hierarchies given metadata found in class structure description that
// was applied using the ExtractClassInfoFromRTTIScript. // was applied using the RecoverClassesFromRTTIScript.
//@category C++ //@category C++
import java.util.*; import java.util.*;

View file

@ -1,191 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//Script to update the given class's virtual functions' function signature data types and
// the given class's vfunction structure field name for any differing functions in
// the class virtual function table(s). To run, put the cursor on any of the desired class's
// virtual functions or at the top a class vftable. The script will not work if the <class>_vftable
// structure is not applied to the vftable using the ExtractClassInfoFromRTTIScript.
//@category C++
import java.util.*;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.DuplicateNameException;
public class UpdateClassFunctionDataScript extends GhidraScript {
@Override
public void run() throws Exception {
if (currentProgram == null) {
println("There is no open program");
return;
}
Function function = getFunctionContaining(currentAddress);
if (function != null) {
Namespace parentNamespace = function.getParentNamespace();
Parameter thisParam = function.getParameter(0);
if (thisParam.getName().equals("this")) {
DataType dataType = thisParam.getDataType();
if (dataType instanceof Pointer) {
Pointer pointer = (Pointer) dataType;
DataType baseDataType = pointer.getDataType();
if (baseDataType.getName().equals(parentNamespace.getName())) {
// call update
println("updating class " + parentNamespace.getName());
updateClassFunctionDataTypes(parentNamespace);
return;
}
}
}
}
Symbol primarySymbol = currentProgram.getSymbolTable().getPrimarySymbol(currentAddress);
if (primarySymbol.getName().equals("vftable") ||
primarySymbol.getName().substring(1).startsWith("vftable")) {
updateClassFunctionDataTypes(primarySymbol.getParentNamespace());
return;
}
}
private void updateClassFunctionDataTypes(Namespace classNamespace)
throws CancelledException, DuplicateNameException, DataTypeDependencyException {
List<Symbol> classVftableSymbols = getClassVftableSymbols(classNamespace);
Iterator<Symbol> vftableIterator = classVftableSymbols.iterator();
while (vftableIterator.hasNext()) {
monitor.checkCanceled();
Symbol vftableSymbol = vftableIterator.next();
Address vftableAddress = vftableSymbol.getAddress();
Data data = getDataAt(vftableAddress);
if (data == null) {
continue;
}
DataType baseDataType = data.getBaseDataType();
if (!(baseDataType instanceof Structure)) {
continue;
}
Structure vfunctionStructure = (Structure) baseDataType;
Category category = getDataTypeCategory(vfunctionStructure);
if (category == null) {
continue;
}
// check that the structure name starts with <classname>_vtable and that it is in
// the dt folder with name <classname>
if (category.getName().equals(classNamespace.getName()) &&
vfunctionStructure.getName().startsWith(classNamespace.getName() + "_vftable")) {
println(
"Updating vfunction signature data types and (if necessary) vtable structure for vftable at address " +
vftableAddress.toString());
updateVfunctionDataTypes(data, vfunctionStructure, vftableAddress);
}
}
}
/**
* Method to find any function signatures in the given vfunction structure that have changed
* and update the function signature data types
* @throws DuplicateNameException
* @throws DataTypeDependencyException
*/
private void updateVfunctionDataTypes(Data structureAtAddress, Structure vfunctionStructure,
Address vftableAddress) throws DuplicateNameException, DataTypeDependencyException {
DataTypeManager dtMan = currentProgram.getDataTypeManager();
int numVfunctions = structureAtAddress.getNumComponents();
for (int i = 0; i < numVfunctions; i++) {
Data dataComponent = structureAtAddress.getComponent(i);
Reference[] referencesFrom = dataComponent.getReferencesFrom();
if (referencesFrom.length != 1) {
continue;
}
Address functionAddress = referencesFrom[0].getToAddress();
Function vfunction = getFunctionAt(functionAddress);
if (vfunction == null) {
continue;
}
FunctionDefinitionDataType functionSignatureDataType =
(FunctionDefinitionDataType) vfunction.getSignature();
DataTypeComponent structureComponent = vfunctionStructure.getComponent(i);
DataType componentDataType = structureComponent.getDataType();
if (!(componentDataType instanceof Pointer)) {
continue;
}
Pointer pointer = (Pointer) componentDataType;
DataType pointedToDataType = pointer.getDataType();
if (functionSignatureDataType.equals(pointedToDataType)) {
continue;
}
// update data type with new new signature
dtMan.replaceDataType(pointedToDataType, functionSignatureDataType, true);
if (!structureComponent.getFieldName().equals(vfunction.getName())) {
structureComponent.setFieldName(vfunction.getName());
}
}
}
private Category getDataTypeCategory(DataType dataType) {
DataTypeManager dataTypeManager = currentProgram.getDataTypeManager();
CategoryPath originalPath = dataType.getCategoryPath();
Category category = dataTypeManager.getCategory(originalPath);
return category;
}
private List<Symbol> getClassVftableSymbols(Namespace classNamespace)
throws CancelledException {
SymbolTable symbolTable = currentProgram.getSymbolTable();
List<Symbol> vftableSymbols = new ArrayList<Symbol>();
SymbolIterator symbols = symbolTable.getSymbols(classNamespace);
while (symbols.hasNext()) {
monitor.checkCanceled();
Symbol symbol = symbols.next();
if (symbol.getName().equals("vftable") ||
symbol.getName().substring(1).startsWith("vftable")) {
vftableSymbols.add(symbol);
}
}
return vftableSymbols;
}
}

View file

@ -0,0 +1,103 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Script to apply any changes the user has made to class virtual function definitions, ie ones they
// have edited in the data type manager. To run the script, put the cursor on any member of the
// desired class in the listing then run the script. For each function definition in the given class
// that differs from the associated function signature in the listing, the script will update the
// listing function signatures of any related virtual vunctions (ie parents and children) and update
// related data types such as function definitions of the given class and related classes and also
// field names in related vftable structures.
// Note: The script will not work if the vftable structures were not originally applied to
// the vftables using the RecoverClassesFromRTTIScript.
// At some point, the Ghidra API will be updated to do this automatically instead of needing the script to do so.
//@category C++
import java.util.List;
import classrecovery.RecoveredClassUtils;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.data.FunctionDefinition;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.Function;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Symbol;
public class ApplyClassFunctionDefinitionUpdatesScript extends GhidraScript {
@Override
public void run() throws Exception {
if (currentProgram == null) {
println("There is no open program");
return;
}
RecoveredClassUtils classUtils = new RecoveredClassUtils(currentProgram, currentLocation,
state.getTool(), this, false, false, false, monitor);
Namespace classNamespace = classUtils.getClassNamespace(currentAddress);
if (classNamespace == null) {
println(
"Either cannot retrieve class namespace or cursor is not in a member of a class namepace");
return;
}
List<Symbol> classVftableSymbols = classUtils.getClassVftableSymbols(classNamespace);
if (classVftableSymbols.isEmpty()) {
println("There are no vftables in this class");
return;
}
println(
"Applying differing function definitions for class " + classNamespace.getName(true));
List<Object> changedItems =
classUtils.applyNewFunctionDefinitions(classNamespace, classVftableSymbols);
if (changedItems.isEmpty()) {
println("No differences found for class " + classNamespace.getName(true) +
" between the vftable listing function signatures and their associated data type manager function definition data types");
return;
}
List<Structure> structuresOnList = classUtils.getStructuresOnList(changedItems);
List<FunctionDefinition> functionDefinitionsOnList =
classUtils.getFunctionDefinitionsOnList(changedItems);
List<Function> functionsOnList = classUtils.getFunctionsOnList(changedItems);
println();
println("Updated structures:");
for (Structure structure : structuresOnList) {
monitor.checkCanceled();
println(structure.getPathName());
}
println();
println("Updated function definitions:");
for (FunctionDefinition functionDef : functionDefinitionsOnList) {
monitor.checkCanceled();
println(functionDef.getPathName());
}
println();
println("Updated functions:");
for (Function function : functionsOnList) {
monitor.checkCanceled();
println(function.getEntryPoint().toString());
}
}
}

View file

@ -0,0 +1,103 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Script to apply any changes the user has made to class virtual function signatures, ie ones they
// have edited in the listing. To run the script, put the cursor on any member of the desired class in
// the listing then run the script. For each function signature in the given class that differs from
// the associated function definition in the data type manager, the script will update the listing
// function signatures of any related virtual vunctions (ie parents and children) and update related
// data types such as function definitions of the given class and related classes and also field names
// in related vftable structures.
// Note: The script will not work if the vftable structures were not originally applied to
// the vftables using the RecoverClassesFromRTTIScript.
// At some point, the Ghidra API will be updated to do this automatically instead of needing the script to do so.
//@category C++
import java.util.List;
import classrecovery.RecoveredClassUtils;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.data.FunctionDefinition;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.Function;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.Symbol;
public class ApplyClassFunctionSignatureUpdatesScript extends GhidraScript {
@Override
public void run() throws Exception {
if (currentProgram == null) {
println("There is no open program");
return;
}
RecoveredClassUtils classUtils = new RecoveredClassUtils(currentProgram, currentLocation,
state.getTool(), this, false, false, false, monitor);
Namespace classNamespace = classUtils.getClassNamespace(currentAddress);
if (classNamespace == null) {
println(
"Either cannot retrieve class namespace or cursor is not in a member of a class namepace");
return;
}
List<Symbol> classVftableSymbols = classUtils.getClassVftableSymbols(classNamespace);
if (classVftableSymbols.isEmpty()) {
println("There are no vftables in this class");
return;
}
println("Applying differing virtual function signatures for class " +
classNamespace.getName(true));
List<Object> changedItems =
classUtils.applyNewFunctionSignatures(classNamespace, classVftableSymbols);
if (changedItems.isEmpty()) {
println("No differences found for class " + classNamespace.getName(true) +
" between the listing virtual function signatures and their associated data type manager function definition data types.");
return;
}
List<Structure> structuresOnList = classUtils.getStructuresOnList(changedItems);
List<FunctionDefinition> functionDefinitionsOnList =
classUtils.getFunctionDefinitionsOnList(changedItems);
List<Function> functionsOnList = classUtils.getFunctionsOnList(changedItems);
println();
println("Updated structures:");
for (Structure structure : structuresOnList) {
monitor.checkCanceled();
println(structure.getPathName());
}
println();
println("Updated function definitions:");
for (FunctionDefinition functionDef : functionDefinitionsOnList) {
monitor.checkCanceled();
println(functionDef.getPathName());
}
println();
println("Updated functions:");
for (Function function : functionsOnList) {
monitor.checkCanceled();
println(function.getEntryPoint().toString());
}
}
}

View file

@ -37,6 +37,17 @@
// NOTE: Windows class recovery is more complete and tested than gcc class recovery, which is still // NOTE: Windows class recovery is more complete and tested than gcc class recovery, which is still
// in early stages of development. Gcc class data types have not been recovered yet but if the program // in early stages of development. Gcc class data types have not been recovered yet but if the program
// has DWARF, there will be some amount of data recovered by the DWARF analyzer in the DWARF data folder. // has DWARF, there will be some amount of data recovered by the DWARF analyzer in the DWARF data folder.
// NOTE: For likely the best results, run this script on freshly analyzed programs. No testing has been
// done on user marked-up programs.
// NOTE: After running this script if you edit function signatures in the listing for a particular
// class and wish to update the corresponding class data (function definition data types, vftable
// structure field names, ...) then you can run the ApplyClassFunctionSignatureUpdatesScript.java
// to have it do so for you.
// Conversely, if you update a particular class's function definitions in the data type manager and
// wish to have related function signatures in the listing updated, as well as other data types that
// are related, then run the ApplyClassFunctionDefinitionsUpdatesScript.java to do so. At some point,
// the Ghidra API will be updated to do this all automatically instead of needing the scripts to do so.
//@category C++ //@category C++
import java.io.File; import java.io.File;
@ -124,8 +135,6 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
int defaultPointerSize; int defaultPointerSize;
RecoveredClassUtils classUtils;
RTTIClassRecoverer recoverClassesFromRTTI; RTTIClassRecoverer recoverClassesFromRTTI;
ExtraScriptUtils extraUtils; ExtraScriptUtils extraUtils;
@ -143,25 +152,18 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
if (isWindows()) { if (isWindows()) {
// TODO: check for typeinfo using the other way i had then pull the hasRTTI in and if first
// is true and second isn't then run the analyzer - move all this into a method
isPDBLoaded = isPDBLoadedInProgram(); isPDBLoaded = isPDBLoadedInProgram();
nameVfunctions = !isPDBLoaded; nameVfunctions = !isPDBLoaded;
recoverClassesFromRTTI = new RTTIWindowsClassRecoverer(currentProgram, recoverClassesFromRTTI = new RTTIWindowsClassRecoverer(currentProgram,
currentLocation, state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS, currentLocation, state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS,
USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, isPDBLoaded, monitor); USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, isPDBLoaded, monitor);
} }
else if (isGcc()) { else if (isGcc()) {
// for now assume gcc has named vfunctions until a way to check is developed
// for now assume gcc has named vfunctions
nameVfunctions = true; nameVfunctions = true;
recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, currentLocation, recoverClassesFromRTTI = new RTTIGccClassRecoverer(currentProgram, currentLocation,
state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS, state.getTool(), this, BOOKMARK_FOUND_FUNCTIONS,
USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, monitor); USE_SHORT_TEMPLATE_NAMES_IN_STRUCTURE_FIELDS, nameVfunctions, monitor);
} }
else { else {
println("This script will not work on this program type"); println("This script will not work on this program type");
@ -185,9 +187,6 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
return; return;
} }
decompilerUtils = recoverClassesFromRTTI.getDecompilerUtils(); decompilerUtils = recoverClassesFromRTTI.getDecompilerUtils();
DecompInterface decompInterface = decompilerUtils.getDecompilerInterface(); DecompInterface decompInterface = decompilerUtils.getDecompilerInterface();
@ -962,12 +961,12 @@ public class RecoverClassesFromRTTIScript extends GhidraScript {
println("Total number of indetermined constructor/destructors: " + println("Total number of indetermined constructor/destructors: " +
remainingIndeterminates.size()); remainingIndeterminates.size());
//TODO: need to get from the new class println("Total fixed incorrect FID functions: " +
// println("Total fixed incorrect FID functions: " + badFIDFunctions.size()); recoverClassesFromRTTI.getBadFIDFunctions().size());
// println("Total resolved functions that had multiple FID possiblities: " + println("Total resolved functions that had multiple FID possiblities: " +
// resolvedFIDFunctions.size()); recoverClassesFromRTTI.getResolvedFIDFunctions().size());
// println("Total fixed functions that had incorrect data types due to incorrect FID: " + println("Total fixed functions that had incorrect data types due to incorrect FID: " +
// fixedFIDFunctions.size()); recoverClassesFromRTTI.getFixedFIDFunctions().size());
} }

View file

@ -29,7 +29,7 @@ package classrecovery;
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
//DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS. //DO NOT RUN. THIS IS NOT A SCRIPT! THIS IS A CLASS THAT IS USED BY SCRIPTS.
import java.util.*; import java.util.*;
import ghidra.app.cmd.function.CreateFunctionCmd; import ghidra.app.cmd.function.CreateFunctionCmd;
@ -506,20 +506,19 @@ public class ExtraScriptUtils extends FlatProgramAPI {
* Method to create a function in the given program at the given address * Method to create a function in the given program at the given address
* @param prog the given program * @param prog the given program
* @param addr the given address * @param addr the given address
* @param tMonitor a cancellable task monitor
* @return true if the function was created, false otherwise * @return true if the function was created, false otherwise
*/ */
public boolean createFunction(Program prog, Address addr, TaskMonitor tMonitor) { public boolean createFunction(Program prog, Address addr) {
try { try {
AddressSet subroutineAddresses = getSubroutineAddresses(prog, addr, tMonitor); AddressSet subroutineAddresses = getSubroutineAddresses(prog, addr);
if (subroutineAddresses.isEmpty()) { if (subroutineAddresses.isEmpty()) {
return false; return false;
} }
CreateFunctionCmd cmd = new CreateFunctionCmd(null, subroutineAddresses.getMinAddress(), CreateFunctionCmd cmd = new CreateFunctionCmd(null, subroutineAddresses.getMinAddress(),
null, SourceType.DEFAULT); null, SourceType.DEFAULT);
if (cmd.applyTo(prog, tMonitor)) { if (cmd.applyTo(prog, monitor)) {
return true; return true;
} }
@ -535,12 +534,11 @@ public class ExtraScriptUtils extends FlatProgramAPI {
* Method to figure out a subroutine address set given an address contained in it * Method to figure out a subroutine address set given an address contained in it
* @param program the given program * @param program the given program
* @param address address in the potential subroutine * @param address address in the potential subroutine
* @param monitor allows canceling
* @return address set of the subroutine to be created * @return address set of the subroutine to be created
* @throws CancelledException if cancelled * @throws CancelledException if cancelled
*/ */
public static AddressSet getSubroutineAddresses(Program program, Address address, public AddressSet getSubroutineAddresses(Program program, Address address)
TaskMonitor monitor) throws CancelledException { throws CancelledException {
// Create a new address set to hold the entire selection. // Create a new address set to hold the entire selection.
AddressSet subroutineAddresses = new AddressSet(); AddressSet subroutineAddresses = new AddressSet();