ghidra/Ghidra/Features/Base/ghidra_scripts/PropagateExternalParameters...

343 lines
12 KiB
Java

/* ###
* 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 propagates Windows external and library function parameter names and types
// It puts the parameter names and types in the comments next to the pushes before a function call.
// It currently does not check for branches in the middle of a series of parameters
//@category Analysis
import java.util.ArrayList;
import java.util.List;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.*;
import ghidra.program.model.data.DataType;
import ghidra.program.model.lang.OperandType;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
public class PropagateExternalParametersScript extends GhidraScript {
private List<PushedParamInfo> results = new ArrayList<>();
@Override
public void run() throws Exception {
Listing listing = currentProgram.getListing();
FunctionManager functionManager = currentProgram.getFunctionManager();
//FunctionIterator externalFunctions = functionManager.getExternalFunctions();
ReferenceManager refMan = currentProgram.getReferenceManager();
// iterate over all external symbols
SymbolTable symTab = currentProgram.getSymbolTable();
SymbolIterator externalSymbols = symTab.getExternalSymbols();
while (externalSymbols.hasNext()) {
Symbol extSym = externalSymbols.next();
if (extSym.getSymbolType() == SymbolType.FUNCTION) {
Function extFunc = functionManager.getFunctionAt(extSym.getAddress());
Parameter[] params = extFunc.getParameters();
if (params.length == 0) {
continue;
}
Reference[] references = extSym.getReferences();
processExternalFunction(listing, refMan, references, extFunc, params,
extSym.getName());
}
}
// use the 'results' to propagate param info to the local variables, data, and params of
// the calling function
//println("Processing propagation results - count: " + results.size());
for (int i = 0; i < results.size(); i++) {
PushedParamInfo ppi = results.get(i);
Instruction instr = listing.getInstructionAt(ppi.getAddress());
int opType = instr.getOperandType(0);
if (!instr.getOperandRefType(0).isData()) {
continue;
}
//If operand of pushed parameter points to data make a symbol and comment at that location
if (((opType & OperandType.ADDRESS) != 0) && (((opType & OperandType.DATA) != 0)) ||
((opType & OperandType.SCALAR) != 0) || ((opType & OperandType.DYNAMIC) != 0)) {
Reference[] refs = listing.getCodeUnitAt(ppi.getAddress()).getOperandReferences(0);
if ((refs.length > 0) && (refs[0].isMemoryReference())) {
Address dataAddress = refs[0].getToAddress();
DataType dt = null;
dt = ppi.getDataType();
Data data = getDataAt(dataAddress);
boolean isString = false;
if ((data != null) && data.hasStringValue()) {
isString = true;
}
String symbolName = new String(ppi.getName() + "_" + dataAddress.toString());
String newComment = new String(
ppi.getName() + " parameter of " + ppi.getCalledFunctionName() + "\n");
if ((getSymbol(symbolName, null) == null) && (isString == false)) {
createLabel(dataAddress, symbolName, true, SourceType.USER_DEFINED);
}
String currentComment = getPlateComment(dataAddress);
if (currentComment == null) {
setPlateComment(dataAddress, newComment);
}
else if (!currentComment.contains(ppi.getCalledFunctionName())) {
setPlateComment(dataAddress, currentComment + newComment);
}
if ((data != null) &&
(listing.getCodeUnitAt(dataAddress).getMnemonicString().startsWith(
"undefined"))) {
clearListing(dataAddress);
}
if (listing.isUndefined(dataAddress, dataAddress.add(dt.getLength() - 1))) {
try {
createData(dataAddress, dt);
printf("Data Created at %s : %s ( %s )\n", dataAddress.toString(),
newComment.replace("\n", ""), ppi.getAddress().toString());
}
catch (Exception e) {
printf("Error making data: %s", e.toString());
}
}
}
}
}
} // end of run
private void processExternalFunction(Listing listing, ReferenceManager refMan,
Reference[] extRefs, Function extFunc, Parameter[] params, String extFuncName) {
for (Reference extRef : extRefs) {
Address refAddr = extRef.getFromAddress();
String refMnemonic = listing.getCodeUnitAt(refAddr).getMnemonicString();
Function calledFromFunc = listing.getFunctionContaining(refAddr);
if (calledFromFunc == null) {
continue;
}
if ((refMnemonic.equals(new String("JMP")) && (calledFromFunc.isThunk()))) {
//println(calledFromFunc.getName() + " is a thunk. Refs are:");
ReferenceIterator tempIter = refMan.getReferencesTo(calledFromFunc.getEntryPoint());
while (tempIter.hasNext()) {
Reference thunkRef = tempIter.next();
Address thunkRefAddr = thunkRef.getFromAddress();
String thunkRefMnemonic =
listing.getCodeUnitAt(thunkRefAddr).getMnemonicString();
Function thunkRefFunc = listing.getFunctionContaining(thunkRefAddr);
if ((thunkRefMnemonic.equals(new String("CALL")) && (thunkRefFunc != null))) {
CodeUnitIterator cuIt =
getCodeUnitsFromFunctionStartToRef(thunkRefFunc, thunkRefAddr);
if (checkEnoughPushes(cuIt, params.length)) {
CodeUnitIterator codeUnitsToRef =
getCodeUnitsFromFunctionStartToRef(thunkRefFunc, thunkRefAddr);
propogateParams(params, codeUnitsToRef, extFunc.getName());
println("Processing external function: " + extFuncName + " at " +
thunkRefAddr.toString());
}
}
}
}
else if ((refMnemonic.equals(new String("CALL")))) {// not a thunk
CodeUnitIterator cuIt = getCodeUnitsFromFunctionStartToRef(calledFromFunc, refAddr);
if (checkEnoughPushes(cuIt, params.length)) {
CodeUnitIterator codeUnitsToRef =
getCodeUnitsFromFunctionStartToRef(calledFromFunc, refAddr);
propogateParams(params, codeUnitsToRef, extFunc.getName());
println("Processing external function: " + extFuncName + " at " +
refAddr.toString());
}
}
}//end of extRef loop
}
/*
* 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
int numParams(CodeUnit cu) {
FunctionManager functionManager = currentProgram.getFunctionManager();
Reference[] opref = cu.getReferencesFrom();
Address toAddr = null;
Function f = null;
int numParams = 0;
if (opref.length != 0) {
toAddr = opref[0].getToAddress();
//f = listing.getFunctionAt(toAddr);
f = functionManager.getReferencedFunction(toAddr);
if (f != null) {
//println("Call in middle at " + cu.getMinAddress().toString() + " " + f.getName());
Parameter[] prms = f.getParameters();
numParams = prms.length;
}
}
return numParams;
}
CodeUnitIterator getCodeUnitsFromFunctionStartToRef(Function func, Address refAddr) {
if (func == null) {
return null;
}
Listing listing = currentProgram.getListing();
AddressSetView funcAddresses = func.getBody();
CodeUnit referenceCodeUnit = listing.getCodeUnitAt(refAddr);
Address referenceMinAddress = referenceCodeUnit.getMinAddress();
CodeUnit previousCodeUnit = listing.getCodeUnitBefore(referenceMinAddress);
Address previousMinAddress = previousCodeUnit.getMinAddress();
AddressIterator it = funcAddresses.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
boolean checkEnoughPushes(CodeUnitIterator cuIterator, int numParams) {
if (cuIterator == null) {
return false;
}
int numPushes = 0;
int numSkips = 0;
while ((cuIterator.hasNext()) && (numPushes < numParams)) {
CodeUnit cu = cuIterator.next();
if (numSkips > 0) {
numSkips--;
}
else if (cu.getMnemonicString().equals(new String("CALL"))) {
numParams += numParams(cu);
}
else if (cu.getMnemonicString().equals(new String("PUSH"))) {
numPushes++;
}
}
if (numPushes >= numParams) {
return true;
}
return false; // not enough params between ref and top of function
}
void propogateParams(Parameter[] params, CodeUnitIterator cuIt, String extFuncName) {
int index = 0;
int numSkips = 0;
boolean hasBranch = false;
while (cuIt.hasNext() && (index < params.length) && !hasBranch) {
CodeUnit cu = cuIt.next();
// 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
if (cu.getLabel() != null) {
hasBranch = true;
}
if (cu.getMnemonicString().equals(new String("CALL"))) {
numSkips += numParams(cu);
//printf("numSkips = %d", numSkips);
}
else if (cu.getMnemonicString().equals(new String("PUSH"))) {
if (numSkips > 0) {
numSkips--;
}
else {
setEOLComment(cu.getMinAddress(), params[index].getDataType().getDisplayName() +
" " + params[index].getName() + " for " + extFuncName);
// add the following to the EOL comment to see the value of the optype
// +" " + toHexString(currentProgram.getListing().getInstructionAt(cu.getMinAddress()).getOperandType(0), false, true)
addResult(params[index].getName(), params[index].getDataType(),
cu.getMinAddress(), extFuncName);
index++;
}
}
}
}
// for now all calledFuncNames are extFuncNames
void addResult(String name, DataType dataType, Address addr, String calledFuncName) {
PushedParamInfo param = new PushedParamInfo(name, dataType, addr, calledFuncName);
results.add(param);
}
// 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;
}
}
}