diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java index 8a3088778d..9f0fcc0fc4 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePlugin.java @@ -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; diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerConnectDialog.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerConnectDialog.java index 6ecaf399e1..cbdc0c2e1a 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerConnectDialog.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/model/DebuggerConnectDialog.java @@ -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 reset( DebuggerModelFactory factory, Program program) { if (factory != null) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java index 2c2c3738a6..3d227507a4 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/tracemgr/DebuggerTraceManagerServicePlugin.java @@ -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 toOpen = Arrays.asList(data) - .stream() - .filter(f -> Trace.class.isAssignableFrom(f.getDomainObjectClass())) + List toOpen = Stream.of(data) + .filter(f -> f != null && Trace.class.isAssignableFrom(f.getDomainObjectClass())) .collect(Collectors.toList()); Collection openTraces = openTraces(toOpen); diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java index 52e46cf30f..4ecc3296fe 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/debug/flatapi/FlatDebuggerAPI.java @@ -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()); diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java index 6511684383..1bcd8b883b 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/gui/AbstractGhidraHeadedDebuggerGUITest.java @@ -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) { diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/model/TestDebuggerProgramLaunchOpinion.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/model/TestDebuggerProgramLaunchOpinion.java index 0f20ba8a8a..faace30e43 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/model/TestDebuggerProgramLaunchOpinion.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/service/model/TestDebuggerProgramLaunchOpinion.java @@ -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 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()); } diff --git a/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetMethod.java b/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetMethod.java index e144efd828..94d4cbad62 100644 --- a/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetMethod.java +++ b/Ghidra/Debug/Framework-Debugging/src/main/java/ghidra/dbg/target/TargetMethod.java @@ -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 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 arguments, Function adjuster) { + arguments.put(name, adjuster.apply((T) arguments.get(name))); + } + @Override public String toString() { return String.format( diff --git a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Step.java b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Step.java index 5d4c24fc21..af90d704cd 100644 --- a/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Step.java +++ b/Ghidra/Debug/Framework-TraceModeling/src/main/java/ghidra/trace/model/time/schedule/Step.java @@ -65,7 +65,7 @@ public interface Step extends Comparable { * *

* 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 diff --git a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/AbstractTaintPcodeExecutorStatePiece.java b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/AbstractTaintPcodeExecutorStatePiece.java index d7f5adb254..938bdad68b 100644 --- a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/AbstractTaintPcodeExecutorStatePiece.java +++ b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/pcode/emu/taint/AbstractTaintPcodeExecutorStatePiece.java @@ -121,10 +121,11 @@ public abstract class AbstractTaintPcodeExecutorStatePiece } @Override - protected Map getRegisterValuesFromSpace(S space, List registers) { + protected Map getRegisterValuesFromSpace(S space, + List registers) { return space.getRegisterValues(registers); } - + @Override public void clear() { for (S space : spaceMap.values()) { diff --git a/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java b/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java index a292077d7d..e53b9b562f 100644 --- a/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java +++ b/Ghidra/Features/Base/src/main/java/help/screenshot/AbstractScreenShotGenerator.java @@ -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. * - *

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)}. + *

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

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)}. + *

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

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)}. + *

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

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)}. + *

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

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)}. + *

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

If you are unsure of what to pass for root, the call + *

+ * If you are unsure of what to pass for root, 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 diff --git a/Ghidra/Features/SystemEmulation/ghidra_scripts/EmuDeskCheckScript.java b/Ghidra/Features/SystemEmulation/ghidra_scripts/EmuDeskCheckScript.java index 811eed3ef3..b590a5bd78 100644 --- a/Ghidra/Features/SystemEmulation/ghidra_scripts/EmuDeskCheckScript.java +++ b/Ghidra/Features/SystemEmulation/ghidra_scripts/EmuDeskCheckScript.java @@ -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*/) { diff --git a/Ghidra/Framework/Gui/src/main/java/generic/test/AbstractGuiTest.java b/Ghidra/Framework/Gui/src/main/java/generic/test/AbstractGuiTest.java index a03f97ce87..aec2c11bba 100644 --- a/Ghidra/Framework/Gui/src/main/java/generic/test/AbstractGuiTest.java +++ b/Ghidra/Framework/Gui/src/main/java/generic/test/AbstractGuiTest.java @@ -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 */ diff --git a/Ghidra/Test/IntegrationTest/build.gradle b/Ghidra/Test/IntegrationTest/build.gradle index 49b52cce81..ffb3221c45 100644 --- a/Ghidra/Test/IntegrationTest/build.gradle +++ b/Ghidra/Test/IntegrationTest/build.gradle @@ -48,6 +48,7 @@ dependencies { if (projectPath.startsWith(ghidraPath) && ( projectPath.contains("/Framework/") || projectPath.contains("/Features/") || + projectPath.contains("/Debug/") || projectPath.contains("/Processors/"))) { api p diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/ghidraclass/debugger/screenshot/TutorialDebuggerMaintenance.java b/Ghidra/Test/IntegrationTest/src/screen/java/ghidraclass/debugger/screenshot/TutorialDebuggerMaintenance.java new file mode 100644 index 0000000000..ffc3d07564 --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/screen/java/ghidraclass/debugger/screenshot/TutorialDebuggerMaintenance.java @@ -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 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); + } +} diff --git a/Ghidra/Test/IntegrationTest/src/screen/java/ghidraclass/debugger/screenshot/TutorialDebuggerScreenShots.java b/Ghidra/Test/IntegrationTest/src/screen/java/ghidraclass/debugger/screenshot/TutorialDebuggerScreenShots.java new file mode 100644 index 0000000000..781940d350 --- /dev/null +++ b/Ghidra/Test/IntegrationTest/src/screen/java/ghidraclass/debugger/screenshot/TutorialDebuggerScreenShots.java @@ -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 botCls) { + Set 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 gdbPathProp = + (Property) 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 configureLauncher(TargetLauncher launcher, + Map arguments, RelPrompt relPrompt) { + if (extraArgs.length() == 0) { + return arguments; + } + Map 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 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 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); + } +} diff --git a/GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.html b/GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.html new file mode 100644 index 0000000000..19ddc8a8cd --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.html @@ -0,0 +1,478 @@ + + + + + + + Ghidra Debugger + + + + + +

+
+

Ghidra Debugger

+
+ +
+

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, but you will +need ncurses.h first:

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

+
./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:

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

  2. +
  3. If you have a CodeBrowser open, close it and return to the main +Ghidra project window.

  4. +
  5. Drag termmines onto the Debugger debugger icon in the Tool +Chest.

  6. +
  7. This will bring up the specimen in the Debugger tool. (If you are +prompted to analyze, choose Yes.)

    +
    + + +
  8. +
  9. In the Debugger tool, click the dropdown ▾ for the debug debug button icon in the global tool +bar, and select “Debug termmines in GDB locally IN-VM.”

  10. +
  11. Wait a bit then verify the Dynamic Listing window (top) is +displaying disassembly code.

    +
    + + +
  12. +
+
+
+

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. 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
  2. +
  3. Select “defaultTools/Debugger.tool”
  4. +
  5. Click Import
  6. +
+
+
+

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:

+
which gdb
+

Note the path given. (If you get an error, then you need to install +GDB.) In a terminal, type the full path of GDB to ensure it executes +properly. Type q to quit GDB.

+
    +
  1. From the Debugger Targets window, click the Connect connect button button.
  2. +
  3. In the Connect dialog, select “gdb” from the dropdown at the +top.
  4. +
  5. Enter the full path, e.g., /usr/bin/gdb, in the “GDB +launch command” field.
  6. +
  7. Click “Connect”
  8. +
  9. If you get an Interpreter window, then things have gone well.
  10. +
  11. Type echo test into it to verify it’s responsive, then +type q to disconnect.
  12. +
  13. Close the Debugger tool, then retry.
  14. +
+
+
+

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 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.
  2. +
  3. In the Ghidra Debugger, find the Targets window, and click the connect Connect button.
  4. +
  5. Select “gdb” from the drop-down box.
  6. +
  7. This dialog should look familiar from the Customized Launching +section. Just click the Connect button.
  8. +
  9. In the Objects window (below the Targets window), expand the node +labeled “Available.”
  10. +
  11. In the filter box, type termmines.
  12. +
  13. Right-click on the termmines process and select Attach. If this +fails, select Available again, and click the +refresh Refresh +button.
  14. +
+
+
+

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

+
+
+

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:

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

+
+
+ + diff --git a/GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.md b/GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.md new file mode 100644 index 0000000000..cf9bc1bbfa --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/A1-GettingStarted.md @@ -0,0 +1,245 @@ + +# Getting Started + +This course assumes you are already familiar with the basics of using Ghidra, including its static analysis features. +To some degree, static analysis is an integral part of debugging with Ghidra. + +## The specimen + +Throughout this course, we will examine the provided "Terminal Minesweeper" specimen, named `termmines`. +If the compiled artifact has not been provided for you, you may build it from source using the provided [Makefile](../ExerciseFiles/Debugger/Makefile), but you will need `ncurses.h` first: + +```bash +cd GhidraClass/ExerciseFiles/Debugger +make +``` + +The specimen is designed for Linux, but should be trivially portable to other Unix systems. +You will need `ncurses` and its development headers and libraries available on your system. +Though source code for the specimen is available, we strongly encourage you to work on the course exercises without referring to it. +Symbols and debug information are removed from the binary. +With some effort, `termmines` may even port to Windows; however, we have not tested this course on Windows. + +It is a good idea to get acquainted with the specimen. +In general, you should take precautions before running code you do not understand or trust. +For `termmines`, the risk is negligible. +Run it: + +```bash +./termmines +``` + +You should see a 9x9 grid and a cursor you can move with the arrow keys. +Hit **Ctrl-C** to exit. +Probe it for help. +Most Linux programs accept a `-h` argument for help: + +```bash +./termmines -h +``` + +You should now have all the information you need to understand how the game works. +If you have never played Minesweeper before, read up online, and perhaps try playing a couple of games. +Don't get distracted, though. + +## Launching on Linux + +On Linux, we will use GDB to debug the specimen. +There are many ways to do this, but for the sake of simplicity, import and launch as follows: + +1. Import `termmines` into a new Ghidra project. +1. If you have a CodeBrowser open, close it and return to the main Ghidra project window. +1. Drag `termmines` onto the Debugger ![debugger icon](images/debugger.png) in the Tool Chest. +1. This will bring up the specimen in the Debugger tool. + (If you are prompted to analyze, choose Yes.) + + ![Debugger tool with termmines open](images/GettingStarted_ToolWSpecimen.png) + +1. In the Debugger tool, click the dropdown ▾ for the debug ![debug button](images/debugger.png) icon in the global tool bar, and select "Debug termmines in GDB locally IN-VM." +1. Wait a bit then verify the Dynamic Listing window (top) is displaying disassembly code. + + ![Debugger tool after launching termmines](images/GettingStarted_DisassemblyAfterLaunch.png) + +## Launching on Windows + +On Windows, we will use dbgeng to debug the specimen. +This is the engine that backs WinDbg. +You may choose an alternative Minesweeper, since terminal applications are less representative of Windows executables. +Follow the same process as for Linux, except import `termmines.exe` and select "Debug termmines.exe in dbgeng locally IN-VM." + +## Launching on macOS + +Unfortunately, things are not so simple on macOS. +See the instructions for [Building LLDB-Java Bindings](../../../Ghidra/Debug/Debugger-swig-lldb/InstructionsForBuildingLLDBInterface.txt). +Once built, follow the same process as for Linux, except select "Debug termmines in LLDB locally IN-VM." + +## Troubleshooting + +### I'm having trouble importing `termmines` + +Check that `termmines` exists. +You may need to build it yourself using `make`. +If it exists and you are still having trouble, please refer to the Beginner course. + +### There is no Debugger icon in my Tool Chest + +Double-check that you are looking at the main Ghidra Project window, not a CodeBrowser. +The tool chest is the box of big icons above the list of imported programs. +If it is not there, you can try importing it from the default tools: + +1. In the menus, select **Tools → Import Default Tools** +1. Select "defaultTools/Debugger.tool" +1. Click Import + +### There is no Debug / Launch icon in the global toolbar + +Double-check that you are in the Debugger tool, not the CodeBrowser tool. +If it is still not there, then you may need to re-import the default Debugger tool as under the previous heading. +If it is still not there, your installation may be corrupt. + +### There is no "Debug termmines in GDB locally IN-VM" option in the launch drop-down + +You may need to install GDB and/or configure Ghidra with its location. +If you have a copy or custom build of GDB in a non-system path, note its full path. +If you intend to use the system's copy of GDB, then in a terminal: + +```bash +which gdb +``` + +Note the path given. +(If you get an error, then you need to install GDB.) +In a terminal, type the full path of GDB to ensure it executes properly. +Type `q` to quit GDB. + +1. From the Debugger Targets window, click the Connect ![connect button](images/connect.png) button. +1. In the Connect dialog, select "gdb" from the dropdown at the top. +1. Enter the full path, e.g., `/usr/bin/gdb`, in the "GDB launch command" field. +1. Click "Connect" +1. If you get an Interpreter window, then things have gone well. +1. Type `echo test` into it to verify it's responsive, then type `q` to disconnect. +1. Close the Debugger tool, then retry. + +### The launch hangs for several seconds and then prompt for a "recorder" + +You probably have a stale GDB connection, so when you launched you now have multiple connections. +For the prompt, select the option with the highest score. +Examine the Targets window to confirm you have multiple GDB connections. +If you know which is the stale connection, you can right-click it and choose **Disconnect**. +Otherwise, use **Disconnect All** from the drop-down menu and re-launch. + +### The Dynamic Listing is empty + +Check for an actual connection. +You should see an entry in the Debugger Targets window, a populated Object window, and there should be an Interpreter window. +If not, then your GDB connector may not be configured properly. +Try the steps under the previous heading. + +If you have an Interpreter window, there are several possibilities: + +#### Ghidra or GDB failed to launch the target: + +Check that the original `termmines` exists and is executable. +It must be at the path from where it was originally imported. +If you imported from a share, consider copying it locally, setting its permissions, then re-importing. + +#### The target was launched, but immediately terminated: + +Check that the specimen has a `main` symbol. +NOTE: It is not sufficient to place a `main` label in Ghidra. +The original file must have a `main` symbol. + +Alternatively, in the menus try **Debugger → 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 ![interrupt button](images/interrupt.png) 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](images/stepinto.png) Step Into button in the main toolbar. +If this is not desirable, then you can toggle **Force Full View** back and forth. +In the Regions window, use the drop-down menu to toggle it on, then toggle it off. +The Dynamic Listing should now be populated. +To go to the program counter, double-click the "pc = ..." label in the top right. + +#### Something else has gone wrong + +Try typing `info inferiors` and similar GDB diagnostic commands into the Interpreter. + +### The listings are in sync, but the Dynamic Listing is grey 00s + +Check the Auto-Read drop-down near the top right of the Dynamic Listing. +It should be set to **Read Visible Memory, RO Once**. + +## Exercise: Launch `termmines` + +If you were following along with an instructor, delete your import of `termmines` and/or start a new Ghidra Project. +Starting from the beginning, import `termmines` and launch it in the Ghidra Debugger with GDB. +When your tool looks like the screenshot with a populated Dynamic Listing, you have completed the exercise. +Disconnect before proceeding to the next exercise. + +## Customized Launching + +For this specimen, you may occasionally need to provide custom command-line parameters. +By default, Ghidra attempts to launch the target without any parameters. +In the menus, use **Debugger → 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](images/connect.png) Connect button. +1. Select "gdb" from the drop-down box. +1. This dialog should look familiar from the Customized Launching section. + Just click the Connect button. +1. In the Objects window (below the Targets window), expand the node labeled "Available." +1. In the filter box, type `termmines`. +1. Right-click on the termmines process and select Attach. + If this fails, select Available again, and click the refresh 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. diff --git a/GhidraDocs/GhidraClass/Debugger/A2-UITour.html b/GhidraDocs/GhidraClass/Debugger/A2-UITour.html new file mode 100644 index 0000000000..fe67e44143 --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/A2-UITour.html @@ -0,0 +1,325 @@ + + + + + + + Ghidra Debugger + + + + + + +
+

