# 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 {
	}
}
```

**NOTE**: The scripting API has been refactored since the transition from Recorder-based to TraceRmi-based targets.
Parts of the API that are back-end agnostic are accessible from the `FlatDebuggerAPI` interface.
Parts of the API that require a specific back end are in `FlatDebuggerRmiAPI`.
The old `FlatDebuggerRecorderAPI` was removed in Ghidra 11.3, and scripts needing it should be ported to `FlatDebuggerRmiAPI`.

Technically, the Debugger's "deep" API is accessible to scripts; however, the flat API is preferred for scripting.
Also, the flat API is usually more stable than the deep API.
However, because the dynamic analysis flat API is newer, it may not be as stable as the static analysis flat API.
It is also worth noting that the `FlatDebuggerAPI` interface *adds* the flat API to your script.
The static analysis flat API is still available, and it will manipulate the static portions of the Debugger tool, just as they would in the CodeBrowser tool.
In this tutorial, we will explore reading machine state, setting breakpoints, waiting for conditions, and controlling the target.

## Dumping the Game Board

We will write a script that assumes the current session is for `termmines` and dumps the game board to the console, allowing you to cheat.
You can label your variables however you would like but, for this tutorial, we will assume you have labeled them `width`, `height`, and `cells`.
If you have not already located and labeled these variables, do so now.

### Checking the Target

First, we will do some validation.
Check that we have an active session (trace):

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

Now, check that the current program is `termmines`:

```java {.numberLines}
if (!"termmines".equals(currentProgram.getName())) {
	throw new AssertionError("The current program must be termmines");
}
```

### Checking the Module Map

Now, check that `termmines` is actually part of the current trace.
There is not a great way to do this directly in the flat API, but we are going to need to map some symbols from the `termmines` module, anyway.
In this step, we will both verify that the user has placed the required labels, as well as verify that those symbols can be mapped to the target:

```java {.numberLines}
List<Symbol> widthSyms = getSymbols("width", null);
if (widthSyms.isEmpty()) {
	throw new AssertionError("Symbol 'width' is required");
}
List<Symbol> heightSyms = getSymbols("height", null);
if (heightSyms.isEmpty()) {
	throw new AssertionError("Symbol 'height' is required");
}
List<Symbol> cellsSyms = getSymbols("cells", null);
if (cellsSyms.isEmpty()) {
	throw new AssertionError("Symbol 'cells' is required");
}

Address widthDyn = translateStaticToDynamic(widthSyms.get(0).getAddress());
if (widthDyn == null) {
	throw new AssertionError("Symbol 'width' is not mapped to target");
}
Address heightDyn = translateStaticToDynamic(heightSyms.get(0).getAddress());
if (heightDyn == null) {
	throw new AssertionError("Symbol 'height' is not mapped to target");
}
Address cellsDyn = translateStaticToDynamic(cellsSyms.get(0).getAddress());
if (cellsDyn == null) {
	throw new AssertionError("Symbol 'cells' is not mapped to target");
}
```

The `getSymbols()` method is part of the static flat API, so it returns symbols from the current static listing.
The `translateStaticToDynamic()` is part of the dynamic flat API.
This allows us to locate that symbol in the dynamic context.

### Reading the Data

Now, we want to read the dimensions and the whole board from the target.
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, launch `termmines` in Ghidra using GDB.
You will need to allow it to set up the first game board before running the script.
The simplest way to do that is to resume and then interrupt the target while it waits for input.
Now, run the script and examine its output.
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.
T