mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-27 11:43:05 +08:00
Merge remote-tracking branch 'origin/patch'
This commit is contained in:
+46
-25
@@ -25,6 +25,7 @@ import ghidra.app.plugin.core.assembler.AssemblyDualTextField;
|
|||||||
import ghidra.app.plugin.core.assembler.PatchInstructionAction;
|
import ghidra.app.plugin.core.assembler.PatchInstructionAction;
|
||||||
import ghidra.app.services.DebuggerControlService;
|
import ghidra.app.services.DebuggerControlService;
|
||||||
import ghidra.app.services.DebuggerControlService.StateEditor;
|
import ghidra.app.services.DebuggerControlService.StateEditor;
|
||||||
|
import ghidra.framework.cmd.BackgroundCommand;
|
||||||
import ghidra.program.model.address.*;
|
import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.lang.*;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.listing.CodeUnit;
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
@@ -32,6 +33,8 @@ import ghidra.program.model.mem.MemoryAccessException;
|
|||||||
import ghidra.program.util.DefaultLanguageService;
|
import ghidra.program.util.DefaultLanguageService;
|
||||||
import ghidra.trace.model.guest.TracePlatform;
|
import ghidra.trace.model.guest.TracePlatform;
|
||||||
import ghidra.trace.model.program.TraceProgramView;
|
import ghidra.trace.model.program.TraceProgramView;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public abstract class AbstractTracePatchInstructionAction extends PatchInstructionAction {
|
public abstract class AbstractTracePatchInstructionAction extends PatchInstructionAction {
|
||||||
protected final DebuggerDisassemblerPlugin plugin;
|
protected final DebuggerDisassemblerPlugin plugin;
|
||||||
@@ -116,37 +119,55 @@ public abstract class AbstractTracePatchInstructionAction extends PatchInstructi
|
|||||||
return Assemblers.getAssembler(language);
|
return Assemblers.getAssembler(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PatchInstructionCommand extends BackgroundCommand<TraceProgramView> {
|
||||||
|
private final byte[] data;
|
||||||
|
|
||||||
|
public PatchInstructionCommand(byte[] data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean applyTo(TraceProgramView view, TaskMonitor monitor) {
|
||||||
|
DebuggerControlService controlService = tool.getService(DebuggerControlService.class);
|
||||||
|
if (controlService == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
StateEditor editor = controlService.createStateEditor(view);
|
||||||
|
Address address = getAddress();
|
||||||
|
|
||||||
|
// Get code unit and dependencies before invalidating it.
|
||||||
|
CodeUnit cu = getCodeUnit();
|
||||||
|
RegisterValue contextValue = getContextValue(cu);
|
||||||
|
TracePlatform platform = getPlatform(cu);
|
||||||
|
|
||||||
|
try {
|
||||||
|
editor.setVariable(address, data).get(1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
|
setStatusMsg("Couldn't patch: " + e);
|
||||||
|
Msg.error(this, "Couldn't patch", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressSetView set = new AddressSet(address, address.add(data.length - 1));
|
||||||
|
TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, address, set);
|
||||||
|
if (contextValue != null) {
|
||||||
|
dis.setInitialContext(contextValue);
|
||||||
|
}
|
||||||
|
dis.run(tool, view);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void applyPatch(byte[] data) throws MemoryAccessException {
|
protected void applyPatch(byte[] data) throws MemoryAccessException {
|
||||||
TraceProgramView view = getView();
|
TraceProgramView view = getView();
|
||||||
if (view == null) {
|
if (view == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DebuggerControlService controlService = tool.getService(DebuggerControlService.class);
|
PatchInstructionCommand patch = new PatchInstructionCommand(data);
|
||||||
if (controlService == null) {
|
patch.run(tool, view);
|
||||||
return;
|
|
||||||
}
|
|
||||||
StateEditor editor = controlService.createStateEditor(view);
|
|
||||||
Address address = getAddress();
|
|
||||||
|
|
||||||
// Get code unit and dependencies before invalidating it.
|
|
||||||
CodeUnit cu = getCodeUnit();
|
|
||||||
RegisterValue contextValue = getContextValue(cu);
|
|
||||||
TracePlatform platform = getPlatform(cu);
|
|
||||||
|
|
||||||
try {
|
|
||||||
editor.setVariable(address, data).get(1, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
|
||||||
throw new MemoryAccessException("Couldn't patch", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
AddressSetView set = new AddressSet(address, address.add(data.length - 1));
|
|
||||||
TraceDisassembleCommand dis = new TraceDisassembleCommand(platform, address, set);
|
|
||||||
if (contextValue != null) {
|
|
||||||
dis.setInitialContext(contextValue);
|
|
||||||
}
|
|
||||||
dis.run(tool, view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TraceProgramView getView() {
|
protected TraceProgramView getView() {
|
||||||
|
|||||||
+34
-14
@@ -20,6 +20,7 @@ import java.util.concurrent.*;
|
|||||||
import ghidra.app.plugin.core.assembler.PatchDataAction;
|
import ghidra.app.plugin.core.assembler.PatchDataAction;
|
||||||
import ghidra.app.services.DebuggerControlService;
|
import ghidra.app.services.DebuggerControlService;
|
||||||
import ghidra.app.services.DebuggerControlService.StateEditor;
|
import ghidra.app.services.DebuggerControlService.StateEditor;
|
||||||
|
import ghidra.framework.cmd.BackgroundCommand;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressRange;
|
import ghidra.program.model.address.AddressRange;
|
||||||
import ghidra.program.model.listing.CodeUnit;
|
import ghidra.program.model.listing.CodeUnit;
|
||||||
@@ -27,6 +28,8 @@ import ghidra.program.model.mem.MemoryAccessException;
|
|||||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
import ghidra.trace.model.listing.TraceData;
|
import ghidra.trace.model.listing.TraceData;
|
||||||
import ghidra.trace.model.program.TraceProgramView;
|
import ghidra.trace.model.program.TraceProgramView;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class TracePatchDataAction extends PatchDataAction {
|
public class TracePatchDataAction extends PatchDataAction {
|
||||||
protected final DebuggerDisassemblerPlugin plugin;
|
protected final DebuggerDisassemblerPlugin plugin;
|
||||||
@@ -41,25 +44,42 @@ public class TracePatchDataAction extends PatchDataAction {
|
|||||||
return super.isApplicableToUnit(cu) && cu instanceof TraceData;
|
return super.isApplicableToUnit(cu) && cu instanceof TraceData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PatchDataCommand extends BackgroundCommand<TraceProgramView> {
|
||||||
|
private final byte[] encoded;
|
||||||
|
|
||||||
|
public PatchDataCommand(byte[] encoded) {
|
||||||
|
this.encoded = encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean applyTo(TraceProgramView view, TaskMonitor monitor) {
|
||||||
|
DebuggerControlService controlService = tool.getService(DebuggerControlService.class);
|
||||||
|
if (controlService == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
StateEditor editor = controlService.createStateEditor(view);
|
||||||
|
Address address = getAddress();
|
||||||
|
|
||||||
|
try {
|
||||||
|
editor.setVariable(address, encoded).get(1, TimeUnit.SECONDS);
|
||||||
|
// Let the trace do everything regarding existing units
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
|
setStatusMsg("Couldn't patch: " + e);
|
||||||
|
Msg.error(this, "Couldn't patch", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void applyPatch(AddressRange rng, byte[] encoded)
|
protected void applyPatch(AddressRange rng, byte[] encoded)
|
||||||
throws MemoryAccessException, CodeUnitInsertionException {
|
throws MemoryAccessException, CodeUnitInsertionException {
|
||||||
if (!(getProgram() instanceof TraceProgramView view)) {
|
if (!(getProgram() instanceof TraceProgramView view)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DebuggerControlService controlService = tool.getService(DebuggerControlService.class);
|
PatchDataCommand patch = new PatchDataCommand(encoded);
|
||||||
if (controlService == null) {
|
patch.run(tool, view);
|
||||||
return;
|
|
||||||
}
|
|
||||||
StateEditor editor = controlService.createStateEditor(view);
|
|
||||||
Address address = getAddress();
|
|
||||||
|
|
||||||
try {
|
|
||||||
editor.setVariable(address, encoded).get(1, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
catch (InterruptedException | ExecutionException | TimeoutException e) {
|
|
||||||
throw new MemoryAccessException("Couldn't patch", e);
|
|
||||||
}
|
|
||||||
// Let the trace do everything regarding existing units
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-4
@@ -466,15 +466,19 @@ public class DebuggerControlPluginTest extends AbstractGhidraHeadedDebuggerInteg
|
|||||||
|
|
||||||
assertTrue(
|
assertTrue(
|
||||||
helper.patchInstructionAction.isAddToPopup(listingProvider.getActionContext(null)));
|
helper.patchInstructionAction.isAddToPopup(listingProvider.getActionContext(null)));
|
||||||
|
long snapBefore = traceManager.getCurrent().getViewSnap();
|
||||||
Instruction ins =
|
Instruction ins =
|
||||||
helper.patchInstructionAt(tb.addr(0x00400123), "imm r0,#0x0", "imm r0,#0x3d2");
|
helper.patchInstructionAt(tb.addr(0x00400123), "imm r0,#0x0", "imm r0,#0x3d2");
|
||||||
assertEquals(2, ins.getLength());
|
assertEquals(2, ins.getLength());
|
||||||
|
|
||||||
|
waitForPass(() -> assertNotEquals(snapBefore, traceManager.getCurrent().getViewSnap()));
|
||||||
long snap = traceManager.getCurrent().getViewSnap();
|
long snap = traceManager.getCurrent().getViewSnap();
|
||||||
assertTrue(Lifespan.isScratch(snap));
|
assertTrue(Lifespan.isScratch(snap));
|
||||||
byte[] bytes = new byte[2];
|
byte[] bytes = new byte[2];
|
||||||
view.getMemory().getBytes(tb.addr(0x00400123), bytes);
|
waitForPass(noExc(() -> {
|
||||||
assertArrayEquals(tb.arr(0x30, 0xd2), bytes);
|
view.getMemory().getBytes(tb.addr(0x00400123), bytes);
|
||||||
|
assertArrayEquals(tb.arr(0x30, 0xd2), bytes);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -517,6 +521,7 @@ public class DebuggerControlPluginTest extends AbstractGhidraHeadedDebuggerInteg
|
|||||||
|
|
||||||
goTo(listingProvider.getListingPanel(), new ProgramLocation(view, tb.addr(0x00400123)));
|
goTo(listingProvider.getListingPanel(), new ProgramLocation(view, tb.addr(0x00400123)));
|
||||||
assertTrue(helper.patchDataAction.isAddToPopup(listingProvider.getActionContext(null)));
|
assertTrue(helper.patchDataAction.isAddToPopup(listingProvider.getActionContext(null)));
|
||||||
|
long snapBefore = traceManager.getCurrent().getViewSnap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: There's a bug in the trace forking: Data units are not replaced when bytes changed.
|
* TODO: There's a bug in the trace forking: Data units are not replaced when bytes changed.
|
||||||
@@ -525,11 +530,14 @@ public class DebuggerControlPluginTest extends AbstractGhidraHeadedDebuggerInteg
|
|||||||
/*Data data =*/ helper.patchDataAt(tb.addr(0x00400123), "0h", "5h");
|
/*Data data =*/ helper.patchDataAt(tb.addr(0x00400123), "0h", "5h");
|
||||||
// assertEquals(2, data.getLength());
|
// assertEquals(2, data.getLength());
|
||||||
|
|
||||||
|
waitForPass(() -> assertNotEquals(snapBefore, traceManager.getCurrent().getViewSnap()));
|
||||||
long snap = traceManager.getCurrent().getViewSnap();
|
long snap = traceManager.getCurrent().getViewSnap();
|
||||||
assertTrue(Lifespan.isScratch(snap));
|
assertTrue(Lifespan.isScratch(snap));
|
||||||
byte[] bytes = new byte[2];
|
byte[] bytes = new byte[2];
|
||||||
view.getMemory().getBytes(tb.addr(0x00400123), bytes);
|
waitForPass(noExc(() -> {
|
||||||
assertArrayEquals(tb.arr(0, 5), bytes);
|
view.getMemory().getBytes(tb.addr(0x00400123), bytes);
|
||||||
|
assertArrayEquals(tb.arr(0, 5), bytes);
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user