mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-23 20:59:58 +00:00
Merge remote-tracking branch 'origin/patch'
This commit is contained in:
commit
f8ec071906
@ -33,6 +33,12 @@
|
||||
href="help/topics/DebuggerControlPlugin/DebuggerControlPlugin.html#emu_actions">emulator
|
||||
controls</A> can then be used.</P>
|
||||
|
||||
<P>To control the initial stack allocation, create a <CODE>STACK</CODE> block in the target
|
||||
program database before emulating. If the stack is already in the target image's memory map,
|
||||
create an overlay block named <CODE>STACK</CODE>. This will initialize the stack pointer
|
||||
without modifying the emulator's memory map. Note that customizing the stack initialization may
|
||||
prevent you from adding a second thread.</P>
|
||||
|
||||
<H3><A name="add_emulated_thread"></A> Add Emulated Thread</H3>
|
||||
|
||||
<P>This action is available whenever a "pure emulation" trace is active. It spawns a new thread
|
||||
|
@ -510,14 +510,14 @@ public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEm
|
||||
if (!block.isExecute()) {
|
||||
return;
|
||||
}*/
|
||||
ProgramLocation progLoc =
|
||||
staticMappings.getOpenMappedLocation(new DefaultTraceLocation(view.getTrace(), null,
|
||||
Lifespan.at(view.getSnap()), tracePc));
|
||||
Program program = progLoc == null ? null : progLoc.getProgram();
|
||||
Address programPc = progLoc == null ? null : progLoc.getAddress();
|
||||
|
||||
long snap =
|
||||
view.getViewport().getOrderedSnaps().stream().filter(s -> s >= 0).findFirst().get();
|
||||
ProgramLocation progLoc = staticMappings.getOpenMappedLocation(
|
||||
new DefaultTraceLocation(trace, null, Lifespan.at(snap), tracePc));
|
||||
Program program = progLoc == null ? null : progLoc.getProgram();
|
||||
Address programPc = progLoc == null ? null : progLoc.getAddress();
|
||||
|
||||
TraceThread thread = ProgramEmulationUtils.launchEmulationThread(trace, snap, program,
|
||||
tracePc, programPc);
|
||||
traceManager.activateThread(thread);
|
||||
|
@ -43,8 +43,7 @@ import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectKeyPath;
|
||||
import ghidra.trace.model.thread.*;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.util.DifferenceAddressSetView;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
/**
|
||||
@ -56,6 +55,7 @@ import ghidra.util.exception.DuplicateNameException;
|
||||
*/
|
||||
public enum ProgramEmulationUtils {
|
||||
;
|
||||
public static final String BLOCK_NAME_STACK = "STACK";
|
||||
|
||||
/**
|
||||
* Conventional prefix for first snapshot to identify "pure emulation" traces.
|
||||
@ -227,10 +227,10 @@ public enum ProgramEmulationUtils {
|
||||
* @param program the program whose context to use
|
||||
* @param tracePc the program counter in the trace's memory map
|
||||
* @param programPc the program counter in the program's memory map
|
||||
* @param stack optionally, the region representing the thread's stack
|
||||
* @param stack optionally, the range for the thread's stack allocation
|
||||
*/
|
||||
public static void initializeRegisters(Trace trace, long snap, TraceThread thread,
|
||||
Program program, Address tracePc, Address programPc, TraceMemoryRegion stack) {
|
||||
Program program, Address tracePc, Address programPc, AddressRange stack) {
|
||||
TraceMemoryManager memory = trace.getMemoryManager();
|
||||
if (thread instanceof TraceObjectThread ot) {
|
||||
TraceObject object = ot.getObject();
|
||||
@ -283,28 +283,92 @@ public enum ProgramEmulationUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static AddressRange allocateStackCustom(Trace trace, long snap, TraceThread thread,
|
||||
Program program) {
|
||||
if (program == null) {
|
||||
return null;
|
||||
}
|
||||
AddressSpace space = trace.getBaseCompilerSpec().getStackBaseSpace();
|
||||
MemoryBlock stackBlock = program.getMemory().getBlock(BLOCK_NAME_STACK);
|
||||
if (stackBlock == null) {
|
||||
return null;
|
||||
}
|
||||
if (space != stackBlock.getStart().getAddressSpace().getPhysicalSpace()) {
|
||||
Msg.showError(ProgramEmulationUtils.class, null, "Invalid STACK block",
|
||||
"The STACK block must be in the stack's base space. Ignoring.");
|
||||
return null;
|
||||
}
|
||||
AddressRange alloc = new AddressRangeImpl(
|
||||
stackBlock.getStart().getPhysicalAddress(),
|
||||
stackBlock.getEnd().getPhysicalAddress());
|
||||
if (stackBlock.isOverlay() || DebuggerStaticMappingUtils.isReal(stackBlock)) {
|
||||
return alloc;
|
||||
}
|
||||
PathPattern patRegion = computePatternRegion(trace);
|
||||
String path = PathUtils.toString(
|
||||
patRegion.applyKeys(stackBlock.getStart() + "-STACK")
|
||||
.getSingletonPath());
|
||||
TraceMemoryManager mm = trace.getMemoryManager();
|
||||
try {
|
||||
return mm.createRegion(path, snap, alloc,
|
||||
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange();
|
||||
}
|
||||
catch (TraceOverlappedRegionException e) {
|
||||
Msg.showError(ProgramEmulationUtils.class, null, "Stack conflict",
|
||||
("The STACK region %s conflicts with another: %s. " +
|
||||
"You may need to initialize the stack pointer manually.").formatted(
|
||||
alloc, e.getConflicts().iterator().next()));
|
||||
return alloc;
|
||||
}
|
||||
catch (DuplicateNameException e) {
|
||||
Msg.showError(ProgramEmulationUtils.class, null, "Stack conflict",
|
||||
("A region already exists with the same name: %s. " +
|
||||
"You may need to initialize the stack pointer manually.")
|
||||
.formatted(path));
|
||||
return alloc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to allocate a new stack region for the given thread
|
||||
*
|
||||
* <p>
|
||||
* If successful, this will create a dynamic memory region representing the stack. If the stack
|
||||
* is specified by an override (STACK block) in the program, and that block overlays the image,
|
||||
* then no region is created.
|
||||
*
|
||||
* @param trace the trace containing the stack and thread
|
||||
* @param snap the creation snap for the new region
|
||||
* @param thread the thread for which the stack is being allocated
|
||||
* @param program the program being emulated (to check for stack allocation override)
|
||||
* @param size the desired size of the region
|
||||
* @return the new region representing the allocated stack
|
||||
* @return the range allocated for the stack
|
||||
*
|
||||
* @throws EmulatorOutOfMemoryException if the stack cannot be allocated
|
||||
*/
|
||||
public static TraceMemoryRegion allocateStack(Trace trace, long snap, TraceThread thread,
|
||||
long size) {
|
||||
public static AddressRange allocateStack(Trace trace, long snap, TraceThread thread,
|
||||
Program program, long size) {
|
||||
AddressRange custom = allocateStackCustom(trace, snap, thread, program);
|
||||
if (custom != null) {
|
||||
return custom;
|
||||
}
|
||||
// Otherwise, just search for an un-allocated block of the given size.
|
||||
AddressSpace space = trace.getBaseCompilerSpec().getStackBaseSpace();
|
||||
AddressSet except0 = new AddressSet(space.getAddress(0x1000), space.getMaxAddress());
|
||||
Address max = space.getMaxAddress();
|
||||
AddressSet eligible;
|
||||
if (max.getOffsetAsBigInteger().compareTo(BigInteger.valueOf(0x1000)) < 0) {
|
||||
eligible = new AddressSet(space.getMinAddress(), max);
|
||||
}
|
||||
else {
|
||||
eligible = new AddressSet(space.getAddress(0x1000), max);
|
||||
}
|
||||
TraceMemoryManager mm = trace.getMemoryManager();
|
||||
AddressSetView left =
|
||||
new DifferenceAddressSetView(except0, mm.getRegionsAddressSet(snap));
|
||||
new DifferenceAddressSetView(eligible, mm.getRegionsAddressSet(snap));
|
||||
PathPattern patRegion = computePatternRegion(trace);
|
||||
try {
|
||||
for (AddressRange candidate : left) {
|
||||
if (Long.compareUnsigned(candidate.getLength(), size) > 0) {
|
||||
if (Long.compareUnsigned(candidate.getLength(), size) >= 0) {
|
||||
AddressRange alloc = new AddressRangeImpl(candidate.getMinAddress(), size);
|
||||
String threadName = PathUtils.isIndex(thread.getName())
|
||||
? PathUtils.parseIndex(thread.getName())
|
||||
@ -313,7 +377,7 @@ public enum ProgramEmulationUtils {
|
||||
patRegion.applyKeys(alloc.getMinAddress() + "-stack " + threadName)
|
||||
.getSingletonPath());
|
||||
return mm.createRegion(path, snap, alloc,
|
||||
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE);
|
||||
TraceMemoryFlag.READ, TraceMemoryFlag.WRITE).getRange();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -373,7 +437,15 @@ public enum ProgramEmulationUtils {
|
||||
public static TraceThread doLaunchEmulationThread(Trace trace, long snap, Program program,
|
||||
Address tracePc, Address programPc) {
|
||||
TraceThread thread = spawnThread(trace, snap);
|
||||
TraceMemoryRegion stack = allocateStack(trace, snap, thread, 0x4000);
|
||||
AddressRange stack;
|
||||
try {
|
||||
stack = allocateStack(trace, snap, thread, program, 0x4000);
|
||||
}
|
||||
catch (EmulatorOutOfMemoryException e) {
|
||||
Msg.warn(ProgramEmulationUtils.class,
|
||||
"Cannot allocate a stack. Please initialize manually.");
|
||||
stack = null;
|
||||
}
|
||||
initializeRegisters(trace, snap, thread, program, tracePc, programPc, stack);
|
||||
return thread;
|
||||
}
|
||||
|
@ -381,7 +381,8 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
||||
assertTrue(result.error() instanceof DecodePcodeExecutionException);
|
||||
|
||||
long scratch = result.snapshot();
|
||||
assertEquals(new BigInteger("003ffffe", 16), regs.getViewValue(scratch, regPC).getUnsignedValue());
|
||||
assertEquals(new BigInteger("003ffffe", 16),
|
||||
regs.getViewValue(scratch, regPC).getUnsignedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -715,4 +716,34 @@ public class DebuggerEmulationServiceTest extends AbstractGhidraHeadedDebuggerGU
|
||||
assertEquals(new BigInteger("5678", 16),
|
||||
regs.getViewValue(scratch, regR2).getUnsignedValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomStack() throws Exception {
|
||||
createProgram();
|
||||
intoProject(program);
|
||||
Memory memory = program.getMemory();
|
||||
Address addrText = addr(program, 0x00400000);
|
||||
Register regSP = program.getRegister("sp");
|
||||
try (Transaction tx = program.openTransaction("Initialize")) {
|
||||
MemoryBlock blockText = memory.createInitializedBlock(".text", addrText, 0x1000,
|
||||
(byte) 0, TaskMonitor.DUMMY, false);
|
||||
blockText.setExecute(true);
|
||||
memory.createUninitializedBlock("STACK", addr(program, 0x00001234), 0x1000, false);
|
||||
}
|
||||
|
||||
programManager.openProgram(program);
|
||||
waitForSwing();
|
||||
codeBrowser.goTo(new ProgramLocation(program, addrText));
|
||||
waitForSwing();
|
||||
|
||||
assertTrue(emulationPlugin.actionEmulateProgram.isEnabled());
|
||||
performAction(emulationPlugin.actionEmulateProgram);
|
||||
|
||||
Trace trace = traceManager.getCurrentTrace();
|
||||
assertNotNull(trace);
|
||||
|
||||
TraceThread thread = Unique.assertOne(trace.getThreadManager().getAllThreads());
|
||||
TraceMemorySpace regs = trace.getMemoryManager().getMemoryRegisterSpace(thread, false);
|
||||
assertEquals(new BigInteger("2234", 16), regs.getViewValue(0, regSP).getUnsignedValue());
|
||||
}
|
||||
}
|
||||
|
@ -36,8 +36,7 @@ import ghidra.app.script.GhidraScript;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.OptionsService;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.pcode.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
@ -56,6 +55,9 @@ public class WindowsResourceReference extends GhidraScript {
|
||||
ArrayList<ArrayList<PcodeOp>> defUseLists = new ArrayList<>();
|
||||
|
||||
protected AddressSetPropertyMap alreadyDoneAddressSetPropertyMap;
|
||||
|
||||
// set of functions that decompilation failed on
|
||||
protected AddressSet badDecompFunctions = new AddressSet();
|
||||
|
||||
public AddressSetPropertyMap getOrCreatePropertyMap(Program program, String mapName) {
|
||||
if (alreadyDoneAddressSetPropertyMap != null) {
|
||||
@ -367,6 +369,11 @@ public class WindowsResourceReference extends GhidraScript {
|
||||
if (f == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if decompilation of this function failed previously
|
||||
if (badDecompFunctions.contains(f.getEntryPoint())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Instruction instr = prog.getListing().getInstructionAt(refAddr);
|
||||
if (instr == null) {
|
||||
@ -376,7 +383,9 @@ public class WindowsResourceReference extends GhidraScript {
|
||||
decompileFunction(f, decompiler);
|
||||
|
||||
if (hfunction == null) {
|
||||
return; // failed to decompile
|
||||
// failed to decompile, add to bad list
|
||||
badDecompFunctions.add(f.getEntryPoint());
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<PcodeOpAST> ops = hfunction.getPcodeOps(refAddr);
|
||||
|
@ -434,6 +434,15 @@ macro complement(op)
|
||||
setNZFlags(op);
|
||||
}
|
||||
|
||||
# Signed shift right.
|
||||
# P-code INT_SRIGHT.
|
||||
macro arithmeticShiftRight(op)
|
||||
{
|
||||
$(C) = op & 1;
|
||||
op = (op s>> 1);
|
||||
setNZFlags(op);
|
||||
}
|
||||
|
||||
macro logicalShiftRight(op)
|
||||
{
|
||||
$(C) = op & 1;
|
||||
@ -450,31 +459,25 @@ macro rotateRightWithCarry(op)
|
||||
setNZFlags(op);
|
||||
}
|
||||
|
||||
macro logicalShiftLeft(op)
|
||||
{
|
||||
local tmp = (op >> 7);
|
||||
$(C) = tmp;
|
||||
op = op << 1;
|
||||
$(V) = tmp ^ (op >> 7);
|
||||
setNZFlags(op);
|
||||
}
|
||||
|
||||
macro rotateLeftWithCarry(op)
|
||||
{
|
||||
local carryIn = $(C);
|
||||
$(C) = op >> 7;
|
||||
local tmp = (op >> 7);
|
||||
$(C) = tmp;
|
||||
op = (op << 1) | carryIn;
|
||||
$(V) = tmp ^ (op >> 7);
|
||||
setNZFlags(op);
|
||||
}
|
||||
|
||||
# Signed shift right.
|
||||
# P-code INT_SRIGHT.
|
||||
macro arithmeticShiftRight(op)
|
||||
{
|
||||
$(C) = op & 1;
|
||||
op = (op s>> 1);
|
||||
setNZFlags(op);
|
||||
}
|
||||
|
||||
macro logicalShiftLeft(op)
|
||||
{
|
||||
$(C) = (op >> 7);
|
||||
op = op << 1;
|
||||
$(Z) = (op == 0);
|
||||
$(N) = (op >> 7);
|
||||
}
|
||||
|
||||
macro increment(op)
|
||||
{
|
||||
$(V) = (op == 0x7F);
|
||||
@ -505,56 +508,69 @@ macro clear(op)
|
||||
|
||||
macro addition(reg, op)
|
||||
{
|
||||
$(C) = carry(reg, op);
|
||||
$(V) = scarry(reg, op);
|
||||
local tmp = reg;
|
||||
local val = op;
|
||||
$(C) = carry(tmp, val);
|
||||
$(V) = scarry(tmp, val);
|
||||
|
||||
reg = reg + op;
|
||||
tmp = tmp + val;
|
||||
|
||||
setNZFlags(reg);
|
||||
setNZFlags(tmp);
|
||||
reg = tmp;
|
||||
}
|
||||
|
||||
macro additionWithCarry(reg, op)
|
||||
{
|
||||
local carryIn = zext($(C));
|
||||
local tmp = reg;
|
||||
local val = op;
|
||||
local mask = 0x0F; # Low nibble mask
|
||||
local tmpResult = reg + op;
|
||||
local result = tmp + val;
|
||||
|
||||
$(H) = (((reg & mask) + (op & mask) + carryIn) >> 4) & 1;
|
||||
$(C) = carry(reg, op) || carry(tmpResult, carryIn);
|
||||
$(V) = scarry(reg, op) ^^ scarry(tmpResult, carryIn);
|
||||
$(H) = (((tmp & mask) + (val & mask) + carryIn) >> 4) & 1;
|
||||
$(C) = carry(tmp, val) || carry(result, carryIn);
|
||||
$(V) = scarry(tmp, val) ^^ scarry(result, carryIn);
|
||||
|
||||
reg = tmpResult + carryIn;
|
||||
tmp = result + carryIn;
|
||||
|
||||
setNZFlags(reg);
|
||||
setNZFlags(tmp);
|
||||
reg = tmp;
|
||||
}
|
||||
|
||||
macro subtraction(reg, op)
|
||||
{
|
||||
$(V) = sborrow(reg, op);
|
||||
reg = reg - op;
|
||||
setNZFlags(reg);
|
||||
$(C) = (reg < op);
|
||||
local tmp = reg;
|
||||
local val = op;
|
||||
$(V) = sborrow(tmp, val);
|
||||
$(C) = (tmp < val);
|
||||
tmp = tmp - val;
|
||||
setNZFlags(tmp);
|
||||
reg = tmp;
|
||||
}
|
||||
|
||||
macro subtractionWithCarry(reg, op)
|
||||
{
|
||||
local carryIn = zext($(C));
|
||||
local tmpResult = reg - op;
|
||||
local tmp = reg;
|
||||
local val = op;
|
||||
local tmpResult = tmp - val;
|
||||
|
||||
$(C) = (reg < op) || (tmpResult < carryIn);
|
||||
$(V) = sborrow(reg, op) ^^ sborrow(tmpResult, carryIn);
|
||||
|
||||
reg = tmpResult - carryIn;
|
||||
|
||||
setNZFlags(reg);
|
||||
$(C) = (tmp < val) || (tmpResult < carryIn);
|
||||
$(V) = sborrow(tmp, val) ^^ sborrow(tmpResult, carryIn);
|
||||
tmp = tmpResult - carryIn;
|
||||
setNZFlags(tmp);
|
||||
reg = tmp;
|
||||
}
|
||||
|
||||
macro compare(reg, op)
|
||||
{
|
||||
$(V) = sborrow(reg, op);
|
||||
local tmp = reg - op;
|
||||
local tmp = reg;
|
||||
local val = op;
|
||||
$(V) = sborrow(tmp, val);
|
||||
$(C) = (tmp < val);
|
||||
tmp = tmp - val;
|
||||
setNZFlags(tmp);
|
||||
$(C) = (tmp < op);
|
||||
reg = tmp;
|
||||
}
|
||||
|
||||
macro logicalAnd(reg, op)
|
||||
@ -609,9 +625,8 @@ macro Push1(reg, op)
|
||||
# Push 2 byte operand op2
|
||||
macro Push2(reg, op)
|
||||
{
|
||||
reg = reg - 1;
|
||||
reg = reg - 2;
|
||||
*:2 reg = op;
|
||||
reg = reg - 1;
|
||||
}
|
||||
|
||||
# Pull 1 byte operand op1
|
||||
@ -624,9 +639,8 @@ macro Pull1(reg, op)
|
||||
# Pull 2 byte operand op2
|
||||
macro Pull2(reg, op)
|
||||
{
|
||||
reg = reg + 1;
|
||||
op = *:2 reg;
|
||||
reg = reg + 1;
|
||||
reg = reg + 2;
|
||||
}
|
||||
|
||||
macro PushUYXDpD()
|
||||
@ -1204,7 +1218,7 @@ macro PushEntireState()
|
||||
|
||||
:ADDB OP1 is (op=0xCB | op=0xDB | op=0xEB | op=0xFB) ... & OP1
|
||||
{
|
||||
setHFlag(A, OP1);
|
||||
setHFlag(B, OP1);
|
||||
addition(B, OP1);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user