Ghidra Debugger

+
+ +
+

A Tour of the Debugger

+

This module assumes you have completed the Getting Started 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 button +Emulate: To be covered in a later module. This will +load the current program (from the Static Listing) into the +emulator.
  • +
  • debug button +Debug: This launches the current program (from the +Static Listing) using a suitable back-end debugger. The drop-down menu +provides a selection of suitable back-end connectors. Clicking the +button will use the last successful connector or the default.
  • +
  • mode button Control +Mode: This drop-down menu sets the mode of the controls and +machine state edits. By default, all actions are directed to the +back-end debugger.
  • +
  • resume button +Resume: Resume execution. This is equivalent to +continue in GDB.
  • +
  • interrupt button +Interrupt: Interrupt, suspend, pause, break, etc. This +is equivalent to Ctrl-C or interrupt in +GDB.
  • +
  • kill button +Kill: Kill, terminate, etc. This is equivalent to +kill in GDB.
  • +
  • disconnect button +Disconnect: Disconnect from the back-end debugger. +Typically, this will also end the session. It is equivalent to +quit in GDB.
  • +
  • step into button +Step Into, Step Over, step out button Step +Out, step last button +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 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 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

+
+
+
+

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.

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

Ghidra Debugger

+
+ +
+

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 Set +Breakpoint, press K on the keyboard, or double-click +the margin.
  2. +
  3. From the Objects window click the add breakpoint Add +Breakpoint button or press F3 on the keyboard.
  4. +
  5. From the Interpreter window, use the GDB command, e.g., +break main.
  6. +
+

The advantage of using the listings is that you can quickly set a +breakpoint at any address. The advantage of using the Objects or +Interpreter window is that you can specify something other than an +address. Often, those specifications still resolve to addresses, and +Ghidra will display them. Ghidra will memorize breakpoints by recording +them as special bookmarks in the imported program. There is some +iconography to communicate the various states of a breakpoint. When all +is well and normal, you should only see enabled enabled breakpoint and +disabled breakpoints. If the target is terminated +(or not launched yet), you may also see ineffective ineffective breakpoint +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:

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

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.
  2. +
  3. From the Objects window, expand the Breakpoints node, right-click a +breakpoint and select Toggle or press T on the +keyboard.
  4. +
  5. 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.
  6. +
  7. From the Interpreter window, use the GDB commands, e.g., +disable 2.
  8. +
+

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.

  2. +
  3. Check the Debug Console window for a note about the missing +module:

    +
    + + +
  4. +
  5. Click the import button — leftmost of the remedial actions. It +will display a file browser pointed at the library file.

  6. +
  7. Proceed with the import and initial analysis as you would in the +CodeBrowser.

  8. +
+

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

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

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

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

  2. +
  3. 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.

  4. +
  5. In the Interpreter panel:

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

  6. +
  7. Back in your terminal running 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.

  8. +
  9. 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.

  10. +
  11. 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.

  12. +
+

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.

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

Ghidra Debugger

+
+ +
+

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 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 location tracking 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 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 Resume. +Your Dynamic Listing should follow the stack pointer. In the menus, +select Debugger → Analysis → Unwind Stack 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 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 +refresh Refresh +button. Examine the Debug Console window for errors / warnings before +spamming this button. To toggle auto read, use the +auto-read 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 +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?
  2. +
  3. How are the cells indexed? Row major, color major? 0-up, 1-up?
  4. +
  5. What is happening around the “border” of the board? Why might the +programmer have chosen this design?
  6. +
+
+
+
+
+

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

+

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

+
+
+ + diff --git a/GhidraDocs/GhidraClass/Debugger/A4-MachineState.md b/GhidraDocs/GhidraClass/Debugger/A4-MachineState.md new file mode 100644 index 0000000000..27ec1696b9 --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/A4-MachineState.md @@ -0,0 +1,400 @@ + +# Examining Machine State: Memory, Registers, and Variables + +This module assumes you know how to launch `termmines` in Ghidra using GDB, and know where to find the basic Debugger GUI components. +It also assumes you know the basic control commands, e.g., Resume, Step, and Interrupt; as well as some basics for breakpoints. +If not, please refer to the previous modules. + +This module will address the following features in more depth: + +* Dynamic Listing window +* Dynamic Bytes window +* Registers window +* Watches window +* Sleigh expressions +* Variable value hovers + +## Machine State + +There are at least two ways to define machine state. +One way based on a high-level understanding of a program is the collective values of all variables. +Another based on a low-level understanding of a program is the collective values of all memory and all registers of all threads. +Because Ghidra is primarily concerned with examining software at the low level, albeit to obtain a high-level understanding, we will generally stick with the low-level definition. +One could argue that machine state also includes the underlying system and hardware, including peripherals. +When debugging user-space applications, Ghidra cannot generally inspect the underlying system, except for a few things granted by the back-end debugger, e.g., the virtual memory map. +Thus, as far as we are concerned, the machine state does not include the underlying system, but it is still something you must be aware of while debugging. + +Note that we also treat *registers* as something separate from *memory*. +While technically, one could argue registers are the closest and smallest memory to the CPU, we think of memory as an addressable array, e.g., DIMMs and flash ROMs. +If applicable, *memory-mapped* registers are considered part of memory by Ghidra. + +The Ghidra Debugger provides mechanisms for viewing and modifying machine state at both the low and high levels. +The Dynamic Listing and Memory viewer provide access to memory, the Registers window provides access to registers, and the Watches window provides access to memory and registers via expressions. +The only means of accessing high-level variables is through value hovers, which are available in all listings and the Decompiler. + +### Patching + +Many of the features allow you to edit the values, i.e., *patch* the live target. +The windows that are adaptations of their static counterparts work more or less as expected. +Edits in any *dynamic* window will attempt to patch the live target. +Edits in any *static* window will **not** patch the live target; they modify the program database as usual. +Some dynamic windows may present tables with editable cells. +These will often include a *write-lock* toggle, like the static Byte viewer, to avoid accidental patching. +Furthermore, you must use the ![control mode](images/record.png) Control Mode toggle in the global toolbar (next to the control actions) to enable patching throughout the Debugger tool. +For now, please only use the "Control Target" and "Control Target w/ Edits Disabled" options. +The write toggle is included here so that actions like copy-paste do not lead to accidental patching. + +## The Dynamic Listing + +Up to this point, we have only used the Dynamic Listing to display the instructions at the program counter. +It can actually view and mark up any part of the machine state in memory, e.g., mapped images, heap pages, stack pages. +Where the memory is mapped to images, Ghidra attempts to synchronize the Static Listing and, by extension, the other static analysis windows. +The Dynamic Listing has most of the same features as the Static Listing. + +Re-launch `termmines` and then navigate to `rand`. +You may notice that the Static Listing has disassembly, but the Dynamic Listing does not. +This is because Ghidra has not observed the program counter at `rand` yet, so it has not automatically disassembled it. +To manually disassemble in the Dynamic Listing, place your cursor where you expect an instruction and press **D**, just like you would in the Static Listing. + +![The dynamic listing after a call to rand](images/State_ListingAfterCallRand.png) + +This action differs in that it does not follow flow. +It proceeds linearly, stopping at any control transfer instruction. + +Now, we will examine the stack segment. +Click the ![location tracking](images/register-marker.png) Track Location drop-down and select **Track Stack Pointer**. +The window should seek to (and highlight in pale green) the address in the stack pointer. +Since the target has just entered `main`, we should expect a return address at the top of the stack. +With your cursor at the stack pointer, press **P** to place a pointer there, just like +you would in the Static Listing. +You can now navigate to that address by double-clicking it. +To return to the stack pointer, you can use the back arrow in the global toolbar, you can click the ![track location](images/register-marker.png) Track Location button, or you can double-click the `sp = [Address]` label in the top right of the Dynamic Listing. + +To examine a more complicated stack segment, we will break at `rand`. +Ensure your breakpoint at `rand` is enabled and press ![resume](images/resume.png) Resume. +Your Dynamic Listing should follow the stack pointer. +In the menus, select **Debugger → Analysis → Unwind from frame 0** or press **U**. + +![The dynamic listing of the stack after a call to rand](images/State_ListingStackAfterCallRand.png) + +**NOTE**: We will cover the Stack window later in the course, which is probably a more suitable way to navigate stack frames. +It is populated by the back-end debugger, which can usually unwind the stack more reliably than Ghidra. +The Unwind Stack action is useful when you want an in-depth understanding of the actual contents of the stack, or when you are emulating. + +Now, switch back to **Track Program Counter**. +If you would like to track both the Program Counter and the Stack Pointer, click the ![clone](images/camera-photo.png) Clone button in the local toolbar. +Like the Static Listing, this clones an instance of the Dynamic Listing, which you can configure differently than the primary Dynamic Listing. +Only the primary Dynamic Listing will synchronize, and it does so only with the primary Static Listing. +**NOTE**: For the sake of disambiguation, we will use the term *clone*, not *snapshot* when referring to multiple instances of a Ghidra window. +While this is inconsistent with the Ghidra Beginner course materials, it is necessary to avoid confusion with snapshots of the machine state, discussed later in this module. + +The dynamic listing offers several additional features: + +### Cache Status Indication + +The listing's contents are read from a live target, which may become unresponsive or otherwise temperamental. +The Debugger uses a database, which acts as a cache separating the GUI from the live target. +The UI requests memory pages from the target, the target asynchronously retrieves those pages and stores them into the database, then the database updates the UI. +This sequence does not always go as expected; thus, pages with stale data are displayed with a grey background. +This may also happen if auto-read is disabled. +Typically, user-space targets are not so temperamental, but others may be, or memory reads could be expensive, in which case disabling automatic memory reads may be advantageous. +If the back-end debugger reports an error while reading memory, the first address of the page will have a red background. +To refresh the visible or selected page(s), click the refresh Refresh button. +Examine the Debug Console window for errors / warnings before spamming this button. +To toggle auto read, use the auto-read Auto-Read drop-down button from the local toolbar. + +### Address Tracking + +We have already demonstrated this, but there are some finer details. +Some of the tracking options depend on the Watches window, discussed later in this module. +On occasion, the location cannot be displayed in the listing, typically because it falls outside of the memory map. +If this happens, the address label at the top right of the listing will have red text. + +### Module / Region Indicator + +In the top left a label will display the name of the section containing the cursor. +If there is no containing section, it will fall back to the containing module and then to the containing region. +Rarely, this label will be empty. +This can happen when the cursor is outside any known region, which only happens if you configure Ghidra to ignore the memory map. + +### Go To + +The Go To action in the Dynamic Listing differs from the one in the Static Listing. +Like the static one, it accepts an address in hexadecimal, possibly prefixed with the address space and a colon. +However, it also accepts Sleigh expressions, allowing you to treat `RAX` as a pointer and go to that address, for example. +We cover Sleigh expressions later in this module. + +### Compare + +The Compare action in the Dynamic Listing also differs from the one in the Static Listing. +It allows the comparison of two machine state snapshots, covered in the [Navigation](A5-Navigation.md) module. + +### Exercise: Reverse Engineer the Board + +All of the features in the default CodeBrowser tool are also in the default Debugger tool, providing you Ghidra's full suite of static analysis tools during your dynamic sessions, albeit they are not as immediately accessible. +Your task is to reverse engineer the game board's layout in memory. +Because you are in a dynamic session, you have an example board to work with. +As you navigate the `.data` section of `termmines` in the Static Listing, the Dynamic Listing will follow along showing you the live values in memory. +You can also experiment by placing code units in the Dynamic Listing before committing to them in the Static Listing. + +#### Questions: + +1. How are the cells allocated? +1. How are the cells indexed? Row major, color major? 0-up, 1-up? +1. What is happening around the "border" of the board? Why might the programmer have chosen this design? + +## The Memory Viewer + +![The dynamic memory view of the stack after a call to rand](images/State_BytesStackAfterCallRand.png) + +Just as the Dynamic Listing is the analog of the Static Listing, the Memory viewer is the analog of the Byte viewer. +It is not visible by default. +To open it, use **Windows → 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 after a call to rand](images/State_RegistersAfterCallRand.png) + +The Registers window gives a view of all the registers on the target and their current values. +The register set can be very large, so there are a few ways to sift and sort. +As in most Ghidra tables, you can filter using the box below the registers table. +Additionally, you can use the column headers to sort. +The columns are: + +* The **Favorite** column indicates which registers are your favorite. + By default, this includes the instruction pointer and the stack pointer. + You can quickly choose your favorite(s) by toggling the check boxes in this column. + Because this column is sorted by default, your favorites are positioned at the top. +* The **Number** column gives the number assigned to the register by Ghidra's processor specification. + This is mostly just to make the default sorting deterministic. +* The **Name** column gives Ghidra's name for the register. + This usually matches the name given by the back-end debugger, but may not. + For example, on x86-64, what Ghidra calls `rflags` GDB calls `eflags`. +* The **Value** column gives the register's current value in hexadecimal. + Values in gray are stale. + Values in red have changed. + Right-clicking this column will present options to Go To the address, as if the register were a pointer. +* The **Type** column allows you to assign a type to, i.e., create a data unit on, the register. + This has more utility for float types than integers, but it may still help you record what you know about how a register is being used. +* The **Representation** column displays the register's value according to its assigned type, if applicable. + If the type is a pointer, then double-clicking this value will go to the address in the Dynamic Listing. + +If you would like to adjust the list of registers in the table, use the ![select registers](images/select-registers.png) Select Registers button in the local toolbar. +This will present all the registers in Ghidra's processor specification, including those which are just artifacts of Sleigh. +Typically, this is not necessary, since the table will include all registers recognized by both Ghidra and the back-end debugger. +Nevertheless, if you believe a register is missing, it is wise to check this selection. + +### Exercise: Reduce the Mines + +If you have not already reverse engineered the mine placement algorithm, do that now. +Think up a strategy you might employ, by patching a register, to reduce the number of mines placed on the board. +The strategy need not result in a permanent change. +It should only affect the round being set up. +For this exercise, you cannot patch memory, but you may place a breakpoint. +Verify your work by playing the round. + +## Watches + +![The watches window in a call to srand](images/State_WatchesInCallSRand.png) + +The Watches window gives the values of several user-specified Sleigh expressions. +This can provide an alternative to the Registers window when you are really only interested in a couple of registers. +It can also watch values in memory. +Furthermore, when a watch has a memory address, the expression will appear as an option in the Location Tracking menus of the Listing and Memory viewers. +Selecting that option will cause the window to follow that watch as its address changes. + +To add a watch, click the ![add](images/add.png) Add button. +A new entry will appear. +Double-click the left-most cell of the row to set or edit the Sleigh expression. +For starters, try something like `RDI`. +(Conventionally, this is the location for the first parameter on Linux x86-64 systems.) +The context menus for the Listing and Registers windows include a "Watch" action, which adds the current selection to the Watches window. + +The columns are: + +* The **Expression** column is the user-defined Sleigh expression. +* The **Address** column is the address of the resulting value, if applicable. + This may be in `register` space. + Double-clicking this cell will go to the address in the Dynamic Listing. +* The **Symbol** column gives the symbol in a mapped static image closest to or containing the address, if applicable. +* The **Value** column gives the "raw" value of the expression. + If the result is in memory, it displays a byte array; otherwise, it displays an integer. +* The **Type** and **Representation** columns work the same as in the Registers window, except they do *not* save the data unit to the database. + This has more uses than the Registers window. + For example, try `*:30 RDI` and set this to `TerminatedCString`. + Whenever `RDI` is a string pointer, this will display the string up to 30 characters. +* The **Error** column reports any errors in compiling or evaluating the expression. + +## Sleigh Expressions + +Watches and Go-To commands are expressed using Ghidra's Sleigh language. +More precisely, expressions are the sub-language of Sleigh for the right-hand side of assignment statements in semantic sections. +If you already know this language, then there is little more to learn. +Of note, you may use labels from mapped program images in your expression. +For example, to see how far a return address is into `main`, you could use `*:8 RSP - main`. + +For the complete specification, see the Semantic Section in the [Sleigh documentation](../../../Ghidra/Features/Decompiler/src/main/doc/sleigh.xml). + +Sleigh is a bit unconventional in that its operators are typed rather than its variables. +All variables are fix-length bit vectors. +Their sizes are specified in bytes, but they have no other type information. + +### Variables and Constants + +Here are some examples of things you can reference by name: + +* **Register**: `RAX` +* **Label**: `main` +* **Constant**: `1234:8` or `0x42d:8` — 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. diff --git a/GhidraDocs/GhidraClass/Debugger/A5-Navigation.html b/GhidraDocs/GhidraClass/Debugger/A5-Navigation.html new file mode 100644 index 0000000000..af2551346b --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/A5-Navigation.html @@ -0,0 +1,376 @@ + + + + + + + Ghidra Debugger + + + + + + +
+

Ghidra Debugger

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

Ghidra Debugger

+
+ +
+

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.
  2. +
  3. From the Modules window, click Map Manually in the local +toolbar.
  4. +
+
+ + +
+

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

  2. +
  3. Change focus to the Dynamic Listing.

  4. +
  5. In the global menu, choose Debugger → Copy Into New +Program.

    +
    + + +
  6. +
  7. Keep Destination set to “New Program.”

  8. +
  9. Ensure “Read live target’s memory” is checked. This will spare +you from having to create a full snapshot manually.

  10. +
  11. Do not check “Use overlays where blocks already +present.” It should not have any effect for a new program, +anyway.

  12. +
  13. It is probably best to include everything, though “Bytes” is the +bare minimum.

  14. +
  15. 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.

  16. +
  17. Click Copy.

  18. +
  19. When prompted, name the program libncurses.

  20. +
  21. You may need to click the termmines tab in the +Static Listing to get the UI to completely update.

  22. +
  23. Click back over to libncurses and save the program. +If you are prompted to analyze, go ahead.

  24. +
+

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.
  2. +
  3. Ensure the cursor in the Static Listing is at the minimum address of +libncurses.
  4. +
  5. In the Static Mappings window, click Add in the toolbar.
  6. +
  7. Click OK.
  8. +
+

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.

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

Ghidra Debugger

+
+ +
+

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 and +A Tour of the Debugger UI 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:

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

  2. +
  3. In the Interpreter, do as you would in GDB:

    +
    target remote 10.0.0.1:12345
  4. +
+

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

    +
    + + +
  2. +
  3. Set “GDB launch command” to the path of gdb on the remote +file system.

  4. +
  5. Leave “Use existing session via new-ui” unchecked.

  6. +
  7. Set “SSH hostname” to the name or IP address of the target +system.

  8. +
  9. If you are not using the standard SSH port, set “SSH TCP port” +accordingly.

  10. +
  11. Set “SSH username” to your username on the target +system.

  12. +
  13. Set “Open SSH config file” to the client config file on the +local file system.

  14. +
  15. If the remote uses DOS line endings (unlikely for a Linux +remote), then check the “Use DOS line endings” box.

  16. +
  17. Click Connect.

  18. +
  19. If prompted, enter your SSH credentials.

  20. +
+

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:

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

+
support/gdbGADPServerRun
+

If not, then you probably just need to tell it where you installed +gdb:

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

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

+
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.
  2. +
  3. Select “Ghidra debug agent (GADP)” from the drop-down.
  4. +
  5. For “Agent network address”, enter 10.0.0.2.
  6. +
  7. For “Agent TCP port”, enter 15432.
  8. +
  9. Click Connect.
  10. +
+

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:

    +
    gdb termmines
  2. +
  3. If needed, do whatever you would like to do before connecting +with Ghidra.

  4. +
  5. In Ghidra, from the Targets window, click Connect, and select +gdb.

  6. +
  7. Check the “Use existing session via new-ui” box.

  8. +
  9. Click Connect.

  10. +
  11. You will be prompted with the name of a pseudo terminal, e.g., +/dev/pts/1.

  12. +
  13. Back in gdb:

    +
    new-ui /dev/pts/1
  14. +
+

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:

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

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

Ghidra Debugger

+
+ +
+

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.

+

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 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.
  2. +
  3. 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.
  4. +
  5. Use a breakpoint to interrupt the live target when it enters this +function.
  6. +
  7. Change the “Control mode” drop-down to “Control Emulator.”
  8. +
  9. Click step into button Step +Into to step the emulator forward.
  10. +
  11. Click skip over button Skip +Over and step back button Step +Back to experiment with different execution paths.
  12. +
+

About those two new actions:

+
    +
  • skip over button +Skip Over: Step the current thread by skipping one +instruction.
  • +
  • step back button +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 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:

+
    +
  • ScheduleSnapshot [ : [ +Step ( ; Step ) * ] [ . +Step ( ; Step ) * ] ]
  • +
  • Step → [ t Id - ] ( +Tick | Skip | Patch )
  • +
  • TickCount
  • +
  • Skips 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.
  2. +
  3. Change back to “Control Target” mode. Ghidra will navigate back to +the current snapshot, so PC will match the live target.
  4. +
  5. Press Ctrl-G and type or paste the schedule in, and +click OK. The program counter should be restored to +exit(-1).
  6. +
+

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.
  2. +
  3. 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.)
  4. +
  5. Use the emulator to extrapolate forward and begin understanding how +the algorithm works.
  6. +
  7. 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.
  8. +
  9. Use extrapolation to debug and test your patch.
  10. +
  11. 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.)
  12. +
+
+
+
+

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. 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.
  2. +
  3. Go to the entry of the command-line argument parsing function.
  4. +
  5. Right-click its first instruction and select Emulate Program +in New Trace.
  6. +
+

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 button 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 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 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 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 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 button +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.
  2. +
  3. You can override the CALL instruction using a Sleigh +breakpoint.
  4. +
  5. You can override the call target using a Sleigh breakpoint.
  6. +
+
+

Skip Technique

+

The skip technique is simplest, but will need to be performed +every time that call is encountered. Press skip over button Skip Over, then use +the Registers or Watches pane to patch RAX. Then press resume button 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.
  2. +
  3. Now, in the Breakpoints panel, right-click the new breakpoint and +select Set Injection (Emulator).
  4. +
  5. This is the fun part: you must now implement the function in Sleigh, +or at least stub it well enough for this particular call.
  6. +
+

Supposing this is a call to strnlen, you could implement +it as:

+
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. 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.
  2. +
  3. In the menus, select Debugger → Configure Emulator → +Invalidate Emulator Cache.
  4. +
  5. Click resume button +Resume.
  6. +
+

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.

+
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.
  2. +
  3. In the menus, select File → Configure.
  4. +
  5. Click the “Configure All Plugins” button in the top right of the +dialog.
  6. +
  7. Activate the DebuggerPcodeStepperPlugin
  8. +
  9. Click OK
  10. +
  11. Click Close
  12. +
+

The stepper should appear stacked over the Threads panel in the +bottom right. Yours will probably still be empty, but here is what it +looks like populated:

+
+P-code stepper + +
+

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.

+
+
+ + diff --git a/GhidraDocs/GhidraClass/Debugger/B2-Emulation.md b/GhidraDocs/GhidraClass/Debugger/B2-Emulation.md new file mode 100644 index 0000000000..2b8aaf68ba --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/B2-Emulation.md @@ -0,0 +1,493 @@ + +# Emulation + +Emulation is a bit of a loaded term, unfortunately. +Most of the confusion deals with the scope of the emulated target. +Do you just need to step through a few instructions, or a whole function? +Do you need to include external modules? +Do you need to simulate system calls? +Do you need to simulate connected devices? +Most of Ghidra's GUI-accessible emulation features focus on the smaller scope, though it does provide programming interfaces for advanced users to extend that scope. +Those more advanced features are covered in [Modeling](B4-Modeling.md). + +This module assumes you have completed the Beginner portion of this course. + +## P-code Emulation and Caveats + +Ghidra's emulator uses the same p-code as is used by the decompiler. +P-code describes the semantics of each instruction by constructing a sequence of p-code operations. +The p-code specifications for most of Ghidra's languages were designed with decompilation, not necessarily emulation, in mind. +While in most cases, p-code for decompilation suffices for emulation, there are cases where design decisions were made, e.g., to keep decompiler output simple, that makes them less suitable for emulation. +This may manifest, e.g., in an excess of user-defined p-code ops, or *userops*. +The [Modeling](B4-Modeling.md) module discusses ways to implement or stub those userops in the emulator. +Some processor modules provide those stubs "out of the box." +If the emulator ever halts with an "unimplemented userop" message, then you have run into this problem. + +## Use Cases + +As already hinted at the start of this module, there are several use cases for emulation, and Ghidra tries to meet these cases by integrating emulation into the Debugger UI. +Some of the use cases accessible from the UI are: + +* Extrapolation and interpolation of a live target. +* Emulation of a program image. +* P-code semantics debugging. + +We will explore each case with a tutorial and exercise. + +## Extrapolation and Interpolation + +This is perhaps the easiest use case, assuming you already have started a live session. +*Extrapolation* is predicting execution of the target into the future, without allowing the actual target to execute. +Instead, we will allow an emulator to step forward, while reading its initial state from the live target. +This allows you, e.g., to experiment with various patches, or to force execution down a certain path. +If you devise a patch, you can then apply it the live target and allow it to execute for real. +*Interpolation* is similar, but from a snapshot that is in the past. +It can help answer the question, "How did I get here?" +It is more limited, because missing state for snapshots in the past cannot be recovered. + +In this tutorial, we will examine the command-line argument parser in `termmines`. + +1. Launch `termmines` using GDB in the Ghidra Debugger. +1. If you have not already, do a bit of static analysis to identify the argument parsing function. + It should be the first function called by `main`. +1. Use a breakpoint to interrupt the live target when it enters this function. +1. Change the "Control mode" drop-down to "Control Emulator." +1. Click ![step into button](images/stepinto.png) Step Into to step the emulator forward. +1. Click ![skip over button](images/skipover.png) Skip Over and ![step back button](images/stepback.png) Step Back to experiment with different execution paths. + +About those two new actions: + +* ![skip over button](images/skipover.png) **Skip Over**: + Step the current thread by skipping one instruction. +* ![step back button](images/stepback.png) **Step Back**: + Step the current thread backward one instruction, or undo an emulated skip or patch. + +Try to get the program counter onto the call to `exit(-1)` using only those three step buttons. + +You should see things behave more or less the same as they would if it were the live target. +The main exceptions are the Objects and Interpreter windows. +Those always display the state of the live target, as they are unaware of the emulator, and their sole purpose is to interact with the live target. +You can make changes to the emulator's machine state, set breakpoints, etc., just as you would in "Control Target" mode. +**NOTE**: You may see Ghidra interact with the target, despite being in "Control Emulator" mode, because Ghidra lazily initializes the emulator's state. +If the emulated target reads a variable that Ghidra has not yet captured into the current snapshot, Ghidra will read that variable from the live target, capture it, and provide its value to the emulator. + +### Stepping Schedules + +If you had not noticed before, the subtitle of the Threads window gives the current snapshot number. +If you have stepped in the emulator, it will also contain the sequence of steps emulated. +Recall the *time* element of the Debugger's "coordinates." +(See the [Navigation](A5-Navigation.md) module if you need a refresher.) +The time element, called the *schedule*, consists of both the current snapshot and the sequence of steps to emulate. +The subtitle displays that schedule. +If you have done any patching of the emulator's state, you may notice some more complicated "steps" in the schedule. +The syntax is: + +* *Schedule* → *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: + +![Stale listing upon starting pure emulation](images/Emulation_LazyStaleListing.png) + +This is perhaps more a matter of preference, but by default, Ghidra will only populate the Dynamic listing with state initialized by the emulator itself. +When the emulator reads, it will "read through" uninitialized state by reading the mapped program image instead. +This spares the loader from having to copy a potentially large program image into the emulator. +In general, you should refer to the Static listing when following the program counter. +If you see contents in the Dynamic listing following the program counter, then you are probably dealing with self-modifying code. + +**NOTE**: If you prefer to see the Dynamic listing initialized with the program image, you may select **Load Emulator from Program** from the Auto-Read drop-down button in the Dynamic Listing. +The loading is still done lazily as each page is viewed in the listing pane. +You will want to change this back when debugging a live target! + +Because we can easily step back and forth as well as navigate to arbitrary points in time, emulation should feel relatively free of risk; however, the point about stubbing dependencies will become apparent. +If you feel the need to start over, there are two methods: +First, you can end the emulation session and restart it. +To end the session, in the Threads panel, right-click the "Emulate termmines" tab and select Close. +You can then restart by right-clicking the first instruction as before. +Second, you can use **Ctrl-G** to go to snapshot 0. +This method is not as clean as the first, because the trace will retain its scratch snapshots. + +Press ![resume button](images/resume.png) Resume to let the emulator run until it crashes. +It should crash pretty quickly and without much ceremony: + +![Listing after crashing](images/Emulation_ListingAfterResume.png) + +In this case, the clearest indication that something has gone wrong is in the top-right of the Dynamic listing. +Recall that the location label is displayed in red when the program counter points outside of mapped memory. +Presumably, the crash was caused by the instruction to be executed next. +To get details about the error, press ![step into button](images/stepinto.png) Step Into. +This should display an error dialog with a full trace of the crash. +In this case, it should be an instruction decode error. +When the emulator reads uninitialized memory, it will get stale 0s; however, when the emulator tries to *execute* uninitialized memory, it will crash. +Most likely, the target called an external function, causing the program counter to land in the fake `EXTERNAL` block. + +To diagnose the crash, press ![step back button](images/stepback.png) Step Back. +After a couple steps back, you should be able to confirm our hypothesis: we got here through a call to the external function `printf`. +You can continue stepping back until you find the decision point that took us down this path. +You should notice it was because `param_1` was 0. +The decompiler can help you recognize that at a glance, but you will still want to use the disassembly to get at precisely the deciding instruction. +The `JZ` (or other conditional jump) is too late; you need to step back to the `TEST EDI,EDI` (or similar) instruction. +(This may, ironically, be the first instruction of the function.) +In the System V AMD64 ABI (Linux x86-64 calling conventions) `RDI` is used to pass the first parameter. +You can hover your mouse over `param_1` in the Decompiler, and it will tell you the location is `EDI:4`, and that its current value is a stale 0. + +### Initializing Other State + +We had just started executing the target function arbitrarily. +Ghidra takes care of a minimal bit of initialization of the trace to start emulation. +Namely, it maps the image to its preferred base. +It allocates space for the main thread's stack and initializes the stack pointer. +Finally, it initializes the program counter. + +It is still up to you to initialize any other state, especially the function's parameters. +Clearly, we will need to initialize `param_1`. +We may need to do a little static analysis around the call to this function to understand what those parameters are, but you could probably make an educated guess: +`param_1` is `argc` and `param_2` is `argv`. +We might as well initialize both. +Luckily, we have plenty of memory, and given the small scope of emulation, we can probably place the strings for `argv` wherever we would like. + +You may prefer to apply patches to the trace database or to the emulator. +The advantage to patching in the emulator is that once you have completed your experiments, you can readily see all of the steps that got you to the current machine state, including all patches. +The disadvantage is that if you have extensive patches, they will pollute the stepping schedule, and things can get unwieldy. + +Alternatively, you can perform the patches in the trace. +When you launched the emulated target, all Ghidra really did was initialize a trace database. +The advantage to patching the trace is that once you have completed your experiments, you will have your initial state captured in a trace snapshot. +The disadvantage is that you will need to remember to invalidate the emulator cache any time you change the initial state. +For this tutorial, we will perform the patches in the emulator. + +**NOTE**: If you wish to try patching the trace, then change to "Control Trace" mode and use the "Navigate backward one snapshot" control action that appears, so that you are patching the initial state, and not a scratch snapshot. +Scratch snapshots are ephemeral snapshots in the trace used to display emulated state. +Changes to these snapshots will affect the display, but will not affect subsequent emulation. +If your current schedule includes any steps, then "Control Trace" is patching a scratch snapshot. + +Now, we will manually "allocate" memory for `argv`. +Luckily, Ghidra allocated 16K of stack space for us! +The target function should not need a full 16K, so we will allocate the lowest addresses of the stack region for our command-line arguments. +If you prefer, you may use the **Add Region** action in the Regions window to manually fabricate a heap region, instead. +In the Regions window, filter for "stack" and take note of the start address, e.g., `00001000`. +We will use the Watches window to perform our patching, though we will also use the Dynamic listing to double check. +Add the following watches: + +* `RSP` — 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 button](images/resume.png) Resume. +Like before, the emulator will crash, but this time you should see "pc = 00000000" in red. +This probably indicates success. +In the Threads window, you should see a schedule similar to `0:t0-{RDI=0x1);t0-16`. +This tells us we first patched RDI, then emulated 16 machine instructions before crashing. +When the parser function returned, it probably read a stale 0 as the return address, so we would expect a decode error at `00000000`. +Step backward once to confirm this hypothesis. + +### Stubbing External Calls + +For this tutorial, we will set the skill level to Advanced by patching in actual command-line arguments. +This continues our lesson in state initialization, but we may also need to stub some external calls, e.g., to `strnlen` and `strcmp`. +We will need to pass in `termmines -s Advanced`, which is three arguments. +Use **Ctrl-G** to go back to snapshot 0, and add the following watches: + +* `*:8 (RSI + 0)` — 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]` + +![Watches for patching command-line arguments](images/Emulation_WatchesForCmdline.png) + +This will generate an extensive list of patch steps, so you may prefer to patch the trace in this case. +Set `RDI` to 3. +Notice that `argv[0]` is supposedly allocated at `00000000` according to the Address column for the watch on `*:8 (RSI + 0)`. +That was determined by the value of `RSI`, which is essentially telling us we need to allocate `argv`, an array of pointers. +We can confirm `RSP` is at the upper end of the stack region, so we allocate `argv` at `00001000`. +To do that, set the value of `RSI` to `0x1000`. +You should see the Address column update for some other watches. +You can double-click any of those addresses to go there in the Dynamic listing. + +**NOTE**: You *do not have* to allocate things in a listed region, but if you want to see those things in the Dynamic listing, it is easiest if you allocate them in a listed region. + +Now, we need to allocate space for each argument's string. +To ensure we do not collide with the space we have already allocated for `argv`, we should place a data unit in the Dynamic listing. +Double-click the Address `00001000` in the Watches window to go to that address in the Dynamic listing. +Press **P** then **[** (left square bracket) to place a 3-pointer array at that address. +We can now see the next available byte is at `00001018`. +**NOTE**: You might set the Dynamic listing to **Do Not Track**, otherwise it may seek back to the PC every time you patch. + +Now that we know where to put `argv[0]`, we need to patch it to `0x0001018`. +This should be the watch on `*:8 (RSI + 0)`. +When you modify the Value column, you can type either bytes (in little-endian order for x86) or the integer value `0x1018`. +That should cause the watch on `*:30 (*:8 (RSI + 0))` to get the address `00001018`. +Using the Repr column, set that watch's value to `"termmines"`. +(The quotes are required.) +Place a string in the Dynamic listing using the **'** (apostrophe) key. +This shows us the next available address is `00001022`, so repeat the process to allocate `argv[1]` and set it to `"-s"`. +Then finally, allocate `argv[2]` and set it to `"Advanced"`. +When you have finished, the Watches pane should look something like this: + +![Watches for patching command-line arguments after setting](images/Emulation_WatchesForCmdlineSet.png) + +The Dynamic listing should look something like this: + +![Listing after setting command-line arguments](images/Emulation_ListingForCmdlineSet.png) + +**NOTE**: The placement of data units is not necessary for the emulator to operate; it only cares about the bytes. +However, it is a useful aide in devising, understanding, and diagnosing machine state. + +Now, click ![resume button](images/resume.png) Resume, and see where the emulator crashes next. +Depending on your compilation of `termmines`, it may crash after returning, or it may crash trying to call `strnlen` or `strcmp`. +If the program counter is `00000000`, then it returned successfully. +This is unfortunate, because you no longer have motivation to stub external calls. + +If the program counter is not `00000000`, then step backward until you get to the `CALL`. +There are at least three techniques for overcoming this. + +1. You can skip the `CALL` and patch `RAX` accordingly. +1. You can override the `CALL` instruction using a Sleigh breakpoint. +1. You can override the call target using a Sleigh breakpoint. + +#### Skip Technique + +The skip technique is simplest, but will need to be performed *every time* that call is encountered. +Press ![skip over button](images/skipover.png) Skip Over, then use the Registers or Watches pane to patch `RAX`. +Then press ![resume button](images/resume.png) Resume. + +#### `CALL` Override Technique + +Overriding the `CALL` is also fairly simple. +While this will handle every encounter, it will not handle other calls to the same external function. + +1. Press **K** in the listing to place a breakpoint on the `CALL` instruction. +1. Now, in the Breakpoints panel, right-click the new breakpoint and select **Set Injection (Emulator)**. +1. This is the fun part: you must now implement the function in Sleigh, or at least stub it well enough for this particular call. + +Supposing this is a call to `strnlen`, you could implement it as: + +```sleigh {.numberLines} +RAX = 0; + +if (*:1 (RDI+RAX) == 0 || RAX >= RSI) goto ; +RAX = RAX + 1; +goto ; + +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 button](images/resume.png) Resume. + +Stubbing any remaining external calls is left as an exercise. +You are successful when the emulator crashes with `pc = 00000000`. + +Clear or disable your breakpoint and invalidate the emulator cache again before proceeding to the next technique. + +#### Target Override Technique + +The target override technique is most thorough, but also the most involved. +It will handle all calls to the external function, e.g., `strnlen`, no matter the call site. +If the call goes through a program linkage table (PLT), then you are in luck, because the call target will be visible in the Dynamic listing. +The PLT entry usually contains a single `JMP` instruction to the actual `strnlen`. +For real target processes, the `JMP` instruction will transfer control to a lazy linker the first time `strnlen` is called from `termmines`. +The linker then finds `strnlen` and patches the table. +In contrast, the Ghidra loader immediately patches the table to point to a fake `::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; + +if (*:1 (RDI+RAX) == 0 || RAX >= RSI) goto ; +RAX = RAX + 1; +goto ; + +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: + +![P-code stepper](images/Emulation_PcodeStepper.png) + +To populate it, you will need a session, either emulated or connected to a back-end debugger. +Use the buttons in the local toolbar to step p-code operations. +The first p-code op of any instruction is to decode the instruction. +Once decoded, the p-code listing (left panel) will populate with the ops of the decoded instruction. +If the current instruction is overridden by a Sleigh breakpoint, the listing will populate with the injected ops instead. +You can then step forward and backward within those. +As you step, the other windows that display machine state will update. + +In addition to registers and memory, p-code has "unique" variables. +These are temporary variables used only within an instruction's implementation. +They are displayed in the right panel. +The table of variables works similarly to the Registers pane. +The columns are: + +* The **Unique** column gives the variable's name and size in bytes. +* The **Bytes** column gives the variable's value in bytes. +* The **Value** column gives the variable's value as an integer, an interpretation of the bytes in the machine's byte order. +* The **Type** column allows you to assign a type. This is ephemeral. +* The **Repr** column gives the variable's value according to the assigned type. + +As you step, you may notice the schedule changes. +It is displayed in the stepper's subtitle as well as the Threads panel's subtitle. +P-code stepping is denoted by the portion of the schedule following the dot. +**NOTE**: You cannot mix instruction steps with p-code op steps. +The instruction steps always precede the p-code ops. +If you click Step Into from the global toolbar in the middle of an instruction, the trailing p-code op steps will be removed and replaced with a single instruction step. +In most cases, this intuitively "finishes" the partial instruction. diff --git a/GhidraDocs/GhidraClass/Debugger/B3-Scripting.html b/GhidraDocs/GhidraClass/Debugger/B3-Scripting.html new file mode 100644 index 0000000000..04c7700543 --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/B3-Scripting.html @@ -0,0 +1,547 @@ + + + + + + + Ghidra Debugger + + + + + + +
+

Ghidra Debugger

+
+ +
+

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

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

+
Trace trace = getCurrentTrace();
+if (trace == null) {
+    throw new AssertionError("There is no active session");
+}
+

Now, check that the current program is termmines:

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

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

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

+
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.
  2. +
  3. Get the target running, and then wait for it to break.
  4. +
  5. Flush the pipelines.
  6. +
  7. Check if the expected conditions are met, esp., that the program +counter is where you expect.
  8. +
  9. If the conditions are not met, then let the target run again and +repeat.
  10. +
  11. Once the conditions are met, perform the desired actions.
  12. +
  13. Optionally remove your instrumentation and/or let the target +run.
  14. +
+
+

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.

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

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

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

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

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

