diff --git a/Ghidra/Features/Decompiler/ghidra_scripts/AddVfunctionCallRefScript.java b/Ghidra/Features/Decompiler/ghidra_scripts/AddVfunctionCallRefScript.java new file mode 100644 index 0000000000..84114c3b49 --- /dev/null +++ b/Ghidra/Features/Decompiler/ghidra_scripts/AddVfunctionCallRefScript.java @@ -0,0 +1,309 @@ +/* ### + * 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. + */ +// This script requires the cursor to be on a vftable->function token in the decompiler and will +// create a CALL reference to the associated function in the listing if it is possible to identify a +// single corresponding applied vftable structure and identify the associated function in the +// listing. +// +//@category C++ + +import java.util.ArrayList; +import java.util.List; + +import ghidra.app.decompiler.*; +import ghidra.app.decompiler.component.DecompilerUtils; +import ghidra.app.script.GhidraScript; +import ghidra.program.model.address.Address; +import ghidra.program.model.data.*; +import ghidra.program.model.listing.*; +import ghidra.program.model.mem.DumbMemBufferImpl; +import ghidra.program.model.mem.MemoryAccessException; +import ghidra.program.model.symbol.RefType; +import ghidra.program.model.symbol.SourceType; +import ghidra.util.exception.CancelledException; + +public class AddVfunctionCallRefScript extends GhidraScript { + private DecompInterface decomplib; + DecompileResults lastResults = null; + + @Override + public void run() throws Exception { + + try { + // get the decompiler context + // setup the decompiler + decomplib = setUpDecompiler(currentProgram); + if (decomplib == null) { + println("Could not setup the decompiler."); + return; + } + + @SuppressWarnings("unused") + + // get the function associated with the current cursor location in the decompiler + // if the cursor is not on a vftable->function construct then it will return null + // if a single function cannot be tied to this construct then return null + Function function = getFunctionAtLocation(); + + // previous function spits out appropriate error message if it returns null + if (function == null) { + return; + } + + println("Associated function found: " + function.getEntryPoint()); + + Address associatedAddress = getAssociatedAddress(); + if (associatedAddress == null) { + println("Cannot find single associated listing address to put reference on."); + return; + } + + // add reference to the function on the CALL operand + addOperandReference(associatedAddress, function); + println("Reference added to " + associatedAddress.toString()); + + // uncomment this line to add a comment link + //setPreComment(associatedAddress, "{@symbol " + function.getEntryPoint() + "}"); + + } + finally { + if (decomplib != null) { + decomplib.dispose(); + } + } + } + + private void addOperandReference(Address address, Function function) { + + Instruction instruction = currentProgram.getListing().getInstructionAt(address); + instruction.addOperandReference(0, function.getEntryPoint(), RefType.UNCONDITIONAL_CALL, + SourceType.ANALYSIS); + } + + private Function getFunctionAtLocation() throws CancelledException { + + if (currentLocation instanceof DecompilerLocation dloc) { + + // get the Varnode under the cursor + ClangToken tokenAtCursor = dloc.getToken(); + Structure structure = getStructure(tokenAtCursor); + if (structure == null) { + println( + "Cursor is not in a valid structure token. Put cursor on a decompiler structure->function name."); + return null; + } + println("Structure containing cursor token: " + structure.getDisplayName()); + + // check that the struct is all function pointers + if (!isVftableStructure(structure)) { + println("This script only works for structures that contain all function pointers"); + return null; + } + + List
appliedData = getAppliedData(structure); + if (appliedData.size() == 0) { + println("No applied structure " + structure.getDisplayName() + + " so cannot find associated function."); + return null; + } + if (appliedData.size() > 1) { + println("Multiple applied structures " + structure.getDisplayName() + + " so cannot find associated function"); + return null; + } + + Address singleAppliedStructAddress = appliedData.get(0); + + println("Single " + structure.getDisplayName() + " found at " + + singleAppliedStructAddress.toString()); + + String functionName = tokenAtCursor.toString(); + Integer offsetOfFunction = getOrdinalOfFunction(structure, functionName); + if (offsetOfFunction == null) { + println("No function named " + functionName + " in structure " + + structure.getPathName() + " so cannot find associated function"); + return null; + } + + int defaultPointerSize = currentProgram.getDefaultPointerSize(); + + Address referencedAddress = getReferencedAddress(currentProgram, + singleAppliedStructAddress.add(defaultPointerSize * offsetOfFunction), true); + if (referencedAddress == null) { + println("No referenced address at offset " + offsetOfFunction); + return null; + } + + Function function = currentProgram.getListing().getFunctionAt(referencedAddress); + if (function == null) { + println("No function at: " + referencedAddress.toString()); + return null; + } + + if (function.isThunk()) { + function = function.getThunkedFunction(true); + } + + return function; + + } + + return null; + + } + + private Address getAssociatedAddress() { + + if (currentLocation instanceof DecompilerLocation dloc) { + // get the Varnode under the cursor + ClangToken token = dloc.getToken(); + + // return the closest single associated address in the listing + return DecompilerUtils.getClosestAddress(currentProgram, token); + } + return null; + } + + private Structure getStructure(ClangToken token) { + + DataType dataType = DecompilerUtils.getDataType(token); + if (!(dataType instanceof Structure)) { + return null; + } + + return (Structure) dataType; + + } + + private List
getAppliedData(DataType dataType) throws CancelledException { + + List
appliedData = new ArrayList<>(); + Listing listing = currentProgram.getListing(); + DataIterator definedData = listing.getDefinedData(true); + while (definedData.hasNext()) { + monitor.checkCancelled(); + + Data nextData = definedData.next(); + if (nextData.getDataType().equals(dataType)) { + appliedData.add(nextData.getAddress()); + } + } + return appliedData; + + } + + private Integer getOrdinalOfFunction(Structure struct, String name) { + + for (DataTypeComponent component : struct.getComponents()) { + DataType dataType = component.getDataType(); + if (dataType instanceof Pointer ptr) { + dataType = ptr.getDataType(); + } + + if (dataType.getName().equals(name)) { + return component.getOrdinal(); + } + } + return null; + } + + // validate that structure contains only functionDef*'s + private boolean isVftableStructure(Structure struct) { + + for (DataTypeComponent component : struct.getComponents()) { + DataType dataType = component.getDataType(); + if (!(dataType instanceof Pointer ptr)) { + return false; + } + + dataType = ptr.getDataType(); + if (!(dataType instanceof FunctionDefinition)) { + return false; + } + } + return true; + } + + private DecompInterface setUpDecompiler(Program program) { + + DecompInterface decompInterface = new DecompInterface(); + + // call it to get results + if (!decompInterface.openProgram(currentProgram)) { + println("Decompile Error: " + decompInterface.getLastMessage()); + return null; + } + + DecompileOptions options = DecompilerUtils.getDecompileOptions(state.getTool(), program); + + decompInterface.setOptions(options); + + decompInterface.toggleCCode(true); + decompInterface.toggleSyntaxTree(true); + decompInterface.setSimplificationStyle("decompile"); + + return decompInterface; + } + + /** + * Method to return the referenced address at the given address in the given program + * Note: this will work whether there is a created reference or not + * @param program the given program + * @param addr the address to look for a referenced address at + * @param getIboIf64bit if true, get the address corresponding to the image base offset instead + * of the full reference address + * @return the first referenced address from the given address + */ + public static Address getReferencedAddress(Program program, Address addr, + boolean getIboIf64bit) { + + int addressSize = addr.getSize(); + if (addressSize == 64 && getIboIf64bit) { + IBO32DataType ibo32 = new IBO32DataType(program.getDataTypeManager()); + int length = ibo32.getLength(); + DumbMemBufferImpl compMemBuffer = new DumbMemBufferImpl(program.getMemory(), addr); + Object value = ibo32.getValue(compMemBuffer, ibo32.getDefaultSettings(), length); + if (value instanceof Address iboAddress) { + return iboAddress; + } + return null; + } + + long offset; + try { + if (addressSize == 32) { + Integer offset32 = program.getMemory().getInt(addr); + offset = Integer.toUnsignedLong(offset32); + } + else if (addressSize == 64) { + offset = program.getMemory().getLong(addr); + } + else { + return null; + } + Address newAddr = addr.getNewAddress(offset); + if (program.getMemory().contains(newAddr)) { + return newAddr; + } + return null; + } + catch (MemoryAccessException e) { + return null; + } + + } + +}