mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GP-3857: Port most Debugger components to TraceRmi.
This commit is contained in:
parent
7e4d2bcfaa
commit
fd4380c07a
222 changed files with 7241 additions and 3752 deletions
|
@ -0,0 +1,185 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.breakpoint;
|
||||
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import db.Transaction;
|
||||
import generic.Unique;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest.TestDebuggerTargetTraceMapper;
|
||||
import ghidra.app.plugin.core.debug.service.breakpoint.DebuggerLogicalBreakpointServicePlugin;
|
||||
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServiceProxyPlugin;
|
||||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
|
||||
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
|
||||
import ghidra.app.plugin.core.decompile.DecompilePlugin;
|
||||
import ghidra.app.plugin.core.decompile.DecompilerProvider;
|
||||
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.viewer.listingpanel.ListingPanel;
|
||||
import ghidra.dbg.model.TestDebuggerModelBuilder;
|
||||
import ghidra.debug.api.action.ActionSource;
|
||||
import ghidra.debug.api.breakpoint.LogicalBreakpoint;
|
||||
import ghidra.debug.api.breakpoint.LogicalBreakpoint.State;
|
||||
import ghidra.debug.api.model.TraceRecorder;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import help.screenshot.GhidraScreenShotGenerator;
|
||||
|
||||
public class DebuggerBreakpointMarkerPluginScreenShots extends GhidraScreenShotGenerator {
|
||||
private DebuggerModelService modelService;
|
||||
private DebuggerTraceManagerService traceManager;
|
||||
private DebuggerStaticMappingService mappingService;
|
||||
private DebuggerLogicalBreakpointService breakpointService;
|
||||
private DebuggerBreakpointMarkerPlugin breakpointMarkerPlugin;
|
||||
private ProgramManager programManager;
|
||||
|
||||
private TestDebuggerModelBuilder mb;
|
||||
|
||||
private CodeViewerProvider listing;
|
||||
|
||||
protected static Address addr(Program program, long offset) {
|
||||
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpMine() throws Exception {
|
||||
modelService = addPlugin(tool, DebuggerModelServiceProxyPlugin.class);
|
||||
traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class);
|
||||
mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class);
|
||||
breakpointService = addPlugin(tool, DebuggerLogicalBreakpointServicePlugin.class);
|
||||
breakpointMarkerPlugin = addPlugin(tool, DebuggerBreakpointMarkerPlugin.class);
|
||||
programManager = addPlugin(tool, ProgramManagerPlugin.class);
|
||||
|
||||
listing = waitForComponentProvider(CodeViewerProvider.class);
|
||||
|
||||
program = programManager.getCurrentProgram();
|
||||
|
||||
mb = new TestDebuggerModelBuilder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaptureDebuggerBreakpointMarkerPlugin() throws Throwable {
|
||||
ListingPanel panel = listing.getListingPanel();
|
||||
|
||||
mb.createTestModel();
|
||||
modelService.addModel(mb.testModel);
|
||||
mb.createTestProcessesAndThreads();
|
||||
TestDebuggerTargetTraceMapper mapper = new TestDebuggerTargetTraceMapper(mb.testProcess1);
|
||||
TraceRecorder recorder =
|
||||
modelService.recordTarget(mb.testProcess1, mapper, ActionSource.AUTOMATIC);
|
||||
Trace trace = recorder.getTrace();
|
||||
|
||||
traceManager.openTrace(trace);
|
||||
traceManager.activateTrace(trace);
|
||||
|
||||
tool.getProject()
|
||||
.getProjectData()
|
||||
.getRootFolder()
|
||||
.createFile("WinHelloCPP", program, TaskMonitor.DUMMY);
|
||||
|
||||
try (Transaction tx = trace.openTransaction("Add Mapping")) {
|
||||
mappingService.addIdentityMapping(trace, program, Lifespan.nowOn(0), true);
|
||||
}
|
||||
waitForValue(() -> mappingService.getOpenMappedLocation(
|
||||
new DefaultTraceLocation(trace, null, Lifespan.at(0), mb.addr(0x00401c60))));
|
||||
|
||||
Msg.debug(this, "Placing breakpoint");
|
||||
breakpointService.placeBreakpointAt(program, addr(program, 0x00401c60), 1,
|
||||
Set.of(TraceBreakpointKind.SW_EXECUTE), "");
|
||||
|
||||
Msg.debug(this, "Disabling breakpoint");
|
||||
LogicalBreakpoint lb = waitForValue(() -> Unique.assertAtMostOne(
|
||||
breakpointService.getBreakpointsAt(program, addr(program, 0x00401c60))));
|
||||
|
||||
lb.disable();
|
||||
waitForCondition(() -> lb.computeState() == State.DISABLED);
|
||||
|
||||
Msg.debug(this, "Placing another");
|
||||
breakpointService.placeBreakpointAt(program, addr(program, 0x00401c63), 1,
|
||||
Set.of(TraceBreakpointKind.SW_EXECUTE), "");
|
||||
|
||||
Msg.debug(this, "Saving program");
|
||||
program.save("Placed breakpoints", TaskMonitor.DUMMY);
|
||||
|
||||
Msg.debug(this, "Clicking and capturing");
|
||||
AbstractDebuggerBreakpointMarkerPluginTest.clickListing(panel, addr(program, 0x00401c66),
|
||||
MouseEvent.BUTTON3);
|
||||
waitForSwing();
|
||||
|
||||
captureProviderWithScreenShot(listing);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaptureDebuggerDecompilerBreakpointMargin() throws Throwable {
|
||||
mb.createTestModel();
|
||||
modelService.addModel(mb.testModel);
|
||||
mb.createTestProcessesAndThreads();
|
||||
TestDebuggerTargetTraceMapper mapper = new TestDebuggerTargetTraceMapper(mb.testProcess1);
|
||||
TraceRecorder recorder =
|
||||
modelService.recordTarget(mb.testProcess1, mapper, ActionSource.AUTOMATIC);
|
||||
Trace trace = recorder.getTrace();
|
||||
|
||||
traceManager.openTrace(trace);
|
||||
traceManager.activateTrace(trace);
|
||||
|
||||
tool.getProject()
|
||||
.getProjectData()
|
||||
.getRootFolder()
|
||||
.createFile("WinHelloCPP", program, TaskMonitor.DUMMY);
|
||||
|
||||
try (Transaction tx = trace.openTransaction("Add Mapping")) {
|
||||
mappingService.addIdentityMapping(trace, program, Lifespan.nowOn(0), true);
|
||||
}
|
||||
waitForValue(() -> mappingService.getOpenMappedLocation(
|
||||
new DefaultTraceLocation(trace, null, Lifespan.at(0), mb.addr(0x00401070))));
|
||||
|
||||
Msg.debug(this, "Placing breakpoint");
|
||||
breakpointService.placeBreakpointAt(program, addr(program, 0x00401070), 1,
|
||||
Set.of(TraceBreakpointKind.SW_EXECUTE), "");
|
||||
|
||||
addPlugin(tool, DecompilePlugin.class);
|
||||
|
||||
DecompilerProvider decompilerProvider = waitForComponentProvider(DecompilerProvider.class);
|
||||
Swing.runNow(() -> tool.showComponentProvider(decompilerProvider, true));
|
||||
goTo(tool, program, addr(program, 0x00401070));
|
||||
waitForCondition(() -> decompilerProvider.getDecompilerPanel().getLines().size() > 4);
|
||||
|
||||
captureIsolatedProvider(decompilerProvider, 500, 700);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaptureDebuggerPlaceBreakpointDialog() throws Throwable {
|
||||
runSwing(
|
||||
() -> listing.goTo(program, new ProgramLocation(program, addr(program, 0x00401c63))));
|
||||
performAction(breakpointMarkerPlugin.actionSetSoftwareBreakpoint, false);
|
||||
DebuggerPlaceBreakpointDialog dialog =
|
||||
waitForDialogComponent(DebuggerPlaceBreakpointDialog.class);
|
||||
|
||||
dialog.setName("After setup");
|
||||
captureDialog(dialog);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.breakpoint;
|
||||
|
||||
import static ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import db.Transaction;
|
||||
import generic.Unique;
|
||||
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerTest.TestDebuggerTargetTraceMapper;
|
||||
import ghidra.app.plugin.core.debug.service.breakpoint.DebuggerLogicalBreakpointServicePlugin;
|
||||
import ghidra.app.plugin.core.debug.service.model.DebuggerModelServiceProxyPlugin;
|
||||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
|
||||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils;
|
||||
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
|
||||
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.dbg.model.TestDebuggerModelBuilder;
|
||||
import ghidra.dbg.target.TargetBreakpointSpec.TargetBreakpointKind;
|
||||
import ghidra.dbg.target.TargetBreakpointSpecContainer;
|
||||
import ghidra.dbg.target.TargetTogglable;
|
||||
import ghidra.dbg.testutil.DebuggerModelTestUtils;
|
||||
import ghidra.debug.api.action.ActionSource;
|
||||
import ghidra.debug.api.breakpoint.LogicalBreakpoint;
|
||||
import ghidra.debug.api.model.TraceRecorder;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.test.ToyProgramBuilder;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import help.screenshot.GhidraScreenShotGenerator;
|
||||
|
||||
public class DebuggerBreakpointsPluginScreenShots extends GhidraScreenShotGenerator
|
||||
implements DebuggerModelTestUtils {
|
||||
|
||||
TestDebuggerModelBuilder mb = new TestDebuggerModelBuilder();
|
||||
DebuggerModelServiceProxyPlugin modelService;
|
||||
DebuggerStaticMappingService mappingService;
|
||||
DebuggerLogicalBreakpointService breakpointService;
|
||||
DebuggerTraceManagerService traceManager;
|
||||
ProgramManager programManager;
|
||||
|
||||
Program program;
|
||||
|
||||
protected static Address addr(Trace trace, long offset) {
|
||||
return trace.getBaseAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
}
|
||||
|
||||
protected static Address addr(Program program, long offset) {
|
||||
return program.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpMine() throws Exception {
|
||||
breakpointService = addPlugin(tool, DebuggerLogicalBreakpointServicePlugin.class);
|
||||
modelService = addPlugin(tool, DebuggerModelServiceProxyPlugin.class);
|
||||
mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class);
|
||||
traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class);
|
||||
programManager = addPlugin(tool, ProgramManagerPlugin.class);
|
||||
|
||||
program = createDefaultProgram("echo", ToyProgramBuilder._X64, this);
|
||||
waitForProgram(program);
|
||||
tool.getProject()
|
||||
.getProjectData()
|
||||
.getRootFolder()
|
||||
.createFile("echo", program, TaskMonitor.DUMMY);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDownMine() {
|
||||
Msg.debug(this, "Tearing down");
|
||||
Msg.debug(this, "Service breakpoints:");
|
||||
for (LogicalBreakpoint lb : breakpointService.getAllBreakpoints()) {
|
||||
Msg.debug(this, " bp: " + lb);
|
||||
}
|
||||
DebuggerBreakpointsProvider provider =
|
||||
waitForComponentProvider(DebuggerBreakpointsProvider.class);
|
||||
Msg.debug(this, "Provider breakpoints:");
|
||||
for (LogicalBreakpointRow row : provider.breakpointTableModel.getModelData()) {
|
||||
Msg.debug(this, " bp: " + row.getLogicalBreakpoint());
|
||||
}
|
||||
if (program != null) {
|
||||
program.release(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaptureDebuggerBreakpointsPlugin() throws Throwable {
|
||||
addPlugin(tool, DebuggerBreakpointsPlugin.class);
|
||||
DebuggerBreakpointsProvider provider =
|
||||
waitForComponentProvider(DebuggerBreakpointsProvider.class);
|
||||
|
||||
mb.createTestModel();
|
||||
modelService.addModel(mb.testModel);
|
||||
mb.createTestProcessesAndThreads();
|
||||
|
||||
TraceRecorder recorder1 = modelService.recordTarget(mb.testProcess1,
|
||||
new TestDebuggerTargetTraceMapper(mb.testProcess1), ActionSource.AUTOMATIC);
|
||||
TraceRecorder recorder3 = modelService.recordTarget(mb.testProcess3,
|
||||
new TestDebuggerTargetTraceMapper(mb.testProcess3), ActionSource.AUTOMATIC);
|
||||
Trace trace1 = recorder1.getTrace();
|
||||
Trace trace3 = recorder3.getTrace();
|
||||
|
||||
programManager.openProgram(program);
|
||||
traceManager.openTrace(trace1);
|
||||
traceManager.openTrace(trace3);
|
||||
|
||||
mb.testProcess1.addRegion("echo:.text", mb.rng(0x00400000, 0x00400fff), "rx");
|
||||
mb.testProcess1.addRegion("echo:.data", mb.rng(0x00600000, 0x00600fff), "rw");
|
||||
mb.testProcess3.addRegion("echo:.text", mb.rng(0x7fac0000, 0x7fac0fff), "rx");
|
||||
|
||||
try (Transaction tx = trace1.openTransaction("Add mapping")) {
|
||||
DebuggerStaticMappingUtils.addMapping(
|
||||
new DefaultTraceLocation(trace1, null, Lifespan.nowOn(0), addr(trace1, 0x00400000)),
|
||||
new ProgramLocation(program, addr(program, 0x00400000)), 0x00210000, false);
|
||||
}
|
||||
try (Transaction tx = trace3.openTransaction("Add mapping")) {
|
||||
DebuggerStaticMappingUtils.addMapping(
|
||||
new DefaultTraceLocation(trace3, null, Lifespan.nowOn(0), addr(trace3, 0x7fac0000)),
|
||||
new ProgramLocation(program, addr(program, 0x00400000)), 0x00010000, false);
|
||||
}
|
||||
waitForSwing();
|
||||
|
||||
try (Transaction tx = program.openTransaction("Add breakpoint")) {
|
||||
program.getBookmarkManager()
|
||||
.setBookmark(addr(program, 0x00401234), LogicalBreakpoint.ENABLED_BOOKMARK_TYPE,
|
||||
"SW_EXECUTE;1", "before connect");
|
||||
program.getBookmarkManager()
|
||||
.setBookmark(addr(program, 0x00604321), LogicalBreakpoint.ENABLED_BOOKMARK_TYPE,
|
||||
"WRITE;4", "write version");
|
||||
}
|
||||
|
||||
TargetBreakpointSpecContainer bc1 =
|
||||
waitFor(() -> Unique.assertAtMostOne(recorder1.collectBreakpointContainers(null)),
|
||||
"No container");
|
||||
waitOn(bc1.placeBreakpoint(mb.addr(0x00401234), Set.of(TargetBreakpointKind.SW_EXECUTE)));
|
||||
waitOn(bc1.placeBreakpoint(mb.rng(0x00604321, 0x00604324),
|
||||
Set.of(TargetBreakpointKind.WRITE)));
|
||||
TargetBreakpointSpecContainer bc3 =
|
||||
waitFor(() -> Unique.assertAtMostOne(recorder3.collectBreakpointContainers(null)),
|
||||
"No container");
|
||||
waitOn(bc3.placeBreakpoint(mb.addr(0x7fac1234), Set.of(TargetBreakpointKind.SW_EXECUTE)));
|
||||
TargetTogglable bp3 = (TargetTogglable) waitForValue(
|
||||
() -> Unique.assertAtMostOne(bc3.getCachedElements().values()));
|
||||
waitOn(bp3.disable());
|
||||
|
||||
TraceBreakpoint bpt = waitForValue(() -> Unique.assertAtMostOne(
|
||||
trace3.getBreakpointManager()
|
||||
.getBreakpointsAt(recorder3.getSnap(), addr(trace3, 0x7fac1234))));
|
||||
|
||||
waitForPass(() -> {
|
||||
Set<LogicalBreakpoint> allBreakpoints = breakpointService.getAllBreakpoints();
|
||||
assertEquals(2, allBreakpoints.size());
|
||||
});
|
||||
waitForPass(() -> {
|
||||
assertFalse(bpt.isEnabled(0));
|
||||
});
|
||||
/**
|
||||
* TODO: Might be necessary to debounce and wait for service callbacks to settle. Sometimes,
|
||||
* there are 3 for just a moment, and then additional callbacks mess things up.
|
||||
*/
|
||||
waitForPass(() -> {
|
||||
assertEquals(2, provider.breakpointTable.getRowCount());
|
||||
assertEquals(3, provider.locationTable.getRowCount());
|
||||
});
|
||||
|
||||
captureIsolatedProvider(provider, 600, 600);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidraclass.debugger.screenshot;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import db.Transaction;
|
||||
import ghidra.app.util.importer.AutoImporter;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.LoadResults;
|
||||
import ghidra.base.project.GhidraProject;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.task.ConsoleTaskMonitor;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class TutorialDebuggerMaintenance extends AbstractGhidraHeadedIntegrationTest {
|
||||
public static final TaskMonitor CONSOLE = new ConsoleTaskMonitor();
|
||||
|
||||
public PluginTool tool;
|
||||
public TestEnv env;
|
||||
public Program program;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Throwable {
|
||||
env = new TestEnv();
|
||||
|
||||
tool = env.launchDefaultTool();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Throwable {
|
||||
if (program != null) {
|
||||
program.release(this);
|
||||
program = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecreateTermminesGzf() throws Throwable {
|
||||
File termmines = Application.getModuleDataFile("TestResources", "termmines").getFile(false);
|
||||
LoadResults<Program> results = AutoImporter.importByUsingBestGuess(termmines,
|
||||
env.getProject(), "/", this, new MessageLog(), CONSOLE);
|
||||
program = results.getPrimaryDomainObject();
|
||||
try (Transaction tx = program.openTransaction("Analyze")) {
|
||||
program.setExecutablePath("/tmp/termmines");
|
||||
GhidraProject.analyze(program);
|
||||
}
|
||||
File dest = new File(termmines.getParentFile(), "termmines.gzf");
|
||||
dest.delete();
|
||||
program.saveToPackedFile(dest, CONSOLE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,819 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidraclass.debugger.screenshot;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.*;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import db.Transaction;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import generic.Unique;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.cmd.disassemble.DisassembleCommand;
|
||||
import ghidra.app.context.ProgramLocationActionContext;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.analysis.AutoAnalysisPlugin;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.action.*;
|
||||
import ghidra.app.plugin.core.debug.gui.breakpoint.DebuggerBreakpointsProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.console.DebuggerConsoleProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.copying.DebuggerCopyActionsPlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.copying.DebuggerCopyIntoProgramDialog;
|
||||
import ghidra.app.plugin.core.debug.gui.diff.DebuggerTraceViewDiffPlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.memory.DebuggerMemoryBytesProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.memory.DebuggerRegionsProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.modules.DebuggerModulesProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.modules.DebuggerStaticMappingProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.pcode.DebuggerPcodeStepperPlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.pcode.DebuggerPcodeStepperProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegistersProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.stack.DebuggerStackProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.stack.vars.VariableValueHoverPlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.target.DebuggerTargetsPlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.thread.DebuggerThreadsProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.time.DebuggerTimeProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.time.DebuggerTimeSelectionDialog;
|
||||
import ghidra.app.plugin.core.debug.gui.watch.DebuggerWatchesProvider;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationServicePlugin;
|
||||
import ghidra.app.plugin.core.debug.service.emulation.DebuggerEmulationServicePlugin.EmulateProgramAction;
|
||||
import ghidra.app.plugin.core.debug.service.model.DebuggerConnectDialog;
|
||||
import ghidra.app.plugin.core.debug.stack.StackUnwinderTest;
|
||||
import ghidra.app.plugin.core.debug.stack.StackUnwinderTest.HoverLocation;
|
||||
import ghidra.app.plugin.core.debug.stack.UnwindStackCommand;
|
||||
import ghidra.app.plugin.core.decompile.DecompilerProvider;
|
||||
import ghidra.app.script.GhidraState;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
|
||||
import ghidra.app.util.importer.AutoImporter;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.LoadResults;
|
||||
import ghidra.async.AsyncTestUtils;
|
||||
import ghidra.dbg.DebuggerModelFactory;
|
||||
import ghidra.dbg.target.TargetLauncher;
|
||||
import ghidra.dbg.target.TargetLauncher.TargetCmdLineLauncher;
|
||||
import ghidra.dbg.testutil.DummyProc;
|
||||
import ghidra.dbg.util.ConfigurableFactory.Property;
|
||||
import ghidra.debug.api.model.DebuggerProgramLaunchOffer;
|
||||
import ghidra.debug.api.model.DebuggerProgramLaunchOffer.*;
|
||||
import ghidra.debug.api.watch.WatchRow;
|
||||
import ghidra.debug.flatapi.FlatDebuggerAPI;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.TestApplicationUtils;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.plugintool.util.PluginUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.util.GhidraProgramUtilities;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
import ghidra.trace.model.modules.TraceSection;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.time.schedule.*;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.ConsoleTaskMonitor;
|
||||
import help.screenshot.GhidraScreenShotGenerator;
|
||||
|
||||
public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
implements AsyncTestUtils {
|
||||
protected static final String TUTORIAL_PATH =
|
||||
TestApplicationUtils.getInstallationDirectory() + "/GhidraDocs/GhidraClass/Debugger/";
|
||||
protected static final File TUTORIAL_DIR = new File(TUTORIAL_PATH);
|
||||
|
||||
protected static final String TERMMINES_PATH = "/tmp/termmines";
|
||||
|
||||
protected final ConsoleTaskMonitor monitor = new ConsoleTaskMonitor();
|
||||
|
||||
protected ProgramManager programManager;
|
||||
protected CodeViewerService staticListingService;
|
||||
protected DebuggerModelService modelService;
|
||||
|
||||
protected DebuggerModelFactory gdbFactory;
|
||||
|
||||
protected final FlatDebuggerAPI flatDbg = new FlatDebuggerAPI() {
|
||||
@Override
|
||||
public GhidraState getState() {
|
||||
Navigatable nav = staticListingService.getNavigatable();
|
||||
return new GhidraState(tool, env.getProject(),
|
||||
nav.getProgram(), nav.getLocation(), nav.getSelection(), nav.getHighlight());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected TestEnv newTestEnv() throws Exception {
|
||||
return new TestEnv("DebuggerCourse");
|
||||
}
|
||||
|
||||
// TODO: Propose this replace waitForProgram
|
||||
public static void waitForDomainObject(DomainObject object) {
|
||||
object.flushEvents();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
protected void intoProject(DomainObject obj) {
|
||||
waitForDomainObject(obj);
|
||||
DomainFolder rootFolder = tool.getProject()
|
||||
.getProjectData()
|
||||
.getRootFolder();
|
||||
waitForCondition(() -> {
|
||||
try {
|
||||
rootFolder.createFile(obj.getName(), obj, monitor);
|
||||
return true;
|
||||
}
|
||||
catch (InvalidNameException | CancelledException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// Usually "object is busy". Try again.
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareTool() {
|
||||
tool = env.launchTool("Debugger");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadProgram() throws Exception {
|
||||
loadProgram("termmines");
|
||||
try (Transaction tx = program.openTransaction("Set exe path")) {
|
||||
program.setExecutablePath(TERMMINES_PATH);
|
||||
}
|
||||
intoProject(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveOrDisplayImage(String name) {
|
||||
if (TUTORIAL_DIR.exists()) {
|
||||
TUTORIAL_DIR.mkdirs();
|
||||
}
|
||||
name = name.substring("test".length());
|
||||
finished(TUTORIAL_DIR, name + ".png");
|
||||
}
|
||||
|
||||
protected CodeViewerService getStaticListingService() {
|
||||
for (CodeViewerService viewer : tool.getServices(CodeViewerService.class)) {
|
||||
if (viewer instanceof DebuggerListingService) {
|
||||
continue;
|
||||
}
|
||||
return viewer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpDebugger() throws Throwable {
|
||||
ResourceFile termminesRsrc = Application.getModuleDataFile("TestResources", "termmines");
|
||||
File termmines = new File(TERMMINES_PATH);
|
||||
try {
|
||||
Files.copy(termminesRsrc.getFile(false).toPath(), termmines.toPath());
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
Msg.warn(this, "Could not update " + TERMMINES_PATH);
|
||||
}
|
||||
catch (FileAlreadyExistsException e) {
|
||||
Files.delete(termmines.toPath());
|
||||
Files.copy(termminesRsrc.getFile(false).toPath(), termmines.toPath());
|
||||
}
|
||||
termmines.setExecutable(true);
|
||||
|
||||
programManager = tool.getService(ProgramManager.class);
|
||||
staticListingService = getStaticListingService();
|
||||
|
||||
modelService = tool.getService(DebuggerModelService.class);
|
||||
gdbFactory = modelService.getModelFactories()
|
||||
.stream()
|
||||
.filter(f -> "gdb".equals(f.getBrief()))
|
||||
.findAny()
|
||||
.get();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Property<String> gdbPathProp =
|
||||
(Property<String>) gdbFactory.getOptions().get("GDB launch command");
|
||||
gdbPathProp.setValue(DummyProc.which("gdb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGettingStarted_ToolWSpecimen() {
|
||||
captureToolWindow(1920, 1080);
|
||||
}
|
||||
|
||||
protected void launchProgramInGdb(String extraArgs) throws Throwable {
|
||||
DebuggerProgramLaunchOffer offer = modelService.getProgramLaunchOffers(program)
|
||||
.filter(o -> "IN-VM GDB".equals(o.getConfigName()))
|
||||
.findFirst()
|
||||
.get();
|
||||
LaunchConfigurator config = new LaunchConfigurator() {
|
||||
@Override
|
||||
public Map<String, ?> configureLauncher(TargetLauncher launcher,
|
||||
Map<String, ?> arguments, RelPrompt relPrompt) {
|
||||
if (extraArgs.length() == 0) {
|
||||
return arguments;
|
||||
}
|
||||
Map<String, Object> adjusted = new HashMap<>(arguments);
|
||||
TargetCmdLineLauncher.PARAMETER_CMDLINE_ARGS.adjust(adjusted,
|
||||
c -> c + " " + extraArgs);
|
||||
return adjusted;
|
||||
}
|
||||
};
|
||||
LaunchResult result = waitOn(offer.launchProgram(monitor, PromptMode.NEVER, config));
|
||||
if (result.exception() != null) {
|
||||
throw result.exception();
|
||||
}
|
||||
}
|
||||
|
||||
protected void launchProgramInGdb() throws Throwable {
|
||||
launchProgramInGdb("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGettingStarted_DisassemblyAfterLaunch() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
|
||||
captureToolWindow(1920, 1080);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBreakpoints_EmptyAfterLaunch() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
|
||||
tool.setSize(1920, 1080);
|
||||
captureProvider(DebuggerBreakpointsProvider.class);
|
||||
}
|
||||
|
||||
protected void placeBreakpointsSRandRand() throws Throwable {
|
||||
assertTrue(flatDbg.execute("break srand"));
|
||||
assertTrue(flatDbg.execute("break rand"));
|
||||
waitForCondition(() -> flatDbg.getAllBreakpoints().size() == 2);
|
||||
}
|
||||
|
||||
protected void placeBreakpointsRand() throws Throwable {
|
||||
assertTrue(flatDbg.execute("break rand"));
|
||||
waitForCondition(() -> flatDbg.getAllBreakpoints().size() == 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBreakpoints_PopAfterSRandRand() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsSRandRand();
|
||||
|
||||
tool.setSize(1920, 1080);
|
||||
captureProvider(DebuggerBreakpointsProvider.class);
|
||||
}
|
||||
|
||||
protected Address navigateToBreakpoint(String comment) {
|
||||
TraceBreakpoint bp = flatDbg.getAllBreakpoints()
|
||||
.stream()
|
||||
.flatMap(l -> l.getTraceBreakpoints().stream())
|
||||
.filter(l -> comment.equals(l.getComment()))
|
||||
.findAny()
|
||||
.get();
|
||||
Address dynAddr = bp.getMinAddress();
|
||||
flatDbg.goToDynamic(dynAddr);
|
||||
return dynAddr;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBreakpoints_MissingModuleNote() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsSRandRand();
|
||||
navigateToBreakpoint("srand");
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerConsoleProvider.class);
|
||||
}
|
||||
|
||||
protected Program importModule(TraceModule module) throws Throwable {
|
||||
Program prog = null;
|
||||
try {
|
||||
MessageLog log = new MessageLog();
|
||||
LoadResults<Program> result = AutoImporter.importByUsingBestGuess(
|
||||
new File(module.getName()), env.getProject(), "/", this, log, monitor);
|
||||
result.save(env.getProject(), this, log, monitor);
|
||||
prog = result.getPrimaryDomainObject();
|
||||
GhidraProgramUtilities.markProgramNotToAskToAnalyze(prog);
|
||||
programManager.openProgram(prog);
|
||||
}
|
||||
finally {
|
||||
if (prog != null) {
|
||||
prog.release(this);
|
||||
}
|
||||
}
|
||||
return prog;
|
||||
}
|
||||
|
||||
protected void analyze(Program prog) {
|
||||
DockingActionIf actAutoAnalyze = Unique.assertOne(getActionsByOwnerAndName(tool,
|
||||
PluginUtils.getPluginNameFromClass(AutoAnalysisPlugin.class), "Auto Analyze"));
|
||||
performAction(actAutoAnalyze);
|
||||
}
|
||||
|
||||
protected TraceModule getModuleContaining(Address dynAddr) {
|
||||
return Unique.assertOne(flatDbg.getCurrentTrace()
|
||||
.getModuleManager()
|
||||
.getModulesAt(flatDbg.getCurrentSnap(), dynAddr));
|
||||
}
|
||||
|
||||
protected void disassembleSymbol(Program prog, String name) {
|
||||
for (Symbol sym : prog.getSymbolTable().getLabelOrFunctionSymbols(name, null)) {
|
||||
tool.executeBackgroundCommand(new DisassembleCommand(sym.getAddress(), null, true),
|
||||
prog);
|
||||
}
|
||||
waitForTasks(600 * 1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBreakpoints_SyncedAfterImportLibC() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsSRandRand();
|
||||
showProvider(DebuggerBreakpointsProvider.class);
|
||||
Address dynAddr = navigateToBreakpoint("srand");
|
||||
TraceModule modLibC = getModuleContaining(dynAddr);
|
||||
Program progLibC = importModule(modLibC);
|
||||
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
|
||||
disassembleSymbol(progLibC, "srand");
|
||||
// Just to be sure.
|
||||
goTo(tool, progLibC, flatDbg.translateDynamicToStatic(dynAddr));
|
||||
|
||||
captureToolWindow(1920, 1080);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBreakpoints_SeedValueAfterBreakSRand() throws Throwable {
|
||||
addPlugin(tool, VariableValueHoverPlugin.class);
|
||||
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsSRandRand();
|
||||
showProvider(DecompilerProvider.class);
|
||||
Address dynAddr = navigateToBreakpoint("srand");
|
||||
TraceModule modLibC = getModuleContaining(dynAddr);
|
||||
Program progLibC = importModule(modLibC);
|
||||
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
|
||||
disassembleSymbol(progLibC, "srand");
|
||||
Address stAddr = flatDbg.translateDynamicToStatic(dynAddr);
|
||||
// Just to be sure.
|
||||
goTo(tool, progLibC, stAddr);
|
||||
flatDbg.resume();
|
||||
|
||||
Function funSRand = progLibC.getFunctionManager().getFunctionAt(stAddr);
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
|
||||
DecompilerProvider dProvider = waitForComponentProvider(DecompilerProvider.class);
|
||||
DecompilerPanel dPanel = dProvider.getDecompilerPanel();
|
||||
HoverLocation loc = StackUnwinderTest.findTokenLocation(dPanel, funSRand, "param_1",
|
||||
"void srand(ulong param_1)");
|
||||
runSwing(() -> dPanel.goToToken(loc.token()));
|
||||
FieldPanel fieldPanel = dPanel.getFieldPanel();
|
||||
Rectangle rect = fieldPanel.getCursorBounds();
|
||||
MouseEvent event =
|
||||
new MouseEvent(fieldPanel, 0, System.currentTimeMillis(), 0, rect.x, rect.y, 0, false);
|
||||
fieldPanel.getHoverHandler().mouseHovered(event);
|
||||
waitForSwing();
|
||||
sleep(500); // Give time for GDB to respond async
|
||||
|
||||
captureProviderWithScreenShot(dProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testState_ListingAfterCallRand() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsRand();
|
||||
flatDbg.resume();
|
||||
flatDbg.stepOut();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerListingProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testState_ListingStackAfterCallRand() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsRand();
|
||||
flatDbg.resume();
|
||||
flatDbg.stepOut();
|
||||
|
||||
DebuggerListingService listingService = tool.getService(DebuggerListingService.class);
|
||||
listingService.setTrackingSpec(SPLocationTrackingSpec.INSTANCE);
|
||||
|
||||
sleep(1000);
|
||||
|
||||
tool.execute(new UnwindStackCommand(tool, flatDbg.getCurrentDebuggerCoordinates()),
|
||||
flatDbg.getCurrentTrace());
|
||||
waitForTasks();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerListingProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testState_BytesStackAfterCallRand() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsRand();
|
||||
flatDbg.resume();
|
||||
flatDbg.stepOut();
|
||||
|
||||
DebuggerMemoryBytesProvider bytesProvider = showProvider(DebuggerMemoryBytesProvider.class);
|
||||
bytesProvider.setTrackingSpec(SPLocationTrackingSpec.INSTANCE);
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerMemoryBytesProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testState_RegistersAfterCallRand() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsRand();
|
||||
flatDbg.resume();
|
||||
flatDbg.stepOut();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerRegistersProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testState_WatchesInCallSRand() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsSRandRand();
|
||||
flatDbg.resume();
|
||||
|
||||
DebuggerWatchesService watchesService = tool.getService(DebuggerWatchesService.class);
|
||||
watchesService.addWatch("RDI");
|
||||
WatchRow watchRetPtr = watchesService.addWatch("*:8 RSP");
|
||||
watchRetPtr.setDataType(
|
||||
new PointerTypedefBuilder(VoidDataType.dataType, 8, null).addressSpace("ram").build());
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerWatchesProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNavigation_ThreadsInCallRand() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsRand();
|
||||
flatDbg.resume();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerThreadsProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNavigation_StackInCallRand() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsRand();
|
||||
Address dynAddr = navigateToBreakpoint("rand");
|
||||
TraceModule modLibC = getModuleContaining(dynAddr);
|
||||
importModule(modLibC);
|
||||
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
|
||||
flatDbg.resume();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerStackProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNavigation_TimeAfterCallSRandCallRand() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsSRandRand();
|
||||
flatDbg.resume(); // srand
|
||||
flatDbg.resume(); // rand.1
|
||||
flatDbg.stepOut();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerTimeProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNavigation_DialogCompareTimes() throws Throwable {
|
||||
launchProgramInGdb(); // main
|
||||
placeBreakpointsRand();
|
||||
Address pc = flatDbg.getProgramCounter();
|
||||
long snapA = flatDbg.getCurrentSnap();
|
||||
TraceModule modTermmines = Unique.assertOne(flatDbg.getCurrentTrace()
|
||||
.getModuleManager()
|
||||
.getModulesAt(snapA, pc));
|
||||
TraceSection secTermminesData = modTermmines.getSectionByName(".data");
|
||||
flatDbg.readMemory(secTermminesData.getStart(),
|
||||
(int) secTermminesData.getRange().getLength(), monitor);
|
||||
|
||||
flatDbg.resume(); // rand.1
|
||||
flatDbg.readMemory(secTermminesData.getStart(),
|
||||
(int) secTermminesData.getRange().getLength(), monitor);
|
||||
|
||||
performAction("Compare",
|
||||
PluginUtils.getPluginNameFromClass(DebuggerTraceViewDiffPlugin.class), false);
|
||||
DebuggerTimeSelectionDialog timeDialog =
|
||||
waitForDialogComponent(DebuggerTimeSelectionDialog.class);
|
||||
timeDialog.setScheduleText(TraceSchedule.snap(snapA).toString());
|
||||
captureDialog(timeDialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNavigation_CompareTimes() throws Throwable {
|
||||
launchProgramInGdb("-M 15"); // main
|
||||
placeBreakpointsRand();
|
||||
Address pc = flatDbg.getProgramCounter();
|
||||
long snapA = flatDbg.getCurrentSnap();
|
||||
TraceModule modTermmines = Unique.assertOne(flatDbg.getCurrentTrace()
|
||||
.getModuleManager()
|
||||
.getModulesAt(snapA, pc));
|
||||
TraceSection secTermminesData = modTermmines.getSectionByName(".data");
|
||||
flatDbg.readMemory(secTermminesData.getStart(),
|
||||
(int) secTermminesData.getRange().getLength(), monitor);
|
||||
|
||||
flatDbg.resume(); // rand.1
|
||||
flatDbg.waitForBreak(1000, TimeUnit.MILLISECONDS);
|
||||
flatDbg.readMemory(secTermminesData.getStart(),
|
||||
(int) secTermminesData.getRange().getLength(), monitor);
|
||||
|
||||
performAction("Compare",
|
||||
PluginUtils.getPluginNameFromClass(DebuggerTraceViewDiffPlugin.class), false);
|
||||
DebuggerTimeSelectionDialog timeDialog =
|
||||
waitForDialogComponent(DebuggerTimeSelectionDialog.class);
|
||||
runSwing(() -> timeDialog.setScheduleText(TraceSchedule.snap(snapA).toString()));
|
||||
runSwing(() -> timeDialog.okCallback());
|
||||
|
||||
DockingActionIf actionNextDiff = waitForValue(() -> {
|
||||
try {
|
||||
return Unique.assertOne(getActionsByOwnerAndName(tool,
|
||||
PluginUtils.getPluginNameFromClass(DebuggerTraceViewDiffPlugin.class),
|
||||
"Next Difference"));
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
waitForCondition(() -> actionNextDiff.isEnabled());
|
||||
flatDbg.goToDynamic(secTermminesData.getStart());
|
||||
performAction(actionNextDiff);
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerListingProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMemoryMap_RegionsAfterLaunch() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerRegionsProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMemoryMap_ModulesAfterLaunch() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerModulesProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMemoryMap_StaticMappingAfterLaunch() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsSRandRand();
|
||||
showProvider(DebuggerStaticMappingProvider.class);
|
||||
Address dynAddr = navigateToBreakpoint("srand");
|
||||
TraceModule modLibC = getModuleContaining(dynAddr);
|
||||
importModule(modLibC);
|
||||
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerStaticMappingProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMemoryMap_CopyNcursesInto() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
TraceModule modNcurses = flatDbg.getCurrentTrace()
|
||||
.getModuleManager()
|
||||
.getAllModules()
|
||||
.stream()
|
||||
.filter(m -> m.getName().contains("ncurses"))
|
||||
.findAny()
|
||||
.get();
|
||||
DebuggerListingService listings = tool.getService(DebuggerListingService.class);
|
||||
runSwing(() -> listings
|
||||
.setCurrentSelection(new ProgramSelection(new AddressSet(modNcurses.getRange()))));
|
||||
performAction("Copy Into New Program",
|
||||
PluginUtils.getPluginNameFromClass(DebuggerCopyActionsPlugin.class), false);
|
||||
captureDialog(DebuggerCopyIntoProgramDialog.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoteTargets_GdbOverSsh() throws Throwable {
|
||||
performAction("Connect", PluginUtils.getPluginNameFromClass(DebuggerTargetsPlugin.class),
|
||||
false);
|
||||
DebuggerConnectDialog dialog = waitForDialogComponent(DebuggerConnectDialog.class);
|
||||
runSwing(() -> dialog.setFactoryByBrief("gdb via SSH"));
|
||||
|
||||
captureDialog(dialog);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoteTargets_Gadp() throws Throwable {
|
||||
performAction("Connect", PluginUtils.getPluginNameFromClass(DebuggerTargetsPlugin.class),
|
||||
false);
|
||||
DebuggerConnectDialog dialog = waitForDialogComponent(DebuggerConnectDialog.class);
|
||||
runSwing(() -> dialog.setFactoryByBrief("Ghidra debug agent (GADP)"));
|
||||
|
||||
captureDialog(dialog);
|
||||
}
|
||||
|
||||
protected Function findCommandLineParser() throws Throwable {
|
||||
for (Data data : program.getListing().getDefinedData(true)) {
|
||||
Object value = data.getValue();
|
||||
if (!(value instanceof String str) || !str.startsWith("Usage: ")) {
|
||||
continue;
|
||||
}
|
||||
for (Reference refToUsage : data.getReferenceIteratorTo()) {
|
||||
Address from = refToUsage.getFromAddress();
|
||||
Function function = program.getFunctionManager().getFunctionContaining(from);
|
||||
if (function != null) {
|
||||
return function;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new AssertionError("Cannot find command-line parsing function");
|
||||
}
|
||||
|
||||
protected CodeViewerProvider getCodeViewerProvider() {
|
||||
return (CodeViewerProvider) staticListingService.getNavigatable(); // HACK
|
||||
}
|
||||
|
||||
protected void goToStaticUntilContext(Address address) {
|
||||
CodeViewerProvider provider = getCodeViewerProvider();
|
||||
waitForCondition(() -> {
|
||||
goTo(tool, program, address);
|
||||
runSwing(() -> provider.contextChanged());
|
||||
return provider.getActionContext(null) instanceof ProgramLocationActionContext;
|
||||
});
|
||||
}
|
||||
|
||||
protected void emulateCommandLineParser() throws Throwable {
|
||||
Function function = findCommandLineParser();
|
||||
goToStaticUntilContext(function.getEntryPoint());
|
||||
performAction(EmulateProgramAction.NAME,
|
||||
PluginUtils.getPluginNameFromClass(DebuggerEmulationServicePlugin.class),
|
||||
getCodeViewerProvider(), true);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmulation_LazyStaleListing() throws Throwable {
|
||||
emulateCommandLineParser();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerListingProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmulation_ListingAfterResume() throws Throwable {
|
||||
emulateCommandLineParser();
|
||||
|
||||
DebuggerListingProvider listing = getProvider(DebuggerListingProvider.class);
|
||||
listing.setAutoReadMemorySpec(
|
||||
AutoReadMemorySpec.fromConfigName(LoadEmulatorAutoReadMemorySpec.CONFIG_NAME));
|
||||
EmulationResult result = flatDbg.getEmulationService()
|
||||
.run(flatDbg.getCurrentPlatform(), flatDbg.getCurrentEmulationSchedule(), monitor,
|
||||
Scheduler.oneThread(flatDbg.getCurrentThread()));
|
||||
flatDbg.getTraceManager().activateTime(result.schedule());
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerListingProvider.class);
|
||||
}
|
||||
|
||||
protected void addWatchesForCmdline() throws Throwable {
|
||||
DebuggerWatchesService watchesService = tool.getService(DebuggerWatchesService.class);
|
||||
watchesService.addWatch("RSP");
|
||||
watchesService.addWatch("RDI");
|
||||
watchesService.addWatch("RSI");
|
||||
|
||||
watchesService.addWatch("*:8 (RSI + 0)");
|
||||
watchesService.addWatch("*:8 (RSI + 8)");
|
||||
watchesService.addWatch("*:8 (RSI + 16)");
|
||||
|
||||
watchesService.addWatch("*:30 (*:8 (RSI + 0))")
|
||||
.setDataType(TerminatedStringDataType.dataType);
|
||||
watchesService.addWatch("*:30 (*:8 (RSI + 8))")
|
||||
.setDataType(TerminatedStringDataType.dataType);
|
||||
watchesService.addWatch("*:30 (*:8 (RSI + 16))")
|
||||
.setDataType(TerminatedStringDataType.dataType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmulation_WatchesForCmdline() throws Throwable {
|
||||
emulateCommandLineParser();
|
||||
addWatchesForCmdline();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerWatchesProvider.class);
|
||||
}
|
||||
|
||||
protected void activateCmdlinePatchedSchedule() throws Throwable {
|
||||
TracePlatform platform = flatDbg.getCurrentPlatform();
|
||||
Address forArgv0 = platform.getAddressFactory().getAddress("00001018");
|
||||
Address forArgv1 = forArgv0.add("termmines\0".length());
|
||||
Address forArgv2 = forArgv1.add("-s\0".length());
|
||||
List<String> sleigh = new ArrayList<>();
|
||||
sleigh.add("RDI=3");
|
||||
sleigh.add("RSI=0x1000");
|
||||
sleigh.addAll(PatchStep.generateSleigh(platform.getLanguage(),
|
||||
forArgv0, "termmines\0".getBytes()));
|
||||
sleigh.addAll(PatchStep.generateSleigh(platform.getLanguage(),
|
||||
forArgv1, "-s\0".getBytes()));
|
||||
sleigh.addAll(PatchStep.generateSleigh(platform.getLanguage(),
|
||||
forArgv2, "Advanced\0".getBytes()));
|
||||
sleigh.add("*:8 (RSI + 0) = 0x" + forArgv0);
|
||||
sleigh.add("*:8 (RSI + 8) = 0x" + forArgv1);
|
||||
sleigh.add("*:8 (RSI + 16) = 0x" + forArgv2);
|
||||
TraceSchedule schedule = flatDbg.getCurrentEmulationSchedule();
|
||||
schedule = schedule.patched(flatDbg.getCurrentThread(), platform.getLanguage(), sleigh);
|
||||
|
||||
flatDbg.getTraceManager().activateTime(schedule);
|
||||
getProvider(DebuggerWatchesProvider.class).waitEvaluate(1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmulation_WatchesForCmdlineSet() throws Throwable {
|
||||
emulateCommandLineParser();
|
||||
addWatchesForCmdline();
|
||||
activateCmdlinePatchedSchedule();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerWatchesProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmulation_ListingForCmdlineSet() throws Throwable {
|
||||
emulateCommandLineParser();
|
||||
activateCmdlinePatchedSchedule();
|
||||
|
||||
Address addrArgv = flatDbg.getCurrentPlatform().getAddressFactory().getAddress("00001000");
|
||||
|
||||
TraceProgramView view = flatDbg.getCurrentView();
|
||||
waitForCondition(() -> view.getSnap() != 0);
|
||||
try (Transaction tx = view.openTransaction("Place units")) {
|
||||
Listing listing = view.getListing();
|
||||
Data datArgv =
|
||||
listing.createData(addrArgv, new ArrayDataType(PointerDataType.dataType, 3, 8));
|
||||
Address forArgv0 = (Address) datArgv.getComponent(0).getValue();
|
||||
Address forArgv1 = (Address) datArgv.getComponent(1).getValue();
|
||||
Address forArgv2 = (Address) datArgv.getComponent(2).getValue();
|
||||
|
||||
listing.createData(forArgv0, TerminatedStringDataType.dataType);
|
||||
listing.createData(forArgv1, TerminatedStringDataType.dataType);
|
||||
listing.createData(forArgv2, TerminatedStringDataType.dataType);
|
||||
}
|
||||
flatDbg.goToDynamic("00001010");
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerListingProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmulation_PcodeStepper() throws Throwable {
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
addPlugin(tool, DebuggerPcodeStepperPlugin.class);
|
||||
emulateCommandLineParser();
|
||||
flatDbg.stepEmuPcodeOp(1, monitor);
|
||||
|
||||
captureProvider(DebuggerPcodeStepperProvider.class);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue