diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CallDepthChangeInfo.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CallDepthChangeInfo.java index df9c15ee7c..35c91e44e8 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CallDepthChangeInfo.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CallDepthChangeInfo.java @@ -4,9 +4,9 @@ * 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. @@ -73,13 +73,27 @@ public class CallDepthChangeInfo { /** * 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 */ 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(); frameReg = program.getCompilerSpec().getStackPointer(); try { - initialize(func, func.getBody(), frameReg, TaskMonitor.DUMMY); + initialize(func, func.getBody(), frameReg, storeDepthAtEachInstuction, TaskMonitor.DUMMY); } catch (CancelledException e) { throw new RuntimeException("Unexpected Exception", e); @@ -88,10 +102,8 @@ public class CallDepthChangeInfo { /** * Construct a new CallDepthChangeInfo object. - * @param func - * function to examine - * @param monitor - * monitor used to cancel the operation + * @param func function to examine + * @param monitor used to cancel the operation * * @throws CancelledException * if the operation was canceled @@ -102,6 +114,8 @@ public class CallDepthChangeInfo { /** * 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 restrictSet set of addresses to restrict flow flowing to. * @param frameReg register that is to have it's depth(value) change tracked @@ -116,34 +130,18 @@ public class CallDepthChangeInfo { if (frameReg == null) { 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, - TaskMonitor monitor) throws CancelledException { + boolean storeDepthAtEachInstuction, TaskMonitor monitor) throws CancelledException { changeMap = new DefaultIntPropertyMap("change"); depthMap = new DefaultIntPropertyMap("depth"); trans = new VarnodeTranslator(program); - symEval = new SymbolicPropogator(program); + symEval = new SymbolicPropogator(program,storeDepthAtEachInstuction); symEval.setParamRefCheck(false); symEval.setReturnRefCheck(false); symEval.setStoredRefCheck(false); @@ -494,18 +492,14 @@ public class CallDepthChangeInfo { @Override public boolean evaluateContextBefore(VarnodeContext context, Instruction instr) { Varnode stackRegVarnode = context.getRegisterVarnode(frameReg); - Varnode stackValue = null; - try { - stackValue = context.getValue(stackRegVarnode, true, this); + Varnode stackValue = context.getValue(stackRegVarnode, true, this); - if (stackValue != null && context.isSymbol(stackValue) && - context.isStackSymbolicSpace(stackValue)) { - int stackPointerDepth = (int) stackValue.getOffset(); - setDepth(instr, stackPointerDepth); - } - } - catch (NotFoundException e) { - // ignore + if (stackValue != null && context.isSymbol(stackValue) && + context.isStackSymbolicSpace(stackValue)) { + long stackPointerDepth = stackValue.getOffset(); + int size = stackValue.getSize(); + stackPointerDepth = (stackPointerDepth << 8 * (8 - size)) >> 8 * (8 - size); + setDepth(instr, (int) stackPointerDepth); } return false; @@ -621,25 +615,14 @@ public class CallDepthChangeInfo { 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 reg the register to get the depth of. * @return the depth of the register at the address. */ 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); if (rValue == null) { 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 reg the register to get the representation of. * @return the string representation of the register value. diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/NewFunctionStackAnalysisCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/NewFunctionStackAnalysisCmd.java index 1bb46f308b..9ff9c76707 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/NewFunctionStackAnalysisCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/NewFunctionStackAnalysisCmd.java @@ -43,6 +43,8 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { 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 final static String X86_NAME = "x86"; + private boolean dontCreateNewVariables = false; private final boolean forceProcessing; @@ -53,6 +55,8 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { private Program program; private Register stackReg; private int purge = 0; + + private boolean isX86 = false; static String DEFAULT_FUNCTION_COMMENT = " FUNCTION"; @@ -101,6 +105,8 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { @Override public boolean applyTo(Program p, TaskMonitor monitor) { program = p; + + isX86 = checkForX86(p); int count = 0; long numAddresses = entryPoints.getNumAddresses(); @@ -138,6 +144,11 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { 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. * @@ -245,7 +256,7 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { * * @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 { // check if this is a jump thunk through a function pointer // if (checkThunk(func, monitor)) { @@ -271,7 +282,7 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { @Override public boolean evaluateContext(VarnodeContext context, Instruction instr) { if (instr.getFlowType().isTerminal()) { - RegisterValue value = context.getRegisterValue(stackReg, instr.getMaxAddress()); + RegisterValue value = context.getRegisterValue(stackReg); if (value != null) { BigInteger signedValue = value.getSignedValue(); if (signedValue != null) { @@ -279,7 +290,7 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { } } } - if (instr.getMnemonicString().equals("LEA")) { + if (isX86 && instr.getMnemonicString().equals("LEA")) { Register destReg = instr.getRegister(0); if (destReg != null) { Varnode value = context.getRegisterVarnodeValue(destReg); @@ -320,18 +331,17 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { if (opIndex == -1) { 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! - if (instr.getMnemonicString().equals("POP")) { + if (isX86 && instr.getMnemonicString().equals("POP")) { Register reg = instr.getRegister(opIndex); if (reg != null && reg.getName().contains("BP")) { return; } } - long extendedOffset = - extendOffset(address.getOffset(), stackReg.getBitLength()); - Function func = - program.getFunctionManager().getFunctionContaining(instr.getMinAddress()); - defineFuncVariable(func, instr, opIndex, (int) extendedOffset, sortedVariables); + long extendedOffset = extendOffset(address.getOffset(), stackReg.getBitLength()); + + defineFuncVariable(symEval, func, instr, opIndex, (int) extendedOffset, sortedVariables); } } @@ -342,9 +352,10 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { }; // 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) { @@ -623,11 +634,11 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { // 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 sortedVariables) { ReferenceManager refMgr = program.getReferenceManager(); - int refSize = getRefSize(instr, opIndex); + int refSize = getRefSize(symEval, instr, opIndex); try { // don't create crazy offsets if (stackOffset > MAX_PARAM_OFFSET || stackOffset < MAX_LOCAL_OFFSET) { @@ -660,15 +671,16 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { /** * Look at the result register to try and figure out stack access size. + * @param symEval * * @param instr instruction being analyzed * @param opIndex operand that has a stack reference. * * @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()) { - PcodeOp[] pcode = instr.getPcode(); + PcodeOp[] pcode = symEval.getInstructionPcode(instr); for (int i = pcode.length - 1; i >= 0; i--) { if (pcode[i].getOpcode() == PcodeOp.LOAD) { Varnode out = pcode[i].getOutput(); @@ -810,7 +822,7 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { offset = minOffset; } else { - dt = Undefined.getUndefinedDataType(size); + dt = Undefined.getUndefinedDataType(refSize); } Variable var; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ConstantPropagationAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ConstantPropagationAnalyzer.java index 039ebe8cda..59e90d3a98 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ConstantPropagationAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/ConstantPropagationAnalyzer.java @@ -108,7 +108,6 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer { final static HashSet handledProcessors = new HashSet(); protected String processorName = "Basic"; - protected AddressSetView EMPTY_ADDRESS_SET = new AddressSet(); public ConstantPropagationAnalyzer() { this("Basic"); @@ -189,7 +188,8 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer { int locationCount = locations.size(); monitor.initialize(locationCount); 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 unanalyzedSet.delete(resultSet); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/StackVariableAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/StackVariableAnalyzer.java index 980ac20eaa..120fc422a6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/StackVariableAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/function/StackVariableAnalyzer.java @@ -15,21 +15,32 @@ */ package ghidra.app.plugin.core.function; +import java.util.*; + import ghidra.app.cmd.function.FunctionStackAnalysisCmd; import ghidra.app.cmd.function.NewFunctionStackAnalysisCmd; import ghidra.app.services.*; import ghidra.app.util.importer.MessageLog; import ghidra.framework.cmd.BackgroundCommand; 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.listing.Function; import ghidra.program.model.listing.Program; +import ghidra.util.Msg; +import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; public class StackVariableAnalyzer extends AbstractAnalyzer { private static final String NAME = "Stack"; 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 doCreateLocalStackVars = true; private boolean doCreateStackParams = false; @@ -42,37 +53,88 @@ public class StackVariableAnalyzer extends AbstractAnalyzer { } @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 cmd; + // first split out all the function locations, make those the starts + // remove those from the bodies from the given set of addresses + Set
locations = new HashSet
(); + 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 cmd; + if (doNewStackAnalysis) { - cmd = new NewFunctionStackAnalysisCmd(set, doCreateStackParams, doCreateLocalStackVars, + cmd = new NewFunctionStackAnalysisCmd(new AddressSet(start, start), doCreateStackParams, doCreateLocalStackVars, false); } else { - cmd = new FunctionStackAnalysisCmd(set, doCreateStackParams, doCreateLocalStackVars, + cmd = new FunctionStackAnalysisCmd(new AddressSet(start, start), doCreateStackParams, doCreateLocalStackVars, false); - } - + } 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
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 fiter = program.getFunctionManager().getFunctionsOverlapping(set); + while (fiter.hasNext()) { + monitor.checkCancelled(); + Function function = fiter.next(); + locations.add(function.getEntryPoint()); + } } - private boolean useOldStackAnalysisByDefault(Program program) { - Language language = program.getLanguage(); - if (language.getProcessor().equals(Processor.findOrPossiblyCreateProcessor("x86"))) { - if (language.getLanguageDescription().getSize() == 16) { - // Prefer using old stack analysis for x86 16-bit with segmented addresses - return true; - } - } - return false; - } +// private boolean useOldStackAnalysisByDefault(Program program) { +// Language language = program.getLanguage(); +// if (language.getProcessor().equals(Processor.findOrPossiblyCreateProcessor("x86"))) { +// if (language.getLanguageDescription().getSize() == 16) { +// // Prefer using old stack analysis for x86 16-bit with segmented addresses +// return true; +// } +// } +// return false; +// } @Override public void registerOptions(Options options, Program program) { options.registerOption(GhidraLanguagePropertyKeys.USE_NEW_FUNCTION_STACK_ANALYSIS, - !useOldStackAnalysisByDefault(program), null, + true, null, "Use General Stack Reference Propogator (This works best on most processors)"); options.registerOption("Create Local Variables", doCreateLocalStackVars, null, @@ -80,18 +142,23 @@ public class StackVariableAnalyzer extends AbstractAnalyzer { options.registerOption("Create Param Variables", doCreateStackParams, null, "Create Function Parameter stack variables and references"); + + options.registerOption(MAX_THREAD_COUNT_OPTION_NAME, maxThreadCount, null, + MAX_THREAD_COUNT_OPTION_DESCRIPTION); } @Override public void optionsChanged(Options options, Program program) { doNewStackAnalysis = options.getBoolean(GhidraLanguagePropertyKeys.USE_NEW_FUNCTION_STACK_ANALYSIS, - !useOldStackAnalysisByDefault(program)); + true); doCreateLocalStackVars = options.getBoolean("Create Local Variables", doCreateLocalStackVars); doCreateStackParams = options.getBoolean("Create Param Variables", doCreateStackParams); + + maxThreadCount = options.getInt(MAX_THREAD_COUNT_OPTION_NAME, maxThreadCount); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/services/AbstractAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/services/AbstractAnalyzer.java index 790e96333c..14c4972b29 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/services/AbstractAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/services/AbstractAnalyzer.java @@ -4,9 +4,9 @@ * 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. @@ -15,9 +15,13 @@ */ 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.framework.options.Options; -import ghidra.program.model.address.AddressSetView; +import ghidra.program.model.address.*; import ghidra.program.model.listing.Program; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -30,6 +34,8 @@ public abstract class AbstractAnalyzer implements Analyzer { private boolean supportsOneTimeAnalysis; private boolean isPrototype = false; private AnalysisPriority priority = AnalysisPriority.LOW_PRIORITY; + + protected static final AddressSetView EMPTY_ADDRESS_SET = new AddressSetViewAdapter(); protected AbstractAnalyzer(String name, String description, AnalyzerType type) { this.name = name; @@ -117,5 +123,96 @@ public abstract class AbstractAnalyzer implements Analyzer { public void registerOptions(Options options, Program program) { // 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
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 callback = new QCallback() { + @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 queue = new ConcurrentQBuilder() + .setThreadPool(pool) + .setMaxInProgress(maxThreads) + .setMonitor(monitor) + .build(callback); + // @formatter:on + + queue.add(locations); + + queue.waitUntilDone(); + + return analyzedSet; + } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java index 5fbb5d3be2..55479b7093 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/SymbolicPropogator.java @@ -15,6 +15,8 @@ */ package ghidra.program.util; +import static ghidra.program.model.symbol.RefType.*; + import java.math.BigInteger; import java.util.*; @@ -38,10 +40,6 @@ import ghidra.util.exception.*; import ghidra.util.task.TaskMonitor; public class SymbolicPropogator { - private static int LRU_SIZE = 4096; - // QUESTIONS - // 1. How are "register-relative" varnodes distinguished based upon target space ? Not sure how we handle wrapping/truncation concerns. - // 1) The offset is the only thing that could be used as a reference. private static final int _POINTER_MIN_BOUNDS = 0x100; @@ -65,20 +63,28 @@ public class SymbolicPropogator { protected AddressSet visitedBody; // body of processed instructions protected boolean hitCodeFlow = false; // no branching so far - protected boolean debug = false; - - private final static NotFoundException valueTooBigException = - new NotFoundException("Value too big to fit in Scalar"); - - private final static NotFoundException divideByZeroException = - new NotFoundException("Divide by zero"); + private boolean debug = false; + + private boolean trackStartEndState = false; // track the start/end values for each instruction private long pointerMask; private int pointerSize; private DataType pointerSizedDT = null; + /* maximum exact instructions to execute, (ie. a run of shift instructions */ protected static final int MAX_EXACT_INSTRUCTIONS = 100; + + /* indicates not currently executing code that has already been followed */ + private static final int NOT_CONTINUING_CURRRENTLY = -1; + /* maximum instructions along to continue along a path that has been followed already */ + private static final int MAX_EXTRA_INSTRUCTION_FLOW = 16; + + private static int LRU_SIZE = 4096; + + /** NOTE: most of these caches are to reduce contention on the program lock to enable better threading. + * Once the lock contention has been reduced, these can be cut back or removed. + */ // Cache flows from instructions Map instructionFlowsCache = new LRUMap<>(LRU_SIZE); @@ -90,12 +96,28 @@ public class SymbolicPropogator { // Cache instructions looked up by containing Map instructionContainingCache = new LRUMap<>(LRU_SIZE); + + // Cache of functions looked up + Map functionAtCache = new LRUMap<>(LRU_SIZE); // cache for pcode callother injection payloads HashMap injectPayloadCache = new HashMap(); public SymbolicPropogator(Program program) { + this (program, false); + } + + /** + * Create symbolic propagation on program + * + * @param program program + * @param trackStartEndState - true to track the each register at the start/end of each instruction + * this will use more memory and be slightly slower + */ + public SymbolicPropogator(Program program, boolean trackStartEndState) { this.program = program; + + this.trackStartEndState = trackStartEndState; Language language = program.getLanguage(); @@ -104,7 +126,7 @@ public class SymbolicPropogator { setPointerMask(program); - context = new VarnodeContext(program, programContext, spaceContext); + context = new VarnodeContext(program, programContext, spaceContext, trackStartEndState); context.setDebug(debug); } @@ -154,6 +176,8 @@ public class SymbolicPropogator { context = saveOffCurrentContext(startAddr); } + context.flowToAddress(Address.NO_ADDRESS, startAddr); + // copy any current registers with values into the context Register[] regWithVals = program.getProgramContext().getRegistersWithValues(); for (Register regWithVal : regWithVals) { @@ -165,14 +189,12 @@ public class SymbolicPropogator { if (!regVal.hasValue()) { continue; } - context.setFutureRegisterValue(startAddr, regVal); - // put it in memory too, if memory mapped - if (regVal.getRegister().getAddress().isMemoryAddress()) { - Register reg = regVal.getRegister(); - context.putValue(context.getRegisterVarnode(reg), context.createConstantVarnode( - regVal.getUnsignedValue().longValue(), reg.getMinimumByteSize()), false); - } + + Register reg = regVal.getRegister(); + context.putValue(context.getRegisterVarnode(reg), context.createConstantVarnode( + regVal.getUnsignedValue().longValue(), reg.getMinimumByteSize()), false); } + context.propogateResults(false); AddressSet bodyDone = null; try { @@ -237,26 +259,9 @@ public class SymbolicPropogator { Language language = program.getLanguage(); ProgramContext newValueContext = new ProgramContextImpl(language); ProgramContext newSpaceContext = new ProgramContextImpl(language); - VarnodeContext newContext = new VarnodeContext(program, newValueContext, newSpaceContext); + VarnodeContext newContext = new VarnodeContext(program, newValueContext, newSpaceContext, trackStartEndState); newContext.setDebug(debug); - int constantSpaceID = program.getAddressFactory().getConstantSpace().getSpaceID(); - // copy any current registers with values into the context - Register[] regWithVals = programContext.getRegistersWithValues(); - for (Register regWithVal : regWithVals) { - RegisterValue regVal; - regVal = programContext.getRegisterValue(regWithVal, startAddr); - RegisterValue spRegVal; - spRegVal = spaceContext.getRegisterValue(regWithVal, startAddr); - - // for now only copy constants into start of current flow, - // maybe should do any value... - // TODO: need a better way to figure this out! - if (regVal != null && (spRegVal == null || (spRegVal.getUnsignedValue() != null && - spRegVal.getUnsignedValue().longValue() == constantSpaceID))) { - newContext.setFutureRegisterValue(startAddr, regVal); - } - } programContext = newValueContext; spaceContext = newSpaceContext; @@ -286,6 +291,12 @@ public class SymbolicPropogator { * if isRegisterRelativeValue() returns true. */ public long getValue() { + if (isRegisterRelativeValue()) { + long off = value; + int size = relativeRegister.getBitLength(); + off = (off << (64 - size)) >> (64 - size); + return off; + } return value; } @@ -308,16 +319,15 @@ public class SymbolicPropogator { /** * Get constant or register relative value assigned to the * specified register at the specified address + * Note: This can only be called safely if trackStartEndState flag is true. + * Otherwise it will just return the current value, not the value at the given address. + * * @param toAddr address * @param reg register * @return register value */ public Value getRegisterValue(Address toAddr, Register reg) { - // - // TODO: WARNING: NO_ADDRESS Might not be correct here, - // will only get a value if it has been stored, or is in the current flowing context! - // Will not be gotten from the FUTURE FLOWING context - // + Varnode val = context.getRegisterVarnodeValue(reg, Address.NO_ADDRESS, toAddr, true); if (val == null) { return null; @@ -336,6 +346,37 @@ public class SymbolicPropogator { return null; } + /** + * Get constant or register relative value assigned to the + * specified register at the specified address after the instruction has executed. + * Note: This can only be called if trackStartEndState flag is true. + * + * @param toAddr address + * @param reg register + * @return register value + * + * @throws UnsupportedOperationException trackStartEndState == false at construction + */ + public Value getEndRegisterValue(Address toAddr, Register reg) { + + Varnode val = context.getEndRegisterVarnodeValue(reg, Address.NO_ADDRESS, toAddr, true); + if (val == null) { + return null; + } + if (context.isConstant(val)) { + return new Value(val.getOffset()); + } + AddressSpace space = val.getAddress().getAddressSpace(); + if (space.getName().startsWith("track_")) { + return new Value(val.getOffset()); + } + Register relativeReg = program.getRegister(space.getName()); + if (relativeReg != null) { + return new Value(relativeReg, val.getOffset()); + } + return null; + } + /** * Do not depend on this method! For display debugging purposes only. * This will change. @@ -370,45 +411,43 @@ public class SymbolicPropogator { } public void setRegister(Address addr, Register stackReg) { - context.flowStart(Address.NO_ADDRESS, addr); - int spaceID = context.getAddressSpace(stackReg.getName()); + context.flowToAddress(Address.NO_ADDRESS, addr); + int spaceID = context.getAddressSpace(stackReg.getName(), stackReg.getBitLength()); Varnode vnode = context.createVarnode(0, spaceID, stackReg.getBitLength() / 8); context.putValue(context.getRegisterVarnode(stackReg), vnode, false); context.propogateResults(false); context.flowEnd(addr); } - protected class SavedFlowState { - Address source; - Address destination; - boolean continueAfterHittingFlow; + record SavedFlowState(VarnodeContext vContext, FlowType flowType, Address source, Address destination, + int pcodeIndex, int continueAfterHittingFlow) { - public SavedFlowState(VarnodeContext vContext, Address source, Address destination, - boolean continueAfterHittingFlow) { - super(); + public SavedFlowState(VarnodeContext vContext, FlowType flowType, Address source, Address destination, + int continueAfterHittingFlow) { + this(vContext,flowType,source,destination,0,continueAfterHittingFlow); + } + + public SavedFlowState(VarnodeContext vContext, FlowType flowType, Address source, Address destination, + int pcodeIndex, int continueAfterHittingFlow) { + this.vContext = vContext; + this.flowType = flowType; this.source = source; this.destination = destination; + this.pcodeIndex = pcodeIndex; this.continueAfterHittingFlow = continueAfterHittingFlow; vContext.pushMemState(); } - - public Address getSource() { - return source; - } - - public Address getDestination() { - return destination; - } - + public boolean isContinueAfterHittingFlow() { - return continueAfterHittingFlow; + return continueAfterHittingFlow != NOT_CONTINUING_CURRRENTLY; } - - public void restoreState(VarnodeContext vContext) { + + public void restoreState() { vContext.popMemState(); } } + // Used to stop runs of the same exact instruction protected int lastFullHashCode = 0; // full byte hash code protected int lastInstrCode = -1; // last instruction prototype hashcode @@ -433,7 +472,7 @@ public class SymbolicPropogator { // prime the context stack with the entry point address Stack contextStack = new Stack<>(); - contextStack.push(new SavedFlowState(vContext, fromAddr, startAddr, true)); + contextStack.push(new SavedFlowState(vContext, null, fromAddr, startAddr, NOT_CONTINUING_CURRRENTLY)); canceled = false; // only stop flowing on unknown bad calls when the stack depth could be unknown @@ -441,6 +480,7 @@ public class SymbolicPropogator { .getDefaultCallingConvention() .getExtrapop() == PrototypeModel.UNKNOWN_EXTRAPOP; + HashMap> visitedMap = new HashMap<>(); while (!contextStack.isEmpty()) { monitor.checkCancelled(); if (canceled) { @@ -448,44 +488,80 @@ public class SymbolicPropogator { return visitedBody; } - // if we run into a flow that has already been done, flow until - // hit our flow again, or hit a branching instruction - boolean hitOtherFlow = false; - SavedFlowState nextFlow = contextStack.pop(); - Address nextAddr = nextFlow.getDestination(); - Address flowFromAddr = nextFlow.getSource(); - boolean continueAfterHittingFlow = nextFlow.isContinueAfterHittingFlow(); - nextFlow.restoreState(vContext); - - // already done it! - if (visitedBody.contains(nextAddr)) { - // allow it to keep flowing until the next branch/call/ret flow! - hitOtherFlow = true; - if (!continueAfterHittingFlow) { + boolean justPopped = true; + Address nextAddr = nextFlow.destination; + Address flowFromAddr = nextFlow.source; + FlowType flowType = nextFlow.flowType; + int pcodeStartIndex = nextFlow.pcodeIndex; + int continueAfterHittingFlow = nextFlow.continueAfterHittingFlow; + nextFlow.restoreState(); + + if (flowType != null) { + // if call flow, is inlined call, only inlined flows are pushed onto the flow stack + // + if (flowType.isCall()) { + AddressSet savedBody = visitedBody; + Function func = getFunctionAt(nextAddr); + flowConstants(nextFlow.source, nextAddr, func.getBody(), eval, vContext, monitor); + visitedBody = savedBody; continue; } + + // if jump flow, make sure it isn't jumping to another function + // + if (flowType.isJump() && !flowType.isConditional()) { + // only jump to a computed location if there is no function there + Function func = getFunctionAt(nextAddr); + if (func != null && !func.getBody().contains(startAddr)) { + // handle jump as if it were a call + vContext.flowStart(nextAddr); + handleFunctionSideEffects(getInstructionAt(flowFromAddr), nextAddr, monitor); + continue; + } + } } - // special flow start, retrieves the flow from/to saved state if there is one, and applies it - // As if a mergeFuture flow had been done. - vContext.flowStart(flowFromAddr, nextAddr); + HashSet
visitSet = visitedMap.get(nextAddr); + if (visitSet != null) { + // already flowed to nextAddr from flowFromAddr + if (visitSet.contains(flowFromAddr)) { + continue; + } + // already hit nextAddr once. + // continue for some number of instructions + if (continueAfterHittingFlow == NOT_CONTINUING_CURRRENTLY) { + continueAfterHittingFlow = 0; + } + } + else { + visitSet = new HashSet<>(); + visitedMap.put(nextAddr, visitSet); + // never flowed to here, but have visited before + if (continueAfterHittingFlow == NOT_CONTINUING_CURRRENTLY && visitedBody.contains(nextAddr)) { + continueAfterHittingFlow = 0; + } + } + + visitSet.add(flowFromAddr); + + // record new flow from one basic block to another + vContext.flowToAddress(fromAddr, nextAddr); lastFullHashCode = 0; lastInstrCode = -1; sameInstrCount = 0; Address maxAddr = null; while (nextAddr != null) { - monitor.checkCancelled(); - // already done it! - if (visitedBody.contains(nextAddr)) { - // allow it to keep flowing until the next branch/call/ret flow! - hitOtherFlow = true; - if (!continueAfterHittingFlow) { - break; - } + // special flow start, retrieves the flow from/to saved state if there is one, and applies it + // As if a mergeFuture flow had been done. + vContext.flowStart(nextAddr); + + if (!visitedBody.contains(nextAddr)) { + // got to a flow never been to before, turn off any continue flow behavior + continueAfterHittingFlow = NOT_CONTINUING_CURRRENTLY; } if (restrictSet != null && !restrictSet.contains(nextAddr)) { @@ -514,22 +590,18 @@ public class SymbolicPropogator { vContext.setCurrentInstruction(instr); - vContext.flowToAddress(flowFromAddr, maxAddr); - if (evaluator != null) { if (evaluator.evaluateContextBefore(vContext, instr)) { visitedBody.add(conflicts); // put the conflict/redone addresses back in return visitedBody; } } - + // // apply the pcode effects // - Address retAddr = applyPcode(vContext, instr, monitor); - - // add this instruction to processed body set - visitedBody.addRange(minInstrAddress, maxAddr); + boolean continueCurrentTrace = applyPcode(contextStack, vContext, instr, pcodeStartIndex, continueAfterHittingFlow, monitor); + pcodeStartIndex = 0; /* Allow evaluateContext routine to change override the flowtype of an instruction. * Jumps Changed to calls will now continue processing. @@ -542,7 +614,7 @@ public class SymbolicPropogator { return visitedBody; } } - + // if the instruction changed it's type to a call, need to handle the call side effects FlowType instrFlow = instr.getFlowType(); if (!originalFlowType.equals(instrFlow) && instrFlow.isCall()) { @@ -551,121 +623,49 @@ public class SymbolicPropogator { handleFunctionSideEffects(instr, target, monitor); } } - - Address callFlowAddr = null; + + // if already hit a flow, only continue through code until MAX_EXTRA instructions or hit a call + if (visitedBody.contains(minInstrAddress) && !justPopped) { + // even if second time through, run a few more instructions to see if get to a call + if (continueAfterHittingFlow > NOT_CONTINUING_CURRRENTLY) { + continueAfterHittingFlow++; + } else { + continueAfterHittingFlow=0; // start counting, hit body + } + if (continueAfterHittingFlow >= MAX_EXTRA_INSTRUCTION_FLOW || instrFlow.isCall()) { + break; + } + } + // add this instruction to processed body set + visitedBody.addRange(minInstrAddress, maxAddr); + + justPopped = false; + + vContext.flowEnd(minInstrAddress); + + // if already hit a flow, only continue until a call is hit, + // TODO: this could be changed to some number of instructions + // if (continueAfterHittingFlow > 0 && (instrFlow.isCall())) { + // break; + // } boolean simpleFlow = isSimpleFallThrough(instrFlow); // once we encounter any flow, must set the hitCodeFlow flag // This should be set after the current instruction has been processed. hitCodeFlow |= !simpleFlow; - // follow flow, except for call flow - boolean doFallThruLast = false; - if (!simpleFlow) { - Address flows[] = getInstructionFlows(instr); - if (flows != null && flows.length > 0) { - // if already hit another flow, don't continue past any type of branching instruction - if (hitOtherFlow) { - nextAddr = null; - break; - } - if (!instrFlow.isCall()) { - for (Address flow : flows) { - contextStack.push(new SavedFlowState(vContext, minInstrAddress, - flow, continueAfterHittingFlow)); - } - } - else if (flows.length > 1) { - // could have attached flows from a callfixup. - Reference[] flowRefs = instr.getReferencesFrom(); - for (Reference flowRef : flowRefs) { - RefType referenceType = flowRef.getReferenceType(); - if (referenceType.isComputed() && referenceType.isJump()) { - contextStack.push(new SavedFlowState(vContext, minInstrAddress, - flowRef.getToAddress(), continueAfterHittingFlow)); - } - } - } - else { - callFlowAddr = flows[0]; - } - } - else if (instrFlow.isComputed() && instrFlow.isCall()) { - // save this fallthru for later, since we might not know the side-effects of the call. - doFallThruLast = true; - } - } - - if (callFlowAddr != null) { - Function func = program.getFunctionManager().getFunctionAt(callFlowAddr); - if (func != null && func.isInline()) { - vContext.mergeToFutureFlowState(maxAddr, callFlowAddr); - vContext.flowEnd(maxAddr); - // Body will get re-initialized. - // The inlined function may be called multiple times, so body of inlined function - // should not be included in the already visited body. - AddressSet savedBody = visitedBody; - flowConstants(maxAddr, callFlowAddr, func.getBody(), eval, vContext, monitor); - visitedBody = savedBody; - vContext.mergeToFutureFlowState(minInstrAddress, maxAddr); - - // - // TODO: WARNING, might not start the flow correctly if there is no future flow here. - // FLOW end will probably work correctly, but.... - // - vContext.flowStart(minInstrAddress, maxAddr); - retAddr = null; - } - } - // go to the fall thru address, unless this instruction had flow // then add it's flow to the end of the list and process other flows Address fallThru = instr.getFallThrough(); nextAddr = null; - if (retAddr != null) { - contextStack.push(new SavedFlowState(vContext, minInstrAddress, retAddr, - continueAfterHittingFlow)); - fallThru = null; - } - - if (fallThru != null) { - if (doFallThruLast) { - vContext.mergeToFutureFlowState(minInstrAddress, fallThru); - - // put it lowest on the stack to do later! - contextStack.push(new SavedFlowState(vContext, minInstrAddress, fallThru, - !callCouldCauseBadStackDepth)); - } - else if (fallThru.compareTo(maxAddr) < 0) { - // this isn't a normal fallthru, must break it up - // don't continue flowing if something else is hit, this is an odd case - vContext.mergeToFutureFlowState(minInstrAddress, fallThru); - - contextStack.push( - new SavedFlowState(vContext, minInstrAddress, fallThru, false)); - } - else { - // no need to store future flow state, will continue on the fall-thru flow - nextAddr = fallThru; - fallThru = null; - } - // Used to break if there were any references to a place. This will miss some references. - // Just follow fallthru for now. Later we can follow if the context is different. - // else if (!program.getReferenceManager().hasReferencesTo(fallThru)) { - //else { - // nextAddr = fallThru; - // fallThru = null; - //} - // else { - // vContext.mergeToFutureFlowState(instr.getMinAddress(), fallThru); // need to push flow for later. - // contextStack.push(new SavedFlowState(instr.getMinAddress(), fallThru, - // continueAfterHittingFlow)); - //} + if (continueCurrentTrace) { + nextAddr = fallThru; } } - vContext.flowEnd(maxAddr); + } + //System.out.println(startAddr + " = " + instructionCount + ", " + continueCount); visitedBody.add(conflicts); // put the conflict/redone addresses back in return visitedBody; } @@ -722,7 +722,7 @@ public class SymbolicPropogator { return false; } - private PcodeOp[] getInstructionPcode(Instruction instruction) { + public PcodeOp[] getInstructionPcode(Instruction instruction) { PcodeOp ops[] = pcodeCache.get(instruction.getMinAddress()); if (ops == null) { ops = instruction.getPcode(true); @@ -731,7 +731,7 @@ public class SymbolicPropogator { return ops; } - private Instruction getInstructionAt(Address addr) { + public Instruction getInstructionAt(Address addr) { Instruction instr = instructionAtCache.get(addr); if (instr != null) { return instr; @@ -739,15 +739,36 @@ public class SymbolicPropogator { if (instructionAtCache.containsKey(addr)) { return null; } + instr = program.getListing().getInstructionAt(addr); + cacheInstruction(addr, instr); + return instr; + } + + public Function getFunctionAt(Address addr) { + Function func = functionAtCache.get(addr); + if (func != null) { + return func; + } + if (functionAtCache.containsKey(addr)) { + return null; + } + + func = program.getFunctionManager().getFunctionAt(addr); + functionAtCache.put(addr, func); + return func; + } + + private void cacheInstruction(Address addr, Instruction instr) { instructionAtCache.put(addr, instr); if (instr != null) { instructionContainingCache.put(instr.getMaxAddress(), instr); + // pre-fill the pcode cache for this instructions + getInstructionPcode(instr); } - return instr; } - private Instruction getInstructionContaining(Address addr) { + public Instruction getInstructionContaining(Address addr) { // try at cache first Instruction instr = getInstructionAt(addr); if (instr != null) { @@ -779,25 +800,36 @@ public class SymbolicPropogator { return flows; } - private Address applyPcode(VarnodeContext vContext, Instruction instruction, - TaskMonitor monitor) { + /** + * Apply pcode from an instruction to current varnode context. + * Following a flow will push a new context state based on the current context state onto the contextStack + * + * @param contextStack context state stack + * @param vContext varnode context + * @param instruction instruction to apply pcode from + * @param continueAfterHittingFlow true if should continue after hitting an already processed flow + * @param monitor to cancel + * @return true to to continue this instruction path, false otherwise + */ + private boolean applyPcode(Stack contextStack, VarnodeContext vContext, Instruction instruction, int startIndex, int continueAfterHittingFlow, TaskMonitor monitor) { Address nextAddr = null; if (instruction == null) { - return nextAddr; + return false; } // might have run into this pcode before, cache it, in case we run into it again. PcodeOp[] ops = getInstructionPcode(instruction); if (ops.length <= 0) { - return nextAddr; + // is a nop + return true; } Address minInstrAddress = instruction.getMinAddress(); if (debug) { - Msg.info(this, minInstrAddress + " " + instruction); + Msg.info(this, minInstrAddress + " " + instruction + " " + startIndex); } // callfixup injection targets that have already been used @@ -810,7 +842,7 @@ public class SymbolicPropogator { boolean injected = false; int ptype = 0; - for (int pcodeIndex = 0; pcodeIndex < ops.length; pcodeIndex++) { + for (int pcodeIndex = startIndex; pcodeIndex < ops.length; pcodeIndex++) { mustClearAll = pcodeIndex < mustClearAllUntil_PcodeIndex; @@ -819,8 +851,9 @@ public class SymbolicPropogator { Varnode out = pcodeOp.getOutput(); Varnode[] in = pcodeOp.getInputs(); - Varnode val1, val2, val3, result; - long lval1, lval2; + Varnode val1, val2, val3; + Varnode result = null; + Long longVal1, longVal2; long lresult; boolean suspectOffset = false; Varnode vt; @@ -840,33 +873,33 @@ public class SymbolicPropogator { break; case PcodeOp.LOAD: + Varnode memVal = null; val1 = vContext.getValue(in[0], evaluator); val2 = vContext.getValue(in[1], evaluator); - - suspectOffset = vContext.isSuspectConstant(val2); - - vt = vContext.getVarnode(in[0], val2, out.getSize(), evaluator); - - // TODO: may need to use DATA refType in some cases - addLoadStoreReference(vContext, instruction, ptype, vt, in[0], in[1], - RefType.READ, suspectOffset==false, monitor); - - // If vt is a bad varnode (bad space, no memory, no value in varnode) you won't get a value - Varnode memVal = vContext.getValue(vt, evaluator); + if (val1 != null && val2 != null) { + suspectOffset = vContext.isSuspectConstant(val2); + + vt = vContext.getVarnode(in[0], val2, out.getSize(), evaluator); + + // TODO: may need to use DATA refType in some cases + + if (vt != null) { + addLoadStoreReference(vContext, instruction, ptype, vt, in[0], in[1], + RefType.READ, suspectOffset==false, monitor); + // If vt is a bad varnode (bad space, no memory, no value in varnode) you won't get a value + memVal = vContext.getValue(vt, evaluator); + } + } vContext.putValue(out, memVal, mustClearAll); - break; case PcodeOp.STORE: Varnode offs = null; - try { - offs = vContext.getValue(in[1], true, evaluator); + offs = vContext.getValue(in[1], true, evaluator); + if (offs != null) { suspectOffset = vContext.isSuspectConstant(offs); out = getStoredLocation(vContext, in[0], offs, in[2]); - } catch (NotFoundException e) { - // if can't get the value of the relative store location - // this isn't an exception, the output will be null/unknown } // TODO: may need to use DATA refType in some cases @@ -875,39 +908,41 @@ public class SymbolicPropogator { val3 = vContext.getValue(in[2], null); - if (!injected) { + if (val3 != null && !injected) { addStoredReferences(vContext, instruction, out, val3, monitor); } vContext.putValue(out, val3, mustClearAll); break; case PcodeOp.BRANCHIND: - try { - val1 = vContext.getValue(in[0], evaluator); + val1 = vContext.getValue(in[0], evaluator); + if (val1 != null) { suspectOffset = vContext.isSuspectConstant(val1); vt = getConstantOrExternal(vContext, minInstrAddress, val1); - makeReference(vContext, instruction, -1, vt, null, - instruction.getFlowType(), ptype, !suspectOffset, monitor); + if (vt != null) { + makeReference(vContext, instruction, -1, vt, null, + instruction.getFlowType(), ptype, !suspectOffset, monitor); + } } - catch (NotFoundException e) { - // constant not found, ignore - } - // even though we don't know the destination, propogate the flow to attached destinations - vContext.propogateResults(false); + + // even if we don't know the destination, branch to any jump + // references already on the branch indirect + vContext.propogateResults(true); Reference[] flowRefs = instruction.getReferencesFrom(); for (Reference flowRef : flowRefs) { RefType referenceType = flowRef.getReferenceType(); if (referenceType.isComputed() && referenceType.isJump()) { - vContext.mergeToFutureFlowState(flowRef.getFromAddress(), - flowRef.getToAddress()); + contextStack.push(new SavedFlowState(vContext, + FlowType.UNCONDITIONAL_JUMP, flowRef.getFromAddress(), + flowRef.getToAddress(), continueAfterHittingFlow)); } } if (evaluator != null && evaluator.evaluateDestination(vContext, instruction)) { canceled = true; - return null; + return false; } break; @@ -917,45 +952,42 @@ public class SymbolicPropogator { Function func = null; val1 = in[0]; if (ptype == PcodeOp.CALLIND) { - try { val1 = vContext.getValue(val1, evaluator); - // TODO: Revisit handling of external functions... - - if (vContext.isConstant(val1)) { - suspectOffset = vContext.isSuspectConstant(val1); - // indirect target - assume single code space (same as instruction) - target = instruction.getAddress() - .getNewTruncatedAddress(val1.getOffset(), true); - } - else if (val1.isAddress()) { - // TODO: could this also occur if a memory location was copied ?? - // unable to resolve indirect value - can we trust stored pointer? - // if not, we must rely on reference to function. - target = resolveFunctionReference(val1.getAddress()); - } - else if (vContext.isExternalSpace(val1.getSpace())) { - target = val1.getAddress(); - } - // if the value didn't get changed, then the real value isn't in here, don't make a reference - if (target != null) { - Reference[] refs = instruction.getReferencesFrom(); - // make sure we aren't replacing a read ref with a call to the same place - if (refs.length <= 0 || - !refs[0].getToAddress().equals(target)) { - target = makeReference(vContext, instruction, Reference.MNEMONIC, - // Use target in case location has shifted (external...) - target.getAddressSpace().getSpaceID(), - target.getAddressableWordOffset(), val1.getSize(), - null, - instruction.getFlowType(), ptype, !suspectOffset, false, monitor); + if (val1 != null) { + // TODO: Revisit handling of external functions... + + if (vContext.isConstant(val1)) { + suspectOffset = vContext.isSuspectConstant(val1); + // indirect target - assume single code space (same as instruction) + target = instruction.getAddress() + .getNewTruncatedAddress(val1.getOffset(), true); + } + else if (val1.isAddress()) { + // TODO: could this also occur if a memory location was copied ?? + // unable to resolve indirect value - can we trust stored pointer? + // if not, we must rely on reference to function. + target = resolveFunctionReference(val1.getAddress()); + } + else if (vContext.isExternalSpace(val1.getSpace())) { + target = val1.getAddress(); + } + // if the value didn't get changed, then the real value isn't in here, don't make a reference + if (target != null) { + Reference[] refs = instruction.getReferencesFrom(); + // make sure we aren't replacing a read ref with a call to the same place + if (refs.length <= 0 || + !refs[0].getToAddress().equals(target)) { + + target = makeReference(vContext, instruction, Reference.MNEMONIC, + // Use target in case location has shifted (external...) + target.getAddressSpace().getSpaceID(), + target.getAddressableWordOffset(), val1.getSize(), + null, + instruction.getFlowType(), ptype, !suspectOffset, false, monitor); + } } } - - } - catch (NotFoundException e) { - val1 = null; - } } else { // CALL will always provide address @@ -970,9 +1002,8 @@ public class SymbolicPropogator { if (target != null) { if (target.isMemoryAddress()) { vContext.propogateResults(false); - vContext.mergeToFutureFlowState(minInstrAddress, target); } - func = prog.getFunctionManager().getFunctionAt(target); + func = getFunctionAt(target); if (func == null && ptype == PcodeOp.CALLIND) { Reference[] refs = instruction.getReferencesFrom(); if (refs != null && refs.length > 0) { @@ -980,7 +1011,7 @@ public class SymbolicPropogator { if (firstRef.getReferenceType().isData() || firstRef.getReferenceType().isIndirect()) { target = firstRef.getToAddress(); - func = prog.getFunctionManager().getFunctionAt(target); + func = getFunctionAt(target); } } } @@ -999,6 +1030,13 @@ public class SymbolicPropogator { } } + if (func != null && func.isInline()) { + // push fallthru pcodeIndex after call + contextStack.push(new SavedFlowState(vContext, FALL_THROUGH, minInstrAddress, func.getEntryPoint(), pcodeIndex+1 , continueAfterHittingFlow)); + // push the call so it will happen first + contextStack.push(new SavedFlowState(vContext, UNCONDITIONAL_CALL, minInstrAddress, func.getEntryPoint(), continueAfterHittingFlow)); + return false; + } handleFunctionSideEffects(instruction, target, monitor); // check for pcode replacement - calling convention uponreturn injection @@ -1010,7 +1048,7 @@ public class SymbolicPropogator { injected = true; continue; } - + break; // for callother, could be an interrupt, need to look at it like a call @@ -1046,10 +1084,11 @@ public class SymbolicPropogator { throw new AssertException("Not a valid Address on instruction at " + instruction.getAddress()); } - vContext.propogateResults(false); - vContext.mergeToFutureFlowState(minInstrAddress, in[0].getAddress()); - pcodeIndex = ops.length; // break out of the processing - break; + vContext.propogateResults(true); + nextAddr = minInstrAddress.getAddressSpace() + .getOverlayAddress(in[0].getAddress()); + contextStack.push(new SavedFlowState(vContext, UNCONDITIONAL_JUMP, minInstrAddress, nextAddr, continueAfterHittingFlow)); + return false; case PcodeOp.CBRANCH: vt = null; @@ -1058,77 +1097,86 @@ public class SymbolicPropogator { int sequenceOffset = (int) in[0].getOffset(); if ((pcodeIndex + sequenceOffset) >= ops.length) { vContext.propogateResults(false); - vContext.mergeToFutureFlowState(minInstrAddress, - instruction.getFallThrough()); } } else if (in[0].isAddress()) { vt = in[0]; vContext.propogateResults(false); - vContext.mergeToFutureFlowState(minInstrAddress, in[0].getAddress()); } - Varnode condition = null; - try { - condition = vContext.getValue(in[1], null); - } - catch (NotFoundException e) { - Address fallThru = instruction.getFallThrough(); - // if the fallthru and conditional branch are the same must CLEAR - // because we don't know which way is right for any value set after this - if (fallThru != null && vt != null && - vt.getOffset() == fallThru.getOffset()) { - // check that the last pcode isn't a branch, which means FT - // came from the conditional branch. - int op = ops[ops.length - 1].getOpcode(); - if (op == PcodeOp.BRANCH || op == PcodeOp.RETURN || - op == PcodeOp.BRANCHIND) { - ptype = op; // this really isn't a fallthru, make it the correct flow - // this will cause the effects after this not to flow to next instr. - } - else { - mustClearAllUntil_PcodeIndex = ops.length; - } - } - else if (internalBranch) { - // if internal flow joins back together, just skip over effect - // Warning this is arbitrary choice of one branch over the other....!!! - // look at pcode up to destination, if all non flow or internal, just skip - int sequenceOffset = pcodeIndex + (int) in[0].getOffset(); - int i = pcodeIndex + 1; - for (; i < sequenceOffset; i++) { - if (isBranch(ops[i])) { - break; - } - } - if (i == sequenceOffset) { - // everything that is in the cache from here on should be cleared - mustClearAllUntil_PcodeIndex = sequenceOffset; - break; - } - } - throw e; - } + Varnode condition = vContext.getValue(in[1], null); - lval1 = vContext.getConstant(condition, null); - - if (lval1 != 0) { + if (condition != null) { + longVal1 = vContext.getConstant(condition, null); + } else { + // couldn't find the condition, so arbitrary which way to go + longVal1 = Long.valueOf(0); // do fallthru first + } + boolean followFalse = evaluator.followFalseConditionalBranches(); + boolean conditionMet = longVal1 != null && longVal1 != 0; + // if conditionMet, followBranch (local/mem), if followFalse, push new state for (local/mem) + // Need to push (local/mem) first, false first, Then push branch, so happens first, return false to continue this run. + // if conditionNotMet, followFallThru, if followFalse, push new state for branch (local/mem) + if (conditionMet) { if (internalBranch) { // handle internal branch int sequenceOffset = (int) in[0].getOffset(); + // only go forwards in sequence, backwards could be a loop if (sequenceOffset > 0) { + if (followFalse) { + contextStack.push(new SavedFlowState(vContext, FALL_THROUGH, minInstrAddress, + minInstrAddress, pcodeIndex+1, continueAfterHittingFlow)); + } pcodeIndex += sequenceOffset - 1; } - else if (!evaluator.followFalseConditionalBranches()) { + else if (!followFalse) { + // if not forward internal branch, and not following False flows, processing pcodeIndex = ops.length; break; } } - else if (!evaluator.followFalseConditionalBranches()) { + else { // memory branch + if (followFalse) { + // push follow false first + contextStack.push(new SavedFlowState(vContext, FALL_THROUGH, minInstrAddress, + minInstrAddress, pcodeIndex+1, continueAfterHittingFlow)); + } + // pcode addresses are raw addresses, make sure address is in same instruction space nextAddr = minInstrAddress.getAddressSpace() .getOverlayAddress(in[0].getAddress()); + contextStack.push(new SavedFlowState(vContext, CONDITIONAL_JUMP, minInstrAddress, + nextAddr, continueAfterHittingFlow)); + pcodeIndex = ops.length; // break out of the processing + return false; // don't keep going + } + } else { + if (internalBranch) { + // handle internal branch + int sequenceOffset = (int) in[0].getOffset(); + // only go forwards in sequence, backwards could be a loop + if (sequenceOffset > 0) { + int internalIndex = pcodeIndex + sequenceOffset; + if (followFalse) { + contextStack.push(new SavedFlowState(vContext, FALL_THROUGH, minInstrAddress, + minInstrAddress, internalIndex, continueAfterHittingFlow)); + } + } + else if (!followFalse) { + // if not forward internal branch, and not following False flows, processing + pcodeIndex = ops.length; + break; + } + } + else { // memory branch + if (followFalse) { + // push follow false first + nextAddr = minInstrAddress.getAddressSpace() + .getOverlayAddress(in[0].getAddress()); + contextStack.push(new SavedFlowState(vContext, CONDITIONAL_JUMP, minInstrAddress, + nextAddr, continueAfterHittingFlow)); + } } } break; @@ -1136,16 +1184,11 @@ public class SymbolicPropogator { case PcodeOp.RETURN: // if return value is a location, give evaluator a chance to check the value - try { - val1 = vContext.getValue(in[0], evaluator); - if (evaluator != null && - evaluator.evaluateReturn(val1, vContext, instruction)) { - canceled = true; - return null; - } - } - catch (NotFoundException e) { - // constant not found, ignore + val1 = vContext.getValue(in[0], evaluator); + if (val1 != null && evaluator != null && + evaluator.evaluateReturn(val1, vContext, instruction)) { + canceled = true; + return false; } // put references on any return value that is a pointer and could be returned @@ -1171,22 +1214,21 @@ public class SymbolicPropogator { break; case PcodeOp.INT_ADD: - try { - val1 = vContext.getValue(in[0], false, evaluator); - } - catch (NotFoundException exc) { + val1 = vContext.getValue(in[0], false, evaluator); + if (val1 == null) { val1 = vContext.createBadVarnode(); } - try { - val2 = vContext.getValue(in[1], false, evaluator); - } - catch (NotFoundException exc) { + val2 = vContext.getValue(in[1], false, evaluator); + if (val2 == null) { val2 = vContext.createBadVarnode(); } if (val1.equals(val2)) { - long v = vContext.getConstant(val1, evaluator); - val1 = val2 = vContext.createConstantVarnode(v, val1.getSize()); + Long v = vContext.getConstant(val1, evaluator); + if (v != null) { + val1 = val2 = vContext.createConstantVarnode(v, val1.getSize()); + } } + result = vContext.add(val1, val2, evaluator); vContext.putValue(out, result, mustClearAll); break; @@ -1205,10 +1247,13 @@ public class SymbolicPropogator { (BinaryOpBehavior) OpBehaviorFactory.getOpBehavior(ptype); val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lresult = binaryBehavior.evaluateBinary(out.getSize(), in[0].getSize(), - vContext.getConstant(val1, evaluator), - vContext.getConstant(val2, evaluator)); - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = binaryBehavior.evaluateBinary(out.getSize(), in[0].getSize(), + longVal1, longVal2); + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; @@ -1216,16 +1261,22 @@ public class SymbolicPropogator { UnaryOpBehavior unaryBehavior = (UnaryOpBehavior) OpBehaviorFactory.getOpBehavior(ptype); val1 = vContext.getValue(in[0], false, evaluator); - lresult = unaryBehavior.evaluateUnary(out.getSize(), in[0].getSize(), - vContext.getConstant(val1, evaluator)); - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + if (longVal1 != null) { + lresult = unaryBehavior.evaluateUnary(out.getSize(), in[0].getSize(), + longVal1); + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_NEGATE: val1 = vContext.getValue(in[0], false, evaluator); - result = vContext.createConstantVarnode( - ~vContext.getConstant(val1, evaluator), val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + if (longVal1 != null) { + result = vContext.createConstantVarnode( + ~longVal1, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; @@ -1236,9 +1287,12 @@ public class SymbolicPropogator { else { val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lresult = vContext.getConstant(val1, evaluator) ^ - vContext.getConstant(val2, evaluator); - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = longVal1 ^ longVal2; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } } vContext.putValue(out, result, mustClearAll); break; @@ -1267,100 +1321,117 @@ public class SymbolicPropogator { case PcodeOp.INT_RIGHT: val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lresult = vContext.getConstant(val1, evaluator) >> vContext - .getConstant(val2, evaluator); - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = longVal1 >> longVal2 ; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_SRIGHT: val1 = vContext.getValue(in[0], true, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lresult = vContext.getConstant(val1, evaluator) >>> vContext - .getConstant(val2, evaluator); - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = longVal1 >>> longVal2; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_MULT: val1 = vContext.getValue(in[0], true, evaluator); val2 = vContext.getValue(in[1], true, evaluator); - lresult = vContext.getConstant(val1, evaluator) * - vContext.getConstant(val2, evaluator); - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = longVal1 * longVal2; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_DIV: val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lval1 = vContext.getConstant(val1, evaluator); - lval2 = vContext.getConstant(val2, evaluator); - if (lval2 == 0) { - throw divideByZeroException; + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null & longVal2 != null) { + if (longVal2 != 0) { + lresult = longVal1 / longVal2; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } } - lresult = lval1 / lval2; - result = vContext.createConstantVarnode(lresult, val1.getSize()); vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_SDIV: val1 = vContext.getValue(in[0], true, evaluator); val2 = vContext.getValue(in[1], true, evaluator); - lval1 = vContext.getConstant(val1, evaluator); - lval2 = vContext.getConstant(val2, evaluator); - if (lval2 == 0) { - throw divideByZeroException; + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + if (longVal2 != 0) { + lresult = longVal1 / longVal2; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } } - lresult = lval1 / lval2; - result = vContext.createConstantVarnode(lresult, val1.getSize()); vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_REM: val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lval1 = vContext.getConstant(val1, evaluator); - lval2 = vContext.getConstant(val2, evaluator); - if (lval2 == 0) { - throw divideByZeroException; + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + if (longVal2 != 0) { + lresult = longVal1 % longVal2; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } } - lresult = lval1 % lval2; - result = vContext.createConstantVarnode(lresult, val1.getSize()); vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_SREM: val1 = vContext.getValue(in[0], true, evaluator); val2 = vContext.getValue(in[1], true, evaluator); - lval1 = vContext.getConstant(val1, evaluator); - lval2 = vContext.getConstant(val2, evaluator); - if (lval2 == 0) { - throw divideByZeroException; + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + if (longVal2 != 0) { + lresult = longVal1 % longVal2; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } } - lresult = lval1 % lval2; - result = vContext.createConstantVarnode(lresult, val1.getSize()); vContext.putValue(out, result, mustClearAll); break; case PcodeOp.SUBPIECE: val1 = vContext.getValue(in[0], true, evaluator); val2 = vContext.getValue(in[1], true, evaluator); - long subbyte = 8 * vContext.getConstant(val2, evaluator); - - if (vContext.isSymbol(val1) & subbyte == 0 && - out.getSize() == instruction.getAddress().getPointerSize()) { - // assume the subpiece is just downcasting to be used as a pointer, just ignore, since this is already an offset, and shouldn't matter. - result = val1; - - } - else if (out.getSize() > 8) { - throw valueTooBigException; - } - else { - lresult = (vContext.getConstant(val1, evaluator) >> (subbyte)) & - maskSize[out.getSize()]; - result = vContext.createConstantVarnode(lresult, out.getSize()); + longVal2 = vContext.getConstant(val2, evaluator); + if (val1 != null && longVal2 != null) { + long subbyte = 8 * longVal2; + + if (vContext.isSymbol(val1) & subbyte == 0 && + out.getSize() == instruction.getAddress().getPointerSize()) { + // assume the subpiece is just downcasting to be used as a pointer, just ignore, since this is already an offset, and shouldn't matter. + result = val1; + } + else if (out.getSize() > 8) { + // too big, result will be null + } + else { + longVal1 = vContext.getConstant(val1, evaluator); + if (longVal1 != null) { + lresult = (longVal1 >> (subbyte)) & maskSize[out.getSize()]; + result = vContext.createConstantVarnode(lresult, out.getSize()); + } + } } vContext.putValue(out, result, mustClearAll); break; @@ -1368,93 +1439,118 @@ public class SymbolicPropogator { case PcodeOp.INT_LESS: val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lval1 = vContext.getConstant(val1, evaluator); - lval2 = vContext.getConstant(val2, evaluator); - lresult = Long.compareUnsigned(lval1, lval2) < 0 ? 1 : 0; - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = Long.compareUnsigned(longVal1, longVal2) < 0 ? 1 : 0; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_SLESS: val1 = vContext.getValue(in[0], true, evaluator); val2 = vContext.getValue(in[1], true, evaluator); - lval1 = vContext.getConstant(val1, evaluator); - lval2 = vContext.getConstant(val2, evaluator); - lresult = (vContext.getConstant(val1, evaluator) < vContext - .getConstant(val2, evaluator)) ? 1 : 0; - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = (longVal1 < longVal2) ? 1 : 0; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_LESSEQUAL: val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lval1 = vContext.getConstant(val1, evaluator); - lval2 = vContext.getConstant(val2, evaluator); - lresult = Long.compareUnsigned(lval1, lval2) <= 0 ? 1 : 0; - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = Long.compareUnsigned(longVal1, longVal2) <= 0 ? 1 : 0; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_SLESSEQUAL: val1 = vContext.getValue(in[0], true, evaluator); val2 = vContext.getValue(in[1], true, evaluator); - lresult = (vContext.getConstant(val1, evaluator) <= vContext - .getConstant(val2, evaluator)) ? 1 : 0; - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = (longVal1 <= longVal2) ? 1 : 0; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_EQUAL: - val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lresult = (vContext.getConstant(val1, evaluator) == vContext - .getConstant(val2, evaluator)) ? 1 : 0; - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = (longVal1 == longVal2) ? 1 : 0; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.INT_NOTEQUAL: val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lresult = (vContext.getConstant(val1, evaluator) != vContext - .getConstant(val2, evaluator)) ? 1 : 0; - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = (longVal1 != longVal2) ? 1 : 0; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.BOOL_NEGATE: val1 = vContext.getValue(in[0], false, evaluator); - lresult = (vContext.getConstant(val1, evaluator) == 0 ? 1 : 0); - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + if (longVal1 != null) { + lresult = (longVal1 == 0 ? 1 : 0); + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.BOOL_XOR: val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lresult = vContext.getConstant(val1, evaluator) ^ - vContext.getConstant(val2, evaluator); - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = longVal1 ^ longVal2; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.BOOL_AND: val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lresult = vContext.getConstant(val1, evaluator) & - vContext.getConstant(val2, evaluator); - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = longVal1 & longVal2; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; case PcodeOp.BOOL_OR: val1 = vContext.getValue(in[0], false, evaluator); val2 = vContext.getValue(in[1], false, evaluator); - lresult = vContext.getConstant(val1, evaluator) | - vContext.getConstant(val2, evaluator); - result = vContext.createConstantVarnode(lresult, val1.getSize()); + longVal1 = vContext.getConstant(val1, evaluator); + longVal2 = vContext.getConstant(val2, evaluator); + if (longVal1 != null && longVal2 != null) { + lresult = longVal1 | longVal2; + result = vContext.createConstantVarnode(lresult, val1.getSize()); + } vContext.putValue(out, result, mustClearAll); break; @@ -1466,12 +1562,6 @@ public class SymbolicPropogator { break; } } - catch (NotFoundException e) { - // didn't have a value for some piece of the computation - if (out != null) { - vContext.putValue(out, vContext.createBadVarnode(), false); - } - } catch (AddressOutOfBoundsException e) { // The computation did something bad for some reason, need to null the out varnode if (out != null) { @@ -1482,24 +1572,17 @@ public class SymbolicPropogator { vContext.propogateResults(true); - Address fallthru = instruction.getFallThrough(); - if (ptype == PcodeOp.BRANCH || ptype == PcodeOp.RETURN || ptype == PcodeOp.BRANCHIND) { - // if says this is branch, but has a fallthru, then really isn't a fallthru - // assume the future flow will have flowed the correct info. - // only assign for branch if it isn't a degenerate fallthru to itself - if (!minInstrAddress.equals(fallthru)) { - nextAddr = fallthru; - } - } - - return nextAddr; + return true; } private Varnode getConstantOrExternal(VarnodeContext vContext, Address minInstrAddress, - Varnode val1) throws NotFoundException { + Varnode val1) { Varnode vt; if (!context.isExternalSpace(val1.getSpace())) { - long lval = vContext.getConstant(val1, evaluator); + Long lval = vContext.getConstant(val1, evaluator); + if (lval == null) { + return null; + } vt = vContext.getVarnode(minInstrAddress.getAddressSpace().getSpaceID(), lval, 0); } else { @@ -1515,15 +1598,7 @@ public class SymbolicPropogator { return null; } - try { - // out is a calculated location for store. If got to here, need to set out - // because it might need to be cleared by a bad value access! - out = vContext.getVarnode(space, offset, size.getSize(), evaluator); - } - catch (NotFoundException e) { - // if can't get the value of the relative store location - // this isn't an exception, the output will be null/unknown - } + out = vContext.getVarnode(space, offset, size.getSize(), evaluator); return out; } @@ -1533,7 +1608,7 @@ public class SymbolicPropogator { Function targetFunc = null; if (target != null) { - targetFunc = program.getFunctionManager().getFunctionAt(target); + targetFunc = getFunctionAt(target); } Address fallThruAddr = instruction.getFallThrough(); // if the call is right below this routine, ignore the call @@ -1573,6 +1648,10 @@ public class SymbolicPropogator { if (targetFunc != null && targetFunc.isInline()) { return; } + + if (targetFunc != null && targetFunc.hasNoReturn()) { + context.propogateResults(false); + } // Update the stack offset if necessary Varnode outStack = context.getStackVarnode(); @@ -1607,15 +1686,13 @@ public class SymbolicPropogator { } } else if (purge != 0) { - try { Varnode purgeVar = context.createConstantVarnode(purge, outStack.getSize()); Varnode val1 = context.getValue(outStack, true, evaluator); - Varnode val2 = context.add(val1, purgeVar, evaluator); + Varnode val2 = null; + if (val1 != null) { + val2 = context.add(val1, purgeVar, evaluator); + } context.putValue(outStack, val2, false); - } - catch (NotFoundException e) { - context.putValue(outStack, context.createBadVarnode(), false); - } } } } @@ -1736,10 +1813,8 @@ public class SymbolicPropogator { * @param out output varnode for pcodeop * @return pcode that should replace callother, null otherwise * - * @throws NotFoundException */ - private PcodeOp[] doCallOtherPcodeInjection(Instruction instr, Varnode ins[], Varnode out) - throws NotFoundException { + private PcodeOp[] doCallOtherPcodeInjection(Instruction instr, Varnode ins[], Varnode out) { Program prog = instr.getProgram(); PcodeInjectLibrary snippetLibrary = prog.getCompilerSpec().getPcodeInjectLibrary(); @@ -1752,7 +1827,7 @@ public class SymbolicPropogator { ArrayList inputs = new ArrayList(); for (int i = 1; i < ins.length; i++) { Varnode vval = context.getValue(ins[i], evaluator); - if (!context.isConstant(vval)) { + if (vval == null || !context.isConstant(vval)) { return null; } inputs.add(vval); @@ -1906,6 +1981,8 @@ public class SymbolicPropogator { PrototypeModel conv; conv = program.getCompilerSpec().getDefaultCallingConvention(); + int extraParamIndex = -1; + Parameter[] params = new Parameter[0]; SourceType signatureSource = SourceType.DEFAULT; if (func != null) { @@ -1914,6 +1991,15 @@ public class SymbolicPropogator { conv = funcConv; } params = func.getParameters(); + // if function is in a namespace, add an extra param index to check, params could be off by thisCall + // not being set + Namespace parentNamespace = func.getParentNamespace(); + if (parentNamespace != null && parentNamespace instanceof GhidraClass) { + PrototypeModel thisConv = program.getCompilerSpec().getCallingConvention(CompilerSpec.CALLING_CONVENTION_thiscall); + if (conv != thisConv) { + extraParamIndex = params.length; + } + } signatureSource = func.getSignatureSource(); } else if (checkForParamPointerRefs) { @@ -1942,6 +2028,9 @@ public class SymbolicPropogator { if (checkForParamPointerRefs) { continue; } + if (dataType instanceof TypeDef tdef) { + dataType = tdef.getBaseDataType(); + } // if undefined, or int/long could still be pointer if (!(Undefined.isUndefined(dataType) || dataType instanceof IntegerDataType)) { continue; @@ -1955,6 +2044,15 @@ public class SymbolicPropogator { createVariableStorageReference(instruction, varnodeContext, monitor, conv, p.getVariableStorage(), dataType, callOffset); } + if (extraParamIndex != -1) { + // TODO Should cache the arg locations for each convention + VariableStorage var = conv.getArgLocation(extraParamIndex, null, pointerSizedDT, program); + // can't trust stack storage if params aren't known + if (!var.isStackStorage()) { + createVariableStorageReference(instruction, varnodeContext, monitor, conv, var, + null, callOffset); + } + } } else if (!checkForParamPointerRefs) { // loop through potential params, since none defined, to find a potential pointer @@ -2024,7 +2122,7 @@ public class SymbolicPropogator { if (newTarget != null ) { makeReference(vContext, instruction, Reference.MNEMONIC, newTarget.getAddressSpace().getSpaceID(), newTarget.getOffset(), 0, - null, reftype.DATA, pcodeType, false, false, monitor); + null, RefType.DATA, pcodeType, false, false, monitor); return; } } @@ -2121,25 +2219,18 @@ public class SymbolicPropogator { // translate the variable relative to the current stackpointer symbolic value Varnode stackVarnode = varnodeContext.getStackVarnode(); - Varnode stackVal = null; - try { - stackVal = varnodeContext.getValue(stackVarnode, null); - if (stackVal == null) { - return; - } - } catch (NotFoundException e) { + Varnode stackVal = varnodeContext.getValue(stackVarnode, null); + if (stackVal == null) { return; } Varnode realSPVarnode = varnodeContext.createVarnode(stackVal.getOffset() + sVnode.getOffset(), stackVal.getSpace(), sVnode.getAddress().getAddressSpace().getPointerSize()); Varnode value = null; - try { - value = varnodeContext.getValue(realSPVarnode,evaluator); - } - catch (NotFoundException e) { + value = varnodeContext.getValue(realSPVarnode,evaluator); + if (value == null) { return; - } + } if (!varnodeContext.isConstant(value)) { return; @@ -2163,7 +2254,6 @@ public class SymbolicPropogator { } reg = rval.getRegister(); - bval = rval.getUnsignedValue(); lastSetAddr = varnodeContext.getLastSetLocation(reg, bval); // if instruction has a delay slot, carefully check the location of the diff --git a/Ghidra/Features/Base/src/main/java/ghidra/program/util/VarnodeContext.java b/Ghidra/Features/Base/src/main/java/ghidra/program/util/VarnodeContext.java index 580072b8f4..06eafbfa66 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/program/util/VarnodeContext.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/program/util/VarnodeContext.java @@ -17,12 +17,12 @@ package ghidra.program.util; import java.math.BigInteger; import java.util.*; -import java.util.Map.Entry; + +import javax.help.UnsupportedOperationException; import org.apache.commons.lang3.ArrayUtils; import ghidra.app.plugin.processors.sleigh.SleighLanguage; -import ghidra.program.disassemble.DisassemblerContextImpl; import ghidra.program.model.address.*; import ghidra.program.model.data.*; import ghidra.program.model.lang.*; @@ -37,19 +37,47 @@ import ghidra.program.model.symbol.ReferenceIterator; import ghidra.util.Msg; import ghidra.util.exception.*; + public class VarnodeContext implements ProcessorContext { - protected DisassemblerContextImpl offsetContext; - protected DisassemblerContextImpl spaceContext; + // trace stack for the saved states during forks of execution flow + // the traces are poped off the stack to restart a previous flow + protected Stack>> memTraces = + new Stack>>(); + protected Stack>> regTraces = + new Stack>>(); + protected Stack>> uniqueTraces = + new Stack>>(); + protected Stack> lastSetSaves = + new Stack>(); - // holds temp memory values for computation - protected Stack> memoryVals = new Stack>(); + HashMap> flowToFromLists = new HashMap<>(); + + // + // Trace record used to keep access flow state stack at beginning and end of each instruction + // + record TraceDepthState(int depth, Stack> state) { } - // holds temp values for computation - private HashMap tempVals = new HashMap<>(); - protected HashMap tempUniqueVals = new HashMap<>(); // unique's stored only by offset + // references to stack based traces at the beginning and ending of each instruction + HashMap addrStartState = new HashMap<>(); + HashMap addrEndState = new HashMap<>(); + + // current flow memory values for computation + protected Stack> memoryVals = new Stack>(); + + // current flow register values for computation + protected Stack> regVals = new Stack>(); + + // current flow unique values for computation + protected Stack> uniqueVals = new Stack>(); + + // temp values for individual instruction computation before being merged into + // the end flow state for an instruction + private HashMap tempVals = new HashMap<>(); + protected HashMap tempUniqueVals = new HashMap<>(); protected boolean keepTempUniqueValues = false; + // Values that must be cleared from final instruction flow state protected HashSet clearVals = new HashSet<>(); // locations where registers were last set to a constant value @@ -62,29 +90,30 @@ public class VarnodeContext implements ProcessorContext { protected VarnodeTranslator trans; // translator for varnodes<-->registers protected Varnode[] retVarnodes = null; // varnodes used to return values - + protected Varnode[] killedVarnodes = null; // varnodes killed by default calling convention - protected Varnode stackVarnode = null; // varnode that represents the stack protected Register stackReg = null; private HashSet validSymbolicStackNames = new HashSet<>(); // list of stack related register names - protected static final NotFoundException notFoundExc = new NotFoundException(); - public final Address BAD_ADDRESS; - + + public final Varnode BAD_VARNODE; + private final int BAD_OFFSET_SPACEID; // address space for offsets from an unknown value; - + static final String SUSPECT_CONST_NAME = "SuspectConst"; private final int SUSPECT_OFFSET_SPACEID; // address space for suspect constant values public final Address SUSPECT_ZERO_ADDRESS; - + public final int BAD_SPACE_ID_VALUE; - - private static final BigInteger BIG_NEGATIVE_ONE = BigInteger.ONE.negate(); + + Varnode byteVarnodes[] = new Varnode[256]; protected boolean hitDest = false; + + protected int pointerBitSize; protected AddressFactory addrFactory = null; @@ -93,32 +122,41 @@ public class VarnodeContext implements ProcessorContext { protected Instruction currentInstruction = null; + boolean isBE = false; + + boolean trackStartEndState = false; + public boolean debug = false; public VarnodeContext(Program program, ProgramContext programContext, - ProgramContext spaceProgramContext) { + ProgramContext spaceProgramContext, boolean trackStartEndState) { this.program = program; + this.isBE = program.getLanguage().isBigEndian(); + this.trackStartEndState = trackStartEndState; // make a copy, because we could be making new spaces. this.addrFactory = new OffsetAddressFactory(program); + + pointerBitSize = program.getDefaultPointerSize() * 8; - BAD_ADDRESS = addrFactory.getAddress(getAddressSpace("BAD_ADDRESS_SPACE"), 0); + BAD_ADDRESS = addrFactory.getAddress(getAddressSpace("BAD_ADDRESS_SPACE",pointerBitSize), 0); BAD_SPACE_ID_VALUE = BAD_ADDRESS.getAddressSpace().getSpaceID(); - - BAD_OFFSET_SPACEID = getAddressSpace("(Bad Address Offset)"); - + + BAD_OFFSET_SPACEID = getAddressSpace("(Bad Address Offset)",pointerBitSize); + + BAD_VARNODE = createBadVarnode(); + /* Suspect constants act like constants, but are in a SuspectConst * address space instead of the constant space. */ - SUSPECT_ZERO_ADDRESS = addrFactory.getAddress(getAddressSpace(SUSPECT_CONST_NAME), 0); - SUSPECT_OFFSET_SPACEID = SUSPECT_ZERO_ADDRESS.getAddressSpace().getSpaceID(); + SUSPECT_ZERO_ADDRESS = addrFactory.getAddress(getAddressSpace(SUSPECT_CONST_NAME,pointerBitSize), 0); + SUSPECT_OFFSET_SPACEID = SUSPECT_ZERO_ADDRESS.getAddressSpace().getSpaceID(); this.programContext = programContext; - offsetContext = new DisassemblerContextImpl(programContext); - spaceContext = new DisassemblerContextImpl(spaceProgramContext); - - memoryVals.push(new HashMap()); + memoryVals.push(new HashMap()); + regVals.push((new HashMap())); + uniqueVals.push(new HashMap()); setupValidSymbolicStackNames(program); @@ -163,102 +201,54 @@ public class VarnodeContext implements ProcessorContext { return null; } - public void flowEnd(Address address) { - offsetContext.flowEnd(address); - spaceContext.flowEnd(address); - currentAddress = null; + // return any known flowAddresses to the toAddr + static final Address[] emptyAddrArr = new Address[0]; + + public Address[] getKnownFlowToAddresses(Address toAddr) { + + ArrayList
arrayList = flowToFromLists.get(toAddr); + if (arrayList == null) { + return emptyAddrArr; + } + return arrayList.toArray(emptyAddrArr); } + /** + * Records flow from/to basic blocks, or non-fallthru flow + */ public void flowToAddress(Address fromAddr, Address toAddr) { // make sure address in same space as from, might be in an overlay toAddr = fromAddr.getAddressSpace().getOverlayAddress(toAddr); - - currentAddress = toAddr; - offsetContext.flowToAddress(fromAddr, toAddr); - spaceContext.flowToAddress(fromAddr, toAddr); - } - - // return any known flowAddresses to the toAddr - public Address[] getKnownFlowToAddresses(Address toAddr) { - return offsetContext.getKnownFlowToAddresses(toAddr); - } - - public void flowStart(Address fromAddr, Address toAddr) { - // make sure address in same space as from, might be in an overlay - toAddr = fromAddr.getAddressSpace().getOverlayAddress(toAddr); - currentAddress = toAddr; - this.lastSet = new HashMap<>(); // clear out any interim last sets... rely on allLastSet now - - offsetContext.flowStart(fromAddr, toAddr); - spaceContext.flowStart(fromAddr, toAddr); - } - - public void copyToFutureFlowState(Address fromAddr, Address toAddr) { - // make sure address in same space as from, might be in an overlay - toAddr = fromAddr.getAddressSpace().getOverlayAddress(toAddr); - - offsetContext.copyToFutureFlowState(fromAddr, toAddr); - spaceContext.copyToFutureFlowState(fromAddr, toAddr); - } - - public boolean mergeToFutureFlowState(Address fromAddr, Address toAddr) { - if (toAddr == null) { - return false; + ArrayList
arrayList = flowToFromLists.get(toAddr); + if (arrayList == null) { + arrayList = new ArrayList
(); + flowToFromLists.put(fromAddr, arrayList); } - - // make sure address in same space as from, might be in an overlay - toAddr = fromAddr.getAddressSpace().getOverlayAddress(toAddr); - - ArrayList conflicts = offsetContext.mergeToFutureFlowState(fromAddr, toAddr); - ArrayList spaceConflicts = - spaceContext.mergeToFutureFlowState(fromAddr, toAddr); - conflicts.addAll(spaceConflicts); - - if (conflicts.size() == 0) { - return false; - } - - // TODO: check if any of the conflicting values have a constant in them. - // if they do, continue processing this flow, if not, then no-need. - // someone else will pick up the constant value and flow it. - boolean isWorthContinueing = false; - for (RegisterValue registerValue : spaceConflicts) { - if (!registerValue.hasValue()) { - continue; - } - registerValue.getUnsignedValue(); - if (BigInteger.ZERO.equals(registerValue.getUnsignedValue())) { - isWorthContinueing = true; - } - } - - // TODO: HACK alert. If the size of the instruction is 1, can't really detect a flow conflict. - // This is the root of all evil with this storage tracking mechanism. -// TODO: for instruction size one, should we set flow context, now that contexts are stored -// in separate states? -// if (program.getLanguage().getInstructionAlignment() == 1) { -// // only do on unalligned processors (x86) -// Instruction instr = program.getListing().getInstructionAt(toAddr); -// if (instr != null && instr.getLength() == 1) { -// return false; -// } -// } -// for (Iterator iterator = conflicts.iterator(); iterator.hasNext();) { -// -// TODO: do not use reg which appears same as initial input state -// Register reg = iterator.next(); -// offsetContext.setValue(reg, address, BigInteger.valueOf(reg.getOffset())); -// spaceContext.setValue(reg, address, -// BigInteger.valueOf(reg.getAddressSpace().getUniqueSpaceID())); -// } - return isWorthContinueing; + arrayList.add(fromAddr); } - - public void setFutureRegisterValue(Address address, RegisterValue regVal) { - offsetContext.setFutureRegisterValue(address, regVal); - // Don't set the space ID, since no spaceID means constant. + + /** + * Start flow at an address, recording any initial state for the current instruction + */ + public void flowStart(Address toAddr) { + currentAddress = toAddr; + + if (trackStartEndState) { + addrStartState.put(toAddr,new TraceDepthState(regVals.size(),regVals)); + regVals.push(new HashMap()); + } + } + + /** + * End flow and save any necessary end flow state for the current instruction at address + */ + public void flowEnd(Address address) { + if (trackStartEndState) { + addrEndState.put(address,new TraceDepthState(regVals.size(),regVals)); + } + currentAddress = null; } /** @@ -311,7 +301,7 @@ public class VarnodeContext implements ProcessorContext { } return retVarnodes; } - + /** * * @param targetFunc function to get killed varnodes for @@ -327,8 +317,8 @@ public class VarnodeContext implements ProcessorContext { if (targetFunc != null) { // TODO handle custom calling convention killed by call when supported - PrototypeModel callingConvention = targetFunc.getCallingConvention(); - + PrototypeModel callingConvention = targetFunc.getCallingConvention(); + if (callingConvention != null) { return callingConvention.getKilledByCallList(); } @@ -338,9 +328,9 @@ public class VarnodeContext implements ProcessorContext { if (killedVarnodes != null) { return killedVarnodes; } - + killedVarnodes = defaultCallingConvention.getKilledByCallList(); - + // clean return varnodes out of list Varnode[] returnVarnodes = getReturnVarnode(null); ArrayList list = new ArrayList(); @@ -409,58 +399,72 @@ public class VarnodeContext implements ProcessorContext { return stackReg; } - public Varnode getValue(Varnode varnode, ContextEvaluator evaluator) throws NotFoundException { + public Varnode getValue(Varnode varnode, ContextEvaluator evaluator) { + if (varnode == null) { + return null; + } return getValue(varnode, false, evaluator); } - public Varnode getValue(Varnode varnode, boolean signed, ContextEvaluator evaluator) - throws NotFoundException { + public Varnode getValue(Varnode varnode, boolean signed, ContextEvaluator evaluator) { + if (varnode == null) { + return null; + } // for constant, return the constant value if (isConstant(varnode)) { return varnode; } Varnode rvnode = null; if (varnode.isUnique()) { - rvnode = tempUniqueVals.get(varnode.getOffset()); + rvnode = getMemoryValue(tempUniqueVals,varnode,signed); + if (rvnode == null && keepTempUniqueValues) { + rvnode = getMemoryValue(uniqueVals,0,varnode,signed); + } } else { - rvnode = tempVals.get(varnode); + rvnode = getMemoryValue(tempVals, varnode, signed); } if (rvnode != null) { if (debug) { Msg.info(this, " Tmp " + varnode + " = " + rvnode); } if (rvnode.getAddress().equals(BAD_ADDRESS)) { - throw notFoundExc; + return null; } return rvnode; } if (isRegister(varnode)) { - Register reg = trans.getRegister(varnode); - if (reg != null) { - BigInteger bigVal = offsetContext.getValue(reg, signed); - if (bigVal != null) { + Varnode value = getMemoryValue(this.regVals, 0, varnode, signed); + if (value != null) { + int spaceVal = value.getSpace(); + + if (value.isConstant()) { + long lvalue = value.getOffset(); + int size = value.getSize(); - BigInteger spaceVal = getTranslatedSpaceValue(reg); // -1 and zero constants pulled from a register are suspect - if (spaceVal == null && (bigVal.equals(BIG_NEGATIVE_ONE) || bigVal.equals(BigInteger.ZERO))) { - spaceVal = BigInteger.valueOf(SUSPECT_OFFSET_SPACEID); + if ((value.getOffset() == -1 || value.getOffset() == 0)) { + spaceVal = SUSPECT_OFFSET_SPACEID; + value = createVarnode(lvalue, spaceVal, size); } - rvnode = createVarnode(bigVal, spaceVal, varnode.getSize()); - if (rvnode == null) { - throw notFoundExc; - } - - if (debug) { - Msg.info(this, " " + reg.getName() + " = " + print(rvnode)); - } - - // value is bad, just return original, someone else will deal with it - if (!rvnode.getAddress().equals(BAD_ADDRESS)) { - return rvnode; + else if (signed) { + lvalue = (lvalue << 8 * (8 - size)) >> 8 * (8 - size); + value = createVarnode(lvalue, spaceVal, size); } } + rvnode = value; + + if (debug) { + Register reg = trans.getRegister(varnode); + String name = (reg != null ? reg.getName() : varnode.toString()); + Msg.info(this, " " + name + " = " + print(rvnode)); + } + + // value is bad, just return original, someone else will deal with it + if (!rvnode.getAddress().equals(BAD_ADDRESS)) { + return rvnode; + } } return varnode; // just return the register then, someone else will deal with it... @@ -474,11 +478,11 @@ public class VarnodeContext implements ProcessorContext { long varnodeOffset = varnode.getOffset(); if (isAddr && (varnodeOffset == 0 || varnodeOffset == 0xffffffff || varnodeOffset == -1L)) { - throw notFoundExc; + return null; } // see if we wrote a value to memory here - Varnode lvalue = getMemoryValue(varnode); + Varnode lvalue = getMemoryValue(varnode, signed); if (lvalue != null) { if (debug) { Msg.info(this, " " + varnode + " = " + print(lvalue)); @@ -486,14 +490,10 @@ public class VarnodeContext implements ProcessorContext { // if this is an offset reference, ONLY allow it to be offset into the stack, no other register offset. // can't count on the offset staying the same. if (isSymbolicAddr) { - // symbolic spaces are off of a register, find the space. - AddressSpace regSpace = addrFactory.getAddressSpace(varnode.getSpace()); - // figure out what register is used for stack values - Register stackRegister = getStackRegister(); - // don't allow a zero/-1 constant pulled from a symbolic space. - if (isConstant(lvalue) && (lvalue.getOffset() == 0 || lvalue.getOffset() == -1)) { - throw notFoundExc; + if (isConstant(lvalue) && + (lvalue.getOffset() == 0 || lvalue.getOffset() == -1)) { + return null; } } return lvalue; @@ -503,21 +503,13 @@ public class VarnodeContext implements ProcessorContext { Address addr = varnode.getAddress(); // if this reference belongs in this address space, must re-map it - if (this.spaceContext.getAddress().getAddressSpace().isOverlaySpace()) { - addr = this.spaceContext.getAddress().getAddressSpace().getOverlayAddress(addr); + if (currentAddress.getAddressSpace().isOverlaySpace()) { + addr = currentAddress.getAddressSpace().getOverlayAddress(addr); } if (isSymbolicAddr) { - throw notFoundExc; + return null; } - // if this is an offset address, find out if we can assume an address into memory -// if ( evaluator != null && isSymbolicSpacevarnode.getSpace()) ) { -// Instruction instr = program.getListing().getInstructionContaining(offsetContext.getAddress()); -// Long lval = evaluator.unknownValue(this, instr, varnode); -// if (lval != null) { -// addr = this.program.getMinAddress().getNewAddress(lval.longValue()+varnode.getOffset()); -// } -// } if (this.program.getListing().getInstructionContaining(addr) != null) { hitDest = true; @@ -527,7 +519,8 @@ public class VarnodeContext implements ProcessorContext { Reference[] refsFrom = program.getReferenceManager().getReferencesFrom(addr); if (refsFrom.length > 0 && refsFrom[0].isExternalReference()) { Address external = refsFrom[0].getToAddress(); - return createVarnode(external.getOffset(), external.getAddressSpace().getSpaceID(), 0); + return createVarnode(external.getOffset(), external.getAddressSpace().getSpaceID(), + 0); } // If the memory is Writeable, then maybe don't trust it @@ -535,12 +528,12 @@ public class VarnodeContext implements ProcessorContext { if (!isReadOnly) { // don't try to see how far away if it is in a different space. if (addr.getAddressSpace() - .equals(this.spaceContext.getAddress().getAddressSpace())) { - long diff = addr.subtract(this.spaceContext.getAddress()); + .equals(currentAddress.getAddressSpace())) { + long diff = addr.subtract(currentAddress); // if the value loaded is too far away, ask the evaluator if it should be trusted. if (diff < 0 || diff > 4096) { if (evaluator != null && !evaluator.allowAccess(this, addr)) { - throw notFoundExc; + return null; } } } @@ -562,12 +555,12 @@ public class VarnodeContext implements ProcessorContext { value = this.program.getMemory().getLong(addr); break; default: - throw notFoundExc; + return null; } // Don't trust zero values loaded out of memory, even if it is read-only memory. if (value == 0) { - throw notFoundExc; + return null; } if (signed) { @@ -590,32 +583,155 @@ public class VarnodeContext implements ProcessorContext { // is there an assumed value that should be returned for any unknown value? if (evaluator != null && !varnode.isAddress()) { - Instruction instr = getCurrentInstruction(offsetContext.getAddress()); + Instruction instr = getCurrentInstruction(currentAddress); Long lval = evaluator.unknownValue(this, instr, varnode); if (lval == null && !varnode.isUnique()) { return varnode; } } - throw notFoundExc; + return null; } /** - * Search the value state stack for the first occurence of the set value + * Search the value state stack for the first occurrence of the set value * * @param varnode varnode to search for a value + * @param signed true if retrieving a signed value * @return first value found on stack, null otherwise */ - protected Varnode getMemoryValue(Varnode varnode) { + protected Varnode getMemoryValue(Varnode varnode, boolean signed) { + return getMemoryValue(memoryVals, 0, varnode, signed); + } + + protected Varnode getMemoryValue(List> valStore, int backupDepth, Varnode varnode, + boolean signed) { // traverse pushed memory value states until find value // if don't find, return null - for (int i = memoryVals.size() - 1; i >= 0; i--) { - HashMap stateLayer = memoryVals.get(i); - Varnode value = stateLayer.get(varnode); - if (value != null) { - return value; + + // build up an array entry for each byte, if any missing, return + int size = varnode.getSize(); + Varnode split[] = new Varnode[size]; + Address addr = varnode.getAddress(); + + for (int i = 0; i < size; i++) { + // go to thru stack til hit for each i + // accumulate each byte, if get to end, fail + HashMap stateLayer = null; + int layer = valStore.size() - 1 - backupDepth; + while (layer >= 0) { + stateLayer = valStore.get(layer); + if (stateLayer == null) { + break; + } + + Varnode value = stateLayer.get(addr.addWrapSpace(i)); + if (value != null) { + split[i] = value; + break; + } + stateLayer = null; + layer--; + } + ; + + if (stateLayer == null) { + return null; } } - return null; + + // no have an array, re-assemble + // if const, then each must be a const byte + // if symbolic, each must be same symbolic value of correct size + long value = 0; + Varnode type = split[0]; + int typesize = type.getSize(); + boolean isconst = type.isConstant(); + if (!isconst && (typesize != 0 && typesize != size)) { + return null; + } + for (int i = 0; i < split.length; i++) { + Varnode vb = split[i]; + if (vb.getSpace() != type.getSpace()) { + return null; + } + if (isconst) { + // assemble constant + value |= (vb.getOffset() << (isBE ? (size - i - 1) : i) * 8); + } + else if (type != vb) { + return null; + } + } + + if (isconst) { + if (size != 0) { + value = (!signed ? value : ((value << (8 - size) * 8)) >> ((8 - size) * 8)); + } + return createConstantVarnode(value, size); + } + + if (signed && typesize != 0 && typesize < 8) { + value = type.getOffset(); + value = ((value << (8 - size) * 8)) >> ((8 - size) * 8); + return createVarnode(value, type.getSpace(), type.getSize()); + } + return type; + } + + protected Varnode getMemoryValue(HashMap valStore, Varnode varnode, + boolean signed) { + + // build up an array entry for each byte, if any missing, return + int size = varnode.getSize(); + Varnode split[] = new Varnode[size]; + Address addr = varnode.getAddress(); + for (int i = 0; i < size; i++) { + // go thru stack til hit for each i + // accumulate each byte, if get to end, fail + Varnode value = valStore.get(addr.addWrapSpace(i)); + if (value == null) { + return null; + } + split[i] = value; + } + + // now have an array, re-assemble + // if const, then each must be a const byte + // if symbolic, each must be same symbolic value of correct size + long value = 0; + Varnode type = split[0]; + int typesize = type.getSize(); + boolean isconst = type.isConstant(); + if (!isconst && typesize != 0 && typesize != size) { + return null; + } + for (int i = 0; i < split.length; i++) { + Varnode vb = split[i]; + if (vb.getSpace() != type.getSpace()) { + return null; + } + if (isconst) { + // assemble constant + value |= (vb.getOffset() << (isBE ? (size - i - 1) : i) * 8); + } + else if (type != vb) { + return null; + } + } + + if (isconst) { + if (size != 0) { + value = (!signed ? value : ((value << (8 - size) * 8)) >> ((8 - size) * 8)); + } + return createConstantVarnode(value, size); + } + + if (signed && typesize != 0 && typesize < 8) { + value = type.getOffset(); + value = ((value << (8 - size) * 8)) >> ((8 - size) * 8); + return createVarnode(value, type.getSpace(), type.getSize()); + } + return type; } /** @@ -625,44 +741,49 @@ public class VarnodeContext implements ProcessorContext { * @param value value to store for the varnode */ protected void putMemoryValue(Varnode out, Varnode value) { + putMemoryValue(memoryVals, out, value); + + } + + protected void putMemoryValue(Stack> valStore, Varnode out, + Varnode value) { + HashMap top = valStore.peek(); + putMemoryValue(top, out, value); + } + + private void putMemoryValue(HashMap top, Varnode out, Varnode value) { // put the value in the top memory value states - memoryVals.peek().put(out, value); - } + int len = out.getSize(); + Address addr = out.getAddress(); + if (len == 1) { + top.put(addr, value); + return; + } + // TODO: add a byte array value for Suspect constant bytes too + if (!value.isConstant()) { + for (int nodeOff = 0; nodeOff < len; nodeOff++) { + top.put(addr.addWrapSpace(nodeOff), value); + } + return; + } - /** - * get the translated stored space value. - * SpaceID is stored invert'ed so that the constants for subpieces will blend, - * but no other space will. - * - * @return null space for constant space, real spaceID otherwise. - */ - private BigInteger getTranslatedSpaceValue(Register reg) { - BigInteger spaceVal = spaceContext.getValue(reg, true); - if (spaceVal != null) { - spaceVal = spaceVal.not(); // only flip space bits that are non-zero - } - if (spaceVal != null && BigInteger.ZERO.equals(spaceVal)) { - return null; - } - return spaceVal; - } + Varnode split[] = splitToBytes(value); + // copy in partial values after + for (int nodeOff = 0; nodeOff < len; nodeOff++) { + if (split == null) { + top.put(addr.addWrapSpace(nodeOff), BAD_VARNODE); + } + else { + top.put(addr.addWrapSpace(nodeOff), split[nodeOff]); + } + // just put in either bad varnode, or partial varnode - /** - * get the translated stored space value. - * SpaceID is stored invert'ed so that the constants for subpieces will blend, - * but no other space will. - * - * @return null space for constant space, real spaceID otherwise. - */ - private BigInteger getTranslatedSpaceValue(Register reg, Address fromAddr, Address toAddr) { - BigInteger spaceVal = spaceContext.getValue(reg, fromAddr, toAddr, true); - if (spaceVal != null) { - spaceVal = spaceVal.not(); // only flip space bits that are non-zero + // TODO: if not constant, then is bad value + // Could just put a new const varnode for const of right size + /// would make above easier, putting back together + // All const,, full value or vnode+offset, other bad + //value = new Varnode(value.getAddress(),(nodeOff << 8) | value.getSize()); } - if (spaceVal != null && BigInteger.ZERO.equals(spaceVal)) { - return null; - } - return spaceVal; } /** @@ -704,7 +825,8 @@ public class VarnodeContext implements ProcessorContext { if (spaceID == BAD_SPACE_ID_VALUE || spc == null) { addr = BAD_ADDRESS; - } else if (spaceID == BAD_OFFSET_SPACEID) { + } + else if (spaceID == BAD_OFFSET_SPACEID) { // special case of unknown value + constant addr = spc.getTruncatedAddress(value, true); } @@ -716,11 +838,46 @@ public class VarnodeContext implements ProcessorContext { } public Varnode createConstantVarnode(long value, int size) { + if (size == 1) { + byte b = (byte) value; + final int offset = 128; + Varnode bv = byteVarnodes[b + offset]; + if (bv == null) { + AddressSpace spc = addrFactory.getConstantSpace(); + Address addr = spc.getAddress(b & 0xff); + bv = new Varnode(addr, size); + byteVarnodes[b + offset] = bv; + } + return bv; + } AddressSpace spc = addrFactory.getConstantSpace(); Address addr = spc.getAddress(value); return new Varnode(addr, size); } + public Varnode[] splitToBytes(Varnode v) { + if (!isConstant(v)) { + return null; + } + + int size = v.getSize(); + Varnode split[] = new Varnode[size]; + long value = v.getOffset(); + if (isBE) { + for (int i = 0; i < v.getSize(); i++) { + long subv = value >> (i * 8); + split[size - i - 1] = createConstantVarnode(subv, 1); + } + } + else { + for (int i = 0; i < v.getSize(); i++) { + long subv = value >> (i * 8); + split[i] = createConstantVarnode(subv, 1); + } + } + return split; + } + public Varnode createBadVarnode() { return new Varnode(BAD_ADDRESS, 0); } @@ -749,22 +906,26 @@ public class VarnodeContext implements ProcessorContext { return; } + if (result == null) { + putValue(out, BAD_VARNODE, false); + return; + } + boolean isSymbolicAddr = isSymbolicSpace(out.getSpace()); if (out.isAddress() || isSymbolicAddr) { if (!isRegister(out)) { if (debug) { Msg.info(this, " " + print(out) + " <- " + print(result) + " at " + - offsetContext.getAddress()); + currentAddress); } - Address location = offsetContext.getAddress(); - // put the location on both the lastSet, and all locations set - addSetVarnodeToLastSetLocations(out, location); + addSetVarnodeToLastSetLocations(out, currentAddress); // don't put a value into a bad address space // could get values pulled from a different badd address offset - if (isSymbolicAddr && out.getAddress().getAddressSpace().getSpaceID() == BAD_OFFSET_SPACEID) { + if (isSymbolicAddr && + out.getAddress().getAddressSpace().getSpaceID() == BAD_OFFSET_SPACEID) { return; } putMemoryValue(out, result); @@ -773,36 +934,38 @@ public class VarnodeContext implements ProcessorContext { } // don't ever store an unknown unique into a location - if (result != null && result.isUnique()) { + if (result.isUnique()) { result = null; } if (out.isUnique()) { if (mustClear) { result = null; } - tempUniqueVals.put(out.getOffset(), result); + putMemoryValue(tempUniqueVals, out, result); } else { // if storing a bad address, need to create a new register/address // relative symbolic space - if (result != null && result.getAddress()==BAD_ADDRESS) { - - String spaceName = out.getAddress().getAddressSpace().getName(); - Register register = getRegister(out); - // if the register is worth tracking as a potential address space - // with stores/loads of constants to it, create a fake address space for it - if (shouldTrackRegister(register)) { - spaceName = register.getName(); - int newRegSpaceID = getAddressSpace(spaceName+"-"+currentAddress); - result = createVarnode(0, newRegSpaceID, out.getSize()); + if (result != null) { + if (result.getAddress() == BAD_ADDRESS) { + String spaceName = out.getAddress().getAddressSpace().getName(); + Register register = getRegister(out); + // if the register is worth tracking as a potential address space + // with stores/loads of constants to it, create a fake address space for it + if (shouldTrackRegister(register)) { + spaceName = register.getName(); + int newRegSpaceID = getAddressSpace(spaceName + "-" + currentAddress, currentAddress.getSize()); + result = createVarnode(0, newRegSpaceID, out.getSize()); + } } + addSetVarnodeToLastSetLocations(out, currentAddress); } - tempVals.put(out, result); + putMemoryValue(tempVals, out, result); } if (debug) { Msg.info(this, " " + print(out) + " <- " + print(result) + " at " + - offsetContext.getAddress()); + currentAddress); } if (mustClear) { clearVals.add(out); @@ -848,36 +1011,29 @@ public class VarnodeContext implements ProcessorContext { * processing of the instruction is finished, so it's effects should be kept. */ public void propogateResults(boolean clearContext) { - Iterator> iter = tempVals.entrySet().iterator(); - - while (iter.hasNext()) { - Entry element = iter.next(); - - Varnode node = element.getKey(); - if (!isRegister(node)) { - continue; - } + Iterator iterator = clearVals.iterator(); + while (iterator.hasNext()) { + Varnode node = iterator.next(); Register reg = trans.getRegister(node); if (reg == null) { continue; } - Varnode val = element.getValue(); - - // if we must clear the values that should be unknown because of a decision stmt - if (clearVals.contains(node)) { - val = null; - } - if (val != null) { - propogateValue(reg, node, val, offsetContext.getAddress()); - } - else { - if (debug) { - Msg.info(this, " " + reg.getName() + "<-" + " Clear"); - } - clearRegister(reg); + if (debug) { + Msg.info(this, " " + reg.getName() + "<-" + " Clear"); } + clearRegister(reg); } + + // clone temp vals an put at address + + // merge tempvals to top of regVals + regVals.peek().putAll(tempVals); + + if (keepTempUniqueValues) { + uniqueVals.peek().putAll(tempUniqueVals); + } + if (clearContext) { if (!keepTempUniqueValues) { tempUniqueVals = new HashMap<>(); @@ -890,12 +1046,12 @@ public class VarnodeContext implements ProcessorContext { public void propogateValue(Register reg, Varnode node, Varnode val, Address address) { if (debug) { Msg.info(this, " " + reg.getName() + "<-" + val.toString() + " at " + - offsetContext.getAddress()); + currentAddress); } addSetVarnodeToLastSetLocations(node, address); - offsetContext.setValue(reg, BigInteger.valueOf(val.getOffset())); + putMemoryValue(regVals, node, val); // set lastSet for any children locations List childRegisters = reg.getChildRegisters(); @@ -906,16 +1062,6 @@ public class VarnodeContext implements ProcessorContext { addSetVarnodeToLastSetLocations(node, address); } } - - // use zero for constants, so space will blend! - BigInteger bigSpaceID = BigInteger.ZERO; - if (!val.isConstant()) { - int spaceID = val.getSpace(); - bigSpaceID = BigInteger.valueOf(spaceID); - } - // Otherwise, invert so they won't blend - bigSpaceID = bigSpaceID.not(); // only flip space bits that are non-zero - spaceContext.setValue(reg, bigSpaceID); } private void addSetVarnodeToLastSetLocations(Varnode node, Address address) { @@ -926,12 +1072,18 @@ public class VarnodeContext implements ProcessorContext { allLastSet.put(node, addressSet); } addressSet.add(address); - + // for registers with parent larger register, must store that they were // last set at this address as well. - if (node.isRegister()) { - Register parentRegister = trans.getRegister(node).getParentRegister(); - if (parentRegister != null) { + // Don't care about really large overlapping registers + if (node.isRegister() && node.getSize() <= 8) { + Register register = trans.getRegister(node); + if (register == null) { + return; + } + Register parentRegister = register.getParentRegister(); + // Don't care about really large overlapping registers + if (parentRegister != null && parentRegister.getBitLength() <= 64) { node = trans.getVarnode(parentRegister); addSetVarnodeToLastSetLocations(node, address); } @@ -1007,34 +1159,40 @@ public class VarnodeContext implements ProcessorContext { return vt; } - public long getConstant(Varnode vnode, ContextEvaluator evaluator) throws NotFoundException { + public Long getConstant(Varnode vnode, ContextEvaluator evaluator) { + if (vnode == null) { + return null; + } + if (!isConstant(vnode)) { if (evaluator == null) { - throw notFoundExc; + return null; } // is there an assumed value that should be returned for any unknown value? - Instruction instr = getCurrentInstruction(offsetContext.getAddress()); + Instruction instr = getCurrentInstruction(currentAddress); Long lval = evaluator.unknownValue(this, instr, vnode); if (lval != null) { return lval.longValue(); } - throw notFoundExc; + return null; } return vnode.getOffset(); } - public Varnode getVarnode(Varnode space, Varnode offset, int size, ContextEvaluator evaluator) - throws NotFoundException { + public Varnode getVarnode(Varnode space, Varnode offset, int size, ContextEvaluator evaluator) { + if (offset == null) { + return null; + } int spaceID = offset.getSpace(); long valbase = 0; if (isRegister(offset)) { Register reg = trans.getRegister(offset); if (reg == null) { - throw notFoundExc; + return null; } - spaceID = getAddressSpace(reg.getName()); + spaceID = getAddressSpace(reg.getName(),reg.getBitLength()); valbase = 0; } else if (offset.isConstant()) { @@ -1049,11 +1207,11 @@ public class VarnodeContext implements ProcessorContext { } else if (OffsetAddressFactory.isSymbolSpace(spaceID)) { if (evaluator == null) { - throw notFoundExc; + return null; } // is there an assumed value that should be returned for any unknown value? - Instruction instr = getCurrentInstruction(offsetContext.getAddress()); + Instruction instr = getCurrentInstruction(currentAddress); Long lval = evaluator.unknownValue(this, instr, offset); valbase = offset.getOffset(); if (lval != null) { @@ -1062,44 +1220,100 @@ public class VarnodeContext implements ProcessorContext { } } else { - throw notFoundExc; + return null; } return getVarnode(spaceID, valbase, size); } /** - * get the value of a register as a varnode (value, space, size) + * Get the value (value, space, size) of a register at the start of the last execution + * flow taken for the instruction at toAddr. + * + * @param reg register to retrieve the start value + * @param fromAddr flow from address (not used currently, future use to retrieve multiple flows) + * @param toAddr address of instruction to retrieve the register flow state + * @param signed true if value is signed, will sext the top bit based on value size + * + * @return instruction start state value for register, or null if no known state * - * @param reg register to get value for - * @param fromAddr from address - * @param toAddr to address - * @param signed true if signed - * @return the register value or null */ public Varnode getRegisterVarnodeValue(Register reg, Address fromAddr, Address toAddr, boolean signed) { - Varnode rvnode = null; if (reg == null) { return null; } + Varnode rvnode = trans.getVarnode(reg); + + // use current regVals; + int backupDepth = 0; + Stack> state = regVals; + + // if has a stored stack state, setup to use that state + TraceDepthState traceDepthState = addrStartState.get(toAddr); + if (traceDepthState != null) { + state= traceDepthState.state(); + backupDepth = state.size() - traceDepthState.depth(); + } + + Varnode value = getMemoryValue(state, backupDepth, rvnode, signed); - BigInteger bigVal = offsetContext.getValue(reg, fromAddr, toAddr, signed); - if (bigVal == null) { + int sizeNeeded = reg.getBitLength() / 8; + + // lucky, got location and full size looking for + if (value != null && (value.getSize() == sizeNeeded || value.getSize() == 0)) { + return value; + } + + return null; + } + + + /** + * Get the value (value, space, size) of a register at the end of the last execution + * flow taken for the instruction at toAddr. + * + * Note: This can only be called if trackStartEndState flag is true. + * + * @param reg register to retrieve the end value + * @param fromAddr flow from address (not used currently, future use to retrieve multiple flows) + * @param toAddr address of instruction to retrieve the register flow state + * @param signed is the value signed or unsigned, will sext the top bit based on value size + * + * @return instruction end state value for register, or null if no known state + * + * @throws UnsupportedOperationException trackStartEndState == false at construction + */ + public Varnode getEndRegisterVarnodeValue(Register reg, Address fromAddr, Address toAddr, + boolean signed) { + + if (!trackStartEndState) { + throw new UnsupportedOperationException("Must construct class with trackStartEndState == true"); + } + + if (reg == null) { return null; } - BigInteger spaceVal = getTranslatedSpaceValue(reg, fromAddr, toAddr); - rvnode = createVarnode(bigVal, spaceVal, reg.getMinimumByteSize()); - if (rvnode == null) { + Varnode rvnode = trans.getVarnode(reg); + + TraceDepthState traceDepthState = addrEndState.get(toAddr); + if (traceDepthState == null) { return null; } - if (!rvnode.getAddress().equals(BAD_ADDRESS)) { - if (debug) { - Msg.info(this, " " + reg.getName() + " = " + print(rvnode)); - } - return rvnode; + + Stack> state = traceDepthState.state(); + int backupDepth = state.size() - traceDepthState.depth(); + + Varnode value = getMemoryValue(state, backupDepth, rvnode, signed); + + int sizeNeeded = reg.getBitLength() / 8; + + // lucky, got location and full size looking for + if (value != null && (value.getSize() == sizeNeeded || value.getSize() == 0)) { + return value; } + return null; } @@ -1115,7 +1329,8 @@ public class VarnodeContext implements ProcessorContext { } /** - * Get the current value of the register at the address + * Get the current value of the register at the address. + * Note: If trackStartEndState flag is false, then this will return the current value. * * @param reg value of register to get * @param toAddr value of register at a location @@ -1129,6 +1344,7 @@ public class VarnodeContext implements ProcessorContext { /** * Get the value of a register that was set coming from an address to an * another address. + * Note: If trackStartEndState flag is false, then this will return the current value. * * @param reg value of register to get * @param fromAddr location the value came from @@ -1137,21 +1353,21 @@ public class VarnodeContext implements ProcessorContext { * @return value of register or null */ public RegisterValue getRegisterValue(Register reg, Address fromAddr, Address toAddr) { - // only return constants - RegisterValue regVal = offsetContext.getRegisterValue(reg, fromAddr, toAddr); - if (regVal == null) { + + Varnode rvnode = getRegisterVarnodeValue(reg, fromAddr, toAddr, false); + if (rvnode == null) { return null; } - BigInteger spaceVal = getTranslatedSpaceValue(reg, fromAddr, toAddr); - if (spaceVal != null) { - int spaceID = spaceVal.intValue(); - // check normal constant and suspect constants - if (spaceID != addrFactory.getConstantSpace().getSpaceID() && - spaceID != SUSPECT_OFFSET_SPACEID) { - return null; - } + + int spaceID = rvnode.getSpace(); + + // check normal constant and suspect constants + if (spaceID != addrFactory.getConstantSpace().getSpaceID() && + spaceID != SUSPECT_OFFSET_SPACEID) { + return null; } - return regVal; + + return new RegisterValue(reg, BigInteger.valueOf(rvnode.getOffset())); } public AddressRangeIterator getRegisterValueAddressRanges(Register reg) { @@ -1170,11 +1386,13 @@ public class VarnodeContext implements ProcessorContext { * @param in varnode to copy from. * @param mustClearAll true if must clear if value is not unique * @param evaluator user provided evaluator if needed - * @throws NotFoundException if there is no known value for in */ - public void copy(Varnode out, Varnode in, boolean mustClearAll, ContextEvaluator evaluator) - throws NotFoundException { + public void copy(Varnode out, Varnode in, boolean mustClearAll, ContextEvaluator evaluator) { Varnode val1 = null; + // if just a copy of itself, do nothing + if (out.equals(in)) { + return; + } val1 = getValue(in, evaluator); // if truncating a constant get a new constant of the proper size if (val1 != null && in.getSize() > out.getSize()) { @@ -1182,11 +1400,10 @@ public class VarnodeContext implements ProcessorContext { val1 = createVarnode(val1.getOffset(), val1.getSpace(), out.getSize()); } } - + if (!in.isRegister() || !out.isRegister()) { // normal case easy get value, put value putValue(out, val1, mustClearAll); - return; } if (mustClearAll) { clearVals.add(out); @@ -1201,13 +1418,12 @@ public class VarnodeContext implements ProcessorContext { * * @param val1 first value * @param val2 second value - * @return varnode that could be a constant, or an offset into a space - * - * @throws NotFoundException if any constant is needed not known + * @return varnode that could be a constant, or an offset into a space, or null */ - public Varnode add(Varnode val1, Varnode val2, ContextEvaluator evaluator) - throws NotFoundException { - + public Varnode add(Varnode val1, Varnode val2, ContextEvaluator evaluator) { + if (val1 == null || val2 == null) { + return null; + } // try to make the constant value the addend. if (isConstant(val1) || val1.isAddress()) { Varnode swap = val1; @@ -1223,12 +1439,12 @@ public class VarnodeContext implements ProcessorContext { else if (isRegister(val1)) { Register reg = trans.getRegister(val1); if (reg == null) { - throw notFoundExc; + return null; } - spaceID = getAddressSpace(reg.getName()); + spaceID = getAddressSpace(reg.getName(),reg.getBitLength()); valbase = 0; // check if evaluator wants to override unknown - Instruction instr = getCurrentInstruction(offsetContext.getAddress()); + Instruction instr = getCurrentInstruction(currentAddress); if (evaluator != null) { Long uval = evaluator.unknownValue(this, instr, val1); if (uval != null) { @@ -1244,7 +1460,7 @@ public class VarnodeContext implements ProcessorContext { spaceID = BAD_OFFSET_SPACEID; valbase = 0; // check if evaluator wants to override unknown - Instruction instr = getCurrentInstruction(offsetContext.getAddress()); + Instruction instr = getCurrentInstruction(currentAddress); if (evaluator != null) { Long uval = evaluator.unknownValue(this, instr, val1); if (uval != null) { @@ -1261,22 +1477,11 @@ public class VarnodeContext implements ProcessorContext { } } else if (isSymbolicSpace(spaceID)) { - Instruction instr = getCurrentInstruction(offsetContext.getAddress()); + Instruction instr = getCurrentInstruction(currentAddress); valbase = val1.getOffset(); if (evaluator != null) { Long uval = evaluator.unknownValue(this, instr, val1); if (uval != null) { -// if (val2.isRegister() && spaceID == getSymbolSpaceID(val2)) { -// valbase += uval.longValue() * 2; -// return createVarnode(valbase, val1.getSize()); -// } -// else { -// valbase = uval.longValue(); -//// valbase += uval.longValue(); -//// spaceID = val2.getSpace(); -// return add(createVarnode(valbase, val1.getSize()), val2, evaluator); -// } - if (val2.isRegister()) { String spaceName = addrFactory.getAddressSpace(spaceID).getName(); Register reg2 = trans.getRegister(val2); @@ -1290,26 +1495,30 @@ public class VarnodeContext implements ProcessorContext { } } valbase = uval.longValue(); -// valbase += uval.longValue(); -// spaceID = val2.getSpace(); return add(createConstantVarnode(valbase, val1.getSize()), val2, evaluator); } } } else { - throw notFoundExc; + return null; } - + // create a new varnode with the correct space and offset // note: if spaceID is a bad space, createVarnode will create a new BAD_ADDRESS - long result = (valbase + getConstant(val2, null)) & + Long val2Const = getConstant(val2, null); + if (val2Const == null) { + return null; + } + long result = (valbase + val2Const) & (0xffffffffffffffffL >>> ((8 - val1.getSize()) * 8)); return createVarnode(result, spaceID, val1.getSize()); } - public Varnode and(Varnode val1, Varnode val2, ContextEvaluator evaluator) - throws NotFoundException { + public Varnode and(Varnode val1, Varnode val2, ContextEvaluator evaluator) { + if (val1 == null || val2 == null) { + return null; + } if (val1.equals(val2)) { return val1; } @@ -1323,9 +1532,9 @@ public class VarnodeContext implements ProcessorContext { if (isRegister(val1)) { Register reg = trans.getRegister(val1); if (reg == null) { - throw notFoundExc; + return null; } - spaceID = getAddressSpace(reg.getName()); + spaceID = getAddressSpace(reg.getName(),reg.getBitLength()); valbase = 0; } else if (val1.isConstant()) { @@ -1337,10 +1546,13 @@ public class VarnodeContext implements ProcessorContext { else if (isSymbolicSpace(spaceID)) { valbase = val1.getOffset(); if (val2.isConstant()) { - long val2Const = getConstant(val2, null); + Long val2Const = getConstant(val2, null); + if (val2Const == null) { + return null; + } // check if the value could be an alignment mask from an unknown register if (((val2Const >> 1) << 1) != val2Const && ((val2Const >> 2) << 2) != val2Const) { - throw notFoundExc; + return null; } } } @@ -1348,15 +1560,21 @@ public class VarnodeContext implements ProcessorContext { return (val1); // can't mess with an external address } else { - throw notFoundExc; + return null; } - long result = (valbase & getConstant(val2, null)) & + Long val2Const = getConstant(val2, null); + if (val2Const == null) { + return null; + } + long result = (valbase & val2Const) & (0xffffffffffffffffL >>> ((8 - val1.getSize()) * 8)); return createVarnode(result, spaceID, val1.getSize()); } - public Varnode or(Varnode val1, Varnode val2, ContextEvaluator evaluator) - throws NotFoundException { + public Varnode or(Varnode val1, Varnode val2, ContextEvaluator evaluator) { + if (val1 == null || val2 == null) { + return null; + } if (val1.equals(val2)) { return val1; } @@ -1367,7 +1585,10 @@ public class VarnodeContext implements ProcessorContext { val2 = swap; } int spaceID = val1.getSpace(); - long val2Const = getConstant(val2, null); + Long val2Const = getConstant(val2, null); + if (val2Const == null) { + return null; + } // got a constant from val2, (value | 0) == value, so just return value if (val2Const == 0) { if (!isSuspectConstant(val2)) { @@ -1375,13 +1596,25 @@ public class VarnodeContext implements ProcessorContext { } spaceID = val2.getSpace(); } - long lresult = getConstant(val1, evaluator) | val2Const; + Long val1Const = getConstant(val1, evaluator); + if (val1Const == null) { + return null; + } + long lresult = val1Const | val2Const; return createVarnode(lresult, spaceID, val1.getSize()); } - public Varnode left(Varnode val1, Varnode val2, ContextEvaluator evaluator) - throws NotFoundException { - long lresult = getConstant(val1, evaluator) << getConstant(val2, evaluator); + public Varnode left(Varnode val1, Varnode val2, ContextEvaluator evaluator) { + if (val1 == null || val2 == null) { + return null; + } + Long val1Const = getConstant(val1, evaluator); + if (val1Const == null) + return null; + Long val2Const = getConstant(val2, evaluator); + if (val2Const == null) + return null; + long lresult = val1Const << val2Const; lresult = lresult & (0xffffffffffffffffL >>> ((8 - val1.getSize()) * 8)); Varnode result = createVarnode(lresult, val1.getSpace(), val1.getSize()); return result; @@ -1389,16 +1622,22 @@ public class VarnodeContext implements ProcessorContext { // flag running out of address spaces, so error only printed once private boolean hitMaxAddressSpaces = false; - - public int getAddressSpace(String name) { + + public int getAddressSpace(String name,int bitSize) { int spaceID; AddressSpace regSpace = addrFactory.getAddressSpace(name); if (regSpace == null) { - regSpace = ((OffsetAddressFactory) addrFactory).createNewOffsetSpace(name); + // don't allow symbolic spaces smaller than a pointer so the offset value can hold a pointer + // TODO: This probably isn't quite right, the offset address space in theory should only be + // the size of the register that is getting an offset. The register could be a + // small 8-bit register used as an offset from a larger value to get a pointer. + int spaceBitSize = bitSize < pointerBitSize ? pointerBitSize : bitSize; + regSpace = ((OffsetAddressFactory) addrFactory).createNewOffsetSpace(name,spaceBitSize); } if (regSpace == null) { if (!hitMaxAddressSpaces) { - Msg.error(this, "VarnodeContext: out of address spaces at @" + currentAddress +" for: " + name); + Msg.error(this, "VarnodeContext: out of address spaces at @" + currentAddress + + " for: " + name); hitMaxAddressSpaces = true; } return BAD_SPACE_ID_VALUE; @@ -1414,17 +1653,18 @@ public class VarnodeContext implements ProcessorContext { * @param val1 first value * @param val2 second value * @return varnode that could be a constant, or an offset into a space - * - * @throws NotFoundException if any constant is needed not known */ - public Varnode subtract(Varnode val1, Varnode val2, ContextEvaluator evaluator) - throws NotFoundException { + public Varnode subtract(Varnode val1, Varnode val2, ContextEvaluator evaluator) { + if (val1 == null || val2 == null) { + return null; + } + // degenerate case, don't need to know the value if (val1.equals(val2)) { - if (val1.getAddress().equals(BAD_ADDRESS)) { - return val1; - } - return createVarnode(0, addrFactory.getConstantSpace().getSpaceID(), val1.getSize()); + if (isBadAddress(val1)) { + return val1; + } + return createVarnode(0, addrFactory.getConstantSpace().getSpaceID(), val1.getSize()); } int spaceID = val1.getSpace(); long valbase = 0; @@ -1437,28 +1677,30 @@ public class VarnodeContext implements ProcessorContext { else if (isRegister(val1)) { Register reg = trans.getRegister(val1); if (reg == null) { - throw notFoundExc; + return null; } - spaceID = getAddressSpace(reg.getName()); + spaceID = getAddressSpace(reg.getName(),reg.getBitLength()); valbase = 0; } else if (isSymbolicSpace(spaceID)) { - Instruction instr = getCurrentInstruction(offsetContext.getAddress()); + Instruction instr = getCurrentInstruction(currentAddress); valbase = val1.getOffset(); if (evaluator != null) { Long uval = evaluator.unknownValue(this, instr, val1); if (uval != null) { valbase = uval.longValue(); -// valbase += uval.longValue(); -// spaceID = val2.getSpace(); return add(createConstantVarnode(valbase, val1.getSize()), val2, evaluator); } } } else { - throw notFoundExc; + return null; } - long result = (valbase - getConstant(val2, null)) & + Long val2Const = getConstant(val2, null); + if (val2Const == null) { + return null; + } + long result = (valbase - val2Const) & (0xffffffffffffffffL >>> ((8 - val1.getSize()) * 8)); return createVarnode(result, spaceID, val1.getSize()); } @@ -1468,17 +1710,18 @@ public class VarnodeContext implements ProcessorContext { * * @param out varnode to extend into (for size) * @param in varnode value to extend the size - * @return - * @throws NotFoundException + * @return new sign extended varnode */ public Varnode extendValue(Varnode out, Varnode[] in, boolean signExtend, - ContextEvaluator evaluator) throws NotFoundException { + ContextEvaluator evaluator) { Varnode vnodeVal; vnodeVal = getValue(in[0], signExtend, evaluator); + if (vnodeVal == null) { + return null; + } if (isConstant(vnodeVal) && in[0].getSize() < out.getSize()) { -// TODO: Is there a better way to do this - it was not sign-extending temp values before if (vnodeVal.getSize() <= 8) { Scalar sVal = new Scalar(8 * vnodeVal.getSize(), vnodeVal.getOffset(), signExtend); vnodeVal = createVarnode(sVal.getValue(), vnodeVal.getSpace(), out.getSize()); @@ -1491,9 +1734,9 @@ public class VarnodeContext implements ProcessorContext { else if (vnodeVal.isRegister() && vnodeVal.getSize() < out.getSize()) { Register reg = getRegister(vnodeVal); if (reg == null) { - throw notFoundExc; + return null; } - int spaceID = getAddressSpace(reg.getName()); + int spaceID = getAddressSpace(reg.getName(),reg.getBitLength()); vnodeVal = createVarnode(0, spaceID, out.getSize()); } return vnodeVal; @@ -1504,60 +1747,36 @@ public class VarnodeContext implements ProcessorContext { if (reg == null) { return; } - // set the register to some other value to flush it, then clear it! -// BigInteger cval; -// cval = offsetContext.getValue(reg, false); -// if (cval != null) { -// offsetContext.setValue(reg, cval.negate()); -// cval = spaceContext.getValue(reg, false); -// if (cval != null) { -// spaceContext.setValue(reg, cval.negate()); -// } -// } -// offsetContext.clearRegister(reg); -// spaceContext.clearRegister(reg); -// offsetContext.setValue(reg, BigInteger.valueOf(Address.NO_ADDRESS.getOffset())); -// spaceContext.setValue(reg, BigInteger.valueOf(Address.NO_ADDRESS.getAddressSpace().getUniqueSpaceID())); // Start new register space String spaceName = reg.getName() + "-" + currentAddress; - int spaceId = getAddressSpace(spaceName); - offsetContext.setValue(reg, BigInteger.ZERO); - // bad value space bits need to be flipped - BigInteger bigSpaceID = BigInteger.valueOf(spaceId).not(); - spaceContext.setValue(reg, bigSpaceID); + int spaceId = getAddressSpace(spaceName,reg.getBitLength()); + + Varnode registerVarnode = getRegisterVarnode(reg); + putMemoryValue(this.regVals, registerVarnode, + createVarnode(0, spaceId, registerVarnode.getSize())); } @Override public Register getRegister(String name) { - return offsetContext.getRegister(name); + return trans.getRegister(name); } @Override public RegisterValue getRegisterValue(Register register) { Varnode regVnode = trans.getVarnode(register); - try { - Varnode value = this.getValue(regVnode, false, null); - if (isConstant(value)) { - return new RegisterValue(register, BigInteger.valueOf(value.getOffset())); - } - } - catch (NotFoundException e) { - // Don't care, turn into a null register + Varnode value = this.getValue(regVnode, false, null); + if (isConstant(value)) { + return new RegisterValue(register, BigInteger.valueOf(value.getOffset())); } return null; } public Varnode getRegisterVarnodeValue(Register register) { Varnode regVnode = trans.getVarnode(register); - try { - Varnode value = this.getValue(regVnode, false, null); - return value; - } - catch (NotFoundException e) { - // Don't care, turn into a null varnode. - } - return null; + + Varnode value = this.getValue(regVnode, false, null); + return value; } public Varnode getRegisterVarnode(Register register) { @@ -1573,27 +1792,27 @@ public class VarnodeContext implements ProcessorContext { @Override public List getRegisters() { - return offsetContext.getRegisters(); + return trans.getRegisters(); } @Override public BigInteger getValue(Register register, boolean signed) { Varnode regVnode = trans.getVarnode(register); - try { - Varnode value = this.getValue(regVnode, signed, null); - if (isConstant(value)) { - return BigInteger.valueOf(value.getOffset()); - } + + Varnode value = this.getValue(regVnode, signed, null); + if (value == null) { + return null; } - catch (NotFoundException e) { - // Don't care, turn into a null value + if (isConstant(value)) { + return BigInteger.valueOf(value.getOffset()); } return null; } @Override public boolean hasValue(Register register) { - return offsetContext.hasValue(register); + Varnode rvnode = getRegisterVarnodeValue(register); + return rvnode != null; } @Override @@ -1607,7 +1826,7 @@ public class VarnodeContext implements ProcessorContext { putValue(regVnode, createConstantVarnode(value.longValue(), regVnode.getSize()), false); propogateResults(false); } - + /** * Check if the varnode is associated with a Symbolic location * @@ -1617,7 +1836,7 @@ public class VarnodeContext implements ProcessorContext { public boolean isSymbol(Varnode varnode) { return isSymbolicSpace(varnode.getAddress().getAddressSpace()); } - + /** * Check if the varnode is associated with a register. * @@ -1640,7 +1859,17 @@ public class VarnodeContext implements ProcessorContext { } return isSuspectConstant(varnode); } - + + /** + * Check if this is a bad address, or offset from a bad address + * + * @param varnode to check + * @return true if should be treated as a constant for most purposes + */ + public boolean isBadAddress(Varnode v) { + return v.getAddress().equals(BAD_ADDRESS) || v.getSpace() == BAD_OFFSET_SPACEID; + } + /** * Check if the constant is a suspect constant * It shouldn't be trusted in certain cases. @@ -1724,14 +1953,40 @@ public class VarnodeContext implements ProcessorContext { * Save the current memory state */ public void pushMemState() { - memoryVals.push(new HashMap()); + Stack> newRegValsTrace = + (Stack>) regVals.clone(); + regTraces.push(newRegValsTrace); + regVals.push(new HashMap()); + +// TODO: only save if need to + Stack> newUniqueValsTrace = + (Stack>) uniqueVals.clone(); + uniqueTraces.push(newUniqueValsTrace); + uniqueVals.push(new HashMap()); + + Stack> newMemValsTrace = + (Stack>) memoryVals.clone(); + newMemValsTrace.push(new HashMap()); + memTraces.push(newMemValsTrace); + memoryVals.push(new HashMap()); + + lastSetSaves.push((HashMap) lastSet.clone()); } /** * restore a previously saved memory state */ public void popMemState() { - memoryVals.pop(); + regVals = regTraces.pop(); + memoryVals = memTraces.pop(); + +// TODO: only save if need to + uniqueVals = uniqueTraces.pop(); + + lastSet = lastSetSaves.pop(); + + tempVals = new HashMap<>(); + clearVals = new HashSet<>(); } } @@ -1762,8 +2017,9 @@ class OffsetAddressFactory extends DefaultAddressFactory { // algorithm is sensitive to the space ID value and must be less than 0x7f. Only types that are // between 0-16 will work correctly because of how the spaceID is calculated based on the space type. // The spaceID is computed using the type. - AddressSpace suspectConstspc = new GenericAddressSpace(VarnodeContext.SUSPECT_CONST_NAME, 64, - AddressSpace.TYPE_JOIN, 0); + AddressSpace suspectConstspc = + new GenericAddressSpace(VarnodeContext.SUSPECT_CONST_NAME, 64, + AddressSpace.TYPE_JOIN, 0); addAddressSpace(suspectConstspc); } catch (DuplicateNameException e) { @@ -1779,7 +2035,7 @@ class OffsetAddressFactory extends DefaultAddressFactory { // Maximum space ID used to create spaces private int curMaxID = 0; - + private int getNextUniqueID() { if (curMaxID == 0) { AddressSpace[] spaces = getAllAddressSpaces(); @@ -1797,10 +2053,16 @@ class OffsetAddressFactory extends DefaultAddressFactory { * @param name of address space * @return new address space, or null if no spaces left to allocate */ - public AddressSpace createNewOffsetSpace(String name) { + public AddressSpace createNewOffsetSpace(String name, int bitSize) { AddressSpace space = null; try { - space = new GenericAddressSpace(name, this.getConstantSpace().getSize(), + if (bitSize > 64) { + bitSize = 64; + } + if (bitSize < 8) { + bitSize = 8; + } + space = new OffsetAddressSpace(name, bitSize, AddressSpace.TYPE_SYMBOL, getNextUniqueID()); super.addAddressSpace(space); } @@ -1819,3 +2081,16 @@ class OffsetAddressFactory extends DefaultAddressFactory { } } + +class OffsetAddressSpace extends GenericAddressSpace { + + public OffsetAddressSpace(String name, int size, int type, int unique) { + super(name, size, type, unique); + } + + @Override + public int compareTo(AddressSpace space) { + int c = getSpaceID() - space.getSpaceID(); + return c; + } +} diff --git a/Ghidra/Features/Base/src/test/java/ghidra/program/util/ConstantPropogationReferenceTest.java b/Ghidra/Features/Base/src/test/java/ghidra/program/util/ConstantPropogationReferenceTest.java index 6015ab4681..ba5686876b 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/program/util/ConstantPropogationReferenceTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/program/util/ConstantPropogationReferenceTest.java @@ -155,126 +155,133 @@ public class ConstantPropogationReferenceTest extends AbstractGenericTest { return super.evaluateContextBefore(context, instr); } - private Varnode regValue(VarnodeContext context, String regName) { - return context.getRegisterVarnodeValue(context.getRegister(regName)); - } - @Override public boolean evaluateContext(VarnodeContext context, Instruction instr) { String loc = instr.getMinAddress().toString(); Varnode registerVarnode; + Long constVal; + boolean isBad = false; - switch (loc) { - case "00001010": - // gp should be 0x14 + t9 offset space - registerVarnode = regValue(context, "gp"); - assertTrue("symbolic value", context.isSymbol(registerVarnode)); - assertEquals("(t9, 0x13b334, 4)", registerVarnode.toString()); - // S3 should be S3 at entry - registerVarnode = regValue(context, "s3"); - assertTrue("register s3", context.isRegister(registerVarnode)); - assertEquals("s3", context.getRegister(registerVarnode).getName()); - break; - case "0000102c": - // s1 restored from space 0x10(s0) space - registerVarnode = regValue(context, "s1"); - assertTrue("constant value", registerVarnode.isConstant()); - assertEquals("(const, 0xa0b0c0d, 4)", registerVarnode.toString()); - break; - case "00001030": - // s1 restored from space 0x10(a1) space - registerVarnode = regValue(context, "s1"); - assertTrue("symbolic value", registerVarnode.isConstant()); - assertEquals("(const, 0x12344567, 4)", registerVarnode.toString()); - break; - case "00001034": - // s1 restored from space 0x10(sp) space - registerVarnode = regValue(context, "s1"); - assertTrue("symbolic value", context.isSymbol(registerVarnode)); - assertEquals("(t9, 0x13b334, 4)", registerVarnode.toString()); - break; - case "00001038": - // s1 restored from space 0x10(s2) space - registerVarnode = regValue(context, "s1"); - //assertTrue("Still s1", registerVarnode.isRegister()); - boolean isBad = false; - try { - context.getConstant(registerVarnode, null); - } - catch (NotFoundException e) { - isBad = true; - } - assertTrue("Can get constant value", isBad); - break; - case "00001040": - // s1 restored from space 0x10(s2) space - stored a3 - registerVarnode = regValue(context, "s1"); - assertTrue("register s3", registerVarnode.isRegister()); - assertEquals("s3", context.getRegister(registerVarnode).getName()); - - Address lastSetLocation = context.getLastSetLocation( - context.getRegisterVarnode(context.getRegister("s2")), null); - assertEquals("s2 last set", null, lastSetLocation); - break; - case "00001048": - // 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()); - isBad = false; - try { - context.getConstant(registerVarnode, null); - } - catch (NotFoundException e) { - isBad = true; - } - assertTrue("Can get constant value", isBad); - break; - case "0000104c": - // 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, "s2"); - //assertTrue("Still s2", registerVarnode.isRegister()); - isBad = false; - try { - context.getConstant(registerVarnode, null); - } - catch (NotFoundException e) { - 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; + switch(loc) { + case "00001010": + // gp should be 0x14 + t9 offset space + registerVarnode = regValue(context,"gp"); + assertTrue("symbolic value", context.isSymbol(registerVarnode)); + assertEquals("(t9, 0x13b334, 4)", registerVarnode.toString()); + // S3 should be S3 at entry + registerVarnode = regValue(context,"s3"); + assertTrue("register s3", context.isRegister(registerVarnode)); + assertEquals("s3", context.getRegister(registerVarnode).getName()); + break; + case "0000102c": + // s1 restored from space 0x10(s0) space + registerVarnode = regValue(context,"s1"); + assertTrue("constant value", registerVarnode.isConstant()); + assertEquals("(const, 0xa0b0c0d, 4)", registerVarnode.toString()); + break; + case "00001030": + // s1 restored from space 0x10(a1) space + registerVarnode = regValue(context,"s1"); + assertTrue("symbolic value", registerVarnode.isConstant()); + assertEquals("(const, 0x12344567, 4)", registerVarnode.toString()); + break; + case "00001034": + // s1 restored from space 0x10(sp) space + registerVarnode = regValue(context,"s1"); + assertTrue("symbolic value", context.isSymbol(registerVarnode)); + assertEquals("(t9, 0x13b334, 4)", registerVarnode.toString()); + break; + case "00001038": + // s1 restored from space 0x10(s2) space + registerVarnode = regValue(context,"s1"); + //assertTrue("Still s1", registerVarnode.isRegister()); + constVal = context.getConstant(registerVarnode, null); + isBad = constVal == null; + assertTrue("Can get constant value", isBad); + break; + case "00001040": + // s1 restored from space 0x10(s2) space - stored a3 + registerVarnode = regValue(context,"s1"); + assertTrue("register s3", registerVarnode.isRegister()); + assertEquals("s3", context.getRegister(registerVarnode).getName()); + + Address lastSetLocation = context.getLastSetLocation(context.getRegisterVarnode(context.getRegister("s2")), null); + assertEquals("s2 last set", null, lastSetLocation); + break; + case "00001048": + // 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()); + constVal = context.getConstant(registerVarnode, null); + isBad = constVal == null; + assertTrue("Can get constant value", isBad); + break; + case "0000104c": + // 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,"s2"); + //assertTrue("Still s2", registerVarnode.isRegister()); + constVal = context.getConstant(registerVarnode, null); + isBad = constVal == null; + assertTrue("Can get constant value", isBad); + lastSetLocation = context.getLastSetLocation(context.getRegisterVarnode(context.getRegister("s2")), null); + assertEquals("s2 last set", 0x104cL, 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"); + constVal = context.getConstant(registerVarnode, null); + isBad = constVal == null; + assertTrue("Can get constant value", isBad); + break; } return super.evaluateContext(context, instr); } }; setRegister(addr("0x1000"), "s1", 0); - SymbolicPropogator symEval = new SymbolicPropogator(program); + SymbolicPropogator symEval = new SymbolicPropogator(program,true); Function func = program.getFunctionManager().getFunctionAt(builder.addr(0x1000)); 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 @@ -542,6 +549,22 @@ public class ConstantPropogationReferenceTest extends AbstractGenericTest { Register gp = context.getRegister(regname); 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) { return builder.addr(address); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/VarnodeTranslator.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/VarnodeTranslator.java index f4240d8b13..8a98e3a460 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/VarnodeTranslator.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/VarnodeTranslator.java @@ -70,6 +70,16 @@ public class VarnodeTranslator { Varnode node = new Varnode(register.getAddress(), register.getMinimumByteSize()); 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 diff --git a/Ghidra/Processors/68000/src/main/java/ghidra/app/plugin/core/analysis/Motorola68KAnalyzer.java b/Ghidra/Processors/68000/src/main/java/ghidra/app/plugin/core/analysis/Motorola68KAnalyzer.java index 9226ea6f8f..ade6740276 100644 --- a/Ghidra/Processors/68000/src/main/java/ghidra/app/plugin/core/analysis/Motorola68KAnalyzer.java +++ b/Ghidra/Processors/68000/src/main/java/ghidra/app/plugin/core/analysis/Motorola68KAnalyzer.java @@ -73,30 +73,25 @@ public class Motorola68KAnalyzer extends ConstantPropagationAnalyzer { if (mnemonic.equals("pea")) { // retrieve the value pushed onto the stack - try { - Varnode stackValue = context.getValue(context.getStackVarnode(), this); - Varnode value = context.getValue(stackValue, this); - if (value != null && value.isConstant()) { - long lval = value.getOffset(); - Address refAddr = instr.getMinAddress().getNewAddress(lval); - if (lval <= 4096 || ((lval % 1024) == 0) || lval < 0 || - lval == 0xffff || lval == 0xff00 || lval == 0xffffff || - lval == 0xff0000 || lval == 0xff00ff || lval == 0xffffffff || - lval == 0xffffff00 || lval == 0xffff0000 || - lval == 0xff000000) { - return false; - } - if (program.getMemory().contains(refAddr)) { - if (instr.getOperandReferences(0).length == 0) { - instr.addOperandReference(0, refAddr, RefType.DATA, - SourceType.ANALYSIS); - } + Varnode stackValue = context.getValue(context.getStackVarnode(), this); + Varnode value = context.getValue(stackValue, this); + if (value != null && value.isConstant()) { + long lval = value.getOffset(); + Address refAddr = instr.getMinAddress().getNewAddress(lval); + if (lval <= 4096 || ((lval % 1024) == 0) || lval < 0 || + lval == 0xffff || lval == 0xff00 || lval == 0xffffff || + lval == 0xff0000 || lval == 0xff00ff || lval == 0xffffffff || + lval == 0xffffff00 || lval == 0xffff0000 || + lval == 0xff000000) { + return false; + } + if (program.getMemory().contains(refAddr)) { + if (instr.getOperandReferences(0).length == 0) { + instr.addOperandReference(0, refAddr, RefType.DATA, + SourceType.ANALYSIS); } } } - catch (NotFoundException e) { - // value not found doesn't matter - } } if (mnemonic.equals("lea")) { Register destReg = instr.getRegister(1); diff --git a/Ghidra/Processors/ARM/src/main/java/ghidra/app/plugin/core/analysis/ArmAnalyzer.java b/Ghidra/Processors/ARM/src/main/java/ghidra/app/plugin/core/analysis/ArmAnalyzer.java index 7c1ac0eb53..90ab885b7d 100644 --- a/Ghidra/Processors/ARM/src/main/java/ghidra/app/plugin/core/analysis/ArmAnalyzer.java +++ b/Ghidra/Processors/ARM/src/main/java/ghidra/app/plugin/core/analysis/ArmAnalyzer.java @@ -4,9 +4,9 @@ * 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. @@ -799,7 +799,7 @@ public class ArmAnalyzer extends ConstantPropagationAnalyzer { if (tmodeRegister != null && listing.getUndefinedDataAt(addr) != null) { boolean inThumbMode = false; RegisterValue curvalue = - context.getRegisterValue(tmodeRegister, instruction.getMinAddress()); + context.getRegisterValue(tmodeRegister); if (curvalue != null && curvalue.hasValue()) { inThumbMode = (curvalue.getUnsignedValue().intValue() == 1); } diff --git a/Ghidra/Processors/MIPS/src/main/java/ghidra/app/plugin/core/analysis/MipsAddressAnalyzer.java b/Ghidra/Processors/MIPS/src/main/java/ghidra/app/plugin/core/analysis/MipsAddressAnalyzer.java index 53cb4da070..d32bea5cb7 100644 --- a/Ghidra/Processors/MIPS/src/main/java/ghidra/app/plugin/core/analysis/MipsAddressAnalyzer.java +++ b/Ghidra/Processors/MIPS/src/main/java/ghidra/app/plugin/core/analysis/MipsAddressAnalyzer.java @@ -4,9 +4,9 @@ * 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. @@ -552,7 +552,7 @@ public class MipsAddressAnalyzer extends ConstantPropagationAnalyzer { if (isamode != null && listing.getUndefinedDataAt(addr) != null) { boolean inM16Mode = false; - RegisterValue curvalue = context.getRegisterValue(isamode, instruction.getMinAddress()); + RegisterValue curvalue = context.getRegisterValue(isamode); if (curvalue != null && curvalue.hasValue()) { inM16Mode = (curvalue.getUnsignedValue().intValue() == 1); }