GP-2641: Add training course materials for the Debugger

This commit is contained in:
Dan 2023-03-21 09:59:48 -04:00
parent d19bf87a60
commit c5da826540
107 changed files with 12176 additions and 75 deletions

View file

@ -89,7 +89,7 @@ import ghidra.util.task.TaskMonitor;
public class DebuggerEmulationServicePlugin extends Plugin implements DebuggerEmulationService {
protected static final int MAX_CACHE_SIZE = 5;
interface EmulateProgramAction {
public interface EmulateProgramAction {
String NAME = "Emulate Program in new Trace";
String DESCRIPTION = "Emulate the current program in a new trace starting at the cursor";
Icon ICON = DebuggerResources.ICON_EMULATE;

View file

@ -305,6 +305,21 @@ public class DebuggerConnectDialog extends ReusableDialogComponentProvider
super.cancelCallback();
}
/**
* For testing and documentation purposes only!
*/
public synchronized void setFactoryByBrief(String brief) {
synchronized (factories) {
for (FactoryEntry fe : factories.values()) {
if (Objects.equals(brief, fe.factory.getBrief())) {
dropdownModel.setSelectedItem(fe);
return;
}
}
throw new AssertionError();
}
}
protected synchronized CompletableFuture<DebuggerObjectModel> reset(
DebuggerModelFactory factory, Program program) {
if (factory != null) {

View file

@ -21,6 +21,7 @@ import java.net.ConnectException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import docking.ActionContext;
import docking.action.DockingAction;
@ -491,9 +492,8 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
return false;
}
List<DomainFile> toOpen = Arrays.asList(data)
.stream()
.filter(f -> Trace.class.isAssignableFrom(f.getDomainObjectClass()))
List<DomainFile> toOpen = Stream.of(data)
.filter(f -> f != null && Trace.class.isAssignableFrom(f.getDomainObjectClass()))
.collect(Collectors.toList());
Collection<Trace> openTraces = openTraces(toOpen);

View file

@ -1140,7 +1140,7 @@ public interface FlatDebuggerAPI {
Language language = platform.getLanguage();
RegisterValue value = readRegister(platform, requireThread(coordinates.getThread()),
coordinates.getFrame(), coordinates.getSnap(), language.getProgramCounter());
if (!value.hasValue()) {
if (value == null || !value.hasValue()) {
return null;
}
return language.getDefaultSpace().getAddress(value.getUnsignedValue().longValue());

View file

@ -559,7 +559,7 @@ public abstract class AbstractGhidraHeadedDebuggerGUITest
}
}
};
protected ConsoleTaskMonitor monitor = new ConsoleTaskMonitor();
protected final ConsoleTaskMonitor monitor = new ConsoleTaskMonitor();
protected void waitRecorder(TraceRecorder recorder) throws Throwable {
if (recorder == null) {

View file

@ -15,18 +15,13 @@
*/
package ghidra.app.plugin.core.debug.service.model;
import static org.junit.Assert.*;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import generic.Unique;
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOffer;
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOpinion;
import ghidra.app.services.DebuggerModelService;
import ghidra.dbg.DebuggerModelFactory;
import ghidra.dbg.model.TestDebuggerModelFactory;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.Program;
import ghidra.util.task.TaskMonitor;
@ -60,8 +55,8 @@ public class TestDebuggerProgramLaunchOpinion implements DebuggerProgramLaunchOp
@Override
public Collection<DebuggerProgramLaunchOffer> getOffers(Program program, PluginTool tool,
DebuggerModelService service) {
DebuggerModelFactory factory = Unique.assertOne(service.getModelFactories());
assertEquals(TestDebuggerModelFactory.class, factory.getClass());
//DebuggerModelFactory factory = Unique.assertOne(service.getModelFactories());
//assertEquals(TestDebuggerModelFactory.class, factory.getClass());
return List.of(new TestDebuggerProgramLaunchOffer());
}

View file

@ -17,8 +17,8 @@ package ghidra.dbg.target;
import java.lang.annotation.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@ -32,9 +32,6 @@ import ghidra.dbg.DebuggerTargetObjectIface;
import ghidra.dbg.agent.AbstractDebuggerObjectModel;
import ghidra.dbg.agent.DefaultTargetObject;
import ghidra.dbg.error.DebuggerIllegalArgumentException;
import ghidra.dbg.target.TargetMethod.*;
import ghidra.dbg.target.TargetMethod.TargetParameterMap.EmptyTargetParameterMap;
import ghidra.dbg.target.TargetMethod.TargetParameterMap.ImmutableTargetParameterMap;
import ghidra.dbg.target.schema.TargetAttributeType;
import ghidra.dbg.util.CollectionUtils.AbstractEmptyMap;
import ghidra.dbg.util.CollectionUtils.AbstractNMap;
@ -448,6 +445,28 @@ public interface TargetMethod extends TargetObject {
return defaultValue;
}
/**
* Set the argument for this parameter
*
* @param arguments the arguments to modify
* @param value the value to assign the parameter
*/
public void set(Map<String, ? super T> arguments, T value) {
arguments.put(name, value);
}
/**
* Adjust the argument for this parameter
*
* @param arguments the arguments to modify
* @param adjuster a function of the old argument to the new argument. If the argument is
* not currently set, the function will receive null.
*/
@SuppressWarnings("unchecked")
public void adjust(Map<String, ? super T> arguments, Function<T, T> adjuster) {
arguments.put(name, adjuster.apply((T) arguments.get(name)));
}
@Override
public String toString() {
return String.format(

View file

@ -65,7 +65,7 @@ public interface Step extends Comparable<Step> {
*
* <p>
* The form of the spec must either be numeric, indicating some number of ticks, or
* brace-enclosed Sleigh code, e.g., {@code "{r0=0x1234;}"}. The latter allows patching machine
* brace-enclosed Sleigh code, e.g., {@code "{r0=0x1234}"}. The latter allows patching machine
* state during execution.
*
* @param threadKey the thread to step, or -1 for the last thread or event thread

View file

@ -121,10 +121,11 @@ public abstract class AbstractTaintPcodeExecutorStatePiece<S extends TaintSpace>
}
@Override
protected Map<Register, TaintVec> getRegisterValuesFromSpace(S space, List<Register> registers) {
protected Map<Register, TaintVec> getRegisterValuesFromSpace(S space,
List<Register> registers) {
return space.getRegisterValues(registers);
}
@Override
public void clear() {
for (S space : spaceMap.values()) {

View file

@ -113,9 +113,13 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
setDockIcon();
}
protected TestEnv newTestEnv() throws Exception {
return new TestEnv();
}
@Before
public void setUp() throws Exception {
env = new TestEnv();
env = newTestEnv();
prepareTool();
@ -285,8 +289,8 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
}
/**
* The same as {@link GhidraScreenShotGenerator#captureIsolatedProvider(Class, int, int)}
* except this method will also capture the containing window.
* The same as {@link GhidraScreenShotGenerator#captureIsolatedProvider(Class, int, int)} except
* this method will also capture the containing window.
*
* @param clazz the provider class
* @param width the width of the capture
@ -465,9 +469,9 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
}
/**
* Captures the provider by using a screen shot and not by painting the provider directly
* (as does {@link #captureProvider(ComponentProvider)}). Use this method if you need to
* capture the provider along with any popup windows.
* Captures the provider by using a screen shot and not by painting the provider directly (as
* does {@link #captureProvider(ComponentProvider)}). Use this method if you need to capture the
* provider along with any popup windows.
*
* @param provider the provider
*/
@ -488,13 +492,14 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
}
/**
* Captures the window, including decorations. This will use a {@link Robot} to create a
* screen capture, which has the effect of getting all items within the window bounds. This
* method is needed if you wish to capture child windows, like popups/hovers.
* Captures the window, including decorations. This will use a {@link Robot} to create a screen
* capture, which has the effect of getting all items within the window bounds. This method is
* needed if you wish to capture child windows, like popups/hovers.
*
* <P>Other capture methods will not use the screen capture mechanism, but rather will
* directly render the given component. In this case, subordinate windows will not be
* captured. For example, see {@link #captureProvider(Class)}.
* <P>
* Other capture methods will not use the screen capture mechanism, but rather will directly
* render the given component. In this case, subordinate windows will not be captured. For
* example, see {@link #captureProvider(Class)}.
*
* @param name the provider's name
*/
@ -505,13 +510,14 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
}
/**
* Captures the window, including decorations. This will use a {@link Robot} to create a
* screen capture, which has the effect of getting all items within the window bounds. This
* method is needed if you wish to capture child windows, like popups/hovers.
* Captures the window, including decorations. This will use a {@link Robot} to create a screen
* capture, which has the effect of getting all items within the window bounds. This method is
* needed if you wish to capture child windows, like popups/hovers.
*
* <P>Other capture methods will not use the screen capture mechanism, but rather will
* directly render the given component. In this case, subordinate windows will not be
* captured. For example, see {@link #captureProvider(Class)}.
* <P>
* Other capture methods will not use the screen capture mechanism, but rather will directly
* render the given component. In this case, subordinate windows will not be captured. For
* example, see {@link #captureProvider(Class)}.
*
* @param clazz the provider's class
*/
@ -521,13 +527,14 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
}
/**
* Captures the window, including decorations. This will use a {@link Robot} to create a
* screen capture, which has the effect of getting all items within the window bounds. This
* method is needed if you wish to capture child windows, like popups/hovers.
* Captures the window, including decorations. This will use a {@link Robot} to create a screen
* capture, which has the effect of getting all items within the window bounds. This method is
* needed if you wish to capture child windows, like popups/hovers.
*
* <P>Other capture methods will not use the screen capture mechanism, but rather will
* directly render the given component. In this case, subordinate windows will not be
* captured. For example, see {@link #captureProvider(Class)}.
* <P>
* Other capture methods will not use the screen capture mechanism, but rather will directly
* render the given component. In this case, subordinate windows will not be captured. For
* example, see {@link #captureProvider(Class)}.
*
* @param provider the provider
*/
@ -537,13 +544,14 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
}
/**
* Captures the window, including decorations. This will use a {@link Robot} to create a
* screen capture, which has the effect of getting all items within the window bounds. This
* method is needed if you wish to capture child windows, like popups/hovers.
* Captures the window, including decorations. This will use a {@link Robot} to create a screen
* capture, which has the effect of getting all items within the window bounds. This method is
* needed if you wish to capture child windows, like popups/hovers.
*
* <P>Other capture methods will not use the screen capture mechanism, but rather will
* directly render the given component. In this case, subordinate windows will not be
* captured. For example, see {@link #captureProvider(Class)}.
* <P>
* Other capture methods will not use the screen capture mechanism, but rather will directly
* render the given component. In this case, subordinate windows will not be captured. For
* example, see {@link #captureProvider(Class)}.
*
* @param name the provider's name
* @param width the desired width
@ -558,13 +566,14 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
}
/**
* Captures the window, including decorations. This will use a {@link Robot} to create a
* screen capture, which has the effect of getting all items within the window bounds. This
* method is needed if you wish to capture child windows, like popups/hovers.
* Captures the window, including decorations. This will use a {@link Robot} to create a screen
* capture, which has the effect of getting all items within the window bounds. This method is
* needed if you wish to capture child windows, like popups/hovers.
*
* <P>Other capture methods will not use the screen capture mechanism, but rather will
* directly render the given component. In this case, subordinate windows will not be
* captured. For example, see {@link #captureProvider(Class)}.
* <P>
* Other capture methods will not use the screen capture mechanism, but rather will directly
* render the given component. In this case, subordinate windows will not be captured. For
* example, see {@link #captureProvider(Class)}.
*
* @param provider the provider's name
* @param width the desired width
@ -1387,19 +1396,19 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
}
/**
* Draws a rectangle around the given component. The root parameter is used to calculate
* screen coordinates. This allows you to capture a sub-component of a UI, drawing
* rectangles around children of said sub-component.
* Draws a rectangle around the given component. The root parameter is used to calculate screen
* coordinates. This allows you to capture a sub-component of a UI, drawing rectangles around
* children of said sub-component.
*
* <P>If you are unsure of what to pass for <code>root</code>, the call
* <P>
* If you are unsure of what to pass for <code>root</code>, the call
* {@link #drawRectangleAround(JComponent, Color, int)} instead.
*
* @param component the component to be en-rectangled
* @param root the outermost container widget being displayed; null implies a
* top-level parent
* @param root the outermost container widget being displayed; null implies a top-level parent
* @param color the rectangle color
* @param padding the space between the rectangle and the component; more space makes
* the component more visible
* @param padding the space between the rectangle and the component; more space makes the
* component more visible
* @return the bounds of the drawn rectangle
*/
public Rectangle drawRectangleAround(JComponent component, JComponent root, Color color,
@ -1586,9 +1595,9 @@ public abstract class AbstractScreenShotGenerator extends AbstractGhidraHeadedIn
}
/**
* Crops a part of the current image, keeping what is inside the given bounds. This method
* creates a shape such that the top and bottom of the cropped image have a jagged line,
* looking somewhat like a sideways lightening bolt.
* Crops a part of the current image, keeping what is inside the given bounds. This method
* creates a shape such that the top and bottom of the cropped image have a jagged line, looking
* somewhat like a sideways lightening bolt.
*
* @param bounds the bounds to keep
* @return the snippet

View file

@ -77,8 +77,6 @@ public class EmuDeskCheckScript extends GhidraScript implements FlatDebuggerAPI
* the syscall stuff does, then we need to allow injection of the already-compiled Sleigh
* program. For now, we'll have to declare the parameter-holding register as a language
* variable.
*
* @param s
*/
@StructuredUserop
public void strlen(/*@Param(name = "RDI", type = "char *") Var s*/) {

View file

@ -15,7 +15,8 @@
*/
package generic.test;
import static org.junit.Assert.*;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import java.awt.*;
import java.awt.event.*;
@ -37,7 +38,6 @@ import org.junit.Assert;
import ghidra.util.*;
import ghidra.util.datastruct.WeakSet;
import ghidra.util.exception.AssertException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.AbstractSwingUpdateManager;
import ghidra.util.task.SwingUpdateManager;
import junit.framework.AssertionFailedError;
@ -91,10 +91,10 @@ public class AbstractGuiTest extends AbstractGenericTest {
* for tasks
*/
public static void waitForTasks() {
doWaitForTasks(PRIVATE_LONG_WAIT_TIMEOUT);
waitForTasks(PRIVATE_LONG_WAIT_TIMEOUT);
}
private static void doWaitForTasks(long timeout) {
public static void waitForTasks(long timeout) {
waitForSwing();
long time = 0;
@ -110,7 +110,6 @@ public class AbstractGuiTest extends AbstractGenericTest {
waitForSwing();
}
/**
* @deprecated Use {@link #waitForSwing()} instead
*/

View file

@ -48,6 +48,7 @@ dependencies {
if (projectPath.startsWith(ghidraPath) && (
projectPath.contains("/Framework/") ||
projectPath.contains("/Features/") ||
projectPath.contains("/Debug/") ||
projectPath.contains("/Processors/"))) {
api p

View file

@ -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);
}
}

View file

@ -0,0 +1,837 @@
/* ###
* 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 java.util.stream.Collectors;
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.gui.watch.WatchRow;
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.service.model.launch.DebuggerProgramLaunchOffer;
import ghidra.app.plugin.core.debug.service.model.launch.DebuggerProgramLaunchOffer.*;
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.debug.workflow.*;
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.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 DebuggerWorkflowService workflowService;
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 void enableBot(Class<? extends DebuggerBot> botCls) {
Set<DebuggerBot> bots = workflowService.getAllBots()
.stream()
.filter(b -> botCls.isInstance(b))
.map(b -> botCls.cast(b))
.collect(Collectors.toSet());
workflowService.enableBots(bots);
}
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();
// This is a front-end plugin. We have to configure it.
workflowService = tool.getService(DebuggerWorkflowService.class);
enableBot(MapModulesDebuggerBot.class);
enableBot(DisassembleAtPcDebuggerBot.class);
enableBot(ShowInterpreterDebuggerBot.class);
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);
}
}

View file

@ -0,0 +1,478 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Ghidra Debugger</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="style.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="nav"><a
class="beginner" href="A1-GettingStarted.html">Getting Started</a><a
class="beginner" href="A2-UITour.html">UI Tour</a><a
class="beginner" href="A3-Breakpoints.html">Breakpoints</a><a
class="beginner" href="A4-MachineState.html">Machine State</a><a
class="beginner" href="A5-Navigation.html">Navigation</a><a
class="beginner" href="A6-MemoryMap.html">Memory Map</a><a
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a>
</header>
<header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#getting-started" id="toc-getting-started">Getting
Started</a>
<ul>
<li><a href="#the-specimen" id="toc-the-specimen">The specimen</a></li>
<li><a href="#launching-on-linux" id="toc-launching-on-linux">Launching
on Linux</a></li>
<li><a href="#launching-on-windows"
id="toc-launching-on-windows">Launching on Windows</a></li>
<li><a href="#launching-on-macos" id="toc-launching-on-macos">Launching
on macOS</a></li>
<li><a href="#troubleshooting"
id="toc-troubleshooting">Troubleshooting</a>
<ul>
<li><a href="#im-having-trouble-importing-termmines"
id="toc-im-having-trouble-importing-termmines">Im having trouble
importing <code>termmines</code></a></li>
<li><a href="#there-is-no-debugger-icon-in-my-tool-chest"
id="toc-there-is-no-debugger-icon-in-my-tool-chest">There is no Debugger
icon in my Tool Chest</a></li>
<li><a href="#there-is-no-debug-launch-icon-in-the-global-toolbar"
id="toc-there-is-no-debug-launch-icon-in-the-global-toolbar">There is no
Debug / Launch icon in the global toolbar</a></li>
<li><a
href="#there-is-no-debug-termmines-in-gdb-locally-in-vm-option-in-the-launch-drop-down"
id="toc-there-is-no-debug-termmines-in-gdb-locally-in-vm-option-in-the-launch-drop-down">There
is no “Debug termmines in GDB locally IN-VM” option in the launch
drop-down</a></li>
<li><a
href="#the-launch-hangs-for-several-seconds-and-then-prompt-for-a-recorder"
id="toc-the-launch-hangs-for-several-seconds-and-then-prompt-for-a-recorder">The
launch hangs for several seconds and then prompt for a
“recorder”</a></li>
<li><a href="#the-dynamic-listing-is-empty"
id="toc-the-dynamic-listing-is-empty">The Dynamic Listing is
empty</a></li>
<li><a
href="#the-listings-are-in-sync-but-the-dynamic-listing-is-grey-00s"
id="toc-the-listings-are-in-sync-but-the-dynamic-listing-is-grey-00s">The
listings are in sync, but the Dynamic Listing is grey 00s</a></li>
</ul></li>
<li><a href="#exercise-launch-termmines"
id="toc-exercise-launch-termmines">Exercise: Launch
<code>termmines</code></a></li>
<li><a href="#customized-launching"
id="toc-customized-launching">Customized Launching</a></li>
<li><a href="#exercise-launch-with-command-line-help"
id="toc-exercise-launch-with-command-line-help">Exercise: Launch with
Command-line Help</a></li>
<li><a href="#attaching" id="toc-attaching">Attaching</a></li>
<li><a href="#exercise-attach" id="toc-exercise-attach">Exercise:
Attach</a></li>
<li><a href="#troubleshooting-1"
id="toc-troubleshooting-1">Troubleshooting</a></li>
</ul></li>
</ul>
</nav>
<section id="getting-started" class="level1">
<h1>Getting Started</h1>
<p>This course assumes you are already familiar with the basics of using
Ghidra, including its static analysis features. To some degree, static
analysis is an integral part of debugging with Ghidra.</p>
<section id="the-specimen" class="level2">
<h2>The specimen</h2>
<p>Throughout this course, we will examine the provided “Terminal
Minesweeper” specimen, named <code>termmines</code>. If the compiled
artifact has not been provided for you, you may build it from source
using the provided <a
href="../ExerciseFiles/Debugger/Makefile">Makefile</a>, but you will
need <code>ncurses.h</code> first:</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="bu">cd</span> GhidraClass/ExerciseFiles/Debugger</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="fu">make</span></span></code></pre></div>
<p>The specimen is designed for Linux, but should be trivially portable
to other Unix systems. You will need <code>ncurses</code> and its
development headers and libraries available on your system. Though
source code for the specimen is available, we strongly encourage you to
work on the course exercises without referring to it. Symbols and debug
information are removed from the binary. With some effort,
<code>termmines</code> may even port to Windows; however, we have not
tested this course on Windows.</p>
<p>It is a good idea to get acquainted with the specimen. In general,
you should take precautions before running code you do not understand or
trust. For <code>termmines</code>, the risk is negligible. Run it:</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">./termmines</span></span></code></pre></div>
<p>You should see a 9x9 grid and a cursor you can move with the arrow
keys. Hit <strong>Ctrl-C</strong> to exit. Probe it for help. Most Linux
programs accept a <code>-h</code> argument for help:</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">./termmines</span> <span class="at">-h</span></span></code></pre></div>
<p>You should now have all the information you need to understand how
the game works. If you have never played Minesweeper before, read up
online, and perhaps try playing a couple of games. Dont get distracted,
though.</p>
</section>
<section id="launching-on-linux" class="level2">
<h2>Launching on Linux</h2>
<p>On Linux, we will use GDB to debug the specimen. There are many ways
to do this, but for the sake of simplicity, import and launch as
follows:</p>
<ol type="1">
<li><p>Import <code>termmines</code> into a new Ghidra project.</p></li>
<li><p>If you have a CodeBrowser open, close it and return to the main
Ghidra project window.</p></li>
<li><p>Drag <code>termmines</code> onto the Debugger <img
src="images/debugger.png" alt="debugger icon" /> in the Tool
Chest.</p></li>
<li><p>This will bring up the specimen in the Debugger tool. (If you are
prompted to analyze, choose Yes.)</p>
<figure>
<img src="images/GettingStarted_ToolWSpecimen.png"
alt="Debugger tool with termmines open" />
<figcaption aria-hidden="true">Debugger tool with termmines
open</figcaption>
</figure></li>
<li><p>In the Debugger tool, click the dropdown ▾ for the debug <img
src="images/debugger.png" alt="debug button" /> icon in the global tool
bar, and select “Debug termmines in GDB locally IN-VM.”</p></li>
<li><p>Wait a bit then verify the Dynamic Listing window (top) is
displaying disassembly code.</p>
<figure>
<img src="images/GettingStarted_DisassemblyAfterLaunch.png"
alt="Debugger tool after launching termmines" />
<figcaption aria-hidden="true">Debugger tool after launching
termmines</figcaption>
</figure></li>
</ol>
</section>
<section id="launching-on-windows" class="level2">
<h2>Launching on Windows</h2>
<p>On Windows, we will use dbgeng to debug the specimen. This is the
engine that backs WinDbg. You may choose an alternative Minesweeper,
since terminal applications are less representative of Windows
executables. Follow the same process as for Linux, except import
<code>termmines.exe</code> and select “Debug termmines.exe in dbgeng
locally IN-VM.”</p>
</section>
<section id="launching-on-macos" class="level2">
<h2>Launching on macOS</h2>
<p>Unfortunately, things are not so simple on macOS. See the
instructions for <a
href="../../../Ghidra/Debug/Debugger-swig-lldb/InstructionsForBuildingLLDBInterface.txt">Building
LLDB-Java Bindings</a>. Once built, follow the same process as for
Linux, except select “Debug termmines in LLDB locally IN-VM.”</p>
</section>
<section id="troubleshooting" class="level2">
<h2>Troubleshooting</h2>
<section id="im-having-trouble-importing-termmines" class="level3">
<h3>Im having trouble importing <code>termmines</code></h3>
<p>Check that <code>termmines</code> exists. You may need to build it
yourself using <code>make</code>. If it exists and you are still having
trouble, please refer to the Beginner course.</p>
</section>
<section id="there-is-no-debugger-icon-in-my-tool-chest" class="level3">
<h3>There is no Debugger icon in my Tool Chest</h3>
<p>Double-check that you are looking at the main Ghidra Project window,
not a CodeBrowser. The tool chest is the box of big icons above the list
of imported programs. If it is not there, you can try importing it from
the default tools:</p>
<ol type="1">
<li>In the menus, select <strong>Tools → Import Default
Tools</strong></li>
<li>Select “defaultTools/Debugger.tool”</li>
<li>Click Import</li>
</ol>
</section>
<section id="there-is-no-debug-launch-icon-in-the-global-toolbar"
class="level3">
<h3>There is no Debug / Launch icon in the global toolbar</h3>
<p>Double-check that you are in the Debugger tool, not the CodeBrowser
tool. If it is still not there, then you may need to re-import the
default Debugger tool as under the previous heading. If it is still not
there, your installation may be corrupt.</p>
</section>
<section
id="there-is-no-debug-termmines-in-gdb-locally-in-vm-option-in-the-launch-drop-down"
class="level3">
<h3>There is no “Debug termmines in GDB locally IN-VM” option in the
launch drop-down</h3>
<p>You may need to install GDB and/or configure Ghidra with its
location. If you have a copy or custom build of GDB in a non-system
path, note its full path. If you intend to use the systems copy of GDB,
then in a terminal:</p>
<div class="sourceCode" id="cb4"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">which</span> gdb</span></code></pre></div>
<p>Note the path given. (If you get an error, then you need to install
GDB.) In a terminal, type the full path of GDB to ensure it executes
properly. Type <code>q</code> to quit GDB.</p>
<ol type="1">
<li>From the Debugger Targets window, click the Connect <img
src="images/connect.png" alt="connect button" /> button.</li>
<li>In the Connect dialog, select “gdb” from the dropdown at the
top.</li>
<li>Enter the full path, e.g., <code>/usr/bin/gdb</code>, in the “GDB
launch command” field.</li>
<li>Click “Connect”</li>
<li>If you get an Interpreter window, then things have gone well.</li>
<li>Type <code>echo test</code> into it to verify its responsive, then
type <code>q</code> to disconnect.</li>
<li>Close the Debugger tool, then retry.</li>
</ol>
</section>
<section
id="the-launch-hangs-for-several-seconds-and-then-prompt-for-a-recorder"
class="level3">
<h3>The launch hangs for several seconds and then prompt for a
“recorder”</h3>
<p>You probably have a stale GDB connection, so when you launched you
now have multiple connections. For the prompt, select the option with
the highest score. Examine the Targets window to confirm you have
multiple GDB connections. If you know which is the stale connection, you
can right-click it and choose <strong>Disconnect</strong>. Otherwise,
use <strong>Disconnect All</strong> from the drop-down menu and
re-launch.</p>
</section>
<section id="the-dynamic-listing-is-empty" class="level3">
<h3>The Dynamic Listing is empty</h3>
<p>Check for an actual connection. You should see an entry in the
Debugger Targets window, a populated Object window, and there should be
an Interpreter window. If not, then your GDB connector may not be
configured properly. Try the steps under the previous heading.</p>
<p>If you have an Interpreter window, there are several
possibilities:</p>
<section id="ghidra-or-gdb-failed-to-launch-the-target" class="level4">
<h4>Ghidra or GDB failed to launch the target:</h4>
<p>Check that the original <code>termmines</code> exists and is
executable. It must be at the path from where it was originally
imported. If you imported from a share, consider copying it locally,
setting its permissions, then re-importing.</p>
</section>
<section id="the-target-was-launched-but-immediately-terminated"
class="level4">
<h4>The target was launched, but immediately terminated:</h4>
<p>Check that the specimen has a <code>main</code> symbol. NOTE: It is
not sufficient to place a <code>main</code> label in Ghidra. The
original file must have a <code>main</code> symbol.</p>
<p>Alternatively, in the menus try <strong>Debugger → Debug termmines →
in GDB locally IN-VM</strong>, and select “Use starti.” This will break
at the system entry point. If you have labeled <code>main</code> in
Ghidra, then you can place a breakpoint there and continue — these
features are covered later in the course.</p>
<p>Alternatively, try debugging the target in GDB from a separate
terminal completely outside of Ghidra to see if things work as
expected.</p>
</section>
<section id="the-target-was-launched-but-has-not-stopped-yet"
class="level4">
<h4>The target was launched, but has not stopped, yet</h4>
<p>Try pressing the Interrupt <img src="images/interrupt.png"
alt="interrupt button" /> button. If that doesnt work or is
unsatisfactory, try the remedies under the previous heading — for an
immediately terminating target.</p>
</section>
<section
id="you-hit-an-uncommon-bug-where-the-memory-map-is-not-applied-properly"
class="level4">
<h4>You hit an uncommon bug where the memory map is not applied
properly</h4>
<p>This is the case if the Dynamic Listing is completely blank but the
Regions window is replete. The Dynamic Listing just needs to be kicked a
little. The easiest way is to step once, using the <img
src="images/stepinto.png" alt="step into" /> Step Into button in the
main toolbar. If this is not desirable, then you can toggle
<strong>Force Full View</strong> back and forth. In the Regions window,
use the drop-down menu to toggle it on, then toggle it off. The Dynamic
Listing should now be populated. To go to the program counter,
double-click the “pc = …” label in the top right.</p>
</section>
<section id="something-else-has-gone-wrong" class="level4">
<h4>Something else has gone wrong</h4>
<p>Try typing <code>info inferiors</code> and similar GDB diagnostic
commands into the Interpreter.</p>
</section>
</section>
<section
id="the-listings-are-in-sync-but-the-dynamic-listing-is-grey-00s"
class="level3">
<h3>The listings are in sync, but the Dynamic Listing is grey 00s</h3>
<p>Check the Auto-Read drop-down near the top right of the Dynamic
Listing. It should be set to <strong>Read Visible Memory, RO
Once</strong>.</p>
</section>
</section>
<section id="exercise-launch-termmines" class="level2">
<h2>Exercise: Launch <code>termmines</code></h2>
<p>If you were following along with an instructor, delete your import of
<code>termmines</code> and/or start a new Ghidra Project. Starting from
the beginning, import <code>termmines</code> and launch it in the Ghidra
Debugger with GDB. When your tool looks like the screenshot with a
populated Dynamic Listing, you have completed the exercise. Disconnect
before proceeding to the next exercise.</p>
</section>
<section id="customized-launching" class="level2">
<h2>Customized Launching</h2>
<p>For this specimen, you may occasionally need to provide custom
command-line parameters. By default, Ghidra attempts to launch the
target without any parameters. In the menus, use <strong>Debugger →
Debug termmmines → in GDB locally IN-VM</strong> to launch with
customizations. Ghidra will remember these customizations the next time
you launch using the drop-down button from the toolbar. The first dialog
allows you to customize the connection to the back-end debugger. Unless
you have a special copy of GDB, you should probably just click Connect.
The second dialog allows you to customize how the back-end debugger
launches the target. This is where you tweak the command line. You can
also change the actual image, in case it has moved or you want to
experiment with a patched version.</p>
</section>
<section id="exercise-launch-with-command-line-help" class="level2">
<h2>Exercise: Launch with Command-line Help</h2>
<p>Launch the specimen so that it prints its usage. When successful, you
will see the usage info in the Debuggers Interpreter window.
<strong>NOTE</strong>: The process will terminate after printing its
usage, and as a result, the rest of the UI will be mostly empty.</p>
</section>
<section id="attaching" class="level2">
<h2>Attaching</h2>
<p>Attaching is slightly more advanced, but because the target will need
to read from stdin, and Ghidra does not properly attach the Interpreter
to stdin, we will need to launch the target in a terminal and attach to
it instead. Note this technique is only possible because the target
waits for input. Depending on the task for future exercises, you may
still need to launch from the Debugger instead of attaching.</p>
<ol type="1">
<li>Run <code>termmines</code> in a proper terminal with the desired
command-line parameters.</li>
<li>In the Ghidra Debugger, find the Targets window, and click the <img
src="images/connect.png" alt="connect" /> Connect button.</li>
<li>Select “gdb” from the drop-down box.</li>
<li>This dialog should look familiar from the Customized Launching
section. Just click the Connect button.</li>
<li>In the Objects window (below the Targets window), expand the node
labeled “Available.”</li>
<li>In the filter box, type <code>termmines</code>.</li>
<li>Right-click on the termmines process and select Attach. If this
fails, select Available again, and click the
<img alt="refresh" src="images/view-refresh.png" width="16px"> Refresh
button.</li>
</ol>
</section>
<section id="exercise-attach" class="level2">
<h2>Exercise: Attach</h2>
<p>Try attaching on your own, if you have not already. Check your work
by typing <code>bt</code> into the Interpreter. If you are in
<code>read</code> you have completed this exercise. Disconnect before
proceeding to the next module: <a href="A2-UITour.html">A Tour of the
UI</a></p>
</section>
<section id="troubleshooting-1" class="level2">
<h2>Troubleshooting</h2>
<p>If you get <code>Operation not permitted</code> or similar when
trying to attach, it is likely your Linux system is configured with
Yamas <code>ptrace_scope=1</code>. We have provided a stub utility
called <code>anyptracer</code>. The utility permits its own process to
be traced by any other process and then executes a shell command. Using
<code>exec</code> as that shell command enables you to execute the
specimen in the permissive process, and thus you can attach to it as if
<code>ptrace_scope=0</code>, but without reducing the security of the
rest of the system. For example:</p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ex">./anyptracer</span> <span class="st">&#39;exec ./termmines&#39;</span></span></code></pre></div>
<p>Alternatively, if you have root access, you can rectify the issue
using the relevant documentation available online.
<strong>Beware!</strong> You should not modify this setting on your
daily driver, as this substantially reduces the security of your system.
Any compromised process would be allowed to attach to and steal data,
e.g., credentials, from any other process owned by the same user.</p>
</section>
</section>
</body>
</html>

View file

@ -0,0 +1,245 @@
# Getting Started
This course assumes you are already familiar with the basics of using Ghidra, including its static analysis features.
To some degree, static analysis is an integral part of debugging with Ghidra.
## The specimen
Throughout this course, we will examine the provided "Terminal Minesweeper" specimen, named `termmines`.
If the compiled artifact has not been provided for you, you may build it from source using the provided [Makefile](../ExerciseFiles/Debugger/Makefile), but you will need `ncurses.h` first:
```bash
cd GhidraClass/ExerciseFiles/Debugger
make
```
The specimen is designed for Linux, but should be trivially portable to other Unix systems.
You will need `ncurses` and its development headers and libraries available on your system.
Though source code for the specimen is available, we strongly encourage you to work on the course exercises without referring to it.
Symbols and debug information are removed from the binary.
With some effort, `termmines` may even port to Windows; however, we have not tested this course on Windows.
It is a good idea to get acquainted with the specimen.
In general, you should take precautions before running code you do not understand or trust.
For `termmines`, the risk is negligible.
Run it:
```bash
./termmines
```
You should see a 9x9 grid and a cursor you can move with the arrow keys.
Hit **Ctrl-C** to exit.
Probe it for help.
Most Linux programs accept a `-h` argument for help:
```bash
./termmines -h
```
You should now have all the information you need to understand how the game works.
If you have never played Minesweeper before, read up online, and perhaps try playing a couple of games.
Don't get distracted, though.
## Launching on Linux
On Linux, we will use GDB to debug the specimen.
There are many ways to do this, but for the sake of simplicity, import and launch as follows:
1. Import `termmines` into a new Ghidra project.
1. If you have a CodeBrowser open, close it and return to the main Ghidra project window.
1. Drag `termmines` onto the Debugger ![debugger icon](images/debugger.png) in the Tool Chest.
1. This will bring up the specimen in the Debugger tool.
(If you are prompted to analyze, choose Yes.)
![Debugger tool with termmines open](images/GettingStarted_ToolWSpecimen.png)
1. In the Debugger tool, click the dropdown &blacktriangledown; for the debug ![debug button](images/debugger.png) icon in the global tool bar, and select "Debug termmines in GDB locally IN-VM."
1. Wait a bit then verify the Dynamic Listing window (top) is displaying disassembly code.
![Debugger tool after launching termmines](images/GettingStarted_DisassemblyAfterLaunch.png)
## Launching on Windows
On Windows, we will use dbgeng to debug the specimen.
This is the engine that backs WinDbg.
You may choose an alternative Minesweeper, since terminal applications are less representative of Windows executables.
Follow the same process as for Linux, except import `termmines.exe` and select "Debug termmines.exe in dbgeng locally IN-VM."
## Launching on macOS
Unfortunately, things are not so simple on macOS.
See the instructions for [Building LLDB-Java Bindings](../../../Ghidra/Debug/Debugger-swig-lldb/InstructionsForBuildingLLDBInterface.txt).
Once built, follow the same process as for Linux, except select "Debug termmines in LLDB locally IN-VM."
## Troubleshooting
### I'm having trouble importing `termmines`
Check that `termmines` exists.
You may need to build it yourself using `make`.
If it exists and you are still having trouble, please refer to the Beginner course.
### There is no Debugger icon in my Tool Chest
Double-check that you are looking at the main Ghidra Project window, not a CodeBrowser.
The tool chest is the box of big icons above the list of imported programs.
If it is not there, you can try importing it from the default tools:
1. In the menus, select **Tools &rarr; Import Default Tools**
1. Select "defaultTools/Debugger.tool"
1. Click Import
### There is no Debug / Launch icon in the global toolbar
Double-check that you are in the Debugger tool, not the CodeBrowser tool.
If it is still not there, then you may need to re-import the default Debugger tool as under the previous heading.
If it is still not there, your installation may be corrupt.
### There is no "Debug termmines in GDB locally IN-VM" option in the launch drop-down
You may need to install GDB and/or configure Ghidra with its location.
If you have a copy or custom build of GDB in a non-system path, note its full path.
If you intend to use the system's copy of GDB, then in a terminal:
```bash
which gdb
```
Note the path given.
(If you get an error, then you need to install GDB.)
In a terminal, type the full path of GDB to ensure it executes properly.
Type `q` to quit GDB.
1. From the Debugger Targets window, click the Connect ![connect button](images/connect.png) button.
1. In the Connect dialog, select "gdb" from the dropdown at the top.
1. Enter the full path, e.g., `/usr/bin/gdb`, in the "GDB launch command" field.
1. Click "Connect"
1. If you get an Interpreter window, then things have gone well.
1. Type `echo test` into it to verify it's responsive, then type `q` to disconnect.
1. Close the Debugger tool, then retry.
### The launch hangs for several seconds and then prompt for a "recorder"
You probably have a stale GDB connection, so when you launched you now have multiple connections.
For the prompt, select the option with the highest score.
Examine the Targets window to confirm you have multiple GDB connections.
If you know which is the stale connection, you can right-click it and choose **Disconnect**.
Otherwise, use **Disconnect All** from the drop-down menu and re-launch.
### The Dynamic Listing is empty
Check for an actual connection.
You should see an entry in the Debugger Targets window, a populated Object window, and there should be an Interpreter window.
If not, then your GDB connector may not be configured properly.
Try the steps under the previous heading.
If you have an Interpreter window, there are several possibilities:
#### Ghidra or GDB failed to launch the target:
Check that the original `termmines` exists and is executable.
It must be at the path from where it was originally imported.
If you imported from a share, consider copying it locally, setting its permissions, then re-importing.
#### The target was launched, but immediately terminated:
Check that the specimen has a `main` symbol.
NOTE: It is not sufficient to place a `main` label in Ghidra.
The original file must have a `main` symbol.
Alternatively, in the menus try **Debugger &rarr; Debug termmines &rarr; in GDB locally IN-VM**, and select "Use starti."
This will break at the system entry point.
If you have labeled `main` in Ghidra, then you can place a breakpoint there and continue &mdash; these features are covered later in the course.
Alternatively, try debugging the target in GDB from a separate terminal completely outside of Ghidra to see if things work as expected.
#### The target was launched, but has not stopped, yet
Try pressing the Interrupt ![interrupt button](images/interrupt.png) button.
If that doesn't work or is unsatisfactory, try the remedies under the previous heading &mdash; for an immediately terminating target.
#### You hit an uncommon bug where the memory map is not applied properly
This is the case if the Dynamic Listing is completely blank but the Regions window is replete.
The Dynamic Listing just needs to be kicked a little.
The easiest way is to step once, using the ![step into](images/stepinto.png) Step Into button in the main toolbar.
If this is not desirable, then you can toggle **Force Full View** back and forth.
In the Regions window, use the drop-down menu to toggle it on, then toggle it off.
The Dynamic Listing should now be populated.
To go to the program counter, double-click the "pc = ..." label in the top right.
#### Something else has gone wrong
Try typing `info inferiors` and similar GDB diagnostic commands into the Interpreter.
### The listings are in sync, but the Dynamic Listing is grey 00s
Check the Auto-Read drop-down near the top right of the Dynamic Listing.
It should be set to **Read Visible Memory, RO Once**.
## Exercise: Launch `termmines`
If you were following along with an instructor, delete your import of `termmines` and/or start a new Ghidra Project.
Starting from the beginning, import `termmines` and launch it in the Ghidra Debugger with GDB.
When your tool looks like the screenshot with a populated Dynamic Listing, you have completed the exercise.
Disconnect before proceeding to the next exercise.
## Customized Launching
For this specimen, you may occasionally need to provide custom command-line parameters.
By default, Ghidra attempts to launch the target without any parameters.
In the menus, use **Debugger &rarr; Debug termmmines &rarr; in GDB locally IN-VM** to launch with customizations.
Ghidra will remember these customizations the next time you launch using the drop-down button from the toolbar.
The first dialog allows you to customize the connection to the back-end debugger.
Unless you have a special copy of GDB, you should probably just click Connect.
The second dialog allows you to customize how the back-end debugger launches the target.
This is where you tweak the command line.
You can also change the actual image, in case it has moved or you want to experiment with a patched version.
## Exercise: Launch with Command-line Help
Launch the specimen so that it prints its usage.
When successful, you will see the usage info in the Debugger's Interpreter window.
**NOTE**: The process will terminate after printing its usage, and as a result, the rest of the UI will be mostly empty.
## Attaching
Attaching is slightly more advanced, but because the target will need to read from stdin, and Ghidra does not properly attach the Interpreter to stdin, we will need to launch the target in a terminal and attach to it instead.
Note this technique is only possible because the target waits for input.
Depending on the task for future exercises, you may still need to launch from the Debugger instead of attaching.
1. Run `termmines` in a proper terminal with the desired command-line parameters.
1. In the Ghidra Debugger, find the Targets window, and click the ![connect](images/connect.png) Connect button.
1. Select "gdb" from the drop-down box.
1. This dialog should look familiar from the Customized Launching section.
Just click the Connect button.
1. In the Objects window (below the Targets window), expand the node labeled "Available."
1. In the filter box, type `termmines`.
1. Right-click on the termmines process and select Attach.
If this fails, select Available again, and click the <img alt="refresh" src="images/view-refresh.png" width="16px"> Refresh button.
## Exercise: Attach
Try attaching on your own, if you have not already.
Check your work by typing `bt` into the Interpreter.
If you are in `read` you have completed this exercise.
Disconnect before proceeding to the next module: [A Tour of the UI](A2-UITour.md)
## Troubleshooting
If you get `Operation not permitted` or similar when trying to attach, it is likely your Linux system is configured with Yama's `ptrace_scope=1`.
We have provided a stub utility called `anyptracer`.
The utility permits its own process to be traced by any other process and then executes a shell command.
Using `exec` as that shell command enables you to execute the specimen in the permissive process, and thus you can attach to it as if `ptrace_scope=0`, but without reducing the security of the rest of the system.
For example:
```bash
./anyptracer 'exec ./termmines'
```
Alternatively, if you have root access, you can rectify the issue using the relevant documentation available online.
**Beware!** You should not modify this setting on your daily driver, as this substantially reduces the security of your system.
Any compromised process would be allowed to attach to and steal data, e.g., credentials, from any other process owned by the same user.

View file

@ -0,0 +1,325 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Ghidra Debugger</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
</style>
<link rel="stylesheet" href="style.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="nav"><a
class="beginner" href="A1-GettingStarted.html">Getting Started</a><a
class="beginner" href="A2-UITour.html">UI Tour</a><a
class="beginner" href="A3-Breakpoints.html">Breakpoints</a><a
class="beginner" href="A4-MachineState.html">Machine State</a><a
class="beginner" href="A5-Navigation.html">Navigation</a><a
class="beginner" href="A6-MemoryMap.html">Memory Map</a><a
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a>
</header>
<header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#a-tour-of-the-debugger" id="toc-a-tour-of-the-debugger">A
Tour of the Debugger</a>
<ul>
<li><a href="#the-debugger-tool" id="toc-the-debugger-tool">The Debugger
Tool</a>
<ul>
<li><a href="#toolbar" id="toc-toolbar">Toolbar</a></li>
<li><a href="#windows" id="toc-windows">Windows</a></li>
</ul></li>
<li><a href="#controlling-the-target"
id="toc-controlling-the-target">Controlling the Target</a></li>
<li><a href="#troubleshooting"
id="toc-troubleshooting">Troubleshooting</a>
<ul>
<li><a
href="#the-listings-are-not-in-sync-i.e.-they-do-not-move-together."
id="toc-the-listings-are-not-in-sync-i.e.-they-do-not-move-together.">The
listings are not in sync, i.e., they do not move together.</a></li>
<li><a
href="#the-listings-seem-to-move-together-but-their-contents-differ."
id="toc-the-listings-seem-to-move-together-but-their-contents-differ.">The
listings seem to move together, but their contents differ.</a></li>
<li><a href="#there-is-no-step-button."
id="toc-there-is-no-step-button.">There is no step button.</a></li>
<li><a
href="#i-can-step-but-i-dont-see-the-effects-in-the-interpreter-window."
id="toc-i-can-step-but-i-dont-see-the-effects-in-the-interpreter-window.">I
can step, but I dont see the effects in the Interpreter
window.</a></li>
<li><a href="#the-step-buttons-are-grayed-out."
id="toc-the-step-buttons-are-grayed-out.">The Step buttons are grayed
out.</a></li>
</ul></li>
<li><a href="#exercise-step-around"
id="toc-exercise-step-around">Exercise: Step Around</a></li>
</ul></li>
</ul>
</nav>
<section id="a-tour-of-the-debugger" class="level1">
<h1>A Tour of the Debugger</h1>
<p>This module assumes you have completed the <a
href="A1-GettingStarted.html">Getting Started</a> module. If not, please
go back.</p>
<p>This module will briefly introduce each window in the Ghidra
Debugger. We assume some familiarity with trap-and-trace debugging. If
you have not used GDB or a similar debugger before, you may find the
Ghidra Debugger difficult to grasp.</p>
<p>If you would like your tool to look more or less like the one
presented in the screenshots here, launch <code>termmines</code> from
the Debugger using GDB.</p>
<section id="the-debugger-tool" class="level2">
<h2>The Debugger Tool</h2>
<p>Like the CodeBrowser tool, the Debugger tool is a preconfigured
collection of plugins and panels that present Ghidras dynamic analysis
features. You may re-configure, save, export, import, etc. the tool to
fit your preferences. For reference, here is a screenshot of the default
configuration after launching <code>termmines</code>:</p>
<figure>
<img src="images/GettingStarted_DisassemblyAfterLaunch.png"
alt="Debugger tool after launching termmines" />
<figcaption aria-hidden="true">Debugger tool after launching
termmines</figcaption>
</figure>
<section id="toolbar" class="level3">
<h3>Toolbar</h3>
<p>Many of the buttons in the global toolbar are the same as in the
CodeBrowser. Coincidentally, in the screenshot, the debugger-specific
buttons start just above the Dynamic Listing in the global toolbar. They
are:</p>
<ul>
<li><img src="images/process.png" alt="emulate button" />
<strong>Emulate</strong>: To be covered in a later module. This will
load the current program (from the Static Listing) into the
emulator.</li>
<li><img src="images/debugger.png" alt="debug button" />
<strong>Debug</strong>: This launches the current program (from the
Static Listing) using a suitable back-end debugger. The drop-down menu
provides a selection of suitable back-end connectors. Clicking the
button will use the last successful connector or the default.</li>
<li><img src="images/record.png" alt="mode button" /> <strong>Control
Mode</strong>: This drop-down menu sets the mode of the controls and
machine state edits. By default, all actions are directed to the
back-end debugger.</li>
<li><img src="images/resume.png" alt="resume button" />
<strong>Resume</strong>: Resume execution. This is equivalent to
<code>continue</code> in GDB.</li>
<li><img src="images/interrupt.png" alt="interrupt button" />
<strong>Interrupt</strong>: Interrupt, suspend, pause, break, etc. This
is equivalent to <strong>Ctrl-C</strong> or <code>interrupt</code> in
GDB.</li>
<li><img src="images/kill.png" alt="kill button" />
<strong>Kill</strong>: Kill, terminate, etc. This is equivalent to
<code>kill</code> in GDB.</li>
<li><img src="images/disconnect.png" alt="disconnect button" />
<strong>Disconnect</strong>: Disconnect from the back-end debugger.
Typically, this will also end the session. It is equivalent to
<code>quit</code> in GDB.</li>
<li><img src="images/stepinto.png" alt="step into button" />
<strong>Step Into</strong>, <img src="images/stepover.png"
alt="step over button" /> <strong>Step Over</strong>, <img
src="images/stepout.png" alt="step out button" /> <strong>Step
Out</strong>, <img src="images/steplast.png" alt="step last button" />
<strong>Step Last</strong>: These buttons step in various ways. In
order, the equivalent commands in GDB are <code>stepi</code>,
<code>nexti</code>, and <code>finish</code>. Step Last has no equivalent
in GDB; it is meant to repeat the last custom/extended step.</li>
</ul>
</section>
<section id="windows" class="level3">
<h3>Windows</h3>
<p>Starting at the top left and working clockwise, the windows are:</p>
<ul>
<li>The <strong>Debugger Targets</strong> window: This lists active
sessions or connections. From here, you can establish new sessions or
terminate existing sessions.</li>
<li>The <strong>Dynamic Listing</strong> window: This is the primary
means of examining the instructions being executed. By default, it
follows the program counter and disassembles from there until the next
control transfer instruction. It supports many of the same operations as
the Static Listing, including patching. The nearest equivalent in GDB is
something like <code>x/10i $pc</code>.</li>
<li>The <strong>Interpreter</strong> window: This is essentially a
terminal emulator providing a command-line interface to the back-end
debugger. It is useful for diagnostics or for issuing commands that do
not have a button in the GUI. Some may also prefer to command the
debugger from here rather than the GUI.</li>
<li>The <strong>Breakpoints</strong> window: This is stacked below the
Interpreter. It lists and manages the breakpoints among all open images
and running targets. The nearest equivalent in GDB is
<code>info break</code>.</li>
<li>The <strong>Registers</strong> window: This is stacked below the
Breakpoints window. It displays and edits the register values for the
current thread. The nearest equivalent in GDB is
<code>info registers</code></li>
<li>The <strong>Modules</strong> window: This is stacked below the
Registers window. It displays the images (and sections, if applicable)
loaded by the target. The equivalent in GDB is
<code>maintenance info sections</code>. Note that this differs from the
Regions window.</li>
<li>The <strong>Threads</strong> window: This lists the threads in the
current target. The tabs at the top list the active targets. The nearest
equivalents in GDB are <code>info threads</code> and
<code>info inferiors</code>.</li>
<li>The <strong>Time</strong> window: This is stacked below the Threads
window. This lists the events and snapshots taken of the current
target.</li>
<li>The <strong>Stack</strong> window: This lists the stack frames for
the current thread. The equivalent in GDB is
<code>backtrace</code>.</li>
<li>The <strong>Watches</strong> window: This is stacked below the Stack
window — pun not intended. It manages current watches. These are
<em>not</em> watchpoints, but rather expressions or variables whose
values to display. To manage watchpoints, use the Breakpoints window or
the Interpreter. The nearest equivalent in GDB is
<code>display</code>.</li>
<li>The <strong>Regions</strong> window: This is stacked below the
Watches window. It lists memory regions for the current target. It
differs from the Modules window, since this includes not only
image-backed regions but other memory regions, e.g., stacks and heaps.
The equivalent in GDB is <code>info proc mappings</code>.</li>
<li>The <strong>Debug Console</strong> window: (Not to be confused with
the Console window from the CodeBrowser.) This displays logging messages
and problems encountered during a session. Some problems are presented
with remedial actions, which may expedite your workflow or aid in
troubleshooting.</li>
<li>The <strong>Objects</strong> window: This models the back-end
debugger as a tree of objects and provides generic actions on those
objects. It is generally more capable, though less integrated, than the
GUI, but not quite as capable as the Interpreter. It is useful for
troubleshooting and for advanced use cases.</li>
</ul>
</section>
</section>
<section id="controlling-the-target" class="level2">
<h2>Controlling the Target</h2>
<p>The control buttons are all located on the global toolbar. Start by
pressing the <img src="images/stepinto.png" alt="step into" /> Step Into
button. Notice that the Dynamic Listing moves forward a single
instruction each time you press it. Also notice that the Static Listing
moves with the Dynamic Listing. You may navigate in either listing, and
so long as there is a corresponding location in the other, the two will
stay synchronized. You may also open the Decompiler just as you would in
the CodeBrowser, and it will stay in sync, too.</p>
<p>When you have clicked <img src="images/stepinto.png"
alt="step into" /> Step Into a sufficient number of times, you should
end up in a subroutine. You can click <img src="images/stepout.png"
alt="step out" /> Step Out to leave the subroutine. Note that the target
is allowed to execute until it returns from the subroutine; it does not
skip out of it. Now, click <img src="images/stepover.png"
alt="step over" /> Step Over until you reach another <code>CALL</code>
instruction. Notice that when you click <img src="images/stepover.png"
alt="step over" /> Step Over again, it will not descend into the
subroutine. Instead, the target is allowed to execute the entire
subroutine before stopping again — after the <code>CALL</code>
instruction.</p>
<p>If you prefer, you may use the GDB commands from the Interpreter
instead of the buttons. Try <code>si</code> and/or <code>ni</code>. You
can also pass arguments which is not possible with the buttons,
e.g. <code>si 10</code> to step 10 instructions in one command.</p>
<p>If you need to terminate the target you should use the <img
src="images/disconnect.png" alt="disconnect" /> Disconnect button rather
than the Kill button, in general. Otherwise, each launch will create a
new connection, and you will end up with several stale connections.
Additionally, if your target exits or otherwise terminates on its own,
you will get a stale connection. Use the Targets window to clean such
connections up. The re-use of connections and/or the use of multiple
concurrent connections is <em>not</em> covered in this course.</p>
</section>
<section id="troubleshooting" class="level2">
<h2>Troubleshooting</h2>
<section
id="the-listings-are-not-in-sync-i.e.-they-do-not-move-together."
class="level3">
<h3>The listings are not in sync, i.e., they do not move together.</h3>
<p>First, check that synchronization is enabled. This is the default
behavior, but, still, check it first. In the top-right of the Dynamic
Listing is its local drop-down menu. Click it and check that
<strong>Auto-Sync Cursor with Static Listing</strong> is selected.</p>
<p>If that does not work, check the top-left label of the Dynamic
Listing to see what module you are in. Also check the Debug Console
window. If you are in a system library, e.g., <code>ld-linux</code>,
then this is the expected behavior. You may optionally import it, as
suggested by the Debug Console, but this is covered later.</p>
<p>If you are not in a system library, then check the Modules window to
see if <code>termmines</code> is listed. If so, it seems the module
mapper failed to realize that module is the current program. Right-click
the module and select “Map to termmines.” Confirm the dialog. If
<code>termmines</code> is not listed, then your version of GDB may not
be supported. If you file a bug report, please include your GDB version,
Linux distribution, and/or other platform details.</p>
</section>
<section
id="the-listings-seem-to-move-together-but-their-contents-differ."
class="level3">
<h3>The listings seem to move together, but their contents differ.</h3>
<p>There is probably a discrepancy between the version you imported and
the version you launched. This should not happen with
<code>termmines</code>, but perhaps you re-ran <code>make</code> between
importing and launching? For other system libraries, this could happen
if you or an administrator applied system updates since you imported.
You probably need to re-import the affected module image(s). If this
happens to you in practice, and you have substantial investment in the
old import, consider using the Version Tracker to port your knowledge to
the new import.</p>
</section>
<section id="there-is-no-step-button." class="level3">
<h3>There is no step button.</h3>
<p>This can happen if the Control Mode is set to the Trace. Perhaps you
played with the Time window? Change the Control Mode back to “Control
Target.”</p>
</section>
<section
id="i-can-step-but-i-dont-see-the-effects-in-the-interpreter-window."
class="level3">
<h3>I can step, but I dont see the effects in the Interpreter
window.</h3>
<p>This can happen if the Control Mode is set to the Emulator. Change
the Control Mode back to “Control Target.”</p>
</section>
<section id="the-step-buttons-are-grayed-out." class="level3">
<h3>The Step buttons are grayed out.</h3>
<p>The target has likely terminated, or you have not selected a thread.
Check the Threads window. If it is empty, re-launch, and perhaps look at
the Troubleshooting section in <a href="A1-GettingStarted.html">Getting
Started</a></p>
</section>
</section>
<section id="exercise-step-around" class="level2">
<h2>Exercise: Step Around</h2>
<p>If you were not already following along with an instructor, then try
some of the stepping buttons. One of the first subroutines called in
<code>termmines</code> parses command-line arguments. Try stepping until
you have entered that subroutine. <strong>TIP</strong>: Use the
Decompiler to help you recognize when you have entered the command-line
parsing subroutine. Alternatively, use the Static Listing and Decompiler
to identify the parsing subroutine (as you would in the CodeBrowser),
and then use the Step buttons to drive the target into it.</p>
</section>
</section>
</body>
</html>

View file

@ -0,0 +1,193 @@
# A Tour of the Debugger
This module assumes you have completed the [Getting Started](A1-GettingStarted.md) module.
If not, please go back.
This module will briefly introduce each window in the Ghidra Debugger.
We assume some familiarity with trap-and-trace debugging.
If you have not used GDB or a similar debugger before, you may find the Ghidra Debugger difficult to grasp.
If you would like your tool to look more or less like the one presented in the screenshots here,
launch `termmines` from the Debugger using GDB.
## The Debugger Tool
Like the CodeBrowser tool, the Debugger tool is a preconfigured collection of plugins and panels that present Ghidra's dynamic analysis features.
You may re-configure, save, export, import, etc. the tool to fit your preferences.
For reference, here is a screenshot of the default configuration after launching `termmines`:
![Debugger tool after launching termmines](images/GettingStarted_DisassemblyAfterLaunch.png)
### Toolbar
Many of the buttons in the global toolbar are the same as in the CodeBrowser.
Coincidentally, in the screenshot, the debugger-specific buttons start just above the Dynamic Listing in the global toolbar.
They are:
* ![emulate button](images/process.png) **Emulate**:
To be covered in a later module.
This will load the current program (from the Static Listing) into the emulator.
* ![debug button](images/debugger.png) **Debug**:
This launches the current program (from the Static Listing) using a suitable back-end debugger.
The drop-down menu provides a selection of suitable back-end connectors.
Clicking the button will use the last successful connector or the default.
* ![mode button](images/record.png) **Control Mode**:
This drop-down menu sets the mode of the controls and machine state edits.
By default, all actions are directed to the back-end debugger.
* ![resume button](images/resume.png) **Resume**:
Resume execution.
This is equivalent to `continue` in GDB.
* ![interrupt button](images/interrupt.png) **Interrupt**:
Interrupt, suspend, pause, break, etc.
This is equivalent to **Ctrl-C** or `interrupt` in GDB.
* ![kill button](images/kill.png) **Kill**:
Kill, terminate, etc.
This is equivalent to `kill` in GDB.
* ![disconnect button](images/disconnect.png) **Disconnect**:
Disconnect from the back-end debugger.
Typically, this will also end the session.
It is equivalent to `quit` in GDB.
* ![step into button](images/stepinto.png) **Step Into**, ![step over button](images/stepover.png) **Step Over**, ![step out button](images/stepout.png) **Step Out**, ![step last button](images/steplast.png) **Step Last**:
These buttons step in various ways.
In order, the equivalent commands in GDB are `stepi`, `nexti`, and `finish`.
Step Last has no equivalent in GDB; it is meant to repeat the last custom/extended step.
### Windows
Starting at the top left and working clockwise, the windows are:
* The **Debugger Targets** window:
This lists active sessions or connections.
From here, you can establish new sessions or terminate existing sessions.
* The **Dynamic Listing** window:
This is the primary means of examining the instructions being executed.
By default, it follows the program counter and disassembles from there until the next control transfer instruction.
It supports many of the same operations as the Static Listing, including patching.
The nearest equivalent in GDB is something like `x/10i $pc`.
* The **Interpreter** window:
This is essentially a terminal emulator providing a command-line interface to the back-end debugger.
It is useful for diagnostics or for issuing commands that do not have a button in the GUI.
Some may also prefer to command the debugger from here rather than the GUI.
* The **Breakpoints** window:
This is stacked below the Interpreter.
It lists and manages the breakpoints among all open images and running targets.
The nearest equivalent in GDB is `info break`.
* The **Registers** window:
This is stacked below the Breakpoints window.
It displays and edits the register values for the current thread.
The nearest equivalent in GDB is `info registers`
* The **Modules** window:
This is stacked below the Registers window.
It displays the images (and sections, if applicable) loaded by the target.
The equivalent in GDB is `maintenance info sections`.
Note that this differs from the Regions window.
* The **Threads** window:
This lists the threads in the current target.
The tabs at the top list the active targets.
The nearest equivalents in GDB are `info threads` and `info inferiors`.
* The **Time** window:
This is stacked below the Threads window.
This lists the events and snapshots taken of the current target.
* The **Stack** window:
This lists the stack frames for the current thread.
The equivalent in GDB is `backtrace`.
* The **Watches** window:
This is stacked below the Stack window &mdash; pun not intended.
It manages current watches.
These are *not* watchpoints, but rather expressions or variables whose values to display.
To manage watchpoints, use the Breakpoints window or the Interpreter.
The nearest equivalent in GDB is `display`.
* The **Regions** window:
This is stacked below the Watches window.
It lists memory regions for the current target.
It differs from the Modules window, since this includes not only image-backed regions but other memory regions, e.g., stacks and heaps.
The equivalent in GDB is `info proc mappings`.
* The **Debug Console** window:
(Not to be confused with the Console window from the CodeBrowser.)
This displays logging messages and problems encountered during a session.
Some problems are presented with remedial actions, which may expedite your workflow or aid in troubleshooting.
* The **Objects** window:
This models the back-end debugger as a tree of objects and provides generic actions on those objects.
It is generally more capable, though less integrated, than the GUI, but not quite as capable as the Interpreter.
It is useful for troubleshooting and for advanced use cases.
## Controlling the Target
The control buttons are all located on the global toolbar.
Start by pressing the ![step into](images/stepinto.png) Step Into button.
Notice that the Dynamic Listing moves forward a single instruction each time you press it.
Also notice that the Static Listing moves with the Dynamic Listing.
You may navigate in either listing, and so long as there is a corresponding location in the other, the two will stay synchronized.
You may also open the Decompiler just as you would in the CodeBrowser, and it will stay in sync, too.
When you have clicked ![step into](images/stepinto.png) Step Into a sufficient number of times, you should end up in a subroutine.
You can click ![step out](images/stepout.png) Step Out to leave the subroutine.
Note that the target is allowed to execute until it returns from the subroutine; it does not skip out of it.
Now, click ![step over](images/stepover.png) Step Over until you reach another `CALL` instruction.
Notice that when you click ![step over](images/stepover.png) Step Over again, it will not descend into the subroutine.
Instead, the target is allowed to execute the entire subroutine before stopping again &mdash; after the `CALL` instruction.
If you prefer, you may use the GDB commands from the Interpreter instead of the buttons.
Try `si` and/or `ni`.
You can also pass arguments which is not possible with the buttons, e.g. `si 10` to step 10 instructions in one command.
If you need to terminate the target you should use the ![disconnect](images/disconnect.png) Disconnect button rather than the Kill button, in general.
Otherwise, each launch will create a new connection, and you will end up with several stale connections.
Additionally, if your target exits or otherwise terminates on its own, you will get a stale connection.
Use the Targets window to clean such connections up.
The re-use of connections and/or the use of multiple concurrent connections is *not* covered in this course.
## Troubleshooting
### The listings are not in sync, i.e., they do not move together.
First, check that synchronization is enabled.
This is the default behavior, but, still, check it first.
In the top-right of the Dynamic Listing is its local drop-down menu.
Click it and check that **Auto-Sync Cursor with Static Listing** is selected.
If that does not work, check the top-left label of the Dynamic Listing to see what module you are in.
Also check the Debug Console window.
If you are in a system library, e.g., `ld-linux`, then this is the expected behavior.
You may optionally import it, as suggested by the Debug Console, but this is covered later.
If you are not in a system library, then check the Modules window to see if `termmines` is listed.
If so, it seems the module mapper failed to realize that module is the current program.
Right-click the module and select "Map to termmines."
Confirm the dialog.
If `termmines` is not listed, then your version of GDB may not be supported.
If you file a bug report, please include your GDB version, Linux distribution, and/or other platform details.
### The listings seem to move together, but their contents differ.
There is probably a discrepancy between the version you imported and the version you launched.
This should not happen with `termmines`, but perhaps you re-ran `make` between importing and launching?
For other system libraries, this could happen if you or an administrator applied system updates since you imported.
You probably need to re-import the affected module image(s).
If this happens to you in practice, and you have substantial investment in the old import, consider using the Version Tracker to port your knowledge to the new import.
### There is no step button.
This can happen if the Control Mode is set to the Trace.
Perhaps you played with the Time window?
Change the Control Mode back to "Control Target."
### I can step, but I don't see the effects in the Interpreter window.
This can happen if the Control Mode is set to the Emulator.
Change the Control Mode back to "Control Target."
### The Step buttons are grayed out.
The target has likely terminated, or you have not selected a thread.
Check the Threads window.
If it is empty, re-launch, and perhaps look at the Troubleshooting section in [Getting Started](A1-GettingStarted.md)
## Exercise: Step Around
If you were not already following along with an instructor, then try some of the stepping buttons.
One of the first subroutines called in `termmines` parses command-line arguments.
Try stepping until you have entered that subroutine.
**TIP**: Use the Decompiler to help you recognize when you have entered the command-line parsing subroutine.
Alternatively, use the Static Listing and Decompiler to identify the parsing subroutine (as you would in the CodeBrowser), and then use the Step buttons to drive the target into it.

View file

@ -0,0 +1,490 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Ghidra Debugger</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="style.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="nav"><a
class="beginner" href="A1-GettingStarted.html">Getting Started</a><a
class="beginner" href="A2-UITour.html">UI Tour</a><a
class="beginner" href="A3-Breakpoints.html">Breakpoints</a><a
class="beginner" href="A4-MachineState.html">Machine State</a><a
class="beginner" href="A5-Navigation.html">Navigation</a><a
class="beginner" href="A6-MemoryMap.html">Memory Map</a><a
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a>
</header>
<header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#using-breakpoints" id="toc-using-breakpoints">Using
Breakpoints</a>
<ul>
<li><a href="#breakpoints" id="toc-breakpoints">Breakpoints</a></li>
<li><a href="#examining-minesweeper-board-setup"
id="toc-examining-minesweeper-board-setup">Examining Minesweeper Board
Setup</a>
<ul>
<li><a href="#set-the-breakpoints" id="toc-set-the-breakpoints">Set the
Breakpoints</a></li>
<li><a href="#toggling-the-breakpoints"
id="toc-toggling-the-breakpoints">Toggling the Breakpoints</a></li>
<li><a href="#importing-libc" id="toc-importing-libc">Importing
<code>libc</code></a></li>
<li><a href="#capturing-the-random-seed"
id="toc-capturing-the-random-seed">Capturing the Random Seed</a></li>
<li><a href="#locating-the-mine-placement-algorithm"
id="toc-locating-the-mine-placement-algorithm">Locating the Mine
Placement Algorithm</a></li>
<li><a href="#exercise-diagram-the-mines"
id="toc-exercise-diagram-the-mines">Exercise: Diagram the Mines</a></li>
<li><a
href="#optional-exercise-replicate-the-boards-forward-engineering"
id="toc-optional-exercise-replicate-the-boards-forward-engineering">Optional
Exercise: Replicate the Boards (Forward Engineering)</a></li>
</ul></li>
</ul></li>
</ul>
</nav>
<section id="using-breakpoints" class="level1">
<h1>Using Breakpoints</h1>
<p>This module assumes you know how to launch <code>termmines</code> in
Ghidra using GDB and know where to find the basic Debugger GUI
components. If not, please refer to the previous modules.</p>
<p>This module will address the Breakpoints window in more depth. While
the breakpoint manager is able to deal with a system of targets, we will
only deal with a single target at a time.</p>
<section id="breakpoints" class="level2">
<h2>Breakpoints</h2>
<p>Most likely, this window is empty if you have been following the
lesson.</p>
<figure>
<img src="images/Breakpoints_EmptyAfterLaunch.png"
alt="The breakpoints window" />
<figcaption aria-hidden="true">The breakpoints window</figcaption>
</figure>
<p>From here, you can toggle and delete existing breakpoints. There are
several ways to set a new breakpoint:</p>
<ol type="1">
<li>From any static or dynamic listing window, including Disassembly,
Memory/Hex, and the Decompiler, right-click and select <img
src="images/breakpoint-enable.png" alt="set breakpoint" /> Set
Breakpoint, press <strong>K</strong> on the keyboard, or double-click
the margin.</li>
<li>From the Objects window click the <img
src="images/breakpoint-enable.png" alt="add breakpoint" /> Add
Breakpoint button or press <strong>F3</strong> on the keyboard.</li>
<li>From the Interpreter window, use the GDB command, e.g.,
<code>break main</code>.</li>
</ol>
<p>The advantage of using the listings is that you can quickly set a
breakpoint at any address. The advantage of using the Objects or
Interpreter window is that you can specify something other than an
address. Often, those specifications still resolve to addresses, and
Ghidra will display them. Ghidra will memorize breakpoints by recording
them as special bookmarks in the imported program. There is some
iconography to communicate the various states of a breakpoint. When all
is well and normal, you should only see enabled <img
src="images/breakpoint-enable.png" alt="enabled breakpoint" /> and
disabled <img src="images/breakpoint-disable.png"
alt="disabled breakpoint" /> breakpoints. If the target is terminated
(or not launched yet), you may also see ineffective <img
src="images/breakpoint-enable-ineff.png" alt="ineffective breakpoint" />
breakpoints.</p>
</section>
<section id="examining-minesweeper-board-setup" class="level2">
<h2>Examining Minesweeper Board Setup</h2>
<p>Suppose we want to cheat at <code>termmines</code>. We might like to
understand how the mines are placed. Knowing that the mines are placed
randomly, we might hypothesize that it is using the <code>srand</code>
and <code>rand</code> functions from the C standard library. While we
can test that hypothesis by examining the imports statically, we might
also like to record some actual values, so we will approach this
dynamically. (This is the Debugger course, after all.) The breakpoint on
<code>srand</code> will allow us to capture the random seed. The
breakpoint on <code>rand</code> will help us find the algorithm that
places the mines.</p>
<section id="set-the-breakpoints" class="level3">
<h3>Set the Breakpoints</h3>
<p>In the Interpreter, type the GDB commands to set breakpoints on
<code>srand</code> and <code>rand</code>:</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">break</span> srand</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">break</span> rand</span></code></pre></div>
<p>The breakpoint window should now be updated:</p>
<figure>
<img src="images/Breakpoints_PopAfterSRandRand.png"
alt="Populated breakpoints window" />
<figcaption aria-hidden="true">Populated breakpoints window</figcaption>
</figure>
<p>For a single target, the lower panel of the Breakpoints window does
not add much information, but it does have some. We will start with the
top panel. This lists the “logical” breakpoints, preferring static
addresses.</p>
<ul>
<li>The left-most column <strong>Enabled</strong> indicates the
breakpoints state. Here, we see the inconsistent <img
src="images/breakpoint-overlay-inconsistent.png" alt="inconsistent" />
overlay, because Ghidra cannot save the breakpoint without a module
image. That is because <code>srand</code> and <code>rand</code> are in a
different module, and we have not yet imported it into Ghidra.</li>
<li>The next column <strong>Name</strong> is the name of the breakpoint.
This is for informational purposes only. You can rename a breakpoint
however you like, and it will have no effect on the target nor back-end
debugger.</li>
<li>The next column <strong>Address</strong> gives the address of the
breakpoint. Notice that the addresses were resolved, even though the
breakpoints were specified by symbol. Typically, this is the
<em>static</em> address of the breakpoint; however, if the module image
is not imported, yet, this will be the <em>dynamic</em> address, subject
to relocation or ASLR.</li>
<li>The next column <strong>Image</strong> gives the name of the
imported image containing the breakpoint. Again, because the module has
not been imported yet, this column is blank.</li>
<li>The next column <strong>Length</strong> gives the length of the
breakpoint. In GDB, this generally applies to watchpoints only.</li>
<li>The next column <strong>Kinds</strong> gives the kinds of
breakpoint. Most breakpoints are software execution breakpoints,
indicated by “SW_EXECUTE.” That is, they are implemented by patching the
targets memory with a special instruction (<code>INT3</code> on x86)
that traps execution. There are also hardware execution breakpoints
indicated by “HW_EXECUTE,” and access breakpoints indicated by “HW_READ”
and/or “HW_WRITE”. <strong>NOTE</strong>: GDB would call these
“watchpoints.” An advantage to software breakpoints is that you can have
a practically unlimited number of them. Some disadvantages are they can
be detected easily, and they are limited to execution breakpoints.</li>
<li>The next column <strong>Locations</strong> counts the number of
locations for the breakpoint. For a single-target session, this should
always be 1.</li>
<li>The final column <strong>Sleigh</strong> is only applicable to the
emulator. It indicates that the breakpoints behavior has been
customized with Sleigh code. This is covered in <a
href="B2-Emulation.html">Emulation</a>.</li>
</ul>
<p>Now, we move to the bottom panel. This lists the breakpoint
locations, as reported by the back-end debugger(s). The Enabled,
Address, and Sleigh columns are the same as the top, but for the
individual <em>dynamic</em> addresses.</p>
<ul>
<li>The <strong>Name</strong> column is the name as designated by the
back-end.</li>
<li>The <strong>Trace</strong> column indicates which target contains
the location. The text here should match one of the tabs from the
Threads panel.</li>
<li>The <strong>Comment</strong> column is a user-defined comment. Its
default value is the specification that generated it, e.g.,
<code>srand</code>.</li>
<li>The <strong>Threads</strong> column indicates if the breakpoint is
scoped to a limited set of threads. Its use is atypical.</li>
</ul>
</section>
<section id="toggling-the-breakpoints" class="level3">
<h3>Toggling the Breakpoints</h3>
<p>While there is no need to toggle the breakpoints right now, it is a
good time to demonstrate the feature. There are several ways to toggle a
breakpoint:</p>
<ol type="1">
<li>In any listing, as in setting a breakpoint, right-click and select a
toggle action, press <strong>K</strong> on the keyboard, or double-click
its icon in the margin.</li>
<li>From the Objects window, expand the Breakpoints node, right-click a
breakpoint and select Toggle or press <strong>T</strong> on the
keyboard.</li>
<li>From the Breakpoints window, single-click the breakpoints status
icon, right-click an entry and select a toggle action, or create a
selection and use a toggling action from the local toolbar. Either panel
works, but the top panel is preferred to keep the breakpoints
consistent. The local toolbar also has actions for toggling all
breakpoints in the session.</li>
<li>From the Interpreter window, use the GDB commands, e.g.,
<code>disable 2</code>.</li>
</ol>
<p>Practice toggling them. Notice that no matter how you toggle the
breakpoints, the display updates. You might also type
<code>info break</code> into the Interpreter to confirm the effect of
toggling breakpoints in the GUI. When you are finished, ensure both
breakpoints are enabled.</p>
</section>
<section id="importing-libc" class="level3">
<h3>Importing <code>libc</code></h3>
<p>While the Debugger can operate without importing external modules, it
generally works better when you have. The symbols <code>srand</code> and
<code>rand</code> are in <code>libc</code>. If you would like to save
the breakpoints we placed on them, you must import the module. You could
do this in the usual manner, but the Debugger offers a convenient way to
import missing modules.</p>
<ol type="1">
<li><p>Navigate to a dynamic address that would be mapped to the missing
module. For our scenario, the easiest way to do that is to double-click
an address in the Breakpoints window. Either one points somewhere in
<code>libc</code>.</p></li>
<li><p>Check the Debug Console window for a note about the missing
module:</p>
<figure>
<img src="images/Breakpoints_MissingModuleNote.png"
alt="Missing module note in the debug console" />
<figcaption aria-hidden="true">Missing module note in the debug
console</figcaption>
</figure></li>
<li><p>Click the import button — leftmost of the remedial actions. It
will display a file browser pointed at the library file.</p></li>
<li><p>Proceed with the import and initial analysis as you would in the
CodeBrowser.</p></li>
</ol>
<p>Once imported, the Breakpoints window should update to reflect the
static addresses, the breakpoints should become consistent, and the
Static Listing should now be synchronized when navigating within
<code>libc</code>.</p>
<figure>
<img src="images/Breakpoints_SyncedAfterImportLibC.png"
alt="The debugger tool with breakpoints synchronized after importing libc" />
<figcaption aria-hidden="true">The debugger tool with breakpoints
synchronized after importing libc</figcaption>
</figure>
</section>
<section id="capturing-the-random-seed" class="level3">
<h3>Capturing the Random Seed</h3>
<p>We can now allow <code>termmines</code> to execute, expecting it to
hit the <code>srand</code> breakpoint first. Click <img
src="images/resume.png" alt="resume" /> Resume. If all goes well, the
target should break at <code>srand</code>. If you have never written
code that uses <code>srand</code> before, you should briefly read its
manual page. It takes a single parameter, the desired seed. That
parameter contains the seed this very moment! We can then examine the
value of the seed by hovering over <code>param_1</code> in the
decompiler.</p>
<figure>
<img src="images/Breakpoints_SeedValueAfterBreakSRand.png"
alt="Seed value in decompiler hover" />
<figcaption aria-hidden="true">Seed value in decompiler
hover</figcaption>
</figure>
<p>We will cover other ways to examine memory and registers in the <a
href="A4-MachineState.html">Machine State</a> module. We have contrived
<code>termmines</code> so that its random seed will always start with
<code>0x5eed____</code>. If you see that in the value displayed, then
you have successfully recovered the seed. This seed will be used in an
optional exercise at the end of this module. You might write it down;
however, if you re-launch <code>termmines</code> between now and then,
you will have a different seed.</p>
</section>
<section id="locating-the-mine-placement-algorithm" class="level3">
<h3>Locating the Mine Placement Algorithm</h3>
<p>Press <img src="images/resume.png" alt="resume" /> Resume again. This
time, the target should break at <code>rand</code>. We are not
interested in the <code>rand</code> function itself, but rather how the
placement algorithm is using it. Press <img src="images/stepout.png"
alt="step out" /> Step Out to allow the target to return from
<code>rand</code>. If you still have the Decompiler up, you should be in
a code block resembling:</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode numberSource c numberLines"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1"></a><span class="cf">while</span> <span class="op">(</span>iVar2 <span class="op">=</span> DAT_00604164<span class="op">,</span> iVar1 <span class="op">=</span> DAT_00604160<span class="op">,</span> iVar10 <span class="op">&lt;</span> _DAT_00604168<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2"></a> iVar3 <span class="op">=</span> rand<span class="op">();</span></span>
<span id="cb2-3"><a href="#cb2-3"></a> iVar2 <span class="op">=</span> DAT_00604164<span class="op">;</span></span>
<span id="cb2-4"><a href="#cb2-4"></a> iVar11 <span class="op">=</span> rand<span class="op">();</span></span>
<span id="cb2-5"><a href="#cb2-5"></a> lVar7 <span class="op">=</span> <span class="op">(</span><span class="dt">long</span><span class="op">)(</span>iVar11 <span class="op">%</span> iVar2 <span class="op">+</span> <span class="dv">1</span><span class="op">)</span> <span class="op">*</span> <span class="bn">0x20</span> <span class="op">+</span> <span class="op">(</span><span class="dt">long</span><span class="op">)(</span>iVar3 <span class="op">%</span> iVar1 <span class="op">+</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb2-6"><a href="#cb2-6"></a> bVar14 <span class="op">=</span> <span class="op">*(</span>byte <span class="op">*)((</span><span class="dt">long</span><span class="op">)&amp;</span>DAT_00604160 <span class="op">+</span> lVar7 <span class="op">+</span> <span class="bn">0x1c</span><span class="op">);</span></span>
<span id="cb2-7"><a href="#cb2-7"></a> <span class="cf">if</span> <span class="op">(-</span><span class="dv">1</span> <span class="op">&lt;</span> <span class="op">(</span><span class="dt">char</span><span class="op">)</span>bVar14<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-8"><a href="#cb2-8"></a> iVar10 <span class="op">=</span> iVar10 <span class="op">+</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb2-9"><a href="#cb2-9"></a> <span class="op">*(</span>byte <span class="op">*)((</span><span class="dt">long</span><span class="op">)&amp;</span>DAT_00604160 <span class="op">+</span> lVar7 <span class="op">+</span> <span class="bn">0x1c</span><span class="op">)</span> <span class="op">=</span> bVar14 <span class="op">|</span> <span class="bn">0x80</span><span class="op">;</span></span>
<span id="cb2-10"><a href="#cb2-10"></a> <span class="op">}</span></span>
<span id="cb2-11"><a href="#cb2-11"></a><span class="op">}</span></span></code></pre></div>
<p>If you are thinking, “I could have just found <code>rand</code> in
the symbol table and followed its XRefs,” you are correct. However, it
is useful to use a dynamic debugging session to drive your analysis
chronologically through execution of the target, even if much of that
analysis is still static. The advantages of a dynamic session along side
static analysis should become more apparent as you progress through this
course.</p>
</section>
<section id="exercise-diagram-the-mines" class="level3">
<h3>Exercise: Diagram the Mines</h3>
<p>You goal is to capture the location of all the mines. So that you can
check your work later, you should run <code>termmines</code> in a
terminal and attach to it from Ghidra. You will probably want to disable
the breakpoints on <code>rand</code> and <code>srand</code> for now.
Devise a strategy using breakpoints and the control buttons (Step,
Resume, etc.) so that you can observe the location of each mine. Use pen
and paper to draw a diagram of the board, and mark the location of each
mine as you observe the algorithm placing it. There should only be 10
mines in Beginner mode. Once the mines are placed, press <img
src="images/resume.png" alt="resume" /> Resume. Check you work by
winning the game. Alternatively, you can intentionally lose to have the
game reveal the mines.</p>
</section>
<section id="optional-exercise-replicate-the-boards-forward-engineering"
class="level3">
<h3>Optional Exercise: Replicate the Boards (Forward Engineering)</h3>
<p>You will need a C development environment for this exercise. Because,
as we have now confirmed, <code>termmines</code> is importing its random
number generator from the system, we can write a program that uses that
same generator. Further, because we can capture the seed, and we know
the placement algorithm, we can perfectly replicate the sequence of game
boards for any <code>termmines</code> session.</p>
<p>Write a program that takes a seed from the user and prints a diagram
of the first game board with the mines indicated. Optionally, have it
print each subsequent game board when the user presses ENTER. Check your
work by re-launching <code>termmines</code> (see note about attaching
below), capturing its seed, inputting it into your program, and then
winning the game. Optionally, win 2 more games in the same session.</p>
<p><strong>NOTE</strong>: We will need a more advanced attaching
technique to check your work, because you will need both to break on
<code>srand</code> (which happens early in the process execution,
ruling out our usual attach technique) and to interact with it in the
terminal (which rules out launching in Ghidra). There are a few ways
around this, including using <code>gdbserver</code> or using
<code>set inferior-tty</code>. If you are already familiar with those,
you can try one. The technique we recommend here is using a stub that
will suspend itself and then execute <code>termmines</code>. We can then
run the stub in a terminal outside of Ghidra, attach to that stub, and
then allow it to proceed into <code>termmines</code>. In this way, we
can attach to the process in the terminal before it reaches
<code>srand</code>. The stub is fairly easy to write in Bash and should
be similar in other shells.</p>
<ol type="1">
<li><p>In a terminal running Bash (see note if youre using
<code>anyptracer</code>):</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">(</span><span class="bu">echo</span> <span class="va">$BASHPID</span><span class="kw">;</span> <span class="bu">kill</span> <span class="at">-SIGSTOP</span> <span class="va">$BASHPID</span><span class="kw">;</span> <span class="bu">exec</span> ./termmines<span class="kw">)</span></span></code></pre></div>
<p>The parentheses will start <code>bash</code> in a new subprocess. The
first two commands cause it to print its own process ID and then suspend
itself. Your terminal should display the PID and report the stopped
process.</p>
<p><strong>NOTE</strong>: If you need to use the <code>anyptracer</code>
stub, then the invocation is more complicated:</p>
<div class="sourceCode" id="cb4"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">./anyptracer</span> <span class="st">&#39;exec bash -c &quot;echo $BASHPID; kill -SIGSTOP $BASHPID; exec ./termmines&quot;&#39;</span></span></code></pre></div>
<p>In principle, it works the same except wrapped in the
<code>anyptracer</code> stub. The parentheses are no longer needed, nor
allowed, since <code>anyptracer</code> is already a subprocess of your
shell. If you include parentheses, you will get a second sub-subprocess
to which you cannot attach.</p></li>
<li><p>In Ghidra, follow the usual steps to attach, but use the PID
printed in your terminal. <strong>NOTE</strong>: The process is still
technically running <code>bash</code> when you attach to it.</p></li>
<li><p>In the Interpreter panel:</p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">break</span> main</span></code></pre></div>
<p><strong>NOTE</strong>: At this point <code>main</code> technically
refers to the symbol in <code>bash</code>, but GDB will adjust its
location once the target loads <code>termmines</code>.</p></li>
<li><p>Back in your terminal running Bash:</p>
<div class="sourceCode" id="cb6"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="bu">fg</span></span></code></pre></div>
<p>This will cause Bash to return the stub to the foreground of the
terminal. Without this step, the system will repeatedly suspend the
process whenever it attempts any I/O on that terminal.</p></li>
<li><p>In Ghidra, press Resume (or use <code>continue</code> in the
Interpreter) until you hit the breakpoint at <code>main</code>. This
permits the stub to complete its third command
<code>exec ./termmines</code>. The <code>exec</code> command is
different than normal command execution. Instead of creating a
subprocess, it <em>replaces</em> the image of the stub process, so the
process is now running <code>termmines</code>.</p></li>
<li><p>Refresh the Modules node in the Objects window. You may need to
clear your filter text. Expand the Modules node and verify it lists
<code>termmines</code> instead of <code>bash</code>. Without this step,
your listings may go out of sync.</p></li>
</ol>
<p>At this point, you are attached to your target running in the
terminal, and you have trapped it at <code>main</code>. Because you were
attached to it when it was still <code>bash</code>, you will likely see
a lot of extraneous history. For example, the Modules panel will report
many of the modules that had been loaded by <code>bash</code>. Please
note their lifespans, however. They should correctly indicate those
modules are no longer loaded. In any case, you now have the tools needed
to check your work for this exercise.</p>
</section>
</section>
</section>
</body>
</html>

View file

@ -0,0 +1,251 @@
# Using Breakpoints
This module assumes you know how to launch `termmines` in Ghidra using GDB and know where to find the basic Debugger GUI components.
If not, please refer to the previous modules.
This module will address the Breakpoints window in more depth.
While the breakpoint manager is able to deal with a system of targets, we will only deal with a single target at a time.
## Breakpoints
Most likely, this window is empty if you have been following the lesson.
![The breakpoints window](images/Breakpoints_EmptyAfterLaunch.png)
From here, you can toggle and delete existing breakpoints.
There are several ways to set a new breakpoint:
1. From any static or dynamic listing window, including Disassembly, Memory/Hex, and the Decompiler, right-click and select ![set breakpoint](images/breakpoint-enable.png) Set Breakpoint, press **K** on the keyboard, or double-click the margin.
1. From the Objects window click the ![add breakpoint](images/breakpoint-enable.png) Add Breakpoint button or press **F3** on the keyboard.
1. From the Interpreter window, use the GDB command, e.g., `break main`.
The advantage of using the listings is that you can quickly set a breakpoint at any address.
The advantage of using the Objects or Interpreter window is that you can specify something other than an address.
Often, those specifications still resolve to addresses, and Ghidra will display them.
Ghidra will memorize breakpoints by recording them as special bookmarks in the imported program.
There is some iconography to communicate the various states of a breakpoint.
When all is well and normal, you should only see enabled ![enabled breakpoint](images/breakpoint-enable.png) and disabled ![disabled breakpoint](images/breakpoint-disable.png) breakpoints.
If the target is terminated (or not launched yet), you may also see ineffective ![ineffective breakpoint](images/breakpoint-enable-ineff.png) breakpoints.
## Examining Minesweeper Board Setup
Suppose we want to cheat at `termmines`.
We might like to understand how the mines are placed.
Knowing that the mines are placed randomly, we might hypothesize that it is using the `srand` and `rand` functions from the C standard library.
While we can test that hypothesis by examining the imports statically, we might also like to record some actual values, so we will approach this dynamically.
(This is the Debugger course, after all.)
The breakpoint on `srand` will allow us to capture the random seed.
The breakpoint on `rand` will help us find the algorithm that places the mines.
### Set the Breakpoints
In the Interpreter, type the GDB commands to set breakpoints on `srand` and `rand`:
```gdb
break srand
break rand
```
The breakpoint window should now be updated:
![Populated breakpoints window](images/Breakpoints_PopAfterSRandRand.png)
For a single target, the lower panel of the Breakpoints window does not add much information, but it does have some.
We will start with the top panel.
This lists the "logical" breakpoints, preferring static addresses.
* The left-most column **Enabled** indicates the breakpoint's state.
Here, we see the inconsistent ![inconsistent](images/breakpoint-overlay-inconsistent.png) overlay, because Ghidra cannot save the breakpoint without a module image.
That is because `srand` and `rand` are in a different module, and we have not yet imported it into Ghidra.
* The next column **Name** is the name of the breakpoint.
This is for informational purposes only.
You can rename a breakpoint however you like, and it will have no effect on the target nor back-end debugger.
* The next column **Address** gives the address of the breakpoint.
Notice that the addresses were resolved, even though the breakpoints were specified by symbol.
Typically, this is the *static* address of the breakpoint; however, if the module image is not imported, yet, this will be the *dynamic* address, subject to relocation or ASLR.
* The next column **Image** gives the name of the imported image containing the breakpoint.
Again, because the module has not been imported yet, this column is blank.
* The next column **Length** gives the length of the breakpoint.
In GDB, this generally applies to watchpoints only.
* The next column **Kinds** gives the kinds of breakpoint.
Most breakpoints are software execution breakpoints, indicated by "SW_EXECUTE."
That is, they are implemented by patching the target's memory with a special instruction (`INT3` on x86) that traps execution.
There are also hardware execution breakpoints indicated by "HW_EXECUTE," and access breakpoints indicated by "HW_READ" and/or "HW_WRITE".
**NOTE**: GDB would call these "watchpoints."
An advantage to software breakpoints is that you can have a practically unlimited number of them. Some disadvantages are they can be detected easily, and they are limited to execution breakpoints.
* The next column **Locations** counts the number of locations for the breakpoint.
For a single-target session, this should always be 1.
* The final column **Sleigh** is only applicable to the emulator.
It indicates that the breakpoint's behavior has been customized with Sleigh code.
This is covered in [Emulation](B2-Emulation.md).
Now, we move to the bottom panel.
This lists the breakpoint locations, as reported by the back-end debugger(s).
The Enabled, Address, and Sleigh columns are the same as the top, but for the individual *dynamic* addresses.
* The **Name** column is the name as designated by the back-end.
* The **Trace** column indicates which target contains the location.
The text here should match one of the tabs from the Threads panel.
* The **Comment** column is a user-defined comment.
Its default value is the specification that generated it, e.g., `srand`.
* The **Threads** column indicates if the breakpoint is scoped to a limited set of threads.
Its use is atypical.
### Toggling the Breakpoints
While there is no need to toggle the breakpoints right now, it is a good time to demonstrate the feature.
There are several ways to toggle a breakpoint:
1. In any listing, as in setting a breakpoint, right-click and select a toggle action, press **K** on the keyboard, or double-click its icon in the margin.
1. From the Objects window, expand the Breakpoints node, right-click a breakpoint and select Toggle or press **T** on the keyboard.
1. From the Breakpoints window, single-click the breakpoint's status icon, right-click an entry and select a toggle action, or create a selection and use a toggling action from the local toolbar.
Either panel works, but the top panel is preferred to keep the breakpoints consistent.
The local toolbar also has actions for toggling all breakpoints in the session.
1. From the Interpreter window, use the GDB commands, e.g., `disable 2`.
Practice toggling them.
Notice that no matter how you toggle the breakpoints, the display updates.
You might also type `info break` into the Interpreter to confirm the effect of toggling breakpoints in the GUI.
When you are finished, ensure both breakpoints are enabled.
### Importing `libc`
While the Debugger can operate without importing external modules, it generally works better when you have.
The symbols `srand` and `rand` are in `libc`.
If you would like to save the breakpoints we placed on them, you must import the module.
You could do this in the usual manner, but the Debugger offers a convenient way to import missing modules.
1. Navigate to a dynamic address that would be mapped to the missing module.
For our scenario, the easiest way to do that is to double-click an address in the Breakpoints window.
Either one points somewhere in `libc`.
1. Check the Debug Console window for a note about the missing module:
![Missing module note in the debug console](images/Breakpoints_MissingModuleNote.png)
1. Click the import button &mdash; leftmost of the remedial actions.
It will display a file browser pointed at the library file.
1. Proceed with the import and initial analysis as you would in the CodeBrowser.
Once imported, the Breakpoints window should update to reflect the static addresses, the breakpoints should become consistent, and the Static Listing should now be synchronized when navigating within `libc`.
![The debugger tool with breakpoints synchronized after importing libc](images/Breakpoints_SyncedAfterImportLibC.png)
### Capturing the Random Seed
We can now allow `termmines` to execute, expecting it to hit the `srand` breakpoint first.
Click ![resume](images/resume.png) Resume.
If all goes well, the target should break at `srand`.
If you have never written code that uses `srand` before, you should briefly read its manual page.
It takes a single parameter, the desired seed.
That parameter contains the seed this very moment!
We can then examine the value of the seed by hovering over `param_1` in the decompiler.
![Seed value in decompiler hover](images/Breakpoints_SeedValueAfterBreakSRand.png)
We will cover other ways to examine memory and registers in the [Machine State](A4-MachineState.md) module.
We have contrived `termmines` so that its random seed will always start with `0x5eed____`.
If you see that in the value displayed, then you have successfully recovered the seed.
This seed will be used in an optional exercise at the end of this module.
You might write it down; however, if you re-launch `termmines` between now and then, you will have a different seed.
### Locating the Mine Placement Algorithm
Press ![resume](images/resume.png) Resume again.
This time, the target should break at `rand`.
We are not interested in the `rand` function itself, but rather how the placement algorithm is using it.
Press ![step out](images/stepout.png) Step Out to allow the target to return from `rand`.
If you still have the Decompiler up, you should be in a code block resembling:
```c {.numberLines}
while (iVar2 = DAT_00604164, iVar1 = DAT_00604160, iVar10 < _DAT_00604168) {
iVar3 = rand();
iVar2 = DAT_00604164;
iVar11 = rand();
lVar7 = (long)(iVar11 % iVar2 + 1) * 0x20 + (long)(iVar3 % iVar1 + 1);
bVar14 = *(byte *)((long)&DAT_00604160 + lVar7 + 0x1c);
if (-1 < (char)bVar14) {
iVar10 = iVar10 + 1;
*(byte *)((long)&DAT_00604160 + lVar7 + 0x1c) = bVar14 | 0x80;
}
}
```
If you are thinking, "I could have just found `rand` in the symbol table and followed its XRefs," you are correct.
However, it is useful to use a dynamic debugging session to drive your analysis chronologically through execution of the target, even if much of that analysis is still static.
The advantages of a dynamic session along side static analysis should become more apparent as you progress through this course.
### Exercise: Diagram the Mines
You goal is to capture the location of all the mines.
So that you can check your work later, you should run `termmines` in a terminal and attach to it from Ghidra.
You will probably want to disable the breakpoints on `rand` and `srand` for now.
Devise a strategy using breakpoints and the control buttons (Step, Resume, etc.) so that you can observe the location of each mine.
Use pen and paper to draw a diagram of the board, and mark the location of each mine as you observe the algorithm placing it.
There should only be 10 mines in Beginner mode.
Once the mines are placed, press ![resume](images/resume.png) Resume.
Check you work by winning the game.
Alternatively, you can intentionally lose to have the game reveal the mines.
### Optional Exercise: Replicate the Boards (Forward Engineering)
You will need a C development environment for this exercise.
Because, as we have now confirmed, `termmines` is importing its random number generator from the system, we can write a program that uses that same generator.
Further, because we can capture the seed, and we know the placement algorithm, we can perfectly replicate the sequence of game boards for any `termmines` session.
Write a program that takes a seed from the user and prints a diagram of the first game board with the mines indicated.
Optionally, have it print each subsequent game board when the user presses ENTER.
Check your work by re-launching `termmines` (see note about attaching below), capturing its seed, inputting it into your program, and then winning the game.
Optionally, win 2 more games in the same session.
**NOTE**: We will need a more advanced attaching technique to check your work, because you will need both to break on `srand` (which happens early in the process' execution, ruling out our usual attach technique) and to interact with it in the terminal (which rules out launching in Ghidra).
There are a few ways around this, including using `gdbserver` or using `set inferior-tty`.
If you are already familiar with those, you can try one.
The technique we recommend here is using a stub that will suspend itself and then execute `termmines`.
We can then run the stub in a terminal outside of Ghidra, attach to that stub, and then allow it to proceed into `termmines`.
In this way, we can attach to the process in the terminal before it reaches `srand`.
The stub is fairly easy to write in Bash and should be similar in other shells.
1. In a terminal running Bash (see note if you're using `anyptracer`):
```bash
(echo $BASHPID; kill -SIGSTOP $BASHPID; exec ./termmines)
```
The parentheses will start `bash` in a new subprocess.
The first two commands cause it to print its own process ID and then suspend itself.
Your terminal should display the PID and report the stopped process.
**NOTE**: If you need to use the `anyptracer` stub, then the invocation is more complicated:
```bash
./anyptracer 'exec bash -c "echo $BASHPID; kill -SIGSTOP $BASHPID; exec ./termmines"'
```
In principle, it works the same except wrapped in the `anyptracer` stub.
The parentheses are no longer needed, nor allowed, since `anyptracer` is already a subprocess of your shell.
If you include parentheses, you will get a second sub-subprocess to which you cannot attach.
1. In Ghidra, follow the usual steps to attach, but use the PID printed in your terminal.
**NOTE**: The process is still technically running `bash` when you attach to it.
1. In the Interpreter panel:
```gdb
break main
```
**NOTE**: At this point `main` technically refers to the symbol in `bash`, but GDB will adjust its location once the target loads `termmines`.
1. Back in your terminal running Bash:
```bash
fg
```
This will cause Bash to return the stub to the foreground of the terminal.
Without this step, the system will repeatedly suspend the process whenever it attempts any I/O on that terminal.
1. In Ghidra, press Resume (or use `continue` in the Interpreter) until you hit the breakpoint at `main`.
This permits the stub to complete its third command `exec ./termmines`.
The `exec` command is different than normal command execution.
Instead of creating a subprocess, it *replaces* the image of the stub process, so the process is now running `termmines`.
1. Refresh the Modules node in the Objects window.
You may need to clear your filter text.
Expand the Modules node and verify it lists `termmines` instead of `bash`.
Without this step, your listings may go out of sync.
At this point, you are attached to your target running in the terminal, and you have trapped it at `main`.
Because you were attached to it when it was still `bash`, you will likely see a lot of extraneous history.
For example, the Modules panel will report many of the modules that had been loaded by `bash`.
Please note their lifespans, however.
They should correctly indicate those modules are no longer loaded.
In any case, you now have the tools needed to check your work for this exercise.

View file

@ -0,0 +1,658 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Ghidra Debugger</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
</style>
<link rel="stylesheet" href="style.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="nav"><a
class="beginner" href="A1-GettingStarted.html">Getting Started</a><a
class="beginner" href="A2-UITour.html">UI Tour</a><a
class="beginner" href="A3-Breakpoints.html">Breakpoints</a><a
class="beginner" href="A4-MachineState.html">Machine State</a><a
class="beginner" href="A5-Navigation.html">Navigation</a><a
class="beginner" href="A6-MemoryMap.html">Memory Map</a><a
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a>
</header>
<header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#examining-machine-state-memory-registers-and-variables"
id="toc-examining-machine-state-memory-registers-and-variables">Examining
Machine State: Memory, Registers, and Variables</a>
<ul>
<li><a href="#machine-state" id="toc-machine-state">Machine State</a>
<ul>
<li><a href="#patching" id="toc-patching">Patching</a></li>
</ul></li>
<li><a href="#the-dynamic-listing" id="toc-the-dynamic-listing">The
Dynamic Listing</a>
<ul>
<li><a href="#cache-status-indication"
id="toc-cache-status-indication">Cache Status Indication</a></li>
<li><a href="#address-tracking" id="toc-address-tracking">Address
Tracking</a></li>
<li><a href="#module-region-indicator"
id="toc-module-region-indicator">Module / Region Indicator</a></li>
<li><a href="#go-to" id="toc-go-to">Go To</a></li>
<li><a href="#compare" id="toc-compare">Compare</a></li>
<li><a href="#exercise-reverse-engineer-the-board"
id="toc-exercise-reverse-engineer-the-board">Exercise: Reverse Engineer
the Board</a></li>
</ul></li>
<li><a href="#the-memory-viewer" id="toc-the-memory-viewer">The Memory
Viewer</a>
<ul>
<li><a href="#exercise-display-the-board-in-hex"
id="toc-exercise-display-the-board-in-hex">Exercise: Display the Board
in Hex</a></li>
</ul></li>
<li><a href="#registers" id="toc-registers">Registers</a>
<ul>
<li><a href="#exercise-reduce-the-mines"
id="toc-exercise-reduce-the-mines">Exercise: Reduce the Mines</a></li>
</ul></li>
<li><a href="#watches" id="toc-watches">Watches</a></li>
<li><a href="#sleigh-expressions" id="toc-sleigh-expressions">Sleigh
Expressions</a>
<ul>
<li><a href="#variables-and-constants"
id="toc-variables-and-constants">Variables and Constants</a></li>
<li><a href="#operators" id="toc-operators">Operators</a></li>
<li><a href="#exercise-find-and-modify-the-board-dimensions"
id="toc-exercise-find-and-modify-the-board-dimensions">Exercise: Find
and Modify the Board Dimensions</a></li>
<li><a href="#exercise-watch-the-cell-to-be-mined"
id="toc-exercise-watch-the-cell-to-be-mined">Exercise: Watch the Cell to
be Mined</a></li>
</ul></li>
<li><a href="#variable-hovers" id="toc-variable-hovers">Variable
Hovers</a></li>
</ul></li>
</ul>
</nav>
<section id="examining-machine-state-memory-registers-and-variables"
class="level1">
<h1>Examining Machine State: Memory, Registers, and Variables</h1>
<p>This module assumes you know how to launch <code>termmines</code> in
Ghidra using GDB, and know where to find the basic Debugger GUI
components. It also assumes you know the basic control commands, e.g.,
Resume, Step, and Interrupt; as well as some basics for breakpoints. If
not, please refer to the previous modules.</p>
<p>This module will address the following features in more depth:</p>
<ul>
<li>Dynamic Listing window</li>
<li>Dynamic Bytes window</li>
<li>Registers window</li>
<li>Watches window</li>
<li>Sleigh expressions</li>
<li>Variable value hovers</li>
</ul>
<section id="machine-state" class="level2">
<h2>Machine State</h2>
<p>There are at least two ways to define machine state. One way based on
a high-level understanding of a program is the collective values of all
variables. Another based on a low-level understanding of a program is
the collective values of all memory and all registers of all threads.
Because Ghidra is primarily concerned with examining software at the low
level, albeit to obtain a high-level understanding, we will generally
stick with the low-level definition. One could argue that machine state
also includes the underlying system and hardware, including peripherals.
When debugging user-space applications, Ghidra cannot generally inspect
the underlying system, except for a few things granted by the back-end
debugger, e.g., the virtual memory map. Thus, as far as we are
concerned, the machine state does not include the underlying system, but
it is still something you must be aware of while debugging.</p>
<p>Note that we also treat <em>registers</em> as something separate from
<em>memory</em>. While technically, one could argue registers are the
closest and smallest memory to the CPU, we think of memory as an
addressable array, e.g., DIMMs and flash ROMs. If applicable,
<em>memory-mapped</em> registers are considered part of memory by
Ghidra.</p>
<p>The Ghidra Debugger provides mechanisms for viewing and modifying
machine state at both the low and high levels. The Dynamic Listing and
Memory viewer provide access to memory, the Registers window provides
access to registers, and the Watches window provides access to memory
and registers via expressions. The only means of accessing high-level
variables is through value hovers, which are available in all listings
and the Decompiler.</p>
<section id="patching" class="level3">
<h3>Patching</h3>
<p>Many of the features allow you to edit the values, i.e.,
<em>patch</em> the live target. The windows that are adaptations of
their static counterparts work more or less as expected. Edits in any
<em>dynamic</em> window will attempt to patch the live target. Edits in
any <em>static</em> window will <strong>not</strong> patch the live
target; they modify the program database as usual. Some dynamic windows
may present tables with editable cells. These will often include a
<em>write-lock</em> toggle, like the static Byte viewer, to avoid
accidental patching. Furthermore, you must use the <img
src="images/record.png" alt="control mode" /> Control Mode toggle in the
global toolbar (next to the control actions) to enable patching
throughout the Debugger tool. For now, please only use the “Control
Target” and “Control Target w/ Edits Disabled” options. The write toggle
is included here so that actions like copy-paste do not lead to
accidental patching.</p>
</section>
</section>
<section id="the-dynamic-listing" class="level2">
<h2>The Dynamic Listing</h2>
<p>Up to this point, we have only used the Dynamic Listing to display
the instructions at the program counter. It can actually view and mark
up any part of the machine state in memory, e.g., mapped images, heap
pages, stack pages. Where the memory is mapped to images, Ghidra
attempts to synchronize the Static Listing and, by extension, the other
static analysis windows. The Dynamic Listing has most of the same
features as the Static Listing.</p>
<p>Re-launch <code>termmines</code> and then navigate to
<code>rand</code>. You may notice that the Static Listing has
disassembly, but the Dynamic Listing does not. This is because Ghidra
has not observed the program counter at <code>rand</code> yet, so it has
not automatically disassembled it. To manually disassemble in the
Dynamic Listing, place your cursor where you expect an instruction and
press <strong>D</strong>, just like you would in the Static Listing.</p>
<figure>
<img src="images/State_ListingAfterCallRand.png"
alt="The dynamic listing after a call to rand" />
<figcaption aria-hidden="true">The dynamic listing after a call to
rand</figcaption>
</figure>
<p>This action differs in that it does not follow flow. It proceeds
linearly, stopping at any control transfer instruction.</p>
<p>Now, we will examine the stack segment. Click the <img
src="images/register-marker.png" alt="location tracking" /> Track
Location drop-down and select <strong>Track Stack Pointer</strong>. The
window should seek to (and highlight in pale green) the address in the
stack pointer. Since the target has just entered <code>main</code>, we
should expect a return address at the top of the stack. With your cursor
at the stack pointer, press <strong>P</strong> to place a pointer there,
just like you would in the Static Listing. You can now navigate to that
address by double-clicking it. To return to the stack pointer, you can
use the back arrow in the global toolbar, you can click the <img
src="images/register-marker.png" alt="track location" /> Track Location
button, or you can double-click the <code>sp = [Address]</code> label in
the top right of the Dynamic Listing.</p>
<p>To examine a more complicated stack segment, we will break at
<code>rand</code>. Ensure your breakpoint at <code>rand</code> is
enabled and press <img src="images/resume.png" alt="resume" /> Resume.
Your Dynamic Listing should follow the stack pointer. In the menus,
select <strong>Debugger → Analysis → Unwind Stack</strong> or press
<strong>U</strong>.</p>
<figure>
<img src="images/State_ListingStackAfterCallRand.png"
alt="The dynamic listing of the stack after a call to rand" />
<figcaption aria-hidden="true">The dynamic listing of the stack after a
call to rand</figcaption>
</figure>
<p><strong>NOTE</strong>: We will cover the Stack window later in the
course, which is probably a more suitable way to navigate stack frames.
It is populated by the back-end debugger, which can usually unwind the
stack more reliably than Ghidra. The Unwind Stack action is useful when
you want an in-depth understanding of the actual contents of the stack,
or when you are emulating.</p>
<p>Now, switch back to <strong>Track Program Counter</strong>. If you
would like to track both the Program Counter and the Stack Pointer,
click the <img src="images/camera-photo.png" alt="clone" /> Clone button
in the local toolbar. Like the Static Listing, this clones an instance
of the Dynamic Listing, which you can configure differently than the
primary Dynamic Listing. Only the primary Dynamic Listing will
synchronize, and it does so only with the primary Static Listing.
<strong>NOTE</strong>: For the sake of disambiguation, we will use the
term <em>clone</em>, not <em>snapshot</em> when referring to multiple
instances of a Ghidra window. While this is inconsistent with the Ghidra
Beginner course materials, it is necessary to avoid confusion with
snapshots of the machine state, discussed later in this module.</p>
<p>The dynamic listing offers several additional features:</p>
<section id="cache-status-indication" class="level3">
<h3>Cache Status Indication</h3>
<p>The listings contents are read from a live target, which may become
unresponsive or otherwise temperamental. The Debugger uses a database,
which acts as a cache separating the GUI from the live target. The UI
requests memory pages from the target, the target asynchronously
retrieves those pages and stores them into the database, then the
database updates the UI. This sequence does not always go as expected;
thus, pages with stale data are displayed with a grey background. This
may also happen if auto-read is disabled. Typically, user-space targets
are not so temperamental, but others may be, or memory reads could be
expensive, in which case disabling automatic memory reads may be
advantageous. If the back-end debugger reports an error while reading
memory, the first address of the page will have a red background. To
refresh the visible or selected page(s), click the
<img alt="refresh" src="images/view-refresh.png" width="16px"/> Refresh
button. Examine the Debug Console window for errors / warnings before
spamming this button. To toggle auto read, use the
<img src="images/autoread.png" alt="auto-read" width="16px"/> Auto-Read
drop-down button from the local toolbar.</p>
</section>
<section id="address-tracking" class="level3">
<h3>Address Tracking</h3>
<p>We have already demonstrated this, but there are some finer details.
Some of the tracking options depend on the Watches window, discussed
later in this module. On occasion, the location cannot be displayed in
the listing, typically because it falls outside of the memory map. If
this happens, the address label at the top right of the listing will
have red text.</p>
</section>
<section id="module-region-indicator" class="level3">
<h3>Module / Region Indicator</h3>
<p>In the top left a label will display the name of the section
containing the cursor. If there is no containing section, it will fall
back to the containing module and then to the containing region. Rarely,
this label will be empty. This can happen when the cursor is outside any
known region, which only happens if you configure Ghidra to ignore the
memory map.</p>
</section>
<section id="go-to" class="level3">
<h3>Go To</h3>
<p>The Go To action in the Dynamic Listing differs from the one in the
Static Listing. Like the static one, it accepts an address in
hexadecimal, possibly prefixed with the address space and a colon.
However, it also accepts Sleigh expressions, allowing you to treat
<code>RAX</code> as a pointer and go to that address, for example. We
cover Sleigh expressions later in this module.</p>
</section>
<section id="compare" class="level3">
<h3>Compare</h3>
<p>The Compare action in the Dynamic Listing also differs from the one
in the Static Listing. It allows the comparison of two machine state
snapshots, covered in the <a href="A5-Navigation.html">Navigation</a>
module.</p>
</section>
<section id="exercise-reverse-engineer-the-board" class="level3">
<h3>Exercise: Reverse Engineer the Board</h3>
<p>All of the features in the default CodeBrowser tool are also in the
default Debugger tool, providing you Ghidras full suite of static
analysis tools during your dynamic sessions, albeit they are not as
immediately accessible. Your task is to reverse engineer the game
boards layout in memory. Because you are in a dynamic session, you have
an example board to work with. As you navigate the <code>.data</code>
section of <code>termmines</code> in the Static Listing, the Dynamic
Listing will follow along showing you the live values in memory. You can
also experiment by placing code units in the Dynamic Listing before
committing to them in the Static Listing.</p>
<section id="questions" class="level4">
<h4>Questions:</h4>
<ol type="1">
<li>How are the cells allocated?</li>
<li>How are the cells indexed? Row major, color major? 0-up, 1-up?</li>
<li>What is happening around the “border” of the board? Why might the
programmer have chosen this design?</li>
</ol>
</section>
</section>
</section>
<section id="the-memory-viewer" class="level2">
<h2>The Memory Viewer</h2>
<figure>
<img src="images/State_BytesStackAfterCallRand.png"
alt="The dynamic memory view of the stack after a call to rand" />
<figcaption aria-hidden="true">The dynamic memory view of the stack
after a call to rand</figcaption>
</figure>
<p>Just as the Dynamic Listing is the analog of the Static Listing, the
Memory viewer is the analog of the Byte viewer. It is not visible by
default. To open it, use <strong>Windows → Byte Viewer → Memory
</strong> in the menus. Its default configuration should be Auto PC,
the same as the Dynamic Listings default. It has all the same
additional Debugger features as the Dynamic Listing. Furthermore, bytes
that have changed are displayed in red text.</p>
<section id="exercise-display-the-board-in-hex" class="level3">
<h3>Exercise: Display the Board in Hex</h3>
<p>This is a bit quick and dirty, but it works and can be useful. Your
task is to configure the Memory viewer so that (within the memory
allocated to the board) the rows and columns of the Memory viewer
correspond to the rows and columns of the game board.
<strong>TIP</strong>: Use the <em>Alignment Address</em> and <em>Bytes
Per Line</em> settings.</p>
</section>
</section>
<section id="registers" class="level2">
<h2>Registers</h2>
<figure>
<img src="images/State_RegistersAfterCallRand.png"
alt="The registers after a call to rand" />
<figcaption aria-hidden="true">The registers after a call to
rand</figcaption>
</figure>
<p>The Registers window gives a view of all the registers on the target
and their current values. The register set can be very large, so there
are a few ways to sift and sort. As in most Ghidra tables, you can
filter using the box below the registers table. Additionally, you can
use the column headers to sort. The columns are:</p>
<ul>
<li>The <strong>Favorite</strong> column indicates which registers are
your favorite. By default, this includes the instruction pointer and the
stack pointer. You can quickly choose your favorite(s) by toggling the
check boxes in this column. Because this column is sorted by default,
your favorites are positioned at the top.</li>
<li>The <strong>Number</strong> column gives the number assigned to the
register by Ghidras processor specification. This is mostly just to
make the default sorting deterministic.</li>
<li>The <strong>Name</strong> column gives Ghidras name for the
register. This usually matches the name given by the back-end debugger,
but may not. For example, on x86-64, what Ghidra calls
<code>rflags</code> GDB calls <code>eflags</code>.</li>
<li>The <strong>Value</strong> column gives the registers current value
in hexadecimal. Values in gray are stale. Values in red have changed.
Right-clicking this column will present options to Go To the address, as
if the register were a pointer.</li>
<li>The <strong>Type</strong> column allows you to assign a type to,
i.e., create a data unit on, the register. This has more utility for
float types than integers, but it may still help you record what you
know about how a register is being used.</li>
<li>The <strong>Representation</strong> column displays the registers
value according to its assigned type, if applicable. If the type is a
pointer, then double-clicking this value will go to the address in the
Dynamic Listing.</li>
</ul>
<p>If you would like to adjust the list of registers in the table, use
the <img src="images/select-registers.png" alt="select registers" />
Select Registers button in the local toolbar. This will present all the
registers in Ghidras processor specification, including those which are
just artifacts of Sleigh. Typically, this is not necessary, since the
table will include all registers recognized by both Ghidra and the
back-end debugger. Nevertheless, if you believe a register is missing,
it is wise to check this selection.</p>
<section id="exercise-reduce-the-mines" class="level3">
<h3>Exercise: Reduce the Mines</h3>
<p>If you have not already reverse engineered the mine placement
algorithm, do that now. Think up a strategy you might employ, by
patching a register, to reduce the number of mines placed on the board.
The strategy need not result in a permanent change. It should only
affect the round being set up. For this exercise, you cannot patch
memory, but you may place a breakpoint. Verify your work by playing the
round.</p>
</section>
</section>
<section id="watches" class="level2">
<h2>Watches</h2>
<figure>
<img src="images/State_WatchesInCallSRand.png"
alt="The watches window in a call to srand" />
<figcaption aria-hidden="true">The watches window in a call to
srand</figcaption>
</figure>
<p>The Watches window gives the values of several user-specified Sleigh
expressions. This can provide an alternative to the Registers window
when you are really only interested in a couple of registers. It can
also watch values in memory. Furthermore, when a watch has a memory
address, the expression will appear as an option in the Location
Tracking menus of the Listing and Memory viewers. Selecting that option
will cause the window to follow that watch as its address changes.</p>
<p>To add a watch, click the <img src="images/add.png" alt="add" /> Add
button. A new entry will appear. Double-click the left-most cell of the
row to set or edit the Sleigh expression. For starters, try something
like <code>RDI</code>. (Conventionally, this is the location for the
first parameter on Linux x86-64 systems.) The context menus for the
Listing and Registers windows include a “Watch” action, which adds the
current selection to the Watches window.</p>
<p>The columns are:</p>
<ul>
<li>The <strong>Expression</strong> column is the user-defined Sleigh
expression.</li>
<li>The <strong>Address</strong> column is the address of the resulting
value, if applicable. This may be in <code>register</code> space.
Double-clicking this cell will go to the address in the Dynamic
Listing.</li>
<li>The <strong>Symbol</strong> column gives the symbol in a mapped
static image closest to or containing the address, if applicable.</li>
<li>The <strong>Value</strong> column gives the “raw” value of the
expression. If the result is in memory, it displays a byte array;
otherwise, it displays an integer.</li>
<li>The <strong>Type</strong> and <strong>Representation</strong>
columns work the same as in the Registers window, except they do
<em>not</em> save the data unit to the database. This has more uses than
the Registers window. For example, try <code>*:30 RDI</code> and set
this to <code>TerminatedCString</code>. Whenever <code>RDI</code> is a
string pointer, this will display the string up to 30 characters.</li>
<li>The <strong>Error</strong> column reports any errors in compiling or
evaluating the expression.</li>
</ul>
</section>
<section id="sleigh-expressions" class="level2">
<h2>Sleigh Expressions</h2>
<p>Watches and Go-To commands are expressed using Ghidras Sleigh
language. More precisely, expressions are the sub-language of Sleigh for
the right-hand side of assignment statements in semantic sections. If
you already know this language, then there is little more to learn. Of
note, you may use labels from mapped program images in your expression.
For example, to see how far a return address is into <code>main</code>,
you could use <code>*:8 RSP - main</code>.</p>
<p>For the complete specification, see the Semantic Section in the <a
href="../../../Ghidra/Features/Decompiler/src/main/doc/sleigh.xml">Sleigh
documentation</a>.</p>
<p>Sleigh is a bit unconventional in that its operators are typed rather
than its variables. All variables are fix-length bit vectors. Their
sizes are specified in bytes, but they have no other type
information.</p>
<section id="variables-and-constants" class="level3">
<h3>Variables and Constants</h3>
<p>Here are some examples of things you can reference by name:</p>
<ul>
<li><strong>Register</strong>: <code>RAX</code></li>
<li><strong>Label</strong>: <code>main</code></li>
<li><strong>Constant</strong>: <code>1234:8</code> or
<code>0x42d:8</code> — the value 1234 encoded as an 8-byte integer</li>
</ul>
<p>Registers vary by processor, but any register known to Ghidras
specification is allowed. (Due to limitations in Sleigh, you cannot
refer to the <code>contextreg</code> or any of its sub-registers.) A
label may come from any Ghidra program database that is mapped to the
current target. Due to limitations in Sleigh, you cannot specify a
labels namespace. The compiler will search only by name and select
arbitrarily from multiple matches.</p>
</section>
<section id="operators" class="level3">
<h3>Operators</h3>
<p>Here we will demonstrate each operator by example:</p>
<ul>
<li><strong>Integer Addition</strong>: <code>RAX + RCX</code></li>
<li><strong>Integer Subtraction</strong>: <code>RAX - RCX</code></li>
<li><strong>Integer Negation</strong>: <code>-RAX</code></li>
<li><strong>Integer Multiplication</strong>: <code>RAX * RCX</code></li>
<li><strong>Unsigned Integer Division</strong>:
<code>RAX / RCX</code></li>
<li><strong>Unsigned Integer Remainder</strong>:
<code>RAX % RCX</code></li>
<li><strong>Signed Integer Division</strong>:
<code>RAX s/ RCX</code></li>
<li><strong>Signed Integer Remainder</strong>:
<code>RAX s% RCX</code></li>
<li><strong>Left Shift</strong>: <code>RAX &lt;&lt; RCX</code></li>
<li><strong>Unsigned Right Shift</strong>:
<code>RAX &gt;&gt; RCX</code></li>
<li><strong>Signed Right Shift</strong>
<code>RAX s&gt;&gt; RCX</code></li>
<li><strong>Integer Comparison</strong>: <code>RAX == RCX</code> or
<code>RAX != RCX</code></li>
<li><strong>Unsigned Integer Comparison</strong>:
<code>RAX &lt; RCX</code> or <code>RAX &gt; RCX</code> or
<code>RAX &lt;= RCX</code> or <code>RAX &gt;= RCX</code></li>
<li><strong>Signed Integer Comparison</strong>:
<code>RAX s&lt; RCX</code> etc.</li>
<li><strong>Float Addition</strong>: <code>MM0 f+ MM1</code></li>
<li><strong>Float Subtraction</strong>: <code>MM0 f- MM1</code></li>
<li><strong>Float Negation</strong>: <code>f-MM0</code></li>
<li><strong>Float Multiplication</strong>: <code>MM0 f* MM1</code></li>
<li><strong>Float Division</strong>: <code>MM0 f/ MM1</code></li>
<li><strong>Float Absolute Value</strong>: <code>abs(MM0)</code></li>
<li><strong>Float Square Root</strong>: <code>sqrt(MM0)</code></li>
<li><strong>Float Comparison</strong>: <code>RAX f== RCX</code> or
<code>RAX f&lt; RCX</code> etc.</li>
<li><strong>Bitwise And</strong>: <code>RAX &amp; RCX</code></li>
<li><strong>Bitwise Or</strong>: <code>RAX | RCX</code></li>
<li><strong>Bitwise Xor</strong>: <code>RAX ^ RCX</code></li>
<li><strong>Bitwise Not</strong>: <code>~RAX</code></li>
<li><strong>Boolean And</strong>: <code>RAX &amp;&amp; RCX</code></li>
<li><strong>Boolean Or</strong>: <code>RAX || RCX</code></li>
<li><strong>Boolean Xor</strong>: <code>RAX ^^ RCX</code></li>
<li><strong>Boolean Not</strong>: <code>!RAX</code></li>
</ul>
<p><strong>NOTE</strong>: If the result of your expression is in
floating point, you will need to set the type of the watch accordingly.
The “raw” display will render the bit vector as an integer or byte
array. To read memory:</p>
<ul>
<li><strong>Dereference</strong>: <code>*:8 RSP</code> or
<code>*[ram]:8 RSP</code></li>
</ul>
<p><strong>NOTE</strong> The <code>[ram]</code> part is optional. On
x86, you will rarely if ever specify the space, since there is only one
physical RAM space. The <code>:8</code> part specifies the number of
bytes to read from memory. It is also optional, but only if the size can
be inferred from the rest of the expression. To manipulate variable
size:</p>
<ul>
<li><strong>Zero Extension</strong>: <code>RAX + zext(EBX)</code></li>
<li><strong>Sign Extension</strong>: <code>RAX + sext(EBX)</code></li>
<li><strong>Truncation</strong>: <code>RAX:4</code> — Equivalent to
<code>EAX</code></li>
<li><strong>Truncation</strong>: <code>AL + RBX(4)</code> — AL added to
the the 5th byte of RBX</li>
<li><strong>Bit Extraction</strong>: <code>RAX[7,8]</code> — Equivalent
to <code>AL</code></li>
</ul>
<p><strong>NOTE</strong>: The second form of truncation drops the
least-significant 4 bytes of RBX and takes as many of the remaining
bytes (1 in this case) as necessary to match size with AL.</p>
<p><strong>NOTE</strong>: Need for these next miscellaneous operators in
Watch expressions is rare:</p>
<ul>
<li><strong>Unsigned Carry</strong>: <code>carry(RAX,RBX)</code></li>
<li><strong>Signed Carry</strong>: <code>scarry(RAX,RBX)</code></li>
<li><strong>Signed Borrow</strong>: <code>sborrow(RAX,RBX)</code></li>
<li><strong>Float NaN</strong>: <code>nan(MM0)</code></li>
<li><strong>Convert Integer to Float</strong>:
<code>MM0 + int2float(RAX)</code> — Context required to infer the float
size</li>
<li><strong>Convert Float to Integer</strong>:
<code>RAX + trunc(MM0)</code> — Context required to infer the integer
size</li>
<li><strong>Convert Float Size</strong>:
<code>MM0 + float2float(MM0_Da)</code> — Context required to infer the
new float size</li>
<li><strong>Float Round Ceiling</strong>: <code>ceil(MM0)</code></li>
<li><strong>Float Round Floor</strong>: <code>floor(MM0)</code></li>
<li><strong>Float Round Nearest</strong>: <code>round(MM0)</code></li>
</ul>
</section>
<section id="exercise-find-and-modify-the-board-dimensions"
class="level3">
<h3>Exercise: Find and Modify the Board Dimensions</h3>
<p>Your task is to set up watches on the width and height of the game
board, and then use those watches to change the size of the board. This
may involve some trial and error, and it may not work perfectly due to
the way <code>ncurses</code> refreshes the screen. For this exercise,
patching memory is expected, and the change should last until the target
is terminated.</p>
<p><strong>TIP</strong>: If the <code>termmines</code> image is subject
to ASLR, and you want your watch expression to generalize over
re-launches, try using <code>main</code> as an anchor for the image
base.</p>
</section>
<section id="exercise-watch-the-cell-to-be-mined" class="level3">
<h3>Exercise: Watch the Cell to be Mined</h3>
<p>Your task is to watch the byte value of the cell that is about to
have a mine placed in it. You will probably want to set a breakpoint
somewhere in the mine placement algorithm. It is okay if the watch does
not <em>always</em> display the correct byte. However, it must be
correct whenever the program counter is at your breakpoint. Register
allocations are fairly volatile, and as a result, watch expressions that
refer to registers are only valid in a limited scope. The rest of the
time, even though the watch may evaluate successfully, its value may
have no real meaning. Check your work by observing the mine bit being
ORed in as you step the target.</p>
<p><strong>TIP</strong>: Try creating watches for the row and column
indices, first. Then, perhaps referring to the Decompiler, formulate the
expression that dereferences that cell in the board.</p>
</section>
</section>
<section id="variable-hovers" class="level2">
<h2>Variable Hovers</h2>
<p>You may have already used these if you completed the exercises in the
<a href="A3-Breakpoints.html">Breakpoints</a> module. If you hover over
a variable in any listing or the Decompiler, the Debugger will attempt
to evaluate it and display information about it. In some cases,
evaluation may involve unwinding the stack. Unwinding proceeds until the
Debugger finds an invocation of the function containing or defining the
variable. If unwinding fails, the Debugger may disregard the stack. In
dynamic windows, the Debugger generally disregards the stack. In static
windows, the Debugger still uses dynamic information to unwind the stack
and evaluate the variable. A variable may be any of the following:</p>
<ul>
<li>A register in the listing. In this case, the hover will report the
registers value in the functions frame.</li>
<li>A local or parameter in the listing. If the variable is allocated in
a register, this behaves the same as hovering that register, except with
additional information presented. If the variable is allocated in stack
space, this only succeeds if unwinding succeeds.</li>
<li>A global variable in the listing. Unwinding is unnecessary for
these.</li>
<li>A local or parameter in the Decompiler. This behaves similarly to
hovering a variable in the Static Listing.</li>
<li>A global in the Decompiler. This behaves similarly to hovering a
global variable in the Static Listing.</li>
<li>A field reference in the Decompiler. A field reference is
essentially a C expression in terms of other variables. This will
evaluate those variables and then evaluate the expression.</li>
</ul>
<p>Depending on the particular variable and other circumstances, the
hover will contain some combination of these rows:</p>
<ul>
<li><strong>Name</strong>: The name of the variable</li>
<li><strong>Type</strong>: The type of the variable</li>
<li><strong>Location</strong>: The static location of the variable,
e.g., <code>Stack[0x4]</code></li>
<li><strong>Status</strong>: A progress indicator</li>
<li><strong>Frame</strong>: If evaluation required unwinding, a
description of the frame used for context</li>
<li><strong>Storage</strong>: The dynamic, physical location of the
variable, e.g., <code>7fffffffe618</code></li>
<li><strong>Bytes</strong>: The raw bytes currently stored in the memory
allocated to the variable</li>
<li><strong>Integer</strong>: The “raw” integer value of the variable,
rendered with varyied signedness and radix</li>
<li><strong>Value</strong>: The value of the variable, according to its
type</li>
<li><strong>Instruction</strong>: If the variable points to code, the
target instruction</li>
<li><strong>Warnings</strong>: Warnings emitted during evaluation</li>
<li><strong>Error</strong>: If the value could not be evaluated, an
explanation or the exception</li>
</ul>
<p>The Name, Type, and Location entries are informational. They tell you
about the variable and its static definition. The Status, Frame, and
Storage entries are also informational, but tell you about the
variables dynamic evaluation. The Bytes, Integer, Value, and
Instruction entries tell you the dynamic value of the variable. Finally,
the Warnings and Error entries provide diagnostics. If there are many
warnings, then the value may not be accurate.</p>
</section>
</section>
</body>
</html>

View file

@ -0,0 +1,400 @@
# Examining Machine State: Memory, Registers, and Variables
This module assumes you know how to launch `termmines` in Ghidra using GDB, and know where to find the basic Debugger GUI components.
It also assumes you know the basic control commands, e.g., Resume, Step, and Interrupt; as well as some basics for breakpoints.
If not, please refer to the previous modules.
This module will address the following features in more depth:
* Dynamic Listing window
* Dynamic Bytes window
* Registers window
* Watches window
* Sleigh expressions
* Variable value hovers
## Machine State
There are at least two ways to define machine state.
One way based on a high-level understanding of a program is the collective values of all variables.
Another based on a low-level understanding of a program is the collective values of all memory and all registers of all threads.
Because Ghidra is primarily concerned with examining software at the low level, albeit to obtain a high-level understanding, we will generally stick with the low-level definition.
One could argue that machine state also includes the underlying system and hardware, including peripherals.
When debugging user-space applications, Ghidra cannot generally inspect the underlying system, except for a few things granted by the back-end debugger, e.g., the virtual memory map.
Thus, as far as we are concerned, the machine state does not include the underlying system, but it is still something you must be aware of while debugging.
Note that we also treat *registers* as something separate from *memory*.
While technically, one could argue registers are the closest and smallest memory to the CPU, we think of memory as an addressable array, e.g., DIMMs and flash ROMs.
If applicable, *memory-mapped* registers are considered part of memory by Ghidra.
The Ghidra Debugger provides mechanisms for viewing and modifying machine state at both the low and high levels.
The Dynamic Listing and Memory viewer provide access to memory, the Registers window provides access to registers, and the Watches window provides access to memory and registers via expressions.
The only means of accessing high-level variables is through value hovers, which are available in all listings and the Decompiler.
### Patching
Many of the features allow you to edit the values, i.e., *patch* the live target.
The windows that are adaptations of their static counterparts work more or less as expected.
Edits in any *dynamic* window will attempt to patch the live target.
Edits in any *static* window will **not** patch the live target; they modify the program database as usual.
Some dynamic windows may present tables with editable cells.
These will often include a *write-lock* toggle, like the static Byte viewer, to avoid accidental patching.
Furthermore, you must use the ![control mode](images/record.png) Control Mode toggle in the global toolbar (next to the control actions) to enable patching throughout the Debugger tool.
For now, please only use the "Control Target" and "Control Target w/ Edits Disabled" options.
The write toggle is included here so that actions like copy-paste do not lead to accidental patching.
## The Dynamic Listing
Up to this point, we have only used the Dynamic Listing to display the instructions at the program counter.
It can actually view and mark up any part of the machine state in memory, e.g., mapped images, heap pages, stack pages.
Where the memory is mapped to images, Ghidra attempts to synchronize the Static Listing and, by extension, the other static analysis windows.
The Dynamic Listing has most of the same features as the Static Listing.
Re-launch `termmines` and then navigate to `rand`.
You may notice that the Static Listing has disassembly, but the Dynamic Listing does not.
This is because Ghidra has not observed the program counter at `rand` yet, so it has not automatically disassembled it.
To manually disassemble in the Dynamic Listing, place your cursor where you expect an instruction and press **D**, just like you would in the Static Listing.
![The dynamic listing after a call to rand](images/State_ListingAfterCallRand.png)
This action differs in that it does not follow flow.
It proceeds linearly, stopping at any control transfer instruction.
Now, we will examine the stack segment.
Click the ![location tracking](images/register-marker.png) Track Location drop-down and select **Track Stack Pointer**.
The window should seek to (and highlight in pale green) the address in the stack pointer.
Since the target has just entered `main`, we should expect a return address at the top of the stack.
With your cursor at the stack pointer, press **P** to place a pointer there, just like
you would in the Static Listing.
You can now navigate to that address by double-clicking it.
To return to the stack pointer, you can use the back arrow in the global toolbar, you can click the ![track location](images/register-marker.png) Track Location button, or you can double-click the `sp = [Address]` label in the top right of the Dynamic Listing.
To examine a more complicated stack segment, we will break at `rand`.
Ensure your breakpoint at `rand` is enabled and press ![resume](images/resume.png) Resume.
Your Dynamic Listing should follow the stack pointer.
In the menus, select **Debugger &rarr; Analysis &rarr; Unwind from frame 0** or press **U**.
![The dynamic listing of the stack after a call to rand](images/State_ListingStackAfterCallRand.png)
**NOTE**: We will cover the Stack window later in the course, which is probably a more suitable way to navigate stack frames.
It is populated by the back-end debugger, which can usually unwind the stack more reliably than Ghidra.
The Unwind Stack action is useful when you want an in-depth understanding of the actual contents of the stack, or when you are emulating.
Now, switch back to **Track Program Counter**.
If you would like to track both the Program Counter and the Stack Pointer, click the ![clone](images/camera-photo.png) Clone button in the local toolbar.
Like the Static Listing, this clones an instance of the Dynamic Listing, which you can configure differently than the primary Dynamic Listing.
Only the primary Dynamic Listing will synchronize, and it does so only with the primary Static Listing.
**NOTE**: For the sake of disambiguation, we will use the term *clone*, not *snapshot* when referring to multiple instances of a Ghidra window.
While this is inconsistent with the Ghidra Beginner course materials, it is necessary to avoid confusion with snapshots of the machine state, discussed later in this module.
The dynamic listing offers several additional features:
### Cache Status Indication
The listing's contents are read from a live target, which may become unresponsive or otherwise temperamental.
The Debugger uses a database, which acts as a cache separating the GUI from the live target.
The UI requests memory pages from the target, the target asynchronously retrieves those pages and stores them into the database, then the database updates the UI.
This sequence does not always go as expected; thus, pages with stale data are displayed with a grey background.
This may also happen if auto-read is disabled.
Typically, user-space targets are not so temperamental, but others may be, or memory reads could be expensive, in which case disabling automatic memory reads may be advantageous.
If the back-end debugger reports an error while reading memory, the first address of the page will have a red background.
To refresh the visible or selected page(s), click the <img alt="refresh" src="images/view-refresh.png" width="16px"/> Refresh button.
Examine the Debug Console window for errors / warnings before spamming this button.
To toggle auto read, use the <img src="images/autoread.png" alt="auto-read" width="16px"/> Auto-Read drop-down button from the local toolbar.
### Address Tracking
We have already demonstrated this, but there are some finer details.
Some of the tracking options depend on the Watches window, discussed later in this module.
On occasion, the location cannot be displayed in the listing, typically because it falls outside of the memory map.
If this happens, the address label at the top right of the listing will have red text.
### Module / Region Indicator
In the top left a label will display the name of the section containing the cursor.
If there is no containing section, it will fall back to the containing module and then to the containing region.
Rarely, this label will be empty.
This can happen when the cursor is outside any known region, which only happens if you configure Ghidra to ignore the memory map.
### Go To
The Go To action in the Dynamic Listing differs from the one in the Static Listing.
Like the static one, it accepts an address in hexadecimal, possibly prefixed with the address space and a colon.
However, it also accepts Sleigh expressions, allowing you to treat `RAX` as a pointer and go to that address, for example.
We cover Sleigh expressions later in this module.
### Compare
The Compare action in the Dynamic Listing also differs from the one in the Static Listing.
It allows the comparison of two machine state snapshots, covered in the [Navigation](A5-Navigation.md) module.
### Exercise: Reverse Engineer the Board
All of the features in the default CodeBrowser tool are also in the default Debugger tool, providing you Ghidra's full suite of static analysis tools during your dynamic sessions, albeit they are not as immediately accessible.
Your task is to reverse engineer the game board's layout in memory.
Because you are in a dynamic session, you have an example board to work with.
As you navigate the `.data` section of `termmines` in the Static Listing, the Dynamic Listing will follow along showing you the live values in memory.
You can also experiment by placing code units in the Dynamic Listing before committing to them in the Static Listing.
#### Questions:
1. How are the cells allocated?
1. How are the cells indexed? Row major, color major? 0-up, 1-up?
1. What is happening around the "border" of the board? Why might the programmer have chosen this design?
## The Memory Viewer
![The dynamic memory view of the stack after a call to rand](images/State_BytesStackAfterCallRand.png)
Just as the Dynamic Listing is the analog of the Static Listing, the Memory viewer is the analog of the Byte viewer.
It is not visible by default.
To open it, use **Windows &rarr; Byte Viewer &rarr; Memory ...** in the menus.
Its default configuration should be Auto PC, the same as the Dynamic Listing's default.
It has all the same additional Debugger features as the Dynamic Listing.
Furthermore, bytes that have changed are displayed in red text.
### Exercise: Display the Board in Hex
This is a bit quick and dirty, but it works and can be useful.
Your task is to configure the Memory viewer so that (within the memory allocated to the board) the rows and columns of the Memory viewer correspond to the rows and columns of the game board.
**TIP**: Use the *Alignment Address* and *Bytes Per Line* settings.
## Registers
![The registers after a call to rand](images/State_RegistersAfterCallRand.png)
The Registers window gives a view of all the registers on the target and their current values.
The register set can be very large, so there are a few ways to sift and sort.
As in most Ghidra tables, you can filter using the box below the registers table.
Additionally, you can use the column headers to sort.
The columns are:
* The **Favorite** column indicates which registers are your favorite.
By default, this includes the instruction pointer and the stack pointer.
You can quickly choose your favorite(s) by toggling the check boxes in this column.
Because this column is sorted by default, your favorites are positioned at the top.
* The **Number** column gives the number assigned to the register by Ghidra's processor specification.
This is mostly just to make the default sorting deterministic.
* The **Name** column gives Ghidra's name for the register.
This usually matches the name given by the back-end debugger, but may not.
For example, on x86-64, what Ghidra calls `rflags` GDB calls `eflags`.
* The **Value** column gives the register's current value in hexadecimal.
Values in gray are stale.
Values in red have changed.
Right-clicking this column will present options to Go To the address, as if the register were a pointer.
* The **Type** column allows you to assign a type to, i.e., create a data unit on, the register.
This has more utility for float types than integers, but it may still help you record what you know about how a register is being used.
* The **Representation** column displays the register's value according to its assigned type, if applicable.
If the type is a pointer, then double-clicking this value will go to the address in the Dynamic Listing.
If you would like to adjust the list of registers in the table, use the ![select registers](images/select-registers.png) Select Registers button in the local toolbar.
This will present all the registers in Ghidra's processor specification, including those which are just artifacts of Sleigh.
Typically, this is not necessary, since the table will include all registers recognized by both Ghidra and the back-end debugger.
Nevertheless, if you believe a register is missing, it is wise to check this selection.
### Exercise: Reduce the Mines
If you have not already reverse engineered the mine placement algorithm, do that now.
Think up a strategy you might employ, by patching a register, to reduce the number of mines placed on the board.
The strategy need not result in a permanent change.
It should only affect the round being set up.
For this exercise, you cannot patch memory, but you may place a breakpoint.
Verify your work by playing the round.
## Watches
![The watches window in a call to srand](images/State_WatchesInCallSRand.png)
The Watches window gives the values of several user-specified Sleigh expressions.
This can provide an alternative to the Registers window when you are really only interested in a couple of registers.
It can also watch values in memory.
Furthermore, when a watch has a memory address, the expression will appear as an option in the Location Tracking menus of the Listing and Memory viewers.
Selecting that option will cause the window to follow that watch as its address changes.
To add a watch, click the ![add](images/add.png) Add button.
A new entry will appear.
Double-click the left-most cell of the row to set or edit the Sleigh expression.
For starters, try something like `RDI`.
(Conventionally, this is the location for the first parameter on Linux x86-64 systems.)
The context menus for the Listing and Registers windows include a "Watch" action, which adds the current selection to the Watches window.
The columns are:
* The **Expression** column is the user-defined Sleigh expression.
* The **Address** column is the address of the resulting value, if applicable.
This may be in `register` space.
Double-clicking this cell will go to the address in the Dynamic Listing.
* The **Symbol** column gives the symbol in a mapped static image closest to or containing the address, if applicable.
* The **Value** column gives the "raw" value of the expression.
If the result is in memory, it displays a byte array; otherwise, it displays an integer.
* The **Type** and **Representation** columns work the same as in the Registers window, except they do *not* save the data unit to the database.
This has more uses than the Registers window.
For example, try `*:30 RDI` and set this to `TerminatedCString`.
Whenever `RDI` is a string pointer, this will display the string up to 30 characters.
* The **Error** column reports any errors in compiling or evaluating the expression.
## Sleigh Expressions
Watches and Go-To commands are expressed using Ghidra's Sleigh language.
More precisely, expressions are the sub-language of Sleigh for the right-hand side of assignment statements in semantic sections.
If you already know this language, then there is little more to learn.
Of note, you may use labels from mapped program images in your expression.
For example, to see how far a return address is into `main`, you could use `*:8 RSP - main`.
For the complete specification, see the Semantic Section in the [Sleigh documentation](../../../Ghidra/Features/Decompiler/src/main/doc/sleigh.xml).
Sleigh is a bit unconventional in that its operators are typed rather than its variables.
All variables are fix-length bit vectors.
Their sizes are specified in bytes, but they have no other type information.
### Variables and Constants
Here are some examples of things you can reference by name:
* **Register**: `RAX`
* **Label**: `main`
* **Constant**: `1234:8` or `0x42d:8` &mdash; the value 1234 encoded as an 8-byte integer
Registers vary by processor, but any register known to Ghidra's specification is allowed.
(Due to limitations in Sleigh, you cannot refer to the `contextreg` or any of its sub-registers.)
A label may come from any Ghidra program database that is mapped to the current target.
Due to limitations in Sleigh, you cannot specify a label's namespace.
The compiler will search only by name and select arbitrarily from multiple matches.
### Operators
Here we will demonstrate each operator by example:
* **Integer Addition**: `RAX + RCX`
* **Integer Subtraction**: `RAX - RCX`
* **Integer Negation**: `-RAX`
* **Integer Multiplication**: `RAX * RCX`
* **Unsigned Integer Division**: `RAX / RCX`
* **Unsigned Integer Remainder**: `RAX % RCX`
* **Signed Integer Division**: `RAX s/ RCX`
* **Signed Integer Remainder**: `RAX s% RCX`
* **Left Shift**: `RAX << RCX`
* **Unsigned Right Shift**: `RAX >> RCX`
* **Signed Right Shift** `RAX s>> RCX`
* **Integer Comparison**: `RAX == RCX` or `RAX != RCX`
* **Unsigned Integer Comparison**: `RAX < RCX` or `RAX > RCX` or `RAX <= RCX` or `RAX >= RCX`
* **Signed Integer Comparison**: `RAX s< RCX` etc.
* **Float Addition**: `MM0 f+ MM1`
* **Float Subtraction**: `MM0 f- MM1`
* **Float Negation**: `f-MM0`
* **Float Multiplication**: `MM0 f* MM1`
* **Float Division**: `MM0 f/ MM1`
* **Float Absolute Value**: `abs(MM0)`
* **Float Square Root**: `sqrt(MM0)`
* **Float Comparison**: `RAX f== RCX` or `RAX f< RCX` etc.
* **Bitwise And**: `RAX & RCX`
* **Bitwise Or**: `RAX | RCX`
* **Bitwise Xor**: `RAX ^ RCX`
* **Bitwise Not**: `~RAX`
* **Boolean And**: `RAX && RCX`
* **Boolean Or**: `RAX || RCX`
* **Boolean Xor**: `RAX ^^ RCX`
* **Boolean Not**: `!RAX`
**NOTE**: If the result of your expression is in floating point, you will need to set the type of the watch accordingly.
The "raw" display will render the bit vector as an integer or byte array.
To read memory:
* **Dereference**: `*:8 RSP` or `*[ram]:8 RSP`
**NOTE** The `[ram]` part is optional.
On x86, you will rarely if ever specify the space, since there is only one physical RAM space.
The `:8` part specifies the number of bytes to read from memory.
It is also optional, but only if the size can be inferred from the rest of the expression.
To manipulate variable size:
* **Zero Extension**: `RAX + zext(EBX)`
* **Sign Extension**: `RAX + sext(EBX)`
* **Truncation**: `RAX:4` &mdash; Equivalent to `EAX`
* **Truncation**: `AL + RBX(4)` &mdash; AL added to the the 5th byte of RBX
* **Bit Extraction**: `RAX[7,8]` &mdash; Equivalent to `AL`
**NOTE**: The second form of truncation drops the least-significant 4 bytes of RBX and takes as many of the remaining bytes (1 in this case) as necessary to match size with AL.
**NOTE**: Need for these next miscellaneous operators in Watch expressions is rare:
* **Unsigned Carry**: `carry(RAX,RBX)`
* **Signed Carry**: `scarry(RAX,RBX)`
* **Signed Borrow**: `sborrow(RAX,RBX)`
* **Float NaN**: `nan(MM0)`
* **Convert Integer to Float**: `MM0 + int2float(RAX)` &mdash; Context required to infer the float size
* **Convert Float to Integer**: `RAX + trunc(MM0)` &mdash; Context required to infer the integer size
* **Convert Float Size**: `MM0 + float2float(MM0_Da)` &mdash; Context required to infer the new float size
* **Float Round Ceiling**: `ceil(MM0)`
* **Float Round Floor**: `floor(MM0)`
* **Float Round Nearest**: `round(MM0)`
### Exercise: Find and Modify the Board Dimensions
Your task is to set up watches on the width and height of the game board, and then use those watches to change the size of the board.
This may involve some trial and error, and it may not work perfectly due to the way `ncurses` refreshes the screen.
For this exercise, patching memory is expected, and the change should last until the target is terminated.
**TIP**: If the `termmines` image is subject to ASLR, and you want your watch expression to generalize over re-launches, try using `main` as an anchor for the image base.
### Exercise: Watch the Cell to be Mined
Your task is to watch the byte value of the cell that is about to have a mine placed in it.
You will probably want to set a breakpoint somewhere in the mine placement algorithm.
It is okay if the watch does not *always* display the correct byte.
However, it must be correct whenever the program counter is at your breakpoint.
Register allocations are fairly volatile, and as a result, watch expressions that refer to registers are only valid in a limited scope.
The rest of the time, even though the watch may evaluate successfully, its value may have no real meaning.
Check your work by observing the mine bit being ORed in as you step the target.
**TIP**: Try creating watches for the row and column indices, first.
Then, perhaps referring to the Decompiler, formulate the expression that dereferences that cell in the board.
## Variable Hovers
You may have already used these if you completed the exercises in the [Breakpoints](A3-Breakpoints.md) module.
If you hover over a variable in any listing or the Decompiler, the Debugger will attempt to evaluate it and display information about it.
In some cases, evaluation may involve unwinding the stack.
Unwinding proceeds until the Debugger finds an invocation of the function containing or defining the variable.
If unwinding fails, the Debugger may disregard the stack.
In dynamic windows, the Debugger generally disregards the stack.
In static windows, the Debugger still uses dynamic information to unwind the stack and evaluate the variable.
A variable may be any of the following:
* A register in the listing.
In this case, the hover will report the register's value in the function's frame.
* A local or parameter in the listing.
If the variable is allocated in a register, this behaves the same as hovering that register, except with additional information presented.
If the variable is allocated in stack space, this only succeeds if unwinding succeeds.
* A global variable in the listing.
Unwinding is unnecessary for these.
* A local or parameter in the Decompiler.
This behaves similarly to hovering a variable in the Static Listing.
* A global in the Decompiler.
This behaves similarly to hovering a global variable in the Static Listing.
* A field reference in the Decompiler.
A field reference is essentially a C expression in terms of other variables.
This will evaluate those variables and then evaluate the expression.
Depending on the particular variable and other circumstances, the hover will contain some combination of these rows:
* **Name**: The name of the variable
* **Type**: The type of the variable
* **Location**: The static location of the variable, e.g., `Stack[0x4]`
* **Status**: A progress indicator
* **Frame**: If evaluation required unwinding, a description of the frame used for context
* **Storage**: The dynamic, physical location of the variable, e.g., `7fffffffe618`
* **Bytes**: The raw bytes currently stored in the memory allocated to the variable
* **Integer**: The "raw" integer value of the variable, rendered with varyied signedness and radix
* **Value**: The value of the variable, according to its type
* **Instruction**: If the variable points to code, the target instruction
* **Warnings**: Warnings emitted during evaluation
* **Error**: If the value could not be evaluated, an explanation or the exception
The Name, Type, and Location entries are informational.
They tell you about the variable and its static definition.
The Status, Frame, and Storage entries are also informational, but tell you about the variable's dynamic evaluation.
The Bytes, Integer, Value, and Instruction entries tell you the dynamic value of the variable.
Finally, the Warnings and Error entries provide diagnostics.
If there are many warnings, then the value may not be accurate.

View file

@ -0,0 +1,376 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Ghidra Debugger</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
</style>
<link rel="stylesheet" href="style.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="nav"><a
class="beginner" href="A1-GettingStarted.html">Getting Started</a><a
class="beginner" href="A2-UITour.html">UI Tour</a><a
class="beginner" href="A3-Breakpoints.html">Breakpoints</a><a
class="beginner" href="A4-MachineState.html">Machine State</a><a
class="beginner" href="A5-Navigation.html">Navigation</a><a
class="beginner" href="A6-MemoryMap.html">Memory Map</a><a
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a>
</header>
<header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#navigation" id="toc-navigation">Navigation</a>
<ul>
<li><a href="#coordinates" id="toc-coordinates">Coordinates</a></li>
<li><a href="#threads" id="toc-threads">Threads</a>
<ul>
<li><a href="#trace-tabs" id="toc-trace-tabs">Trace Tabs</a></li>
</ul></li>
<li><a href="#stack" id="toc-stack">Stack</a>
<ul>
<li><a href="#exercise-name-the-function"
id="toc-exercise-name-the-function">Exercise: Name the Function</a></li>
</ul></li>
<li><a href="#time" id="toc-time">Time</a>
<ul>
<li><a href="#sparse-vs.-full-snapshots"
id="toc-sparse-vs.-full-snapshots">Sparse vs. Full Snapshots</a></li>
<li><a href="#comparing-snapshots"
id="toc-comparing-snapshots">Comparing Snapshots</a></li>
<li><a href="#exercise-find-the-time"
id="toc-exercise-find-the-time">Exercise: Find the Time</a></li>
</ul></li>
</ul></li>
</ul>
</nav>
<section id="navigation" class="level1">
<h1>Navigation</h1>
<p>This module assumes you know how to launch <code>termmines</code> in
Ghidra using GDB, and know where to find the basic Debugger GUI
components. It also assumes you are familiar with the concepts of
breakpoints and machine state in Ghidra. If not, please refer to the
previous modules.</p>
<p>This module will address the following features in more depth:</p>
<ul>
<li>The Threads window</li>
<li>The Stack window</li>
<li>The Time window</li>
</ul>
<section id="coordinates" class="level2">
<h2>Coordinates</h2>
<p>The term <em>location</em> is already established in Ghidra to refer
to the current program and current address. There are more elements to a
“location” in a dynamic session, so we add additional elements to form
the concept of your current <em>coordinates</em>. All of these elements
can affect the information displayed in other windows, especially those
dealing with machine state.</p>
<ul>
<li>The current <em>trace</em>. A trace database is where all of the
Debugger windows (except Targets and Objects) gather their information.
It is the analog of the program database, but for dynamic analysis.</li>
<li>The current <em>thread</em>. A thread is a unit of execution, either
a processor core or a platform-defined virtual thread. Each thread has
its own register context. In Ghidra, this means each has its own
instance of the processor specifications “register” space.</li>
<li>The current <em>frame</em>. A frame is a call record on the stack.
For example, <code>main</code> may call <code>getc</code>, which may in
turn call <code>read</code>. If you wish to examine the state of
<code>main</code>, you would navigate 2 frames up the stack. Because
functions often save registers to the stack, the back-end debugger may
“unwind” the stack and present the restored registers.</li>
<li>The current <em>time</em>. In general, time refers to the current
snapshot. Whenever the target becomes suspended, Ghidra creates a
snapshot in the current trace. If you wish to examine the machine state
at a previous time, you would navigate to an earlier snapshot. “Time”
may also include steps of emulation, but that is covered in the <a
href="B2-Emulation.html">Emulation</a> module.</li>
</ul>
<p>In general, there is a window dedicated to navigating each element of
your current coordinates.</p>
</section>
<section id="threads" class="level2">
<h2>Threads</h2>
<p>If you do not have an active session already, launch
<code>termmines</code>.</p>
<figure>
<img src="images/Navigation_ThreadsInCallRand.png"
alt="Threads window" />
<figcaption aria-hidden="true">Threads window</figcaption>
</figure>
<p>The Threads window displays a list of all threads ever observed in
the target. This includes threads which have been terminated.
Unfortunately, <code>termmines</code> is a single-threaded application,
so you will only see one row. If there were more, you could switch to a
different thread by double-clicking it in the table. The columns
are:</p>
<ul>
<li>The <strong>Name</strong> column gives the name of the thread. This
may include the back-end debuggers thread id, the target platforms
system thread id, and/or the back-end debuggers display text for the
thread.</li>
<li>The <strong>Created</strong> column gives the snapshot when the
thread was first observed.</li>
<li>The <strong>Destroyed</strong> column gives the snapshot when the
thread was first observed as terminated. If this is empty, the thread is
still alive.</li>
<li>The <strong>State</strong> column gives the state of the thread.
This may be one of ALIVE, RUNNING, STOPPED, TERMINATED, or UNKNOWN.</li>
<li>The <strong>Comment</strong> column allows you to annotate the
thread, e.g., if you discover it has a dedicated purpose.</li>
<li>The <strong>Plot</strong> column plots the threads life spans in a
chart.</li>
</ul>
<p><strong>NOTE</strong>: Most of the time, switching threads will also
change what thread is being controlled by the Control actions in the
global toolbar. This may vary subtly, depending on the action and the
target. For example, the <img src="images/resume.png" alt="resume" />
Resume button will usually allow all threads to execute; whereas the
<img src="images/stepinto.png" alt="step into" /> Step Into button will
usually step only the current thread. If the targets thread scheduler
cannot schedule your current thread, the behavior is not clearly
defined: It may step a different thread, it may cause the target to
block until the thread can be scheduled, or it may do something
else.</p>
<p>When you switch threads, everything that depends on the current
thread may change, in particular the Stack window and any machine-state
window that involves register values. The Registers window will display
the values for the new thread, the Watches window will re-evaluate all
expressions, and the Dynamic Listing and Memory views may seek to
different addresses, depending on their location tracking
configurations.</p>
<section id="trace-tabs" class="level3">
<h3>Trace Tabs</h3>
<p>The Threads window also has a row of tabs at the very top. This is a
list of open traces, i.e., of targets you are debugging. You can also
open old traces to examine a targets machine state <em>post
mortem</em>. In general, you should only have one trace open at a time,
but there are use cases where you might have multiple. For example, you
could debug both the client and server of a network application. To
switch to another trace, single-click its tab.</p>
<p>When you switch traces, every Debugger window that depends on the
current trace will update. Thats every window except Targets and
Objects. The Breakpoints window may change slightly, depending on its
configuration, because it is designed to present all breakpoints in the
session.</p>
</section>
</section>
<section id="stack" class="level2">
<h2>Stack</h2>
<p>Ensure your breakpoint on <code>rand</code> is enabled, and resume
until you hit it.</p>
<figure>
<img src="images/Navigation_StackInCallRand.png" alt="Stack window" />
<figcaption aria-hidden="true">Stack window</figcaption>
</figure>
<p>The stack window displays a list of all the frames for the current
thread. Each thread has its own execution stack, so the frame element is
actually dependent on the thread element. The call records are listed
from innermost to outermost. Here, <code>main</code> has called an
unnamed function, which has in turn called <code>rand</code>. The
columns are:</p>
<ul>
<li>The <strong>Level</strong> column gives the frame number. This is
the number of calls that must be unwound from the current machine state
to reach the frame.</li>
<li>The <strong>PC</strong> column gives the address of the next
instruction in that frame. The PC of frame 0 is the value of the PC
register. Then, the PC of frame 1 is the return address of frame 0, and
so on.</li>
<li>The <strong>Function</strong> column gives the name of the function
containing the PC mapped to its static program database, if
available.</li>
<li>The <strong>Comment</strong> column allows you to annotate the
frame.</li>
</ul>
<p>Double-click the row with the unnamed function (frame 1) to switch to
it. When you switch frames, any machine-state window that involves
register values may change. <strong>NOTE</strong>: Some back-end
debuggers do not recover register values when unwinding stack frames.
For those targets, some windows may display stale meaningless values in
frames other than 0.</p>
<section id="exercise-name-the-function" class="level3">
<h3>Exercise: Name the Function</h3>
<p>Your Dynamic and Static Listings should now be in the unknown
function. If you have not already done so, reverse engineer this
function and give it a name.</p>
</section>
</section>
<section id="time" class="level2">
<h2>Time</h2>
<p>Re-launch <code>termmines</code>, ensure both of your breakpoints at
<code>srand</code> and <code>rand</code> are enabled, and resume until
you hit <code>rand</code>, then step out. Now, switch to the Time
window.</p>
<figure>
<img src="images/Navigation_TimeAfterCallSRandCallRand.png"
alt="Time window" />
<figcaption aria-hidden="true">Time window</figcaption>
</figure>
<p>It displays a list of all the snapshots for the current trace. In
general, every event generates a snapshot. By default, the most recent
snapshot is at the bottom. The columns are:</p>
<ul>
<li>The <strong>Snap</strong> column numbers each snapshot. Other
windows that indicate life spans refer to these numbers.</li>
<li>The <strong>Timestamp</strong> column gives the time when the
snapshot was created, i.e., the time when the event occurred.</li>
<li>The <strong>Event Thread</strong> column indicates which thread
caused the target to break. This only applies to snapshots that were
created because of an event, which is most.</li>
<li>The <strong>Schedule</strong> column describes the snapshot in
relation to another. It typically only applies to emulator / scratch
snapshots, which are covered later in this course.</li>
<li>The <strong>Description</strong> column describes the event that
generated the snapshot.</li>
</ul>
<p>Switch to the snapshot where you hit <code>srand</code> (snapshot 2
in our screenshot) by double-clicking it in the table. This will cause
all the machine-state windows to update including the Stack window. If
you try navigating around the Dynamic Listing, you will likely find
stale areas indicated by a grey background.</p>
<p><strong>NOTE</strong>: Navigating into the past will automatically
change the Control mode. This is to avoid confusion, since you may
perform a control action based on the state you see, which is no longer
the state of the live target. Switch back by using the Control mode
drop-down button in the global toolbar. When you select <strong>Control
Target</strong> (with or without edits), the Debugger will navigate
forward to the latest snapshot.</p>
<section id="sparse-vs.-full-snapshots" class="level3">
<h3>Sparse vs. Full Snapshots</h3>
<p>Regarding the stale areas: the Debugger cannot request the back-end
debugger provide machine state from the past. (Integration with timeless
back-end debuggers is not yet supported.) Remember, the trace is used as
a cache, so it will only be populated with the pages and registers that
you observed at the time. Thus, most snapshots are <em>sparse</em>
snapshots. The most straightforward way to capture a <em>full</em>
snapshot is the
<img alt="refresh" src="images/view-refresh.png" width="16px"> Refresh
button with a broad selection in the Dynamic Listing. We give the exact
steps in the next heading. To capture registers, ensure you navigate to
each thread whose registers you want to capture.</p>
</section>
<section id="comparing-snapshots" class="level3">
<h3>Comparing Snapshots</h3>
<p>A common technique for finding the address of a variable is to take
and compare snapshots. Ideally, the snapshots are taken when only the
variable you are trying to locate has changed. Depending on the program,
this is not always possible, but the technique can be repeated to rule
out many false positives. The actual variable should show up in the
difference every time.</p>
<p>For example, to find the variable that holds the number of mines, we
can try to compare memory before and after parsing the command-line
arguments. Because parsing happens before waiting for user input, we
will need to launch (not attach) the target.</p>
<ol type="1">
<li><p>Launch <code>termmines -M 15</code> in the Debugger. (See <a
href="A1-GettingStarted.html">Getting Started</a> to review launching
with custom parameters.)</p></li>
<li><p>Ensure your breakpoint at <code>srand</code> is enabled.</p></li>
<li><p>Use <strong>Ctrl-A</strong> to Select All the addresses.</p></li>
<li><p>Click the
<img alt="refresh" src="images/view-refresh.png" width="16px"> Refresh
button. <strong>NOTE</strong>: It is normal for some errors to occur
here. We note a more surgical approach below.</p></li>
<li><p>Wait a moment for the capture to finish.</p></li>
<li><p>Optionally, press <strong>Ctrl-Shift-N</strong> to rename the
snapshot so you can easily identify it later. Alternatively, edit the
snapshots Description from the table in the Time window.</p></li>
<li><p>Press <img src="images/resume.png" alt="resume" /> Resume,
expecting it to break at <code>srand</code>.</p></li>
<li><p>Capture another full snapshot using Select All and
Refresh.</p></li>
<li><p>Click the <img src="images/table_relationship.png"
alt="compare" /> Compare button in the Dynamic Listing.</p></li>
<li><p>In the dialog, select the first snapshot you took.</p>
<figure>
<img src="images/Navigation_DialogCompareTimes.png"
alt="The compare times dialog" />
<figcaption aria-hidden="true">The compare times dialog</figcaption>
</figure></li>
<li><p>Click OK.</p></li>
</ol>
<p>The result is a side-by-side listing of the two snapshots with
differences highlighted in orange. Unlike the Static program comparison
tool, this only highlights differences in <em>byte</em> values. You can
now use the Next and Previous Difference buttons in the Dynamic Listing
to find the variable.</p>
<figure>
<img src="images/Navigation_CompareTimes.png"
alt="The listing with comparison" />
<figcaption aria-hidden="true">The listing with comparison</figcaption>
</figure>
<p>Notice that you see the command-line specified value 15 on the left,
and the default value 10 on the right. This confirms we have very likely
found the variable.</p>
<p><strong>NOTE</strong>: Using Select All to create your snapshots can
be a bit aggressive. Instead, we might guess the variable is somewhere
in the <code>.data</code> section and narrow our search. For one,
including so much memory increases the prevalence of false positives,
not to mention the wasted time and disk space. Second, many of the pages
in the memory map are not actually committed, leading to tons of errors
trying to capture them all. Granted, there are use cases where a full
snapshot is appropriate. Some alternatives, which we will cover in the
<a href="A6-MemoryMap.html">Memory Map</a> module, allow you to zero in
on the <code>.data</code> section:</p>
<ul>
<li>Use the Memory Map window (borrowed from the CodeBrowser) to
navigate to the <code>.data</code> section. The Dynamic Listing will
stay in sync and consequently capture the contents of the first page.
This specimen has a small enough <code>.data</code> section to fit in a
single page, but that is generally not the case in practice.</li>
<li>Use the Regions window to select the addresses in the
<code>.data</code> section, then click Refresh in the Dynamic Listing.
This will capture the full <code>.data</code> section, no matter how
many pages.</li>
<li>Use the lower pane of the Modules window to select the addresses in
the <code>.data</code> section, then click Refresh in the Dynamic
Listing. This will also capture the full <code>.data</code>
section.</li>
</ul>
</section>
<section id="exercise-find-the-time" class="level3">
<h3>Exercise: Find the Time</h3>
<p>In <code>termmines</code>, unlike other Minesweeper clones, your
score is not printed until you win. Your goal is to achieve a remarkable
score by patching a variable right before winning. Considering it is a
single-threaded application, take a moment to think about how your time
might be measured. <strong>TIP</strong>: Because you will need to play
the game, you will need to attach rather than launch. Use the snapshot
comparison method to locate the variable. Then place an appropriate
breakpoint, win the game, patch the variable, and score 0 seconds!</p>
<p>If you chose a poor breakpoint or have no breakpoint at all, you
should still score better than 3 seconds. Once you know where the
variable is, you can check its XRefs in the Static Listing and devise a
better breakpoint. You have completed this exercise when you can
reliably score 0 seconds for games you win.</p>
<p><strong>NOTE</strong>: If you are following and/or adapting this
course using a different specimen, the timing implementation and
threading may be different, but the technique still works.</p>
</section>
</section>
</section>
</body>
</html>

View file

@ -0,0 +1,220 @@
# Navigation
This module assumes you know how to launch `termmines` in Ghidra using GDB, and know where to find the basic Debugger GUI components.
It also assumes you are familiar with the concepts of breakpoints and machine state in Ghidra.
If not, please refer to the previous modules.
This module will address the following features in more depth:
* The Threads window
* The Stack window
* The Time window
## Coordinates
The term *location* is already established in Ghidra to refer to the current program and current address.
There are more elements to a "location" in a dynamic session, so we add additional elements to form the concept of your current *coordinates*.
All of these elements can affect the information displayed in other windows, especially those dealing with machine state.
* The current *trace*.
A trace database is where all of the Debugger windows (except Targets and Objects) gather their information.
It is the analog of the program database, but for dynamic analysis.
* The current *thread*.
A thread is a unit of execution, either a processor core or a platform-defined virtual thread.
Each thread has its own register context.
In Ghidra, this means each has its own instance of the processor specification's "register" space.
* The current *frame*.
A frame is a call record on the stack.
For example, `main` may call `getc`, which may in turn call `read`.
If you wish to examine the state of `main`, you would navigate 2 frames up the stack.
Because functions often save registers to the stack, the back-end debugger may "unwind" the stack and present the restored registers.
* The current *time*.
In general, time refers to the current snapshot.
Whenever the target becomes suspended, Ghidra creates a snapshot in the current trace.
If you wish to examine the machine state at a previous time, you would navigate to an earlier snapshot.
"Time" may also include steps of emulation, but that is covered in the [Emulation](B2-Emulation.md) module.
In general, there is a window dedicated to navigating each element of your current coordinates.
## Threads
If you do not have an active session already, launch `termmines`.
![Threads window](images/Navigation_ThreadsInCallRand.png)
The Threads window displays a list of all threads ever observed in the target.
This includes threads which have been terminated.
Unfortunately, `termmines` is a single-threaded application, so you will only see one row.
If there were more, you could switch to a different thread by double-clicking it in the table.
The columns are:
* The **Name** column gives the name of the thread.
This may include the back-end debugger's thread id, the target platform's system thread id, and/or the back-end debugger's display text for the thread.
* The **Created** column gives the snapshot when the thread was first observed.
* The **Destroyed** column gives the snapshot when the thread was first observed as terminated.
If this is empty, the thread is still alive.
* The **State** column gives the state of the thread.
This may be one of ALIVE, RUNNING, STOPPED, TERMINATED, or UNKNOWN.
* The **Comment** column allows you to annotate the thread, e.g., if you discover it has a dedicated purpose.
* The **Plot** column plots the threads' life spans in a chart.
**NOTE**: Most of the time, switching threads will also change what thread is being controlled by the Control actions in the global toolbar.
This may vary subtly, depending on the action and the target.
For example, the ![resume](images/resume.png) Resume button will usually allow all threads to execute; whereas the ![step into](images/stepinto.png) Step Into button will usually step only the current thread.
If the target's thread scheduler cannot schedule your current thread, the behavior is not clearly defined:
It may step a different thread, it may cause the target to block until the thread can be scheduled, or it may do something else.
When you switch threads, everything that depends on the current thread may change, in particular the Stack window and any machine-state window that involves register values.
The Registers window will display the values for the new thread, the Watches window will re-evaluate all expressions, and the Dynamic Listing and Memory views may seek to different addresses, depending on their location tracking configurations.
### Trace Tabs
The Threads window also has a row of tabs at the very top.
This is a list of open traces, i.e., of targets you are debugging.
You can also open old traces to examine a target's machine state *post mortem*.
In general, you should only have one trace open at a time, but there are use cases where you might have multiple.
For example, you could debug both the client and server of a network application.
To switch to another trace, single-click its tab.
When you switch traces, every Debugger window that depends on the current trace will update.
That's every window except Targets and Objects.
The Breakpoints window may change slightly, depending on its configuration, because it is designed to present all breakpoints in the session.
## Stack
Ensure your breakpoint on `rand` is enabled, and resume until you hit it.
![Stack window](images/Navigation_StackInCallRand.png)
The stack window displays a list of all the frames for the current thread.
Each thread has its own execution stack, so the frame element is actually dependent on the thread element.
The call records are listed from innermost to outermost.
Here, `main` has called an unnamed function, which has in turn called `rand`.
The columns are:
* The **Level** column gives the frame number.
This is the number of calls that must be unwound from the current machine state to reach the frame.
* The **PC** column gives the address of the next instruction in that frame.
The PC of frame 0 is the value of the PC register.
Then, the PC of frame 1 is the return address of frame 0, and so on.
* The **Function** column gives the name of the function containing the PC mapped to its static program database, if available.
* The **Comment** column allows you to annotate the frame.
Double-click the row with the unnamed function (frame 1) to switch to it.
When you switch frames, any machine-state window that involves register values may change.
**NOTE**: Some back-end debuggers do not recover register values when unwinding stack frames.
For those targets, some windows may display stale meaningless values in frames other than 0.
### Exercise: Name the Function
Your Dynamic and Static Listings should now be in the unknown function.
If you have not already done so, reverse engineer this function and give it a name.
## Time
Re-launch `termmines`, ensure both of your breakpoints at `srand` and `rand` are enabled, and resume until you hit `rand`, then step out.
Now, switch to the Time window.
![Time window](images/Navigation_TimeAfterCallSRandCallRand.png)
It displays a list of all the snapshots for the current trace.
In general, every event generates a snapshot.
By default, the most recent snapshot is at the bottom.
The columns are:
* The **Snap** column numbers each snapshot.
Other windows that indicate life spans refer to these numbers.
* The **Timestamp** column gives the time when the snapshot was created, i.e., the time when the event occurred.
* The **Event Thread** column indicates which thread caused the target to break.
This only applies to snapshots that were created because of an event, which is most.
* The **Schedule** column describes the snapshot in relation to another.
It typically only applies to emulator / scratch snapshots, which are covered later in this course.
* The **Description** column describes the event that generated the snapshot.
Switch to the snapshot where you hit `srand` (snapshot 2 in our screenshot) by double-clicking it in the table.
This will cause all the machine-state windows to update including the Stack window.
If you try navigating around the Dynamic Listing, you will likely find stale areas indicated by a grey background.
**NOTE**: Navigating into the past will automatically change the Control mode.
This is to avoid confusion, since you may perform a control action based on the state you see, which is no longer the state of the live target.
Switch back by using the Control mode drop-down button in the global toolbar.
When you select **Control Target** (with or without edits), the Debugger will navigate forward to the latest snapshot.
### Sparse vs. Full Snapshots
Regarding the stale areas: the Debugger cannot request the back-end debugger provide machine state from the past.
(Integration with timeless back-end debuggers is not yet supported.)
Remember, the trace is used as a cache, so it will only be populated with the pages and registers that you observed at the time.
Thus, most snapshots are *sparse* snapshots.
The most straightforward way to capture a *full* snapshot is the <img alt="refresh" src="images/view-refresh.png" width="16px"> Refresh button with a broad selection in the Dynamic Listing.
We give the exact steps in the next heading.
To capture registers, ensure you navigate to each thread whose registers you want to capture.
### Comparing Snapshots
A common technique for finding the address of a variable is to take and compare snapshots.
Ideally, the snapshots are taken when only the variable you are trying to locate has changed.
Depending on the program, this is not always possible, but the technique can be repeated to rule out many false positives.
The actual variable should show up in the difference every time.
For example, to find the variable that holds the number of mines, we can try to compare memory before and after parsing the command-line arguments.
Because parsing happens before waiting for user input, we will need to launch (not attach) the target.
1. Launch `termmines -M 15` in the Debugger.
(See [Getting Started](A1-GettingStarted.md) to review launching with custom parameters.)
1. Ensure your breakpoint at `srand` is enabled.
1. Use **Ctrl-A** to Select All the addresses.
1. Click the <img alt="refresh" src="images/view-refresh.png" width="16px"> Refresh button.
**NOTE**: It is normal for some errors to occur here.
We note a more surgical approach below.
1. Wait a moment for the capture to finish.
1. Optionally, press **Ctrl-Shift-N** to rename the snapshot so you can easily identify it later.
Alternatively, edit the snapshot's Description from the table in the Time window.
1. Press ![resume](images/resume.png) Resume, expecting it to break at `srand`.
1. Capture another full snapshot using Select All and Refresh.
1. Click the ![compare](images/table_relationship.png) Compare button in the Dynamic Listing.
1. In the dialog, select the first snapshot you took.
![The compare times dialog](images/Navigation_DialogCompareTimes.png)
1. Click OK.
The result is a side-by-side listing of the two snapshots with differences highlighted in orange.
Unlike the Static program comparison tool, this only highlights differences in *byte* values.
You can now use the Next and Previous Difference buttons in the Dynamic Listing to find the variable.
![The listing with comparison](images/Navigation_CompareTimes.png)
Notice that you see the command-line specified value 15 on the left, and the default value 10 on the right.
This confirms we have very likely found the variable.
**NOTE**: Using Select All to create your snapshots can be a bit aggressive.
Instead, we might guess the variable is somewhere in the `.data` section and narrow our search.
For one, including so much memory increases the prevalence of false positives, not to mention the wasted time and disk space.
Second, many of the pages in the memory map are not actually committed, leading to tons of errors trying to capture them all.
Granted, there are use cases where a full snapshot is appropriate.
Some alternatives, which we will cover in the [Memory Map](A6-MemoryMap.md) module, allow you to zero in on the `.data` section:
* Use the Memory Map window (borrowed from the CodeBrowser) to navigate to the `.data` section.
The Dynamic Listing will stay in sync and consequently capture the contents of the first page.
This specimen has a small enough `.data` section to fit in a single page, but that is generally not the case in practice.
* Use the Regions window to select the addresses in the `.data` section, then click Refresh in the Dynamic Listing.
This will capture the full `.data` section, no matter how many pages.
* Use the lower pane of the Modules window to select the addresses in the `.data` section, then click Refresh in the Dynamic Listing.
This will also capture the full `.data` section.
### Exercise: Find the Time
In `termmines`, unlike other Minesweeper clones, your score is not printed until you win.
Your goal is to achieve a remarkable score by patching a variable right before winning.
Considering it is a single-threaded application, take a moment to think about how your time might be measured.
**TIP**: Because you will need to play the game, you will need to attach rather than launch.
Use the snapshot comparison method to locate the variable.
Then place an appropriate breakpoint, win the game, patch the variable, and score 0 seconds!
If you chose a poor breakpoint or have no breakpoint at all, you should still score better than 3 seconds.
Once you know where the variable is, you can check its XRefs in the Static Listing and devise a better breakpoint.
You have completed this exercise when you can reliably score 0 seconds for games you win.
**NOTE**: If you are following and/or adapting this course using a different specimen, the timing implementation and threading may be different, but the technique still works.

View file

@ -0,0 +1,334 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Ghidra Debugger</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
</style>
<link rel="stylesheet" href="style.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="nav"><a
class="beginner" href="A1-GettingStarted.html">Getting Started</a><a
class="beginner" href="A2-UITour.html">UI Tour</a><a
class="beginner" href="A3-Breakpoints.html">Breakpoints</a><a
class="beginner" href="A4-MachineState.html">Machine State</a><a
class="beginner" href="A5-Navigation.html">Navigation</a><a
class="beginner" href="A6-MemoryMap.html">Memory Map</a><a
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a>
</header>
<header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#memory-map" id="toc-memory-map">Memory Map</a>
<ul>
<li><a href="#regions" id="toc-regions">Regions</a></li>
<li><a href="#modules" id="toc-modules">Modules</a></li>
<li><a href="#optional-exercise-find-the-time-surgically"
id="toc-optional-exercise-find-the-time-surgically">Optional Exercise:
Find the Time Surgically</a></li>
<li><a href="#static-mappings" id="toc-static-mappings">Static
Mappings</a></li>
<li><a href="#moving-knowledge-from-dynamic-to-static"
id="toc-moving-knowledge-from-dynamic-to-static">Moving Knowledge from
Dynamic to Static</a></li>
<li><a href="#exercise-export-and-map-ncurses"
id="toc-exercise-export-and-map-ncurses">Exercise: Export and Map
<code>ncurses</code></a></li>
<li><a href="#exercise-cheat-like-the-devs"
id="toc-exercise-cheat-like-the-devs">Exercise: Cheat Like the
Devs</a></li>
</ul></li>
</ul>
</nav>
<section id="memory-map" class="level1">
<h1>Memory Map</h1>
<p>This modules assumes you know how to launch <code>termmines</code> in
Ghidra using GDB, and know where to find the basic Debugger GUI
components. If not, please refer to the previous modules.</p>
<p>This module will address the following features in more depth:</p>
<ul>
<li>The Regions window</li>
<li>The Modules window</li>
<li>The Static Mappings window</li>
</ul>
<p>If you do not have an active session, please launch
<code>termmines</code> in the Debugger.</p>
<section id="regions" class="level2">
<h2>Regions</h2>
<figure>
<img src="images/MemoryMap_RegionsAfterLaunch.png"
alt="Regions window after launch" />
<figcaption aria-hidden="true">Regions window after launch</figcaption>
</figure>
<p>The Regions window displays a list of all the memory regions known to
the back-end debugger. In practice, not all targets will report this
information. The nearest analog from the CodeBrowser is the Memory Map
window. Unlike the Memory Map window, the Regions window includes all
regions mapped to external modules, as well as regions allocated for
stacks, heaps, or other system objects. The columns are:</p>
<ul>
<li>The <strong>Name</strong> column gives the name of the region. For
file-backed mappings, this should include the name of the file. It may
or may not include a section name. Typically, the name will include the
start address to avoid collisions.</li>
<li>The <strong>Lifespan</strong> column gives the span of snapshots
where the region has been observed. Memory maps can change during the
course of execution, and this is how Ghidra records and presents that
history.</li>
<li>The <strong>Start</strong> column gives the minimum address of the
region.</li>
<li>The <strong>End</strong> column gives the maximum (inclusive)
address of the region.</li>
<li>The <strong>Length</strong> column gives the number of bytes in the
region.</li>
<li>The <strong>Read</strong>, <strong>Write</strong>,
<strong>Execute</strong>, and <strong>Volatile</strong> columns give the
permissions/flags of the region.</li>
</ul>
<p>Try using the filter and column headers to sift and sort for
interesting regions. Double-click the start or end address to navigate
to them in the Dynamic Listing. Select one or more regions, right-click,
and choose <strong>Select Addresses</strong>. That should select all the
addresses in those regions in the Dynamic Listing. Used with the Refresh
button, you can surgically capture memory into the current snapshot.</p>
</section>
<section id="modules" class="level2">
<h2>Modules</h2>
<figure>
<img src="images/MemoryMap_ModulesAfterLaunch.png"
alt="Modules window after launch" />
<figcaption aria-hidden="true">Modules window after launch</figcaption>
</figure>
<p>The Modules window has two panes. The top pane displays a list of all
the <em>modules</em> known to the back-end debugger. The bottom pane
displays a list of all the <em>sections</em> known to the back-end
debugger. In practice, not all targets will report module information.
Fewer targets report section information. The nearest analog to the
bottom panel from the CodeBrowser is (also) the Memory Map window. The
top panel has no real analog; however, the tabs above the Static Listing
pane serve a similar purpose.</p>
<p>For a target that reports section information, the bottom panel will
display a lot of the same information as the Regions window. The columns
differ slightly, and the sections panel will <em>not</em> include
stacks, heaps, etc.</p>
<p>The module columns are:</p>
<ul>
<li>The <strong>Base</strong> column gives the image base for the
module. This should be the minimum address of the module.</li>
<li>The <strong>Max Address</strong> column gives the maximum address of
the module.</li>
<li>The <strong>Name</strong> column gives the (short) name of the
module.</li>
<li>The <strong>Module Name</strong> column gives the full file path of
the module <em>on target</em>. In some cases, this gives some other
description of the module.</li>
<li>The <strong>Lifespan</strong> column gives the span of snapshots
where the module has been observed.</li>
<li>The <strong>Length</strong> column gives the distance between the
base and max address (inclusive). Note that not every address between
base and max is necessarily mapped to the module. ELF headers specify
the load address of each section, so the memory footprint usually has
many gaps.</li>
</ul>
<p>The section columns are:</p>
<ul>
<li>The <strong>Start Address</strong> gives the minimum address of the
section.</li>
<li>The <strong>End Address</strong> gives the maximum (inclusive)
address of the section.</li>
<li>The <strong>Section Name</strong> gives the name of the
section.</li>
<li>The <strong>Module Name</strong> gives the name of the module
containing the section.</li>
<li>The <strong>Length</strong> gives the number of bytes contained in
the section.</li>
</ul>
<p><strong>NOTE</strong>: There is no lifespan column for a section. The
lifespan of a section is the lifespan of its containing module.</p>
<p>Try using the filter and column headers in each pane to sift and
sort. This is especially helpful for the sections: Type the name of a
module or section. You can also toggle the filter button in the local
toolbar to filter the sections pane to those contained in a selected
module from the top pane. Double-click any address to navigate to it.
Make a selection of modules or sections, right-click, and choose
<strong>Select Addresses</strong>. Again, combined with the Dynamic
Listings Refresh button, you can capture memory surgically.</p>
</section>
<section id="optional-exercise-find-the-time-surgically" class="level2">
<h2>Optional Exercise: Find the Time Surgically</h2>
<p>Repeat the “Find the Time” exercise from the previous module, but use
the Modules and Regions windows to form a more surgical selection for
capturing into the snapshots.</p>
</section>
<section id="static-mappings" class="level2">
<h2>Static Mappings</h2>
<p>The Static Mappings window provides user access to the traces static
mapping table. There are two ways to open the window:</p>
<ol type="1">
<li>In the menu: <strong>Window → Debugger → Static
Mappings</strong>.</li>
<li>From the Modules window, click Map Manually in the local
toolbar.</li>
</ol>
<figure>
<img src="images/MemoryMap_StaticMappingAfterLaunch.png"
alt="Static mappings window after launch" />
<figcaption aria-hidden="true">Static mappings window after
launch</figcaption>
</figure>
<p>Each row in the table is a range of mapped addresses. The columns
are:</p>
<ul>
<li>The <strong>Dynamic Address</strong> column gives the minimum
dynamic address in the mapped range.</li>
<li>The <strong>Static Program</strong> column gives the Ghidra URL of
the static image.</li>
<li>The <strong>Static Address</strong> column gives the minimum static
address in the mapped range.</li>
<li>The <strong>Length</strong> column gives the number of bytes in the
range.</li>
<li>The <strong>Shift</strong> column gives the difference in address
offsets from static to dynamic.</li>
<li>The <strong>Lifespan</strong> column gives the span of snapshots for
which this mapped range applies.</li>
</ul>
<p>The Ghidra Debugger relies heavily on Module information to
synchronize the listings and to correlate its static and dynamic
knowledge. Instead of using the module list directly for this
correlation, it populates a <em>static mapping</em> table. This permits
other sources, including user overrides, to inform the correlation. By
default, whenever a new program is opened and/or imported, the Debugger
will attempt to match it to a module in the trace and map it.
Furthermore, when you navigate to an address in a module that it is not
yet mapped to a program, it will search your project for a match and
open it automatically. You may notice the two address columns, as well
as the shift column. This illustrates that the Debugger can recognize
and cope with module relocation, especially from ASLR.</p>
<p>There are many ways to manually override the mappings:</p>
<ul>
<li>From the Modules window, select one or more modules, and choose from
the <strong>Map Module</strong> actions. Selecting a single module at a
time, it is possible to surgically map each to a chosen program.</li>
<li>From the Sections window, select one or more sections, and choose
from the <strong>Map Section</strong> actions. This is certainly more
tedious and atypical, but it allows the surgical mapping of each section
to a chosen memory block from among your open programs.</li>
<li>From the Regions window, select one or more regions, and choose from
the <strong>Map Region</strong> actions.</li>
<li>Click the Map Identically button in the Modules window toolbar.</li>
<li>Use the Add and Remove buttons in the Static Mappings window
toolbar.</li>
</ul>
<p>These methods are not described in detail here. For more information,
hover over the relevant actions and press <strong>F1</strong> for
help.</p>
</section>
<section id="moving-knowledge-from-dynamic-to-static" class="level2">
<h2>Moving Knowledge from Dynamic to Static</h2>
<p>There are occasions when it is necessary or convenient to transfer
data or markup from the dynamic session into a static program database.
For example, suppose during experimentation, you have placed a bunch of
code units in the Dynamic Listing. You might have done this because the
memory is uninitialized in the Static Listing, and you preferred some
trial and error in the Dynamic Listing, where the memory is populated.
In this case, you would want to copy those code units (though not
necessarily the byte values) from the Dynamic Listing into the Static
Listing. After selecting the units to copy, from the menus, you would
use <strong>Debugger → Copy Into Current Program</strong>.</p>
<p>In another example, you might not have an on-disk image for a module,
but you would still like to perform static analysis on that module. In
this case, you would want to copy everything within that module from the
dynamic session into a program database. After selecting the addresses
in that module, from the menus, you would use <strong>Debugger → Copy
Into New Program</strong>.</p>
<p>For demonstration, we will walk through this second case, pretending
we cannot load <code>libncurses</code> from disk:</p>
<ol type="1">
<li><p>In the top pane of the Modules window, right-click
<code>libncurses</code> and choose <strong>Select Addresses</strong>.
(Do not click <strong>Import From File System</strong>, since we are
pretending you cannot.)</p></li>
<li><p>Change focus to the Dynamic Listing.</p></li>
<li><p>In the global menu, choose <strong>Debugger → Copy Into New
Program</strong>.</p>
<figure>
<img src="images/MemoryMap_CopyNcursesInto.png"
alt="Copy dialog for ncurses" />
<figcaption aria-hidden="true">Copy dialog for ncurses</figcaption>
</figure></li>
<li><p>Keep Destination set to “New Program.”</p></li>
<li><p>Ensure “Read live targets memory” is checked. This will spare
you from having to create a full snapshot manually.</p></li>
<li><p>Do <em>not</em> check “Use overlays where blocks already
present.” It should not have any effect for a new program,
anyway.</p></li>
<li><p>It is probably best to include everything, though “Bytes” is the
bare minimum.</p></li>
<li><p>The table displays the <em>copy plan</em>. For a new program,
this will copy with an identical mapping of addresses, which is probably
the best plan, since the target system has already applied fixups. Do
not change any addresses, lest your corrupt references in the
copy.</p></li>
<li><p>Click Copy.</p></li>
<li><p>When prompted, name the program <code>libncurses</code>.</p></li>
<li><p>You may need to click the <code>termmines</code> tab in the
Static Listing to get the UI to completely update.</p></li>
<li><p>Click back over to <code>libncurses</code> and save the program.
If you are prompted to analyze, go ahead.</p></li>
</ol>
<p>Undoubtedly, we would like to map that new program into our dynamic
session.</p>
<ol type="1">
<li>Again, in the top pane of the Modules window, right-click
<code>libncurses</code> and choose <strong>Select
Addresses</strong>.</li>
<li>Ensure the cursor in the Static Listing is at the minimum address of
<code>libncurses</code>.</li>
<li>In the Static Mappings window, click Add in the toolbar.</li>
<li>Click OK.</li>
</ol>
<p><strong>NOTE</strong>: This should be done by choosing <strong>Map to
libncurses</strong> in the right-click menu, but a bug seems to stifle
that, currently.</p>
</section>
<section id="exercise-export-and-map-ncurses" class="level2">
<h2>Exercise: Export and Map <code>ncurses</code></h2>
<p>Repeat this technique for the “system-supplied DSO” module. In
practice, there is no real reason to do this, but this particular module
prevents you from using <strong>Import From File System</strong>.</p>
</section>
<section id="exercise-cheat-like-the-devs" class="level2">
<h2>Exercise: Cheat Like the Devs</h2>
<p>This concludes the portion on the basic features of the Ghidra
Debugger. Now, lets put your new knowledge to good use!</p>
<p>The developers left a cheat code in <code>termmines</code>. Your goal
is to figure out the cheat code, determine what it does, and describe
how it is implemented. If you have already stumbled upon this cheat, you
must still explain how it is implemented.</p>
</section>
</section>
</body>
</html>

View file

@ -0,0 +1,190 @@
# Memory Map
This modules assumes you know how to launch `termmines` in Ghidra using GDB, and know where to find the basic Debugger GUI components.
If not, please refer to the previous modules.
This module will address the following features in more depth:
* The Regions window
* The Modules window
* The Static Mappings window
If you do not have an active session, please launch `termmines` in the Debugger.
## Regions
![Regions window after launch](images/MemoryMap_RegionsAfterLaunch.png)
The Regions window displays a list of all the memory regions known to the back-end debugger.
In practice, not all targets will report this information.
The nearest analog from the CodeBrowser is the Memory Map window.
Unlike the Memory Map window, the Regions window includes all regions mapped to external modules, as well as regions allocated for stacks, heaps, or other system objects.
The columns are:
* The **Name** column gives the name of the region.
For file-backed mappings, this should include the name of the file.
It may or may not include a section name.
Typically, the name will include the start address to avoid collisions.
* The **Lifespan** column gives the span of snapshots where the region has been observed.
Memory maps can change during the course of execution, and this is how Ghidra records and presents that history.
* The **Start** column gives the minimum address of the region.
* The **End** column gives the maximum (inclusive) address of the region.
* The **Length** column gives the number of bytes in the region.
* The **Read**, **Write**, **Execute**, and **Volatile** columns give the permissions/flags of the region.
Try using the filter and column headers to sift and sort for interesting regions.
Double-click the start or end address to navigate to them in the Dynamic Listing.
Select one or more regions, right-click, and choose **Select Addresses**.
That should select all the addresses in those regions in the Dynamic Listing.
Used with the Refresh button, you can surgically capture memory into the current snapshot.
## Modules
![Modules window after launch](images/MemoryMap_ModulesAfterLaunch.png)
The Modules window has two panes.
The top pane displays a list of all the *modules* known to the back-end debugger.
The bottom pane displays a list of all the *sections* known to the back-end debugger.
In practice, not all targets will report module information.
Fewer targets report section information.
The nearest analog to the bottom panel from the CodeBrowser is (also) the Memory Map window.
The top panel has no real analog; however, the tabs above the Static Listing pane serve a similar purpose.
For a target that reports section information, the bottom panel will display a lot of the same information as the Regions window.
The columns differ slightly, and the sections panel will *not* include stacks, heaps, etc.
The module columns are:
* The **Base** column gives the image base for the module.
This should be the minimum address of the module.
* The **Max Address** column gives the maximum address of the module.
* The **Name** column gives the (short) name of the module.
* The **Module Name** column gives the full file path of the module *on target*.
In some cases, this gives some other description of the module.
* The **Lifespan** column gives the span of snapshots where the module has been observed.
* The **Length** column gives the distance between the base and max address (inclusive).
Note that not every address between base and max is necessarily mapped to the module.
ELF headers specify the load address of each section, so the memory footprint usually has many gaps.
The section columns are:
* The **Start Address** gives the minimum address of the section.
* The **End Address** gives the maximum (inclusive) address of the section.
* The **Section Name** gives the name of the section.
* The **Module Name** gives the name of the module containing the section.
* The **Length** gives the number of bytes contained in the section.
**NOTE**: There is no lifespan column for a section.
The lifespan of a section is the lifespan of its containing module.
Try using the filter and column headers in each pane to sift and sort.
This is especially helpful for the sections: Type the name of a module or section.
You can also toggle the filter button in the local toolbar to filter the sections pane to those contained in a selected module from the top pane.
Double-click any address to navigate to it.
Make a selection of modules or sections, right-click, and choose **Select Addresses**.
Again, combined with the Dynamic Listing's Refresh button, you can capture memory surgically.
## Optional Exercise: Find the Time Surgically
Repeat the "Find the Time" exercise from the previous module, but use the Modules and Regions windows to form a more surgical selection for capturing into the snapshots.
## Static Mappings
The Static Mappings window provides user access to the trace's static mapping table.
There are two ways to open the window:
1. In the menu: **Window &rarr; Debugger &rarr; Static Mappings**.
1. From the Modules window, click Map Manually in the local toolbar.
![Static mappings window after launch](images/MemoryMap_StaticMappingAfterLaunch.png)
Each row in the table is a range of mapped addresses.
The columns are:
* The **Dynamic Address** column gives the minimum dynamic address in the mapped range.
* The **Static Program** column gives the Ghidra URL of the static image.
* The **Static Address** column gives the minimum static address in the mapped range.
* The **Length** column gives the number of bytes in the range.
* The **Shift** column gives the difference in address offsets from static to dynamic.
* The **Lifespan** column gives the span of snapshots for which this mapped range applies.
The Ghidra Debugger relies heavily on Module information to synchronize the listings and to correlate its static and dynamic knowledge.
Instead of using the module list directly for this correlation, it populates a *static mapping* table.
This permits other sources, including user overrides, to inform the correlation.
By default, whenever a new program is opened and/or imported, the Debugger will attempt to match it to a module in the trace and map it.
Furthermore, when you navigate to an address in a module that it is not yet mapped to a program, it will search your project for a match and open it automatically.
You may notice the two address columns, as well as the shift column.
This illustrates that the Debugger can recognize and cope with module relocation, especially from ASLR.
There are many ways to manually override the mappings:
* From the Modules window, select one or more modules, and choose from the **Map Module** actions.
Selecting a single module at a time, it is possible to surgically map each to a chosen program.
* From the Sections window, select one or more sections, and choose from the **Map Section** actions.
This is certainly more tedious and atypical, but it allows the surgical mapping of each section to a chosen memory block from among your open programs.
* From the Regions window, select one or more regions, and choose from the **Map Region** actions.
* Click the Map Identically button in the Modules window toolbar.
* Use the Add and Remove buttons in the Static Mappings window toolbar.
These methods are not described in detail here.
For more information, hover over the relevant actions and press **F1** for help.
## Moving Knowledge from Dynamic to Static
There are occasions when it is necessary or convenient to transfer data or markup from the dynamic session into a static program database.
For example, suppose during experimentation, you have placed a bunch of code units in the Dynamic Listing.
You might have done this because the memory is uninitialized in the Static Listing, and you preferred some trial and error in the Dynamic Listing, where the memory is populated.
In this case, you would want to copy those code units (though not necessarily the byte values) from the Dynamic Listing into the Static Listing.
After selecting the units to copy, from the menus, you would use **Debugger &rarr; Copy Into Current Program**.
In another example, you might not have an on-disk image for a module, but you would still like to perform static analysis on that module.
In this case, you would want to copy everything within that module from the dynamic session into a program database.
After selecting the addresses in that module, from the menus, you would use **Debugger &rarr; Copy Into New Program**.
For demonstration, we will walk through this second case, pretending we cannot load `libncurses` from disk:
1. In the top pane of the Modules window, right-click `libncurses` and choose **Select Addresses**.
(Do not click **Import From File System**, since we are pretending you cannot.)
1. Change focus to the Dynamic Listing.
1. In the global menu, choose **Debugger &rarr; Copy Into New Program**.
![Copy dialog for ncurses](images/MemoryMap_CopyNcursesInto.png)
1. Keep Destination set to "New Program."
1. Ensure "Read live target's memory" is checked.
This will spare you from having to create a full snapshot manually.
1. Do *not* check "Use overlays where blocks already present."
It should not have any effect for a new program, anyway.
1. It is probably best to include everything, though "Bytes" is the bare minimum.
1. The table displays the *copy plan*.
For a new program, this will copy with an identical mapping of addresses, which is probably the best plan, since the target system has already applied fixups.
Do not change any addresses, lest your corrupt references in the copy.
1. Click Copy.
1. When prompted, name the program `libncurses`.
1. You may need to click the `termmines` tab in the Static Listing to get the UI to completely update.
1. Click back over to `libncurses` and save the program.
If you are prompted to analyze, go ahead.
Undoubtedly, we would like to map that new program into our dynamic session.
1. Again, in the top pane of the Modules window, right-click `libncurses` and choose **Select Addresses**.
1. Ensure the cursor in the Static Listing is at the minimum address of `libncurses`.
1. In the Static Mappings window, click Add in the toolbar.
1. Click OK.
**NOTE**: This should be done by choosing **Map to libncurses** in the right-click menu, but a bug seems to stifle that, currently.
## Exercise: Export and Map `ncurses`
Repeat this technique for the "system-supplied DSO" module.
In practice, there is no real reason to do this, but this particular module prevents you from using **Import From File System**.
## Exercise: Cheat Like the Devs
This concludes the portion on the basic features of the Ghidra Debugger.
Now, let's put your new knowledge to good use!
The developers left a cheat code in `termmines`.
Your goal is to figure out the cheat code, determine what it does, and describe how it is implemented.
If you have already stumbled upon this cheat, you must still explain how it is implemented.

View file

@ -0,0 +1,352 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Ghidra Debugger</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="style.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="nav"><a
class="beginner" href="A1-GettingStarted.html">Getting Started</a><a
class="beginner" href="A2-UITour.html">UI Tour</a><a
class="beginner" href="A3-Breakpoints.html">Breakpoints</a><a
class="beginner" href="A4-MachineState.html">Machine State</a><a
class="beginner" href="A5-Navigation.html">Navigation</a><a
class="beginner" href="A6-MemoryMap.html">Memory Map</a><a
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a>
</header>
<header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#remote-targets" id="toc-remote-targets">Remote Targets</a>
<ul>
<li><a href="#module-mapping-caveats"
id="toc-module-mapping-caveats">Module Mapping Caveats</a></li>
<li><a href="#variation-in-configuration"
id="toc-variation-in-configuration">Variation in Configuration</a></li>
<li><a href="#using-gdbserver" id="toc-using-gdbserver">Using
<code>gdbserver</code></a></li>
<li><a href="#using-ssh" id="toc-using-ssh">Using SSH</a></li>
<li><a href="#using-gadp" id="toc-using-gadp">Using GADP</a>
<ul>
<li><a href="#using-gadp-locally" id="toc-using-gadp-locally">Using GADP
Locally</a></li>
<li><a href="#using-gadp-remotely" id="toc-using-gadp-remotely">Using
GADP Remotely</a></li>
</ul></li>
<li><a href="#using-a-pty-pseudo-terminal"
id="toc-using-a-pty-pseudo-terminal">Using a pty
(pseudo-terminal)</a></li>
<li><a href="#rube-goldberg-configurations"
id="toc-rube-goldberg-configurations">Rube Goldberg
Configurations</a></li>
<li><a href="#exercise-debug-your-friends-termmines"
id="toc-exercise-debug-your-friends-termmines">Exercise: Debug your
Friends <code>termmines</code></a></li>
</ul></li>
</ul>
</nav>
<section id="remote-targets" class="level1">
<h1>Remote Targets</h1>
<p>This is the first module of the Advanced part of this course. It
assumes you have completed the Beginner portion. At the very least, you
should complete <a href="A1-GettingStarted.html">Getting Started</a> and
<a href="A2-UITour.html">A Tour of the Debugger UI</a> first.</p>
<section id="module-mapping-caveats" class="level2">
<h2>Module Mapping Caveats</h2>
<p>Beware! Many of the conveniences in Ghidra assume that the target is
running from the same file system as Ghidra, which will not be the case
when the target is remote. Be sure your current project is populated
only with programs imported from the targets file system. Additionally,
if prompted to import something new, be sure to redirect to the remote
file system, because the dialog will default to the path on the local
file system.</p>
</section>
<section id="variation-in-configuration" class="level2">
<h2>Variation in Configuration</h2>
<p>There are a number of configurations for remote debugging with many
moving parts. Some of those parts are contributed by Ghidras Debugger,
some are not. Depending on your particular target and platform, there
may be several options available to you. Consider a remote Linux target
in user space. While this list is not exhaustive, some options are:</p>
<ul>
<li>Use <code>gdbserver</code></li>
<li>Use SSH</li>
<li>Use GADP</li>
<li>Use a pty</li>
</ul>
<p>Generally, for each of these options it boils down to which
components will be colocated with the target and which will be colocated
with Ghidra.</p>
</section>
<section id="using-gdbserver" class="level2">
<h2>Using <code>gdbserver</code></h2>
<p>In this configuration, Ghidra and GDB will be located in the users
local environment, while <code>gdbserver</code> and the specimen will be
located in the target environment. The procedure follows directly from
GDBs manual, but with some Ghidra-specific steps. First, prepare the
target, which for demonstration purposes has the IP address
10.0.0.1:</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">gdbserver</span> 10.0.0.1:12345 termmines</span></code></pre></div>
<p>Then, connect from Ghidra using GDB:</p>
<ol type="1">
<li><p>From the Targets window, click Connect, select “gdb,” and click
Connect.</p></li>
<li><p>In the Interpreter, do as you would in GDB:</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">target</span> <span class="kw">remote</span> 10.0.0.1:12345</span></code></pre></div></li>
</ol>
<p>The target should now be added to the Debugger session, and things
should work as usual.</p>
</section>
<section id="using-ssh" class="level2">
<h2>Using SSH</h2>
<p>In this configuration, only Ghidra is required to be in the users
local environment, while <code>sshd</code>, <code>gdb</code> and the
specimen will be located in the target environment.
<strong>NOTE</strong>: The full <code>gdb</code>, not just
<code>gdbserver</code>, must be installed on the target system.</p>
<ol type="1">
<li><p>From the Targets window, click Connect, and select “gdb via
SSH.”</p>
<figure>
<img src="images/RemoteTargets_GdbOverSsh.png"
alt="Connect dialog for gdb via SSH" />
<figcaption aria-hidden="true">Connect dialog for gdb via
SSH</figcaption>
</figure></li>
<li><p>Set “GDB launch command” to the path of gdb <em>on the remote
file system</em>.</p></li>
<li><p>Leave “Use existing session via new-ui” unchecked.</p></li>
<li><p>Set “SSH hostname” to the name or IP address of the target
system.</p></li>
<li><p>If you are not using the standard SSH port, set “SSH TCP port”
accordingly.</p></li>
<li><p>Set “SSH username” to your username on the target
system.</p></li>
<li><p>Set “Open SSH config file” to the client config file <em>on the
local file system</em>.</p></li>
<li><p>If the remote uses DOS line endings (unlikely for a Linux
remote), then check the “Use DOS line endings” box.</p></li>
<li><p>Click Connect.</p></li>
<li><p>If prompted, enter your SSH credentials.</p></li>
</ol>
<p>If everything goes well, the Objects window should populate, and you
should get an Interpreter window presenting the remote GDB CLI. You may
use it in the usual manner to launch your target. Alternatively, in the
Objects window, click the Launch or Quick Launch button to launch the
current program. If prompted for the target command line, remember you
must provide the path <em>on the remote file system</em>.</p>
<p>The target should now be added to the Debugger session, and things
should work as usual.</p>
</section>
<section id="using-gadp" class="level2">
<h2>Using GADP</h2>
<p>GADP (Ghidra Asynchronous Debugging Protocol) is a protocol
contributed by the Ghidra Debugger. It allows any of Ghidras back-end
connectors to be deployed as an <em>agent</em>. The agent connects to
the back-end as usual, but then opens a TCP socket and waits for Ghidra
to connect.</p>
<section id="using-gadp-locally" class="level3">
<h3>Using GADP Locally</h3>
<p>When debugging locally, the UI may offer “GADP” as an alternative to
“IN-VM”. If the back-end connector tends to crash Ghidra, you may prefer
to select GADP. Typically, GADP will slow things down as information is
marshalled across a TCP connection. However, if the connector crashes,
Ghidra will simply drop the connection, whereas the IN-VM connector
would crash Ghidra, too.</p>
</section>
<section id="using-gadp-remotely" class="level3">
<h3>Using GADP Remotely</h3>
<p>In this configuration, only Ghidra is required to be in the users
local environment. The target environment must have <code>gdb</code>,
<code>java</code>, and some portion of Ghidra installed.</p>
<p>If you can install Ghidra on the remote system, there is a script to
launch the headless agent:</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="bu">cd</span> /path/to/ghidra</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="ex">support/gdbGADPServerRun</span> <span class="at">-h</span></span></code></pre></div>
<p>This should print help for you. Typically, you can just run the agent
without any extra command-line arguments:</p>
<div class="sourceCode" id="cb4"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">support/gdbGADPServerRun</span></span></code></pre></div>
<p>If not, then you probably just need to tell it where you installed
<code>gdb</code>:</p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ex">support/gdbGADPServerRun</span> <span class="at">--agent-args</span> <span class="at">-g</span> /path/to/bin/gdb</span></code></pre></div>
<p>If you cannot install Ghidra, or do not want to, then you can build a
standalone jar. You will still need to install the JRE on the target,
likely the same version as recommended for Ghidra.</p>
<p>Refer to the root README file to get started with a build from
source. You may stop short of the <code>gradle buildGhidra</code> step,
though it may be helpful to avoid trouble. Then, build the executable
jar for the GDB agent:</p>
<div class="sourceCode" id="cb6"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ex">gradle</span> Debugger-agent-gdb:nodepJar</span></code></pre></div>
<p>This will create the file
<code>Ghidra/Debug/Debugger-agent-gdb/build/libs/Debugger-agent-gdb-nodep.jar</code>.
Copy the file to the target system. Now, run it:</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ex">java</span> <span class="at">-jar</span> Debugger-agent-gdb-nodep.jar <span class="at">-h</span></span></code></pre></div>
<p>Once the agent is running, it should print its port number, and you
can connect from Ghidra. For demonstration, we will assume it is
listening at 10.0.0.2 on port 15432.</p>
<ol type="1">
<li>From the Targets window, click Connect.</li>
<li>Select “Ghidra debug agent (GADP)” from the drop-down.</li>
<li>For “Agent network address”, enter 10.0.0.2.</li>
<li>For “Agent TCP port”, enter 15432.</li>
<li>Click Connect.</li>
</ol>
<p>That should complete the connection. You should see Objects populated
and get an Interpreter window. You can then proceed to launch or attach
a target in that connection using either the Objects window or the
Interpreter window.</p>
</section>
</section>
<section id="using-a-pty-pseudo-terminal" class="level2">
<h2>Using a pty (pseudo-terminal)</h2>
<p>If your copy of GDB supports the <code>new-ui</code> command (all
versions 8.0 and up should), then you may use any of the GDB connectors
(including the local IN-VM one) to join Ghidra to an existing GDB
session:</p>
<ol type="1">
<li><p>Run <code>gdb</code> from a proper terminal:</p>
<div class="sourceCode" id="cb8"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="fu">gdb</span> termmines</span></code></pre></div></li>
<li><p>If needed, do whatever you would like to do before connecting
with Ghidra.</p></li>
<li><p>In Ghidra, from the Targets window, click Connect, and select
<code>gdb</code>.</p></li>
<li><p>Check the “Use existing session via new-ui” box.</p></li>
<li><p>Click Connect.</p></li>
<li><p>You will be prompted with the name of a pseudo terminal, e.g.,
<code>/dev/pts/1</code>.</p></li>
<li><p>Back in <code>gdb</code>:</p>
<div class="sourceCode" id="cb9"><pre
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">new-ui</span> /dev/pts/1</span></code></pre></div></li>
</ol>
<p>That should complete the connection. If there was a target active in
the existing GDB session, Ghidra should recognize it, and things should
work as usual. If there was not a target, then you should at least see
Objects populated and get an Interpreter window. You can then proceed to
launch or attach a target in that connection using either the Objects
window or the Interpreter window.</p>
<p>This same checkbox is available in the “gdb via SSH” connector. Note
that the remote system must support pseudo terminals, and the name of
the pseudo terminal is from the <em>remote file system</em>.</p>
<p>To activate this configuration in the standalone GADP agent, use the
<code>-x</code> option:</p>
<div class="sourceCode" id="cb10"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ex">java</span> <span class="at">-jar</span> Debugger-agent-gdb-node.jar <span class="at">--agent-args</span> <span class="at">-x</span></span></code></pre></div>
</section>
<section id="rube-goldberg-configurations" class="level2">
<h2>Rube Goldberg Configurations</h2>
<p>While you should always prefer the simpler configuration, it is
possible to combine components to meet a variety of needs. For example,
to debug a native Android target from Windows, you could run Ghidra on
Windows, connect it to GDB via SSH to a Linux virtual machine, e.g.,
WSL2, and then connect that to <code>gdbserver</code> running in an
Android emulator.</p>
</section>
<section id="exercise-debug-your-friends-termmines" class="level2">
<h2>Exercise: Debug your Friends <code>termmines</code></h2>
<p>If you are in a classroom setting, pair up. Otherwise, play both
roles, preferably using separate machines for Ghidra and the target.
Using either <code>gdbserver</code>, gdb via SSH, or the GDB agent,
debug <code>termmines</code>. One of you should prepare the target
environment. The other should connect to it and launch the specimen.
Then trade roles, choose a different configuration, and do it again.</p>
</section>
</section>
</body>
</html>

View file

@ -0,0 +1,195 @@
# Remote Targets
This is the first module of the Advanced part of this course.
It assumes you have completed the Beginner portion.
At the very least, you should complete [Getting Started](A1-GettingStarted.md) and [A Tour of the Debugger UI](A2-UITour.md) first.
## Module Mapping Caveats
Beware!
Many of the conveniences in Ghidra assume that the target is running from the same file system as Ghidra, which will not be the case when the target is remote.
Be sure your current project is populated only with programs imported from the target's file system.
Additionally, if prompted to import something new, be sure to redirect to the remote file system, because the dialog will default to the path on the local file system.
## Variation in Configuration
There are a number of configurations for remote debugging with many moving parts.
Some of those parts are contributed by Ghidra's Debugger, some are not.
Depending on your particular target and platform, there may be several options available to you.
Consider a remote Linux target in user space.
While this list is not exhaustive, some options are:
* Use `gdbserver`
* Use SSH
* Use GADP
* Use a pty
Generally, for each of these options it boils down to which components will be colocated with the target and which will be colocated with Ghidra.
## Using `gdbserver`
In this configuration, Ghidra and GDB will be located in the user's local environment, while `gdbserver` and the specimen will be located in the target environment.
The procedure follows directly from GDB's manual, but with some Ghidra-specific steps.
First, prepare the target, which for demonstration purposes has the IP address 10.0.0.1:
```bash
gdbserver 10.0.0.1:12345 termmines
```
Then, connect from Ghidra using GDB:
1. From the Targets window, click Connect, select "gdb," and click Connect.
1. In the Interpreter, do as you would in GDB:
```gdb
target remote 10.0.0.1:12345
```
The target should now be added to the Debugger session, and things should work as usual.
## Using SSH
In this configuration, only Ghidra is required to be in the user's local environment, while `sshd`, `gdb` and the specimen will be located in the target environment.
**NOTE**: The full `gdb`, not just `gdbserver`, must be installed on the target system.
1. From the Targets window, click Connect, and select "gdb via SSH."
![Connect dialog for gdb via SSH](images/RemoteTargets_GdbOverSsh.png)
1. Set "GDB launch command" to the path of gdb *on the remote file system*.
1. Leave "Use existing session via new-ui" unchecked.
1. Set "SSH hostname" to the name or IP address of the target system.
1. If you are not using the standard SSH port, set "SSH TCP port" accordingly.
1. Set "SSH username" to your username on the target system.
1. Set "Open SSH config file" to the client config file *on the local file system*.
1. If the remote uses DOS line endings (unlikely for a Linux remote), then check the "Use DOS line endings" box.
1. Click Connect.
1. If prompted, enter your SSH credentials.
If everything goes well, the Objects window should populate, and you should get an Interpreter window presenting the remote GDB CLI.
You may use it in the usual manner to launch your target.
Alternatively, in the Objects window, click the Launch or Quick Launch button to launch the current program.
If prompted for the target command line, remember you must provide the path *on the remote file system*.
The target should now be added to the Debugger session, and things should work as usual.
## Using GADP
GADP (Ghidra Asynchronous Debugging Protocol) is a protocol contributed by the Ghidra Debugger.
It allows any of Ghidra's back-end connectors to be deployed as an *agent*.
The agent connects to the back-end as usual, but then opens a TCP socket and waits for Ghidra to connect.
### Using GADP Locally
When debugging locally, the UI may offer "GADP" as an alternative to "IN-VM".
If the back-end connector tends to crash Ghidra, you may prefer to select GADP.
Typically, GADP will slow things down as information is marshalled across a TCP connection.
However, if the connector crashes, Ghidra will simply drop the connection, whereas the IN-VM connector would crash Ghidra, too.
### Using GADP Remotely
In this configuration, only Ghidra is required to be in the user's local environment.
The target environment must have `gdb`, `java`, and some portion of Ghidra installed.
If you can install Ghidra on the remote system, there is a script to launch the headless agent:
```bash
cd /path/to/ghidra
support/gdbGADPServerRun -h
```
This should print help for you.
Typically, you can just run the agent without any extra command-line arguments:
```bash
support/gdbGADPServerRun
```
If not, then you probably just need to tell it where you installed `gdb`:
```bash
support/gdbGADPServerRun --agent-args -g /path/to/bin/gdb
```
If you cannot install Ghidra, or do not want to, then you can build a standalone jar.
You will still need to install the JRE on the target, likely the same version as recommended for Ghidra.
Refer to the root README file to get started with a build from source.
You may stop short of the `gradle buildGhidra` step, though it may be helpful to avoid trouble.
Then, build the executable jar for the GDB agent:
```bash
gradle Debugger-agent-gdb:nodepJar
```
This will create the file `Ghidra/Debug/Debugger-agent-gdb/build/libs/Debugger-agent-gdb-nodep.jar`.
Copy the file to the target system.
Now, run it:
```bash
java -jar Debugger-agent-gdb-nodep.jar -h
```
Once the agent is running, it should print its port number, and you can connect from Ghidra.
For demonstration, we will assume it is listening at 10.0.0.2 on port 15432.
1. From the Targets window, click Connect.
1. Select "Ghidra debug agent (GADP)" from the drop-down.
1. For "Agent network address", enter 10.0.0.2.
1. For "Agent TCP port", enter 15432.
1. Click Connect.
That should complete the connection.
You should see Objects populated and get an Interpreter window.
You can then proceed to launch or attach a target in that connection using either the Objects window or
the Interpreter window.
## Using a pty (pseudo-terminal)
If your copy of GDB supports the `new-ui` command (all versions 8.0 and up should), then you may use any of the GDB connectors (including the local IN-VM one) to join Ghidra to an existing GDB session:
1. Run `gdb` from a proper terminal:
```bash
gdb termmines
```
1. If needed, do whatever you would like to do before connecting with Ghidra.
1. In Ghidra, from the Targets window, click Connect, and select `gdb`.
1. Check the "Use existing session via new-ui" box.
1. Click Connect.
1. You will be prompted with the name of a pseudo terminal, e.g., `/dev/pts/1`.
1. Back in `gdb`:
```gdb
new-ui /dev/pts/1
```
That should complete the connection.
If there was a target active in the existing GDB session, Ghidra should recognize it, and things should work as usual.
If there was not a target, then you should at least see Objects populated and get an Interpreter window.
You can then proceed to launch or attach a target in that connection using either the Objects window or the Interpreter window.
This same checkbox is available in the "gdb via SSH" connector.
Note that the remote system must support pseudo terminals, and the name of the pseudo terminal is from the *remote file system*.
To activate this configuration in the standalone GADP agent, use the `-x` option:
```bash
java -jar Debugger-agent-gdb-node.jar --agent-args -x
```
## Rube Goldberg Configurations
While you should always prefer the simpler configuration, it is possible to combine components to meet a variety of needs.
For example, to debug a native Android target from Windows, you could run Ghidra on Windows, connect it to GDB via SSH to a Linux virtual machine, e.g., WSL2, and then connect that to `gdbserver` running in an Android emulator.
## Exercise: Debug your Friend's `termmines`
If you are in a classroom setting, pair up.
Otherwise, play both roles, preferably using separate machines for Ghidra and the target.
Using either `gdbserver`, gdb via SSH, or the GDB agent, debug `termmines`.
One of you should prepare the target environment.
The other should connect to it and launch the specimen.
Then trade roles, choose a different configuration, and do it again.

View file

@ -0,0 +1,823 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Ghidra Debugger</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="style.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="nav"><a
class="beginner" href="A1-GettingStarted.html">Getting Started</a><a
class="beginner" href="A2-UITour.html">UI Tour</a><a
class="beginner" href="A3-Breakpoints.html">Breakpoints</a><a
class="beginner" href="A4-MachineState.html">Machine State</a><a
class="beginner" href="A5-Navigation.html">Navigation</a><a
class="beginner" href="A6-MemoryMap.html">Memory Map</a><a
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a>
</header>
<header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#emulation" id="toc-emulation">Emulation</a>
<ul>
<li><a href="#p-code-emulation-and-caveats"
id="toc-p-code-emulation-and-caveats">P-code Emulation and
Caveats</a></li>
<li><a href="#use-cases" id="toc-use-cases">Use Cases</a></li>
<li><a href="#extrapolation-and-interpolation"
id="toc-extrapolation-and-interpolation">Extrapolation and
Interpolation</a>
<ul>
<li><a href="#stepping-schedules" id="toc-stepping-schedules">Stepping
Schedules</a></li>
<li><a href="#exercise-demonstrate-the-cell-numbers"
id="toc-exercise-demonstrate-the-cell-numbers">Exercise: Demonstrate the
Cell Numbers</a></li>
</ul></li>
<li><a href="#emulating-a-program-image"
id="toc-emulating-a-program-image">Emulating a Program Image</a>
<ul>
<li><a href="#initializing-other-state"
id="toc-initializing-other-state">Initializing Other State</a></li>
<li><a href="#stubbing-external-calls"
id="toc-stubbing-external-calls">Stubbing External Calls</a></li>
<li><a href="#wrapping-up" id="toc-wrapping-up">Wrapping Up</a></li>
<li><a href="#optional-exercise-patch-the-placement-algorithm"
id="toc-optional-exercise-patch-the-placement-algorithm">Optional
Exercise: Patch the Placement Algorithm</a></li>
</ul></li>
<li><a href="#debugging-p-code-semantics"
id="toc-debugging-p-code-semantics">Debugging P-code Semantics</a></li>
</ul></li>
</ul>
</nav>
<section id="emulation" class="level1">
<h1>Emulation</h1>
<p>Emulation is a bit of a loaded term, unfortunately. Most of the
confusion deals with the scope of the emulated target. Do you just need
to step through a few instructions, or a whole function? Do you need to
include external modules? Do you need to simulate system calls? Do you
need to simulate connected devices? Most of Ghidras GUI-accessible
emulation features focus on the smaller scope, though it does provide
programming interfaces for advanced users to extend that scope. Those
more advanced features are covered in <a
href="B4-Modeling.html">Modeling</a>.</p>
<p>This module assumes you have completed the Beginner portion of this
course.</p>
<section id="p-code-emulation-and-caveats" class="level2">
<h2>P-code Emulation and Caveats</h2>
<p>Ghidras emulator uses the same p-code as is used by the decompiler.
P-code describes the semantics of each instruction by constructing a
sequence of p-code operations. The p-code specifications for most of
Ghidras languages were designed with decompilation, not necessarily
emulation, in mind. While in most cases, p-code for decompilation
suffices for emulation, there are cases where design decisions were
made, e.g., to keep decompiler output simple, that makes them less
suitable for emulation. This may manifest, e.g., in an excess of
user-defined p-code ops, or <em>userops</em>. The <a
href="B4-Modeling.html">Modeling</a> module discusses ways to implement
or stub those userops in the emulator. Some processor modules provide
those stubs “out of the box.” If the emulator ever halts with an
“unimplemented userop” message, then you have run into this problem.</p>
</section>
<section id="use-cases" class="level2">
<h2>Use Cases</h2>
<p>As already hinted at the start of this module, there are several use
cases for emulation, and Ghidra tries to meet these cases by integrating
emulation into the Debugger UI. Some of the use cases accessible from
the UI are:</p>
<ul>
<li>Extrapolation and interpolation of a live target.</li>
<li>Emulation of a program image.</li>
<li>P-code semantics debugging.</li>
</ul>
<p>We will explore each case with a tutorial and exercise.</p>
</section>
<section id="extrapolation-and-interpolation" class="level2">
<h2>Extrapolation and Interpolation</h2>
<p>This is perhaps the easiest use case, assuming you already have
started a live session. <em>Extrapolation</em> is predicting execution
of the target into the future, without allowing the actual target to
execute. Instead, we will allow an emulator to step forward, while
reading its initial state from the live target. This allows you, e.g.,
to experiment with various patches, or to force execution down a certain
path. If you devise a patch, you can then apply it the live target and
allow it to execute for real. <em>Interpolation</em> is similar, but
from a snapshot that is in the past. It can help answer the question,
“How did I get here?” It is more limited, because missing state for
snapshots in the past cannot be recovered.</p>
<p>In this tutorial, we will examine the command-line argument parser in
<code>termmines</code>.</p>
<ol type="1">
<li>Launch <code>termmines</code> using GDB in the Ghidra Debugger.</li>
<li>If you have not already, do a bit of static analysis to identify the
argument parsing function. It should be the first function called by
<code>main</code>.</li>
<li>Use a breakpoint to interrupt the live target when it enters this
function.</li>
<li>Change the “Control mode” drop-down to “Control Emulator.”</li>
<li>Click <img src="images/stepinto.png" alt="step into button" /> Step
Into to step the emulator forward.</li>
<li>Click <img src="images/skipover.png" alt="skip over button" /> Skip
Over and <img src="images/stepback.png" alt="step back button" /> Step
Back to experiment with different execution paths.</li>
</ol>
<p>About those two new actions:</p>
<ul>
<li><img src="images/skipover.png" alt="skip over button" />
<strong>Skip Over</strong>: Step the current thread by skipping one
instruction.</li>
<li><img src="images/stepback.png" alt="step back button" />
<strong>Step Back</strong>: Step the current thread backward one
instruction, or undo an emulated skip or patch.</li>
</ul>
<p>Try to get the program counter onto the call to <code>exit(-1)</code>
using only those three step buttons.</p>
<p>You should see things behave more or less the same as they would if
it were the live target. The main exceptions are the Objects and
Interpreter windows. Those always display the state of the live target,
as they are unaware of the emulator, and their sole purpose is to
interact with the live target. You can make changes to the emulators
machine state, set breakpoints, etc., just as you would in “Control
Target” mode. <strong>NOTE</strong>: You may see Ghidra interact with
the target, despite being in “Control Emulator” mode, because Ghidra
lazily initializes the emulators state. If the emulated target reads a
variable that Ghidra has not yet captured into the current snapshot,
Ghidra will read that variable from the live target, capture it, and
provide its value to the emulator.</p>
<section id="stepping-schedules" class="level3">
<h3>Stepping Schedules</h3>
<p>If you had not noticed before, the subtitle of the Threads window
gives the current snapshot number. If you have stepped in the emulator,
it will also contain the sequence of steps emulated. Recall the
<em>time</em> element of the Debuggers “coordinates.” (See the <a
href="A5-Navigation.html">Navigation</a> module if you need a
refresher.) The time element, called the <em>schedule</em>, consists of
both the current snapshot and the sequence of steps to emulate. The
subtitle displays that schedule. If you have done any patching of the
emulators state, you may notice some more complicated “steps” in the
schedule. The syntax is:</p>
<ul>
<li><em>Schedule</em><em>Snapshot</em> [ <code>:</code> [
<em>Step</em> ( <code>;</code> <em>Step</em> ) * ] [ <code>.</code>
<em>Step</em> ( <code>;</code> <em>Step</em> ) * ] ]</li>
<li><em>Step</em> → [ <code>t</code> <em>Id</em> <code>-</code> ] (
<em>Tick</em> | <em>Skip</em> | <em>Patch</em> )</li>
<li><em>Tick</em><em>Count</em></li>
<li><em>Skip</em><code>s</code> <em>Count</em></li>
<li><em>Patch</em><code>{</code> <em>SleighStmt</em>
<code>}</code></li>
</ul>
<p>In essence, the schedule is the starting snapshot, followed by zero
or more machine-instruction steps followed by zero or more
p-code-operation steps. Each step is optionally preceded by a thread id.
If omitted, the thread id is the same as the previous step. If the first
step has no thread id, it applies to the snapshots event thread. A
plain number indicates the number of instructions or operations to
execute. An <code>s</code> prefix indicates skip instead of execute.
Curly braces specify a patch using a single Sleigh statement. Here are
some examples:</p>
<ul>
<li><code>0</code> — The first snapshot in the trace.</li>
<li><code>3</code> — Snapshot number 3.</li>
<li><code>3:10</code> — Emulate 10 machine instructions on the event
thread, starting at snapshot 3.</li>
<li><code>3:t1-10</code> — Same as above, but on the second thread
rather than the event thread.</li>
<li><code>3:10;t1-10</code> — Start at snapshot 3. Step the event thread
10 instructions. Step the second thread 10 instructions.</li>
<li><code>3:10.4</code> — Start at snapshot 3. Step the event thread 10
instructions then 4 p-code ops.</li>
<li><code>3:{RAX=0x1234};10</code> — Start at snapshot 3. Override RAX
with 0x1234, then step 10 instructions.</li>
</ul>
<p>The explication of schedules allows Ghidra to cache emulated machine
states and manage its emulators internally. You can have Ghidra recall
or generate the machine state for any schedule by pressing
<strong>Ctrl-G</strong> or using <strong>Debugger → Go To Time</strong>
in the menus.</p>
<p>Assuming you got the program counter onto <code>exit(-1)</code>
earlier:</p>
<ol type="1">
<li>Write down the current schedule.</li>
<li>Change back to “Control Target” mode. Ghidra will navigate back to
the current snapshot, so PC will match the live target.</li>
<li>Press <strong>Ctrl-G</strong> and type or paste the schedule in, and
click OK. The program counter should be restored to
<code>exit(-1)</code>.</li>
</ol>
<p><strong>NOTE</strong>: The thread IDs used in schedules are internal
to the current trace database. Most likely, they <em>do not</em>
correspond to the thread IDs assigned by the back-end debugger.</p>
</section>
<section id="exercise-demonstrate-the-cell-numbers" class="level3">
<h3>Exercise: Demonstrate the Cell Numbers</h3>
<p>The board setup routine in <code>termmines</code> first places mines
randomly and then, for each empty cell, counts the number of neighboring
cells with mines. In this exercise, you will use extrapolation to
experiment and devise a patch to demonstrate all possible counts of
neighboring mines:</p>
<ol type="1">
<li>Run <code>termmines</code> in a proper terminal and attach to
it.</li>
<li>Use a breakpoint to trap it at the point where it has placed mines,
but before it has counted the neighboring cells with mines. (Use
<strong>Shift-R</strong> in <code>termmines</code> to reset the
game.)</li>
<li>Use the emulator to extrapolate forward and begin understanding how
the algorithm works.</li>
<li>Move the mines by patching the board to demonstrate every number of
neighboring mines. That is, when the board is revealed at the end of the
game, all the numbers 1 through 8 should appear somewhere.</li>
<li>Use extrapolation to debug and test your patch.</li>
<li>Once you have devised your patch, apply it to the live target.
(Copy-Paste is probably the easiest way to transfer the state from
emulator to target.)</li>
</ol>
</section>
</section>
<section id="emulating-a-program-image" class="level2">
<h2>Emulating a Program Image</h2>
<p>This use case allows you to load “any” Ghidra program database into
the emulator, without a back-end debugger, host environment, or other
dependencies. The result and efficacy of this method depends greatly on
what is captured in the program database. When Ghidra imports an ELF
file, it simulates the OSs loader, but only to a degree: It places each
section at its load memory address, it applies relocation fixups, etc.
The resulting program database is suitable for emulating that image, but
in relative isolation. It is probably not possible to load a library
module into that same database nor into the same emulator and expect
proper linkage. Ghidras loaders often “fix up” references to external
symbols by allocating a special <code>EXTERNAL</code> block, and placing
the external symbols there. There is (currently) no means to re-fix up.
If, however, you import a firmware image for an embedded device, or a
memory dump of a process, then the image may already have all the code
and linkage necessary.</p>
<p>It is too tedious to categorize every possible situation and failure
mode here. When you encounter an error, you should diagnose it with
particular attention to the contents of your program image, and how it
expects to interact with its environment: the host system, connected
hardware, etc. The UI has some facilities to stub out dependencies, but
if you find yourself creating and applying an extensive suite of stubs,
you may want to consider <a href="B4-Modeling.html">Modeling</a>. This
allows you to code your stubs into a library, facilitating re-use and
repeatability.</p>
<p>Emulation need not start at the images designated entry point. In
this tutorial, we will examine the command-line argument parsing
routine.</p>
<ol type="1">
<li>Ensure you have no active targets in the Debugger, but have
<code>termmines</code> open in the Static listing.</li>
<li>Go to the entry of the command-line argument parsing function.</li>
<li>Right-click its first instruction and select <strong>Emulate Program
in New Trace</strong>.</li>
</ol>
<p>This will map the program into a new trace. Technically, it is not
actually loaded into an emulator, yet, because Ghidra allocates and
caches emulators as needed. Instead, what you have is a single-snapshot
trace without a live target. The initial state is snapshot 0, and
emulation is started by navigating to a schedule, just like in
extrapolation. You might be unnerved by the apparently empty and stale
Dynamic listing:</p>
<figure>
<img src="images/Emulation_LazyStaleListing.png"
alt="Stale listing upon starting pure emulation" />
<figcaption aria-hidden="true">Stale listing upon starting pure
emulation</figcaption>
</figure>
<p>This is perhaps more a matter of preference, but by default, Ghidra
will only populate the Dynamic listing with state initialized by the
emulator itself. When the emulator reads, it will “read through”
uninitialized state by reading the mapped program image instead. This
spares the loader from having to copy a potentially large program image
into the emulator. In general, you should refer to the Static listing
when following the program counter. If you see contents in the Dynamic
listing following the program counter, then you are probably dealing
with self-modifying code.</p>
<p><strong>NOTE</strong>: If you prefer to see the Dynamic listing
initialized with the program image, you may select <strong>Load Emulator
from Program</strong> from the Auto-Read drop-down button in the Dynamic
Listing. The loading is still done lazily as each page is viewed in the
listing pane. You will want to change this back when debugging a live
target!</p>
<p>Because we can easily step back and forth as well as navigate to
arbitrary points in time, emulation should feel relatively free of risk;
however, the point about stubbing dependencies will become apparent. If
you feel the need to start over, there are two methods: First, you can
end the emulation session and restart it. To end the session, in the
Threads panel, right-click the “Emulate termmines” tab and select Close.
You can then restart by right-clicking the first instruction as before.
Second, you can use <strong>Ctrl-G</strong> to go to snapshot 0. This
method is not as clean as the first, because the trace will retain its
scratch snapshots.</p>
<p>Press <img src="images/resume.png" alt="resume button" /> Resume to
let the emulator run until it crashes. It should crash pretty quickly
and without much ceremony:</p>
<figure>
<img src="images/Emulation_ListingAfterResume.png"
alt="Listing after crashing" />
<figcaption aria-hidden="true">Listing after crashing</figcaption>
</figure>
<p>In this case, the clearest indication that something has gone wrong
is in the top-right of the Dynamic listing. Recall that the location
label is displayed in red when the program counter points outside of
mapped memory. Presumably, the crash was caused by the instruction to be
executed next. To get details about the error, press <img
src="images/stepinto.png" alt="step into button" /> Step Into. This
should display an error dialog with a full trace of the crash. In this
case, it should be an instruction decode error. When the emulator reads
uninitialized memory, it will get stale 0s; however, when the emulator
tries to <em>execute</em> uninitialized memory, it will crash. Most
likely, the target called an external function, causing the program
counter to land in the fake <code>EXTERNAL</code> block.</p>
<p>To diagnose the crash, press <img src="images/stepback.png"
alt="step back button" /> Step Back. After a couple steps back, you
should be able to confirm our hypothesis: we got here through a call to
the external function <code>printf</code>. You can continue stepping
back until you find the decision point that took us down this path. You
should notice it was because <code>param_1</code> was 0. The decompiler
can help you recognize that at a glance, but you will still want to use
the disassembly to get at precisely the deciding instruction. The
<code>JZ</code> (or other conditional jump) is too late; you need to
step back to the <code>TEST EDI,EDI</code> (or similar) instruction.
(This may, ironically, be the first instruction of the function.) In the
System V AMD64 ABI (Linux x86-64 calling conventions) <code>RDI</code>
is used to pass the first parameter. You can hover your mouse over
<code>param_1</code> in the Decompiler, and it will tell you the
location is <code>EDI:4</code>, and that its current value is a stale
0.</p>
<section id="initializing-other-state" class="level3">
<h3>Initializing Other State</h3>
<p>We had just started executing the target function arbitrarily. Ghidra
takes care of a minimal bit of initialization of the trace to start
emulation. Namely, it maps the image to its preferred base. It allocates
space for the main threads stack and initializes the stack pointer.
Finally, it initializes the program counter.</p>
<p>It is still up to you to initialize any other state, especially the
functions parameters. Clearly, we will need to initialize
<code>param_1</code>. We may need to do a little static analysis around
the call to this function to understand what those parameters are, but
you could probably make an educated guess: <code>param_1</code> is
<code>argc</code> and <code>param_2</code> is <code>argv</code>. We
might as well initialize both. Luckily, we have plenty of memory, and
given the small scope of emulation, we can probably place the strings
for <code>argv</code> wherever we would like.</p>
<p>You may prefer to apply patches to the trace database or to the
emulator. The advantage to patching in the emulator is that once you
have completed your experiments, you can readily see all of the steps
that got you to the current machine state, including all patches. The
disadvantage is that if you have extensive patches, they will pollute
the stepping schedule, and things can get unwieldy.</p>
<p>Alternatively, you can perform the patches in the trace. When you
launched the emulated target, all Ghidra really did was initialize a
trace database. The advantage to patching the trace is that once you
have completed your experiments, you will have your initial state
captured in a trace snapshot. The disadvantage is that you will need to
remember to invalidate the emulator cache any time you change the
initial state. For this tutorial, we will perform the patches in the
emulator.</p>
<p><strong>NOTE</strong>: If you wish to try patching the trace, then
change to “Control Trace” mode and use the “Navigate backward one
snapshot” control action that appears, so that you are patching the
initial state, and not a scratch snapshot. Scratch snapshots are
ephemeral snapshots in the trace used to display emulated state. Changes
to these snapshots will affect the display, but will not affect
subsequent emulation. If your current schedule includes any steps, then
“Control Trace” is patching a scratch snapshot.</p>
<p>Now, we will manually “allocate” memory for <code>argv</code>.
Luckily, Ghidra allocated 16K of stack space for us! The target function
should not need a full 16K, so we will allocate the lowest addresses of
the stack region for our command-line arguments. If you prefer, you may
use the <strong>Add Region</strong> action in the Regions window to
manually fabricate a heap region, instead. In the Regions window, filter
for “stack” and take note of the start address, e.g.,
<code>00001000</code>. We will use the Watches window to perform our
patching, though we will also use the Dynamic listing to double check.
Add the following watches:</p>
<ul>
<li><code>RSP</code> — to confirm the stack pointer is far from
<code>argv</code>.</li>
<li><code>RDI</code> — the location of <code>param_1</code>, i.e.,
<code>argc</code>.</li>
<li><code>RSI</code> — the location of <code>param_2</code>, i.e.,
<code>argv</code>.</li>
</ul>
<p>To start, we will just try to return successfully from the parser.
From the behavior we have observed, it requires at least
<code>argv[0]</code> to be present. Conventionally, this is the name of
the binary as it was invoked from the shell, i.e.,
<code>termmines</code>. There are few reasons a UNIX program might want
to examine this “argument.” First, if the binary actually implements
many commands, like <code>busybox</code> does, then that binary needs to
know the actual command. Second, if the binary needs to print usage
information, it may like to echo back the actual invocation. It is
possible we may only need to initialize <code>argc</code>, since the
parser may not actually <em>use</em> the value of
<code>argv[0]</code>.</p>
<p>Use the Watches window to set <code>RDI</code> to 1, then click <img
src="images/resume.png" alt="resume button" /> Resume. Like before, the
emulator will crash, but this time you should see “pc = 00000000” in
red. This probably indicates success. In the Threads window, you should
see a schedule similar to <code>0:t0-{RDI=0x1);t0-16</code>. This tells
us we first patched RDI, then emulated 16 machine instructions before
crashing. When the parser function returned, it probably read a stale 0
as the return address, so we would expect a decode error at
<code>00000000</code>. Step backward once to confirm this
hypothesis.</p>
</section>
<section id="stubbing-external-calls" class="level3">
<h3>Stubbing External Calls</h3>
<p>For this tutorial, we will set the skill level to Advanced by
patching in actual command-line arguments. This continues our lesson in
state initialization, but we may also need to stub some external calls,
e.g., to <code>strnlen</code> and <code>strcmp</code>. We will need to
pass in <code>termmines -s Advanced</code>, which is three arguments.
Use <strong>Ctrl-G</strong> to go back to snapshot 0, and add the
following watches:</p>
<ul>
<li><code>*:8 (RSI + 0)</code> — the address of the first argument,
i.e., <code>argv[0]</code>.</li>
<li><code>*:30 (*:8 (RSI + 0))</code> with type
<code>TerminatedCString</code> — at most 30 characters of the first
argument.</li>
<li><code>*:8 (RSI + 8)</code><code>argv[1]</code></li>
<li><code>*:30 (*:8 (RSI + 8))</code> with type
<code>TerminatedCString</code> — contents of <code>argv[1]</code></li>
<li><code>*:8 (RSI + 16)</code><code>argv[2]</code></li>
<li><code>*:30 (*:8 (RSI + 16))</code> with type
<code>TerminatedCString</code> — contents of <code>argv[2]</code></li>
</ul>
<figure>
<img src="images/Emulation_WatchesForCmdline.png"
alt="Watches for patching command-line arguments" />
<figcaption aria-hidden="true">Watches for patching command-line
arguments</figcaption>
</figure>
<p>This will generate an extensive list of patch steps, so you may
prefer to patch the trace in this case. Set <code>RDI</code> to 3.
Notice that <code>argv[0]</code> is supposedly allocated at
<code>00000000</code> according to the Address column for the watch on
<code>*:8 (RSI + 0)</code>. That was determined by the value of
<code>RSI</code>, which is essentially telling us we need to allocate
<code>argv</code>, an array of pointers. We can confirm <code>RSP</code>
is at the upper end of the stack region, so we allocate
<code>argv</code> at <code>00001000</code>. To do that, set the value of
<code>RSI</code> to <code>0x1000</code>. You should see the Address
column update for some other watches. You can double-click any of those
addresses to go there in the Dynamic listing.</p>
<p><strong>NOTE</strong>: You <em>do not have</em> to allocate things in
a listed region, but if you want to see those things in the Dynamic
listing, it is easiest if you allocate them in a listed region.</p>
<p>Now, we need to allocate space for each arguments string. To ensure
we do not collide with the space we have already allocated for
<code>argv</code>, we should place a data unit in the Dynamic listing.
Double-click the Address <code>00001000</code> in the Watches window to
go to that address in the Dynamic listing. Press <strong>P</strong> then
<strong>[</strong> (left square bracket) to place a 3-pointer array at
that address. We can now see the next available byte is at
<code>00001018</code>. <strong>NOTE</strong>: You might set the Dynamic
listing to <strong>Do Not Track</strong>, otherwise it may seek back to
the PC every time you patch.</p>
<p>Now that we know where to put <code>argv[0]</code>, we need to patch
it to <code>0x0001018</code>. This should be the watch on
<code>*:8 (RSI + 0)</code>. When you modify the Value column, you can
type either bytes (in little-endian order for x86) or the integer value
<code>0x1018</code>. That should cause the watch on
<code>*:30 (*:8 (RSI + 0))</code> to get the address
<code>00001018</code>. Using the Repr column, set that watchs value to
<code>"termmines"</code>. (The quotes are required.) Place a string in
the Dynamic listing using the <strong></strong> (apostrophe) key. This
shows us the next available address is <code>00001022</code>, so repeat
the process to allocate <code>argv[1]</code> and set it to
<code>"-s"</code>. Then finally, allocate <code>argv[2]</code> and set
it to <code>"Advanced"</code>. When you have finished, the Watches pane
should look something like this:</p>
<figure>
<img src="images/Emulation_WatchesForCmdlineSet.png"
alt="Watches for patching command-line arguments after setting" />
<figcaption aria-hidden="true">Watches for patching command-line
arguments after setting</figcaption>
</figure>
<p>The Dynamic listing should look something like this:</p>
<figure>
<img src="images/Emulation_ListingForCmdlineSet.png"
alt="Listing after setting command-line arguments" />
<figcaption aria-hidden="true">Listing after setting command-line
arguments</figcaption>
</figure>
<p><strong>NOTE</strong>: The placement of data units is not necessary
for the emulator to operate; it only cares about the bytes. However, it
is a useful aide in devising, understanding, and diagnosing machine
state.</p>
<p>Now, click <img src="images/resume.png" alt="resume button" />
Resume, and see where the emulator crashes next. Depending on your
compilation of <code>termmines</code>, it may crash after returning, or
it may crash trying to call <code>strnlen</code> or <code>strcmp</code>.
If the program counter is <code>00000000</code>, then it returned
successfully. This is unfortunate, because you no longer have motivation
to stub external calls.</p>
<p>If the program counter is not <code>00000000</code>, then step
backward until you get to the <code>CALL</code>. There are at least
three techniques for overcoming this.</p>
<ol type="1">
<li>You can skip the <code>CALL</code> and patch <code>RAX</code>
accordingly.</li>
<li>You can override the <code>CALL</code> instruction using a Sleigh
breakpoint.</li>
<li>You can override the call target using a Sleigh breakpoint.</li>
</ol>
<section id="skip-technique" class="level4">
<h4>Skip Technique</h4>
<p>The skip technique is simplest, but will need to be performed
<em>every time</em> that call is encountered. Press <img
src="images/skipover.png" alt="skip over button" /> Skip Over, then use
the Registers or Watches pane to patch <code>RAX</code>. Then press <img
src="images/resume.png" alt="resume button" /> Resume.</p>
</section>
<section id="call-override-technique" class="level4">
<h4><code>CALL</code> Override Technique</h4>
<p>Overriding the <code>CALL</code> is also fairly simple. While this
will handle every encounter, it will not handle other calls to the same
external function.</p>
<ol type="1">
<li>Press <strong>K</strong> in the listing to place a breakpoint on the
<code>CALL</code> instruction.</li>
<li>Now, in the Breakpoints panel, right-click the new breakpoint and
select <strong>Set Injection (Emulator)</strong>.</li>
<li>This is the fun part: you must now implement the function in Sleigh,
or at least stub it well enough for this particular call.</li>
</ol>
<p>Supposing this is a call to <code>strnlen</code>, you could implement
it as:</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode numberSource sleigh numberLines"><code class="sourceCode sleighsyntax"><span id="cb1-1"><a href="#cb1-1"></a>RAX = <span class="dv">0</span>;</span>
<span id="cb1-2"><a href="#cb1-2"></a>&lt;<span class="at">loop</span>&gt;</span>
<span id="cb1-3"><a href="#cb1-3"></a><span class="cf">if</span> (*:<span class="dv">1</span> (RDI+RAX) == <span class="dv">0</span> || RAX &gt;= RSI) <span class="cf">goto</span> &lt;<span class="at">exit</span>&gt;;</span>
<span id="cb1-4"><a href="#cb1-4"></a>RAX = RAX + <span class="dv">1</span>;</span>
<span id="cb1-5"><a href="#cb1-5"></a><span class="cf">goto</span> &lt;<span class="at">loop</span>&gt;;</span>
<span id="cb1-6"><a href="#cb1-6"></a>&lt;<span class="at">exit</span>&gt;</span>
<span id="cb1-7"><a href="#cb1-7"></a>emu_skip_decoded();</span></code></pre></div>
<p>While Sleigh has fairly nice C-like expressions, it unfortunately
does not have C-like control structures. We are essentially writing a
for loop. The System V AMD64 ABI specifies RAX is for the return value,
so we can just use it directly as the counter. RDI points to the string
to measure, and RSI gives the maximum length. We initialize RAX to 0,
and then check if the current character is NULL, or the count has
exceeded the maximum length. If so, we are done; if not, we increment
RAX and repeat. Finally, because we are <em>replacing</em> the semantics
of the <code>CALL</code> instruction, we tell the emulator to skip the
current instruction.</p>
<p>For the complete specification of Sleigh, see the Semantic Section in
the <a
href="../../../Ghidra/Features/Decompiler/src/main/doc/sleigh.xml">Sleigh
documentation</a>. The emulator adds a few userops:</p>
<ul>
<li><code>emu_skip_decoded()</code>: Skip the current instruction.</li>
<li><code>emu_exec_decoded()</code>: Execute the current
instruction.</li>
<li><code>emu_swi()</code>: Interrupt, as in a breakpoint.</li>
</ul>
<p>Some control flow is required in the Sleigh injection, otherwise, the
emulator may never advance past the current instruction. An explicit
call to <code>emu_exec_decoded()</code> allows you to insert logic
before and/or after the original instruction; however, if the original
instruction branches, then the logic you placed <em>after</em> will not
be reached. An explicit call to <code>emu_skip_decoded()</code> allows
you to omit the original instruction altogether. It immediately falls
through to the next instruction. The <code>emu_swi()</code> userop
allows you to maintain breakpoint behavior, perhaps to debug your
injection.</p>
<p>After you have written your Sleigh code:</p>
<ol type="1">
<li>Click OK on the Set Injection dialog.</li>
<li>In the menus, select <strong>Debugger → Configure Emulator →
Invalidate Emulator Cache</strong>.</li>
<li>Click <img src="images/resume.png" alt="resume button" />
Resume.</li>
</ol>
<p>Stubbing any remaining external calls is left as an exercise. You are
successful when the emulator crashes with
<code>pc = 00000000</code>.</p>
<p>Clear or disable your breakpoint and invalidate the emulator cache
again before proceeding to the next technique.</p>
</section>
<section id="target-override-technique" class="level4">
<h4>Target Override Technique</h4>
<p>The target override technique is most thorough, but also the most
involved. It will handle all calls to the external function, e.g.,
<code>strnlen</code>, no matter the call site. If the call goes through
a program linkage table (PLT), then you are in luck, because the call
target will be visible in the Dynamic listing. The PLT entry usually
contains a single <code>JMP</code> instruction to the actual
<code>strnlen</code>. For real target processes, the <code>JMP</code>
instruction will transfer control to a lazy linker the first time
<code>strnlen</code> is called from <code>termmines</code>. The linker
then finds <code>strnlen</code> and patches the table. In contrast, the
Ghidra loader immediately patches the table to point to a fake
<code>&lt;EXTERNAL&gt;::strnlen</code> symbol. The <code>EXTERNAL</code>
block is not visible in the Dynamic listing, so we will override the
<code>JMP</code> in the PLT.</p>
<p>The Sleigh code is nearly identical, but we must code an x86
<code>RET</code> into it. Because we allow the <code>CALL</code> to
execute normally, we must restore the stack. Furthermore, we must return
control back to the caller, just like a real x86 subroutine would. We
also no longer need <code>emu_skip_decoded()</code>, because the
<code>RET</code> will provide the necessary control transfer.</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode numberSource sleigh numberLines"><code class="sourceCode sleighsyntax"><span id="cb2-1"><a href="#cb2-1"></a>RAX = <span class="dv">0</span>;</span>
<span id="cb2-2"><a href="#cb2-2"></a>&lt;<span class="at">loop</span>&gt;</span>
<span id="cb2-3"><a href="#cb2-3"></a><span class="cf">if</span> (*:<span class="dv">1</span> (RDI+RAX) == <span class="dv">0</span> || RAX &gt;= RSI) <span class="cf">goto</span> &lt;<span class="at">exit</span>&gt;;</span>
<span id="cb2-4"><a href="#cb2-4"></a>RAX = RAX + <span class="dv">1</span>;</span>
<span id="cb2-5"><a href="#cb2-5"></a><span class="cf">goto</span> &lt;<span class="at">loop</span>&gt;;</span>
<span id="cb2-6"><a href="#cb2-6"></a>&lt;<span class="at">exit</span>&gt;</span>
<span id="cb2-7"><a href="#cb2-7"></a>RIP = *:<span class="dv">8</span> RSP;</span>
<span id="cb2-8"><a href="#cb2-8"></a>RSP = RSP + <span class="dv">8</span>;</span>
<span id="cb2-9"><a href="#cb2-9"></a><span class="cf">return</span> [RIP];</span></code></pre></div>
<p>Notice that we cannot just write <code>RET</code>, but instead must
write the Sleigh code to mimic a <code>RET</code>. As with the
<code>CALL</code> override technique, you must now invalidate the
emulator cache and resume. Stubbing any remaining external functions is
left as an exercise. You are successful when the emulator crashes with
<code>pc = 00000000</code>.</p>
</section>
</section>
<section id="wrapping-up" class="level3">
<h3>Wrapping Up</h3>
<p>As you can see, depending on the scope of emulation, and the
particulars of the target function, emulating a program image can be
quite involved. Whatever technique you choose, once you have
successfully returned from the command-line argument parser, you should
check for the expected effects.</p>
<p>In the Static listing, navigate to the variable that stores the
boards dimensions. (Finding that variable is a task in the Beginner
portion, but it can be found pretty easily with some manual static
analysis.) In the Dynamic listing, you should notice that the values
have changed to reflect the Advanced skill level.</p>
</section>
<section id="optional-exercise-patch-the-placement-algorithm"
class="level3">
<h3>Optional Exercise: Patch the Placement Algorithm</h3>
<p>In this exercise, you will use emulation to devise an assembly patch
to <code>termmines</code> to change the mine placement algorithm.
Instead of random placement, please have them placed left to right, top
to bottom. We recommend you devise your patch using the Assembler (Patch
Instruction action) in the Static listing, then test and debug your
patch using the Emulator. Perhaps patch the Dynamic listing to try quick
tweaks before committing them to the Static listing. Once you have it,
export the patched binary and run it in a proper terminal.</p>
</section>
</section>
<section id="debugging-p-code-semantics" class="level2">
<h2>Debugging P-code Semantics</h2>
<p>The last use case for emulation we will cover in this course is
debugging p-code semantics. This use case is a bit niche, so we will not
cover it too deeply. It is useful for debugging processor modules. It is
also useful in system modeling, since a lot of that is accomplished
using Sleigh p-code. Perhaps the most useful case related to this module
is to debug Sleigh injections.</p>
<p>Ghidra has a dedicated panel for stepping the emulator one p-code
operation at a time. This panel is not included in the default Debugger
tool, so it must be configured:</p>
<ol type="1">
<li>If you have not already, open the Debugger tool.</li>
<li>In the menus, select <strong>File → Configure</strong>.</li>
<li>Click the “Configure All Plugins” button in the top right of the
dialog.</li>
<li>Activate the <code>DebuggerPcodeStepperPlugin</code></li>
<li>Click OK</li>
<li>Click Close</li>
</ol>
<p>The stepper should appear stacked over the Threads panel in the
bottom right. Yours will probably still be empty, but here is what it
looks like populated:</p>
<figure>
<img src="images/Emulation_PcodeStepper.png" alt="P-code stepper" />
<figcaption aria-hidden="true">P-code stepper</figcaption>
</figure>
<p>To populate it, you will need a session, either emulated or connected
to a back-end debugger. Use the buttons in the local toolbar to step
p-code operations. The first p-code op of any instruction is to decode
the instruction. Once decoded, the p-code listing (left panel) will
populate with the ops of the decoded instruction. If the current
instruction is overridden by a Sleigh breakpoint, the listing will
populate with the injected ops instead. You can then step forward and
backward within those. As you step, the other windows that display
machine state will update.</p>
<p>In addition to registers and memory, p-code has “unique” variables.
These are temporary variables used only within an instructions
implementation. They are displayed in the right panel. The table of
variables works similarly to the Registers pane. The columns are:</p>
<ul>
<li>The <strong>Unique</strong> column gives the variables name and
size in bytes.</li>
<li>The <strong>Bytes</strong> column gives the variables value in
bytes.</li>
<li>The <strong>Value</strong> column gives the variables value as an
integer, an interpretation of the bytes in the machines byte
order.</li>
<li>The <strong>Type</strong> column allows you to assign a type. This
is ephemeral.</li>
<li>The <strong>Repr</strong> column gives the variables value
according to the assigned type.</li>
</ul>
<p>As you step, you may notice the schedule changes. It is displayed in
the steppers subtitle as well as the Threads panels subtitle. P-code
stepping is denoted by the portion of the schedule following the dot.
<strong>NOTE</strong>: You cannot mix instruction steps with p-code op
steps. The instruction steps always precede the p-code ops. If you click
Step Into from the global toolbar in the middle of an instruction, the
trailing p-code op steps will be removed and replaced with a single
instruction step. In most cases, this intuitively “finishes” the partial
instruction.</p>
</section>
</section>
</body>
</html>

View file

@ -0,0 +1,493 @@
# Emulation
Emulation is a bit of a loaded term, unfortunately.
Most of the confusion deals with the scope of the emulated target.
Do you just need to step through a few instructions, or a whole function?
Do you need to include external modules?
Do you need to simulate system calls?
Do you need to simulate connected devices?
Most of Ghidra's GUI-accessible emulation features focus on the smaller scope, though it does provide programming interfaces for advanced users to extend that scope.
Those more advanced features are covered in [Modeling](B4-Modeling.md).
This module assumes you have completed the Beginner portion of this course.
## P-code Emulation and Caveats
Ghidra's emulator uses the same p-code as is used by the decompiler.
P-code describes the semantics of each instruction by constructing a sequence of p-code operations.
The p-code specifications for most of Ghidra's languages were designed with decompilation, not necessarily emulation, in mind.
While in most cases, p-code for decompilation suffices for emulation, there are cases where design decisions were made, e.g., to keep decompiler output simple, that makes them less suitable for emulation.
This may manifest, e.g., in an excess of user-defined p-code ops, or *userops*.
The [Modeling](B4-Modeling.md) module discusses ways to implement or stub those userops in the emulator.
Some processor modules provide those stubs "out of the box."
If the emulator ever halts with an "unimplemented userop" message, then you have run into this problem.
## Use Cases
As already hinted at the start of this module, there are several use cases for emulation, and Ghidra tries to meet these cases by integrating emulation into the Debugger UI.
Some of the use cases accessible from the UI are:
* Extrapolation and interpolation of a live target.
* Emulation of a program image.
* P-code semantics debugging.
We will explore each case with a tutorial and exercise.
## Extrapolation and Interpolation
This is perhaps the easiest use case, assuming you already have started a live session.
*Extrapolation* is predicting execution of the target into the future, without allowing the actual target to execute.
Instead, we will allow an emulator to step forward, while reading its initial state from the live target.
This allows you, e.g., to experiment with various patches, or to force execution down a certain path.
If you devise a patch, you can then apply it the live target and allow it to execute for real.
*Interpolation* is similar, but from a snapshot that is in the past.
It can help answer the question, "How did I get here?"
It is more limited, because missing state for snapshots in the past cannot be recovered.
In this tutorial, we will examine the command-line argument parser in `termmines`.
1. Launch `termmines` using GDB in the Ghidra Debugger.
1. If you have not already, do a bit of static analysis to identify the argument parsing function.
It should be the first function called by `main`.
1. Use a breakpoint to interrupt the live target when it enters this function.
1. Change the "Control mode" drop-down to "Control Emulator."
1. Click ![step into button](images/stepinto.png) Step Into to step the emulator forward.
1. Click ![skip over button](images/skipover.png) Skip Over and ![step back button](images/stepback.png) Step Back to experiment with different execution paths.
About those two new actions:
* ![skip over button](images/skipover.png) **Skip Over**:
Step the current thread by skipping one instruction.
* ![step back button](images/stepback.png) **Step Back**:
Step the current thread backward one instruction, or undo an emulated skip or patch.
Try to get the program counter onto the call to `exit(-1)` using only those three step buttons.
You should see things behave more or less the same as they would if it were the live target.
The main exceptions are the Objects and Interpreter windows.
Those always display the state of the live target, as they are unaware of the emulator, and their sole purpose is to interact with the live target.
You can make changes to the emulator's machine state, set breakpoints, etc., just as you would in "Control Target" mode.
**NOTE**: You may see Ghidra interact with the target, despite being in "Control Emulator" mode, because Ghidra lazily initializes the emulator's state.
If the emulated target reads a variable that Ghidra has not yet captured into the current snapshot, Ghidra will read that variable from the live target, capture it, and provide its value to the emulator.
### Stepping Schedules
If you had not noticed before, the subtitle of the Threads window gives the current snapshot number.
If you have stepped in the emulator, it will also contain the sequence of steps emulated.
Recall the *time* element of the Debugger's "coordinates."
(See the [Navigation](A5-Navigation.md) module if you need a refresher.)
The time element, called the *schedule*, consists of both the current snapshot and the sequence of steps to emulate.
The subtitle displays that schedule.
If you have done any patching of the emulator's state, you may notice some more complicated "steps" in the schedule.
The syntax is:
* *Schedule* &rarr; *Snapshot* \[ `:` \[ *Step* ( `;` *Step* ) \* \] \[ `.` *Step* ( `;` *Step* ) \* \] \]
* *Step* &rarr; [ `t` *Id* `-` ] ( *Tick* | *Skip* | *Patch* )
* *Tick* &rarr; *Count*
* *Skip* &rarr; `s` *Count*
* *Patch* &rarr; `{` *SleighStmt* `}`
In essence, the schedule is the starting snapshot, followed by zero or more machine-instruction steps followed by zero or more p-code-operation steps.
Each step is optionally preceded by a thread id.
If omitted, the thread id is the same as the previous step.
If the first step has no thread id, it applies to the snapshot's event thread.
A plain number indicates the number of instructions or operations to execute.
An `s` prefix indicates skip instead of execute.
Curly braces specify a patch using a single Sleigh statement.
Here are some examples:
* `0` &mdash; The first snapshot in the trace.
* `3` &mdash; Snapshot number 3.
* `3:10` &mdash; Emulate 10 machine instructions on the event thread, starting at snapshot 3.
* `3:t1-10` &mdash; Same as above, but on the second thread rather than the event thread.
* `3:10;t1-10` &mdash; Start at snapshot 3. Step the event thread 10 instructions. Step the second thread 10 instructions.
* `3:10.4` &mdash; Start at snapshot 3. Step the event thread 10 instructions then 4 p-code ops.
* `3:{RAX=0x1234};10` &mdash; Start at snapshot 3. Override RAX with 0x1234, then step 10 instructions.
The explication of schedules allows Ghidra to cache emulated machine states and manage its emulators internally.
You can have Ghidra recall or generate the machine state for any schedule by pressing **Ctrl-G** or using **Debugger &rarr; Go To Time** in the menus.
Assuming you got the program counter onto `exit(-1)` earlier:
1. Write down the current schedule.
1. Change back to "Control Target" mode.
Ghidra will navigate back to the current snapshot, so PC will match the live target.
1. Press **Ctrl-G** and type or paste the schedule in, and click OK.
The program counter should be restored to `exit(-1)`.
**NOTE**: The thread IDs used in schedules are internal to the current trace database.
Most likely, they *do not* correspond to the thread IDs assigned by the back-end debugger.
### Exercise: Demonstrate the Cell Numbers
The board setup routine in `termmines` first places mines randomly and then, for each empty cell, counts the number of neighboring cells with mines.
In this exercise, you will use extrapolation to experiment and devise a patch to demonstrate all possible counts of neighboring mines:
1. Run `termmines` in a proper terminal and attach to it.
1. Use a breakpoint to trap it at the point where it has placed mines, but before it has counted the neighboring cells with mines.
(Use **Shift-R** in `termmines` to reset the game.)
1. Use the emulator to extrapolate forward and begin understanding how the algorithm works.
1. Move the mines by patching the board to demonstrate every number of neighboring mines.
That is, when the board is revealed at the end of the game, all the numbers 1 through 8 should appear somewhere.
1. Use extrapolation to debug and test your patch.
1. Once you have devised your patch, apply it to the live target.
(Copy-Paste is probably the easiest way to transfer the state from emulator to target.)
## Emulating a Program Image
This use case allows you to load "any" Ghidra program database into the emulator, without a back-end debugger, host environment, or other dependencies.
The result and efficacy of this method depends greatly on what is captured in the program database.
When Ghidra imports an ELF file, it simulates the OS's loader, but only to a degree:
It places each section at its load memory address, it applies relocation fixups, etc.
The resulting program database is suitable for emulating that image, but in relative isolation.
It is probably not possible to load a library module into that same database nor into the same emulator and expect proper linkage.
Ghidra's loaders often "fix up" references to external symbols by allocating a special `EXTERNAL` block, and placing the external symbols there.
There is (currently) no means to re-fix up.
If, however, you import a firmware image for an embedded device, or a memory dump of a process, then the image may already have all the code and linkage necessary.
It is too tedious to categorize every possible situation and failure mode here.
When you encounter an error, you should diagnose it with particular attention to the contents of your program image, and how it expects to interact with its environment: the host system, connected hardware, etc.
The UI has some facilities to stub out dependencies, but if you find yourself creating and applying an extensive suite of stubs, you may want to consider [Modeling](B4-Modeling.md).
This allows you to code your stubs into a library, facilitating re-use and repeatability.
Emulation need not start at the image's designated entry point.
In this tutorial, we will examine the command-line argument parsing routine.
1. Ensure you have no active targets in the Debugger, but have `termmines` open in the Static listing.
1. Go to the entry of the command-line argument parsing function.
1. Right-click its first instruction and select **Emulate Program in New Trace**.
This will map the program into a new trace.
Technically, it is not actually loaded into an emulator, yet, because Ghidra allocates and caches emulators as needed.
Instead, what you have is a single-snapshot trace without a live target.
The initial state is snapshot 0, and emulation is started by navigating to a schedule, just like in extrapolation.
You might be unnerved by the apparently empty and stale Dynamic listing:
![Stale listing upon starting pure emulation](images/Emulation_LazyStaleListing.png)
This is perhaps more a matter of preference, but by default, Ghidra will only populate the Dynamic listing with state initialized by the emulator itself.
When the emulator reads, it will "read through" uninitialized state by reading the mapped program image instead.
This spares the loader from having to copy a potentially large program image into the emulator.
In general, you should refer to the Static listing when following the program counter.
If you see contents in the Dynamic listing following the program counter, then you are probably dealing with self-modifying code.
**NOTE**: If you prefer to see the Dynamic listing initialized with the program image, you may select **Load Emulator from Program** from the Auto-Read drop-down button in the Dynamic Listing.
The loading is still done lazily as each page is viewed in the listing pane.
You will want to change this back when debugging a live target!
Because we can easily step back and forth as well as navigate to arbitrary points in time, emulation should feel relatively free of risk; however, the point about stubbing dependencies will become apparent.
If you feel the need to start over, there are two methods:
First, you can end the emulation session and restart it.
To end the session, in the Threads panel, right-click the "Emulate termmines" tab and select Close.
You can then restart by right-clicking the first instruction as before.
Second, you can use **Ctrl-G** to go to snapshot 0.
This method is not as clean as the first, because the trace will retain its scratch snapshots.
Press ![resume button](images/resume.png) Resume to let the emulator run until it crashes.
It should crash pretty quickly and without much ceremony:
![Listing after crashing](images/Emulation_ListingAfterResume.png)
In this case, the clearest indication that something has gone wrong is in the top-right of the Dynamic listing.
Recall that the location label is displayed in red when the program counter points outside of mapped memory.
Presumably, the crash was caused by the instruction to be executed next.
To get details about the error, press ![step into button](images/stepinto.png) Step Into.
This should display an error dialog with a full trace of the crash.
In this case, it should be an instruction decode error.
When the emulator reads uninitialized memory, it will get stale 0s; however, when the emulator tries to *execute* uninitialized memory, it will crash.
Most likely, the target called an external function, causing the program counter to land in the fake `EXTERNAL` block.
To diagnose the crash, press ![step back button](images/stepback.png) Step Back.
After a couple steps back, you should be able to confirm our hypothesis: we got here through a call to the external function `printf`.
You can continue stepping back until you find the decision point that took us down this path.
You should notice it was because `param_1` was 0.
The decompiler can help you recognize that at a glance, but you will still want to use the disassembly to get at precisely the deciding instruction.
The `JZ` (or other conditional jump) is too late; you need to step back to the `TEST EDI,EDI` (or similar) instruction.
(This may, ironically, be the first instruction of the function.)
In the System V AMD64 ABI (Linux x86-64 calling conventions) `RDI` is used to pass the first parameter.
You can hover your mouse over `param_1` in the Decompiler, and it will tell you the location is `EDI:4`, and that its current value is a stale 0.
### Initializing Other State
We had just started executing the target function arbitrarily.
Ghidra takes care of a minimal bit of initialization of the trace to start emulation.
Namely, it maps the image to its preferred base.
It allocates space for the main thread's stack and initializes the stack pointer.
Finally, it initializes the program counter.
It is still up to you to initialize any other state, especially the function's parameters.
Clearly, we will need to initialize `param_1`.
We may need to do a little static analysis around the call to this function to understand what those parameters are, but you could probably make an educated guess:
`param_1` is `argc` and `param_2` is `argv`.
We might as well initialize both.
Luckily, we have plenty of memory, and given the small scope of emulation, we can probably place the strings for `argv` wherever we would like.
You may prefer to apply patches to the trace database or to the emulator.
The advantage to patching in the emulator is that once you have completed your experiments, you can readily see all of the steps that got you to the current machine state, including all patches.
The disadvantage is that if you have extensive patches, they will pollute the stepping schedule, and things can get unwieldy.
Alternatively, you can perform the patches in the trace.
When you launched the emulated target, all Ghidra really did was initialize a trace database.
The advantage to patching the trace is that once you have completed your experiments, you will have your initial state captured in a trace snapshot.
The disadvantage is that you will need to remember to invalidate the emulator cache any time you change the initial state.
For this tutorial, we will perform the patches in the emulator.
**NOTE**: If you wish to try patching the trace, then change to "Control Trace" mode and use the "Navigate backward one snapshot" control action that appears, so that you are patching the initial state, and not a scratch snapshot.
Scratch snapshots are ephemeral snapshots in the trace used to display emulated state.
Changes to these snapshots will affect the display, but will not affect subsequent emulation.
If your current schedule includes any steps, then "Control Trace" is patching a scratch snapshot.
Now, we will manually "allocate" memory for `argv`.
Luckily, Ghidra allocated 16K of stack space for us!
The target function should not need a full 16K, so we will allocate the lowest addresses of the stack region for our command-line arguments.
If you prefer, you may use the **Add Region** action in the Regions window to manually fabricate a heap region, instead.
In the Regions window, filter for "stack" and take note of the start address, e.g., `00001000`.
We will use the Watches window to perform our patching, though we will also use the Dynamic listing to double check.
Add the following watches:
* `RSP` &mdash; to confirm the stack pointer is far from `argv`.
* `RDI` &mdash; the location of `param_1`, i.e., `argc`.
* `RSI` &mdash; the location of `param_2`, i.e., `argv`.
To start, we will just try to return successfully from the parser.
From the behavior we have observed, it requires at least `argv[0]` to be present.
Conventionally, this is the name of the binary as it was invoked from the shell, i.e., `termmines`.
There are few reasons a UNIX program might want to examine this "argument."
First, if the binary actually implements many commands, like `busybox` does, then that binary needs to know the actual command.
Second, if the binary needs to print usage information, it may like to echo back the actual invocation.
It is possible we may only need to initialize `argc`, since the parser may not actually *use* the value of `argv[0]`.
Use the Watches window to set `RDI` to 1, then click ![resume button](images/resume.png) Resume.
Like before, the emulator will crash, but this time you should see "pc = 00000000" in red.
This probably indicates success.
In the Threads window, you should see a schedule similar to `0:t0-{RDI=0x1);t0-16`.
This tells us we first patched RDI, then emulated 16 machine instructions before crashing.
When the parser function returned, it probably read a stale 0 as the return address, so we would expect a decode error at `00000000`.
Step backward once to confirm this hypothesis.
### Stubbing External Calls
For this tutorial, we will set the skill level to Advanced by patching in actual command-line arguments.
This continues our lesson in state initialization, but we may also need to stub some external calls, e.g., to `strnlen` and `strcmp`.
We will need to pass in `termmines -s Advanced`, which is three arguments.
Use **Ctrl-G** to go back to snapshot 0, and add the following watches:
* `*:8 (RSI + 0)` &mdash; the address of the first argument, i.e., `argv[0]`.
* `*:30 (*:8 (RSI + 0))` with type `TerminatedCString` &mdash; at most 30 characters of the first argument.
* `*:8 (RSI + 8)` &mdash; `argv[1]`
* `*:30 (*:8 (RSI + 8))` with type `TerminatedCString` &mdash; contents of `argv[1]`
* `*:8 (RSI + 16)` &mdash; `argv[2]`
* `*:30 (*:8 (RSI + 16))` with type `TerminatedCString` &mdash; contents of `argv[2]`
![Watches for patching command-line arguments](images/Emulation_WatchesForCmdline.png)
This will generate an extensive list of patch steps, so you may prefer to patch the trace in this case.
Set `RDI` to 3.
Notice that `argv[0]` is supposedly allocated at `00000000` according to the Address column for the watch on `*:8 (RSI + 0)`.
That was determined by the value of `RSI`, which is essentially telling us we need to allocate `argv`, an array of pointers.
We can confirm `RSP` is at the upper end of the stack region, so we allocate `argv` at `00001000`.
To do that, set the value of `RSI` to `0x1000`.
You should see the Address column update for some other watches.
You can double-click any of those addresses to go there in the Dynamic listing.
**NOTE**: You *do not have* to allocate things in a listed region, but if you want to see those things in the Dynamic listing, it is easiest if you allocate them in a listed region.
Now, we need to allocate space for each argument's string.
To ensure we do not collide with the space we have already allocated for `argv`, we should place a data unit in the Dynamic listing.
Double-click the Address `00001000` in the Watches window to go to that address in the Dynamic listing.
Press **P** then **[** (left square bracket) to place a 3-pointer array at that address.
We can now see the next available byte is at `00001018`.
**NOTE**: You might set the Dynamic listing to **Do Not Track**, otherwise it may seek back to the PC every time you patch.
Now that we know where to put `argv[0]`, we need to patch it to `0x0001018`.
This should be the watch on `*:8 (RSI + 0)`.
When you modify the Value column, you can type either bytes (in little-endian order for x86) or the integer value `0x1018`.
That should cause the watch on `*:30 (*:8 (RSI + 0))` to get the address `00001018`.
Using the Repr column, set that watch's value to `"termmines"`.
(The quotes are required.)
Place a string in the Dynamic listing using the **'** (apostrophe) key.
This shows us the next available address is `00001022`, so repeat the process to allocate `argv[1]` and set it to `"-s"`.
Then finally, allocate `argv[2]` and set it to `"Advanced"`.
When you have finished, the Watches pane should look something like this:
![Watches for patching command-line arguments after setting](images/Emulation_WatchesForCmdlineSet.png)
The Dynamic listing should look something like this:
![Listing after setting command-line arguments](images/Emulation_ListingForCmdlineSet.png)
**NOTE**: The placement of data units is not necessary for the emulator to operate; it only cares about the bytes.
However, it is a useful aide in devising, understanding, and diagnosing machine state.
Now, click ![resume button](images/resume.png) Resume, and see where the emulator crashes next.
Depending on your compilation of `termmines`, it may crash after returning, or it may crash trying to call `strnlen` or `strcmp`.
If the program counter is `00000000`, then it returned successfully.
This is unfortunate, because you no longer have motivation to stub external calls.
If the program counter is not `00000000`, then step backward until you get to the `CALL`.
There are at least three techniques for overcoming this.
1. You can skip the `CALL` and patch `RAX` accordingly.
1. You can override the `CALL` instruction using a Sleigh breakpoint.
1. You can override the call target using a Sleigh breakpoint.
#### Skip Technique
The skip technique is simplest, but will need to be performed *every time* that call is encountered.
Press ![skip over button](images/skipover.png) Skip Over, then use the Registers or Watches pane to patch `RAX`.
Then press ![resume button](images/resume.png) Resume.
#### `CALL` Override Technique
Overriding the `CALL` is also fairly simple.
While this will handle every encounter, it will not handle other calls to the same external function.
1. Press **K** in the listing to place a breakpoint on the `CALL` instruction.
1. Now, in the Breakpoints panel, right-click the new breakpoint and select **Set Injection (Emulator)**.
1. This is the fun part: you must now implement the function in Sleigh, or at least stub it well enough for this particular call.
Supposing this is a call to `strnlen`, you could implement it as:
```sleigh {.numberLines}
RAX = 0;
<loop>
if (*:1 (RDI+RAX) == 0 || RAX >= RSI) goto <exit>;
RAX = RAX + 1;
goto <loop>;
<exit>
emu_skip_decoded();
```
While Sleigh has fairly nice C-like expressions, it unfortunately does not have C-like control structures.
We are essentially writing a for loop.
The System V AMD64 ABI specifies RAX is for the return value, so we can just use it directly as the counter.
RDI points to the string to measure, and RSI gives the maximum length.
We initialize RAX to 0, and then check if the current character is NULL, or the count has exceeded the maximum length.
If so, we are done; if not, we increment RAX and repeat.
Finally, because we are *replacing* the semantics of the `CALL` instruction, we tell the emulator to skip the current instruction.
For the complete specification of Sleigh, see the Semantic Section in the [Sleigh documentation](../../../Ghidra/Features/Decompiler/src/main/doc/sleigh.xml).
The emulator adds a few userops:
* `emu_skip_decoded()`: Skip the current instruction.
* `emu_exec_decoded()`: Execute the current instruction.
* `emu_swi()`: Interrupt, as in a breakpoint.
Some control flow is required in the Sleigh injection, otherwise, the emulator may never advance past the current instruction.
An explicit call to `emu_exec_decoded()` allows you to insert logic before and/or after the original instruction; however, if the original instruction branches, then the logic you placed *after* will not be reached.
An explicit call to `emu_skip_decoded()` allows you to omit the original instruction altogether.
It immediately falls through to the next instruction.
The `emu_swi()` userop allows you to maintain breakpoint behavior, perhaps to debug your injection.
After you have written your Sleigh code:
1. Click OK on the Set Injection dialog.
1. In the menus, select **Debugger &rarr; Configure Emulator &rarr; Invalidate Emulator Cache**.
1. Click ![resume button](images/resume.png) Resume.
Stubbing any remaining external calls is left as an exercise.
You are successful when the emulator crashes with `pc = 00000000`.
Clear or disable your breakpoint and invalidate the emulator cache again before proceeding to the next technique.
#### Target Override Technique
The target override technique is most thorough, but also the most involved.
It will handle all calls to the external function, e.g., `strnlen`, no matter the call site.
If the call goes through a program linkage table (PLT), then you are in luck, because the call target will be visible in the Dynamic listing.
The PLT entry usually contains a single `JMP` instruction to the actual `strnlen`.
For real target processes, the `JMP` instruction will transfer control to a lazy linker the first time `strnlen` is called from `termmines`.
The linker then finds `strnlen` and patches the table.
In contrast, the Ghidra loader immediately patches the table to point to a fake `<EXTERNAL>::strnlen` symbol.
The `EXTERNAL` block is not visible in the Dynamic listing, so we will override the `JMP` in the PLT.
The Sleigh code is nearly identical, but we must code an x86 `RET` into it.
Because we allow the `CALL` to execute normally, we must restore the stack.
Furthermore, we must return control back to the caller, just like a real x86 subroutine would.
We also no longer need `emu_skip_decoded()`, because the `RET` will provide the necessary control transfer.
```sleigh {.numberLines}
RAX = 0;
<loop>
if (*:1 (RDI+RAX) == 0 || RAX >= RSI) goto <exit>;
RAX = RAX + 1;
goto <loop>;
<exit>
RIP = *:8 RSP;
RSP = RSP + 8;
return [RIP];
```
Notice that we cannot just write `RET`, but instead must write the Sleigh code to mimic a `RET`.
As with the `CALL` override technique, you must now invalidate the emulator cache and resume.
Stubbing any remaining external functions is left as an exercise.
You are successful when the emulator crashes with `pc = 00000000`.
### Wrapping Up
As you can see, depending on the scope of emulation, and the particulars of the target function, emulating a program image can be quite involved.
Whatever technique you choose, once you have successfully returned from the command-line argument parser, you should check for the expected effects.
In the Static listing, navigate to the variable that stores the board's dimensions.
(Finding that variable is a task in the Beginner portion, but it can be found pretty easily with some manual static analysis.)
In the Dynamic listing, you should notice that the values have changed to reflect the Advanced skill level.
### Optional Exercise: Patch the Placement Algorithm
In this exercise, you will use emulation to devise an assembly patch to `termmines` to change the mine placement algorithm.
Instead of random placement, please have them placed left to right, top to bottom.
We recommend you devise your patch using the Assembler (Patch Instruction action) in the Static listing, then test and debug your patch using the Emulator.
Perhaps patch the Dynamic listing to try quick tweaks before committing them to the Static listing.
Once you have it, export the patched binary and run it in a proper terminal.
## Debugging P-code Semantics
The last use case for emulation we will cover in this course is debugging p-code semantics.
This use case is a bit niche, so we will not cover it too deeply.
It is useful for debugging processor modules.
It is also useful in system modeling, since a lot of that is accomplished using Sleigh p-code.
Perhaps the most useful case related to this module is to debug Sleigh injections.
Ghidra has a dedicated panel for stepping the emulator one p-code operation at a time.
This panel is not included in the default Debugger tool, so it must be configured:
1. If you have not already, open the Debugger tool.
1. In the menus, select **File &rarr; Configure**.
1. Click the "Configure All Plugins" button in the top right of the dialog.
1. Activate the `DebuggerPcodeStepperPlugin`
1. Click OK
1. Click Close
The stepper should appear stacked over the Threads panel in the bottom right.
Yours will probably still be empty, but here is what it looks like populated:
![P-code stepper](images/Emulation_PcodeStepper.png)
To populate it, you will need a session, either emulated or connected to a back-end debugger.
Use the buttons in the local toolbar to step p-code operations.
The first p-code op of any instruction is to decode the instruction.
Once decoded, the p-code listing (left panel) will populate with the ops of the decoded instruction.
If the current instruction is overridden by a Sleigh breakpoint, the listing will populate with the injected ops instead.
You can then step forward and backward within those.
As you step, the other windows that display machine state will update.
In addition to registers and memory, p-code has "unique" variables.
These are temporary variables used only within an instruction's implementation.
They are displayed in the right panel.
The table of variables works similarly to the Registers pane.
The columns are:
* The **Unique** column gives the variable's name and size in bytes.
* The **Bytes** column gives the variable's value in bytes.
* The **Value** column gives the variable's value as an integer, an interpretation of the bytes in the machine's byte order.
* The **Type** column allows you to assign a type. This is ephemeral.
* The **Repr** column gives the variable's value according to the assigned type.
As you step, you may notice the schedule changes.
It is displayed in the stepper's subtitle as well as the Threads panel's subtitle.
P-code stepping is denoted by the portion of the schedule following the dot.
**NOTE**: You cannot mix instruction steps with p-code op steps.
The instruction steps always precede the p-code ops.
If you click Step Into from the global toolbar in the middle of an instruction, the trailing p-code op steps will be removed and replaced with a single instruction step.
In most cases, this intuitively "finishes" the partial instruction.

View file

@ -0,0 +1,547 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Ghidra Debugger</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { color: #008000; } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { color: #008000; font-weight: bold; } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="style.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="nav"><a
class="beginner" href="A1-GettingStarted.html">Getting Started</a><a
class="beginner" href="A2-UITour.html">UI Tour</a><a
class="beginner" href="A3-Breakpoints.html">Breakpoints</a><a
class="beginner" href="A4-MachineState.html">Machine State</a><a
class="beginner" href="A5-Navigation.html">Navigation</a><a
class="beginner" href="A6-MemoryMap.html">Memory Map</a><a
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a>
</header>
<header id="title-block-header">
<h1 class="title">Ghidra Debugger</h1>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#debugger-scripting" id="toc-debugger-scripting">Debugger
Scripting</a>
<ul>
<li><a href="#the-debugger-scripting-api"
id="toc-the-debugger-scripting-api">The Debugger Scripting API</a></li>
<li><a href="#dumping-the-game-board"
id="toc-dumping-the-game-board">Dumping the Game Board</a>
<ul>
<li><a href="#checking-the-target" id="toc-checking-the-target">Checking
the Target</a></li>
<li><a href="#checking-the-module-map"
id="toc-checking-the-module-map">Checking the Module Map</a></li>
<li><a href="#reading-the-data" id="toc-reading-the-data">Reading the
Data</a></li>
<li><a href="#dumping-the-board" id="toc-dumping-the-board">Dumping the
Board</a></li>
<li><a href="#test-the-script" id="toc-test-the-script">Test the
Script</a></li>
<li><a href="#exercise-remove-the-mines"
id="toc-exercise-remove-the-mines">Exercise: Remove the Mines</a></li>
</ul></li>
<li><a href="#waiting-on-reacting-to-events"
id="toc-waiting-on-reacting-to-events">Waiting on / Reacting to
Events</a>
<ul>
<li><a href="#exercise-always-win-in-0-seconds"
id="toc-exercise-always-win-in-0-seconds">Exercise: Always Win in 0
Seconds</a></li>
<li><a href="#solution-always-win-in-0-seconds"
id="toc-solution-always-win-in-0-seconds">Solution: Always Win in 0
Seconds</a></li>
</ul></li>
<li><a href="#learning-more" id="toc-learning-more">Learning
More</a></li>
</ul></li>
</ul>
</nav>
<section id="debugger-scripting" class="level1">
<h1>Debugger Scripting</h1>
<p>This module assumes you have completed the Beginner portion of this
course, as well as the Scripting module of the Intermediate course.</p>
<p>As with Ghidra Scripting, the primary use case we consider in this
module is automation. It also permits some one-off analysis of a live
target or interacting with the dynamic target. There are also some
extension points useful for <a href="B4-Modeling.html">Modeling</a> that
are easily accessed in scripts for prototyping.</p>
<p>The script development environment is set up exactly the same as it
is for the rest of Ghidra.</p>
<section id="the-debugger-scripting-api" class="level2">
<h2>The Debugger Scripting API</h2>
<p>To create a Debugger script, do as you normally would then append
<code>implements FlatDebuggerAPI</code> to the scripts class
declaration, e.g.:</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">import</span> <span class="im">ghidra</span><span class="op">.</span><span class="im">app</span><span class="op">.</span><span class="im">script</span><span class="op">.</span><span class="im">GhidraScript</span><span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2"></a><span class="kw">import</span> <span class="im">ghidra</span><span class="op">.</span><span class="im">debug</span><span class="op">.</span><span class="im">flatapi</span><span class="op">.</span><span class="im">FlatDebuggerAPI</span><span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3"></a></span>
<span id="cb1-4"><a href="#cb1-4"></a><span class="kw">public</span> <span class="kw">class</span> DemoDebuggerScript <span class="kw">extends</span> GhidraScript <span class="kw">implements</span> FlatDebuggerAPI <span class="op">{</span></span>
<span id="cb1-5"><a href="#cb1-5"></a> <span class="at">@Override</span></span>
<span id="cb1-6"><a href="#cb1-6"></a> <span class="kw">protected</span> <span class="dt">void</span> <span class="fu">run</span><span class="op">()</span> <span class="kw">throws</span> <span class="bu">Exception</span> <span class="op">{</span></span>
<span id="cb1-7"><a href="#cb1-7"></a> <span class="op">}</span></span>
<span id="cb1-8"><a href="#cb1-8"></a><span class="op">}</span></span></code></pre></div>
<p>Technically, the Debuggers “deep” API is accessible to scripts;
however, the flat API is preferred for scripting. Also, the flat API is
usually more stable than the deep API. However, because the dynamic
analysis flat API is newer, it may not be as stable as the static
analysis flat API. It is also worth noting that the
<code>FlatDebuggerAPI</code> interface <em>adds</em> the flat API to
your script. The static analysis flat API is still available, and it
will manipulate the static portions of the Debugger tool, just as they
would in the CodeBrowser tool. In this tutorial, we will explore reading
machine state, setting breakpoints, waiting for conditions, and
controlling the target.</p>
</section>
<section id="dumping-the-game-board" class="level2">
<h2>Dumping the Game Board</h2>
<p>We will write a script that assumes the current session is for
<code>termmines</code> and dumps the game board to the console, allowing
you to cheat. You can label your variables however you would like but,
for this tutorial, we will assume you have labeled them
<code>width</code>, <code>height</code>, and <code>cells</code>. If you
have not already located and labeled these variables, do so now.</p>
<section id="checking-the-target" class="level3">
<h3>Checking the Target</h3>
<p>First, we will do some validation. Check that we have an active
session (trace):</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb2-1"><a href="#cb2-1"></a>Trace trace <span class="op">=</span> <span class="fu">getCurrentTrace</span><span class="op">();</span></span>
<span id="cb2-2"><a href="#cb2-2"></a><span class="cf">if</span> <span class="op">(</span>trace <span class="op">==</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-3"><a href="#cb2-3"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;There is no active session&quot;</span><span class="op">);</span></span>
<span id="cb2-4"><a href="#cb2-4"></a><span class="op">}</span></span></code></pre></div>
<p>Now, check that the current program is <code>termmines</code>:</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb3-1"><a href="#cb3-1"></a><span class="cf">if</span> <span class="op">(!</span><span class="st">&quot;termmines&quot;</span><span class="op">.</span><span class="fu">equals</span><span class="op">(</span>currentProgram<span class="op">.</span><span class="fu">getName</span><span class="op">()))</span> <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;The current program must be termmines&quot;</span><span class="op">);</span></span>
<span id="cb3-3"><a href="#cb3-3"></a><span class="op">}</span></span></code></pre></div>
</section>
<section id="checking-the-module-map" class="level3">
<h3>Checking the Module Map</h3>
<p>Now, check that <code>termmines</code> is actually part of the
current trace. There is not a great way to do this directly in the flat
API, but we are going to need to map some symbols from the
<code>termmines</code> module, anyway. In this step, we will both verify
that the user has placed the required labels, as well as verify that
those symbols can be mapped to the target:</p>
<div class="sourceCode" id="cb4"><pre
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb4-1"><a href="#cb4-1"></a><span class="bu">List</span><span class="op">&lt;</span>Symbol<span class="op">&gt;</span> widthSyms <span class="op">=</span> <span class="fu">getSymbols</span><span class="op">(</span><span class="st">&quot;width&quot;</span><span class="op">,</span> <span class="kw">null</span><span class="op">);</span></span>
<span id="cb4-2"><a href="#cb4-2"></a><span class="cf">if</span> <span class="op">(</span>widthSyms<span class="op">.</span><span class="fu">isEmpty</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb4-3"><a href="#cb4-3"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Symbol &#39;width&#39; is required&quot;</span><span class="op">);</span></span>
<span id="cb4-4"><a href="#cb4-4"></a><span class="op">}</span></span>
<span id="cb4-5"><a href="#cb4-5"></a><span class="bu">List</span><span class="op">&lt;</span>Symbol<span class="op">&gt;</span> heightSyms <span class="op">=</span> <span class="fu">getSymbols</span><span class="op">(</span><span class="st">&quot;height&quot;</span><span class="op">,</span> <span class="kw">null</span><span class="op">);</span></span>
<span id="cb4-6"><a href="#cb4-6"></a><span class="cf">if</span> <span class="op">(</span>heightSyms<span class="op">.</span><span class="fu">isEmpty</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb4-7"><a href="#cb4-7"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Symbol &#39;height&#39; is required&quot;</span><span class="op">);</span></span>
<span id="cb4-8"><a href="#cb4-8"></a><span class="op">}</span></span>
<span id="cb4-9"><a href="#cb4-9"></a><span class="bu">List</span><span class="op">&lt;</span>Symbol<span class="op">&gt;</span> cellsSyms <span class="op">=</span> <span class="fu">getSymbols</span><span class="op">(</span><span class="st">&quot;cells&quot;</span><span class="op">,</span> <span class="kw">null</span><span class="op">);</span></span>
<span id="cb4-10"><a href="#cb4-10"></a><span class="cf">if</span> <span class="op">(</span>cellsSyms<span class="op">.</span><span class="fu">isEmpty</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb4-11"><a href="#cb4-11"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Symbol &#39;cells&#39; is required&quot;</span><span class="op">);</span></span>
<span id="cb4-12"><a href="#cb4-12"></a><span class="op">}</span></span>
<span id="cb4-13"><a href="#cb4-13"></a></span>
<span id="cb4-14"><a href="#cb4-14"></a>Address widthDyn <span class="op">=</span> <span class="fu">translateStaticToDynamic</span><span class="op">(</span>widthSyms<span class="op">.</span><span class="fu">get</span><span class="op">(</span><span class="dv">0</span><span class="op">).</span><span class="fu">getAddress</span><span class="op">());</span></span>
<span id="cb4-15"><a href="#cb4-15"></a><span class="cf">if</span> <span class="op">(</span>widthDyn <span class="op">==</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-16"><a href="#cb4-16"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Symbol &#39;width&#39; is not mapped to target&quot;</span><span class="op">);</span></span>
<span id="cb4-17"><a href="#cb4-17"></a><span class="op">}</span></span>
<span id="cb4-18"><a href="#cb4-18"></a>Address heightDyn <span class="op">=</span> <span class="fu">translateStaticToDynamic</span><span class="op">(</span>heightSyms<span class="op">.</span><span class="fu">get</span><span class="op">(</span><span class="dv">0</span><span class="op">).</span><span class="fu">getAddress</span><span class="op">());</span></span>
<span id="cb4-19"><a href="#cb4-19"></a><span class="cf">if</span> <span class="op">(</span>heightDyn <span class="op">==</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-20"><a href="#cb4-20"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Symbol &#39;height&#39; is not mapped to target&quot;</span><span class="op">);</span></span>
<span id="cb4-21"><a href="#cb4-21"></a><span class="op">}</span></span>
<span id="cb4-22"><a href="#cb4-22"></a>Address cellsDyn <span class="op">=</span> <span class="fu">translateStaticToDynamic</span><span class="op">(</span>cellsSyms<span class="op">.</span><span class="fu">get</span><span class="op">(</span><span class="dv">0</span><span class="op">).</span><span class="fu">getAddress</span><span class="op">());</span></span>
<span id="cb4-23"><a href="#cb4-23"></a><span class="cf">if</span> <span class="op">(</span>cellsDyn <span class="op">==</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-24"><a href="#cb4-24"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Symbol &#39;cells&#39; is not mapped to target&quot;</span><span class="op">);</span></span>
<span id="cb4-25"><a href="#cb4-25"></a><span class="op">}</span></span></code></pre></div>
<p>The <code>getSymbols()</code> method is part of the static flat API,
so it returns symbols from the current static listing. The
<code>translateStaticToDynamic()</code> is part of the dynamic flat API.
This allows us to locate that symbol in the dynamic context.</p>
</section>
<section id="reading-the-data" class="level3">
<h3>Reading the Data</h3>
<p>Now, we want to read the dimensions and the whole board to the trace.
You should know from earlier exercises that the board is allocated 32
cells by 32 cells, so we will want to read at least 1024 bytes. Note
that this will implicitly capture the board to the trace:</p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb5-1"><a href="#cb5-1"></a><span class="dt">byte</span><span class="op">[]</span> widthDat <span class="op">=</span> <span class="fu">readMemory</span><span class="op">(</span>widthDyn<span class="op">,</span> <span class="dv">4</span><span class="op">,</span> monitor<span class="op">);</span></span>
<span id="cb5-2"><a href="#cb5-2"></a><span class="dt">byte</span><span class="op">[]</span> heightDat <span class="op">=</span> <span class="fu">readMemory</span><span class="op">(</span>heightDyn<span class="op">,</span> <span class="dv">4</span><span class="op">,</span> monitor<span class="op">);</span></span>
<span id="cb5-3"><a href="#cb5-3"></a><span class="dt">byte</span><span class="op">[]</span> cellsData <span class="op">=</span> <span class="fu">readMemory</span><span class="op">(</span>cellsDyn<span class="op">,</span> <span class="dv">1024</span><span class="op">,</span> monitor<span class="op">);</span></span></code></pre></div>
</section>
<section id="dumping-the-board" class="level3">
<h3>Dumping the Board</h3>
<p>Beyond this, everything is pretty standard Java / Ghidra scripting.
We will need to do some quick conversion of the bytes to integers, and
then we can iterate over the cells and print the mines locations:</p>
<div class="sourceCode" id="cb6"><pre
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb6-1"><a href="#cb6-1"></a><span class="dt">int</span> width <span class="op">=</span> <span class="bu">ByteBuffer</span><span class="op">.</span><span class="fu">wrap</span><span class="op">(</span>widthDat<span class="op">).</span><span class="fu">order</span><span class="op">(</span><span class="bu">ByteOrder</span><span class="op">.</span><span class="fu">LITTLE_ENDIAN</span><span class="op">).</span><span class="fu">getInt</span><span class="op">();</span></span>
<span id="cb6-2"><a href="#cb6-2"></a><span class="dt">int</span> height <span class="op">=</span> <span class="bu">ByteBuffer</span><span class="op">.</span><span class="fu">wrap</span><span class="op">(</span>heightDat<span class="op">).</span><span class="fu">order</span><span class="op">(</span><span class="bu">ByteOrder</span><span class="op">.</span><span class="fu">LITTLE_ENDIAN</span><span class="op">).</span><span class="fu">getInt</span><span class="op">();</span></span>
<span id="cb6-3"><a href="#cb6-3"></a><span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> y <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> y <span class="op">&lt;</span> height<span class="op">;</span> y<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb6-4"><a href="#cb6-4"></a> <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> x <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> x <span class="op">&lt;</span> width<span class="op">;</span> x<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb6-5"><a href="#cb6-5"></a> <span class="cf">if</span> <span class="op">((</span>cellsData<span class="op">[(</span>y <span class="op">+</span> <span class="dv">1</span><span class="op">)</span> <span class="op">*</span> <span class="dv">32</span> <span class="op">+</span> x <span class="op">+</span> <span class="dv">1</span><span class="op">]</span> <span class="op">&amp;</span> <span class="bn">0x80</span><span class="op">)</span> <span class="op">==</span> <span class="bn">0x80</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-6"><a href="#cb6-6"></a> <span class="fu">println</span><span class="op">(</span><span class="st">&quot;Mine at (%d,%d)&quot;</span><span class="op">.</span><span class="fu">formatted</span><span class="op">(</span>x<span class="op">,</span> y<span class="op">));</span></span>
<span id="cb6-7"><a href="#cb6-7"></a> <span class="op">}</span></span>
<span id="cb6-8"><a href="#cb6-8"></a> <span class="op">}</span></span>
<span id="cb6-9"><a href="#cb6-9"></a><span class="op">}</span></span></code></pre></div>
</section>
<section id="test-the-script" class="level3">
<h3>Test the Script</h3>
<p>To test, run <code>termmines</code> in a proper terminal and attach
to it from Ghidra using GDB. Now, run the script. Resume and play the
game. Once you win, check that the script output describes the actual
board.</p>
</section>
<section id="exercise-remove-the-mines" class="level3">
<h3>Exercise: Remove the Mines</h3>
<p>Write a script that will remove the mines from the board.
<strong>NOTE</strong>: The <code>writeMemory()</code> and related
methods are all subject to the current control mode. If the mode is
read-only, the script cannot modify the targets machine state using
those methods.</p>
</section>
</section>
<section id="waiting-on-reacting-to-events" class="level2">
<h2>Waiting on / Reacting to Events</h2>
<p>Most of the Debugger is implemented using asynchronous event-driven
programming. This will become apparent if you browse any deeper beyond
the flat API. Check the return value carefully. A method that might
intuitively return <code>void</code> may actually return
<code>CompletableFuture&lt;Void&gt;</code>. Javas completable futures
allow you to register callbacks and/or chain additional futures onto
them.</p>
<p>However, Ghidras scripting system provides a dedicated thread for
each execution of a script, so it is acceptable to use the
<code>.get()</code> methods instead, essentially converting to a
synchronous style. Most of the methods in the flat API will do this for
you. See also the flat APIs <code>waitOn()</code> method. The most
common two methods to use when waiting for a condition is
<code>waitForBreak()</code> and <code>flushAsyncPipelines()</code>. The
first simply waits for the target to enter the STOPPED state. Once that
happens, the framework and UI will get to work interrogating the
back-end debugger to update the various displays. Unfortunately, if a
script does not wait for this update to complete, it may be subject to
race conditions. Thus, the second method politely waits for everything
else to finish. Sadly, it may slow your script down.</p>
<p>The general template for waiting on a condition is a bit klunky, but
conceptually straightforward:</p>
<ol type="1">
<li>Set up your instrumentation, e.g., breakpoints.</li>
<li>Get the target running, and then wait for it to break.</li>
<li>Flush the pipelines.</li>
<li>Check if the expected conditions are met, esp., that the program
counter is where you expect.</li>
<li>If the conditions are not met, then let the target run again and
repeat.</li>
<li>Once the conditions are met, perform the desired actions.</li>
<li>Optionally remove your instrumentation and/or let the target
run.</li>
</ol>
<section id="exercise-always-win-in-0-seconds" class="level3">
<h3>Exercise: Always Win in 0 Seconds</h3>
<p><strong>NOTE</strong>: The solution to this exercise is given as a
tutorial below, but give it an honest try before peeking. If you are not
already familiar with Eclipses searching and discovery features, try
pressing <strong>Ctrl-O</strong> twice in the editor for your script.
You should now be able to type patterns, optionally with wildcards, to
help you find applicable methods.</p>
<p>Your task is to write a script that will wait for the player to win
then patch the machine state, so that the game always prints a score of
0 seconds. Some gotchas to consider up front:</p>
<ul>
<li>You may want to verify and/or correct the targets execution state.
See <code>getExecutionState()</code> and <code>interrupt()</code>. You
will not likely be able to place or toggle breakpoints while the target
is running.</li>
<li>Methods like <code>writeMemory()</code> are subject to the current
control mode. You may want to check and/or correct this at the top of
your script.</li>
<li>If you require the user to mark code locations with a label, note
that those labels will likely end up in the containing functions
namespace. You will need to provide that namespace to
<code>getSymbols()</code>.</li>
<li>If you need to set breakpoints, you should try to toggle an existing
breakpoint at that location before adding a new one. Otherwise, you may
generate a pile of breakpoints and/or needlessly increment GDBs
breakpoint numbers.</li>
</ul>
<p>You are successful when you can attach to a running
<code>termmines</code> and execute your script. Then, assuming you win
the game, the game should award you a score of 0 seconds. It is OK if
you have to re-execute your script after each win.</p>
</section>
<section id="solution-always-win-in-0-seconds" class="level3">
<h3>Solution: Always Win in 0 Seconds</h3>
<p>As in the previous scripting tutorial, we will do some verifications
at the top of the script. Your level of pedantry may vary.</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb7-1"><a href="#cb7-1"></a>Trace trace <span class="op">=</span> <span class="fu">getCurrentTrace</span><span class="op">();</span></span>
<span id="cb7-2"><a href="#cb7-2"></a><span class="cf">if</span> <span class="op">(</span>trace <span class="op">==</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-3"><a href="#cb7-3"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;There is no active session&quot;</span><span class="op">);</span></span>
<span id="cb7-4"><a href="#cb7-4"></a><span class="op">}</span></span>
<span id="cb7-5"><a href="#cb7-5"></a></span>
<span id="cb7-6"><a href="#cb7-6"></a><span class="cf">if</span> <span class="op">(!</span><span class="st">&quot;termmines&quot;</span><span class="op">.</span><span class="fu">equals</span><span class="op">(</span>currentProgram<span class="op">.</span><span class="fu">getName</span><span class="op">()))</span> <span class="op">{</span></span>
<span id="cb7-7"><a href="#cb7-7"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;The current program must be termmines&quot;</span><span class="op">);</span></span>
<span id="cb7-8"><a href="#cb7-8"></a><span class="op">}</span></span>
<span id="cb7-9"><a href="#cb7-9"></a></span>
<span id="cb7-10"><a href="#cb7-10"></a><span class="cf">if</span> <span class="op">(</span><span class="fu">getExecutionState</span><span class="op">(</span>trace<span class="op">).</span><span class="fu">isRunning</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb7-11"><a href="#cb7-11"></a> monitor<span class="op">.</span><span class="fu">setMessage</span><span class="op">(</span><span class="st">&quot;Interrupting target and waiting for STOPPED&quot;</span><span class="op">);</span></span>
<span id="cb7-12"><a href="#cb7-12"></a> <span class="fu">interrupt</span><span class="op">();</span></span>
<span id="cb7-13"><a href="#cb7-13"></a> <span class="fu">waitForBreak</span><span class="op">(</span><span class="dv">3</span><span class="op">,</span> <span class="bu">TimeUnit</span><span class="op">.</span><span class="fu">SECONDS</span><span class="op">);</span></span>
<span id="cb7-14"><a href="#cb7-14"></a><span class="op">}</span></span>
<span id="cb7-15"><a href="#cb7-15"></a><span class="fu">flushAsyncPipelines</span><span class="op">(</span>trace<span class="op">);</span></span>
<span id="cb7-16"><a href="#cb7-16"></a></span>
<span id="cb7-17"><a href="#cb7-17"></a><span class="cf">if</span> <span class="op">(!</span><span class="fu">getControlService</span><span class="op">().</span><span class="fu">getCurrentMode</span><span class="op">(</span>trace<span class="op">).</span><span class="fu">canEdit</span><span class="op">(</span><span class="fu">getCurrentDebuggerCoordinates</span><span class="op">()))</span> <span class="op">{</span></span>
<span id="cb7-18"><a href="#cb7-18"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Current control mode is read-only&quot;</span><span class="op">);</span></span>
<span id="cb7-19"><a href="#cb7-19"></a><span class="op">}</span></span></code></pre></div>
<p>The first two blocks check that there is an active target with
<code>termmines</code> as the current program. As before, the
association of the current program to the current target will be
implicitly verified when we map symbols. The second block will interrupt
the target if it is running. We then allow everything to sync up before
checking the control mode. We could instead change the control mode to
<strong>Target w/Edits</strong>, but I prefer to keep the user aware
that the script needs to modify target machine state.</p>
<p>Next, we retrieve and map our symbols. This works pretty much the
same as in the previous scripting tutorial, but with attention to the
containing function namespace. The way <code>termmines</code> computes
the score is to record the start time of the game. Then, when the player
wins, it subtracts the recorded time from the current time. This script
requires the user to label the start time variable <code>timer</code>,
and to label the instruction that computes the score
<code>reset_timer</code>. The function that prints the score must be
named <code>print_win</code>.</p>
<div class="sourceCode" id="cb8"><pre
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb8-1"><a href="#cb8-1"></a><span class="bu">List</span><span class="op">&lt;</span>Symbol<span class="op">&gt;</span> timerSyms <span class="op">=</span> <span class="fu">getSymbols</span><span class="op">(</span><span class="st">&quot;timer&quot;</span><span class="op">,</span> <span class="kw">null</span><span class="op">);</span></span>
<span id="cb8-2"><a href="#cb8-2"></a><span class="cf">if</span> <span class="op">(</span>timerSyms<span class="op">.</span><span class="fu">isEmpty</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb8-3"><a href="#cb8-3"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Symbol &#39;timer&#39; is required&quot;</span><span class="op">);</span></span>
<span id="cb8-4"><a href="#cb8-4"></a><span class="op">}</span></span>
<span id="cb8-5"><a href="#cb8-5"></a><span class="bu">List</span><span class="op">&lt;</span>Function<span class="op">&gt;</span> winFuncs <span class="op">=</span> <span class="fu">getGlobalFunctions</span><span class="op">(</span><span class="st">&quot;print_win&quot;</span><span class="op">);</span></span>
<span id="cb8-6"><a href="#cb8-6"></a><span class="cf">if</span> <span class="op">(</span>winFuncs<span class="op">.</span><span class="fu">isEmpty</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb8-7"><a href="#cb8-7"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Function &#39;print_win&#39; is required&quot;</span><span class="op">);</span></span>
<span id="cb8-8"><a href="#cb8-8"></a><span class="op">}</span></span>
<span id="cb8-9"><a href="#cb8-9"></a><span class="bu">List</span><span class="op">&lt;</span>Symbol<span class="op">&gt;</span> resetSyms <span class="op">=</span> <span class="fu">getSymbols</span><span class="op">(</span><span class="st">&quot;reset_timer&quot;</span><span class="op">,</span> winFuncs<span class="op">.</span><span class="fu">get</span><span class="op">(</span><span class="dv">0</span><span class="op">));</span></span>
<span id="cb8-10"><a href="#cb8-10"></a><span class="cf">if</span> <span class="op">(</span>resetSyms<span class="op">.</span><span class="fu">isEmpty</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb8-11"><a href="#cb8-11"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Symbol &#39;reset_timer&#39; is required&quot;</span><span class="op">);</span></span>
<span id="cb8-12"><a href="#cb8-12"></a><span class="op">}</span></span>
<span id="cb8-13"><a href="#cb8-13"></a></span>
<span id="cb8-14"><a href="#cb8-14"></a>Address timerDyn <span class="op">=</span> <span class="fu">translateStaticToDynamic</span><span class="op">(</span>timerSyms<span class="op">.</span><span class="fu">get</span><span class="op">(</span><span class="dv">0</span><span class="op">).</span><span class="fu">getAddress</span><span class="op">());</span></span>
<span id="cb8-15"><a href="#cb8-15"></a><span class="cf">if</span> <span class="op">(</span>timerDyn <span class="op">==</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-16"><a href="#cb8-16"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Symbol &#39;timer&#39; is not mapped to target&quot;</span><span class="op">);</span></span>
<span id="cb8-17"><a href="#cb8-17"></a><span class="op">}</span></span>
<span id="cb8-18"><a href="#cb8-18"></a>Address resetDyn <span class="op">=</span> <span class="fu">translateStaticToDynamic</span><span class="op">(</span>resetSyms<span class="op">.</span><span class="fu">get</span><span class="op">(</span><span class="dv">0</span><span class="op">).</span><span class="fu">getAddress</span><span class="op">());</span></span>
<span id="cb8-19"><a href="#cb8-19"></a><span class="cf">if</span> <span class="op">(</span>resetDyn <span class="op">==</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-20"><a href="#cb8-20"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Symbol &#39;reset_timer&#39; is not mapped to target&quot;</span><span class="op">);</span></span>
<span id="cb8-21"><a href="#cb8-21"></a><span class="op">}</span></span></code></pre></div>
<section id="toggling-and-setting-breakpoints" class="level4">
<h4>Toggling and Setting Breakpoints</h4>
<p>The first actual operation we perform on the debug session is to
toggle or place a breakpoint on the <code>reset_timer</code> label. The
API prefers to specify breakpoints in the static context, but you can do
either. To establish that context, you must use a
<code>ProgramLocation</code>. For static context, use the current
(static) program as the program. For dynamic context, use the current
(dynamic) trace view as the program — see
<code>getCurrentView()</code>.</p>
<p>To avoid creating a pile of breakpoints, we will first attempt to
enable an existing breakpoint at the desired location. Technically, the
existing breakpoints may not be execute breakpoints, but we will blindly
assume they are. Again, your level of pedantry may vary. The
<code>breakpointsEnable</code> method will return the existing
breakpoints, so we can check that and create a new breakpoint, if
necessary:</p>
<div class="sourceCode" id="cb9"><pre
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb9-1"><a href="#cb9-1"></a>ProgramLocation breakLoc <span class="op">=</span></span>
<span id="cb9-2"><a href="#cb9-2"></a> <span class="kw">new</span> <span class="fu">ProgramLocation</span><span class="op">(</span>currentProgram<span class="op">,</span> resetSyms<span class="op">.</span><span class="fu">get</span><span class="op">(</span><span class="dv">0</span><span class="op">).</span><span class="fu">getAddress</span><span class="op">());</span></span>
<span id="cb9-3"><a href="#cb9-3"></a><span class="bu">Set</span><span class="op">&lt;</span>LogicalBreakpoint<span class="op">&gt;</span> breaks <span class="op">=</span> <span class="fu">breakpointsEnable</span><span class="op">(</span>breakLoc<span class="op">);</span></span>
<span id="cb9-4"><a href="#cb9-4"></a><span class="cf">if</span> <span class="op">(</span>breaks <span class="op">==</span> <span class="kw">null</span> <span class="op">||</span> breaks<span class="op">.</span><span class="fu">isEmpty</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb9-5"><a href="#cb9-5"></a> <span class="fu">breakpointSetSoftwareExecute</span><span class="op">(</span>breakLoc<span class="op">,</span> <span class="st">&quot;reset timer&quot;</span><span class="op">);</span></span>
<span id="cb9-6"><a href="#cb9-6"></a><span class="op">}</span></span></code></pre></div>
</section>
<section id="waiting-to-hit-the-breakpoint" class="level4">
<h4>Waiting to Hit the Breakpoint</h4>
<p>This next loop is quite extensive, but it follows the template given
earlier for waiting on conditions. It is an indefinite loop, so we
should check the monitor for cancellation somewhat frequently. This
implies we should use relatively short timeouts in our API calls. In our
case, we just want to confirm that the cause of breaking was hitting our
breakpoint. We do not need to be precise in this check; it suffices to
check the program counter:</p>
<div class="sourceCode" id="cb10"><pre
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb10-1"><a href="#cb10-1"></a><span class="cf">while</span> <span class="op">(</span><span class="kw">true</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-2"><a href="#cb10-2"></a> monitor<span class="op">.</span><span class="fu">checkCanceled</span><span class="op">();</span></span>
<span id="cb10-3"><a href="#cb10-3"></a></span>
<span id="cb10-4"><a href="#cb10-4"></a> TargetExecutionState execState <span class="op">=</span> <span class="fu">getExecutionState</span><span class="op">(</span>trace<span class="op">);</span></span>
<span id="cb10-5"><a href="#cb10-5"></a> <span class="cf">switch</span> <span class="op">(</span>execState<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-6"><a href="#cb10-6"></a> <span class="cf">case</span> STOPPED<span class="op">:</span></span>
<span id="cb10-7"><a href="#cb10-7"></a> <span class="fu">resume</span><span class="op">();</span></span>
<span id="cb10-8"><a href="#cb10-8"></a> <span class="cf">break</span><span class="op">;</span></span>
<span id="cb10-9"><a href="#cb10-9"></a> <span class="cf">case</span> TERMINATED<span class="op">:</span></span>
<span id="cb10-10"><a href="#cb10-10"></a> <span class="cf">case</span> <span class="bu">INACTIVE</span><span class="op">:</span></span>
<span id="cb10-11"><a href="#cb10-11"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Target terminated&quot;</span><span class="op">);</span></span>
<span id="cb10-12"><a href="#cb10-12"></a> <span class="cf">case</span> ALIVE<span class="op">:</span></span>
<span id="cb10-13"><a href="#cb10-13"></a> <span class="fu">println</span><span class="op">(</span></span>
<span id="cb10-14"><a href="#cb10-14"></a> <span class="st">&quot;I don&#39;t know whether or not the target is running. Please make it RUNNING.&quot;</span><span class="op">);</span></span>
<span id="cb10-15"><a href="#cb10-15"></a> <span class="cf">break</span><span class="op">;</span></span>
<span id="cb10-16"><a href="#cb10-16"></a> <span class="cf">case</span> RUNNING<span class="op">:</span></span>
<span id="cb10-17"><a href="#cb10-17"></a> <span class="co">/**</span></span>
<span id="cb10-18"><a href="#cb10-18"></a> <span class="co">*</span> Probably timed out waiting for break<span class="co">. </span>That<span class="co">&#39;</span>s fine<span class="co">.</span> Give the player time to</span>
<span id="cb10-19"><a href="#cb10-19"></a> <span class="co">*</span> win<span class="co">.</span></span>
<span id="cb10-20"><a href="#cb10-20"></a> <span class="co">*/</span></span>
<span id="cb10-21"><a href="#cb10-21"></a> <span class="cf">break</span><span class="op">;</span></span>
<span id="cb10-22"><a href="#cb10-22"></a> <span class="kw">default</span><span class="op">:</span></span>
<span id="cb10-23"><a href="#cb10-23"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Unrecognized state: &quot;</span> <span class="op">+</span> execState<span class="op">);</span></span>
<span id="cb10-24"><a href="#cb10-24"></a> <span class="op">}</span></span>
<span id="cb10-25"><a href="#cb10-25"></a> <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb10-26"><a href="#cb10-26"></a> monitor<span class="op">.</span><span class="fu">setMessage</span><span class="op">(</span><span class="st">&quot;Waiting for player to win&quot;</span><span class="op">);</span></span>
<span id="cb10-27"><a href="#cb10-27"></a> <span class="fu">waitForBreak</span><span class="op">(</span><span class="dv">1</span><span class="op">,</span> <span class="bu">TimeUnit</span><span class="op">.</span><span class="fu">SECONDS</span><span class="op">);</span></span>
<span id="cb10-28"><a href="#cb10-28"></a> <span class="op">}</span></span>
<span id="cb10-29"><a href="#cb10-29"></a> <span class="cf">catch</span> <span class="op">(</span><span class="bu">TimeoutException</span> e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-30"><a href="#cb10-30"></a> <span class="co">// Give the player time to win.</span></span>
<span id="cb10-31"><a href="#cb10-31"></a> <span class="cf">continue</span><span class="op">;</span></span>
<span id="cb10-32"><a href="#cb10-32"></a> <span class="op">}</span></span>
<span id="cb10-33"><a href="#cb10-33"></a> <span class="fu">flushAsyncPipelines</span><span class="op">(</span>trace<span class="op">);</span></span>
<span id="cb10-34"><a href="#cb10-34"></a> Address pc <span class="op">=</span> <span class="fu">getProgramCounter</span><span class="op">();</span></span>
<span id="cb10-35"><a href="#cb10-35"></a> <span class="fu">println</span><span class="op">(</span><span class="st">&quot;STOPPED at pc = &quot;</span> <span class="op">+</span> pc<span class="op">);</span></span>
<span id="cb10-36"><a href="#cb10-36"></a> <span class="cf">if</span> <span class="op">(</span>resetDyn<span class="op">.</span><span class="fu">equals</span><span class="op">(</span>pc<span class="op">))</span> <span class="op">{</span></span>
<span id="cb10-37"><a href="#cb10-37"></a> <span class="cf">break</span><span class="op">;</span></span>
<span id="cb10-38"><a href="#cb10-38"></a> <span class="op">}</span></span>
<span id="cb10-39"><a href="#cb10-39"></a><span class="op">}</span></span></code></pre></div>
<p>The “center” of this loop is a call to <code>waitForBreak()</code>.
This is the simplest primitive for waiting on the target to meet any
condition. Because we expect the user to take more than a second to win
the game, we should expect a timeout exception and just keep waiting.
Using a timeout of 1 second ensures we can terminate promptly should the
user cancel the script.</p>
<p>Before waiting, we need to make sure the target is running. Because
we could repeat the loop while the target is already running, we should
only call <code>resume()</code> if the target is stopped. There are
utility methods on <code>TargetExecutionState</code> like
<code>isRunning()</code>, which you might prefer to use. Here, we
exhaustively handle every kind of state using a switch statement, which
does make the code a bit verbose.</p>
<p>When the target does break, we first allow the UI to finish
interrogating the target. We can then reliably retrieve and check the
program counter. If the PC matches the dynamic location of
<code>reset_timer</code>, then the player has won, and we need to reset
the start time.</p>
</section>
<section id="patching-the-start-time" class="level4">
<h4>Patching the Start Time</h4>
<p>When the player has won, this particular compilation of
<code>termmines</code> first calls <code>time</code> to get the current
time and moves it into <code>ECX</code>. It then subtracts, using a
memory operand, the recorded start time. There are certainly other
strategies, but this script expects the user to label that
<code>SUB</code> instruction <code>reset_timer</code>. We would like the
result of that computation to be 0, so we will simply copy the value of
<code>ECX</code> over the recorded start time:</p>
<div class="sourceCode" id="cb11"><pre
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb11-1"><a href="#cb11-1"></a><span class="dt">int</span> time <span class="op">=</span> <span class="fu">readRegister</span><span class="op">(</span><span class="st">&quot;ECX&quot;</span><span class="op">).</span><span class="fu">getUnsignedValue</span><span class="op">().</span><span class="fu">intValue</span><span class="op">();</span></span>
<span id="cb11-2"><a href="#cb11-2"></a><span class="cf">if</span> <span class="op">(!</span><span class="fu">writeMemory</span><span class="op">(</span>timerDyn<span class="op">,</span></span>
<span id="cb11-3"><a href="#cb11-3"></a> <span class="bu">ByteBuffer</span><span class="op">.</span><span class="fu">allocate</span><span class="op">(</span><span class="dv">4</span><span class="op">).</span><span class="fu">order</span><span class="op">(</span><span class="bu">ByteOrder</span><span class="op">.</span><span class="fu">LITTLE_ENDIAN</span><span class="op">).</span><span class="fu">putInt</span><span class="op">(</span>time<span class="op">).</span><span class="fu">array</span><span class="op">()))</span> <span class="op">{</span></span>
<span id="cb11-4"><a href="#cb11-4"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">AssertionError</span><span class="op">(</span><span class="st">&quot;Could not write over timer. Does control mode allow edits?&quot;</span><span class="op">);</span></span>
<span id="cb11-5"><a href="#cb11-5"></a><span class="op">}</span></span>
<span id="cb11-6"><a href="#cb11-6"></a></span>
<span id="cb11-7"><a href="#cb11-7"></a><span class="fu">resume</span><span class="op">();</span></span></code></pre></div>
<p>The final <code>resume()</code> simply allows the target to finish
printing the score, which ought to be 0 now!</p>
</section>
</section>
</section>
<section id="learning-more" class="level2">
<h2>Learning More</h2>
<p>For another demonstration of the flat API, see <a
href="../../../Ghidra/Debug/Debugger/ghidra_scripts/DemoDebuggerScript.java">DemoDebuggerScript</a>,
or just ask Eclipse for all the implementations of
<code>FlatDebuggerAPI</code>. If you want a list of methods with
explanations, you should refer to the documentation in the
<code>FlatDebuggerAPI</code> interface.</p>
</section>
</section>
</body>
</html>

View file

@ -0,0 +1,363 @@
# Debugger Scripting
This module assumes you have completed the Beginner portion of this course, as well as the Scripting module of the Intermediate course.
As with Ghidra Scripting, the primary use case we consider in this module is automation.
It also permits some one-off analysis of a live target or interacting with the dynamic target.
There are also some extension points useful for [Modeling](B4-Modeling.md) that are easily accessed in scripts for prototyping.
The script development environment is set up exactly the same as it is for the rest of Ghidra.
## The Debugger Scripting API
To create a Debugger script, do as you normally would then append `implements FlatDebuggerAPI` to the script's class declaration, e.g.:
```java {.numberLines}
import ghidra.app.script.GhidraScript;
import ghidra.debug.flatapi.FlatDebuggerAPI;
public class DemoDebuggerScript extends GhidraScript implements FlatDebuggerAPI {
@Override
protected void run() throws Exception {
}
}
```
Technically, the Debugger's "deep" API is accessible to scripts; however, the flat API is preferred for scripting.
Also, the flat API is usually more stable than the deep API.
However, because the dynamic analysis flat API is newer, it may not be as stable as the static analysis flat API.
It is also worth noting that the `FlatDebuggerAPI` interface *adds* the flat API to your script.
The static analysis flat API is still available, and it will manipulate the static portions of the Debugger tool, just as they would in the CodeBrowser tool.
In this tutorial, we will explore reading machine state, setting breakpoints, waiting for conditions, and controlling the target.
## Dumping the Game Board
We will write a script that assumes the current session is for `termmines` and dumps the game board to the console, allowing you to cheat.
You can label your variables however you would like but, for this tutorial, we will assume you have labeled them `width`, `height`, and `cells`.
If you have not already located and labeled these variables, do so now.
### Checking the Target
First, we will do some validation.
Check that we have an active session (trace):
```java {.numberLines}
Trace trace = getCurrentTrace();
if (trace == null) {
throw new AssertionError("There is no active session");
}
```
Now, check that the current program is `termmines`:
```java {.numberLines}
if (!"termmines".equals(currentProgram.getName())) {
throw new AssertionError("The current program must be termmines");
}
```
### Checking the Module Map
Now, check that `termmines` is actually part of the current trace.
There is not a great way to do this directly in the flat API, but we are going to need to map some symbols from the `termmines` module, anyway.
In this step, we will both verify that the user has placed the required labels, as well as verify that those symbols can be mapped to the target:
```java {.numberLines}
List<Symbol> widthSyms = getSymbols("width", null);
if (widthSyms.isEmpty()) {
throw new AssertionError("Symbol 'width' is required");
}
List<Symbol> heightSyms = getSymbols("height", null);
if (heightSyms.isEmpty()) {
throw new AssertionError("Symbol 'height' is required");
}
List<Symbol> cellsSyms = getSymbols("cells", null);
if (cellsSyms.isEmpty()) {
throw new AssertionError("Symbol 'cells' is required");
}
Address widthDyn = translateStaticToDynamic(widthSyms.get(0).getAddress());
if (widthDyn == null) {
throw new AssertionError("Symbol 'width' is not mapped to target");
}
Address heightDyn = translateStaticToDynamic(heightSyms.get(0).getAddress());
if (heightDyn == null) {
throw new AssertionError("Symbol 'height' is not mapped to target");
}
Address cellsDyn = translateStaticToDynamic(cellsSyms.get(0).getAddress());
if (cellsDyn == null) {
throw new AssertionError("Symbol 'cells' is not mapped to target");
}
```
The `getSymbols()` method is part of the static flat API, so it returns symbols from the current static listing.
The `translateStaticToDynamic()` is part of the dynamic flat API.
This allows us to locate that symbol in the dynamic context.
### Reading the Data
Now, we want to read the dimensions and the whole board to the trace.
You should know from earlier exercises that the board is allocated 32 cells by 32 cells, so we will want to read at least 1024 bytes.
Note that this will implicitly capture the board to the trace:
```java {.numberLines}
byte[] widthDat = readMemory(widthDyn, 4, monitor);
byte[] heightDat = readMemory(heightDyn, 4, monitor);
byte[] cellsData = readMemory(cellsDyn, 1024, monitor);
```
### Dumping the Board
Beyond this, everything is pretty standard Java / Ghidra scripting.
We will need to do some quick conversion of the bytes to integers, and then we can iterate over the cells and print the mines' locations:
```java {.numberLines}
int width = ByteBuffer.wrap(widthDat).order(ByteOrder.LITTLE_ENDIAN).getInt();
int height = ByteBuffer.wrap(heightDat).order(ByteOrder.LITTLE_ENDIAN).getInt();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if ((cellsData[(y + 1) * 32 + x + 1] & 0x80) == 0x80) {
println("Mine at (%d,%d)".formatted(x, y));
}
}
}
```
### Test the Script
To test, run `termmines` in a proper terminal and attach to it from Ghidra using GDB.
Now, run the script.
Resume and play the game.
Once you win, check that the script output describes the actual board.
### Exercise: Remove the Mines
Write a script that will remove the mines from the board.
**NOTE**: The `writeMemory()` and related methods are all subject to the current control mode.
If the mode is read-only, the script cannot modify the target's machine state using those methods.
## Waiting on / Reacting to Events
Most of the Debugger is implemented using asynchronous event-driven programming.
This will become apparent if you browse any deeper beyond the flat API.
Check the return value carefully.
A method that might intuitively return `void` may actually return `CompletableFuture<Void>`.
Java's completable futures allow you to register callbacks and/or chain additional futures onto them.
However, Ghidra's scripting system provides a dedicated thread for each execution of a script, so it is acceptable to use the `.get()` methods instead, essentially converting to a synchronous style.
Most of the methods in the flat API will do this for you.
See also the flat API's `waitOn()` method.
The most common two methods to use when waiting for a condition is `waitForBreak()` and `flushAsyncPipelines()`.
The first simply waits for the target to enter the STOPPED state.
Once that happens, the framework and UI will get to work interrogating the back-end debugger to update the various displays.
Unfortunately, if a script does not wait for this update to complete, it may be subject to race conditions.
Thus, the second method politely waits for everything else to finish.
Sadly, it may slow your script down.
The general template for waiting on a condition is a bit klunky, but conceptually straightforward:
1. Set up your instrumentation, e.g., breakpoints.
1. Get the target running, and then wait for it to break.
1. Flush the pipelines.
1. Check if the expected conditions are met, esp., that the program counter is where you expect.
1. If the conditions are not met, then let the target run again and repeat.
1. Once the conditions are met, perform the desired actions.
1. Optionally remove your instrumentation and/or let the target run.
### Exercise: Always Win in 0 Seconds
**NOTE**: The solution to this exercise is given as a tutorial below, but give it an honest try before peeking.
If you are not already familiar with Eclipse's searching and discovery features, try pressing **Ctrl-O** twice in the editor for your script.
You should now be able to type patterns, optionally with wildcards, to help you find applicable methods.
Your task is to write a script that will wait for the player to win then patch the machine state, so that the game always prints a score of 0 seconds.
Some gotchas to consider up front:
* You may want to verify and/or correct the target's execution state.
See `getExecutionState()` and `interrupt()`.
You will not likely be able to place or toggle breakpoints while the target is running.
* Methods like `writeMemory()` are subject to the current control mode.
You may want to check and/or correct this at the top of your script.
* If you require the user to mark code locations with a label, note that those labels will likely end up in the containing function's namespace.
You will need to provide that namespace to `getSymbols()`.
* If you need to set breakpoints, you should try to toggle an existing breakpoint at that location before adding a new one.
Otherwise, you may generate a pile of breakpoints and/or needlessly increment GDB's breakpoint numbers.
You are successful when you can attach to a running `termmines` and execute your script.
Then, assuming you win the game, the game should award you a score of 0 seconds.
It is OK if you have to re-execute your script after each win.
### Solution: Always Win in 0 Seconds
As in the previous scripting tutorial, we will do some verifications at the top of the script.
Your level of pedantry may vary.
```java {.numberLines}
Trace trace = getCurrentTrace();
if (trace == null) {
throw new AssertionError("There is no active session");
}
if (!"termmines".equals(currentProgram.getName())) {
throw new AssertionError("The current program must be termmines");
}
if (getExecutionState(trace).isRunning()) {
monitor.setMessage("Interrupting target and waiting for STOPPED");
interrupt();
waitForBreak(3, TimeUnit.SECONDS);
}
flushAsyncPipelines(trace);
if (!getControlService().getCurrentMode(trace).canEdit(getCurrentDebuggerCoordinates())) {
throw new AssertionError("Current control mode is read-only");
}
```
The first two blocks check that there is an active target with `termmines` as the current program.
As before, the association of the current program to the current target will be implicitly verified when we map symbols.
The second block will interrupt the target if it is running.
We then allow everything to sync up before checking the control mode.
We could instead change the control mode to **Target w/Edits**, but I prefer to keep the user aware that the script needs to modify target machine state.
Next, we retrieve and map our symbols.
This works pretty much the same as in the previous scripting tutorial, but with attention to the containing function namespace.
The way `termmines` computes the score is to record the start time of the game.
Then, when the player wins, it subtracts the recorded time from the current time.
This script requires the user to label the start time variable `timer`, and to label the instruction that computes the score `reset_timer`.
The function that prints the score must be named `print_win`.
```java {.numberLines}
List<Symbol> timerSyms = getSymbols("timer", null);
if (timerSyms.isEmpty()) {
throw new AssertionError("Symbol 'timer' is required");
}
List<Function> winFuncs = getGlobalFunctions("print_win");
if (winFuncs.isEmpty()) {
throw new AssertionError("Function 'print_win' is required");
}
List<Symbol> resetSyms = getSymbols("reset_timer", winFuncs.get(0));
if (resetSyms.isEmpty()) {
throw new AssertionError("Symbol 'reset_timer' is required");
}
Address timerDyn = translateStaticToDynamic(timerSyms.get(0).getAddress());
if (timerDyn == null) {
throw new AssertionError("Symbol 'timer' is not mapped to target");
}
Address resetDyn = translateStaticToDynamic(resetSyms.get(0).getAddress());
if (resetDyn == null) {
throw new AssertionError("Symbol 'reset_timer' is not mapped to target");
}
```
#### Toggling and Setting Breakpoints
The first actual operation we perform on the debug session is to toggle or place a breakpoint on the `reset_timer` label.
The API prefers to specify breakpoints in the static context, but you can do either.
To establish that context, you must use a `ProgramLocation`.
For static context, use the current (static) program as the program.
For dynamic context, use the current (dynamic) trace view as the program &mdash; see `getCurrentView()`.
To avoid creating a pile of breakpoints, we will first attempt to enable an existing breakpoint at the desired location.
Technically, the existing breakpoints may not be execute breakpoints, but we will blindly assume they are.
Again, your level of pedantry may vary.
The `breakpointsEnable` method will return the existing breakpoints, so we can check that and create a new breakpoint, if necessary:
```java {.numberLines}
ProgramLocation breakLoc =
new ProgramLocation(currentProgram, resetSyms.get(0).getAddress());
Set<LogicalBreakpoint> breaks = breakpointsEnable(breakLoc);
if (breaks == null || breaks.isEmpty()) {
breakpointSetSoftwareExecute(breakLoc, "reset timer");
}
```
#### Waiting to Hit the Breakpoint
This next loop is quite extensive, but it follows the template given earlier for waiting on conditions.
It is an indefinite loop, so we should check the monitor for cancellation somewhat frequently.
This implies we should use relatively short timeouts in our API calls.
In our case, we just want to confirm that the cause of breaking was hitting our breakpoint.
We do not need to be precise in this check; it suffices to check the program counter:
```java {.numberLines}
while (true) {
monitor.checkCanceled();
TargetExecutionState execState = getExecutionState(trace);
switch (execState) {
case STOPPED:
resume();
break;
case TERMINATED:
case INACTIVE:
throw new AssertionError("Target terminated");
case ALIVE:
println(
"I don't know whether or not the target is running. Please make it RUNNING.");
break;
case RUNNING:
/**
* Probably timed out waiting for break. That's fine. Give the player time to
* win.
*/
break;
default:
throw new AssertionError("Unrecognized state: " + execState);
}
try {
monitor.setMessage("Waiting for player to win");
waitForBreak(1, TimeUnit.SECONDS);
}
catch (TimeoutException e) {
// Give the player time to win.
continue;
}
flushAsyncPipelines(trace);
Address pc = getProgramCounter();
println("STOPPED at pc = " + pc);
if (resetDyn.equals(pc)) {
break;
}
}
```
The "center" of this loop is a call to `waitForBreak()`.
This is the simplest primitive for waiting on the target to meet any condition.
Because we expect the user to take more than a second to win the game, we should expect a timeout exception and just keep waiting.
Using a timeout of 1 second ensures we can terminate promptly should the user cancel the script.
Before waiting, we need to make sure the target is running.
Because we could repeat the loop while the target is already running, we should only call `resume()` if the target is stopped.
There are utility methods on `TargetExecutionState` like `isRunning()`, which you might prefer to use.
Here, we exhaustively handle every kind of state using a switch statement, which does make the code a bit verbose.
When the target does break, we first allow the UI to finish interrogating the target.
We can then reliably retrieve and check the program counter.
If the PC matches the dynamic location of `reset_timer`, then the player has won, and we need to reset the start time.
#### Patching the Start Time
When the player has won, this particular compilation of `termmines` first calls `time` to get the current time and moves it into `ECX`.
It then subtracts, using a memory operand, the recorded start time.
There are certainly other strategies, but this script expects the user to label that `SUB` instruction `reset_timer`.
We would like the result of that computation to be 0, so we will simply copy the value of `ECX` over the recorded start time:
```java {.numberLines}
int time = readRegister("ECX").getUnsignedValue().intValue();
if (!writeMemory(timerDyn,
ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(time).array())) {
throw new AssertionError("Could not write over timer. Does control mode allow edits?");
}
resume();
```
The final `resume()` simply allows the target to finish printing the score, which ought to be 0 now!
## Learning More
For another demonstration of the flat API, see [DemoDebuggerScript](../../../Ghidra/Debug/Debugger/ghidra_scripts/DemoDebuggerScript.java), or just ask Eclipse for all the implementations of `FlatDebuggerAPI`.
If you want a list of methods with explanations, you should refer to the documentation in the `FlatDebuggerAPI` interface.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,936 @@
# P-code Modeling
This module assumes you have completed the [Emulation](B2-Emulation.md) and [Scripting](B3-Scripting.md) portions of this course.
It also assumes you have fairly deep knowledge of Ghidra's low p-code.
Modeling is another one of those loaded terms.
Here we are going to focus on its use in what we will call *augmented emulation*.
This is used for things like dynamic taint analysis and concolic execution.
The idea is to leverage the emulator for concrete execution while augmenting it with some auxiliary model, e.g., taint labels or symbolic expressions.
Ghidra's abstract emulator implementations facilitate the composition of independent models so, if careful attention is given to your implementation, the auxiliary model can be re-used for other cases, perhaps even in static analysis.
This module will address the following aspects of modeling:
* Environment, i.e., p-code userops and stubbing.
* Arithmetic operations.
* Storage, addressing, and memory operations.
* Use in dynamic analysis.
* Use in static analysis.
* Integration with the GUI.
Modeling is definitely a development task.
There is generally a specific interface for each aspect, and Ghidra may provide abstract implementations of them, which you may choose to use or ignore.
If you do not already have a development environment set up, you will need to do that now.
Either use the GhidraDev plugin for Eclipse and associate it with an installation of Ghidra, or clone the `ghidra` source repository and prepare it for development in Eclipse.
When prototyping, you may find it easiest to develop a script, which is what this tutorial will do.
## Modeling the Environment
There are different pieces to the environment.
This covers the implementation of p-code userops, which generally covers everything not modeled by p-code.
For example, the x86-64 `SYSCALL` instruction just invokes the `syscall()` userop, which provides a hook for implementing them.
Modeling system calls is such a common case that Ghidra provides a special programming interface for it.
Stubbing external functions is covered, in part, by the [Emulation](B2-Emulation.md) module.
By providing common stubs in a userop library, the user can stub the external function by placing a Sleigh breakpoint that invokes the appropriate userop.
### Modeling by Java Callbacks
A userop library is created by implementing the `PcodeUseropLibrary` interface, most likely by extending `AnnotatedPcodeUseropLibrary`.
For example, to provide a stub for `strlen`:
```java {.numberLines}
public static class JavaStdLibPcodeUseropLibrary<T> extends AnnotatedPcodeUseropLibrary<T> {
private final AddressSpace space;
private final Register regRSP;
private final Register regRAX;
private final Register regRDI;
private final Register regRSI;
public JavaStdLibPcodeUseropLibrary(SleighLanguage language) {
space = language.getDefaultSpace();
regRSP = language.getRegister("RSP");
regRAX = language.getRegister("RAX");
regRDI = language.getRegister("RDI");
regRSI = language.getRegister("RSI");
}
@PcodeUserop
public void __x86_64_RET(
@OpExecutor PcodeExecutor<T> executor,
@OpState PcodeExecutorState<T> state) {
PcodeArithmetic<T> arithmetic = state.getArithmetic();
T tRSP = state.getVar(regRSP, Reason.EXECUTE_READ);
long lRSP = arithmetic.toLong(tRSP, Purpose.OTHER);
T tReturn = state.getVar(space, lRSP, 8, true, Reason.EXECUTE_READ);
long lReturn = arithmetic.toLong(tReturn, Purpose.BRANCH);
state.setVar(regRSP, arithmetic.fromConst(lRSP + 8, 8));
((PcodeThreadExecutor<T>) executor).getThread()
.overrideCounter(space.getAddress(lReturn));
}
@PcodeUserop
public void __libc_strlen(@OpState PcodeExecutorState<T> state) {
PcodeArithmetic<T> arithmetic = state.getArithmetic();
T tStr = state.getVar(regRDI, Reason.EXECUTE_READ);
long lStr = arithmetic.toLong(tStr, Purpose.OTHER);
T tMaxlen = state.getVar(regRSI, Reason.EXECUTE_READ);
long lMaxlen = arithmetic.toLong(tMaxlen, Purpose.OTHER);
for (int i = 0; i < lMaxlen; i++) {
T tChar = state.getVar(space, lStr + i, 1, false, Reason.EXECUTE_READ);
if (arithmetic.toLong(tChar, Purpose.OTHER) == 0) {
state.setVar(regRAX, arithmetic.fromConst(Integer.toUnsignedLong(i), 8));
break;
}
}
}
}
```
Here, we implement the stub using Java callbacks.
This is more useful when modeling things outside of Ghidra's definition of machine state, e.g., to simulate kernel objects in an underlying operating system.
Nevertheless, it can be used to model simple state changes as well.
A user would place a breakpoint at either the call site or the call target, have it invoke `__libc_strlen()`, and then invoke either `emu_skip_decoded()` or `__x86_64_RET()` depending on where the breakpoint was placed.
### Modeling by Sleigh Semantics
The advantage to Java callbacks is that things are relatively intuitive to do, but the temptation, which we intentionally demonstrate here, is to make everything concrete.
You may notice the library uses a type parameter `T`, which specifies the type of all variables in the emulator's state.
Leaving it as `T` indicates the library is compatible with any type.
For a concrete emulator, `T = byte[]`, and so there is no loss in making things concrete, and then converting back to `T` using the arithmetic object.
However, if the emulator has been augmented, as we will discuss below, the model may become confused, because values computed by a careless userop will appear to the model a literal constant.
To avoid this, you should keep everything a T and use the arithmetic object to perform any arithmetic operations.
Alternatively, you can implement the userop using pre-compiled Sleigh code:
```java {.numberLines}
public static class SleighStdLibPcodeUseropLibrary<T> extends AnnotatedPcodeUseropLibrary<T> {
private static final String SRC_RET = """
RIP = *:8 RSP;
RSP = RSP + 8;
return [RIP];
""";
private static final String SRC_STRLEN = """
__result = 0;
<loop>
if (*:1 (str+__result) == 0 || __result >= maxlen) goto <exit>;
__result = __result + 1;
goto <loop>;
<exit>
""";
private final Register regRAX;
private final Register regRDI;
private final Register regRSI;
private final Varnode vnRAX;
private final Varnode vnRDI;
private final Varnode vnRSI;
private PcodeProgram progRet;
private PcodeProgram progStrlen;
public SleighStdLibPcodeUseropLibrary(SleighLanguage language) {
regRAX = language.getRegister("RAX");
regRDI = language.getRegister("RDI");
regRSI = language.getRegister("RSI");
vnRAX = new Varnode(regRAX.getAddress(), regRAX.getMinimumByteSize());
vnRDI = new Varnode(regRDI.getAddress(), regRDI.getMinimumByteSize());
vnRSI = new Varnode(regRSI.getAddress(), regRSI.getMinimumByteSize());
}
@PcodeUserop
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_RET, PcodeUseropLibrary.nil(), List.of());
}
progRet.execute(executor, library);
}
@PcodeUserop
public void __libc_strlen(@OpExecutor PcodeExecutor<T> executor,
@OpLibrary PcodeUseropLibrary<T> library) {
if (progStrlen == null) {
progStrlen = SleighProgramCompiler.compileUserop(executor.getLanguage(),
"__libc_strlen", List.of("__result", "str", "maxlen"),
SRC_STRLEN, PcodeUseropLibrary.nil(), List.of(vnRAX, vnRDI, vnRSI));
}
progStrlen.execute(executor, library);
}
}
```
At construction, we capture the varnodes we need to use.
We could just use them directly in the source, but this demonstrates the ability to alias them, which makes the Sleigh source more re-usable across target architectures.
We then lazily compile each userop upon its first invocation.
These are technically still Java callbacks, but our implementation delegates to the executor, giving it the compiled p-code program.
The advantage here is that the code will properly use the underlying arithmetic appropriately.
However, for some models, that may actually not be desired.
Some symbolic models might just like to see a literal call to `strlen()`.
### Modeling by Structured Sleigh
The disadvantage to pre-compiled p-code is all the boilerplate and manual handling of Sleigh compilation.
Additionally, when stubbing C functions, you have to be mindful of the types, and things may get complicated enough that you pine for more C-like control structures.
The same library can be implemented using an incubating feature we call *Structured Sleigh*:
```java {.numberLines}
public static class StructuredStdLibPcodeUseropLibrary<T>
extends AnnotatedPcodeUseropLibrary<T> {
public StructuredStdLibPcodeUseropLibrary(CompilerSpec cs) {
new MyStructuredPart(cs).generate(ops);
}
public static class MyStructuredPart extends StructuredSleigh {
protected MyStructuredPart(CompilerSpec cs) {
super(cs);
}
@StructuredUserop
public void __x86_64_RET() {
Var RSP = lang("RSP", type("void **"));
Var RIP = lang("RIP", type("void *"));
RIP.set(RSP.deref());
RSP.addiTo(8);
_return(RIP);
}
@StructuredUserop
public void __libc_strlen() {
Var result = lang("RAX", type("long"));
Var str = lang("RDI", type("char *"));
Var maxlen = lang("RSI", type("long"));
_for(result.set(0), result.ltiu(maxlen).andb(str.index(result).deref().eq(0)),
result.inc(), () -> {
});
}
}
}
```
This is about as succinct as we can get specifying p-code behaviors in Java.
While these may appear like callbacks into Java methods that use a special API for state manipulation, that is not entirely accurate.
The Java method is invoked once as a way to "transpile" the Structured Sleigh into standard Sleigh semantic code.
That code is then compiled to p-code, which will be executed whenever the userop is called.
In a sense, Structured Sleigh is a DSL hosted in Java....
Unfortunately, we cannot overload operators in Java, so we are stuck using method invocations.
Another disadvantage is the dependence on a compiler spec for type resolution.
Structured Sleigh is not the best suited for all circumstances, e.g., the implementation of `__x86_64_RET` is odd to express.
Arguably, there is no real need to ascribe high-level types to `RSP` and `RIP` when expressing low-level operations.
Luckily, these implementation techniques can be mixed.
A single library can implement the `RET` using pre-compiled Sleigh, but `strlen` using Structured Sleigh.
### Modeling System Calls
We will not cover this in depth, but here are some good examples:
* [DemoSyscallLibrary](../../../Ghidra/Features/SystemEmulation/ghidra_scripts/DemoSyscallLibrary.java)
* [EmuLinuxAmd64SyscallUseropLibrary](../../../Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/linux/EmuLinuxAmd64SyscallUseropLibrary.java)
* [EmuLinuxX86SyscallUseropLibrary](../../../Ghidra/Features/SystemEmulation/src/main/java/ghidra/pcode/emu/linux/EmuLinuxX86SyscallUseropLibrary.java)
More can be obtained by finding all implementations of `EmuSyscallLibrary` in your IDE.
The Linux system call libraries are incomplete.
They only provide a few simple file operations, but it is sufficient to demonstrate the simulation of an underlying operating system.
They can also be extended and/or composed to provide additional system calls.
### Using Custom Userop Libraries
The use of a custom library in a stand-alone emulation script is pretty straightforward:
```java {.numberLines}
public class CustomLibraryScript extends GhidraScript {
@Override
protected void run() throws Exception {
PcodeEmulator emu = new PcodeEmulator(currentProgram.getLanguage()) {
@Override
protected PcodeUseropLibrary<byte[]> createUseropLibrary() {
return super.createUseropLibrary()
.compose(new ModelingScript.StructuredStdLibPcodeUseropLibrary<>(
currentProgram.getCompilerSpec()));
}
};
emu.inject(currentAddress, """
__libc_strlen();
__X86_64_RET();
""");
// TODO: Initialize the emulator's memory from the current program
PcodeThread<byte[]> thread = emu.newThread();
// TODO: Initialize the thread's registers
while (true) {
monitor.checkCancelled();
thread.stepInstruction(100);
}
}
}
```
The key is to override `createUseropLibrary()` in an anonymous extension of the `PcodeEmulator`.
It is polite to compose your library with the one already provided by the super class, lest you remove userops and cause unexpected crashes later.
For the sake of demonstration, we have included an injection that uses the custom library, and we have included a monitored loop to execute a single thread indefinitely.
The initialization of the machine and its one thread is left to the script writer.
The emulation *is not* implicitly associated with the program!
You must copy the program image into its state, and you should choose a different location for the injection.
Refer to the example scripts in Ghidra's `SystemEmulation` module.
If you would like to (temporarily) override the GUI with a custom userop library, you can by overriding the GUI's emulator factory:
```java {.numberLines}
public class InstallCustomLibraryScript extends GhidraScript implements FlatDebuggerAPI {
public static class CustomBytesDebuggerPcodeEmulator extends BytesDebuggerPcodeEmulator {
private CustomBytesDebuggerPcodeEmulator(PcodeDebuggerAccess access) {
super(access);
}
@Override
protected PcodeUseropLibrary<byte[]> createUseropLibrary() {
return super.createUseropLibrary()
.compose(new ModelingScript.SleighStdLibPcodeUseropLibrary<>(
(SleighLanguage) access.getLanguage()));
}
}
public static class CustomBytesDebuggerPcodeEmulatorFactory
extends BytesDebuggerPcodeEmulatorFactory {
@Override
public DebuggerPcodeMachine<?> create(PcodeDebuggerAccess access) {
return new CustomBytesDebuggerPcodeEmulator(access);
}
}
@Override
protected void run() throws Exception {
getEmulationService().setEmulatorFactory(new CustomBytesDebuggerPcodeEmulatorFactory());
}
}
```
This will make your custom userops available in Sleigh injections.
**NOTE**: There is currently no way to introduce custom userops to Watches or the Go To dialog.
## Modeling Arithmetic Operations
The remaining sections deal in modeling things other than concrete emulation.
In most dynamic analysis cases, we will *augment* a concrete emulator with some other abstract execution model, e.g., for dynamic taint analysis or concolic emulation.
Ghidra's emulation framework favors the composition of execution models.
This allows you to focus on the abstract execution model and later compose it with the concrete model to form the full augmented model.
This also facilitates the creation of re-usable components, but that still requires some forethought.
Modeling the arithmetic is fairly straightforward.
For demonstration we will develop a model for building up symbolic expressions.
The idea is that after doing some number of steps of emulation, the user can examine not only the concrete value of a variable, but the expression that generated it in terms of the variables at the start of the stepping schedule.
We *will not* attempt to simplify or otherwise analyze these expressions.
For that, you would want to use a proper SMT, which is beyond the scope of this tutorial.
### The Model
We will represent constants as literals, and then build up expression trees as each operation is applied.
The number of operators can get extensive, and your particular use case / target may not require all of them.
That said, if you intend for your model to be adopted broadly, you should strive for as complete an implementation as reasonably possible.
At the very least, strive to provide extension points where you predict the need to alter or add features.
In this tutorial, we will elide all but what is necessary to illustrate the implementation.
If it is not already provided to you by your dependencies, you will need to devise the actual model.
These need not extend from nor implement any Ghidra-specific interface, but they can.
```java {.numberLines}
public class ModelingScript extends GhidraScript {
interface Expr {
}
interface UnExpr extends Expr {
Expr u();
}
interface BinExpr extends Expr {
Expr l();
Expr r();
}
record LitExpr(BigInteger val, int size) implements Expr {
}
record VarExpr(Varnode vn) implements Expr {
public VarExpr(AddressSpace space, long offset, int size) {
this(space.getAddress(offset), size);
}
public VarExpr(Address address, int size) {
this(new Varnode(address, size));
}
}
record InvExpr(Expr u) implements UnExpr {
}
record AddExpr(Expr l, Expr r) implements BinExpr {
}
record SubExpr(Expr l, Expr r) implements BinExpr {
}
@Override
protected void run() throws Exception {
// TODO Auto-generated method stub
}
}
```
It should be fairly apparent how you could add more expression types to complete the model.
There is some odd nuance in the naming of p-code operations, so do read the documentation carefully.
If you are not entirely certain what an operation does, take a look at [OpBehaviorFactory](../../../Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/pcode/opbehavior/OpBehaviorFactory.java).
You can also examine the concrete implementation on byte arrays [BytesPcodeArithmetic](../../../Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/BytesPcodeArithmetic.java).
### Mapping the Model
Now, to map the model to p-code, we implement the `PcodeArithmetic` interface.
In many cases, the implementation can be an enumeration: one for big endian and one for little endian.
Rarely, it can be a singleton.
Conventionally, you should also include static methods for retrieving an instance by endianness or processor language:
```java {.numberLines}
public enum ExprPcodeArithmetic implements PcodeArithmetic<Expr> {
BE(Endian.BIG), LE(Endian.LITTLE);
public static ExprPcodeArithmetic forEndian(Endian endian) {
return endian.isBigEndian() ? BE : LE;
}
public static ExprPcodeArithmetic forLanguage(Language language) {
return language.isBigEndian() ? BE : LE;
}
private final Endian endian;
private ExprPcodeArithmetic(Endian endian) {
this.endian = endian;
}
@Override
public Endian getEndian() {
return endian;
}
@Override
public Expr unaryOp(int opcode, int sizeout, int sizein1, Expr in1) {
return switch (opcode) {
case PcodeOp.INT_NEGATE -> new InvExpr(in1);
default -> throw new UnsupportedOperationException(PcodeOp.getMnemonic(opcode));
};
}
@Override
public Expr binaryOp(int opcode, int sizeout, int sizein1, Expr in1, int sizein2,
Expr in2) {
return switch (opcode) {
case PcodeOp.INT_ADD -> new AddExpr(in1, in2);
case PcodeOp.INT_SUB -> new SubExpr(in1, in2);
default -> throw new UnsupportedOperationException(PcodeOp.getMnemonic(opcode));
};
}
@Override
public Expr modBeforeStore(int sizeout, int sizeinAddress, Expr inAddress, int sizeinValue,
Expr inValue) {
return inValue;
}
@Override
public Expr modAfterLoad(int sizeout, int sizeinAddress, Expr inAddress, int sizeinValue,
Expr inValue) {
return inValue;
}
@Override
public Expr fromConst(byte[] value) {
if (endian.isBigEndian()) {
return new LitExpr(new BigInteger(1, value), value.length);
}
byte[] reversed = Arrays.copyOf(value, value.length);
ArrayUtils.reverse(reversed);
return new LitExpr(new BigInteger(1, reversed), reversed.length);
}
@Override
public Expr fromConst(BigInteger value, int size, boolean isContextreg) {
return new LitExpr(value, size);
}
@Override
public Expr fromConst(long value, int size) {
return fromConst(BigInteger.valueOf(value), size);
}
@Override
public byte[] toConcrete(Expr value, Purpose purpose) {
throw new UnsupportedOperationException();
}
@Override
public long sizeOf(Expr value) {
throw new UnsupportedOperationException();
}
}
```
We have implemented two arithmetic models: one for big-endian languages and one for little-endian.
The endianness comes into play when we encode constant values passed to `fromConst()`.
We must convert the `byte[]` value to a big integer accordingly.
The choice of `BigInteger` is merely a matter of preference; you could easily just have `LitExpr` encapsulate the `byte[]` and worry about how to interpret them later.
We also override all implementations of `fromConst()` to avoid the back-and-forth conversion between `BigInteger` and `byte[]`.
The implementations of `unaryOp()` and `binaryOp()` are straightforward.
Just switch on the opcode and construct the appropriate expression.
This is a place where you might want to provide extensibility.
**NOTE**: If you would like to capture location information, i.e., what instruction performed this operation, then you can override the default `unaryOp()` and `binaryOp()` methods, which receive the actual `PcodeOp` object.
You can get both the opcode and the sequence number (address, index) from that `PcodeOp`.
The ones with signatures taking the integer opcode can just throw an `AssertionError`.
The implementations of `modBeforeStore()` and `modAfterLoad()` are stubs.
They provide an opportunity to capture dereferencing information.
We do not need that information, so we just return the value.
The `mod` methods tread a bit into storage and addressing, which we cover more thoroughly later, but they model memory operations to the extent they do not actually require a storage mechanism.
For example, were this a dynamic taint analyzer, we could use `modAfterLoad()` to record that a value was retrieved via a tainted address.
The `inValue` parameter gives the `Expr` actually retrieved from the emulator's storage, and `inAddress` gives the address (really just the `Expr` piece) used to retrieve it.
Conversely, in `modBeforeStore()`, `inValue` gives the value about to be stored, and `inAddress` gives the address used to store it.
We implement neither `toConcrete()` nor `sizeOf()`.
Since we will be augmenting a concrete emulator, these methods will be provided by the concrete piece.
If this model is ever to be used in static analysis, then it may be worthwhile to implement these methods, so the model may be used independently of the concrete emulator.
In that case, the methods should attempt to do as documented but may throw an exception upon failure.
## Modeling Storage, Addressing, and Memory Operations
The emulator's storage model is a `PcodeExecutorState`.
Since we desire an augmented emulator, we will need to provide it a `PcodeExecutorState<Pair<byte[], Expr>>`.
This tells Java the state is capable of working with pairs of concrete state and the abstract model state.
Addresses in that state are also pairs.
For augmented emulation, the storage model often borrows the concrete addressing model; thus, we will use only the `byte[]` element for our addressing.
The composition of states with the same addressing model is common enough that Ghidra provides abstract components to facilitate it.
The relevant interface is `PcodeExecutorStatePiece`, which is the one we actually implement, by extending from `AbstractLongOffsetPcodeExecutorStatePiece`.
**NOTE**: If you do not desire a concrete address model, then you should implement `PcodeExecutorState<Expr>` directly.
A "state" is also "state piece" whose address model is the same as its value model, so states can still be composed.
On one hand, the abstractly-addressed state provides a component that is readily used in both static and dynamic analysis; whereas, the concretely-addressed piece is suited only for dynamic analysis.
On the other hand, you may have some difficulty correlating concrete and abstract pieces during dynamic analysis when aliasing and indirection is involved.
Now for the code.
Be mindful of all the adjectives.
If you are not already familiar with Java naming conventions for "enterprise applications" or our particular implementation of them, you are about to see it on full display.
```java {.numberLines}
public static class ExprSpace {
protected final NavigableMap<Long, Expr> map;
protected final AddressSpace space;
protected ExprSpace(AddressSpace space, NavigableMap<Long, Expr> map) {
this.space = space;
this.map = map;
}
public ExprSpace(AddressSpace space) {
this(space, new TreeMap<>());
}
public void clear() {
map.clear();
}
public void set(long offset, Expr val) {
// TODO: Handle overlaps / offcut gets and sets
map.put(offset, val);
}
public Expr get(long offset, int size) {
// TODO: Handle overlaps / offcut gets and sets
Expr expr = map.get(offset);
return expr != null ? expr : new VarExpr(space, offset, size);
}
}
public static abstract class AbstractExprPcodeExecutorStatePiece<S extends ExprSpace> extends
AbstractLongOffsetPcodeExecutorStatePiece<byte[], Expr, S> {
protected final AbstractSpaceMap<S> spaceMap = newSpaceMap();
public AbstractExprPcodeExecutorStatePiece(Language language) {
super(language, BytesPcodeArithmetic.forLanguage(language),
ExprPcodeArithmetic.forLanguage(language));
}
protected abstract AbstractSpaceMap<S> newSpaceMap();
@Override
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
for (S space : spaceMap.values()) {
space.clear();
}
}
@Override
protected S getForSpace(AddressSpace space, boolean toWrite) {
return spaceMap.getForSpace(space, toWrite);
}
@Override
protected void setInSpace(ExprSpace space, long offset, int size, Expr val) {
space.set(offset, val);
}
@Override
protected Expr getFromSpace(S space, long offset, int size, Reason reason) {
return space.get(offset, size);
}
@Override
protected Map<Register, Expr> getRegisterValuesFromSpace(S s, List<Register> registers) {
throw new UnsupportedOperationException();
}
}
public static class ExprPcodeExecutorStatePiece
extends AbstractExprPcodeExecutorStatePiece<ExprSpace> {
public ExprPcodeExecutorStatePiece(Language language) {
super(language);
}
@Override
protected AbstractSpaceMap<ExprSpace> newSpaceMap() {
return new SimpleSpaceMap<ExprSpace>() {
@Override
protected ExprSpace newSpace(AddressSpace space) {
return new ExprSpace(space);
}
};
}
}
public static class BytesExprPcodeExecutorState extends PairedPcodeExecutorState<byte[], Expr> {
public BytesExprPcodeExecutorState(PcodeExecutorStatePiece<byte[], byte[]> concrete) {
super(new PairedPcodeExecutorStatePiece<>(concrete,
new ExprPcodeExecutorStatePiece(concrete.getLanguage())));
}
}
```
The abstract class implements a strategy where a dedicated object handles each address space.
Each object typically maintains of map of offsets (type `long`) to the model type, i.e., `Expr`.
We provide that object type `ExprSpace`, which is where we implement most of our actual storage.
**WARNING**: For the sake of simplicity in demonstration, we have neglected many details.
Notably, we have neglected the possibility that writes overlap or that reads are offcut from the variables actually stored there.
This may not seem like a huge problem, but it is actually quite common, esp., since x86 registers are structured.
A write to `RAX` followed by a read from `EAX` will immediately demonstrate this issue.
Nevertheless, we leave those details as an exercise.
The remaining parts are mostly boilerplate.
We implement the "state piece" interface by creating another abstract class.
An abstract class is not absolutely necessary, but it will be useful when we integrate the model with traces and the Debugger GUI later.
We are given the language and applicable arithmetics, which we just pass to the super constructor.
We need not implement a concrete buffer.
This would only be required if we needed to decode instructions from the abstract storage model.
For dynamic analysis, we would bind concrete buffers from the concrete piece, not the abstract.
For static analysis, you would need to decide whether to just use the statically disassembled instructions or to try decoding from the abstract model.
The `clear()` method is implemented by clearing the map of address spaces.
Note that the abstract implementation does not provide that map for us, so we must provide it and the logic to clear it.
The next three methods are for getting spaces from that map and then setting and getting values in them.
The last method `getRegisterValuesFromSpace()` is more for user inspection, so it need not be implemented, at least not yet.
Finally, we complete the implementation of the state piece with `ExprPcodeExecutorStatePiece`, which provides the actual map and an `ExprSpace` factory method `newSpace()`.
The implementation of `ExprPcodeExecutorState` is simple.
It takes the concrete piece and pairs it with a new piece for our model.
## Model-Specific Userops
We do not cover this deeply, but there are two examples in Ghidra:
* [TaintPcodeUseropLibrary](../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/TaintPcodeUseropLibrary.java)
* [TaintFileReadsLinuxAmd64SyscallLibrary](../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/lib/TaintFileReadsLinuxAmd64SyscallLibrary.java)
The first provides a means of marking variables with taint.
Unlike our `Expr` model, which automatically generates a `VarExpr` whenever a variable is read for the first time, the taint analyzer assumes no state is tainted.
You may notice the library does not use a generic `T`, but instead requires `T=Pair<byte[], TaintVec>`.
This will ensure the library is only used with a taint-augmented emulator.
The second demonstrates the ability to extend Ghidra's system call libraries, not only with additional calls, but also with additional models.
## Constructing the Augmented Emulator
Ghidra supports the construction of augmented emulators through the `AuxEmulatorPartsFactory<Expr>` interface.
These are typically singletons.
```java {.numberLines}
public enum BytesExprEmulatorPartsFactory implements AuxEmulatorPartsFactory<Expr> {
INSTANCE;
@Override
public PcodeArithmetic<Expr> getArithmetic(Language language) {
return ExprPcodeArithmetic.forLanguage(language);
}
@Override
public PcodeUseropLibrary<Pair<byte[], Expr>> createSharedUseropLibrary(
AuxPcodeEmulator<Expr> emulator) {
return PcodeUseropLibrary.nil();
}
@Override
public PcodeUseropLibrary<Pair<byte[], Expr>> createLocalUseropStub(
AuxPcodeEmulator<Expr> emulator) {
return PcodeUseropLibrary.nil();
}
@Override
public PcodeUseropLibrary<Pair<byte[], Expr>> createLocalUseropLibrary(
AuxPcodeEmulator<Expr> emulator, PcodeThread<Pair<byte[], Expr>> thread) {
return PcodeUseropLibrary.nil();
}
@Override
public PcodeExecutorState<Pair<byte[], Expr>> createSharedState(
AuxPcodeEmulator<Expr> emulator, BytesPcodeExecutorStatePiece concrete) {
return new BytesExprPcodeExecutorState(concrete);
}
@Override
public PcodeExecutorState<Pair<byte[], Expr>> createLocalState(
AuxPcodeEmulator<Expr> emulator, PcodeThread<Pair<byte[], Expr>> thread,
BytesPcodeExecutorStatePiece concrete) {
return new BytesExprPcodeExecutorState(concrete);
}
}
public class BytesExprPcodeEmulator extends AuxPcodeEmulator<Expr> {
public BytesExprPcodeEmulator(Language language) {
super(language);
}
@Override
protected AuxEmulatorPartsFactory<ModelingScript.Expr> getPartsFactory() {
return BytesExprEmulatorPartsFactory.INSTANCE;
}
}
```
Lots of boilerplate.
Essentially, all the parts factory does is give us a flat interface for providing all the parts necessary to construct our augmented emulator: the model arithmetic, userop libraries for the machine and threads, state for the machine and threads.
For the arithmetic, we trivially provide the arithmetic for the given language.
For the userop libraries, we just provide the empty library.
If you had custom libraries and/or model-specific libraries, you would compose them here.
Finally, for the states, we just take the provided concrete state and construct our augmented state.
## Use in Dynamic Analysis
What we have constructed so far is suitable for constructing and using our augmented emulator in a script.
Using it is about as straightforward as the plain concrete emulator.
The exception may be when accessing its state, you will need to be cognizant of the pairing.
```java {.numberLines}
public class ModelingScript extends GhidraScript {
// ...
@Override
protected void run() throws Exception {
BytesExprPcodeEmulator emu = new BytesExprPcodeEmulator(currentProgram.getLanguage());
// TODO: Initialize the machine
PcodeExecutorState<Pair<byte[], Expr>> state = emu.getSharedState();
state.setVar(currentAddress, 4, true,
Pair.of(new byte[] { 1, 2, 3, 4 }, new VarExpr(currentAddress, 4)));
PcodeThread<Pair<byte[], Expr>> thread = emu.newThread();
// TODO: Initialize the thread
while (true) {
monitor.checkCancelled();
thread.stepInstruction(100);
}
}
}
```
**NOTE**: When accessed as a paired state, all sets will affect both pieces.
If you use the arithmetic to generate them, remember that it will use `fromConst` on both arithmetics to generate the pair, so you may be setting the right side to a `LitExpr`.
To modify just one side of the pair, cast the state to `PairedPcodeExecutorState`, and then use `getLeft()`, and `getRight()` to retrieve the separate pieces.
## Use in Static Analysis
We do not go into depth here, especially since this is not formalized.
There are many foundational utilities not factored out yet.
Nevertheless, for an example where the `PcodeArithmetic` and `PcodeExecutorState` interfaces are used in static analysis, see the Debugger's stack unwinder.
While unwinding a full stack technically qualifies as dynamic analysis, the analysis of each individual function to recover stack frame information is purely static.
See [UnwindAnalysis](../../../Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/stack/UnwindAnalysis.java) and its sibling files.
## GUI Integration
This part is rather tedious.
It is mostly boilerplate, and the only real functionality we need to provide is a means of serializing `Expr` to the trace database.
Ideally, this serialization is also human readable, since that will make it straightforward to display in the UI.
Typically, there are two more stages of integration.
First is integration with traces, which involves the aforementioned serialization.
Second is integration with targets, which often does not apply to abstract models, but could.
Each stage involves an extension to the lower stage's state.
Java does not allow multiple inheritance, so we will have to be clever in our factoring, but we generally cannot escape the boilerplate.
```java {.numberLines}
public static class ExprTraceSpace extends ExprSpace {
protected final PcodeTracePropertyAccess<String> property;
public ExprTraceSpace(AddressSpace space, PcodeTracePropertyAccess<String> property) {
super(space);
this.property = property;
}
@Override
protected Expr whenNull(long offset, int size) {
String string = property.get(space.getAddress(offset));
return deserialize(string);
}
public void writeDown(PcodeTracePropertyAccess<String> into) {
if (space.isUniqueSpace()) {
return;
}
for (Entry<Long, Expr> entry : map.entrySet()) {
// TODO: Ignore and/or clear non-entries
into.put(space.getAddress(entry.getKey()), serialize(entry.getValue()));
}
}
protected String serialize(Expr expr) {
return Unfinished.TODO();
}
protected Expr deserialize(String string) {
return Unfinished.TODO();
}
}
public static class ExprTracePcodeExecutorStatePiece
extends AbstractExprPcodeExecutorStatePiece<ExprTraceSpace>
implements TracePcodeExecutorStatePiece<byte[], Expr> {
public static final String NAME = "Taint";
protected final PcodeTraceDataAccess data;
protected final PcodeTracePropertyAccess<String> property;
public ExprTracePcodeExecutorStatePiece(PcodeTraceDataAccess data) {
super(data.getLanguage());
this.data = data;
this.property = data.getPropertyAccess(NAME, String.class);
}
@Override
public PcodeTraceDataAccess getData() {
return data;
}
@Override
protected AbstractSpaceMap<ExprTraceSpace> newSpaceMap() {
return new CacheingSpaceMap<PcodeTracePropertyAccess<String>, ExprTraceSpace>() {
@Override
protected PcodeTracePropertyAccess<String> getBacking(AddressSpace space) {
return property;
}
@Override
protected ExprTraceSpace newSpace(AddressSpace space,
PcodeTracePropertyAccess<String> backing) {
return new ExprTraceSpace(space, property);
}
};
}
@Override
public ExprTracePcodeExecutorStatePiece fork() {
throw new UnsupportedOperationException();
}
@Override
public void writeDown(PcodeTraceDataAccess into) {
PcodeTracePropertyAccess<String> property = into.getPropertyAccess(NAME, String.class);
for (ExprTraceSpace space : spaceMap.values()) {
space.writeDown(property);
}
}
}
```
Because we do not need any additional logic for target integration, we do not need to extend the state pieces any further.
The concrete pieces that we augment will contain all the target integration needed.
We have left the serialization as an exercise, though.
Last, we implement the full parts factory and use it to construct and install a full `Expr`-augmented emulator factory:
```java {.numberLines}
public static class BytesExprDebuggerPcodeEmulator extends AuxDebuggerPcodeEmulator<Expr> {
public BytesExprDebuggerPcodeEmulator(PcodeDebuggerAccess access) {
super(access);
}
@Override
protected AuxDebuggerEmulatorPartsFactory<Expr> getPartsFactory() {
return BytesExprDebuggerEmulatorPartsFactory.INSTANCE;
}
}
public static class BytesExprDebuggerPcodeEmulatorFactory
implements DebuggerPcodeEmulatorFactory {
@Override
public String getTitle() {
return "Expr";
}
@Override
public DebuggerPcodeMachine<?> create(PcodeDebuggerAccess access) {
return new BytesExprDebuggerPcodeEmulator(access);
}
}
```
The factory can then be installed using a script.
The script will set your factory as the current emulator factory for the whole tool; however, your script-based factory will not be listed in the menus.
Also, if you change your emulator, you must re-run the script to install those modifications.
You might also want to invalidate the emulation cache.
```java {.numberLines}
public class InstallExprEmulatorScript extends GhidraScript implements FlatDebuggerAPI {
@Override
protected void run() throws Exception {
getEmulationService()
.setEmulatorFactory(new ModelingScript.BytesExprDebuggerPcodeEmulatorFactory());
}
}
```
Alternatively, and this is recommended once your emulator is "production ready," you should create a proper Module project using the GhidraDev plugin for Eclipse.
You will need to break all the nested classes from your script out into separate files.
So long as your factory class is public, named with the suffix `DebuggerPcodeEmulatorFactory`, implements the interface, and included in Ghidra's classpath, Ghidra should find and list it in the **Debugger &rarr; Configure Emulator** menu.
### Displaying and Manipulating Abstract State
Once you have an emulator factory, the bulk of the work is done.
However, at this point, users can only interact with the abstract portion of the emulator's state through scripts, or by invoking custom userops in patch steps from the **Go To Time** dialog.
To display the abstract state in the UI, you need to develop two additional components: one for display in the Dynamic Listing (for memory state) and one for display in the Registers window (for register state).
(Display of custom state in the Watches or P-code Stepper panes is not supported.)
Unlike an emulator factory, these components cannot be installed via a script.
They must be provided as classes in a proper Ghidra Module.
Since string-based serialization may be a common case, we may eventually provide abstract implementations to make that case easy.
For now, we refer you to the implementations for the Taint-augmented emulator:
* For memory state: [TaintFieldFactory](../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintFieldFactory.java)
* For regsiter state: [TaintDebuggerRegisterColumnFactory](../../../Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintDebuggerRegisterColumnFactory.java)
Anything more than that would require completely custom providers, plugins, etc.

View file

@ -0,0 +1,22 @@
PANDOC=pandoc
default: all
%.html: %.md
$(PANDOC) -B navhead.htm --section-divs --toc -c style.css -s --lua-filter=links-to-html.lua --syntax-definition=gdb_syntax.xml --syntax-definition=sleigh_syntax.xml --metadata title="Ghidra Debugger" $< -o $@
all: \
A1-GettingStarted.html \
A2-UITour.html \
A3-Breakpoints.html \
A4-MachineState.html \
A5-Navigation.html \
A6-MemoryMap.html \
B1-RemoteTargets.html \
B2-Emulation.html \
B3-Scripting.html \
B4-Modeling.html
clean:
-rm *.html

View file

@ -0,0 +1,24 @@
# Debugger Ghidra Class
This is the courseware for the Debugger training class.
It is meant to be viewed directly in GitHub or GitLab but can also be rendered for offline viewing in a classroom.
The courseware was developed with Linux user-space targets in mind, but many of the concepts and information apply to the other platforms.
There are differences in getting started, some subtleties in target behavior, and of course different instruction set architectures, but for the most part, the user interface is the same across the platforms.
## Table of Contents
### Beginner Material
1. [Getting Started](A1-GettingStarted.md)
1. [A Tour of the UI](A2-UITour.md)
1. [Breakpoints](A3-Breakpoints.md)
1. [Machine State: Memory, Registers, and Variables](A4-MachineState.md)
1. [Navigation](A5-Navigation.md)
1. [Memory Map](A6-MemoryMap.md)
### Advanced Material
1. [Remote Targets](B1-RemoteTargets.md)
1. [Emulation](B2-Emulation.md)
1. [Scripting](B3-Scripting.md)
1. [Modeling](B4-Modeling.md)

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE language
[
]>
<language name="GDB" section="Scripts"
version="1" kateversion="5.79"
extensions="*.gdb"
priority="5">
<highlighting>
<list name="controlflow">
</list>
<list name="keywords">
<item>target</item>
<item>remote</item>
<item>break</item>
<item>new-ui</item>
</list>
<contexts>
<context attribute="Normal Text" lineEndContext="#stay" name="Normal">
<DetectSpaces />
<IncludeRules context="match keywords" />
<DetectIdentifier />
</context>
<context name="match keywords" attribute="Normal Text" lineEndContext="#pop">
<keyword attribute="Control Flow" context="#stay" String="controlflow"/>
<keyword attribute="Keyword" context="#stay" String="keywords"/>
</context>
</contexts>
<itemDatas>
<itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false"/>
<itemData name="Control Flow" defStyleNum="dsControlFlow" spellChecking="false"/>
<itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
</itemDatas>
</highlighting>
<general>
<keywords casesensitive="1" additionalDeliminator="#'&quot;" weakDeliminator="-" />
</general>
</language>
<!-- kate: replace-tabs on; tab-width 2; indent-width 2; -->

View file

@ -0,0 +1,48 @@
/* ###
* 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.
*/
import ghidra.app.script.GhidraScript;
import ghidra.pcode.emu.PcodeEmulator;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.exec.PcodeUseropLibrary;
public class CustomLibraryScript extends GhidraScript {
@Override
protected void run() throws Exception {
PcodeEmulator emu = new PcodeEmulator(currentProgram.getLanguage()) {
@Override
protected PcodeUseropLibrary<byte[]> createUseropLibrary() {
return super.createUseropLibrary()
.compose(new ModelingScript.StructuredStdLibPcodeUseropLibrary<>(
currentProgram.getCompilerSpec()));
}
};
emu.inject(currentAddress, """
__libc_strlen();
__X86_64_RET();
""");
// TODO: Initialize the emulator's memory from the current program
PcodeThread<byte[]> thread = emu.newThread();
// TODO: Initialize the thread's registers
while (true) {
monitor.checkCanceled();
thread.stepInstruction(100);
}
}
}

View file

@ -0,0 +1,85 @@
/* ###
* 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.
*/
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import ghidra.app.script.GhidraScript;
import ghidra.debug.flatapi.FlatDebuggerAPI;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.Symbol;
import ghidra.trace.model.Trace;
public class DumpBoardScript extends GhidraScript implements FlatDebuggerAPI {
@Override
protected void run() throws Exception {
// --------------------------------
Trace trace = getCurrentTrace();
if (trace == null) {
throw new AssertionError("There is no active session");
}
// --------------------------------
if (!"termmines".equals(currentProgram.getName())) {
throw new AssertionError("The current program must be termmines");
}
// --------------------------------
List<Symbol> widthSyms = getSymbols("width", null);
if (widthSyms.isEmpty()) {
throw new AssertionError("Symbol 'width' is required");
}
List<Symbol> heightSyms = getSymbols("height", null);
if (heightSyms.isEmpty()) {
throw new AssertionError("Symbol 'height' is required");
}
List<Symbol> cellsSyms = getSymbols("cells", null);
if (cellsSyms.isEmpty()) {
throw new AssertionError("Symbol 'cells' is required");
}
Address widthDyn = translateStaticToDynamic(widthSyms.get(0).getAddress());
if (widthDyn == null) {
throw new AssertionError("Symbol 'width' is not mapped to target");
}
Address heightDyn = translateStaticToDynamic(heightSyms.get(0).getAddress());
if (heightDyn == null) {
throw new AssertionError("Symbol 'height' is not mapped to target");
}
Address cellsDyn = translateStaticToDynamic(cellsSyms.get(0).getAddress());
if (cellsDyn == null) {
throw new AssertionError("Symbol 'cells' is not mapped to target");
}
// --------------------------------
byte[] widthDat = readMemory(widthDyn, 4, monitor);
byte[] heightDat = readMemory(heightDyn, 4, monitor);
byte[] cellsData = readMemory(cellsDyn, 1024, monitor);
// --------------------------------
int width = ByteBuffer.wrap(widthDat).order(ByteOrder.LITTLE_ENDIAN).getInt();
int height = ByteBuffer.wrap(heightDat).order(ByteOrder.LITTLE_ENDIAN).getInt();
println("Width: " + width);
println("Height: " + height);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if ((cellsData[(y + 1) * 32 + x + 1] & 0x80) == 0x80) {
println("Mine at (%d,%d)".formatted(x, y));
}
}
}
}
}

View file

@ -0,0 +1,49 @@
/* ###
* 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.
*/
import ghidra.app.plugin.core.debug.service.emulation.*;
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerAccess;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.script.GhidraScript;
import ghidra.debug.flatapi.FlatDebuggerAPI;
import ghidra.pcode.exec.PcodeUseropLibrary;
public class InstallCustomLibraryScript extends GhidraScript implements FlatDebuggerAPI {
public static class CustomBytesDebuggerPcodeEmulator extends BytesDebuggerPcodeEmulator {
private CustomBytesDebuggerPcodeEmulator(PcodeDebuggerAccess access) {
super(access);
}
@Override
protected PcodeUseropLibrary<byte[]> createUseropLibrary() {
return super.createUseropLibrary()
.compose(new ModelingScript.SleighStdLibPcodeUseropLibrary<>(
(SleighLanguage) access.getLanguage()));
}
}
public static class CustomBytesDebuggerPcodeEmulatorFactory
extends BytesDebuggerPcodeEmulatorFactory {
@Override
public DebuggerPcodeMachine<?> create(PcodeDebuggerAccess access) {
return new CustomBytesDebuggerPcodeEmulator(access);
}
}
@Override
protected void run() throws Exception {
getEmulationService().setEmulatorFactory(new CustomBytesDebuggerPcodeEmulatorFactory());
}
}

View file

@ -0,0 +1,25 @@
/* ###
* 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.
*/
import ghidra.app.script.GhidraScript;
import ghidra.debug.flatapi.FlatDebuggerAPI;
public class InstallExprEmulatorScript extends GhidraScript implements FlatDebuggerAPI {
@Override
protected void run() throws Exception {
getEmulationService()
.setEmulatorFactory(new ModelingScript.BytesExprDebuggerPcodeEmulatorFactory());
}
}

View file

@ -0,0 +1,681 @@
/* ###
* 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.
*/
import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.tuple.Pair;
import ghidra.app.plugin.core.debug.service.emulation.*;
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerAccess;
import ghidra.app.plugin.processors.sleigh.SleighLanguage;
import ghidra.app.script.GhidraScript;
import ghidra.lifecycle.Unfinished;
import ghidra.pcode.emu.DefaultPcodeThread.PcodeThreadExecutor;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.emu.auxiliary.AuxEmulatorPartsFactory;
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
import ghidra.pcode.exec.*;
import ghidra.pcode.exec.PcodeArithmetic.Purpose;
import ghidra.pcode.exec.PcodeExecutorStatePiece.Reason;
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerEmulatorPartsFactory;
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerPcodeEmulator;
import ghidra.pcode.exec.trace.*;
import ghidra.pcode.exec.trace.auxiliary.AuxTracePcodeEmulator;
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
import ghidra.pcode.struct.StructuredSleigh;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.*;
import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
public class ModelingScript extends GhidraScript {
// ----------------------
public static class JavaStdLibPcodeUseropLibrary<T> extends AnnotatedPcodeUseropLibrary<T> {
private final AddressSpace space;
private final Register regRSP;
private final Register regRAX;
private final Register regRDI;
private final Register regRSI;
public JavaStdLibPcodeUseropLibrary(SleighLanguage language) {
space = language.getDefaultSpace();
regRSP = language.getRegister("RSP");
regRAX = language.getRegister("RAX");
regRDI = language.getRegister("RDI");
regRSI = language.getRegister("RSI");
}
@PcodeUserop
public void __x86_64_RET(
@OpExecutor PcodeExecutor<T> executor,
@OpState PcodeExecutorState<T> state) {
PcodeArithmetic<T> arithmetic = state.getArithmetic();
T tRSP = state.getVar(regRSP, Reason.EXECUTE_READ);
long lRSP = arithmetic.toLong(tRSP, Purpose.OTHER);
T tReturn = state.getVar(space, lRSP, 8, true, Reason.EXECUTE_READ);
long lReturn = arithmetic.toLong(tReturn, Purpose.BRANCH);
state.setVar(regRSP, arithmetic.fromConst(lRSP + 8, 8));
((PcodeThreadExecutor<T>) executor).getThread()
.overrideCounter(space.getAddress(lReturn));
}
@PcodeUserop
public void __libc_strlen(@OpState PcodeExecutorState<T> state) {
PcodeArithmetic<T> arithmetic = state.getArithmetic();
T tStr = state.getVar(regRDI, Reason.EXECUTE_READ);
long lStr = arithmetic.toLong(tStr, Purpose.OTHER);
T tMaxlen = state.getVar(regRSI, Reason.EXECUTE_READ);
long lMaxlen = arithmetic.toLong(tMaxlen, Purpose.OTHER);
for (int i = 0; i < lMaxlen; i++) {
T tChar = state.getVar(space, lStr + i, 1, false, Reason.EXECUTE_READ);
if (arithmetic.toLong(tChar, Purpose.OTHER) == 0) {
state.setVar(regRAX, arithmetic.fromConst(Integer.toUnsignedLong(i), 8));
break;
}
}
}
}
// ----------------------
public static class SleighStdLibPcodeUseropLibrary<T> extends AnnotatedPcodeUseropLibrary<T> {
private static final String SRC_RET = """
RIP = *:8 RSP;
RSP = RSP + 8;
return [RIP];
""";
private static final String SRC_STRLEN = """
__result = 0;
<loop>
if (*:1 (str+__result) == 0 || __result >= maxlen) goto <exit>;
__result = __result + 1;
goto <loop>;
<exit>
""";
private final Register regRAX;
private final Register regRDI;
private final Register regRSI;
private final Varnode vnRAX;
private final Varnode vnRDI;
private final Varnode vnRSI;
private PcodeProgram progRet;
private PcodeProgram progStrlen;
public SleighStdLibPcodeUseropLibrary(SleighLanguage language) {
regRAX = language.getRegister("RAX");
regRDI = language.getRegister("RDI");
regRSI = language.getRegister("RSI");
vnRAX = new Varnode(regRAX.getAddress(), regRAX.getMinimumByteSize());
vnRDI = new Varnode(regRDI.getAddress(), regRDI.getMinimumByteSize());
vnRSI = new Varnode(regRSI.getAddress(), regRSI.getMinimumByteSize());
}
@PcodeUserop
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_RET, PcodeUseropLibrary.nil(), List.of());
}
progRet.execute(executor, library);
}
@PcodeUserop
public void __libc_strlen(@OpExecutor PcodeExecutor<T> executor,
@OpLibrary PcodeUseropLibrary<T> library) {
if (progStrlen == null) {
progStrlen = SleighProgramCompiler.compileUserop(executor.getLanguage(),
"__libc_strlen", List.of("__result", "str", "maxlen"),
SRC_STRLEN, PcodeUseropLibrary.nil(), List.of(vnRAX, vnRDI, vnRSI));
}
progStrlen.execute(executor, library);
}
}
// ----------------------
public static class StructuredStdLibPcodeUseropLibrary<T>
extends AnnotatedPcodeUseropLibrary<T> {
public StructuredStdLibPcodeUseropLibrary(CompilerSpec cs) {
new MyStructuredPart(cs).generate(ops);
}
public static class MyStructuredPart extends StructuredSleigh {
protected MyStructuredPart(CompilerSpec cs) {
super(cs);
}
@StructuredUserop
public void __x86_64_RET() {
Var RSP = lang("RSP", type("void **"));
Var RIP = lang("RIP", type("void *"));
RIP.set(RSP.deref());
RSP.addiTo(8);
_return(RIP);
}
@StructuredUserop
public void __libc_strlen() {
Var result = lang("RAX", type("long"));
Var str = lang("RDI", type("char *"));
Var maxlen = lang("RSI", type("long"));
_for(result.set(0), result.ltiu(maxlen).andb(str.index(result).deref().eq(0)),
result.inc(), () -> {
});
}
}
}
// ----------------------
interface Expr {
}
interface UnExpr extends Expr {
Expr u();
}
interface BinExpr extends Expr {
Expr l();
Expr r();
}
record LitExpr(BigInteger val, int size) implements Expr {
}
record VarExpr(Varnode vn) implements Expr {
public VarExpr(AddressSpace space, long offset, int size) {
this(space.getAddress(offset), size);
}
public VarExpr(Address address, int size) {
this(new Varnode(address, size));
}
}
record InvExpr(Expr u) implements UnExpr {
}
record AddExpr(Expr l, Expr r) implements BinExpr {
}
record SubExpr(Expr l, Expr r) implements BinExpr {
}
// ----------------------
public enum ExprPcodeArithmetic implements PcodeArithmetic<Expr> {
BE(Endian.BIG), LE(Endian.LITTLE);
public static ExprPcodeArithmetic forEndian(Endian endian) {
return endian.isBigEndian() ? BE : LE;
}
public static ExprPcodeArithmetic forLanguage(Language language) {
return language.isBigEndian() ? BE : LE;
}
private final Endian endian;
private ExprPcodeArithmetic(Endian endian) {
this.endian = endian;
}
@Override
public Endian getEndian() {
return endian;
}
@Override
public Expr unaryOp(int opcode, int sizeout, int sizein1, Expr in1) {
return switch (opcode) {
case PcodeOp.INT_NEGATE -> new InvExpr(in1);
default -> throw new UnsupportedOperationException(PcodeOp.getMnemonic(opcode));
};
}
@Override
public Expr binaryOp(int opcode, int sizeout, int sizein1, Expr in1, int sizein2,
Expr in2) {
return switch (opcode) {
case PcodeOp.INT_ADD -> new AddExpr(in1, in2);
case PcodeOp.INT_SUB -> new SubExpr(in1, in2);
default -> throw new UnsupportedOperationException(PcodeOp.getMnemonic(opcode));
};
}
@Override
public Expr modBeforeStore(int sizeout, int sizeinAddress, Expr inAddress, int sizeinValue,
Expr inValue) {
return inValue;
}
@Override
public Expr modAfterLoad(int sizeout, int sizeinAddress, Expr inAddress, int sizeinValue,
Expr inValue) {
return inValue;
}
@Override
public Expr fromConst(byte[] value) {
if (endian.isBigEndian()) {
return new LitExpr(new BigInteger(1, value), value.length);
}
byte[] reversed = Arrays.copyOf(value, value.length);
ArrayUtils.reverse(reversed);
return new LitExpr(new BigInteger(1, reversed), reversed.length);
}
@Override
public Expr fromConst(BigInteger value, int size, boolean isContextreg) {
return new LitExpr(value, size);
}
@Override
public Expr fromConst(long value, int size) {
return fromConst(BigInteger.valueOf(value), size);
}
@Override
public byte[] toConcrete(Expr value, Purpose purpose) {
throw new UnsupportedOperationException();
}
@Override
public long sizeOf(Expr value) {
throw new UnsupportedOperationException();
}
}
// ----------------------
public static class ExprSpace {
protected final NavigableMap<Long, Expr> map;
protected final AddressSpace space;
protected ExprSpace(AddressSpace space, NavigableMap<Long, Expr> map) {
this.space = space;
this.map = map;
}
public ExprSpace(AddressSpace space) {
this(space, new TreeMap<>());
}
public void clear() {
map.clear();
}
public void set(long offset, Expr val) {
// TODO: Handle overlaps / offcut gets and sets
map.put(offset, val);
}
public Expr get(long offset, int size) {
// TODO: Handle overlaps / offcut gets and sets
Expr expr = map.get(offset);
return expr != null ? expr : whenNull(offset, size);
}
protected Expr whenNull(long offset, int size) {
return new VarExpr(space, offset, size);
}
}
public static abstract class AbstractBytesExprPcodeExecutorStatePiece<S extends ExprSpace>
extends
AbstractLongOffsetPcodeExecutorStatePiece<byte[], Expr, S> {
protected final AbstractSpaceMap<S> spaceMap = newSpaceMap();
public AbstractBytesExprPcodeExecutorStatePiece(Language language) {
super(language, BytesPcodeArithmetic.forLanguage(language),
ExprPcodeArithmetic.forLanguage(language));
}
protected abstract AbstractSpaceMap<S> newSpaceMap();
@Override
public MemBuffer getConcreteBuffer(Address address, Purpose purpose) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
for (S space : spaceMap.values()) {
space.clear();
}
}
@Override
protected S getForSpace(AddressSpace space, boolean toWrite) {
return spaceMap.getForSpace(space, toWrite);
}
@Override
protected void setInSpace(ExprSpace space, long offset, int size, Expr val) {
space.set(offset, val);
}
@Override
protected Expr getFromSpace(S space, long offset, int size, Reason reason) {
return space.get(offset, size);
}
@Override
protected Map<Register, Expr> getRegisterValuesFromSpace(S s, List<Register> registers) {
throw new UnsupportedOperationException();
}
}
public static class ExprPcodeExecutorStatePiece
extends AbstractBytesExprPcodeExecutorStatePiece<ExprSpace> {
public ExprPcodeExecutorStatePiece(Language language) {
super(language);
}
@Override
protected AbstractSpaceMap<ExprSpace> newSpaceMap() {
return new SimpleSpaceMap<ExprSpace>() {
@Override
protected ExprSpace newSpace(AddressSpace space) {
return new ExprSpace(space);
}
};
}
}
public static class BytesExprPcodeExecutorState extends PairedPcodeExecutorState<byte[], Expr> {
public BytesExprPcodeExecutorState(PcodeExecutorStatePiece<byte[], byte[]> concrete) {
super(new PairedPcodeExecutorStatePiece<>(concrete,
new ExprPcodeExecutorStatePiece(concrete.getLanguage())));
}
}
// ----------------------
public enum BytesExprEmulatorPartsFactory implements AuxEmulatorPartsFactory<Expr> {
INSTANCE;
@Override
public PcodeArithmetic<Expr> getArithmetic(Language language) {
return ExprPcodeArithmetic.forLanguage(language);
}
@Override
public PcodeUseropLibrary<Pair<byte[], Expr>> createSharedUseropLibrary(
AuxPcodeEmulator<Expr> emulator) {
return PcodeUseropLibrary.nil();
}
@Override
public PcodeUseropLibrary<Pair<byte[], Expr>> createLocalUseropStub(
AuxPcodeEmulator<Expr> emulator) {
return PcodeUseropLibrary.nil();
}
@Override
public PcodeUseropLibrary<Pair<byte[], Expr>> createLocalUseropLibrary(
AuxPcodeEmulator<Expr> emulator, PcodeThread<Pair<byte[], Expr>> thread) {
return PcodeUseropLibrary.nil();
}
@Override
public PcodeExecutorState<Pair<byte[], Expr>> createSharedState(
AuxPcodeEmulator<Expr> emulator, BytesPcodeExecutorStatePiece concrete) {
return new BytesExprPcodeExecutorState(concrete);
}
@Override
public PcodeExecutorState<Pair<byte[], Expr>> createLocalState(
AuxPcodeEmulator<Expr> emulator, PcodeThread<Pair<byte[], Expr>> thread,
BytesPcodeExecutorStatePiece concrete) {
return new BytesExprPcodeExecutorState(concrete);
}
}
public class BytesExprPcodeEmulator extends AuxPcodeEmulator<Expr> {
public BytesExprPcodeEmulator(Language language) {
super(language);
}
@Override
protected AuxEmulatorPartsFactory<Expr> getPartsFactory() {
return BytesExprEmulatorPartsFactory.INSTANCE;
}
}
// ----------------------
public static class ExprTraceSpace extends ExprSpace {
protected final PcodeTracePropertyAccess<String> property;
public ExprTraceSpace(AddressSpace space, PcodeTracePropertyAccess<String> property) {
super(space);
this.property = property;
}
@Override
protected Expr whenNull(long offset, int size) {
String string = property.get(space.getAddress(offset));
return deserialize(string);
}
public void writeDown(PcodeTracePropertyAccess<String> into) {
if (space.isUniqueSpace()) {
return;
}
for (Entry<Long, Expr> entry : map.entrySet()) {
// TODO: Ignore and/or clear non-entries
into.put(space.getAddress(entry.getKey()), serialize(entry.getValue()));
}
}
protected String serialize(Expr expr) {
return Unfinished.TODO();
}
protected Expr deserialize(String string) {
return Unfinished.TODO();
}
}
public static class BytesExprTracePcodeExecutorStatePiece
extends AbstractBytesExprPcodeExecutorStatePiece<ExprTraceSpace>
implements TracePcodeExecutorStatePiece<byte[], Expr> {
public static final String NAME = "Taint";
protected final PcodeTraceDataAccess data;
protected final PcodeTracePropertyAccess<String> property;
public BytesExprTracePcodeExecutorStatePiece(PcodeTraceDataAccess data) {
super(data.getLanguage());
this.data = data;
this.property = data.getPropertyAccess(NAME, String.class);
}
@Override
public PcodeTraceDataAccess getData() {
return data;
}
@Override
protected AbstractSpaceMap<ExprTraceSpace> newSpaceMap() {
return new CacheingSpaceMap<PcodeTracePropertyAccess<String>, ExprTraceSpace>() {
@Override
protected PcodeTracePropertyAccess<String> getBacking(AddressSpace space) {
return property;
}
@Override
protected ExprTraceSpace newSpace(AddressSpace space,
PcodeTracePropertyAccess<String> backing) {
return new ExprTraceSpace(space, property);
}
};
}
@Override
public BytesExprTracePcodeExecutorStatePiece fork() {
throw new UnsupportedOperationException();
}
@Override
public void writeDown(PcodeTraceDataAccess into) {
PcodeTracePropertyAccess<String> property = into.getPropertyAccess(NAME, String.class);
for (ExprTraceSpace space : spaceMap.values()) {
space.writeDown(property);
}
}
}
public static class BytesExprTracePcodeExecutorState
extends PairedTracePcodeExecutorState<byte[], Expr> {
public BytesExprTracePcodeExecutorState(
TracePcodeExecutorStatePiece<byte[], byte[]> concrete) {
super(new PairedTracePcodeExecutorStatePiece<>(concrete,
new BytesExprTracePcodeExecutorStatePiece(concrete.getData())));
}
}
enum BytesExprDebuggerEmulatorPartsFactory implements AuxDebuggerEmulatorPartsFactory<Expr> {
INSTANCE;
@Override
public PcodeArithmetic<Expr> getArithmetic(Language language) {
return ExprPcodeArithmetic.forLanguage(language);
}
@Override
public PcodeUseropLibrary<Pair<byte[], Expr>> createSharedUseropLibrary(
AuxPcodeEmulator<Expr> emulator) {
return PcodeUseropLibrary.nil();
}
@Override
public PcodeUseropLibrary<Pair<byte[], Expr>> createLocalUseropStub(
AuxPcodeEmulator<Expr> emulator) {
return PcodeUseropLibrary.nil();
}
@Override
public PcodeUseropLibrary<Pair<byte[], Expr>> createLocalUseropLibrary(
AuxPcodeEmulator<Expr> emulator,
PcodeThread<Pair<byte[], Expr>> thread) {
return PcodeUseropLibrary.nil();
}
@Override
public PcodeExecutorState<Pair<byte[], Expr>> createSharedState(
AuxPcodeEmulator<Expr> emulator,
BytesPcodeExecutorStatePiece concrete) {
return new BytesExprPcodeExecutorState(concrete);
}
@Override
public PcodeExecutorState<Pair<byte[], Expr>> createLocalState(
AuxPcodeEmulator<Expr> emulator,
PcodeThread<Pair<byte[], Expr>> thread,
BytesPcodeExecutorStatePiece concrete) {
return new BytesExprPcodeExecutorState(concrete);
}
@Override
public TracePcodeExecutorState<Pair<byte[], Expr>> createTraceSharedState(
AuxTracePcodeEmulator<Expr> emulator,
BytesTracePcodeExecutorStatePiece concrete) {
return new BytesExprTracePcodeExecutorState(concrete);
}
@Override
public TracePcodeExecutorState<Pair<byte[], Expr>> createTraceLocalState(
AuxTracePcodeEmulator<Expr> emulator,
PcodeThread<Pair<byte[], Expr>> thread,
BytesTracePcodeExecutorStatePiece concrete) {
return new BytesExprTracePcodeExecutorState(concrete);
}
@Override
public TracePcodeExecutorState<Pair<byte[], Expr>> createDebuggerSharedState(
AuxDebuggerPcodeEmulator<Expr> emulator,
RWTargetMemoryPcodeExecutorStatePiece concrete) {
return new BytesExprTracePcodeExecutorState(concrete);
}
@Override
public TracePcodeExecutorState<Pair<byte[], Expr>> createDebuggerLocalState(
AuxDebuggerPcodeEmulator<Expr> emulator,
PcodeThread<Pair<byte[], Expr>> thread,
RWTargetRegistersPcodeExecutorStatePiece concrete) {
return new BytesExprTracePcodeExecutorState(concrete);
}
}
public static class BytesExprDebuggerPcodeEmulator extends AuxDebuggerPcodeEmulator<Expr> {
public BytesExprDebuggerPcodeEmulator(PcodeDebuggerAccess access) {
super(access);
}
@Override
protected AuxDebuggerEmulatorPartsFactory<Expr> getPartsFactory() {
return BytesExprDebuggerEmulatorPartsFactory.INSTANCE;
}
}
public static class BytesExprDebuggerPcodeEmulatorFactory
implements DebuggerPcodeEmulatorFactory {
@Override
public String getTitle() {
return "Expr";
}
@Override
public DebuggerPcodeMachine<?> create(PcodeDebuggerAccess access) {
return new BytesExprDebuggerPcodeEmulator(access);
}
}
// ----------------------
@Override
protected void run() throws Exception {
BytesExprPcodeEmulator emu = new BytesExprPcodeEmulator(currentProgram.getLanguage());
// TODO: Initialize the machine
PcodeExecutorState<Pair<byte[], Expr>> state = emu.getSharedState();
state.setVar(currentAddress, 4, true,
Pair.of(new byte[] { 1, 2, 3, 4 }, new VarExpr(currentAddress, 4)));
PcodeThread<Pair<byte[], Expr>> thread = emu.newThread();
// TODO: Initialize the thread
while (true) {
monitor.checkCancelled();
thread.stepInstruction(100);
}
}
}

View file

@ -0,0 +1,138 @@
/* ###
* 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.
*/
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import ghidra.app.script.GhidraScript;
import ghidra.app.services.LogicalBreakpoint;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
import ghidra.debug.flatapi.FlatDebuggerAPI;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.Trace;
public class ZeroTimerScript extends GhidraScript implements FlatDebuggerAPI {
@Override
protected void run() throws Exception {
// --------------------------------
Trace trace = getCurrentTrace();
if (trace == null) {
throw new AssertionError("There is no active session");
}
if (!"termmines".equals(currentProgram.getName())) {
throw new AssertionError("The current program must be termmines");
}
if (getExecutionState(trace).isRunning()) {
monitor.setMessage("Interrupting target and waiting for STOPPED");
interrupt();
waitForBreak(3, TimeUnit.SECONDS);
}
flushAsyncPipelines(trace);
if (!getControlService().getCurrentMode(trace).canEdit(getCurrentDebuggerCoordinates())) {
throw new AssertionError("Current control mode is read-only");
}
// --------------------------------
List<Symbol> timerSyms = getSymbols("timer", null);
if (timerSyms.isEmpty()) {
throw new AssertionError("Symbol 'timer' is required");
}
List<Function> winFuncs = getGlobalFunctions("print_win");
if (winFuncs.isEmpty()) {
throw new AssertionError("Function 'print_win' is required");
}
List<Symbol> resetSyms = getSymbols("reset_timer", winFuncs.get(0));
if (resetSyms.isEmpty()) {
throw new AssertionError("Symbol 'reset_timer' is required");
}
Address timerDyn = translateStaticToDynamic(timerSyms.get(0).getAddress());
if (timerDyn == null) {
throw new AssertionError("Symbol 'timer' is not mapped to target");
}
Address resetDyn = translateStaticToDynamic(resetSyms.get(0).getAddress());
if (resetDyn == null) {
throw new AssertionError("Symbol 'reset_timer' is not mapped to target");
}
// --------------------------------
ProgramLocation breakLoc =
new ProgramLocation(currentProgram, resetSyms.get(0).getAddress());
Set<LogicalBreakpoint> breaks = breakpointsEnable(breakLoc);
if (breaks == null || breaks.isEmpty()) {
breakpointSetSoftwareExecute(breakLoc, "reset timer");
}
// --------------------------------
while (true) {
monitor.checkCanceled();
TargetExecutionState execState = getExecutionState(trace);
switch (execState) {
case STOPPED:
resume();
break;
case TERMINATED:
case INACTIVE:
throw new AssertionError("Target terminated");
case ALIVE:
println(
"I don't know whether or not the target is running. Please make it RUNNING.");
break;
case RUNNING:
/**
* Probably timed out waiting for break. That's fine. Give the player time to
* win.
*/
break;
default:
throw new AssertionError("Unrecognized state: " + execState);
}
try {
monitor.setMessage("Waiting for player to win");
waitForBreak(1, TimeUnit.SECONDS);
}
catch (TimeoutException e) {
// Give the player time to win.
continue;
}
flushAsyncPipelines(trace);
Address pc = getProgramCounter();
println("STOPPED at pc = " + pc);
if (resetDyn.equals(pc)) {
break;
}
}
// --------------------------------
int time = readRegister("ECX").getUnsignedValue().intValue();
if (!writeMemory(timerDyn,
ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(time).array())) {
throw new AssertionError("Could not write over timer. Does control mode allow edits?");
}
resume();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,6 @@
-- https://stackoverflow.com/questions/40993488/convert-markdown-links-to-html-with-pandoc
function Link(el)
el.target = string.gsub(el.target, "%.md", ".html")
return el
end

View file

@ -0,0 +1,12 @@
<header id="nav"><a
class="beginner" href="A1-GettingStarted.html">Getting Started</a><a
class="beginner" href="A2-UITour.html">UI Tour</a><a
class="beginner" href="A3-Breakpoints.html">Breakpoints</a><a
class="beginner" href="A4-MachineState.html">Machine State</a><a
class="beginner" href="A5-Navigation.html">Navigation</a><a
class="beginner" href="A6-MemoryMap.html">Memory Map</a><a
class="advanced" href="B1-RemoteTargets.html">Remote Targets</a><a
class="advanced" href="B2-Emulation.html">Emulation</a><a
class="advanced" href="B3-Scripting.html">Scripting</a><a
class="advanced" href="B4-Modeling.html">Modeling</a>
</header>

Some files were not shown because too many files have changed in this diff Show more