From 427d857d516d8a260fe44b899e261c73c7fbff15 Mon Sep 17 00:00:00 2001
From: Dan <46821332+nsadeveloper789@users.noreply.github.com>
Date: Wed, 1 Jul 2026 18:28:31 +0000
Subject: [PATCH] GP-6787: Add 'Emulate Function' action and dialog.
---
.../IdentityDebuggerAddressTranslator.java | 95 ++
.../TraceRmiConnectionManagerProvider.java | 4 +
.../tree/TraceRmiConnectionNode.java | 17 +-
.../connection/tree/TraceRmiServiceNode.java | 10 +
Ghidra/Debug/Debugger/certification.manifest | 1 +
.../ListAllKnownMemoryScript.java | 38 +
.../DebuggerEmulationServicePlugin.html | 189 +++
.../images/DebuggerEmulateFunctionDialog.png | Bin 0 -> 29638 bytes
.../DebuggerListingPlugin.html | 2 +-
.../gui/action/BasicAutoReadMemorySpec.java | 2 +-
.../DebuggerBreakpointsProvider.java | 40 +-
.../gui/emulation/AddressInputVerifier.java | 58 +
.../DebuggerEmulateFunctionDialog.java | 1148 +++++++++++++++++
.../emulation/FunctionEmulationHarness.java | 346 +++++
.../core/debug/gui/emulation/InputColumn.java | 92 ++
.../core/debug/gui/emulation/InputRow.java | 81 ++
.../debug/gui/emulation/InputsTableModel.java | 24 +
.../core/debug/gui/emulation/LocAndVal.java | 266 ++++
.../gui/emulation/LongInputVerifier.java | 48 +
.../debug/gui/emulation/OutputColumn.java | 87 ++
.../core/debug/gui/emulation/OutputRow.java | 31 +
.../gui/emulation/OutputsTableModel.java | 24 +
.../core/debug/gui/emulation/RawStyle.java | 83 ++
.../gui/emulation/VarDataTypeEditor.java | 67 +
.../core/debug/gui/emulation/VarRow.java | 134 ++
.../core/debug/gui/emulation/VarStorage.java | 77 ++
.../debug/gui/emulation/VarStorageNode.java | 85 ++
.../debug/gui/emulation/VarTableModel.java | 69 +
.../core/debug/gui/watch/DefaultWatchRow.java | 2 +-
.../service/breakpoint/ProgramBreakpoint.java | 29 +-
.../DebuggerEmulationIntegration.java | 47 +-
.../DebuggerEmulationServicePlugin.java | 54 +
.../data/AbstractPcodeDebuggerAccess.java | 17 +-
.../data/DefaultPcodeDebuggerAccess.java | 17 +-
.../DefaultPcodeDebuggerMemoryAccess.java | 108 +-
.../DefaultPcodeDebuggerPropertyAccess.java | 13 +-
.../DefaultPcodeDebuggerRegistersAccess.java | 18 +-
.../data/InternalPcodeDebuggerDataAccess.java | 24 +-
.../data/TranslatedPcodeDebuggerAccess.java | 92 ++
.../TranslatedPcodeDebuggerMemoryAccess.java | 139 ++
.../modules/DebuggerStaticMappingContext.java | 2 +-
.../core/debug/stack/SymPcodeExecutor.java | 9 +-
.../debug/stack/SymPcodeExecutorState.java | 5 +-
.../AbstractMappedMemoryBytesVisitor.java | 12 +-
.../core/debug/utils/ManagedDomainObject.java | 31 +-
.../ghidra/pcode/exec/DebuggerPcodeUtils.java | 50 +-
...ggerEmulationServicePluginScreenShots.java | 145 +++
.../exec/trace/TraceEmulationIntegration.java | 102 +-
.../data/AbstractPcodeTraceDataAccess.java | 10 +-
.../trace/BytesTracePcodeEmulatorTest.java | 3 +-
.../emu/symz3/state/SymZ3PieceHandler.java | 12 +-
.../util/DataTypeChooserDialogTestHelper.java | 24 +
.../docking/menu/DialogToolbarButton.java | 1 +
.../pcode/emu/AbstractPcodeMachine.java | 8 +-
.../TrigPcodeUseropLibraryFactory.java | 2 +-
.../DiagnosticsPcodeUseropLibraryFactory.java | 72 ++
.../java/ghidra/pcode/exec/PcodeExecutor.java | 10 +
.../pcode/exec/PcodeUseropLibraryFactory.java | 99 +-
.../program/database/bookmark/BookmarkDB.java | 4 +
.../program/model/data/PointerDataType.java | 15 +-
.../program/model/listing/Bookmark.java | 10 +-
.../ghidra/program/model/mem/MemBuffer.java | 14 +
.../M68kPcodeUseropLibraryFactory.java | 2 +-
.../Aarch64PcodeUseropLibraryFactory.java | 2 +-
.../ArmPcodeUseropLibraryFactory.java | 2 +-
.../HexagonPcodeUseropLibraryFactory.java | 2 +-
.../MipsPcodeUseropLibraryFactory.java | 2 +-
.../PpcPcodeUseropLibraryFactory.java | 2 +-
.../XtensaPcodeUseropLibraryFactory.java | 2 +-
.../TricorePcodeUseropLibraryFactory.java | 2 +-
.../X86PcodeUseropLibraryFactory.java | 2 +-
.../java/agent/TraceRmiPythonClientTest.java | 32 +-
.../rmi/AbstractDbgEngTraceRmiTest.java | 11 +-
.../agent/dbgeng/rmi/DbgEngCommandsTest.java | 117 +-
.../agent/dbgeng/rmi/DbgEngHooksTest.java | 16 +-
.../agent/dbgeng/rmi/DbgEngMethodsTest.java | 155 +--
.../dbgeng/rmi/DbgEngStackUnwindTest.java | 33 +-
.../drgn/rmi/AbstractDrgnTraceRmiTest.java | 9 +-
.../java/agent/drgn/rmi/DrgnCommandsTest.java | 85 +-
.../java/agent/drgn/rmi/DrgnMethodsTest.java | 35 +-
.../gdb/rmi/AbstractGdbTraceRmiTest.java | 9 +-
.../java/agent/gdb/rmi/GdbCommandsTest.java | 117 +-
.../java/agent/gdb/rmi/GdbHooksTest.java | 7 +-
.../java/agent/gdb/rmi/GdbMethodsTest.java | 178 +--
.../java/rmi/AbstractJavaTraceRmiTest.java | 9 +-
.../java/agent/java/rmi/JavaCommandsTest.java | 117 +-
.../java/agent/java/rmi/JavaHooksTest.java | 32 +-
.../java/agent/java/rmi/JavaMethodsTest.java | 78 +-
.../lldb/rmi/AbstractLldbTraceRmiTest.java | 12 +-
.../java/agent/lldb/rmi/LldbCommandsTest.java | 117 +-
.../java/agent/lldb/rmi/LldbHooksTest.java | 7 +-
.../java/agent/lldb/rmi/LldbMethodsTest.java | 160 +--
.../agent/lldb/rmi/LldbStackUnwindTest.java | 34 +-
.../rmi/AbstractX64dbgTraceRmiTest.java | 11 +-
.../agent/x64dbg/rmi/X64dbgCommandsTest.java | 114 +-
.../agent/x64dbg/rmi/X64dbgHooksTest.java | 18 +-
.../agent/x64dbg/rmi/X64dbgMethodsTest.java | 141 +-
.../DebuggerEmulateFunctionDialogTest.java | 553 ++++++++
98 files changed, 5384 insertions(+), 1159 deletions(-)
create mode 100644 Ghidra/Debug/Debugger-api/src/main/java/ghidra/debug/api/modules/IdentityDebuggerAddressTranslator.java
create mode 100644 Ghidra/Debug/Debugger/ghidra_scripts/ListAllKnownMemoryScript.java
create mode 100644 Ghidra/Debug/Debugger/src/main/help/help/topics/DebuggerEmulationServicePlugin/images/DebuggerEmulateFunctionDialog.png
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/AddressInputVerifier.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/DebuggerEmulateFunctionDialog.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/FunctionEmulationHarness.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputColumn.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputRow.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/InputsTableModel.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/LocAndVal.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/LongInputVerifier.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputColumn.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputRow.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/OutputsTableModel.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/RawStyle.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarDataTypeEditor.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarRow.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarStorage.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarStorageNode.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/emulation/VarTableModel.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/TranslatedPcodeDebuggerAccess.java
create mode 100644 Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/service/emulation/data/TranslatedPcodeDebuggerMemoryAccess.java
create mode 100644 Ghidra/Debug/Debugger/src/screen/java/ghidra/app/plugin/core/debug/service/emulation/DebuggerEmulationServicePluginScreenShots.java
create mode 100644 Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/datamgr/util/DataTypeChooserDialogTestHelper.java
create mode 100644 Ghidra/Framework/Emulation/src/main/java/ghidra/pcode/exec/DiagnosticsPcodeUseropLibraryFactory.java
create mode 100644 Ghidra/Test/DebuggerIntegrationTest/src/test/java/ghidra/app/plugin/core/debug/gui/emulation/DebuggerEmulateFunctionDialogTest.java
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
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.
+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.
+ +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.
+ +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.
+ +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.
+ +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.
+ +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:
+ +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.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.
+ +For when things have gone so far south you need to start over. Typically this is followed by + a Refresh, too.
+ +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.
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.
+ +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.
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.
+ +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.
+ +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:
+ +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."