mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch
'origin/GP-339_ghidra007_RecoverClassesFromRTTI--SQUASHED'
This commit is contained in:
commit
8d9f4b5257
16 changed files with 18037 additions and 0 deletions
1279
Ghidra/Features/Base/ghidra_scripts/GccRttiAnalysisScript.java
Normal file
1279
Ghidra/Features/Base/ghidra_scripts/GccRttiAnalysisScript.java
Normal file
File diff suppressed because it is too large
Load diff
201
Ghidra/Features/Base/ghidra_scripts/GraphClassesScript.java
Normal file
201
Ghidra/Features/Base/ghidra_scripts/GraphClassesScript.java
Normal file
|
@ -0,0 +1,201 @@
|
|||
/* ###
|
||||
* 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 graph class hierarchies given metadata found in class structure description that
|
||||
// was applied using the ExtractClassInfoFromRTTIScript.
|
||||
//@category C++
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.services.GraphDisplayBroker;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.service.graph.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class GraphClassesScript extends GhidraScript {
|
||||
|
||||
List<Structure> classStructures = new ArrayList<Structure>();
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
if (currentProgram == null) {
|
||||
println("There is no open program");
|
||||
return;
|
||||
}
|
||||
|
||||
DataTypeManager dataTypeManager = currentProgram.getDataTypeManager();
|
||||
|
||||
String path = new String("ClassDataTypes");
|
||||
CategoryPath dataTypePath = new CategoryPath(CategoryPath.ROOT, path);
|
||||
|
||||
Category category = dataTypeManager.getCategory(dataTypePath);
|
||||
if (category == null) {
|
||||
println(
|
||||
"/ClassDataTypes folder does not exist so there is no class data to process. Please run the ExtractClassInfoFromRTTIScript to generate the necessary information needed to run this script.");
|
||||
return;
|
||||
}
|
||||
|
||||
Category[] subCategories = category.getCategories();
|
||||
|
||||
getClassStructures(subCategories);
|
||||
|
||||
AttributedGraph graph = createGraph();
|
||||
if (graph.getVertexCount() == 0) {
|
||||
println(
|
||||
"There was no metadata in the class structures so a graph could not be created. Please run the ExtractClassInfoFromRTTIScript to generate the necessary information needed to run this script.");
|
||||
}
|
||||
else {
|
||||
showGraph(graph);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void getClassStructures(Category[] categories) throws CancelledException {
|
||||
|
||||
for (Category category : categories) {
|
||||
monitor.checkCanceled();
|
||||
DataType[] dataTypes = category.getDataTypes();
|
||||
for (DataType dataType : dataTypes) {
|
||||
monitor.checkCanceled();
|
||||
if (dataType.getName().equals(category.getName()) &&
|
||||
dataType instanceof Structure) {
|
||||
|
||||
// if the data type name is the same as the folder name then
|
||||
// it is the main class structure
|
||||
Structure classStructure = (Structure) dataType;
|
||||
if (!classStructures.contains(classStructure)) {
|
||||
classStructures.add(classStructure);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Category[] subcategories = category.getCategories();
|
||||
|
||||
if (subcategories.length > 0) {
|
||||
getClassStructures(subcategories);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AttributedGraph createGraph() throws CancelledException {
|
||||
|
||||
AttributedGraph g = new AttributedGraph();
|
||||
|
||||
Iterator<Structure> classStructuresIterator = classStructures.iterator();
|
||||
while (classStructuresIterator.hasNext()) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
|
||||
Structure classStructure = classStructuresIterator.next();
|
||||
|
||||
String description = classStructure.getDescription();
|
||||
String mainClassName = getClassName(description);
|
||||
|
||||
if (mainClassName == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AttributedVertex classVertex = g.addVertex(mainClassName);
|
||||
|
||||
int numParents = 0;
|
||||
while (description.contains(":")) {
|
||||
|
||||
numParents++;
|
||||
|
||||
int indexOfColon = description.indexOf(":", 0);
|
||||
|
||||
description = description.substring(indexOfColon + 1);
|
||||
|
||||
int endOfBlock = description.indexOf(":", 0);
|
||||
if (endOfBlock == -1) {
|
||||
endOfBlock = description.length();
|
||||
}
|
||||
|
||||
String parentName = description.substring(0, endOfBlock);
|
||||
|
||||
description = description.substring(endOfBlock);
|
||||
|
||||
boolean isVirtualParent = false;
|
||||
if (parentName.contains("virtual")) {
|
||||
isVirtualParent = true;
|
||||
}
|
||||
|
||||
parentName = parentName.replace("virtual", "");
|
||||
parentName = parentName.replace(" ", "");
|
||||
|
||||
|
||||
AttributedVertex parentVertex = g.addVertex(parentName);
|
||||
|
||||
AttributedEdge edge = g.addEdge(parentVertex, classVertex);
|
||||
if (isVirtualParent) {
|
||||
edge.setAttribute("Color", "Orange");
|
||||
}
|
||||
// else leave it default lime green
|
||||
}
|
||||
|
||||
// no parent = blue vertex
|
||||
if (numParents == 0) {
|
||||
classVertex.setAttribute("Color", "Blue");
|
||||
}
|
||||
// single parent = green vertex
|
||||
else if (numParents == 1) {
|
||||
classVertex.setAttribute("Color", "Green");
|
||||
}
|
||||
// multiple parents = red vertex
|
||||
else {
|
||||
classVertex.setAttribute("Color", "Red");
|
||||
}
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
private void showGraph(AttributedGraph graph) throws Exception {
|
||||
|
||||
GraphDisplay display;
|
||||
PluginTool tool = state.getTool();
|
||||
GraphDisplayBroker broker = tool.getService(GraphDisplayBroker.class);
|
||||
GraphDisplayProvider service = broker.getGraphDisplayProvider("Default Graph Display");
|
||||
display = service.getGraphDisplay(false, TaskMonitor.DUMMY);
|
||||
display.setGraph(graph, "test graph", false, TaskMonitor.DUMMY);
|
||||
}
|
||||
|
||||
private String getClassName(String description) {
|
||||
|
||||
// parse description for class hierarchy
|
||||
if (!description.startsWith("class")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// skip "class " to get overall class
|
||||
description = description.substring(6);
|
||||
int indexOfColon = description.indexOf(":", 0);
|
||||
String mainClassName;
|
||||
if (indexOfColon == -1) {
|
||||
mainClassName = description;
|
||||
}
|
||||
else {
|
||||
mainClassName = description.substring(0, indexOfColon - 1);
|
||||
}
|
||||
mainClassName = mainClassName.replace(" ", "");
|
||||
|
||||
return mainClassName;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
|
||||
public class SearchForImageBaseOffsets extends GhidraScript {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
if (currentProgram == null) {
|
||||
println("No open program");
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentProgram.getMemory().isBigEndian()) {
|
||||
println("This script only looks for little endian image base offsets");
|
||||
return;
|
||||
}
|
||||
|
||||
Address imageBase = currentProgram.getImageBase();
|
||||
|
||||
long currentAddressOffset = currentAddress.getOffset();
|
||||
long imageBaseOffset = imageBase.getOffset();
|
||||
|
||||
long currentAddressIbo = imageBaseOffset ^ currentAddressOffset;
|
||||
|
||||
byte searchBytes[] = createLittleEndianByteArray(currentAddressIbo, 8);
|
||||
println("searching for possible ibo64 references to " + currentAddress.toString() + " ...");
|
||||
searchForByteArray(searchBytes);
|
||||
|
||||
searchBytes = createLittleEndianByteArray(currentAddressIbo, 4);
|
||||
println("searching for possible ibo32 references to " + currentAddress.toString() + " ...");
|
||||
searchForByteArray(searchBytes);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create a byte array out of the given long value
|
||||
* @param value the given value
|
||||
* @param numBytes the number of bytes from the low end of the value to copy into the array
|
||||
* @return the little endian byte array for the given value
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
private byte[] createLittleEndianByteArray(long value, int numBytes)
|
||||
throws CancelledException {
|
||||
|
||||
|
||||
byte byteArray[] = new byte[numBytes];
|
||||
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
monitor.checkCanceled();
|
||||
byteArray[i] = (byte) (value >> (8 * i) & 0xff);
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
private void searchForByteArray(byte[] byteArray) throws CancelledException {
|
||||
Address start = currentProgram.getMinAddress();
|
||||
Address found = find(start, byteArray);
|
||||
while (found != null) {
|
||||
monitor.checkCanceled();
|
||||
println(found.toString());
|
||||
start = found.add(1);
|
||||
found = find(start, byteArray);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
/* ###
|
||||
* 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
import ghidra.app.decompiler.*;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.OptionsService;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.data.ParameterDefinition;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.pcode.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class DecompilerScriptUtils {
|
||||
|
||||
private Program program;
|
||||
private PluginTool tool;
|
||||
private TaskMonitor monitor;
|
||||
|
||||
private DecompInterface decompInterface;
|
||||
|
||||
DecompilerScriptUtils(Program program, PluginTool tool, TaskMonitor monitor) {
|
||||
this.program = program;
|
||||
this.monitor = monitor;
|
||||
this.tool = tool;
|
||||
|
||||
decompInterface = setupDecompilerInterface();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to setup the decompiler interface for the given program
|
||||
* @return the interface to the decompiler
|
||||
*/
|
||||
public DecompInterface setupDecompilerInterface() {
|
||||
|
||||
decompInterface = new DecompInterface();
|
||||
|
||||
DecompileOptions options;
|
||||
options = new DecompileOptions();
|
||||
OptionsService service = tool.getService(OptionsService.class);
|
||||
if (service != null) {
|
||||
ToolOptions opt = service.getOptions("Decompiler");
|
||||
options.grabFromToolAndProgram(null, opt, program);
|
||||
}
|
||||
decompInterface.setOptions(options);
|
||||
|
||||
decompInterface.toggleCCode(true);
|
||||
decompInterface.toggleSyntaxTree(true);
|
||||
decompInterface.setSimplificationStyle("decompile");
|
||||
|
||||
if (!decompInterface.openProgram(program)) {
|
||||
return null;
|
||||
}
|
||||
return decompInterface;
|
||||
|
||||
}
|
||||
|
||||
public DecompInterface getDecompilerInterface() {
|
||||
return decompInterface;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to decompile the given function and return the function's HighFunction
|
||||
* @param function the given function
|
||||
* @return the HighFunction for the given function or null if there are issues decompiling the function
|
||||
*/
|
||||
public HighFunction getHighFunction(Function function) {
|
||||
|
||||
DecompileResults res = decompInterface.decompileFunction(function,
|
||||
decompInterface.getOptions().getDefaultTimeout(), null);
|
||||
|
||||
decompInterface.flushCache();
|
||||
return res.getHighFunction();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get the decompiler version of the given function's return type (which is not always
|
||||
* the same as the listing version)
|
||||
* @param function the given function
|
||||
* @return the decompiler version of the given function's return type (which is not always the
|
||||
* same as the listing version)
|
||||
*/
|
||||
public DataType getDecompilerReturnType(Function function) {
|
||||
|
||||
DecompileResults decompRes = decompInterface.decompileFunction(function,
|
||||
decompInterface.getOptions().getDefaultTimeout(), monitor);
|
||||
|
||||
//If can't decompile, return null
|
||||
if (decompRes == null || decompRes.getHighFunction() == null ||
|
||||
decompRes.getHighFunction().getFunctionPrototype() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return decompRes.getHighFunction().getFunctionPrototype().getReturnType();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to retrieve the function signature string from the decompiler function prototype. NOTE:
|
||||
* if there is a this param, it will not be included.
|
||||
* @param function the given function
|
||||
* @param includeReturn if true, include the return type in the signature string
|
||||
* @return the function signature string
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public String getFunctionSignatureString(Function function, boolean includeReturn)
|
||||
throws CancelledException {
|
||||
|
||||
if (function == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
|
||||
DecompileResults decompRes = decompInterface.decompileFunction(function,
|
||||
decompInterface.getOptions().getDefaultTimeout(), monitor);
|
||||
|
||||
//If can't decompile, show the listing version of the function signature
|
||||
if (decompRes == null || decompRes.getHighFunction() == null ||
|
||||
decompRes.getHighFunction().getFunctionPrototype() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
HighFunction highFunction = decompRes.getHighFunction();
|
||||
|
||||
FunctionPrototype functionPrototype = highFunction.getFunctionPrototype();
|
||||
|
||||
if (includeReturn) {
|
||||
stringBuffer.append(functionPrototype.getReturnType().getDisplayName() + " ");
|
||||
}
|
||||
|
||||
stringBuffer.append(function.getName() + "(");
|
||||
ParameterDefinition[] parameterDefinitions = functionPrototype.getParameterDefinitions();
|
||||
|
||||
if (parameterDefinitions == null) {
|
||||
stringBuffer.append(");");
|
||||
}
|
||||
else {
|
||||
int paramCount = parameterDefinitions.length;
|
||||
for (int i = 0; i < parameterDefinitions.length; i++) {
|
||||
monitor.checkCanceled();
|
||||
ParameterDefinition param = parameterDefinitions[i];
|
||||
|
||||
if (param.getName().equals("this")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
stringBuffer.append(param.getDataType().getDisplayName() + " " + param.getName());
|
||||
|
||||
if (i == paramCount) {
|
||||
stringBuffer.append(");");
|
||||
}
|
||||
else {
|
||||
stringBuffer.append(", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameters from the decompiler for the given function
|
||||
* @param function the given function
|
||||
* @return the decompiler parameters for the given function
|
||||
*/
|
||||
public ParameterDefinition[] getParametersFromDecompiler(Function function) {
|
||||
|
||||
DecompileResults decompRes = decompInterface.decompileFunction(function,
|
||||
decompInterface.getOptions().getDefaultTimeout(), monitor);
|
||||
|
||||
if (decompRes == null || decompRes.getHighFunction() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return decompRes.getHighFunction().getFunctionPrototype().getParameterDefinitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to dispose the decompiler interface
|
||||
*/
|
||||
public void disposeDecompilerInterface() {
|
||||
decompInterface.closeProgram();
|
||||
decompInterface.dispose();
|
||||
}
|
||||
|
||||
public Address getAssignedAddressFromPcode(Varnode storedValue) {
|
||||
|
||||
long addressOffset;
|
||||
if (storedValue.isConstant()) {
|
||||
addressOffset = storedValue.getOffset();
|
||||
Address possibleAddress = toAddr(addressOffset);
|
||||
if (possibleAddress == null || !program.getMemory().contains(possibleAddress)) {
|
||||
return null;
|
||||
}
|
||||
return possibleAddress;
|
||||
}
|
||||
|
||||
PcodeOp valuePcodeOp = storedValue.getDef();
|
||||
|
||||
if (valuePcodeOp == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (valuePcodeOp.getOpcode() == PcodeOp.CAST || valuePcodeOp.getOpcode() == PcodeOp.COPY) {
|
||||
|
||||
Varnode constantVarnode = valuePcodeOp.getInput(0);
|
||||
return getAssignedAddressFromPcode(constantVarnode);
|
||||
|
||||
}
|
||||
else if (valuePcodeOp.getOpcode() != PcodeOp.PTRSUB) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// don't need to check isConst bc always is
|
||||
Varnode constantVarnode = valuePcodeOp.getInput(1);
|
||||
addressOffset = constantVarnode.getOffset();
|
||||
Address possibleAddress = toAddr(addressOffset);
|
||||
if (possibleAddress == null || !program.getMemory().contains(possibleAddress)) {
|
||||
return null;
|
||||
}
|
||||
return possibleAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get the called address from the given CALL pcodeOp's input Varnode
|
||||
* @param pcodeOpInput the Varnode from a CALL pcodeOp input
|
||||
* @return the calledAddress
|
||||
*/
|
||||
public Address getCalledAddressFromCallingPcodeOp(Varnode pcodeOpInput) {
|
||||
|
||||
PcodeOp def = pcodeOpInput.getDef();
|
||||
if (def == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Varnode defInput = def.getInput(1);
|
||||
if (defInput == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Address defInputAddress = defInput.getAddress();
|
||||
if (defInputAddress == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long offset = defInputAddress.getOffset();
|
||||
|
||||
Address calledAddress = program.getMinAddress().getNewAddress(offset);
|
||||
|
||||
return calledAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new address with the specified offset in the default address space.
|
||||
* @param offset the offset for the new address
|
||||
* @return a new address with the specified offset in the default address space
|
||||
*/
|
||||
public final Address toAddr(long offset) {
|
||||
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,381 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class EditStructureUtils {
|
||||
|
||||
EditStructureUtils() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to determine if the given containing struct has components at the given offset
|
||||
* that are either the same, all undefined1's or equivalently sized undefineds as the components
|
||||
* in the given possible newInternalStruct
|
||||
* @param containingStruct the given structure to check
|
||||
* @param offset the offset at the containing struct to check for equivalent components
|
||||
* @param newInternalStruct the possible new internal struct to replace at the given offset in the
|
||||
* given containing struct
|
||||
* @param monitor task monitor
|
||||
* @return true if components in the containing struct are replaceable with the possible new
|
||||
* internal struct, false otherwise
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public boolean hasReplaceableComponentsAtOffset(Structure containingStruct, int offset,
|
||||
Structure newInternalStruct, TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
DataTypeComponent[] newStructComponents = newInternalStruct.getComponents();
|
||||
|
||||
for (DataTypeComponent newStructComponent : newStructComponents) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
|
||||
int structOffset = newStructComponent.getOffset();
|
||||
|
||||
DataTypeComponent currentComponentAtOffset =
|
||||
containingStruct.getComponentAt(offset + structOffset);
|
||||
|
||||
DataType newStructCompDt = newStructComponent.getDataType();
|
||||
DataType containingComDt = currentComponentAtOffset.getDataType();
|
||||
|
||||
// if component dts are equal continue
|
||||
if (newStructCompDt.equals(containingComDt)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if containing is all undefined1s at equivalent location then continue
|
||||
if (hasEnoughUndefined1sAtOffset(containingStruct, offset + structOffset,
|
||||
newStructCompDt.getLength(), monitor)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise if lengths not equal then return false
|
||||
if (newStructCompDt.getLength() != containingComDt.getLength()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// component lengths are equal if it gets here
|
||||
// if containing is not undefined then return false because the components would be
|
||||
// incompatible types
|
||||
if (!containingComDt.getName().startsWith("undefined")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to determine if there are at least the given length of undefined size 1 components at the given offset in the given structure
|
||||
* @param structure the given structure
|
||||
* @param offset the given offset
|
||||
* @param length the length of undefine size 1 components to check for starting at given offset
|
||||
* @param monitor task monitor
|
||||
* @return true if there are at least length undefined size 1 components at the given offset in the given structure
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public boolean hasEnoughUndefined1sAtOffset(Structure structure, int offset, int length,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
if (structure.getLength() < offset + length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = offset; i < offset + length; i++) {
|
||||
monitor.checkCanceled();
|
||||
DataTypeComponent component = structure.getComponentAt(i);
|
||||
if (component == null) {
|
||||
return false;
|
||||
}
|
||||
DataType dataType = component.getDataType();
|
||||
if (dataType.getName().equals("undefined") && dataType.getLength() == 1) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to check that all components starting with the one at the given offset and encompassing
|
||||
* all up to the given length from the given offset are an undefined data type. If so, clear them all and return true.
|
||||
* If not, do nothing and return false.
|
||||
* @param structure the given structure
|
||||
* @param offset the given offset in the structure
|
||||
* @param length the total length from the offset to hopefully clear
|
||||
* @param monitor task monitor
|
||||
* @return true if successfully cleared from offset to offset+length, false otherwise
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public boolean clearLengthAtOffset(Structure structure, int offset, int length,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
if (structure.getLength() < offset + length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Integer> offsetsToClear = new ArrayList<Integer>();
|
||||
|
||||
int endOfClear = offset + length;
|
||||
|
||||
while (offset < endOfClear) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
|
||||
DataTypeComponent component = structure.getComponentAt(offset);
|
||||
DataType dataType = component.getDataType();
|
||||
|
||||
// return false if it would clear too much
|
||||
if (offset + dataType.getLength() > endOfClear) {
|
||||
return false;
|
||||
}
|
||||
|
||||
offsetsToClear.add(offset);
|
||||
offset += dataType.getLength();
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (offsetsToClear.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Iterator<Integer> offsetIterator = offsetsToClear.iterator();
|
||||
while (offsetIterator.hasNext()) {
|
||||
Integer componentOffset = offsetIterator.next();
|
||||
// need to get ordinal from component using offset because after clearing
|
||||
// component size > 1, the index changes
|
||||
DataTypeComponent component = structure.getComponentAt(componentOffset);
|
||||
int index = component.getOrdinal();
|
||||
structure.clearComponent(index);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to determine if data type is an undefined size 1 data type
|
||||
* @param dataType the given data type
|
||||
* @return true if given data type is undefined size 1, false otherwise
|
||||
*/
|
||||
public boolean isUndefined1(DataType dataType) {
|
||||
|
||||
if (isUndefined(dataType) && dataType.getLength() == 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to determine if data type is an undefined data type of any size
|
||||
* @param dataType the given data type
|
||||
* @return true if given data type is undefined of any size, false otherwise
|
||||
*/
|
||||
public boolean isUndefined(DataType dataType) {
|
||||
if (dataType.getName().contains("undefined")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to determine if there are at least the given length of undefined (any size) components at the given offset in the given structure
|
||||
* @param structure the given structure
|
||||
* @param offset the given offset
|
||||
* @param length the total length of undefined components to check for starting at given offset
|
||||
* @param monitor task monitor
|
||||
* @return true if there are at least total length of undefined components at the given offset in the given structure
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public boolean hasEnoughUndefinedsOfAnyLengthAtOffset(Structure structure, int offset,
|
||||
int length, TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
if (structure.getLength() < offset + length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int endOfRange = offset + length;
|
||||
|
||||
while (offset < endOfRange) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
|
||||
DataTypeComponent component = structure.getComponentAt(offset);
|
||||
DataType dataType = component.getDataType();
|
||||
if (isUndefined(dataType)) {
|
||||
|
||||
offset += dataType.getLength();
|
||||
if (offset > endOfRange) {
|
||||
return false;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to add a field to the given structure. If the structure already has data
|
||||
* at the given offset, don't replace. If there is undefined data there then replace
|
||||
* it with the data type. If the structure empty, insert the data type at the given offset.
|
||||
* If the structure is not big enough and not-empty, grow it so there is room to replace.
|
||||
* @param structure the given structure
|
||||
* @param offset the offset to add a field
|
||||
* @param dataType the data type to add to the field at the given offset
|
||||
* @param fieldName the name of field
|
||||
* @param monitor task monitor
|
||||
* @return the updated structure data type
|
||||
* @throws IllegalArgumentException if issue inserting data type into structure
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public Structure addDataTypeToStructure(Structure structure, int offset,
|
||||
DataType dataType, String fieldName, TaskMonitor monitor)
|
||||
throws CancelledException, IllegalArgumentException {
|
||||
|
||||
int dataTypeLength = dataType.getLength();
|
||||
|
||||
int endOfDataTypeInStruct = offset + dataTypeLength;
|
||||
|
||||
int roomForData = structure.getLength() - endOfDataTypeInStruct;
|
||||
|
||||
// if structure isn't defined insert
|
||||
if (structure.isNotYetDefined()) {
|
||||
structure.insertAtOffset(offset, dataType, dataTypeLength, fieldName, null);
|
||||
return structure;
|
||||
}
|
||||
|
||||
// if not enough room, grow the structure
|
||||
if (roomForData < 0) {
|
||||
structure.growStructure(0 - roomForData);
|
||||
}
|
||||
|
||||
// else replace only if data already there are enough undefined data types at
|
||||
// that offset to fit the new data type
|
||||
if (hasEnoughUndefined1sAtOffset(structure, offset, dataTypeLength, monitor)) {
|
||||
structure.replaceAtOffset(offset, dataType, dataTypeLength, fieldName, null);
|
||||
}
|
||||
|
||||
return structure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to determine if the given structure has room at the given offset to have a component of the given length added to it
|
||||
* @param structureDataType the given structure
|
||||
* @param offset the offset to check for available room
|
||||
* @param lengthToAdd the length of bytes wanted to add at the offset
|
||||
* @param monitor task monitor
|
||||
* @return true if the given structure has room at the given offset to have a component of the given length added to it
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public boolean canAdd(Structure structureDataType, int offset, int lengthToAdd,
|
||||
TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
||||
// not big enough so return true so it can be grown
|
||||
DataTypeComponent component = structureDataType.getComponentAt(offset);
|
||||
if (component == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// no matter what size, if the data type at the offset is defined, return false
|
||||
// so it is not replaced
|
||||
if (!component.getDataType().getName().equals("undefined")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if structure isn't big enough but what is there is all undefined
|
||||
// return true to grow it
|
||||
int structLen = structureDataType.getLength();
|
||||
int spaceAvailable = structLen - (offset + lengthToAdd);
|
||||
|
||||
if (spaceAvailable < 0) {
|
||||
int overflow = 0 - spaceAvailable;
|
||||
return hasEnoughUndefined1sAtOffset(structureDataType, offset, structLen - overflow,
|
||||
monitor);
|
||||
}
|
||||
|
||||
// if structure is big enough and there is room at the offset return true
|
||||
return hasEnoughUndefined1sAtOffset(structureDataType, offset, lengthToAdd, monitor);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to retrieve the number of undefined size 1 components in the given structure before the given offset
|
||||
* @param structure the given structure
|
||||
* @param offset the given offset
|
||||
* @param monitor task monitor
|
||||
* @return the number of undefined size 1 components in the given structure before the given offset
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public int getNumberOfUndefinedsBeforeOffset(Structure structure, int offset,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
int numUndefineds = 0;
|
||||
int index = offset - 1;
|
||||
|
||||
while (index >= 0) {
|
||||
monitor.checkCanceled();
|
||||
DataTypeComponent component = structure.getComponentAt(index);
|
||||
if (component.getDataType().getName().equals("undefined") &&
|
||||
component.getLength() == 1) {
|
||||
index--;
|
||||
numUndefineds++;
|
||||
}
|
||||
else {
|
||||
return numUndefineds;
|
||||
}
|
||||
}
|
||||
return numUndefineds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to retrieve the number of undefined size 1 components starting at the given offset in the given structure
|
||||
* @param structure the given structure
|
||||
* @param offset the given offset
|
||||
* @param monitor task monitor
|
||||
* @return the number of undefined size 1 components starting at the given offset in the given structure
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
public int getNumberOfUndefinedsStartingAtOffset(Structure structure, int offset,
|
||||
TaskMonitor monitor) throws CancelledException {
|
||||
|
||||
int numUndefineds = 0;
|
||||
int index = offset;
|
||||
|
||||
while (index < structure.getLength()) {
|
||||
monitor.checkCanceled();
|
||||
DataTypeComponent component = structure.getComponentAt(index);
|
||||
if (component.getDataType().getName().equals("undefined") &&
|
||||
component.getLength() == 1) {
|
||||
index++;
|
||||
numUndefineds++;
|
||||
}
|
||||
else {
|
||||
return numUndefineds;
|
||||
}
|
||||
}
|
||||
return numUndefineds;
|
||||
}
|
||||
|
||||
|
||||
}
|
1376
Ghidra/Features/Decompiler/ghidra_scripts/ExtraScriptUtils.java
Normal file
1376
Ghidra/Features/Decompiler/ghidra_scripts/ExtraScriptUtils.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,292 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.NamespaceUtils;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.flatapi.FlatProgramAPI;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.program.model.symbol.SymbolType;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class RTTIClassRecoverer extends RecoveredClassUtils {
|
||||
|
||||
boolean programHasRTTIApplied = false;
|
||||
|
||||
String ghidraVersion;
|
||||
Program program;
|
||||
TaskMonitor monitor;
|
||||
|
||||
RTTIClassRecoverer(Program program, ProgramLocation location, PluginTool tool,
|
||||
FlatProgramAPI api, boolean createBookmarks, boolean useShortTemplates,
|
||||
boolean nameVfunctions,
|
||||
TaskMonitor monitor) {
|
||||
|
||||
super(program, location, tool, api, createBookmarks, useShortTemplates, nameVfunctions,
|
||||
monitor);
|
||||
|
||||
this.program = program;
|
||||
this.monitor = monitor;
|
||||
this.location = location;
|
||||
this.tool = tool;
|
||||
this.api = api;
|
||||
this.createBookmarks = createBookmarks;
|
||||
this.useShortTemplates = useShortTemplates;
|
||||
this.nameVfunctions = nameVfunctions;
|
||||
|
||||
ghidraVersion = getVersionOfGhidra();
|
||||
}
|
||||
|
||||
|
||||
public DecompilerScriptUtils getDecompilerUtils() {
|
||||
return decompilerUtils;
|
||||
}
|
||||
|
||||
public int getDefaultPointerSize() {
|
||||
return defaultPointerSize;
|
||||
}
|
||||
|
||||
public DataTypeManager getDataTypeManager() {
|
||||
return dataTypeManager;
|
||||
}
|
||||
|
||||
public boolean containsRTTI() throws CancelledException {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isValidProgramType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isValidProgramSize() {
|
||||
|
||||
if (defaultPointerSize != 4 && defaultPointerSize != 8) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the version of Ghidra that was used to analyze this program
|
||||
* @return a string containing the version number of Ghidra used to analyze the current program
|
||||
*/
|
||||
public String getVersionOfGhidra() {
|
||||
|
||||
Options options = program.getOptions("Program Information");
|
||||
Object ghidraVersionObject = options.getObject("Created With Ghidra Version", null);
|
||||
|
||||
return ghidraVersionObject.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void fixUpProgram() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
public List<RecoveredClass> createRecoveredClasses() {
|
||||
|
||||
return new ArrayList<RecoveredClass>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Method to promote the namespace is a class namespace.
|
||||
* @param vftableNamespace the namespace for the vftable
|
||||
* @return true if namespace is (now) a class namespace or false if it could not be promoted.
|
||||
*/
|
||||
public Namespace promoteToClassNamespace(Namespace vftableNamespace) {
|
||||
|
||||
try {
|
||||
Namespace newClass = NamespaceUtils.convertNamespaceToClass(vftableNamespace);
|
||||
|
||||
SymbolType symbolType = newClass.getSymbol().getSymbolType();
|
||||
if (symbolType == SymbolType.CLASS) {
|
||||
return newClass;
|
||||
}
|
||||
Msg.debug(this,
|
||||
"Could not promote " + vftableNamespace.getName() + " to a class namespace");
|
||||
return null;
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
|
||||
Msg.debug(this, "Could not promote " + vftableNamespace.getName() +
|
||||
" to a class namespace because " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to iterate over all the RecoveredClass objects and see if there is an existing class structure data type already
|
||||
* if so, add it to the RecoveredClass object
|
||||
* @param recoveredClasses List of RecoveredClass objects
|
||||
* @throws CancelledException when cancelled
|
||||
*/
|
||||
public void retrieveExistingClassStructures(List<RecoveredClass> recoveredClasses)
|
||||
throws CancelledException {
|
||||
|
||||
Iterator<RecoveredClass> recoveredClassIterator = recoveredClasses.iterator();
|
||||
while (recoveredClassIterator.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
RecoveredClass recoveredClass = recoveredClassIterator.next();
|
||||
|
||||
// if class is non-virtual have to search for an existing class datatype
|
||||
if (!recoveredClass.hasVftable()) {
|
||||
DataType[] possibleExistingClassStructures =
|
||||
extraUtils.getDataTypes(recoveredClass.getName());
|
||||
if (possibleExistingClassStructures.length == 0) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < possibleExistingClassStructures.length; i++) {
|
||||
monitor.checkCanceled();
|
||||
if (!(possibleExistingClassStructures[i] instanceof Structure)) {
|
||||
continue;
|
||||
}
|
||||
if (possibleExistingClassStructures[i].isNotYetDefined()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Structure existingClassStructure =
|
||||
(Structure) possibleExistingClassStructures[i];
|
||||
|
||||
recoveredClass.addExistingClassStructure(existingClassStructure);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//Iterate over constructor/destructor functions
|
||||
List<Function> constructorOrDestructorFunctions =
|
||||
recoveredClass.getConstructorOrDestructorFunctions();
|
||||
Iterator<Function> constDestIterator = constructorOrDestructorFunctions.iterator();
|
||||
while (constDestIterator.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
Function constDestFunction = constDestIterator.next();
|
||||
Namespace parentNamespace = constDestFunction.getParentNamespace();
|
||||
if (!parentNamespace.equals(recoveredClass.getClassNamespace())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (recoveredClass.hasExistingClassStructure()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int parameterCount = constDestFunction.getParameterCount();
|
||||
|
||||
if (parameterCount == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DataType dataType = constDestFunction.getParameter(0).getDataType();
|
||||
|
||||
CategoryPath dataTypePath = dataType.getDataTypePath().getCategoryPath();
|
||||
|
||||
if (!(dataType instanceof Pointer)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String dataTypeName = dataType.getName();
|
||||
dataTypeName = dataTypeName.replace(" *", "");
|
||||
|
||||
if (!dataTypeName.equals(recoveredClass.getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Structure existingClassStructure =
|
||||
(Structure) dataTypeManager.getDataType(dataTypePath, dataTypeName);
|
||||
|
||||
if (!existingClassStructure.isNotYetDefined()) {
|
||||
recoveredClass.addExistingClassStructure(existingClassStructure);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Method to get class data information from destructors if a class has no constructors
|
||||
* @param recoveredClasses list of classes
|
||||
* @throws CancelledException if cancelled
|
||||
* @throws InvalidInputException if issues setting function return
|
||||
* @throws DuplicateNameException if try to create same symbol name already in namespace
|
||||
* @throws CircularDependencyException if parent namespace is descendent of given namespace
|
||||
*/
|
||||
public void figureOutClassDataMembers(List<RecoveredClass> recoveredClasses)
|
||||
throws CancelledException, DuplicateNameException, InvalidInputException,
|
||||
CircularDependencyException {
|
||||
|
||||
Iterator<RecoveredClass> classIterator = recoveredClasses.iterator();
|
||||
while (classIterator.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
RecoveredClass recoveredClass = classIterator.next();
|
||||
|
||||
// we can only figure out structure info for functions with vftable since that is
|
||||
// what we use to determine which variable is being used to store the class structure
|
||||
if (!recoveredClass.hasVftable()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the class already has an existing class structure from pdb then no need to process
|
||||
if (recoveredClass.hasExistingClassStructure()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Function> memberFunctionsToProcess = new ArrayList<Function>();
|
||||
|
||||
memberFunctionsToProcess.addAll(recoveredClass.getConstructorList());
|
||||
memberFunctionsToProcess.addAll(recoveredClass.getDestructorList());
|
||||
memberFunctionsToProcess.addAll(recoveredClass.getIndeterminateList());
|
||||
|
||||
memberFunctionsToProcess.addAll(recoveredClass.getInlinedConstructorList());
|
||||
|
||||
Iterator<Function> memberFunctionIterator = memberFunctionsToProcess.iterator();
|
||||
while (memberFunctionIterator.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
Function memberFunction = memberFunctionIterator.next();
|
||||
|
||||
if (getVftableReferences(memberFunction) == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip if other classes contain this function as an inline inlined destructor or
|
||||
// inlined indeterminate
|
||||
if (isInlineDestructorOrIndeterminateInAnyClass(memberFunction)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
gatherClassMemberDataInfoForFunction(recoveredClass, memberFunction);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
1820
Ghidra/Features/Decompiler/ghidra_scripts/RTTIGccClassRecoverer.java
Normal file
1820
Ghidra/Features/Decompiler/ghidra_scripts/RTTIGccClassRecoverer.java
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
665
Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClass.java
Normal file
665
Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClass.java
Normal file
|
@ -0,0 +1,665 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.symbol.Namespace;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* The Class that describes object oriented classes that exist in the program
|
||||
*/
|
||||
public class RecoveredClass {
|
||||
|
||||
private String name;
|
||||
private CategoryPath classPath;
|
||||
private Namespace classNamespace;
|
||||
private DataTypeManager dataTypeManager;
|
||||
|
||||
private List<Address> vftableAddresses = new ArrayList<Address>();
|
||||
private List<Function> allClassVirtualFunctions = new ArrayList<Function>();
|
||||
|
||||
private boolean hasParentClass = false;
|
||||
private boolean hasChildClass = false;
|
||||
private boolean hasVftable = true;
|
||||
|
||||
private boolean hasSingleInheritance = false;
|
||||
private boolean hasMultipleInheritance = false;
|
||||
private boolean hasMultipleVirtualInheritance = false;
|
||||
|
||||
private List<Function> constructorAndDestructorList = new ArrayList<Function>();
|
||||
private List<Function> constructorList = new ArrayList<Function>();
|
||||
private List<Function> inlinedConstructorList = new ArrayList<Function>();
|
||||
private List<Function> destructorList = new ArrayList<Function>();
|
||||
private List<Function> inlinedDestructorList = new ArrayList<Function>();
|
||||
private List<Function> deletingDestructors = new ArrayList<Function>();
|
||||
private List<Function> nonThisDestructors = new ArrayList<Function>();
|
||||
private List<Function> cloneFunctions = new ArrayList<Function>();
|
||||
private List<Function> indeterminateList = new ArrayList<Function>();
|
||||
private List<Function> indeterminateInlineList = new ArrayList<Function>();
|
||||
|
||||
private Map<Address, List<Function>> vftableToVfunctionsMap =
|
||||
new HashMap<Address, List<Function>>();
|
||||
|
||||
private List<RecoveredClass> classHierarchy = new ArrayList<RecoveredClass>();
|
||||
private Map<RecoveredClass, List<RecoveredClass>> classHierarchyMap =
|
||||
new HashMap<RecoveredClass, List<RecoveredClass>>();
|
||||
|
||||
private Map<Address, RecoveredClass> vftableToBaseClassMap =
|
||||
new HashMap<Address, RecoveredClass>();
|
||||
private Map<Integer, Address> orderToVftableMap = new HashMap<Integer, Address>();
|
||||
|
||||
private Map<Integer, Address> classOffsetToVftableMap = new HashMap<Integer, Address>();
|
||||
|
||||
private List<RecoveredClass> parentList = new ArrayList<RecoveredClass>();
|
||||
private Map<RecoveredClass, Boolean> parentToBaseTypeMap =
|
||||
new HashMap<RecoveredClass, Boolean>();
|
||||
|
||||
private List<RecoveredClass> childClasses = new ArrayList<RecoveredClass>();
|
||||
|
||||
private Address vbtableAddress = null;
|
||||
private Structure vbtableStructure = null;
|
||||
private int vbtableOffset = NONE;
|
||||
|
||||
private boolean inheritsVirtualAncestor = false;
|
||||
private boolean isPublicClass = false;
|
||||
|
||||
private Structure existingClassStructure = null;
|
||||
private Structure computedClassStructure = null;
|
||||
private boolean hasExistingClassStructure = false;
|
||||
private Function vbaseDestructor = null;
|
||||
|
||||
private String shortenedTemplateName = new String();
|
||||
|
||||
private static final int NONE = -1;
|
||||
|
||||
TaskMonitor monitor = TaskMonitor.DUMMY;
|
||||
EditStructureUtils structUtils;
|
||||
|
||||
|
||||
RecoveredClass(String name, CategoryPath classPath, Namespace classNamespace,
|
||||
DataTypeManager dataTypeManager) {
|
||||
this.name = name;
|
||||
this.classPath = classPath;
|
||||
this.classNamespace = classNamespace;
|
||||
this.dataTypeManager = dataTypeManager;
|
||||
|
||||
this.structUtils = new EditStructureUtils();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<Function> getVirtualFunctions(Address vftableAddress) {
|
||||
return vftableToVfunctionsMap.get(vftableAddress);
|
||||
}
|
||||
|
||||
public List<Function> getAllVirtualFunctions() {
|
||||
return allClassVirtualFunctions;
|
||||
}
|
||||
|
||||
public CategoryPath getClassPath() {
|
||||
return classPath;
|
||||
}
|
||||
|
||||
public Namespace getClassNamespace() {
|
||||
return classNamespace;
|
||||
}
|
||||
|
||||
public void setHasParentClass(boolean bool) {
|
||||
hasParentClass = bool;
|
||||
return;
|
||||
}
|
||||
|
||||
public boolean hasParentClass() {
|
||||
return hasParentClass;
|
||||
}
|
||||
|
||||
public void setHasChildClass(boolean bool) {
|
||||
hasChildClass = true;
|
||||
}
|
||||
|
||||
public boolean hasChildClass() {
|
||||
return hasChildClass;
|
||||
}
|
||||
|
||||
public void addParent(RecoveredClass recoveredClass) {
|
||||
if (!parentList.contains(recoveredClass)) {
|
||||
parentList.add(recoveredClass);
|
||||
}
|
||||
}
|
||||
|
||||
public List<RecoveredClass> getParentList() {
|
||||
return parentList;
|
||||
}
|
||||
|
||||
public void addVftableAddress(Address address) {
|
||||
vftableAddresses.add(address);
|
||||
setHasVftable(true);
|
||||
}
|
||||
|
||||
public List<Address> getVftableAddresses() {
|
||||
return vftableAddresses;
|
||||
}
|
||||
|
||||
public void addVftableVfunctionsMapping(Address vftAddress, List<Function> vfunctions) {
|
||||
vftableToVfunctionsMap.put(vftAddress, vfunctions);
|
||||
allClassVirtualFunctions.addAll(vfunctions);
|
||||
}
|
||||
|
||||
public void addVftableToBaseClassMapping(Address vftAddress, RecoveredClass recoveredClass) {
|
||||
vftableToBaseClassMap.put(vftAddress, recoveredClass);
|
||||
}
|
||||
|
||||
public RecoveredClass getVftableBaseClass(Address address) {
|
||||
return vftableToBaseClassMap.get(address);
|
||||
}
|
||||
|
||||
public void addOrderToVftableMapping(Integer order, Address address) {
|
||||
orderToVftableMap.put(order, address);
|
||||
}
|
||||
|
||||
public Map<Integer, Address> getOrderToVftableMap() {
|
||||
return orderToVftableMap;
|
||||
}
|
||||
|
||||
public void addClassOffsetToVftableMapping(int offset, Address vftableAddress)
|
||||
throws Exception {
|
||||
|
||||
// already have this mapping
|
||||
if (classOffsetToVftableMap.get(offset) == vftableAddress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!classOffsetToVftableMap.keySet().contains(offset)) {
|
||||
|
||||
// error if try to add same address to different offset
|
||||
if (classOffsetToVftableMap.values().contains(vftableAddress)) {
|
||||
throw new Exception(name + " trying to add same vftable address " +
|
||||
vftableAddress.toString() + " to new offset " + offset);
|
||||
}
|
||||
|
||||
classOffsetToVftableMap.put(offset, vftableAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
// error if try to add different address to same offset
|
||||
Address address = classOffsetToVftableMap.get(offset);
|
||||
if (!address.equals(vftableAddress)) {
|
||||
throw new Exception(name + " trying to add different vftable address (old: " +
|
||||
vftableAddress.toString() + " new: " + address.toString() + ") to same offset " +
|
||||
offset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Map<Integer, Address> getClassOffsetToVftableMap() {
|
||||
return classOffsetToVftableMap;
|
||||
}
|
||||
|
||||
public void addParentToBaseTypeMapping(RecoveredClass recoveredClass, Boolean isVirtualBase) {
|
||||
|
||||
if (!parentToBaseTypeMap.keySet().contains(recoveredClass)) {
|
||||
parentToBaseTypeMap.put(recoveredClass, isVirtualBase);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<RecoveredClass, Boolean> getParentToBaseTypeMap() {
|
||||
return parentToBaseTypeMap;
|
||||
}
|
||||
|
||||
public void setVbtableAddress(Address address) {
|
||||
vbtableAddress = address;
|
||||
}
|
||||
|
||||
public Address getVbtableAddress() {
|
||||
return vbtableAddress;
|
||||
}
|
||||
|
||||
public void setVbtableStructure(Structure structure) {
|
||||
vbtableStructure = structure;
|
||||
}
|
||||
|
||||
public Structure getVbtableStructure() {
|
||||
return vbtableStructure;
|
||||
}
|
||||
|
||||
public void setVbtableOffset(int offset) {
|
||||
vbtableOffset = offset;
|
||||
}
|
||||
|
||||
public int getVbtableOffset() {
|
||||
return vbtableOffset;
|
||||
}
|
||||
|
||||
public void setInheritsVirtualAncestor(boolean setting) {
|
||||
inheritsVirtualAncestor = setting;
|
||||
}
|
||||
|
||||
public boolean inheritsVirtualAncestor() {
|
||||
return inheritsVirtualAncestor;
|
||||
}
|
||||
|
||||
public void setIsPublicClass(boolean setting) {
|
||||
isPublicClass = setting;
|
||||
}
|
||||
|
||||
public boolean isPublicClass() {
|
||||
return isPublicClass;
|
||||
}
|
||||
|
||||
public void setHasVftable(boolean setting) {
|
||||
hasVftable = setting;
|
||||
}
|
||||
|
||||
public boolean hasVftable() {
|
||||
return hasVftable;
|
||||
}
|
||||
|
||||
public void setHasSingleInheritance(boolean hasSingleInheritanceSetting) {
|
||||
hasSingleInheritance = hasSingleInheritanceSetting;
|
||||
}
|
||||
|
||||
public boolean hasSingleInheritance() {
|
||||
return hasSingleInheritance;
|
||||
}
|
||||
|
||||
public void setHasMultipleInheritance(boolean hasMultipleInheritanceSetting) {
|
||||
hasMultipleInheritance = hasMultipleInheritanceSetting;
|
||||
}
|
||||
|
||||
public boolean hasMultipleInheritance() {
|
||||
return hasMultipleInheritance;
|
||||
}
|
||||
|
||||
public void setHasMultipleVirtualInheritance(boolean hasMultVirtSetting) {
|
||||
hasMultipleVirtualInheritance = hasMultVirtSetting;
|
||||
}
|
||||
|
||||
public boolean hasMultipleVirtualInheritance() {
|
||||
return hasMultipleVirtualInheritance;
|
||||
}
|
||||
|
||||
public void addConstructorDestructorList(List<Function> list) {
|
||||
Iterator<Function> iterator = list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
|
||||
Function function = iterator.next();
|
||||
if (!constructorAndDestructorList.contains(function)) {
|
||||
constructorAndDestructorList.add(function);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public void removeFromConstructorDestructorList(Function function) {
|
||||
if (constructorAndDestructorList.contains(function)) {
|
||||
constructorAndDestructorList.remove(function);
|
||||
}
|
||||
}
|
||||
|
||||
public void addConstructor(Function function) {
|
||||
if (!constructorList.contains(function)) {
|
||||
constructorList.add(function);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public void addDestructor(Function function) {
|
||||
if (!destructorList.contains(function)) {
|
||||
destructorList.add(function);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public void addInlinedConstructor(Function function) {
|
||||
if (!inlinedConstructorList.contains(function)) {
|
||||
inlinedConstructorList.add(function);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public void addInlinedDestructor(Function function) {
|
||||
if (!inlinedDestructorList.contains(function)) {
|
||||
inlinedDestructorList.add(function);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public void addNonThisDestructor(Function function) {
|
||||
if (!nonThisDestructors.contains(function)) {
|
||||
nonThisDestructors.add(function);
|
||||
}
|
||||
}
|
||||
|
||||
public void addIndeterminateInline(Function function) {
|
||||
if (!indeterminateInlineList.contains(function)) {
|
||||
indeterminateInlineList.add(function);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public void removeIndeterminateInline(Function function) {
|
||||
if (indeterminateInlineList.contains(function)) {
|
||||
indeterminateInlineList.remove(function);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public void addIndeterminateConstructorOrDestructorList(List<Function> list) {
|
||||
Iterator<Function> iterator = list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Function function = iterator.next();
|
||||
if (!indeterminateList.contains(function)) {
|
||||
indeterminateList.add(function);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public void removeIndeterminateConstructorOrDestructor(Function function) {
|
||||
if (indeterminateList.contains(function)) {
|
||||
indeterminateList.remove(function);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public List<Function> getConstructorOrDestructorFunctions() {
|
||||
return constructorAndDestructorList;
|
||||
}
|
||||
|
||||
public List<Function> getConstructorList() {
|
||||
return constructorList;
|
||||
}
|
||||
|
||||
public List<Function> getInlinedConstructorList() {
|
||||
return inlinedConstructorList;
|
||||
}
|
||||
|
||||
public List<Function> getDestructorList() {
|
||||
return destructorList;
|
||||
}
|
||||
|
||||
public List<Function> getInlinedDestructorList() {
|
||||
return inlinedDestructorList;
|
||||
}
|
||||
|
||||
public List<Function> getNonThisDestructors() {
|
||||
return nonThisDestructors;
|
||||
}
|
||||
|
||||
public List<Function> getIndeterminateList() {
|
||||
return indeterminateList;
|
||||
}
|
||||
|
||||
public List<Function> getIndeterminateInlineList() {
|
||||
return indeterminateInlineList;
|
||||
}
|
||||
|
||||
public void addChildClass(RecoveredClass recoveredClass) {
|
||||
if (!childClasses.contains(recoveredClass)) {
|
||||
childClasses.add(recoveredClass);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public List<RecoveredClass> getChildClasses() {
|
||||
return childClasses;
|
||||
}
|
||||
|
||||
public void addExistingClassStructure(Structure classStructure) {
|
||||
if (classStructure == null) {
|
||||
return;
|
||||
}
|
||||
existingClassStructure = classStructure;
|
||||
hasExistingClassStructure = true;
|
||||
return;
|
||||
}
|
||||
|
||||
public Structure getExistingClassStructure() {
|
||||
return existingClassStructure;
|
||||
}
|
||||
|
||||
public boolean hasExistingClassStructure() {
|
||||
return hasExistingClassStructure;
|
||||
}
|
||||
|
||||
public void updateClassMemberStructure(Structure structure) throws CancelledException {
|
||||
|
||||
// initialize by copying first structure
|
||||
if (computedClassStructure == null) {
|
||||
|
||||
computedClassStructure =
|
||||
new StructureDataType(classPath, name, structure.getLength(), dataTypeManager);
|
||||
|
||||
int numComponents = structure.getNumComponents();
|
||||
for (int i = 0; i < numComponents; i++) {
|
||||
DataTypeComponent component = structure.getComponent(i);
|
||||
int offset = component.getOffset();
|
||||
|
||||
computedClassStructure.replaceAtOffset(offset, component.getDataType(),
|
||||
component.getDataType().getLength(), component.getFieldName(),
|
||||
component.getComment());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// update initial structure using all further structures
|
||||
if (structure.getLength() > computedClassStructure.getLength()) {
|
||||
computedClassStructure.growStructure(
|
||||
structure.getLength() - computedClassStructure.getLength());
|
||||
}
|
||||
|
||||
DataTypeComponent[] definedComponents = structure.getDefinedComponents();
|
||||
for (DataTypeComponent newComponent : definedComponents) {
|
||||
|
||||
DataType newComponentDataType = newComponent.getDataType();
|
||||
|
||||
int offset = newComponent.getOffset();
|
||||
|
||||
DataTypeComponent currentComponent = computedClassStructure.getComponentAt(offset);
|
||||
DataType currentComponentDataType = currentComponent.getDataType();
|
||||
|
||||
if (currentComponentDataType.equals(newComponentDataType)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int length = newComponentDataType.getLength();
|
||||
String fieldName = newComponent.getFieldName();
|
||||
String comment = newComponent.getComment();
|
||||
|
||||
// if it is any empty placeholder structure - replace with
|
||||
// undefined1 dt
|
||||
if (newComponentDataType instanceof Structure &&
|
||||
newComponentDataType.isNotYetDefined()) {
|
||||
|
||||
computedClassStructure.replaceAtOffset(offset, new Undefined1DataType(), 1,
|
||||
fieldName, comment);
|
||||
continue;
|
||||
}
|
||||
|
||||
// replace pointers to existing class data type with void pointer of same size
|
||||
if (newComponentDataType instanceof Pointer &&
|
||||
newComponentDataType.getName().equals(name + " *")) {
|
||||
|
||||
DataType voidDT = new VoidDataType();
|
||||
|
||||
Pointer pointer = new PointerDataType();
|
||||
if (newComponentDataType.getLength() == 4) {
|
||||
pointer = new Pointer32DataType(voidDT);
|
||||
}
|
||||
if (newComponentDataType.getLength() == 8) {
|
||||
pointer = new Pointer64DataType(voidDT);
|
||||
}
|
||||
computedClassStructure.replaceAtOffset(offset, pointer, pointer.getLength(),
|
||||
fieldName, comment);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if the new component is a non-empty structure, check to see if the current
|
||||
// structure has undefined or equivalent components and replace with new struct if so
|
||||
if (newComponentDataType instanceof Structure) {
|
||||
if (structUtils.hasReplaceableComponentsAtOffset(computedClassStructure,
|
||||
offset, (Structure) newComponentDataType, monitor)) {
|
||||
|
||||
boolean successfulClear =
|
||||
structUtils.clearLengthAtOffset(computedClassStructure, offset,
|
||||
length, monitor);
|
||||
|
||||
if (successfulClear) {
|
||||
computedClassStructure.replaceAtOffset(offset, newComponentDataType, length,
|
||||
fieldName, comment);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// if current component is undefined size 1 and new component is not undefined size 1
|
||||
// then replace it
|
||||
if (structUtils.isUndefined1(currentComponentDataType) &&
|
||||
!structUtils.isUndefined1(newComponentDataType)) {
|
||||
if (structUtils.hasEnoughUndefinedsOfAnyLengthAtOffset(computedClassStructure,
|
||||
offset, length, monitor)) {
|
||||
boolean successfulClear =
|
||||
structUtils.clearLengthAtOffset(computedClassStructure, offset,
|
||||
length, monitor);
|
||||
|
||||
if (successfulClear) {
|
||||
computedClassStructure.replaceAtOffset(offset, newComponentDataType, length,
|
||||
fieldName, comment);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// if new component is not an undefined data type and the current componenent(s)
|
||||
// that make up new component length are all undefineds then clear and replace
|
||||
// the current component(s) with the new one
|
||||
if (structUtils.isUndefined(currentComponentDataType) &&
|
||||
!structUtils.isUndefined(newComponentDataType)) {
|
||||
|
||||
if (structUtils.hasEnoughUndefinedsOfAnyLengthAtOffset(computedClassStructure,
|
||||
offset, length, monitor)) {
|
||||
boolean successfulClear =
|
||||
structUtils.clearLengthAtOffset(computedClassStructure, offset,
|
||||
length, monitor);
|
||||
|
||||
if (successfulClear) {
|
||||
computedClassStructure.replaceAtOffset(offset, newComponentDataType, length,
|
||||
fieldName, comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: remove once FillOutStructCmd updates to put Undefined1 instead of undefined
|
||||
// for saved offsets
|
||||
public void updateClassMemberStructureUndefineds(NoisyStructureBuilder componentMap) {
|
||||
|
||||
Iterator<Entry<Long, DataType>> componentMapIterator = componentMap.iterator();
|
||||
while (componentMapIterator.hasNext()) {
|
||||
Entry<Long, DataType> next = componentMapIterator.next();
|
||||
Long offset = next.getKey();
|
||||
DataType dataType = next.getValue();
|
||||
|
||||
if (computedClassStructure.getLength() < (offset.intValue() + dataType.getLength())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (structUtils.isUndefined1(dataType)) {
|
||||
dataType = new Undefined1DataType();
|
||||
DataTypeComponent component =
|
||||
computedClassStructure.getComponentAt(offset.intValue());
|
||||
|
||||
if (!component.getDataType().equals(dataType)) {
|
||||
computedClassStructure.replaceAtOffset(offset.intValue(), dataType,
|
||||
dataType.getLength(), component.getFieldName(), component.getComment());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Structure getComputedClassStructure() {
|
||||
return computedClassStructure;
|
||||
}
|
||||
|
||||
public void addCloneFunction(Function function) {
|
||||
if (!cloneFunctions.contains(function)) {
|
||||
cloneFunctions.add(function);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Function> getCloneFunctions() {
|
||||
return cloneFunctions;
|
||||
}
|
||||
|
||||
public void addDeletingDestructor(Function function) {
|
||||
if (!deletingDestructors.contains(function)) {
|
||||
deletingDestructors.add(function);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Function> getDeletingDestructors() {
|
||||
return deletingDestructors;
|
||||
}
|
||||
|
||||
public void setVBaseDestructor(Function function) {
|
||||
vbaseDestructor = function;
|
||||
}
|
||||
|
||||
public Function getVBaseDestructor() {
|
||||
return vbaseDestructor;
|
||||
}
|
||||
|
||||
public void setClassHierarchy(List<RecoveredClass> list) {
|
||||
classHierarchy.addAll(list);
|
||||
return;
|
||||
}
|
||||
|
||||
public List<RecoveredClass> getClassHierarchy() {
|
||||
return classHierarchy;
|
||||
}
|
||||
|
||||
public void addClassHierarchyMapping(RecoveredClass recoveredClass,
|
||||
List<RecoveredClass> recoveredClasses) {
|
||||
|
||||
if (!classHierarchyMap.keySet().contains(recoveredClass)) {
|
||||
classHierarchyMap.put(recoveredClass, recoveredClasses);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<RecoveredClass, List<RecoveredClass>> getClassHierarchyMap() {
|
||||
return classHierarchyMap;
|
||||
}
|
||||
|
||||
public void addShortenedTemplatedName(String shortenedName) {
|
||||
shortenedTemplateName = shortenedName;
|
||||
}
|
||||
|
||||
public String getShortenedTemplateName() {
|
||||
return shortenedTemplateName;
|
||||
}
|
||||
}
|
||||
|
6650
Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClassUtils.java
Normal file
6650
Ghidra/Features/Decompiler/ghidra_scripts/RecoveredClassUtils.java
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,651 @@
|
|||
/* ###
|
||||
* 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 fix up Windows RTTI vtables and structures
|
||||
//@category C++
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.CircularDependencyException;
|
||||
import ghidra.program.model.listing.Data;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.util.exception.*;
|
||||
|
||||
public class FixUpRttiAnalysisScript extends GhidraScript {
|
||||
|
||||
private static final String RTTI_BASE_CLASS_ARRAY_LABEL = "RTTI_Base_Class_Array";
|
||||
private static final String RTTI_CLASS_HIERARCHY_DESCRIPTOR_LABEL =
|
||||
"RTTI_Class_Hierarchy_Descriptor";
|
||||
private static final String RTTI_BASE_CLASS_DESCRIPTOR_LABEL = "RTTI_Base_Class_Descriptor";
|
||||
private static final String RTTI_BASE_COMPLETE_OBJECT_LOADER_LABEL =
|
||||
"RTTI_Complete_Object_Locator";
|
||||
private static final String RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME = "RTTIBaseClassDescriptor";
|
||||
private static final String RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME =
|
||||
"RTTICompleteObjectLocator";
|
||||
private static final String RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME =
|
||||
"RTTIClassHierarchyDescriptor";
|
||||
private static final String VFTABLE_META_PTR_LABEL = "vftable_meta_ptr";
|
||||
private static final String VFTABLE_LABEL = "vftable";
|
||||
|
||||
SymbolTable symbolTable = null;
|
||||
DataTypeManager dataTypeManager = null;
|
||||
GlobalNamespace globalNamespace = null;
|
||||
int defaultPointerSize = 0;
|
||||
boolean isWindows = false;
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
if (currentProgram == null) {
|
||||
println("There is no open program");
|
||||
return;
|
||||
}
|
||||
|
||||
setIsWindows();
|
||||
|
||||
if (!isWindows) {
|
||||
println("This script only handles Windows programs");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: check version and only run if before 9.3?
|
||||
|
||||
symbolTable = currentProgram.getSymbolTable();
|
||||
dataTypeManager = currentProgram.getDataTypeManager();
|
||||
globalNamespace = (GlobalNamespace) currentProgram.getGlobalNamespace();
|
||||
|
||||
defaultPointerSize = currentProgram.getDefaultPointerSize();
|
||||
if (defaultPointerSize != 4 && defaultPointerSize != 8) {
|
||||
println("This script only works on 32 or 64 bit programs");
|
||||
return;
|
||||
}
|
||||
|
||||
applyMissingRTTIStructures();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to find and apply missing RTTI structures
|
||||
* @throws CancelledException if cancelled
|
||||
* @throws Exception if error applying label or data
|
||||
*/
|
||||
private List<Symbol> applyMissingRTTIStructures() throws CancelledException, Exception {
|
||||
|
||||
List<Symbol> completeObjectLocatorSymbols = createMissingRTTICompleteObjectLocator();
|
||||
|
||||
List<Symbol> baseClassDescriptorSymbols = createMissingBaseClassDescriptors();
|
||||
|
||||
List<Address> classHierarchyDescriptors = createMissingClassHierarchyDescriptors(
|
||||
baseClassDescriptorSymbols, completeObjectLocatorSymbols);
|
||||
|
||||
createMissingBaseClassArrays(classHierarchyDescriptors);
|
||||
|
||||
List<Symbol> vftableSymbols = createMissingVftableSymbols(completeObjectLocatorSymbols);
|
||||
return vftableSymbols;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set the global variable isWindows
|
||||
*/
|
||||
private void setIsWindows() {
|
||||
|
||||
String compilerID =
|
||||
currentProgram.getCompilerSpec().getCompilerSpecID().getIdAsString().toLowerCase();
|
||||
isWindows = compilerID.contains("windows");
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to iterate over all symbols with Base Class Descriptor symbol and if
|
||||
* the correct data type has not already been created, do so.
|
||||
* @return List of all symbols with valid (even previously) BaseClassDescriptor structure applied
|
||||
* @throws CancelledException when cancelled
|
||||
* @throws Exception when data cannot be created
|
||||
*/
|
||||
private List<Symbol> createMissingRTTICompleteObjectLocator()
|
||||
throws CancelledException, Exception {
|
||||
|
||||
List<Symbol> completeObjectLocatorSymbols = new ArrayList<Symbol>();
|
||||
|
||||
SymbolIterator dataSymbols =
|
||||
symbolTable.getSymbols(getInitializedMemory(), SymbolType.LABEL, true);
|
||||
|
||||
while (dataSymbols.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
Symbol symbol = dataSymbols.next();
|
||||
if (!symbol.getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_LABEL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Data data = getDataAt(symbol.getAddress());
|
||||
if (data != null &&
|
||||
data.getDataType().getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME)) {
|
||||
completeObjectLocatorSymbols.add(symbol);
|
||||
continue;
|
||||
}
|
||||
|
||||
// for some reason it was named but not created so create it
|
||||
data = createCompleteObjectLocator(symbol.getAddress());
|
||||
if (data != null &&
|
||||
data.getDataType().getName().contains(RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME)) {
|
||||
completeObjectLocatorSymbols.add(symbol);
|
||||
continue;
|
||||
}
|
||||
|
||||
println("Cannot create RTTI_CompleteObjectLocator at " + symbol.getAddress());
|
||||
|
||||
}
|
||||
return completeObjectLocatorSymbols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create a CompleteObjectLocator structure at the given address
|
||||
* @param address the address where the structure will be created
|
||||
* @return the created CompleteObjectLocator data or null if it couldn't be created
|
||||
* @throws CancelledException if cancelled
|
||||
* @throws Exception if error creating data
|
||||
*/
|
||||
private Data createCompleteObjectLocator(Address address) throws CancelledException, Exception {
|
||||
|
||||
DataType completeObjLocatorDataType = dataTypeManager.getDataType(CategoryPath.ROOT,
|
||||
RTTI_BASE_COMPLETE_OBJECT_LOADER_DATA_NAME);
|
||||
if (completeObjLocatorDataType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int sizeOfDt = completeObjLocatorDataType.getLength();
|
||||
|
||||
clearListing(address, address.add(sizeOfDt));
|
||||
Data completeObjectLocator = createData(address, completeObjLocatorDataType);
|
||||
if (completeObjectLocator == null) {
|
||||
return null;
|
||||
}
|
||||
return completeObjectLocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to iterate over all symbols with Base Class Descriptor symbol and if
|
||||
* the correct data type has not already been created, do so.
|
||||
* @return List of all symbols with valid (even previously) BaseClassDescriptor structure applied
|
||||
* @throws Exception when cancelled
|
||||
*/
|
||||
private List<Symbol> createMissingBaseClassDescriptors() throws Exception {
|
||||
|
||||
List<Symbol> baseClassDescriptorSymbols = new ArrayList<Symbol>();
|
||||
|
||||
SymbolIterator dataSymbols =
|
||||
symbolTable.getSymbols(getInitializedMemory(), SymbolType.LABEL, true);
|
||||
|
||||
while (dataSymbols.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
Symbol symbol = dataSymbols.next();
|
||||
if (!symbol.getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_LABEL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Data data = getDataAt(symbol.getAddress());
|
||||
if (data != null &&
|
||||
data.getDataType().getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) {
|
||||
baseClassDescriptorSymbols.add(symbol);
|
||||
continue;
|
||||
}
|
||||
|
||||
// for some reason it was named but not created so create it
|
||||
data = createBaseClassDescriptor(symbol.getAddress());
|
||||
if (data != null &&
|
||||
data.getDataType().getName().contains(RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) {
|
||||
baseClassDescriptorSymbols.add(symbol);
|
||||
continue;
|
||||
}
|
||||
|
||||
println("Cannot create RTTI_Base_Class_Descriptor at " + symbol.getAddress());
|
||||
|
||||
}
|
||||
return baseClassDescriptorSymbols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create a BaseClassDescriptor structure at the given address
|
||||
* @param baseClassDescriptorAddress the address where the structure will be created
|
||||
* @return the created BaseClassDescriptor data or null if it couldn't be created
|
||||
* @throws CancelledException if cancelled
|
||||
* @throws Exception if error creating data
|
||||
*/
|
||||
private Data createBaseClassDescriptor(Address baseClassDescriptorAddress)
|
||||
throws CancelledException, Exception {
|
||||
|
||||
DataType baseClassDescriptor =
|
||||
dataTypeManager.getDataType(CategoryPath.ROOT, RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME);
|
||||
|
||||
int sizeOfDt = baseClassDescriptor.getLength();
|
||||
|
||||
clearListing(baseClassDescriptorAddress, baseClassDescriptorAddress.add(sizeOfDt));
|
||||
Data baseClassDescArray = createData(baseClassDescriptorAddress, baseClassDescriptor);
|
||||
if (baseClassDescArray == null) {
|
||||
return null;
|
||||
}
|
||||
return baseClassDescArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to apply missing RTTI Base Class Descriptor structures and symbols
|
||||
* @param address address to apply the missing structure and symbol
|
||||
* @param numBaseClasses number of base classes in the array pointing to BaseClassDescriptors
|
||||
* @param classNamespace name of the class
|
||||
* @throws AddressOutOfBoundsException if try clear listing at address out of bounds
|
||||
* @throws MemoryAccessException if cannot access memory
|
||||
* @throws CancelledException if cancelled
|
||||
* @throws Exception if issue making data
|
||||
*/
|
||||
private void createBaseClassDescriptors(Address address, int numBaseClasses,
|
||||
Namespace classNamespace) throws CancelledException, MemoryAccessException,
|
||||
AddressOutOfBoundsException, Exception {
|
||||
|
||||
for (int i = 0; i < numBaseClasses; i++) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
|
||||
Address baseClassDescriptorAddress = getReferencedAddress(address.add(i * 4));
|
||||
|
||||
Data baseClassDescriptor = getDataAt(baseClassDescriptorAddress);
|
||||
if (baseClassDescriptor == null || !baseClassDescriptor.getDataType().getName().equals(
|
||||
RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME)) {
|
||||
|
||||
int num1 = getInt(baseClassDescriptorAddress.add(8));
|
||||
int num2 = getInt(baseClassDescriptorAddress.add(12));
|
||||
int num3 = getInt(baseClassDescriptorAddress.add(16));
|
||||
int num4 = getInt(baseClassDescriptorAddress.add(20));
|
||||
|
||||
baseClassDescriptor = createBaseClassDescriptor(baseClassDescriptorAddress);
|
||||
if (baseClassDescriptor != null) {
|
||||
symbolTable.createLabel(
|
||||
baseClassDescriptorAddress, RTTI_BASE_CLASS_DESCRIPTOR_LABEL + "_at_(" +
|
||||
num1 + "," + num2 + "," + num3 + "," + num4 + ")",
|
||||
classNamespace, SourceType.ANALYSIS);
|
||||
}
|
||||
else {
|
||||
println(
|
||||
"Failed to create a baseClassDescArray structure at " + address.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param baseClassDescriptors the given list of BaseClassDescriptor symbols
|
||||
* @param completeObjectLocators the given list of CompleteObjectLocator symbols
|
||||
* @return list of ClassHierarchyDescriptor addresses
|
||||
* @throws CancelledException if cancelled
|
||||
* @throws MemoryAccessException if memory cannot be read
|
||||
* @throws InvalidInputException if issue setting return type
|
||||
* @throws AddressOutOfBoundsException if try clear listing at address out of bounds
|
||||
* @throws Exception if there is an issue creating a label
|
||||
*/
|
||||
private List<Address> createMissingClassHierarchyDescriptors(List<Symbol> baseClassDescriptors,
|
||||
List<Symbol> completeObjectLocators) throws CancelledException, MemoryAccessException,
|
||||
InvalidInputException, AddressOutOfBoundsException, Exception {
|
||||
|
||||
List<Address> classHierarchyDescriptorAddresses = new ArrayList<Address>();
|
||||
|
||||
Iterator<Symbol> baseClassDescriptorIterator = baseClassDescriptors.iterator();
|
||||
while (baseClassDescriptorIterator.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
Symbol symbol = baseClassDescriptorIterator.next();
|
||||
Address classHierarchyDescriptorAddress = createClassHierarchyDescriptor(
|
||||
symbol.getAddress().add(24), symbol.getParentNamespace());
|
||||
|
||||
if (classHierarchyDescriptorAddress != null &&
|
||||
!classHierarchyDescriptorAddresses.contains(classHierarchyDescriptorAddress)) {
|
||||
classHierarchyDescriptorAddresses.add(classHierarchyDescriptorAddress);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Iterator<Symbol> completeObjectLocatorIterator = completeObjectLocators.iterator();
|
||||
while (completeObjectLocatorIterator.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
Symbol symbol = completeObjectLocatorIterator.next();
|
||||
Address classHierarchyDescriptorAddress = createClassHierarchyDescriptor(
|
||||
symbol.getAddress().add(16), symbol.getParentNamespace());
|
||||
if (classHierarchyDescriptorAddress != null &&
|
||||
!classHierarchyDescriptorAddresses.contains(classHierarchyDescriptorAddress)) {
|
||||
classHierarchyDescriptorAddresses.add(classHierarchyDescriptorAddress);
|
||||
}
|
||||
}
|
||||
|
||||
return classHierarchyDescriptorAddresses;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param address the address where the ClassHierarchyDescriptor is to be created
|
||||
* @param classNamespace the namespace of the class
|
||||
* @return the given class's ClassHierarchyDescriptor address
|
||||
* @throws CancelledException if cancelled
|
||||
* @throws MemoryAccessException if memory cannot be read
|
||||
* @throws InvalidInputException if issue setting return type
|
||||
* @throws Exception if issue creating label
|
||||
*/
|
||||
private Address createClassHierarchyDescriptor(Address address, Namespace classNamespace)
|
||||
throws CancelledException, MemoryAccessException, InvalidInputException, Exception {
|
||||
|
||||
Address classHierarchyDescriptorAddress = getReferencedAddress(address);
|
||||
|
||||
Data classHierarchyStructure = getDataAt(classHierarchyDescriptorAddress);
|
||||
|
||||
if (classHierarchyStructure != null &&
|
||||
classHierarchyStructure.getDataType().getName().equals(
|
||||
RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME)) {
|
||||
return classHierarchyDescriptorAddress;
|
||||
|
||||
}
|
||||
|
||||
Symbol classHierarchySymbol;
|
||||
|
||||
classHierarchySymbol = symbolTable.createLabel(classHierarchyDescriptorAddress,
|
||||
RTTI_CLASS_HIERARCHY_DESCRIPTOR_LABEL, classNamespace, SourceType.ANALYSIS);
|
||||
|
||||
classHierarchyStructure = createClassHierarchyStructure(classHierarchyDescriptorAddress);
|
||||
|
||||
if (classHierarchyStructure == null) {
|
||||
println("Failed to create a classHierarchyDescriptor structure at " +
|
||||
classHierarchyDescriptorAddress.toString());
|
||||
symbolTable.removeSymbolSpecial(classHierarchySymbol);
|
||||
return null;
|
||||
}
|
||||
return classHierarchyDescriptorAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create a ClassHierarchyDescriptor structure at the given address
|
||||
* @param classHierarchyDescriptorAddress the address where the structure will be created
|
||||
* @return the created ClassHierarchyDescriptor data or null if it couldn't be created
|
||||
* @throws CancelledException if cancelled
|
||||
* @throws AddressOutOfBoundsException if try clear listing at address out of bounds
|
||||
* @throws Exception if issue creating data
|
||||
*/
|
||||
private Data createClassHierarchyStructure(Address classHierarchyDescriptorAddress)
|
||||
throws CancelledException, AddressOutOfBoundsException, Exception {
|
||||
|
||||
DataType classHDatatype = dataTypeManager.getDataType(CategoryPath.ROOT,
|
||||
RTTI_CLASS_HIERARCHY_DESCRIPTOR_DATA_NAME);
|
||||
int sizeOfDt = classHDatatype.getLength();
|
||||
clearListing(classHierarchyDescriptorAddress,
|
||||
classHierarchyDescriptorAddress.add(sizeOfDt));
|
||||
|
||||
Data classHierarchyStructure = createData(classHierarchyDescriptorAddress, classHDatatype);
|
||||
if (classHierarchyStructure == null) {
|
||||
return null;
|
||||
}
|
||||
return classHierarchyStructure;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param classHierarchyDescriptors the given list of applied ClassHierarchyDescriptor structures
|
||||
* @return a list of base class array addresses
|
||||
* @throws CancelledException if cancelled
|
||||
* @throws MemoryAccessException if memory cannot be read
|
||||
* @throws AddressOutOfBoundsException if try clear listing at address out of bounds
|
||||
* @throws Exception if there is an issue creating a label
|
||||
*/
|
||||
private List<Address> createMissingBaseClassArrays(List<Address> classHierarchyDescriptors)
|
||||
throws CancelledException, MemoryAccessException, AddressOutOfBoundsException,
|
||||
Exception {
|
||||
|
||||
List<Address> baseClassArrayAddresses = new ArrayList<Address>();
|
||||
|
||||
Iterator<Address> classHierarchyDescriptorIterator = classHierarchyDescriptors.iterator();
|
||||
|
||||
while (classHierarchyDescriptorIterator.hasNext()) {
|
||||
|
||||
monitor.checkCanceled();
|
||||
|
||||
Address classHierarchyDescriptorAddress = classHierarchyDescriptorIterator.next();
|
||||
Symbol classHierarchyDescriptorSymbol =
|
||||
symbolTable.getPrimarySymbol(classHierarchyDescriptorAddress);
|
||||
Namespace classNamespace = classHierarchyDescriptorSymbol.getParentNamespace();
|
||||
|
||||
int numBaseClasses = getInt(classHierarchyDescriptorAddress.add(8));
|
||||
|
||||
Address baseClassArrayAddress =
|
||||
getReferencedAddress(classHierarchyDescriptorAddress.add(12));
|
||||
|
||||
Data baseClassDescArray = getDataAt(baseClassArrayAddress);
|
||||
|
||||
if (baseClassDescArray != null && baseClassDescArray.isArray()) {
|
||||
baseClassArrayAddresses.add(baseClassArrayAddress);
|
||||
continue;
|
||||
}
|
||||
|
||||
baseClassDescArray = createBaseClassArray(baseClassArrayAddress, numBaseClasses);
|
||||
if (baseClassDescArray != null && baseClassDescArray.isArray()) {
|
||||
Symbol primarySymbol = symbolTable.getPrimarySymbol(baseClassArrayAddress);
|
||||
if (primarySymbol == null ||
|
||||
!primarySymbol.getName().contains(RTTI_BASE_CLASS_ARRAY_LABEL)) {
|
||||
|
||||
symbolTable.createLabel(baseClassArrayAddress, RTTI_BASE_CLASS_ARRAY_LABEL,
|
||||
classNamespace, SourceType.ANALYSIS);
|
||||
}
|
||||
baseClassArrayAddresses.add(baseClassArrayAddress);
|
||||
createBaseClassDescriptors(baseClassArrayAddress, numBaseClasses, classNamespace);
|
||||
continue;
|
||||
}
|
||||
|
||||
println("Failed to create a baseClassDescArray structure at " +
|
||||
baseClassArrayAddress.toString());
|
||||
}
|
||||
return baseClassArrayAddresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create a base class array at the given address with the given number of base class's in the array
|
||||
* @param baseClassArrayAddress the address where the array will be created
|
||||
* @param numBaseClasses the number of BaseClass's in the array
|
||||
* @return the created BaseClassArray data or null if cannot retrieve it
|
||||
* @throws CancelledException if cancelled
|
||||
* @throws Exception if error creating data
|
||||
*/
|
||||
private Data createBaseClassArray(Address baseClassArrayAddress, int numBaseClasses)
|
||||
throws CancelledException, Exception {
|
||||
|
||||
int sizeOfDt;
|
||||
ArrayDataType baseClassDescArrayDT;
|
||||
|
||||
int addressSize = baseClassArrayAddress.getSize();
|
||||
if (addressSize == 32) {
|
||||
DataType baseClassDescriptor = dataTypeManager.getDataType(CategoryPath.ROOT,
|
||||
RTTI_BASE_CLASS_DESCRIPTOR_DATA_NAME);
|
||||
PointerDataType baseClassDescriptorPtr = new PointerDataType(baseClassDescriptor);
|
||||
sizeOfDt = baseClassDescriptorPtr.getLength();
|
||||
|
||||
baseClassDescArrayDT =
|
||||
new ArrayDataType(baseClassDescriptorPtr, numBaseClasses, sizeOfDt);
|
||||
}
|
||||
else if (addressSize == 64) {
|
||||
DataType imageBaseOffset =
|
||||
dataTypeManager.getDataType(CategoryPath.ROOT, "ImageBaseOffset32");
|
||||
sizeOfDt = imageBaseOffset.getLength();
|
||||
baseClassDescArrayDT = new ArrayDataType(imageBaseOffset, numBaseClasses, sizeOfDt);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
|
||||
clearListing(baseClassArrayAddress, baseClassArrayAddress.add(numBaseClasses * sizeOfDt));
|
||||
Data baseClassDescArray = createData(baseClassArrayAddress, baseClassDescArrayDT);
|
||||
|
||||
if (baseClassDescArray == null) {
|
||||
return null;
|
||||
}
|
||||
return baseClassDescArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create missing vftables and return a list of them
|
||||
* @param completeObjectLocatorSymbols the list of completeObjectLocatorSymbols
|
||||
* @return list of vftable symbols
|
||||
* @throws CancelledException if cancelled
|
||||
* @throws InvalidInputException if invalid input
|
||||
* @throws CircularDependencyException if namespace has circular dependency
|
||||
* @throws DuplicateNameException if try to create label with duplicate name in namespace
|
||||
*/
|
||||
private List<Symbol> createMissingVftableSymbols(List<Symbol> completeObjectLocatorSymbols)
|
||||
throws CancelledException, InvalidInputException, DuplicateNameException,
|
||||
CircularDependencyException {
|
||||
|
||||
List<Symbol> vftables = new ArrayList<Symbol>();
|
||||
|
||||
Iterator<Symbol> iterator = completeObjectLocatorSymbols.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
monitor.checkCanceled();
|
||||
Symbol completeObjectLocatorSymbol = iterator.next();
|
||||
|
||||
Address completeObjectLocatorAddress = completeObjectLocatorSymbol.getAddress();
|
||||
|
||||
Namespace classNamespace = completeObjectLocatorSymbol.getParentNamespace();
|
||||
if (classNamespace.equals(globalNamespace)) {
|
||||
println("no class namespace for " + completeObjectLocatorAddress.toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference[] referencesTo = getReferencesTo(completeObjectLocatorAddress);
|
||||
if (referencesTo.length == 0) {
|
||||
println("no refs to " + completeObjectLocatorAddress.toString());
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Reference refTo : referencesTo) {
|
||||
Address vftableMetaPointer = refTo.getFromAddress();
|
||||
if (vftableMetaPointer == null) {
|
||||
println("can't retrieve meta address");
|
||||
continue;
|
||||
}
|
||||
Address vftableAddress = vftableMetaPointer.add(defaultPointerSize);
|
||||
if (vftableAddress == null) {
|
||||
println("can't retrieve vftable address");
|
||||
continue;
|
||||
}
|
||||
|
||||
// if not created, create vftable meta pointer label
|
||||
|
||||
if (getGivenSymbol(vftableAddress, VFTABLE_META_PTR_LABEL,
|
||||
classNamespace) == null) {
|
||||
|
||||
symbolTable.createLabel(vftableMetaPointer, VFTABLE_META_PTR_LABEL,
|
||||
classNamespace, SourceType.ANALYSIS);
|
||||
}
|
||||
|
||||
// if not created, create vftable label
|
||||
Symbol vftableSymbol =
|
||||
getGivenSymbol(vftableAddress, VFTABLE_LABEL, classNamespace);
|
||||
if (vftableSymbol == null) {
|
||||
|
||||
vftableSymbol = symbolTable.createLabel(vftableAddress, VFTABLE_LABEL,
|
||||
classNamespace, SourceType.ANALYSIS);
|
||||
|
||||
if (vftableSymbol == null) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vftables.contains(vftableSymbol)) {
|
||||
vftables.add(vftableSymbol);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return vftables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to retrieve the symbol with the given address, containing name (containing to account
|
||||
* for pdb case where sometimes has extra chars) and namespace
|
||||
* @param address the given address
|
||||
* @param name the given name
|
||||
* @param namespace the given namespace
|
||||
* @return the symbol with the given address, containing name, with given namespace
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
private Symbol getGivenSymbol(Address address, String name, Namespace namespace)
|
||||
throws CancelledException {
|
||||
|
||||
Symbol[] symbols = symbolTable.getSymbols(address);
|
||||
for (Symbol sym : symbols) {
|
||||
monitor.checkCanceled();
|
||||
if (sym.getName().contains(name) && sym.getParentNamespace().equals(namespace)) {
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to return referenced address at the given address
|
||||
* @param address the address to look for a referenced address at
|
||||
* @return the first referenced address from the given address
|
||||
* @throws MemoryAccessException if memory cannot be read
|
||||
*/
|
||||
private Address getReferencedAddress(Address address) throws MemoryAccessException {
|
||||
|
||||
//TODO: switch to this then test then just rewrite the call and get rid of this method
|
||||
// MSDataTypeUtils.getReferencedAddress(currentProgram, address);
|
||||
// this will work whether there is a created reference or not
|
||||
int addressSize = address.getSize();
|
||||
if (addressSize == 32) {
|
||||
long offset = getInt(address);
|
||||
|
||||
return address.getNewAddress(offset);
|
||||
}
|
||||
|
||||
// this currently will workn only if there is a created reference
|
||||
// TODO: get ibo bytes and figure out what the ibo ref address would be
|
||||
if (addressSize == 64) {
|
||||
Reference refs[] = getReferencesFrom(address);
|
||||
if (refs.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return refs[0].getToAddress();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to retrieve the AddressSet of the current program's initialized memory
|
||||
* @return the AddressSet of the current program's initialized memory
|
||||
* @throws CancelledException if cancelled
|
||||
*/
|
||||
private AddressSet getInitializedMemory() throws CancelledException {
|
||||
|
||||
AddressSet dataAddresses = new AddressSet();
|
||||
MemoryBlock[] blocks = currentProgram.getMemory().getBlocks();
|
||||
|
||||
for (MemoryBlock block : blocks) {
|
||||
monitor.checkCanceled();
|
||||
|
||||
if (block.isInitialized()) {
|
||||
dataAddresses.add(block.getStart(), block.getEnd());
|
||||
}
|
||||
}
|
||||
return dataAddresses;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/* ###
|
||||
* 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 fix up Windows RTTI vtables and structures
|
||||
//@category C++
|
||||
|
||||
import ghidra.app.cmd.data.rtti.RttiUtil;
|
||||
import ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin.PEUtil;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
public class IdPeRttiScript extends GhidraScript {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
if (currentProgram == null) {
|
||||
if (!isRunningHeadless()) {
|
||||
println("There is no open program.");
|
||||
return;
|
||||
}
|
||||
currentProgram.setTemporary(true);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isPe = PEUtil.isVisualStudioOrClangPe(currentProgram);
|
||||
if (!isPe) {
|
||||
if (!isRunningHeadless()) {
|
||||
println("The current program is not a Visual Studio or Clang PE program.");
|
||||
return;
|
||||
}
|
||||
currentProgram.setTemporary(true);
|
||||
return;
|
||||
}
|
||||
|
||||
Address commonVfTableAddress = RttiUtil.findTypeInfoVftableAddress(currentProgram, monitor);
|
||||
|
||||
if (commonVfTableAddress == null) {
|
||||
if (!isRunningHeadless()) {
|
||||
println("The current program does not appear to contain RTTI.");
|
||||
return;
|
||||
}
|
||||
currentProgram.setTemporary(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isRunningHeadless()) {
|
||||
println("The current program is a Visual Studio PE or Clang that contains RTTI.");
|
||||
return;
|
||||
}
|
||||
currentProgram.setTemporary(false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/* ###
|
||||
* 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 fix up Windows RTTI vtables and structures
|
||||
//@category C++
|
||||
|
||||
import ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin.RttiAnalyzer;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.services.Analyzer;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
|
||||
public class RunRttiAnalyzerScript extends GhidraScript {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
runRTTIAnalyzer();
|
||||
}
|
||||
|
||||
private void runRTTIAnalyzer() throws Exception {
|
||||
Analyzer analyzer = new RttiAnalyzer();
|
||||
analyzer.added(currentProgram, currentProgram.getAddressFactory().getAddressSet(), monitor,
|
||||
new MessageLog());
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue