GP-2641: Add training course materials for the Debugger
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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*/) {
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -48,6 +48,7 @@ dependencies {
|
|||
if (projectPath.startsWith(ghidraPath) && (
|
||||
projectPath.contains("/Framework/") ||
|
||||
projectPath.contains("/Features/") ||
|
||||
projectPath.contains("/Debug/") ||
|
||||
projectPath.contains("/Processors/"))) {
|
||||
|
||||
api p
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidraclass.debugger.screenshot;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import db.Transaction;
|
||||
import ghidra.app.util.importer.AutoImporter;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.app.util.opinion.LoadResults;
|
||||
import ghidra.base.project.GhidraProject;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.task.ConsoleTaskMonitor;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class TutorialDebuggerMaintenance extends AbstractGhidraHeadedIntegrationTest {
|
||||
public static final TaskMonitor CONSOLE = new ConsoleTaskMonitor();
|
||||
|
||||
public PluginTool tool;
|
||||
public TestEnv env;
|
||||
public Program program;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Throwable {
|
||||
env = new TestEnv();
|
||||
|
||||
tool = env.launchDefaultTool();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Throwable {
|
||||
if (program != null) {
|
||||
program.release(this);
|
||||
program = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecreateTermminesGzf() throws Throwable {
|
||||
File termmines = Application.getModuleDataFile("TestResources", "termmines").getFile(false);
|
||||
LoadResults<Program> results = AutoImporter.importByUsingBestGuess(termmines,
|
||||
env.getProject(), "/", this, new MessageLog(), CONSOLE);
|
||||
program = results.getPrimaryDomainObject();
|
||||
try (Transaction tx = program.openTransaction("Analyze")) {
|
||||
program.setExecutablePath("/tmp/termmines");
|
||||
GhidraProject.analyze(program);
|
||||
}
|
||||
File dest = new File(termmines.getParentFile(), "termmines.gzf");
|
||||
dest.delete();
|
||||
program.saveToPackedFile(dest, CONSOLE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,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);
|
||||
}
|
||||
}
|
478
GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.html
Normal 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">I’m 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. Don’t 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>I’m 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 system’s 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 it’s 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 doesn’t 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 Debugger’s 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
|
||||
Yama’s <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">'exec ./termmines'</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>
|
245
GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.md
Normal 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  in the Tool Chest.
|
||||
1. This will bring up the specimen in the Debugger tool.
|
||||
(If you are prompted to analyze, choose Yes.)
|
||||
|
||||

|
||||
|
||||
1. In the Debugger tool, click the dropdown ▾ for the debug  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.
|
||||
|
||||

|
||||
|
||||
## 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 → 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  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 → Debug termmines → 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 — 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  button.
|
||||
If that doesn't work or is unsatisfactory, try the remedies under the previous heading — 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 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 → Debug termmmines → 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 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.
|
325
GhidraDocs/GhidraClass/Debugger/A2-UITour.html
Normal 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 don’t 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 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 <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 don’t 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>
|
193
GhidraDocs/GhidraClass/Debugger/A2-UITour.md
Normal 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`:
|
||||
|
||||

|
||||
|
||||
### 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**:
|
||||
To be covered in a later module.
|
||||
This will load the current program (from the Static Listing) into the emulator.
|
||||
*  **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.
|
||||
*  **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**:
|
||||
Resume execution.
|
||||
This is equivalent to `continue` in GDB.
|
||||
*  **Interrupt**:
|
||||
Interrupt, suspend, pause, break, etc.
|
||||
This is equivalent to **Ctrl-C** or `interrupt` in GDB.
|
||||
*  **Kill**:
|
||||
Kill, terminate, etc.
|
||||
This is equivalent to `kill` in GDB.
|
||||
*  **Disconnect**:
|
||||
Disconnect from the back-end debugger.
|
||||
Typically, this will also end the session.
|
||||
It is equivalent to `quit` in GDB.
|
||||
*  **Step Into**,  **Step Over**,  **Step Out**,  **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 — 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 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 a sufficient number of times, you should end up in a subroutine.
|
||||
You can click  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 until you reach another `CALL` instruction.
|
||||
Notice that when you click  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 `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 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.
|
490
GhidraDocs/GhidraClass/Debugger/A3-Breakpoints.html
Normal 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
|
||||
breakpoint’s 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
|
||||
target’s 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 breakpoint’s 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 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.</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"><</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">)&</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"><</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">)&</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 you’re 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">'exec bash -c "echo $BASHPID; kill -SIGSTOP $BASHPID; exec ./termmines"'</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>
|
251
GhidraDocs/GhidraClass/Debugger/A3-Breakpoints.md
Normal 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.
|
||||
|
||||

|
||||
|
||||
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, press **K** on the keyboard, or double-click the margin.
|
||||
1. From the Objects window click the  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  and disabled  breakpoints.
|
||||
If the target is terminated (or not launched yet), you may also see ineffective  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:
|
||||
|
||||

|
||||
|
||||
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  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:
|
||||
|
||||

|
||||
|
||||
1. Click the import button — 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`.
|
||||
|
||||

|
||||
|
||||
### Capturing the Random Seed
|
||||
|
||||
We can now allow `termmines` to execute, expecting it to hit the `srand` breakpoint first.
|
||||
Click  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.
|
||||
|
||||

|
||||
|
||||
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 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 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.
|
||||
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.
|
658
GhidraDocs/GhidraClass/Debugger/A4-MachineState.html
Normal 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 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.</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 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 <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 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.</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 Ghidra’s processor specification. This is mostly just to
|
||||
make the default sorting deterministic.</li>
|
||||
<li>The <strong>Name</strong> 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
|
||||
<code>rflags</code> GDB calls <code>eflags</code>.</li>
|
||||
<li>The <strong>Value</strong> 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.</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 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.</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 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.</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 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 <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 Ghidra’s
|
||||
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
|
||||
label’s 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 << RCX</code></li>
|
||||
<li><strong>Unsigned Right Shift</strong>:
|
||||
<code>RAX >> RCX</code></li>
|
||||
<li><strong>Signed Right Shift</strong>
|
||||
<code>RAX s>> 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 < RCX</code> or <code>RAX > RCX</code> or
|
||||
<code>RAX <= RCX</code> or <code>RAX >= RCX</code></li>
|
||||
<li><strong>Signed Integer Comparison</strong>:
|
||||
<code>RAX s< 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< RCX</code> etc.</li>
|
||||
<li><strong>Bitwise And</strong>: <code>RAX & 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 && 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
|
||||
register’s value in the function’s 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
|
||||
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.</p>
|
||||
</section>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
400
GhidraDocs/GhidraClass/Debugger/A4-MachineState.md
Normal 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 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.
|
||||
|
||||

|
||||
|
||||
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  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 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.
|
||||
Your Dynamic Listing should follow the stack pointer.
|
||||
In the menus, select **Debugger → Analysis → Unwind from frame 0** or press **U**.
|
||||
|
||||

|
||||
|
||||
**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 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
|
||||
|
||||

|
||||
|
||||
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 → Byte Viewer → 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 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 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 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 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` — 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` — Equivalent to `EAX`
|
||||
* **Truncation**: `AL + RBX(4)` — AL added to the the 5th byte of RBX
|
||||
* **Bit Extraction**: `RAX[7,8]` — 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)` — Context required to infer the float size
|
||||
* **Convert Float to Integer**: `RAX + trunc(MM0)` — Context required to infer the integer size
|
||||
* **Convert Float Size**: `MM0 + float2float(MM0_Da)` — 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.
|
376
GhidraDocs/GhidraClass/Debugger/A5-Navigation.html
Normal 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 specification’s “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 debugger’s thread id, the target platform’s
|
||||
system thread id, and/or the back-end debugger’s 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 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.</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 target’s 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. 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.</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
|
||||
snapshot’s 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>
|
220
GhidraDocs/GhidraClass/Debugger/A5-Navigation.md
Normal 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`.
|
||||
|
||||

|
||||
|
||||
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 button will usually allow all threads to execute; whereas the  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.
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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, expecting it to break at `srand`.
|
||||
1. Capture another full snapshot using Select All and Refresh.
|
||||
1. Click the  Compare button in the Dynamic Listing.
|
||||
1. In the dialog, select the first snapshot you took.
|
||||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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.
|
334
GhidraDocs/GhidraClass/Debugger/A6-MemoryMap.html
Normal 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
|
||||
Listing’s 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 trace’s 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 target’s 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, let’s 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>
|
190
GhidraDocs/GhidraClass/Debugger/A6-MemoryMap.md
Normal 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
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||

|
||||
|
||||
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 → Debugger → Static Mappings**.
|
||||
1. From the Modules window, click Map Manually in the local toolbar.
|
||||
|
||||

|
||||
|
||||
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 → 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 → 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 → Copy Into New Program**.
|
||||
|
||||

|
||||
|
||||
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.
|
352
GhidraDocs/GhidraClass/Debugger/B1-RemoteTargets.html
Normal 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
|
||||
Friend’s <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 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.</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 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:</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 user’s
|
||||
local environment, while <code>gdbserver</code> 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:</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 user’s
|
||||
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 Ghidra’s 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 user’s
|
||||
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 Friend’s <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>
|
195
GhidraDocs/GhidraClass/Debugger/B1-RemoteTargets.md
Normal 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."
|
||||
|
||||

|
||||
|
||||
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.
|
823
GhidraDocs/GhidraClass/Debugger/B2-Emulation.html
Normal 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 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 <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>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 <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 emulator’s
|
||||
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 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.</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 Debugger’s “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
|
||||
emulator’s 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 snapshot’s 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 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 <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 image’s 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 thread’s 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
|
||||
function’s 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 argument’s 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 watch’s 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><<span class="at">loop</span>></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 >= RSI) <span class="cf">goto</span> <<span class="at">exit</span>>;</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> <<span class="at">loop</span>>;</span>
|
||||
<span id="cb1-6"><a href="#cb1-6"></a><<span class="at">exit</span>></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><EXTERNAL>::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><<span class="at">loop</span>></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 >= RSI) <span class="cf">goto</span> <<span class="at">exit</span>>;</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> <<span class="at">loop</span>>;</span>
|
||||
<span id="cb2-6"><a href="#cb2-6"></a><<span class="at">exit</span>></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
|
||||
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.</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 instruction’s
|
||||
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 variable’s name and
|
||||
size in bytes.</li>
|
||||
<li>The <strong>Bytes</strong> column gives the variable’s value in
|
||||
bytes.</li>
|
||||
<li>The <strong>Value</strong> column gives the variable’s value as an
|
||||
integer, an interpretation of the bytes in the machine’s 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 variable’s value
|
||||
according to the assigned type.</li>
|
||||
</ul>
|
||||
<p>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.
|
||||
<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>
|
493
GhidraDocs/GhidraClass/Debugger/B2-Emulation.md
Normal 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 to step the emulator forward.
|
||||
1. Click  Skip Over and  Step Back to experiment with different execution paths.
|
||||
|
||||
About those two new actions:
|
||||
|
||||
*  **Skip Over**:
|
||||
Step the current thread by skipping one instruction.
|
||||
*  **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* → *Snapshot* \[ `:` \[ *Step* ( `;` *Step* ) \* \] \[ `.` *Step* ( `;` *Step* ) \* \] \]
|
||||
* *Step* → [ `t` *Id* `-` ] ( *Tick* | *Skip* | *Patch* )
|
||||
* *Tick* → *Count*
|
||||
* *Skip* → `s` *Count*
|
||||
* *Patch* → `{` *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` — The first snapshot in the trace.
|
||||
* `3` — Snapshot number 3.
|
||||
* `3:10` — Emulate 10 machine instructions on the event thread, starting at snapshot 3.
|
||||
* `3:t1-10` — Same as above, but on the second thread rather than the event thread.
|
||||
* `3:10;t1-10` — Start at snapshot 3. Step the event thread 10 instructions. Step the second thread 10 instructions.
|
||||
* `3:10.4` — Start at snapshot 3. Step the event thread 10 instructions then 4 p-code ops.
|
||||
* `3:{RAX=0x1234};10` — 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 → 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:
|
||||
|
||||

|
||||
|
||||
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 to let the emulator run until it crashes.
|
||||
It should crash pretty quickly and without much ceremony:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
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.
|
||||
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` — to confirm the stack pointer is far from `argv`.
|
||||
* `RDI` — the location of `param_1`, i.e., `argc`.
|
||||
* `RSI` — 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.
|
||||
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)` — the address of the first argument, i.e., `argv[0]`.
|
||||
* `*:30 (*:8 (RSI + 0))` with type `TerminatedCString` — at most 30 characters of the first argument.
|
||||
* `*:8 (RSI + 8)` — `argv[1]`
|
||||
* `*:30 (*:8 (RSI + 8))` with type `TerminatedCString` — contents of `argv[1]`
|
||||
* `*:8 (RSI + 16)` — `argv[2]`
|
||||
* `*:30 (*:8 (RSI + 16))` with type `TerminatedCString` — contents of `argv[2]`
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
The Dynamic listing should look something like this:
|
||||
|
||||

|
||||
|
||||
**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, 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, then use the Registers or Watches pane to patch `RAX`.
|
||||
Then press  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 → Configure Emulator → Invalidate Emulator Cache**.
|
||||
1. Click  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 → 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:
|
||||
|
||||

|
||||
|
||||
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.
|
547
GhidraDocs/GhidraClass/Debugger/B3-Scripting.html
Normal 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 script’s 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 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
|
||||
<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">"There is no active session"</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">"termmines"</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">"The current program must be termmines"</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"><</span>Symbol<span class="op">></span> widthSyms <span class="op">=</span> <span class="fu">getSymbols</span><span class="op">(</span><span class="st">"width"</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">"Symbol 'width' is required"</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"><</span>Symbol<span class="op">></span> heightSyms <span class="op">=</span> <span class="fu">getSymbols</span><span class="op">(</span><span class="st">"height"</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">"Symbol 'height' is required"</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"><</span>Symbol<span class="op">></span> cellsSyms <span class="op">=</span> <span class="fu">getSymbols</span><span class="op">(</span><span class="st">"cells"</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">"Symbol 'cells' is required"</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">"Symbol 'width' is not mapped to target"</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">"Symbol 'height' is not mapped to target"</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">"Symbol 'cells' is not mapped to target"</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"><</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"><</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">&</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">"Mine at (%d,%d)"</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 target’s 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<Void></code>. Java’s completable futures
|
||||
allow you to register callbacks and/or chain additional futures onto
|
||||
them.</p>
|
||||
<p>However, Ghidra’s 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 API’s <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 Eclipse’s 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 target’s 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 function’s
|
||||
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 GDB’s
|
||||
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">"There is no active session"</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">"termmines"</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">"The current program must be termmines"</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">"Interrupting target and waiting for STOPPED"</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">"Current control mode is read-only"</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"><</span>Symbol<span class="op">></span> timerSyms <span class="op">=</span> <span class="fu">getSymbols</span><span class="op">(</span><span class="st">"timer"</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">"Symbol 'timer' is required"</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"><</span>Function<span class="op">></span> winFuncs <span class="op">=</span> <span class="fu">getGlobalFunctions</span><span class="op">(</span><span class="st">"print_win"</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">"Function 'print_win' is required"</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"><</span>Symbol<span class="op">></span> resetSyms <span class="op">=</span> <span class="fu">getSymbols</span><span class="op">(</span><span class="st">"reset_timer"</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">"Symbol 'reset_timer' is required"</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">"Symbol 'timer' is not mapped to target"</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">"Symbol 'reset_timer' is not mapped to target"</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"><</span>LogicalBreakpoint<span class="op">></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">"reset timer"</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">"Target terminated"</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">"I don't know whether or not the target is running. Please make it RUNNING."</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">'</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">"Unrecognized state: "</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">"Waiting for player to win"</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">"STOPPED at pc = "</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">"ECX"</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">"Could not write over timer. Does control mode allow edits?"</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>
|
363
GhidraDocs/GhidraClass/Debugger/B3-Scripting.md
Normal 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 — 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.
|
1202
GhidraDocs/GhidraClass/Debugger/B4-Modeling.html
Normal file
936
GhidraDocs/GhidraClass/Debugger/B4-Modeling.md
Normal 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 → 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.
|
22
GhidraDocs/GhidraClass/Debugger/Makefile
Normal 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
|
24
GhidraDocs/GhidraClass/Debugger/README.md
Normal 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)
|
43
GhidraDocs/GhidraClass/Debugger/gdb_syntax.xml
Normal 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="#'"" weakDeliminator="-" />
|
||||
</general>
|
||||
</language>
|
||||
<!-- kate: replace-tabs on; tab-width 2; indent-width 2; -->
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 207 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 190 KiB |
After Width: | Height: | Size: 128 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 22 KiB |
BIN
GhidraDocs/GhidraClass/Debugger/images/RemoteTargets_Gadp.png
Normal file
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 14 KiB |
BIN
GhidraDocs/GhidraClass/Debugger/images/add.png
Normal file
After Width: | Height: | Size: 733 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/autoread.png
Normal file
After Width: | Height: | Size: 942 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/breakpoint-disable.png
Normal file
After Width: | Height: | Size: 684 B |
After Width: | Height: | Size: 513 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/breakpoint-enable.png
Normal file
After Width: | Height: | Size: 561 B |
After Width: | Height: | Size: 305 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/camera-photo.png
Normal file
After Width: | Height: | Size: 864 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/connect.png
Normal file
After Width: | Height: | Size: 740 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/debugger.png
Normal file
After Width: | Height: | Size: 730 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/disconnect.png
Normal file
After Width: | Height: | Size: 556 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/interrupt.png
Normal file
After Width: | Height: | Size: 211 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/kill.png
Normal file
After Width: | Height: | Size: 210 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/process.png
Normal file
After Width: | Height: | Size: 649 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/record.png
Normal file
After Width: | Height: | Size: 483 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/register-marker.png
Normal file
After Width: | Height: | Size: 315 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/resume.png
Normal file
After Width: | Height: | Size: 396 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/select-registers.png
Normal file
After Width: | Height: | Size: 363 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/skipover.png
Normal file
After Width: | Height: | Size: 497 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/stepback.png
Normal file
After Width: | Height: | Size: 428 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/stepinto.png
Normal file
After Width: | Height: | Size: 404 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/steplast.png
Normal file
After Width: | Height: | Size: 410 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/stepout.png
Normal file
After Width: | Height: | Size: 413 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/stepover.png
Normal file
After Width: | Height: | Size: 428 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/table_relationship.png
Normal file
After Width: | Height: | Size: 663 B |
BIN
GhidraDocs/GhidraClass/Debugger/images/view-refresh.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
6
GhidraDocs/GhidraClass/Debugger/links-to-html.lua
Normal 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
|
||||
|
12
GhidraDocs/GhidraClass/Debugger/navhead.htm
Normal 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>
|