Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

constant propagation of memory #3581

Open
goatshriek opened this issue Nov 2, 2021 · 7 comments
Open

constant propagation of memory #3581

goatshriek opened this issue Nov 2, 2021 · 7 comments

Comments

@goatshriek
Copy link
Contributor

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 subclass VarnodeContext to use its protected getMemoryValue 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.

@dgutson
Copy link

dgutson commented May 19, 2022

Any update on this?

@goatshriek
Copy link
Contributor Author

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 Varnodes 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)

@dgutson
Copy link

dgutson commented Nov 5, 2022

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.

@goatshriek
Copy link
Contributor Author

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...

@goatshriek goatshriek reopened this Nov 5, 2022
@dgutson
Copy link

dgutson commented Nov 5, 2022

We will fix it.

@dgutson
Copy link

dgutson commented Nov 5, 2022

@lautalom could you please take a look?

@lockbox
Copy link

lockbox commented Jul 3, 2024

bump 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants