From 4723729d806fb2e33e7b3b768d14c5b3b8835127 Mon Sep 17 00:00:00 2001 From: emteere <47253321+emteere@users.noreply.github.com> Date: Thu, 3 Jul 2025 17:49:53 +0000 Subject: [PATCH] GP-5804 Set SymbolicPropogator to record register begin/end state in basic constructor. Better document recordBeginEndState flag. --- .../MultiInstructionMemReference.java | 2 +- .../PropagateConstantReferences.java | 6 +-- .../PropagateX86ConstantReferences.java | 6 +-- .../ResolveX86orX64LinuxSyscallsScript.java | 6 +-- .../cmd/function/CreateThunkFunctionCmd.java | 2 +- .../function/NewFunctionStackAnalysisCmd.java | 2 +- .../analysis/ConstantPropagationAnalyzer.java | 2 +- .../core/analysis/GolangSymbolAnalyzer.java | 2 +- .../program/util/SymbolicPropogator.java | 41 +++++++++++++------ .../ghidra/program/util/VarnodeContext.java | 22 +++++----- .../analysis/DecompilerSwitchAnalyzer.java | 6 +-- .../core/analysis/Motorola68KAnalyzer.java | 6 +-- .../analysis/AARCH64PltThunkAnalyzer.java | 6 +-- .../app/plugin/core/analysis/ArmAnalyzer.java | 2 +- .../core/analysis/PPC64CallStubAnalyzer.java | 6 +-- 15 files changed, 66 insertions(+), 51 deletions(-) diff --git a/Ghidra/Features/Base/ghidra_scripts/MultiInstructionMemReference.java b/Ghidra/Features/Base/ghidra_scripts/MultiInstructionMemReference.java index f6943f5d5f..a05b1b1295 100644 --- a/Ghidra/Features/Base/ghidra_scripts/MultiInstructionMemReference.java +++ b/Ghidra/Features/Base/ghidra_scripts/MultiInstructionMemReference.java @@ -353,7 +353,7 @@ public class MultiInstructionMemReference extends GhidraScript { } } - SymbolicPropogator symEval = new SymbolicPropogator(currentProgram); + SymbolicPropogator symEval = new SymbolicPropogator(currentProgram, false); symEval.setParamRefCheck(false); symEval.setReturnRefCheck(false); symEval.setStoredRefCheck(false); diff --git a/Ghidra/Features/Base/ghidra_scripts/PropagateConstantReferences.java b/Ghidra/Features/Base/ghidra_scripts/PropagateConstantReferences.java index 0c6bd32aa6..0a1e2dcd31 100644 --- a/Ghidra/Features/Base/ghidra_scripts/PropagateConstantReferences.java +++ b/Ghidra/Features/Base/ghidra_scripts/PropagateConstantReferences.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. @@ -72,7 +72,7 @@ public class PropagateConstantReferences extends GhidraScript { // use context to fill out addresses on certain instructions ContextEvaluator eval = new ConstantPropagationContextEvaluator(monitor, true); - SymbolicPropogator symEval = new SymbolicPropogator(currentProgram); + SymbolicPropogator symEval = new SymbolicPropogator(currentProgram,false); symEval.flowConstants(start, func.getBody(), eval, true, monitor); } diff --git a/Ghidra/Features/Base/ghidra_scripts/PropagateX86ConstantReferences.java b/Ghidra/Features/Base/ghidra_scripts/PropagateX86ConstantReferences.java index b49126b358..cae5ecc358 100644 --- a/Ghidra/Features/Base/ghidra_scripts/PropagateX86ConstantReferences.java +++ b/Ghidra/Features/Base/ghidra_scripts/PropagateX86ConstantReferences.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. @@ -140,7 +140,7 @@ public class PropagateX86ConstantReferences extends GhidraScript { eval.setTrustWritableMemory(true) .setCreateComplexDataFromPointers(true); - SymbolicPropogator symEval = new SymbolicPropogator(currentProgram); + SymbolicPropogator symEval = new SymbolicPropogator(currentProgram,false); symEval.setParamRefCheck(true); symEval.setReturnRefCheck(true); symEval.setStoredRefCheck(true); diff --git a/Ghidra/Features/Base/ghidra_scripts/ResolveX86orX64LinuxSyscallsScript.java b/Ghidra/Features/Base/ghidra_scripts/ResolveX86orX64LinuxSyscallsScript.java index 9fee16e16c..75bb5dbd53 100644 --- a/Ghidra/Features/Base/ghidra_scripts/ResolveX86orX64LinuxSyscallsScript.java +++ b/Ghidra/Features/Base/ghidra_scripts/ResolveX86orX64LinuxSyscallsScript.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. @@ -282,7 +282,7 @@ public class ResolveX86orX64LinuxSyscallsScript extends GhidraScript { for (Function func : funcsToCalls.keySet()) { Address start = func.getEntryPoint(); ContextEvaluator eval = new ConstantPropagationContextEvaluator(monitor, true); - SymbolicPropogator symEval = new SymbolicPropogator(program); + SymbolicPropogator symEval = new SymbolicPropogator(program, true); symEval.flowConstants(start, func.getBody(), eval, true, tMonitor); for (Address callSite : funcsToCalls.get(func)) { Value val = symEval.getRegisterValue(callSite, syscallReg); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CreateThunkFunctionCmd.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CreateThunkFunctionCmd.java index f2a1501ad0..24eb186393 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CreateThunkFunctionCmd.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/function/CreateThunkFunctionCmd.java @@ -462,7 +462,7 @@ public class CreateThunkFunctionCmd extends BackgroundCommand { } final AtomicInteger foundCount = new AtomicInteger(0); - SymbolicPropogator prop = new SymbolicPropogator(program); + SymbolicPropogator prop = new SymbolicPropogator(program,false); // try to compute the thunk by flowing constants from the start of the block prop.flowConstants(jumpBlockAt.getFirstStartAddress(), jumpBlockAt, 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 9ff9c76707..dc6430d670 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 @@ -270,7 +270,7 @@ public class NewFunctionStackAnalysisCmd extends BackgroundCommand { // Add stack variables with undefined types to map to simplify merging addFunctionStackVariablesToSortedList(func, sortedVariables); - SymbolicPropogator symEval = new SymbolicPropogator(program); + SymbolicPropogator symEval = new SymbolicPropogator(program, false); symEval.setParamRefCheck(false); symEval.setReturnRefCheck(false); symEval.setStoredRefCheck(false); 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 59e90d3a98..08c45c8488 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 @@ -471,7 +471,7 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer { flowStart = func.getEntryPoint(); } - SymbolicPropogator symEval = new SymbolicPropogator(program); + SymbolicPropogator symEval = new SymbolicPropogator(program, false); symEval.setParamRefCheck(checkParamRefsOption); symEval.setParamPointerRefCheck(checkPointerParamRefsOption); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java index d7458e6527..ff5637d8dc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/analysis/GolangSymbolAnalyzer.java @@ -1125,7 +1125,7 @@ public class GolangSymbolAnalyzer extends AbstractAnalyzer { GoTypeManager goTypes = goBinary.getGoTypes(); ContextEvaluator eval = new ConstantPropagationContextEvaluator(monitor, true); - SymbolicPropogator symEval = new SymbolicPropogator(program); + SymbolicPropogator symEval = new SymbolicPropogator(program, true); symEval.flowConstants(callingFunc.getEntryPoint(), callingFunc.getBody(), eval, true, monitor); 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 211a29d763..951a251f3a 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 @@ -65,7 +65,7 @@ public class SymbolicPropogator { private boolean debug = false; - private boolean trackStartEndState = false; // track the start/end values for each instruction + private boolean recordStartEndState = false; // record the start/end values for registers at each instruction private long pointerMask; private int pointerSize; @@ -103,21 +103,35 @@ public class SymbolicPropogator { // cache for pcode callother injection payloads HashMap injectPayloadCache = new HashMap(); + /** + * Create SymbolicPropagator for program. + * + * This will record all values at the beginning and ending of instructions. + * Recording all values can take more time and memory. So if the SymbolicEvaluator + * callback mechanism is being used, use the alternate constructor with false for + * recordStartEndState. + * + */ public SymbolicPropogator(Program program) { - this (program, false); + this (program, true); } /** - * Create symbolic propagation on program + * Create SymbolicPropagator for program either recording or start/end state at each instruction. + * + * NOTE: if you are going to inspect values at instructions after {@link SymbolicPropogator}.flowConstants() + * has completed, then you should pass true for recordStartEndState. If you are using a custom + * SymbolicEvaluator with the flowConstants() method, then you should pass false. * * @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 + * @param recordStartEndState - true to record the value of each register at the start/end of each + * instruction This will use more memory and be slightly slower. If inspecting + * values after flowContants() has completed, you must pass true. */ - public SymbolicPropogator(Program program, boolean trackStartEndState) { + public SymbolicPropogator(Program program, boolean recordStartEndState) { this.program = program; - this.trackStartEndState = trackStartEndState; + this.recordStartEndState = recordStartEndState; Language language = program.getLanguage(); @@ -126,7 +140,7 @@ public class SymbolicPropogator { setPointerMask(program); - context = new VarnodeContext(program, programContext, spaceContext, trackStartEndState); + context = new VarnodeContext(program, programContext, spaceContext, recordStartEndState); context.setDebug(debug); } @@ -259,7 +273,7 @@ public class SymbolicPropogator { Language language = program.getLanguage(); ProgramContext newValueContext = new ProgramContextImpl(language); ProgramContext newSpaceContext = new ProgramContextImpl(language); - VarnodeContext newContext = new VarnodeContext(program, newValueContext, newSpaceContext, trackStartEndState); + VarnodeContext newContext = new VarnodeContext(program, newValueContext, newSpaceContext, recordStartEndState); newContext.setDebug(debug); programContext = newValueContext; @@ -318,8 +332,9 @@ 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. + * specified register at the specified address. + * + * Note: This can only be called safely if recordStartEndState flag is true. * Otherwise it will just return the current value, not the value at the given address. * * @param toAddr address @@ -349,13 +364,13 @@ public class SymbolicPropogator { /** * 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. + * Note: This can only be called if recordStartEndState flag is true. * * @param toAddr address * @param reg register * @return register value * - * @throws UnsupportedOperationException trackStartEndState == false at construction + * @throws UnsupportedOperationException recordStartEndState == false at construction */ public Value getEndRegisterValue(Address toAddr, Register reg) { 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 311e32f259..69f652c43e 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 @@ -126,15 +126,15 @@ public class VarnodeContext implements ProcessorContext { boolean isBE = false; - boolean trackStartEndState = false; + boolean recordStartEndState = false; public boolean debug = false; public VarnodeContext(Program program, ProgramContext programContext, - ProgramContext spaceProgramContext, boolean trackStartEndState) { + ProgramContext spaceProgramContext, boolean recordStartEndState) { this.program = program; this.isBE = program.getLanguage().isBigEndian(); - this.trackStartEndState = trackStartEndState; + this.recordStartEndState = recordStartEndState; // make a copy, because we could be making new spaces. this.addrFactory = new OffsetAddressFactory(program); @@ -237,7 +237,7 @@ public class VarnodeContext implements ProcessorContext { public void flowStart(Address toAddr) { currentAddress = toAddr; - if (trackStartEndState) { + if (recordStartEndState) { addrStartState.put(toAddr,new TraceDepthState(regVals.size(),regVals)); regVals.push(new HashMap()); } @@ -247,7 +247,7 @@ public class VarnodeContext implements ProcessorContext { * End flow and save any necessary end flow state for the current instruction at address */ public void flowEnd(Address address) { - if (trackStartEndState) { + if (recordStartEndState) { addrEndState.put(address,new TraceDepthState(regVals.size(),regVals)); } currentAddress = null; @@ -1273,7 +1273,7 @@ public class VarnodeContext implements ProcessorContext { * 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. + * Note: This can only be called if recordStartEndState 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) @@ -1282,13 +1282,13 @@ public class VarnodeContext implements ProcessorContext { * * @return instruction end state value for register, or null if no known state * - * @throws UnsupportedOperationException trackStartEndState == false at construction + * @throws UnsupportedOperationException recordStartEndState == 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 (!recordStartEndState) { + throw new UnsupportedOperationException("Must construct class with recordStartEndState == true"); } if (reg == null) { @@ -1330,7 +1330,7 @@ public class VarnodeContext implements ProcessorContext { /** * Get the current value of the register at the address. - * Note: If trackStartEndState flag is false, then this will return the current value. + * Note: If recordStartEndState 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 @@ -1344,7 +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. + * Note: If recordStartEndState flag is false, then this will return the current value. * * @param reg value of register to get * @param fromAddr location the value came from diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/DecompilerSwitchAnalyzer.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/DecompilerSwitchAnalyzer.java index 142233d89f..85673203a0 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/DecompilerSwitchAnalyzer.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/plugin/core/analysis/DecompilerSwitchAnalyzer.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. @@ -479,7 +479,7 @@ public class DecompilerSwitchAnalyzer extends AbstractAnalyzer { // NOTE: Assumption, we have found all flows leading to the switch that might split the basic block final AtomicInteger foundCount = new AtomicInteger(0); - SymbolicPropogator prop = new SymbolicPropogator(program); + SymbolicPropogator prop = new SymbolicPropogator(program,false); prop.flowConstants(jumpBlockAt.getFirstStartAddress(), jumpBlockAt, new ContextEvaluatorAdapter() { @Override 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 ade6740276..1051696e48 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 @@ -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. @@ -342,7 +342,7 @@ public class Motorola68KAnalyzer extends ConstantPropagationAnalyzer { SwitchEvaluator switchEvaluator = new SwitchEvaluator(); // clear past constants. This example doesn't seem to depend on them - symEval = new SymbolicPropogator(program); + symEval = new SymbolicPropogator(program,false); // now flow with the simple block of this branch.... // for each unknown branch destination, diff --git a/Ghidra/Processors/AARCH64/src/main/java/ghidra/app/plugin/core/analysis/AARCH64PltThunkAnalyzer.java b/Ghidra/Processors/AARCH64/src/main/java/ghidra/app/plugin/core/analysis/AARCH64PltThunkAnalyzer.java index 084a99ec1c..7ad5223236 100644 --- a/Ghidra/Processors/AARCH64/src/main/java/ghidra/app/plugin/core/analysis/AARCH64PltThunkAnalyzer.java +++ b/Ghidra/Processors/AARCH64/src/main/java/ghidra/app/plugin/core/analysis/AARCH64PltThunkAnalyzer.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. @@ -175,7 +175,7 @@ public class AARCH64PltThunkAnalyzer extends AbstractAnalyzer { private void analyzePltThunk(Program program, Address entryAddr, int thunkSize, TaskMonitor monitor) throws CancelledException { - SymbolicPropogator symEval = new SymbolicPropogator(program); + SymbolicPropogator symEval = new SymbolicPropogator(program, false); symEval.setParamRefCheck(false); symEval.setReturnRefCheck(false); symEval.setStoredRefCheck(false); 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 90ab885b7d..bb515bc4d3 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 @@ -541,7 +541,7 @@ public class ArmAnalyzer extends ConstantPropagationAnalyzer { SymbolicPropogator targetEval = symEval; // if this is a tbX instruction, don't assume any old values if (targetInstr != null && targetInstr.getMnemonicString().startsWith("tb")) { - targetEval = new SymbolicPropogator(program); + targetEval = new SymbolicPropogator(program, false); } Address zeroAddr = targetInstr.getMinAddress().getNewAddress(0); diff --git a/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/plugin/core/analysis/PPC64CallStubAnalyzer.java b/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/plugin/core/analysis/PPC64CallStubAnalyzer.java index 97d7cac188..a7fbd20651 100644 --- a/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/plugin/core/analysis/PPC64CallStubAnalyzer.java +++ b/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/plugin/core/analysis/PPC64CallStubAnalyzer.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. @@ -248,7 +248,7 @@ public class PPC64CallStubAnalyzer extends AbstractAnalyzer { private void analyzeCallStub(Program program, Function stubFunction, int stubLength, TaskMonitor monitor) throws CancelledException { - SymbolicPropogator symEval = new SymbolicPropogator(program); + SymbolicPropogator symEval = new SymbolicPropogator(program, false); symEval.setParamRefCheck(false); symEval.setReturnRefCheck(false); symEval.setStoredRefCheck(false);