+
+
+ + diff --git a/GhidraDocs/GhidraClass/Debugger/B3-Scripting.md b/GhidraDocs/GhidraClass/Debugger/B3-Scripting.md new file mode 100644 index 0000000000..7d1234c02f --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/B3-Scripting.md @@ -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 widthSyms = getSymbols("width", null); +if (widthSyms.isEmpty()) { + throw new AssertionError("Symbol 'width' is required"); +} +List heightSyms = getSymbols("height", null); +if (heightSyms.isEmpty()) { + throw new AssertionError("Symbol 'height' is required"); +} +List 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`. +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 timerSyms = getSymbols("timer", null); +if (timerSyms.isEmpty()) { + throw new AssertionError("Symbol 'timer' is required"); +} +List winFuncs = getGlobalFunctions("print_win"); +if (winFuncs.isEmpty()) { + throw new AssertionError("Function 'print_win' is required"); +} +List 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 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. diff --git a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html new file mode 100644 index 0000000000..4eff47a23e --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.html @@ -0,0 +1,1202 @@ + + + + + + + Ghidra Debugger + + + + + + +
+

Ghidra Debugger

+
+ +
+

P-code Modeling

+

This module assumes you have completed the Emulation and Scripting 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 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:

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

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

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

+ +

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:

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

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

+
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. +You can also examine the concrete implementation on byte arrays BytesPcodeArithmetic.

+
+
+

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:

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

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

+ +

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.

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

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

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

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

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

+ +

Anything more than that would require completely custom providers, +plugins, etc.

+
+
+
+ + diff --git a/GhidraDocs/GhidraClass/Debugger/B4-Modeling.md b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.md new file mode 100644 index 0000000000..2a491eb64b --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/B4-Modeling.md @@ -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 extends AnnotatedPcodeUseropLibrary { + 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 executor, + @OpState PcodeExecutorState state) { + PcodeArithmetic 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) executor).getThread() + .overrideCounter(space.getAddress(lReturn)); + } + + @PcodeUserop + public void __libc_strlen(@OpState PcodeExecutorState state) { + PcodeArithmetic 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 extends AnnotatedPcodeUseropLibrary { + private static final String SRC_RET = """ + RIP = *:8 RSP; + RSP = RSP + 8; + return [RIP]; + """; + private static final String SRC_STRLEN = """ + __result = 0; + + if (*:1 (str+__result) == 0 || __result >= maxlen) goto ; + __result = __result + 1; + goto ; + + """; + 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 executor, + @OpLibrary PcodeUseropLibrary 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 executor, + @OpLibrary PcodeUseropLibrary 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 + extends AnnotatedPcodeUseropLibrary { + 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 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 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 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 { + 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>`. +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` 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 map; + protected final AddressSpace space; + + protected ExprSpace(AddressSpace space, NavigableMap 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 extends + AbstractLongOffsetPcodeExecutorStatePiece { + + protected final AbstractSpaceMap spaceMap = newSpaceMap(); + + public AbstractExprPcodeExecutorStatePiece(Language language) { + super(language, BytesPcodeArithmetic.forLanguage(language), + ExprPcodeArithmetic.forLanguage(language)); + } + + protected abstract AbstractSpaceMap 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 getRegisterValuesFromSpace(S s, List registers) { + throw new UnsupportedOperationException(); + } +} + +public static class ExprPcodeExecutorStatePiece + extends AbstractExprPcodeExecutorStatePiece { + public ExprPcodeExecutorStatePiece(Language language) { + super(language); + } + + @Override + protected AbstractSpaceMap newSpaceMap() { + return new SimpleSpaceMap() { + @Override + protected ExprSpace newSpace(AddressSpace space) { + return new ExprSpace(space); + } + }; + } +} + +public static class BytesExprPcodeExecutorState extends PairedPcodeExecutorState { + public BytesExprPcodeExecutorState(PcodeExecutorStatePiece 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`. +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` interface. +These are typically singletons. + +```java {.numberLines} +public enum BytesExprEmulatorPartsFactory implements AuxEmulatorPartsFactory { + INSTANCE; + + @Override + public PcodeArithmetic getArithmetic(Language language) { + return ExprPcodeArithmetic.forLanguage(language); + } + + @Override + public PcodeUseropLibrary> createSharedUseropLibrary( + AuxPcodeEmulator emulator) { + return PcodeUseropLibrary.nil(); + } + + @Override + public PcodeUseropLibrary> createLocalUseropStub( + AuxPcodeEmulator emulator) { + return PcodeUseropLibrary.nil(); + } + + @Override + public PcodeUseropLibrary> createLocalUseropLibrary( + AuxPcodeEmulator emulator, PcodeThread> thread) { + return PcodeUseropLibrary.nil(); + } + + @Override + public PcodeExecutorState> createSharedState( + AuxPcodeEmulator emulator, BytesPcodeExecutorStatePiece concrete) { + return new BytesExprPcodeExecutorState(concrete); + } + + @Override + public PcodeExecutorState> createLocalState( + AuxPcodeEmulator emulator, PcodeThread> thread, + BytesPcodeExecutorStatePiece concrete) { + return new BytesExprPcodeExecutorState(concrete); + } +} + +public class BytesExprPcodeEmulator extends AuxPcodeEmulator { + public BytesExprPcodeEmulator(Language language) { + super(language); + } + + @Override + protected AuxEmulatorPartsFactory 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> state = emu.getSharedState(); + state.setVar(currentAddress, 4, true, + Pair.of(new byte[] { 1, 2, 3, 4 }, new VarExpr(currentAddress, 4))); + PcodeThread> 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 property; + + public ExprTraceSpace(AddressSpace space, PcodeTracePropertyAccess 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 into) { + if (space.isUniqueSpace()) { + return; + } + + for (Entry 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 + implements TracePcodeExecutorStatePiece { + public static final String NAME = "Taint"; + + protected final PcodeTraceDataAccess data; + protected final PcodeTracePropertyAccess 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 newSpaceMap() { + return new CacheingSpaceMap, ExprTraceSpace>() { + @Override + protected PcodeTracePropertyAccess getBacking(AddressSpace space) { + return property; + } + + @Override + protected ExprTraceSpace newSpace(AddressSpace space, + PcodeTracePropertyAccess backing) { + return new ExprTraceSpace(space, property); + } + }; + } + + @Override + public ExprTracePcodeExecutorStatePiece fork() { + throw new UnsupportedOperationException(); + } + + @Override + public void writeDown(PcodeTraceDataAccess into) { + PcodeTracePropertyAccess 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 { + public BytesExprDebuggerPcodeEmulator(PcodeDebuggerAccess access) { + super(access); + } + + @Override + protected AuxDebuggerEmulatorPartsFactory 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. \ No newline at end of file diff --git a/GhidraDocs/GhidraClass/Debugger/Makefile b/GhidraDocs/GhidraClass/Debugger/Makefile new file mode 100644 index 0000000000..7f9213e501 --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/Makefile @@ -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 diff --git a/GhidraDocs/GhidraClass/Debugger/README.md b/GhidraDocs/GhidraClass/Debugger/README.md new file mode 100644 index 0000000000..2ce74d918b --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/README.md @@ -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) diff --git a/GhidraDocs/GhidraClass/Debugger/gdb_syntax.xml b/GhidraDocs/GhidraClass/Debugger/gdb_syntax.xml new file mode 100644 index 0000000000..8e2cc7fcfd --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/gdb_syntax.xml @@ -0,0 +1,43 @@ + + + + + + + + + target + remote + break + new-ui + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/CustomLibraryScript.java b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/CustomLibraryScript.java new file mode 100644 index 0000000000..0f14fa38c8 --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/CustomLibraryScript.java @@ -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 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 thread = emu.newThread(); + + // TODO: Initialize the thread's registers + + while (true) { + monitor.checkCanceled(); + thread.stepInstruction(100); + } + } +} diff --git a/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/DumpBoardScript.java b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/DumpBoardScript.java new file mode 100644 index 0000000000..9f6538745d --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/DumpBoardScript.java @@ -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 widthSyms = getSymbols("width", null); + if (widthSyms.isEmpty()) { + throw new AssertionError("Symbol 'width' is required"); + } + List heightSyms = getSymbols("height", null); + if (heightSyms.isEmpty()) { + throw new AssertionError("Symbol 'height' is required"); + } + List 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)); + } + } + } + } +} diff --git a/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/InstallCustomLibraryScript.java b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/InstallCustomLibraryScript.java new file mode 100644 index 0000000000..c991629623 --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/InstallCustomLibraryScript.java @@ -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 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()); + } +} diff --git a/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/InstallExprEmulatorScript.java b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/InstallExprEmulatorScript.java new file mode 100644 index 0000000000..c1dde686da --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/InstallExprEmulatorScript.java @@ -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()); + } +} diff --git a/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ModelingScript.java b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ModelingScript.java new file mode 100644 index 0000000000..9f1454da33 --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ModelingScript.java @@ -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 extends AnnotatedPcodeUseropLibrary { + 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 executor, + @OpState PcodeExecutorState state) { + PcodeArithmetic 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) executor).getThread() + .overrideCounter(space.getAddress(lReturn)); + } + + @PcodeUserop + public void __libc_strlen(@OpState PcodeExecutorState state) { + PcodeArithmetic 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 extends AnnotatedPcodeUseropLibrary { + private static final String SRC_RET = """ + RIP = *:8 RSP; + RSP = RSP + 8; + return [RIP]; + """; + private static final String SRC_STRLEN = """ + __result = 0; + + if (*:1 (str+__result) == 0 || __result >= maxlen) goto ; + __result = __result + 1; + goto ; + + """; + 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 executor, + @OpLibrary PcodeUseropLibrary 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 executor, + @OpLibrary PcodeUseropLibrary 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 + extends AnnotatedPcodeUseropLibrary { + 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 { + 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 map; + protected final AddressSpace space; + + protected ExprSpace(AddressSpace space, NavigableMap 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 + extends + AbstractLongOffsetPcodeExecutorStatePiece { + + protected final AbstractSpaceMap spaceMap = newSpaceMap(); + + public AbstractBytesExprPcodeExecutorStatePiece(Language language) { + super(language, BytesPcodeArithmetic.forLanguage(language), + ExprPcodeArithmetic.forLanguage(language)); + } + + protected abstract AbstractSpaceMap 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 getRegisterValuesFromSpace(S s, List registers) { + throw new UnsupportedOperationException(); + } + } + + public static class ExprPcodeExecutorStatePiece + extends AbstractBytesExprPcodeExecutorStatePiece { + public ExprPcodeExecutorStatePiece(Language language) { + super(language); + } + + @Override + protected AbstractSpaceMap newSpaceMap() { + return new SimpleSpaceMap() { + @Override + protected ExprSpace newSpace(AddressSpace space) { + return new ExprSpace(space); + } + }; + } + } + + public static class BytesExprPcodeExecutorState extends PairedPcodeExecutorState { + public BytesExprPcodeExecutorState(PcodeExecutorStatePiece concrete) { + super(new PairedPcodeExecutorStatePiece<>(concrete, + new ExprPcodeExecutorStatePiece(concrete.getLanguage()))); + } + } + + // ---------------------- + + public enum BytesExprEmulatorPartsFactory implements AuxEmulatorPartsFactory { + INSTANCE; + + @Override + public PcodeArithmetic getArithmetic(Language language) { + return ExprPcodeArithmetic.forLanguage(language); + } + + @Override + public PcodeUseropLibrary> createSharedUseropLibrary( + AuxPcodeEmulator emulator) { + return PcodeUseropLibrary.nil(); + } + + @Override + public PcodeUseropLibrary> createLocalUseropStub( + AuxPcodeEmulator emulator) { + return PcodeUseropLibrary.nil(); + } + + @Override + public PcodeUseropLibrary> createLocalUseropLibrary( + AuxPcodeEmulator emulator, PcodeThread> thread) { + return PcodeUseropLibrary.nil(); + } + + @Override + public PcodeExecutorState> createSharedState( + AuxPcodeEmulator emulator, BytesPcodeExecutorStatePiece concrete) { + return new BytesExprPcodeExecutorState(concrete); + } + + @Override + public PcodeExecutorState> createLocalState( + AuxPcodeEmulator emulator, PcodeThread> thread, + BytesPcodeExecutorStatePiece concrete) { + return new BytesExprPcodeExecutorState(concrete); + } + } + + public class BytesExprPcodeEmulator extends AuxPcodeEmulator { + public BytesExprPcodeEmulator(Language language) { + super(language); + } + + @Override + protected AuxEmulatorPartsFactory getPartsFactory() { + return BytesExprEmulatorPartsFactory.INSTANCE; + } + } + + // ---------------------- + + public static class ExprTraceSpace extends ExprSpace { + protected final PcodeTracePropertyAccess property; + + public ExprTraceSpace(AddressSpace space, PcodeTracePropertyAccess 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 into) { + if (space.isUniqueSpace()) { + return; + } + + for (Entry 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 + implements TracePcodeExecutorStatePiece { + public static final String NAME = "Taint"; + + protected final PcodeTraceDataAccess data; + protected final PcodeTracePropertyAccess 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 newSpaceMap() { + return new CacheingSpaceMap, ExprTraceSpace>() { + @Override + protected PcodeTracePropertyAccess getBacking(AddressSpace space) { + return property; + } + + @Override + protected ExprTraceSpace newSpace(AddressSpace space, + PcodeTracePropertyAccess backing) { + return new ExprTraceSpace(space, property); + } + }; + } + + @Override + public BytesExprTracePcodeExecutorStatePiece fork() { + throw new UnsupportedOperationException(); + } + + @Override + public void writeDown(PcodeTraceDataAccess into) { + PcodeTracePropertyAccess property = into.getPropertyAccess(NAME, String.class); + for (ExprTraceSpace space : spaceMap.values()) { + space.writeDown(property); + } + } + } + + public static class BytesExprTracePcodeExecutorState + extends PairedTracePcodeExecutorState { + + public BytesExprTracePcodeExecutorState( + TracePcodeExecutorStatePiece concrete) { + super(new PairedTracePcodeExecutorStatePiece<>(concrete, + new BytesExprTracePcodeExecutorStatePiece(concrete.getData()))); + } + } + + enum BytesExprDebuggerEmulatorPartsFactory implements AuxDebuggerEmulatorPartsFactory { + INSTANCE; + + @Override + public PcodeArithmetic getArithmetic(Language language) { + return ExprPcodeArithmetic.forLanguage(language); + } + + @Override + public PcodeUseropLibrary> createSharedUseropLibrary( + AuxPcodeEmulator emulator) { + return PcodeUseropLibrary.nil(); + } + + @Override + public PcodeUseropLibrary> createLocalUseropStub( + AuxPcodeEmulator emulator) { + return PcodeUseropLibrary.nil(); + } + + @Override + public PcodeUseropLibrary> createLocalUseropLibrary( + AuxPcodeEmulator emulator, + PcodeThread> thread) { + return PcodeUseropLibrary.nil(); + } + + @Override + public PcodeExecutorState> createSharedState( + AuxPcodeEmulator emulator, + BytesPcodeExecutorStatePiece concrete) { + return new BytesExprPcodeExecutorState(concrete); + } + + @Override + public PcodeExecutorState> createLocalState( + AuxPcodeEmulator emulator, + PcodeThread> thread, + BytesPcodeExecutorStatePiece concrete) { + return new BytesExprPcodeExecutorState(concrete); + } + + @Override + public TracePcodeExecutorState> createTraceSharedState( + AuxTracePcodeEmulator emulator, + BytesTracePcodeExecutorStatePiece concrete) { + return new BytesExprTracePcodeExecutorState(concrete); + } + + @Override + public TracePcodeExecutorState> createTraceLocalState( + AuxTracePcodeEmulator emulator, + PcodeThread> thread, + BytesTracePcodeExecutorStatePiece concrete) { + return new BytesExprTracePcodeExecutorState(concrete); + } + + @Override + public TracePcodeExecutorState> createDebuggerSharedState( + AuxDebuggerPcodeEmulator emulator, + RWTargetMemoryPcodeExecutorStatePiece concrete) { + return new BytesExprTracePcodeExecutorState(concrete); + } + + @Override + public TracePcodeExecutorState> createDebuggerLocalState( + AuxDebuggerPcodeEmulator emulator, + PcodeThread> thread, + RWTargetRegistersPcodeExecutorStatePiece concrete) { + return new BytesExprTracePcodeExecutorState(concrete); + } + } + + public static class BytesExprDebuggerPcodeEmulator extends AuxDebuggerPcodeEmulator { + public BytesExprDebuggerPcodeEmulator(PcodeDebuggerAccess access) { + super(access); + } + + @Override + protected AuxDebuggerEmulatorPartsFactory 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> state = emu.getSharedState(); + state.setVar(currentAddress, 4, true, + Pair.of(new byte[] { 1, 2, 3, 4 }, new VarExpr(currentAddress, 4))); + PcodeThread> thread = emu.newThread(); + // TODO: Initialize the thread + while (true) { + monitor.checkCancelled(); + thread.stepInstruction(100); + } + } +} diff --git a/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ZeroTimerScript.java b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ZeroTimerScript.java new file mode 100644 index 0000000000..f156d67526 --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/ghidra_scripts/ZeroTimerScript.java @@ -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 timerSyms = getSymbols("timer", null); + if (timerSyms.isEmpty()) { + throw new AssertionError("Symbol 'timer' is required"); + } + List winFuncs = getGlobalFunctions("print_win"); + if (winFuncs.isEmpty()) { + throw new AssertionError("Function 'print_win' is required"); + } + List 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 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(); + } +} diff --git a/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_EmptyAfterLaunch.png b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_EmptyAfterLaunch.png new file mode 100644 index 0000000000..d83ab47b05 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_EmptyAfterLaunch.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_MissingModuleNote.png b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_MissingModuleNote.png new file mode 100644 index 0000000000..2ff7492afb Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_MissingModuleNote.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_PopAfterSRandRand.png b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_PopAfterSRandRand.png new file mode 100644 index 0000000000..2868bc5c31 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_PopAfterSRandRand.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_SeedValueAfterBreakSRand.png b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_SeedValueAfterBreakSRand.png new file mode 100644 index 0000000000..0da30f4413 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_SeedValueAfterBreakSRand.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_SyncedAfterImportLibC.png b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_SyncedAfterImportLibC.png new file mode 100644 index 0000000000..55a28d5920 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Breakpoints_SyncedAfterImportLibC.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Emulation_LazyStaleListing.png b/GhidraDocs/GhidraClass/Debugger/images/Emulation_LazyStaleListing.png new file mode 100644 index 0000000000..3beb8399fb Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Emulation_LazyStaleListing.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Emulation_ListingAfterResume.png b/GhidraDocs/GhidraClass/Debugger/images/Emulation_ListingAfterResume.png new file mode 100644 index 0000000000..7e4952511d Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Emulation_ListingAfterResume.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Emulation_ListingForCmdlineSet.png b/GhidraDocs/GhidraClass/Debugger/images/Emulation_ListingForCmdlineSet.png new file mode 100644 index 0000000000..080487a407 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Emulation_ListingForCmdlineSet.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Emulation_PcodeStepper.png b/GhidraDocs/GhidraClass/Debugger/images/Emulation_PcodeStepper.png new file mode 100644 index 0000000000..19fe541ca8 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Emulation_PcodeStepper.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Emulation_WatchesForCmdline.png b/GhidraDocs/GhidraClass/Debugger/images/Emulation_WatchesForCmdline.png new file mode 100644 index 0000000000..51db1ca272 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Emulation_WatchesForCmdline.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Emulation_WatchesForCmdlineSet.png b/GhidraDocs/GhidraClass/Debugger/images/Emulation_WatchesForCmdlineSet.png new file mode 100644 index 0000000000..f4003839b6 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Emulation_WatchesForCmdlineSet.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_DisassemblyAfterLaunch.png b/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_DisassemblyAfterLaunch.png new file mode 100644 index 0000000000..6cc2a5f7d9 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_DisassemblyAfterLaunch.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_ToolWSpecimen.png b/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_ToolWSpecimen.png new file mode 100644 index 0000000000..8c9fa927cd Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/GettingStarted_ToolWSpecimen.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_CopyNcursesInto.png b/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_CopyNcursesInto.png new file mode 100644 index 0000000000..c5713a9edd Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_CopyNcursesInto.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_ModulesAfterLaunch.png b/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_ModulesAfterLaunch.png new file mode 100644 index 0000000000..78eba91fea Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_ModulesAfterLaunch.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_RegionsAfterLaunch.png b/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_RegionsAfterLaunch.png new file mode 100644 index 0000000000..bcc43ace86 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_RegionsAfterLaunch.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_StaticMappingAfterLaunch.png b/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_StaticMappingAfterLaunch.png new file mode 100644 index 0000000000..52af3e3264 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/MemoryMap_StaticMappingAfterLaunch.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Navigation_CompareTimes.png b/GhidraDocs/GhidraClass/Debugger/images/Navigation_CompareTimes.png new file mode 100644 index 0000000000..4d05cb070a Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Navigation_CompareTimes.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Navigation_DialogCompareTimes.png b/GhidraDocs/GhidraClass/Debugger/images/Navigation_DialogCompareTimes.png new file mode 100644 index 0000000000..46fe31fdc5 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Navigation_DialogCompareTimes.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Navigation_StackInCallRand.png b/GhidraDocs/GhidraClass/Debugger/images/Navigation_StackInCallRand.png new file mode 100644 index 0000000000..623c8b42f5 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Navigation_StackInCallRand.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Navigation_ThreadsInCallRand.png b/GhidraDocs/GhidraClass/Debugger/images/Navigation_ThreadsInCallRand.png new file mode 100644 index 0000000000..83a30d9b57 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Navigation_ThreadsInCallRand.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/Navigation_TimeAfterCallSRandCallRand.png b/GhidraDocs/GhidraClass/Debugger/images/Navigation_TimeAfterCallSRandCallRand.png new file mode 100644 index 0000000000..4407aa6833 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/Navigation_TimeAfterCallSRandCallRand.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/RemoteTargets_Gadp.png b/GhidraDocs/GhidraClass/Debugger/images/RemoteTargets_Gadp.png new file mode 100644 index 0000000000..a838a21490 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/RemoteTargets_Gadp.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/RemoteTargets_GdbOverSsh.png b/GhidraDocs/GhidraClass/Debugger/images/RemoteTargets_GdbOverSsh.png new file mode 100644 index 0000000000..47e54d4587 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/RemoteTargets_GdbOverSsh.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/State_BytesStackAfterCallRand.png b/GhidraDocs/GhidraClass/Debugger/images/State_BytesStackAfterCallRand.png new file mode 100644 index 0000000000..9e7b941f4a Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/State_BytesStackAfterCallRand.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/State_ListingAfterCallRand.png b/GhidraDocs/GhidraClass/Debugger/images/State_ListingAfterCallRand.png new file mode 100644 index 0000000000..a58b8664c9 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/State_ListingAfterCallRand.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/State_ListingStackAfterCallRand.png b/GhidraDocs/GhidraClass/Debugger/images/State_ListingStackAfterCallRand.png new file mode 100644 index 0000000000..0ee46d004a Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/State_ListingStackAfterCallRand.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/State_RegistersAfterCallRand.png b/GhidraDocs/GhidraClass/Debugger/images/State_RegistersAfterCallRand.png new file mode 100644 index 0000000000..4de94d6c5d Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/State_RegistersAfterCallRand.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/State_WatchesInCallSRand.png b/GhidraDocs/GhidraClass/Debugger/images/State_WatchesInCallSRand.png new file mode 100644 index 0000000000..f3fafc9bf1 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/State_WatchesInCallSRand.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/add.png b/GhidraDocs/GhidraClass/Debugger/images/add.png new file mode 100644 index 0000000000..6332fefea4 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/add.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/autoread.png b/GhidraDocs/GhidraClass/Debugger/images/autoread.png new file mode 100644 index 0000000000..eb7491aefc Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/autoread.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/breakpoint-disable.png b/GhidraDocs/GhidraClass/Debugger/images/breakpoint-disable.png new file mode 100644 index 0000000000..4d3a03b13c Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/breakpoint-disable.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/breakpoint-enable-ineff.png b/GhidraDocs/GhidraClass/Debugger/images/breakpoint-enable-ineff.png new file mode 100644 index 0000000000..b417f1f897 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/breakpoint-enable-ineff.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/breakpoint-enable.png b/GhidraDocs/GhidraClass/Debugger/images/breakpoint-enable.png new file mode 100644 index 0000000000..2d618a99a7 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/breakpoint-enable.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/breakpoint-overlay-inconsistent.png b/GhidraDocs/GhidraClass/Debugger/images/breakpoint-overlay-inconsistent.png new file mode 100644 index 0000000000..6eafda829e Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/breakpoint-overlay-inconsistent.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/camera-photo.png b/GhidraDocs/GhidraClass/Debugger/images/camera-photo.png new file mode 100644 index 0000000000..1e8e88636c Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/camera-photo.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/connect.png b/GhidraDocs/GhidraClass/Debugger/images/connect.png new file mode 100644 index 0000000000..219885a15e Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/connect.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/debugger.png b/GhidraDocs/GhidraClass/Debugger/images/debugger.png new file mode 100644 index 0000000000..cec4164be4 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/debugger.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/disconnect.png b/GhidraDocs/GhidraClass/Debugger/images/disconnect.png new file mode 100644 index 0000000000..de33d19e82 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/disconnect.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/interrupt.png b/GhidraDocs/GhidraClass/Debugger/images/interrupt.png new file mode 100644 index 0000000000..f6ae4fc5db Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/interrupt.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/kill.png b/GhidraDocs/GhidraClass/Debugger/images/kill.png new file mode 100644 index 0000000000..4599a64450 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/kill.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/process.png b/GhidraDocs/GhidraClass/Debugger/images/process.png new file mode 100644 index 0000000000..4aef23713c Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/process.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/record.png b/GhidraDocs/GhidraClass/Debugger/images/record.png new file mode 100644 index 0000000000..69f9ee123a Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/record.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/register-marker.png b/GhidraDocs/GhidraClass/Debugger/images/register-marker.png new file mode 100644 index 0000000000..d6506a58f3 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/register-marker.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/resume.png b/GhidraDocs/GhidraClass/Debugger/images/resume.png new file mode 100644 index 0000000000..b9f6990bff Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/resume.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/select-registers.png b/GhidraDocs/GhidraClass/Debugger/images/select-registers.png new file mode 100644 index 0000000000..c8ffc2ba31 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/select-registers.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/skipover.png b/GhidraDocs/GhidraClass/Debugger/images/skipover.png new file mode 100644 index 0000000000..3dd8717a12 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/skipover.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/stepback.png b/GhidraDocs/GhidraClass/Debugger/images/stepback.png new file mode 100644 index 0000000000..a1fad09fe5 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/stepback.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/stepinto.png b/GhidraDocs/GhidraClass/Debugger/images/stepinto.png new file mode 100644 index 0000000000..4c92124af4 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/stepinto.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/steplast.png b/GhidraDocs/GhidraClass/Debugger/images/steplast.png new file mode 100644 index 0000000000..0c5c187b87 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/steplast.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/stepout.png b/GhidraDocs/GhidraClass/Debugger/images/stepout.png new file mode 100644 index 0000000000..1e8886167a Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/stepout.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/stepover.png b/GhidraDocs/GhidraClass/Debugger/images/stepover.png new file mode 100644 index 0000000000..72d995c318 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/stepover.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/table_relationship.png b/GhidraDocs/GhidraClass/Debugger/images/table_relationship.png new file mode 100644 index 0000000000..28b8505c0e Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/table_relationship.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/images/view-refresh.png b/GhidraDocs/GhidraClass/Debugger/images/view-refresh.png new file mode 100644 index 0000000000..cab4d02c75 Binary files /dev/null and b/GhidraDocs/GhidraClass/Debugger/images/view-refresh.png differ diff --git a/GhidraDocs/GhidraClass/Debugger/links-to-html.lua b/GhidraDocs/GhidraClass/Debugger/links-to-html.lua new file mode 100644 index 0000000000..68b7c40c15 --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/links-to-html.lua @@ -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 + diff --git a/GhidraDocs/GhidraClass/Debugger/navhead.htm b/GhidraDocs/GhidraClass/Debugger/navhead.htm new file mode 100644 index 0000000000..73c401f17d --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/navhead.htm @@ -0,0 +1,12 @@ + diff --git a/GhidraDocs/GhidraClass/Debugger/sleigh_syntax.xml b/GhidraDocs/GhidraClass/Debugger/sleigh_syntax.xml new file mode 100644 index 0000000000..3e67f15011 --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/sleigh_syntax.xml @@ -0,0 +1,85 @@ + + + + + +]> + + + + goto + if + return + + + + scarry + carry + sborrow + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GhidraDocs/GhidraClass/Debugger/style.css b/GhidraDocs/GhidraClass/Debugger/style.css new file mode 100644 index 0000000000..f9304a1b91 --- /dev/null +++ b/GhidraDocs/GhidraClass/Debugger/style.css @@ -0,0 +1,162 @@ +/* ### + * 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. + */ +body { + font-family: sans-serif; + background-color: #222; + color: #eee; + padding: 0; + margin: 0; + counter-reset: figure; +} + +header#title-block-header { + overflow: hidden; + background-color: #224; + position: fixed; + z-index: 1; + top: 0; + width: 100%; + height: 1.5cm; +} + +header#title-block-header h1 { + padding: 0.2cm; + margin: 0; + color: #eee; +} + +header#nav { + overflow: hidden; + background-color: #224; + position: fixed; + z-index: 1; + top: 1.5cm; + width: 100%; + height: 0.5cm; +} + +header#nav a { + font-size: smaller; + padding: 0 0.5em 0 0.5em; +} + +header#nav a.beginner + a.advanced { + border-left: 3px solid #eee; +} + +nav#TOC { + overflow: auto; + background-color: #235; + position: fixed; + height: calc(100% - 2cm - 0.4cm); + width: calc(7cm - 0.4cm); + top: 2cm; + left: 0; + padding: 0.2cm; +} + +nav#TOC ul { + padding: 0; + margin: 0; +} + +nav#TOC ul li { + padding: 0; + margin: 0 0 0 1em; +} + +section.level1 { + position: fixed; + left: 7cm; + top: 2cm; + width: calc(100% - 7cm - 0.4cm); + height: calc(100% - 2cm - 0.4cm); + overflow: auto; + padding: 0.2cm; +} + +section h1 { + border-bottom: 2pt solid #234; +} + +section h2 { + border-bottom: 1pt solid #234; +} + +a { + color: #cec; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +img { + max-width: 100%; +} + +div.sourceCode { + position: relative; + color: black; + background-color: #fff; + padding: 0.4cm 0.2cm 0.2cm 0.2cm; + border-radius: 3px; + box-shadow: 0 0 5px black inset; +} + +pre > code.sourceCode > span > a:first-child::before { + text-decoration: none; +} + +div.sourceCode:has(pre) > pre::before { + font-size: smaller; + font-style: italic; + color: #888; + position: absolute; + top: 0px; + right: 1em; +} + +div.sourceCode:has(pre.bash) > pre::before { + content: 'Shell'; +} + +div.sourceCode:has(pre.java) > pre::before { + content: 'Java'; +} + +div.sourceCode:has(pre.c) > pre::before { + content: 'C'; +} + +div.sourceCode:has(pre.gdb) > pre::before { + content: 'GDB'; +} + +div.sourceCode:has(pre.sleigh) > pre::before { + content: 'Sleigh'; +} + +figcaption::before { + counter-increment: figure; + content: 'Figure ' counter(figure) ': '; + font-weight: bold; +} + +figcaption { +} + diff --git a/GhidraDocs/GhidraClass/ExerciseFiles/Debugger/Makefile b/GhidraDocs/GhidraClass/ExerciseFiles/Debugger/Makefile new file mode 100644 index 0000000000..1d26b815e9 --- /dev/null +++ b/GhidraDocs/GhidraClass/ExerciseFiles/Debugger/Makefile @@ -0,0 +1,14 @@ + +default: all + +all: termmines anyptracer + +termmines: termmines.c + $(CC) -O2 -o $@ $^ -lncurses + strip -Kmain -K_main $@ + +termmines-dev: termmines.c + $(CC) -O0 -g -o $@ $^ -lncurses + +anyptracer: anyptracer.c + $(CC) -o $@ $^ diff --git a/GhidraDocs/GhidraClass/ExerciseFiles/Debugger/anyptracer.c b/GhidraDocs/GhidraClass/ExerciseFiles/Debugger/anyptracer.c new file mode 100644 index 0000000000..5c1fc00ae8 --- /dev/null +++ b/GhidraDocs/GhidraClass/ExerciseFiles/Debugger/anyptracer.c @@ -0,0 +1,37 @@ +/* ### + * 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. + */ +#include +#include +#include +#include + +int main(int argc, char *argv[], char *envp[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %s COMMAND\n\ +\n\ + COMMAND A shell command to execute as in /bin/sh -c COMMAND. This must be\n\ + a single argument. To run a command with multiple arguments,\n\ + enclose the full command in quotations.\n\ +\n\ +Execute the given COMMAND allowing any other process to attach to it. This is\n\ +done using prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY), so that on Linux systems\n\ +with Yama ptrace_scope=1, gdb, strace, lldb, etc., can attach to it.\n\ +", argv[0]); + } + char* shell = getpwuid(geteuid())->pw_shell; + prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY); + execl(shell, shell, "-c", argv[1], (char *) NULL); +} diff --git a/GhidraDocs/GhidraClass/ExerciseFiles/Debugger/termmines.c b/GhidraDocs/GhidraClass/ExerciseFiles/Debugger/termmines.c new file mode 100644 index 0000000000..97c115330e --- /dev/null +++ b/GhidraDocs/GhidraClass/ExerciseFiles/Debugger/termmines.c @@ -0,0 +1,570 @@ +/* ### + * 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. + */ +#include +#include +#include +#include + +#include + +#define MIN_WIDTH 10 +#define MAX_WIDTH 30 + +#define MIN_HEIGHT 10 +#define MAX_HEIGHT 30 + +#define RAW(x, y) (state.cells[y][x]) +#define CELL(x, y) RAW(x+1, y+1) + +#define MASK_MINE 0x80 +#define IS_MINE(x) ((x) & MASK_MINE) +#define SET_MINE(x) ((x) |= MASK_MINE) + +#define MASK_FLAG 0x40 +#define IS_FLAG(x) ((x) & MASK_FLAG) +#define SET_FLAG(x) ((x) |= MASK_FLAG) +#define CLR_FLAG(x) ((x) &=~MASK_FLAG) +#define TGL_FLAG(x) ((x) ^= MASK_FLAG) + +#define MASK_OPEN 0x20 +#define IS_OPEN(x) ((x) & MASK_OPEN) +#define SET_OPEN(x) ((x) |= MASK_OPEN) + +#define MASK_AROUND 0x0f +#define AROUND(x) ((x) & MASK_AROUND) + +typedef struct GAME_STATE { + int width; + int height; + int mines; + int start; + int alive; + int x; + int y; + char cells[MAX_HEIGHT + 2][MAX_WIDTH + 2]; +} GAME_STATE, *PGAME_STATE; + +GAME_STATE state = { 9, 9, 10 }; + +int cheat_seq[] = { + KEY_UP, + KEY_UP, + KEY_DOWN, + KEY_DOWN, + KEY_LEFT, + KEY_RIGHT, + KEY_LEFT, + KEY_RIGHT, + 'b', + 'a', + '\n' +}; + +#define CHEAT_COUNT (sizeof(cheat_seq)/sizeof(cheat_seq[0])) + +int cheat = 0; + +void print_usage(char *cmd) { + fprintf(stderr, + "Usage: %s [-s SKILL] [-W WIDTH] [-H HEIGHT] [-M MINES] [-h]\n\ +\n\ + -s SKILL Select a preset dimension and mine count. One of:\n\ + Beginner: 9x9 with 10 mines. (default)\n\ + Intermediate: 16x16 with 40 mines.\n\ + Advanced: 30x16 with 99 mines.\n\ + -W WIDTH Set the width of the game board.\n\ + Must be between 8 and 30\n\ + -H HEIGHT Set the height of the game board.\n\ + Must be between 8 and 24\n\ + -M MINES Set the number of mines.\n\ + Must be between 10 and (WIDTH-1)*(HEIGHT-1)\n\ + Set the WIDTH and HEIGHT first\n\ + -h Print this help\n\ +\n\ +Gameplay:\n\ +\n\ +It's a fairly standard game of minesweeper, except the timer is not displayed\n\ +until you win. Refer to other resources for the rules. Here's an example board:\n\ +\n\ ++ +---+---+\n\ + 1 | : | : |\n\ + +---+---+\n\ + 1 2 2 1 1 | : | : |\n\ + +---+ +---+---+---+---+\n\ + 1 | # |[*]| : | : | : | : |\n\ + +---+---+---+---+---+---+\n\ + 1 2 2 | : | : | : | : | : | : |\n\ ++---+---+---+---+---+---+---+---+---+\n\ +| : | : | : | : | : | : | : | : | : |\n\ ++---+---+---+---+---+---+---+---+---+\n\ +| : | : | : | : | : | : | : | : | : |\n\ ++---+---+---+---+---+---+---+---+---+\n\ +| : | : | : | : | : | : | : | : | : |\n\ ++---+---+---+---+---+---+---+---+---+\n\ +| : | : | : | : | : | : | : | : | : |\n\ ++---+---+---+---+---+---+---+---+---+\n\ +| : | : | : | : | : | : | : | : | : |\n\ ++---+---+---+---+---+---+---+---+---+\n\ +\n\ +Concealed cells are indicated by a colon (:).\n\ +Flagged cells are indicated by a hash (#).\n\ +Mines are indicated by an asterisk (*).\n\ +Empty cells are indicated by empty space. These have no mines around them.\n\ +Non-empty open cells are indicated by the number of surrounding mines.\n\ +The cursor is indicated by brackets [ ] around the cell's contents.\n\ +\n\ +ARROW KEYS: Navigate the cursor around the board.\n\ +SPACE: Clear a concealed cell, or\n\ + Clear all cells surrounding an open cell. This is only allowed when\n\ + the number of flagged cells surrounding the cursor matches the\n\ + number of surrounding mines indicated by the cell's contents.\n\ +F: Flag a cell believed to conceal a mine, or\n\ + Unflag a cell no longer believed to conceal a mine. A flagged cell\n\ + cannot be cleared.\n\ +SHIFT+R: Start a new game.\n\ +CTRL+C: Quit.\n\ +", + cmd); +} + +void timer_func() { +} + +#define ARGS_ERR() do { \ + fprintf(stderr, "Invalid argument: %s\n", argv[i]); \ + print_usage(argv[0]); \ + exit(-1); \ +} while (0) + +#define REQ_ARG(name) do { \ + i++; \ + if (i >= argc) { \ + fprintf(stderr, "Missing argument: %s\n", name); \ + print_usage(argv[0]); \ + exit(-1); \ + } \ +} while (0) + +void parse_skill(char *arg) { + if (strcmp("Beginner", arg) == 0) { + state.width = 9; + state.height = 9; + state.mines = 10; + } + else if (strcmp("Intermediate", arg) == 0) { + state.width = 16; + state.height = 16; + state.mines = 40; + } + else if (strcmp("Advanced", arg) == 0) { + state.width = 30; + state.height = 16; + state.mines = 99; + } + else { + fprintf(stderr, "Invalid SKILL: %s\n", arg); + exit(-1); + } +} + +unsigned long parse_int(char *a, char *name, + unsigned long min, unsigned long max) { + char *e; + unsigned long val = strtoul(a, &e, 10); + if (*e != 0 || val < min || max < val) { + fprintf(stderr, "Invalid %s: %s. Must be an integer between %d and %d.\n", + name, a, min, max); + exit(-1); + } +} + +void parse_width(char *arg) { + state.width = parse_int(arg, "WIDTH", 8, 30); +} + +void parse_height(char *arg) { + state.height = parse_int(arg, "HEIGHT", 8, 24); +} + +void parse_mines(char *arg) { + state.mines = parse_int(arg, "MINES", 10, (state.width - 1) * (state.height - 1)); +} + +void parse_args(int argc, char **argv) { + if (argc == 0) { + print_usage("?????"); + exit(-1); + } + for (int i = 1; i < argc; i++) { + if (strnlen(argv[i], 3) != 2 || argv[i][0] != '-') { + ARGS_ERR(); + } + switch (argv[i][1]) { + case 's': + REQ_ARG("SKILL"); + parse_skill(argv[i]); + break; + case 'W': + REQ_ARG("WIDTH"); + parse_width(argv[i]); + break; + case 'H': + REQ_ARG("HEIGHT"); + parse_height(argv[i]); + break; + case 'M': + REQ_ARG("MINES"); + parse_mines(argv[i]); + break; + case 'h': + print_usage(argv[0]); + exit(0); + break; + default: + ARGS_ERR(); + } + } +} + +int rand_rng(int n) { + return rand() % n; +} + +int count_around(int X, int Y) { + int count = 0; + for (int y = Y - 1; y <= Y + 1; y++) { + for (int x = X - 1; x <= X + 1; x++) { + if (IS_MINE(RAW(x,y))) { + count++; + } + } + } + return count; +} + +int count_flags_around(int X, int Y) { + int count = 0; + for (int y = Y - 1; y <= Y + 1; y++) { + for (int x = X - 1; x <= X + 1; x++) { + if (IS_FLAG(CELL(x,y))) { + count++; + } + } + } + return count; +} + +void setup_board() { + memset(state.cells, 0, sizeof(state.cells)); + for (int mines = 0; mines < state.mines;) { + int x = rand_rng(state.width); + int y = rand_rng(state.height); + if (!IS_MINE(CELL(x,y))) { + SET_MINE(CELL(x,y)); + mines++; + } + } + for (int y = 1; y <= state.height; y++) { + for (int x = 1; x <= state.width; x++) { + if (!IS_MINE(RAW(x,y))) { + RAW(x,y) |= count_around(x, y); + } + } + } + for (int y = 1; y <= state.height; y++) { + SET_OPEN(RAW(0,y)); + SET_OPEN(RAW(state.width+1,y)); + } + for (int x = 1; x <= state.width; x++) { + SET_OPEN(RAW(x,0)); + SET_OPEN(RAW(x,state.height+1)); + } + state.alive = TRUE; +} + +void print_grid() { + for (int y = 0; y <= state.height; y++) { + for (int x = 0; x <= state.width; x++) { + move(y * 2, x * 4); + if (IS_OPEN(RAW(x ,y )) && IS_OPEN(RAW(x+1,y )) && + IS_OPEN(RAW(x ,y+1)) && IS_OPEN(RAW(x+1,y+1))) { + addch(' '); + } + else { + addch('+'); + } + if (x > 0) { + move(y * 2, x * 4 - 3); + if (IS_OPEN(RAW(x,y)) && IS_OPEN(RAW(x,y+1))) { + addstr(" "); + } + else { + addstr("---"); + } + } + if (y > 0) { + move(y * 2 - 1, x * 4); + if (IS_OPEN(RAW(x,y)) && IS_OPEN(RAW(x+1,y))) { + addch(' '); + } + else { + addch('|'); + } + } + } + } +} + +int around_attrs[] = { + A_NORMAL, + COLOR_PAIR(4) | A_NORMAL, + COLOR_PAIR(2) | A_NORMAL, + COLOR_PAIR(1) | A_NORMAL, + COLOR_PAIR(4) | A_BOLD, + COLOR_PAIR(5) | A_NORMAL, + COLOR_PAIR(6) | A_NORMAL, + COLOR_PAIR(7) | A_DIM, + COLOR_PAIR(7) | A_NORMAL +}; + +void display_board() { + for (int y = 0; y < state.height; y++) { + for (int x = 0; x < state.width; x++) { + move(y * 2 + 1, x * 4 + 2); + if (IS_FLAG(CELL(x,y))) { + addch('#'); + } + else if (state.alive && !IS_OPEN(CELL(x,y)) && cheat != CHEAT_COUNT) { + addch(':'); + } + else if (IS_MINE(CELL(x,y))) { + addch('*'); + } + else { + int around = AROUND(CELL(x,y)); + if (around == 0) { + addch(' '); + } + else { + addch('0' + around | around_attrs[around]); + } + } + } + } +} + +void display_cursor() { + mvaddch(state.y * 2 + 1, state.x * 4 + 1, '['); + mvaddch(state.y * 2 + 1, state.x * 4 + 3, ']'); + move(state.y * 2 + 1, state.x * 4 + 2); +} + +void erase_cursor() { + mvaddch(state.y * 2 + 1, state.x * 4 + 1, ' '); + mvaddch(state.y * 2 + 1, state.x * 4 + 3, ' '); +} + +void myexit() { + endwin(); +} + +void clear_status() { + mvaddstr(state.height * 2 + 2, 0, " "); +} + +void print_status(char *msg) { + clear_status(); + mvaddstr(state.height * 2 + 2, 0, msg); +} + +void print_status_coord(char *msg) { + clear_status(); + mvprintw(state.height * 2 + 2, 0, "%s (%d,%d)", msg, state.x, state.y); +} + +void print_win() { + clear_status(); + int elapsed = time(NULL) - state.start; + mvprintw(state.height * 2 + 2, 0, "You win! Score: %d seconds", elapsed); +} + +void do_clear_cell(int x, int y); + +void clear_around(int x, int y) { + do_clear_cell(x - 1, y - 1); + do_clear_cell(x + 0, y - 1); + do_clear_cell(x + 1, y - 1); + + do_clear_cell(x - 1, y + 0); + // Skip (x, y) + do_clear_cell(x + 1, y + 0); + + do_clear_cell(x - 1, y + 1); + do_clear_cell(x + 0, y + 1); + do_clear_cell(x + 1, y + 1); +} + +void do_clear_cell(int x, int y) { + if (!state.alive) { + return; + } + if (x == -1 || y == -1 || x == state.width || y == state.height) { + return; + } + if (!IS_FLAG(CELL(x,y)) && !IS_OPEN(CELL(x,y))) { + SET_OPEN(CELL(x,y)); + if (IS_MINE(CELL(x,y))) { + print_status_coord("Tripped mine at"); + state.alive = FALSE; + return; + } + if (AROUND(CELL(x,y)) == 0) { + clear_around(x, y); + } + } +} + +void super_clear(int x, int y) { + if (!state.alive) { + return; + } + if (count_flags_around(x, y) == AROUND(CELL(x,y))) { + clear_around(x, y); + if (state.alive) { + print_status_coord("Super cleared"); + } + } + else { + print_status_coord("Incorrect flag number around"); + } +} + +void clear_cell(int x, int y) { + if (state.start == 0) { + state.start = time(NULL); + } + if (IS_FLAG(CELL(x,y))) { + print_status_coord("Cannot clear flagged cell"); + } + else if (IS_OPEN(CELL(x,y))) { + super_clear(x, y); + } + else { + do_clear_cell(x, y); + if (state.alive) { + print_status_coord("Cleared"); + } + } +} + +void flag_cell(int x, int y) { + if (!state.alive) { + return; + } + if (IS_OPEN(CELL(x,y))) { + return; + } + if (IS_FLAG(CELL(x,y))) { + print_status_coord("Removed flag"); + CLR_FLAG(CELL(x,y)); + } + else { + print_status_coord("Flagged cell"); + SET_FLAG(CELL(x,y)); + } +} + +void check_win() { + for (int y = 0; y < state.height; y++) { + for (int x = 0; x < state.width; x++) { + int c = CELL(x, y); + if (!IS_OPEN(c) && !IS_MINE(c)) { + return; + } + } + } + print_win(); + state.alive = FALSE; +} + +int main(int argc, char **argv) { + parse_args(argc, argv); + + srand(0x5eed0000 | 0x0000ffff & time(NULL)); + setup_board(); + + initscr(); + atexit(myexit); + noecho(); + keypad(stdscr, TRUE); + start_color(); + init_pair(1, COLOR_RED, COLOR_BLACK); + init_pair(2, COLOR_GREEN, COLOR_BLACK); + init_pair(3, COLOR_YELLOW, COLOR_BLACK); + init_pair(4, COLOR_BLUE, COLOR_BLACK); + init_pair(5, COLOR_MAGENTA, COLOR_BLACK); + init_pair(6, COLOR_CYAN, COLOR_BLACK); + init_pair(7, COLOR_WHITE, COLOR_BLACK); + + while (1) { + print_grid(); + display_board(); + display_cursor(); + + refresh(); + int ch = getch(); + erase_cursor(); + switch (ch) { + case 'R': + setup_board(); + print_status("Reset"); + cheat = 0; + break; + case KEY_LEFT: + if (state.x > 0) + state.x--; + break; + case KEY_RIGHT: + if (state.x < state.width - 1) + state.x++; + break; + case KEY_UP: + if (state.y > 0) + state.y--; + break; + case KEY_DOWN: + if (state.y < state.height - 1) + state.y++; + break; + case ' ': + clear_cell(state.x, state.y); + check_win(); + break; + case 'f': + flag_cell(state.x, state.y); + break; + } + if (cheat == CHEAT_COUNT) { + } + else if (ch == cheat_seq[cheat]) { + cheat++; + } + else { + cheat = 0; + } + } +} + diff --git a/GhidraDocs/build.gradle b/GhidraDocs/build.gradle index 0c5685db7d..7fe619e189 100644 --- a/GhidraDocs/build.gradle +++ b/GhidraDocs/build.gradle @@ -24,6 +24,7 @@ sourceSets { ghidraClass { java { srcDir 'GhidraClass/AdvancedDevelopment/contrib/gadc/ghidra_scripts' + srcDir 'GhidraClass/Debugger/ghidra_scripts' compileClasspath += main.output } } @@ -34,7 +35,9 @@ configurations { } dependencies { - api project(':Base') + api project(':Base') + api project(':Debugger') + api project(':SystemEmulation') } rootProject.assembleDistribution { diff --git a/GhidraDocs/certification.manifest b/GhidraDocs/certification.manifest index 9b5eda28ab..fd1c8ec6ca 100644 --- a/GhidraDocs/certification.manifest +++ b/GhidraDocs/certification.manifest @@ -23,6 +23,86 @@ GhidraClass/Beginner/Images/GhidraLogo64.png||GHIDRA||||END| GhidraClass/Beginner/Introduction_to_Ghidra_Student_Guide.html||GHIDRA|||This file contains mostly Ghidra content, but also includes code that is available for distribution, without restrictions, from https://github.com/paulrouget/dzslides.|END| GhidraClass/Beginner/Introduction_to_Ghidra_Student_Guide_withNotes.html||Public Domain|||Slight modification of code that is available for distribution, without restrictions, (original extremely permissive wtf license allows us to change IP to Public Domain),from https://github.com/paulrouget/dzslides.|END| GhidraClass/Beginner/README.txt||GHIDRA||||END| +GhidraClass/Debugger/A1-GettingStarted.html||GHIDRA||||END| +GhidraClass/Debugger/A1-GettingStarted.md||GHIDRA||||END| +GhidraClass/Debugger/A2-UITour.html||GHIDRA||||END| +GhidraClass/Debugger/A2-UITour.md||GHIDRA||||END| +GhidraClass/Debugger/A3-Breakpoints.html||GHIDRA||||END| +GhidraClass/Debugger/A3-Breakpoints.md||GHIDRA||||END| +GhidraClass/Debugger/A4-MachineState.html||GHIDRA||||END| +GhidraClass/Debugger/A4-MachineState.md||GHIDRA||||END| +GhidraClass/Debugger/A5-Navigation.html||GHIDRA||||END| +GhidraClass/Debugger/A5-Navigation.md||GHIDRA||||END| +GhidraClass/Debugger/A6-MemoryMap.html||GHIDRA||||END| +GhidraClass/Debugger/A6-MemoryMap.md||GHIDRA||||END| +GhidraClass/Debugger/B1-RemoteTargets.html||GHIDRA||||END| +GhidraClass/Debugger/B1-RemoteTargets.md||GHIDRA||||END| +GhidraClass/Debugger/B2-Emulation.html||GHIDRA||||END| +GhidraClass/Debugger/B2-Emulation.md||GHIDRA||||END| +GhidraClass/Debugger/B3-Scripting.html||GHIDRA||||END| +GhidraClass/Debugger/B3-Scripting.md||GHIDRA||||END| +GhidraClass/Debugger/B4-Modeling.html||GHIDRA||||END| +GhidraClass/Debugger/B4-Modeling.md||GHIDRA||||END| +GhidraClass/Debugger/Makefile||GHIDRA||||END| +GhidraClass/Debugger/README.md||GHIDRA||||END| +GhidraClass/Debugger/gdb_syntax.xml||GHIDRA||||END| +GhidraClass/Debugger/images/Breakpoints_EmptyAfterLaunch.png||GHIDRA||||END| +GhidraClass/Debugger/images/Breakpoints_MissingModuleNote.png||GHIDRA||||END| +GhidraClass/Debugger/images/Breakpoints_PopAfterSRandRand.png||GHIDRA||||END| +GhidraClass/Debugger/images/Breakpoints_SeedValueAfterBreakSRand.png||GHIDRA||||END| +GhidraClass/Debugger/images/Breakpoints_SyncedAfterImportLibC.png||GHIDRA||||END| +GhidraClass/Debugger/images/Emulation_LazyStaleListing.png||GHIDRA||||END| +GhidraClass/Debugger/images/Emulation_ListingAfterResume.png||GHIDRA||||END| +GhidraClass/Debugger/images/Emulation_ListingForCmdlineSet.png||GHIDRA||||END| +GhidraClass/Debugger/images/Emulation_PcodeStepper.png||GHIDRA||||END| +GhidraClass/Debugger/images/Emulation_WatchesForCmdline.png||GHIDRA||||END| +GhidraClass/Debugger/images/Emulation_WatchesForCmdlineSet.png||GHIDRA||||END| +GhidraClass/Debugger/images/GettingStarted_DisassemblyAfterLaunch.png||GHIDRA||||END| +GhidraClass/Debugger/images/GettingStarted_ToolWSpecimen.png||GHIDRA||||END| +GhidraClass/Debugger/images/MemoryMap_CopyNcursesInto.png||GHIDRA||||END| +GhidraClass/Debugger/images/MemoryMap_ModulesAfterLaunch.png||GHIDRA||||END| +GhidraClass/Debugger/images/MemoryMap_RegionsAfterLaunch.png||GHIDRA||||END| +GhidraClass/Debugger/images/MemoryMap_StaticMappingAfterLaunch.png||GHIDRA||||END| +GhidraClass/Debugger/images/Navigation_CompareTimes.png||GHIDRA||||END| +GhidraClass/Debugger/images/Navigation_DialogCompareTimes.png||GHIDRA||||END| +GhidraClass/Debugger/images/Navigation_StackInCallRand.png||GHIDRA||||END| +GhidraClass/Debugger/images/Navigation_ThreadsInCallRand.png||GHIDRA||||END| +GhidraClass/Debugger/images/Navigation_TimeAfterCallSRandCallRand.png||GHIDRA||||END| +GhidraClass/Debugger/images/RemoteTargets_Gadp.png||GHIDRA||||END| +GhidraClass/Debugger/images/RemoteTargets_GdbOverSsh.png||GHIDRA||||END| +GhidraClass/Debugger/images/State_BytesStackAfterCallRand.png||GHIDRA||||END| +GhidraClass/Debugger/images/State_ListingAfterCallRand.png||GHIDRA||||END| +GhidraClass/Debugger/images/State_ListingStackAfterCallRand.png||GHIDRA||||END| +GhidraClass/Debugger/images/State_RegistersAfterCallRand.png||GHIDRA||||END| +GhidraClass/Debugger/images/State_WatchesInCallSRand.png||GHIDRA||||END| +GhidraClass/Debugger/images/add.png||GHIDRA||||END| +GhidraClass/Debugger/images/autoread.png||GHIDRA||||END| +GhidraClass/Debugger/images/breakpoint-disable.png||GHIDRA||||END| +GhidraClass/Debugger/images/breakpoint-enable-ineff.png||GHIDRA||||END| +GhidraClass/Debugger/images/breakpoint-enable.png||GHIDRA||||END| +GhidraClass/Debugger/images/breakpoint-overlay-inconsistent.png||GHIDRA||||END| +GhidraClass/Debugger/images/camera-photo.png||GHIDRA||||END| +GhidraClass/Debugger/images/connect.png||GHIDRA||||END| +GhidraClass/Debugger/images/debugger.png||GHIDRA||||END| +GhidraClass/Debugger/images/disconnect.png||GHIDRA||||END| +GhidraClass/Debugger/images/interrupt.png||GHIDRA||||END| +GhidraClass/Debugger/images/kill.png||GHIDRA||||END| +GhidraClass/Debugger/images/process.png||GHIDRA||||END| +GhidraClass/Debugger/images/record.png||GHIDRA||||END| +GhidraClass/Debugger/images/register-marker.png||GHIDRA||||END| +GhidraClass/Debugger/images/resume.png||GHIDRA||||END| +GhidraClass/Debugger/images/select-registers.png||GHIDRA||||END| +GhidraClass/Debugger/images/skipover.png||GHIDRA||||END| +GhidraClass/Debugger/images/stepback.png||GHIDRA||||END| +GhidraClass/Debugger/images/stepinto.png||GHIDRA||||END| +GhidraClass/Debugger/images/steplast.png||GHIDRA||||END| +GhidraClass/Debugger/images/stepout.png||GHIDRA||||END| +GhidraClass/Debugger/images/stepover.png||GHIDRA||||END| +GhidraClass/Debugger/images/table_relationship.png||GHIDRA||||END| +GhidraClass/Debugger/images/view-refresh.png||GHIDRA||||END| +GhidraClass/Debugger/links-to-html.lua||GHIDRA||||END| +GhidraClass/Debugger/navhead.htm||GHIDRA||||END| +GhidraClass/Debugger/sleigh_syntax.xml||GHIDRA||||END| GhidraClass/ExerciseFiles/Advanced/animals||GHIDRA||||END| GhidraClass/ExerciseFiles/Advanced/compilerVsDecompiler||GHIDRA||||END| GhidraClass/ExerciseFiles/Advanced/createStructure||GHIDRA||||END| @@ -39,6 +119,7 @@ GhidraClass/ExerciseFiles/Advanced/setRegister||GHIDRA||||END| GhidraClass/ExerciseFiles/Advanced/sharedReturn||GHIDRA||||END| GhidraClass/ExerciseFiles/Advanced/switch||GHIDRA||||END| GhidraClass/ExerciseFiles/Advanced/write||GHIDRA||||END| +GhidraClass/ExerciseFiles/Debugger/Makefile||GHIDRA||||END| GhidraClass/ExerciseFiles/Emulation/Source/README.txt||GHIDRA||||END| GhidraClass/ExerciseFiles/VersionTracking/WallaceSrc.exe||GHIDRA||||END| GhidraClass/ExerciseFiles/VersionTracking/WallaceVersion2.exe||GHIDRA||||END|