Open goatshriek opened 2 years ago
Any update on this?
I took another look at this now that I'm 6 months wiser, and I was able to get something working that suits my needs. The crux of my problem was that the SymbolicPropogator
uses its own internal scheme for address spaces and other things, meaning you can't just get storage Varnode
s for local variables and throw them at the memory retrieval function. That probably explains why the function is protected: so that people like me don't get any bright ideas about abusing it.
You can do a sort of Varnode
translation though, which is what the script below does. I'm sure there are lots of opportunities to make it cleaner, but I have what I need so this issue is done for my part. I would be curious to hear the Ghidra team's thoughts on this approach and if there is potentially a less ugly way to do it. I suppose the issue could be left open in that case to track a potential enhancement to Ghidra itself if that is deemed useful.
I'll also submit a separate pull request sometime in the future with documentation improvements I made while stumbling through this. Perhaps it will help others come up with something cleaner.
import java.util.HashMap;
import ghidra.app.plugin.core.analysis.ConstantPropagationContextEvaluator;
import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.ProgramContext;
import ghidra.program.model.listing.Variable;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.util.ContextEvaluator;
import ghidra.program.util.SymbolicPropogator;
import ghidra.program.util.VarnodeContext;
public class MemoryConstantDemo extends GhidraScript {
public class MemoryEnabledSymbolicPropogator extends SymbolicPropogator {
public MemoryEnabledSymbolicPropogator(Program program) {
super(program);
this.context = new MemoryEnabledVarnodeContext(program, this.programContext, this.spaceContext);
this.context.setDebug(true);
}
public Value getMemoryValue(Address toAddr, Address memory) {
return null;
}
public MemoryEnabledVarnodeContext getContext() {
return (MemoryEnabledVarnodeContext) this.context;
}
}
public class MemoryEnabledVarnodeContext extends VarnodeContext {
public MemoryEnabledVarnodeContext(Program program, ProgramContext programContext,
ProgramContext spaceProgramContext) {
super(program, programContext, spaceProgramContext);
}
public Varnode newGetMemoryValue(Varnode varnode) {
return this.getMemoryValue(varnode);
}
@Override
protected void putMemoryValue(Varnode out, Varnode value) {
println("putMemoryValue called:");
println("out: " + out.toString() + " (" + out.getClass() + ")");
println("value: " + value.toString() + " (" + value.getClass() + ")");
println();
super.putMemoryValue(out, value);
}
/* useful if you just want to visually inspect */
public void dumpMemory() {
for (HashMap<Varnode, Varnode> mem : memoryVals) {
for (Varnode v : mem.keySet()) {
println("# memory entry");
println(v.toString() + ": " + mem.get(v).toString());
println("space id: " + v.getSpace());
println("offset: " + v.getOffset());
println();
}
}
}
public AddressSpace getAddressSpaceItself(String name) {
return addrFactory.getAddressSpace(name);
}
}
public void run() throws Exception {
Function func = currentProgram.getFunctionManager().getFunctionContaining(currentAddress);
if (func == null) {
printerr("there is no current function!");
return;
}
Address start = func.getEntryPoint();
// do the flow analysis
ContextEvaluator eval = new ConstantPropagationContextEvaluator(true);
MemoryEnabledSymbolicPropogator symEval = new MemoryEnabledSymbolicPropogator(currentProgram);
symEval.flowConstants(start, func.getBody(), eval, true, monitor);
// get the internal address space used by the propogator for ESP
AddressSpace espSpace = symEval.getContext().getAddressSpaceItself("ESP");
println("ESP address space: " + espSpace);
for (Variable v : func.getStackFrame().getLocals()) {
println("local variable: " + v.toString());
Varnode use = v.getFirstStorageVarnode();
println("first use varnode: " + use.toString());
// create the varnode the internal propogator would have used for this local
long translatedOffset = use.getOffset() + 0x100000000L;
Varnode contextVarnode = new Varnode(espSpace.getTruncatedAddress(translatedOffset, true), use.getSize());
println("equivalent varnode: " + contextVarnode);
// search for it!
Varnode result = symEval.getContext().newGetMemoryValue(contextVarnode);
if (result == null) {
println("no symbolic entry found");
} else {
println("found symbolic entry: " + result);
}
println();
}
}
}
Some of the output from running this against an example function:
local variable: [undefined1 local_fc@Stack[-0xfc]:1]
first use varnode: (stack, 0xffffffffffffff04, 1)
equivalent varnode: (ESP, 0xffffff04, 1)
no symbolic entry found
local variable: [undefined1 local_40@Stack[-0x40]:1]
first use varnode: (stack, 0xffffffffffffffc0, 1)
equivalent varnode: (ESP, 0xffffffc0, 1)
no symbolic entry found
local variable: [undefined4 local_18@Stack[-0x18]:4]
first use varnode: (stack, 0xffffffffffffffe8, 4)
equivalent varnode: (ESP, 0xffffffe8, 4)
found symbolic entry: (BAD_ADDRESS_SPACE, 0x0, 0)
local variable: [undefined4 local_10@Stack[-0x10]:4]
first use varnode: (stack, 0xfffffffffffffff0, 4)
equivalent varnode: (ESP, 0xfffffff0, 4)
found symbolic entry: (BAD_ADDRESS_SPACE, 0x0, 0)
local variable: [void * puStack12@Stack[-0xc]:4]
first use varnode: (stack, 0xfffffffffffffff4, 4)
equivalent varnode: (ESP, 0xfffffff4, 4)
found symbolic entry: (const, 0x41ab00, 4)
local variable: [undefined4 local_8@Stack[-0x8]:4]
first use varnode: (stack, 0xfffffffffffffff8, 4)
equivalent varnode: (ESP, 0xfffffff8, 4)
found symbolic entry: (const, 0xffffffff, 4)
EDIT. Sorry @goatshriek I thought that the person who closed the issue was a maintainer rather than you :) I don't think this issue should be closed. The workaround you posted is useful, but I do think that the functionality should be provided natively by Ghidra.
Sure, I will leave it open in hopes that they will get to it at some point. The lack of any sort of response in over a year is not encouraging though...
We will fix it.
@lautalom could you please take a look?
bump :slightly_smiling_face:
One of the scripts packaged with Ghidra,
ResolveX86orX64LinuxSyscallsScript
, makes use of a constant propagater that allows constant register values to be retrieved. This turns out to be pretty handy for many things.The class that is used,
SymbolicPropogator
, has an easy way to retrieve register values at a specific point of execution. However, there doesn't seem to be an equivalent capability for memory addresses. I've tried to subclassVarnodeContext
to use its protectedgetMemoryValue
method, but haven't had any success with that, at least not when passing it stack Varnodes.Does anyone know of a way to do constant propagation with memory addresses in addition to registers? It seems like this should be possible, but I can't find a way to do it. Particularly, I'm looking for a simple way to see if stack-based variables can be resolved to constant values.