diff --git a/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/modules/IdentityDebuggerAddressTranslator.java b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/modules/IdentityDebuggerAddressTranslator.java
new file mode 100644
index 0000000000..5393c02e0d
--- /dev/null
+++ b/Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/modules/IdentityDebuggerAddressTranslator.java
@@ -0,0 +1,95 @@
+/* ###
+ * IP: GHIDRA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package ghidra.debug.api.modules;
+
+import java.net.URL;
+import java.util.*;
+
+import ghidra.program.model.address.AddressSetView;
+import ghidra.program.model.listing.Program;
+import ghidra.program.util.ProgramLocation;
+import ghidra.trace.model.*;
+import ghidra.trace.model.program.TraceProgramView;
+
+public class IdentityDebuggerAddressTranslator implements DebuggerAddressTranslator {
+ private final Trace trace;
+ private final Program program;
+
+ public IdentityDebuggerAddressTranslator(Trace trace, Program program) {
+ this.trace = trace;
+ this.program = program;
+ }
+
+ @Override
+ public Set getOpenMappedProgramsAtSnap(Trace trace, long snap) {
+ return Set.of(program);
+ }
+
+ @Override
+ public ProgramLocation getOpenMappedLocation(TraceLocation loc) {
+ return new ProgramLocation(program, loc.getAddress());
+ }
+
+ @Override
+ public ProgramLocation getStaticLocationFromDynamic(ProgramLocation loc) {
+ return new ProgramLocation(program, loc.getAddress());
+ }
+
+ @Override
+ public Set getOpenMappedLocations(ProgramLocation loc) {
+ return Set.of(new DefaultTraceLocation(trace, null, Lifespan.ALL, loc.getAddress()));
+ }
+
+ @Override
+ public TraceLocation getOpenMappedLocation(Trace trace, ProgramLocation loc, long snap) {
+ if (trace != this.trace) {
+ return null;
+ }
+ return new DefaultTraceLocation(trace, null, Lifespan.ALL, loc.getAddress());
+ }
+
+ @Override
+ public ProgramLocation getDynamicLocationFromStatic(TraceProgramView view,
+ ProgramLocation loc) {
+ if (view.getTrace() != this.trace) {
+ return null;
+ }
+ return new ProgramLocation(view, loc.getAddress());
+ }
+
+ @Override
+ public Map> getOpenMappedViews(Trace trace,
+ AddressSetView set, long snap) {
+ return Map.ofEntries(
+ Map.entry(program, set.stream().map(r -> new MappedAddressRange(r, r)).toList()));
+ }
+
+ @Override
+ public Map> getOpenMappedViews(Program program,
+ AddressSetView set) {
+ if (program != this.program) {
+ return Map.of();
+ }
+ return Map.ofEntries(Map.entry(new DefaultTraceSpan(trace, Lifespan.ALL),
+ set.stream().map(r -> new MappedAddressRange(r, r)).toList()));
+ }
+
+ @Override
+ public Set getMappedProgramUrlsInView(Trace trace, AddressSetView set, long snap) {
+ // This is not necessary here
+ return Set.of();
+ }
+}
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/TraceRmiConnectionManagerProvider.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/TraceRmiConnectionManagerProvider.java
index 515a7306f6..ecc170c747 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/TraceRmiConnectionManagerProvider.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/TraceRmiConnectionManagerProvider.java
@@ -239,6 +239,10 @@ public class TraceRmiConnectionManagerProvider extends ComponentProviderAdapter
createActions();
}
+ public void cleanConnectionNode(TraceRmiConnectionNode node) {
+ rootNode.cleanConnectionNode(node);
+ }
+
private void buildMainPanel() {
mainPanel = new JPanel(new BorderLayout());
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiConnectionNode.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiConnectionNode.java
index 5f6a5f61e3..e98c2d0f0b 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiConnectionNode.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiConnectionNode.java
@@ -42,7 +42,13 @@ public class TraceRmiConnectionNode extends AbstractTraceRmiManagerNode {
@Override
public String getDisplayText() {
- return connection.getDescription() + " at " + connection.getRemoteAddress();
+ try {
+ return connection.getDescription() + " at " + connection.getRemoteAddress();
+ }
+ catch (Exception e) {
+ provider.cleanConnectionNode(this);
+ return "Error: " + e;
+ }
}
@Override
@@ -52,8 +58,13 @@ public class TraceRmiConnectionNode extends AbstractTraceRmiManagerNode {
@Override
public String getToolTip() {
- return "Trace RMI Connection to " + connection.getDescription() + " at " +
- connection.getRemoteAddress();
+ try {
+ return "Trace RMI Connection to " + connection.getDescription() + " at " +
+ connection.getRemoteAddress();
+ }
+ catch (Exception e) {
+ return "Error: " + e;
+ }
}
@Override
diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiServiceNode.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiServiceNode.java
index db3539667e..97045868eb 100644
--- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiServiceNode.java
+++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/gui/tracermi/connection/tree/TraceRmiServiceNode.java
@@ -25,6 +25,7 @@ import ghidra.debug.api.target.Target;
import ghidra.debug.api.target.TargetPublicationListener;
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
import ghidra.debug.api.tracermi.*;
+import ghidra.lifecycle.Internal;
import ghidra.util.Msg;
import ghidra.util.Swing;
@@ -83,6 +84,15 @@ public class TraceRmiServiceNode extends AbstractTraceRmiManagerNode
removeNode(node);
}
+ @Internal
+ public void cleanConnectionNode(TraceRmiConnectionNode node) {
+ TraceRmiConnection connection = node.getConnection();
+ synchronized (connectionNodes) {
+ connectionNodes.remove(connection);
+ }
+ removeNode(node);
+ }
+
private TraceRmiAcceptorNode newAcceptorNode(TraceRmiAcceptor acceptor) {
return new TraceRmiAcceptorNode(provider, acceptor);
}
diff --git a/Ghidra/Debug/Debugger/certification.manifest b/Ghidra/Debug/Debugger/certification.manifest
index 96169f2e80..c25baeb7ac 100644
--- a/Ghidra/Debug/Debugger/certification.manifest
+++ b/Ghidra/Debug/Debugger/certification.manifest
@@ -26,6 +26,7 @@ src/main/help/help/topics/DebuggerCopyActionsPlugin/DebuggerCopyActionsPlugin.ht
src/main/help/help/topics/DebuggerCopyActionsPlugin/images/DebuggerCopyIntoProgramDialog.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerDisassemblerPlugin/DebuggerDisassemblerPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html||GHIDRA||||END|
+src/main/help/help/topics/DebuggerEmulationServicePlugin/images/DebuggerEmulateFunctionDialog.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html||GHIDRA||||END|
src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerGoToDialog.png||GHIDRA||||END|
src/main/help/help/topics/DebuggerListingPlugin/images/DebuggerListingPlugin.png||GHIDRA||||END|
diff --git a/Ghidra/Debug/Debugger/ghidra_scripts/ListAllKnownMemoryScript.java b/Ghidra/Debug/Debugger/ghidra_scripts/ListAllKnownMemoryScript.java
new file mode 100644
index 0000000000..70d13dab19
--- /dev/null
+++ b/Ghidra/Debug/Debugger/ghidra_scripts/ListAllKnownMemoryScript.java
@@ -0,0 +1,38 @@
+/* ###
+ * 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;
+import ghidra.program.model.address.AddressSetView;
+import ghidra.trace.model.Trace;
+import ghidra.trace.model.memory.TraceMemoryOperations.StatePredicate;
+
+//List all address ranges that are "known" at the current snapshot
+//This script must be run from the Debugger tool, or another tool with the required plugins.
+//@category Debugger
+//@keybinding
+//@menupath
+//@toolbar
+public class ListAllKnownMemoryScript extends GhidraScript implements FlatDebuggerAPI {
+ @Override
+ protected void run() throws Exception {
+ Trace trace = requireCurrentTrace();
+ long snap = getCurrentSnap();
+
+ AddressSetView addresses =
+ trace.getMemoryManager().getAddressesWithState(snap, StatePredicate.IS_KNOWN);
+ show("Known Memory at %d".formatted(snap), addresses);
+ }
+}
diff --git a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html
index e8ea74094e..8651258391 100644
--- a/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html
+++ b/Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/DebuggerEmulationServicePlugin.html
@@ -48,6 +48,12 @@
Optionally, other registers can be initialized via the UI or a script. The new thread is
activated so that control actions will affect it by default.
+ Emulate Function
+
+ This action is available whenever the cursor is within a function in the Static Listing. It
+ displays a dialog for harnessing and emulating the
+ current function.
+
Configure Emulator
This action is always available. It lists emulators available for configuration. Selecting
@@ -62,5 +68,188 @@
emulated breakpoint, or when you patch the trace database. If you do not invalidate the cache,
the effects of your change may not appear, since the trace manager may recall a cached snapshot
instead of actually emulating.
+
+ Emulate Function Dialog
+
+ This dialog provides a means of harnessing and emulating a target function. Depending on the
+ size and scope of the function and the configuration of the emulator, the emulation may or may
+ not complete. See further below for options and actions in the dialog.
+
+
+

+
+
+ The dialog supports the addition of vararg arguments, custom initializations, and heap
+ initializations. All of these inputs are handled in the top table. The "Run" button will
+ perform the emulation and capture the outputs into the bottom table. Analogous to the inputs,
+ the Outputs table can be configured with custom variables and pointer dereferences. The
+ emulation session can optionally be captured into a trace, which is opened automatically in
+ your tool, usually the Debugger or Emulator. Multiple sessions can be run from the same dialog,
+ allowing trial-and-error runs while also iterating on the target function's markup.
+
+ Option: Ending Return Address
+
+ In order to detect the proper completion of the target function, the harness places a
+ breakpoint at a "sentinel" address. That sentinel address must be placed where the function
+ expects the return address. This option allows specification of the sentinel address.
+ Generally, anything easily recognizable that does not conflict with a real address should work.
+ NOTE: You cannot specify the location of the return address. This is determined
+ automatically through static analysis, but you can place the sentinel address through a custom
+ input, if the analysis fails or is incorrect.
+
+ Option: Snapshot Period
+
+ By default, the harness captures a snapshot of the emulator's state into a trace after every
+ instruction step. This feature can be disabled entirely by setting this option to 0 (zero), in
+ which case no trace is captured or opened in your tool. Only the outputs are captured.
+ Otherwise, the period indicates how many instructions are executed between consecutive
+ snapshots. If only a starting and ending snapshot are desired, set this to a large number. The
+ initial and final snapshots are always captured, no matter the (non-zero) period.
+
+ Option: Next Allocation
+
+ Set this to the location of the heap, if applicable. Any address not already used by the
+ program with plenty of space above should suffice. It may help if the upper digits of the
+ address are easily recognizable. Several of the below actions on the Inputs table will
+ automatically initialize pointers to the start of an "allocated block". This field is then
+ automatically incremented by the size of that allocation. You may adjust this field at any time
+ to "undo" allocations, establish a second heap, etc.
+
+ Inputs table
+
+ The Inputs table lists of all the configured inputs. The types and values of the inputs are
+ specified in this table. NOTE: No actual initializations are performed until right
+ before emulation. If two inputs happen to be at the same address, that conflict will not be
+ discovered until clicking "Run." No two inputs can have the same name. Adding an input with a
+ duplicate name replaces the existing one.
+
+ The default inputs are derived from the target function's parameters. Adjusting the type of
+ an input does not edit or update the target function in the program database. The
+ columns are:
+
+
+ - Name — Provides an informal name for the input. For parameters, this is the name of
+ the parameter. For custom inputs, this is "Custom 1" counting up in the order added. For
+ vararg arguments, this is, e.g., "Vararg 3" couting up in the order added, starting after the
+ number of fixed arguments. For allocated inputs, this is a C-like expression derived from the
+ pointer to it and its type. This field cannot be edited.
+
+ - Storage — Provides a formal Sleigh expression describing the location of the input.
+ For custom inputs, this is specified by the user when added. For parameter and vararg inputs,
+ this is derived from the target function's signature, custom storage, and/or calling
+ convention. For pointer allocations, this is derived from the pointer to it and its type.
+ This field cannot be edited.
+
+ - Value — Controls the raw initial value of the input variable. This can be specified
+ as a positive integer, e.g.,
0x1234, so long as it fits in the storage, or as a
+ byte sequence, e.g., { 34 12 }, so long as its length matches that of the
+ storage.
+
+ - Type — Controls the type of the input variable. This has no direct impact on the
+ emulation nor on the program database (except to resolve the type), but permits the initial
+ value to be specified using a representation suitable to that type.
+
+ - Repr — Controls the initial value of the input variable as represented by the
+ chosen type. NOTE: some types, particularly structure types, do not have a string
+ representation, and so this box may appear empty. This field can be edited only if the chosen
+ type implements an encoder.
+
+
+
+
+ Action: Remove Input
+
+ This action removes the selected input(s) from the table. A removed input is no longer
+ initialized. NOTE: Inputs can have dependencies, e.g., an allocated block depends on its
+ pointer, because the storage location of that block is computed from the value of the pointer.
+ Removing the pointer will leave it uninitialized, likely resulting in the block's location
+ being 0 (zero). This will generally still work, but is usually not desired. Re-adding the
+ relevant pointer can fix this. So long as the old and new names match, the dependency
+ relationship is restored, too.
+
+ Action: Clear Inputs
+
+ For when things have gone so far south you need to start over. Typically this is followed by
+ a Refresh, too.
+
+ Action: Add Custom Input
+
+ This action prompts the user for a Sleigh expression of a variable's storage and adds it as
+ a Custom input. Typically, this is a register, e.g., RAX, or a fixed address and
+ size, e.g., *:8 0x00401234.
+
+ Action: Add Vararg Input
+
+ This action is only enabled if the target function has variable arguments. It prompts the
+ user for a type, derives the storage from the target function's signature and calling convetion
+ and adds it as a Vararg input. NOTE: Adjustments to this row's type will not
+ automatically update the argument's storage. Consider deleting and re-adding an argument along
+ with all its subsequent arguments if consistent storage is desired.
+
+ Action: Allocate and Add Pointer Inputs
+
+ This action is available when the selected input has a pointer type. (Multiple selections
+ are supported.) The size of the pointed-to type is calculated; the pointer's value is
+ initialized to the Next Allocation, which is then incremented by the
+ size of the pointed-to type. A new input row is then added and selected, describing the
+ variable pointed to by the formerly-selected input. Under normal operation, this allocates a
+ single instance of the pointed-to type. To instead allocate an array of the pointed-to type,
+ hold <Shift> when clicking the button. If the pointed-to type is a composite
+ (struct or union), a separate row is generated for each field. For the array case, yes, this
+ results in an n-by-m set of new rows. If the pointed-to type is a string (or
+ char), hold <Ctrl> when clicking the button. This will prompt the
+ user for string data type settings and then for the initial string value. It encodes it and
+ allocates sufficient space for the encoding. NOTE: So long as edits to the Repr column
+ result in encodings of smaller or equal size, no re-allocation is necessary. The table will
+ still permit the edit, but it may result in conflicts. Either update the pointer values
+ manually — this will automatically adjust the storage location of the pointed-to variable
+ — or delete and re-allocate the string input.
+
+ Action: Refresh Inputs
+
+ This re-adds (likely replacing) all the inputs derived from the function's parameters. This
+ is especially useful when trying to derive a function's signature by trial and error. Clicking
+ this button after editing a function's signature will update the Parameter inputs accordingly.
+ NOTE: This will not remove any Parameter inputs.
+
+ Action: Type Settings
+
+ This action is available in the right-click context menu on rows with an assigned type. It
+ controls the settings on the chosen datatype, e.g., radix for integer types, or encodings for
+ string types.
+
+ Outputs table
+
+ The Outputs table is analogous to the Inputs table, except that it displays values captured
+ from the last successful emulation. The default output is derived from the target function's
+ return type. It has the same columns and similar actions with the following exceptions:
+
+
+ - There is no Add Vararg Output action.
+
+ - Only the Type column can be edited.
+
+ - Adding Pointed-to outputs does not "allocate," since the pointer is expected to be set by
+ the target function.
+
+ - When adding pointed-to strings, the prompt is for the number of bytes to capture. The
+ string decoder should only use what it needs, so just pick a reasonable maximum expected
+ length. Adjust the type and settings using other actions afterward.
+
+
+ Probe Outputs
+
+ The Outputs table also supports "Probe" outputs. These are outputs automatically generated
+ during the emulation of the target function via the emu_probe userop. These are
+ configured using the Set
+ Injection action on a breakpoint. The breakpoint need only be enabled. Even if
+ "ineffective,"" it will be installed by the harness. The emu_probe userop accepts
+ exactly one argument. All Probe outputs are cleared at the start of emulation. Each time the
+ userop is executed, its argument is captured with its location and value at the time of
+ invocation. These are then added as Probe outputs, counting 1-up by invocation. If the
+ userop is invoked within a loop of the target code, each run-time invocation gets a distinct
+ row. Types can be applied as usual. NOTE: If the userop argument is anything other than
+ a simple varnode, the location will be "$Unique."