Candidate release of source code.

This commit is contained in:
Dan
2019-03-26 13:45:32 -04:00
parent db81e6b3b0
commit 79d8f164f8
12449 changed files with 2800756 additions and 16 deletions
@@ -0,0 +1,356 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.model.DomainObject;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.data.*;
import ghidra.program.model.data.DataUtilities.ClearDataMode;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
/**
* This is the abstract command to extend when creating a specific data type or related data type.
*/
public abstract class AbstractCreateDataBackgroundCmd<T extends AbstractCreateDataTypeModel>
extends BackgroundCommand {
protected final String name;
protected final Address address;
protected final int count;
protected final DataValidationOptions validationOptions;
protected final DataApplyOptions applyOptions;
protected T model;
protected TaskMonitor monitor;
/**
* Constructs an abstract command for applying a dataType, that extends this class,
* at the address indicated by the model.
* @param model the model for the data type
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
protected AbstractCreateDataBackgroundCmd(T model, DataApplyOptions applyOptions) {
super("Create " + model.getName() + " Data", true, true, true);
this.model = model;
this.name = model.getName();
this.address = model.getAddress();
this.count = model.getCount();
validationOptions = model.getValidationOptions();
this.applyOptions = applyOptions;
}
/**
* Constructs a command for applying a specific dataType at an address using the default
* data validation options and default data apply options.
* @param name the name indicating the data type being created.
* @param address the address where the data should be created using the data type.
* @param count the number of the indicated data type to create. If more than 1, then an array
* of the data type will be created where count indicates the number of elements.
* data too.
*/
protected AbstractCreateDataBackgroundCmd(String name, Address address, int count) {
this(name, address, count, new DataValidationOptions(), new DataApplyOptions());
}
/**
* Constructs a command for applying a specific dataType at an address.
* @param name the name indicating the data type being created.
* @param address the address where the data should be created using the data type.
* @param count the number of the indicated data type to create. If more than 1, then an array
* of the data type will be created where count indicates the number of elements.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
protected AbstractCreateDataBackgroundCmd(String name, Address address, int count,
DataValidationOptions validationOptions, DataApplyOptions applyOptions) {
super("Create " + name + " Data", true, true, true);
this.name = name;
this.address = address;
this.count = count;
this.validationOptions = validationOptions;
this.applyOptions = applyOptions;
}
@Override
public final boolean applyTo(DomainObject obj, TaskMonitor taskMonitor) {
try {
if (!(obj instanceof Program)) {
String message = "Can only apply a " + name + " data type to a program.";
handleError(message);
return false;
}
return doApplyTo((Program) obj, taskMonitor);
}
catch (CancelledException e) {
setStatusMsg("User cancelled " + getName() + ".");
// FUTURE: Throw this exception instead of catching it, once BackgroundCommand throws it.
return false;
}
}
/**
* Gets the model that is used to validate and create the data type for this command.
* @param program the program where this command is applying the data type.
* @return the data type's model.
*/
protected abstract T createModel(Program program);
/**
* Gets the data type that needs to be validated and created by this command.
* @return the data type or null.
*/
protected final DataType getDataType() {
// If model isn't initialized, then we shouldn't even be getting to this point.
// The create...() methods use model prior to calling this method.
return model.getDataType();
}
/**
* Creates the data type for this command and may also create referred to data types.
* Also creates references, symbols, and functions as indicated by the options.
* @param program the program where this command will create the data type.
* @param taskMonitor a task monitor for cancelling or providing status information while
* creating the data type.
* @return true if the data type creation completes successfully.
* @throws CancelledException if the user cancels this task.
*/
private boolean doApplyTo(Program program, TaskMonitor taskMonitor) throws CancelledException {
try {
monitor = taskMonitor;
monitor.checkCanceled();
model = createModel(program);
model.validate();
// Are there any instructions in the way?
try {
if (model.isBlockedByInstructions()) {
String message =
"Cannot create data in " + program.getDomainFile().getPathname() +
" using " + model.getName() + " from " + model.getAddress() + " to " +
model.getEndAddress() + " since there are instructions in the way.";
handleErrorMessage(program, model.getAddress(), message);
return false;
}
}
catch (InvalidDataTypeException e) {
handleErrorMessage(program, model.getAddress(), e.getMessage());
return false;
}
createData();
boolean success = true;
// Create markup for the data just created.
try {
createMarkup();
}
catch (InvalidInputException e) {
// Catch the exception and output the error, but still should create
// associated data if possible, even though markup failed.
handleErrorMessage(program, name, address, address, e);
success = false;
}
// If following data when applying, create any data referred to by the data just created.
if (applyOptions.shouldFollowData() && !createAssociatedData()) {
success = false;
}
setStatusMsg(getName() + " completed successfully!");
return success;
}
catch (AddressOutOfBoundsException | CodeUnitInsertionException | DataTypeConflictException
| InvalidDataTypeException e) {
handleErrorMessage(program, name, address, address, e);
return false;
}
}
/**
* Creates data at this command's address using the data type obtained from the model.
* <br>If you need to create data other than by using the data type returned from getDataType(),
* you should override this method.
* @throws CodeUnitInsertionException if the data can't be created.
* @throws CancelledException if the user cancels this task.
*/
protected void createData() throws CodeUnitInsertionException, CancelledException {
Program program = model.getProgram();
Memory memory = program.getMemory();
DataType dt = getDataType();
if (dt == null) {
throw new CodeUnitInsertionException(
"Unable to get data type from model, " + model.getName() + ".");
}
if (!memory.getLoadedAndInitializedAddressSet().contains(address)) {
String message = "Can't create an " + dt.getName() + " @ " + address +
" which isn't in loaded and initialized memory for " + program.getName();
throw new CodeUnitInsertionException(message);
}
// When creating data, this will create an array with count number of elements
// of the model's data type if the data type obtained from the model hasn't
// already done so.
if (!model.isDataTypeAlreadyBasedOnCount() && count > 1) {
// If there are multiple then create as an array.
dt = new ArrayDataType(dt, count, dt.getLength(), program.getDataTypeManager());
}
monitor.checkCanceled();
// Is the data type already applied at the address?
if (matchingDataExists(dt, program, address)) {
return;
}
monitor.checkCanceled();
// Create data at the address using the datatype.
DataUtilities.createData(program, address, dt, dt.getLength(), false, getClearDataMode());
}
/**
* Check for data at the indicated address in the specified program and determine if it has
* the desired data type.
* @param dt the desired data type for the data
* @param program the program to be checked
* @param startAddress the address to check for the data
* @return true if data with the desired data type exists at the indicated address in the
* specified program.
*/
protected boolean matchingDataExists(DataType dt, Program program, Address startAddress) {
Listing listing = program.getListing();
Data dataAt = listing.getDataAt(startAddress);
DataTypeManager dataTypeManager = program.getDataTypeManager();
DataType resolvedDt = dataTypeManager.resolve(dt, DataTypeConflictHandler.DEFAULT_HANDLER);
if (dataAt != null && dataAt.getDataType() == resolvedDt) {
return true; // Already set to the desired data type.
}
return false;
}
/**
* Creates references, symbols, and functions for this data type as indicated by the options.
* @return true if all desired types of associated annotations were created.
* @throws CancelledException is thrown if the user cancels this command.
* @throws InvalidInputException if the model doesn't have valid information for creating
* some of the markup.
* @throws InvalidDataTypeException if this model or an associated model, which is needed
* for some of the markup, isn't valid
*/
protected abstract boolean createMarkup()
throws CancelledException, InvalidInputException, InvalidDataTypeException;
/**
* Creates the associated data that is indicated by the model's data type components.
* Also creates references, symbols, and functions as indicated by the options.
* @return true if all associated data was created that was desired.
* throws CancelledException is thrown if the user cancels this command.
*/
protected abstract boolean createAssociatedData() throws CancelledException;
/**
* Creates an error message that the named type of data structure couldn't be created at the
* indicated address and outputs the error message to the log and also as a status message for
* this command. This also creates a bookmark with the error message at the indicated address.
* @param program the program where the error bookmark should be created.
* @param dataName the data type name of the data that couldn't be created.
* @param dataAddress the address where the data couldn't be created.
* @param bookmarkAddress the address where the error bookmark should be created.
*/
protected void handleErrorMessage(Program program, String dataName, Address dataAddress,
Address bookmarkAddress) {
handleErrorMessage(program, dataName, dataAddress, bookmarkAddress, null);
}
/**
* Creates an error message that the named type of data structure couldn't be created at the
* indicated address and outputs the error message to the log and also as a status message for
* this command. This also creates a bookmark with the error message at the indicated address.
* @param program the program where the error bookmark should be created.
* @param dataName the data type name of the data that couldn't be created.
* @param dataAddress the address where the data couldn't be created.
* @param bookmarkAddress the address where the error bookmark should be created.
* @param e1 the exception whose message should be added to the error in the log. It's message
* gives additional details about why the data creation failed.
*/
protected void handleErrorMessage(Program program, String dataName, Address dataAddress,
Address bookmarkAddress, Exception e1) {
String message = "Couldn't create " + dataName + " data @ " + dataAddress + ".";
String detailedMessage =
(e1 == null || e1.getMessage() == null) ? message : (message + " " + e1.getMessage());
handleErrorMessage(program, bookmarkAddress, detailedMessage, message);
}
/**
* Output the error message to the log and as a status message for this command. This also
* creates a bookmark with the error message at the indicated address.
* @param program the program where the error bookmark should be created.
* @param bookmarkAddress the address where an error bookmark should be created.
* @param message the error message.
*/
protected void handleErrorMessage(Program program, Address bookmarkAddress, String message) {
handleErrorMessage(program, bookmarkAddress, message, message);
}
/**
* Output the error message to the log and output a possibly shorter status message for this
* command. This also creates a bookmark with the error message at the indicated address.
* @param program the program where the error bookmark should be created.
* @param bookmarkAddress the address where an error bookmark should be created.
* @param errorMessage the detailed error message.
* @param statusMessage an abbreviated error message that will appear as a status message.
*/
protected void handleErrorMessage(Program program, Address bookmarkAddress, String errorMessage,
String statusMessage) {
Msg.error(this, errorMessage);
setStatusMsg(statusMessage);
if (applyOptions.shouldCreateBookmarks()) {
BookmarkManager bookmarkManager = program.getBookmarkManager();
bookmarkManager.setBookmark(bookmarkAddress, BookmarkType.ERROR, "Data", errorMessage);
}
}
/**
* Output the error message to the log and as a status message for this command.
* @param message the error message.
*/
protected void handleError(String message) {
Msg.error(this, message);
setStatusMsg(message);
}
protected ClearDataMode getClearDataMode() {
return (applyOptions.shouldClearDefinedData()) ? ClearDataMode.CLEAR_ALL_CONFLICT_DATA
: ClearDataMode.CLEAR_ALL_UNDEFINED_CONFLICT_DATA;
}
}
@@ -0,0 +1,153 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
/**
* This command will create a TypeDescriptor data type. Since unsized arrays are not properly
* handled due to the current data type API limitations, this creates a dynamic RTTI0DataType.
*/
public class CreateTypeDescriptorBackgroundCmd
extends AbstractCreateDataBackgroundCmd<TypeDescriptorModel> {
private static final String RTTI_0_NAME = "RTTI Type Descriptor";
/**
* Constructs a command for applying a TypeDescriptor data type at an address using the
* default validation and apply options.
* @param address the address where the data should be created using the data type.
*/
public CreateTypeDescriptorBackgroundCmd(Address address) {
super(TypeDescriptorModel.DATA_TYPE_NAME, address, 1);
}
/**
* Constructs a command for applying a TypeDescriptor data type at an address using the
* indicated options.
* @param address the address where the data should be created using the data type.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateTypeDescriptorBackgroundCmd(Address address,
DataValidationOptions validationOptions, DataApplyOptions applyOptions) {
super(TypeDescriptorModel.DATA_TYPE_NAME, address, 1, validationOptions, applyOptions);
}
/**
* Constructs a command for applying a TypeDescriptor data type at the address indicated
* by the model and using the indicated options.
* @param model the model indicating the TypeDescriptor data to be created by this command.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateTypeDescriptorBackgroundCmd(TypeDescriptorModel model,
DataApplyOptions applyOptions) {
super(model, applyOptions);
}
private void loadModel(Program program) {
if (model == null || program != model.getProgram()) {
model = new TypeDescriptorModel(program, address, validationOptions);
}
}
@Override
protected TypeDescriptorModel createModel(Program program) {
if (model == null) {
loadModel(program);
}
return model;
}
/**
* Create the data corresponding to a RTTI0 TypeDescriptor structure which contains a flexible-array
* as its last component ( char[0] name ). The string data associated with this flexible char array will
* be applied as a sized character array immediately following the structure whose size does not include
* the char array bytes.
* @throws CodeUnitInsertionException
* @throws CancelledException
*/
@Override
protected void createData() throws CodeUnitInsertionException, CancelledException {
super.createData(); // create the TypeDesciptor structure
// Determine the size of the flexible char array storage and create properly sized array
DataType dataType = model.getDataType();
int structLen = dataType.getLength();
Address arrayAddr = model.getAddress().add(structLen);
DataType charArray =
new ArrayDataType(CharDataType.dataType, model.getDataTypeLength() - structLen, 1);
// Create 'name' char[0] data at the address immediately following structure
Program program = model.getProgram();
Data nameData = DataUtilities.createData(program, arrayAddr, charArray,
charArray.getLength(), false, getClearDataMode());
if (nameData != null) {
nameData.setComment(CodeUnit.EOL_COMMENT, "TypeDescriptor.name");
}
else {
Msg.error(this, "Failed to create TypeDescriptor name at " + arrayAddr);
}
}
@Override
protected boolean createAssociatedData() throws CancelledException {
// No associated data to create.
return true;
}
@Override
protected boolean createMarkup() throws CancelledException, InvalidInputException {
monitor.checkCanceled();
Program program = model.getProgram();
String demangledName = model.getDemangledTypeDescriptor();
if (demangledName == null) {
return false;
}
String prefix = demangledName + " ";
// Plate Comment
EHDataTypeUtilities.createPlateCommentIfNeeded(program, prefix, RTTI_0_NAME, null, address,
applyOptions);
monitor.checkCanceled();
// Label
EHDataTypeUtilities.createSymbolIfNeeded(program, prefix, RTTI_0_NAME, null, address,
applyOptions);
return true;
}
}
@@ -0,0 +1,382 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data;
import ghidra.app.cmd.disassemble.DisassembleCommand;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.DumbMemBufferImpl;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.scalar.Scalar;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg;
import ghidra.util.exception.InvalidInputException;
/**
* This class provides static utility methods for use with the exception handling models and
* data types.
*/
public class EHDataTypeUtilities {
private EHDataTypeUtilities() {
// utility class; can't create
}
/**
* If the indicated component in the data type exists and is an ehstate value, this returns
* the integer value contained in that component of the data type.
* @param dataType the data type whose base type is a structure and whose component's
* integer value is wanted.
* @param componentOrdinal 0-based ordinal indicating the component whose integer value is being
* determined by this method.
* @param memBuffer memory buffer that starts where the indicated data type is laid down.
* @return the integer value held by indicated component in the data type when laid down on
* the specified memory. If the value can't be determined, 0 is returned.
*/
public static int getEHStateValue(DataType dataType, int componentOrdinal,
MemBuffer memBuffer) {
return getIntegerValue(dataType, componentOrdinal, memBuffer);
}
/**
* If the indicated component in the data type exists and is a count value, this returns
* the integer value contained in that component of the data type.
* @param dataType the data type whose base type is a structure and whose component's
* integer value is wanted. (i.e., The component data type referenced by the ordinal value must
* be one that returns a Scalar value; such as an IntegerDataType, EnumDataType,
* UndefinedDataType, etc.)
* @param componentOrdinal 0-based ordinal indicating the component whose integer value is being
* determined by this method.
* @param memBuffer memory buffer that starts where the indicated data type is laid down.
* @return the integer value held by indicated component in the data type when laid down on
* the specified memory. If the value can't be determined, 0 is returned.
*/
public static int getCount(DataType dataType, int componentOrdinal, MemBuffer memBuffer) {
return getIntegerValue(dataType, componentOrdinal, memBuffer);
}
/**
* If the indicated component in the data type exists and is an integer value, this returns
* the integer value contained in that component of the data type.
* @param dataType the data type whose base type is a structure and whose component's
* integer value is wanted. (i.e., The component data type referenced by the ordinal value must
* be one that returns a Scalar value; such as an IntegerDataType, EnumDataType,
* UndefinedDataType, etc.)
* @param componentOrdinal 0-based ordinal indicating the component whose integer value is being
* determined by this method.
* @param memBuffer memory buffer that starts where the indicated data type is laid down.
* @return the integer value held by indicated component in the data type when laid down on
* the specified memory.
*/
public static int getIntegerValue(DataType dataType, int componentOrdinal,
MemBuffer memBuffer) {
Scalar scalar = getScalarValue(dataType, componentOrdinal, memBuffer);
return (int) scalar.getValue();
}
/**
* If the indicated component in the data type exists and is a Scalar value, this returns
* the scalar value contained in that component of the data type.
* @param dataType the data type whose base type is a structure and whose component's
* scalar value is wanted. (i.e., The component data type referenced by the ordinal value must
* be one that returns a Scalar value; such as an IntegerDataType, EnumDataType,
* UndefinedDataType, etc.)
* @param componentOrdinal 0-based ordinal indicating the component whose scalar value is being
* determined by this method.
* @param memBuffer memory buffer that starts where the indicated data type is laid down.
* @return the scalar value held by indicated component in the data type when laid down on
* the specified memory.
*/
public static Scalar getScalarValue(DataType dataType, int componentOrdinal,
MemBuffer memBuffer) {
DataTypeComponent comp = getComponent(dataType, componentOrdinal, memBuffer);
if (comp == null) {
throw new IllegalArgumentException("Couldn't get component " + componentOrdinal +
" of " + dataType.getName() + " @ " + memBuffer.getAddress() + ".");
}
Address compAddress = getComponentAddress(comp, memBuffer);
DataType compDt = comp.getDataType();
int length = comp.getLength();
DumbMemBufferImpl compMemBuffer = new DumbMemBufferImpl(memBuffer.getMemory(), compAddress);
Object value = compDt.getValue(compMemBuffer, comp.getDefaultSettings(), length);
if (value instanceof Scalar) {
return (Scalar) value;
}
throw new IllegalArgumentException(
"Component " + componentOrdinal + " of " + dataType.getName() + " is a " +
compDt.getName() + " data type, which doesn't produce a Scalar value.");
}
private static Address getComponentAddress(DataTypeComponent comp, MemBuffer memBuffer) {
int offset = comp.getOffset();
Address minAddress = memBuffer.getAddress();
try {
return minAddress.add(offset);
}
catch (AddressOutOfBoundsException e) {
throw new IllegalArgumentException("Can't get component " + comp.getOrdinal() +
" from memory buffer for data type " + comp.getParent().getName() + ".", e);
}
}
private static DataTypeComponent getComponent(DataType dataType, int componentOrdinal,
MemBuffer memBuffer) {
if (dataType == null) {
throw new IllegalArgumentException("Data type cannot be null.");
}
if (dataType instanceof DynamicDataType) {
DynamicDataType dynamicDt = (DynamicDataType) dataType;
return dynamicDt.getComponent(componentOrdinal, memBuffer);
}
if (dataType instanceof TypeDef) {
dataType = ((TypeDef) dataType).getBaseDataType();
}
if (!(dataType instanceof Structure)) {
throw new IllegalArgumentException("Data type " + dataType.getName() +
" must be a structure or a typedef on a structure.");
}
Structure struct = (Structure) dataType;
return struct.getComponent(componentOrdinal);
}
/**
* If the indicated component in the data type exists and is an absolute or relative value that
* equates to an address, this returns the address indicated by that component of the data type.
* @param dataType the data type whose base type is a structure and whose component's
* address value is wanted. (i.e., The component data type referenced by the ordinal value
* must be one that returns an Address value; such as a PointerDataType or
* ImageBaseOffset32DataType)
* @param componentOrdinal 0-based ordinal indicating the component whose address value is being
* determined by this method.
* @param memBuffer memory buffer that starts where the indicated data type is laid down.
* @return the address value held by indicated component in the data type when laid down on
* the specified memory or null if component is not used (i.e., value == 0).
*/
public static Address getAddress(DataType dataType, int componentOrdinal, MemBuffer memBuffer) {
DataTypeComponent comp = getComponent(dataType, componentOrdinal, memBuffer);
if (comp == null) {
throw new IllegalArgumentException("Couldn't get component " + componentOrdinal +
" of " + dataType.getName() + " @ " + memBuffer.getAddress() + ".");
}
Address compAddress = getComponentAddress(comp, memBuffer);
DataType compDt = comp.getDataType();
int length = comp.getLength();
DumbMemBufferImpl compMemBuffer = new DumbMemBufferImpl(memBuffer.getMemory(), compAddress);
Object value = compDt.getValue(compMemBuffer, comp.getDefaultSettings(), length);
if (value == null) {
return null;
}
if (value instanceof Address) {
return (Address) value;
}
throw new IllegalArgumentException(
"Component " + componentOrdinal + " of " + dataType.getName() + " is a " +
compDt.getName() + " data type, which doesn't produce an Address value.");
}
/**
* Gets the address of the indicated component in the data type that is placed in a program
* at the indicated memory buffer. If the specified address can't be determined then
* null is returned.
* @param dataType the data type whose base type is a structure and whose component's
* address is wanted.
* @param componentOrdinal 0-based ordinal indicating the component whose address is being
* determined by this method.
* @param memBuffer memory buffer that starts where the indicated data type is laid down.
* @return the component address or null.
*/
public static Address getComponentAddress(DataType dataType, int componentOrdinal,
MemBuffer memBuffer) {
DataTypeComponent comp = getComponent(dataType, componentOrdinal, memBuffer);
if (comp == null) {
throw new IllegalArgumentException("Couldn't get component " + componentOrdinal +
" of " + dataType.getName() + " @ " + memBuffer.getAddress() + ".");
}
return getComponentAddress(comp, memBuffer);
}
/**
* Creates a comment if it doesn't already exist at the specified address in the program
* and if it doesn't contain the <code>dataTypeName</code> string.
* The comment will contain the prefix, the <code>dataTypeName</code>, and the suffix.
* If a comment already exists without containing the dataTypeName,
* then this comment will be appended to the existing one.
*
* @param program the program.
* @param prefix the prefix string that precedes the dataTypeName
* @param dataTypeName the name for the data type at the indicated address.
* @param suffix the suffix that follows the dataTypeName
* @param address the address where the plate comment should be created in the program.
* @param applyOptions options indicating whether or not to apply comments.
*
* @return the comment or null.
*/
public static String createPlateCommentIfNeeded(Program program, String prefix,
String dataTypeName, String suffix, Address address, DataApplyOptions applyOptions) {
Listing listing = program.getListing();
String existingComment = listing.getComment(CodeUnit.PLATE_COMMENT, address);
if (!applyOptions.shouldCreateComments()) {
return existingComment;
}
if (dataTypeName != null) {
if (existingComment != null && existingComment.contains(dataTypeName)) {
return existingComment;
}
String appliedPrefix = (prefix != null) ? (prefix) : "";
String appliedSuffix = (suffix != null) ? (suffix) : "";
String appliedExisting = (existingComment != null) ? (existingComment + "\n") : "";
String appliedComment = appliedExisting + appliedPrefix + dataTypeName + appliedSuffix;
listing.setComment(address, CodeUnit.PLATE_COMMENT, appliedComment);
return appliedComment;
}
return existingComment;
}
/**
* Creates a symbol if one containing the <code>dataTypeName</code> doesn't already exist at
* the specified address in the program.
*
* @param program the program.
* @param prefix the symbol prefix to be used in the symbol name.
* @param dataTypeName the dataTypeName to be used in the symbol name.
* @param suffix the symbol suffix to be used in the symbol name.
* @param address the address where the symbol should be created in the program.
* @param applyOptions options indicating whether or not to apply comments.
*
* @return the symbol or null.
*
* @throws InvalidInputException thrown if symbol can't be created as specified.
*/
public static Symbol createSymbolIfNeeded(Program program, String prefix, String dataTypeName,
String suffix, Address address, DataApplyOptions applyOptions)
throws InvalidInputException {
if (dataTypeName == null || !applyOptions.shouldCreateLabel()) {
return null;
}
// Make sure we have underscores in name
dataTypeName = SymbolUtilities.replaceInvalidChars(dataTypeName, true);
SymbolTable symbolTable = program.getSymbolTable();
Symbol[] symbols = symbolTable.getSymbols(address);
for (Symbol symbol : symbols) {
if (symbol.getName().contains(dataTypeName)) {
return null; // Already have one with dataTypeName.
}
}
String appliedPrefix = (prefix != null) ? (prefix) : "";
String appliedSuffix = (suffix != null) ? (suffix) : "";
String appliedSymbol = appliedPrefix + dataTypeName + appliedSuffix;
appliedSymbol = SymbolUtilities.replaceInvalidChars(appliedSymbol, true);
return symbolTable.createLabel(address, appliedSymbol, SourceType.ANALYSIS);
}
/**
* Creates a symbol if it doesn't already exist at the specified address in the program
* by adding an underscore and the address to the symbolPrefix string that is passed to it.
* @param program the program.
* @param symbolPrefix the symbol prefix to be used in the symbol name.
* @param symbolAddress the address where the symbol should be created in the program.
* @return the symbol or null.
* @throws InvalidInputException thrown if symbol can't be created as specified.
*/
public static Symbol createSymbolIfNeeded(Program program, String symbolPrefix,
Address symbolAddress) throws InvalidInputException {
SymbolTable symbolTable = program.getSymbolTable();
Symbol primarySymbol = symbolTable.getPrimarySymbol(symbolAddress);
if (primarySymbol != null && primarySymbol.getSource() != SourceType.DEFAULT) {
return null; // Not needed. Non-default symbol already there.
}
String addressAppendedName =
SymbolUtilities.getAddressAppendedName(symbolPrefix, symbolAddress);
return symbolTable.createLabel(symbolAddress, addressAppendedName, SourceType.ANALYSIS);
}
/**
* Creates a function at the specified address in the program if there isn't already one there
* and if it can be created.
* @param program the program
* @param functionAddress the entry point address.
* @return true if function was created or already exists.
*/
public static boolean createFunctionIfNeeded(Program program, Address functionAddress) {
// If there isn't an instruction at the function address yet, then disassemble there.
Listing listing = program.getListing();
Instruction inst = listing.getInstructionAt(functionAddress);
if (inst == null) {
DisassembleCommand cmd = new DisassembleCommand(functionAddress, null, true);
if (!cmd.applyTo(program) || cmd.getDisassembledAddressSet().isEmpty()) {
Msg.error(EHDataTypeUtilities.class, "Failed to disassemble at " + functionAddress);
return false;
}
}
// If there isn't a function at the function address yet, then try to create one there.
FunctionManager functionManager = program.getFunctionManager();
Function function = functionManager.getFunctionAt(functionAddress);
if (function == null) {
CreateFunctionCmd cmd = new CreateFunctionCmd(functionAddress);
if (!cmd.applyTo(program)) {
Msg.error(EHDataTypeUtilities.class,
"Failed to create function at " + functionAddress);
return false;
}
}
return true;
}
/**
* Determines if the specified address is a valid address in the program's memory.
* @param program the program to check.
* @param address the address to check.
* @return true if the address is valid address in memory.
*/
public static boolean isValidAddress(Program program, Address address) {
if (address == null) {
throw new IllegalArgumentException("address cannot be null.");
}
return program.getMemory().getLoadedAndInitializedAddressSet().contains(address);
}
/**
* Checks to determine if the address in the indicated program exists in memory and
* could possibly be used for creating a function. It is expected to have undefined data
* or an instruction at the address.
* @param program the program to check.
* @param functionAddress the address to check.
* @return true if it appears to be a valid (very loosely defined) location for a function.
*/
public static boolean isValidForFunction(Program program, Address functionAddress) {
if (functionAddress == null) {
throw new IllegalArgumentException("functionAddress cannot be null.");
}
if (!isValidAddress(program, functionAddress)) {
return false;
}
Listing listing = program.getListing();
// Should be instruction or undefined data.
return (listing.getInstructionAt(functionAddress) != null) ||
(listing.getUndefinedDataAt(functionAddress) != null);
}
}
@@ -0,0 +1,205 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
import ghidra.app.cmd.data.*;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.exception.*;
/**
* This command will create a HandlerType exception handler data type or an array of them.
* If there are any existing instructions in the area to be made into data, the command will fail.
* Any data in the area will be replaced with the new dataType.
*/
public class CreateEHCatchHandlerMapBackgroundCmd
extends AbstractCreateDataBackgroundCmd<EHCatchHandlerModel> {
/**
* Constructs a command for applying a HandlerType exception handling data type at an
* address.
* @param address the address where the data should be created using the data type.
* @param count the number of the indicated data type to create.
*/
public CreateEHCatchHandlerMapBackgroundCmd(Address address, int count) {
super(EHCatchHandlerModel.DATA_TYPE_NAME, address, count);
}
/**
* Constructs a command for applying a HandlerType exception handling data type at an
* address.
* @param address the address where the data should be created using the data type.
* @param count the number of the indicated data type to create.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateEHCatchHandlerMapBackgroundCmd(Address address, int count,
DataValidationOptions validationOptions, DataApplyOptions applyOptions) {
super(EHCatchHandlerModel.DATA_TYPE_NAME, address, count, validationOptions, applyOptions);
}
/**
* Constructs a command for applying a HandlerType exception handling data type at the
* address indicated by the model.
* @param catchHandlerModel the model for the data type
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
CreateEHCatchHandlerMapBackgroundCmd(EHCatchHandlerModel catchHandlerModel,
DataApplyOptions applyOptions) {
super(catchHandlerModel, applyOptions);
}
@Override
protected EHCatchHandlerModel createModel(Program program) {
if (model == null) {
model = new EHCatchHandlerModel(program, count, address, validationOptions);
}
return model;
}
@Override
protected boolean createAssociatedData() throws CancelledException {
return createTypeDescriptors();
}
/**
* Creates the associated TypeDescriptor data.
* @param program the program where this command is applying the data.
* @param monitor the task monitor for cancelling creation of the TypeDescriptor.
* @return true if successful.
* @throws CancelledException if the user cancels this task.
*/
private boolean createTypeDescriptors() throws CancelledException {
monitor.setMessage("Creating TypeDescriptors for HandlerTypes");
boolean result = true;
Program program = model.getProgram();
for (int catchHandlerOrdinal = 0; catchHandlerOrdinal < count; catchHandlerOrdinal++) {
monitor.checkCanceled();
Address compAddress;
Address typeDescriptorAddress;
try {
compAddress = model.getComponentAddressOfTypeDescriptorAddress(catchHandlerOrdinal);
typeDescriptorAddress = model.getTypeDescriptorAddress(catchHandlerOrdinal);
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
if (typeDescriptorAddress == null) {
continue; // No type descriptor for this HandlerType record.
}
TypeDescriptorModel typeDescriptorModel;
try {
typeDescriptorModel = model.getTypeDescriptorModel(catchHandlerOrdinal);
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
try {
typeDescriptorModel.validate();
}
catch (InvalidDataTypeException e1) {
handleErrorMessage(program, typeDescriptorModel.getName(),
typeDescriptorModel.getAddress(), compAddress, e1);
result = false;
continue;
}
int typeDescriptorCount = typeDescriptorModel.getCount();
if (typeDescriptorCount == 0) {
continue; // No type descriptor for this HandlerType record.
}
CreateTypeDescriptorBackgroundCmd cmd =
new CreateTypeDescriptorBackgroundCmd(typeDescriptorModel, applyOptions);
result &= cmd.applyTo(program, monitor);
}
return result;
}
@Override
protected boolean createMarkup() throws CancelledException {
return createHandlerRefsAndSymbols();
}
/**
* Creates data references to the catch handler function and based on options it creates a
* label for the catch handler function, disassembles it, and creates the function.
* @param program the program where this command is creating references and symbols.
* @param monitor the task monitor for cancelling creation of the catch handler reference,
* label, and function.
* @return true if successful.
* @throws CancelledException if the user cancels this task.
*/
private boolean createHandlerRefsAndSymbols() throws CancelledException {
monitor.setMessage("Creating catch handler markup");
Program program = model.getProgram();
boolean result = true;
EHCatchHandlerModel catchHandlerModel = createModel(program);
for (int catchHandlerOrdinal = 0; catchHandlerOrdinal < count; catchHandlerOrdinal++) {
monitor.checkCanceled();
Address compAddress;
Address refAddress;
try {
compAddress =
catchHandlerModel.getComponentAddressOfCatchHandlerAddress(catchHandlerOrdinal);
refAddress = catchHandlerModel.getCatchHandlerAddress(catchHandlerOrdinal);
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
if (refAddress == null) {
continue; // No catch handler for this HandlerType record.
}
if (applyOptions.shouldCreateLabel()) {
String catchHandlerName;
try {
catchHandlerName = catchHandlerModel.getCatchHandlerName(catchHandlerOrdinal);
Symbol symbol = EHDataTypeUtilities.createSymbolIfNeeded(program,
catchHandlerName, refAddress);
if (symbol == null) {
result = false;
}
}
catch (InvalidDataTypeException | InvalidInputException e) {
String message =
"Couldn't create name for catch handler at " + refAddress.toString() + ".";
handleErrorMessage(program, compAddress, message + " " + e.getMessage(),
message);
result = false;
}
}
if (applyOptions.shouldCreateFunction()) {
boolean success = EHDataTypeUtilities.createFunctionIfNeeded(program, refAddress);
if (!success) {
result = false;
}
}
}
return result;
}
}
@@ -0,0 +1,133 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
import ghidra.app.cmd.data.AbstractCreateDataBackgroundCmd;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* This command will create a ESTypeList exception handler data type.
* If there are any existing instructions in the area to be made into data, the command will fail.
* Any data in the area will be replaced with the new dataType.
*/
public class CreateEHESTypeListBackgroundCmd
extends AbstractCreateDataBackgroundCmd<EHESTypeListModel> {
/**
* Constructs a command for applying an ESTypeList exception handling data type at an address.
* @param address the address where the data should be created using the data type.
*/
public CreateEHESTypeListBackgroundCmd(Address address) {
super(EHESTypeListModel.DATA_TYPE_NAME, address, 1);
}
/**
* Constructs a command for applying an ESTypeList exception handling data type at an address.
* @param address the address where the data should be created using the data type.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateEHESTypeListBackgroundCmd(Address address, DataValidationOptions validationOptions,
DataApplyOptions applyOptions) {
super(EHESTypeListModel.DATA_TYPE_NAME, address, 1, validationOptions, applyOptions);
}
/**
* Constructs a command for applying a ESTypeList exception handling data type at the
* address indicated by the model.
* @param esTypeListModel the model for the data type
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
CreateEHESTypeListBackgroundCmd(EHESTypeListModel esTypeListModel,
DataApplyOptions applyOptions) {
super(esTypeListModel, applyOptions);
}
@Override
protected EHESTypeListModel createModel(Program program) {
if (model == null) {
model = new EHESTypeListModel(program, address, validationOptions);
}
return model;
}
@Override
protected boolean createAssociatedData() throws CancelledException {
return createCatchHandlerMapEntries();
}
private boolean createCatchHandlerMapEntries() throws CancelledException {
monitor.setMessage("Creating HandlerTypes for ESTypeList");
Program program = model.getProgram();
Address compAddress;
Address handlerTypeMapAddress;
int catchHandlerCount;
try {
compAddress = model.getComponentAddressOfHandlerTypeMapAddress();
handlerTypeMapAddress = model.getHandlerTypeMapAddress();
catchHandlerCount = model.getHandlerTypeCount();
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
if (handlerTypeMapAddress == null || (catchHandlerCount == 0)) {
return true; // No catch handler info to create.
}
monitor.checkCanceled();
EHCatchHandlerModel catchHandlerModel;
try {
catchHandlerModel = model.getCatchHandlerModel();
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
try {
catchHandlerModel.validate();
}
catch (InvalidDataTypeException e1) {
handleErrorMessage(program, catchHandlerModel.getName(), handlerTypeMapAddress,
compAddress, e1);
return false;
}
monitor.checkCanceled();
CreateEHCatchHandlerMapBackgroundCmd cmd =
new CreateEHCatchHandlerMapBackgroundCmd(catchHandlerModel, applyOptions);
return cmd.applyTo(program, monitor);
}
@Override
protected boolean createMarkup() throws CancelledException {
return true; // No markup.
}
}
@@ -0,0 +1,253 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
import ghidra.app.cmd.data.AbstractCreateDataBackgroundCmd;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.lang.UndefinedValueException;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* This command will create a FuncInfo exception handler data type.
* If there are any existing instructions in the area to be made into data, the command will fail.
* Any data in the area will be replaced with the new dataType.
*/
public class CreateEHFuncInfoBackgroundCmd
extends AbstractCreateDataBackgroundCmd<EHFunctionInfoModel> {
/**
* Constructs a command for applying a FuncInfo exception handling dataType at an address.
* @param address the address where the data should be created using the data type.
*/
public CreateEHFuncInfoBackgroundCmd(Address address) {
super(EHFunctionInfoModel.DATA_TYPE_NAME, address, 1);
}
/**
* Constructs a command for applying a FuncInfo exception handling dataType at an address.
* @param address the address where the data should be created using the data type.
* @param validationvalidationOptions, applyOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateEHFuncInfoBackgroundCmd(Address address, DataValidationOptions validationOptions,
DataApplyOptions applyOptions) {
super(EHFunctionInfoModel.DATA_TYPE_NAME, address, 1, validationOptions, applyOptions);
}
/**
* Constructs a command for applying a FuncInfo exception handling data type at the
* address indicated by the model.
* @param funcInfoModel the model for the data type
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
CreateEHFuncInfoBackgroundCmd(EHFunctionInfoModel funcInfoModel,
DataApplyOptions applyOptions) {
super(funcInfoModel, applyOptions);
}
@Override
protected EHFunctionInfoModel createModel(Program program) {
if (model == null) {
model = new EHFunctionInfoModel(program, address, validationOptions);
}
return model;
}
@Override
protected boolean createAssociatedData() throws CancelledException {
// If this is being called then the model should be valid.
boolean unwindMapSuccess = createUnwindMapEntries();
boolean tryBlockMapSuccess = createTryBlockMapEntries();
boolean ipToStateMapSuccess = createIPToStateMapEntries();
boolean typeListSuccess = createESTypeListEntries();
return unwindMapSuccess && tryBlockMapSuccess && ipToStateMapSuccess && typeListSuccess;
}
private boolean createUnwindMapEntries() throws CancelledException {
monitor.setMessage("Creating UnwindMap");
monitor.checkCanceled();
Address compAddress;
Address unwindMapAddress;
int unwindCount;
try {
compAddress = model.getComponentAddressOfUnwindMapAddress();
unwindMapAddress = model.getUnwindMapAddress();
unwindCount = model.getUnwindCount();
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
if (unwindMapAddress == null || (unwindCount == 0)) {
return true; // No unwind info to create.
}
EHUnwindModel unwindModel;
try {
unwindModel = model.getUnwindModel();
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
try {
unwindModel.validate();
}
catch (InvalidDataTypeException e1) {
handleErrorMessage(model.getProgram(), unwindModel.getName(), unwindMapAddress,
compAddress, e1);
return false;
}
CreateEHUnwindMapBackgroundCmd cmd =
new CreateEHUnwindMapBackgroundCmd(unwindModel, applyOptions);
return cmd.applyTo(model.getProgram(), monitor);
}
private boolean createTryBlockMapEntries() throws CancelledException {
monitor.setMessage("Creating TryBlockMap");
monitor.checkCanceled();
Address compAddress;
Address tryBlockMapAddress;
int tryBlockCount;
try {
compAddress = model.getComponentAddressOfTryBlockMapAddress();
tryBlockMapAddress = model.getTryBlockMapAddress();
tryBlockCount = model.getTryBlockCount();
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
if (tryBlockMapAddress == null || tryBlockCount == 0) {
return true; // No try block info to create.
}
EHTryBlockModel tryBlockModel;
try {
tryBlockModel = model.getTryBlockModel();
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
try {
tryBlockModel.validate();
}
catch (InvalidDataTypeException e1) {
handleErrorMessage(model.getProgram(), tryBlockModel.getName(), tryBlockMapAddress,
compAddress, e1);
return false;
}
CreateEHTryBlockMapBackgroundCmd cmd =
new CreateEHTryBlockMapBackgroundCmd(tryBlockModel, applyOptions);
return cmd.applyTo(model.getProgram(), monitor);
}
private boolean createIPToStateMapEntries() throws CancelledException {
monitor.setMessage("Creating IPToStateMap");
monitor.checkCanceled();
Address compAddress;
Address ipToStateMapAddress;
int ipToStateCount;
try {
compAddress = model.getComponentAddressOfIPToStateMapAddress();
ipToStateMapAddress = model.getIPToStateMapAddress();
ipToStateCount = model.getIPToStateCount();
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
if (ipToStateMapAddress == null || ipToStateCount == 0) {
return true; // No IP to state info to create.
}
EHIPToStateModel ipToStateModel;
try {
ipToStateModel = model.getIPToStateModel();
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
try {
ipToStateModel.validate();
}
catch (InvalidDataTypeException e1) {
handleErrorMessage(model.getProgram(), ipToStateModel.getName(), ipToStateMapAddress,
compAddress, e1);
return false;
}
CreateEHIPToStateMapBackgroundCmd cmd =
new CreateEHIPToStateMapBackgroundCmd(ipToStateModel, applyOptions);
return cmd.applyTo(model.getProgram(), monitor);
}
private boolean createESTypeListEntries() throws CancelledException {
monitor.setMessage("Creating ESTypeList");
monitor.checkCanceled();
Address compAddress;
Address esTypeListAddress;
EHESTypeListModel esTypeListModel;
try {
compAddress = model.getComponentAddressOfESTypeListAddress();
esTypeListAddress = model.getESTypeListAddress();
esTypeListModel = model.getESTypeListModel();
}
catch (UndefinedValueException e) {
return true; // No ES type list to create.
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
if (esTypeListAddress == null) {
return true; // No ES type list to create.
}
try {
esTypeListModel.validate();
}
catch (InvalidDataTypeException e1) {
handleErrorMessage(model.getProgram(), esTypeListModel.getName(), esTypeListAddress,
compAddress, e1);
return false;
}
CreateEHESTypeListBackgroundCmd cmd =
new CreateEHESTypeListBackgroundCmd(esTypeListModel, applyOptions);
return cmd.applyTo(model.getProgram(), monitor);
}
@Override
protected boolean createMarkup() throws CancelledException {
return true; // No markup.
}
}
@@ -0,0 +1,99 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
import ghidra.app.cmd.data.AbstractCreateDataBackgroundCmd;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.CancelledException;
/**
* This command will create a IPToStateMapEntry exception handler data type or an array of them.
* If there are any existing instructions in the area to be made into data, the command will fail.
* Any data in the area will be replaced with the new dataType.
*/
public class CreateEHIPToStateMapBackgroundCmd
extends AbstractCreateDataBackgroundCmd<EHIPToStateModel> {
/**
* Constructs a command for applying an IPToStateMapEntry exception handling data type at an
* address.
* @param address the address where the data should be created using the data type.
* @param count the number of the indicated data type to create.
*/
public CreateEHIPToStateMapBackgroundCmd(Address address, int count) {
super(EHIPToStateModel.DATA_TYPE_NAME, address, count);
}
/**
* Constructs a command for applying an IPToStateMapEntry exception handling data type at an
* address.
* @param address the address where the data should be created using the data type.
* @param count the number of the indicated data type to create.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateEHIPToStateMapBackgroundCmd(Address address, int count,
DataValidationOptions validationOptions, DataApplyOptions applyOptions) {
super(EHIPToStateModel.DATA_TYPE_NAME, address, count, validationOptions, applyOptions);
}
/**
* Constructs a command for applying a IPToStateMapEntry exception handling data type at the
* address indicated by the model.
* @param ipToStateModel the model for the data type
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
CreateEHIPToStateMapBackgroundCmd(EHIPToStateModel ipToStateModel,
DataApplyOptions applyOptions) {
super(ipToStateModel, applyOptions);
}
@Override
protected EHIPToStateModel createModel(Program program) {
if (model == null) {
model = new EHIPToStateModel(program, count, address, validationOptions);
}
return model;
}
@Override
protected boolean createAssociatedData() throws CancelledException {
return createIpRefs();
}
private boolean createIpRefs() {
// NOTE: Current components which utilize ibo32 get a reference created
// automatically by the CodeManager. Components which produce Scalar values
// (e.g., ULONG) are ignored.
return true;
}
@Override
protected boolean createMarkup() throws CancelledException {
return true; // No markup.
}
}
@@ -0,0 +1,141 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
import ghidra.app.cmd.data.AbstractCreateDataBackgroundCmd;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
/**
* This command will create a TryBlockMapEntry exception handler data type or an array of them.
* If there are any existing instructions in the area to be made into data, the command will fail.
* Any data in the area will be replaced with the new dataType.
*/
public class CreateEHTryBlockMapBackgroundCmd
extends AbstractCreateDataBackgroundCmd<EHTryBlockModel> {
/**
* Constructs a command for applying a TryBlockMapEntry exception handling data type at an
* address.
* @param address the address where the data should be created using the data type.
* @param count the number of the indicated data type to create.
*/
public CreateEHTryBlockMapBackgroundCmd(Address address, int count) {
super(EHTryBlockModel.DATA_TYPE_NAME, address, count);
}
/**
* Constructs a command for applying a TryBlockMapEntry exception handling data type at an
* address.
* @param address the address where the data should be created using the data type.
* @param count the number of the indicated data type to create.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateEHTryBlockMapBackgroundCmd(Address address, int count,
DataValidationOptions validationOptions, DataApplyOptions applyOptions) {
super(EHTryBlockModel.DATA_TYPE_NAME, address, count, validationOptions, applyOptions);
}
/**
* Constructs a command for applying a TryBlockMapEntry exception handling data type at the
* address indicated by the model.
* @param tryBlockModel the model for the data type
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
CreateEHTryBlockMapBackgroundCmd(EHTryBlockModel tryBlockModel, DataApplyOptions applyOptions) {
super(tryBlockModel, applyOptions);
}
@Override
protected EHTryBlockModel createModel(Program program) {
if (model == null) {
model = new EHTryBlockModel(program, count, address, validationOptions);
}
return model;
}
@Override
protected boolean createAssociatedData() throws CancelledException {
return createCatchHandlerMapEntries();
}
private boolean createCatchHandlerMapEntries() throws CancelledException {
monitor.setMessage("Creating HandlerTypes from TryBlockMap");
boolean result = true;
Program program = model.getProgram();
for (int tryBlockEntryOrdinal = 0; tryBlockEntryOrdinal < count; tryBlockEntryOrdinal++) {
monitor.checkCanceled();
Address compAddress;
Address catchHandlerMapAddress;
int catchHandlerCount;
try {
compAddress =
model.getComponentAddressOfCatchHandlerMapAddress(tryBlockEntryOrdinal);
catchHandlerMapAddress = model.getCatchHandlerMapAddress(tryBlockEntryOrdinal);
catchHandlerCount = model.getCatchHandlerCount(tryBlockEntryOrdinal);
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
if (catchHandlerMapAddress == null || (catchHandlerCount == 0)) {
continue; // No catch handler info to create.
}
EHCatchHandlerModel catchHandlerModel;
try {
catchHandlerModel = model.getCatchHandlerModel(tryBlockEntryOrdinal);
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
try {
catchHandlerModel.validate();
}
catch (InvalidDataTypeException e1) {
handleErrorMessage(program, catchHandlerModel.getName(), catchHandlerMapAddress,
compAddress, e1);
result = false;
continue;
}
monitor.checkCanceled();
CreateEHCatchHandlerMapBackgroundCmd cmd =
new CreateEHCatchHandlerMapBackgroundCmd(catchHandlerModel, applyOptions);
result &= cmd.applyTo(program, monitor);
}
return result;
}
@Override
protected boolean createMarkup() throws CancelledException {
return true; // No markup.
}
}
@@ -0,0 +1,137 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
import ghidra.app.cmd.data.AbstractCreateDataBackgroundCmd;
import ghidra.app.cmd.data.EHDataTypeUtilities;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import ghidra.util.exception.*;
/**
* This command will create a UnwindMapEntry exception handler data type or an array of them.
* If there are any existing instructions in the area to be made into data, the command will fail.
* Any data in the area will be replaced with the new dataType.
*/
public class CreateEHUnwindMapBackgroundCmd extends AbstractCreateDataBackgroundCmd<EHUnwindModel> {
/**
* Constructs a command for applying an UnwindMapEntry exception handling data type at an
* address.
* @param address the address where the data should be created using the data type.
* @param count the number of the indicated data type to create.
*/
public CreateEHUnwindMapBackgroundCmd(Address address, int count) {
super(EHUnwindModel.DATA_TYPE_NAME, address, count);
}
/**
* Constructs a command for applying an UnwindMapEntry exception handling data type at an
* address.
* @param address the address where the data should be created using the data type.
* @param count the number of the indicated data type to create.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateEHUnwindMapBackgroundCmd(Address address, int count,
DataValidationOptions validationOptions, DataApplyOptions applyOptions) {
super(EHUnwindModel.DATA_TYPE_NAME, address, count, validationOptions, applyOptions);
}
/**
* Constructs a command for applying a UnwindMapEntry exception handling data type at the
* address indicated by the model.
* @param unwindModel the model for the data type
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
CreateEHUnwindMapBackgroundCmd(EHUnwindModel unwindModel, DataApplyOptions applyOptions) {
super(unwindModel, applyOptions);
}
@Override
protected EHUnwindModel createModel(Program program) {
if (model == null) {
model = new EHUnwindModel(program, count, address, validationOptions);
}
return model;
}
@Override
protected boolean createAssociatedData() throws CancelledException {
return createActionRefsAndSymbols();
}
private boolean createActionRefsAndSymbols() throws CancelledException {
monitor.setMessage("Creating Unwind action markup");
boolean result = true;
Program program = model.getProgram();
for (int unwindEntryOrdinal = 0; unwindEntryOrdinal < count; unwindEntryOrdinal++) {
monitor.checkCanceled();
Address compAddress;
Address actionAddress;
try {
compAddress = model.getComponentAddressOfActionAddress(unwindEntryOrdinal);
actionAddress = model.getActionAddress(unwindEntryOrdinal);
}
catch (InvalidDataTypeException e) {
throw new AssertException(e); // Shouldn't happen. create...() is only called if model is valid.
}
if (actionAddress == null) {
continue; // No unwind action address for this UnwindMap record.
}
if (applyOptions.shouldCreateLabel()) {
try {
Symbol symbol =
EHDataTypeUtilities.createSymbolIfNeeded(program, "Unwind", actionAddress);
if (symbol == null) {
result = false;
}
}
catch (InvalidInputException e) {
String message = "Couldn't create name for unwind action at " +
actionAddress.toString() + ".";
handleErrorMessage(program, compAddress, message + " " + e.getMessage(),
message);
result = false;
}
}
if (applyOptions.shouldCreateFunction()) {
boolean success =
EHDataTypeUtilities.createFunctionIfNeeded(program, actionAddress);
if (!success) {
result = false;
}
}
}
return result;
}
@Override
protected boolean createMarkup() throws CancelledException {
return true; // No markup.
}
}
@@ -0,0 +1,395 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import ghidra.app.cmd.data.*;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.exception.AssertException;
/**
* Model for exception handling information about the HandlerType data type and its
* associated exception handling data types.
* <br>
* This is based on data type information from ehdata.h
*/
public class EHCatchHandlerModel extends AbstractCreateDataTypeModel {
public static final String DATA_TYPE_NAME = "HandlerType";
private static String STRUCTURE_NAME = STRUCT_PREFIX + DATA_TYPE_NAME;
private static final int ADJECTIVES_ORDINAL = 0;
private static final int TYPE_DESCRIPTOR_ORDINAL = 1;
private static final int CATCH_OBJECT_ORDINAL = 2;
private static final int HANDLER_ORDINAL = 3;
private static final int FUNCTION_FRAME_ORDINAL = 4;
private DataType dataType;
/**
* Creates the model for the exception handling HandlerType data type.
* @param program the program
* @param catchHandlerCount the number of HandlerType data types expected at the map address.
* @param catchHandlerMapAddress the address in the program for the map of HandlerType data
* types.
*/
public EHCatchHandlerModel(Program program, int catchHandlerCount,
Address catchHandlerMapAddress, DataValidationOptions validationOptions) {
super(program, catchHandlerCount, catchHandlerMapAddress, validationOptions);
}
@Override
public String getName() {
return DATA_TYPE_NAME;
}
/**
* Whether or not the memory at the indicated address appears to be a valid location for the
* indicated number of HandlerType data types.
* @throws InvalidDataTypeException if this model's location does not appear to be a valid
* group of catch handler entries. The exception has a message indicating
* why it does not appear to be a valid location for the data type.
*/
@Override
protected void validateModelSpecificInfo() throws InvalidDataTypeException {
// Does each catch handler map entry refer to a valid catch handler and type descriptor?
Program program = getProgram();
int numEntries = getCount();
for (int catchHandlerOrdinal = 0; catchHandlerOrdinal < numEntries; catchHandlerOrdinal++) {
Address catchHandlerAddress = getCatchHandlerAddress(catchHandlerOrdinal);
if (catchHandlerAddress == null ||
!EHDataTypeUtilities.isValidForFunction(program, catchHandlerAddress)) {
throw new InvalidDataTypeException(getName() + " data type at " + getAddress() +
" doesn't refer to a valid location for a catch handler for entry " +
catchHandlerOrdinal + ".");
}
Address typeDescriptorAddress = getTypeDescriptorAddress(catchHandlerOrdinal);
if (typeDescriptorAddress != null &&
!EHDataTypeUtilities.isValidAddress(program, typeDescriptorAddress)) {
throw new InvalidDataTypeException(getName() + " data type at " + getAddress() +
" doesn't refer to a valid location for the type descriptor for entry " +
catchHandlerOrdinal + ".");
}
}
}
/**
* This gets the HandlerType structure for the indicated program.
* @param program the program which will contain this data type.
* @return the HandlerType structure.
*/
public static DataType getDataType(Program program) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean isRelative = isRelative(program);
CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH);
StructureDataType struct =
getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME);
// Add the components.
DataType compDt;
/* comps[0] */
compDt = new UnsignedIntegerDataType(dataTypeManager);
struct.add(compDt, "adjectives", null);
/* comps[1] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
struct.add(compDt, "dispType", null);
}
else {
compDt = new PointerDataType(TypeDescriptorModel.getDataType(program), dataTypeManager);
struct.add(compDt, "pType", null);
}
/* comps[2] */
if (isRelative) {
compDt = new IntegerDataType(dataTypeManager);
}
else {
compDt = new TypedefDataType(new CategoryPath("/crtdefs.h"), "ptrdiff_t",
new IntegerDataType(dataTypeManager), dataTypeManager);
}
struct.add(compDt, "dispCatchObj", null);
/* comps[3] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
struct.add(compDt, "dispOfHandler", null);
}
else {
compDt = new PointerDataType(new VoidDataType(dataTypeManager), dataTypeManager);
struct.add(compDt, "addressOfHandler", null);
}
// Only some 64 bit programs have this displacement of the address of the function frame.
/* comps[4] */
if (isRelative) { // Needs more checking here still. Incorrectly puts it in all 64 bit programs.
compDt = new DWordDataType(dataTypeManager);
struct.add(compDt, "dispFrame", null);
}
TypedefDataType typeDefDt =
new TypedefDataType(categoryPath, DATA_TYPE_NAME, struct, dataTypeManager);
return MSDataTypeUtils.getMatchingDataType(program, typeDefDt);
}
/**
* This gets the HandlerType structure for this model.
* @return the HandlerType structure.
*/
@Override
public DataType getDataType() {
if (dataType == null) {
dataType = getDataType(getProgram());
}
return dataType;
}
@Override
protected int getDataTypeLength() {
return getDataType().getLength();
}
/**
* Gets the type descriptor model for the type descriptor address in the
* HandlerType entry indicated by the ordinal.
* @param catchHandlerOrdinal 0-based ordinal indicating which HandlerType entry in the map.
* @return the model for the type descriptor, if there is one.
* @throws InvalidDataTypeException if valid HandlerType data can't be created for
* the indicated ordinal.
*/
public TypeDescriptorModel getTypeDescriptorModel(int catchHandlerOrdinal)
throws InvalidDataTypeException {
checkValidity(catchHandlerOrdinal);
Address typeDescriptorAddress = getTypeDescriptorAddress(catchHandlerOrdinal);
if (typeDescriptorAddress != null) {
return new TypeDescriptorModel(getProgram(), typeDescriptorAddress, validationOptions);
}
return null;
}
/**
* Gets the address, if there is one, of the type descriptor address in the
* HandlerType entry indicated by the ordinal. Otherwise, this returns null.
* @param catchHandlerOrdinal 0-based ordinal indicating which HandlerType entry in the map.
* @return the address of the type descriptor or null.
* @throws InvalidDataTypeException if valid HandlerType data can't be created for
* the indicated ordinal.
*/
public Address getTypeDescriptorAddress(int catchHandlerOrdinal)
throws InvalidDataTypeException {
checkValidity(catchHandlerOrdinal);
DataType catchHandlerDt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(catchHandlerOrdinal, catchHandlerDt);
// component 1 is action pointer or displacement.
Address refAddress = EHDataTypeUtilities.getAddress(catchHandlerDt, TYPE_DESCRIPTOR_ORDINAL,
specificMemBuffer);
return getAdjustedAddress(refAddress, 0);
}
/**
* Gets the address, if there is one, of the component with the type descriptor address in the
* HandlerType entry indicated by the ordinal. Otherwise, this returns null.
* @param catchHandlerOrdinal 0-based ordinal indicating which HandlerType entry in the map.
* @return the address of the component with the type descriptor address or null.
* @throws InvalidDataTypeException if valid HandlerType data can't be created for
* the indicated ordinal.
*/
public Address getComponentAddressOfTypeDescriptorAddress(int catchHandlerOrdinal)
throws InvalidDataTypeException {
checkValidity(catchHandlerOrdinal);
DataType catchHandlerDt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(catchHandlerOrdinal, catchHandlerDt);
// component 1 is action pointer or displacement.
return EHDataTypeUtilities.getComponentAddress(catchHandlerDt, TYPE_DESCRIPTOR_ORDINAL,
specificMemBuffer);
}
/**
* Gets the address of the catch handler in the map as indicated by the ordinal, if there is one.
* Otherwise, this returns null.
* @param catchHandlerOrdinal 0-based ordinal indicating which HandlerType entry in the map.
* @return the address of the catch handler within the indicated HandlerType entry in the
* map or null.
* @throws InvalidDataTypeException if valid HandlerType data can't be created for
* the indicated ordinal.
*/
public Address getCatchHandlerAddress(int catchHandlerOrdinal) throws InvalidDataTypeException {
checkValidity(catchHandlerOrdinal);
DataType catchHandlerDt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(catchHandlerOrdinal, catchHandlerDt);
// component 3 is handler pointer or displacement.
Address refAddress =
EHDataTypeUtilities.getAddress(catchHandlerDt, HANDLER_ORDINAL, specificMemBuffer);
return getAdjustedAddress(refAddress, 0);
}
/**
* Gets the address, if there is one, of the component with the catch handler address in the
* HandlerType entry indicated by the ordinal. Otherwise, this returns null.
* @return the address of the component with the catch handler address or null.
* @throws InvalidDataTypeException if valid HandlerType data can't be created for
* the indicated ordinal.
*/
public Address getComponentAddressOfCatchHandlerAddress(int catchHandlerOrdinal)
throws InvalidDataTypeException {
checkValidity(catchHandlerOrdinal);
DataType dt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(catchHandlerOrdinal, dt);
// component 3 is handler pointer or displacement.
return EHDataTypeUtilities.getComponentAddress(dt, HANDLER_ORDINAL, specificMemBuffer);
}
/**
* Gets a modifier that provides information about specific modifications for the catch
* handler type in the indicated HandlerType map entry.
* @param catchHandlerOrdinal 0-based ordinal indicating which HandlerType entry in the map.
* @return the modifier information for the catch handler type or NO_MODIFIERS if they
* can't be determined.
* @throws InvalidDataTypeException if valid HandlerType data can't be created for
* the indicated ordinal.
*/
public EHCatchHandlerTypeModifier getModifiers(int catchHandlerOrdinal)
throws InvalidDataTypeException {
checkValidity(catchHandlerOrdinal);
DataType dt = getDataType();
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
Structure struct = (Structure) dt; // We know this from getDataType().
DataTypeComponent component = struct.getComponent(ADJECTIVES_ORDINAL);
int offset = component.getOffset();
MemBuffer specificMemBuffer = getSpecificMemBuffer(catchHandlerOrdinal, dt);
try {
// adjectives are at start of catch handler memory buffer.
int modifiers = specificMemBuffer.getInt(offset); // Can throw MemoryAccessException
return new EHCatchHandlerTypeModifier(modifiers);
}
catch (MemoryAccessException e) {
throw new AssertException(e); // Shouldn't happen; checkValidity() would have failed above.
}
}
/**
* Gets a name for the catch handler function based on its type descriptor or whether it is a
* catch all. The name will be for the HandlerType in the map that is indicated by the ordinal.
* @param catchHandlerOrdinal 0-based ordinal indicating which HandlerType entry in the map.
* @return the name for the catch handler function.
* @throws InvalidDataTypeException if valid HandlerType data can't be created for
* the indicated ordinal.
*/
public String getCatchHandlerName(int catchHandlerOrdinal) throws InvalidDataTypeException {
String name = "Catch";
DataType dt = getDataType();
if (dt instanceof TypeDef) {
dt = ((TypeDef) dt).getBaseDataType();
}
if (!(dt instanceof Structure)) {
return name;
}
// Commented out the following until we can get the demangled type name to use in the catch function name.
// String typeName = null;
// Address typeDescriptorAddress = getTypeDescriptorAddress(catchHandlerOrdinal);
// if (typeDescriptorAddress != null) {
// typeName = getTypeName(catchHandlerOrdinal);
// }
// if (typeName != null) {
// name += "_" + typeName;
// }
// else {
EHCatchHandlerTypeModifier modifiers = getModifiers(catchHandlerOrdinal);
if (modifiers.isAllCatch()) {
name += "_All";
}
// }
return name;
}
// Commented out the following until we can get the demangled type name to use in the catch function name.
// private String getTypeName(int catchHandlerOrdinal) throws InvalidDataTypeException {
// TypeDescriptorModel typeDescriptorModel = getTypeDescriptorModel(catchHandlerOrdinal);
// String typeName = typeDescriptorModel.getTypeName();
// if (typeName != null) {
// // Try to demangle the name.
// // Option 1
// DemangledObject demangledObject = DemanglerUtil.demangle(getProgram(), typeName);
// if (demangledObject != null) {
// String demangledTypeName = demangledObject.getName();
// if (demangledTypeName != null && !typeName.equals(demangledTypeName)) {
// typeName = demangledTypeName;
// }
// }
//// // Option 2
//// DemanglerCmd demanglerCmd = new DemanglerCmd(typeNameAddress, typeName);
//// boolean success = demanglerCmd.applyTo(program);
//// if (success) {
//// String demangledTypeName = demanglerCmd.getResult();
//// if (demangledTypeName != null && !typeName.equals(demangledTypeName)) {
//// typeName = demangledTypeName;
//// }
//// }
// }
// return typeName;
// }
/**
* Gets the scalar for the catch object displacement in the indicated HandlerType map entry.
* @param catchHandlerOrdinal 0-based ordinal indicating which HandlerType entry in the map.
* @return the scalar for the catch object displacement.
* @throws InvalidDataTypeException if valid HandlerType data can't be created for
* the indicated ordinal.
*/
public Scalar getCatchObjectDisplacement(int catchHandlerOrdinal)
throws InvalidDataTypeException {
checkValidity(catchHandlerOrdinal);
DataType catchHandlerDt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(catchHandlerOrdinal, catchHandlerDt);
// component 2 is displacement of catch object.
// component is an int or typedef on an int.
return EHDataTypeUtilities.getScalarValue(catchHandlerDt, CATCH_OBJECT_ORDINAL,
specificMemBuffer);
}
/**
* Gets the scalar for the displacement of the address of the function frame in the
* indicated HandlerType map entry.
* @param catchHandlerOrdinal 0-based ordinal indicating which HandlerType entry in the map.
* @return scalar for the displacement of the address of the function frame.
* @throws InvalidDataTypeException if valid HandlerType data can't be created for
* the indicated ordinal.
*/
public Scalar getFunctionFrameAddressDisplacement(int catchHandlerOrdinal)
throws InvalidDataTypeException {
checkValidity(catchHandlerOrdinal);
DataType catchHandlerDt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(catchHandlerOrdinal, catchHandlerDt);
// component 4 is the displacement of the address of function frame.
// Component is a dword.
return EHDataTypeUtilities.getScalarValue(catchHandlerDt, FUNCTION_FRAME_ORDINAL,
specificMemBuffer);
}
}
@@ -0,0 +1,126 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
/**
* This is a class for dealing with the adjectives (modifier flags) from an exception handling
* HandlerType data type.
* <br>
* This is based on data type information from ehdata.h
*/
public class EHCatchHandlerTypeModifier {
public static final EHCatchHandlerTypeModifier NO_MODIFIERS = new EHCatchHandlerTypeModifier(0);
// Catch Handler Type Modifiers
private static int CONST_BIT = 0x00000001;
private static int VOLATILE_BIT = 0x00000002;
private static int UNALIGNED_BIT = 0x00000004;
private static int REFERENCE_BIT = 0x00000008;
private static int RESUMABLE_BIT = 0x00000010;
private static int ALL_CATCH_BIT = 0x00000040;
private static int COMPLUS_BIT = 0x80000000;
private int modifiers;
/**
* Creates the object for dealing with the adjectives (modifier flags) from an exception handling
* HandlerType data type. It provides methods to check if modifiers are set for a handler type.
* @param modifiers the value of the adjectives (modifier flags) from the HandlerType data type.
*/
public EHCatchHandlerTypeModifier(int modifiers) {
this.modifiers = modifiers;
}
private boolean isBitSet(int bitToCheck) {
return (modifiers & bitToCheck) == bitToCheck;
}
/**
* Determine if the handler type referenced is a const.
* @return true if the handler type referenced is a const.
*/
public boolean isConst() {
return isBitSet(CONST_BIT);
}
/**
* Determine if the handler type referenced is volatile.
* @return true if the handler type referenced is volatile.
*/
public boolean isVolatile() {
return isBitSet(VOLATILE_BIT);
}
/**
* Determine if the handler type referenced is unaligned.
* @return true if the handler type referenced is unaligned.
*/
public boolean isUnaligned() {
return isBitSet(UNALIGNED_BIT);
}
/**
* Determine if the catch type is by reference.
* @return true if the catch type is by reference.
*/
public boolean isByReference() {
return isBitSet(REFERENCE_BIT);
}
/**
* Determine if the catch function can possibly resume.
* @return true if the function is resumable.
*/
public boolean isResumable() {
return isBitSet(RESUMABLE_BIT);
}
/**
* Determine if the exception handler is a standard C++ all catch(...).
* @return true if the function is a catch(...).
*/
public boolean isAllCatch() {
return isBitSet(ALL_CATCH_BIT);
}
/**
* Determine if this exception handler is complus.
* @return true if the handler is complus.
*/
public boolean isComplus() {
return isBitSet(COMPLUS_BIT);
}
@Override
public int hashCode() {
return modifiers;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
EHCatchHandlerTypeModifier other = (EHCatchHandlerTypeModifier) obj;
if (modifiers != other.modifiers)
return false;
return true;
}
}
@@ -0,0 +1,183 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import ghidra.app.cmd.data.AbstractCreateDataTypeModel;
import ghidra.app.cmd.data.EHDataTypeUtilities;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
/**
* Model for exception handling information about the ESTypeList data type and its
* associated exception handling data types.
* <br>
* This is based on data type information from ehdata.h
*/
public class EHESTypeListModel extends AbstractCreateDataTypeModel {
public static final String DATA_TYPE_NAME = "ESTypeList";
private static final String STRUCTURE_NAME = STRUCT_PREFIX + DATA_TYPE_NAME;
private static final int COUNT_ORDINAL = 0;
private static final int TYPE_ARRAY_ORDINAL = 1;
private DataType dataType;
/**
* Creates the model for the exception handling ESTypeList data type.
* @param program the program
* @param esTypeListAddress the address in the program for the ESTypeList data type.
*/
public EHESTypeListModel(Program program, Address esTypeListAddress,
DataValidationOptions validationOptions) {
super(program, 1, esTypeListAddress, validationOptions);
}
@Override
public String getName() {
return DATA_TYPE_NAME;
}
/**
* Whether or not the memory at the indicated address appears to be a valid location for the
* indicated number of ESTypeList data types.
* @throws InvalidDataTypeException if this model's location does not appear to be a valid
* group of ESTypeList entries. The exception has a message indicating
* why it does not appear to be a valid location for the data type.
*/
@Override
protected void validateModelSpecificInfo() throws InvalidDataTypeException {
int handlerTypeCount = getHandlerTypeCount();
Address handlerTypeMapAddress = getHandlerTypeMapAddress();
// Does the handler type map have a count and address.
if (handlerTypeCount == 0 || handlerTypeMapAddress == null ||
(isRelative() && imageBaseAddress.equals(handlerTypeMapAddress))) {
throw new InvalidDataTypeException(getName() + " data type doesn't have any map data.");
}
// Are the pointers or displacements to valid addresses.
if (!isValidMap(getHandlerTypeCount(), getHandlerTypeMapAddress())) {
throw new InvalidDataTypeException(getName() + " data type at " + getAddress() +
" doesn't have a valid handler type map.");
}
}
/**
* This gets the ESTypeList structure for the indicated program.
* @param program the program which will contain this data type.
* @return the ESTypeList structure.
*/
public static DataType getDataType(Program program) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean isRelative = isRelative(program);
CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH);
StructureDataType struct =
getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME);
// Add the components.
DataType compDt;
/* comps[0] */
compDt = new IntegerDataType(dataTypeManager);
struct.add(compDt, "nCount", null);
/* comps[1] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
struct.add(compDt, "dispTypeArray", null);
}
else {
compDt = new PointerDataType(EHCatchHandlerModel.getDataType(program), dataTypeManager);
struct.add(compDt, "pTypeArray", null);
}
TypedefDataType typeDefDt =
new TypedefDataType(categoryPath, DATA_TYPE_NAME, struct, dataTypeManager);
return MSDataTypeUtils.getMatchingDataType(program, typeDefDt);
}
/**
* This gets the ESTypeList structure for this model.
* @return the ESTypeList structure.
*/
@Override
public DataType getDataType() {
if (dataType == null) {
dataType = getDataType(getProgram());
}
return dataType;
}
@Override
protected int getDataTypeLength() {
return getDataType().getLength();
}
/**
* Gets the catch handler model for the catch handler address in the ESTypeList.
* @return the catch handler model, which may be invalid.
* @throws InvalidDataTypeException if valid ESTypeList data can't be created at the model's address.
*/
public EHCatchHandlerModel getCatchHandlerModel() throws InvalidDataTypeException {
checkValidity();
EHCatchHandlerModel catchHandlerModel = new EHCatchHandlerModel(getProgram(),
getHandlerTypeCount(), getHandlerTypeMapAddress(), validationOptions);
return catchHandlerModel;
}
/**
* Gets the handler type entry count, if there is one, for this ESTypeList.
* @return the catch handler type count
* @throws InvalidDataTypeException if valid ESTypeList data can't be created at the model's address.
*/
public int getHandlerTypeCount() throws InvalidDataTypeException {
checkValidity();
// component 0 is number of catch handler type list records
return EHDataTypeUtilities.getCount(getDataType(), COUNT_ORDINAL, getMemBuffer());
}
/**
* Gets the address of the handler type map, if there is one. Otherwise, this returns null.
* @return the address of the handler type map or null.
* @throws InvalidDataTypeException if valid ESTypeList data can't be created at the model's address.
*/
public Address getHandlerTypeMapAddress() throws InvalidDataTypeException {
checkValidity();
// component 1 is catch handler type list pointer or displacement.
Address mapAddress =
EHDataTypeUtilities.getAddress(getDataType(), TYPE_ARRAY_ORDINAL, getMemBuffer());
return getAdjustedAddress(mapAddress, getHandlerTypeCount());
}
/**
* Gets the address of the component containing the address of the handler type map,
* if there is one. Otherwise, this returns null.
* @return the address of the component with the address of the handler type map or null.
* @throws InvalidDataTypeException if valid ESTypeList data can't be created at the model's address.
*/
public Address getComponentAddressOfHandlerTypeMapAddress() throws InvalidDataTypeException {
checkValidity();
// component 1 is catch handler type list pointer or displacement.
return EHDataTypeUtilities.getComponentAddress(getDataType(), TYPE_ARRAY_ORDINAL,
getMemBuffer());
}
}
@@ -0,0 +1,200 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import ghidra.app.cmd.data.AbstractCreateDataTypeModel;
import ghidra.app.cmd.data.EHDataTypeUtilities;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.scalar.Scalar;
/**
* Model for exception handling information about the IpToStateMapEntry data type and its
* associated exception handling data types.
* <br>
* This is based on data type information from ehdata.h
*/
public class EHIPToStateModel extends AbstractCreateDataTypeModel {
public static final String DATA_TYPE_NAME = "IPToStateMapEntry";
private static String STRUCTURE_NAME = STRUCT_PREFIX + DATA_TYPE_NAME;
private static final int IP_ORDINAL = 0;
private static final int STATE_ORDINAL = 1;
private DataType dataType;
/**
* Creates the model for the exception handling IpToStateMapEntry data type.
* @param program the program
* @param ipToStateCount the number of IpToStateMapEntry data types expected at the map address.
* @param ipToStateMapAddress the address in the program for the map of IpToStateMapEntry data
* types.
*/
public EHIPToStateModel(Program program, int ipToStateCount, Address ipToStateMapAddress,
DataValidationOptions validationOptions) {
super(program, ipToStateCount, ipToStateMapAddress, validationOptions);
}
@Override
public String getName() {
return DATA_TYPE_NAME;
}
/**
* Whether or not the memory at the indicated address appears to be a valid location for the
* indicated number of ipToState map entry data types.
* @throws InvalidDataTypeException if this model's location does not appear to be a valid
* group of ipToState map entries. The exception has a message indicating
* why it does not appear to be a valid location for the data type.
*/
@Override
protected void validateModelSpecificInfo() throws InvalidDataTypeException {
// Does each IP to State map entry refer to a valid address for the IP?
Program program = getProgram();
int numEntries = getCount();
for (int ipToStateOrdinal = 0; ipToStateOrdinal < numEntries; ipToStateOrdinal++) {
Object ip = getIP(ipToStateOrdinal);
if (ip instanceof Address) {
Address ipAddress = (Address) ip;
if (!EHDataTypeUtilities.isValidAddress(program, ipAddress)) {
throw new InvalidDataTypeException(getName() + " data type at " + getAddress() +
" doesn't refer to a valid location for the IP.");
}
}
}
}
/**
* This gets the IPToStateMap structure for the indicated program.
* @param program the program which will contain this data type.
* @return the IPToStateMap structure.
*/
public static DataType getDataType(Program program) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean isRelative = isRelative(program);
CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH);
StructureDataType struct =
getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME);
// Add the components.
DataType compDt;
/* comps[0] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
}
else {
DataType dwordDt = new TypedefDataType(new CategoryPath("/WinDef.h"), "DWORD",
new UnsignedLongDataType(dataTypeManager), dataTypeManager);
compDt = new TypedefDataType(new CategoryPath("/wtypes.h"), "ULONG", dwordDt,
dataTypeManager);
}
struct.add(compDt, "Ip", null);
/* comps[1] */
DataType ehStateDt = MSDataTypeUtils.getEHStateDataType(program);
struct.add(ehStateDt, "state", null);
TypedefDataType typedefDt =
new TypedefDataType(categoryPath, DATA_TYPE_NAME, struct, dataTypeManager);
return MSDataTypeUtils.getMatchingDataType(program, typedefDt);
}
/**
* This gets the IPToStateMap structure for this model.
* @return the IPToStateMap structure.
*/
@Override
public DataType getDataType() {
if (dataType == null) {
dataType = getDataType(getProgram());
}
return dataType;
}
@Override
protected int getDataTypeLength() {
return getDataType().getLength();
}
/**
* Gets the IP value for the IP To State map as either an address of IP or as an IP value,
* if there is one, in the IpToStateMapEntry indicated by the ordinal.
* @param ipToStateOrdinal 0-based ordinal indicating which IpToStateMapEntry in the map.
* @return the IP value (as either an Address or a Scalar) for IP To State map.
* @throws InvalidDataTypeException if valid IPToStateEntry data can't be created for
* the indicated ordinal.
*/
public Object getIP(int ipToStateEntryOrdinal) throws InvalidDataTypeException {
checkValidity(ipToStateEntryOrdinal);
DataType ipToStateDt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(ipToStateEntryOrdinal, ipToStateDt);
// component 0 is ULONG or displacement.
if (isRelative()) { // displacement
Address refAddress =
EHDataTypeUtilities.getAddress(ipToStateDt, IP_ORDINAL, specificMemBuffer);
return getAdjustedAddress(refAddress, 0);
}
// ULONG - Does this actually represent an address?
Scalar scalarValue =
EHDataTypeUtilities.getScalarValue(ipToStateDt, IP_ORDINAL, specificMemBuffer);
return scalarValue;
}
/**
* Gets the address of the component containing the IP To State address, if there is one
* in the IpToStateMapEntry indicated by the ordinal.
* Otherwise, this returns null.
* @param ipToStateOrdinal 0-based ordinal indicating which IpToStateMapEntry in the map.
* @return the address of the component with the IP To State address or null.
* @throws InvalidDataTypeException if valid IPToStateEntry data can't be created for
* the indicated ordinal.
*/
public Address getComponentAddressOfIPAddress(int ipToStateEntryOrdinal)
throws InvalidDataTypeException {
checkValidity(ipToStateEntryOrdinal);
DataType dt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(ipToStateEntryOrdinal, dt);
// component 0 is ULONG or displacement.
return EHDataTypeUtilities.getComponentAddress(dt, IP_ORDINAL, specificMemBuffer);
}
/**
* Gets the state value, if there is one, in the IpToStateMapEntry indicated by the ordinal.
* @param ipToStateOrdinal 0-based ordinal indicating which IpToStateMapEntry in the map.
* @return the state value.
* @throws InvalidDataTypeException if valid IPToStateEntry data can't be created for
* the indicated ordinal.
*/
public int getState(int ipToStateEntryOrdinal) throws InvalidDataTypeException {
checkValidity(ipToStateEntryOrdinal);
DataType ipToStateDt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(ipToStateEntryOrdinal, ipToStateDt);
// component 1 is state value.
return EHDataTypeUtilities.getEHStateValue(ipToStateDt, STATE_ORDINAL, specificMemBuffer);
}
}
@@ -0,0 +1,263 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import ghidra.app.cmd.data.AbstractCreateDataTypeModel;
import ghidra.app.cmd.data.EHDataTypeUtilities;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemBuffer;
/**
* Model for exception handling information about the TryBlockMapEntry data type and its
* associated exception handling data types.
* <br>
* This is based on data type information from ehdata.h
*/
public class EHTryBlockModel extends AbstractCreateDataTypeModel {
public static String DATA_TYPE_NAME = "TryBlockMapEntry";
private static String STRUCTURE_NAME = STRUCT_PREFIX + DATA_TYPE_NAME;
private static final int TRY_LOW_ORDINAL = 0;
private static final int TRY_HIGH_ORDINAL = 1;
private static final int CATCH_HIGH_ORDINAL = 2;
private static final int CATCH_COUNT_ORDINAL = 3;
private static final int HANDLER_ARRAY_ORDINAL = 4;
private DataType dataType;
/**
* Creates the model for the exception handling TryBlockMapEntry data type.
* @param program the program
* @param tryBlockCount the number of TryBlockMapEntry data types expected at the map address.
* @param tryBlockMapAddress the address in the program for the map of TryBlockMapEntry data
* types.
*/
public EHTryBlockModel(Program program, int tryBlockCount, Address tryBlockMapAddress,
DataValidationOptions validationOptions) {
super(program, tryBlockCount, tryBlockMapAddress, validationOptions);
}
@Override
public String getName() {
return DATA_TYPE_NAME;
}
/**
* Whether or not the memory at the indicated address appears to be a valid location for the
* indicated number of try block map entry data types.
* @throws InvalidDataTypeException if this model's location does not appear to be a valid
* group of try block map entries. The exception has a message indicating
* why it does not appear to be a valid location for the data type.
*/
@Override
protected void validateModelSpecificInfo() throws InvalidDataTypeException {
// Does each try map entry have a valid catch handler?
int numEntries = getCount();
for (int tryBlockOrdinal = 0; tryBlockOrdinal < numEntries; tryBlockOrdinal++) {
if (!isValidMap(getCatchHandlerCount(tryBlockOrdinal),
getCatchHandlerMapAddress(tryBlockOrdinal))) {
throw new InvalidDataTypeException(getName() + " data type at " + getAddress() +
" doesn't have a valid catch handler map.");
}
}
}
/**
* This gets the TryBlockMapEntry structure for the indicated program.
* @param program the program which will contain this data type.
* @return the TryBlockMapEntry structure.
*/
public static DataType getDataType(Program program) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean isRelative = isRelative(program);
CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH);
StructureDataType struct =
getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME);
DataType ehStateDt = MSDataTypeUtils.getEHStateDataType(program);
// Add the components.
DataType compDt;
/* comps[0] */
struct.add(ehStateDt, "tryLow", null);
/* comps[1] */
struct.add(ehStateDt, "tryHigh", null);
/* comps[2] */
struct.add(ehStateDt, "catchHigh", null);
/* comps[3] */
compDt = new IntegerDataType(dataTypeManager);
struct.add(compDt, "nCatches", null);
/* comps[4] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
struct.add(compDt, "dispHandlerArray", null);
}
else {
compDt = new PointerDataType(EHCatchHandlerModel.getDataType(program), dataTypeManager);
struct.add(compDt, "pHandlerArray", null);
}
TypedefDataType typedefDt =
new TypedefDataType(categoryPath, DATA_TYPE_NAME, struct, dataTypeManager);
return MSDataTypeUtils.getMatchingDataType(program, typedefDt);
}
/**
* This gets the TryBlockMapEntry structure for this model.
* @return the TryBlockMapEntry structure.
*/
@Override
public DataType getDataType() {
if (dataType == null) {
dataType = getDataType(getProgram());
}
return dataType;
}
@Override
protected int getDataTypeLength() {
return getDataType().getLength();
}
/**
* Gets the low state value of the try, if there is one, in the TryBlockMapEntry indicated
* by the ordinal.
* @param tryBlockOrdinal 0-based ordinal indicating which TryBlockMapEntry in the map.
* @return the low state value of the try
* @throws InvalidDataTypeException
*/
public int getTryLow(int tryBlockOrdinal) throws InvalidDataTypeException {
checkValidity(tryBlockOrdinal);
DataType dt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(tryBlockOrdinal, dt);
// component 0 is the low state value of the try
return EHDataTypeUtilities.getEHStateValue(dt, TRY_LOW_ORDINAL, specificMemBuffer);
}
/**
* Gets the high state value of the try, if there is one, in the TryBlockMapEntry indicated
* by the ordinal.
* @param tryBlockOrdinal 0-based ordinal indicating which TryBlockMapEntry in the map.
* @return the high state value of the try
* @throws InvalidDataTypeException if valid TryBlockMapEntry data can't be created for
* the indicated ordinal.
*/
public int getTryHigh(int tryBlockOrdinal) throws InvalidDataTypeException {
checkValidity(tryBlockOrdinal);
DataType dt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(tryBlockOrdinal, dt);
// component 1 is the high state value of the try
return EHDataTypeUtilities.getEHStateValue(dt, TRY_HIGH_ORDINAL, specificMemBuffer);
}
/**
* Gets the high state value of the catches, if there is one, in the TryBlockMapEntry indicated
* by the ordinal.
* @param tryBlockOrdinal 0-based ordinal indicating which TryBlockMapEntry in the map.
* @return the high state value of the catches
* @throws InvalidDataTypeException if valid TryBlockMapEntry data can't be created for
* the indicated ordinal.
*/
public int getCatchHigh(int tryBlockOrdinal) throws InvalidDataTypeException {
checkValidity(tryBlockOrdinal);
DataType dt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(tryBlockOrdinal, dt);
// component 2 is the high state value of the catches
return EHDataTypeUtilities.getEHStateValue(dt, CATCH_HIGH_ORDINAL, specificMemBuffer);
}
/**
* Gets the catch handler model for the catch handler address in the
* TryBlockMapEntry indicated by the ordinal.
* @param tryBlockOrdinal 0-based ordinal indicating which TryBlockMapEntry in the map.
* @return the catch handler model, which may be invalid.
* @throws InvalidDataTypeException if valid TryBlockMapEntry data can't be created for
* the indicated ordinal.
*/
public EHCatchHandlerModel getCatchHandlerModel(int tryBlockOrdinal)
throws InvalidDataTypeException {
checkValidity();
EHCatchHandlerModel catchHandlerModel =
new EHCatchHandlerModel(getProgram(), getCatchHandlerCount(tryBlockOrdinal),
getCatchHandlerMapAddress(tryBlockOrdinal), validationOptions);
return catchHandlerModel;
}
/**
* Gets the catch handler map's entry count, if there is one, in the TryBlockMapEntry
* indicated by the ordinal.
* @param tryBlockOrdinal 0-based ordinal indicating which TryBlockMapEntry in the map.
* @return the catch handler count
* @throws InvalidDataTypeException if valid TryBlockMapEntry data can't be created for
* the indicated ordinal.
*/
public int getCatchHandlerCount(int tryBlockOrdinal) throws InvalidDataTypeException {
checkValidity(tryBlockOrdinal);
DataType dt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(tryBlockOrdinal, dt);
// component 3 is number of catch handler records
return EHDataTypeUtilities.getCount(dt, CATCH_COUNT_ORDINAL, specificMemBuffer);
}
/**
* Gets the catch handler map's address, if there is one, in the TryBlockMapEntry
* indicated by the ordinal.
* @param tryBlockOrdinal 0-based ordinal indicating which TryBlockMapEntry in the map.
* @return the catch handler map's address
* @throws InvalidDataTypeException if valid TryBlockMapEntry data can't be created for
* the indicated ordinal.
*/
public Address getCatchHandlerMapAddress(int tryBlockOrdinal) throws InvalidDataTypeException {
checkValidity(tryBlockOrdinal);
DataType dt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(tryBlockOrdinal, dt);
// component 4 is action pointer or displacement.
Address mapAddress =
EHDataTypeUtilities.getAddress(dt, HANDLER_ARRAY_ORDINAL, specificMemBuffer);
return getAdjustedAddress(mapAddress, getCatchHandlerCount(tryBlockOrdinal));
}
/**
* Gets the component address of the catch handler map's address, if there is one, in the
* TryBlockMapEntry indicated by the ordinal.
* @param tryBlockOrdinal 0-based ordinal indicating which TryBlockMapEntry in the map.
* @return the address of the component with the catch handler map's address
* @throws InvalidDataTypeException if valid TryBlockMapEntry data can't be created for
* the indicated ordinal.
*/
public Address getComponentAddressOfCatchHandlerMapAddress(int tryBlockOrdinal)
throws InvalidDataTypeException {
checkValidity(tryBlockOrdinal);
DataType dt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(tryBlockOrdinal, dt);
// component 4 is action pointer or displacement.
return EHDataTypeUtilities.getComponentAddress(dt, HANDLER_ARRAY_ORDINAL,
specificMemBuffer);
}
}
@@ -0,0 +1,193 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.exceptionhandling;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import ghidra.app.cmd.data.AbstractCreateDataTypeModel;
import ghidra.app.cmd.data.EHDataTypeUtilities;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemBuffer;
/**
* Model for exception handling information about the UnwindMapEntry data type and its
* associated exception handling data types.
* <br>
* This is based on data type information from ehdata.h
*/
public class EHUnwindModel extends AbstractCreateDataTypeModel {
public static final String DATA_TYPE_NAME = "UnwindMapEntry";
private static String STRUCTURE_NAME = STRUCT_PREFIX + DATA_TYPE_NAME;
private static final int TO_STATE_ORDINAL = 0;
private static final int ACTION_ORDINAL = 1;
private DataType dataType;
/**
* Creates the model for the exception handling TryBlockMapEntry data type.
* @param program the program
* @param unwindCount the number of UnwindMapEntry data types expected at the map address.
* @param unwindMapAddress the address in the program for the map of UnwindMapEntry data
* types.
*/
public EHUnwindModel(Program program, int unwindCount, Address unwindMapAddress,
DataValidationOptions validationOptions) {
super(program, unwindCount, unwindMapAddress, validationOptions);
}
@Override
public String getName() {
return DATA_TYPE_NAME;
}
/**
* Whether or not the memory at the indicated address appears to be a valid location for the
* indicated number of unwind map entry data types.
* @throws InvalidDataTypeException if this model's location does not appear to be a valid
* group of unwind map entries. The exception has a message indicating
* why it does not appear to be a valid location for the data type.
*/
@Override
protected void validateModelSpecificInfo() throws InvalidDataTypeException {
// Does each unwind map entry refer to a valid action?
Program program = getProgram();
int numEntries = getCount();
for (int unwindBlockOrdinal = 0; unwindBlockOrdinal < numEntries; unwindBlockOrdinal++) {
Address actionAddress = getActionAddress(unwindBlockOrdinal);
if ((actionAddress != null) &&
!EHDataTypeUtilities.isValidForFunction(program, actionAddress)) {
throw new InvalidDataTypeException(getName() + " data type at " + getAddress() +
" doesn't refer to a valid location for an action.");
}
}
}
/**
* This gets the UnwindMapEntry structure for the indicated program.
* @param program the program which will contain this data type.
* @return the UnwindMapEntry structure.
*/
public static DataType getDataType(Program program) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean isRelative = isRelative(program);
CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH);
StructureDataType struct =
getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME);
// Add the components.
DataType compDt;
/* comps[0] */
DataType ehStateDt = MSDataTypeUtils.getEHStateDataType(program);
if (ehStateDt == null) {
ehStateDt = new IntegerDataType(dataTypeManager);
}
struct.add(ehStateDt, "toState", null);
/* comps[1] */
if (isRelative) {
compDt = new ImageBaseOffset32DataType(dataTypeManager);
}
else {
FunctionDefinitionDataType functionDefDt = new FunctionDefinitionDataType(
new CategoryPath("/ehdata.h/functions"), "action", dataTypeManager);
functionDefDt.setReturnType(new VoidDataType(dataTypeManager));
compDt = new PointerDataType(functionDefDt, dataTypeManager);
}
struct.add(compDt, "action", null);
TypedefDataType typedefDt =
new TypedefDataType(categoryPath, DATA_TYPE_NAME, struct, dataTypeManager);
return MSDataTypeUtils.getMatchingDataType(program, typedefDt);
}
/**
* This gets the UnwindMapEntry structure for this model.
* @return the UnwindMapEntry structure.
*/
@Override
public DataType getDataType() {
if (dataType == null) {
dataType = getDataType(getProgram());
}
return dataType;
}
@Override
protected int getDataTypeLength() {
return getDataType().getLength();
}
/**
* Gets the To State value of the unwind, if there is one, in the UnwindMapEntry indicated
* by the ordinal.
* @param unwindOrdinal 0-based ordinal indicating which UnwindMapEntry in the map.
* @return the To State value of the unwind
* @throws InvalidDataTypeException if valid UnwindMapEntry data can't be created for
* the indicated ordinal.
*/
public int getToState(int unwindOrdinal) throws InvalidDataTypeException {
checkValidity(unwindOrdinal);
DataType dt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(unwindOrdinal, dt);
// component 0 is the To State for this unwind entry.
return EHDataTypeUtilities.getEHStateValue(dt, TO_STATE_ORDINAL, specificMemBuffer);
}
/**
* Gets the unwind action address, if there is one, in the UnwindMapEntry
* indicated by the ordinal.
* @param unwindOrdinal 0-based ordinal indicating which UnwindMapEntry in the map.
* @return the unwind action address or null.
* @throws InvalidDataTypeException if valid UnwindMapEntry data can't be created for
* the indicated ordinal.
*/
public Address getActionAddress(int unwindOrdinal) throws InvalidDataTypeException {
checkValidity(unwindOrdinal);
DataType unwindDt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(unwindOrdinal, unwindDt);
// component 1 is action pointer or displacement.
Address refAddress =
EHDataTypeUtilities.getAddress(unwindDt, ACTION_ORDINAL, specificMemBuffer);
return getAdjustedAddress(refAddress, 0);
}
/**
* Gets the address of the component containing the unwind action address, if there is one.
* Otherwise, this returns null.
* @param unwindOrdinal 0-based ordinal indicating which UnwindMapEntry in the map.
* @return the address of the component with the unwind action address or null.
* @throws InvalidDataTypeException if valid UnwindMapEntry data can't be created for
* the indicated ordinal.
*/
public Address getComponentAddressOfActionAddress(int unwindOrdinal)
throws InvalidDataTypeException {
checkValidity(unwindOrdinal);
DataType dt = getDataType();
MemBuffer specificMemBuffer = getSpecificMemBuffer(unwindOrdinal, dt);
// component 1 is action pointer or displacement.
return EHDataTypeUtilities.getComponentAddress(dt, ACTION_ORDINAL, specificMemBuffer);
}
}
@@ -0,0 +1,62 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import ghidra.app.cmd.data.AbstractCreateDataTypeModel;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
public abstract class AbstractCreateRttiDataModel extends AbstractCreateDataTypeModel {
/**
* Constructor for the abstract create RTTI data type model. This constructor assumes
* that only a single data type will be created at the indicated address in the program.
* @param program the program where the data type would be created.
* @param address the address where the data type would be created.
* @param validationOptions options indicating how to validate the data type at the indicated
* address.
*/
public AbstractCreateRttiDataModel(Program program, Address address,
DataValidationOptions validationOptions) {
super(program, address, validationOptions);
}
/**
* Constructor for the abstract create RTTI data type model. This constructor expects
* to create <code>count</code> number of data types at the indicated address in the program.
* If more than one data type is being created, they will be in an array data type.
* @param program the program where the data type would be created.
* @param count the number of data types to create.
* @param address the address where the data type would be created.
* @param validationOptions options indicating how to validate the data type at the indicated
* address.
*/
public AbstractCreateRttiDataModel(Program program, int count, Address address,
DataValidationOptions validationOptions) {
super(program, count, address, validationOptions);
}
/**
* Determines that when following data references from this data type to referenced data types,
* it will eventually traverse via direct (pointer) references or relative
* (image base offset) references to the RTTI 0 data type at the indicated address.
* @param rtti0Address the address of the RTTI 0 to which this data refers directly or
* indirectly through other RTTI types.
* @return true if a path can be traversed through referenced RTTI data to get to the RTTI 0.
*/
public abstract boolean refersToRtti0(Address rtti0Address);
}
@@ -0,0 +1,135 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import ghidra.app.cmd.data.*;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.SymbolUtilities;
import ghidra.util.exception.CancelledException;
/**
* This command will create an RTTI1 data type.
* If there are any existing instructions in the area to be made into data, the command will fail.
* Any data in the area will be replaced with the new dataType.
*/
public class CreateRtti1BackgroundCmd extends AbstractCreateDataBackgroundCmd<Rtti1Model> {
private static final String RTTI_1_NAME = "RTTI Base Class Descriptor";
/**
* Constructs a command for applying an RTTI1 dataType at an address.
* @param address the address where the data should be created using the data type.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateRtti1BackgroundCmd(Address address, DataValidationOptions validationOptions,
DataApplyOptions applyOptions) {
super(Rtti1Model.DATA_TYPE_NAME, address, 1, validationOptions, applyOptions);
}
/**
* Constructs a command for applying an RTTI1 dataType at the address indicated by the
* model.
* @param rtti1Model the model for the data type
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
CreateRtti1BackgroundCmd(Rtti1Model rtti1Model, DataApplyOptions applyOptions) {
super(rtti1Model, applyOptions);
}
@Override
protected Rtti1Model createModel(Program program) {
if (model == null || program != model.getProgram()) {
model = new Rtti1Model(program, address, validationOptions);
}
return model;
}
@Override
protected boolean createAssociatedData() throws CancelledException {
return createRtti0();
}
private boolean createRtti0() throws CancelledException {
monitor.checkCanceled();
CreateTypeDescriptorBackgroundCmd cmd =
new CreateTypeDescriptorBackgroundCmd(model.getRtti0Model(), applyOptions);
return cmd.applyTo(model.getProgram(), monitor);
}
@Override
protected boolean createMarkup() throws CancelledException {
monitor.checkCanceled();
Program program = model.getProgram();
TypeDescriptorModel rtti0Model = model.getRtti0Model();
monitor.checkCanceled();
if (rtti0Model != null) {
String suffix = "";
try {
suffix = " at " + getPMDAttrList(program);
}
catch (InvalidDataTypeException e) {
// Couldn't get pmd and attributes so leave it off and simply log the error.
String message = "Unable to get PMD and attributes for RTTI1 at " + address + ".";
handleError(message);
}
// Plate Comment
EHDataTypeUtilities.createPlateCommentIfNeeded(program,
RttiUtil.getDescriptorTypeNamespace(rtti0Model) + Namespace.NAMESPACE_DELIMITER,
RTTI_1_NAME, suffix, address, applyOptions);
monitor.checkCanceled();
// Label
if (applyOptions.shouldCreateLabel()) {
String rtti1Suffix = RTTI_1_NAME + suffix;
rtti1Suffix = SymbolUtilities.replaceInvalidChars(rtti1Suffix, true);
RttiUtil.createSymbolFromDemangledType(program, address, rtti0Model, rtti1Suffix);
}
}
return true;
}
private String getPMDAttrList(Program program) throws InvalidDataTypeException {
int mDisp = model.getMDisp();
int pDisp = model.getPDisp();
int vDisp = model.getVDisp();
int attributes = model.getAttributes();
return "(" + mDisp + "," + pDisp + "," + vDisp + "," + attributes + ")";
}
}
@@ -0,0 +1,132 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import ghidra.app.cmd.data.*;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Namespace;
import ghidra.util.exception.CancelledException;
/**
* This command will create an RTTI2 data type.
* If there are any existing instructions in the area to be made into data, the command will fail.
* Any data in the area will be replaced with the new dataType.
*/
public class CreateRtti2BackgroundCmd extends AbstractCreateDataBackgroundCmd<Rtti2Model> {
private static final String RTTI_2_NAME = "RTTI Base Class Array";
// The following count variable is only for initializing the model.
// Get the actual number of RTTI 1 entries from the model directly using model.getCount().
private int rtti1Count;
/**
* Constructs a command for applying an RTTI2 dataType at an address.
* @param address the address where the data should be created using the data type.
* @param rtti1Count the number of RTTI1 data types expected at the RTTI2 address.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateRtti2BackgroundCmd(Address address, int rtti1Count,
DataValidationOptions validationOptions, DataApplyOptions applyOptions) {
super(Rtti2Model.DATA_TYPE_NAME, address, 1, validationOptions, applyOptions);
this.rtti1Count = rtti1Count;
}
/**
* Constructs a command for applying an RTTI2 dataType at the address indicated by the
* model.
* @param rtti2Model the model for the data type
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
CreateRtti2BackgroundCmd(Rtti2Model rtti2Model, DataApplyOptions applyOptions) {
super(rtti2Model, applyOptions);
}
@Override
protected Rtti2Model createModel(Program program) {
if (model == null || program != model.getProgram()) {
model = new Rtti2Model(program, rtti1Count, address, validationOptions);
}
return model;
}
@Override
protected boolean createAssociatedData() throws CancelledException {
return createRtti1s();
}
private boolean createRtti1s() throws CancelledException {
int itemCount = model.getCount();
// Loop over the RTTI 1 pointers and create referenced RTTI 1 structures.
for (int rtti1Index = 0; rtti1Index < itemCount; rtti1Index++) {
try {
if (!createRtti1(rtti1Index)) {
return false; // Failed to create the RTTI1 data, markup, or associated data.
}
}
catch (InvalidDataTypeException e) {
return false; // Failed to create the RTTI1 data, markup, or associated data.
}
}
return true;
}
private boolean createRtti1(int rtti1Index)
throws CancelledException, InvalidDataTypeException {
monitor.checkCanceled();
CreateRtti1BackgroundCmd cmd =
new CreateRtti1BackgroundCmd(model.getRtti1Model(rtti1Index), applyOptions);
return cmd.applyTo(model.getProgram(), monitor);
}
@Override
protected boolean createMarkup() throws CancelledException, InvalidDataTypeException {
monitor.checkCanceled();
Program program = model.getProgram();
TypeDescriptorModel rtti0Model = model.getRtti0Model();
monitor.checkCanceled();
// Plate Comment
EHDataTypeUtilities.createPlateCommentIfNeeded(program,
RttiUtil.getDescriptorTypeNamespace(rtti0Model) + Namespace.NAMESPACE_DELIMITER,
RTTI_2_NAME, null, address, applyOptions);
monitor.checkCanceled();
// Label
if (applyOptions.shouldCreateLabel()) {
RttiUtil.createSymbolFromDemangledType(program, address, rtti0Model, RTTI_2_NAME);
}
return true;
}
}
@@ -0,0 +1,121 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import ghidra.app.cmd.data.*;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Namespace;
import ghidra.util.exception.CancelledException;
/**
* This command will create an RTTI3 data type.
* If there are any existing instructions in the area to be made into data, the command will fail.
* Any data in the area will be replaced with the new dataType.
*/
public class CreateRtti3BackgroundCmd extends AbstractCreateDataBackgroundCmd<Rtti3Model> {
private static final String RTTI_3_NAME = "RTTI Class Hierarchy Descriptor";
/**
* Constructs a command for applying an RTTI3 dataType at an address.
* @param address the address where the data should be created using the data type.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateRtti3BackgroundCmd(Address address, DataValidationOptions validationOptions,
DataApplyOptions applyOptions) {
super(Rtti3Model.DATA_TYPE_NAME, address, 1, validationOptions, applyOptions);
}
/**
* Constructs a command for applying an RTTI3 dataType at the address indicated by the
* model.
* @param rtti3Model the model for the data type
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
CreateRtti3BackgroundCmd(Rtti3Model rtti3Model, DataApplyOptions applyOptions) {
super(rtti3Model, applyOptions);
}
@Override
protected Rtti3Model createModel(Program program) {
if (model == null || program != model.getProgram()) {
model = new Rtti3Model(program, address, validationOptions);
}
return model;
}
@Override
protected boolean createAssociatedData() throws CancelledException {
try {
return createRtti2();
}
catch (InvalidDataTypeException e) {
// log message
handleErrorMessage(model.getProgram(), model.getAddress(), e.getMessage());
return false; // return since no other markup
}
}
private boolean createRtti2() throws CancelledException, InvalidDataTypeException {
monitor.checkCanceled();
CreateRtti2BackgroundCmd cmd =
new CreateRtti2BackgroundCmd(model.getRtti2Model(), applyOptions);
return cmd.applyTo(model.getProgram(), monitor);
}
@Override
protected boolean createMarkup() throws CancelledException, InvalidDataTypeException {
monitor.checkCanceled();
Program program = model.getProgram();
TypeDescriptorModel rtti0Model = model.getRtti0Model();
monitor.checkCanceled();
if (rtti0Model != null) {
// Plate Comment
EHDataTypeUtilities.createPlateCommentIfNeeded(program,
RttiUtil.getDescriptorTypeNamespace(rtti0Model) + Namespace.NAMESPACE_DELIMITER,
RTTI_3_NAME, null, address, applyOptions);
monitor.checkCanceled();
// Label
if (applyOptions.shouldCreateLabel()) {
RttiUtil.createSymbolFromDemangledType(program, address, rtti0Model, RTTI_3_NAME);
}
}
return true;
}
}
@@ -0,0 +1,196 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import java.util.List;
import java.util.Set;
import ghidra.app.cmd.data.*;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.util.ProgramMemoryUtil;
import ghidra.util.exception.CancelledException;
/**
* This command will create an RTTI4 data type.
* If there are any existing instructions in the area to be made into data, the command will fail.
* Any data in the area will be replaced with the new dataType.
*/
public class CreateRtti4BackgroundCmd extends AbstractCreateDataBackgroundCmd<Rtti4Model> {
private static final String RTTI_4_NAME = "RTTI Complete Object Locator";
private List<MemoryBlock> vfTableBlocks;
/**
* Constructs a command for applying an RTTI4 dataType at an address.
* @param address the address where the data should be created using the data type.
* @param vfTableBlocks a list of the only memory blocks to be searched for vf tables.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateRtti4BackgroundCmd(Address address, List<MemoryBlock> vfTableBlocks,
DataValidationOptions validationOptions, DataApplyOptions applyOptions) {
super(Rtti4Model.DATA_TYPE_NAME, address, 1, validationOptions, applyOptions);
this.vfTableBlocks = vfTableBlocks;
}
@Override
protected Rtti4Model createModel(Program program) {
if (model == null || program != model.getProgram()) {
model = new Rtti4Model(program, address, validationOptions);
}
return model;
}
@Override
protected boolean createAssociatedData() throws CancelledException {
monitor.checkCanceled();
boolean createRtti0Success;
try {
createRtti0Success = createRtti0();
}
catch (InvalidDataTypeException e) {
createRtti0Success = false;
// log message and continue with other markup.
handleErrorMessage(model.getProgram(), model.getAddress(), e.getMessage());
}
boolean createRtti3Success;
try {
createRtti3Success = createRtti3();
}
catch (InvalidDataTypeException e) {
createRtti3Success = false;
// log message and continue with other markup.
handleErrorMessage(model.getProgram(), model.getAddress(), e.getMessage());
}
boolean createVfTableSuccess = createVfTable();
return createRtti0Success && createRtti3Success && createVfTableSuccess;
}
private boolean createRtti0() throws CancelledException, InvalidDataTypeException {
monitor.checkCanceled();
CreateTypeDescriptorBackgroundCmd cmd =
new CreateTypeDescriptorBackgroundCmd(model.getRtti0Model(), applyOptions);
return cmd.applyTo(model.getProgram(), monitor);
}
private boolean createRtti3() throws CancelledException, InvalidDataTypeException {
monitor.checkCanceled();
CreateRtti3BackgroundCmd cmd =
new CreateRtti3BackgroundCmd(model.getRtti3Model(), applyOptions);
return cmd.applyTo(model.getProgram(), monitor);
}
private boolean createVfTable() throws CancelledException {
monitor.checkCanceled();
Program program = model.getProgram();
Address rtti4Address = address;
int defaultPointerSize = program.getDefaultPointerSize();
int alignment = defaultPointerSize; // Align the vf table based on the size of the pointers in it.
Set<Address> directRtti4Refs =
ProgramMemoryUtil.findDirectReferences(program, vfTableBlocks, alignment, rtti4Address,
monitor);
VfTableModel validVfTableModel = null;
for (Address possibleVfMetaAddr : directRtti4Refs) {
monitor.checkCanceled();
Address possibleVfTableAddr = possibleVfMetaAddr.add(defaultPointerSize);
// Validate the model. Don't apply the command if invalid.
try {
VfTableModel vfTableModel =
new VfTableModel(program, possibleVfTableAddr, validationOptions);
vfTableModel.validate();
if (validVfTableModel != null) {
String message = "More than one possible vfTable found for " +
Rtti4Model.DATA_TYPE_NAME + " @ " + rtti4Address;
handleErrorMessage(program, rtti4Address, message);
return false;
}
validVfTableModel = vfTableModel;
}
catch (InvalidDataTypeException e) {
continue; // This isn't a valid model.
}
}
if (validVfTableModel == null) {
String message =
"No vfTable found for " + Rtti4Model.DATA_TYPE_NAME + " @ " + rtti4Address;
handleErrorMessage(program, rtti4Address, message);
return false;
}
monitor.checkCanceled();
CreateVfTableBackgroundCmd cmd =
new CreateVfTableBackgroundCmd(validVfTableModel, applyOptions);
return cmd.applyTo(program, monitor);
}
@Override
protected boolean createMarkup() throws CancelledException, InvalidDataTypeException {
monitor.checkCanceled();
Program program = model.getProgram();
TypeDescriptorModel rtti0Model = model.getRtti0Model();
monitor.checkCanceled();
if (rtti0Model != null) {
// Plate Comment
EHDataTypeUtilities.createPlateCommentIfNeeded(program, RttiUtil.CONST_PREFIX +
RttiUtil.getDescriptorTypeNamespace(rtti0Model) + Namespace.NAMESPACE_DELIMITER,
RTTI_4_NAME, null, address, applyOptions);
monitor.checkCanceled();
// Label
if (applyOptions.shouldCreateLabel()) {
RttiUtil.createSymbolFromDemangledType(program, address, rtti0Model, RTTI_4_NAME);
}
}
return true;
}
}
@@ -0,0 +1,244 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAbsoluteAddress;
import ghidra.app.cmd.data.*;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.util.CodeUnitInsertionException;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
/**
* This command will create a virtual function table using an array data type.
* If there are any existing instructions in the area to be made into data, the command will fail.
* Any data in the area will be replaced with the new dataType.
*/
public class CreateVfTableBackgroundCmd extends AbstractCreateDataBackgroundCmd<VfTableModel> {
private static final String NAME = "vftable";
private static final String VF_TABLE_LABEL = "vftable";
private static final String META_LABEL = "meta";
private static final String NAME_SEPARATOR = "_";
/**
* Constructs a command for applying a vf table at an address.
* @param address the address where the vf table should be created.
* @param validationOptions the options for controlling how validation is performed when
* determining whether or not to create the data structure at the indicated address.
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
public CreateVfTableBackgroundCmd(Address address, DataValidationOptions validationOptions,
DataApplyOptions applyOptions) {
super(NAME, address, 1, validationOptions, applyOptions);
}
/**
* Constructs a command for applying a vf table dataType at the address indicated by the
* model.
* @param vfTableModel the model for the data type
* @param applyOptions the options for creating the new data structure and its associated
* markup in the program as well as whether to follow other data references and create their
* data too.
*/
CreateVfTableBackgroundCmd(VfTableModel vfTableModel, DataApplyOptions applyOptions) {
super(vfTableModel, applyOptions);
}
@Override
protected VfTableModel createModel(Program program) {
if (model == null || program != model.getProgram()) {
model = new VfTableModel(program, address, validationOptions);
}
return model;
}
@Override
protected boolean createAssociatedData() throws CancelledException {
monitor.checkCanceled();
boolean createTerminatorSuccess;
try {
createTerminatorSuccess = createTableTerminator();
}
catch (InvalidDataTypeException e) {
createTerminatorSuccess = false;
// log message and continue with other markup.
handleErrorMessage(model.getProgram(), model.getAddress(), e.getMessage());
}
boolean createMetaSuccess = createMetaPointer();
return createTerminatorSuccess && createMetaSuccess;
}
private boolean createTableTerminator() throws CancelledException, InvalidDataTypeException {
Program program = model.getProgram();
// Create a zero pointer at the end of the vf table.
DataType dataType = getDataType();
if (dataType == null) {
return false;
}
long displacement = dataType.getLength();
Address terminatorAddress = address.add(displacement);
try {
Address referencedAddress = getAbsoluteAddress(program, terminatorAddress);
if (referencedAddress == null || referencedAddress.getOffset() != 0) {
return false;
}
Data data = DataUtilities.createData(program, terminatorAddress,
PointerDataType.dataType, -1, false, getClearDataMode());
TypeDescriptorModel rtti0Model = model.getRtti0Model();
if (rtti0Model != null) {
monitor.checkCanceled();
String demangledTypeDescriptor = rtti0Model.getDemangledTypeDescriptor();
String prefixString = ((demangledTypeDescriptor != null)
? (demangledTypeDescriptor + Namespace.NAMESPACE_DELIMITER)
: "");
data.setComment(CodeUnit.EOL_COMMENT,
"terminator for " + prefixString + VF_TABLE_LABEL);
return true;
}
return false;
}
catch (CodeUnitInsertionException e) {
Msg.error(this, "Couldn't create vf table's null terminator pointer. " + e.getMessage(),
e);
return false;
}
}
private boolean createMetaPointer() {
Program program = model.getProgram();
Address metaAddress = address.subtract(program.getDefaultPointerSize());
// Create a pointer to the RTTI4 associated with the vf table.
DataType metaPointer = new PointerDataType(program.getDataTypeManager());
try {
DataUtilities.createData(program, metaAddress, metaPointer, metaPointer.getLength(),
false, getClearDataMode());
return true;
}
catch (CodeUnitInsertionException e) {
Msg.error(this, "Couldn't create vf table's meta pointer. " + e.getMessage(), e);
return false;
}
}
@Override
protected boolean createMarkup() throws CancelledException, InvalidDataTypeException {
boolean createdVfTableMarkup = createVfTableMarkup();
boolean createdMetaMarkup = createMetaMarkup();
return createdVfTableMarkup && createdMetaMarkup;
}
private boolean createVfTableMarkup() throws CancelledException, InvalidDataTypeException {
Address vfTableAddress = address;
Program program = model.getProgram();
monitor.checkCanceled();
TypeDescriptorModel rtti0Model = model.getRtti0Model();
if (rtti0Model != null) {
// Plate Comment
EHDataTypeUtilities.createPlateCommentIfNeeded(program,
RttiUtil.CONST_PREFIX + RttiUtil.getDescriptorTypeNamespace(rtti0Model) +
Namespace.NAMESPACE_DELIMITER,
VF_TABLE_LABEL, null, vfTableAddress, applyOptions);
monitor.checkCanceled();
// Label
if (applyOptions.shouldCreateLabel()) {
RttiUtil.createSymbolFromDemangledType(program, vfTableAddress, rtti0Model,
VF_TABLE_LABEL);
}
}
// Create functions that are referred to by the vf table.
if (applyOptions.shouldCreateFunction()) {
int elementCount = model.getElementCount();
for (int tableElementIndex = 0; tableElementIndex < elementCount; tableElementIndex++) {
monitor.checkCanceled();
Address vfPointer = model.getVirtualFunctionPointer(tableElementIndex);
if (vfPointer != null) {
EHDataTypeUtilities.createFunctionIfNeeded(program, vfPointer);
}
}
}
return true;
}
private boolean createMetaMarkup() throws CancelledException, InvalidDataTypeException {
Program program = model.getProgram();
Address metaAddress = getMetaAddress(program);
monitor.checkCanceled();
TypeDescriptorModel rtti0Model = model.getRtti0Model();
if (rtti0Model != null) {
// Plate Comment
EHDataTypeUtilities.createPlateCommentIfNeeded(
program, META_LABEL + " pointer for " +
RttiUtil.getDescriptorTypeNamespace(rtti0Model) + Namespace.NAMESPACE_DELIMITER,
VF_TABLE_LABEL, null, metaAddress, applyOptions);
monitor.checkCanceled();
// Label
if (applyOptions.shouldCreateLabel()) {
RttiUtil.createSymbolFromDemangledType(program, metaAddress, rtti0Model,
VF_TABLE_LABEL + NAME_SEPARATOR + META_LABEL + "_ptr");
}
}
return true;
}
/**
* Gets the address for the location of the meta data, which is a pointer to the RTTI4
* structure for this virtual function table (vftable).
* @param program the program containing the vftable being created by this command
* @return the address that contains the pointer to the RTTI 4 structure associated with
* the vftable.
*/
private Address getMetaAddress(Program program) {
return address.subtract(program.getDefaultPointerSize());
}
}
@@ -0,0 +1,405 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getReferencedAddress;
import ghidra.app.cmd.data.EHDataTypeUtilities;
import ghidra.app.cmd.data.TypeDescriptorModel;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
/**
*/
/**
* Model for run-time type information about the RTTI1 data type, which represents a
* BaseClassDescriptor structure.
* <p>
* Fields for this RunTimeTypeInformation structure can be found on http://www.openrce.org
* <p>
* <pre>
* struct BaseClassDescriptor {
* 4byte_ptr_or_disp pTypeDescriptor; // ref to TypeDescriptor (RTTI 0) for class
* dword numContainedBases; // count of extended classes in BaseClassArray (RTTI 2)
* struct pmd where; // member displacement structure
* dword attributes; // bit flags
* 4byte_ptr_or_disp pClassHierarchyDescriptor; // ref to ClassHierarchyDescriptor (RTTI 3) for class
* }
* </pre>
* <p>
* <pre>
* struct pmd {
* int mdisp; // member displacement
* int pdisp; // vbtable displacement
* int vdisp; // displacement within vbtable
* }
* </pre>
* <p>
* RTTI_Base_Class_Descriptor is the label for the RTTI1 data structure.
*/
public class Rtti1Model extends AbstractCreateRttiDataModel {
public static final String DATA_TYPE_NAME = "RTTIBaseClassDescriptor";
private static String STRUCTURE_NAME = "_s__" + DATA_TYPE_NAME;
private static final int NUM_BASES_ORDINAL = 1;
private static final int MEMBER_DISP_ORDINAL = 2;
private static final int ATTRIBUTES_ORDINAL = 3;
private static final int CLASS_HIERARCHY_POINTER_ORDINAL = 4;
private static final int TYPE_DESC_POINTER_OFFSET = 0;
private static final int NUM_BASES_OFFSET = 4;
private static final int CLASS_HIERARCHY_POINTER_OFFSET = 24;
private static final int MDISP_ORDINAL = 0;
private static final int PDISP_ORDINAL = 1;
private static final int VDISP_ORDINAL = 2;
private DataType dataType;
private TypeDescriptorModel rtti0Model;
private Rtti3Model rtti3Model;
/**
* Creates the model for the BaseClassDescriptor (RTTI 1) data type.
* @param program the program
* @param rtti1Address the address in the program for the RTTI1 data type.
* @param validationOptions options indicating how to validate the data type at the indicated
* address.
*/
public Rtti1Model(Program program, Address rtti1Address,
DataValidationOptions validationOptions) {
super(program, 1, rtti1Address, validationOptions);
}
@Override
public String getName() {
return DATA_TYPE_NAME;
}
/**
* Whether or not the memory at the indicated address appears to be a valid location for the
* indicated number of HandlerType data types.
* @throws InvalidDataTypeException if this model's location does not appear to be a valid
* group of catch handler entries. The exception has a message indicating
* why it does not appear to be a valid location for the data type.
*/
@Override
protected void validateModelSpecificInfo() throws InvalidDataTypeException {
Program program = getProgram();
Memory memory = program.getMemory();
Address startAddress = getAddress();
DataType rtti1Dt = getDataType();
DataType baseDt = DataTypeUtils.getBaseDataType(rtti1Dt);
Structure rtti1Struct = (Structure) baseDt;
int length = rtti1Dt.getLength();
// Test that we can get the expected number of bytes.
MSDataTypeUtils.getBytes(memory, startAddress, length);
boolean validateReferredToData = validationOptions.shouldValidateReferredToData();
// First component is either a direct reference or an image base offset.
Address rtti0Address = getReferencedAddress(program, startAddress);
if (rtti0Address == null) {
invalid(); // throws Exception
}
rtti0Model = new TypeDescriptorModel(program, rtti0Address, validationOptions);
if (validateReferredToData) {
try {
rtti0Model.validate();
}
catch (Exception e) {
invalid(e); // throws Exception
}
}
else if (!rtti0Model.isLoadedAndInitializedAddress()) {
invalid("Data referencing " + rtti0Model.getName() +
" data type isn't a loaded and initialized address " + rtti0Address + ".");
}
// Middle bytes are 5 dword numeric values.
try {
// numBases should be >= 0
int numBases = memory.getInt(startAddress.add(NUM_BASES_OFFSET));
// Check for valid numBases?
if (numBases < 0) {
invalid(); // throws Exception
}
DataTypeComponent pmdComponent = rtti1Struct.getComponent(MEMBER_DISP_ORDINAL);
DataType pmdDt = pmdComponent.getDataType();
DataType baseDataType = DataTypeUtils.getBaseDataType(pmdDt);
Structure pmdDataType = (Structure) baseDataType;
int pmdOffset = pmdComponent.getOffset();
int mdispOffset = pmdDataType.getComponent(MDISP_ORDINAL).getOffset(); // mdisp
int pdispOffset = pmdDataType.getComponent(PDISP_ORDINAL).getOffset(); // pdisp
int vdispOffset = pmdDataType.getComponent(VDISP_ORDINAL).getOffset(); // vdisp
// member displacement should be >= 0
int mDisp = memory.getInt(startAddress.add(pmdOffset + mdispOffset));
if (mDisp < 0) {
invalid(); // throws Exception
}
// vbtable displacement should be >= -1
int pDisp = memory.getInt(startAddress.add(pmdOffset + pdispOffset));
if (pDisp < -1) {
invalid(); // throws Exception
}
// displacement within vbtable should be >= 0
int vDisp = memory.getInt(startAddress.add(pmdOffset + vdispOffset));
if (vDisp < 0) {
invalid(); // throws Exception
}
// attributes can be any bit mask number, so don't check it
// int attributes = memory.getInt(startAddress.add(ATTRIBUTES_OFFSET));
}
catch (MemoryAccessException | AddressOutOfBoundsException e) {
invalid(); // throws Exception
}
// Last component is either a direct reference or an image base offset.
Address rtti3Address =
getReferencedAddress(program, startAddress.add(CLASS_HIERARCHY_POINTER_OFFSET));
if (rtti3Address == null) {
invalid(); // throws Exception
}
// Make sure we don't follow flow or will get stuck in infinite loop.
DataValidationOptions dontFollowOptions = new DataValidationOptions(validationOptions);
dontFollowOptions.setValidateReferredToData(false);
rtti3Model = new Rtti3Model(program, rtti3Address, dontFollowOptions);
if (validateReferredToData) {
try {
rtti3Model.validate();
}
catch (Exception e) {
invalid(e); // throws Exception
}
}
else if (!rtti3Model.isLoadedAndInitializedAddress()) {
invalid("Data referencing " + rtti3Model.getName() +
" data type isn't a loaded and initialized address " + rtti3Address + ".");
}
}
/**
* This gets the BaseClassDescriptor (RTTI 1) structure for the indicated program.
* @param program the program which will contain this data type.
* @return the BaseClassDescriptor (RTTI 1) structure.
*/
public static DataType getDataType(Program program) {
// Create simple data types for RTTI 1 & RTTI 3.
DataType rtti1Dt = getSimpleDataType(program);
DataType rtti3Dt = Rtti3Model.getSimpleDataType(program);
// Now make each refer to the other.
setRtti3DataType(rtti1Dt, program, rtti3Dt);
Rtti3Model.setRtti1DataType(rtti3Dt, program, rtti1Dt);
return MSDataTypeUtils.getMatchingDataType(program, rtti1Dt);
}
/**
* Make the indicated RTTI 1 refer to the indicated RTTI 3.
* @param rtti1Dt the RTTI 1 data type
* @param program the program that contains the data types
* @param rtti3Dt the RTTI 3 data type
*/
static void setRtti3DataType(DataType rtti1Dt, Program program, DataType rtti3Dt) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean is64Bit = MSDataTypeUtils.is64Bit(program);
Structure rtti1Struct = (Structure) DataTypeUtils.getBaseDataType(rtti1Dt);
DataType rtti3RefDt =
is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType(rtti3Dt);
rtti1Struct.replace(CLASS_HIERARCHY_POINTER_ORDINAL, rtti3RefDt, rtti3RefDt.getLength(),
"pClassHierarchyDescriptor", "ref to ClassHierarchyDescriptor (RTTI 3) for class");
}
/**
* This gets the BaseClassDescriptor (RTTI 1) structure for the indicated program.
* @param program the program which will contain this data type.
* @return the BaseClassDescriptor (RTTI 1) structure.
*/
static DataType getSimpleDataType(Program program) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean is64Bit = MSDataTypeUtils.is64Bit(program);
DataType rtti0Dt = TypeDescriptorModel.getDataType(program);
DataType rtti0RefDt =
is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType(rtti0Dt);
DataType rtti3RefDt =
is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType();
CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH);
StructureDataType struct =
getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME);
// Add the components.
struct.add(rtti0RefDt, "pTypeDescriptor", "ref to TypeDescriptor (RTTI 0) for class");
DWordDataType dWordDataType = new DWordDataType(dataTypeManager);
struct.add(dWordDataType, "numContainedBases",
"count of extended classes in BaseClassArray (RTTI 2)");
Structure pmdDataType = MSDataTypeUtils.getPMDDataType(program);
struct.add(pmdDataType, "where", "member displacement structure");
struct.add(dWordDataType, "attributes", "bit flags");
struct.add(rtti3RefDt, "pClassHierarchyDescriptor",
"ref to ClassHierarchyDescriptor (RTTI 3) for class");
return new TypedefDataType(categoryPath, DATA_TYPE_NAME, struct, dataTypeManager);
}
@Override
public DataType getDataType() {
if (dataType == null) {
dataType = getDataType(getProgram());
}
return dataType;
}
@Override
protected int getDataTypeLength() {
return getDataType().getLength();
}
/**
* Gets the address of the RTTI 0 structure that is referred to by a component of this RTTI 1.
* @return the address of the RTTI 0
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public Address getRtti0Address() throws InvalidDataTypeException {
checkValidity();
Program program = getProgram();
Address rtti1Address = getAddress();
Address rtti0ComponentAddress = rtti1Address.add(TYPE_DESC_POINTER_OFFSET);
return getReferencedAddress(program, rtti0ComponentAddress);
}
/**
* Gets the number of extended base classes in the base class array.
* @return the number of base classes
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public int getNumBases() throws InvalidDataTypeException {
checkValidity();
return EHDataTypeUtilities.getIntegerValue(getDataType(), NUM_BASES_ORDINAL,
getMemBuffer());
}
private int getPmdValue(int pmdOrdinal) throws InvalidDataTypeException {
checkValidity();
DataType rtti1Dt = getDataType();
DataType baseDt = DataTypeUtils.getBaseDataType(rtti1Dt);
Structure rtti1Struct = (Structure) baseDt;
DataTypeComponent component = rtti1Struct.getComponent(MEMBER_DISP_ORDINAL);
int pmdOffset = component.getOffset();
Address rtti1Address = getAddress();
Address pmdAddress = rtti1Address.add(pmdOffset);
DataType pmdDataType = component.getDataType();
MemBuffer pmdMemBuffer = new DumbMemBufferImpl(getProgram().getMemory(), pmdAddress);
return EHDataTypeUtilities.getIntegerValue(pmdDataType, pmdOrdinal, pmdMemBuffer);
}
/**
* Gets the member displacement for this RTTI 1.
* @return the member displacement
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public int getMDisp() throws InvalidDataTypeException {
return getPmdValue(MDISP_ORDINAL);
}
/**
* Gets the vbtable displacement for this RTTI 1.
* @return the vbtable displacement
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public int getPDisp() throws InvalidDataTypeException {
return getPmdValue(PDISP_ORDINAL);
}
/**
* Gets the displacement within the vbtable for this RTTI 1.
* @return the displacement in the vbtable
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public int getVDisp() throws InvalidDataTypeException {
return getPmdValue(VDISP_ORDINAL);
}
/**
* Gets the value of the attributes field for this RTTI 1.
* @return the attributes.
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public int getAttributes() throws InvalidDataTypeException {
checkValidity();
return EHDataTypeUtilities.getIntegerValue(getDataType(), ATTRIBUTES_ORDINAL,
getMemBuffer());
}
/**
* Gets the address of the RTTI 3 structure that is referred to by a component of this RTTI 1.
* @return the address of the RTTI 3
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public Address getRtti3Address() throws InvalidDataTypeException {
checkValidity();
Program program = getProgram();
Address rtti1Address = getAddress();
Address rtti3ComponentAddress = rtti1Address.add(CLASS_HIERARCHY_POINTER_OFFSET);
return getReferencedAddress(program, rtti3ComponentAddress);
}
@Override
public boolean refersToRtti0(Address rtti0Address) {
Address referredToAddress;
try {
referredToAddress = getRtti0Address();
}
catch (InvalidDataTypeException e) {
return false;
}
return rtti0Address.equals(referredToAddress);
}
/**
* Gets the type descriptor (RTTI 0) model associated with this RTTI 1.
* @return the type descriptor (RTTI 0) model or null.
*/
public TypeDescriptorModel getRtti0Model() {
try {
checkValidity();
}
catch (InvalidDataTypeException e) {
return null;
}
return rtti0Model;
}
}
@@ -0,0 +1,396 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getReferencedAddress;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.cmd.data.TypeDescriptorModel;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
import ghidra.docking.settings.Settings;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.DumbMemBufferImpl;
import ghidra.program.model.mem.Memory;
/**
* Model for run-time type information about the RTTI 2 data type, which represents an
* array of either pointers or displacements to the BaseClassDescriptors (RTTI 1s) for
* a class.
* <p>
* Fields for this RunTimeTypeInformation structure can be found on http://www.openrce.org
* <p>
* RTTI_Base_Class_Array is the label for the RTTI2 data structure.
*/
public class Rtti2Model extends AbstractCreateRttiDataModel {
public static final String DATA_TYPE_NAME = "RTTIBaseClassArray";
private DataType dataType;
private DataType simpleIndividualEntryDataType;
private int entrySize;
private List<Rtti1Model> rtti1Models;
/**
* Creates the model for the RTTI2 data type.
* @param program the program
* @param rtti1Count the number of RTTI1 data type references expected at the RTTI2 address.
* @param rtti2Address the address in the program for the RTTI Base Class Array.
* @param validationOptions options indicating how to validate the data type at the indicated
* address.
*/
public Rtti2Model(Program program, int rtti1Count, Address rtti2Address,
DataValidationOptions validationOptions) {
super(program, rtti1Count, rtti2Address, validationOptions);
simpleIndividualEntryDataType = getSimpleIndividualEntryDataType(program);
entrySize = simpleIndividualEntryDataType.getLength();
rtti1Models = new ArrayList<>();
}
@Override
public String getName() {
return DATA_TYPE_NAME;
}
@Override
protected void validateModelSpecificInfo() throws InvalidDataTypeException {
Program program = getProgram();
Address startAddress = getAddress();
long numEntries = getCount();
Memory memory = program.getMemory();
// Each entry is a 4 byte value.
if (numEntries == 0) {
numEntries = getNumEntries(program, startAddress);
}
if (numEntries == 0 || !validRefData(memory, startAddress)) {
invalid();
}
boolean validateReferredToData = validationOptions.shouldValidateReferredToData();
validateAllRtti1RefEntries(program, startAddress, numEntries, validateReferredToData);
}
private void validateAllRtti1RefEntries(Program program, Address startAddress, long numEntries,
boolean validateReferredToData) throws InvalidDataTypeException {
Memory memory = program.getMemory();
Address addr = startAddress;
for (int ordinal = 0; ordinal < numEntries && addr != null &&
validRefData(memory, addr); ordinal++) {
validateRtti1ReferenceEntry(program, validateReferredToData, addr);
try {
addr = addr.add(entrySize); // Add the data type size.
}
catch (AddressOutOfBoundsException e) {
if (ordinal < (numEntries - 1)) {
invalid();
}
break;
}
}
}
private void validateRtti1ReferenceEntry(Program program, boolean validateReferredToData,
Address addr) throws InvalidDataTypeException {
// Each component is either a direct reference or an image base offset.
Address rtti1Address = getReferencedAddress(program, addr);
if (rtti1Address == null) {
invalid();
}
Rtti1Model rtti1Model = new Rtti1Model(program, rtti1Address, validationOptions);
rtti1Models.add(rtti1Model);
if (validateReferredToData) {
rtti1Model.validate();
}
else if (!rtti1Model.isLoadedAndInitializedAddress()) {
rtti1Models.clear();
invalid("Data referencing " + Rtti1Model.DATA_TYPE_NAME +
" data type isn't a loaded and initialized address " + rtti1Address + ".");
}
}
/**
* This gets the data type for an individual entry in the array of RTTI 1 references
* produced by this model.
* @param program the program which will contain this data type.
* @param rtti1Dt the RTTI 1 data type associated with this RTTI 2.
* @return the data type for an individual array entry.
*/
public static DataType getIndividualEntryDataType(Program program, DataType rtti1Dt) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
if (MSDataTypeUtils.is64Bit(program)) {
return new ImageBaseOffset32DataType(dataTypeManager);
}
return new PointerDataType(rtti1Dt, dataTypeManager);
}
/**
* This gets the data type for an individual entry in the array of RTTI 1 references
* produced by this model.
* @param program the program which will contain this data type.
* @return the data type for an individual array entry.
*/
static DataType getSimpleIndividualEntryDataType(Program program) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
if (MSDataTypeUtils.is64Bit(program)) {
return new ImageBaseOffset32DataType(dataTypeManager);
}
return new PointerDataType(dataTypeManager);
}
/**
* This gets the BaseClassArray (RTTI 2) structure for the indicated program.
* @param program the program which will contain this data type.
* @return the BaseClassArray (RTTI 2) structure or null.
*/
public DataType getDataType(Program program) {
DataType rtti1Dt = Rtti1Model.getDataType(program);
return getDataType(program, rtti1Dt);
}
private DataType getDataType(Program program, DataType rtti1Dt) {
setIsDataTypeAlreadyBasedOnCount(true);
DataTypeManager dataTypeManager = program.getDataTypeManager();
int numElements = getCount();
// Each entry is a 4 byte value.
if (numElements == 0) {
numElements = getNumEntries(program, getAddress());
}
if (numElements <= 0) {
return null; // invalid for rtti 2.
}
DataType individualEntryDataType = getIndividualEntryDataType(program, rtti1Dt);
ArrayDataType array = new ArrayDataType(individualEntryDataType, numElements,
rtti1Dt.getLength(), dataTypeManager);
return MSDataTypeUtils.getMatchingDataType(program, array);
}
/**
* This gets the BaseClassArray (RTTI 2) structure for this model.
* @return the BaseClassArray (RTTI 2) structure.
*/
@Override
public DataType getDataType() {
if (dataType == null) {
dataType = getDataType(getProgram());
}
return dataType;
}
@Override
protected int getDataTypeLength() {
DataType dt = getDataType();
return (dt != null) ? dt.getLength() : 0;
}
@Override
public boolean refersToRtti0(Address rtti0Address) {
try {
checkValidity();
}
catch (InvalidDataTypeException e) {
return false;
}
Program program = getProgram();
long rtti1Count = getCount();
Address rtti2Address = getAddress();
long numEntries = (rtti1Count != 0) ? rtti1Count : getNumEntries(program, rtti2Address);
if (numEntries == 0) {
return false;
}
if (validationOptions.shouldValidateReferredToData()) {
for (Rtti1Model rtti1Model : rtti1Models) {
if (rtti1Model.refersToRtti0(rtti0Address)) {
return true;
}
}
return false;
}
Address addr = rtti2Address;
Memory memory = program.getMemory();
for (int ordinal = 0; ordinal < numEntries && addr != null &&
validRefData(memory, addr); ordinal++) {
// Each component is either a direct reference or an image base offset.
Address rtti1Address = getReferencedAddress(program, addr);
if (rtti1Address == null) {
return false;
}
Rtti1Model rtti1Model = new Rtti1Model(program, rtti1Address, validationOptions);
if (rtti1Model.refersToRtti0(rtti0Address)) {
return true;
}
addr = addr.add(4); // Add the data type size.
}
return false;
}
private int getNumEntries(Program program, Address rtti2Address) {
Memory memory = program.getMemory();
Address addr = rtti2Address;
boolean shouldValidateReferredToData = validationOptions.shouldValidateReferredToData();
int ordinal = 0;
for (; addr != null && validRefData(memory, addr); ordinal++) {
// Each component is either a direct reference or an image base offset.
Address rtti1Address = getReferencedAddress(program, addr);
if (rtti1Address == null) {
return ordinal; // It has reached the end.
}
Rtti1Model rtti1Model = new Rtti1Model(program, rtti1Address, validationOptions);
if (shouldValidateReferredToData) {
try {
rtti1Model.validate();
}
catch (InvalidDataTypeException e1) {
return ordinal; // It has reached the end.
}
}
else if (!rtti1Model.isLoadedAndInitializedAddress()) {
return ordinal;
}
try {
addr = addr.add(entrySize); // Add the data type size.
}
catch (AddressOutOfBoundsException e) {
return ordinal + 1; // Ordinal hasn't been incremented yet.
}
}
return ordinal;
}
private boolean validRefData(Memory memory, Address addr) {
Program program = memory.getProgram();
boolean is64Bit = MSDataTypeUtils.is64Bit(program);
DumbMemBufferImpl refBuffer = new DumbMemBufferImpl(memory, addr);
Settings settings = simpleIndividualEntryDataType.getDefaultSettings();
Object value = simpleIndividualEntryDataType.getValue(refBuffer, settings, 4);
if (value instanceof Address) {
Address address = (Address) value;
if (is64Bit && program.getImageBase().equals(address)) {
return false; // zero value.
}
if (!is64Bit && address.getOffset() == 0L) {
return false; // zero value.
}
return memory.getLoadedAndInitializedAddressSet().contains(address);
}
return false;
}
/**
* Get the Base Class Types (type descriptor names) for the RTTI for this model.
* @return the class names or an empty list if the name(s) can't be determined.
* @throws InvalidDataTypeException if an invalid model is encountered when trying to get
* the base class types. The exception has a message indicating
* why it does not appear to be a valid location for the data type.
*/
public List<String> getBaseClassTypes() throws InvalidDataTypeException {
List<String> names = new ArrayList<>();
Program program = getProgram();
long rtti1Count = getCount();
for (int rtti1Index = 0; rtti1Index < rtti1Count; rtti1Index++) {
Address rtti1Address = getRtti1Address(rtti1Index);
Rtti1Model rtti1Model = new Rtti1Model(program, rtti1Address, validationOptions);
if (validationOptions != null) {
try {
rtti1Model.validate();
}
catch (Exception e) {
invalid("Not a valid " + Rtti1Model.DATA_TYPE_NAME + " @" + rtti1Address);
}
}
TypeDescriptorModel rtti0ModelForRtti1 = rtti1Model.getRtti0Model();
String structName = rtti0ModelForRtti1.getDescriptorName();
if (structName == null) {
return new ArrayList<>(); // If a name can't be determined return an empty list.
}
names.add(structName);
}
return names;
}
/**
* Gets address referred to by the RTTI 1 pointer at the specified index in the RTTI2's array
* @param rtti1Index index of the RTTI 1 pointer in the array
* @return the address of the RTTI 1.
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public Address getRtti1Address(int rtti1Index) throws InvalidDataTypeException {
checkValidity();
Address rtti1Address = getAddress().add(rtti1Index * entrySize);
return getReferencedAddress(getProgram(), rtti1Address);
}
/**
* Gets the type descriptor (RTTI 0) model associated with this RTTI 2.
* @return the type descriptor (RTTI 0) model or null.
* @throws InvalidDataTypeException if this model's validation fails.
*/
public TypeDescriptorModel getRtti0Model() throws InvalidDataTypeException {
checkValidity();
// If valid, we will have the RTTI1 models already.
if (!rtti1Models.isEmpty()) {
// The first RTTI 1 indicates the class for this RTTI 2.
Rtti1Model rtti1Model = rtti1Models.get(0);
return rtti1Model.getRtti0Model();
}
throw new InvalidDataTypeException(
getDefaultInvalidMessage() + " The array needs at least one entry.");
}
/**
* Gets the BaseClassDescriptor (RTTI 1) model associated with this RTTI 2.
* @param rtti1Index index of the RTTI 1 pointer in the array
* @return the BaseClassDescriptor (RTTI 1) model or null.
* @throws InvalidDataTypeException if this model's validation fails.
*/
public Rtti1Model getRtti1Model(int rtti1Index) throws InvalidDataTypeException {
checkValidity();
return rtti1Models.get(rtti1Index);
}
}
@@ -0,0 +1,319 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAlignedPack4Structure;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getReferencedAddress;
import java.util.List;
import ghidra.app.cmd.data.EHDataTypeUtilities;
import ghidra.app.cmd.data.TypeDescriptorModel;
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.scalar.Scalar;
import ghidra.util.Msg;
/**
* Model for run-time type information about the RTTI3 data type, which represents a
* ClassHierarchyDescriptor structure.
* <p>
* Fields for this RunTimeTypeInformation structure can be found on http://www.openrce.org
* <p>
* <pre>
* struct ClassHierarchyDescriptor {
* dword signature;
* dword attributes; // bit flags
* dword numBaseClasses; // count of RTTI 1 ref entries in RTTI 2 array
* 4byte_ptr_or_disp pBaseClassArray; // ref to BaseClassArray (RTTI 2)
* }
* </pre>
* <p>
* RTTI_Class_Hierarchy_Descriptor is the label for the RTTI3 data structure.
*/
public class Rtti3Model extends AbstractCreateRttiDataModel {
public static final String DATA_TYPE_NAME = "RTTIClassHierarchyDescriptor";
private static String STRUCTURE_NAME = "_s__" + DATA_TYPE_NAME;
private static final int SIGNATURE_ORDINAL = 0;
private static final int ATTRIBUTES_ORDINAL = 1;
private static final int BASE_ARRAY_PTR_ORDINAL = 3;
private static final int NUM_BASES_OFFSET = 8;
private static final int BASE_ARRAY_PTR_OFFSET = 12;
private static final long MAX_RTTI_1_COUNT = 1000;
private DataType dataType;
private Rtti2Model rtti2Model;
/**
* Creates the model for the RTTI3 data type.
* @param program the program
* @param rtti3Address the address in the program for the RTTI3 data
* types.
* @param validationOptions options indicating how to validate the data type at the indicated
* address.
*/
public Rtti3Model(Program program, Address rtti3Address,
DataValidationOptions validationOptions) {
super(program, rtti3Address, validationOptions);
}
@Override
public String getName() {
return DATA_TYPE_NAME;
}
@Override
protected void validateModelSpecificInfo() throws InvalidDataTypeException {
Program program = getProgram();
// Num1 is dword at SIGNATURE_OFFSET.
// No additional validation for this yet.
// Num2 is dword at ATTRIBUTES_OFFSET.
// No additional validation for this yet.
// Next four bytes after 2 dwords should be number of RTTI1 pointers in RTTI2.
int rtti1Count = getRtti1Count();
if (rtti1Count < 1 || rtti1Count > MAX_RTTI_1_COUNT) { // For now assume we shouldn't be seeing more than 1000 pointers in RTTI2.
throw new InvalidDataTypeException(getName() + " data type at " + getAddress() +
" doesn't have a valid " + Rtti1Model.DATA_TYPE_NAME + " count.");
}
boolean validateReferredToData = validationOptions.shouldValidateReferredToData();
// Last component should refer to RTTI2.
Address rtti2Address = getRtti2Address();
if (rtti2Address == null) {
throw new InvalidDataTypeException(getName() + " data type at " + getAddress() +
" doesn't refer to a valid location for the " + Rtti2Model.DATA_TYPE_NAME + ".");
}
rtti2Model = new Rtti2Model(program, rtti1Count, rtti2Address, validationOptions);
if (validateReferredToData) {
rtti2Model.validate();
}
else if (!rtti2Model.isLoadedAndInitializedAddress()) {
throw new InvalidDataTypeException("Data referencing " + rtti2Model.getName() +
" data type isn't a loaded and initialized address " + rtti2Address + ".");
}
}
/**
* This gets the ClassHierarchyDescriptor (RTTI 3) structure for the indicated program.
* @param program the program which will contain this data type.
* @return the ClassHierarchyDescriptor (RTTI 3) structure.
*/
public static DataType getDataType(Program program) {
// Create simple data types for RTTI 1 & RTTI 3.
DataType rtti3Dt = getSimpleDataType(program);
DataType rtti1Dt = Rtti1Model.getSimpleDataType(program);
// Now make each refer to the other.
setRtti1DataType(rtti3Dt, program, rtti1Dt);
Rtti1Model.setRtti3DataType(rtti1Dt, program, rtti3Dt);
return MSDataTypeUtils.getMatchingDataType(program, rtti3Dt);
}
/**
* Make the indicated RTTI 3 refer to the indicated RTTI 1 through the RTTI 2 reference.
* @param rtti3Dt the RTTI 3 data type
* @param program the program that contains the data types
* @param rtti1Dt the RTTI 1 data type
*/
static void setRtti1DataType(DataType rtti3Dt, Program program, DataType rtti1Dt) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean is64Bit = MSDataTypeUtils.is64Bit(program);
Structure rtti3Struct = (Structure) DataTypeUtils.getBaseDataType(rtti3Dt);
DataType individualRtti2EntryDt = Rtti2Model.getIndividualEntryDataType(program, rtti1Dt);
DataType rtti2RefDt = is64Bit ? new ImageBaseOffset32DataType(dataTypeManager)
: new PointerDataType(individualRtti2EntryDt);
rtti3Struct.replace(BASE_ARRAY_PTR_ORDINAL, rtti2RefDt, rtti2RefDt.getLength(),
"pBaseClassArray", "ref to BaseClassArray (RTTI 2)");
}
/**
* This gets the ClassHierarchyDescriptor (RTTI 3) structure for the indicated program.
* @param program the program which will contain this data type.
* @return the ClassHierarchyDescriptor (RTTI 3) structure.
*/
static DataType getSimpleDataType(Program program) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
boolean is64Bit = MSDataTypeUtils.is64Bit(program);
CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH);
StructureDataType struct =
getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME);
// Add the components.
DWordDataType dWordDataType = new DWordDataType(dataTypeManager);
struct.add(dWordDataType, "signature", null);
struct.add(dWordDataType, "attributes", "bit flags");
struct.add(dWordDataType, "numBaseClasses", "number of base classes (i.e. rtti1Count)");
DataType rtti2Dt = Rtti2Model.getSimpleIndividualEntryDataType(program);
DataType rtti2RefDt =
is64Bit ? new ImageBaseOffset32DataType(dataTypeManager) : new PointerDataType(rtti2Dt);
struct.add(rtti2RefDt, "pBaseClassArray", "ref to BaseClassArray (RTTI 2)");
return new TypedefDataType(categoryPath, DATA_TYPE_NAME, struct, dataTypeManager);
}
@Override
public DataType getDataType() {
if (dataType == null) {
dataType = getDataType(getProgram());
}
return dataType;
}
@Override
protected int getDataTypeLength() {
return getDataType().getLength();
}
/**
* Gets the signature value from this RTTI 3.
* @return the signature value
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public int getSignature() throws InvalidDataTypeException {
checkValidity();
return EHDataTypeUtilities.getIntegerValue(getDataType(), SIGNATURE_ORDINAL,
getMemBuffer());
}
/**
* Gets the attributes value from this RTTI3.
* @return the attributes
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public int getAttributes() throws InvalidDataTypeException {
checkValidity();
return EHDataTypeUtilities.getIntegerValue(getDataType(), ATTRIBUTES_ORDINAL,
getMemBuffer());
}
/**
* Gets the number of RTTI1 structures that are referred to by an RTTI3 structure being placed
* at the rtti3Address of the indicated memory.
* @return the RTTI1 count or 0.
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public int getRtti1Count() throws InvalidDataTypeException {
checkValidity();
Address rtti3Address = getAddress();
return getRtti1Count(getProgram(), rtti3Address);
}
public static int getRtti1Count(Program program, Address rtti3Address) {
Memory memory = program.getMemory();
Address rtti1CountAddress = rtti3Address.add(NUM_BASES_OFFSET);
int rtti1Count = 0;
try {
rtti1Count =
(int) new Scalar(32, memory.getInt(rtti1CountAddress, memory.isBigEndian()))
.getValue();
return rtti1Count;
}
catch (MemoryAccessException e) {
Msg.error(Rtti3Model.class, "Unexpected Exception: " + e.getMessage(), e);
return 0;
}
}
/**
* Gets the address of the RTTI2 that is referred to from an RTTI3 structure that is placed at
* the indicated address.
* @return the address of the RTTI2 structure or null.
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public Address getRtti2Address() throws InvalidDataTypeException {
checkValidity();
return getRtti2Address(getProgram(), getAddress());
}
private static Address getRtti2Address(Program program, Address rtti3Address) {
Memory memory = program.getMemory();
Address rtti2CompAddress = rtti3Address.add(BASE_ARRAY_PTR_OFFSET);
Address pointedToAddress = getReferencedAddress(program, rtti2CompAddress);
if (pointedToAddress == null || !memory.contains(pointedToAddress)) {
return null;
}
return pointedToAddress;
}
@Override
public boolean refersToRtti0(Address rtti0Address) {
try {
checkValidity();
return rtti2Model.refersToRtti0(rtti0Address);
}
catch (InvalidDataTypeException e) {
return false;
}
}
/**
* Get the Base Class Types (type descriptor names) for the RTTI for this model.
* @return the class names or an empty list if the name(s) can't be determined.
* @throws InvalidDataTypeException if an invalid model is encountered when trying to get
* the base class types. The exception has a message indicating
* why it does not appear to be a valid location for the data type.
*/
public List<String> getBaseClassTypes() throws InvalidDataTypeException {
checkValidity();
return rtti2Model.getBaseClassTypes();
}
/**
* Gets the type descriptor (RTTI 0) model associated with this RTTI 3.
* @return the type descriptor (RTTI 0) model or null.
* @throws InvalidDataTypeException if this model's validation fails.
*/
public TypeDescriptorModel getRtti0Model() throws InvalidDataTypeException {
checkValidity();
return rtti2Model.getRtti0Model();
}
/**
* Gets the BaseClassArray (RTTI 2) model associated with this RTTI 3.
* @return the BaseClassArray (RTTI 2) model or null.
* @throws InvalidDataTypeException if this model's validation fails.
*/
public Rtti2Model getRtti2Model() throws InvalidDataTypeException {
checkValidity();
return rtti2Model;
}
}
@@ -0,0 +1,349 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.*;
import java.util.List;
import ghidra.app.cmd.data.EHDataTypeUtilities;
import ghidra.app.cmd.data.TypeDescriptorModel;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
/**
* Model for run-time type information about the RTTI4 data type, which represents a
* CompleteObjectLocator structure.
* <p>
* Fields for this RunTimeTypeInformation structure can be found on http://www.openrce.org
* <p>
* <pre>
* struct CompleteObjectLocator {
* dword signature;
* dword offset; // offset of vbtable within class
* dword cdOffset; // constructor displacement offset
* 4byte_ptr_or_disp pRtti0; // ref to TypeDescriptor (RTTI 0) for class
* 4byte_ptr_or_disp pRtti3; // ref to ClassHierarchyDescriptor (RTTI 3)
* }
* </pre>
* <p>
* RTTI_Complete_Object_Locator is the label for the RTTI4 data structure.
*/
public class Rtti4Model extends AbstractCreateRttiDataModel {
public static final String DATA_TYPE_NAME = "RTTICompleteObjectLocator";
private static String STRUCTURE_NAME = "_s__" + DATA_TYPE_NAME;
private final static int SIGNATURE_ORDINAL = 0;
private final static int VB_TABLE_OFFSET_ORDINAL = 1;
private final static int CONSTRUCTOR_DISP_OFFSET_ORDINAL = 2;
private final static int SIGNATURE_OFFSET = 0;
private final static int VB_TABLE_OFFSET_OFFSET = 4;
private final static int CONSTRUCTOR_DISP_OFFSET_OFFSET = 8;
private final static int RTTI_0_PTR_OFFSET = 12;
private final static int RTTI_3_PTR_OFFSET = 16;
private DataType dataType;
private TypeDescriptorModel rtti0Model;
private Rtti3Model rtti3Model;
/**
* Creates the model for the RTTI4 data type.
* @param program the program
* @param rtti4Address the address in the program for the RTTI4 data
* types.
* @param validationOptions options indicating how to validate the data type at the indicated
* address.
*/
public Rtti4Model(Program program, Address rtti4Address,
DataValidationOptions validationOptions) {
super(program, 1, rtti4Address, validationOptions);
}
@Override
public String getName() {
return DATA_TYPE_NAME;
}
@Override
public void validateModelSpecificInfo() throws InvalidDataTypeException {
Program program = getProgram();
Address startAddress = getAddress();
boolean validateReferredToData = validationOptions.shouldValidateReferredToData();
// Num1 is dword at SIGNATURE_OFFSET.
// No additional validation for this yet.
// Num2 is dword at VB_TABLE_OFFSET_OFFSET.
// No additional validation for this yet.
// Num3 is dword at CONSTRUCTOR_DISP_OFFSET_OFFSET.
// No additional validation for this yet.
// Next component should refer to RTTI0.
Address rtti0CompAddress = startAddress.add(RTTI_0_PTR_OFFSET);
Address rtti0Address = getReferencedAddress(program, rtti0CompAddress);
if (rtti0Address == null) {
throw new InvalidDataTypeException(getName() + " data type at " + getAddress() +
" doesn't refer to a valid location " + rtti0Address + " for the Type Descriptor.");
}
rtti0Model = new TypeDescriptorModel(program, rtti0Address, validationOptions);
if (validateReferredToData) {
rtti0Model.validate();
}
else if (!rtti0Model.isLoadedAndInitializedAddress()) {
throw new InvalidDataTypeException("Data referencing " + rtti0Model.getName() +
" data type isn't a loaded and initialized address " + rtti0Address + ".");
}
// Last 4 bytes should refer to RTTI3.
Address rtti3CompAddress = startAddress.add(RTTI_3_PTR_OFFSET);
Address rtti3Address = getReferencedAddress(program, rtti3CompAddress);
if (rtti3Address == null) {
throw new InvalidDataTypeException(getName() + " data type at " + getAddress() +
" doesn't refer to a valid location for the Class Hierarchy Descriptor.");
}
rtti3Model = new Rtti3Model(program, rtti3Address, validationOptions);
if (validateReferredToData) {
rtti3Model.validate();
}
else if (!rtti3Model.isLoadedAndInitializedAddress()) {
throw new InvalidDataTypeException("Data referencing " + rtti3Model.getName() +
" data type isn't a loaded and initialized address " + rtti3Address + ".");
}
}
/**
* This gets the CompleteObjectLocator (RTTI 4) structure for the indicated program.
* @param program the program which will contain this data type.
* @return the CompleteObjectLocator (RTTI 4) structure.
*/
public static DataType getDataType(Program program) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
CategoryPath categoryPath = new CategoryPath(CATEGORY_PATH);
StructureDataType struct =
getAlignedPack4Structure(dataTypeManager, categoryPath, STRUCTURE_NAME);
// Add the components.
DWordDataType dWordDataType = new DWordDataType(dataTypeManager);
struct.add(dWordDataType, "signature", null);
struct.add(dWordDataType, "offset", "offset of vbtable within class");
struct.add(dWordDataType, "cdOffset", "constructor displacement offset");
DataType rtti0RefDt =
getReferenceDataType(program, TypeDescriptorModel.getDataType(program));
struct.add(rtti0RefDt, "pTypeDescriptor", "ref to TypeDescriptor (RTTI 0) for class");
DataType rtti3Dt = Rtti3Model.getDataType(program);
DataType rtti3RefDt = getReferenceDataType(program, rtti3Dt);
struct.add(rtti3RefDt, "pClassDescriptor", "ref to ClassHierarchyDescriptor (RTTI 3)");
TypedefDataType typedefDt =
new TypedefDataType(categoryPath, DATA_TYPE_NAME, struct, dataTypeManager);
return MSDataTypeUtils.getMatchingDataType(program, typedefDt);
}
/**
* Gets the offset of the field in this RTTI 4 that has the signature.
* @return the offset of the signature
*/
public static int getSignatureComponentOffset() {
return SIGNATURE_OFFSET;
}
/**
* Gets the offset of the field in this RTTI 4 that has the offset of the vb table in the class.
* @return the offset of the vb table offset
*/
public static int getVBTableComponentOffset() {
return VB_TABLE_OFFSET_OFFSET;
}
/**
* Gets the offset of the field in this RTTI 4 that has the constructor displacement offset.
* @return the offset of the constructor displacement offset
*/
public static int getConstructorDisplacementComponentOffset() {
return CONSTRUCTOR_DISP_OFFSET_OFFSET;
}
/**
* Gets the offset of the field in this RTTI 4 that has the RTTI 0 pointer.
* @return the offset of the RTTI 0 pointer
*/
public static int getRtti0PointerComponentOffset() {
return RTTI_0_PTR_OFFSET;
}
/**
* Gets the offset of the field in this RTTI 4 that has the RTTI 3 pointer.
* @return the offset of the RTTI 3 pointer
*/
public static int getRtti3PointerComponentOffset() {
return RTTI_3_PTR_OFFSET;
}
/**
* Gets the address of the component for the RTTI 0 pointer.
* @return the component address of the RTTI 0 pointer
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public Address getRtti0FieldAddress() throws InvalidDataTypeException {
checkValidity();
return getAddress().add(RTTI_0_PTR_OFFSET);
}
/**
* Gets the address of the component for the RTTI 3 pointer.
* @return the component address of the RTTI 3 pointer
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public Address getRtti3FieldAddress() throws InvalidDataTypeException {
checkValidity();
return getAddress().add(RTTI_3_PTR_OFFSET);
}
/**
* Gets the signature value in this RTTI 4.
* @return the signature value
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public int getSignature() throws InvalidDataTypeException {
checkValidity();
return EHDataTypeUtilities.getIntegerValue(getDataType(), SIGNATURE_ORDINAL,
getMemBuffer());
}
/**
* Gets the virtual base table offset in this RTTI 4.
* @return the vb table offset
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public int getVbTableOffset() throws InvalidDataTypeException {
checkValidity();
return EHDataTypeUtilities.getIntegerValue(getDataType(), VB_TABLE_OFFSET_ORDINAL,
getMemBuffer());
}
/**
* Gets the constructor displacement offset in this RTTI 4.
* @return the constructor displacement offset
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public int getConstructorOffset() throws InvalidDataTypeException {
checkValidity();
return EHDataTypeUtilities.getIntegerValue(getDataType(), CONSTRUCTOR_DISP_OFFSET_ORDINAL,
getMemBuffer());
}
/**
* Gets the address of the RTTI 0 structure that is pointed to by this RTTI 4.
* @return the address of the RTTI 0
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public Address getRtti0Address() throws InvalidDataTypeException {
checkValidity();
Address rtti0CompAddress = getAddress().add(RTTI_0_PTR_OFFSET);
return getReferencedAddress(getProgram(), rtti0CompAddress);
}
/**
* Gets the address of the RTTI 3 structure that is pointed to by this RTTI 4.
* @return the address of the RTTI 3
* @throws InvalidDataTypeException if this isn't a valid model at the specified address.
*/
public Address getRtti3Address() throws InvalidDataTypeException {
checkValidity();
Address rtti3CompAddress = getAddress().add(RTTI_3_PTR_OFFSET);
return getReferencedAddress(getProgram(), rtti3CompAddress);
}
@Override
public DataType getDataType() {
if (dataType == null) {
dataType = getDataType(getProgram());
}
return dataType;
}
@Override
protected int getDataTypeLength() {
return getDataType().getLength();
}
@Override
public boolean refersToRtti0(Address rtti0Address) {
// Check the RTTI 0 reference in this RTTI 4.
Address rtti0AddressInRtti4;
try {
checkValidity();
rtti0AddressInRtti4 = getRtti0Address();
if (rtti0AddressInRtti4 == null || !rtti0AddressInRtti4.equals(rtti0Address)) {
return false;
}
// Check the RTTI 0 reference that should be reached via the RTTI 3 reference.
return rtti3Model.refersToRtti0(rtti0Address);
}
catch (InvalidDataTypeException e) {
return false;
}
}
/**
* Get the Base Class Types (type descriptor names) for the RTTI for this model.
* @return the class names or an empty list if the name(s) can't be determined.
* @throws InvalidDataTypeException if an invalid model is encountered when trying to get
* the base class types. The exception has a message indicating
* why it does not appear to be a valid location for the data type.
*/
public List<String> getBaseClassTypes() throws InvalidDataTypeException {
checkValidity();
return rtti3Model.getBaseClassTypes();
}
/**
* Gets the type descriptor (RTTI 0) model associated with this RTTI 4.
* @return the type descriptor (RTTI 0) model or null.
* @throws InvalidDataTypeException if this model's validation fails.
*/
public TypeDescriptorModel getRtti0Model() throws InvalidDataTypeException {
checkValidity();
return rtti0Model;
}
/**
* Gets the ClassHierarchyDescriptor (RTTI 3) model associated with this RTTI 4.
* @return the ClassHierarchyDescriptor (RTTI 3) model or null.
* @throws InvalidDataTypeException if this model's validation fails.
*/
public Rtti3Model getRtti3Model() throws InvalidDataTypeException {
checkValidity();
return rtti3Model;
}
}
@@ -0,0 +1,168 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAbsoluteAddress;
import ghidra.app.cmd.data.TypeDescriptorModel;
import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.PseudoDisassembler;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.GhidraClass;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg;
import ghidra.util.exception.InvalidInputException;
/**
* RttiUtil provides constants and static methods for processing RTTI information.
*/
class RttiUtil {
static final String CONST_PREFIX = "const ";
private RttiUtil() {
// utility class; can't create
}
/**
* Function that will create a symbol based on the <code>rttiSuffix</code>, which is in the
* class or namespace that is indicated by the <code>demangledType</code> string.
*
* @param program the program where the symbol is being created
* @param rttiAddress Address of the RTTI datatype
* @param typeDescriptorModel the model for the type descriptor structure
* @param rttiSuffix suffix name indicating which type of RTTI structure
* @return the symbol or null.
*/
static Symbol createSymbolFromDemangledType(Program program, Address rttiAddress,
TypeDescriptorModel typeDescriptorModel, String rttiSuffix) {
rttiSuffix = SymbolUtilities.replaceInvalidChars(rttiSuffix, true);
// Get or create the namespace for this RTTI's type descriptor.
Namespace classNamespace = typeDescriptorModel.getDescriptorAsNamespace();
// If the RTTI's type descriptor is for a class or struct then promote its
// namespace to a class.
// <br>Note: For now this assumes all classes and structs with RTTI data must
// actually be classes. In the future this might need additional checking before
// promoting some "struct" ref types to being a class, if we can better determine
// whether or not they are actually classes.
String refType = typeDescriptorModel.getRefType(); // Can be null.
boolean makeClass = "class".equals(refType) || "struct".equals(refType);
SymbolTable symbolTable = program.getSymbolTable();
if (makeClass && (classNamespace != null) && !(classNamespace instanceof GhidraClass)) {
try {
classNamespace = NamespaceUtils.convertNamespaceToClass(classNamespace);
}
catch (InvalidInputException iie) {
Msg.error(RttiUtil.class,
"Unable to convert namespace to class for namespace " + classNamespace + ".",
iie);
}
}
// See if the symbol already exists for the RTTI data.
Symbol matchingSymbol = symbolTable.getSymbol(rttiSuffix, rttiAddress, classNamespace);
if (matchingSymbol != null) {
return matchingSymbol;
}
// Don't create it if a similar symbol already exists at the address of the data.
Symbol[] symbols = symbolTable.getSymbols(rttiAddress);
for (Symbol symbol : symbols) {
String name = symbol.getName();
if (name.contains(rttiSuffix)) {
return symbol; // Similar symbol already exists.
}
}
try {
// Didn't find the symbol, so create it.
return symbolTable.createLabel(rttiAddress, rttiSuffix, classNamespace,
SourceType.IMPORTED);
}
catch (InvalidInputException e) {
Msg.error(RttiUtil.class,
"Unable to create label for " + rttiSuffix + " at " + rttiAddress + ".", e);
return null;
}
}
/**
* Determines the number of vf addresses in the vf table that begins at the specified base
* address.
* @param program the program whose memory is providing their addresses
* @param vfTableBaseAddress the base address in the program for the vf table
* @return the number of virtual function addresses in the vf table
*/
static int getVfTableCount(Program program, Address vfTableBaseAddress) {
Memory memory = program.getMemory();
MemoryBlock textBlock = memory.getBlock(".text");
AddressSetView initializedAddresses = memory.getLoadedAndInitializedAddressSet();
PseudoDisassembler pseudoDisassembler = new PseudoDisassembler(program);
// Create pointers starting at the address until reaching a 0 pointer.
// Terminate the possible table at any entry containing a cross reference that
// is beyond the first table entry and don't include it.
int tableSize = 0;
Address currentVfPointerAddress = vfTableBaseAddress;
int defaultPointerSize = program.getDefaultPointerSize();
while (true) {
Address referencedAddress = getAbsoluteAddress(program, currentVfPointerAddress);
if (referencedAddress == null) {
break; // Cannot get a virtual function address.
}
if (referencedAddress.getOffset() == 0) {
break; // Encountered 0 entry.
}
if (!initializedAddresses.contains(referencedAddress)) {
break; // Not pointing to initialized memory.
}
if ((textBlock != null) ? !textBlock.equals(memory.getBlock(referencedAddress))
: false) {
break; // Not pointing to text section.
}
if (!pseudoDisassembler.isValidSubroutine(referencedAddress, true)) {
break; // Not pointing to possible function.
}
tableSize++; // Count this entry in the table.
// Advance to the next table entry address.
currentVfPointerAddress = currentVfPointerAddress.add(defaultPointerSize);
}
return tableSize;
}
/**
* Gets the namespace referred to by the type descriptor model if it can determine the
* namespace. Otherwise it returns the empty string.
* @param rtti0Model the model for the type descriptor whose namespace is to be returned.
* @return the namespace or the empty string.
*/
static String getDescriptorTypeNamespace(TypeDescriptorModel rtti0Model) {
String descriptorTypeNamespace = rtti0Model.getDescriptorTypeNamespace(); // Can be null.
if (descriptorTypeNamespace == null) {
descriptorTypeNamespace = ""; // Couldn't get namespace so leave it off.
}
return descriptorTypeNamespace;
}
}
@@ -0,0 +1,200 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data.rtti;
import static ghidra.app.util.datatype.microsoft.MSDataTypeUtils.getAbsoluteAddress;
import ghidra.app.cmd.data.AbstractCreateDataTypeModel;
import ghidra.app.cmd.data.TypeDescriptorModel;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.datatype.microsoft.MSDataTypeUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOutOfBoundsException;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.Program;
/**
* Model for vf table information associated with a CompleteObjectLocator (RTTI 4) data type.
* <p>
* VF Table
* <p>
* Info for the association of this data can be found on http://www.openrce.org
* <p>
*/
public class VfTableModel extends AbstractCreateDataTypeModel {
public static final String DATA_TYPE_NAME = "vftable";
private DataType dataType;
private Rtti4Model rtti4Model;
private int elementCount = -1;
private Program lastProgram;
private DataType lastDataType;
private int lastElementCount = -1;
/**
* Creates the model for the vf table data.
* @param program the program
* @param vfTableAddress the address in the program for the vf table data.
* @param validationOptions options indicating how to validate the data type at the indicated
* address.
*/
public VfTableModel(Program program, Address vfTableAddress,
DataValidationOptions validationOptions) {
super(program, RttiUtil.getVfTableCount(program, vfTableAddress), vfTableAddress,
validationOptions);
}
@Override
public String getName() {
return DATA_TYPE_NAME;
}
@Override
public void validateModelSpecificInfo() throws InvalidDataTypeException {
Program program = getProgram();
Address startAddress = getAddress();
// Get the model from the meta pointer.
Address metaAddress = getMetaAddress();
Address rtti4Address = getAbsoluteAddress(program, metaAddress);
rtti4Model = new Rtti4Model(program, rtti4Address, validationOptions);
// Get the table
DataType individualEntryDataType = new PointerDataType(program.getDataTypeManager());
long entrySize = individualEntryDataType.getLength();
// Each entry is a pointer to where a function can possibly be created.
long numEntries = RttiUtil.getVfTableCount(program, startAddress);
if (numEntries == 0) {
throw new InvalidDataTypeException(
getName() + " data type at " + getAddress() + " doesn't have a valid vf table.");
}
Address vfTableFieldAddress = startAddress;
for (int ordinal = 0; ordinal < numEntries && vfTableFieldAddress != null; ordinal++) {
// Each component is a pointer (to a function).
Address functionAddress = getAbsoluteAddress(program, vfTableFieldAddress);
if (functionAddress == null) {
throw new InvalidDataTypeException(
getName() + " at " + getAddress() + " doesn't refer to a valid function.");
}
try {
vfTableFieldAddress = vfTableFieldAddress.add(entrySize); // Add the data type size.
}
catch (AddressOutOfBoundsException e) {
if (ordinal < (numEntries - 1)) {
throw new InvalidDataTypeException(
getName() + " at " + getAddress() + " isn't valid.");
}
break;
}
}
}
/**
* This gets the vf table structure for the indicated program.
* @param program the program which will contain this data.
* @return the vf table structure as an array.
*/
private DataType getDataType(Program program) {
if (program != lastProgram) {
setIsDataTypeAlreadyBasedOnCount(true);
lastProgram = program;
lastDataType = null;
lastElementCount = -1;
lastElementCount = RttiUtil.getVfTableCount(program, getAddress());
if (lastElementCount > 0) {
DataTypeManager dataTypeManager = program.getDataTypeManager();
PointerDataType pointerDt = new PointerDataType(dataTypeManager);
// Create an array of pointers and return it.
ArrayDataType arrayDataType = new ArrayDataType(pointerDt, lastElementCount,
pointerDt.getLength(), dataTypeManager);
lastDataType = MSDataTypeUtils.getMatchingDataType(program, arrayDataType);
}
else {
lastDataType = null;
}
}
return lastDataType;
}
@Override
public DataType getDataType() {
if (dataType == null) {
dataType = getDataType(getProgram());
}
return dataType;
}
@Override
protected int getDataTypeLength() {
DataType dt = getDataType();
return (dt != null) ? dt.getLength() : 0;
}
/**
* Gets the address of the virtual function pointed to by the vf table element at the index
* specified by <code>tableElementIndex</code>.
* @param tableElementIndex index of the vf table element
* @return the virtual function's address or null
*/
public Address getVirtualFunctionPointer(int tableElementIndex) {
Address tableAddress = getAddress();
int defaultPointerSize = getDefaultPointerSize();
Address address = tableAddress.add(defaultPointerSize * tableElementIndex);
return getAbsoluteAddress(getProgram(), address);
}
/**
* Gets the number of elements in the vf table. Returns 0 if this model isn't for a valid vf table.
* @return the number of vf table elements or 0.
*/
public int getElementCount() {
if (elementCount == -1) {
elementCount = RttiUtil.getVfTableCount(getProgram(), getAddress());
}
return elementCount;
}
/**
* Gets the type descriptor (RTTI 0) model associated with this vf table.
* @return the type descriptor (RTTI 0) model or null.
* @throws InvalidDataTypeException if this model's validation fails.
*/
public TypeDescriptorModel getRtti0Model() throws InvalidDataTypeException {
checkValidity();
return rtti4Model.getRtti0Model();
}
/**
* Gets the address of the location containing the meta pointer, which points to the RTTI 4
* associated with this vf table.
* @return the address of the meta pointer
*/
private Address getMetaAddress() {
return getAddress().subtract(getProgram().getDefaultPointerSize());
}
}
@@ -0,0 +1,160 @@
/* ###
* 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.
*/
package ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin;
import java.util.List;
import ghidra.app.cmd.data.exceptionhandling.CreateEHFuncInfoBackgroundCmd;
import ghidra.app.cmd.data.exceptionhandling.EHFunctionInfoModel;
import ghidra.app.plugin.core.searchmem.RegExSearchData;
import ghidra.app.services.*;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.PeLoader;
import ghidra.app.util.opinion.PeLoader.CompilerOpinion.CompilerEnum;
import ghidra.framework.cmd.Command;
import ghidra.program.model.address.*;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.lang.CompilerSpecID;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.util.ProgramMemoryUtil;
import ghidra.util.datastruct.ListAccumulator;
import ghidra.util.exception.CancelledException;
import ghidra.util.search.memory.*;
import ghidra.util.task.TaskMonitor;
/**
* Analyzer that finds Windows PE Visual Studio exception handling data structures and creates data using the
* exception handling data types. Data flows can be followed to find other data and functions. If
* the data appears valid, labels, references, and functions can be created based on the data.
*/
public class PEExceptionAnalyzer extends AbstractAnalyzer {
private static final String NAME = "Windows x86 PE Exception Handling";
private static final String DESCRIPTION =
"Marks up exception handling data structures within a Visual Studio windows PE program.";
// MATCH_LIMIT is the maximum number of matches allowed when searching for FuncInfo magic numbers.
private static final int MATCH_LIMIT = Integer.MAX_VALUE; // May need to change this limit later.
// MAX_MAP_ENTRY_COUNT is the maximum expected value for a count of map entries.
private static final int MAX_MAP_ENTRY_COUNT = 16000;
/**
* Creates an analyzer for determining PE exception handling data.
*/
public PEExceptionAnalyzer() {
super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
setPriority(AnalysisPriority.FORMAT_ANALYSIS.after().after());
setDefaultEnablement(true);
}
@Override
public boolean canAnalyze(Program program) {
CompilerSpecID compilerSpecID = program.getCompilerSpec().getCompilerSpecID();
return compilerSpecID.getIdAsString().equals("windows") &&
program.getExecutableFormat().equals(PeLoader.PE_NAME) &&
program.getCompiler().equals(CompilerEnum.VisualStudio.toString());
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
// Get the memory blocks to search for exception handling structures.
List<MemoryBlock> ehBlocks =
ProgramMemoryUtil.getMemoryBlocksStartingWithName(program, set, ".rdata", monitor);
if (ehBlocks.isEmpty()) {
ehBlocks =
ProgramMemoryUtil.getMemoryBlocksStartingWithName(program, set, ".text", monitor);
}
// search for FuncInfo data type's magic number pattern.
String lePattern =
"[\\x20,\\x21,\\x22]\\x05\\x93[\\x19,\\x39,\\x59,\\x79,\\x99,\\xb9,\\xd9,\\xf9]";
String bePattern =
"[\\x19,\\x39,\\x59,\\x79,\\x99,\\xb9,\\xd9,\\xf9]\\x93\\x05[\\x20,\\x21,\\x22]";
RegExSearchData regExSearchData = RegExSearchData
.createRegExSearchData(program.getLanguage().isBigEndian() ? bePattern : lePattern);
int alignment = 4;
SearchInfo searchInfo = new SearchInfo(regExSearchData, MATCH_LIMIT, false, true, alignment,
false, new CodeUnitSearchInfo(false, true, true), null);
// Only want to search loaded and initialized addresses.
AddressSet intersection =
program.getMemory().getLoadedAndInitializedAddressSet().intersect(set);
// Only want to search exception handling memory blocks.
intersection = getAddressSet(ehBlocks).intersect(intersection);
RegExMemSearcherAlgorithm searcher =
new RegExMemSearcherAlgorithm(searchInfo, intersection, program, true);
ListAccumulator<MemSearchResult> accumulator = new ListAccumulator<>();
searcher.search(accumulator, monitor);
List<MemSearchResult> results = accumulator.asList();
// Establish the options to use when creating the exception handling data.
// For now these are fixed. Later these may need to come from analysis options.
DataValidationOptions validationOptions = new DataValidationOptions();
DataApplyOptions applyOptions = new DataApplyOptions();
monitor.setMaximum(results.size());
// Attempt to create data at each address if it appears to be valid for the data type.
int count = 0;
for (MemSearchResult result : results) {
monitor.setProgress(count++);
if (monitor.isCancelled()) {
return false;
}
Address address = result.getAddress();
if (address.getOffset() % alignment != 0) {
continue; // Skip non-aligned addresses.
}
// Validate the possible FuncInfo before trying to create it.
EHFunctionInfoModel model =
new EHFunctionInfoModel(program, address, validationOptions);
try {
model.validate();
model.validateCounts(MAX_MAP_ENTRY_COUNT);
model.validateLocationsInSameBlock();
// Create FuncInfo data at the address of the magic number, if the data appears valid.
// This can also create associated exception handling data based on the options.
Command cmd =
new CreateEHFuncInfoBackgroundCmd(address, validationOptions, applyOptions);
cmd.applyTo(program);
}
catch (InvalidDataTypeException e) {
// Doesn't appear valid so just move on to the next one.
// This doesn't log an error because we are trying to find valid exception
// handling structures and apply them.
}
}
return true;
}
private AddressSet getAddressSet(List<MemoryBlock> blocks) {
AddressSet addressSet = new AddressSet();
for (MemoryBlock memoryBlock : blocks) {
addressSet.add(memoryBlock.getStart(), memoryBlock.getEnd());
}
return addressSet;
}
}
@@ -0,0 +1,134 @@
/* ###
* 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.
*/
package ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin;
import java.io.IOException;
import generic.continues.RethrowContinuesFactory;
import ghidra.app.util.bin.MemoryByteProvider;
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
import ghidra.app.util.bin.format.mz.DOSHeader;
import ghidra.app.util.bin.format.pe.Constants;
import ghidra.app.util.datatype.microsoft.GuidInfo;
import ghidra.app.util.datatype.microsoft.GuidUtil;
import ghidra.app.util.opinion.BinaryLoader;
import ghidra.app.util.opinion.PeLoader;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.TypeDef;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.reloc.Relocation;
public class PEUtil {
static public boolean canAnalyze(Program program) {
String format = program.getExecutableFormat();
if (format.equals(PeLoader.PE_NAME)) {
return true;
}
if (format.equals(BinaryLoader.BINARY_NAME)) {
MemoryByteProvider mbp =
new MemoryByteProvider(program.getMemory(),
program.getAddressFactory().getDefaultAddressSpace());
try {
FactoryBundledWithBinaryReader reader =
new FactoryBundledWithBinaryReader(RethrowContinuesFactory.INSTANCE, mbp, true/*LittleEndian*/);
DOSHeader dosHeader = DOSHeader.createDOSHeader(reader);
if (dosHeader.e_magic() == DOSHeader.IMAGE_DOS_SIGNATURE) {
int peHeaderStartIndex = dosHeader.e_lfanew();
int peMagicNumber = reader.readInt(peHeaderStartIndex);
if (peMagicNumber == Constants.IMAGE_NT_SIGNATURE) {
return true;
}
}
}
catch (IOException e) {
}
}
return false;
}
static DataType getActualType(DataType dataType) {
if (dataType instanceof TypeDef) {
return getActualType(((TypeDef) dataType).getDataType());
}
return dataType;
}
static boolean isValidPointer(Program program, Address addr) {
Memory memory = program.getMemory();
AddressFactory addressFactory = program.getAddressFactory();
AddressSpace defaultSpace = addressFactory.getDefaultAddressSpace();
try {
int addrAsInt = memory.getInt(addr);
Address pointedToAddr =
addressFactory.getAddress(defaultSpace.getBaseSpaceID(), addrAsInt);
return memory.contains(pointedToAddr);
}
catch (MemoryAccessException e) {
}
return false;
}
static boolean isValidGuidPointer(Program program, Address addr) {
Memory memory = program.getMemory();
AddressFactory addressFactory = program.getAddressFactory();
AddressSpace defaultSpace = addressFactory.getDefaultAddressSpace();
try {
int addrAsInt = memory.getInt(addr);
Address pointedToAddr =
addressFactory.getAddress(defaultSpace.getBaseSpaceID(), addrAsInt);
if (memory.contains(pointedToAddr)) {
GuidInfo guidInfo = GuidUtil.getKnownGuid(program, pointedToAddr);
if (guidInfo != null) {
return true;
}
}
}
catch (MemoryAccessException e) {
}
return false;
}
static long getBytesToEndOfBlock(Program program, Address addr) {
Memory memory = program.getMemory();
Address endAddr = memory.getBlock(addr).getEnd();
return endAddr.subtract(addr);
}
static long getBytesToNextReferredToAddress(Program program, Address addr) {
AddressIterator refIter =
program.getReferenceManager().getReferenceDestinationIterator(addr.add(1L), true);
if (refIter.hasNext()) {
Address nextAddr = refIter.next();
if (nextAddr != null) {
return nextAddr.subtract(addr);
}
}
return 0;
}
static long getBytesToNextRelocation(Program program, Address addr) {
Relocation nextReloc = program.getRelocationTable().getRelocationAfter(addr);
if (nextReloc != null) {
return nextReloc.getAddress().subtract(addr);
}
return 0;
}
}
@@ -0,0 +1,432 @@
/* ###
* 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.
*/
package ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.app.cmd.data.CreateDataCmd;
import ghidra.app.services.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.data.Undefined;
import ghidra.program.model.lang.OperandType;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
public class PropagateExternalParametersAnalyzer extends AbstractAnalyzer {
private static final String NAME = "WindowsPE x86 Propagate External Parameters";
private static final String DESCRIPTION =
"This analyzer uses external Windows function call parameter information to populate " +
"comments next to pushed parameters. In some cases, data is labeled and commented as well";
private List<PushedParamInfo> results = new ArrayList<>();
private Program currentProgram;
public PropagateExternalParametersAnalyzer() {
super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
setSupportsOneTimeAnalysis();
setPriority(AnalysisPriority.DATA_TYPE_PROPOGATION.after().after().after().after().after());
// setPrototype();
}
private void processExternalFunction(Listing listing, ReferenceManager refMan,
Reference[] extRefs, Function externalFunction, Parameter[] params) {
String externalFunctionName = externalFunction.getName();
for (Reference extRef : extRefs) {
Address fromAddr = extRef.getFromAddress();
Function callingFunction = listing.getFunctionContaining(fromAddr);
if (callingFunction == null) {
continue;
}
String mnemonic = listing.getCodeUnitAt(fromAddr).getMnemonicString();
if ((mnemonic.equals("JMP") && (callingFunction.isThunk()))) {
processThunkReference(listing, refMan, externalFunction, params, callingFunction);
}
else if ((mnemonic.equals("CALL"))) {// not a thunk
CodeUnitIterator it = getCodeUnitsFromFunctionStartToRef(callingFunction, fromAddr);
if (hasEnoughPushes(it, params.length)) {
CodeUnitIterator codeUnitsToRef =
getCodeUnitsFromFunctionStartToRef(callingFunction, fromAddr);
propogateParams(params, codeUnitsToRef, externalFunctionName);
}
}
}
}
private void processThunkReference(Listing listing, ReferenceManager refMan,
Function externalFunction, Parameter[] params, Function callingFunction) {
ReferenceIterator iterator = refMan.getReferencesTo(callingFunction.getEntryPoint());
for (Reference thunkRef : iterator) {
Address thunkAddr = thunkRef.getFromAddress();
Function thunk = listing.getFunctionContaining(thunkAddr);
if (thunk == null) {
continue;
}
String thunkMnemonic = listing.getCodeUnitAt(thunkAddr).getMnemonicString();
if (!thunkMnemonic.equals("CALL")) {
continue;
}
CodeUnitIterator cuIt = getCodeUnitsFromFunctionStartToRef(thunk, thunkAddr);
if (hasEnoughPushes(cuIt, params.length)) {
CodeUnitIterator codeUnitsToRef =
getCodeUnitsFromFunctionStartToRef(thunk, thunkAddr);
propogateParams(params, codeUnitsToRef, externalFunction.getName());
}
}
}
// * Function to skip the parameters of a call that is in the middle of the parameters I am
// * trying to populate. For example:
// * PUSH arg 4 to call func1 ; put arg 4 of func1 here
// * PUSH arg 3 to call func1 ; put arg 3 of func1 here
// * PUSH arg 3 to call func2 ---|
// * PUSH arg 2 to call func2 |
// * PUSH arg 1 to call func2 | -- want to bypass these
// * CALL func2 ___|
// * PUSH arg 2 to call func1 ; put arg2 of func1 here
// * PUSH arg 1 to call func1 ; put arg1 of func1 here
// * CALL func1
// get the number of pushes for a code unit if it is a call
private int numParams(CodeUnit cu) {
Reference[] references = cu.getReferencesFrom();
if (references.length != 0) {
Address toAddr = references[0].getToAddress();
FunctionManager functionManager = currentProgram.getFunctionManager();
Function f = functionManager.getReferencedFunction(toAddr);
if (f != null) {
Parameter[] params = f.getParameters();
return params.length;
}
}
return 0;
}
private CodeUnitIterator getCodeUnitsFromFunctionStartToRef(Function function,
Address referenceAddress) {
if (function == null) {
return null;
}
Listing listing = currentProgram.getListing();
AddressSetView functionBody = function.getBody();
CodeUnit referenceCodeUnit = listing.getCodeUnitAt(referenceAddress);
Address referenceMinAddress = referenceCodeUnit.getMinAddress();
CodeUnit previousCodeUnit = listing.getCodeUnitBefore(referenceMinAddress);
Address previousMinAddress = previousCodeUnit.getMinAddress();
AddressIterator it = functionBody.getAddresses(previousMinAddress, false);
AddressSet addrSet = new AddressSet();
while (it.hasNext()) {
Address addr = it.next();
addrSet.addRange(addr, addr);
}
return listing.getCodeUnits(addrSet, false);
}
/**
* This will return true if enough pushes before top of function.
* This will return false if not enough pushes or if not a function.
*/
private boolean hasEnoughPushes(CodeUnitIterator iterator, int numParams) {
if (iterator == null) {
return false;
}
int numPushes = 0;
int numSkips = 0;
while ((iterator.hasNext()) && (numPushes < numParams)) {
CodeUnit cu = iterator.next();
if (numSkips > 0) {
numSkips--;
}
else if (cu.getMnemonicString().equals("CALL")) {
numParams += numParams(cu);
}
else if (cu.getMnemonicString().equals("PUSH")) {
numPushes++;
}
}
// Have enough params between ref and top of function?
return numPushes >= numParams;
}
private void propogateParams(Parameter[] params, CodeUnitIterator iterator,
String externalFunctionName) {
int index = 0;
int numSkips = 0;
while (iterator.hasNext() && index < params.length) {
// Need to take into account calls between the pushes and skip the
// pushes for those calls skip pushes that are used for another call.
// If label, then probably a branch, allow current push to be commented and
// next time through stop. Can also be a branch if not label there but
// this case should still have parameters set
// before it as long as not an unconditional jump - this wouldn't make
// sense so it shouldn't happen
CodeUnit cu = iterator.next();
boolean isBranch = cu.getLabel() != null;
if (cu.getMnemonicString().equals("CALL")) {
numSkips += numParams(cu);
}
else if (cu.getMnemonicString().equals("PUSH")) {
if (numSkips > 0) {
numSkips--;
}
else {
Parameter param = params[index];
DataType dt = param.getDataType();
String name = param.getName();
SetCommentCmd cmd = new SetCommentCmd(cu.getAddress(), CodeUnit.EOL_COMMENT,
dt.getDisplayName() + " " + name + " for " + externalFunctionName);
cmd.applyTo(currentProgram);
// add the following to the EOL comment to see the value of the optype
addResult(name, dt, cu.getMinAddress(), externalFunctionName);
index++;
}
}
if (isBranch) {
break;
}
}
}
// for now all calledFuncNames are extFuncNames -might change if I add others later
private void addResult(String name, DataType dataType, Address addr, String calledFuncName) {
PushedParamInfo param = new PushedParamInfo(name, dataType, addr, calledFuncName);
results.add(param);
}
@Override
public boolean canAnalyze(Program program) {
return PEUtil.canAnalyze(program);
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
this.currentProgram = program;
Listing listing = program.getListing();
FunctionManager functionManager = program.getFunctionManager();
ReferenceManager referenceManager = program.getReferenceManager();
// iterate over all external symbols
SymbolTable symbolTable = program.getSymbolTable();
SymbolIterator externalSymbols = symbolTable.getExternalSymbols();
for (Symbol externalSymbol : externalSymbols) {
if (externalSymbol.getSymbolType() != SymbolType.FUNCTION) {
continue;
}
Function externalFunction = functionManager.getFunctionAt(externalSymbol.getAddress());
Parameter[] params = externalFunction.getParameters();
if (params.length == 0) {
continue;
}
Reference[] references = externalSymbol.getReferences();
processExternalFunction(listing, referenceManager, references, externalFunction,
params);
}
// use the 'results' to propagate param info to the local variables, data, and params of
// the calling function
Msg.trace(this, "Processing propagation results - count: " + results.size());
for (int i = 0; i < results.size(); i++) {
PushedParamInfo paramInfo = results.get(i);
Address paramAddress = paramInfo.getAddress();
Instruction instruction = listing.getInstructionAt(paramAddress);
// wait on applying data types - the microsoft analyzer does some of this
// see how much/well it does first
if (!instruction.getOperandRefType(0).isData()) {
continue;
}
int opType = instruction.getOperandType(0);
if (!isAddressReferenceOperand(opType)) {
continue;
}
Address referencedAddress = getReferencedAddress(paramAddress);
if (referencedAddress == null) {
continue;
}
String paramName = paramInfo.getName();
String symbolName = paramName + "_" + referencedAddress.toString();
addSymbol(symbolTable, referencedAddress, symbolName);
String paramText = paramName + " parameter of " + paramInfo.getCalledFunctionName();
String newComment = paramText + "\n";
Msg.trace(this, "External Function Call at " + paramAddress + " : " + paramText +
" at " + referencedAddress.toString());
createComment(referencedAddress, newComment, paramInfo);
clearUndefinedDataType(referencedAddress, monitor);
createData(paramInfo, referencedAddress);
}
return true;
}
private void createComment(Address dataAddress, String newComment, PushedParamInfo info) {
Listing listing = currentProgram.getListing();
String plateComment = listing.getComment(CodeUnit.PLATE_COMMENT, dataAddress);
if (plateComment == null) {
// add a comment
SetCommentCmd cmd = new SetCommentCmd(dataAddress, CodeUnit.PLATE_COMMENT, newComment);
cmd.applyTo(currentProgram);
}
else if (!plateComment.contains(info.getCalledFunctionName())) {
// update the existing comment
String updatedComment = plateComment + "\n" + newComment;
SetCommentCmd cmd =
new SetCommentCmd(dataAddress, CodeUnit.PLATE_COMMENT, updatedComment);
cmd.applyTo(currentProgram);
}
}
private void createData(PushedParamInfo paramInfo, Address address) {
Listing listing = currentProgram.getListing();
DataType dt = paramInfo.getDataType();
if (!listing.isUndefined(address, address.add(dt.getLength() - 1))) {
return; // don't overwrite existing data
}
CreateDataCmd cmd = new CreateDataCmd(address, dt);
if (!cmd.applyTo(currentProgram)) {
Msg.error(this, "Error making data: " + cmd.getStatusMsg());
}
}
private void clearUndefinedDataType(Address address, TaskMonitor monitor)
throws CancelledException {
Listing listing = currentProgram.getListing();
Data data = listing.getDefinedDataAt(address);
if (data == null) {
return;
}
DataType dt = data.getDataType();
if (Undefined.isUndefined(dt)) {
listing.clearCodeUnits(address, address, false, monitor);
}
}
private void addSymbol(SymbolTable symbolTable, Address address, String symbolName) {
Listing listing = currentProgram.getListing();
Data data = listing.getDefinedDataAt(address);
if (data != null && data.hasStringValue()) {
return; // don't add symbol for string (not sure why)
}
try {
Symbol newSymbol =
symbolTable.createLabel(address, symbolName, SourceType.USER_DEFINED);
newSymbol.setPrimary();
}
catch (InvalidInputException e) {
// shouldn't happen
Msg.trace(this, "Unexpected exception", e);
}
}
private Address getReferencedAddress(Address address) {
Listing listing = currentProgram.getListing();
Reference[] refs = listing.getCodeUnitAt(address).getOperandReferences(0);
if ((refs.length > 0) && (refs[0].isMemoryReference())) {
return refs[0].getToAddress();
}
return null;
}
private boolean isAddressReferenceOperand(int opType) {
if ((opType & OperandType.ADDRESS) == 0) {
return false;
}
//@formatter:off
return ((opType & OperandType.DATA) != 0) ||
((opType & OperandType.SCALAR) != 0) ||
((opType & OperandType.DYNAMIC) != 0);
//@formatter:on
}
//==================================================================================================
// Inner Classes
//==================================================================================================
// info about the pushed parameter that gets applied to the calling functions params and locals and referenced data
private class PushedParamInfo {
private String name;
private DataType dataType;
private Address addr;
private String calledFunctionName;
PushedParamInfo(String name, DataType dataType, Address addr, String calledFunctionName) {
this.name = name;
this.dataType = dataType;
this.addr = addr;
this.calledFunctionName = calledFunctionName;
}
String getName() {
return name;
}
DataType getDataType() {
return dataType;
}
Address getAddress() {
return addr;
}
String getCalledFunctionName() {
return calledFunctionName;
}
}
}
@@ -0,0 +1,253 @@
/* ###
* 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.
*/
package ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin;
import java.util.*;
import ghidra.app.cmd.data.CreateTypeDescriptorBackgroundCmd;
import ghidra.app.cmd.data.TypeDescriptorModel;
import ghidra.app.cmd.data.rtti.CreateRtti4BackgroundCmd;
import ghidra.app.cmd.data.rtti.Rtti4Model;
import ghidra.app.services.*;
import ghidra.app.util.datatype.microsoft.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.model.address.*;
import ghidra.program.model.data.InvalidDataTypeException;
import ghidra.program.model.lang.UndefinedValueException;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.util.ProgramMemoryUtil;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
/**
* Finds Run-Time Type Information (RTTI) data structures within a Windows program. It creates data
* where the RTTI structures are found and annotates them using symbols and comments.
*/
public class RttiAnalyzer extends AbstractAnalyzer {
private static final String NAME = "Windows x86 PE RTTI Analyzer";
private static final String DESCRIPTION =
"This analyzer finds and creates all of the RTTI metadata structures and their associated vf tables.";
// TODO If we want the RTTI analyzer to find all type descriptors regardless of whether
// they are used for RTTI, then change the CLASS_PREFIX_CHARS to ".". Need to be
// careful that changing to this doesn't cause problems to RTTI analysis.
private static final String CLASS_PREFIX_CHARS = ".?A";
public static final String TYPE_INFO_STRING = ".?AVtype_info@@";
private DataValidationOptions validationOptions;
private DataApplyOptions applyOptions;
/**
* Constructs an RttiAnalyzer.
*/
public RttiAnalyzer() {
super(NAME, DESCRIPTION, AnalyzerType.BYTE_ANALYZER);
setSupportsOneTimeAnalysis();
// Set priority of RTTI analyzer to run after Demangler so can see if better
// plate comment or label already exists from Demangler.
setPriority(AnalysisPriority.DATA_TYPE_PROPOGATION.before().before());
setDefaultEnablement(true);
validationOptions = new DataValidationOptions();
applyOptions = new DataApplyOptions();
}
@Override
public boolean canAnalyze(Program program) {
return PEUtil.canAnalyze(program);
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
List<MemoryBlock> dataBlocks =
ProgramMemoryUtil.getMemoryBlocksStartingWithName(program, set, ".data", monitor);
List<Address> typeInfoAddresses =
ProgramMemoryUtil.findString(TYPE_INFO_STRING, program, dataBlocks, set, monitor);
int typeInfoCount = typeInfoAddresses.size();
if (typeInfoCount != 1) {
if (typeInfoCount == 0) {
log.appendMsg(this.getName(), "Couldn't find type info structure.");
return true;
}
log.appendMsg(this.getName(),
"Found " + typeInfoCount + " type info structures when expecting only 1.");
return false;
}
// Found exactly 1 type info string, so use it to find RTTI structures.
Address typeInfoStringAddress = typeInfoAddresses.get(0);
Address typeInfoRtti0Address =
TypeDescriptorModel.getBaseAddress(program, typeInfoStringAddress);
if (typeInfoRtti0Address == null) {
log.appendMsg(this.getName(), "Couldn't find RTTI type info structure.");
return true;
}
// Get the address of the vf table data in common for all RTTI 0.
TypeDescriptorModel typeDescriptorModel =
new TypeDescriptorModel(program, typeInfoRtti0Address, validationOptions);
try {
Address commonVfTableAddress = typeDescriptorModel.getVFTableAddress();
if (commonVfTableAddress == null) {
log.appendMsg(this.getName(),
"Couldn't get vf table address for RTTI 0 @ " + typeInfoRtti0Address + ". ");
return false;
}
int alignment = program.getDefaultPointerSize();
Set<Address> possibleTypeAddresses = ProgramMemoryUtil.findDirectReferences(program,
dataBlocks, alignment, commonVfTableAddress, monitor);
// We now have a list of potential rtti0 addresses.
processRtti0(possibleTypeAddresses, program, monitor);
return true;
}
catch (InvalidDataTypeException | UndefinedValueException e) {
log.appendMsg(this.getName(), "Couldn't get vf table address for RTTI 0 @ " +
typeInfoRtti0Address + ". " + e.getMessage());
return false;
}
}
private void processRtti0(Collection<Address> possibleRtti0Addresses, Program program,
TaskMonitor monitor) throws CancelledException {
monitor.setMaximum(possibleRtti0Addresses.size());
monitor.setMessage("Creating RTTI Data...");
int count = 0;
for (Address rtti0Address : possibleRtti0Addresses) {
monitor.checkCanceled();
monitor.setProgress(count++);
// Validate
TypeDescriptorModel typeModel =
new TypeDescriptorModel(program, rtti0Address, validationOptions);
try {
// Check that name matches the expected format.
String typeName = typeModel.getTypeName(); // can be null.
if (typeName == null || !typeName.startsWith(CLASS_PREFIX_CHARS)) {
continue; // Invalid so don't create.
}
}
catch (InvalidDataTypeException e) {
continue; // Invalid so don't create.
}
// Create the TypeDescriptor (RTTI 0) regardless of the other RTTI structures.
CreateTypeDescriptorBackgroundCmd typeDescCmd = new CreateTypeDescriptorBackgroundCmd(
rtti0Address, validationOptions, applyOptions);
typeDescCmd.applyTo(program, monitor);
// Create any valid RTTI4s for this TypeDescriptor
processRtti4sForRtti0(program, rtti0Address, monitor);
}
}
private void processRtti4sForRtti0(Program program, Address rtti0Address, TaskMonitor monitor)
throws CancelledException {
List<MemoryBlock> rDataBlocks = ProgramMemoryUtil.getMemoryBlocksStartingWithName(program,
program.getMemory(), ".rdata", monitor);
List<Address> rtti4Addresses =
getRtti4Addresses(program, rDataBlocks, rtti0Address, validationOptions, monitor);
for (Address rtti4Address : rtti4Addresses) {
monitor.checkCanceled();
CreateRtti4BackgroundCmd cmd =
new CreateRtti4BackgroundCmd(rtti4Address, rDataBlocks, validationOptions,
applyOptions);
cmd.applyTo(program, monitor);
}
}
/**
* Gets the base addresses of all the RTTI 4 structures that appear to be associated with
* the RTTI 0 at the indicated base address.
* @param program the program containing the RTTI 0 data structure.
* @param rtti4Blocks the memory blocks to be searched for RTTI4 structures.
* @param rtti0Address the base address of the RTTI 0 structure in the program
* @param validationOptions options indicating how validation is performed for data structures
* @param monitor the task monitor for cancelling a task
* @return the RTTI 4 base addresses associated with the RTTI 0
* @throws CancelledException if the user cancels this task.
*/
private static List<Address> getRtti4Addresses(Program program, List<MemoryBlock> rtti4Blocks,
Address rtti0Address, DataValidationOptions validationOptions, TaskMonitor monitor)
throws CancelledException {
monitor.checkCanceled();
List<Address> addresses = new ArrayList<>(); // the RTTI 4 addresses
int rtti0PointerOffset = Rtti4Model.getRtti0PointerComponentOffset();
Set<Address> refsToRtti0 = getRefsToRtti0(program, rtti4Blocks, rtti0Address);
// for each RTTI 0 now see if we can get RTTI4s that refer to it.
for (Address refAddress : refsToRtti0) {
monitor.checkCanceled();
Address possibleRtti4Address;
try {
possibleRtti4Address = refAddress.subtractNoWrap(rtti0PointerOffset);
}
catch (AddressOverflowException e) {
continue; // Couldn't get an Rtti4 address.
}
Rtti4Model rtti4Model =
new Rtti4Model(program, possibleRtti4Address, validationOptions);
try {
rtti4Model.validate();
}
catch (InvalidDataTypeException e) {
continue; // Only process valid RTTI 4 data.
}
// Check that the RTTI 0 is referred to both directly from the RTTI 4 and indirectly
// through the RTTI 3.
boolean refersToRtti0 = rtti4Model.refersToRtti0(rtti0Address);
if (!refersToRtti0) {
continue; // Only process valid RTTI 4 data.
}
addresses.add(possibleRtti4Address);
}
return addresses;
}
private static Set<Address> getRefsToRtti0(Program program, List<MemoryBlock> dataBlocks,
Address rtti0Address) throws CancelledException {
Set<Address> refsToRtti0;
if (MSDataTypeUtils.is64Bit(program)) {
refsToRtti0 = ProgramMemoryUtil.findImageBaseOffsets32(program, 4, rtti0Address,
TaskMonitor.DUMMY);
}
else {
refsToRtti0 = ProgramMemoryUtil.findDirectReferences(program, dataBlocks,
program.getDefaultPointerSize(), rtti0Address, TaskMonitor.DUMMY);
}
return refsToRtti0;
}
}
@@ -0,0 +1,165 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* 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.
*/
package ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.script.*;
import ghidra.app.services.*;
import ghidra.app.util.importer.MessageLog;
import ghidra.app.util.opinion.PeLoader;
import ghidra.framework.model.Project;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import java.io.PrintWriter;
public class WindowsResourceReferenceAnalyzer extends AbstractAnalyzer {
private static final String NAME = "WindowsResourceReference";
private static final String DESCRIPTION =
"Given certain Key windows API calls, tries to create references at the use of windows Resources.";
boolean scriptWasFound = false;
private final static String OPTION_NAME_CREATE_BOOKMARKS = "Create Analysis Bookmarks";
private static final String OPTION_DESCRIPTION_CREATE_BOOKMARKS =
"Select this check box if you want this analyzer to create analysis bookmarks when items of interest are created/identified by the analyzer.";
private final static boolean OPTION_DEFAULT_CREATE_BOOKMARKS_ENABLED = true;
private boolean createBookmarksEnabled = OPTION_DEFAULT_CREATE_BOOKMARKS_ENABLED;
public WindowsResourceReferenceAnalyzer() {
super(NAME, DESCRIPTION, AnalyzerType.INSTRUCTION_ANALYZER);
setSupportsOneTimeAnalysis();
setPriority(AnalysisPriority.DATA_TYPE_PROPOGATION);
setDefaultEnablement(true);
}
@Override
public boolean canAnalyze(Program program) {
String format = program.getExecutableFormat();
if (format.equals(PeLoader.PE_NAME)) {
return true;
}
return false;
}
@Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
runScript(program, set, "WindowsResourceReference.java", monitor);
return true;
}
public boolean runScript(Program program, AddressSetView set, String scriptName,
TaskMonitor monitor) {
AutoAnalysisManager analysisManager = AutoAnalysisManager.getAnalysisManager(program);
PluginTool tool = analysisManager.getAnalysisTool();
Project project = findProject(tool);
GhidraState state =
new GhidraState(tool, project, program, new ProgramLocation(program,
set.getMinAddress()), new ProgramSelection(set), null);
try {
ScriptInfo scriptInfo = GhidraScriptUtil.findScriptByName(scriptName);
if (scriptInfo == null) {
throw new IllegalAccessException("Couldn't find script");
}
GhidraScriptProvider provider =
GhidraScriptUtil.getProvider(scriptInfo.getSourceFile());
if (provider == null) {
throw new IllegalAccessException("Couldn't find script provider");
}
PrintWriter writer = getOutputMsgStream(tool);
GhidraScript script = provider.getScriptInstance(scriptInfo.getSourceFile(), writer);
script.set(state, monitor, writer);
// This code was added so the analyzer won't print script messages to console
// This also adds the ability to pass the option to add or not add bookmarks to the script
String[] scriptArguments = { "false", String.valueOf(createBookmarksEnabled) };
script.runScript(scriptName, scriptArguments);
return true;
}
catch (IllegalAccessException e) {
Msg.warn(this, "Unable to access script: " + scriptName, e);
}
catch (InstantiationException e) {
Msg.warn(this, "Unable to instantiate script: " + scriptName, e);
}
catch (ClassNotFoundException e) {
Msg.warn(this, "Unable to locate script class: " + e.getMessage(), e);
}
catch (CancelledException e) {
Msg.warn(this, "User cancelled script.", e);
}
catch (Exception e) {
Msg.warn("Error running script: " + scriptName + "\n" + e.getMessage(), e);
e.printStackTrace();
}
return false;
}
private Project findProject(PluginTool tool) {
if (tool != null) {
return tool.getProject();
}
return null;
}
private PrintWriter getOutputMsgStream(PluginTool tool) {
if (tool != null) {
ConsoleService console = tool.getService(ConsoleService.class);
if (console != null) {
return console.getStdOut();
}
}
return new PrintWriter(System.out);
}
@Override
public boolean removed(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log)
throws CancelledException {
return false;
}
@Override
public void registerOptions(Options options, Program program) {
options.registerOption(OPTION_NAME_CREATE_BOOKMARKS, createBookmarksEnabled, null,
OPTION_DESCRIPTION_CREATE_BOOKMARKS);
}
@Override
public void optionsChanged(Options options, Program program) {
createBookmarksEnabled =
options.getBoolean(OPTION_NAME_CREATE_BOOKMARKS, createBookmarksEnabled);
}
}
@@ -0,0 +1,479 @@
/* ###
* 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.
*/
package ghidra.app.cmd.data;
import static org.junit.Assert.*;
import org.junit.Assert;
import generic.test.AbstractGenericTest;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.plugin.prototype.MicrosoftCodeAnalyzerPlugin.RttiAnalyzer;
import ghidra.app.services.DataTypeManagerService;
import ghidra.app.util.datatype.microsoft.DataApplyOptions;
import ghidra.app.util.datatype.microsoft.DataValidationOptions;
import ghidra.app.util.opinion.PeLoader;
import ghidra.app.util.opinion.PeLoader.CompilerOpinion.CompilerEnum;
import ghidra.framework.store.LockException;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.address.*;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.DumbMemBufferImpl;
/**
* Abstract class that is extended by the CreateDataType tests.
* It provides ProgramBuilder and Options setup for the tests.
*/
public class AbstractCreateDataTypeModelTest extends AbstractGenericTest {
protected static DataValidationOptions defaultValidationOptions = new DataValidationOptions();
protected static DataApplyOptions defaultApplyOptions = new DataApplyOptions();
protected DataValidationOptions noFollowValidationOptions = new DataValidationOptions();
protected DataApplyOptions noFollowApplyOptions = new DataApplyOptions();
private static DataTypeManagerService service;
protected AbstractCreateDataTypeModelTest() {
super();
noFollowValidationOptions.setValidateReferredToData(false);
noFollowApplyOptions.setFollowData(false);
}
/**
* Setup DTM service such that the same instance if used across all test methods.
* This assumes that a tool is not used and that the DefaultDataTypeManagerService
* is used.
*/
protected void setupDTMService(Program program) {
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
if (service != null) {
setInstanceField("service", analysisMgr, service);
}
else {
service = analysisMgr.getDataTypeManagerService();
assertTrue("DefaultDataTypeManagerService".equals(service.getClass().getSimpleName()));
Runtime.getRuntime().addShutdownHook(new ShutdownServiceHook());
}
}
protected void preserveDTMService(Program program) {
if (program != null) {
// do not dispose analysis manager since we want to keep DTM service alive
AutoAnalysisManager analysisMgr = AutoAnalysisManager.getAnalysisManager(program);
setInstanceField("service", analysisMgr, null);
}
}
private class ShutdownServiceHook extends Thread {
@Override
public void run() {
if (service != null) {
invokeInstanceMethod("dispose", service);
}
}
}
private void setExecFormatAndCompiler(ProgramBuilder builder) {
setExecFormatAndCompiler(builder, PeLoader.PE_NAME, CompilerEnum.VisualStudio.toString());
}
private void setExecFormatAndCompiler(ProgramBuilder builder, String execFormat,
String compiler) {
ProgramDB program = builder.getProgram();
int txID = program.startTransaction("Setting format and compiler.");
boolean commit = false;
try {
if (execFormat != null) {
program.setExecutableFormat(execFormat);
}
if (compiler != null) {
program.setCompiler(compiler);
}
commit = true;
}
finally {
program.endTransaction(txID, commit);
}
}
private void setImageBase(ProgramBuilder builder, long imageBase)
throws AddressOverflowException, LockException, IllegalStateException {
ProgramDB program = builder.getProgram();
int txID = program.startTransaction("Setting image base.");
boolean commit = false;
try {
program.setImageBase(builder.addr(imageBase), true);
commit = true;
}
finally {
program.endTransaction(txID, commit);
}
}
/**
* Creates a 32 bit program builder that can be used for testing.
* @return the program builder for a 32 bit VisualStudio x86 PE program.
* @throws Exception if it fails to create the ProgramBuilder
*/
protected ProgramBuilder build32BitX86() throws Exception {
ProgramBuilder builder =
new ProgramBuilder("test32BitX86", ProgramBuilder._X86, "windows", null);
setExecFormatAndCompiler(builder);
setImageBase(builder, 0x01000000L);
builder.createMemory(".text", "0x01001000", 0x2000);
builder.createMemory(".rdata", "0x01003000", 0x2000);
builder.createMemory(".data", "0x01005000", 0x2000);
setupDTMService(builder.getProgram());
builder.setBytes("0x01005008", RttiAnalyzer.TYPE_INFO_STRING.getBytes());
return builder;
}
/**
* Creates a 64 bit program builder that can be used for testing.
* @return the program builder for a 64 bit VisualStudio x86 PE program.
* @throws Exception if it fails to create the ProgramBuilder
*/
protected ProgramBuilder build64BitX86() throws Exception {
ProgramBuilder builder =
new ProgramBuilder("test64BitX86", ProgramBuilder._X64, "windows", null);
setExecFormatAndCompiler(builder);
setImageBase(builder, 0x101000000L);
builder.createMemory(".text", "0x101001000", 0x2000);
builder.createMemory(".rdata", "0x101003000", 0x2000);
builder.createMemory(".data", "0x101005000", 0x2000);
setupDTMService(builder.getProgram());
builder.setBytes("0x101005010", RttiAnalyzer.TYPE_INFO_STRING.getBytes());
return builder;
}
/**
* Creates a 64 bit program builder that can be used for testing.
* @return the program builder for a 64 bit non-VisualStudio x86 PE program.
* @throws Exception if it fails to create the ProgramBuilder
*/
protected ProgramBuilder build64BitX86NonVS() throws Exception {
ProgramBuilder builder =
new ProgramBuilder("test64BitX86", ProgramBuilder._X64, "windows", null);
setExecFormatAndCompiler(builder, PeLoader.PE_NAME, null);
setImageBase(builder, 0x101000000L);
builder.createMemory(".text", "0x101001000", 0x2000);
builder.createMemory(".rdata", "0x101003000", 0x2000);
builder.createMemory(".data", "0x101005000", 0x2000);
builder.setBytes("0x101005010", RttiAnalyzer.TYPE_INFO_STRING.getBytes());
return builder;
}
protected void setupCode32Bytes(ProgramBuilder builder, String address) throws Exception {
String byteString = "6a 01" // push
+ " 83 ec 20" // sub
+ " 8b e5" // mov
+ " 5d" // pop
+ " c3"; // ret
builder.setBytes(address, byteString, false);
}
protected void setupCode32Instructions(ProgramBuilder builder, String address)
throws Exception {
String byteString = "6a 01" // push
+ " 83 ec 20" // sub
+ " 8b e5" // mov
+ " 5d" // pop
+ " c3"; // ret
builder.setBytes(address, byteString, true);
}
@SuppressWarnings("unused")
protected void setupCode32Data(ProgramBuilder builder, String address) throws Exception {
setupCode32Bytes(builder, address);
builder.applyDataType(address, new WordDataType());
}
protected void setupCode64Bytes(ProgramBuilder builder, String address) throws Exception {
String byteString = "40 55" // push
+ " 48 83 ec 20" // sub
+ " 48 8b ea" // mov
+ " 5d" // pop
+ " c3"; // ret
builder.setBytes(address, byteString, false);
}
protected void setupCode64Instructions(ProgramBuilder builder, String address)
throws Exception {
String byteString = "40 55" // push
+ " 48 83 ec 20" // sub
+ " 48 8b ea" // mov
+ " 5d" // pop
+ " c3"; // ret
builder.setBytes(address, byteString, true);
}
@SuppressWarnings("unused")
protected void setupCode64Data(ProgramBuilder builder, String address) throws Exception {
setupCode64Bytes(builder, address);
builder.applyDataType(address, new WordDataType());
}
protected String getHexAddressAsIbo32ByteString(ProgramBuilder builder, String hexAddress,
boolean bigEndian) {
Program program = builder.getProgram();
Address imageBase = program.getImageBase();
Address address = builder.addr(hexAddress);
long offset = address.subtract(imageBase);
return getIntAsByteString((int) offset, bigEndian);
}
protected String getIntAsByteString(int value, boolean bigEndian) {
String hexString = Integer.toHexString(value);
int length = hexString.length();
if (length > 8) {
throw new IllegalArgumentException("Value exceeds 8 hex digits.");
}
int leadingZeros = 8 - length;
StringBuffer buf = new StringBuffer();
for (int i = 0; i < leadingZeros; i++) {
buf.append('0');
}
buf.append(hexString);
String hexDigits = buf.toString();
return getByteString(bigEndian, hexDigits);
}
protected String getHexAddress32AsByteString(String hexAddress, boolean bigEndian) {
return getHexAddressAsByteString(hexAddress, bigEndian, 8);
}
protected String getHexAddress64AsByteString(String hexAddress, boolean bigEndian) {
return getHexAddressAsByteString(hexAddress, bigEndian, 16);
}
protected String getHexAddressAsByteString(String hexAddress, boolean bigEndian,
int numHexDigits) {
int indexOf = hexAddress.indexOf("0x");
if (indexOf != 0) {
throw new IllegalArgumentException("Hex address strings must start with 0x.");
}
String hexDigits = hexAddress.substring(2, hexAddress.length());
int hexLength = hexDigits.length();
if (hexLength > numHexDigits) {
throw new IllegalArgumentException(
"hexAddress can't be more than " + numHexDigits + " digits.");
}
int missingZeros = numHexDigits - hexLength;
StringBuffer buf = new StringBuffer();
for (int i = 0; i < missingZeros; i++) {
buf.append('0');
}
buf.append(hexDigits);
String string = buf.toString();
return getByteString(bigEndian, string);
}
// protected String getHexAddressAsByteString(String hexAddress, boolean bigEndian) {
// int indexOf = hexAddress.indexOf("0x");
// if (indexOf != 0) {
// throw new IllegalArgumentException("Hex address strings must start with 0x.");
// }
// String hexDigits = hexAddress.substring(2, hexAddress.length());
// return getByteString(bigEndian, hexDigits);
// }
protected String getByteString(boolean bigEndian, String hexDigits) {
if (hexDigits.length() % 2 == 1) {
hexDigits = "0" + hexDigits;
}
StringBuffer buf = new StringBuffer();
int numPairs = hexDigits.length() / 2;
for (int i = numPairs - 1; i >= 0; i--) {
int beginIndex = i * 2;
int endIndex = beginIndex + 2;
String next2Digits = hexDigits.substring(beginIndex, endIndex);
if (bigEndian) {
if (buf.length() > 0) {
buf.insert(0, ' ');
}
buf.insert(0, next2Digits);
}
else {
if (buf.length() > 0) {
buf.append(' ');
}
buf.append(next2Digits);
}
}
return buf.toString();
}
protected void checkArrayData(ProgramDB program, long address, DataType elementDt,
int numElements) {
Listing listing = program.getListing();
Data data = listing.getDataAt(addr(program, address));
DataType dataType = data.getDataType();
if (!(dataType instanceof Array)) {
fail("Data type " + dataType.getName() + " isn't an array.");
}
Array array = (Array) dataType;
assertEquals(numElements, array.getNumElements());
String name = dataType.getName();
assertEquals(elementDt.getName() + "[" + numElements + "]", name);
int expectedDtLength = elementDt.getLength() * numElements;
assertEquals(expectedDtLength, expectedDtLength);
DataType baseDataType = array.getDataType();
assertTrue(baseDataType.isEquivalent(elementDt));
}
protected void checkSimpleData(ProgramDB program, long address, DataType elementDt) {
Listing listing = program.getListing();
Data data = listing.getDataAt(addr(program, address));
DataType dataType = data.getDataType();
String name = dataType.getName();
assertEquals(elementDt.getName(), name);
int expectedDtLength = elementDt.getLength();
assertEquals(expectedDtLength, expectedDtLength);
assertTrue(dataType.isEquivalent(elementDt));
}
protected void CheckTypeDefOnStructureData(ProgramDB program, long address, String expectedName,
String[] expectedFieldNames, int expectedDtLength) {
CheckStructureData(program, address, expectedName, expectedFieldNames, null,
expectedDtLength, true);
}
protected void CheckStructureData(ProgramDB program, long address, String expectedName,
String[] expectedFieldNames, int expectedDtLength) {
CheckStructureData(program, address, expectedName, expectedFieldNames, null,
expectedDtLength, false);
}
protected void CheckStructureData(ProgramDB program, long address, String expectedName,
String[] expectedFieldNames, String flexArrayName, int expectedDtLength) {
CheckStructureData(program, address, expectedName, expectedFieldNames, flexArrayName,
expectedDtLength, false);
}
protected void CheckStructureData(ProgramDB program, long address, String expectedName,
String[] expectedFieldNames, String flexArrayName, int expectedDtLength,
boolean isTypeDefOfStructure) {
Listing listing = program.getListing();
Data data = listing.getDataAt(addr(program, address));
DataType dataType = data.getDataType();
String name = dataType.getName();
assertEquals(expectedName, name);
assertEquals(expectedDtLength, dataType.getLength());
DataType baseDataType = dataType;
if (isTypeDefOfStructure) {
assertTrue("DataType " + name + " wasn't a TypeDef.", dataType instanceof TypeDef);
baseDataType = ((TypeDef) dataType).getBaseDataType();
}
assertTrue("DataType " + name + "'s base data type wasn't a Structure.",
baseDataType instanceof Structure);
Structure structure = (Structure) baseDataType;
assertEquals("Mismatch in expected structure component count: " + name,
expectedFieldNames.length, structure.getNumComponents());
DataTypeComponent[] components = structure.getComponents();
for (int i = 0; i < components.length; i++) {
assertEquals(
"Expected component " + i + " to be named " + expectedFieldNames[i] + " but was " +
components[i].getFieldName(),
expectedFieldNames[i], components[i].getFieldName());
}
if (flexArrayName != null) {
DataTypeComponent flexibleArrayComponent = structure.getFlexibleArrayComponent();
assertNotNull("Structure does not contain flexible array: " + name,
flexibleArrayComponent);
assertEquals(
"Expected flexible array named " + flexArrayName + " but was " +
flexibleArrayComponent.getFieldName(),
flexArrayName, flexibleArrayComponent.getFieldName());
}
else {
assertFalse("Structure contains unexpected flexible array component: " + name,
structure.hasFlexibleArrayComponent());
}
}
protected void CheckDynamicStructureData(ProgramDB program, long address, String expectedName,
String[] expectedFieldNames, int expectedDtLength) {
Listing listing = program.getListing();
Address dataAddress = addr(program, address);
Data data = listing.getDataAt(dataAddress);
DataType dataType = data.getDataType();
String name = dataType.getName();
assertEquals(expectedName, name);
assertEquals(expectedDtLength, data.getLength());
assertTrue("DataType " + dataType.getName() + " isn't a DynamicDataType.",
dataType instanceof DynamicDataType);
DynamicDataType dynamicDt = (DynamicDataType) dataType;
DumbMemBufferImpl memBuffer = new DumbMemBufferImpl(program.getMemory(), dataAddress);
assertEquals(expectedFieldNames.length, dynamicDt.getNumComponents(memBuffer));
DataTypeComponent[] components = dynamicDt.getComponents(memBuffer);
for (int i = 0; i < components.length; i++) {
assertEquals(
"Expected component " + i + " to be named " + expectedFieldNames[i] + " but was " +
components[i].getFieldName(),
expectedFieldNames[i], components[i].getFieldName());
}
}
protected void checkNoData(ProgramDB program, long address) {
Listing listing = program.getListing();
Data data = listing.getDataAt(addr(program, address));
assertEquals(DefaultDataType.dataType, data.getDataType());
}
protected Address addr(Program program, long address) {
AddressFactory addressFactory = program.getAddressFactory();
AddressSpace defaultAddressSpace = addressFactory.getDefaultAddressSpace();
return defaultAddressSpace.getAddress(address);
}
protected void checkInvalidModel(AbstractCreateDataTypeModel model, String errorMessage) {
try {
model.validate();
Assert.fail("Model validation should have failed.");
}
catch (InvalidDataTypeException e) {
// Should fail validation with expected error message.
assertEquals(errorMessage, e.getMessage());
}
}
protected void checkTypeDescriptorData(ProgramDB program, long address, int structLength,
int nameArrayLength, String expectedTypeName) {
CheckStructureData(program, address, "TypeDescriptor", new String[] { "pVFTable", "spare" },
"name", structLength);
checkTypeName(program, address, expectedTypeName);
}
private void checkTypeName(ProgramDB program, long address, String expectedTypeName) {
TypeDescriptorModel typeDescriptorModel =
new TypeDescriptorModel(program, addr(program, address), defaultValidationOptions);
try {
String typeName = typeDescriptorModel.getTypeName();
assertEquals(expectedTypeName, typeName);
}
catch (InvalidDataTypeException e) {
fail("Couldn't get type name for TypeDescriptor @ " + address);
}
}
}