mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
124 lines
4.6 KiB
Java
124 lines
4.6 KiB
Java
/* ###
|
|
* 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.
|
|
*/
|
|
//An example debugger script
|
|
//It launches the current program, places saved breakpoints, and runs it until termination.
|
|
//This script must be run from the Debugger tool, or another tool with the required plugins.
|
|
//This script has only been tested with /usr/bin/echo.
|
|
//@category Debugger
|
|
//@keybinding
|
|
//@menupath
|
|
//@toolbar
|
|
|
|
import java.util.Set;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import ghidra.app.script.GhidraScript;
|
|
import ghidra.debug.api.breakpoint.LogicalBreakpoint;
|
|
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.LaunchResult;
|
|
import ghidra.debug.flatapi.FlatDebuggerRmiAPI;
|
|
import ghidra.program.model.address.Address;
|
|
import ghidra.trace.model.Trace;
|
|
|
|
public class DemoDebuggerScript extends GhidraScript implements FlatDebuggerRmiAPI {
|
|
|
|
@Override
|
|
protected void run() throws Exception {
|
|
/**
|
|
* Here we'll just launch the current program. Note that this is not guaranteed to succeed
|
|
* at all. Launching is subject to an opinion-based service. If no offers are made, this
|
|
* will fail. If the target system is missing required components, this will fail. If the
|
|
* target behaves in an unexpected way, this may fail. One example is targets without an
|
|
* initial break. If Ghidra does not recognize the target platform, this will fail. Etc.,
|
|
* etc., this may fail.
|
|
*
|
|
* In the event of failure, nothing is cleaned up automatically, since in some cases, the
|
|
* user may be expected to intervene. In our case; however, there's no way to continue this
|
|
* script on a repaired target, so we'll close the connection on failure. An alternative
|
|
* design for this script would expect the user to have already launched a target, and it
|
|
* would just operate on the "current target."
|
|
*/
|
|
println("Launching " + currentProgram);
|
|
LaunchResult result = launch(monitor);
|
|
if (result.exception() != null) {
|
|
printerr("Failed to launch " + currentProgram + ": " + result.exception());
|
|
|
|
result.close();
|
|
return;
|
|
}
|
|
Trace trace = result.trace();
|
|
println("Successfully launched in trace " + trace);
|
|
|
|
/**
|
|
* Breakpoints are highly dependent on the module map. To work correctly, the target
|
|
* debugger must record the module map into the trace, and Ghidra must recognize the module
|
|
* names and map them to programs open in the tool. These events all occur asynchronously,
|
|
* usually immediately after launch. Most launchers will wait for the target program module
|
|
* to be mapped to its Ghidra program database, but the breakpoint service may still be
|
|
* processing the new mapping.
|
|
*/
|
|
flushAsyncPipelines(trace);
|
|
|
|
/**
|
|
* There is also breakpointsEnable(), but that operates on an address-by-address basis,
|
|
* which doesn't quite make sense in this case. We'll instead use getBreakpoints(Program)
|
|
* and enable them only in the new trace. The nested for is to deal with the fact that
|
|
* getBreakpoints(Program) returns a map from address to breakpoint set, i.e., a collection
|
|
* of collections.
|
|
*/
|
|
println("Enabling breakpoints");
|
|
for (Set<LogicalBreakpoint> bs : getBreakpoints(currentProgram).values()) {
|
|
for (LogicalBreakpoint lb : bs) {
|
|
println(" " + lb);
|
|
if (lb.getTraceAddress(trace) == null) {
|
|
printerr(" Not mapped!");
|
|
}
|
|
else {
|
|
waitOn(lb.enableForTrace(trace));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This runs the target, recording memory around the PC and SP at each break, until it
|
|
* terminates.
|
|
*/
|
|
while (isTargetAlive()) {
|
|
waitForBreak(10, TimeUnit.SECONDS);
|
|
/**
|
|
* The target is going to perform some reads upon break, so let's allow them to
|
|
* complete.
|
|
*/
|
|
flushAsyncPipelines(trace);
|
|
|
|
println("Reading PC");
|
|
Address pc = getProgramCounter();
|
|
println("Reading 1024 bytes at PC=" + pc);
|
|
readMemory(pc, 1024, monitor);
|
|
println("Reading SP");
|
|
Address sp = getStackPointer();
|
|
println("Reading 8096 bytes at SP=" + sp);
|
|
readMemory(sp, 8096, monitor);
|
|
/**
|
|
* Allow the commands we just issued to settle.
|
|
*/
|
|
flushAsyncPipelines(trace);
|
|
|
|
println("Resuming");
|
|
resume();
|
|
}
|
|
println("Terminated");
|
|
}
|
|
}
|