GP-4512 Constant propagation and stack analysis performance changes

This commit is contained in:
emteere 2025-05-08 17:35:25 -04:00 committed by ghidra1
parent df505c40a3
commit c396867209
12 changed files with 1724 additions and 1167 deletions

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -73,13 +73,27 @@ public class CallDepthChangeInfo {
/** /**
* Construct a new CallDepthChangeInfo object. * Construct a new CallDepthChangeInfo object.
* Using this constructor will NOT track the stack depth at the start/end of each instruction.
*
* @param func function to examine * @param func function to examine
*/ */
public CallDepthChangeInfo(Function func) { public CallDepthChangeInfo(Function func) {
this(func, false);
}
/**
* Construct a new CallDepthChangeInfo object.
* Allows calls to getRegDepth() and getRegValueRepresentation()
*
* @param func function to examine
* @param storeDepthAtEachInstuction true to track stack at start/end of each instruction. allowing
* a call to
*/
public CallDepthChangeInfo(Function func, boolean storeDepthAtEachInstuction) {
this.program = func.getProgram(); this.program = func.getProgram();
frameReg = program.getCompilerSpec().getStackPointer(); frameReg = program.getCompilerSpec().getStackPointer();
try { try {
initialize(func, func.getBody(), frameReg, TaskMonitor.DUMMY); initialize(func, func.getBody(), frameReg, storeDepthAtEachInstuction, TaskMonitor.DUMMY);
} }
catch (CancelledException e) { catch (CancelledException e) {
throw new RuntimeException("Unexpected Exception", e); throw new RuntimeException("Unexpected Exception", e);
@ -88,10 +102,8 @@ public class CallDepthChangeInfo {
/** /**
* Construct a new CallDepthChangeInfo object. * Construct a new CallDepthChangeInfo object.
* @param func * @param func function to examine
* function to examine * @param monitor used to cancel the operation
* @param monitor
* monitor used to cancel the operation
* *
* @throws CancelledException * @throws CancelledException
* if the operation was canceled * if the operation was canceled
@ -102,6 +114,8 @@ public class CallDepthChangeInfo {
/** /**
* Construct a new CallDepthChangeInfo object. * Construct a new CallDepthChangeInfo object.
* Using this constructor will track the stack depth at the start/end of each instruction.
*
* @param function function to examine * @param function function to examine
* @param restrictSet set of addresses to restrict flow flowing to. * @param restrictSet set of addresses to restrict flow flowing to.
* @param frameReg register that is to have it's depth(value) change tracked * @param frameReg register that is to have it's depth(value) change tracked
@ -116,34 +130,18 @@ public class CallDepthChangeInfo {
if (frameReg == null) { if (frameReg == null) {
frameReg = program.getCompilerSpec().getStackPointer(); frameReg = program.getCompilerSpec().getStackPointer();
} }
initialize(function, restrictSet, frameReg, monitor); // track start/end values at each instruction
initialize(function, restrictSet, frameReg, true, monitor);
} }
/**
* Construct a new CallDepthChangeInfo object.
*
* @param program program containing the function to examime
* @param addr address within the function to examine
* @param restrictSet set of addresses to restrict flow flowing to.
* @param frameReg register that is to have it's depth(value) change tracked
* @param monitor monitor used to cancel the operation
* @throws CancelledException
* if the operation was canceled
*/
public CallDepthChangeInfo(Program program, Address addr, AddressSetView restrictSet,
Register frameReg, TaskMonitor monitor) throws CancelledException {
Function func = program.getFunctionManager().getFunctionContaining(addr);
Register stackPtrReg = program.getCompilerSpec().getStackPointer();
initialize(func, restrictSet, stackPtrReg, monitor);
}
private void initialize(Function func, AddressSetView restrictSet, Register reg, private void initialize(Function func, AddressSetView restrictSet, Register reg,
TaskMonitor monitor) throws CancelledException { boolean storeDepthAtEachInstuction, TaskMonitor monitor) throws CancelledException {
changeMap = new DefaultIntPropertyMap("change"); changeMap = new DefaultIntPropertyMap("change");
depthMap = new DefaultIntPropertyMap("depth"); depthMap = new DefaultIntPropertyMap("depth");
trans = new VarnodeTranslator(program); trans = new VarnodeTranslator(program);
symEval = new SymbolicPropogator(program); symEval = new SymbolicPropogator(program,storeDepthAtEachInstuction);
symEval.setParamRefCheck(false); symEval.setParamRefCheck(false);
symEval.setReturnRefCheck(false); symEval.setReturnRefCheck(false);
symEval.setStoredRefCheck(false); symEval.setStoredRefCheck(false);
@ -494,18 +492,14 @@ public class CallDepthChangeInfo {
@Override @Override
public boolean evaluateContextBefore(VarnodeContext context, Instruction instr) { public boolean evaluateContextBefore(VarnodeContext context, Instruction instr) {
Varnode stackRegVarnode = context.getRegisterVarnode(frameReg); Varnode stackRegVarnode = context.getRegisterVarnode(frameReg);
Varnode stackValue = null; Varnode stackValue = context.getValue(stackRegVarnode, true, this);
try {
stackValue = context.getValue(stackRegVarnode, true, this);
if (stackValue != null && context.isSymbol(stackValue) && if (stackValue != null && context.isSymbol(stackValue) &&
context.isStackSymbolicSpace(stackValue)) { context.isStackSymbolicSpace(stackValue)) {
int stackPointerDepth = (int) stackValue.getOffset(); long stackPointerDepth = stackValue.getOffset();
setDepth(instr, stackPointerDepth); int size = stackValue.getSize();
} stackPointerDepth = (stackPointerDepth << 8 * (8 - size)) >> 8 * (8 - size);
} setDepth(instr, (int) stackPointerDepth);
catch (NotFoundException e) {
// ignore
} }
return false; return false;
@ -621,25 +615,14 @@ public class CallDepthChangeInfo {
return getRegDepth(addr, stackReg); return getRegDepth(addr, stackReg);
} }
/** /** Get the stack register depth at address.
* To have a valid value, the class must be constructed to storeDepthAtEachInstuction
*
* @param addr the address to get the register depth at. * @param addr the address to get the register depth at.
* @param reg the register to get the depth of. * @param reg the register to get the depth of.
* @return the depth of the register at the address. * @return the depth of the register at the address.
*/ */
public int getRegDepth(Address addr, Register reg) { public int getRegDepth(Address addr, Register reg) {
// OK lets CHEAT...
// Since single instructions will give the wrong value,
// get the value as of the end of the last instruction that fell into this one!
Instruction instr = this.program.getListing().getInstructionAt(addr);
if (instr != null && instr.getLength() < 2) {
Address fallAddr = instr.getFallFrom();
if (fallAddr != null) {
addr = fallAddr;
}
// just in case this instruction falling from is bigger than 1 byte
instr = program.getListing().getInstructionAt(addr);
addr = instr.getMaxAddress();
}
Value rValue = symEval.getRegisterValue(addr, reg); Value rValue = symEval.getRegisterValue(addr, reg);
if (rValue == null) { if (rValue == null) {
return Function.INVALID_STACK_DEPTH_CHANGE; return Function.INVALID_STACK_DEPTH_CHANGE;
@ -657,6 +640,11 @@ public class CallDepthChangeInfo {
} }
/** /**
* Get the stack register value as a printable string. This can be an equation
* of register+value.
*
* To have a valid value, the class must be constructed to storeDepthAtEachInstuction
*
* @param addr the address of the register value to get the representation of. * @param addr the address of the register value to get the representation of.
* @param reg the register to get the representation of. * @param reg the register to get the representation of.
* @return the string representation of the register value. * @return the string representation of the register value.

View file

@ -43,6 +43,8 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
private static final int MAX_PARAM_OFFSET = 2048; // max size of param reference space private static final int MAX_PARAM_OFFSET = 2048; // max size of param reference space
private static final int MAX_LOCAL_OFFSET = -(64 * 1024); // max size of local reference space private static final int MAX_LOCAL_OFFSET = -(64 * 1024); // max size of local reference space
private final static String X86_NAME = "x86";
private boolean dontCreateNewVariables = false; private boolean dontCreateNewVariables = false;
private final boolean forceProcessing; private final boolean forceProcessing;
@ -53,6 +55,8 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
private Program program; private Program program;
private Register stackReg; private Register stackReg;
private int purge = 0; private int purge = 0;
private boolean isX86 = false;
static String DEFAULT_FUNCTION_COMMENT = " FUNCTION"; static String DEFAULT_FUNCTION_COMMENT = " FUNCTION";
@ -101,6 +105,8 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
@Override @Override
public boolean applyTo(Program p, TaskMonitor monitor) { public boolean applyTo(Program p, TaskMonitor monitor) {
program = p; program = p;
isX86 = checkForX86(p);
int count = 0; int count = 0;
long numAddresses = entryPoints.getNumAddresses(); long numAddresses = entryPoints.getNumAddresses();
@ -138,6 +144,11 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
return true; return true;
} }
private boolean checkForX86(Program p) {
return program.getLanguage().getProcessor().equals(
Processor.findOrPossiblyCreateProcessor(X86_NAME)) && program.getDefaultPointerSize() <= 32;
}
/** /**
* Analyze a function to build a stack frame based on stack references. * Analyze a function to build a stack frame based on stack references.
* *
@ -245,7 +256,7 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
* *
* @param func - function to analyze stack pointer references * @param func - function to analyze stack pointer references
*/ */
private int createStackPointerVariables(Function func, TaskMonitor monitor) private int createStackPointerVariables(final Function func, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
// check if this is a jump thunk through a function pointer // check if this is a jump thunk through a function pointer
// if (checkThunk(func, monitor)) { // if (checkThunk(func, monitor)) {
@ -271,7 +282,7 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
@Override @Override
public boolean evaluateContext(VarnodeContext context, Instruction instr) { public boolean evaluateContext(VarnodeContext context, Instruction instr) {
if (instr.getFlowType().isTerminal()) { if (instr.getFlowType().isTerminal()) {
RegisterValue value = context.getRegisterValue(stackReg, instr.getMaxAddress()); RegisterValue value = context.getRegisterValue(stackReg);
if (value != null) { if (value != null) {
BigInteger signedValue = value.getSignedValue(); BigInteger signedValue = value.getSignedValue();
if (signedValue != null) { if (signedValue != null) {
@ -279,7 +290,7 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
} }
} }
} }
if (instr.getMnemonicString().equals("LEA")) { if (isX86 && instr.getMnemonicString().equals("LEA")) {
Register destReg = instr.getRegister(0); Register destReg = instr.getRegister(0);
if (destReg != null) { if (destReg != null) {
Varnode value = context.getRegisterVarnodeValue(destReg); Varnode value = context.getRegisterVarnodeValue(destReg);
@ -320,18 +331,17 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
if (opIndex == -1) { if (opIndex == -1) {
return; return;
} }
// if restoring the same value into the register, don't record the reference
// TODO: Dirty Dirty nasty Hack for POP EBP problem, only very few cases of this! // TODO: Dirty Dirty nasty Hack for POP EBP problem, only very few cases of this!
if (instr.getMnemonicString().equals("POP")) { if (isX86 && instr.getMnemonicString().equals("POP")) {
Register reg = instr.getRegister(opIndex); Register reg = instr.getRegister(opIndex);
if (reg != null && reg.getName().contains("BP")) { if (reg != null && reg.getName().contains("BP")) {
return; return;
} }
} }
long extendedOffset = long extendedOffset = extendOffset(address.getOffset(), stackReg.getBitLength());
extendOffset(address.getOffset(), stackReg.getBitLength());
Function func = defineFuncVariable(symEval, func, instr, opIndex, (int) extendedOffset, sortedVariables);
program.getFunctionManager().getFunctionContaining(instr.getMinAddress());
defineFuncVariable(func, instr, opIndex, (int) extendedOffset, sortedVariables);
} }
} }
@ -342,9 +352,10 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
}; };
// set the stack pointer to be tracked // set the stack pointer to be tracked
symEval.setRegister(func.getEntryPoint(), stackReg); Address entryPoint = func.getEntryPoint();
symEval.setRegister(entryPoint, stackReg);
symEval.flowConstants(func.getEntryPoint(), func.getBody(), eval, true, monitor); symEval.flowConstants(entryPoint, func.getBody(), eval, true, monitor);
if (sortedVariables.size() != 0) { if (sortedVariables.size() != 0) {
@ -623,11 +634,11 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
// return true; // return true;
// } // }
private void defineFuncVariable(Function func, Instruction instr, int opIndex, int stackOffset, private void defineFuncVariable(SymbolicPropogator symEval, Function func, Instruction instr, int opIndex, int stackOffset,
List<Variable> sortedVariables) { List<Variable> sortedVariables) {
ReferenceManager refMgr = program.getReferenceManager(); ReferenceManager refMgr = program.getReferenceManager();
int refSize = getRefSize(instr, opIndex); int refSize = getRefSize(symEval, instr, opIndex);
try { try {
// don't create crazy offsets // don't create crazy offsets
if (stackOffset > MAX_PARAM_OFFSET || stackOffset < MAX_LOCAL_OFFSET) { if (stackOffset > MAX_PARAM_OFFSET || stackOffset < MAX_LOCAL_OFFSET) {
@ -660,15 +671,16 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
/** /**
* Look at the result register to try and figure out stack access size. * Look at the result register to try and figure out stack access size.
* @param symEval
* *
* @param instr instruction being analyzed * @param instr instruction being analyzed
* @param opIndex operand that has a stack reference. * @param opIndex operand that has a stack reference.
* *
* @return size of value referenced on the stack * @return size of value referenced on the stack
*/ */
private int getRefSize(Instruction instr, int opIndex) { private int getRefSize(SymbolicPropogator symEval, Instruction instr, int opIndex) {
if (instr.getProgram().getLanguage().supportsPcode()) { if (instr.getProgram().getLanguage().supportsPcode()) {
PcodeOp[] pcode = instr.getPcode(); PcodeOp[] pcode = symEval.getInstructionPcode(instr);
for (int i = pcode.length - 1; i >= 0; i--) { for (int i = pcode.length - 1; i >= 0; i--) {
if (pcode[i].getOpcode() == PcodeOp.LOAD) { if (pcode[i].getOpcode() == PcodeOp.LOAD) {
Varnode out = pcode[i].getOutput(); Varnode out = pcode[i].getOutput();
@ -810,7 +822,7 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand<Program> {
offset = minOffset; offset = minOffset;
} }
else { else {
dt = Undefined.getUndefinedDataType(size); dt = Undefined.getUndefinedDataType(refSize);
} }
Variable var; Variable var;

View file

@ -108,7 +108,6 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
final static HashSet<String> handledProcessors = new HashSet<String>(); final static HashSet<String> handledProcessors = new HashSet<String>();
protected String processorName = "Basic"; protected String processorName = "Basic";
protected AddressSetView EMPTY_ADDRESS_SET = new AddressSet();
public ConstantPropagationAnalyzer() { public ConstantPropagationAnalyzer() {
this("Basic"); this("Basic");
@ -189,7 +188,8 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
int locationCount = locations.size(); int locationCount = locations.size();
monitor.initialize(locationCount); monitor.initialize(locationCount);
if (locationCount != 0) { if (locationCount != 0) {
AddressSetView resultSet = runAddressAnalysis(program, locations, monitor); monitor.setMessage(getName());
AddressSetView resultSet = runParallelAddressAnalysis(program, locations, null, maxThreadCount, monitor);
// get rid of any reached addresses // get rid of any reached addresses
unanalyzedSet.delete(resultSet); unanalyzedSet.delete(resultSet);
} }

View file

@ -15,21 +15,32 @@
*/ */
package ghidra.app.plugin.core.function; package ghidra.app.plugin.core.function;
import java.util.*;
import ghidra.app.cmd.function.FunctionStackAnalysisCmd; import ghidra.app.cmd.function.FunctionStackAnalysisCmd;
import ghidra.app.cmd.function.NewFunctionStackAnalysisCmd; import ghidra.app.cmd.function.NewFunctionStackAnalysisCmd;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.framework.cmd.BackgroundCommand; import ghidra.framework.cmd.BackgroundCommand;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.*;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.Msg;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
public class StackVariableAnalyzer extends AbstractAnalyzer { public class StackVariableAnalyzer extends AbstractAnalyzer {
private static final String NAME = "Stack"; private static final String NAME = "Stack";
private static final String DESCRIPTION = "Creates stack variables for a function."; private static final String DESCRIPTION = "Creates stack variables for a function.";
protected static final String MAX_THREAD_COUNT_OPTION_NAME = "Max Threads";
protected static final String MAX_THREAD_COUNT_OPTION_DESCRIPTION =
"Maximum threads for stack variable reference creation. Too many threads causes thrashing in DB.";
protected static final int MAX_THREAD_COUNT_OPTION_DEFAULT_VALUE = 2;
protected int maxThreadCount = MAX_THREAD_COUNT_OPTION_DEFAULT_VALUE;
private boolean doNewStackAnalysis = true; private boolean doNewStackAnalysis = true;
private boolean doCreateLocalStackVars = true; private boolean doCreateLocalStackVars = true;
private boolean doCreateStackParams = false; private boolean doCreateStackParams = false;
@ -42,37 +53,88 @@ public class StackVariableAnalyzer extends AbstractAnalyzer {
} }
@Override @Override
public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) { public boolean added(Program program, AddressSetView set, TaskMonitor monitor, MessageLog log) throws CancelledException {
BackgroundCommand<Program> cmd; BackgroundCommand<Program> cmd;
// first split out all the function locations, make those the starts
// remove those from the bodies from the given set of addresses
Set<Address> locations = new HashSet<Address>();
findDefinedFunctions(program, set, locations, monitor);
int locationCount = locations.size();
monitor.initialize(locationCount);
try {
monitor.setMessage(getName());
AddressSetView resultSet = runParallelAddressAnalysis(program, locations, null, maxThreadCount, monitor);
}
catch (Exception e) {
Msg.error(this, "caught exception", e);
e.printStackTrace();
}
return true;
}
@Override
public AddressSetView analyzeLocation(final Program program, Address start, AddressSetView set,
final TaskMonitor monitor) throws CancelledException {
BackgroundCommand<Program> cmd;
if (doNewStackAnalysis) { if (doNewStackAnalysis) {
cmd = new NewFunctionStackAnalysisCmd(set, doCreateStackParams, doCreateLocalStackVars, cmd = new NewFunctionStackAnalysisCmd(new AddressSet(start, start), doCreateStackParams, doCreateLocalStackVars,
false); false);
} }
else { else {
cmd = new FunctionStackAnalysisCmd(set, doCreateStackParams, doCreateLocalStackVars, cmd = new FunctionStackAnalysisCmd(new AddressSet(start, start), doCreateStackParams, doCreateLocalStackVars,
false); false);
} }
cmd.applyTo(program, monitor); cmd.applyTo(program, monitor);
return true;
return EMPTY_ADDRESS_SET;
}
/**
* Find function locations and adding the function entry points to locations
*
* @param program program
* @param set remove known function bodies from the set, leave entry points
* @param locations set of known function start addresses
* @param monitor to cancel
* @throws CancelledException if cancelled
*/
protected void findDefinedFunctions(Program program, AddressSetView set,
Set<Address> locations, TaskMonitor monitor) throws CancelledException {
monitor.setMessage("Finding function locations...");
long total = set.getNumAddresses();
monitor.initialize(total);
// iterate over functions in program
// add each defined function start to the list
// return the address set that is minus the bodies of each function
Iterator<Function> fiter = program.getFunctionManager().getFunctionsOverlapping(set);
while (fiter.hasNext()) {
monitor.checkCancelled();
Function function = fiter.next();
locations.add(function.getEntryPoint());
}
} }
private boolean useOldStackAnalysisByDefault(Program program) { // private boolean useOldStackAnalysisByDefault(Program program) {
Language language = program.getLanguage(); // Language language = program.getLanguage();
if (language.getProcessor().equals(Processor.findOrPossiblyCreateProcessor("x86"))) { // if (language.getProcessor().equals(Processor.findOrPossiblyCreateProcessor("x86"))) {
if (language.getLanguageDescription().getSize() == 16) { // if (language.getLanguageDescription().getSize() == 16) {
// Prefer using old stack analysis for x86 16-bit with segmented addresses // // Prefer using old stack analysis for x86 16-bit with segmented addresses
return true; // return true;
} // }
} // }
return false; // return false;
} // }
@Override @Override
public void registerOptions(Options options, Program program) { public void registerOptions(Options options, Program program) {
options.registerOption(GhidraLanguagePropertyKeys.USE_NEW_FUNCTION_STACK_ANALYSIS, options.registerOption(GhidraLanguagePropertyKeys.USE_NEW_FUNCTION_STACK_ANALYSIS,
!useOldStackAnalysisByDefault(program), null, true, null,
"Use General Stack Reference Propogator (This works best on most processors)"); "Use General Stack Reference Propogator (This works best on most processors)");
options.registerOption("Create Local Variables", doCreateLocalStackVars, null, options.registerOption("Create Local Variables", doCreateLocalStackVars, null,
@ -80,18 +142,23 @@ public class StackVariableAnalyzer extends AbstractAnalyzer {
options.registerOption("Create Param Variables", doCreateStackParams, null, options.registerOption("Create Param Variables", doCreateStackParams, null,
"Create Function Parameter stack variables and references"); "Create Function Parameter stack variables and references");
options.registerOption(MAX_THREAD_COUNT_OPTION_NAME, maxThreadCount, null,
MAX_THREAD_COUNT_OPTION_DESCRIPTION);
} }
@Override @Override
public void optionsChanged(Options options, Program program) { public void optionsChanged(Options options, Program program) {
doNewStackAnalysis = doNewStackAnalysis =
options.getBoolean(GhidraLanguagePropertyKeys.USE_NEW_FUNCTION_STACK_ANALYSIS, options.getBoolean(GhidraLanguagePropertyKeys.USE_NEW_FUNCTION_STACK_ANALYSIS,
!useOldStackAnalysisByDefault(program)); true);
doCreateLocalStackVars = doCreateLocalStackVars =
options.getBoolean("Create Local Variables", doCreateLocalStackVars); options.getBoolean("Create Local Variables", doCreateLocalStackVars);
doCreateStackParams = options.getBoolean("Create Param Variables", doCreateStackParams); doCreateStackParams = options.getBoolean("Create Param Variables", doCreateStackParams);
maxThreadCount = options.getInt(MAX_THREAD_COUNT_OPTION_NAME, maxThreadCount);
} }
} }

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,9 +15,13 @@
*/ */
package ghidra.app.services; package ghidra.app.services;
import java.util.Set;
import generic.concurrent.*;
import ghidra.app.plugin.core.analysis.AutoAnalysisManager;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.Options; import ghidra.framework.options.Options;
import ghidra.program.model.address.AddressSetView; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -30,6 +34,8 @@ public abstract class AbstractAnalyzer implements Analyzer {
private boolean supportsOneTimeAnalysis; private boolean supportsOneTimeAnalysis;
private boolean isPrototype = false; private boolean isPrototype = false;
private AnalysisPriority priority = AnalysisPriority.LOW_PRIORITY; private AnalysisPriority priority = AnalysisPriority.LOW_PRIORITY;
protected static final AddressSetView EMPTY_ADDRESS_SET = new AddressSetViewAdapter();
protected AbstractAnalyzer(String name, String description, AnalyzerType type) { protected AbstractAnalyzer(String name, String description, AnalyzerType type) {
this.name = name; this.name = name;
@ -117,5 +123,96 @@ public abstract class AbstractAnalyzer implements Analyzer {
public void registerOptions(Options options, Program program) { public void registerOptions(Options options, Program program) {
// do nothing // do nothing
} }
/**
* Analyze a single location
*
* @param program - program to analyze
* @param start - location to start flowing constants
* @param set - restriction set of addresses to analyze
* @param monitor - monitor to check canceled
*
* @return - set of addresses actually flowed to
* @throws CancelledException
*/
public AddressSetView analyzeLocation(final Program program, Address start, AddressSetView set,
final TaskMonitor monitor) throws CancelledException {
return EMPTY_ADDRESS_SET;
}
/**
* Run constant an analysis at each location in parallel
*
* @param program program
* @param locations points to analyze
* @param restrictedSet set to restrict analysis to, null if none
* @param maxThreads maximum number of threads to use
* @param monitor to cancel
* @return set of addresses covered during analysis
*
* @throws CancelledException if cancelled
* @throws InterruptedException if interrupted
* @throws Exception any exception
*/
protected AddressSetView runParallelAddressAnalysis(final Program program, final Set<Address> locations, final AddressSetView restrictedSet, int maxThreads,
final TaskMonitor monitor) throws CancelledException, InterruptedException, Exception {
monitor.checkCancelled();
final AddressSet analyzedSet = new AddressSet();
if (locations.isEmpty()) {
return analyzedSet;
}
GThreadPool pool = AutoAnalysisManager.getSharedAnalsysThreadPool();
monitor.setMaximum(locations.size());
QCallback<Address, AddressSetView> callback = new QCallback<Address, AddressSetView>() {
@Override
public AddressSetView process(Address loc, TaskMonitor taskMonitor) {
synchronized (analyzedSet) {
if (analyzedSet.contains(loc)) {
taskMonitor.incrementProgress(1);
return EMPTY_ADDRESS_SET;
}
}
try {
AddressSetView result = analyzeLocation(program, loc, restrictedSet, taskMonitor);
synchronized (analyzedSet) {
analyzedSet.add(result);
}
taskMonitor.incrementProgress(1);
return result;
}
catch (CancelledException e) {
return null; // monitor was cancelled
}
}
};
// bound check thread limit
if (maxThreads > pool.getMaxThreadCount()) {
maxThreads = pool.getMaxThreadCount();
}
if (maxThreads < 1) {
maxThreads = 1;
}
// @formatter:off
ConcurrentQ<Address, AddressSetView> queue = new ConcurrentQBuilder<Address, AddressSetView>()
.setThreadPool(pool)
.setMaxInProgress(maxThreads)
.setMonitor(monitor)
.build(callback);
// @formatter:on
queue.add(locations);
queue.waitUntilDone();
return analyzedSet;
}
} }

View file

@ -155,126 +155,133 @@ public class ConstantPropogationReferenceTest extends AbstractGenericTest {
return super.evaluateContextBefore(context, instr); return super.evaluateContextBefore(context, instr);
} }
private Varnode regValue(VarnodeContext context, String regName) {
return context.getRegisterVarnodeValue(context.getRegister(regName));
}
@Override @Override
public boolean evaluateContext(VarnodeContext context, Instruction instr) { public boolean evaluateContext(VarnodeContext context, Instruction instr) {
String loc = instr.getMinAddress().toString(); String loc = instr.getMinAddress().toString();
Varnode registerVarnode; Varnode registerVarnode;
Long constVal;
boolean isBad = false;
switch (loc) { switch(loc) {
case "00001010": case "00001010":
// gp should be 0x14 + t9 offset space // gp should be 0x14 + t9 offset space
registerVarnode = regValue(context, "gp"); registerVarnode = regValue(context,"gp");
assertTrue("symbolic value", context.isSymbol(registerVarnode)); assertTrue("symbolic value", context.isSymbol(registerVarnode));
assertEquals("(t9, 0x13b334, 4)", registerVarnode.toString()); assertEquals("(t9, 0x13b334, 4)", registerVarnode.toString());
// S3 should be S3 at entry // S3 should be S3 at entry
registerVarnode = regValue(context, "s3"); registerVarnode = regValue(context,"s3");
assertTrue("register s3", context.isRegister(registerVarnode)); assertTrue("register s3", context.isRegister(registerVarnode));
assertEquals("s3", context.getRegister(registerVarnode).getName()); assertEquals("s3", context.getRegister(registerVarnode).getName());
break; break;
case "0000102c": case "0000102c":
// s1 restored from space 0x10(s0) space // s1 restored from space 0x10(s0) space
registerVarnode = regValue(context, "s1"); registerVarnode = regValue(context,"s1");
assertTrue("constant value", registerVarnode.isConstant()); assertTrue("constant value", registerVarnode.isConstant());
assertEquals("(const, 0xa0b0c0d, 4)", registerVarnode.toString()); assertEquals("(const, 0xa0b0c0d, 4)", registerVarnode.toString());
break; break;
case "00001030": case "00001030":
// s1 restored from space 0x10(a1) space // s1 restored from space 0x10(a1) space
registerVarnode = regValue(context, "s1"); registerVarnode = regValue(context,"s1");
assertTrue("symbolic value", registerVarnode.isConstant()); assertTrue("symbolic value", registerVarnode.isConstant());
assertEquals("(const, 0x12344567, 4)", registerVarnode.toString()); assertEquals("(const, 0x12344567, 4)", registerVarnode.toString());
break; break;
case "00001034": case "00001034":
// s1 restored from space 0x10(sp) space // s1 restored from space 0x10(sp) space
registerVarnode = regValue(context, "s1"); registerVarnode = regValue(context,"s1");
assertTrue("symbolic value", context.isSymbol(registerVarnode)); assertTrue("symbolic value", context.isSymbol(registerVarnode));
assertEquals("(t9, 0x13b334, 4)", registerVarnode.toString()); assertEquals("(t9, 0x13b334, 4)", registerVarnode.toString());
break; break;
case "00001038": case "00001038":
// s1 restored from space 0x10(s2) space // s1 restored from space 0x10(s2) space
registerVarnode = regValue(context, "s1"); registerVarnode = regValue(context,"s1");
//assertTrue("Still s1", registerVarnode.isRegister()); //assertTrue("Still s1", registerVarnode.isRegister());
boolean isBad = false; constVal = context.getConstant(registerVarnode, null);
try { isBad = constVal == null;
context.getConstant(registerVarnode, null); assertTrue("Can get constant value", isBad);
} break;
catch (NotFoundException e) { case "00001040":
isBad = true; // s1 restored from space 0x10(s2) space - stored a3
} registerVarnode = regValue(context,"s1");
assertTrue("Can get constant value", isBad); assertTrue("register s3", registerVarnode.isRegister());
break; assertEquals("s3", context.getRegister(registerVarnode).getName());
case "00001040":
// s1 restored from space 0x10(s2) space - stored a3 Address lastSetLocation = context.getLastSetLocation(context.getRegisterVarnode(context.getRegister("s2")), null);
registerVarnode = regValue(context, "s1"); assertEquals("s2 last set", null, lastSetLocation);
assertTrue("register s3", registerVarnode.isRegister()); break;
assertEquals("s3", context.getRegister(registerVarnode).getName()); case "00001048":
// s1 restored from space 0x10(s2) after s2 has been set again
Address lastSetLocation = context.getLastSetLocation( // it should no longer be s3 that was stored in another s2 relative space
context.getRegisterVarnode(context.getRegister("s2")), null); registerVarnode = regValue(context,"s1");
assertEquals("s2 last set", null, lastSetLocation); //assertTrue("Still s1", registerVarnode.isRegister());
break; constVal = context.getConstant(registerVarnode, null);
case "00001048": isBad = constVal == null;
// s1 restored from space 0x10(s2) after s2 has been set again assertTrue("Can get constant value", isBad);
// it should no longer be s3 that was stored in another s2 relative space break;
registerVarnode = regValue(context, "s1"); case "0000104c":
//assertTrue("Still s1", registerVarnode.isRegister()); // s1 restored from space 0x10(s2) after s2 has been set again
isBad = false; // it should no longer be s3 that was stored in another s2 relative space
try { registerVarnode = regValue(context,"s2");
context.getConstant(registerVarnode, null); //assertTrue("Still s2", registerVarnode.isRegister());
} constVal = context.getConstant(registerVarnode, null);
catch (NotFoundException e) { isBad = constVal == null;
isBad = true; assertTrue("Can get constant value", isBad);
} lastSetLocation = context.getLastSetLocation(context.getRegisterVarnode(context.getRegister("s2")), null);
assertTrue("Can get constant value", isBad); assertEquals("s2 last set", 0x104cL, lastSetLocation.getOffset());
break; break;
case "0000104c": case "00001054":
// s1 restored from space 0x10(s2) after s2 has been set again // s1 restored from space 0x10(s2) after s2 has been set again
// it should no longer be s3 that was stored in another s2 relative space // it should no longer be s3 that was stored in another s2 relative space
registerVarnode = regValue(context, "s2"); registerVarnode = regValue(context,"s1");
//assertTrue("Still s2", registerVarnode.isRegister()); //assertTrue("Still s1", registerVarnode.isRegister());
isBad = false; //assertEquals(context.getRegister(registerVarnode).getName(),"s1");
try { constVal = context.getConstant(registerVarnode, null);
context.getConstant(registerVarnode, null); isBad = constVal == null;
} assertTrue("Can get constant value", isBad);
catch (NotFoundException e) { break;
isBad = true;
}
assertTrue("Can get constant value", isBad);
lastSetLocation = context.getLastSetLocation(
context.getRegisterVarnode(context.getRegister("s2")), null);
assertEquals("s2 last set", 0x104fL, lastSetLocation.getOffset());
break;
case "00001054":
// s1 restored from space 0x10(s2) after s2 has been set again
// it should no longer be s3 that was stored in another s2 relative space
registerVarnode = regValue(context, "s1");
//assertTrue("Still s1", registerVarnode.isRegister());
//assertEquals(context.getRegister(registerVarnode).getName(),"s1");
isBad = false;
try {
context.getConstant(registerVarnode, null);
}
catch (NotFoundException e) {
isBad = true;
}
assertTrue("Can get constant value", isBad);
break;
} }
return super.evaluateContext(context, instr); return super.evaluateContext(context, instr);
} }
}; };
setRegister(addr("0x1000"), "s1", 0); setRegister(addr("0x1000"), "s1", 0);
SymbolicPropogator symEval = new SymbolicPropogator(program); SymbolicPropogator symEval = new SymbolicPropogator(program,true);
Function func = program.getFunctionManager().getFunctionAt(builder.addr(0x1000)); Function func = program.getFunctionManager().getFunctionAt(builder.addr(0x1000));
symEval.flowConstants(codeStart, func.getBody(), eval, true, TaskMonitor.DUMMY); symEval.flowConstants(codeStart, func.getBody(), eval, true, TaskMonitor.DUMMY);
Value registerValue = symEval.getRegisterValue(addr("0x1010"), null); Value registerValue;
// get gp at start of instruction
registerValue = regValue(symEval, addr("0x1010"), "gp");
assertNotNull(registerValue);
assertTrue(registerValue.isRegisterRelativeValue());
assertEquals(registerValue.getRelativeRegister().getName(), "t9");
assertEquals(registerValue.getValue(), 0x13b334L);
// get s1 at start of instruction
registerValue = regValue(symEval, addr("0x1034"), "s1");
assertNotNull(registerValue);
assertFalse(registerValue.isRegisterRelativeValue());
assertEquals(registerValue.getValue(), 0x12344567L);
// get s1 at end of instruction
registerValue = regEndValue(symEval, addr("0x1034"), "s1");
assertNotNull(registerValue);
assertTrue(registerValue.isRegisterRelativeValue());
assertEquals(registerValue.getRelativeRegister().getName(), "t9");
assertEquals(registerValue.getValue(), 0x13b334L);
// get sp at end of instruction
registerValue = regValue(symEval, addr("0x1058"), "sp");
assertNotNull(registerValue);
assertTrue(registerValue.isRegisterRelativeValue());
assertEquals(registerValue.getRelativeRegister().getName(), "sp");
assertEquals(-32, registerValue.getValue());
// TODO: should be 0 at end
// registerValue = regEndValue(symEval, addr("0x1058"), "sp");
// assertEquals(0, registerValue.getValue());
} }
@Test @Test
@ -542,6 +549,22 @@ public class ConstantPropogationReferenceTest extends AbstractGenericTest {
Register gp = context.getRegister(regname); Register gp = context.getRegister(regname);
context.setRegisterValue(a, a.add(12), new RegisterValue(gp, BigInteger.valueOf(value))); context.setRegisterValue(a, a.add(12), new RegisterValue(gp, BigInteger.valueOf(value)));
} }
private Varnode regValue(VarnodeContext context, String regName) {
return context.getRegisterVarnodeValue(context.getRegister(regName));
}
private Value regValue(SymbolicPropogator symEval, Address addr, String regName) {
ProgramContext context = program.getProgramContext();
Register reg = context.getRegister(regName);
return symEval.getRegisterValue(addr, reg);
}
private Value regEndValue(SymbolicPropogator symEval, Address addr, String regName) {
ProgramContext context = program.getProgramContext();
Register reg = context.getRegister(regName);
return symEval.getEndRegisterValue(addr, reg);
}
private Address addr(String address) { private Address addr(String address) {
return builder.addr(address); return builder.addr(address);

View file

@ -70,6 +70,16 @@ public class VarnodeTranslator {
Varnode node = new Varnode(register.getAddress(), register.getMinimumByteSize()); Varnode node = new Varnode(register.getAddress(), register.getMinimumByteSize());
return node; return node;
} }
/**
* Get register given a register name
*
* @param name register name
* @return register
*/
public Register getRegister(String name) {
return language.getRegister(name);
}
/** /**
* Get all defined registers for the program this translator was created * Get all defined registers for the program this translator was created

View file

@ -73,30 +73,25 @@ public class Motorola68KAnalyzer extends ConstantPropagationAnalyzer {
if (mnemonic.equals("pea")) { if (mnemonic.equals("pea")) {
// retrieve the value pushed onto the stack // retrieve the value pushed onto the stack
try { Varnode stackValue = context.getValue(context.getStackVarnode(), this);
Varnode stackValue = context.getValue(context.getStackVarnode(), this); Varnode value = context.getValue(stackValue, this);
Varnode value = context.getValue(stackValue, this); if (value != null && value.isConstant()) {
if (value != null && value.isConstant()) { long lval = value.getOffset();
long lval = value.getOffset(); Address refAddr = instr.getMinAddress().getNewAddress(lval);
Address refAddr = instr.getMinAddress().getNewAddress(lval); if (lval <= 4096 || ((lval % 1024) == 0) || lval < 0 ||
if (lval <= 4096 || ((lval % 1024) == 0) || lval < 0 || lval == 0xffff || lval == 0xff00 || lval == 0xffffff ||
lval == 0xffff || lval == 0xff00 || lval == 0xffffff || lval == 0xff0000 || lval == 0xff00ff || lval == 0xffffffff ||
lval == 0xff0000 || lval == 0xff00ff || lval == 0xffffffff || lval == 0xffffff00 || lval == 0xffff0000 ||
lval == 0xffffff00 || lval == 0xffff0000 || lval == 0xff000000) {
lval == 0xff000000) { return false;
return false; }
} if (program.getMemory().contains(refAddr)) {
if (program.getMemory().contains(refAddr)) { if (instr.getOperandReferences(0).length == 0) {
if (instr.getOperandReferences(0).length == 0) { instr.addOperandReference(0, refAddr, RefType.DATA,
instr.addOperandReference(0, refAddr, RefType.DATA, SourceType.ANALYSIS);
SourceType.ANALYSIS);
}
} }
} }
} }
catch (NotFoundException e) {
// value not found doesn't matter
}
} }
if (mnemonic.equals("lea")) { if (mnemonic.equals("lea")) {
Register destReg = instr.getRegister(1); Register destReg = instr.getRegister(1);

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -799,7 +799,7 @@ public class ArmAnalyzer extends ConstantPropagationAnalyzer {
if (tmodeRegister != null && listing.getUndefinedDataAt(addr) != null) { if (tmodeRegister != null && listing.getUndefinedDataAt(addr) != null) {
boolean inThumbMode = false; boolean inThumbMode = false;
RegisterValue curvalue = RegisterValue curvalue =
context.getRegisterValue(tmodeRegister, instruction.getMinAddress()); context.getRegisterValue(tmodeRegister);
if (curvalue != null && curvalue.hasValue()) { if (curvalue != null && curvalue.hasValue()) {
inThumbMode = (curvalue.getUnsignedValue().intValue() == 1); inThumbMode = (curvalue.getUnsignedValue().intValue() == 1);
} }

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -552,7 +552,7 @@ public class MipsAddressAnalyzer extends ConstantPropagationAnalyzer {
if (isamode != null && listing.getUndefinedDataAt(addr) != null) { if (isamode != null && listing.getUndefinedDataAt(addr) != null) {
boolean inM16Mode = false; boolean inM16Mode = false;
RegisterValue curvalue = context.getRegisterValue(isamode, instruction.getMinAddress()); RegisterValue curvalue = context.getRegisterValue(isamode);
if (curvalue != null && curvalue.hasValue()) { if (curvalue != null && curvalue.hasValue()) {
inM16Mode = (curvalue.getUnsignedValue().intValue() == 1); inM16Mode = (curvalue.getUnsignedValue().intValue() == 1);
} }