ghidra/Ghidra/Features/Decompiler/ghidra_scripts/ShowConstantUse.java

1079 lines
34 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.
*/
// Given a variable in the decompiler, walk backward through function calls to find any constants
// that find their way directly into the variable. Very useful for getting a list of all the
// constants passed to a parameter, or to a parameter at a given location in the program.
//
// The guts of this script past the main could be used to analyze
// constants passed to any function on any processor.
// It is not restricted to windows.
//
//@category Search
import java.util.*;
import generic.jar.ResourceFile;
import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.DecompilerUtils;
import ghidra.app.script.*;
import ghidra.app.tablechooser.*;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.util.OptionsService;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.*;
import ghidra.program.model.pcode.*;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.ReferenceIterator;
import ghidra.program.util.*;
import ghidra.util.SystemUtilities;
import ghidra.util.UndefinedFunction;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.InvalidInputException;
public class ShowConstantUse extends GhidraScript {
private DecompInterface decomplib;
DecompileResults lastResults = null;
TableChooserDialog tableDialog;
@Override
public void run() throws Exception {
TableChooserExecutor executor = null;
// executor = createTableExecutor();
tableDialog = createTableChooserDialog("Constant Values", executor);
configureTableColumns(tableDialog);
tableDialog.show();
tableDialog.setMessage("Searching...");
try {
// get the decompiler context
// setup the decompiler
decomplib = setUpDecompiler(currentProgram);
// find out what we are on (param, variable, etc...
@SuppressWarnings("unused")
HashMap<Address, Long> constLocs;
Varnode var = getVarnodeLocation();
if (var != null && !var.isConstant()) {
constLocs = backtrackToConstant(var, tableDialog);
}
else {
// if couldn't find a varnode location, then must be on a
// function parameter with no body
// just backtrack the parameter from the function parameter
if (currentLocation instanceof FunctionParameterFieldLocation) {
FunctionParameterFieldLocation funcPFL =
(FunctionParameterFieldLocation) currentLocation;
Function f = funcPFL.getParameter().getFunction();
int paramIndex = funcPFL.getParameter().getOrdinal();
constLocs = backtrackParamToConstant(f, paramIndex, tableDialog);
}
else if (currentLocation instanceof VariableNameFieldLocation) {
VariableNameFieldLocation varNFL = (VariableNameFieldLocation) currentLocation;
Variable funcvar = varNFL.getVariable();
Function f = funcvar.getFunction();
if (funcvar instanceof Parameter) {
int paramIndex = ((Parameter) funcvar).getOrdinal();
constLocs = backtrackParamToConstant(f, paramIndex, tableDialog);
}
}
else if (currentLocation instanceof DecompilerLocation) {
// must be inside the decompiler, locate the parameter we are on within a function.
DecompilerLocation decL = (DecompilerLocation) currentLocation;
ClangToken token = decL.getToken();
if (token instanceof ClangVariableToken) {
ClangVariableToken clangVar = (ClangVariableToken) token;
ClangNode parent = token.Parent();
if (parent instanceof ClangStatement) {
ClangStatement clangStmt = (ClangStatement) parent;
PcodeOp pcodeOp = clangStmt.getPcodeOp();
if (pcodeOp.getOpcode() == PcodeOp.CALL) {
Varnode input = pcodeOp.getInput(0);
if (input.isAddress()) {
Address faddr = input.getAddress();
Function f = getReferencedFunction(faddr);
int paramIndex = 0;
for (int i = 0; i < clangStmt.numChildren(); i++) {
ClangNode child = clangStmt.Child(i);
if (child.equals(clangVar)) {
constLocs = backtrackParamToConstant(f, paramIndex,
tableDialog);
break;
}
if (child instanceof ClangVariableToken) {
paramIndex++;
}
}
}
}
}
}
}
else {
tableDialog.setMessage("**** please put the cursor on a variable! ****");
}
}
}
finally {
decomplib.dispose();
}
tableDialog.setMessage("Finished!");
}
private Function getReferencedFunction(Address functionAddress) {
Function f = currentProgram.getFunctionManager().getFunctionAt(functionAddress);
// couldn't find the function, see if there is an external ref there.
if (f == null) {
Reference[] referencesFrom =
currentProgram.getReferenceManager().getReferencesFrom(functionAddress);
for (Reference reference : referencesFrom) {
if (reference.isExternalReference()) {
functionAddress = reference.getToAddress();
f = currentProgram.getFunctionManager().getFunctionAt(functionAddress);
if (f != null) {
break;
}
}
}
}
return f;
}
/**
* Builds the configurable columns for the TableDialog. More columns could
* be added.
*
* @param tableChooserDialog
*/
private void configureTableColumns(TableChooserDialog tableChooserDialog) {
// First column added is the Constant value that is found.
// Note the special compare method that must compare the constant
// values not as a default string rendering, but by actual value.
StringColumnDisplay constColumn = new StringColumnDisplay() {
@Override
public int compare(AddressableRowObject o1, AddressableRowObject o2) {
ConstUseLocation e1 = (ConstUseLocation) o1;
ConstUseLocation e2 = (ConstUseLocation) o2;
Long v1 = e1.getConstValue();
Long v2 = e2.getConstValue();
if (SystemUtilities.isEqual(v1, v2)) {
return 0;
}
if (v1 == null) {
return -1;
}
if (v2 == null) {
return 1;
}
return (int) (v1 - v2);
}
@Override
public String getColumnName() {
return "Constant Value";
}
@Override
public String getColumnValue(AddressableRowObject rowObject) {
ConstUseLocation entry = (ConstUseLocation) rowObject;
Long val = entry.getConstValue();
if (val == null) {
return "";
}
return Long.toHexString(val);
}
};
// Displays a preview of anything defined at an address if the constant
// discovered in the backtrack search could be treated as an address
//
StringColumnDisplay addrPreviewColumn = new StringColumnDisplay() {
@Override
public String getColumnName() {
return "Addr Preview";
}
@Override
public String getColumnValue(AddressableRowObject rowObject) {
ConstUseLocation entry = (ConstUseLocation) rowObject;
Long val = entry.getConstValue();
if (val == null) {
return "";
}
Address addr = entry.getAddress();
if (addr == null) {
return "";
}
Address potAddr = addr.getNewAddress(val);
if (addr.getAddressSpace().isOverlaySpace()) {
potAddr = addr.getAddressSpace().getOverlayAddress(potAddr);
}
if (!currentProgram.getMemory().contains(potAddr)) {
return "";
}
Listing listing = currentProgram.getListing();
Data data = listing.getDefinedDataAt(potAddr);
if (data != null) {
return data.toString();
}
Function f = currentProgram.getFunctionManager().getFunctionAt(potAddr);
if (f != null) {
return f.getPrototypeString(false, false);
}
return "";
}
};
// column to display the name of the function containing the address
// where a
// constant is found to be used.
StringColumnDisplay funcColumn = new StringColumnDisplay() {
@Override
public String getColumnName() {
return "Func Name";
}
@Override
public String getColumnValue(AddressableRowObject rowObject) {
ConstUseLocation entry = (ConstUseLocation) rowObject;
Function func = entry.getProgram().getFunctionManager().getFunctionContaining(
entry.getAddress());
if (func == null) {
return "";
}
return func.getName();
}
};
// column to display the note at the address where a
// this is usually an error condition
StringColumnDisplay noteColumn = new StringColumnDisplay() {
@Override
public String getColumnName() {
return "Note";
}
@Override
public String getColumnValue(AddressableRowObject rowObject) {
ConstUseLocation entry = (ConstUseLocation) rowObject;
String note = entry.getNote();
if (note == null) {
return "";
}
return note;
}
};
tableChooserDialog.addCustomColumn(constColumn);
tableChooserDialog.addCustomColumn(addrPreviewColumn);
tableChooserDialog.addCustomColumn(funcColumn);
tableChooserDialog.addCustomColumn(noteColumn);
}
/**
* Sample execution task Execution class called whenever the execute button
* in the table is called. NOTE: the execute button is not setup, so this is
* just and example
*
* Useful if you are back tracking constants for malloc or calloc Runs
* another script that will create a structure on the return variable of
* calloc/malloc. It pulls a little trick when calling the CreateStructure
* script by creating an artificial ScriptState. This is a useful technique
* for other scripts as well.
*
* @return
*/
@SuppressWarnings("unused")
private TableChooserExecutor createTableExecutor() {
TableChooserExecutor executor = new TableChooserExecutor() {
@Override
public String getButtonName() {
return "Create Structure";
}
@Override
public boolean execute(AddressableRowObject rowObject) {
ConstUseLocation constLoc = (ConstUseLocation) rowObject;
System.out.println("Follow Structure : " + rowObject.getAddress());
Program cp = constLoc.getProgram();
Address entry = constLoc.getAddress();
// If we will change something in program, have to open a
// transaction
int trans = cp.startTransaction("Run Script" + entry);
try {
System.out.println("Create Structure at " + entry);
runScript("CreateStructure.java", cp, entry);
}
finally {
cp.endTransaction(trans, true);
}
return false; // don't remove row from display table
}
public void runScript(String name, Program prog, Address loc) {
GhidraState scriptState = new GhidraState(state.getTool(), state.getProject(), prog,
new ProgramLocation(prog, loc), null, null);
try {
ResourceFile scriptSource = GhidraScriptUtil.findScriptByName(name);
if (scriptSource != null) {
GhidraScriptProvider provider = GhidraScriptUtil.getProvider(scriptSource);
GhidraScript script = provider.getScriptInstance(scriptSource, writer);
script.execute(scriptState, monitor, writer);
return;
}
}
catch (Exception exc) {
exc.printStackTrace();
}
throw new IllegalArgumentException("Script does not exist: " + name);
}
};
return executor;
}
// Object that gathers the constant used locations within the program
//
class ConstUseLocation implements AddressableRowObject {
private Program program;
private Address addr;
private Long constVal;
private String note;
ConstUseLocation(Program prog, Address addr, Long constVal, String note) {
this.addr = addr;
this.constVal = constVal;
this.program = prog;
this.note = note;
}
public Program getProgram() {
return program;
}
@Override
public Address getAddress() {
return addr;
}
public Long getConstValue() {
return constVal;
}
public String getNote() {
return note;
}
}
/**
* Try to locate the Varnode that represents the variable in the listing or
* decompiler. In the decompiler this could be a local/parameter at any
* point in the decompiler. In the listing, it must be a parameter variable.
*
* @return
*/
private Varnode getVarnodeLocation() {
Varnode var = null;
if (currentLocation instanceof DecompilerLocation) {
DecompilerLocation dloc;
// get the Varnode under the cursor
dloc = (DecompilerLocation) currentLocation;
ClangToken tokenAtCursor = dloc.getToken();
var = DecompilerUtils.getVarnodeRef(tokenAtCursor);
// fixupParams(dloc.getDecompile(), currentLocation.getAddress());
if (tokenAtCursor == null) {
println("**** please put the cursor on a variable in the decompiler!");
return null;
}
lastResults = dloc.getDecompile();
}
else {
// if we don't have one, make one, and map variable to a varnode
HighSymbol highVar = computeVariableLocation(currentProgram, currentLocation);
if (highVar != null) {
var = highVar.getHighVariable().getRepresentative();
}
else {
return null;
}
}
return var;
}
private void addConstants(TableChooserDialog tableChooserDialog,
HashMap<Address, Long> constLocs) {
Set<Address> keys;
keys = constLocs.keySet();
Address[] keyArray = keys.toArray(new Address[0]);
Arrays.sort(keyArray);
for (Address loc : keyArray) {
Long constant = constLocs.get(loc);
tableChooserDialog.add(new ConstUseLocation(currentProgram, loc, constant, null));
}
}
private void addConstantProblem(TableChooserDialog tableChooserDialog, Address refAddr,
String problem) {
tableChooserDialog.add(new ConstUseLocation(currentProgram, refAddr, null, problem));
println(problem);
}
private HighSymbol computeVariableLocation(Program currProgram, ProgramLocation location) {
HighSymbol highVar = null;
Address storageAddress = null;
// make sure what we are over can be mapped to decompiler
// param, local, etc...
if (location instanceof VariableLocation) {
VariableLocation varLoc = (VariableLocation) location;
storageAddress = varLoc.getVariable().getMinAddress();
}
else if (location instanceof FunctionParameterFieldLocation) {
FunctionParameterFieldLocation funcPFL = (FunctionParameterFieldLocation) location;
storageAddress = funcPFL.getParameter().getMinAddress();
}
else if (location instanceof OperandFieldLocation) {
OperandFieldLocation opLoc = (OperandFieldLocation) location;
int opindex = opLoc.getOperandIndex();
if (opindex >= 0) {
Instruction instr = currProgram.getListing().getInstructionAt(opLoc.getAddress());
if (instr != null) {
Register reg = instr.getRegister(opindex);
if (reg != null) {
storageAddress = reg.getAddress();
}
}
}
}
if (storageAddress == null) {
return null;
}
Address addr = currentLocation.getAddress();
if (addr == null) {
return null;
}
Function f = currProgram.getFunctionManager().getFunctionContaining(addr);
if (f == null) {
return null;
}
DecompileResults results = decompileFunction(f, decomplib);
HighFunction hf = results.getHighFunction();
if (hf == null) {
return null;
}
// try to map the variable
highVar = hf.getMappedSymbol(storageAddress, f.getEntryPoint().subtractWrap(1L));
if (highVar == null) {
highVar = hf.getMappedSymbol(storageAddress, null);
}
if (highVar == null) {
highVar = hf.getMappedSymbol(storageAddress, f.getEntryPoint());
}
if (highVar != null) {
// fixupParams(results, location.getAddress());
}
return highVar;
}
// These contains fields that are part of the back-tracking process if a
// variable is traced
// to the parameter of function, this is an entry for that location
public class FunctionParamUse {
String name;
Address addr;
Integer paramIndex;
Varnode representative;
ArrayList<PcodeOp> defUseList;
public FunctionParamUse(String name, Address addr, int i, Varnode varnode,
ArrayList<PcodeOp> defUseList) {
this.name = name;
this.addr = addr;
this.paramIndex = i;
this.defUseList = defUseList;
this.representative = varnode;
}
public String getName() {
return name;
}
public Address getAddress() {
return addr;
}
public int getParamIndex() {
return paramIndex;
}
public Varnode getRepresentative() {
return representative;
}
public ArrayList<PcodeOp> getDefUseList() {
return defUseList;
}
}
/**
* Backtrack to a constant given a varnode within a decompiled function This
* isn't useful for functions that can't be decompiled
*
* @param var
* varnode that represents a variable in a decompilation
* @param tableChooserDialog
* - accumulate entries. Don't like passing it, but this way the
* user gets immediate feedback as locations are found
* @return a map of Addresses->constants (constants could be NULL)
* @throws CancelledException
*/
private HashMap<Address, Long> backtrackToConstant(Varnode var,
TableChooserDialog tableChooserDialog) throws CancelledException {
HashMap<Address, Long> constUse = new HashMap<Address, Long>();
if (!decomplib.openProgram(currentProgram)) {
println("Decompile Error: " + decomplib.getLastMessage());
return constUse;
}
// follow varnode back to any constants, accumulating back to function
// param
ArrayList<FunctionParamUse> funcList = new ArrayList<FunctionParamUse>();
ArrayList<PcodeOp> defUseList = new ArrayList<PcodeOp>();
followToParam(constUse, defUseList, lastResults.getHighFunction(), var, funcList, null);
addConstants(tableChooserDialog, constUse);
return followFunctionParamToConstant(funcList, constUse, tableChooserDialog);
}
/**
* Backtrack to a constant given a start position of a parameter of a given
* function Useful if you want to start from a function paramter.
*
* @param f
* - function to start in
* @param paramIndex
* - parameter index to backtrack from
* @param tableChooserDialog
* - accumulate entries. Don't like passing it, but this way the
* user gets immediate feedback as locations are found
* @return a map of Addresses->constants (constants could be NULL)
* @throws CancelledException
*/
private HashMap<Address, Long> backtrackParamToConstant(Function f, int paramIndex,
TableChooserDialog tableChooserDialog) throws CancelledException {
HashMap<Address, Long> constUse = new HashMap<Address, Long>();
ArrayList<FunctionParamUse> funcList = new ArrayList<FunctionParamUse>();
if (f == null) {
return constUse;
}
Varnode pvnode = null;
Parameter parm = f.getParameter(paramIndex);
if (parm == null) {
this.popup(
"Please put the cursor on a function parameter variable\nIf the function has not had it's parameters identified\nplease do so and try again");
return constUse;
}
// TODO: Parameter storage could consist of more than one varnode
pvnode = parm.getFirstStorageVarnode();
if (pvnode == null) {
return constUse;
}
funcList.add(new FunctionParamUse(f.getName(), f.getEntryPoint(), paramIndex, pvnode,
new ArrayList<PcodeOp>()));
addConstants(tableChooserDialog, constUse);
return followFunctionParamToConstant(funcList, constUse, tableChooserDialog);
}
private HashMap<Address, Long> followFunctionParamToConstant(
ArrayList<FunctionParamUse> funcList, HashMap<Address, Long> constUse,
TableChooserDialog tableChooserDialog) throws CancelledException {
// any routines we bumped into, process back up the chain
HashSet<Address> doneRoutines = new HashSet<Address>();
while (funcList.size() > 0) {
// get the next function the variable has been traced back to
FunctionParamUse funcVarUse = funcList.remove(0);
Address addr = funcVarUse.getAddress();
int paramIndex = funcVarUse.getParamIndex();
ArrayList<PcodeOp> defUseList = funcVarUse.getDefUseList();
// will do this at another time.
if (doneRoutines.contains(addr)) {
continue;
}
doneRoutines.add(addr);
// find all functions referring to that place
ReferenceIterator referencesTo =
currentProgram.getReferenceManager().getReferencesTo(addr);
for (Reference reference : referencesTo) {
monitor.checkCanceled();
// get function containing.
Address refAddr = reference.getFromAddress();
if (refAddr.getAddressSpace().getType() == AddressSpace.TYPE_NONE ||
refAddr.isExternalAddress()) {
continue;
}
Function refFunc =
currentProgram.getFunctionManager().getFunctionContaining(refAddr);
HashMap<Address, Long> localConstUse = new HashMap<Address, Long>();
if (refFunc == null) {
localConstUse.put(refAddr, null);
String problem = "*** No function at " + refAddr +
".\nCould not analyze constant use past this undefined function!";
addConstantProblem(tableChooserDialog, refAddr, problem);
refFunc = UndefinedFunction.findFunction(currentProgram, refAddr, monitor);
}
if (refFunc != null) {
// decompile function
// look for call to this function
// display call
@SuppressWarnings("unchecked")
ArrayList<PcodeOp> localDefUseList = (ArrayList<PcodeOp>) defUseList.clone();
this.monitor.setMessage("Analyzing : " + refFunc.getName() + " for refs to " +
addr + ":" + paramIndex);
analyzeFunction(localConstUse, decomplib, currentProgram, refFunc, refAddr,
funcVarUse, paramIndex, localDefUseList, funcList);
addConstants(tableChooserDialog, localConstUse);
}
constUse.putAll(localConstUse);
}
}
return constUse;
}
private void addErrorNote(Address address, String problem) {
this.addConstantProblem(tableDialog, address, problem);
}
/**
* Analyze a functions references
*
* @param constUse
* @param funcVarUse
* @param funcList
*/
public void analyzeFunction(HashMap<Address, Long> constUse, DecompInterface decompInterface,
Program prog, Function f, Address refAddr, FunctionParamUse funcVarUse, int paramIndex,
ArrayList<PcodeOp> defUseList, ArrayList<FunctionParamUse> funcList) {
if (f == null) {
return;
}
decompileFunction(f, decompInterface);
Instruction instr = prog.getListing().getInstructionAt(refAddr);
if (hfunction == null) {
return;
}
Iterator<PcodeOpAST> ops = hfunction.getPcodeOps(refAddr.getPhysicalAddress());
while (ops.hasNext() && !monitor.isCancelled()) {
PcodeOpAST pcodeOpAST = ops.next();
// System.out.println(pcodeOpAST);
if (pcodeOpAST.getOpcode() == PcodeOp.CALL) {
// get the second parameter
Varnode parm = pcodeOpAST.getInput(paramIndex + 1); // 1st param
// is the
// call dest
if (parm == null) {
constUse.put(instr.getAddress(), null);
String problem = " *** Warning, it appears that function '" +
funcVarUse.getName() + "' at " + funcVarUse.getAddress() +
" does not have it's parameters recovered!\n" +
" Use Commit Params/Return in the decompiler on this function.";
addErrorNote(instr.getAddress(), problem);
break;
}
// see if it is a constant
if (parm.isConstant() || parm.isAddress()) {
// then this is a resource id
// lookup the resource and create a reference
long value = parm.getOffset();
// TODO: not so fast, if there is a defUseList, must apply
// it to get the real constant USED!
try {
value = applyDefUseList(value, defUseList);
constUse.put(instr.getAddress(), value);
println(" " + f.getName() + " " + instr.getAddress() + " : 0x" +
Long.toHexString(value));
}
catch (InvalidInputException exc) {
// do nothing
}
}
else {
followToParam(constUse, defUseList, hfunction, parm, funcList, null);
}
break;
// if this is anything else, get the high variable to see if it
// can be traced back to a param
// then repeat with any calls to this function at whatever the
// param is
}
else if (pcodeOpAST.getOpcode() == PcodeOp.BRANCH) {
Address faddr = pcodeOpAST.getInput(0).getAddress();
if (funcVarUse == null || faddr == null) {
continue;
}
if (!faddr.equals(funcVarUse.getAddress())) {
continue;
}
Varnode rep = funcVarUse.getRepresentative();
if (rep == null) {
continue;
}
followToParam(constUse, defUseList, hfunction, rep, funcList, null);
}
}
}
private long applyDefUseList(long value, ArrayList<PcodeOp> defUseList)
throws InvalidInputException {
if (defUseList == null || defUseList.size() <= 0) {
return value;
}
Iterator<PcodeOp> iterator = defUseList.iterator();
while (iterator.hasNext()) {
PcodeOp pcodeOp = iterator.next();
int opcode = pcodeOp.getOpcode();
switch (opcode) {
case PcodeOp.INT_AND:
if (pcodeOp.getInput(0).isConstant()) {
value = value & pcodeOp.getInput(0).getOffset();
}
else if (pcodeOp.getInput(1).isConstant()) {
value = value & pcodeOp.getInput(1).getOffset();
}
else {
throw new InvalidInputException(
" Unhandled Pcode OP " + pcodeOp.toString());
}
break;
default:
throw new InvalidInputException(" Unhandled Pcode OP " + pcodeOp.toString());
}
}
return value;
}
private void followToParam(HashMap<Address, Long> constUse, ArrayList<PcodeOp> defUseList,
HighFunction highFunction, Varnode vnode, ArrayList<FunctionParamUse> funcList,
HashSet<SequenceNumber> doneSet) {
// follow back up through
PcodeOp def = vnode.getDef();
Function function = highFunction.getFunction();
if (def == null) {
HighVariable hvar = vnode.getHigh();
if (hvar instanceof HighParam) {
funcList.add(new FunctionParamUse(function.getName(), function.getEntryPoint(),
((HighParam) hvar).getSlot(), hvar.getRepresentative(), defUseList));
return;
}
if (hvar instanceof HighGlobal) {
followThroughGlobal(constUse, defUseList, hvar, funcList, doneSet);
}
return;
}
// have we done this vnode source already?
Address funcEntry = function.getEntryPoint();
SequenceNumber seqNum = def.getSeqnum();
remapAddress(funcEntry, def.getOutput().getPCAddress());
if (doneSet == null) {
doneSet = new HashSet<SequenceNumber>();
}
if (seqNum != null && doneSet.contains(seqNum)) {
return;
}
doneSet.add(seqNum);
int opcode = def.getOpcode();
switch (opcode) {
case PcodeOp.COPY:
if (def.getInput(0).isConstant()) {
long value = def.getInput(0).getOffset();
try {
value = applyDefUseList(value, defUseList);
constUse.put(remapAddress(funcEntry, def.getOutput().getPCAddress()),
value);
println(" " + function.getName() + " " +
def.getOutput().getPCAddress() + " : 0x" + Long.toHexString(value));
}
catch (InvalidInputException exc) {
// Do nothing
}
return;
}
followToParam(constUse, defUseList, highFunction, def.getInput(0), funcList,
doneSet);
return;
case PcodeOp.LOAD:
if (def.getInput(0).isConstant() && def.getInput(1).isConstant()) {
long space = def.getInput(0).getOffset();
long offset = def.getInput(1).getOffset();
if (space != funcEntry.getAddressSpace().getSpaceID()) {
break;
}
try {
offset = applyDefUseList(offset, defUseList);
constUse.put(remapAddress(funcEntry, def.getOutput().getPCAddress()),
offset);
println(" " + function.getName() + " " +
def.getOutput().getPCAddress() + " : 0x" + Long.toHexString(offset));
}
catch (InvalidInputException exc) {
// Do nothing
}
return;
}
followToParam(constUse, defUseList, highFunction, def.getInput(0), funcList,
doneSet);
return;
case PcodeOp.INT_ZEXT:
followToParam(constUse, defUseList, highFunction, def.getInput(0), funcList,
doneSet);
return;
case PcodeOp.MULTIEQUAL:
followToParam(constUse, defUseList, highFunction, def.getInput(0), funcList,
doneSet);
@SuppressWarnings("unchecked")
ArrayList<PcodeOp> splitUseList = (ArrayList<PcodeOp>) defUseList.clone();
followToParam(constUse, splitUseList, highFunction, def.getInput(1), funcList,
doneSet);
return;
case PcodeOp.CAST:
followToParam(constUse, defUseList, highFunction, def.getInput(0), funcList,
doneSet);
return;
case PcodeOp.INDIRECT:
Varnode output = def.getOutput();
if (output.getAddress().equals(def.getInput(0).getAddress())) {
followToParam(constUse, defUseList, highFunction, def.getInput(0), funcList,
doneSet);
return;
}
if (def.getInput(0).isUnique()) {
followToParam(constUse, defUseList, highFunction, def.getInput(0), funcList,
doneSet);
return;
}
break;
case PcodeOp.INT_AND:
if (def.getInput(1).isConstant()) {
defUseList.add(0, def);
followToParam(constUse, defUseList, highFunction, def.getInput(0), funcList,
doneSet);
return;
}
break;
case PcodeOp.INT_ADD:
if (def.getInput(1).isConstant()) {
defUseList.add(0, def);
followToParam(constUse, defUseList, highFunction, def.getInput(0), funcList,
doneSet);
return;
}
if (vnode.getHigh() instanceof HighParam) {
// Do nothing
}
break;
case PcodeOp.PTRADD: // Pointer + some offset (usually an array access)
// if (!def.getInput(1).isConstant() ||
// !def.getInput(2).isConstant()) {
// break;
// }
// DataType outDt = def.getOutput().getHigh().getDataType();
// println (" type = " + outDt.getName());
// long value = getSigned(def.getInput(1)) *
// def.getInput(2).getOffset();
//
// try {
// value = applyDefUseList(value, defUseList);
// constUse.put(def.getOutput().getPCAddress(), value);
// } catch (InvalidInputException exc) {}
break;
case PcodeOp.PTRSUB: // Pointer + some sub element access (usually a
// structure ref)
Varnode offsetVal = def.getInput(1);
if (!offsetVal.isConstant()) {
break;
}
Varnode baseVal = def.getInput(0);
if (baseVal.isConstant()) {
// both constant, just use it and return the address
long value = baseVal.getOffset() + offsetVal.getOffset();
try {
value = applyDefUseList(value, defUseList);
constUse.put(remapAddress(funcEntry, def.getOutput().getPCAddress()),
value);
println(" " + function.getName() + " " +
def.getOutput().getPCAddress() + " : 0x" + Long.toHexString(value));
}
catch (InvalidInputException exc) {
// Do nothing
}
return;
}
// TODO: handle access into data structure
// DataType inDt = def.getInput(0).getHigh().getDataType();
// DataType subDt = def.getOutput().getHigh().getDataType();
// println (" type = " + subDt.getName());
// long subOff = getSigned(def.getInput(1));
// outDt = subDt;
// if (subDt instanceof Pointer) {
// outDt = ((Pointer) subDt).getDataType();
// if (outDt == null) {
// }
// }
// try {
// subOff = applyDefUseList(subOff, defUseList);
// constUse.put(def.getOutput().getPCAddress(), subOff);
// } catch (InvalidInputException exc) {}
break;
}
constUse.put(remapAddress(funcEntry, vnode.getPCAddress()), null);
println(" " + function.getName() + " " + vnode.getPCAddress() + " : Lost");
// println(" Lost IT! " + vnode.getPCAddress());
}
private void followThroughGlobal(HashMap<Address, Long> constUse, ArrayList<PcodeOp> defUseList,
HighVariable hvar,
ArrayList<FunctionParamUse> funcList,
HashSet<SequenceNumber> doneSet) {
Address loc = hvar.getRepresentative().getAddress();
PcodeOp def = hvar.getRepresentative().getDef();
SequenceNumber seqnum = null;
if (def != null) {
seqnum = def.getSeqnum();
if (doneSet.contains(seqnum)) {
return;
}
}
else {
seqnum = new SequenceNumber(loc, 0);
}
ReferenceIterator referencesTo = currentProgram.getReferenceManager().getReferencesTo(loc);
while (referencesTo.hasNext()) {
Reference reference = referencesTo.next();
if (!reference.getReferenceType().isWrite()) {
continue;
}
currentProgram.getFunctionManager().getFunctionContaining(reference.getFromAddress());
Address refAddr = reference.getFromAddress();
addErrorNote(refAddr, "Write to global variable");
}
doneSet.add(seqnum);
}
private Address remapAddress(Address funcEntry, Address address) {
if (funcEntry.getAddressSpace().isOverlaySpace()) {
address = funcEntry.getAddressSpace().getOverlayAddress(address);
}
return address;
}
// Decompiler stuff - cache some information about the last decompilation
private HighFunction hfunction = null;
private Address lastDecompiledFuncAddr = null;
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;
options = new DecompileOptions();
OptionsService service = state.getTool().getService(OptionsService.class);
if (service != null) {
ToolOptions opt = service.getOptions("Decompiler");
options.grabFromToolAndProgram(null, opt, program);
}
decompInterface.setOptions(options);
decompInterface.toggleCCode(true);
decompInterface.toggleSyntaxTree(true);
decompInterface.setSimplificationStyle("decompile");
return decompInterface;
}
public DecompileResults decompileFunction(Function f, DecompInterface decompInterface) {
// don't decompile the function again if it was the same as the last one
//
if (!f.getEntryPoint().equals(lastDecompiledFuncAddr)) {
lastResults = decompInterface.decompileFunction(f,
decompInterface.getOptions().getDefaultTimeout(), monitor);
}
hfunction = lastResults.getHighFunction();
lastDecompiledFuncAddr = f.getEntryPoint();
return lastResults;
}
}