mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-26 07:47:07 +08:00
GP-6369: Convert remaining uses of EmulatorHelper to PcodeEmulator.
This commit is contained in:
+12
-7
@@ -230,7 +230,7 @@ public enum TraceEmulationIntegration {
|
||||
* @param set the uninitialized portion required
|
||||
* @return the addresses in {@code set} that remain uninitialized
|
||||
* @see PcodeEmulationCallbacks#readUninitialized(PcodeThread, PcodeExecutorStatePiece,
|
||||
* AddressSetView)
|
||||
* AddressSetView, Reason)
|
||||
*/
|
||||
AddressSetView readUninitialized(PcodeTraceDataAccess acc, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<A, T> piece, AddressSetView set);
|
||||
@@ -246,12 +246,14 @@ public enum TraceEmulationIntegration {
|
||||
* @param space the address space
|
||||
* @param offset the offset at the start of the uninitialized portion
|
||||
* @param length the size in bytes of the uninitialized portion
|
||||
* @param reason the reason for reading
|
||||
* @return the number of bytes just initialized, typically 0 or {@code length}
|
||||
* @see PcodeEmulationCallbacks#readUninitialized(PcodeThread, PcodeExecutorStatePiece,
|
||||
* AddressSpace, Object, int)
|
||||
* AddressSpace, Object, int, Reason)
|
||||
*/
|
||||
default int abstractReadUninit(PcodeTraceDataAccess acc, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<A, T> piece, AddressSpace space, A offset, int length) {
|
||||
PcodeExecutorStatePiece<A, T> piece, AddressSpace space, A offset, int length,
|
||||
Reason reason) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -542,7 +544,8 @@ public enum TraceEmulationIntegration {
|
||||
*/
|
||||
@Override
|
||||
public int abstractReadUninit(PcodeTraceDataAccess acc, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<A, T> piece, AddressSpace space, A offset, int length) {
|
||||
PcodeExecutorStatePiece<A, T> piece, AddressSpace space, A offset, int length,
|
||||
Reason reason) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@@ -783,14 +786,16 @@ public enum TraceEmulationIntegration {
|
||||
|
||||
@Override
|
||||
public <B, U> int readUninitialized(PcodeThread<Object> thread,
|
||||
PcodeExecutorStatePiece<B, U> piece, AddressSpace space, B offset, int length) {
|
||||
PcodeExecutorStatePiece<B, U> piece, AddressSpace space, B offset, int length,
|
||||
Reason reason) {
|
||||
PcodeTraceDataAccess acc = space.isRegisterSpace() ? getRegAccess(thread) : memAccess;
|
||||
return handlerFor(piece).abstractReadUninit(acc, thread, piece, space, offset, length);
|
||||
return handlerFor(piece).abstractReadUninit(acc, thread, piece, space, offset, length,
|
||||
reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <B, U> AddressSetView readUninitialized(PcodeThread<Object> thread,
|
||||
PcodeExecutorStatePiece<B, U> piece, AddressSetView set) {
|
||||
PcodeExecutorStatePiece<B, U> piece, AddressSetView set, Reason reason) {
|
||||
if (set.isEmpty()) {
|
||||
return set;
|
||||
}
|
||||
|
||||
+1
-1
@@ -118,7 +118,7 @@ public class TaintPcodeExecutorStatePiece
|
||||
@Override
|
||||
protected TaintVec getFromSpace(TaintSpace space, long offset, int size, Reason reason,
|
||||
PcodeStateCallbacks cb) {
|
||||
return space.get(offset, size, cb);
|
||||
return space.get(offset, size, reason, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+11
-8
@@ -18,6 +18,7 @@ package ghidra.pcode.emu.taint.state;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Register;
|
||||
@@ -85,14 +86,15 @@ public class TaintSpace {
|
||||
*
|
||||
* @param offset the offset
|
||||
* @param buf the vector to receive taint sets
|
||||
* @param reason the reason for reading
|
||||
* @param cb callbacks to receive emulation events
|
||||
*/
|
||||
public void getInto(long offset, TaintVec buf, PcodeStateCallbacks cb) {
|
||||
public void getInto(long offset, TaintVec buf, Reason reason, PcodeStateCallbacks cb) {
|
||||
for (int i = 0; i < buf.length; i++) {
|
||||
TaintSet s = taints.get(offset + i);
|
||||
if (s == null) {
|
||||
if (cb.readUninitialized(piece, PcodeStateCallbacks.rngSet(space, offset + i, 1))
|
||||
.isEmpty()) {
|
||||
if (cb.readUninitialized(piece, PcodeStateCallbacks.rngSet(space, offset + i, 1),
|
||||
reason).isEmpty()) {
|
||||
s = taints.get(offset + i);
|
||||
}
|
||||
}
|
||||
@@ -107,17 +109,18 @@ public class TaintSpace {
|
||||
* Retrieve the taint sets for the variable at the given offset
|
||||
*
|
||||
* <p>
|
||||
* This works the same as {@link #getInto(long, TaintVec, PcodeStateCallbacks)}, but creates a
|
||||
* new vector of the given size, reads the taint sets, and returns the vector.
|
||||
* This works the same as {@link #getInto(long, TaintVec, Reason, PcodeStateCallbacks)}, but
|
||||
* creates a new vector of the given size, reads the taint sets, and returns the vector.
|
||||
*
|
||||
* @param offset the offset
|
||||
* @param size the size of the variable
|
||||
* @param reason the reason for reading
|
||||
* @param cb callbacks to receive emulation events
|
||||
* @return the taint vector for that variable
|
||||
*/
|
||||
public TaintVec get(long offset, int size, PcodeStateCallbacks cb) {
|
||||
public TaintVec get(long offset, int size, Reason reason, PcodeStateCallbacks cb) {
|
||||
TaintVec vec = new TaintVec(size);
|
||||
getInto(offset, vec, cb);
|
||||
getInto(offset, vec, reason, cb);
|
||||
return vec;
|
||||
}
|
||||
|
||||
@@ -153,7 +156,7 @@ public class TaintSpace {
|
||||
}
|
||||
PcodeOp pcodeOp = ops.get(offset); // Needed here to generate the TaintVec
|
||||
TaintVec vec = new TaintVec(MathUtilities.unsignedMin(1024, end - offset), pcodeOp);
|
||||
getInto(offset, vec, PcodeStateCallbacks.NONE);
|
||||
getInto(offset, vec, Reason.INSPECT, PcodeStateCallbacks.NONE);
|
||||
return Map.entry(offset, vec);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -109,9 +109,10 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
||||
* @param size the number of bytes to read (the size of the value)
|
||||
* @return the read value
|
||||
*/
|
||||
protected SymValueZ3 getUnique(SymValueZ3 offset, int size, PcodeStateCallbacks cb) {
|
||||
protected SymValueZ3 getUnique(SymValueZ3 offset, int size, Reason reason,
|
||||
PcodeStateCallbacks cb) {
|
||||
S s = getForSpace(uniqueSpace, false);
|
||||
return getFromSpace(s, offset, size, cb);
|
||||
return getFromSpace(s, offset, size, reason, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,10 +144,11 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
||||
* @param space the address space
|
||||
* @param offset the offset within the space
|
||||
* @param size the number of bytes to read (the size of the value)
|
||||
* @param reason the reason for reading
|
||||
* @param cb callbacks to receive emulation events
|
||||
* @return the read value
|
||||
*/
|
||||
protected abstract SymValueZ3 getFromSpace(S space, SymValueZ3 offset, int size,
|
||||
protected abstract SymValueZ3 getFromSpace(S space, SymValueZ3 offset, int size, Reason reason,
|
||||
PcodeStateCallbacks cb);
|
||||
|
||||
/**
|
||||
@@ -229,7 +231,7 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
||||
}
|
||||
}
|
||||
if (space.isUniqueSpace()) {
|
||||
return getUnique(offset, size, cb);
|
||||
return getUnique(offset, size, reason, cb);
|
||||
}
|
||||
S s = getForSpace(space, false);
|
||||
//Msg.info(this, "Now we likely have a space to get from: " + s);
|
||||
@@ -237,7 +239,7 @@ public abstract class AbstractSymZ3OffsetPcodeExecutorStatePiece<S>
|
||||
return getFromNullSpace(size, cb);
|
||||
}
|
||||
//offset = quantizeOffset(space, offset);
|
||||
return getFromSpace(s, offset, size, cb);
|
||||
return getFromSpace(s, offset, size, reason, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -144,9 +144,9 @@ public class SymZ3PcodeExecutorStatePiece
|
||||
* the storage space.
|
||||
*/
|
||||
@Override
|
||||
protected SymValueZ3 getFromSpace(SymZ3Space space, SymValueZ3 offset, int size,
|
||||
protected SymValueZ3 getFromSpace(SymZ3Space space, SymValueZ3 offset, int size, Reason reason,
|
||||
PcodeStateCallbacks cb) {
|
||||
return space.get(offset, size, cb);
|
||||
return space.get(offset, size, reason, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-2
@@ -23,6 +23,7 @@ import com.microsoft.z3.Context;
|
||||
import ghidra.pcode.emu.symz3.AbstractSymZ3OffsetPcodeExecutorStatePiece;
|
||||
import ghidra.pcode.emu.symz3.SymZ3MemoryMap;
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
@@ -53,9 +54,9 @@ public class SymZ3MemorySpace extends SymZ3Space {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) {
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb) {
|
||||
if (!mmap.hasValueFor(offset, size)) {
|
||||
cb.readUninitialized(piece, space, offset, size);
|
||||
cb.readUninitialized(piece, space, offset, size, reason);
|
||||
}
|
||||
return mmap.load(offset, size, true, cb);
|
||||
}
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ public class SymZ3PieceHandler
|
||||
@Override
|
||||
public int abstractReadUninit(PcodeTraceDataAccess acc, PcodeThread<?> thread,
|
||||
PcodeExecutorStatePiece<SymValueZ3, SymValueZ3> piece, AddressSpace space,
|
||||
SymValueZ3 offset, int length) {
|
||||
SymValueZ3 offset, int length, Reason reason) {
|
||||
String string = acc.getPropertyAccess(NAME, String.class).get(Address.NO_ADDRESS);
|
||||
if (string == null) {
|
||||
return 0;
|
||||
|
||||
+3
-2
@@ -23,6 +23,7 @@ import com.microsoft.z3.Context;
|
||||
import ghidra.pcode.emu.symz3.AbstractSymZ3OffsetPcodeExecutorStatePiece;
|
||||
import ghidra.pcode.emu.symz3.SymZ3RegisterMap;
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Language;
|
||||
@@ -87,7 +88,7 @@ public class SymZ3RegisterSpace extends SymZ3Space {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) {
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb) {
|
||||
Register r = getRegister(offset, size);
|
||||
if (r == null) {
|
||||
Msg.warn(this, "unable to get register with space: " + space.getSpaceID() +
|
||||
@@ -95,7 +96,7 @@ public class SymZ3RegisterSpace extends SymZ3Space {
|
||||
return null;
|
||||
}
|
||||
if (!rmap.hasValueForRegister(r)) {
|
||||
cb.readUninitialized(piece, space, offset, size);
|
||||
cb.readUninitialized(piece, space, offset, size, reason);
|
||||
}
|
||||
SymValueZ3 result = this.rmap.getRegister(r);
|
||||
return result;
|
||||
|
||||
+3
-1
@@ -22,6 +22,7 @@ import java.util.stream.Stream;
|
||||
import com.microsoft.z3.Context;
|
||||
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
||||
@@ -36,7 +37,8 @@ import ghidra.symz3.model.SymValueZ3;
|
||||
public abstract class SymZ3Space {
|
||||
public abstract void set(SymValueZ3 offset, int size, SymValueZ3 val, PcodeStateCallbacks cb);
|
||||
|
||||
public abstract SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb);
|
||||
public abstract SymValueZ3 get(SymValueZ3 offset, int size, Reason reason,
|
||||
PcodeStateCallbacks cb);
|
||||
|
||||
public abstract String printableSummary();
|
||||
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@ import java.util.stream.Stream;
|
||||
import com.microsoft.z3.*;
|
||||
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.symz3.model.SymValueZ3;
|
||||
|
||||
@@ -86,7 +87,7 @@ public class SymZ3UniqueSpace extends SymZ3Space {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) {
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb) {
|
||||
assert offset != null;
|
||||
try (Context ctx = new Context()) {
|
||||
BitVecExpr b = offset.getBitVecExpr(ctx);
|
||||
|
||||
+2
-1
@@ -26,6 +26,7 @@ import ghidra.pcode.emu.symz3.SymZ3MemoryMap;
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.emu.symz3.state.SymZ3PieceHandler;
|
||||
import ghidra.pcode.emu.symz3.state.SymZ3WriteDownHelper;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
|
||||
import ghidra.program.model.address.*;
|
||||
@@ -86,7 +87,7 @@ public class SymZ3TraceMemorySpace extends SymZ3TraceSpace {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) {
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb) {
|
||||
if (mmap.hasValueFor(offset, size)) {
|
||||
return mmap.load(offset, size, true, cb);
|
||||
}
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@ import com.microsoft.z3.Context;
|
||||
import ghidra.pcode.emu.symz3.SymZ3RegisterMap;
|
||||
import ghidra.pcode.emu.symz3.lib.Z3InfixPrinter;
|
||||
import ghidra.pcode.emu.symz3.state.SymZ3WriteDownHelper;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.pcode.exec.PcodeStateCallbacks;
|
||||
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
@@ -96,7 +97,7 @@ public class SymZ3TraceRegisterSpace extends SymZ3TraceSpace {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, PcodeStateCallbacks cb) {
|
||||
public SymValueZ3 get(SymValueZ3 offset, int size, Reason reason, PcodeStateCallbacks cb) {
|
||||
assert offset != null;
|
||||
Register r = getRegister(offset, size);
|
||||
if (r == null) {
|
||||
|
||||
@@ -25,27 +25,34 @@
|
||||
// to the function "deobfuscate" so that the various return values can be recorded with a comment
|
||||
// placed just after the call.
|
||||
//@category Examples.Emulation
|
||||
import ghidra.app.emulator.EmulatorHelper;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.util.opinion.ElfLoader;
|
||||
import ghidra.pcode.emulate.EmulateExecutionState;
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.pcode.exec.InterruptPcodeExecutionException;
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
|
||||
public class EmuX86DeobfuscateExampleScript extends GhidraScript {
|
||||
|
||||
private static String PROGRAM_NAME = "deobExample";
|
||||
private static final String PROGRAM_NAME = "deobExample";
|
||||
|
||||
private EmulatorHelper emuHelper;
|
||||
private PcodeEmulator emu;
|
||||
private PcodeThread<byte[]> emuThread;
|
||||
private PcodeArithmetic<byte[]> arithmetic;
|
||||
|
||||
// Important breakpoint locations
|
||||
private Address deobfuscateCall;
|
||||
private Address deobfuscateReturn;
|
||||
|
||||
// Function locations
|
||||
private Function mainFunction;
|
||||
private Address mainFunctionEntry; // start of emulation address
|
||||
|
||||
// Address used as final return location
|
||||
@@ -66,18 +73,21 @@ public class EmuX86DeobfuscateExampleScript extends GhidraScript {
|
||||
!"x86:LE:64:default".equals(currentProgram.getLanguageID().toString()) ||
|
||||
!ElfLoader.ELF_NAME.equals(format)) {
|
||||
|
||||
printerr(
|
||||
"This emulation example script is specifically intended to be executed against the\n" +
|
||||
PROGRAM_NAME +
|
||||
" program whose source is contained within the GhidraClass exercise files\n" +
|
||||
"(see docs/GhidraClass/ExerciseFiles/Emulation/" + PROGRAM_NAME + ".c).\n" +
|
||||
"This program should be compiled using gcc for x86 64-bit, imported into your project, \n" +
|
||||
"analyzed and open as the active program before running ths script.");
|
||||
printerr("""
|
||||
This emulation example script is specifically intended to be executed against
|
||||
the %s program whose source is contained within the GhidraClass exercise files
|
||||
(see docs/GhidraClass/ExerciseFiles/Emulation/%s.c). This program should be
|
||||
compiled using gcc for x86 64-bit, imported into your project, analyzed and
|
||||
open as the active program before running ths script."""
|
||||
.formatted(PROGRAM_NAME, PROGRAM_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
// Identify function to be emulated
|
||||
mainFunctionEntry = getSymbolAddress("main");
|
||||
mainFunction = getGlobalFunctions("main").stream()
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new NoSuchElementException("main"));
|
||||
mainFunctionEntry = mainFunction.getEntryPoint();
|
||||
|
||||
// Obtain entry instruction in order to establish initial processor context
|
||||
Instruction entryInstr = getInstructionAt(mainFunctionEntry);
|
||||
@@ -100,53 +110,54 @@ public class EmuX86DeobfuscateExampleScript extends GhidraScript {
|
||||
// Remove prior pre-comment
|
||||
setPreComment(deobfuscateReturn, null);
|
||||
|
||||
// Establish emulation helper
|
||||
emuHelper = new EmulatorHelper(currentProgram);
|
||||
try {
|
||||
// Establish emulator
|
||||
emu = new PcodeEmulator(currentProgram.getLanguage());
|
||||
monitor.addCancelledListener(() -> {
|
||||
emu.setSuspended(true);
|
||||
});
|
||||
emuThread = emu.newThread();
|
||||
arithmetic = emuThread.getArithmetic();
|
||||
EmulatorUtilities.loadProgram(emu, currentProgram);
|
||||
|
||||
// Initialize stack pointer (not used by this example)
|
||||
long stackOffset =
|
||||
(entryInstr.getAddress().getAddressSpace().getMaxAddress().getOffset() >>> 1) -
|
||||
0x7fff;
|
||||
emuHelper.writeRegister(emuHelper.getStackPointerRegister(), stackOffset);
|
||||
// Initialize program counter, registers from context, and stack pointer
|
||||
EmulatorUtilities.initializeForFunction(emuThread, mainFunction);
|
||||
Address stackBase =
|
||||
EmulatorUtilities.inspectStackPointer(emuThread, currentProgram.getCompilerSpec());
|
||||
|
||||
// Setup breakpoints
|
||||
emuHelper.setBreakpoint(deobfuscateCall);
|
||||
emuHelper.setBreakpoint(deobfuscateReturn);
|
||||
// Setup breakpoints
|
||||
emu.addBreakpoint(deobfuscateCall, "1:1");
|
||||
emu.addBreakpoint(deobfuscateReturn, "1:1");
|
||||
|
||||
// Set controlled return location so we can identify return from emulated function
|
||||
controlledReturnAddr = getAddress(CONTROLLED_RETURN_OFFSET);
|
||||
emuHelper.writeStackValue(0, 8, CONTROLLED_RETURN_OFFSET);
|
||||
emuHelper.setBreakpoint(controlledReturnAddr);
|
||||
// Set controlled return location so we can identify return from emulated function
|
||||
controlledReturnAddr = getAddress(CONTROLLED_RETURN_OFFSET);
|
||||
emuThread.getState()
|
||||
.setVar(stackBase.add(8), 8, false, arithmetic.fromConst(controlledReturnAddr));
|
||||
emu.addBreakpoint(controlledReturnAddr, "1:1");
|
||||
|
||||
Msg.debug(this, "EMU starting at " + mainFunctionEntry);
|
||||
Msg.debug(this, "EMU starting at " + emuThread.getCounter());
|
||||
|
||||
// Execution loop until return from function or error occurs
|
||||
while (!monitor.isCancelled()) {
|
||||
boolean success =
|
||||
(emuHelper.getEmulateExecutionState() == EmulateExecutionState.BREAKPOINT)
|
||||
? emuHelper.run(monitor)
|
||||
: emuHelper.run(mainFunctionEntry, entryInstr, monitor);
|
||||
Address executionAddress = emuHelper.getExecutionAddress();
|
||||
if (monitor.isCancelled()) {
|
||||
println("Emulation cancelled");
|
||||
return;
|
||||
}
|
||||
if (executionAddress.equals(controlledReturnAddr)) {
|
||||
println("Returned from function");
|
||||
return;
|
||||
}
|
||||
if (!success) {
|
||||
String lastError = emuHelper.getLastError();
|
||||
printerr("Emulation Error: " + lastError);
|
||||
return;
|
||||
}
|
||||
processBreakpoint(executionAddress);
|
||||
// Execution loop until return from function or error occurs
|
||||
while (!monitor.isCancelled()) {
|
||||
try {
|
||||
emuThread.run();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
// cleanup resources and release hold on currentProgram
|
||||
emuHelper.dispose();
|
||||
catch (InterruptPcodeExecutionException e) {
|
||||
// Hit a breakpoint. Good.
|
||||
}
|
||||
catch (Throwable t) {
|
||||
printerr("Emulation Error: " + t);
|
||||
return;
|
||||
}
|
||||
if (monitor.isCancelled()) {
|
||||
println("Emulation cancelled");
|
||||
return;
|
||||
}
|
||||
Address executionAddress = emuThread.getCounter();
|
||||
if (executionAddress.equals(controlledReturnAddr)) {
|
||||
println("Returned from function");
|
||||
return;
|
||||
}
|
||||
processBreakpoint(executionAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,19 +167,25 @@ public class EmuX86DeobfuscateExampleScript extends GhidraScript {
|
||||
|
||||
/**
|
||||
* Perform processing for the various breakpoints.
|
||||
*
|
||||
* @param addr current execute address where emulation has been suspended
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
private void processBreakpoint(Address addr) throws Exception {
|
||||
|
||||
if (addr.equals(deobfuscateCall)) {
|
||||
lastDeobfuscateArg0 = emuHelper.readRegister("RDI").longValue();
|
||||
Register rdi = currentProgram.getRegister("RDI");
|
||||
lastDeobfuscateArg0 =
|
||||
emuThread.getState().inspectRegisterValue(rdi).getUnsignedValue().longValue();
|
||||
}
|
||||
|
||||
else if (addr.equals(deobfuscateReturn)) {
|
||||
long deobfuscateReturnValue = emuHelper.readRegister("RAX").longValue();
|
||||
String str = "deobfuscate(src=0x" + Long.toHexString(lastDeobfuscateArg0) + ") -> \"" +
|
||||
emuHelper.readNullTerminatedString(getAddress(deobfuscateReturnValue), 32) + "\"";
|
||||
Register rax = currentProgram.getRegister("RAX");
|
||||
long deobfuscateReturnValue =
|
||||
emuThread.getState().inspectRegisterValue(rax).getUnsignedValue().longValue();
|
||||
String read = EmulatorUtilities.decodeNullTerminatedString(emuThread.getState(),
|
||||
getAddress(deobfuscateReturnValue));
|
||||
String str = """
|
||||
deobfuscate(src=0x%x) -> "%s"\
|
||||
""".formatted(lastDeobfuscateArg0, read);
|
||||
String comment = getPreComment(deobfuscateReturn);
|
||||
if (comment == null) {
|
||||
comment = "";
|
||||
@@ -192,14 +209,4 @@ public class EmuX86DeobfuscateExampleScript extends GhidraScript {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Address getSymbolAddress(String symbolName) throws NotFoundException {
|
||||
Symbol symbol = SymbolUtilities.getLabelOrFunctionSymbol(currentProgram, symbolName,
|
||||
err -> Msg.error(this, err));
|
||||
if (symbol != null) {
|
||||
return symbol.getAddress();
|
||||
}
|
||||
throw new NotFoundException("Failed to locate label: " + symbolName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,23 +23,23 @@
|
||||
// data. This script hooks the functions "malloc", "free" and "use_string" where the later
|
||||
// simply prints the deobfuscated string passed as an argument.
|
||||
//@category Examples.Emulation
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.emulator.EmulatorHelper;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import ghidra.app.util.opinion.ElfLoader;
|
||||
import ghidra.pcode.emulate.EmulateExecutionState;
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.InsufficientBytesException;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.pcode.Varnode;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
|
||||
public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript {
|
||||
|
||||
private static String PROGRAM_NAME = "deobHookExample";
|
||||
private static final String PROGRAM_NAME = "deobHookExample";
|
||||
|
||||
// Heap allocation area
|
||||
private static final int MALLOC_REGION_SIZE = 0x1000;
|
||||
@@ -47,7 +47,10 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript {
|
||||
// Address used as final return location
|
||||
private static final long CONTROLLED_RETURN_OFFSET = 0;
|
||||
|
||||
private EmulatorHelper emuHelper;
|
||||
private PcodeEmulator emu;
|
||||
private PcodeThread<byte[]> emuThread;
|
||||
private PcodeArithmetic<byte[]> arithmetic;
|
||||
|
||||
private SimpleMallocMgr mallocMgr;
|
||||
|
||||
// Important breakpoint locations for hooking behavior not contained with binary (e.g., dynamic library)
|
||||
@@ -57,6 +60,7 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript {
|
||||
private Address useStringEntry;
|
||||
|
||||
// Function locations
|
||||
private Function mainFunction;
|
||||
private Address mainFunctionEntry; // start of emulation
|
||||
private Address controlledReturnAddr; // end of emulation
|
||||
|
||||
@@ -69,18 +73,19 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript {
|
||||
!"x86:LE:64:default".equals(currentProgram.getLanguageID().toString()) ||
|
||||
!ElfLoader.ELF_NAME.equals(format)) {
|
||||
|
||||
printerr(
|
||||
"This emulation example script is specifically intended to be executed against the\n" +
|
||||
PROGRAM_NAME +
|
||||
" program whose source is contained within the GhidraClass exercise files\n" +
|
||||
"(see docs/GhidraClass/ExerciseFiles/Emulation/" + PROGRAM_NAME + ".c).\n" +
|
||||
"This program should be compiled using gcc for x86 64-bit, imported into your project, \n" +
|
||||
"analyzed and open as the active program before running ths script.");
|
||||
printerr("""
|
||||
This emulation example script is specifically intended to be executed against
|
||||
the %s program whose source is contained within the GhidraClass exercise files
|
||||
(see docs/GhidraClass/ExerciseFiles/Emulation/%s.c). This program should be
|
||||
compiled using gcc for x86 64-bit, imported into your project, analyzed and
|
||||
open as the active program before running ths script."""
|
||||
.formatted(PROGRAM_NAME, PROGRAM_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
// Identify function be emulated
|
||||
mainFunctionEntry = getSymbolAddress("main");
|
||||
mainFunction = currentProgram.getFunctionManager().getFunctionAt(mainFunctionEntry);
|
||||
useStringEntry = getSymbolAddress("use_string");
|
||||
|
||||
// Identify important symbol addresses
|
||||
@@ -88,59 +93,71 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript {
|
||||
freeEntry = getExternalThunkAddress("free");
|
||||
strlenEntry = getExternalThunkAddress("strlen");
|
||||
|
||||
// Establish emulation helper
|
||||
emuHelper = new EmulatorHelper(currentProgram);
|
||||
try {
|
||||
// Initialize stack pointer (not used by this example)
|
||||
long stackOffset =
|
||||
(mainFunctionEntry.getAddressSpace().getMaxAddress().getOffset() >>> 1) - 0x7fff;
|
||||
emuHelper.writeRegister(emuHelper.getStackPointerRegister(), stackOffset);
|
||||
|
||||
// Establish simple malloc memory manager with memory region spaced relative to stack pointer
|
||||
mallocMgr = new SimpleMallocMgr(getAddress(stackOffset - 0x10000), MALLOC_REGION_SIZE);
|
||||
|
||||
// Setup hook breakpoints
|
||||
emuHelper.setBreakpoint(mallocEntry);
|
||||
emuHelper.setBreakpoint(freeEntry);
|
||||
emuHelper.setBreakpoint(strlenEntry);
|
||||
emuHelper.setBreakpoint(useStringEntry);
|
||||
|
||||
// Set controlled return location so we can identify return from emulated function
|
||||
controlledReturnAddr = getAddress(CONTROLLED_RETURN_OFFSET);
|
||||
emuHelper.writeStackValue(0, 8, CONTROLLED_RETURN_OFFSET);
|
||||
emuHelper.setBreakpoint(controlledReturnAddr);
|
||||
|
||||
// This example directly manipulates the PC register to facilitate hooking
|
||||
// which must alter the PC during a breakpoint, and optional stepping which does not
|
||||
// permit an initial address to be specified.
|
||||
emuHelper.writeRegister(emuHelper.getPCRegister(), mainFunctionEntry.getOffset());
|
||||
Msg.debug(this, "EMU starting at " + emuHelper.getExecutionAddress());
|
||||
|
||||
// Execution loop until return from function or error occurs
|
||||
while (!monitor.isCancelled()) {
|
||||
// Use stepping if needed for troubleshooting - although it runs much slower
|
||||
//boolean success = emuHelper.step();
|
||||
boolean success = emuHelper.run(monitor);
|
||||
Address executionAddress = emuHelper.getExecutionAddress();
|
||||
if (monitor.isCancelled()) {
|
||||
println("Emulation cancelled");
|
||||
return;
|
||||
}
|
||||
if (executionAddress.equals(controlledReturnAddr)) {
|
||||
println("Returned from function");
|
||||
return;
|
||||
}
|
||||
if (!success) {
|
||||
String lastError = emuHelper.getLastError();
|
||||
printerr("Emulation Error: " + lastError);
|
||||
return;
|
||||
}
|
||||
processBreakpoint(executionAddress);
|
||||
// Establish emulator
|
||||
emu = new PcodeEmulator(currentProgram.getLanguage()) {
|
||||
@Override
|
||||
protected PcodeUseropLibrary<byte[]> createUseropLibrary() {
|
||||
return super.createUseropLibrary().compose(new DeobfUseropLibrary<byte[]>());
|
||||
}
|
||||
};
|
||||
monitor.addCancelledListener(() -> {
|
||||
emu.setSuspended(true);
|
||||
});
|
||||
emuThread = emu.newThread();
|
||||
arithmetic = emuThread.getArithmetic();
|
||||
EmulatorUtilities.loadProgram(emu, currentProgram);
|
||||
|
||||
// Initialize program counter, registers from context, and stack pointer
|
||||
// Request more stack space that normal, so we can use the extra for the heap
|
||||
EmulatorUtilities.initializeForFunction(emuThread, mainFunction,
|
||||
EmulatorUtilities.DEFAULT_STACK_SIZE + MALLOC_REGION_SIZE);
|
||||
Address stackBase =
|
||||
EmulatorUtilities.inspectStackPointer(emuThread, currentProgram.getCompilerSpec());
|
||||
|
||||
// Establish simple malloc memory manager with memory region spaced relative to stack pointer
|
||||
mallocMgr = new SimpleMallocMgr(
|
||||
stackBase.subtract(EmulatorUtilities.DEFAULT_STACK_SIZE + MALLOC_REGION_SIZE),
|
||||
MALLOC_REGION_SIZE);
|
||||
|
||||
// Setup hook breakpoints
|
||||
emu.inject(mallocEntry, """
|
||||
RAX = __libc_malloc(RDI);
|
||||
__x86_64_RET();
|
||||
""");
|
||||
emu.inject(freeEntry, """
|
||||
__libc_free(RDI);
|
||||
__x86_64_RET();
|
||||
""");
|
||||
emu.inject(strlenEntry, """
|
||||
RAX = __libc_strlen(RDI);
|
||||
__x86_64_RET();
|
||||
""");
|
||||
emu.inject(useStringEntry, """
|
||||
__hook_useString(RDI);
|
||||
emu_exec_decoded();
|
||||
""");
|
||||
|
||||
// Set controlled return location so we can identify return from emulated function
|
||||
controlledReturnAddr = getAddress(CONTROLLED_RETURN_OFFSET);
|
||||
emuThread.getState()
|
||||
.setVar(stackBase.add(8), 8, false, arithmetic.fromConst(controlledReturnAddr));
|
||||
emu.addBreakpoint(controlledReturnAddr, "1:1");
|
||||
|
||||
emuThread.overrideCounter(mainFunctionEntry);
|
||||
emuThread.overrideContext(
|
||||
currentProgram.getProgramContext().getDisassemblyContext(mainFunctionEntry));
|
||||
Msg.debug(this, "EMU starting at " + emuThread.getCounter());
|
||||
|
||||
// First call to run should break after final return
|
||||
try {
|
||||
emuThread.run();
|
||||
}
|
||||
finally {
|
||||
// cleanup resources and release hold on currentProgram
|
||||
emuHelper.dispose();
|
||||
catch (InterruptPcodeExecutionException e) {
|
||||
// Hit the breakpoint. Good.
|
||||
}
|
||||
catch (Throwable t) {
|
||||
printerr("Emulation error: " + t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,61 +166,9 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript {
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform processing for the various hook points where breakpoints have been set.
|
||||
* @param addr current execute address where emulation has been suspended
|
||||
* @throws Exception if an error occurs
|
||||
*/
|
||||
private void processBreakpoint(Address addr) throws Exception {
|
||||
|
||||
// malloc hook
|
||||
if (addr.equals(mallocEntry)) {
|
||||
int size = emuHelper.readRegister("RDI").intValue();
|
||||
Address memAddr = mallocMgr.malloc(size);
|
||||
emuHelper.writeRegister("RAX", memAddr.getOffset());
|
||||
}
|
||||
|
||||
// free hook
|
||||
else if (addr.equals(freeEntry)) {
|
||||
Address freeAddr = getAddress(emuHelper.readRegister("RDI").longValue());
|
||||
mallocMgr.free(freeAddr);
|
||||
}
|
||||
|
||||
// strlen hook
|
||||
else if (addr.equals(strlenEntry)) {
|
||||
Address ptr = getAddress(emuHelper.readRegister("RDI").longValue());
|
||||
int len = 0;
|
||||
while (emuHelper.readMemoryByte(ptr) != 0) {
|
||||
++len;
|
||||
ptr = ptr.next();
|
||||
}
|
||||
emuHelper.writeRegister("RAX", len);
|
||||
}
|
||||
|
||||
// use_string hook - print string
|
||||
else if (addr.equals(useStringEntry)) {
|
||||
Address stringAddr = getAddress(emuHelper.readRegister("RDI").longValue());
|
||||
String str = emuHelper.readNullTerminatedString(stringAddr, 32);
|
||||
println("use_string: " + str); // output string argument to consoles
|
||||
}
|
||||
|
||||
// unexpected
|
||||
else {
|
||||
if (emuHelper.getEmulateExecutionState() != EmulateExecutionState.BREAKPOINT) {
|
||||
// assume we are stepping and simply return
|
||||
return;
|
||||
}
|
||||
throw new NotFoundException("Unhandled breakpoint at " + addr);
|
||||
}
|
||||
|
||||
// force early return
|
||||
long returnOffset = emuHelper.readStackValue(0, 8, false).longValue();
|
||||
|
||||
emuHelper.writeRegister(emuHelper.getPCRegister(), returnOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thunk function corresponding to an external function. Such thunks
|
||||
* should reside within the EXTERNAL block. (Note: this is specific to the ELF import)
|
||||
* Get the thunk function corresponding to an external function. Such thunks should reside
|
||||
* within the EXTERNAL block. (Note: this is specific to the ELF import)
|
||||
*
|
||||
* @param symbolName external function name
|
||||
* @return address of thunk function which corresponds to an external function
|
||||
* @throws NotFoundException if thunk not found
|
||||
@@ -212,7 +177,7 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript {
|
||||
Symbol externalSymbol = currentProgram.getSymbolTable().getExternalSymbol(symbolName);
|
||||
if (externalSymbol != null && externalSymbol.getSymbolType() == SymbolType.FUNCTION) {
|
||||
Function f = (Function) externalSymbol.getObject();
|
||||
Address[] thunkAddrs = f.getFunctionThunkAddresses();
|
||||
Address[] thunkAddrs = f.getFunctionThunkAddresses(false);
|
||||
if (thunkAddrs.length == 1) {
|
||||
return thunkAddrs[0];
|
||||
}
|
||||
@@ -222,6 +187,7 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript {
|
||||
|
||||
/**
|
||||
* Get the global namespace symbol address which corresponds to the specified name.
|
||||
*
|
||||
* @param symbolName global symbol name
|
||||
* @return symbol address
|
||||
* @throws NotFoundException if symbol not found
|
||||
@@ -235,6 +201,66 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript {
|
||||
throw new NotFoundException("Failed to locate label: " + symbolName);
|
||||
}
|
||||
|
||||
public class DeobfUseropLibrary<T> extends AnnotatedPcodeUseropLibrary<T> {
|
||||
static final String SRC_X86_RET = """
|
||||
RIP = *:8 RSP;
|
||||
RSP = RSP + 8;
|
||||
return [RIP];
|
||||
""";
|
||||
PcodeProgram progRet;
|
||||
|
||||
@PcodeUserop(canInline = true)
|
||||
public void __x86_64_RET(@OpExecutor PcodeExecutor<T> executor,
|
||||
@OpLibrary PcodeUseropLibrary<T> library) {
|
||||
if (progRet == null) {
|
||||
progRet = SleighProgramCompiler.compileUserop(executor.getLanguage(),
|
||||
"__x86_64_RET", List.of(), SRC_X86_RET, PcodeUseropLibrary.nil(), List.of());
|
||||
}
|
||||
progRet.execute(executor, library);
|
||||
}
|
||||
|
||||
@PcodeUserop(functional = true, hasSideEffects = true)
|
||||
public long __libc_malloc(int size) throws InsufficientBytesException {
|
||||
Address memAddr = mallocMgr.malloc(size);
|
||||
return memAddr.getOffset();
|
||||
}
|
||||
|
||||
@PcodeUserop(functional = true, hasSideEffects = true)
|
||||
public void __libc_free(long ptr) {
|
||||
mallocMgr.free(getAddress(ptr));
|
||||
}
|
||||
|
||||
static final String SRC_STRLEN = """
|
||||
__result = 0;
|
||||
<loop>
|
||||
if (*:1 (str+__result) == 0 || __result >= maxlen) goto <exit>;
|
||||
__result = __result + 1;
|
||||
goto <loop>;
|
||||
<exit>""";
|
||||
private PcodeProgram progStrlen;
|
||||
|
||||
@PcodeUserop(canInline = true)
|
||||
public void __libc_strlen(@OpExecutor PcodeExecutor<T> executor,
|
||||
@OpLibrary PcodeUseropLibrary<T> library, @OpOutput Varnode out, Varnode start) {
|
||||
Varnode const128 =
|
||||
new Varnode(executor.getLanguage().getAddressFactory().getConstantAddress(128), 4);
|
||||
// NOTE: This assumes all calls to __libc_strlen have the same output and input varnodes
|
||||
if (progStrlen == null) {
|
||||
progStrlen = SleighProgramCompiler.compileUserop(executor.getLanguage(),
|
||||
"__libc_strlen", List.of("__result", "str", "maxlen"),
|
||||
SRC_STRLEN, PcodeUseropLibrary.nil(), List.of(out, start, const128));
|
||||
}
|
||||
progStrlen.execute(executor, library);
|
||||
}
|
||||
|
||||
@PcodeUserop
|
||||
public void __hook_useString(@OpState PcodeExecutorState<T> state, long ptr) {
|
||||
Address addr = state.getLanguage().getDefaultDataSpace().getAddress(ptr);
|
||||
String str = EmulatorUtilities.decodeNullTerminatedString(state, addr);
|
||||
println("use_string: " + str); // output string argument to consoles
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>SimpleMallocMgr</code> provides a simple malloc memory manager to be used by the
|
||||
* malloc/free hooked implementations.
|
||||
@@ -246,8 +272,9 @@ public class EmuX86GccDeobfuscateHookExampleScript extends GhidraScript {
|
||||
|
||||
/**
|
||||
* <code>SimpleMallocMgr</code> constructor.
|
||||
* @param rangeStart start of the free malloc region (i.e., Heap) which has been
|
||||
* deemed a safe
|
||||
*
|
||||
* @param rangeStart start of the free malloc region (i.e., Heap) which has been deemed a
|
||||
* safe
|
||||
* @param byteSize
|
||||
* @throws AddressOverflowException
|
||||
*/
|
||||
|
||||
+220
-139
File diff suppressed because it is too large
Load Diff
+2
-5
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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.
|
||||
@@ -20,8 +19,6 @@ import ghidra.program.model.address.Address;
|
||||
|
||||
public interface ExecutionListener extends TestLogger {
|
||||
|
||||
public void stepCompleted(EmulatorTestRunner testRunner);
|
||||
|
||||
public void logWrite(EmulatorTestRunner testRunner, Address address, int size, byte[] values);
|
||||
|
||||
public void logRead(EmulatorTestRunner testRunner, Address address, int size, byte[] values);
|
||||
|
||||
+57
-35
@@ -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,11 +15,14 @@
|
||||
*/
|
||||
package ghidra.test.processors.support;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.emulator.EmulatorHelper;
|
||||
import ghidra.app.util.PseudoDisassembler;
|
||||
import ghidra.docking.settings.SettingsImpl;
|
||||
import ghidra.pcode.emu.PcodeThread;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.pcode.memstate.MemoryState;
|
||||
import ghidra.pcode.utils.Utils;
|
||||
import ghidra.program.disassemble.Disassembler;
|
||||
@@ -36,9 +39,9 @@ import ghidra.util.exception.AssertException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
/**
|
||||
* <code>PCodeTestAbstractControlBlock</code> data is models the general capabilities
|
||||
* of the TestInfo data structure which is used for different puposes as handled
|
||||
* by extensions of this class.
|
||||
* <code>PCodeTestAbstractControlBlock</code> data is models the general capabilities of the
|
||||
* TestInfo data structure which is used for different puposes as handled by extensions of this
|
||||
* class.
|
||||
*/
|
||||
public abstract class PCodeTestAbstractControlBlock {
|
||||
|
||||
@@ -59,17 +62,18 @@ public abstract class PCodeTestAbstractControlBlock {
|
||||
private HashMap<String, FunctionInfo> functionMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Construct test control block instance for the specified program.
|
||||
* Construct test control block instance for the specified program.
|
||||
*
|
||||
* @param program program containing control block structure
|
||||
* @param infoStructAddr program address where structure resides
|
||||
* @param infoStruct appropriate Info structure definition which will have array
|
||||
* of FunctionInfo immediately following.
|
||||
* @param infoStruct appropriate Info structure definition which will have array of FunctionInfo
|
||||
* immediately following.
|
||||
*/
|
||||
PCodeTestAbstractControlBlock(Program program, Address infoStructAddr, Structure infoStruct) {
|
||||
this.program = program;
|
||||
this.pointerSize = program.getDataTypeManager().getDataOrganization().getPointerSize();
|
||||
this.infoStructAddr = infoStructAddr;
|
||||
this.infoProgramStruct = (Structure) infoStruct.clone(program.getDataTypeManager());
|
||||
this.infoProgramStruct = infoStruct.clone(program.getDataTypeManager());
|
||||
|
||||
codeSpace = program.getAddressFactory().getDefaultAddressSpace();
|
||||
dataSpace = program.getLanguage().getDefaultDataSpace();
|
||||
@@ -95,10 +99,10 @@ public abstract class PCodeTestAbstractControlBlock {
|
||||
}
|
||||
|
||||
/**
|
||||
* Force an existing reference to refer to the code space. Pointers
|
||||
* created in the data space refer to the data space by default, this method
|
||||
* is used to change these pointers in the data space to refer to
|
||||
* code.
|
||||
* Force an existing reference to refer to the code space. Pointers created in the data space
|
||||
* refer to the data space by default, this method is used to change these pointers in the data
|
||||
* space to refer to code.
|
||||
*
|
||||
* @param addr location with data space which contains code reference
|
||||
*/
|
||||
void forceCodePointer(Address addr) {
|
||||
@@ -169,8 +173,8 @@ public abstract class PCodeTestAbstractControlBlock {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a Data pointer at the specified address and return the referenced
|
||||
* address.
|
||||
* Check for a Data pointer at the specified address and return the referenced address.
|
||||
*
|
||||
* @param addr address of stored pointer
|
||||
* @return pointer referenced address or null if no pointer found
|
||||
*/
|
||||
@@ -182,7 +186,8 @@ public abstract class PCodeTestAbstractControlBlock {
|
||||
return (Address) data.getValue();
|
||||
}
|
||||
|
||||
protected Address readCodePointer(MemBuffer buffer, int bufferOffset, boolean updateReference) throws MemoryAccessException {
|
||||
protected Address readCodePointer(MemBuffer buffer, int bufferOffset, boolean updateReference)
|
||||
throws MemoryAccessException {
|
||||
Address codePtr = readPointer(buffer, bufferOffset, codeSpace, updateReference);
|
||||
|
||||
// treat null pointer as special case - just return it
|
||||
@@ -312,8 +317,9 @@ public abstract class PCodeTestAbstractControlBlock {
|
||||
new TerminatedStringDataType(program.getDataTypeManager());
|
||||
|
||||
Structure functionInfoStruct =
|
||||
(Structure) infoProgramStruct.getDataTypeManager().getDataType(CategoryPath.ROOT,
|
||||
"FunctionInfo");
|
||||
(Structure) infoProgramStruct.getDataTypeManager()
|
||||
.getDataType(CategoryPath.ROOT,
|
||||
"FunctionInfo");
|
||||
if (functionInfoStruct == null) {
|
||||
throw new AssertException("FunctionInfo structure not yet resolved");
|
||||
}
|
||||
@@ -383,46 +389,62 @@ public abstract class PCodeTestAbstractControlBlock {
|
||||
|
||||
}
|
||||
|
||||
protected String emuReadString(EmulatorHelper emu, Address strPtrAddr) {
|
||||
/**
|
||||
* Read an ASCII-encoded string, perhaps with wide characters, from the emulator.
|
||||
* <p>
|
||||
* This is really a hack, but suffices for these tests. The character size is taken from the
|
||||
* program's data organization. However, even if wide characters are used, this only reads the
|
||||
* least-significant byte, effectively truncating each to the ASCII-2 range [0-255].
|
||||
*
|
||||
* @param emu the emulator
|
||||
* @param strPtrAddr the pointer to the string
|
||||
* @return the string
|
||||
*/
|
||||
protected String emuReadString(PcodeThread<byte[]> emu, Address strPtrAddr) {
|
||||
|
||||
DataOrganization dataOrganization =
|
||||
emu.getProgram().getDataTypeManager().getDataOrganization();
|
||||
program.getDataTypeManager().getDataOrganization();
|
||||
int charSize = dataOrganization.getCharSize();
|
||||
boolean isBigEndian = emu.getProgram().getMemory().isBigEndian();
|
||||
boolean isBigEndian = emu.getLanguage().isBigEndian();
|
||||
|
||||
MemoryState memState = emu.getEmulator().getMemState();
|
||||
long offset = strPtrAddr.getOffset();
|
||||
PcodeExecutorState<byte[]> state = emu.getState();
|
||||
if (isBigEndian) {
|
||||
offset += (charSize - 1);
|
||||
strPtrAddr = strPtrAddr.add(charSize - 1);
|
||||
}
|
||||
char[] buffer = new char[128];
|
||||
MemBuffer memBuf = state.getConcreteBuffer(strPtrAddr, Purpose.INSPECT);
|
||||
|
||||
int offset = 0;
|
||||
int index = 0;
|
||||
byte[] buffer = new byte[128];
|
||||
while (index < buffer.length) {
|
||||
buffer[index] =
|
||||
(char) (memState.getValue(strPtrAddr.getAddressSpace(), offset, 1) & 0xff);
|
||||
try {
|
||||
buffer[index] = memBuf.getByte(offset);
|
||||
}
|
||||
catch (MemoryAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
if (buffer[index] == 0) {
|
||||
break;
|
||||
}
|
||||
offset += charSize;
|
||||
++index;
|
||||
}
|
||||
return new String(buffer, 0, index);
|
||||
return new String(buffer, 0, index, Charset.forName("ASCII"));
|
||||
}
|
||||
|
||||
protected long emuRead(EmulatorHelper emu, Address addr, int size) {
|
||||
protected long emuRead(PcodeThread<byte[]> emu, Address addr, int size) {
|
||||
if (size < 1 || size > 8) {
|
||||
throw new IllegalArgumentException("Unsupported EMU read size: " + size);
|
||||
}
|
||||
MemoryState memState = emu.getEmulator().getMemState();
|
||||
return memState.getValue(addr.getAddressSpace(), addr.getOffset(), size);
|
||||
return emu.getArithmetic()
|
||||
.toLong(emu.getState().inspectConcrete(addr, size), Purpose.INSPECT);
|
||||
}
|
||||
|
||||
protected void emuWrite(EmulatorHelper emu, Address addr, int size, long value) {
|
||||
protected void emuWrite(PcodeThread<byte[]> emu, Address addr, int size, long value) {
|
||||
if (size < 1 || size > 8) {
|
||||
throw new IllegalArgumentException("Unsupported EMU read size: " + size);
|
||||
}
|
||||
MemoryState memState = emu.getEmulator().getMemState();
|
||||
memState.setValue(addr.getAddressSpace(), addr.getOffset(), size, value);
|
||||
emu.getState().setVar(addr, size, false, emu.getArithmetic().fromConst(value, size));
|
||||
}
|
||||
|
||||
protected Address getMirroredDataAddress(EmulatorTestRunner emuTestRunner, Address addr) {
|
||||
|
||||
+36
-23
@@ -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.
|
||||
@@ -27,9 +27,9 @@ import ghidra.program.model.util.CodeUnitInsertionException;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* <code>PCodeTestControlBlock</code> data is read from each binary test file and
|
||||
* identified by the MAIN_CONTROL_BLOCK_MAGIC 64-bit character field value at the start of the
|
||||
* data structure. Only one instance of this should exist within the binary.
|
||||
* <code>PCodeTestControlBlock</code> data is read from each binary test file and identified by the
|
||||
* MAIN_CONTROL_BLOCK_MAGIC 64-bit character field value at the start of the data structure. Only
|
||||
* one instance of this should exist within the binary.
|
||||
*/
|
||||
public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock {
|
||||
|
||||
@@ -72,13 +72,14 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock {
|
||||
private final PCodeTestResults testResults;
|
||||
|
||||
/**
|
||||
* Construct test control block instance for the specified
|
||||
* program. Create TestInfo structure data within program if requested.
|
||||
* Construct test control block instance for the specified program. Create TestInfo structure
|
||||
* data within program if requested.
|
||||
*
|
||||
* @param program program containing control block structure
|
||||
* @param restrictedSet the restricted memory area which should be searched
|
||||
* for control structures
|
||||
* @param restrictedSet the restricted memory area which should be searched for control
|
||||
* structures
|
||||
* @param testInfoStructAddr address of Main TestInfo structure
|
||||
* @param testFile original binary test file
|
||||
* @param testFile original binary test file
|
||||
* @param cachedProgramPath program path within program file cache
|
||||
* @param applyStruct create structure Data within program if true
|
||||
* @throws InvalidControlBlockException
|
||||
@@ -109,6 +110,7 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock {
|
||||
|
||||
/**
|
||||
* Find Main TestInfo structure within memory and return instance of PCodeTestControlBlock
|
||||
*
|
||||
* @param program
|
||||
* @param testFile original binary test file
|
||||
* @param restrictedSet a restricted set to be searched for control structures
|
||||
@@ -294,33 +296,36 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock {
|
||||
|
||||
/**
|
||||
* Enable/Diable sprintf use within P-Code test emulation.
|
||||
*
|
||||
* @param emuTestRunner emulator test runner
|
||||
* @param enable sprintf enablement
|
||||
*/
|
||||
void setSprintfEnabled(EmulatorTestRunner emuTestRunner, boolean enable) {
|
||||
Address addr =
|
||||
getMirroredDataAddress(emuTestRunner, infoStructAddr.add(sprintfEnableOffset));
|
||||
emuWrite(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4, enable ? 1 : 0);
|
||||
emuWrite(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4, enable ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 'numpass' field value from emulation memory state
|
||||
*
|
||||
* @param emuTestRunner emulator test runner
|
||||
* @return 'numpass' field value
|
||||
*/
|
||||
int getNumberPassed(EmulatorTestRunner emuTestRunner) {
|
||||
Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(numPassOffset));
|
||||
return (int) emuRead(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4);
|
||||
return (int) emuRead(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 'numpass' field value within emulation memory state
|
||||
*
|
||||
* @param emuTestRunner emulator test runner
|
||||
* @param value field value
|
||||
*/
|
||||
void setNumberPassed(EmulatorTestRunner emuTestRunner, int value) {
|
||||
Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(numPassOffset));
|
||||
emuWrite(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4, value);
|
||||
emuWrite(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4, value);
|
||||
}
|
||||
|
||||
void resultIgnored(String testGroupName, String testName, boolean passed) {
|
||||
@@ -335,6 +340,7 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock {
|
||||
|
||||
/**
|
||||
* Get the number of passed tests which should be ignored
|
||||
*
|
||||
* @return number of passed tests which should be ignored
|
||||
*/
|
||||
int getNumberPassedIgnored() {
|
||||
@@ -343,6 +349,7 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock {
|
||||
|
||||
/**
|
||||
* Get the number of failed tests which should be ignored
|
||||
*
|
||||
* @return number of failed tests which should be ignored
|
||||
*/
|
||||
int getNumberFailedIgnored() {
|
||||
@@ -359,62 +366,68 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock {
|
||||
|
||||
/**
|
||||
* Get 'numfail' field value from emulation memory state
|
||||
*
|
||||
* @param emuTestRunner emulator test runner
|
||||
* @return 'numfail' field value
|
||||
*/
|
||||
int getNumberFailed(EmulatorTestRunner emuTestRunner) {
|
||||
Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(numFailOffset));
|
||||
return (int) emuRead(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4);
|
||||
return (int) emuRead(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 'numfail' field value within emulation memory state
|
||||
*
|
||||
* @param emuTestRunner emulator test runner
|
||||
* @param value field value
|
||||
*/
|
||||
void setNumberFailed(EmulatorTestRunner emuTestRunner, int value) {
|
||||
Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(numFailOffset));
|
||||
emuWrite(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4, value);
|
||||
emuWrite(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 'lastTestPos' field value from emulation memory state
|
||||
*
|
||||
* @param emuTestRunner emulator test runner
|
||||
* @return 'lastTestPos' field value
|
||||
*/
|
||||
int getLastTestIndex(EmulatorTestRunner emuTestRunner) {
|
||||
Address addr = getMirroredDataAddress(emuTestRunner, infoStructAddr.add(lastTestPosOffset));
|
||||
return (int) emuRead(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4);
|
||||
return (int) emuRead(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 'lastErrorLine' field value from emulation memory state
|
||||
*
|
||||
* @param emuTestRunner emulator test runner
|
||||
* @return 'lastErrorLine' field value
|
||||
*/
|
||||
int getLastErrorLine(EmulatorTestRunner emuTestRunner) {
|
||||
Address addr =
|
||||
getMirroredDataAddress(emuTestRunner, infoStructAddr.add(lastErrorLineOffset));
|
||||
return (int) emuRead(emuTestRunner.getEmulatorHelper(), addr, SIZEOF_U4);
|
||||
return (int) emuRead(emuTestRunner.getEmulatorThread(), addr, SIZEOF_U4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 'lastErrorFile' string value from emulation memory state. Must follow string
|
||||
* pointer contained within lastErrorFile field.
|
||||
* Get 'lastErrorFile' string value from emulation memory state. Must follow string pointer
|
||||
* contained within lastErrorFile field.
|
||||
*
|
||||
* @param emuTestRunner emulator test runner
|
||||
* @return 'lastErrorLine' field value
|
||||
*/
|
||||
String getLastErrorFile(EmulatorTestRunner emuTestRunner) {
|
||||
Address addr =
|
||||
getMirroredDataAddress(emuTestRunner, infoStructAddr.add(lastErrorFileOffset));
|
||||
long fileNameOffset = emuRead(emuTestRunner.getEmulatorHelper(), addr, pointerSize);
|
||||
long fileNameOffset = emuRead(emuTestRunner.getEmulatorThread(), addr, pointerSize);
|
||||
addr = addr.getNewAddress(fileNameOffset, true);
|
||||
addr = getMirroredDataAddress(emuTestRunner, addr);
|
||||
return emuReadString(emuTestRunner.getEmulatorHelper(), addr);
|
||||
return emuReadString(emuTestRunner.getEmulatorThread(), addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the last test function to be run
|
||||
*
|
||||
* @param emuTestRunner
|
||||
* @return last test function name
|
||||
*/
|
||||
@@ -422,10 +435,10 @@ public class PCodeTestControlBlock extends PCodeTestAbstractControlBlock {
|
||||
PCodeTestGroup activeGroup) {
|
||||
Address ptrStorageAddr = infoStructAddr.add(lastFuncOffset);
|
||||
Address ptrAddr = getMirroredDataAddress(emuTestRunner, ptrStorageAddr);
|
||||
long funcNameOffset = emuRead(emuTestRunner.getEmulatorHelper(), ptrAddr, pointerSize);
|
||||
long funcNameOffset = emuRead(emuTestRunner.getEmulatorThread(), ptrAddr, pointerSize);
|
||||
Address strAddr = ptrAddr.getNewAddress(funcNameOffset, true);
|
||||
strAddr = getMirroredDataAddress(emuTestRunner, strAddr);
|
||||
String fnName = emuReadString(emuTestRunner.getEmulatorHelper(), strAddr);
|
||||
String fnName = emuReadString(emuTestRunner.getEmulatorThread(), strAddr);
|
||||
if ("none".equals(fnName)) {
|
||||
if (logger != null) {
|
||||
logger.log(activeGroup, "ERROR last executed function name pointer stored at " +
|
||||
|
||||
+83
-81
@@ -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.
|
||||
@@ -65,20 +65,21 @@ import utilities.util.FileUtilities;
|
||||
import utility.application.ApplicationLayout;
|
||||
|
||||
/**
|
||||
* <code>ProcessorEmulatorTestAdapter</code> provides an abstract JUnit test implementation
|
||||
* for processor-specific test cases. All test cases which extend this class must have a
|
||||
* class name which ends with 'EmulatorTest' and starts with the processor designator which
|
||||
* will be used to identify associated test binaries within either the processor module's
|
||||
* data/pcodetests/ directory or the Ghidra/Test/TestResources/data/pcodetests/ directory generally
|
||||
* contained within the binary repository (e.g., ghidra.bin).
|
||||
* <code>ProcessorEmulatorTestAdapter</code> provides an abstract JUnit test implementation for
|
||||
* processor-specific test cases. All test cases which extend this class must have a class name
|
||||
* which ends with 'EmulatorTest' and starts with the processor designator which will be used to
|
||||
* identify associated test binaries within either the processor module's data/pcodetests/ directory
|
||||
* or the Ghidra/Test/TestResources/data/pcodetests/ directory generally contained within the binary
|
||||
* repository (e.g., ghidra.bin).
|
||||
* <p>
|
||||
* Within the pcodetests directory all files and folders which start with the prefix
|
||||
* {@literal <processor-designator>_pcodetest*} will be processed. All files contained within a matching
|
||||
* subdirectory will be treated as related binaries and imported. Any *.gzf file will be
|
||||
* imported but assumed to be pre-analyzed. Binary files to be imported and analyzed must
|
||||
* utilize the *.out file extension.
|
||||
* {@literal <processor-designator>_pcodetest*} will be processed. All files contained within a
|
||||
* matching subdirectory will be treated as related binaries and imported. Any *.gzf file will be
|
||||
* imported but assumed to be pre-analyzed. Binary files to be imported and analyzed must utilize
|
||||
* the *.out file extension.
|
||||
* <p>
|
||||
* JUnit X86EmulatorTest could utilize the following binary file naming strategy:
|
||||
*
|
||||
* <pre>
|
||||
* pcodetests/X86_PCodeTests
|
||||
* - binary1.o
|
||||
@@ -92,25 +93,22 @@ import utility.application.ApplicationLayout;
|
||||
* - pcodetests/X86_PCodeTest.out
|
||||
* </pre>
|
||||
*
|
||||
* Any *.out binary found will be imported and analyzed. The resulting program will
|
||||
* be stored as a gzf in the test-output cache directory. These cached files will be used
|
||||
* instead of a test resource binary if that binary's md5 checksum has not changed since its cached
|
||||
* gzf was created. This use of cache files will allow the tests to run quickly on subsequent
|
||||
* executions. If re-analysis is required, the cache will need to be cleared manually.
|
||||
* Any *.out binary found will be imported and analyzed. The resulting program will be stored as a
|
||||
* gzf in the test-output cache directory. These cached files will be used instead of a test
|
||||
* resource binary if that binary's md5 checksum has not changed since its cached gzf was created.
|
||||
* This use of cache files will allow the tests to run quickly on subsequent executions. If
|
||||
* re-analysis is required, the cache will need to be cleared manually.
|
||||
*
|
||||
* NOTES:
|
||||
* 1. Dummy Test Methods must be added for all known test groups. See bottom of this file. This
|
||||
* all allows for the single test trace mode execution to work within Eclipse.
|
||||
* 2. Trace logging disabled by default when all test groups are run (see buildEmulatorTestSuite method).
|
||||
* Specific traceLevel and traceLog file controlled via environment properties
|
||||
* EmuTestTraceLevel and EmuTestTraceFile.
|
||||
* 3. The TestInfo structure must be properly maintained within the datatype archive EmuTesting.gdt
|
||||
* and field naming consistent with use in PCodeTestControlBlock.java
|
||||
* 4. The {@link #initializeState(EmulatorTestRunner, Program)} may be overriden to initialize the
|
||||
* register values if needed. This should be based upon symbols or other program information
|
||||
* if possible since hardcoded constants may not track future builds of a test binaries.
|
||||
* An attempt is made to initialize the stack pointer automatically based upon well known
|
||||
* stack initialization symbols.
|
||||
* NOTES: 1. Dummy Test Methods must be added for all known test groups. See bottom of this file.
|
||||
* This all allows for the single test trace mode execution to work within Eclipse. 2. Trace logging
|
||||
* disabled by default when all test groups are run (see buildEmulatorTestSuite method). Specific
|
||||
* traceLevel and traceLog file controlled via environment properties EmuTestTraceLevel and
|
||||
* EmuTestTraceFile. 3. The TestInfo structure must be properly maintained within the datatype
|
||||
* archive EmuTesting.gdt and field naming consistent with use in PCodeTestControlBlock.java 4. The
|
||||
* {@link #initializeState(EmulatorTestRunner, Program)} may be overriden to initialize the register
|
||||
* values if needed. This should be based upon symbols or other program information if possible
|
||||
* since hardcoded constants may not track future builds of a test binaries. An attempt is made to
|
||||
* initialize the stack pointer automatically based upon well known stack initialization symbols.
|
||||
*/
|
||||
public abstract class ProcessorEmulatorTestAdapter extends TestCase implements ExecutionListener {
|
||||
|
||||
@@ -495,10 +493,11 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
}
|
||||
|
||||
/**
|
||||
* Create TestSuite based upon available test groups contained within binary
|
||||
* test files associated with target processor.
|
||||
* @param emulatorTestClass test which extends <code>ProcessorEmulatorTestAdapter</code>
|
||||
* and whose name ends with "EmulatorTest".
|
||||
* Create TestSuite based upon available test groups contained within binary test files
|
||||
* associated with target processor.
|
||||
*
|
||||
* @param emulatorTestClass test which extends <code>ProcessorEmulatorTestAdapter</code> and
|
||||
* whose name ends with "EmulatorTest".
|
||||
* @return test suite
|
||||
*/
|
||||
public static final Test buildEmulatorTestSuite(Class<?> emulatorTestClass) {
|
||||
@@ -825,7 +824,8 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
|
||||
int byteCount = dumpSize * elementSize;
|
||||
|
||||
byte[] bytes = emulatorTestRunner.getEmulatorHelper().readMemory(dumpAddr, byteCount);
|
||||
byte[] bytes =
|
||||
emulatorTestRunner.getEmulatorThread().getState().inspectConcrete(dumpAddr, byteCount);
|
||||
int index = 0;
|
||||
|
||||
log(null, "MEMORY DUMP (" + elementFormat + "): " + comment);
|
||||
@@ -902,11 +902,6 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
log(testRunner.getTestGroup(), " Write " + formatAssignmentString(address, size, values));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stepCompleted(EmulatorTestRunner testRunner) {
|
||||
logState(testRunner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the stack trace into a string
|
||||
*/
|
||||
@@ -1088,8 +1083,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
}
|
||||
|
||||
/**
|
||||
* Single unit test which handles named test group specified during test
|
||||
* instantiation.
|
||||
* Single unit test which handles named test group specified during test instantiation.
|
||||
*/
|
||||
@Override
|
||||
public final void runTest() {
|
||||
@@ -1187,7 +1181,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
|
||||
boolean done;
|
||||
if (traceDisabled) {
|
||||
done = testRunner.execute(EXECUTION_TIMEOUT_MS, TaskMonitor.DUMMY);
|
||||
done = testRunner.execute(EXECUTION_TIMEOUT_MS);
|
||||
}
|
||||
else {
|
||||
done = testRunner.executeSingleStep(MAX_EXECUTION_STEPS);
|
||||
@@ -1327,6 +1321,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
|
||||
/**
|
||||
* Get symbol name which defines initial stack pointer offset
|
||||
*
|
||||
* @return stack symbol or null
|
||||
*/
|
||||
protected String getPreferredStackSymbolName() {
|
||||
@@ -1350,8 +1345,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
Address currentAddr = testRunner.getCurrentAddress();
|
||||
|
||||
// dump context register state if decode failure
|
||||
RegisterValue contextRegValue =
|
||||
testRunner.getEmulatorHelper().getEmulator().getContextRegisterValue();
|
||||
RegisterValue contextRegValue = testRunner.getEmulatorThread().getContext();
|
||||
if (contextRegValue == null) {
|
||||
return;
|
||||
}
|
||||
@@ -1383,7 +1377,8 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
}
|
||||
|
||||
// check for memory modification
|
||||
byte[] emuBytes = testRunner.getEmulatorHelper().readMemory(currentAddr, len);
|
||||
byte[] emuBytes =
|
||||
testRunner.getEmulatorThread().getState().inspectConcrete(currentAddr, len);
|
||||
byte[] programBytes = new byte[emuBytes.length];
|
||||
program.getMemory().getBytes(currentAddr, programBytes);
|
||||
if (!Arrays.equals(emuBytes, programBytes)) {
|
||||
@@ -1425,6 +1420,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
|
||||
/**
|
||||
* Get the maximum defined memory address ignoring any overlays which have been defined.
|
||||
*
|
||||
* @return max defined physical address
|
||||
*/
|
||||
protected static final Address getMaxDefinedMemoryAddress(Program program) {
|
||||
@@ -1441,10 +1437,10 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
}
|
||||
|
||||
/**
|
||||
* Add specified test names to the set of tests which should be ignored due to know issues
|
||||
* or limitations. The tests will still be executed, if present, however they will
|
||||
* not be included in pass/fail counts. They will appear in log as "(IGNORED)" test
|
||||
* result.
|
||||
* Add specified test names to the set of tests which should be ignored due to know issues or
|
||||
* limitations. The tests will still be executed, if present, however they will not be included
|
||||
* in pass/fail counts. They will appear in log as "(IGNORED)" test result.
|
||||
*
|
||||
* @param testNames one or more test names to be ignored
|
||||
*/
|
||||
protected void addIgnoredTests(String... testNames) {
|
||||
@@ -1456,10 +1452,10 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
//
|
||||
|
||||
/**
|
||||
* Get the processor designator used to identify test binary files/folder.
|
||||
* The default implementation requires the JUnit test class name to end with
|
||||
* "EmulatorTest" where the portion of the name proceeding this suffix will be
|
||||
* used as the processor designator
|
||||
* Get the processor designator used to identify test binary files/folder. The default
|
||||
* implementation requires the JUnit test class name to end with "EmulatorTest" where the
|
||||
* portion of the name proceeding this suffix will be used as the processor designator
|
||||
*
|
||||
* @return processor designator
|
||||
*/
|
||||
protected String getProcessorDesignator() {
|
||||
@@ -1473,6 +1469,7 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
|
||||
/**
|
||||
* Get CUint file designator if use of A, B, C... is not suitable.
|
||||
*
|
||||
* @param fileIndex file index within sorted list
|
||||
* @param filePath binary file path
|
||||
* @return short file designator for use in qualified test name
|
||||
@@ -1482,12 +1479,12 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked for each program immediately prior to executing a test group.
|
||||
* By default this method will initialize the register states based upon the
|
||||
* specific register values/context stored at the test group function entry point.
|
||||
* Such register values may have been established via the processor specification,
|
||||
* loader or analyzers. A specific test may override or extend
|
||||
* this behavior for other registers as needed.
|
||||
* Invoked for each program immediately prior to executing a test group. By default this method
|
||||
* will initialize the register states based upon the specific register values/context stored at
|
||||
* the test group function entry point. Such register values may have been established via the
|
||||
* processor specification, loader or analyzers. A specific test may override or extend this
|
||||
* behavior for other registers as needed.
|
||||
*
|
||||
* @param testRunner emulator group test runner/facilitator
|
||||
* @param program
|
||||
* @throws Exception if initialization criteria has not been satisfied
|
||||
@@ -1513,11 +1510,11 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked immediately following import allow byte processing prior to
|
||||
* control structure identification.
|
||||
* NOTE: This method will only be invoked once during the first test setup
|
||||
* for all test binaries found. This method will not be invoked
|
||||
* during subsequent tests since the analyzed program will be cached.
|
||||
* Invoked immediately following import allow byte processing prior to control structure
|
||||
* identification. NOTE: This method will only be invoked once during the first test setup for
|
||||
* all test binaries found. This method will not be invoked during subsequent tests since the
|
||||
* analyzed program will be cached.
|
||||
*
|
||||
* @param program
|
||||
* @throws Exception
|
||||
*/
|
||||
@@ -1527,10 +1524,10 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
|
||||
/**
|
||||
* Invoked prior to analysis to allow analysis options or other pre-analysis
|
||||
* inspection/modification to be performed.
|
||||
* NOTE: This method will only be invoked once during the first test setup
|
||||
* for all test binaries found. This method will not be invoked
|
||||
* during subsequent tests since the analyzed program will be cached.
|
||||
* inspection/modification to be performed. NOTE: This method will only be invoked once during
|
||||
* the first test setup for all test binaries found. This method will not be invoked during
|
||||
* subsequent tests since the analyzed program will be cached.
|
||||
*
|
||||
* @param program
|
||||
* @throws Exception
|
||||
*/
|
||||
@@ -1539,11 +1536,11 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked for non-gzf files immediately after the analyze method to
|
||||
* perform any follow-up changes of inspection of the program.
|
||||
* NOTE: This method will only be invoked once during the first test setup
|
||||
* for all test binaries found. This method will not be invoked
|
||||
* during subsequent tests since the analyzed program will be cached.
|
||||
* Invoked for non-gzf files immediately after the analyze method to perform any follow-up
|
||||
* changes of inspection of the program. NOTE: This method will only be invoked once during the
|
||||
* first test setup for all test binaries found. This method will not be invoked during
|
||||
* subsequent tests since the analyzed program will be cached.
|
||||
*
|
||||
* @param program
|
||||
* @throws Exception
|
||||
*/
|
||||
@@ -1552,10 +1549,10 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked for non-gzf files to perform auto-analysis.
|
||||
* NOTE: This method will only be invoked once during the first test setup
|
||||
* for all test binaries found. This method will not be invoked
|
||||
* Invoked for non-gzf files to perform auto-analysis. NOTE: This method will only be invoked
|
||||
* once during the first test setup for all test binaries found. This method will not be invoked
|
||||
* during subsequent tests since the analyzed program will be cached.
|
||||
*
|
||||
* @param program
|
||||
* @throws Exception
|
||||
*/
|
||||
@@ -1713,8 +1710,9 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the loader class which should be used. A null value should be
|
||||
* return to use the preferred loader.
|
||||
* Get the loader class which should be used. A null value should be return to use the preferred
|
||||
* loader.
|
||||
*
|
||||
* @return loader class or null
|
||||
*/
|
||||
protected Class<? extends Loader> getLoaderClass() {
|
||||
@@ -2137,7 +2135,9 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
}
|
||||
|
||||
/**
|
||||
* Force proper code address alignment to compensate for address encoding schemes (e.g., Thumb mode)
|
||||
* Force proper code address alignment to compensate for address encoding schemes (e.g., Thumb
|
||||
* mode)
|
||||
*
|
||||
* @param offset
|
||||
* @param alignment
|
||||
* @return
|
||||
@@ -2147,7 +2147,9 @@ public abstract class ProcessorEmulatorTestAdapter extends TestCase implements E
|
||||
}
|
||||
|
||||
/**
|
||||
* Force proper code address alignment to compensate for address encoding schemes (e.g., Thumb mode)
|
||||
* Force proper code address alignment to compensate for address encoding schemes (e.g., Thumb
|
||||
* mode)
|
||||
*
|
||||
* @param addr
|
||||
* @param alignment
|
||||
* @return
|
||||
|
||||
@@ -159,7 +159,7 @@ public class AdaptedEmulator implements Emulator {
|
||||
|
||||
@Override
|
||||
public <A, T> AddressSetView readUninitialized(PcodeExecutorStatePiece<A, T> piece,
|
||||
AddressSetView set) {
|
||||
AddressSetView set, Reason reason) {
|
||||
PcodeExecutorStatePiece<A, byte[]> bytesPiece =
|
||||
PcodeStateCallbacks.checkValueDomain(piece, byte[].class);
|
||||
if (bytesPiece == null) {
|
||||
|
||||
+13
-11
@@ -18,6 +18,7 @@ package ghidra.pcode.emu;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
@@ -29,18 +30,18 @@ import ghidra.program.model.pcode.PcodeOp;
|
||||
* <p>
|
||||
* The two (currently) methods that do not implement a pure broadcast pattern are
|
||||
* {@link #handleMissingUserop(PcodeThread, PcodeOp, PcodeFrame, String, PcodeUseropLibrary)} and
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView)}. For a missing
|
||||
* userop, it will terminate after the first delegate returns {@code true}, in which case it also
|
||||
* returns {@code true}. If all delegates return {@code false}, then it returns {@code false}. For
|
||||
* an uninitialized read, each delegate's returned "still-uninitialized" set is passed to the
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView, Reason)}. For a
|
||||
* missing userop, it will terminate after the first delegate returns {@code true}, in which case it
|
||||
* also returns {@code true}. If all delegates return {@code false}, then it returns {@code false}.
|
||||
* For an uninitialized read, each delegate's returned "still-uninitialized" set is passed to the
|
||||
* subsequent delegate. The first delegate gets the set as passed into the composition. The set
|
||||
* returned by the composition is that returned by the last delegate. This terminates early when any
|
||||
* delegate returns the empty set.
|
||||
*
|
||||
* <p>
|
||||
* One (currently) other method has a non-void return type:
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int)}. The
|
||||
* callback is broadcast as expected, and the return value is the max returned by the delegates.
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int, Reason)}.
|
||||
* The callback is broadcast as expected, and the return value is the max returned by the delegates.
|
||||
*
|
||||
* @param <T> the emulator's value domain
|
||||
*/
|
||||
@@ -207,8 +208,8 @@ public class ComposedPcodeEmulationCallbacks<T> implements PcodeEmulationCallbac
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A, U> int readUninitialized(PcodeThread<T> thread,
|
||||
PcodeExecutorStatePiece<A, U> piece, AddressSpace space, A offset, int length) {
|
||||
public <A, U> int readUninitialized(PcodeThread<T> thread, PcodeExecutorStatePiece<A, U> piece,
|
||||
AddressSpace space, A offset, int length, Reason reason) {
|
||||
/**
|
||||
* NOTE: This could use some work. It's a bit onerous to specify arbitrary, possibly
|
||||
* disjoint, sets of offsets of type A. Can only be guaranteed to mean something if A is
|
||||
@@ -218,7 +219,8 @@ public class ComposedPcodeEmulationCallbacks<T> implements PcodeEmulationCallbac
|
||||
*/
|
||||
int maxL = 0;
|
||||
for (PcodeEmulationCallbacks<T> d : delegates) {
|
||||
maxL = Math.max(maxL, d.readUninitialized(thread, piece, space, offset, length));
|
||||
maxL =
|
||||
Math.max(maxL, d.readUninitialized(thread, piece, space, offset, length, reason));
|
||||
if (maxL == length) {
|
||||
return maxL;
|
||||
}
|
||||
@@ -228,10 +230,10 @@ public class ComposedPcodeEmulationCallbacks<T> implements PcodeEmulationCallbac
|
||||
|
||||
@Override
|
||||
public <A, U> AddressSetView readUninitialized(PcodeThread<T> thread,
|
||||
PcodeExecutorStatePiece<A, U> piece, AddressSetView set) {
|
||||
PcodeExecutorStatePiece<A, U> piece, AddressSetView set, Reason reason) {
|
||||
AddressSetView remains = set;
|
||||
for (PcodeEmulationCallbacks<T> d : delegates) {
|
||||
remains = d.readUninitialized(thread, piece, remains);
|
||||
remains = d.readUninitialized(thread, piece, remains, reason);
|
||||
if (remains.isEmpty()) {
|
||||
return remains;
|
||||
}
|
||||
|
||||
@@ -374,9 +374,13 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
|
||||
@Override
|
||||
public void assignContext(RegisterValue context) {
|
||||
if (!context.getRegister().isProcessorContext()) {
|
||||
if (context.getRegister().getBaseRegister() != contextreg) {
|
||||
throw new IllegalArgumentException("context must be the contextreg value");
|
||||
}
|
||||
if (this.context == null) {
|
||||
assert this.contextreg == Register.NO_CONTEXT;
|
||||
return;
|
||||
}
|
||||
this.context = this.context.assign(context.getRegister(), context);
|
||||
}
|
||||
|
||||
@@ -386,7 +390,14 @@ public class DefaultPcodeThread<T> implements PcodeThread<T> {
|
||||
}
|
||||
|
||||
protected final void writeContext(RegisterValue context) {
|
||||
if (contextreg == Register.NO_CONTEXT && context == null) {
|
||||
return;
|
||||
}
|
||||
assignContext(context);
|
||||
if (this.context == null) {
|
||||
assert this.contextreg == Register.NO_CONTEXT;
|
||||
return;
|
||||
}
|
||||
state.setVar(contextreg, arithmetic.fromConst(
|
||||
this.context.getUnsignedValueIgnoreMask(),
|
||||
contextreg.getMinimumByteSize(), true));
|
||||
|
||||
@@ -16,16 +16,20 @@
|
||||
package ghidra.pcode.emu;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.util.Arrays;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import ghidra.pcode.exec.PcodeArithmetic;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutorState;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.mem.*;
|
||||
import ghidra.util.DifferenceAddressSetView;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@@ -232,6 +236,51 @@ public enum EmulatorUtilities {
|
||||
return chooseStackRange(program, entry, DEFAULT_STACK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the given thread's registers with the given program counter and (optionall)
|
||||
* program register context.
|
||||
*
|
||||
* @param <T> the type of values in the emulator
|
||||
* @param thread the thread to initialize
|
||||
* @param program optionally, the program whose context to read
|
||||
* @param pc the program counter, also used for register context
|
||||
*/
|
||||
public static <T> void initializeRegisters(PcodeThread<T> thread, Program program, Address pc) {
|
||||
if (program != null) {
|
||||
ThreadPcodeExecutorState<T> state = thread.getState();
|
||||
ProgramProcessorContext ctx =
|
||||
new ProgramProcessorContext(program.getProgramContext(), pc);
|
||||
for (Register reg : ctx.getRegisters()) {
|
||||
if (!reg.isBaseRegister() || reg.isProcessorContext()) {
|
||||
continue;
|
||||
}
|
||||
RegisterValue rv = ctx.getRegisterValue(reg);
|
||||
if (rv == null || !rv.hasAnyValue()) {
|
||||
continue;
|
||||
}
|
||||
if (!rv.hasValue()) {
|
||||
rv = new RegisterValue(reg, BigInteger.ZERO).combineValues(rv);
|
||||
}
|
||||
/**
|
||||
* NOTE: In theory, there's no need to combine masked values, if this is a fresh
|
||||
* emulator. If I had to guess, the client would want their values to take
|
||||
* precedence, so they should overwrite the values after calling this method.
|
||||
* Combining can be problematic, because the emulator could return some abstraction
|
||||
* for the current value.
|
||||
*/
|
||||
state.setRegisterValue(rv);
|
||||
}
|
||||
}
|
||||
|
||||
thread.overrideCounter(pc);
|
||||
if (program != null) {
|
||||
thread.overrideContext(program.getProgramContext().getDisassemblyContext(pc));
|
||||
}
|
||||
else {
|
||||
thread.overrideContextWithDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a thread to emulate a given function
|
||||
*
|
||||
@@ -250,31 +299,88 @@ public enum EmulatorUtilities {
|
||||
Register sp = cSpec.getStackPointer();
|
||||
ThreadPcodeExecutorState<T> state = thread.getState();
|
||||
|
||||
ProgramProcessorContext ctx =
|
||||
new ProgramProcessorContext(program.getProgramContext(), entry);
|
||||
for (Register reg : ctx.getRegisters()) {
|
||||
if (!reg.isBaseRegister()) {
|
||||
continue;
|
||||
}
|
||||
RegisterValue rv = ctx.getRegisterValue(reg);
|
||||
if (rv == null || !rv.hasAnyValue()) {
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* NOTE: In theory, there's no need to combine masked values, if this is a fresh
|
||||
* emulator. If I had to guess, the client would want their values to take precedence,
|
||||
* so they should overwrite the values after calling this method. Combining can be
|
||||
* problematic, because the emulator could return some abstraction for the current
|
||||
* value.
|
||||
*/
|
||||
state.setRegisterValue(rv);
|
||||
}
|
||||
initializeRegisters(thread, program, entry);
|
||||
|
||||
AddressRange stack = chooseStackRange(program, entry);
|
||||
AddressRange stack = chooseStackRange(program, entry, stackSize);
|
||||
long stackOffset = cSpec.stackGrowsNegative() ? stack.getMaxAddress().getOffset() + 1
|
||||
: stack.getMinAddress().getOffset();
|
||||
state.setVar(sp, arithmetic.fromConst(stackOffset, sp.getMinimumByteSize()));
|
||||
}
|
||||
|
||||
thread.overrideCounter(entry);
|
||||
/**
|
||||
* Prepare a thread to emulate a given function, using the default stack size
|
||||
*
|
||||
* @param <T> the type of values in the emulator
|
||||
* @param thread the thread whose state to initialize
|
||||
* @param function the function to prepare to enter
|
||||
*/
|
||||
public static <T> void initializeForFunction(PcodeThread<T> thread, Function function) {
|
||||
initializeForFunction(thread, function, DEFAULT_STACK_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspect the value of the stack pointer, and return it as an address
|
||||
*
|
||||
* @param <T> the type of values in the emulator
|
||||
* @param thread the thread whose stack pointer to inspect
|
||||
* @param cSpec the compiler spec defining the stack register and base space
|
||||
* @return the address pointed to by the stack pointer
|
||||
*/
|
||||
public static <T> Address inspectStackPointer(PcodeThread<T> thread, CompilerSpec cSpec) {
|
||||
Register sp = cSpec.getStackPointer();
|
||||
long stackOffset =
|
||||
thread.getState().inspectRegisterValue(sp).getUnsignedValue().longValueExact();
|
||||
return cSpec.getStackBaseSpace().getAddress(stackOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a null-terminated string of the given charset from memory
|
||||
* <p>
|
||||
* This retrieves bytes one at a time from the given state and feeds them to a decoder until
|
||||
* that decoder emits a null character.
|
||||
*
|
||||
* @param state the memory
|
||||
* @param ptr the starting address
|
||||
* @param cs the character set
|
||||
* @param maxBytes the maximum number of bytes to read
|
||||
* @return the decoded string
|
||||
*/
|
||||
public static String decodeNullTerminatedString(PcodeExecutorState<?> state, Address ptr,
|
||||
Charset cs, int maxBytes) {
|
||||
CharsetDecoder decoder = cs.newDecoder();
|
||||
ByteBuffer in = ByteBuffer.allocate(1);
|
||||
CharBuffer out = CharBuffer.allocate(maxBytes); // should never be more chars than bytes
|
||||
MemBuffer buf = state.getConcreteBuffer(ptr, Purpose.INSPECT);
|
||||
for (int i = 0; i < maxBytes; i++) {
|
||||
in.position(0);
|
||||
int len = buf.getBytes(in.array(), i);
|
||||
in.limit(len);
|
||||
if (len == 0) {
|
||||
// Flush out any partials and be done
|
||||
decoder.decode(in, out, true);
|
||||
return out.flip().toString();
|
||||
}
|
||||
decoder.decode(in, out, false);
|
||||
int nullPos = out.position() - 1;
|
||||
if (nullPos >= 0 && out.get(nullPos) == 0) {
|
||||
return out.position(nullPos).flip().toString();
|
||||
}
|
||||
}
|
||||
in.position(0);
|
||||
in.limit(0);
|
||||
// Flush out any partials and be done
|
||||
decoder.decode(in, out, true);
|
||||
return out.flip().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode an ASCII null-terminated string of at most 128 bytes from memory
|
||||
*
|
||||
* @param state the memory
|
||||
* @param ptr the starting address
|
||||
* @return the decoded string
|
||||
*/
|
||||
public static String decodeNullTerminatedString(PcodeExecutorState<?> state, Address ptr) {
|
||||
return decodeNullTerminatedString(state, ptr, Charset.forName("ASCII"), 128);
|
||||
}
|
||||
}
|
||||
|
||||
+22
-16
@@ -17,6 +17,7 @@ package ghidra.pcode.emu;
|
||||
|
||||
import ghidra.pcode.exec.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.lang.RegisterValue;
|
||||
import ghidra.program.model.listing.Instruction;
|
||||
@@ -378,18 +379,19 @@ public interface PcodeEmulationCallbacks<T> {
|
||||
* @param space the address space of the operand
|
||||
* @param offset the offset of the operand
|
||||
* @param length the size of the operand
|
||||
* @param reason the reason for reading
|
||||
* @return the length of the operand just initialized, typically 0 or {@code length}
|
||||
*/
|
||||
default <A, U> int readUninitialized(PcodeThread<T> thread,
|
||||
PcodeExecutorStatePiece<A, U> piece, AddressSpace space, A offset, int length) {
|
||||
default <A, U> int readUninitialized(PcodeThread<T> thread, PcodeExecutorStatePiece<A, U> piece,
|
||||
AddressSpace space, A offset, int length, Reason reason) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Typically used from within
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int)}
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int, Reason)}
|
||||
* to forward to the callback for concrete addressing
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView)}.
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView, Reason)}.
|
||||
*
|
||||
* @param <A> the piece's address domain
|
||||
* @param <U> the piece's value domain
|
||||
@@ -399,13 +401,15 @@ public interface PcodeEmulationCallbacks<T> {
|
||||
* @param space the address space of the operand
|
||||
* @param offset the offset of the operand
|
||||
* @param length the size of the operand
|
||||
* @param reason the reason for reading
|
||||
* @return the length of the operand just initialized, typically 0 or {@code length}
|
||||
*/
|
||||
default <A, U> int delegateReadUninitialized(PcodeThread<T> thread,
|
||||
PcodeExecutorStatePiece<A, U> piece, AddressSpace space, A offset, int length) {
|
||||
PcodeExecutorStatePiece<A, U> piece, AddressSpace space, A offset, int length,
|
||||
Reason reason) {
|
||||
long lOffset = piece.getAddressArithmetic().toLong(offset, Purpose.LOAD);
|
||||
AddressSet set = PcodeStateCallbacks.rngSet(space, lOffset, length);
|
||||
AddressSetView remains = readUninitialized(thread, piece, set);
|
||||
AddressSetView remains = readUninitialized(thread, piece, set, reason);
|
||||
if (set == remains) {
|
||||
return 0;
|
||||
}
|
||||
@@ -432,18 +436,19 @@ public interface PcodeEmulationCallbacks<T> {
|
||||
* {@link #dataWritten(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int, Object)}
|
||||
* @param piece the state piece
|
||||
* @param set the uninitialized portion required
|
||||
* @param reason the reason for reading
|
||||
* @return the addresses in {@code set} that remain uninitialized
|
||||
*/
|
||||
default <A, U> AddressSetView readUninitialized(PcodeThread<T> thread,
|
||||
PcodeExecutorStatePiece<A, U> piece, AddressSetView set) {
|
||||
PcodeExecutorStatePiece<A, U> piece, AddressSetView set, Reason reason) {
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Typically used from within
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView)} to forward
|
||||
* to the callback for abstract addressing
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int)}.
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView, Reason)} to
|
||||
* forward to the callback for abstract addressing
|
||||
* {@link #readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int, Reason)}.
|
||||
*
|
||||
* @param <A> the piece's address domain
|
||||
* @param <U> the piece's value domain
|
||||
@@ -451,10 +456,11 @@ public interface PcodeEmulationCallbacks<T> {
|
||||
* {@link #dataWritten(PcodeThread, PcodeExecutorStatePiece, AddressSpace, Object, int, Object)}
|
||||
* @param piece the state piece
|
||||
* @param set the uninitialized portion required
|
||||
* @param reason the reason for reading
|
||||
* @return the addresses in {@code set} that remain uninitialized
|
||||
*/
|
||||
default <A, U> AddressSetView delegateReadUninitialized(PcodeThread<T> thread,
|
||||
PcodeExecutorStatePiece<A, U> piece, AddressSetView set) {
|
||||
PcodeExecutorStatePiece<A, U> piece, AddressSetView set, Reason reason) {
|
||||
if (set.isEmpty()) {
|
||||
return set;
|
||||
}
|
||||
@@ -463,7 +469,7 @@ public interface PcodeEmulationCallbacks<T> {
|
||||
for (AddressRange range : set) {
|
||||
int l = readUninitialized(thread, piece, range.getAddressSpace(),
|
||||
piece.getAddressArithmetic().fromConst(range.getMinAddress()),
|
||||
(int) range.getLength());
|
||||
(int) range.getLength(), reason);
|
||||
if (l == 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -497,14 +503,14 @@ public interface PcodeEmulationCallbacks<T> {
|
||||
|
||||
@Override
|
||||
public <A, U> int readUninitialized(PcodeExecutorStatePiece<A, U> piece,
|
||||
AddressSpace space, A offset, int length) {
|
||||
return cb.readUninitialized(thread, piece, space, offset, length);
|
||||
AddressSpace space, A offset, int length, Reason reason) {
|
||||
return cb.readUninitialized(thread, piece, space, offset, length, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A, U> AddressSetView readUninitialized(PcodeExecutorStatePiece<A, U> piece,
|
||||
AddressSetView set) {
|
||||
return cb.readUninitialized(thread, piece, set);
|
||||
AddressSetView set, Reason reason) {
|
||||
return cb.readUninitialized(thread, piece, set, reason);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ public abstract class AbstractBytesPcodeExecutorStatePiece<S extends BytesPcodeE
|
||||
Address min = address.add(addressOffset);
|
||||
AddressSet set = new AddressSet(min, min.add(buffer.remaining() - 1));
|
||||
if (set.equals(
|
||||
cb.readUninitialized(AbstractBytesPcodeExecutorStatePiece.this, set))) {
|
||||
cb.readUninitialized(AbstractBytesPcodeExecutorStatePiece.this, set, reason))) {
|
||||
return 0;
|
||||
}
|
||||
source = getForSpace(address.getAddressSpace(), false);
|
||||
|
||||
+1
-1
@@ -259,7 +259,7 @@ public abstract class AbstractLongOffsetPcodeExecutorStatePiece<A, T, S>
|
||||
S s = getForSpace(space, false);
|
||||
if (s == null) {
|
||||
AddressSet set = PcodeStateCallbacks.rngSet(space, offset, size);
|
||||
if (set.equals(cb.readUninitialized(this, set))) {
|
||||
if (set.equals(cb.readUninitialized(this, set, reason))) {
|
||||
return getFromNullSpace(size, reason, cb);
|
||||
}
|
||||
s = getForSpace(space, false);
|
||||
|
||||
+1
-1
@@ -211,7 +211,7 @@ public class BytesPcodeExecutorStateSpace {
|
||||
if (uninitialized.isEmpty()) {
|
||||
return readBytes(offset, size, reason);
|
||||
}
|
||||
uninitialized = cb.readUninitialized(piece, uninitialized);
|
||||
uninitialized = cb.readUninitialized(piece, uninitialized, reason);
|
||||
if (uninitialized.isEmpty()) {
|
||||
return readBytes(offset, size, reason);
|
||||
}
|
||||
|
||||
+18
-13
@@ -17,6 +17,7 @@ package ghidra.pcode.exec;
|
||||
|
||||
import ghidra.pcode.emu.*;
|
||||
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
|
||||
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
/**
|
||||
@@ -29,8 +30,8 @@ import ghidra.program.model.address.*;
|
||||
* {@link PcodeExecutorState}s and/or {@link PcodeExecutorStatePiece}s just to introduce
|
||||
* integration-driven behaviors. E.g., to lazily load state from an external machine-state snapshot,
|
||||
* the client should implement the
|
||||
* {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView)} or
|
||||
* {@link PcodeEmulationCallbacks#readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView)}
|
||||
* {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView, Reason)} or
|
||||
* {@link PcodeEmulationCallbacks#readUninitialized(PcodeThread, PcodeExecutorStatePiece, AddressSetView, Reason)}
|
||||
* callback rather than extending {@link BytesPcodeExecutorStatePiece}.
|
||||
*/
|
||||
public interface PcodeStateCallbacks {
|
||||
@@ -164,18 +165,19 @@ public interface PcodeStateCallbacks {
|
||||
* @param space the address space of the operand
|
||||
* @param offset the offset of the operand
|
||||
* @param length the size of the operand
|
||||
* @param reason the reason for reading
|
||||
* @return the length of the operand just initialized, typically 0 or {@code length}
|
||||
*/
|
||||
default <A, T> int readUninitialized(PcodeExecutorStatePiece<A, T> piece,
|
||||
AddressSpace space, A offset, int length) {
|
||||
AddressSpace space, A offset, int length, Reason reason) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Typically used from within
|
||||
* {@link #readUninitialized(PcodeExecutorStatePiece, AddressSpace, Object, int)} to forward to
|
||||
* the callback for concrete addressing
|
||||
* {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView)}.
|
||||
* {@link #readUninitialized(PcodeExecutorStatePiece, AddressSpace, Object, int, Reason)} to
|
||||
* forward to the callback for concrete addressing
|
||||
* {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView, Reason)}.
|
||||
*
|
||||
* @param <A> the piece's address domain
|
||||
* @param <T> the piece's value domain
|
||||
@@ -183,13 +185,14 @@ public interface PcodeStateCallbacks {
|
||||
* @param space the address space of the operand
|
||||
* @param offset the offset of the operand
|
||||
* @param length the size of the operand
|
||||
* @param reason the reason for reading
|
||||
* @return the length of the operand just initialized, typically 0 or {@code length}
|
||||
*/
|
||||
default <A, T> int delegateReadUninitialized(PcodeExecutorStatePiece<A, T> piece,
|
||||
AddressSpace space, A offset, int length) {
|
||||
AddressSpace space, A offset, int length, Reason reason) {
|
||||
long lOffset = piece.getAddressArithmetic().toLong(offset, Purpose.LOAD);
|
||||
AddressSet set = PcodeStateCallbacks.rngSet(space, lOffset, length);
|
||||
AddressSetView remains = readUninitialized(piece, set);
|
||||
AddressSetView remains = readUninitialized(piece, set, reason);
|
||||
if (set == remains) {
|
||||
return 0;
|
||||
}
|
||||
@@ -214,27 +217,29 @@ public interface PcodeStateCallbacks {
|
||||
* @param <T> the piece's value domain
|
||||
* @param piece the state piece
|
||||
* @param set the uninitialized portion required
|
||||
* @param reason the reason for reading
|
||||
* @return the addresses in {@code set} that remain uninitialized
|
||||
*/
|
||||
default <A, T> AddressSetView readUninitialized(PcodeExecutorStatePiece<A, T> piece,
|
||||
AddressSetView set) {
|
||||
AddressSetView set, Reason reason) {
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Typically used from within
|
||||
* {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView)} to forward to the
|
||||
* {@link #readUninitialized(PcodeExecutorStatePiece, AddressSetView, Reason)} to forward to the
|
||||
* callback for abstract addressing
|
||||
* {@link #readUninitialized(PcodeExecutorStatePiece, AddressSpace, Object, int)}.
|
||||
* {@link #readUninitialized(PcodeExecutorStatePiece, AddressSpace, Object, int, Reason)}.
|
||||
*
|
||||
* @param <A> the piece's address domain
|
||||
* @param <T> the piece's value domain
|
||||
* @param piece the state piece
|
||||
* @param set the uninitialized portion required
|
||||
* @param reason the reason for reading
|
||||
* @return the addresses in {@code set} that remain uninitialized
|
||||
*/
|
||||
default <A, T> AddressSetView delegateReadUninitialized(PcodeExecutorStatePiece<A, T> piece,
|
||||
AddressSetView set) {
|
||||
AddressSetView set, Reason reason) {
|
||||
if (set.isEmpty()) {
|
||||
return set;
|
||||
}
|
||||
@@ -242,7 +247,7 @@ public interface PcodeStateCallbacks {
|
||||
for (AddressRange range : set) {
|
||||
int l = readUninitialized(piece, range.getAddressSpace(),
|
||||
piece.getAddressArithmetic().fromConst(range.getMinAddress()),
|
||||
(int) range.getLength());
|
||||
(int) range.getLength(), reason);
|
||||
if (l == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -287,9 +287,9 @@ the Python source over and add it to your <code>PYTHONPATH</code>.</p>
|
||||
<p>Double-check that you have installed all the required packages and
|
||||
their dependencies. A common forgotten or incorrectly-versioned
|
||||
dependency is <code>protobuf</code>. We developed using
|
||||
<code>protobuf==3.20.3</code>, newer versions generally work fine. Its “sdist” package is distributed with
|
||||
Ghidra under <code>Debugger-rmi-trace/pypkg/dist</code> for your
|
||||
convenience.</p>
|
||||
<code>protobuf==3.20.3</code>, newer versions generally work fine. Its
|
||||
“sdist” package is distributed with Ghidra under
|
||||
<code>Debugger-rmi-trace/pypkg/dist</code> for your convenience.</p>
|
||||
<p>It is also possible that <code>gdb</code> has embedded a different
|
||||
version of the interpreter than the one that <code>python3</code>
|
||||
provides. This can happen if you built GDB or Python yourself, or you
|
||||
|
||||
@@ -780,13 +780,13 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
|
||||
<span id="cb8-18"><a href="#cb8-18"></a> cb<span class="op">.</span><span class="fu">dataWritten</span><span class="op">(</span>piece<span class="op">,</span> space<span class="op">.</span><span class="fu">getAddress</span><span class="op">(</span>offset<span class="op">),</span> size<span class="op">,</span> val<span class="op">);</span></span>
|
||||
<span id="cb8-19"><a href="#cb8-19"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-20"><a href="#cb8-20"></a></span>
|
||||
<span id="cb8-21"><a href="#cb8-21"></a> <span class="kw">public</span> Expr <span class="fu">get</span><span class="op">(</span><span class="dt">long</span> offset<span class="op">,</span> <span class="dt">int</span> size<span class="op">,</span> PcodeStateCallbacks cb<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-21"><a href="#cb8-21"></a> <span class="kw">public</span> Expr <span class="fu">get</span><span class="op">(</span><span class="dt">long</span> offset<span class="op">,</span> <span class="dt">int</span> size<span class="op">,</span> Reason reason<span class="op">,</span> PcodeStateCallbacks cb<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-22"><a href="#cb8-22"></a> <span class="co">// </span><span class="al">TODO</span><span class="co">: Handle overlaps / offcut gets and sets</span></span>
|
||||
<span id="cb8-23"><a href="#cb8-23"></a> Expr expr <span class="op">=</span> map<span class="op">.</span><span class="fu">get</span><span class="op">(</span>offset<span class="op">);</span></span>
|
||||
<span id="cb8-24"><a href="#cb8-24"></a> <span class="cf">if</span> <span class="op">(</span>expr <span class="op">==</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-25"><a href="#cb8-25"></a> <span class="dt">byte</span><span class="op">[]</span> aOffset <span class="op">=</span></span>
|
||||
<span id="cb8-26"><a href="#cb8-26"></a> piece<span class="op">.</span><span class="fu">getAddressArithmetic</span><span class="op">().</span><span class="fu">fromConst</span><span class="op">(</span>offset<span class="op">,</span> space<span class="op">.</span><span class="fu">getPointerSize</span><span class="op">());</span></span>
|
||||
<span id="cb8-27"><a href="#cb8-27"></a> <span class="cf">if</span> <span class="op">(</span>cb<span class="op">.</span><span class="fu">readUninitialized</span><span class="op">(</span>piece<span class="op">,</span> space<span class="op">,</span> aOffset<span class="op">,</span> size<span class="op">)</span> <span class="op">!=</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-27"><a href="#cb8-27"></a> <span class="cf">if</span> <span class="op">(</span>cb<span class="op">.</span><span class="fu">readUninitialized</span><span class="op">(</span>piece<span class="op">,</span> space<span class="op">,</span> aOffset<span class="op">,</span> size<span class="op">,</span> reason<span class="op">)</span> <span class="op">!=</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-28"><a href="#cb8-28"></a> <span class="cf">return</span> map<span class="op">.</span><span class="fu">get</span><span class="op">(</span>offset<span class="op">);</span></span>
|
||||
<span id="cb8-29"><a href="#cb8-29"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-30"><a href="#cb8-30"></a> <span class="op">}</span></span>
|
||||
@@ -846,7 +846,7 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
|
||||
<span id="cb8-84"><a href="#cb8-84"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-85"><a href="#cb8-85"></a> <span class="kw">protected</span> Expr <span class="fu">getFromSpace</span><span class="op">(</span>ExprSpace space<span class="op">,</span> <span class="dt">long</span> offset<span class="op">,</span> <span class="dt">int</span> size<span class="op">,</span> Reason reason<span class="op">,</span></span>
|
||||
<span id="cb8-86"><a href="#cb8-86"></a> PcodeStateCallbacks cb<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-87"><a href="#cb8-87"></a> <span class="cf">return</span> space<span class="op">.</span><span class="fu">get</span><span class="op">(</span>offset<span class="op">,</span> size<span class="op">,</span> cb<span class="op">);</span></span>
|
||||
<span id="cb8-87"><a href="#cb8-87"></a> <span class="cf">return</span> space<span class="op">.</span><span class="fu">get</span><span class="op">(</span>offset<span class="op">,</span> size<span class="op">,</span> reason<span class="op">,</span> cb<span class="op">);</span></span>
|
||||
<span id="cb8-88"><a href="#cb8-88"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-89"><a href="#cb8-89"></a></span>
|
||||
<span id="cb8-90"><a href="#cb8-90"></a> <span class="at">@Override</span></span>
|
||||
|
||||
@@ -555,13 +555,13 @@ public static class ExprSpace {
|
||||
cb.dataWritten(piece, space.getAddress(offset), size, val);
|
||||
}
|
||||
|
||||
public Expr get(long offset, int size, PcodeStateCallbacks cb) {
|
||||
public Expr get(long offset, int size, Reason reason, PcodeStateCallbacks cb) {
|
||||
// TODO: Handle overlaps / offcut gets and sets
|
||||
Expr expr = map.get(offset);
|
||||
if (expr == null) {
|
||||
byte[] aOffset =
|
||||
piece.getAddressArithmetic().fromConst(offset, space.getPointerSize());
|
||||
if (cb.readUninitialized(piece, space, aOffset, size) != 0) {
|
||||
if (cb.readUninitialized(piece, space, aOffset, size, reason) != 0) {
|
||||
return map.get(offset);
|
||||
}
|
||||
}
|
||||
@@ -621,7 +621,7 @@ public static class ExprPcodeExecutorStatePiece
|
||||
@Override
|
||||
protected Expr getFromSpace(ExprSpace space, long offset, int size, Reason reason,
|
||||
PcodeStateCallbacks cb) {
|
||||
return space.get(offset, size, cb);
|
||||
return space.get(offset, size, reason, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -336,13 +336,13 @@ public class ModelingScript extends GhidraScript {
|
||||
cb.dataWritten(piece, space.getAddress(offset), size, val);
|
||||
}
|
||||
|
||||
public Expr get(long offset, int size, PcodeStateCallbacks cb) {
|
||||
public Expr get(long offset, int size, Reason reason, PcodeStateCallbacks cb) {
|
||||
// TODO: Handle overlaps / offcut gets and sets
|
||||
Expr expr = map.get(offset);
|
||||
if (expr == null) {
|
||||
byte[] aOffset =
|
||||
piece.getAddressArithmetic().fromConst(offset, space.getPointerSize());
|
||||
if (cb.readUninitialized(piece, space, aOffset, size) != 0) {
|
||||
if (cb.readUninitialized(piece, space, aOffset, size, reason) != 0) {
|
||||
return map.get(offset);
|
||||
}
|
||||
}
|
||||
@@ -402,7 +402,7 @@ public class ModelingScript extends GhidraScript {
|
||||
@Override
|
||||
protected Expr getFromSpace(ExprSpace space, long offset, int size, Reason reason,
|
||||
PcodeStateCallbacks cb) {
|
||||
return space.get(offset, size, cb);
|
||||
return space.get(offset, size, reason, cb);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,4 +7,4 @@ Sample scripts deobExampleX86 and deobHookExampleX86 may be built under Linux.
|
||||
|
||||
Once these examples have been compiled they may be imported into a Ghidra project and the
|
||||
corresponding Ghidra Scripts (EmuX86DeobfuscateExampleScript and EmuX86GccDeobfuscateHookExampleScript)
|
||||
used to demonstrate the use of the EmulatorHelper class.
|
||||
used to demonstrate the use of the PcodeEmulator class.
|
||||
|
||||
Reference in New Issue
Block a user