Merge remote-tracking branch 'origin/GP-3887_Dan_traceRmiTutorial--SQUASHED'
@ -1224,6 +1224,7 @@ def put_regions():
|
||||
regobj.set_value('_executable', r.perms == None or 'x' in r.perms)
|
||||
regobj.set_value('_offset', r.offset)
|
||||
regobj.set_value('_objfile', r.objfile)
|
||||
regobj.set_value('_display', f'{r.objfile} (0x{r.start:x}-0x{r.end:x})')
|
||||
regobj.insert()
|
||||
STATE.trace.proxy_object_path(
|
||||
MEMORY_PATTERN.format(infnum=inf.num)).retain_values(keys)
|
||||
|
@ -57,6 +57,7 @@ GNU_DEBUGDATA_PREFIX = ".gnu_debugdata for "
|
||||
class Module(namedtuple('BaseModule', ['name', 'base', 'max', 'sections'])):
|
||||
pass
|
||||
|
||||
|
||||
class Index:
|
||||
def __init__(self, regions):
|
||||
self.regions = {}
|
||||
@ -64,6 +65,7 @@ class Index:
|
||||
for r in regions:
|
||||
self.regions[r.start] = r
|
||||
self.bases.append(r.start)
|
||||
|
||||
def compute_base(self, address):
|
||||
index = bisect.bisect_right(self.bases, address) - 1
|
||||
if index == -1:
|
||||
@ -78,6 +80,7 @@ class Index:
|
||||
else:
|
||||
return region.start
|
||||
|
||||
|
||||
class Section(namedtuple('BaseSection', ['name', 'start', 'end', 'offset', 'attrs'])):
|
||||
def better(self, other):
|
||||
start = self.start if self.start != 0 else other.start
|
||||
@ -103,8 +106,6 @@ class ModuleInfoReader(object):
|
||||
if mat is None:
|
||||
return None
|
||||
n = mat['name']
|
||||
if n.startswith(GNU_DEBUGDATA_PREFIX):
|
||||
return None
|
||||
return None if mat is None else mat['name']
|
||||
|
||||
def section_from_line(self, line):
|
||||
@ -135,7 +136,7 @@ class ModuleInfoReader(object):
|
||||
for line in out.split('\n'):
|
||||
n = self.name_from_line(line)
|
||||
if n is not None:
|
||||
if name is not None:
|
||||
if name is not None and not name.startswith(GNU_DEBUGDATA_PREFIX):
|
||||
modules[name] = self.finish_module(name, sections, index)
|
||||
name = n
|
||||
sections = {}
|
||||
@ -148,7 +149,7 @@ class ModuleInfoReader(object):
|
||||
if s.name in sections:
|
||||
s = s.better(sections[s.name])
|
||||
sections[s.name] = s
|
||||
if name is not None:
|
||||
if name is not None and not name.startswith(GNU_DEBUGDATA_PREFIX):
|
||||
modules[name] = self.finish_module(name, sections, index)
|
||||
return modules
|
||||
|
||||
@ -292,8 +293,9 @@ class BreakpointLocationInfoReaderV8(object):
|
||||
inf = gdb.selected_inferior()
|
||||
thread_groups = [inf.num]
|
||||
if breakpoint.location is not None and breakpoint.location.startswith("*0x"):
|
||||
address = int(breakpoint.location[1:],16)
|
||||
loc = BreakpointLocation(address, breakpoint.enabled, thread_groups)
|
||||
address = int(breakpoint.location[1:], 16)
|
||||
loc = BreakpointLocation(
|
||||
address, breakpoint.enabled, thread_groups)
|
||||
return [loc]
|
||||
return []
|
||||
|
||||
@ -312,7 +314,8 @@ class BreakpointLocationInfoReaderV9(object):
|
||||
return []
|
||||
try:
|
||||
address = gdb.parse_and_eval(breakpoint.location).address
|
||||
loc = BreakpointLocation(address, breakpoint.enabled, thread_groups)
|
||||
loc = BreakpointLocation(
|
||||
address, breakpoint.enabled, thread_groups)
|
||||
return [loc]
|
||||
except Exception as e:
|
||||
print(f"Error parsing bpt location = {breakpoint.location}")
|
||||
@ -338,6 +341,7 @@ def _choose_breakpoint_location_info_reader():
|
||||
|
||||
BREAKPOINT_LOCATION_INFO_READER = _choose_breakpoint_location_info_reader()
|
||||
|
||||
|
||||
def set_bool_param_by_api(name, value):
|
||||
gdb.set_parameter(name, value)
|
||||
|
||||
@ -353,6 +357,7 @@ def choose_set_parameter():
|
||||
else:
|
||||
return set_bool_param_by_cmd
|
||||
|
||||
|
||||
set_bool_param = choose_set_parameter()
|
||||
|
||||
|
||||
@ -360,7 +365,7 @@ def get_level(frame):
|
||||
if hasattr(frame, "level"):
|
||||
return frame.level()
|
||||
else:
|
||||
level = -1;
|
||||
level = -1
|
||||
f = frame
|
||||
while f is not None:
|
||||
level += 1
|
||||
@ -371,16 +376,16 @@ def get_level(frame):
|
||||
class RegisterDesc(namedtuple('BaseRegisterDesc', ['name'])):
|
||||
pass
|
||||
|
||||
|
||||
def get_register_descs(arch, group='all'):
|
||||
if hasattr(arch, "registers"):
|
||||
return arch.registers(group)
|
||||
else:
|
||||
descs = []
|
||||
regset = gdb.execute(f"info registers {group}", to_string=True).strip().split('\n')
|
||||
regset = gdb.execute(
|
||||
f"info registers {group}", to_string=True).strip().split('\n')
|
||||
for line in regset:
|
||||
if not line.startswith(" "):
|
||||
tokens = line.strip().split()
|
||||
descs.append(RegisterDesc(tokens[0]))
|
||||
descs.append(RegisterDesc(tokens[0]))
|
||||
return descs
|
||||
|
||||
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package ghidra.app.plugin.core.debug.gui.tracermi.launcher;
|
||||
|
||||
import static ghidra.app.plugin.core.debug.gui.tracermi.launcher.TraceRmiLauncherServicePlugin.getProgramName;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -52,7 +54,7 @@ public class LaunchAction extends MultiActionDockingAction {
|
||||
Program program = plugin.currentProgram;
|
||||
String title = program == null
|
||||
? "Configure and Launch ..."
|
||||
: "Configure and Launch %s using...".formatted(program.getName());
|
||||
: "Configure and Launch %s using...".formatted(getProgramName(program));
|
||||
return Stream.concat(Stream.of(title), menuPath.stream()).toArray(String[]::new);
|
||||
}
|
||||
|
||||
@ -77,12 +79,12 @@ public class LaunchAction extends MultiActionDockingAction {
|
||||
Long last = saved.get(offer.getConfigName());
|
||||
if (last == null) {
|
||||
// NB. If program == null, this will always happen.
|
||||
// Thus, no worries about program.getName() below.
|
||||
// Thus, no worries about getProgramName(program) below.
|
||||
continue;
|
||||
}
|
||||
String title = program == null
|
||||
? "Re-launch " + offer.getTitle()
|
||||
: "Re-launch %s using %s".formatted(program.getName(), offer.getTitle());
|
||||
: "Re-launch %s using %s".formatted(getProgramName(program), offer.getTitle());
|
||||
actions.add(new ActionBuilder(offer.getConfigName(), plugin.getName())
|
||||
.popupMenuPath(title)
|
||||
.popupMenuGroup("0", "%016x".formatted(Long.MAX_VALUE - last))
|
||||
@ -158,11 +160,11 @@ public class LaunchAction extends MultiActionDockingAction {
|
||||
return "Configure and launch";
|
||||
}
|
||||
if (offer == null) {
|
||||
return "Configure and launch " + program.getName();
|
||||
return "Configure and launch " + getProgramName(program);
|
||||
}
|
||||
if (program == null) {
|
||||
return "Re-launch " + offer.getTitle();
|
||||
}
|
||||
return "Re-launch %s using %s".formatted(program.getName(), offer.getTitle());
|
||||
return "Re-launch %s using %s".formatted(getProgramName(program), offer.getTitle());
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.LaunchConfigurator;
|
||||
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.PromptMode;
|
||||
import ghidra.debug.spi.tracermi.TraceRmiLaunchOpinion;
|
||||
import ghidra.formats.gfilesystem.FSRL;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.options.*;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
@ -236,10 +237,18 @@ public class TraceRmiLauncherServicePlugin extends Plugin
|
||||
executeTask(new ConfigureAndLaunchTask(offer));
|
||||
}
|
||||
|
||||
protected static String getProgramName(Program program) {
|
||||
DomainFile df = program.getDomainFile();
|
||||
if (df != null) {
|
||||
return df.getName();
|
||||
}
|
||||
return program.getName();
|
||||
}
|
||||
|
||||
protected String[] constructLaunchMenuPrefix() {
|
||||
return new String[] {
|
||||
DebuggerPluginPackage.NAME,
|
||||
"Configure and Launch " + currentProgram.getName() + " using..." };
|
||||
"Configure and Launch " + getProgramName(currentProgram) + " using..." };
|
||||
}
|
||||
|
||||
protected String[] prependConfigAndLaunch(List<String> menuPath) {
|
||||
|
@ -810,6 +810,11 @@ public class TraceRmiHandler implements TraceRmiConnection {
|
||||
}
|
||||
DebuggerCoordinates finalCoords = object == null ? coords : coords.object(object);
|
||||
Swing.runLater(() -> {
|
||||
DebuggerTraceManagerService traceManager = this.traceManager;
|
||||
if (traceManager == null) {
|
||||
// Can happen during tear down.
|
||||
return;
|
||||
}
|
||||
if (!traceManager.getOpenTraces().contains(open.trace)) {
|
||||
traceManager.openTrace(open.trace);
|
||||
traceManager.activate(finalCoords, ActivationCause.SYNC_MODEL);
|
||||
|
@ -26,7 +26,6 @@ import ghidra.app.plugin.core.debug.utils.MiscellaneousUtils;
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.app.services.ProgramManager;
|
||||
import ghidra.framework.cmd.BackgroundCommand;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.AutoConfigState.ConfigFieldCodec;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
@ -105,9 +104,9 @@ public interface AutoMapSpec extends ExtensionPoint {
|
||||
if (mappingService == null || programManager == null) {
|
||||
return;
|
||||
}
|
||||
BackgroundCommand cmd = new BackgroundCommand(getTaskTitle(), true, true, false) {
|
||||
BackgroundCommand<Trace> cmd = new BackgroundCommand<>(getTaskTitle(), true, true, false) {
|
||||
@Override
|
||||
public boolean applyTo(DomainObject obj, TaskMonitor monitor) {
|
||||
public boolean applyTo(Trace trace, TaskMonitor monitor) {
|
||||
try {
|
||||
performMapping(mappingService, trace, programManager, monitor);
|
||||
return true;
|
||||
|
@ -799,7 +799,7 @@ public class DebuggerCopyIntoProgramDialog extends ReusableDialogComponentProvid
|
||||
|
||||
protected void executeCapture(AddressRange range, Target target, TaskMonitor monitor)
|
||||
throws Exception {
|
||||
target.readMemory(new AddressSet(range), monitor);
|
||||
target.readMemoryAsync(new AddressSet(range), monitor).get();
|
||||
}
|
||||
|
||||
protected void executePlan(TaskMonitor monitor) throws Exception {
|
||||
|
@ -64,6 +64,11 @@ public interface DisplaysObjectValues {
|
||||
.collect(Collectors.joining(":"));
|
||||
}
|
||||
|
||||
default String getStringsDisplay(String[] strings) {
|
||||
return Stream.of(strings)
|
||||
.collect(Collectors.joining(":"));
|
||||
}
|
||||
|
||||
default String getPrimitiveValueDisplay(Object value) {
|
||||
assert !(value instanceof TraceObject);
|
||||
assert !(value instanceof TraceObjectValue);
|
||||
@ -89,6 +94,9 @@ public interface DisplaysObjectValues {
|
||||
if (value instanceof long[] longs) {
|
||||
return getLongsDisplay(longs);
|
||||
}
|
||||
if (value instanceof String[] strings) {
|
||||
return getStringsDisplay(strings);
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
|
@ -42,13 +42,13 @@ public class DebuggerModuleMapProposalDialog
|
||||
implements EnumeratedTableColumn<ModuleMapTableColumns, ModuleMapEntry> {
|
||||
REMOVE("Remove", String.class, e -> "Remove Proposed Entry", (e, v) -> nop()),
|
||||
MODULE_NAME("Module", String.class, e -> e.getModule().getName()),
|
||||
DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getModule().getBase()),
|
||||
DYNAMIC_BASE("Dynamic Base", Address.class, e -> e.getFromRange().getMinAddress()),
|
||||
CHOOSE("Choose", String.class, e -> "Choose Program", (e, v) -> nop()),
|
||||
PROGRAM_NAME("Program", String.class, e -> (e.getToProgram().getDomainFile() == null
|
||||
? e.getToProgram().getName()
|
||||
: e.getToProgram().getDomainFile().getName())),
|
||||
STATIC_BASE("Static Base", Address.class, e -> e.getToProgram().getImageBase()),
|
||||
SIZE("Size", Long.class, e -> e.getModuleRange().getLength()),
|
||||
STATIC_BASE("Static Base", Address.class, e -> e.getToRange().getMinAddress()),
|
||||
SIZE("Size", Long.class, e -> e.getFromRange().getLength()),
|
||||
MEMORIZE("Memorize", Boolean.class, ModuleMapEntry::isMemorize, ModuleMapEntry::setMemorize);
|
||||
|
||||
private final String header;
|
||||
|
@ -32,6 +32,7 @@ import ghidra.framework.plugintool.util.PluginStatus;
|
||||
packageName = DebuggerPluginPackage.NAME,
|
||||
status = PluginStatus.RELEASED,
|
||||
eventsConsumed = {
|
||||
ProgramOpenedPluginEvent.class,
|
||||
ProgramActivatedPluginEvent.class,
|
||||
ProgramLocationPluginEvent.class,
|
||||
ProgramClosedPluginEvent.class,
|
||||
@ -65,7 +66,10 @@ public class DebuggerModulesPlugin extends AbstractDebuggerPlugin {
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
super.processEvent(event);
|
||||
if (event instanceof ProgramActivatedPluginEvent ev) {
|
||||
if (event instanceof ProgramOpenedPluginEvent ev) {
|
||||
provider.programOpened(ev.getProgram());
|
||||
}
|
||||
else if (event instanceof ProgramActivatedPluginEvent ev) {
|
||||
provider.setProgram(ev.getActiveProgram());
|
||||
}
|
||||
else if (event instanceof ProgramLocationPluginEvent ev) {
|
||||
|
@ -1078,6 +1078,12 @@ public class DebuggerModulesProvider extends ComponentProviderAdapter {
|
||||
actionMapSectionTo.getPopupMenuData().setMenuItemName(name);
|
||||
}
|
||||
|
||||
public void programOpened(Program program) {
|
||||
// TODO: Debounce this?
|
||||
cueAutoMap = true;
|
||||
doCuedAutoMap();
|
||||
}
|
||||
|
||||
public void programClosed(Program program) {
|
||||
if (currentProgram == program) {
|
||||
currentProgram = null;
|
||||
|
@ -614,7 +614,7 @@ public class DebuggerPcodeStepperProvider extends ComponentProviderAdapter {
|
||||
pcodeTable.setTableHeader(null);
|
||||
pcodeTable.setBackground(COLOR_BACKGROUND);
|
||||
pcodeTable.setSelectionBackground(COLOR_BACKGROUND_CURSOR);
|
||||
pcodeTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
pcodeTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
||||
pcodeTable.getSelectionModel().addListSelectionListener(evt -> {
|
||||
if (evt.getValueIsAdjusting()) {
|
||||
return;
|
||||
|
@ -106,6 +106,9 @@ public class UniqueRow {
|
||||
}
|
||||
|
||||
public RefType getRefType() {
|
||||
if (provider.pcodeTable.getSelectedRowCount() != 1) {
|
||||
return RefType.NONE;
|
||||
}
|
||||
int index = provider.pcodeTable.getSelectedRow();
|
||||
if (index == -1) {
|
||||
return RefType.NONE;
|
||||
|
@ -42,8 +42,8 @@ import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.modules.TraceConflictedMappingException;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectKeyPath;
|
||||
import ghidra.trace.model.target.*;
|
||||
import ghidra.trace.model.target.TraceObject.ConflictResolution;
|
||||
import ghidra.trace.model.thread.*;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.util.*;
|
||||
@ -70,7 +70,8 @@ public class ProgramEmulationUtils {
|
||||
<attribute name='Modules' schema='ModuleContainer' />
|
||||
<attribute name='Threads' schema='ThreadContainer' />
|
||||
</schema>
|
||||
<schema name='BreakpointContainer' elementResync='NEVER' attributeResync='NEVER'>
|
||||
<schema name='BreakpointContainer' canonical='yes' elementResync='NEVER'
|
||||
attributeResync='NEVER'>
|
||||
<interface name='BreakpointSpecContainer' />
|
||||
<interface name='BreakpointLocationContainer' />
|
||||
<element schema='Breakpoint' />
|
||||
@ -494,6 +495,20 @@ public class ProgramEmulationUtils {
|
||||
throw new EmulatorOutOfMemoryException();
|
||||
}
|
||||
|
||||
protected static void createObjects(Trace trace) {
|
||||
TraceObjectManager om = trace.getObjectManager();
|
||||
om.createRootObject(EMU_SESSION_SCHEMA);
|
||||
|
||||
om.createObject(TraceObjectKeyPath.parse("Breakpoints"))
|
||||
.insert(Lifespan.ALL, ConflictResolution.DENY);
|
||||
om.createObject(TraceObjectKeyPath.parse("Memory"))
|
||||
.insert(Lifespan.ALL, ConflictResolution.DENY);
|
||||
om.createObject(TraceObjectKeyPath.parse("Modules"))
|
||||
.insert(Lifespan.ALL, ConflictResolution.DENY);
|
||||
om.createObject(TraceObjectKeyPath.parse("Threads"))
|
||||
.insert(Lifespan.ALL, ConflictResolution.DENY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new trace with a single thread, ready for emulation of the given program
|
||||
*
|
||||
@ -510,7 +525,8 @@ public class ProgramEmulationUtils {
|
||||
try {
|
||||
trace = new DBTrace(getTraceName(program), program.getCompilerSpec(), consumer);
|
||||
try (Transaction tx = trace.openTransaction("Emulate")) {
|
||||
trace.getObjectManager().createRootObject(EMU_SESSION_SCHEMA);
|
||||
createObjects(trace);
|
||||
|
||||
TraceSnapshot initial =
|
||||
trace.getTimeManager().createSnapshot(EMULATION_STARTED_AT + pc);
|
||||
long snap = initial.getKey();
|
||||
|
@ -34,7 +34,6 @@ import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.modules.*;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.ComparatorMath;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
@ -163,11 +162,17 @@ public enum DebuggerStaticMappingUtils {
|
||||
private Address min = null;
|
||||
private Address max = null;
|
||||
|
||||
public void consider(Address min, Address max) {
|
||||
this.min = this.min == null ? min : ComparatorMath.cmin(this.min, min);
|
||||
this.max = this.max == null ? max : ComparatorMath.cmax(this.max, max);
|
||||
}
|
||||
|
||||
public void consider(AddressRange range) {
|
||||
min = min == null ? range.getMinAddress()
|
||||
: ComparatorMath.cmin(min, range.getMinAddress());
|
||||
max = max == null ? range.getMaxAddress()
|
||||
: ComparatorMath.cmax(max, range.getMaxAddress());
|
||||
consider(range.getMinAddress(), range.getMaxAddress());
|
||||
}
|
||||
|
||||
public void consider(Address address) {
|
||||
consider(address, address);
|
||||
}
|
||||
|
||||
public Address getMin() {
|
||||
@ -181,6 +186,10 @@ public enum DebuggerStaticMappingUtils {
|
||||
public long getLength() {
|
||||
return max.subtract(min) + 1;
|
||||
}
|
||||
|
||||
public AddressRange getRange() {
|
||||
return new AddressRangeImpl(getMin(), getMax());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isReal(MemoryBlock block) {
|
||||
|
@ -17,6 +17,7 @@ package ghidra.app.plugin.core.debug.service.modules;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingUtils.Extrema;
|
||||
import ghidra.app.services.DebuggerStaticMappingService;
|
||||
import ghidra.debug.api.modules.ModuleMapProposal;
|
||||
import ghidra.debug.api.modules.ModuleMapProposal.ModuleMapEntry;
|
||||
@ -27,6 +28,7 @@ import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.memory.TraceMemoryRegion;
|
||||
import ghidra.trace.model.memory.TraceObjectMemoryRegion;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
import ghidra.util.MathUtilities;
|
||||
|
||||
public class DefaultModuleMapProposal
|
||||
extends AbstractMapProposal<TraceModule, Program, ModuleMapEntry>
|
||||
@ -81,20 +83,24 @@ public class DefaultModuleMapProposal
|
||||
* @param program the program image whose size to compute
|
||||
* @return the size
|
||||
*/
|
||||
public static long computeImageSize(Program program) {
|
||||
Address imageBase = program.getImageBase();
|
||||
long imageSize = 0;
|
||||
public static AddressRange computeImageRange(Program program) {
|
||||
Extrema extrema = new Extrema();
|
||||
// TODO: How to handle Harvard architectures?
|
||||
for (MemoryBlock block : program.getMemory().getBlocks()) {
|
||||
if (!includeBlock(program, block)) {
|
||||
continue;
|
||||
}
|
||||
imageSize = Math.max(imageSize, block.getEnd().subtract(imageBase) + 1);
|
||||
// includeBlock checks address space is same as image base
|
||||
extrema.consider(block.getAddressRange());
|
||||
}
|
||||
return imageSize;
|
||||
if (program.getImageBase().getOffset() != 0) {
|
||||
extrema.consider(program.getImageBase());
|
||||
}
|
||||
return extrema.getRange();
|
||||
}
|
||||
|
||||
protected AddressRange moduleRange;
|
||||
protected AddressRange imageRange;
|
||||
protected boolean memorize = false;
|
||||
|
||||
/**
|
||||
@ -113,6 +119,7 @@ public class DefaultModuleMapProposal
|
||||
AddressRange moduleRange) {
|
||||
super(module.getTrace(), module, program, program);
|
||||
this.moduleRange = moduleRange;
|
||||
this.imageRange = quantize(computeImageRange(program));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -125,9 +132,18 @@ public class DefaultModuleMapProposal
|
||||
return getModule().getLifespan();
|
||||
}
|
||||
|
||||
private long getLength() {
|
||||
return MathUtilities.unsignedMin(moduleRange.getLength(), imageRange.getLength());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getFromRange() {
|
||||
return moduleRange;
|
||||
try {
|
||||
return new AddressRangeImpl(moduleRange.getMinAddress(), getLength());
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -138,21 +154,13 @@ public class DefaultModuleMapProposal
|
||||
@Override
|
||||
public void setProgram(Program program) {
|
||||
setToObject(program, program);
|
||||
try {
|
||||
this.moduleRange = quantize(
|
||||
new AddressRangeImpl(getModule().getBase(), computeImageSize(program)));
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
// This is terribly unlikely
|
||||
throw new IllegalArgumentException(
|
||||
"Specified program is too large for module's memory space");
|
||||
}
|
||||
this.imageRange = quantize(computeImageRange(program));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getToRange() {
|
||||
try {
|
||||
return new AddressRangeImpl(getToProgram().getImageBase(), moduleRange.getLength());
|
||||
return new AddressRangeImpl(imageRange.getMinAddress(), getLength());
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
throw new AssertionError(e);
|
||||
@ -174,10 +182,8 @@ public class DefaultModuleMapProposal
|
||||
|
||||
// indexed by region's offset from module base
|
||||
protected final NavigableMap<Long, ModuleRegionMatcher> matchers = new TreeMap<>();
|
||||
protected Address imageBase;
|
||||
protected Address moduleBase;
|
||||
protected long imageSize;
|
||||
protected AddressRange moduleRange; // TODO: This is now in the trace schema. Use it.
|
||||
protected AddressRange imageRange;
|
||||
protected AddressRange moduleRange;
|
||||
|
||||
protected DefaultModuleMapProposal(TraceModule module, Program program) {
|
||||
super(module.getTrace(), program);
|
||||
@ -196,8 +202,8 @@ public class DefaultModuleMapProposal
|
||||
}
|
||||
|
||||
private void processProgram() {
|
||||
imageBase = program.getImageBase();
|
||||
imageSize = DefaultModuleMapEntry.computeImageSize(program);
|
||||
imageRange = quantize(DefaultModuleMapEntry.computeImageRange(program));
|
||||
Address imageBase = imageRange.getMinAddress(); // not precisely, but good enough
|
||||
// TODO: How to handle Harvard architectures?
|
||||
for (MemoryBlock block : program.getMemory().getBlocks()) {
|
||||
if (!DefaultModuleMapEntry.includeBlock(program, block)) {
|
||||
@ -211,13 +217,8 @@ public class DefaultModuleMapProposal
|
||||
* Must be called after processProgram, so that image size is known
|
||||
*/
|
||||
private void processModule() {
|
||||
moduleBase = module.getBase();
|
||||
try {
|
||||
moduleRange = quantize(new AddressRangeImpl(moduleBase, imageSize));
|
||||
}
|
||||
catch (AddressOverflowException e) {
|
||||
return; // Just score it as having no matches?
|
||||
}
|
||||
moduleRange = quantize(module.getRange());
|
||||
Address moduleBase = moduleRange.getMinAddress();
|
||||
Lifespan lifespan = module.getLifespan();
|
||||
for (TraceMemoryRegion region : module.getTrace()
|
||||
.getMemoryManager()
|
||||
|
@ -499,6 +499,21 @@ public enum PathUtils {
|
||||
return Objects.equals(ancestor, successor.subList(0, ancestor.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming the first path is an ancestor of the second, compute the relative path from the
|
||||
* first to the second.
|
||||
*
|
||||
* @param ancestor the ancestor (from)
|
||||
* @param successor the successor (to)
|
||||
* @return the relative path
|
||||
*/
|
||||
public static List<String> relativize(List<String> ancestor, List<String> successor) {
|
||||
if (!isAncestor(ancestor, successor)) {
|
||||
throw new IllegalArgumentException("First must be an ancestor of the second");
|
||||
}
|
||||
return successor.subList(ancestor.size(), successor.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given object-valued attribute is a link.
|
||||
*
|
||||
@ -538,6 +553,7 @@ public enum PathUtils {
|
||||
* Check whether a given attribute should be displayed.
|
||||
*
|
||||
* @param key the key of the given attribute
|
||||
* @return true if hidden
|
||||
*/
|
||||
public static boolean isHidden(String key) {
|
||||
return key.startsWith(TargetObject.PREFIX_INVISIBLE);
|
||||
|
@ -121,8 +121,19 @@ public class DBTraceObjectBreakpointLocation
|
||||
@Override
|
||||
public String getName() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME, String.class, "");
|
||||
String display = TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
|
||||
TargetObject.DISPLAY_ATTRIBUTE_NAME, String.class, null);
|
||||
if (display != null) {
|
||||
return display;
|
||||
}
|
||||
TraceObject container =
|
||||
object.queryCanonicalAncestorsTargetInterface(TargetBreakpointSpecContainer.class)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (container == null) {
|
||||
return ""; // Should be impossible, but maybe not a sane schema
|
||||
}
|
||||
return container.getCanonicalPath().relativize(object.getCanonicalPath()).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,8 +323,16 @@ public class DBTraceObjectBreakpointLocation
|
||||
@Override
|
||||
public String getComment() {
|
||||
try (LockHold hold = object.getTrace().lockRead()) {
|
||||
return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(), KEY_COMMENT,
|
||||
String.class, "");
|
||||
String comment = TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
|
||||
KEY_COMMENT, String.class, "");
|
||||
if (!comment.isBlank()) {
|
||||
return comment;
|
||||
}
|
||||
TraceObjectBreakpointSpec spec = getSpecification();
|
||||
if (spec == null) {
|
||||
return "";
|
||||
}
|
||||
return spec.getExpression();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,6 +181,12 @@ public class DBTraceObjectBreakpointSpec
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExpression() {
|
||||
return TraceObjectInterfaceUtils.getValue(object, getPlacedSnap(),
|
||||
TargetBreakpointSpec.EXPRESSION_ATTRIBUTE_NAME, String.class, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<TraceThread> getThreads() {
|
||||
throw new UnsupportedOperationException("Ask a location instead");
|
||||
|
@ -18,7 +18,12 @@ package ghidra.trace.database.time;
|
||||
import java.io.IOException;
|
||||
|
||||
import db.DBRecord;
|
||||
import ghidra.dbg.target.TargetEventScope;
|
||||
import ghidra.trace.database.target.DBTraceObject;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.target.TraceObjectValue;
|
||||
import ghidra.trace.model.thread.TraceObjectThread;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.trace.model.time.TraceSnapshot;
|
||||
import ghidra.trace.model.time.schedule.TraceSchedule;
|
||||
@ -143,7 +148,26 @@ public class DBTraceSnapshot extends DBAnnotatedObject implements TraceSnapshot
|
||||
@Override
|
||||
public TraceThread getEventThread() {
|
||||
try (LockHold hold = LockHold.lock(manager.lock.readLock())) {
|
||||
return eventThread;
|
||||
if (eventThread != null) {
|
||||
return eventThread;
|
||||
}
|
||||
// TODO: Can it be something other than root?
|
||||
DBTraceObject root = manager.trace.getObjectManager().getRootObject();
|
||||
if (root == null) {
|
||||
return null;
|
||||
}
|
||||
if (!root.getTargetSchema().getInterfaces().contains(TargetEventScope.class)) {
|
||||
return null;
|
||||
}
|
||||
TraceObjectValue eventAttr =
|
||||
root.getAttribute(getKey(), TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME);
|
||||
if (eventAttr == null) {
|
||||
return null;
|
||||
}
|
||||
if (!(eventAttr.getValue() instanceof TraceObject eventObj)) {
|
||||
return null;
|
||||
}
|
||||
return eventObj.queryInterface(TraceObjectThread.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,5 +43,7 @@ public interface TraceObjectBreakpointSpec extends TraceBreakpoint, TraceObjectI
|
||||
|
||||
Collection<? extends TraceObjectBreakpointLocation> getLocations();
|
||||
|
||||
String getExpression();
|
||||
|
||||
void setKinds(Lifespan lifespan, Collection<TraceBreakpointKind> kinds);
|
||||
}
|
||||
|
@ -224,8 +224,8 @@ public final class TraceObjectKeyPath implements Comparable<TraceObjectKeyPath>
|
||||
/**
|
||||
* Stream, starting with the longer paths, paths that match the given predicates
|
||||
*
|
||||
* @param matcher
|
||||
* @return
|
||||
* @param predicates the predicates to filter the ancestor paths
|
||||
* @return the stream of matching paths, longest to shortest
|
||||
*/
|
||||
public Stream<TraceObjectKeyPath> streamMatchingAncestry(PathPredicates predicates) {
|
||||
if (!predicates.ancestorMatches(keyList, false)) {
|
||||
@ -253,4 +253,15 @@ public final class TraceObjectKeyPath implements Comparable<TraceObjectKeyPath>
|
||||
public boolean isAncestor(TraceObjectKeyPath that) {
|
||||
return PathUtils.isAncestor(keyList, that.keyList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming this is an ancestor of the given successor, compute the relative path from here to
|
||||
* there
|
||||
*
|
||||
* @param successor the successor
|
||||
* @return the relative path
|
||||
*/
|
||||
public TraceObjectKeyPath relativize(TraceObjectKeyPath successor) {
|
||||
return TraceObjectKeyPath.of(PathUtils.relativize(keyList, successor.keyList));
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import static org.junit.Assert.assertTrue;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
@ -50,23 +51,24 @@ 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.objects.components.DebuggerMethodInvocationDialog;
|
||||
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.tracermi.connection.TraceRmiConnectionManagerPlugin;
|
||||
import ghidra.app.plugin.core.debug.gui.watch.DebuggerWatchesProvider;
|
||||
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.stack.StackUnwinderTest;
|
||||
import ghidra.app.plugin.core.debug.stack.StackUnwinderTest.HoverLocation;
|
||||
import ghidra.app.plugin.core.debug.stack.UnwindStackCommand;
|
||||
import ghidra.app.plugin.core.decompile.DecompilerProvider;
|
||||
import ghidra.app.plugin.core.terminal.TerminalProvider;
|
||||
import ghidra.app.script.GhidraState;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.services.DebuggerEmulationService.EmulationResult;
|
||||
@ -74,19 +76,17 @@ 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.api.model.DebuggerProgramLaunchOffer;
|
||||
import ghidra.debug.api.model.DebuggerProgramLaunchOffer.*;
|
||||
import ghidra.debug.api.modules.ModuleMapProposal;
|
||||
import ghidra.debug.api.tracermi.RemoteMethod;
|
||||
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer;
|
||||
import ghidra.debug.api.tracermi.TraceRmiLaunchOffer.*;
|
||||
import ghidra.debug.api.watch.WatchRow;
|
||||
import ghidra.debug.flatapi.FlatDebuggerAPI;
|
||||
import ghidra.debug.flatapi.FlatDebuggerRmiAPI;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.TestApplicationUtils;
|
||||
import ghidra.framework.model.DomainFolder;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.framework.plugintool.util.PluginUtils;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSet;
|
||||
@ -96,11 +96,11 @@ import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.util.GhidraProgramUtilities;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.pty.*;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.trace.model.breakpoint.TraceBreakpoint;
|
||||
import ghidra.trace.model.breakpoint.*;
|
||||
import ghidra.trace.model.guest.TracePlatform;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
import ghidra.trace.model.modules.TraceSection;
|
||||
import ghidra.trace.model.modules.*;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.trace.model.time.schedule.*;
|
||||
import ghidra.util.InvalidNameException;
|
||||
@ -117,26 +117,36 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
|
||||
protected static final String TERMMINES_PATH = "/tmp/termmines";
|
||||
|
||||
static class MyTestEnv extends TestEnv {
|
||||
public MyTestEnv(String projectName) throws IOException {
|
||||
super(projectName);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PluginTool launchDefaultToolByName(String toolName) {
|
||||
return super.launchDefaultToolByName(toolName);
|
||||
}
|
||||
}
|
||||
|
||||
protected final ConsoleTaskMonitor monitor = new ConsoleTaskMonitor();
|
||||
|
||||
protected TerminalService terminalService;
|
||||
protected ProgramManager programManager;
|
||||
protected CodeViewerService staticListingService;
|
||||
protected DebuggerModelService modelService;
|
||||
protected MyTestEnv env;
|
||||
|
||||
protected DebuggerModelFactory gdbFactory;
|
||||
|
||||
protected final FlatDebuggerAPI flatDbg = new FlatDebuggerAPI() {
|
||||
protected final FlatDebuggerRmiAPI flatDbg = new FlatDebuggerRmiAPI() {
|
||||
@Override
|
||||
public GhidraState getState() {
|
||||
Navigatable nav = staticListingService.getNavigatable();
|
||||
return new GhidraState(tool, env.getProject(),
|
||||
nav.getProgram(), nav.getLocation(), nav.getSelection(), nav.getHighlight());
|
||||
return new GhidraState(tool, env.getProject(), nav.getProgram(), nav.getLocation(),
|
||||
nav.getSelection(), nav.getHighlight());
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected TestEnv newTestEnv() throws Exception {
|
||||
return new TestEnv("DebuggerCourse");
|
||||
return env = new MyTestEnv("DebuggerCourse");
|
||||
}
|
||||
|
||||
// TODO: Propose this replace waitForProgram
|
||||
@ -167,7 +177,7 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
|
||||
@Override
|
||||
public void prepareTool() {
|
||||
tool = env.launchTool("Debugger");
|
||||
tool = env.launchDefaultToolByName("Debugger");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -214,20 +224,23 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
}
|
||||
termmines.setExecutable(true);
|
||||
|
||||
terminalService = tool.getService(TerminalService.class);
|
||||
programManager = tool.getService(ProgramManager.class);
|
||||
staticListingService = getStaticListingService();
|
||||
}
|
||||
|
||||
modelService = tool.getService(DebuggerModelService.class);
|
||||
gdbFactory = modelService.getModelFactories()
|
||||
.stream()
|
||||
.filter(f -> "gdb".equals(f.getBrief()))
|
||||
.findAny()
|
||||
.get();
|
||||
@Test
|
||||
public void testGettingStarted_Termmines() throws Throwable {
|
||||
Pty pty = PtyFactory.local().openpty();
|
||||
pty.getChild().session(new String[] { TERMMINES_PATH }, Map.of("TERM", "xterm-256color"));
|
||||
PtyParent parent = pty.getParent();
|
||||
try (Terminal terminal =
|
||||
terminalService.createWithStreams(Charset.forName("utf8"), parent.getInputStream(),
|
||||
parent.getOutputStream())) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Property<String> gdbPathProp =
|
||||
(Property<String>) gdbFactory.getOptions().get("GDB launch command");
|
||||
gdbPathProp.setValue(DummyProc.which("gdb"));
|
||||
TerminalProvider provider = waitForComponentProvider(TerminalProvider.class);
|
||||
captureIsolatedProvider(provider, 600, 600);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -235,32 +248,46 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
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() {
|
||||
protected void captureLaunchDialog(String title) {
|
||||
TraceRmiLaunchOffer offer = flatDbg.getLaunchOffers(program)
|
||||
.stream()
|
||||
.filter(o -> title.equals(o.getTitle()))
|
||||
.findAny()
|
||||
.orElseThrow();
|
||||
|
||||
runSwingLater(() -> offer.launchProgram(monitor, new LaunchConfigurator() {
|
||||
@Override
|
||||
public Map<String, ?> configureLauncher(TargetLauncher launcher,
|
||||
Map<String, ?> arguments, RelPrompt relPrompt) {
|
||||
if (extraArgs.length() == 0) {
|
||||
return arguments;
|
||||
}
|
||||
Map<String, Object> adjusted = new HashMap<>(arguments);
|
||||
TargetCmdLineLauncher.PARAMETER_CMDLINE_ARGS.adjust(adjusted,
|
||||
c -> c + " " + extraArgs);
|
||||
return adjusted;
|
||||
public PromptMode getPromptMode() {
|
||||
return PromptMode.ALWAYS;
|
||||
}
|
||||
};
|
||||
LaunchResult result = waitOn(offer.launchProgram(monitor, PromptMode.NEVER, config));
|
||||
}));
|
||||
|
||||
captureDialog(DebuggerMethodInvocationDialog.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGettingStarted_LaunchGDBDialog() {
|
||||
captureLaunchDialog("gdb");
|
||||
}
|
||||
|
||||
protected LaunchResult launchProgramInGdb(String extraArgs) throws Throwable {
|
||||
TraceRmiLaunchOffer offer = flatDbg.getLaunchOffers(program)
|
||||
.stream()
|
||||
.filter(o -> "gdb".equals(o.getTitle()))
|
||||
.findAny()
|
||||
.orElseThrow();
|
||||
LaunchResult result = flatDbg.launch(offer, Map.ofEntries(
|
||||
Map.entry("env:OPT_START_CMD", "start"),
|
||||
Map.entry("args", extraArgs)),
|
||||
monitor);
|
||||
if (result.exception() != null) {
|
||||
throw result.exception();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void launchProgramInGdb() throws Throwable {
|
||||
launchProgramInGdb("");
|
||||
protected LaunchResult launchProgramInGdb() throws Throwable {
|
||||
return launchProgramInGdb("");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -278,15 +305,33 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
captureProvider(DebuggerBreakpointsProvider.class);
|
||||
}
|
||||
|
||||
protected void placeBreakpointsSRandRand() throws Throwable {
|
||||
protected void waitBreakSpecExists(String expression) {
|
||||
waitForCondition(() -> flatDbg.getAllBreakpoints()
|
||||
.stream()
|
||||
.flatMap(lb -> lb.getTraceBreakpoints().stream())
|
||||
.<TraceObjectBreakpointSpec> mapMulti((loc, down) -> {
|
||||
if (loc instanceof TraceObjectBreakpointLocation oloc) {
|
||||
down.accept(oloc.getSpecification());
|
||||
}
|
||||
})
|
||||
.distinct()
|
||||
.filter(l -> expression.equals(l.getExpression()))
|
||||
.count() == 1);
|
||||
}
|
||||
|
||||
protected void placeBreakpointsSRand() throws Throwable {
|
||||
assertTrue(flatDbg.execute("break srand"));
|
||||
assertTrue(flatDbg.execute("break rand"));
|
||||
waitForCondition(() -> flatDbg.getAllBreakpoints().size() == 2);
|
||||
waitBreakSpecExists("srand");
|
||||
}
|
||||
|
||||
protected void placeBreakpointsRand() throws Throwable {
|
||||
assertTrue(flatDbg.execute("break rand"));
|
||||
waitForCondition(() -> flatDbg.getAllBreakpoints().size() == 1);
|
||||
waitBreakSpecExists("rand");
|
||||
}
|
||||
|
||||
protected void placeBreakpointsSRandRand() throws Throwable {
|
||||
placeBreakpointsSRand();
|
||||
placeBreakpointsRand();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -298,11 +343,16 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
captureProvider(DebuggerBreakpointsProvider.class);
|
||||
}
|
||||
|
||||
protected Address navigateToBreakpoint(String comment) {
|
||||
protected Address navigateToBreakpoint(String expression) {
|
||||
TraceBreakpoint bp = flatDbg.getAllBreakpoints()
|
||||
.stream()
|
||||
.flatMap(l -> l.getTraceBreakpoints().stream())
|
||||
.filter(l -> comment.equals(l.getComment()))
|
||||
.<TraceObjectBreakpointLocation> mapMulti((loc, down) -> {
|
||||
if (loc instanceof TraceObjectBreakpointLocation oloc) {
|
||||
down.accept(oloc);
|
||||
}
|
||||
})
|
||||
.filter(l -> expression.equals(l.getSpecification().getExpression()))
|
||||
.findAny()
|
||||
.get();
|
||||
Address dynAddr = bp.getMinAddress();
|
||||
@ -367,6 +417,14 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
Address dynAddr = navigateToBreakpoint("srand");
|
||||
TraceModule modLibC = getModuleContaining(dynAddr);
|
||||
Program progLibC = importModule(modLibC);
|
||||
|
||||
// This module might be symlinked, so module name and file name may not match.
|
||||
DebuggerStaticMappingService mappings = tool.getService(DebuggerStaticMappingService.class);
|
||||
ModuleMapProposal proposal = mappings.proposeModuleMap(modLibC, progLibC);
|
||||
try (Transaction tx = modLibC.getTrace().openTransaction("Map")) {
|
||||
mappings.addModuleMappings(proposal.computeMap().values(), monitor, true);
|
||||
}
|
||||
|
||||
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
|
||||
disassembleSymbol(progLibC, "srand");
|
||||
// Just to be sure.
|
||||
@ -385,9 +443,16 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
Address dynAddr = navigateToBreakpoint("srand");
|
||||
TraceModule modLibC = getModuleContaining(dynAddr);
|
||||
Program progLibC = importModule(modLibC);
|
||||
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
|
||||
|
||||
// This module might be symlinked, so module name and file name may not match.
|
||||
DebuggerStaticMappingService mappings = tool.getService(DebuggerStaticMappingService.class);
|
||||
ModuleMapProposal proposal = mappings.proposeModuleMap(modLibC, progLibC);
|
||||
try (Transaction tx = modLibC.getTrace().openTransaction("Map")) {
|
||||
mappings.addModuleMappings(proposal.computeMap().values(), monitor, true);
|
||||
}
|
||||
|
||||
Address stAddr = waitForValue(() -> flatDbg.translateDynamicToStatic(dynAddr));
|
||||
disassembleSymbol(progLibC, "srand");
|
||||
Address stAddr = flatDbg.translateDynamicToStatic(dynAddr);
|
||||
// Just to be sure.
|
||||
goTo(tool, progLibC, stAddr);
|
||||
flatDbg.resume();
|
||||
@ -488,6 +553,18 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
public void testNavigation_ThreadsInCallRand() throws Throwable {
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsRand();
|
||||
Address dynAddr = navigateToBreakpoint("rand");
|
||||
TraceModule modLibC = getModuleContaining(dynAddr);
|
||||
Program progLibC = importModule(modLibC);
|
||||
|
||||
// This module might be symlinked, so module name and file name may not match.
|
||||
DebuggerStaticMappingService mappings = tool.getService(DebuggerStaticMappingService.class);
|
||||
ModuleMapProposal proposal = mappings.proposeModuleMap(modLibC, progLibC);
|
||||
try (Transaction tx = modLibC.getTrace().openTransaction("Map")) {
|
||||
mappings.addModuleMappings(proposal.computeMap().values(), monitor, true);
|
||||
}
|
||||
|
||||
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
|
||||
flatDbg.resume();
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
@ -500,7 +577,15 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
placeBreakpointsRand();
|
||||
Address dynAddr = navigateToBreakpoint("rand");
|
||||
TraceModule modLibC = getModuleContaining(dynAddr);
|
||||
importModule(modLibC);
|
||||
Program progLibC = importModule(modLibC);
|
||||
|
||||
// This module might be symlinked, so module name and file name may not match.
|
||||
DebuggerStaticMappingService mappings = tool.getService(DebuggerStaticMappingService.class);
|
||||
ModuleMapProposal proposal = mappings.proposeModuleMap(modLibC, progLibC);
|
||||
try (Transaction tx = modLibC.getTrace().openTransaction("Map")) {
|
||||
mappings.addModuleMappings(proposal.computeMap().values(), monitor, true);
|
||||
}
|
||||
|
||||
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
|
||||
flatDbg.resume();
|
||||
|
||||
@ -513,8 +598,11 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
launchProgramInGdb();
|
||||
placeBreakpointsSRandRand();
|
||||
flatDbg.resume(); // srand
|
||||
Thread.sleep(500);
|
||||
flatDbg.resume(); // rand.1
|
||||
Thread.sleep(500);
|
||||
flatDbg.stepOut();
|
||||
Thread.sleep(500);
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
captureProvider(DebuggerTimeProvider.class);
|
||||
@ -522,18 +610,29 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
|
||||
@Test
|
||||
public void testNavigation_DialogCompareTimes() throws Throwable {
|
||||
launchProgramInGdb(); // main
|
||||
LaunchResult result = launchProgramInGdb(); // main
|
||||
placeBreakpointsRand();
|
||||
Address pc = flatDbg.getProgramCounter();
|
||||
long snapA = flatDbg.getCurrentSnap();
|
||||
TraceModule modTermmines = Unique.assertOne(flatDbg.getCurrentTrace()
|
||||
.getModuleManager()
|
||||
.getModulesAt(snapA, pc));
|
||||
try (Transaction tx = result.trace().openTransaction("Name snapshot")) {
|
||||
result.trace()
|
||||
.getTimeManager()
|
||||
.getSnapshot(snapA, false)
|
||||
.setDescription("Initial snapshot");
|
||||
}
|
||||
TraceObjectModule modTermmines =
|
||||
(TraceObjectModule) Unique.assertOne(flatDbg.getCurrentTrace()
|
||||
.getModuleManager()
|
||||
.getModulesAt(snapA, pc));
|
||||
|
||||
RemoteMethod refreshSections = result.connection().getMethods().get("refresh_sections");
|
||||
refreshSections.invoke(Map.of("node", modTermmines.getObject()));
|
||||
TraceSection secTermminesData = modTermmines.getSectionByName(".data");
|
||||
flatDbg.readMemory(secTermminesData.getStart(),
|
||||
(int) secTermminesData.getRange().getLength(), monitor);
|
||||
|
||||
flatDbg.resume(); // rand.1
|
||||
Thread.sleep(500);
|
||||
flatDbg.readMemory(secTermminesData.getStart(),
|
||||
(int) secTermminesData.getRange().getLength(), monitor);
|
||||
|
||||
@ -547,13 +646,23 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
|
||||
@Test
|
||||
public void testNavigation_CompareTimes() throws Throwable {
|
||||
launchProgramInGdb("-M 15"); // main
|
||||
LaunchResult result = launchProgramInGdb("-M 15"); // main
|
||||
placeBreakpointsRand();
|
||||
Address pc = flatDbg.getProgramCounter();
|
||||
long snapA = flatDbg.getCurrentSnap();
|
||||
TraceModule modTermmines = Unique.assertOne(flatDbg.getCurrentTrace()
|
||||
.getModuleManager()
|
||||
.getModulesAt(snapA, pc));
|
||||
try (Transaction tx = result.trace().openTransaction("Name snapshot")) {
|
||||
result.trace()
|
||||
.getTimeManager()
|
||||
.getSnapshot(snapA, false)
|
||||
.setDescription("Initial snapshot");
|
||||
}
|
||||
TraceObjectModule modTermmines =
|
||||
(TraceObjectModule) Unique.assertOne(flatDbg.getCurrentTrace()
|
||||
.getModuleManager()
|
||||
.getModulesAt(snapA, pc));
|
||||
|
||||
RemoteMethod refreshSections = result.connection().getMethods().get("refresh_sections");
|
||||
refreshSections.invoke(Map.of("node", modTermmines.getObject()));
|
||||
TraceSection secTermminesData = modTermmines.getSectionByName(".data");
|
||||
flatDbg.readMemory(secTermminesData.getStart(),
|
||||
(int) secTermminesData.getRange().getLength(), monitor);
|
||||
@ -582,6 +691,10 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
});
|
||||
waitForCondition(() -> actionNextDiff.isEnabled());
|
||||
flatDbg.goToDynamic(secTermminesData.getStart());
|
||||
// Because auto-strack is a little broken right now
|
||||
Thread.sleep(500);
|
||||
flatDbg.goToDynamic(secTermminesData.getStart());
|
||||
|
||||
performAction(actionNextDiff);
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
@ -611,7 +724,15 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
showProvider(DebuggerStaticMappingProvider.class);
|
||||
Address dynAddr = navigateToBreakpoint("srand");
|
||||
TraceModule modLibC = getModuleContaining(dynAddr);
|
||||
importModule(modLibC);
|
||||
Program progLibC = importModule(modLibC);
|
||||
|
||||
// This module might be symlinked, so module name and file name may not match.
|
||||
DebuggerStaticMappingService mappings = tool.getService(DebuggerStaticMappingService.class);
|
||||
ModuleMapProposal proposal = mappings.proposeModuleMap(modLibC, progLibC);
|
||||
try (Transaction tx = modLibC.getTrace().openTransaction("Map")) {
|
||||
mappings.addModuleMappings(proposal.computeMap().values(), monitor, true);
|
||||
}
|
||||
|
||||
waitForCondition(() -> flatDbg.translateDynamicToStatic(dynAddr) != null);
|
||||
|
||||
runSwing(() -> tool.setSize(1920, 1080));
|
||||
@ -631,29 +752,30 @@ public class TutorialDebuggerScreenShots extends GhidraScreenShotGenerator
|
||||
DebuggerListingService listings = tool.getService(DebuggerListingService.class);
|
||||
runSwing(() -> listings
|
||||
.setCurrentSelection(new ProgramSelection(new AddressSet(modNcurses.getRange()))));
|
||||
DebuggerListingProvider listingProvider =
|
||||
waitForComponentProvider(DebuggerListingProvider.class);
|
||||
performAction("Copy Into New Program",
|
||||
PluginUtils.getPluginNameFromClass(DebuggerCopyActionsPlugin.class), false);
|
||||
PluginUtils.getPluginNameFromClass(DebuggerCopyActionsPlugin.class), listingProvider,
|
||||
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);
|
||||
public void testRemoteTargets_GdbPlusGdbserverViaSsh() throws Throwable {
|
||||
captureLaunchDialog("gdb + gdbserver via ssh");
|
||||
}
|
||||
|
||||
@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)"));
|
||||
public void testRemoteTargets_GdbViaSsh() throws Throwable {
|
||||
captureLaunchDialog("gdb via ssh");
|
||||
}
|
||||
|
||||
captureDialog(dialog);
|
||||
@Test
|
||||
public void testRemoteTargets_AcceptTraceRmi() throws Throwable {
|
||||
performAction("Connect by Accept",
|
||||
PluginUtils.getPluginNameFromClass(TraceRmiConnectionManagerPlugin.class),
|
||||
false);
|
||||
captureDialog(DebuggerMethodInvocationDialog.class);
|
||||
}
|
||||
|
||||
protected Function findCommandLineParser() throws Throwable {
|
||||
|
@ -128,16 +128,14 @@ icon in my Tool Chest</a></li>
|
||||
<li><a href="#there-is-no-debug-launch-icon-in-the-global-toolbar"
|
||||
id="toc-there-is-no-debug-launch-icon-in-the-global-toolbar">There is no
|
||||
Debug / Launch icon in the global toolbar</a></li>
|
||||
<li><a href="#there-is-no-gdb-option-in-the-launch-drop-down"
|
||||
id="toc-there-is-no-gdb-option-in-the-launch-drop-down">There is no
|
||||
<strong>gdb</strong> option in the launch drop-down</a></li>
|
||||
<li><a
|
||||
href="#there-is-no-debug-termmines-in-gdb-locally-in-vm-option-in-the-launch-drop-down"
|
||||
id="toc-there-is-no-debug-termmines-in-gdb-locally-in-vm-option-in-the-launch-drop-down">There
|
||||
is no “Debug termmines in GDB locally IN-VM” option in the launch
|
||||
drop-down</a></li>
|
||||
<li><a
|
||||
href="#the-launch-hangs-for-several-seconds-and-then-prompt-for-a-recorder"
|
||||
id="toc-the-launch-hangs-for-several-seconds-and-then-prompt-for-a-recorder">The
|
||||
launch hangs for several seconds and then prompt for a
|
||||
“recorder”</a></li>
|
||||
href="#the-launch-hangs-for-several-seconds-and-then-i-get-prompted-with-a-wall-of-text"
|
||||
id="toc-the-launch-hangs-for-several-seconds-and-then-i-get-prompted-with-a-wall-of-text">The
|
||||
launch hangs for several seconds and then I get prompted with a wall of
|
||||
text</a></li>
|
||||
<li><a href="#the-dynamic-listing-is-empty"
|
||||
id="toc-the-dynamic-listing-is-empty">The Dynamic Listing is
|
||||
empty</a></li>
|
||||
@ -192,8 +190,16 @@ trust. For <code>termmines</code>, the risk is negligible. Run it:</p>
|
||||
<div class="sourceCode" id="cb2"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="ex">./termmines</span></span></code></pre></div>
|
||||
<p>You should see a 9x9 grid and a cursor you can move with the arrow
|
||||
keys. Hit <strong>Ctrl-C</strong> to exit. Probe it for help. Most Linux
|
||||
programs accept a <code>-h</code> argument for help:</p>
|
||||
keys.</p>
|
||||
<figure>
|
||||
<img src="images/GettingStarted_Termmines.png"
|
||||
alt="Termmines running in a Terminal" />
|
||||
<figcaption aria-hidden="true">Termmines running in a
|
||||
Terminal</figcaption>
|
||||
</figure>
|
||||
<p>Hit <strong><code>CTRL</code>-<code>C</code></strong> to exit. Probe
|
||||
it for help. Most Linux programs accept a <code>-h</code> argument for
|
||||
help:</p>
|
||||
<div class="sourceCode" id="cb3"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">./termmines</span> <span class="at">-h</span></span></code></pre></div>
|
||||
<p>You should now have all the information you need to understand how
|
||||
@ -223,7 +229,18 @@ open</figcaption>
|
||||
</figure></li>
|
||||
<li><p>In the Debugger tool, click the dropdown ▾ for the debug <img
|
||||
src="images/debugger.png" alt="debug button" /> icon in the global tool
|
||||
bar, and select “Debug termmines in GDB locally IN-VM.”</p></li>
|
||||
bar, and select <strong>Configure and Launch termmines using… →
|
||||
gdb</strong>.</p>
|
||||
<figure>
|
||||
<img src="images/GettingStarted_LaunchGDBDialog.png"
|
||||
alt="Launch GDB Dialog" />
|
||||
<figcaption aria-hidden="true">Launch GDB Dialog</figcaption>
|
||||
</figure></li>
|
||||
<li><p>Change the <strong>Run Command</strong> to “start” (not
|
||||
“starti”). <strong>NOTE</strong>: In practice, this is rarely
|
||||
recommended, because most targets do not export their <code>main</code>
|
||||
function.</p></li>
|
||||
<li><p>Click the <strong>Launch</strong> button in the dialog.</p></li>
|
||||
<li><p>Wait a bit then verify the Dynamic Listing window (top) is
|
||||
displaying disassembly code.</p>
|
||||
<figure>
|
||||
@ -236,20 +253,18 @@ termmines</figcaption>
|
||||
</section>
|
||||
<section id="launching-on-windows" class="level2">
|
||||
<h2>Launching on Windows</h2>
|
||||
<p>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
|
||||
<code>termmines.exe</code> and select “Debug termmines.exe in dbgeng
|
||||
locally IN-VM.”</p>
|
||||
<p>On Windows, we will use the Windows Debugger dbgeng.dll 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 <code>termmines.exe</code> and select
|
||||
<strong>Configure and Launch termmines.exe using… → dbgeng</strong>.</p>
|
||||
</section>
|
||||
<section id="launching-on-macos" class="level2">
|
||||
<h2>Launching on macOS</h2>
|
||||
<p>Unfortunately, things are not so simple on macOS. See the
|
||||
instructions for <a
|
||||
href="../../../Ghidra/Debug/Debugger-swig-lldb/InstructionsForBuildingLLDBInterface.txt">Building
|
||||
LLDB-Java Bindings</a>. Once built, follow the same process as for
|
||||
Linux, except select “Debug termmines in LLDB locally IN-VM.”</p>
|
||||
<p>On macOS, we will use LLDB to debug the specimen. This is the
|
||||
debugger included with Xcode. Follow the same process as for Linux,
|
||||
except choose <strong>lldb</strong> in the last menu.</p>
|
||||
</section>
|
||||
<section id="troubleshooting" class="level2">
|
||||
<h2>Troubleshooting</h2>
|
||||
@ -280,73 +295,71 @@ 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.</p>
|
||||
</section>
|
||||
<section
|
||||
id="there-is-no-debug-termmines-in-gdb-locally-in-vm-option-in-the-launch-drop-down"
|
||||
<section id="there-is-no-gdb-option-in-the-launch-drop-down"
|
||||
class="level3">
|
||||
<h3>There is no “Debug termmines in GDB locally IN-VM” option in the
|
||||
launch drop-down</h3>
|
||||
<p>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:</p>
|
||||
<div class="sourceCode" id="cb4"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">which</span> gdb</span></code></pre></div>
|
||||
<p>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 <code>q</code> to quit GDB.</p>
|
||||
<ol type="1">
|
||||
<li>From the Debugger Targets window, click the Connect <img
|
||||
src="images/connect.png" alt="connect button" /> button.</li>
|
||||
<li>In the Connect dialog, select “gdb” from the dropdown at the
|
||||
top.</li>
|
||||
<li>Enter the full path, e.g., <code>/usr/bin/gdb</code>, in the “GDB
|
||||
launch command” field.</li>
|
||||
<li>Click “Connect”</li>
|
||||
<li>If you get an Interpreter window, then things have gone well.</li>
|
||||
<li>Type <code>echo test</code> into it to verify it’s responsive, then
|
||||
type <code>q</code> to disconnect.</li>
|
||||
<li>Close the Debugger tool, then retry.</li>
|
||||
</ol>
|
||||
<h3>There is no <strong>gdb</strong> option in the launch drop-down</h3>
|
||||
<p>You may have an older Debugger tool still configured for
|
||||
Recorder-based targets. We are transitioning to TraceRmi-based targets.
|
||||
Delete your Debugger tool and re-import the default one using the
|
||||
instructions above. If it is still not there, it’s possible your
|
||||
installation is corrupt. Search for a file called
|
||||
<code>local-gdb.sh</code> in your installation. Unlike the previous
|
||||
system, Trace RMI will not probe your system for dependencies nor hide
|
||||
incompatible launchers. All installed launchers should be present in the
|
||||
menus, even though some may not work on your configuration.</p>
|
||||
</section>
|
||||
<section
|
||||
id="the-launch-hangs-for-several-seconds-and-then-prompt-for-a-recorder"
|
||||
id="the-launch-hangs-for-several-seconds-and-then-i-get-prompted-with-a-wall-of-text"
|
||||
class="level3">
|
||||
<h3>The launch hangs for several seconds and then prompt for a
|
||||
“recorder”</h3>
|
||||
<p>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 <strong>Disconnect</strong>. Otherwise,
|
||||
use <strong>Disconnect All</strong> from the drop-down menu and
|
||||
re-launch.</p>
|
||||
<h3>The launch hangs for several seconds and then I get prompted with a
|
||||
wall of text</h3>
|
||||
<p>Read the wall of text. The first line should tell you the exception
|
||||
that it encountered. Often this is a timeout. Press the
|
||||
<strong>Keep</strong> button and then find the Terminal, usually in the
|
||||
bottom right. If you do not see it there, check the <strong>Window →
|
||||
Terminals</strong> menu. Once you have found the Terminal, check its
|
||||
output <em>starting at the top</em> for diagnostic messages. If you have
|
||||
something like <code>bash: gdb: command not found</code>, it is because
|
||||
you are missing <code>gdb</code>, or you need to tell Ghidra where to
|
||||
find it.</p>
|
||||
<p>If it is just missing, then install it and try again. If you need to
|
||||
tell Ghidra where it is, then in the launcher drop-down, select
|
||||
<strong>Configure and Launch termmines using… → gdb</strong>. DO NOT
|
||||
select <strong>Re-launch termmines using gdb</strong>, since this will
|
||||
not allow you to correct the configuration.</p>
|
||||
</section>
|
||||
<section id="the-dynamic-listing-is-empty" class="level3">
|
||||
<h3>The Dynamic Listing is empty</h3>
|
||||
<p>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.</p>
|
||||
<p>If you have an Interpreter window, there are several
|
||||
<strong>Connection Manager</strong> window, a populated
|
||||
<strong>Model</strong> window, and there should be a
|
||||
<strong>Terminal</strong> window. If not, then your GDB connector may
|
||||
not be configured properly. Try the steps under the previous
|
||||
heading.</p>
|
||||
<p>If you have a <strong>Terminal</strong> window, there are several
|
||||
possibilities:</p>
|
||||
<section id="ghidra-or-gdb-failed-to-launch-the-target" class="level4">
|
||||
<h4>Ghidra or GDB failed to launch the target:</h4>
|
||||
<p>Check that the original <code>termmines</code> 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.</p>
|
||||
<p>If this is the case, you should see an error message in the Terminal,
|
||||
e.g.: <code>termmines: no such file or directory</code>. Check that the
|
||||
original <code>termmines</code> exists and is executable. You may also
|
||||
need to adjust the <strong>Image</strong> option when configuring the
|
||||
launch.</p>
|
||||
</section>
|
||||
<section id="the-target-was-launched-but-immediately-terminated"
|
||||
class="level4">
|
||||
<h4>The target was launched, but immediately terminated:</h4>
|
||||
<p>Check that the specimen has a <code>main</code> symbol. NOTE: It is
|
||||
<p>If this is the case, you should see a message in the Terminal, e.g.:
|
||||
<code>[Inferior 1 (process 1234) exited normally]</code>. Check that the
|
||||
specimen has a <code>main</code> symbol. <strong>NOTE</strong>: It is
|
||||
not sufficient to place a <code>main</code> label in Ghidra. The
|
||||
original file must have a <code>main</code> symbol.</p>
|
||||
<p>Alternatively, in the menus try <strong>Debugger → Debug termmines →
|
||||
in GDB locally IN-VM</strong>, and select “Use starti.” This will break
|
||||
at the system entry point. If you have labeled <code>main</code> in
|
||||
Ghidra, then you can place a breakpoint there and continue — these
|
||||
features are covered later in the course.</p>
|
||||
<p>Alternatively, in the menus try <strong>Debugger → Configure and
|
||||
Launch termmines using → gdb</strong>, and select “starti” for
|
||||
<strong>Run Command</strong>. This will break at the system entry point.
|
||||
If you have labeled <code>main</code> in Ghidra, then you can place a
|
||||
breakpoint there and continue — these features are covered later in the
|
||||
course.</p>
|
||||
<p>Alternatively, try debugging the target in GDB from a separate
|
||||
terminal completely outside of Ghidra to see if things work as
|
||||
expected.</p>
|
||||
@ -356,37 +369,37 @@ class="level4">
|
||||
<h4>The target was launched, but has not stopped, yet</h4>
|
||||
<p>Try pressing the Interrupt <img src="images/interrupt.png"
|
||||
alt="interrupt button" /> button. If that doesn’t work or is
|
||||
unsatisfactory, try the remedies under the previous heading — for an
|
||||
immediately terminating target.</p>
|
||||
unsatisfactory, try the remedies under the previous heading.</p>
|
||||
</section>
|
||||
<section
|
||||
id="you-hit-an-uncommon-bug-where-the-memory-map-is-not-applied-properly"
|
||||
class="level4">
|
||||
<h4>You hit an uncommon bug where the memory map is not applied
|
||||
properly</h4>
|
||||
<p>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 <img
|
||||
src="images/stepinto.png" alt="step into" /> Step Into button in the
|
||||
main toolbar. If this is not desirable, then you can toggle
|
||||
<strong>Force Full View</strong> 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.</p>
|
||||
<p>This is the case if the <strong>Dynamic Listing</strong> is
|
||||
completely blank but the <strong>Regions</strong> window is replete. The
|
||||
<strong>Dynamic Listing</strong> just needs to be kicked a little. The
|
||||
easiest way is to step once, using the <img src="images/stepinto.png"
|
||||
alt="step into" /> <strong>Step Into</strong> button in the main
|
||||
toolbar. If this is not desirable, then you can toggle <strong>Force
|
||||
Full View</strong> back and forth. In the <strong>Regions</strong>
|
||||
window, use the drop-down menu to toggle it on, then toggle it off. The
|
||||
<strong>Dynamic Listing</strong> should now be populated. To go to the
|
||||
program counter, double-click the “pc = …” label in the top right.</p>
|
||||
</section>
|
||||
<section id="something-else-has-gone-wrong" class="level4">
|
||||
<h4>Something else has gone wrong</h4>
|
||||
<p>Try typing <code>info inferiors</code> and similar GDB diagnostic
|
||||
commands into the Interpreter.</p>
|
||||
commands into the <strong>Terminal</strong>.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section
|
||||
id="the-listings-are-in-sync-but-the-dynamic-listing-is-grey-00s"
|
||||
class="level3">
|
||||
<h3>The listings are in sync, but the Dynamic Listing is grey 00s</h3>
|
||||
<p>Check the Auto-Read drop-down near the top right of the Dynamic
|
||||
Listing. It should be set to <strong>Read Visible Memory, RO
|
||||
Once</strong>.</p>
|
||||
<p>Check the <strong>Auto-Read</strong> drop-down near the top right of
|
||||
the <strong>Dynamic Listing</strong>. It should be set to <strong>Read
|
||||
Visible Memory, RO Once</strong>.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="exercise-launch-termmines" class="level2">
|
||||
@ -395,63 +408,66 @@ Once</strong>.</p>
|
||||
<code>termmines</code> and/or start a new Ghidra Project. Starting from
|
||||
the beginning, import <code>termmines</code> 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.</p>
|
||||
populated <strong>Dynamic Listing</strong>, you have completed the
|
||||
exercise. Disconnect before proceeding to the next exercise.</p>
|
||||
</section>
|
||||
<section id="customized-launching" class="level2">
|
||||
<h2>Customized Launching</h2>
|
||||
<p>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 <strong>Debugger →
|
||||
Debug termmmines → in GDB locally IN-VM</strong> 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.</p>
|
||||
target without any parameters. In the <strong>Debugger</strong> menu, or
|
||||
the <strong>Launch</strong> button’s drop-down menu, use
|
||||
<strong>Configure and Launch termmmines → gdb</strong> to adjust your
|
||||
configuration. This is where you can specify the image path and
|
||||
command-line parameters of your target. Ghidra will remember this
|
||||
configuration the next time you launch using the drop-down button from
|
||||
the toolbar. Launchers with memorized configurations are presented as
|
||||
<strong>Re-launch termmines using…</strong> options. Using one of those
|
||||
entries will re-launch with the saved configuration rather than
|
||||
prompting.</p>
|
||||
</section>
|
||||
<section id="exercise-launch-with-command-line-help" class="level2">
|
||||
<h2>Exercise: Launch with Command-line Help</h2>
|
||||
<p>Launch the specimen so that it prints its usage. When successful, you
|
||||
will see the usage info in the Debugger’s Interpreter window.
|
||||
<strong>NOTE</strong>: The process will terminate after printing its
|
||||
usage, and as a result, the rest of the UI will be mostly empty.</p>
|
||||
will see the usage info in the Debugger’s <strong>Terminal</strong>
|
||||
window. <strong>NOTE</strong>: The process will terminate after printing
|
||||
its usage, and as a result, the rest of the UI will be mostly empty.</p>
|
||||
</section>
|
||||
<section id="attaching" class="level2">
|
||||
<h2>Attaching</h2>
|
||||
<p>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.</p>
|
||||
<p>Attaching is slightly more advanced, but can be useful if the target
|
||||
is part of a larger system, and it needs to be running <em>in situ</em>.
|
||||
For this section, we will just run <code>termmines</code> in a separate
|
||||
terminal and then attach to it from Ghidra. This used to be required,
|
||||
because the older Recorder-based system did not provide target I/O, but
|
||||
this limitation is overcome by the new <strong>Terminal</strong> window
|
||||
when using Trace RMI. Note this technique is only possible because the
|
||||
target waits for input.</p>
|
||||
<ol type="1">
|
||||
<li>Run <code>termmines</code> in a proper terminal with the desired
|
||||
command-line parameters.</li>
|
||||
<li>In the Ghidra Debugger, find the Targets window, and click the <img
|
||||
src="images/connect.png" alt="connect" /> Connect button.</li>
|
||||
<li>Select “gdb” from the drop-down box.</li>
|
||||
<li>This dialog should look familiar from the Customized Launching
|
||||
section. Just click the Connect button.</li>
|
||||
<li>In the Objects window (below the Targets window), expand the node
|
||||
labeled “Available.”</li>
|
||||
<li>Run <code>termmines</code> in a terminal outside of Ghidra with the
|
||||
desired command-line parameters.</li>
|
||||
<li>In the Ghidra Debugger, use the <strong>Launch</strong> button
|
||||
drop-down and select <strong>Configured and Launch termmines using… →
|
||||
raw gdb</strong>. The “raw” connector will give us a GDB session without
|
||||
a target.</li>
|
||||
<li>Ghidra needs to know the location of gdb and the architecture of the
|
||||
intended target. The defaults are correct for 64-bit x86 targets using
|
||||
the system’s copy of GDB. Probably, you can just click
|
||||
<strong>Launch</strong>.</li>
|
||||
<li>In the <strong>Model</strong> window (to the left), expand the
|
||||
<em>Available</em> node.</li>
|
||||
<li>In the filter box, type <code>termmines</code>.</li>
|
||||
<li>Right-click on the termmines process and select Attach. If this
|
||||
fails, select Available again, and click the
|
||||
<img alt="refresh" src="images/view-refresh.png" width="16px"> Refresh
|
||||
button.</li>
|
||||
<li>Note the PID, e.g. 1234, then in the <strong>Terminal</strong> type,
|
||||
e.g., <code>attach 1234</code>.</li>
|
||||
</ol>
|
||||
</section>
|
||||
<section id="exercise-attach" class="level2">
|
||||
<h2>Exercise: Attach</h2>
|
||||
<p>Try attaching on your own, if you have not already. Check your work
|
||||
by typing <code>bt</code> into the Interpreter. If you are in
|
||||
<code>read</code> you have completed this exercise. Disconnect before
|
||||
proceeding to the next module: <a href="A2-UITour.html">A Tour of the
|
||||
UI</a></p>
|
||||
by typing <code>bt</code> into the <strong>Terminal</strong>. If you are
|
||||
in <code>read</code> you have completed this exercise. Quit GDB from the
|
||||
<strong>Terminal</strong> before proceeding to the next module: <a
|
||||
href="A2-UITour.html">A Tour of the UI</a></p>
|
||||
</section>
|
||||
<section id="troubleshooting-1" class="level2">
|
||||
<h2>Troubleshooting</h2>
|
||||
@ -464,14 +480,15 @@ be traced by any other process and then executes a shell command. Using
|
||||
specimen in the permissive process, and thus you can attach to it as if
|
||||
<code>ptrace_scope=0</code>, but without reducing the security of the
|
||||
rest of the system. For example:</p>
|
||||
<div class="sourceCode" id="cb5"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ex">./anyptracer</span> <span class="st">'exec ./termmines'</span></span></code></pre></div>
|
||||
<div class="sourceCode" id="cb4"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">./anyptracer</span> <span class="st">'exec ./termmines'</span></span></code></pre></div>
|
||||
<p>Alternatively, if you have root access, you can rectify the issue
|
||||
using the relevant documentation available online.
|
||||
<strong>Beware!</strong> 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.</p>
|
||||
<strong>Beware!</strong> You should not set <code>ptrace_scope=0</code>
|
||||
globally, except on a system set aside for debugging, as this
|
||||
substantially reduces the security of that 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.</p>
|
||||
</section>
|
||||
</section>
|
||||
</body>
|
||||
|
@ -30,7 +30,10 @@ Run it:
|
||||
```
|
||||
|
||||
You should see a 9x9 grid and a cursor you can move with the arrow keys.
|
||||
Hit **Ctrl-C** to exit.
|
||||
|
||||
![Termmines running in a Terminal](images/GettingStarted_Termmines.png)
|
||||
|
||||
Hit **`CTRL`-`C`** to exit.
|
||||
Probe it for help.
|
||||
Most Linux programs accept a `-h` argument for help:
|
||||
|
||||
@ -55,23 +58,29 @@ There are many ways to do this, but for the sake of simplicity, import and launc
|
||||
|
||||
![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. In the Debugger tool, click the dropdown ▾ for the debug ![debug button](images/debugger.png) icon in the global tool bar, and select **Configure and Launch termmines using... → gdb**.
|
||||
|
||||
![Launch GDB Dialog](images/GettingStarted_LaunchGDBDialog.png)
|
||||
|
||||
1. Change the **Run Command** to "start" (not "starti").
|
||||
**NOTE**: In practice, this is rarely recommended, because most targets do not export their `main` function.
|
||||
1. Click the **Launch** button in the dialog.
|
||||
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.
|
||||
On Windows, we will use the Windows Debugger dbgeng.dll 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."
|
||||
Follow the same process as for Linux, except import `termmines.exe` and select **Configure and Launch termmines.exe using... → dbgeng**.
|
||||
|
||||
## 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."
|
||||
On macOS, we will use LLDB to debug the specimen.
|
||||
This is the debugger included with Xcode.
|
||||
Follow the same process as for Linux, except choose **lldb** in the last menu.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@ -97,59 +106,53 @@ 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
|
||||
### There is no **gdb** 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:
|
||||
You may have an older Debugger tool still configured for Recorder-based targets.
|
||||
We are transitioning to TraceRmi-based targets.
|
||||
Delete your Debugger tool and re-import the default one using the instructions above.
|
||||
If it is still not there, it's possible your installation is corrupt.
|
||||
Search for a file called `local-gdb.sh` in your installation.
|
||||
Unlike the previous system, Trace RMI will not probe your system for dependencies nor hide incompatible launchers.
|
||||
All installed launchers should be present in the menus, even though some may not work on your configuration.
|
||||
|
||||
```bash
|
||||
which gdb
|
||||
```
|
||||
### The launch hangs for several seconds and then I get prompted with a wall of text
|
||||
|
||||
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.
|
||||
Read the wall of text.
|
||||
The first line should tell you the exception that it encountered.
|
||||
Often this is a timeout.
|
||||
Press the **Keep** button and then find the Terminal, usually in the bottom right.
|
||||
If you do not see it there, check the **Window → Terminals** menu.
|
||||
Once you have found the Terminal, check its output *starting at the top* for diagnostic messages.
|
||||
If you have something like `bash: gdb: command not found`, it is because you are missing `gdb`, or you need to tell Ghidra where to find it.
|
||||
|
||||
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.
|
||||
If it is just missing, then install it and try again.
|
||||
If you need to tell Ghidra where it is, then in the launcher drop-down, select **Configure and Launch termmines using... → gdb**.
|
||||
DO NOT select **Re-launch termmines using gdb**, since this will not allow you to correct the configuration.
|
||||
|
||||
### 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.
|
||||
You should see an entry in the **Connection Manager** window, a populated **Model** window, and there should be a **Terminal** 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:
|
||||
If you have a **Terminal** window, there are several possibilities:
|
||||
|
||||
#### Ghidra or GDB failed to launch the target:
|
||||
|
||||
If this is the case, you should see an error message in the Terminal, e.g.: `termmines: no such file or directory`.
|
||||
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.
|
||||
You may also need to adjust the **Image** option when configuring the launch.
|
||||
|
||||
#### The target was launched, but immediately terminated:
|
||||
|
||||
If this is the case, you should see a message in the Terminal, e.g.: `[Inferior 1 (process 1234) exited normally]`.
|
||||
Check that the specimen has a `main` symbol.
|
||||
NOTE: It is not sufficient to place a `main` label in Ghidra.
|
||||
**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."
|
||||
Alternatively, in the menus try **Debugger → Configure and Launch termmines using → gdb**, and select "starti" for **Run Command**.
|
||||
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.
|
||||
|
||||
@ -158,75 +161,74 @@ Alternatively, try debugging the target in GDB from a separate terminal complete
|
||||
#### 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.
|
||||
If that doesn't work or is unsatisfactory, try the remedies under the previous heading.
|
||||
|
||||
#### 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.
|
||||
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.
|
||||
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.
|
||||
Try typing `info inferiors` and similar GDB diagnostic commands into the **Terminal**.
|
||||
|
||||
### 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.
|
||||
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.
|
||||
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.
|
||||
In the **Debugger** menu, or the **Launch** button's drop-down menu, use **Configure and Launch termmmines → gdb** to adjust your configuration.
|
||||
This is where you can specify the image path and command-line parameters of your target.
|
||||
Ghidra will remember this configuration the next time you launch using the drop-down button from the toolbar.
|
||||
Launchers with memorized configurations are presented as **Re-launch termmines using...** options.
|
||||
Using one of those entries will re-launch with the saved configuration rather than prompting.
|
||||
|
||||
## 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.
|
||||
When successful, you will see the usage info in the Debugger's **Terminal** 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.
|
||||
Attaching is slightly more advanced, but can be useful if the target is part of a larger system, and it needs to be running *in situ*.
|
||||
For this section, we will just run `termmines` in a separate terminal and then attach to it from Ghidra.
|
||||
This used to be required, because the older Recorder-based system did not provide target I/O, but this limitation is overcome by the new **Terminal** window
|
||||
when using Trace RMI.
|
||||
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. Run `termmines` in a terminal outside of Ghidra with the desired command-line parameters.
|
||||
1. In the Ghidra Debugger, use the **Launch** button drop-down and select **Configured and Launch termmines using... → raw gdb**.
|
||||
The "raw" connector will give us a GDB session without a target.
|
||||
1. Ghidra needs to know the location of gdb and the architecture of the intended target.
|
||||
The defaults are correct for 64-bit x86 targets using the system's copy of GDB.
|
||||
Probably, you can just click **Launch**.
|
||||
1. In the **Model** window (to the left), expand the *Available* node.
|
||||
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 <img alt="refresh" src="images/view-refresh.png" width="16px"> Refresh button.
|
||||
|
||||
1. Note the PID, e.g. 1234, then in the **Terminal** type, e.g., `attach 1234`.
|
||||
|
||||
## Exercise: Attach
|
||||
|
||||
Try attaching on your own, if you have not already.
|
||||
Check your work by typing `bt` into the Interpreter.
|
||||
Check your work by typing `bt` into the **Terminal**.
|
||||
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)
|
||||
Quit GDB from the **Terminal** before proceeding to the next module: [A Tour of the UI](A2-UITour.md)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@ -241,5 +243,5 @@ For example:
|
||||
```
|
||||
|
||||
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.
|
||||
**Beware!** You should not set `ptrace_scope=0` globally, except on a system set aside for debugging, as this substantially reduces the security of that 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.
|
||||
|
@ -18,6 +18,70 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
|
||||
/* CSS for syntax highlighting */
|
||||
pre > code.sourceCode { white-space: pre; position: relative; }
|
||||
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
|
||||
pre > code.sourceCode > span:empty { height: 1.2em; }
|
||||
.sourceCode { overflow: visible; }
|
||||
code.sourceCode > span { color: inherit; text-decoration: inherit; }
|
||||
div.sourceCode { margin: 1em 0; }
|
||||
pre.sourceCode { margin: 0; }
|
||||
@media screen {
|
||||
div.sourceCode { overflow: auto; }
|
||||
}
|
||||
@media print {
|
||||
pre > code.sourceCode { white-space: pre-wrap; }
|
||||
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
|
||||
}
|
||||
pre.numberSource code
|
||||
{ counter-reset: source-line 0; }
|
||||
pre.numberSource code > span
|
||||
{ position: relative; left: -4em; counter-increment: source-line; }
|
||||
pre.numberSource code > span > a:first-child::before
|
||||
{ content: counter(source-line);
|
||||
position: relative; left: -1em; text-align: right; vertical-align: baseline;
|
||||
border: none; display: inline-block;
|
||||
-webkit-touch-callout: none; -webkit-user-select: none;
|
||||
-khtml-user-select: none; -moz-user-select: none;
|
||||
-ms-user-select: none; user-select: none;
|
||||
padding: 0 4px; width: 4em;
|
||||
color: #aaaaaa;
|
||||
}
|
||||
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
|
||||
div.sourceCode
|
||||
{ }
|
||||
@media screen {
|
||||
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
|
||||
}
|
||||
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code span.at { color: #7d9029; } /* Attribute */
|
||||
code span.bn { color: #40a070; } /* BaseN */
|
||||
code span.bu { color: #008000; } /* BuiltIn */
|
||||
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code span.ch { color: #4070a0; } /* Char */
|
||||
code span.cn { color: #880000; } /* Constant */
|
||||
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code span.dt { color: #902000; } /* DataType */
|
||||
code span.dv { color: #40a070; } /* DecVal */
|
||||
code span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code span.ex { } /* Extension */
|
||||
code span.fl { color: #40a070; } /* Float */
|
||||
code span.fu { color: #06287e; } /* Function */
|
||||
code span.im { color: #008000; font-weight: bold; } /* Import */
|
||||
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code span.op { color: #666666; } /* Operator */
|
||||
code span.ot { color: #007020; } /* Other */
|
||||
code span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code span.ss { color: #bb6688; } /* SpecialString */
|
||||
code span.st { color: #4070a0; } /* String */
|
||||
code span.va { color: #19177c; } /* Variable */
|
||||
code span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
</style>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<!--[if lt IE 9]>
|
||||
@ -67,10 +131,9 @@ listings seem to move together, but their contents differ.</a></li>
|
||||
<li><a href="#there-is-no-step-button."
|
||||
id="toc-there-is-no-step-button.">There is no step button.</a></li>
|
||||
<li><a
|
||||
href="#i-can-step-but-i-dont-see-the-effects-in-the-interpreter-window."
|
||||
id="toc-i-can-step-but-i-dont-see-the-effects-in-the-interpreter-window.">I
|
||||
can step, but I don’t see the effects in the Interpreter
|
||||
window.</a></li>
|
||||
href="#i-can-step-but-i-dont-see-the-effects-in-the-terminal-window."
|
||||
id="toc-i-can-step-but-i-dont-see-the-effects-in-the-terminal-window.">I
|
||||
can step, but I don’t see the effects in the Terminal window.</a></li>
|
||||
<li><a href="#the-step-buttons-are-grayed-out."
|
||||
id="toc-the-step-buttons-are-grayed-out.">The Step buttons are grayed
|
||||
out.</a></li>
|
||||
@ -90,8 +153,8 @@ 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.</p>
|
||||
<p>If you would like your tool to look more or less like the one
|
||||
presented in the screenshots here, launch <code>termmines</code> from
|
||||
the Debugger using GDB.</p>
|
||||
presented in the screenshot here, launch <code>termmines</code> from the
|
||||
Debugger using GDB.</p>
|
||||
<section id="the-debugger-tool" class="level2">
|
||||
<h2>The Debugger Tool</h2>
|
||||
<p>Like the CodeBrowser tool, the Debugger tool is a preconfigured
|
||||
@ -112,15 +175,16 @@ CodeBrowser. Coincidentally, in the screenshot, the debugger-specific
|
||||
buttons start just above the Dynamic Listing in the global toolbar. They
|
||||
are:</p>
|
||||
<ul>
|
||||
<li><img src="images/debugger.png" alt="launch button" />
|
||||
<strong>Launch</strong>: This launches the current program (from the
|
||||
Static Listing) using a suitable back-end debugger. The drop-down menu
|
||||
provides a selection of previously-used launchers and a sub-menu of all
|
||||
available launchers. Clicking the button will use the most recent
|
||||
configuration, whether or not it succeeded.</li>
|
||||
<li><img src="images/process.png" alt="emulate button" />
|
||||
<strong>Emulate</strong>: To be covered in a later module. This will
|
||||
load the current program (from the Static Listing) into the
|
||||
emulator.</li>
|
||||
<li><img src="images/debugger.png" alt="debug button" />
|
||||
<strong>Debug</strong>: 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.</li>
|
||||
<li><img src="images/record.png" alt="mode button" /> <strong>Control
|
||||
Mode</strong>: This drop-down menu sets the mode of the controls and
|
||||
machine state edits. By default, all actions are directed to the
|
||||
@ -130,8 +194,8 @@ back-end debugger.</li>
|
||||
<code>continue</code> in GDB.</li>
|
||||
<li><img src="images/interrupt.png" alt="interrupt button" />
|
||||
<strong>Interrupt</strong>: Interrupt, suspend, pause, break, etc. This
|
||||
is equivalent to <strong>Ctrl-C</strong> or <code>interrupt</code> in
|
||||
GDB.</li>
|
||||
is equivalent to <strong><code>CTRL</code>-<code>C</code></strong> or
|
||||
<code>interrupt</code> in GDB.</li>
|
||||
<li><img src="images/kill.png" alt="kill button" />
|
||||
<strong>Kill</strong>: Kill, terminate, etc. This is equivalent to
|
||||
<code>kill</code> in GDB.</li>
|
||||
@ -144,111 +208,138 @@ Typically, this will also end the session. It is equivalent to
|
||||
alt="step over button" /> <strong>Step Over</strong>, <img
|
||||
src="images/stepout.png" alt="step out button" /> <strong>Step
|
||||
Out</strong>, <img src="images/steplast.png" alt="step last button" />
|
||||
<strong>Step Last</strong>: These buttons step in various ways. In
|
||||
<strong>Step [Extended]</strong>: These buttons step in various ways. In
|
||||
order, the equivalent commands in GDB are <code>stepi</code>,
|
||||
<code>nexti</code>, and <code>finish</code>. Step Last has no equivalent
|
||||
in GDB; it is meant to repeat the last custom/extended step.</li>
|
||||
<code>nexti</code>, and <code>finish</code>. Step [Extended] represents
|
||||
additional step commands supported by the back end. GDB provides
|
||||
<strong>Advance</strong> and <strong>Return</strong>.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="windows" class="level3">
|
||||
<h3>Windows</h3>
|
||||
<p>Starting at the top left and working clockwise, the windows are:</p>
|
||||
<ul>
|
||||
<li>The <strong>Debugger Targets</strong> window: This lists active
|
||||
sessions or connections. From here, you can establish new sessions or
|
||||
terminate existing sessions.</li>
|
||||
<li>The <strong>Debug Console</strong> window: (Not to be confused with
|
||||
the CodeBrowser’s Console window.) This lists problems, diagnostics,
|
||||
progress, and recommendations throughout the tool. Some problems are
|
||||
presented with remedial actions, which may expedite your workflow or aid
|
||||
in troubleshooting.</li>
|
||||
<li>The <strong>Connections</strong> window: This is stacked below the
|
||||
Debug Console. This lists active sessions or connections. From here, you
|
||||
can establish new sessions or terminate existing sessions.</li>
|
||||
<li>The <strong>Dynamic Listing</strong> 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 <code>x/10i $pc</code>.</li>
|
||||
<li>The <strong>Interpreter</strong> 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.</li>
|
||||
<li>The <strong>Breakpoints</strong> 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
|
||||
something like <code>x/10i $pc</code>. The tabs at the top list the
|
||||
active traces. Traces with a <img src="images/record.png"
|
||||
alt="record" /> icon represent live targets. The nearest equivalent to
|
||||
the tabs in GDB is <code>info inferiors</code>.</li>
|
||||
<li>The <strong>Breakpoints</strong> window: This is on the right. It
|
||||
lists and manages the breakpoints among all open program databases and
|
||||
running targets. The nearest equivalent in GDB is
|
||||
<code>info break</code>.</li>
|
||||
<li>The <strong>Registers</strong> 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
|
||||
<code>info registers</code></li>
|
||||
<code>info registers</code>.</li>
|
||||
<li>The <strong>Memory</strong> window: This is stacked below the
|
||||
Breakpoints window. It displays the raw bytes of memory from the current
|
||||
trace or target. It supports many of the same operations as the
|
||||
CodeBrowser’s Bytes window, including patching.</li>
|
||||
<li>The <strong>Decompiler</strong> window: While not a dynamic analysis
|
||||
window, it bears mentioning how this operates with the Debugger. It is
|
||||
stacked below the Breakpoints window, more or less in the same place as
|
||||
in the CodeBrowser. The Dynamic listing strives to synchronize Ghidra’s
|
||||
static analysis windows with the dynamic target. So long as the correct
|
||||
program database is imported and mapped at the program counter, this
|
||||
window should display decompilation of the function containing it.</li>
|
||||
<li>The <strong>Modules</strong> 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
|
||||
<code>maintenance info sections</code>. Note that this differs from the
|
||||
Regions window.</li>
|
||||
<li>The <strong>Threads</strong> window: This lists the threads in the
|
||||
current target. The tabs at the top list the active targets. The nearest
|
||||
equivalents in GDB are <code>info threads</code> and
|
||||
<code>info inferiors</code>.</li>
|
||||
<li>The <strong>Time</strong> window: This is stacked below the Threads
|
||||
<li>The <strong>Terminal</strong> window: This is on the bottom right.
|
||||
This is a terminal emulator providing a command-line interface to the
|
||||
back-end debugger and/or target I/O. 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. In some
|
||||
configurations, the target may have its own Terminal, separate from the
|
||||
back-end debugger’s.</li>
|
||||
<li>The <strong>Threads</strong> window: This is stacked below the
|
||||
Terminal window. It lists the threads in the current target. The nearest
|
||||
equivalent in GDB is <code>info threads</code>.</li>
|
||||
<li>The <strong>Time</strong> window: This is stacked below the Terminal
|
||||
window. This lists the events and snapshots taken of the current
|
||||
target.</li>
|
||||
<li>The <strong>Stack</strong> window: This lists the stack frames for
|
||||
the current thread. The equivalent in GDB is
|
||||
<li>The <strong>Static Mappings</strong> window: This is stacked below
|
||||
the Terminal window. It lists mappings from the current trace (dynamic
|
||||
address ranges) to program databases (static address ranges). Generally,
|
||||
this list is populated automatically, but may still be useful for
|
||||
diagnostics or manual mapping.</li>
|
||||
<li>The <strong>Stack</strong> window: This is on the bottom left. It
|
||||
lists the stack frames for the current thread. The equivalent in GDB is
|
||||
<code>backtrace</code>.</li>
|
||||
<li>The <strong>Watches</strong> window: This is stacked below the Stack
|
||||
window — pun not intended. It manages current watches. These are
|
||||
<em>not</em> 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
|
||||
values you wish to display. To manage watchpoints, use the Breakpoints
|
||||
window or the Terminal. The nearest equivalent in GDB is
|
||||
<code>display</code>.</li>
|
||||
<li>The <strong>Regions</strong> 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 <code>info proc mappings</code>.</li>
|
||||
<li>The <strong>Debug Console</strong> 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.</li>
|
||||
<li>The <strong>Objects</strong> 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.</li>
|
||||
<li>The <strong>Regions</strong> window: This is stacked below the Stack
|
||||
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 <code>info proc mappings</code>.</li>
|
||||
<li>The <strong>Model</strong> window: The back-end debugger populates
|
||||
an object model in the trace database. It is from this model that many
|
||||
other windows derive their contents: Threads, Modules, Regions, etc.
|
||||
This window presents that model and provides access to generic actions
|
||||
on the contained objects. It is generally more capable, though less
|
||||
integrated, than the other parts of the GUI, but not quite as capable as
|
||||
the Terminal. For some advanced use cases, where Ghidra does not yet
|
||||
provide built-in actions, it is essential.</li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
<section id="controlling-the-target" class="level2">
|
||||
<h2>Controlling the Target</h2>
|
||||
<p>The control buttons are all located on the global toolbar. Start by
|
||||
pressing the <img src="images/stepinto.png" alt="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.</p>
|
||||
pressing the <img src="images/stepinto.png" alt="step into" />
|
||||
<strong>Step Into</strong> 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.</p>
|
||||
<p>When you have clicked <img src="images/stepinto.png"
|
||||
alt="step into" /> Step Into a sufficient number of times, you should
|
||||
end up in a subroutine. You can click <img src="images/stepout.png"
|
||||
alt="step out" /> 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 <img src="images/stepover.png"
|
||||
alt="step over" /> Step Over until you reach another <code>CALL</code>
|
||||
instruction. Notice that when you click <img src="images/stepover.png"
|
||||
alt="step over" /> 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 <code>CALL</code>
|
||||
alt="step into" /> <strong>Step Into</strong> a sufficient number of
|
||||
times, you should end up in a subroutine. You can click <img
|
||||
src="images/stepout.png" alt="step out" /> <strong>Step Out</strong> 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
|
||||
<img src="images/stepover.png" alt="step over" /> <strong>Step
|
||||
Over</strong> until you reach another <code>CALL</code> instruction.
|
||||
Notice that when you click <img src="images/stepover.png"
|
||||
alt="step over" /> <strong>Step Over</strong> again, it will not descend
|
||||
into the subroutine. Instead, the target is allowed to execute the
|
||||
entire subroutine before stopping again — after the <code>CALL</code>
|
||||
instruction.</p>
|
||||
<p>If you prefer, you may use the GDB commands from the Interpreter
|
||||
instead of the buttons. Try <code>si</code> and/or <code>ni</code>. You
|
||||
can also pass arguments which is not possible with the buttons,
|
||||
<p>If you prefer, you may use the GDB commands from the Terminal instead
|
||||
of the buttons. Try <code>si</code> and/or <code>ni</code>. You can also
|
||||
pass arguments which is not possible with the buttons,
|
||||
e.g. <code>si 10</code> to step 10 instructions in one command.</p>
|
||||
<p>If you need to terminate the target you should use the <img
|
||||
src="images/disconnect.png" alt="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 <em>not</em> covered in this course.</p>
|
||||
src="images/disconnect.png" alt="disconnect" />
|
||||
<strong>Disconnect</strong> button rather than the <strong>Kill</strong>
|
||||
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 Connections window to clean such connections
|
||||
up, or just type <code>quit</code> into the session’s Terminal. The
|
||||
re-use of connections and/or the use of multiple concurrent connections
|
||||
is <em>not</em> covered in this course.</p>
|
||||
</section>
|
||||
<section id="troubleshooting" class="level2">
|
||||
<h2>Troubleshooting</h2>
|
||||
@ -264,14 +355,23 @@ Listing is its local drop-down menu. Click it and check that
|
||||
Listing to see what module you are in. Also check the Debug Console
|
||||
window. If you are in a system library, e.g., <code>ld-linux</code>,
|
||||
then this is the expected behavior. You may optionally import it, as
|
||||
suggested by the Debug Console, but this is covered later.</p>
|
||||
suggested by the Debug Console, but this is covered later. You may also
|
||||
try typing into the Terminal, <em>one command at a time</em>, checking
|
||||
for errors after each:</p>
|
||||
<div class="sourceCode" id="cb1"><pre
|
||||
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">break</span> main</span>
|
||||
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>continue</span></code></pre></div>
|
||||
<p>That should get you from the system entry into the target’s
|
||||
<code>main</code> routine, assuming it has one. Next time you launch,
|
||||
check the configuration and change <strong>Run Command</strong> to
|
||||
“start”, not “starti”.</p>
|
||||
<p>If you are not in a system library, then check the Modules window to
|
||||
see if <code>termmines</code> 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
|
||||
<code>termmines</code> 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.</p>
|
||||
the module and select <strong>Map to termmines</strong>. Confirm the
|
||||
dialog. If <code>termmines</code> 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.</p>
|
||||
</section>
|
||||
<section
|
||||
id="the-listings-seem-to-move-together-but-their-contents-differ."
|
||||
@ -284,29 +384,32 @@ 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.</p>
|
||||
old program database, consider using the Version Tracker to port your
|
||||
knowledge to the new database.</p>
|
||||
</section>
|
||||
<section id="there-is-no-step-button." class="level3">
|
||||
<h3>There is no step button.</h3>
|
||||
<p>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.”</p>
|
||||
<p>This can happen if the Control Mode is set to <strong>Control
|
||||
Trace</strong>. Perhaps you played with the Time window? The Control
|
||||
Mode drop-down is leftmost in the “Control” group, immediately right of
|
||||
the <strong>Launch</strong> and <strong>Emulate</strong> buttons. Its
|
||||
icon differs for each mode. Change it back to <strong>Control
|
||||
Target</strong>.</p>
|
||||
</section>
|
||||
<section
|
||||
id="i-can-step-but-i-dont-see-the-effects-in-the-interpreter-window."
|
||||
id="i-can-step-but-i-dont-see-the-effects-in-the-terminal-window."
|
||||
class="level3">
|
||||
<h3>I can step, but I don’t see the effects in the Interpreter
|
||||
window.</h3>
|
||||
<p>This can happen if the Control Mode is set to the Emulator. Change
|
||||
the Control Mode back to “Control Target.”</p>
|
||||
<h3>I can step, but I don’t see the effects in the Terminal window.</h3>
|
||||
<p>This can happen if the Control Mode is set to <strong>Control
|
||||
Emulator</strong>. See the above heading about Control Mode. Change it
|
||||
back to <strong>Control Target</strong>.</p>
|
||||
</section>
|
||||
<section id="the-step-buttons-are-grayed-out." class="level3">
|
||||
<h3>The Step buttons are grayed out.</h3>
|
||||
<p>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 <a href="A1-GettingStarted.html">Getting
|
||||
Started</a></p>
|
||||
Check the Threads window or the Model window. If it is empty, re-launch,
|
||||
and perhaps look at the Troubleshooting section in <a
|
||||
href="A1-GettingStarted.html">Getting Started</a></p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="exercise-step-around" class="level2">
|
||||
@ -318,7 +421,8 @@ you have entered that subroutine. <strong>TIP</strong>: 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.</p>
|
||||
and then use the <strong>Step</strong> buttons to drive the target into
|
||||
it.</p>
|
||||
</section>
|
||||
</section>
|
||||
</body>
|
||||
|
@ -8,7 +8,7 @@ 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,
|
||||
If you would like your tool to look more or less like the one presented in the screenshot here,
|
||||
launch `termmines` from the Debugger using GDB.
|
||||
|
||||
## The Debugger Tool
|
||||
@ -25,13 +25,13 @@ 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:
|
||||
|
||||
* ![launch button](images/debugger.png) **Launch**:
|
||||
This launches the current program (from the Static Listing) using a suitable back-end debugger.
|
||||
The drop-down menu provides a selection of previously-used launchers and a sub-menu of all available launchers.
|
||||
Clicking the button will use the most recent configuration, whether or not it succeeded.
|
||||
* ![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.
|
||||
@ -40,7 +40,7 @@ They are:
|
||||
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.
|
||||
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.
|
||||
@ -48,16 +48,21 @@ They are:
|
||||
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**:
|
||||
* ![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 [Extended]**:
|
||||
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.
|
||||
Step [Extended] represents additional step commands supported by the back end. GDB provides **Advance** and **Return**.
|
||||
|
||||
### Windows
|
||||
|
||||
Starting at the top left and working clockwise, the windows are:
|
||||
|
||||
* The **Debugger Targets** window:
|
||||
* The **Debug Console** window:
|
||||
(Not to be confused with the CodeBrowser's Console window.)
|
||||
This lists problems, diagnostics, progress, and recommendations throughout the tool.
|
||||
Some problems are presented with remedial actions, which may expedite your workflow or aid in troubleshooting.
|
||||
* The **Connections** window:
|
||||
This is stacked below the Debug Console.
|
||||
This lists active sessions or connections.
|
||||
From here, you can establish new sessions or terminate existing sessions.
|
||||
* The **Dynamic Listing** window:
|
||||
@ -65,77 +70,94 @@ Starting at the top left and working clockwise, the windows are:
|
||||
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 tabs at the top list the active traces.
|
||||
Traces with a ![record](images/record.png) icon represent live targets.
|
||||
The nearest equivalent to the tabs in GDB is `info inferiors`.
|
||||
* The **Breakpoints** window:
|
||||
This is stacked below the Interpreter.
|
||||
It lists and manages the breakpoints among all open images and running targets.
|
||||
This is on the right.
|
||||
It lists and manages the breakpoints among all open program databases 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 nearest equivalent in GDB is `info registers`.
|
||||
* The **Memory** window:
|
||||
This is stacked below the Breakpoints window.
|
||||
It displays the raw bytes of memory from the current trace or target.
|
||||
It supports many of the same operations as the CodeBrowser's Bytes window, including patching.
|
||||
* The **Decompiler** window:
|
||||
While not a dynamic analysis window, it bears mentioning how this operates with the Debugger.
|
||||
It is stacked below the Breakpoints window, more or less in the same place as in the CodeBrowser.
|
||||
The Dynamic listing strives to synchronize Ghidra's static analysis windows with the dynamic target.
|
||||
So long as the correct program database is imported and mapped at the program counter, this window should display decompilation of the function containing it.
|
||||
* 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 **Terminal** window:
|
||||
This is on the bottom right.
|
||||
This is a terminal emulator providing a command-line interface to the back-end debugger and/or target I/O.
|
||||
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.
|
||||
In some configurations, the target may have its own Terminal, separate from the back-end debugger's.
|
||||
* 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`.
|
||||
This is stacked below the Terminal window.
|
||||
It lists the threads in the current target.
|
||||
The nearest equivalent in GDB is `info threads`.
|
||||
* The **Time** window:
|
||||
This is stacked below the Threads window.
|
||||
This is stacked below the Terminal window.
|
||||
This lists the events and snapshots taken of the current target.
|
||||
* The **Static Mappings** window:
|
||||
This is stacked below the Terminal window.
|
||||
It lists mappings from the current trace (dynamic address ranges) to program databases (static address ranges).
|
||||
Generally, this list is populated automatically, but may still be useful for diagnostics or manual mapping.
|
||||
* The **Stack** window:
|
||||
This lists the stack frames for the current thread.
|
||||
This is on the bottom left.
|
||||
It 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.
|
||||
These are *not* watchpoints, but rather expressions or variables whose values you wish to display.
|
||||
To manage watchpoints, use the Breakpoints window or the Terminal.
|
||||
The nearest equivalent in GDB is `display`.
|
||||
* The **Regions** window:
|
||||
This is stacked below the Watches window.
|
||||
This is stacked below the Stack 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.
|
||||
* The **Model** window:
|
||||
The back-end debugger populates an object model in the trace database.
|
||||
It is from this model that many other windows derive their contents: Threads, Modules, Regions, etc.
|
||||
This window presents that model and provides access to generic actions on the contained objects.
|
||||
It is generally more capable, though less integrated, than the other parts of the GUI, but not quite as capable as the Terminal.
|
||||
For some advanced use cases, where Ghidra does not yet provide built-in actions, it is essential.
|
||||
|
||||
## 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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
If you prefer, you may use the GDB commands from the Terminal 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.
|
||||
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.
|
||||
Use the Connections window to clean such connections up, or just type `quit` into the session's Terminal.
|
||||
The re-use of connections and/or the use of multiple concurrent connections is *not* covered in this course.
|
||||
|
||||
## Troubleshooting
|
||||
@ -151,10 +173,19 @@ If that does not work, check the top-left label of the Dynamic Listing to see wh
|
||||
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.
|
||||
You may also try typing into the Terminal, *one command at a time*, checking for errors after each:
|
||||
|
||||
```gdb
|
||||
break main
|
||||
continue
|
||||
```
|
||||
|
||||
That should get you from the system entry into the target's `main` routine, assuming it has one.
|
||||
Next time you launch, check the configuration and change **Run Command** to "start", not "starti".
|
||||
|
||||
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."
|
||||
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.
|
||||
@ -165,23 +196,26 @@ There is probably a discrepancy between the version you imported and the version
|
||||
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.
|
||||
If this happens to you in practice, and you have substantial investment in the old program database, consider using the Version Tracker to port your knowledge to the new database.
|
||||
|
||||
### There is no step button.
|
||||
|
||||
This can happen if the Control Mode is set to the Trace.
|
||||
This can happen if the Control Mode is set to **Control Trace**.
|
||||
Perhaps you played with the Time window?
|
||||
Change the Control Mode back to "Control Target."
|
||||
The Control Mode drop-down is leftmost in the "Control" group, immediately right of the **Launch** and **Emulate** buttons.
|
||||
Its icon differs for each mode.
|
||||
Change it back to **Control Target**.
|
||||
|
||||
### I can step, but I don't see the effects in the Interpreter window.
|
||||
### I can step, but I don't see the effects in the Terminal window.
|
||||
|
||||
This can happen if the Control Mode is set to the Emulator.
|
||||
Change the Control Mode back to "Control Target."
|
||||
This can happen if the Control Mode is set to **Control Emulator**.
|
||||
See the above heading about Control Mode.
|
||||
Change it 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.
|
||||
Check the Threads window or the Model window.
|
||||
If it is empty, re-launch, and perhaps look at the Troubleshooting section in [Getting Started](A1-GettingStarted.md)
|
||||
|
||||
## Exercise: Step Around
|
||||
@ -190,4 +224,4 @@ If you were not already following along with an instructor, then try some of the
|
||||
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.
|
||||
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.
|
||||
|
@ -158,28 +158,24 @@ several ways to set a new breakpoint:</p>
|
||||
<li>From any static or dynamic listing window, including Disassembly,
|
||||
Memory/Hex, and the Decompiler, right-click and select <img
|
||||
src="images/breakpoint-enable.png" alt="set breakpoint" /> Set
|
||||
Breakpoint, press <strong>K</strong> on the keyboard, or double-click
|
||||
the margin.</li>
|
||||
<li>From the Objects window click the <img
|
||||
src="images/breakpoint-enable.png" alt="add breakpoint" /> Add
|
||||
Breakpoint button or press <strong>F3</strong> on the keyboard.</li>
|
||||
<li>From the Interpreter window, use the GDB command, e.g.,
|
||||
Breakpoint, press <strong><code>K</code></strong> on the keyboard, or
|
||||
double-click the margin.</li>
|
||||
<li>From the Terminal window, use the GDB command, e.g.,
|
||||
<code>break main</code>.</li>
|
||||
</ol>
|
||||
<p>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 <img
|
||||
src="images/breakpoint-enable.png" alt="enabled breakpoint" /> and
|
||||
disabled <img src="images/breakpoint-disable.png"
|
||||
alt="disabled breakpoint" /> breakpoints. If the target is terminated
|
||||
(or not launched yet), you may also see ineffective <img
|
||||
src="images/breakpoint-enable-ineff.png" alt="ineffective breakpoint" />
|
||||
breakpoints.</p>
|
||||
breakpoint at any address. The advantage of using the Terminal 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 program database. There is some iconography to communicate the
|
||||
various states of a breakpoint. When all is well and normal, you should
|
||||
only see enabled <img src="images/breakpoint-enable.png"
|
||||
alt="enabled breakpoint" /> and disabled <img
|
||||
src="images/breakpoint-disable.png" alt="disabled breakpoint" />
|
||||
breakpoints. If the target is terminated (or not launched yet), you may
|
||||
also see ineffective <img src="images/breakpoint-enable-ineff.png"
|
||||
alt="ineffective breakpoint" /> breakpoints.</p>
|
||||
</section>
|
||||
<section id="examining-minesweeper-board-setup" class="level2">
|
||||
<h2>Examining Minesweeper Board Setup</h2>
|
||||
@ -195,7 +191,7 @@ breakpoint on <code>rand</code> will help us find the algorithm that
|
||||
places the mines.</p>
|
||||
<section id="set-the-breakpoints" class="level3">
|
||||
<h3>Set the Breakpoints</h3>
|
||||
<p>In the Interpreter, type the GDB commands to set breakpoints on
|
||||
<p>In the Terminal, type the GDB commands to set breakpoints on
|
||||
<code>srand</code> and <code>rand</code>:</p>
|
||||
<div class="sourceCode" id="cb1"><pre
|
||||
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">break</span> srand</span>
|
||||
@ -208,15 +204,15 @@ alt="Populated breakpoints window" />
|
||||
</figure>
|
||||
<p>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.</p>
|
||||
top panel. This lists the <em>logical</em> breakpoints, preferring
|
||||
static addresses.</p>
|
||||
<ul>
|
||||
<li>The left-most column <strong>Enabled</strong> indicates the
|
||||
<li>The left-most column <strong>State</strong> indicates the
|
||||
breakpoint’s state. Here, we see the inconsistent <img
|
||||
src="images/breakpoint-overlay-inconsistent.png" alt="inconsistent" />
|
||||
overlay, because Ghidra cannot save the breakpoint without a module
|
||||
image. That is because <code>srand</code> and <code>rand</code> are in a
|
||||
different module, and we have not yet imported it into Ghidra.</li>
|
||||
overlay, because Ghidra cannot save the breakpoint without a program
|
||||
database. That is because <code>srand</code> and <code>rand</code> are
|
||||
in a different module, and we have not yet imported it into Ghidra.</li>
|
||||
<li>The next column <strong>Name</strong> 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
|
||||
@ -227,44 +223,43 @@ breakpoints were specified by symbol. Typically, this is the
|
||||
<em>static</em> address of the breakpoint; however, if the module image
|
||||
is not imported, yet, this will be the <em>dynamic</em> address, subject
|
||||
to relocation or ASLR.</li>
|
||||
<li>The next column <strong>Image</strong> gives the name of the
|
||||
imported image containing the breakpoint. Again, because the module has
|
||||
not been imported yet, this column is blank.</li>
|
||||
<li>The next column <strong>Image</strong> gives the name of the program
|
||||
database containing the breakpoint. Again, because the module has not
|
||||
been imported yet, this column is blank.</li>
|
||||
<li>The next column <strong>Length</strong> gives the length of the
|
||||
breakpoint. In GDB, this generally applies to watchpoints only.</li>
|
||||
<li>The next column <strong>Kinds</strong> 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 (<code>INT3</code> on x86)
|
||||
that traps execution. There are also hardware execution breakpoints
|
||||
target’s memory with a special instruction that traps execution —
|
||||
<code>INT3</code> on x86. There are also hardware execution breakpoints
|
||||
indicated by “HW_EXECUTE,” and access breakpoints indicated by “HW_READ”
|
||||
and/or “HW_WRITE”. <strong>NOTE</strong>: 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.</li>
|
||||
and/or “HW_WRITE”. <strong>NOTE</strong>: GDB would call access
|
||||
breakpoints <em>watchpoints</em>. 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.</li>
|
||||
<li>The next column <strong>Locations</strong> counts the number of
|
||||
locations for the breakpoint. For a single-target session, this should
|
||||
always be 1.</li>
|
||||
locations for the breakpoint. For a single-target session, this is most
|
||||
likely 1.</li>
|
||||
<li>The final column <strong>Sleigh</strong> is only applicable to the
|
||||
emulator. It indicates that the breakpoint’s behavior has been
|
||||
customized with Sleigh code. This is covered in <a
|
||||
href="B2-Emulation.html">Emulation</a>.</li>
|
||||
</ul>
|
||||
<p>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 <em>dynamic</em> addresses.</p>
|
||||
locations, as reported by the back-end debugger(s). The State, Address,
|
||||
and Sleigh columns are the same as the top, but for the individual
|
||||
<em>dynamic</em> addresses.</p>
|
||||
<ul>
|
||||
<li>The <strong>Name</strong> column is the name as designated by the
|
||||
back-end.</li>
|
||||
<li>The <strong>Trace</strong> column indicates which target contains
|
||||
the location. The text here should match one of the tabs from the
|
||||
Threads panel.</li>
|
||||
Dynamic Listing panel.</li>
|
||||
<li>The <strong>Comment</strong> column is a user-defined comment. Its
|
||||
default value is the specification that generated it, e.g.,
|
||||
<code>srand</code>.</li>
|
||||
<li>The <strong>Threads</strong> column indicates if the breakpoint is
|
||||
scoped to a limited set of threads. Its use is atypical.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="toggling-the-breakpoints" class="level3">
|
||||
@ -274,25 +269,35 @@ good time to demonstrate the feature. There are several ways to toggle a
|
||||
breakpoint:</p>
|
||||
<ol type="1">
|
||||
<li>In any listing, as in setting a breakpoint, right-click and select a
|
||||
toggle action, press <strong>K</strong> on the keyboard, or double-click
|
||||
its icon in the margin.</li>
|
||||
<li>From the Objects window, expand the Breakpoints node, right-click a
|
||||
breakpoint and select Toggle or press <strong>T</strong> on the
|
||||
keyboard.</li>
|
||||
toggle action, press <strong><code>K</code></strong> on the keyboard, or
|
||||
double-click its icon in the margin.</li>
|
||||
<li>From the Model window, expand the <em>Breakpoints</em> node and
|
||||
double-click a breakpoint, or select one with the keyboard and press
|
||||
<strong><code>ENTER</code></strong>.</li>
|
||||
<li>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.</li>
|
||||
<li>From the Interpreter window, use the GDB commands, e.g.,
|
||||
<li>From the Terminal window, use the GDB commands, e.g.,
|
||||
<code>disable 2</code>.</li>
|
||||
</ol>
|
||||
<p>Practice toggling them. Notice that no matter how you toggle the
|
||||
breakpoints, the display updates. You might also type
|
||||
<code>info break</code> into the Interpreter to confirm the effect of
|
||||
<code>info break</code> into the Terminal to confirm the effect of
|
||||
toggling breakpoints in the GUI. When you are finished, ensure both
|
||||
breakpoints are enabled.</p>
|
||||
<p><strong>NOTE</strong>: In all parts of the GUI, except the Model
|
||||
window, Ghidra prefers to toggle breakpoint locations. Without getting
|
||||
into details, this is the second level down of breakpoints shown in the
|
||||
Model tree. If you set a breakpoint, and GDB calls this breakpoint 2,
|
||||
then you toggle it in the listing, Ghidra will toggle, e.g., breakpoint
|
||||
<em>location</em> 2.1, not the breakpoint <em>specification</em> 2. If
|
||||
you disable breakpoint 2 using the Model or Terminal window, it may
|
||||
become impossible to toggle the breakpoint in the Listing or Breakpoints
|
||||
windows. If you find your session in this condition, just re-enable the
|
||||
troublesome breakpoints in the Model or Terminal window.</p>
|
||||
</section>
|
||||
<section id="importing-libc" class="level3">
|
||||
<h3>Importing <code>libc</code></h3>
|
||||
@ -323,13 +328,32 @@ CodeBrowser.</p></li>
|
||||
<p>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
|
||||
<code>libc</code>.</p>
|
||||
<code>libc</code>. <strong>NOTE</strong>: Ghidra has not automatically
|
||||
disassembled the dynamic listing, because the program counter has not
|
||||
actually landed there, yet.</p>
|
||||
<figure>
|
||||
<img src="images/Breakpoints_SyncedAfterImportLibC.png"
|
||||
alt="The debugger tool with breakpoints synchronized after importing libc" />
|
||||
<figcaption aria-hidden="true">The debugger tool with breakpoints
|
||||
synchronized after importing libc</figcaption>
|
||||
</figure>
|
||||
<section id="troubleshooting" class="level4">
|
||||
<h4>Troubleshooting</h4>
|
||||
<p>If it seems nothing has changed, except now you have a second program
|
||||
database open, then the new module may not be successfully mapped.</p>
|
||||
<ol type="1">
|
||||
<li>Re-check the Debug Console window and verify the note has been
|
||||
removed.</li>
|
||||
<li>If not, it might be because the module is symlinked in the file
|
||||
system, so the name of the module and the name of the program database
|
||||
do not match.</li>
|
||||
<li>Ensure that <code>libc</code> is the current program (tab) in the
|
||||
Static Listing.</li>
|
||||
<li>In the Modules window, right-click on <code>libc</code>, and select
|
||||
<strong>Map Module to libc</strong>. (Names and titles will likely
|
||||
differ.)</li>
|
||||
</ol>
|
||||
</section>
|
||||
</section>
|
||||
<section id="capturing-the-random-seed" class="level3">
|
||||
<h3>Capturing the Random Seed</h3>
|
||||
@ -388,18 +412,27 @@ course.</p>
|
||||
</section>
|
||||
<section id="exercise-diagram-the-mines" class="level3">
|
||||
<h3>Exercise: Diagram the Mines</h3>
|
||||
<p>You goal is to capture the location of all the mines. So that you can
|
||||
check your work later, you should run <code>termmines</code> in a
|
||||
terminal and attach to it from Ghidra. You will probably want to disable
|
||||
the breakpoints on <code>rand</code> and <code>srand</code> 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 <img
|
||||
src="images/resume.png" alt="resume" /> Resume. Check you work by
|
||||
winning the game. Alternatively, you can intentionally lose to have the
|
||||
game reveal the mines.</p>
|
||||
<p>You goal is to capture the location of all the mines. You will
|
||||
probably want to disable the breakpoints on <code>rand</code> and
|
||||
<code>srand</code> 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 <img src="images/resume.png" alt="resume" /> Resume. Check
|
||||
you work by winning the game. Alternatively, you can intentionally lose
|
||||
to have the game reveal the mines.</p>
|
||||
<section id="troubleshooting-1" class="level4">
|
||||
<h4>Troubleshooting</h4>
|
||||
<p>You may find that running both GDB and <code>termmines</code> in the
|
||||
same Terminal makes viewing the game board difficult. The next time you
|
||||
launch, be sure to use the <strong>Configure and Launch</strong>
|
||||
sub-menu, then enable the <strong>Inferior TTY</strong> option. This
|
||||
should start two Terminals, one with GDB and a second dedicated to
|
||||
<code>termmines</code>. The game board will no longer be corrupted by
|
||||
GDB’s prompts and diagnostics. You will probably want to undock the
|
||||
<code>termmines</code> Terminal and resize it to fit the board.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="optional-exercise-replicate-the-boards-forward-engineering"
|
||||
class="level3">
|
||||
@ -412,77 +445,11 @@ the placement algorithm, we can perfectly replicate the sequence of game
|
||||
boards for any <code>termmines</code> session.</p>
|
||||
<p>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 <code>termmines</code> (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.</p>
|
||||
<p><strong>NOTE</strong>: We will need a more advanced attaching
|
||||
technique to check your work, because you will need both to break on
|
||||
<code>srand</code> (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 <code>gdbserver</code> or using
|
||||
<code>set inferior-tty</code>. 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 <code>termmines</code>. We can then
|
||||
run the stub in a terminal outside of Ghidra, attach to that stub, and
|
||||
then allow it to proceed into <code>termmines</code>. In this way, we
|
||||
can attach to the process in the terminal before it reaches
|
||||
<code>srand</code>. The stub is fairly easy to write in Bash and should
|
||||
be similar in other shells.</p>
|
||||
<ol type="1">
|
||||
<li><p>In a terminal running Bash (see note if you’re using
|
||||
<code>anyptracer</code>):</p>
|
||||
<div class="sourceCode" id="cb3"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">(</span><span class="bu">echo</span> <span class="va">$BASHPID</span><span class="kw">;</span> <span class="bu">kill</span> <span class="at">-SIGSTOP</span> <span class="va">$BASHPID</span><span class="kw">;</span> <span class="bu">exec</span> ./termmines<span class="kw">)</span></span></code></pre></div>
|
||||
<p>The parentheses will start <code>bash</code> 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.</p>
|
||||
<p><strong>NOTE</strong>: If you need to use the <code>anyptracer</code>
|
||||
stub, then the invocation is more complicated:</p>
|
||||
<div class="sourceCode" id="cb4"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">./anyptracer</span> <span class="st">'exec bash -c "echo $BASHPID; kill -SIGSTOP $BASHPID; exec ./termmines"'</span></span></code></pre></div>
|
||||
<p>In principle, it works the same except wrapped in the
|
||||
<code>anyptracer</code> stub. The parentheses are no longer needed, nor
|
||||
allowed, since <code>anyptracer</code> is already a subprocess of your
|
||||
shell. If you include parentheses, you will get a second sub-subprocess
|
||||
to which you cannot attach.</p></li>
|
||||
<li><p>In Ghidra, follow the usual steps to attach, but use the PID
|
||||
printed in your terminal. <strong>NOTE</strong>: The process is still
|
||||
technically running <code>bash</code> when you attach to it.</p></li>
|
||||
<li><p>In the Interpreter panel:</p>
|
||||
<div class="sourceCode" id="cb5"><pre
|
||||
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">break</span> main</span></code></pre></div>
|
||||
<p><strong>NOTE</strong>: At this point <code>main</code> technically
|
||||
refers to the symbol in <code>bash</code>, but GDB will adjust its
|
||||
location once the target loads <code>termmines</code>.</p></li>
|
||||
<li><p>Back in your terminal running Bash:</p>
|
||||
<div class="sourceCode" id="cb6"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="bu">fg</span></span></code></pre></div>
|
||||
<p>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.</p></li>
|
||||
<li><p>In Ghidra, press Resume (or use <code>continue</code> in the
|
||||
Interpreter) until you hit the breakpoint at <code>main</code>. This
|
||||
permits the stub to complete its third command
|
||||
<code>exec ./termmines</code>. The <code>exec</code> command is
|
||||
different than normal command execution. Instead of creating a
|
||||
subprocess, it <em>replaces</em> the image of the stub process, so the
|
||||
process is now running <code>termmines</code>.</p></li>
|
||||
<li><p>Refresh the Modules node in the Objects window. You may need to
|
||||
clear your filter text. Expand the Modules node and verify it lists
|
||||
<code>termmines</code> instead of <code>bash</code>. Without this step,
|
||||
your listings may go out of sync.</p></li>
|
||||
</ol>
|
||||
<p>At this point, you are attached to your target running in the
|
||||
terminal, and you have trapped it at <code>main</code>. Because you were
|
||||
attached to it when it was still <code>bash</code>, 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 <code>bash</code>. 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.</p>
|
||||
print each subsequent game board when the user presses
|
||||
<strong>ENTER</strong>. Check your work by re-launching
|
||||
<code>termmines</code>, capturing its seed, inputting it into your
|
||||
program, and then winning the game. Optionally, win 2 more games in the
|
||||
same session.</p>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
@ -16,14 +16,13 @@ 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](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`.
|
||||
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 Terminal 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.
|
||||
The advantage of using the Terminal 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.
|
||||
Ghidra will memorize breakpoints by recording them as special bookmarks in the program database.
|
||||
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.
|
||||
@ -40,7 +39,7 @@ 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`:
|
||||
In the Terminal, type the GDB commands to set breakpoints on `srand` and `rand`:
|
||||
|
||||
```gdb
|
||||
break srand
|
||||
@ -53,10 +52,10 @@ 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.
|
||||
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.
|
||||
* The left-most column **State** 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 program database.
|
||||
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.
|
||||
@ -64,51 +63,55 @@ This lists the "logical" breakpoints, preferring static addresses.
|
||||
* 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.
|
||||
* The next column **Image** gives the name of the program database 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.
|
||||
That is, they are implemented by patching the target's memory with a special instruction that traps execution — `INT3` on x86.
|
||||
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."
|
||||
**NOTE**: GDB would call access breakpoints *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.
|
||||
For a single-target session, this is most likely 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 State, 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 text here should match one of the tabs from the Dynamic Listing 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. 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 Model window, expand the *Breakpoints* node and double-click a breakpoint, or select one with the keyboard and press **`ENTER`**.
|
||||
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`.
|
||||
1. From the Terminal 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.
|
||||
You might also type `info break` into the Terminal to confirm the effect of toggling breakpoints in the GUI.
|
||||
When you are finished, ensure both breakpoints are enabled.
|
||||
|
||||
**NOTE**: In all parts of the GUI, except the Model window, Ghidra prefers to toggle breakpoint locations.
|
||||
Without getting into details, this is the second level down of breakpoints shown in the Model tree.
|
||||
If you set a breakpoint, and GDB calls this breakpoint 2, then you toggle it in the listing, Ghidra will toggle, e.g., breakpoint *location* 2.1, not the breakpoint *specification* 2.
|
||||
If you disable breakpoint 2 using the Model or Terminal window, it may become impossible to toggle the breakpoint in the Listing or Breakpoints windows.
|
||||
If you find your session in this condition, just re-enable the troublesome breakpoints in the Model or Terminal window.
|
||||
|
||||
### Importing `libc`
|
||||
|
||||
While the Debugger can operate without importing external modules, it generally works better when you have.
|
||||
@ -128,9 +131,19 @@ You could do this in the usual manner, but the Debugger offers a convenient way
|
||||
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`.
|
||||
**NOTE**: Ghidra has not automatically disassembled the dynamic listing, because the program counter has not actually landed there, yet.
|
||||
|
||||
![The debugger tool with breakpoints synchronized after importing libc](images/Breakpoints_SyncedAfterImportLibC.png)
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
If it seems nothing has changed, except now you have a second program database open, then the new module may not be successfully mapped.
|
||||
|
||||
1. Re-check the Debug Console window and verify the note has been removed.
|
||||
1. If not, it might be because the module is symlinked in the file system, so the name of the module and the name of the program database do not match.
|
||||
1. Ensure that `libc` is the current program (tab) in the Static Listing.
|
||||
1. In the Modules window, right-click on `libc`, and select **Map Module to libc**. (Names and titles will likely differ.)
|
||||
|
||||
### Capturing the Random Seed
|
||||
|
||||
We can now allow `termmines` to execute, expecting it to hit the `srand` breakpoint first.
|
||||
@ -178,7 +191,6 @@ The advantages of a dynamic session along side static analysis should become mor
|
||||
### 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.
|
||||
@ -187,6 +199,14 @@ 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.
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
You may find that running both GDB and `termmines` in the same Terminal makes viewing the game board difficult.
|
||||
The next time you launch, be sure to use the **Configure and Launch** sub-menu, then enable the **Inferior TTY** option.
|
||||
This should start two Terminals, one with GDB and a second dedicated to `termmines`.
|
||||
The game board will no longer be corrupted by GDB's prompts and diagnostics.
|
||||
You will probably want to undock the `termmines` Terminal and resize it to fit the board.
|
||||
|
||||
### Optional Exercise: Replicate the Boards (Forward Engineering)
|
||||
|
||||
You will need a C development environment for this exercise.
|
||||
@ -194,58 +214,6 @@ Because, as we have now confirmed, `termmines` is importing its random number ge
|
||||
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, have it print each subsequent game board when the user presses **ENTER**.
|
||||
Check your work by re-launching `termmines`, 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.
|
||||
|
@ -107,7 +107,7 @@ not, please refer to the previous modules.</p>
|
||||
<p>This module will address the following features in more depth:</p>
|
||||
<ul>
|
||||
<li>Dynamic Listing window</li>
|
||||
<li>Dynamic Bytes window</li>
|
||||
<li>Dynamic Bytes (Memory) window</li>
|
||||
<li>Registers window</li>
|
||||
<li>Watches window</li>
|
||||
<li>Sleigh expressions</li>
|
||||
@ -154,10 +154,10 @@ may present tables with editable cells. These will often include a
|
||||
accidental patching. Furthermore, you must use the <img
|
||||
src="images/record.png" alt="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.</p>
|
||||
throughout the Debugger tool. For now, please only use the
|
||||
<strong>Control Target</strong> and <strong>Control Target w/ Edits
|
||||
Disabled</strong> options. The write toggle is included here so that
|
||||
actions like copy-paste do not lead to accidental patching.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="the-dynamic-listing" class="level2">
|
||||
@ -190,19 +190,19 @@ Location drop-down and select <strong>Track Stack Pointer</strong>. The
|
||||
window should seek to (and highlight in pale green) the address in the
|
||||
stack pointer. Since the target has just entered <code>main</code>, we
|
||||
should expect a return address at the top of the stack. With your cursor
|
||||
at the stack pointer, press <strong>P</strong> 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 <img
|
||||
src="images/register-marker.png" alt="track location" /> Track Location
|
||||
button, or you can double-click the <code>sp = [Address]</code> label in
|
||||
the top right of the Dynamic Listing.</p>
|
||||
at the stack pointer, press <strong><code>P</code></strong> 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 <img src="images/register-marker.png" alt="track location" /> Track
|
||||
Location button, or you can double-click the <code>sp = [Address]</code>
|
||||
label in the top right of the Dynamic Listing.</p>
|
||||
<p>To examine a more complicated stack segment, we will break at
|
||||
<code>rand</code>. Ensure your breakpoint at <code>rand</code> is
|
||||
enabled and press <img src="images/resume.png" alt="resume" /> Resume.
|
||||
Your Dynamic Listing should follow the stack pointer. In the menus,
|
||||
select <strong>Debugger → Analysis → Unwind Stack</strong> or press
|
||||
<strong>U</strong>.</p>
|
||||
select <strong>Debugger → Analysis → Unwind from frame 0</strong> or
|
||||
press <strong><code>U</code></strong>.</p>
|
||||
<figure>
|
||||
<img src="images/State_ListingStackAfterCallRand.png"
|
||||
alt="The dynamic listing of the stack after a call to rand" />
|
||||
@ -231,9 +231,9 @@ snapshots of the machine state, discussed later in this module.</p>
|
||||
<section id="cache-status-indication" class="level3">
|
||||
<h3>Cache Status Indication</h3>
|
||||
<p>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
|
||||
unresponsive or otherwise temperamental. The Debugger uses a trace
|
||||
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
|
||||
@ -241,8 +241,8 @@ 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
|
||||
memory, the page will have a red background. To refresh the visible or
|
||||
selected page(s), click the
|
||||
<img alt="refresh" src="images/view-refresh.png" width="16px"/> Refresh
|
||||
button. Examine the Debug Console window for errors / warnings before
|
||||
spamming this button. To toggle auto read, use the
|
||||
@ -269,12 +269,12 @@ memory map.</p>
|
||||
</section>
|
||||
<section id="go-to" class="level3">
|
||||
<h3>Go To</h3>
|
||||
<p>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
|
||||
<code>RAX</code> as a pointer and go to that address, for example. We
|
||||
cover Sleigh expressions later in this module.</p>
|
||||
<p>The <strong>Go To</strong> 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 <code>RAX</code> as a pointer and go to that address, for example.
|
||||
We cover Sleigh expressions later in this module.</p>
|
||||
</section>
|
||||
<section id="compare" class="level3">
|
||||
<h3>Compare</h3>
|
||||
@ -315,12 +315,12 @@ alt="The dynamic memory view of the stack after a call to rand" />
|
||||
after a call to rand</figcaption>
|
||||
</figure>
|
||||
<p>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 <strong>Windows → Byte Viewer → Memory
|
||||
…</strong> 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.</p>
|
||||
Memory viewer is the analog of the Bytes viewer. To open it, use
|
||||
<strong>Windows → Byte Viewer → Memory …</strong> 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.</p>
|
||||
<section id="exercise-display-the-board-in-hex" class="level3">
|
||||
<h3>Exercise: Display the Board in Hex</h3>
|
||||
<p>This is a bit quick and dirty, but it works and can be useful. Your
|
||||
@ -401,16 +401,17 @@ srand</figcaption>
|
||||
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.</p>
|
||||
address, the expression will appear as an option in the <strong>Location
|
||||
Tracking</strong> menus of the Listing and Memory viewers. Selecting
|
||||
that option will cause the window to follow that watch as its address
|
||||
changes.</p>
|
||||
<p>To add a watch, click the <img src="images/add.png" alt="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 <code>RDI</code>. (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.</p>
|
||||
Listing and Registers windows include a <strong>Watch</strong> action,
|
||||
which adds the current selection to the Watches window.</p>
|
||||
<p>The columns are:</p>
|
||||
<ul>
|
||||
<li>The <strong>Expression</strong> column is the user-defined Sleigh
|
||||
@ -420,7 +421,8 @@ value, if applicable. This may be in <code>register</code> space.
|
||||
Double-clicking this cell will go to the address in the Dynamic
|
||||
Listing.</li>
|
||||
<li>The <strong>Symbol</strong> column gives the symbol in a mapped
|
||||
static image closest to or containing the address, if applicable.</li>
|
||||
program database closest to or containing the address, if
|
||||
applicable.</li>
|
||||
<li>The <strong>Value</strong> column gives the “raw” value of the
|
||||
expression. If the result is in memory, it displays a byte array;
|
||||
otherwise, it displays an integer.</li>
|
||||
@ -436,13 +438,14 @@ evaluating the expression.</li>
|
||||
</section>
|
||||
<section id="sleigh-expressions" class="level2">
|
||||
<h2>Sleigh Expressions</h2>
|
||||
<p>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 <code>main</code>,
|
||||
you could use <code>*:8 RSP - main</code>.</p>
|
||||
<p>Watches and Go-To commands are expressed using Ghidra’s
|
||||
<em>Sleigh</em> 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
|
||||
databases in your expression. For example, to see how far a return
|
||||
address is into <code>main</code>, you could use
|
||||
<code>*:8 RSP - main</code>.</p>
|
||||
<p>For the complete specification, see the Semantic Section in the <a
|
||||
href="../../../Ghidra/Features/Decompiler/src/main/doc/sleigh.xml">Sleigh
|
||||
documentation</a>.</p>
|
||||
@ -521,7 +524,7 @@ array. To read memory:</p>
|
||||
<li><strong>Dereference</strong>: <code>*:8 RSP</code> or
|
||||
<code>*[ram]:8 RSP</code></li>
|
||||
</ul>
|
||||
<p><strong>NOTE</strong> The <code>[ram]</code> part is optional. On
|
||||
<p><strong>NOTE</strong>: The <code>[ram]</code> part is optional. On
|
||||
x86, you will rarely if ever specify the space, since there is only one
|
||||
physical RAM space. The <code>:8</code> part specifies the number of
|
||||
bytes to read from memory. It is also optional, but only if the size can
|
||||
@ -636,7 +639,7 @@ variable, e.g., <code>7fffffffe618</code></li>
|
||||
<li><strong>Bytes</strong>: The raw bytes currently stored in the memory
|
||||
allocated to the variable</li>
|
||||
<li><strong>Integer</strong>: The “raw” integer value of the variable,
|
||||
rendered with varyied signedness and radix</li>
|
||||
rendered with varied signedness and radix</li>
|
||||
<li><strong>Value</strong>: The value of the variable, according to its
|
||||
type</li>
|
||||
<li><strong>Instruction</strong>: If the variable points to code, the
|
||||
|
@ -8,7 +8,7 @@ If not, please refer to the previous modules.
|
||||
This module will address the following features in more depth:
|
||||
|
||||
* Dynamic Listing window
|
||||
* Dynamic Bytes window
|
||||
* Dynamic Bytes (Memory) window
|
||||
* Registers window
|
||||
* Watches window
|
||||
* Sleigh expressions
|
||||
@ -41,7 +41,7 @@ Edits in any *static* window will **not** patch the live target; they modify the
|
||||
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.
|
||||
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
|
||||
@ -65,7 +65,7 @@ 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
|
||||
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.
|
||||
@ -73,7 +73,7 @@ To return to the stack pointer, you can use the back arrow in the global toolbar
|
||||
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**.
|
||||
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)
|
||||
|
||||
@ -93,12 +93,12 @@ 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 Debugger uses a trace 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.
|
||||
If the back-end debugger reports an error while reading memory, the page will have a red background.
|
||||
To refresh the visible or selected page(s), click the <img alt="refresh" src="images/view-refresh.png" width="16px"/> Refresh button.
|
||||
Examine the Debug Console window for errors / warnings before spamming this button.
|
||||
To toggle auto read, use the <img src="images/autoread.png" alt="auto-read" width="16px"/> Auto-Read drop-down button from the local toolbar.
|
||||
@ -119,7 +119,7 @@ This can happen when the cursor is outside any known region, which only happens
|
||||
|
||||
### Go To
|
||||
|
||||
The Go To action in the Dynamic Listing differs from the one in the Static Listing.
|
||||
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.
|
||||
@ -147,8 +147,7 @@ You can also experiment by placing code units in the Dynamic Listing before comm
|
||||
|
||||
![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.
|
||||
Just as the Dynamic Listing is the analog of the Static Listing, the Memory viewer is the analog of the Bytes viewer.
|
||||
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.
|
||||
@ -209,7 +208,7 @@ Verify your work by playing the round.
|
||||
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.
|
||||
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.
|
||||
@ -217,7 +216,7 @@ 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 context menus for the Listing and Registers windows include a **Watch** action, which adds the current selection to the Watches window.
|
||||
|
||||
The columns are:
|
||||
|
||||
@ -225,7 +224,7 @@ The columns are:
|
||||
* 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 **Symbol** column gives the symbol in a mapped program database 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.
|
||||
@ -236,10 +235,10 @@ The columns are:
|
||||
|
||||
## Sleigh Expressions
|
||||
|
||||
Watches and Go-To commands are expressed using Ghidra's Sleigh language.
|
||||
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.
|
||||
Of note, you may use labels from mapped program databases 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).
|
||||
@ -303,7 +302,7 @@ To read memory:
|
||||
|
||||
* **Dereference**: `*:8 RSP` or `*[ram]:8 RSP`
|
||||
|
||||
**NOTE** The `[ram]` part is optional.
|
||||
**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.
|
||||
@ -386,7 +385,7 @@ Depending on the particular variable and other circumstances, the hover will con
|
||||
* **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
|
||||
* **Integer**: The "raw" integer value of the variable, rendered with varied 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
|
||||
|
@ -45,10 +45,8 @@
|
||||
<li><a href="#navigation" id="toc-navigation">Navigation</a>
|
||||
<ul>
|
||||
<li><a href="#coordinates" id="toc-coordinates">Coordinates</a></li>
|
||||
<li><a href="#threads" id="toc-threads">Threads</a>
|
||||
<ul>
|
||||
<li><a href="#trace-tabs" id="toc-trace-tabs">Trace Tabs</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#threads" id="toc-threads">Threads</a></li>
|
||||
<li><a href="#stack" id="toc-stack">Stack</a>
|
||||
<ul>
|
||||
<li><a href="#exercise-name-the-function"
|
||||
@ -75,6 +73,7 @@ breakpoints and machine state in Ghidra. If not, please refer to the
|
||||
previous modules.</p>
|
||||
<p>This module will address the following features in more depth:</p>
|
||||
<ul>
|
||||
<li>The Trace tabs — in the Dynamic Listing window</li>
|
||||
<li>The Threads window</li>
|
||||
<li>The Stack window</li>
|
||||
<li>The Time window</li>
|
||||
@ -89,8 +88,9 @@ can affect the information displayed in other windows, especially those
|
||||
dealing with machine state.</p>
|
||||
<ul>
|
||||
<li>The current <em>trace</em>. 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.</li>
|
||||
Debugger windows (except Connections and Terminal) gather their
|
||||
information. It is the analog of the program database, but for dynamic
|
||||
analysis.</li>
|
||||
<li>The current <em>thread</em>. 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
|
||||
@ -109,12 +109,26 @@ may also include steps of emulation, but that is covered in the <a
|
||||
href="B2-Emulation.html">Emulation</a> module.</li>
|
||||
</ul>
|
||||
<p>In general, there is a window dedicated to navigating each element of
|
||||
your current coordinates.</p>
|
||||
your current coordinates. If you do not have an active session already,
|
||||
launch <code>termmines</code>.</p>
|
||||
</section>
|
||||
<section id="trace-tabs" class="level2">
|
||||
<h2>Trace Tabs</h2>
|
||||
<p>The Dynamic Listing window 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 <em>post
|
||||
mortem</em>. 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.</p>
|
||||
<p>When you switch traces, every Debugger window that depends on the
|
||||
current trace will update. That’s every window except Connections and
|
||||
Terminal. (Each connection has its own Terminal windows.) The
|
||||
Breakpoints window may change slightly, depending on its configuration,
|
||||
because it is designed to present all breakpoints in the session.</p>
|
||||
</section>
|
||||
<section id="threads" class="level2">
|
||||
<h2>Threads</h2>
|
||||
<p>If you do not have an active session already, launch
|
||||
<code>termmines</code>.</p>
|
||||
<figure>
|
||||
<img src="images/Navigation_ThreadsInCallRand.png"
|
||||
alt="Threads window" />
|
||||
@ -131,15 +145,12 @@ are:</p>
|
||||
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.</li>
|
||||
<li>The <strong>Created</strong> column gives the snapshot when the
|
||||
thread was first observed.</li>
|
||||
<li>The <strong>Destroyed</strong> column gives the snapshot when the
|
||||
thread was first observed as terminated. If this is empty, the thread is
|
||||
still alive.</li>
|
||||
<li>The <strong>PC</strong> column gives the program counter of the
|
||||
thread.</li>
|
||||
<li>The <strong>Function</strong> column gives the function from a
|
||||
mapped program database containing the program counter.</li>
|
||||
<li>The <strong>State</strong> column gives the state of the thread.
|
||||
This may be one of ALIVE, RUNNING, STOPPED, TERMINATED, or UNKNOWN.</li>
|
||||
<li>The <strong>Comment</strong> column allows you to annotate the
|
||||
thread, e.g., if you discover it has a dedicated purpose.</li>
|
||||
<li>The <strong>Plot</strong> column plots the threads’ life spans in a
|
||||
chart.</li>
|
||||
</ul>
|
||||
@ -161,21 +172,6 @@ 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.</p>
|
||||
<section id="trace-tabs" class="level3">
|
||||
<h3>Trace Tabs</h3>
|
||||
<p>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 <em>post
|
||||
mortem</em>. 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.</p>
|
||||
<p>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.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="stack" class="level2">
|
||||
<h2>Stack</h2>
|
||||
@ -202,8 +198,8 @@ so on.</li>
|
||||
<li>The <strong>Function</strong> column gives the name of the function
|
||||
containing the PC mapped to its static program database, if
|
||||
available.</li>
|
||||
<li>The <strong>Comment</strong> column allows you to annotate the
|
||||
frame.</li>
|
||||
<li>The <strong>Module</strong> column gives the name of the module
|
||||
containing the PC.</li>
|
||||
</ul>
|
||||
<p>Double-click the row with the unnamed function (frame 1) to switch to
|
||||
it. When you switch frames, any machine-state window that involves
|
||||
@ -230,7 +226,7 @@ alt="Time window" />
|
||||
<figcaption aria-hidden="true">Time window</figcaption>
|
||||
</figure>
|
||||
<p>It displays a list of all the snapshots for the current trace. In
|
||||
general, every event generates a snapshot. By default, the most recent
|
||||
general, every pause generates a snapshot. By default, the most recent
|
||||
snapshot is at the bottom. The columns are:</p>
|
||||
<ul>
|
||||
<li>The <strong>Snap</strong> column numbers each snapshot. Other
|
||||
@ -244,33 +240,36 @@ created because of an event, which is most.</li>
|
||||
relation to another. It typically only applies to emulator / scratch
|
||||
snapshots, which are covered later in this course.</li>
|
||||
<li>The <strong>Description</strong> column describes the event that
|
||||
generated the snapshot.</li>
|
||||
generated the snapshot. This can be edited in the table, or by pressing
|
||||
<strong><code>CTRL</code>-<code>SHIFT</code>-<code>N</code></strong> to
|
||||
mark interesting snapshots.</li>
|
||||
</ul>
|
||||
<p>Switch to the snapshot where you hit <code>srand</code> (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.</p>
|
||||
<p><strong>NOTE</strong>: 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 <strong>Control
|
||||
Target</strong> (with or without edits), the Debugger will navigate
|
||||
forward to the latest snapshot.</p>
|
||||
<p>Before we can navigate back in time, you must change the <em>Control
|
||||
Mode</em> to <strong>Control Trace</strong>. Now, switch to the snapshot
|
||||
where you hit <code>srand</code> (snapshot 1 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.</p>
|
||||
<p><strong>NOTE</strong>: The control-mode change is required to avoid
|
||||
confusion between recorded and live states. When you switch back to
|
||||
<strong>Control Target</strong> (with or without edits), the Debugger
|
||||
will navigate forward to the latest snapshot and prohibit navigating to
|
||||
the past.</p>
|
||||
<section id="sparse-vs.-full-snapshots" class="level3">
|
||||
<h3>Sparse vs. Full Snapshots</h3>
|
||||
<p>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 <em>sparse</em>
|
||||
back-end debuggers is nascent.) 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 <em>sparse</em>
|
||||
snapshots. The most straightforward way to capture a <em>full</em>
|
||||
snapshot is the
|
||||
<img alt="refresh" src="images/view-refresh.png" width="16px"> 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.</p>
|
||||
<img alt="refresh" src="images/view-refresh.png" width="16px">
|
||||
<strong>Read Memory</strong> 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.</p>
|
||||
</section>
|
||||
<section id="comparing-snapshots" class="level3">
|
||||
<h3>Comparing Snapshots</h3>
|
||||
@ -289,15 +288,18 @@ will need to launch (not attach) the target.</p>
|
||||
href="A1-GettingStarted.html">Getting Started</a> to review launching
|
||||
with custom parameters.)</p></li>
|
||||
<li><p>Ensure your breakpoint at <code>srand</code> is enabled.</p></li>
|
||||
<li><p>Use <strong>Ctrl-A</strong> to Select All the addresses.</p></li>
|
||||
<li><p>Use <strong><code>CTRL</code>-<code>A</code></strong> to Select
|
||||
All the addresses.</p></li>
|
||||
<li><p>Click the
|
||||
<img alt="refresh" src="images/view-refresh.png" width="16px"> Refresh
|
||||
button. <strong>NOTE</strong>: It is normal for some errors to occur
|
||||
here. We note a more surgical approach below.</p></li>
|
||||
<li><p>Wait a moment for the capture to finish.</p></li>
|
||||
<li><p>Optionally, press <strong>Ctrl-Shift-N</strong> to rename the
|
||||
snapshot so you can easily identify it later. Alternatively, edit the
|
||||
snapshot’s Description from the table in the Time window.</p></li>
|
||||
<li><p>Optionally, press
|
||||
<strong><code>CTRL</code>-<code>SHIFT</code>-<code>N</code></strong> to
|
||||
rename the snapshot so you can easily identify it later, e.g., “Initial
|
||||
snapshot.” Alternatively, edit the snapshot’s Description from the table
|
||||
in the Time window.</p></li>
|
||||
<li><p>Press <img src="images/resume.png" alt="resume" /> Resume,
|
||||
expecting it to break at <code>srand</code>.</p></li>
|
||||
<li><p>Capture another full snapshot using Select All and
|
||||
@ -310,7 +312,7 @@ alt="compare" /> Compare button in the Dynamic Listing.</p></li>
|
||||
alt="The compare times dialog" />
|
||||
<figcaption aria-hidden="true">The compare times dialog</figcaption>
|
||||
</figure></li>
|
||||
<li><p>Click OK.</p></li>
|
||||
<li><p>Click <strong>OK</strong>.</p></li>
|
||||
</ol>
|
||||
<p>The result is a side-by-side listing of the two snapshots with
|
||||
differences highlighted in orange. Unlike the Static program comparison
|
||||
@ -341,14 +343,19 @@ navigate to the <code>.data</code> section. The Dynamic Listing will
|
||||
stay in sync and consequently capture the contents of the first page.
|
||||
This specimen has a small enough <code>.data</code> section to fit in a
|
||||
single page, but that is generally not the case in practice.</li>
|
||||
<li>Use the Regions window to select the addresses in the
|
||||
<code>.data</code> section, then click Refresh in the Dynamic Listing.
|
||||
<li>If there are more pages, use the Regions window. Click the
|
||||
<strong>Select Rows</strong> then the <strong>Select Addresses</strong>
|
||||
buttons to expand the selection to the whole region containing the
|
||||
cursor. Then click <strong>Read Memory</strong> in the Dynamic Listing.
|
||||
This will capture the full <code>.data</code> section, no matter how
|
||||
many pages.</li>
|
||||
<li>Use the lower pane of the Modules window to select the addresses in
|
||||
the <code>.data</code> section, then click Refresh in the Dynamic
|
||||
Listing. This will also capture the full <code>.data</code>
|
||||
section.</li>
|
||||
<li>Alternatively, Use the Modules window to select
|
||||
<code>termmines</code> and load its sections. (Not all debuggers support
|
||||
this.) Click the <strong>Show Sections Table</strong> button in the
|
||||
local toolbar to enable the lower pane. Use the Sections table to select
|
||||
the addresses in the <code>.data</code> section, then click <strong>Read
|
||||
Memory</strong> in the Dynamic Listing. This will also capture the full
|
||||
<code>.data</code> section.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="exercise-find-the-time" class="level3">
|
||||
@ -358,9 +365,10 @@ 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. <strong>TIP</strong>: 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!</p>
|
||||
the game, you should enable <strong>Inferior TTY</strong> in the
|
||||
launcher. Use the snapshot comparison method to locate the variable.
|
||||
Then place an appropriate breakpoint, win the game, patch the variable,
|
||||
and score 0 seconds!</p>
|
||||
<p>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
|
||||
|
@ -7,6 +7,7 @@ If not, please refer to the previous modules.
|
||||
|
||||
This module will address the following features in more depth:
|
||||
|
||||
* The Trace tabs — in the Dynamic Listing window
|
||||
* The Threads window
|
||||
* The Stack window
|
||||
* The Time window
|
||||
@ -18,7 +19,7 @@ There are more elements to a "location" in a dynamic session, so we add addition
|
||||
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.
|
||||
A trace database is where all of the Debugger windows (except Connections and Terminal) 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.
|
||||
@ -36,11 +37,24 @@ All of these elements can affect the information displayed in other windows, esp
|
||||
"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.
|
||||
If you do not have an active session already, launch `termmines`.
|
||||
|
||||
## Trace Tabs
|
||||
|
||||
The Dynamic Listing window 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 Connections and Terminal.
|
||||
(Each connection has its own Terminal windows.)
|
||||
The Breakpoints window may change slightly, depending on its configuration, because it is designed to present all breakpoints in the session.
|
||||
|
||||
## 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.
|
||||
@ -51,12 +65,10 @@ 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 **PC** column gives the program counter of the thread.
|
||||
* The **Function** column gives the function from a mapped program database containing the program counter.
|
||||
* 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.
|
||||
@ -68,19 +80,6 @@ It may step a different thread, it may cause the target to block until the threa
|
||||
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.
|
||||
@ -99,7 +98,7 @@ The columns are:
|
||||
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.
|
||||
* The **Module** column gives the name of the module containing the PC.
|
||||
|
||||
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.
|
||||
@ -119,7 +118,7 @@ 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.
|
||||
In general, every pause generates a snapshot.
|
||||
By default, the most recent snapshot is at the bottom.
|
||||
The columns are:
|
||||
|
||||
@ -131,23 +130,23 @@ The columns are:
|
||||
* 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.
|
||||
This can be edited in the table, or by pressing **`CTRL`-`SHIFT`-`N`** to mark interesting snapshots.
|
||||
|
||||
Switch to the snapshot where you hit `srand` (snapshot 2 in our screenshot) by double-clicking it in the table.
|
||||
Before we can navigate back in time, you must change the *Control Mode* to **Control Trace**.
|
||||
Now, switch to the snapshot where you hit `srand` (snapshot 1 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.
|
||||
**NOTE**: The control-mode change is required to avoid confusion between recorded and live states.
|
||||
When you switch back to **Control Target** (with or without edits), the Debugger will navigate forward to the latest snapshot and prohibit navigating to the past.
|
||||
|
||||
### 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.)
|
||||
(Integration with timeless back-end debuggers is nascent.)
|
||||
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 <img alt="refresh" src="images/view-refresh.png" width="16px"> Refresh button with a broad selection in the Dynamic Listing.
|
||||
The most straightforward way to capture a *full* snapshot is the <img alt="refresh" src="images/view-refresh.png" width="16px"> **Read Memory** 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.
|
||||
|
||||
@ -164,12 +163,12 @@ Because parsing happens before waiting for user input, we will need to launch (n
|
||||
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. Use **`CTRL`-`A`** to Select All the addresses.
|
||||
1. Click the <img alt="refresh" src="images/view-refresh.png" width="16px"> 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.
|
||||
1. Optionally, press **`CTRL`-`SHIFT`-`N`** to rename the snapshot so you can easily identify it later, e.g., "Initial snapshot."
|
||||
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.
|
||||
@ -178,7 +177,7 @@ Because parsing happens before waiting for user input, we will need to launch (n
|
||||
|
||||
![The compare times dialog](images/Navigation_DialogCompareTimes.png)
|
||||
|
||||
1. Click OK.
|
||||
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.
|
||||
@ -199,9 +198,13 @@ Some alternatives, which we will cover in the [Memory Map](A6-MemoryMap.md) modu
|
||||
* 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.
|
||||
* If there are more pages, use the Regions window. Click the **Select Rows** then the **Select Addresses** buttons to expand the selection to the whole region containing the cursor.
|
||||
Then click **Read Memory** 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.
|
||||
* Alternatively, Use the Modules window to select `termmines` and load its sections.
|
||||
(Not all debuggers support this.)
|
||||
Click the **Show Sections Table** button in the local toolbar to enable the lower pane.
|
||||
Use the Sections table to select the addresses in the `.data` section, then click **Read Memory** in the Dynamic Listing.
|
||||
This will also capture the full `.data` section.
|
||||
|
||||
### Exercise: Find the Time
|
||||
@ -209,7 +212,7 @@ Some alternatives, which we will cover in the [Memory Map](A6-MemoryMap.md) modu
|
||||
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.
|
||||
**TIP**: Because you will need to play the game, you should enable **Inferior TTY** in the launcher.
|
||||
Use the snapshot comparison method to locate the variable.
|
||||
Then place an appropriate breakpoint, win the game, patch the variable, and score 0 seconds!
|
||||
|
||||
|
@ -94,26 +94,23 @@ stacks, heaps, or other system objects. The columns are:</p>
|
||||
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.</li>
|
||||
<li>The <strong>Lifespan</strong> 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.</li>
|
||||
<li>The <strong>Start</strong> column gives the minimum address of the
|
||||
region.</li>
|
||||
<li>The <strong>End</strong> column gives the maximum (inclusive)
|
||||
address of the region.</li>
|
||||
<li>The <strong>Length</strong> column gives the number of bytes in the
|
||||
region.</li>
|
||||
<li>The <strong>Read</strong>, <strong>Write</strong>,
|
||||
<strong>Execute</strong>, and <strong>Volatile</strong> columns give the
|
||||
permissions/flags of the region.</li>
|
||||
<li>The <strong>Read</strong>, <strong>Write</strong>, and
|
||||
<strong>Execute</strong> columns give the permissions of the
|
||||
region.</li>
|
||||
</ul>
|
||||
<p>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 <strong>Select Addresses</strong>. 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.</p>
|
||||
addresses in those regions in the Dynamic Listing. Used with the
|
||||
<strong>Read Memory</strong> button in the Dynamic Listing, you can
|
||||
selectively capture memory into the current snapshot.</p>
|
||||
</section>
|
||||
<section id="modules" class="level2">
|
||||
<h2>Modules</h2>
|
||||
@ -122,14 +119,16 @@ button, you can surgically capture memory into the current snapshot.</p>
|
||||
alt="Modules window after launch" />
|
||||
<figcaption aria-hidden="true">Modules window after launch</figcaption>
|
||||
</figure>
|
||||
<p>The Modules window has two panes. The top pane displays a list of all
|
||||
the <em>modules</em> known to the back-end debugger. The bottom pane
|
||||
displays a list of all the <em>sections</em> 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.</p>
|
||||
<p>The Modules window has two panes, though the second is disabled by
|
||||
default. The top pane displays a list of all the <em>modules</em> known
|
||||
to the back-end debugger. The bottom pane displays a list of all the
|
||||
<em>sections</em> known to the back-end debugger. In practice, not all
|
||||
targets will report module information. Fewer targets report section
|
||||
information, and those that do may only report them to Ghidra when
|
||||
specifically requested. 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.</p>
|
||||
<p>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 <em>not</em> include
|
||||
@ -138,44 +137,27 @@ stacks, heaps, etc.</p>
|
||||
<ul>
|
||||
<li>The <strong>Base</strong> column gives the image base for the
|
||||
module. This should be the minimum address of the module.</li>
|
||||
<li>The <strong>Max Address</strong> column gives the maximum address of
|
||||
the module.</li>
|
||||
<li>The <strong>Max</strong> column gives the maximum address of the
|
||||
module.</li>
|
||||
<li>The <strong>Name</strong> column gives the (short) name of the
|
||||
module.</li>
|
||||
<li>The <strong>Module Name</strong> column gives the full file path of
|
||||
the module <em>on target</em>. In some cases, this gives some other
|
||||
description of the module.</li>
|
||||
<li>The <strong>Lifespan</strong> column gives the span of snapshots
|
||||
where the module has been observed.</li>
|
||||
<li>The <strong>Mapping</strong> column gives the mapped Ghidra program
|
||||
database for the module.</li>
|
||||
<li>The <strong>Length</strong> 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.</li>
|
||||
</ul>
|
||||
<p>The section columns are:</p>
|
||||
<ul>
|
||||
<li>The <strong>Start Address</strong> gives the minimum address of the
|
||||
section.</li>
|
||||
<li>The <strong>End Address</strong> gives the maximum (inclusive)
|
||||
address of the section.</li>
|
||||
<li>The <strong>Section Name</strong> gives the name of the
|
||||
section.</li>
|
||||
<li>The <strong>Module Name</strong> gives the name of the module
|
||||
containing the section.</li>
|
||||
<li>The <strong>Length</strong> gives the number of bytes contained in
|
||||
the section.</li>
|
||||
</ul>
|
||||
<p><strong>NOTE</strong>: There is no lifespan column for a section. The
|
||||
lifespan of a section is the lifespan of its containing module.</p>
|
||||
<p>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
|
||||
<strong>Select Addresses</strong>. Again, combined with the Dynamic
|
||||
Listing’s Refresh button, you can capture memory surgically.</p>
|
||||
<p>See the Help (press <strong><code>F1</code></strong> in the Modules
|
||||
panel) to learn more about the Sections table, if desired. It can be
|
||||
enabled using the <strong>Show Sections Table</strong> button in the
|
||||
local toolbar.</p>
|
||||
<p>Double-click any address to navigate to it. Make a selection of
|
||||
modules or sections, right-click, and choose <strong>Select
|
||||
Addresses</strong>. Again, combined with the Dynamic Listing’s
|
||||
<strong>Read Memory</strong> button, you can capture memory
|
||||
selectively.</p>
|
||||
</section>
|
||||
<section id="optional-exercise-find-the-time-surgically" class="level2">
|
||||
<h2>Optional Exercise: Find the Time Surgically</h2>
|
||||
@ -232,19 +214,20 @@ and cope with module relocation, especially from ASLR.</p>
|
||||
<li>From the Modules window, select one or more modules, and choose from
|
||||
the <strong>Map Module</strong> actions. Selecting a single module at a
|
||||
time, it is possible to surgically map each to a chosen program.</li>
|
||||
<li>From the Sections window, select one or more sections, and choose
|
||||
<li>From the Sections panel, select one or more sections, and choose
|
||||
from the <strong>Map Section</strong> 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.</li>
|
||||
<li>From the Regions window, select one or more regions, and choose from
|
||||
the <strong>Map Region</strong> actions.</li>
|
||||
<li>Click the Map Identically button in the Modules window toolbar.</li>
|
||||
<li>Use the Add and Remove buttons in the Static Mappings window
|
||||
toolbar.</li>
|
||||
<li>Click the <strong>Map Identically</strong> button in the Modules
|
||||
window toolbar.</li>
|
||||
<li>Use the <strong>Add</strong> and <strong>Remove</strong> buttons in
|
||||
the Static Mappings window toolbar.</li>
|
||||
</ul>
|
||||
<p>These methods are not described in detail here. For more information,
|
||||
hover over the relevant actions and press <strong>F1</strong> for
|
||||
help.</p>
|
||||
hover over the relevant actions and press
|
||||
<strong><code>F1</code></strong> for help.</p>
|
||||
</section>
|
||||
<section id="moving-knowledge-from-dynamic-to-static" class="level2">
|
||||
<h2>Moving Knowledge from Dynamic to Static</h2>
|
||||
@ -256,14 +239,14 @@ 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 <strong>Debugger → Copy Into Current Program</strong>.</p>
|
||||
Listing. After selecting the units to copy, you would use
|
||||
<strong>Debugger → Copy Into Current Program</strong> in the menus.</p>
|
||||
<p>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 <strong>Debugger → Copy
|
||||
Into New Program</strong>.</p>
|
||||
in that module, you would use <strong>Debugger → Copy Into New
|
||||
Program</strong>.</p>
|
||||
<p>For demonstration, we will walk through this second case, pretending
|
||||
we cannot load <code>libncurses</code> from disk:</p>
|
||||
<ol type="1">
|
||||
@ -279,20 +262,22 @@ Program</strong>.</p>
|
||||
alt="Copy dialog for ncurses" />
|
||||
<figcaption aria-hidden="true">Copy dialog for ncurses</figcaption>
|
||||
</figure></li>
|
||||
<li><p>Keep Destination set to “New Program.”</p></li>
|
||||
<li><p>Ensure “Read live target’s memory” is checked. This will spare
|
||||
you from having to create a full snapshot manually.</p></li>
|
||||
<li><p>Do <em>not</em> check “Use overlays where blocks already
|
||||
present.” It should not have any effect for a new program,
|
||||
<li><p>Keep <strong>Destination</strong> set to “<New
|
||||
Program>.”</p></li>
|
||||
<li><p>Ensure <strong>Read live target’s memory</strong> is checked.
|
||||
This will spare you from having to create a full snapshot
|
||||
manually.</p></li>
|
||||
<li><p>Do <em>not</em> check <strong>Use overlays where blocks already
|
||||
exist</strong>. It should not have any effect for a new program,
|
||||
anyway.</p></li>
|
||||
<li><p>It is probably best to include everything, though “Bytes” is the
|
||||
bare minimum.</p></li>
|
||||
<li><p>It is probably best to include everything, though
|
||||
<strong>Bytes</strong> is the bare minimum.</p></li>
|
||||
<li><p>The table displays the <em>copy plan</em>. 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.</p></li>
|
||||
<li><p>Click Copy.</p></li>
|
||||
<li><p>Click <strong>Copy</strong>.</p></li>
|
||||
<li><p>When prompted, name the program <code>libncurses</code>.</p></li>
|
||||
<li><p>You may need to click the <code>termmines</code> tab in the
|
||||
Static Listing to get the UI to completely update.</p></li>
|
||||
@ -302,17 +287,13 @@ If you are prompted to analyze, go ahead.</p></li>
|
||||
<p>Undoubtedly, we would like to map that new program into our dynamic
|
||||
session.</p>
|
||||
<ol type="1">
|
||||
<li>Again, in the top pane of the Modules window, right-click
|
||||
<code>libncurses</code> and choose <strong>Select
|
||||
Addresses</strong>.</li>
|
||||
<li>Ensure the cursor in the Static Listing is at the minimum address of
|
||||
<code>libncurses</code>.</li>
|
||||
<li>In the Static Mappings window, click Add in the toolbar.</li>
|
||||
<li>Click OK.</li>
|
||||
<li>Ensure that the new <code>libncurses</code> capture is still the
|
||||
current program.</li>
|
||||
<li>In the top pane of the Modules window, right-click
|
||||
<code>libncurses</code> and choose <strong>Map to
|
||||
libncurses</strong>.</li>
|
||||
<li>Check the proposed mapping and click <strong>OK</strong>.</li>
|
||||
</ol>
|
||||
<p><strong>NOTE</strong>: This should be done by choosing <strong>Map to
|
||||
libncurses</strong> in the right-click menu, but a bug seems to stifle
|
||||
that, currently.</p>
|
||||
</section>
|
||||
<section id="exercise-export-and-map-ncurses" class="level2">
|
||||
<h2>Exercise: Export and Map <code>ncurses</code></h2>
|
||||
|
@ -26,28 +26,26 @@ The columns are:
|
||||
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.
|
||||
* The **Read**, **Write**, and **Execute** columns give the permissions 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.
|
||||
Used with the **Read Memory** button in the Dynamic Listing, you can selectively capture memory into the current snapshot.
|
||||
|
||||
## Modules
|
||||
|
||||
![Modules window after launch](images/MemoryMap_ModulesAfterLaunch.png)
|
||||
|
||||
The Modules window has two panes.
|
||||
The Modules window has two panes, though the second is disabled by default.
|
||||
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.
|
||||
Fewer targets report section information, and those that do may only report them to Ghidra when specifically requested.
|
||||
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.
|
||||
|
||||
@ -58,32 +56,19 @@ 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 **Max** 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 **Mapping** column gives the mapped Ghidra program database for the module.
|
||||
* 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:
|
||||
See the Help (press **`F1`** in the Modules panel) to learn more about the Sections table, if desired.
|
||||
It can be enabled using the **Show Sections Table** button in the local toolbar.
|
||||
|
||||
* 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.
|
||||
Again, combined with the Dynamic Listing's **Read Memory** button, you can capture memory selectively.
|
||||
|
||||
## Optional Exercise: Find the Time Surgically
|
||||
|
||||
@ -121,14 +106,14 @@ 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.
|
||||
* From the Sections panel, 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.
|
||||
* 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.
|
||||
For more information, hover over the relevant actions and press **`F1`** for help.
|
||||
|
||||
## Moving Knowledge from Dynamic to Static
|
||||
|
||||
@ -136,11 +121,11 @@ There are occasions when it is necessary or convenient to transfer data or marku
|
||||
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**.
|
||||
After selecting the units to copy, you would use **Debugger → Copy Into Current Program** in the menus.
|
||||
|
||||
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**.
|
||||
After selecting the addresses in that module, you would use **Debugger → Copy Into New Program**.
|
||||
|
||||
For demonstration, we will walk through this second case, pretending we cannot load `libncurses` from disk:
|
||||
|
||||
@ -151,16 +136,16 @@ For demonstration, we will walk through this second case, pretending we cannot l
|
||||
|
||||
![Copy dialog for ncurses](images/MemoryMap_CopyNcursesInto.png)
|
||||
|
||||
1. Keep Destination set to "New Program."
|
||||
1. Ensure "Read live target's memory" is checked.
|
||||
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."
|
||||
1. Do *not* check **Use overlays where blocks already exist**.
|
||||
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. 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. 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.
|
||||
@ -168,12 +153,9 @@ For demonstration, we will walk through this second case, pretending we cannot l
|
||||
|
||||
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.
|
||||
1. Ensure that the new `libncurses` capture is still the current program.
|
||||
1. In the top pane of the Modules window, right-click `libncurses` and choose **Map to libncurses**.
|
||||
1. Check the proposed mapping and click **OK**.
|
||||
|
||||
## Exercise: Export and Map `ncurses`
|
||||
|
||||
|
@ -112,19 +112,21 @@
|
||||
id="toc-module-mapping-caveats">Module Mapping Caveats</a></li>
|
||||
<li><a href="#variation-in-configuration"
|
||||
id="toc-variation-in-configuration">Variation in Configuration</a></li>
|
||||
<li><a href="#using-gdbserver" id="toc-using-gdbserver">Using
|
||||
<code>gdbserver</code></a></li>
|
||||
<li><a href="#using-ssh" id="toc-using-ssh">Using SSH</a></li>
|
||||
<li><a href="#using-gadp" id="toc-using-gadp">Using GADP</a>
|
||||
<li><a href="#using-gdbserver-over-ssh"
|
||||
id="toc-using-gdbserver-over-ssh">Using <code>gdbserver</code> over
|
||||
SSH</a></li>
|
||||
<li><a href="#using-trace-rmi-over-ssh"
|
||||
id="toc-using-trace-rmi-over-ssh">Using Trace RMI over SSH</a>
|
||||
<ul>
|
||||
<li><a href="#using-gadp-locally" id="toc-using-gadp-locally">Using GADP
|
||||
Locally</a></li>
|
||||
<li><a href="#using-gadp-remotely" id="toc-using-gadp-remotely">Using
|
||||
GADP Remotely</a></li>
|
||||
<li><a href="#troubleshooting"
|
||||
id="toc-troubleshooting">Troubleshooting</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#using-a-pty-pseudo-terminal"
|
||||
id="toc-using-a-pty-pseudo-terminal">Using a pty
|
||||
(pseudo-terminal)</a></li>
|
||||
<li><a href="#using-gdbserver-manually"
|
||||
id="toc-using-gdbserver-manually">Using <code>gdbserver</code>
|
||||
manually</a></li>
|
||||
<li><a href="#connecting-trace-rmi-manually"
|
||||
id="toc-connecting-trace-rmi-manually">Connecting Trace RMI
|
||||
manually</a></li>
|
||||
<li><a href="#rube-goldberg-configurations"
|
||||
id="toc-rube-goldberg-configurations">Rube Goldberg
|
||||
Configurations</a></li>
|
||||
@ -158,176 +160,193 @@ 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:</p>
|
||||
<ul>
|
||||
<li>Use <code>gdbserver</code></li>
|
||||
<li>Use SSH</li>
|
||||
<li>Use GADP</li>
|
||||
<li>Use a pty</li>
|
||||
<li>Use <code>gdbserver</code> over SSH</li>
|
||||
<li>Use Trace RMI over SSH</li>
|
||||
<li>Use <code>gdbserver</code> and connect to it manually</li>
|
||||
<li>Connect Trace RMI manually</li>
|
||||
</ul>
|
||||
<p>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.</p>
|
||||
</section>
|
||||
<section id="using-gdbserver" class="level2">
|
||||
<h2>Using <code>gdbserver</code></h2>
|
||||
<section id="using-gdbserver-over-ssh" class="level2">
|
||||
<h2>Using <code>gdbserver</code> over SSH</h2>
|
||||
<p>In this configuration, Ghidra and GDB will be located in the user’s
|
||||
local environment, while <code>gdbserver</code> 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:</p>
|
||||
<div class="sourceCode" id="cb1"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">gdbserver</span> 10.0.0.1:12345 termmines</span></code></pre></div>
|
||||
<p>Then, connect from Ghidra using GDB:</p>
|
||||
located in the target environment. We will connect the local
|
||||
<code>gdb</code> to the remote <code>gdbserver</code> by forwarding
|
||||
stdio over SSH.</p>
|
||||
<ol type="1">
|
||||
<li><p>From the Targets window, click Connect, select “gdb,” and click
|
||||
Connect.</p></li>
|
||||
<li><p>In the Interpreter, do as you would in GDB:</p>
|
||||
<div class="sourceCode" id="cb2"><pre
|
||||
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">target</span> <span class="kw">remote</span> 10.0.0.1:12345</span></code></pre></div></li>
|
||||
</ol>
|
||||
<p>The target should now be added to the Debugger session, and things
|
||||
should work as usual.</p>
|
||||
</section>
|
||||
<section id="using-ssh" class="level2">
|
||||
<h2>Using SSH</h2>
|
||||
<p>In this configuration, only Ghidra is required to be in the user’s
|
||||
local environment, while <code>sshd</code>, <code>gdb</code> and the
|
||||
specimen will be located in the target environment.
|
||||
<strong>NOTE</strong>: The full <code>gdb</code>, not just
|
||||
<code>gdbserver</code>, must be installed on the target system.</p>
|
||||
<ol type="1">
|
||||
<li><p>From the Targets window, click Connect, and select “gdb via
|
||||
SSH.”</p>
|
||||
<li><p>First, prepare the target, which for demonstration purposes has
|
||||
the IP address 10.0.0.1. Generally, this just means booting it up and
|
||||
ensuring it has <code>gdbserver</code> installed. <strong>NOTE</strong>:
|
||||
You do not need to run <code>gdbserver</code> or the target binary. The
|
||||
launcher will do that for you.</p></li>
|
||||
<li><p>From the launch menu, select <strong>gdb + gdbserver via
|
||||
ssh</strong>.</p>
|
||||
<figure>
|
||||
<img src="images/RemoteTargets_GdbOverSsh.png"
|
||||
<img src="images/RemoteTargets_GdbPlusGdbserverViaSsh.png"
|
||||
alt="Connect dialog for gdb + gdbserver via ssh" />
|
||||
<figcaption aria-hidden="true">Connect dialog for gdb + gdbserver via
|
||||
ssh</figcaption>
|
||||
</figure></li>
|
||||
<li><p>Read the wall of text, at least the first time, and verify the
|
||||
remote system is prepared.</p></li>
|
||||
<li><p>Fill out the options appropriately. Notably, correct the location
|
||||
of the target image to point at its location on the <em>target</em>
|
||||
system. Enter “user@10.0.0.1” for the <strong>[User@]Host</strong>
|
||||
option, substituting your username for the remote system.</p></li>
|
||||
<li><p>Click <strong>Launch</strong>.</p></li>
|
||||
</ol>
|
||||
<p>At this point, most things will work the same as they would for a
|
||||
local target.</p>
|
||||
</section>
|
||||
<section id="using-trace-rmi-over-ssh" class="level2">
|
||||
<h2>Using Trace RMI over SSH</h2>
|
||||
<p>In this configuration, Ghidra will be located in the user’ls local
|
||||
environment, while <code>gdb</code> and the specimen will be located in
|
||||
the target environment. Notice that we are <em>not</em> using
|
||||
<code>gdbserver</code>. We will connect the local Ghidra to the remote
|
||||
<code>gdb</code> by forwarding Trace RMI over SSH. See the help (press
|
||||
<strong><code>F1</code></strong> on the <strong>gdb via ssh</strong>
|
||||
menu item for advantages and disadvantages of using this
|
||||
vs. <code>gdbserver</code>.</p>
|
||||
<ol type="1">
|
||||
<li><p>First, prepare the target. This is more involved than using
|
||||
<code>gdbserver</code>, since you will need to ensure <code>gdb</code>
|
||||
and the Trace RMI plugin for it are installed. The packages, which
|
||||
should be included with Ghidra, are <code>ghidratrace</code> and
|
||||
<code>ghidragdb</code>. If you installed <code>gdb</code> and
|
||||
<code>python3</code> from your distribution’s repositories, installation
|
||||
of the Python packages should just be a matter of using
|
||||
<code>pip</code>:</p>
|
||||
<div class="sourceCode" id="cb1"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="ex">python3</span> <span class="at">-m</span> pip install /path/to/ghidratrace....whl</span></code></pre></div>
|
||||
<p>Chances are, GDB embeds the same Python, so they become importable
|
||||
from <code>gdb</code>:</p>
|
||||
<div class="sourceCode" id="cb2"><pre
|
||||
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>python import ghidragdb</span></code></pre></div>
|
||||
<p>You can quit GDB, since that was just for verifying the
|
||||
installation.</p></li>
|
||||
<li><p>From the launch menu, select <strong>gdb via ssh</strong>.</p>
|
||||
<figure>
|
||||
<img src="images/RemoteTargets_GdbViaSsh.png"
|
||||
alt="Connect dialog for gdb via SSH" />
|
||||
<figcaption aria-hidden="true">Connect dialog for gdb via
|
||||
SSH</figcaption>
|
||||
</figure></li>
|
||||
<li><p>Set “GDB launch command” to the path of gdb <em>on the remote
|
||||
file system</em>.</p></li>
|
||||
<li><p>Leave “Use existing session via new-ui” unchecked.</p></li>
|
||||
<li><p>Set “SSH hostname” to the name or IP address of the target
|
||||
system.</p></li>
|
||||
<li><p>If you are not using the standard SSH port, set “SSH TCP port”
|
||||
accordingly.</p></li>
|
||||
<li><p>Set “SSH username” to your username on the target
|
||||
system.</p></li>
|
||||
<li><p>Set “Open SSH config file” to the client config file <em>on the
|
||||
local file system</em>.</p></li>
|
||||
<li><p>If the remote uses DOS line endings (unlikely for a Linux
|
||||
remote), then check the “Use DOS line endings” box.</p></li>
|
||||
<li><p>Click Connect.</p></li>
|
||||
<li><p>If prompted, enter your SSH credentials.</p></li>
|
||||
<li><p>Fill out the options appropriately. Notably, correct the location
|
||||
of the target image to point at its location on the <em>target</em>
|
||||
system. Enter “user@10.0.0.1” for the <strong>[User@]Host</strong>
|
||||
option, substituting your username for the remote system.</p></li>
|
||||
<li><p>Click <strong>Launch</strong>.</p></li>
|
||||
</ol>
|
||||
<p>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 <em>on the remote file system</em>.</p>
|
||||
<p>The target should now be added to the Debugger session, and things
|
||||
should work as usual.</p>
|
||||
</section>
|
||||
<section id="using-gadp" class="level2">
|
||||
<h2>Using GADP</h2>
|
||||
<p>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 <em>agent</em>. The agent connects to
|
||||
the back-end as usual, but then opens a TCP socket and waits for Ghidra
|
||||
to connect.</p>
|
||||
<section id="using-gadp-locally" class="level3">
|
||||
<h3>Using GADP Locally</h3>
|
||||
<p>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.</p>
|
||||
</section>
|
||||
<section id="using-gadp-remotely" class="level3">
|
||||
<h3>Using GADP Remotely</h3>
|
||||
<p>In this configuration, only Ghidra is required to be in the user’s
|
||||
local environment. The target environment must have <code>gdb</code>,
|
||||
<code>java</code>, and some portion of Ghidra installed.</p>
|
||||
<p>If you can install Ghidra on the remote system, there is a script to
|
||||
launch the headless agent:</p>
|
||||
<p>At this point, most things will work the same as they would for a
|
||||
local target.</p>
|
||||
<section id="troubleshooting" class="level3">
|
||||
<h3>Troubleshooting</h3>
|
||||
<section id="i-cant-find-the-python-packages-to-install" class="level4">
|
||||
<h4>I can’t find the Python packages to install</h4>
|
||||
<p>These should be located in the Ghidra installation. Search for files
|
||||
ending in <code>.whl</code>. Alternatively, you can build the packages
|
||||
from source. The source is included with Ghidra. If you are able to do
|
||||
local debugging with Ghidra and <code>gdb</code>, then the source is
|
||||
definitely present and functioning.</p>
|
||||
<div class="sourceCode" id="cb3"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="bu">cd</span> /path/to/ghidra</span>
|
||||
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="ex">support/gdbGADPServerRun</span> <span class="at">-h</span></span></code></pre></div>
|
||||
<p>This should print help for you. Typically, you can just run the agent
|
||||
without any extra command-line arguments:</p>
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="ex">python3</span> <span class="at">-m</span> pip install build</span>
|
||||
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="bu">cd</span> /path/to/ghidra/Ghidra/Debug/Debugger-rmi-trace/pypkg</span>
|
||||
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="ex">python3</span> <span class="at">-m</span> build</span></code></pre></div>
|
||||
<p>This should output a <code>.whl</code> file. Send that over to the
|
||||
target system and install it. If that doesn’t work, then in the worst
|
||||
case, copy the Python source over and add it to your
|
||||
<code>PYTHONPATH</code>.</p>
|
||||
</section>
|
||||
<section id="the-python-import-ghidragdb-command-fails" class="level4">
|
||||
<h4>The <code>python import ghidragdb</code> command fails</h4>
|
||||
<p>Double-check that you have installed all the required packages and
|
||||
their dependencies. A common forgotten or incorrectly-versioned
|
||||
dependency is <code>protobuf</code>. We developed using
|
||||
<code>protobuf==3.20.3</code>.</p>
|
||||
<p>It is also possible that <code>gdb</code> has embedded a different
|
||||
version of the interpreter than the one that <code>python3</code>
|
||||
provides. This can happen if you built GDB or Python yourself, or you
|
||||
installed them from a non-standard repository. Check the actual path of
|
||||
the Python library used by <code>gdb</code>:</p>
|
||||
<div class="sourceCode" id="cb4"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ex">support/gdbGADPServerRun</span></span></code></pre></div>
|
||||
<p>If not, then you probably just need to tell it where you installed
|
||||
<code>gdb</code>:</p>
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">ldd</span> <span class="va">$(</span><span class="fu">which</span> gdb<span class="va">)</span></span></code></pre></div>
|
||||
<p>Or, inside <code>gdb</code>:</p>
|
||||
<div class="sourceCode" id="cb5"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ex">support/gdbGADPServerRun</span> <span class="at">--agent-args</span> <span class="at">-g</span> /path/to/bin/gdb</span></code></pre></div>
|
||||
<p>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.</p>
|
||||
<p>Refer to the root README file to get started with a build from
|
||||
source. You may stop short of the <code>gradle buildGhidra</code> step,
|
||||
though it may be helpful to avoid trouble. Then, build the executable
|
||||
jar for the GDB agent:</p>
|
||||
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>(gdb) python-interactive</span>
|
||||
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>>>> import sys</span>
|
||||
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>>>> sys.version</span></code></pre></div>
|
||||
<p>Suppose this identifies version 3.7. Retry the installation commands
|
||||
using <code>python3.7 -m pip ...</code>. If you have multiple copies of
|
||||
the same version in different locations, you may need to invoke
|
||||
<code>python3</code> using its complete path.</p>
|
||||
<p>In the worst case, copy the Python source over and add it to your
|
||||
<code>PYTHONPATH</code>.</p>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
<section id="using-gdbserver-manually" class="level2">
|
||||
<h2>Using <code>gdbserver</code> manually</h2>
|
||||
<p>The configuration and result here are similar using
|
||||
<code>gdbserver</code> over SSH, but will be performed manually.</p>
|
||||
<ol type="1">
|
||||
<li><p>First, prepare the target. This time, you will need to start
|
||||
<code>gdbserver</code> on the remote system manually. For demonstration,
|
||||
we will listen on 10.0.0.1 port 12345:</p>
|
||||
<div class="sourceCode" id="cb6"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ex">gradle</span> Debugger-agent-gdb:nodepJar</span></code></pre></div>
|
||||
<p>This will create the file
|
||||
<code>Ghidra/Debug/Debugger-agent-gdb/build/libs/Debugger-agent-gdb-nodep.jar</code>.
|
||||
Copy the file to the target system. Now, run it:</p>
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ex">gdbserver</span> 10.0.0.1:12345 termmines</span></code></pre></div></li>
|
||||
<li><p>From the launch menu, select <strong>remote
|
||||
gdb</strong>.</p></li>
|
||||
<li><p>Fill out the options appropriately. Notably, enter “10.0.0.1” for
|
||||
the <strong>Host</strong> option, and “12345” for the
|
||||
<strong>Port</strong> option.</p></li>
|
||||
<li><p>Click <strong>Launch</strong>.</p></li>
|
||||
</ol>
|
||||
<p>At this point, most things will work the same as they would for a
|
||||
local target.</p>
|
||||
</section>
|
||||
<section id="connecting-trace-rmi-manually" class="level2">
|
||||
<h2>Connecting Trace RMI manually</h2>
|
||||
<p>The configuration and result here are similar to using Trace RMI over
|
||||
SSH, but will be performed manually.</p>
|
||||
<ol type="1">
|
||||
<li><p>First, prepare the target. Follow the same installation steps as
|
||||
above for Trace RMI over SSH, if you have not already.</p></li>
|
||||
<li><p>In Ghidra’s Connections window, click <strong>Accept a single
|
||||
inbound TCP connection</strong> in the local toolbar.</p>
|
||||
<figure>
|
||||
<img src="images/RemoteTargets_AcceptTraceRmi.png"
|
||||
alt="TraceRMI Accept Dialog" />
|
||||
<figcaption aria-hidden="true">TraceRMI Accept Dialog</figcaption>
|
||||
</figure></li>
|
||||
<li><p>Set <strong>Host/Address</strong> to “10.0.0.1”, so that we can
|
||||
connect to it over the network. <strong>NOTE</strong>: You may leave the
|
||||
port as “0” or pick a specific port, assuming you have permission to use
|
||||
it.</p></li>
|
||||
<li><p>Click <strong>Listen</strong>, and then take note of the
|
||||
acceptor’s port number in the Connections window, e.g.,
|
||||
“12345.”</p></li>
|
||||
<li><p>Now, on the remote system, start <code>gdb</code> and type:</p>
|
||||
<div class="sourceCode" id="cb7"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="ex">java</span> <span class="at">-jar</span> Debugger-agent-gdb-nodep.jar <span class="at">-h</span></span></code></pre></div>
|
||||
<p>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.</p>
|
||||
<ol type="1">
|
||||
<li>From the Targets window, click Connect.</li>
|
||||
<li>Select “Ghidra debug agent (GADP)” from the drop-down.</li>
|
||||
<li>For “Agent network address”, enter 10.0.0.2.</li>
|
||||
<li>For “Agent TCP port”, enter 15432.</li>
|
||||
<li>Click Connect.</li>
|
||||
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>python import ghidragdb</span>
|
||||
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>file termmines</span>
|
||||
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a># set args, if you'd like</span>
|
||||
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>ghidra trace connect 10.0.0.1:12345</span>
|
||||
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>ghidra trace start</span>
|
||||
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>ghidra trace sync-enable</span>
|
||||
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>starti</span></code></pre></div></li>
|
||||
</ol>
|
||||
<p>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.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="using-a-pty-pseudo-terminal" class="level2">
|
||||
<h2>Using a pty (pseudo-terminal)</h2>
|
||||
<p>If your copy of GDB supports the <code>new-ui</code> 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:</p>
|
||||
<ol type="1">
|
||||
<li><p>Run <code>gdb</code> from a proper terminal:</p>
|
||||
<div class="sourceCode" id="cb8"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="fu">gdb</span> termmines</span></code></pre></div></li>
|
||||
<li><p>If needed, do whatever you would like to do before connecting
|
||||
with Ghidra.</p></li>
|
||||
<li><p>In Ghidra, from the Targets window, click Connect, and select
|
||||
<code>gdb</code>.</p></li>
|
||||
<li><p>Check the “Use existing session via new-ui” box.</p></li>
|
||||
<li><p>Click Connect.</p></li>
|
||||
<li><p>You will be prompted with the name of a pseudo terminal, e.g.,
|
||||
<code>/dev/pts/1</code>.</p></li>
|
||||
<li><p>Back in <code>gdb</code>:</p>
|
||||
<div class="sourceCode" id="cb9"><pre
|
||||
class="sourceCode gdb"><code class="sourceCode gdbsyntax"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">new-ui</span> /dev/pts/1</span></code></pre></div></li>
|
||||
</ol>
|
||||
<p>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.</p>
|
||||
<p>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 <em>remote file system</em>.</p>
|
||||
<p>To activate this configuration in the standalone GADP agent, use the
|
||||
<code>-x</code> option:</p>
|
||||
<div class="sourceCode" id="cb10"><pre
|
||||
class="sourceCode bash"><code class="sourceCode bash"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ex">java</span> <span class="at">-jar</span> Debugger-agent-gdb-node.jar <span class="at">--agent-args</span> <span class="at">-x</span></span></code></pre></div>
|
||||
<p>At this point, most things will work the same as they would for a
|
||||
local target. You may notice Ghidra has not given you a new terminal.
|
||||
Just use the one you already have on the remote target.</p>
|
||||
<p>A notable advantage of this configuration is that you can enter
|
||||
whatever <code>gdb</code> commands you want to start your target. Here
|
||||
we demonstrated the simplest case of a “native” target. It is also
|
||||
possible to use this procedure to connect Ghidra into a running
|
||||
<code>gdb</code> session.</p>
|
||||
</section>
|
||||
<section id="rube-goldberg-configurations" class="level2">
|
||||
<h2>Rube Goldberg Configurations</h2>
|
||||
@ -342,10 +361,10 @@ Android emulator.</p>
|
||||
<h2>Exercise: Debug your Friend’s <code>termmines</code></h2>
|
||||
<p>If you are in a classroom setting, pair up. Otherwise, play both
|
||||
roles, preferably using separate machines for Ghidra and the target.
|
||||
Using either <code>gdbserver</code>, gdb via SSH, or the GDB agent,
|
||||
debug <code>termmines</code>. 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.</p>
|
||||
Using one of the above procedures, debug <code>termmines</code>. One of
|
||||
you should prepare the target environment. The other should connect to
|
||||
it and launch the specimen. Then trade roles, choose a different
|
||||
procedure, and do it again.</p>
|
||||
</section>
|
||||
</section>
|
||||
</body>
|
||||
|
@ -20,166 +20,169 @@ Depending on your particular target and platform, there may be several options a
|
||||
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
|
||||
* Use `gdbserver` over SSH
|
||||
* Use Trace RMI over SSH
|
||||
* Use `gdbserver` and connect to it manually
|
||||
* Connect Trace RMI manually
|
||||
|
||||
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`
|
||||
## Using `gdbserver` over SSH
|
||||
|
||||
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:
|
||||
We will connect the local `gdb` to the remote `gdbserver` by forwarding stdio over SSH.
|
||||
|
||||
```bash
|
||||
gdbserver 10.0.0.1:12345 termmines
|
||||
```
|
||||
1. First, prepare the target, which for demonstration purposes has the IP address 10.0.0.1.
|
||||
Generally, this just means booting it up and ensuring it has `gdbserver` installed.
|
||||
**NOTE**: You do not need to run `gdbserver` or the target binary.
|
||||
The launcher will do that for you.
|
||||
1. From the launch menu, select **gdb + gdbserver via ssh**.
|
||||
|
||||
Then, connect from Ghidra using GDB:
|
||||
![Connect dialog for gdb + gdbserver via ssh](images/RemoteTargets_GdbPlusGdbserverViaSsh.png)
|
||||
|
||||
1. From the Targets window, click Connect, select "gdb," and click Connect.
|
||||
1. In the Interpreter, do as you would in GDB:
|
||||
1. Read the wall of text, at least the first time, and verify the remote system is prepared.
|
||||
1. Fill out the options appropriately.
|
||||
Notably, correct the location of the target image to point at its location on the *target* system.
|
||||
Enter "user@10.0.0.1" for the **[User@]Host** option, substituting your username for the remote system.
|
||||
1. Click **Launch**.
|
||||
|
||||
```gdb
|
||||
target remote 10.0.0.1:12345
|
||||
```
|
||||
At this point, most things will work the same as they would for a local target.
|
||||
|
||||
The target should now be added to the Debugger session, and things should work as usual.
|
||||
## Using Trace RMI over SSH
|
||||
|
||||
## Using SSH
|
||||
In this configuration, Ghidra will be located in the user'ls local environment, while `gdb` and the specimen will be located in the target environment.
|
||||
Notice that we are *not* using `gdbserver`.
|
||||
We will connect the local Ghidra to the remote `gdb` by forwarding Trace RMI over SSH.
|
||||
See the help (press **`F1`** on the **gdb via ssh** menu item for advantages and disadvantages of using this vs. `gdbserver`.
|
||||
|
||||
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:
|
||||
1. First, prepare the target.
|
||||
This is more involved than using `gdbserver`, since you will need to ensure `gdb` and the Trace RMI plugin for it are installed.
|
||||
The packages, which should be included with Ghidra, are `ghidratrace` and `ghidragdb`.
|
||||
If you installed `gdb` and `python3` from your distribution's repositories, installation of the Python packages should just be a matter of using `pip`:
|
||||
|
||||
```bash
|
||||
gdb termmines
|
||||
python3 -m pip install /path/to/ghidratrace....whl
|
||||
```
|
||||
|
||||
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`:
|
||||
Chances are, GDB embeds the same Python, so they become importable from `gdb`:
|
||||
|
||||
```gdb
|
||||
new-ui /dev/pts/1
|
||||
python import ghidragdb
|
||||
```
|
||||
|
||||
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.
|
||||
You can quit GDB, since that was just for verifying the installation.
|
||||
|
||||
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*.
|
||||
1. From the launch menu, select **gdb via ssh**.
|
||||
|
||||
To activate this configuration in the standalone GADP agent, use the `-x` option:
|
||||
![Connect dialog for gdb via SSH](images/RemoteTargets_GdbViaSsh.png)
|
||||
|
||||
1. Fill out the options appropriately.
|
||||
Notably, correct the location of the target image to point at its location on the *target* system.
|
||||
Enter "user@10.0.0.1" for the **[User@]Host** option, substituting your username for the remote system.
|
||||
1. Click **Launch**.
|
||||
|
||||
At this point, most things will work the same as they would for a local target.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
#### I can't find the Python packages to install
|
||||
|
||||
These should be located in the Ghidra installation.
|
||||
Search for files ending in `.whl`.
|
||||
Alternatively, you can build the packages from source.
|
||||
The source is included with Ghidra.
|
||||
If you are able to do local debugging with Ghidra and `gdb`, then the source is definitely present and functioning.
|
||||
|
||||
```bash
|
||||
java -jar Debugger-agent-gdb-node.jar --agent-args -x
|
||||
python3 -m pip install build
|
||||
cd /path/to/ghidra/Ghidra/Debug/Debugger-rmi-trace/pypkg
|
||||
python3 -m build
|
||||
```
|
||||
|
||||
This should output a `.whl` file.
|
||||
Send that over to the target system and install it.
|
||||
If that doesn't work, then in the worst case, copy the Python source over and add it to your `PYTHONPATH`.
|
||||
|
||||
#### The `python import ghidragdb` command fails
|
||||
|
||||
Double-check that you have installed all the required packages and their dependencies.
|
||||
A common forgotten or incorrectly-versioned dependency is `protobuf`.
|
||||
We developed using `protobuf==3.20.3`.
|
||||
|
||||
It is also possible that `gdb` has embedded a different version of the interpreter than the one that `python3` provides.
|
||||
This can happen if you built GDB or Python yourself, or you installed them from a non-standard repository.
|
||||
Check the actual path of the Python library used by `gdb`:
|
||||
|
||||
```bash
|
||||
ldd $(which gdb)
|
||||
```
|
||||
|
||||
Or, inside `gdb`:
|
||||
|
||||
```gdb
|
||||
(gdb) python-interactive
|
||||
>>> import sys
|
||||
>>> sys.version
|
||||
```
|
||||
|
||||
Suppose this identifies version 3.7.
|
||||
Retry the installation commands using `python3.7 -m pip ...`.
|
||||
If you have multiple copies of the same version in different locations, you may need to invoke `python3` using its complete path.
|
||||
|
||||
In the worst case, copy the Python source over and add it to your `PYTHONPATH`.
|
||||
|
||||
## Using `gdbserver` manually
|
||||
|
||||
The configuration and result here are similar using `gdbserver` over SSH, but will be performed manually.
|
||||
|
||||
1. First, prepare the target.
|
||||
This time, you will need to start `gdbserver` on the remote system manually.
|
||||
For demonstration, we will listen on 10.0.0.1 port 12345:
|
||||
|
||||
```bash
|
||||
gdbserver 10.0.0.1:12345 termmines
|
||||
```
|
||||
|
||||
1. From the launch menu, select **remote gdb**.
|
||||
1. Fill out the options appropriately.
|
||||
Notably, enter "10.0.0.1" for the **Host** option, and "12345" for the **Port** option.
|
||||
1. Click **Launch**.
|
||||
|
||||
At this point, most things will work the same as they would for a local target.
|
||||
|
||||
## Connecting Trace RMI manually
|
||||
|
||||
The configuration and result here are similar to using Trace RMI over SSH, but will be performed manually.
|
||||
|
||||
1. First, prepare the target.
|
||||
Follow the same installation steps as above for Trace RMI over SSH, if you have not already.
|
||||
1. In Ghidra's Connections window, click **Accept a single inbound TCP connection** in the local toolbar.
|
||||
|
||||
![TraceRMI Accept Dialog](images/RemoteTargets_AcceptTraceRmi.png)
|
||||
|
||||
1. Set **Host/Address** to "10.0.0.1", so that we can connect to it over the network.
|
||||
**NOTE**: You may leave the port as "0" or pick a specific port, assuming you have permission to use it.
|
||||
1. Click **Listen**, and then take note of the acceptor's port number in the Connections window, e.g., "12345."
|
||||
1. Now, on the remote system, start `gdb` and type:
|
||||
|
||||
```gdb
|
||||
python import ghidragdb
|
||||
file termmines
|
||||
# set args, if you'd like
|
||||
ghidra trace connect 10.0.0.1:12345
|
||||
ghidra trace start
|
||||
ghidra trace sync-enable
|
||||
starti
|
||||
```
|
||||
|
||||
At this point, most things will work the same as they would for a local target.
|
||||
You may notice Ghidra has not given you a new terminal.
|
||||
Just use the one you already have on the remote target.
|
||||
|
||||
A notable advantage of this configuration is that you can enter whatever `gdb` commands you want to start your target.
|
||||
Here we demonstrated the simplest case of a "native" target.
|
||||
It is also possible to use this procedure to connect Ghidra into a running `gdb` session.
|
||||
|
||||
## Rube Goldberg Configurations
|
||||
|
||||
While you should always prefer the simpler configuration, it is possible to combine components to meet a variety of needs.
|
||||
@ -189,7 +192,7 @@ For example, to debug a native Android target from Windows, you could run Ghidra
|
||||
|
||||
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`.
|
||||
Using one of the above procedures, 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.
|
||||
Then trade roles, choose a different procedure, and do it again.
|
||||
|
@ -203,12 +203,14 @@ argument parsing function. It should be the first function called by
|
||||
<code>main</code>.</li>
|
||||
<li>Use a breakpoint to interrupt the live target when it enters this
|
||||
function.</li>
|
||||
<li>Change the “Control mode” drop-down to “Control Emulator.”</li>
|
||||
<li>Click <img src="images/stepinto.png" alt="step into button" /> Step
|
||||
Into to step the emulator forward.</li>
|
||||
<li>Click <img src="images/skipover.png" alt="skip over button" /> Skip
|
||||
Over and <img src="images/stepback.png" alt="step back button" /> Step
|
||||
Back to experiment with different execution paths.</li>
|
||||
<li>Change the <strong>Control mode</strong> drop-down to
|
||||
<strong>Control Emulator</strong>.</li>
|
||||
<li>Click <img src="images/stepinto.png" alt="step into button" />
|
||||
<strong>Step Into</strong> to step the emulator forward.</li>
|
||||
<li>Click <img src="images/skipover.png" alt="skip over button" />
|
||||
<strong>Skip Over</strong> and <img src="images/stepback.png"
|
||||
alt="step back button" /> <strong>Step Back</strong> to experiment with
|
||||
different execution paths.</li>
|
||||
</ol>
|
||||
<p>About those two new actions:</p>
|
||||
<ul>
|
||||
@ -219,27 +221,27 @@ instruction.</li>
|
||||
<strong>Step Back</strong>: Step the current thread backward one
|
||||
instruction, or undo an emulated skip or patch.</li>
|
||||
</ul>
|
||||
<p>Try to get the program counter onto the call to <code>exit(-1)</code>
|
||||
using only those three step buttons.</p>
|
||||
<p><strong>Quick Exercise</strong>: Try to get the program counter onto
|
||||
the call to <code>exit(-1)</code> using only those three step
|
||||
buttons.</p>
|
||||
<p>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. <strong>NOTE</strong>: 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.</p>
|
||||
it were the live target. The main exception is the Terminal window. It
|
||||
always displays the state of the live target, as it is unaware of the
|
||||
emulator. You can make changes to the emulator’s machine state, set
|
||||
breakpoints, etc., just as you would in <strong>Control Target</strong>
|
||||
mode. <strong>NOTE</strong>: You may see Ghidra interact with the
|
||||
target, despite being in <strong>Control Emulator</strong> 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.</p>
|
||||
<section id="stepping-schedules" class="level3">
|
||||
<h3>Stepping Schedules</h3>
|
||||
<p>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
|
||||
<em>time</em> element of the Debugger’s “coordinates.” (See the <a
|
||||
href="A5-Navigation.html">Navigation</a> module if you need a
|
||||
<em>time</em> element of the Debugger’s <em>coordinates</em>. (See the
|
||||
<a href="A5-Navigation.html">Navigation</a> module if you need a
|
||||
refresher.) The time element, called the <em>schedule</em>, 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
|
||||
@ -282,17 +284,19 @@ with 0x1234, then step 10 instructions.</li>
|
||||
<p>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
|
||||
<strong>Ctrl-G</strong> or using <strong>Debugger → Go To Time</strong>
|
||||
in the menus.</p>
|
||||
<strong><code>CTRL</code>-<code>G</code></strong> or using
|
||||
<strong>Debugger → Go To Time</strong> in the menus.</p>
|
||||
<p>Assuming you got the program counter onto <code>exit(-1)</code>
|
||||
earlier:</p>
|
||||
<ol type="1">
|
||||
<li>Write down the current schedule.</li>
|
||||
<li>Change back to “Control Target” mode. Ghidra will navigate back to
|
||||
the current snapshot, so PC will match the live target.</li>
|
||||
<li>Press <strong>Ctrl-G</strong> and type or paste the schedule in, and
|
||||
click OK. The program counter should be restored to
|
||||
<code>exit(-1)</code>.</li>
|
||||
<li>Change back to <strong>Control Target</strong> mode. Ghidra will
|
||||
navigate back to the current snapshot, so PC will match the live
|
||||
target.</li>
|
||||
<li>Change back (again) to <strong>Control Emulator</strong> mode.</li>
|
||||
<li>Press <strong><code>CTRL</code>-<code>G</code></strong> and type or
|
||||
paste the schedule in, and click <strong>OK</strong>. The program
|
||||
counter should be restored to <code>exit(-1)</code>.</li>
|
||||
</ol>
|
||||
<p><strong>NOTE</strong>: The thread IDs used in schedules are internal
|
||||
to the current trace database. Most likely, they <em>do not</em>
|
||||
@ -306,12 +310,12 @@ cells with mines. In this exercise, you will use extrapolation to
|
||||
experiment and devise a patch to demonstrate all possible counts of
|
||||
neighboring mines:</p>
|
||||
<ol type="1">
|
||||
<li>Run <code>termmines</code> in a proper terminal and attach to
|
||||
it.</li>
|
||||
<li>Launch <code>termmines</code> using GDB with <strong>Inferior
|
||||
TTY</strong> enabled.</li>
|
||||
<li>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
|
||||
<strong>Shift-R</strong> in <code>termmines</code> to reset the
|
||||
game.)</li>
|
||||
<strong><code>SHIFT</code>-<code>R</code></strong> in
|
||||
<code>termmines</code> to reset the game.)</li>
|
||||
<li>Use the emulator to extrapolate forward and begin understanding how
|
||||
the algorithm works.</li>
|
||||
<li>Move the mines by patching the board to demonstrate every number of
|
||||
@ -384,56 +388,56 @@ listing following the program counter, then you are probably dealing
|
||||
with self-modifying code.</p>
|
||||
<p><strong>NOTE</strong>: If you prefer to see the Dynamic listing
|
||||
initialized with the program image, you may select <strong>Load Emulator
|
||||
from Program</strong> 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!</p>
|
||||
from Program</strong> from the <strong>Auto-Read</strong> 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!</p>
|
||||
<p>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 <strong>Ctrl-G</strong> to go to snapshot 0. This
|
||||
method is not as clean as the first, because the trace will retain its
|
||||
scratch snapshots.</p>
|
||||
<p>Press <img src="images/resume.png" alt="resume button" /> Resume to
|
||||
let the emulator run until it crashes. It should crash pretty quickly
|
||||
and without much ceremony:</p>
|
||||
end the emulation session and restart it. To end the session, close the
|
||||
“Emulate termmines” tab in the Dynamic Listing window. You can then
|
||||
restart by right-clicking the first instruction as before. Second, you
|
||||
can use <strong><code>CTRL</code>-<code>G</code></strong> to go to
|
||||
snapshot 0. This method is not as clean as the first, because the trace
|
||||
will retain its scratch snapshots.</p>
|
||||
<p>Press <img src="images/resume.png" alt="resume button" />
|
||||
<strong>Resume</strong> to let the emulator run until it crashes. It
|
||||
should crash pretty quickly and without much ceremony:</p>
|
||||
<figure>
|
||||
<img src="images/Emulation_ListingAfterResume.png"
|
||||
alt="Listing after crashing" />
|
||||
<figcaption aria-hidden="true">Listing after crashing</figcaption>
|
||||
</figure>
|
||||
<p>In this case, the clearest indication that something has gone wrong
|
||||
is in the top-right of the Dynamic listing. Recall that the location
|
||||
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 <img
|
||||
src="images/stepinto.png" alt="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 <em>execute</em> uninitialized memory, it will crash. Most
|
||||
likely, the target called an external function, causing the program
|
||||
counter to land in the fake <code>EXTERNAL</code> block.</p>
|
||||
src="images/stepinto.png" alt="step into button" /> <strong>Step
|
||||
Into</strong>. 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 <em>execute</em> uninitialized memory, it
|
||||
will crash. Most likely, the target called an external function, causing
|
||||
the program counter to land in the fake <code>EXTERNAL</code> block.</p>
|
||||
<p>To diagnose the crash, press <img src="images/stepback.png"
|
||||
alt="step back button" /> 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 <code>printf</code>. You can continue stepping
|
||||
back until you find the decision point that took us down this path. You
|
||||
should notice it was because <code>param_1</code> 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
|
||||
<code>JZ</code> (or other conditional jump) is too late; you need to
|
||||
step back to the <code>TEST EDI,EDI</code> (or similar) instruction.
|
||||
(This may, ironically, be the first instruction of the function.) In the
|
||||
System V AMD64 ABI (Linux x86-64 calling conventions) <code>RDI</code>
|
||||
is used to pass the first parameter. You can hover your mouse over
|
||||
<code>param_1</code> in the Decompiler, and it will tell you the
|
||||
location is <code>EDI:4</code>, and that its current value is a stale
|
||||
0.</p>
|
||||
alt="step back button" /> <strong>Step Back</strong>. After a couple
|
||||
steps back, you should be able to confirm our hypothesis: we got here
|
||||
through a call to the external function <code>printf</code>. You can
|
||||
continue stepping back until you find the decision point that took us
|
||||
down this path. You should notice it was because <code>param_1</code>
|
||||
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 <code>JZ</code> (or other conditional jump) is too
|
||||
late; you need to step back to the <code>TEST EDI,EDI</code> (or
|
||||
similar) instruction. (This may, ironically, be the first instruction of
|
||||
the function.) In the System V AMD64 ABI (Linux x86-64 calling
|
||||
conventions) <code>RDI</code> is used to pass the first parameter. You
|
||||
can hover your mouse over <code>param_1</code> in the Decompiler, and it
|
||||
will tell you the location is <code>EDI:4</code>, and that its current
|
||||
value is a stale 0.</p>
|
||||
<section id="initializing-other-state" class="level3">
|
||||
<h3>Initializing Other State</h3>
|
||||
<p>We had just started executing the target function arbitrarily. Ghidra
|
||||
@ -465,13 +469,14 @@ remember to invalidate the emulator cache any time you change the
|
||||
initial state. For this tutorial, we will perform the patches in the
|
||||
emulator.</p>
|
||||
<p><strong>NOTE</strong>: 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.</p>
|
||||
change to <strong>Control Trace</strong> mode and use the
|
||||
<strong>Navigate backward one snapshot</strong> 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 <strong>Control Trace</strong> is
|
||||
patching a scratch snapshot.</p>
|
||||
<p>Now, we will manually “allocate” memory for <code>argv</code>.
|
||||
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
|
||||
@ -480,7 +485,7 @@ use the <strong>Add Region</strong> 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.,
|
||||
<code>00001000</code>. We will use the Watches window to perform our
|
||||
patching, though we will also use the Dynamic listing to double check.
|
||||
patching, though we will also use the Dynamic Listing to double check.
|
||||
Add the following watches:</p>
|
||||
<ul>
|
||||
<li><code>RSP</code> — to confirm the stack pointer is far from
|
||||
@ -503,15 +508,15 @@ possible we may only need to initialize <code>argc</code>, since the
|
||||
parser may not actually <em>use</em> the value of
|
||||
<code>argv[0]</code>.</p>
|
||||
<p>Use the Watches window to set <code>RDI</code> to 1, then click <img
|
||||
src="images/resume.png" alt="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 <code>0:t0-{RDI=0x1);t0-16</code>. 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
|
||||
<code>00000000</code>. Step backward once to confirm this
|
||||
hypothesis.</p>
|
||||
src="images/resume.png" alt="resume button" /> <strong>Resume</strong>.
|
||||
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
|
||||
<code>0:t0-{RDI=0x1);t0-16</code>. 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 <code>00000000</code>. Step backward
|
||||
once to confirm this hypothesis.</p>
|
||||
</section>
|
||||
<section id="stubbing-external-calls" class="level3">
|
||||
<h3>Stubbing External Calls</h3>
|
||||
@ -520,8 +525,8 @@ 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 <code>strnlen</code> and <code>strcmp</code>. We will need to
|
||||
pass in <code>termmines -s Advanced</code>, which is three arguments.
|
||||
Use <strong>Ctrl-G</strong> to go back to snapshot 0, and add the
|
||||
following watches:</p>
|
||||
Use <strong><code>CTRL</code>-<code>G</code></strong> to go back to
|
||||
snapshot 0, and add the following watches:</p>
|
||||
<ul>
|
||||
<li><code>*:8 (RSI + 0)</code> — the address of the first argument,
|
||||
i.e., <code>argv[0]</code>.</li>
|
||||
@ -552,20 +557,21 @@ is at the upper end of the stack region, so we allocate
|
||||
<code>argv</code> at <code>00001000</code>. To do that, set the value of
|
||||
<code>RSI</code> to <code>0x1000</code>. 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.</p>
|
||||
addresses to go there in the Dynamic Listing.</p>
|
||||
<p><strong>NOTE</strong>: You <em>do not have</em> 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.</p>
|
||||
Listing, it is easiest if you allocate them in a listed region.</p>
|
||||
<p>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
|
||||
<code>argv</code>, we should place a data unit in the Dynamic listing.
|
||||
Double-click the Address <code>00001000</code> in the Watches window to
|
||||
go to that address in the Dynamic listing. Press <strong>P</strong> then
|
||||
<strong>[</strong> (left square bracket) to place a 3-pointer array at
|
||||
that address. We can now see the next available byte is at
|
||||
<code>00001018</code>. <strong>NOTE</strong>: You might set the Dynamic
|
||||
listing to <strong>Do Not Track</strong>, otherwise it may seek back to
|
||||
the PC every time you patch.</p>
|
||||
go to that address in the Dynamic Listing. Press
|
||||
<strong><code>P</code></strong> then <strong><code>[</code></strong>
|
||||
(left square bracket) to place a 3-pointer array at that address. We can
|
||||
now see the next available byte is at <code>00001018</code>.
|
||||
<strong>NOTE</strong>: You might set the Dynamic Listing to <strong>Do
|
||||
Not Track</strong>, otherwise it may seek back to the PC every time you
|
||||
patch.</p>
|
||||
<p>Now that we know where to put <code>argv[0]</code>, we need to patch
|
||||
it to <code>0x0001018</code>. This should be the watch on
|
||||
<code>*:8 (RSI + 0)</code>. When you modify the Value column, you can
|
||||
@ -574,19 +580,20 @@ type either bytes (in little-endian order for x86) or the integer value
|
||||
<code>*:30 (*:8 (RSI + 0))</code> to get the address
|
||||
<code>00001018</code>. Using the Repr column, set that watch’s value to
|
||||
<code>"termmines"</code>. (The quotes are required.) Place a string in
|
||||
the Dynamic listing using the <strong>’</strong> (apostrophe) key. This
|
||||
shows us the next available address is <code>00001022</code>, so repeat
|
||||
the process to allocate <code>argv[1]</code> and set it to
|
||||
<code>"-s"</code>. Then finally, allocate <code>argv[2]</code> and set
|
||||
it to <code>"Advanced"</code>. When you have finished, the Watches pane
|
||||
should look something like this:</p>
|
||||
the Dynamic Listing using the <strong><code>'</code></strong>
|
||||
(apostrophe) key. This shows us the next available address is
|
||||
<code>00001022</code>, so repeat the process to allocate
|
||||
<code>argv[1]</code> and set it to <code>"-s"</code>. Then finally,
|
||||
allocate <code>argv[2]</code> and set it to <code>"Advanced"</code>.
|
||||
When you have finished, the Watches pane should look something like
|
||||
this:</p>
|
||||
<figure>
|
||||
<img src="images/Emulation_WatchesForCmdlineSet.png"
|
||||
alt="Watches for patching command-line arguments after setting" />
|
||||
<figcaption aria-hidden="true">Watches for patching command-line
|
||||
arguments after setting</figcaption>
|
||||
</figure>
|
||||
<p>The Dynamic listing should look something like this:</p>
|
||||
<p>The Dynamic Listing should look something like this:</p>
|
||||
<figure>
|
||||
<img src="images/Emulation_ListingForCmdlineSet.png"
|
||||
alt="Listing after setting command-line arguments" />
|
||||
@ -598,12 +605,12 @@ for the emulator to operate; it only cares about the bytes. However, it
|
||||
is a useful aide in devising, understanding, and diagnosing machine
|
||||
state.</p>
|
||||
<p>Now, click <img src="images/resume.png" alt="resume button" />
|
||||
Resume, and see where the emulator crashes next. Depending on your
|
||||
compilation of <code>termmines</code>, it may crash after returning, or
|
||||
it may crash trying to call <code>strnlen</code> or <code>strcmp</code>.
|
||||
If the program counter is <code>00000000</code>, then it returned
|
||||
successfully. This is unfortunate, because you no longer have motivation
|
||||
to stub external calls.</p>
|
||||
<strong>Resume</strong>, and see where the emulator crashes next.
|
||||
Depending on your compilation of <code>termmines</code>, it may crash
|
||||
after returning, or it may crash trying to call <code>strnlen</code> or
|
||||
<code>strcmp</code>. If the program counter is <code>00000000</code>,
|
||||
then it returned successfully. This is unfortunate, because you no
|
||||
longer have motivation to stub external calls.</p>
|
||||
<p>If the program counter is not <code>00000000</code>, then step
|
||||
backward until you get to the <code>CALL</code>. There are at least
|
||||
three techniques for overcoming this.</p>
|
||||
@ -618,9 +625,10 @@ breakpoint.</li>
|
||||
<h4>Skip Technique</h4>
|
||||
<p>The skip technique is simplest, but will need to be performed
|
||||
<em>every time</em> that call is encountered. Press <img
|
||||
src="images/skipover.png" alt="skip over button" /> Skip Over, then use
|
||||
the Registers or Watches pane to patch <code>RAX</code>. Then press <img
|
||||
src="images/resume.png" alt="resume button" /> Resume.</p>
|
||||
src="images/skipover.png" alt="skip over button" /> <strong>Skip
|
||||
Over</strong>, then use the Registers or Watches pane to patch
|
||||
<code>RAX</code>. Then press <img src="images/resume.png"
|
||||
alt="resume button" /> <strong>Resume</strong>.</p>
|
||||
</section>
|
||||
<section id="call-override-technique" class="level4">
|
||||
<h4><code>CALL</code> Override Technique</h4>
|
||||
@ -628,8 +636,8 @@ src="images/resume.png" alt="resume button" /> Resume.</p>
|
||||
will handle every encounter, it will not handle other calls to the same
|
||||
external function.</p>
|
||||
<ol type="1">
|
||||
<li>Press <strong>K</strong> in the listing to place a breakpoint on the
|
||||
<code>CALL</code> instruction.</li>
|
||||
<li>Press <strong><code>K</code></strong> in the listing to place a
|
||||
breakpoint on the <code>CALL</code> instruction.</li>
|
||||
<li>Now, in the Breakpoints panel, right-click the new breakpoint and
|
||||
select <strong>Set Injection (Emulator)</strong>.</li>
|
||||
<li>This is the fun part: you must now implement the function in Sleigh,
|
||||
@ -677,11 +685,11 @@ allows you to maintain breakpoint behavior, perhaps to debug your
|
||||
injection.</p>
|
||||
<p>After you have written your Sleigh code:</p>
|
||||
<ol type="1">
|
||||
<li>Click OK on the Set Injection dialog.</li>
|
||||
<li>Click <strong>OK</strong> on the Set Injection dialog.</li>
|
||||
<li>In the menus, select <strong>Debugger → Configure Emulator →
|
||||
Invalidate Emulator Cache</strong>.</li>
|
||||
<li>Click <img src="images/resume.png" alt="resume button" />
|
||||
Resume.</li>
|
||||
<strong>Resume</strong>.</li>
|
||||
</ol>
|
||||
<p>Stubbing any remaining external calls is left as an exercise. You are
|
||||
successful when the emulator crashes with
|
||||
@ -695,7 +703,7 @@ again before proceeding to the next technique.</p>
|
||||
involved. It will handle all calls to the external function, e.g.,
|
||||
<code>strnlen</code>, 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
|
||||
target will be visible in the Dynamic Listing. The PLT entry usually
|
||||
contains a single <code>JMP</code> instruction to the actual
|
||||
<code>strnlen</code>. For real target processes, the <code>JMP</code>
|
||||
instruction will transfer control to a lazy linker the first time
|
||||
@ -703,7 +711,7 @@ instruction will transfer control to a lazy linker the first time
|
||||
then finds <code>strnlen</code> and patches the table. In contrast, the
|
||||
Ghidra loader immediately patches the table to point to a fake
|
||||
<code><EXTERNAL>::strnlen</code> symbol. The <code>EXTERNAL</code>
|
||||
block is not visible in the Dynamic listing, so we will override the
|
||||
block is not visible in the Dynamic Listing, so we will override the
|
||||
<code>JMP</code> in the PLT.</p>
|
||||
<p>The Sleigh code is nearly identical, but we must code an x86
|
||||
<code>RET</code> into it. Because we allow the <code>CALL</code> to
|
||||
@ -736,10 +744,10 @@ 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.</p>
|
||||
<p>In the Static listing, navigate to the variable that stores the
|
||||
<p>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
|
||||
analysis.) In the Dynamic Listing, you should notice that the values
|
||||
have changed to reflect the Advanced skill level.</p>
|
||||
</section>
|
||||
<section id="optional-exercise-patch-the-placement-algorithm"
|
||||
@ -748,11 +756,12 @@ class="level3">
|
||||
<p>In this exercise, you will use emulation to devise an assembly patch
|
||||
to <code>termmines</code> 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.</p>
|
||||
to bottom. We recommend you devise your patch using the Assembler
|
||||
(<strong>Patch Instruction</strong> 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 outside
|
||||
of Ghidra.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="debugging-p-code-semantics" class="level2">
|
||||
@ -769,11 +778,11 @@ tool, so it must be configured:</p>
|
||||
<ol type="1">
|
||||
<li>If you have not already, open the Debugger tool.</li>
|
||||
<li>In the menus, select <strong>File → Configure</strong>.</li>
|
||||
<li>Click the “Configure All Plugins” button in the top right of the
|
||||
dialog.</li>
|
||||
<li>Click the <strong>Configure All Plugins</strong> button in the top
|
||||
right of the dialog.</li>
|
||||
<li>Activate the <code>DebuggerPcodeStepperPlugin</code></li>
|
||||
<li>Click OK</li>
|
||||
<li>Click Close</li>
|
||||
<li>Click <strong>OK</strong></li>
|
||||
<li>Click <strong>Close</strong></li>
|
||||
</ol>
|
||||
<p>The stepper should appear stacked over the Threads panel in the
|
||||
bottom right. Yours will probably still be empty, but here is what it
|
||||
@ -791,10 +800,11 @@ 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.</p>
|
||||
<p>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:</p>
|
||||
<p>In addition to registers and memory, p-code has <em>unique</em>
|
||||
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:</p>
|
||||
<ul>
|
||||
<li>The <strong>Unique</strong> column gives the variable’s name and
|
||||
size in bytes.</li>
|
||||
@ -813,10 +823,10 @@ 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.
|
||||
<strong>NOTE</strong>: 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.</p>
|
||||
<strong>Step Into</strong> 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.</p>
|
||||
</section>
|
||||
</section>
|
||||
</body>
|
||||
|
@ -51,9 +51,9 @@ In this tutorial, we will examine the command-line argument parser in `termmines
|
||||
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.
|
||||
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:
|
||||
|
||||
@ -62,20 +62,20 @@ About those two new actions:
|
||||
* ![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.
|
||||
**Quick Exercise**: 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.
|
||||
The main exception is the Terminal window.
|
||||
It always displays the state of the live target, as it is unaware of the emulator.
|
||||
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."
|
||||
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.
|
||||
@ -106,14 +106,15 @@ Here are some examples:
|
||||
* `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.
|
||||
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.
|
||||
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.
|
||||
1. Change back (again) to **Control Emulator** mode.
|
||||
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.
|
||||
@ -124,9 +125,9 @@ Most likely, they *do not* correspond to the thread IDs assigned by the back-end
|
||||
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. Launch `termmines` using GDB with **Inferior TTY** enabled.
|
||||
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.)
|
||||
(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.
|
||||
@ -172,33 +173,33 @@ This spares the loader from having to copy a potentially large program image int
|
||||
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.
|
||||
**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.
|
||||
To end the session, close the "Emulate termmines" tab in the Dynamic Listing window.
|
||||
You can then restart by right-clicking the first instruction as before.
|
||||
Second, you can use **Ctrl-G** to go to snapshot 0.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
@ -233,17 +234,17 @@ The advantage to patching the trace is that once you have completed your experim
|
||||
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.
|
||||
**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.
|
||||
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.
|
||||
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`.
|
||||
@ -258,7 +259,7 @@ First, if the binary actually implements many commands, like `busybox` does, the
|
||||
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.
|
||||
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`.
|
||||
@ -271,7 +272,7 @@ Step backward once to confirm this hypothesis.
|
||||
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:
|
||||
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.
|
||||
@ -289,16 +290,16 @@ That was determined by the value of `RSI`, which is essentially telling us we ne
|
||||
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.
|
||||
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.
|
||||
**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.
|
||||
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.
|
||||
**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)`.
|
||||
@ -306,21 +307,21 @@ When you modify the Value column, you can type either bytes (in little-endian or
|
||||
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.
|
||||
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:
|
||||
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.
|
||||
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.
|
||||
@ -335,15 +336,15 @@ There are at least three techniques for overcoming this.
|
||||
#### 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.
|
||||
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. 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.
|
||||
|
||||
@ -382,9 +383,9 @@ The `emu_swi()` userop allows you to maintain breakpoint behavior, perhaps to de
|
||||
|
||||
After you have written your Sleigh code:
|
||||
|
||||
1. Click OK on the Set Injection dialog.
|
||||
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.
|
||||
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`.
|
||||
@ -395,12 +396,12 @@ Clear or disable your breakpoint and invalidate the emulator cache again before
|
||||
|
||||
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.
|
||||
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 `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.
|
||||
@ -429,17 +430,17 @@ You are successful when the emulator crashes with `pc = 00000000`.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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 outside of Ghidra.
|
||||
|
||||
## Debugging P-code Semantics
|
||||
|
||||
@ -454,10 +455,10 @@ This panel is not included in the default Debugger tool, so it must be configure
|
||||
|
||||
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. Click the **Configure All Plugins** button in the top right of the dialog.
|
||||
1. Activate the `DebuggerPcodeStepperPlugin`
|
||||
1. Click OK
|
||||
1. Click Close
|
||||
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:
|
||||
@ -472,7 +473,7 @@ If the current instruction is overridden by a Sleigh breakpoint, the listing wil
|
||||
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.
|
||||
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.
|
||||
@ -489,5 +490,5 @@ It is displayed in the stepper's subtitle as well as the Threads panel's subtitl
|
||||
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.
|
||||
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.
|
||||
|
@ -168,6 +168,17 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
|
||||
<span id="cb1-6"><a href="#cb1-6"></a> <span class="kw">protected</span> <span class="dt">void</span> <span class="fu">run</span><span class="op">()</span> <span class="kw">throws</span> <span class="bu">Exception</span> <span class="op">{</span></span>
|
||||
<span id="cb1-7"><a href="#cb1-7"></a> <span class="op">}</span></span>
|
||||
<span id="cb1-8"><a href="#cb1-8"></a><span class="op">}</span></span></code></pre></div>
|
||||
<p><strong>NOTE</strong>: The scripting API has been refactored a little
|
||||
since the transition from Recorder-based to TraceRmi-based targets.
|
||||
Parts of the API that are back-end agnostic are accessible from the
|
||||
<code>FlatDebuggerAPI</code> interface. Parts of the API that require a
|
||||
specific back end are in <code>FlatDebuggerRmiAPI</code> and
|
||||
<code>FlatDebuggerRecorderAPI</code>, the latter of which is deprecated.
|
||||
If a script written for version 11.0.2 or prior is not compiling, it can
|
||||
most likely be patched up by changing
|
||||
<code>implements FlatDebuggerAPI</code> to
|
||||
<code>implements FlatDebuggerRecorderAPI</code>, but we recommend
|
||||
porting it to use <code>implements FlatDebuggerRmiAPI</code>.</p>
|
||||
<p>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
|
||||
@ -244,10 +255,11 @@ This allows us to locate that symbol in the dynamic context.</p>
|
||||
</section>
|
||||
<section id="reading-the-data" class="level3">
|
||||
<h3>Reading the Data</h3>
|
||||
<p>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:</p>
|
||||
<p>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:</p>
|
||||
<div class="sourceCode" id="cb5"><pre
|
||||
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb5-1"><a href="#cb5-1"></a><span class="dt">byte</span><span class="op">[]</span> widthDat <span class="op">=</span> <span class="fu">readMemory</span><span class="op">(</span>widthDyn<span class="op">,</span> <span class="dv">4</span><span class="op">,</span> monitor<span class="op">);</span></span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a><span class="dt">byte</span><span class="op">[]</span> heightDat <span class="op">=</span> <span class="fu">readMemory</span><span class="op">(</span>heightDyn<span class="op">,</span> <span class="dv">4</span><span class="op">,</span> monitor<span class="op">);</span></span>
|
||||
@ -271,18 +283,20 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
|
||||
</section>
|
||||
<section id="test-the-script" class="level3">
|
||||
<h3>Test the Script</h3>
|
||||
<p>To test, run <code>termmines</code> 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.</p>
|
||||
<p>To test, launch <code>termmines</code> 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.</p>
|
||||
</section>
|
||||
<section id="exercise-remove-the-mines" class="level3">
|
||||
<h3>Exercise: Remove the Mines</h3>
|
||||
<p>Write a script that will remove the mines from the board.
|
||||
<strong>NOTE</strong>: The <code>writeMemory()</code> 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.</p>
|
||||
methods are all subject to the current <strong>Control Mode</strong>. If
|
||||
the mode is read-only, the script cannot modify the target’s machine
|
||||
state using those methods.</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="waiting-on-reacting-to-events" class="level2">
|
||||
@ -326,9 +340,9 @@ run.</li>
|
||||
<p><strong>NOTE</strong>: 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 <strong>Ctrl-O</strong> twice in the editor for your script.
|
||||
You should now be able to type patterns, optionally with wildcards, to
|
||||
help you find applicable methods.</p>
|
||||
pressing <strong><code>CTRL</code>-<code>O</code></strong> twice in the
|
||||
editor for your script. You should now be able to type patterns,
|
||||
optionally with wildcards, to help you find applicable methods.</p>
|
||||
<p>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:</p>
|
||||
@ -338,8 +352,8 @@ See <code>getExecutionState()</code> and <code>interrupt()</code>. You
|
||||
will not likely be able to place or toggle breakpoints while the target
|
||||
is running.</li>
|
||||
<li>Methods like <code>writeMemory()</code> are subject to the current
|
||||
control mode. You may want to check and/or correct this at the top of
|
||||
your script.</li>
|
||||
<strong>Control Mode</strong>. You may want to check and/or correct this
|
||||
at the top of your script.</li>
|
||||
<li>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
|
||||
@ -351,13 +365,13 @@ breakpoint numbers.</li>
|
||||
</ul>
|
||||
<p>You are successful when you can attach to a running
|
||||
<code>termmines</code> and execute your script. Then, assuming you win
|
||||
the game, the game should award you a score of 0 seconds. It is OK if
|
||||
the game, the game should award you a score of 0 seconds. It is okay if
|
||||
you have to re-execute your script after each win.</p>
|
||||
</section>
|
||||
<section id="solution-always-win-in-0-seconds" class="level3">
|
||||
<h3>Solution: Always Win in 0 Seconds</h3>
|
||||
<p>As in the previous scripting tutorial, we will do some verifications
|
||||
at the top of the script. Your level of pedantry may vary.</p>
|
||||
<p>As in the previous script, we will do some verifications at the top
|
||||
of the script. Your level of pedantry may vary.</p>
|
||||
<div class="sourceCode" id="cb7"><pre
|
||||
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb7-1"><a href="#cb7-1"></a>Trace trace <span class="op">=</span> <span class="fu">getCurrentTrace</span><span class="op">();</span></span>
|
||||
<span id="cb7-2"><a href="#cb7-2"></a><span class="cf">if</span> <span class="op">(</span>trace <span class="op">==</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
|
||||
@ -384,17 +398,17 @@ 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
|
||||
<strong>Target w/Edits</strong>, but I prefer to keep the user aware
|
||||
that the script needs to modify target machine state.</p>
|
||||
<strong>Control Target</strong> (with edits), but I prefer to keep the
|
||||
user aware that the script needs to modify target machine state.</p>
|
||||
<p>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 <code>termmines</code> 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 <code>timer</code>,
|
||||
and to label the instruction that computes the score
|
||||
<code>reset_timer</code>. The function that prints the score must be
|
||||
named <code>print_win</code>.</p>
|
||||
same as in the previous script, but with attention to the containing
|
||||
function namespace. The way <code>termmines</code> 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 <code>timer</code>, and to
|
||||
label the instruction that computes the score <code>reset_timer</code>.
|
||||
The function that prints the score must be named
|
||||
<code>print_win</code>.</p>
|
||||
<div class="sourceCode" id="cb8"><pre
|
||||
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb8-1"><a href="#cb8-1"></a><span class="bu">List</span><span class="op"><</span>Symbol<span class="op">></span> timerSyms <span class="op">=</span> <span class="fu">getSymbols</span><span class="op">(</span><span class="st">"timer"</span><span class="op">,</span> <span class="kw">null</span><span class="op">);</span></span>
|
||||
<span id="cb8-2"><a href="#cb8-2"></a><span class="cf">if</span> <span class="op">(</span>timerSyms<span class="op">.</span><span class="fu">isEmpty</span><span class="op">())</span> <span class="op">{</span></span>
|
||||
@ -429,7 +443,7 @@ either. To establish that context, you must use a
|
||||
<code>getCurrentView()</code>.</p>
|
||||
<p>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
|
||||
existing breakpoints may not be EXECUTE breakpoints, but we will blindly
|
||||
assume they are. Again, your level of pedantry may vary. The
|
||||
<code>breakpointsEnable</code> method will return the existing
|
||||
breakpoints, so we can check that and create a new breakpoint, if
|
||||
@ -453,7 +467,7 @@ breakpoint. We do not need to be precise in this check; it suffices to
|
||||
check the program counter:</p>
|
||||
<div class="sourceCode" id="cb10"><pre
|
||||
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb10-1"><a href="#cb10-1"></a><span class="cf">while</span> <span class="op">(</span><span class="kw">true</span><span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a> monitor<span class="op">.</span><span class="fu">checkCanceled</span><span class="op">();</span></span>
|
||||
<span id="cb10-2"><a href="#cb10-2"></a> monitor<span class="op">.</span><span class="fu">checkCancelled</span><span class="op">();</span></span>
|
||||
<span id="cb10-3"><a href="#cb10-3"></a></span>
|
||||
<span id="cb10-4"><a href="#cb10-4"></a> TargetExecutionState execState <span class="op">=</span> <span class="fu">getExecutionState</span><span class="op">(</span>trace<span class="op">);</span></span>
|
||||
<span id="cb10-5"><a href="#cb10-5"></a> <span class="cf">switch</span> <span class="op">(</span>execState<span class="op">)</span> <span class="op">{</span></span>
|
||||
@ -491,12 +505,12 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
|
||||
<span id="cb10-37"><a href="#cb10-37"></a> <span class="cf">break</span><span class="op">;</span></span>
|
||||
<span id="cb10-38"><a href="#cb10-38"></a> <span class="op">}</span></span>
|
||||
<span id="cb10-39"><a href="#cb10-39"></a><span class="op">}</span></span></code></pre></div>
|
||||
<p>The “center” of this loop is a call to <code>waitForBreak()</code>.
|
||||
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.</p>
|
||||
<p>The “center” of this loop is a call to <code>waitForBreak()</code> on
|
||||
line 27. 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.</p>
|
||||
<p>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 <code>resume()</code> if the target is stopped. There are
|
||||
|
@ -24,6 +24,11 @@ public class DemoDebuggerScript extends GhidraScript implements FlatDebuggerAPI
|
||||
}
|
||||
```
|
||||
|
||||
**NOTE**: The scripting API has been refactored a little 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` and `FlatDebuggerRecorderAPI`, the latter of which is deprecated.
|
||||
If a script written for version 11.0.2 or prior is not compiling, it can most likely be patched up by changing `implements FlatDebuggerAPI` to `implements FlatDebuggerRecorderAPI`, but we recommend porting it to use `implements 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.
|
||||
@ -97,7 +102,7 @@ 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.
|
||||
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:
|
||||
|
||||
@ -126,15 +131,17 @@ for (int y = 0; y < height; y++) {
|
||||
|
||||
### Test the Script
|
||||
|
||||
To test, run `termmines` in a proper terminal and attach to it from Ghidra using GDB.
|
||||
Now, run 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.
|
||||
**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
|
||||
@ -168,7 +175,7 @@ The general template for waiting on a condition is a bit klunky, but conceptuall
|
||||
### 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.
|
||||
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.
|
||||
@ -177,7 +184,7 @@ 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.
|
||||
* 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()`.
|
||||
@ -186,11 +193,11 @@ Some gotchas to consider up front:
|
||||
|
||||
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.
|
||||
It is okay 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.
|
||||
As in the previous script, we will do some verifications at the top of the script.
|
||||
Your level of pedantry may vary.
|
||||
|
||||
```java {.numberLines}
|
||||
@ -219,10 +226,10 @@ The first two blocks check that there is an active target with `termmines` as th
|
||||
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.
|
||||
We could instead change the control mode to **Control Target** (with 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.
|
||||
This works pretty much the same as in the previous script, 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`.
|
||||
@ -261,7 +268,7 @@ 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.
|
||||
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:
|
||||
|
||||
@ -284,7 +291,7 @@ We do not need to be precise in this check; it suffices to check the program cou
|
||||
|
||||
```java {.numberLines}
|
||||
while (true) {
|
||||
monitor.checkCanceled();
|
||||
monitor.checkCancelled();
|
||||
|
||||
TargetExecutionState execState = getExecutionState(trace);
|
||||
switch (execState) {
|
||||
@ -324,7 +331,7 @@ while (true) {
|
||||
}
|
||||
```
|
||||
|
||||
The "center" of this loop is a call to `waitForBreak()`.
|
||||
The "center" of this loop is a call to `waitForBreak()` on line 27.
|
||||
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.
|
||||
|
@ -266,19 +266,20 @@ depending on where the breakpoint was placed.</p>
|
||||
<section id="modeling-by-sleigh-semantics" class="level3">
|
||||
<h3>Modeling by Sleigh Semantics</h3>
|
||||
<p>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
|
||||
intuitive to do, but the temptation, which we intentionally demonstrated
|
||||
above, is to make everything concrete. You may notice the library uses a
|
||||
type parameter <code>T</code>, which specifies the type of all variables
|
||||
in the emulator’s state. Leaving it as <code>T</code> indicates the
|
||||
library is compatible with any type. For a concrete emulator,
|
||||
<code>T = byte[]</code>, and so there is no loss in making things
|
||||
<code>T := byte[]</code>, and so there is no loss in making things
|
||||
concrete, and then converting back to <code>T</code> 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:</p>
|
||||
<code>arithmetic</code> 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 <code>arithmetic</code> object to perform any arithmetic operations.
|
||||
Alternatively, you can implement the userop using pre-compiled Sleigh
|
||||
code:</p>
|
||||
<div class="sourceCode" id="cb2"><pre
|
||||
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> SleighStdLibPcodeUseropLibrary<span class="op"><</span>T<span class="op">></span> <span class="kw">extends</span> AnnotatedPcodeUseropLibrary<span class="op"><</span>T<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb2-2"><a href="#cb2-2"></a> <span class="kw">private</span> <span class="dt">static</span> <span class="dt">final</span> <span class="bu">String</span> SRC_RET <span class="op">=</span> <span class="st">"""</span></span>
|
||||
@ -341,9 +342,9 @@ 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.</p>
|
||||
<p>The advantage here is that the code will properly use the underlying
|
||||
<p>The advantage here is that the p-code will 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
|
||||
not be desired. Some symbolic models might just like to see an abstract
|
||||
call to <code>strlen()</code>.</p>
|
||||
</section>
|
||||
<section id="modeling-by-structured-sleigh" class="level3">
|
||||
@ -466,7 +467,7 @@ into its state, and you should choose a different location for the
|
||||
injection. Refer to the example scripts in Ghidra’s
|
||||
<code>SystemEmulation</code> module.</p>
|
||||
<p>If you would like to (temporarily) override the GUI with a custom
|
||||
userop library, you can by overriding the GUI’s emulator factory:</p>
|
||||
userop library, you can by setting the GUI’s emulator factory:</p>
|
||||
<div class="sourceCode" id="cb5"><pre
|
||||
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">public</span> <span class="kw">class</span> InstallCustomLibraryScript <span class="kw">extends</span> GhidraScript <span class="kw">implements</span> FlatDebuggerAPI <span class="op">{</span></span>
|
||||
<span id="cb5-2"><a href="#cb5-2"></a> <span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> CustomBytesDebuggerPcodeEmulator <span class="kw">extends</span> BytesDebuggerPcodeEmulator <span class="op">{</span></span>
|
||||
@ -738,7 +739,7 @@ one we actually implement, by extending from
|
||||
<code>AbstractLongOffsetPcodeExecutorStatePiece</code>.</p>
|
||||
<p><strong>NOTE</strong>: If you do not desire a concrete address model,
|
||||
then you should implement <code>PcodeExecutorState<Expr></code>
|
||||
directly. A “state” is also “state piece” whose address model is the
|
||||
directly. A “state” is also a “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
|
||||
@ -772,81 +773,85 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
|
||||
<span id="cb8-20"><a href="#cb8-20"></a> map<span class="op">.</span><span class="fu">put</span><span class="op">(</span>offset<span class="op">,</span> val<span class="op">);</span></span>
|
||||
<span id="cb8-21"><a href="#cb8-21"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-22"><a href="#cb8-22"></a></span>
|
||||
<span id="cb8-23"><a href="#cb8-23"></a> <span class="kw">public</span> Expr <span class="fu">get</span><span class="op">(</span><span class="dt">long</span> offset<span class="op">,</span> <span class="dt">int</span> size<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-24"><a href="#cb8-24"></a> <span class="co">// </span><span class="al">TODO</span><span class="co">: Handle overlaps / offcut gets and sets</span></span>
|
||||
<span id="cb8-25"><a href="#cb8-25"></a> Expr expr <span class="op">=</span> map<span class="op">.</span><span class="fu">get</span><span class="op">(</span>offset<span class="op">);</span></span>
|
||||
<span id="cb8-26"><a href="#cb8-26"></a> <span class="cf">return</span> expr <span class="op">!=</span> <span class="kw">null</span> <span class="op">?</span> expr <span class="op">:</span> <span class="kw">new</span> <span class="fu">VarExpr</span><span class="op">(</span>space<span class="op">,</span> offset<span class="op">,</span> size<span class="op">);</span></span>
|
||||
<span id="cb8-27"><a href="#cb8-27"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-28"><a href="#cb8-28"></a><span class="op">}</span></span>
|
||||
<span id="cb8-29"><a href="#cb8-29"></a></span>
|
||||
<span id="cb8-30"><a href="#cb8-30"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">abstract</span> <span class="kw">class</span> AbstractExprPcodeExecutorStatePiece<span class="op"><</span>S <span class="kw">extends</span> ExprSpace<span class="op">></span> <span class="kw">extends</span></span>
|
||||
<span id="cb8-31"><a href="#cb8-31"></a> AbstractLongOffsetPcodeExecutorStatePiece<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">,</span> S<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb8-32"><a href="#cb8-32"></a></span>
|
||||
<span id="cb8-33"><a href="#cb8-33"></a> <span class="kw">protected</span> <span class="dt">final</span> AbstractSpaceMap<span class="op"><</span>S<span class="op">></span> spaceMap <span class="op">=</span> <span class="fu">newSpaceMap</span><span class="op">();</span></span>
|
||||
<span id="cb8-34"><a href="#cb8-34"></a></span>
|
||||
<span id="cb8-35"><a href="#cb8-35"></a> <span class="kw">public</span> <span class="fu">AbstractExprPcodeExecutorStatePiece</span><span class="op">(</span>Language language<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-36"><a href="#cb8-36"></a> <span class="kw">super</span><span class="op">(</span>language<span class="op">,</span> BytesPcodeArithmetic<span class="op">.</span><span class="fu">forLanguage</span><span class="op">(</span>language<span class="op">),</span></span>
|
||||
<span id="cb8-37"><a href="#cb8-37"></a> ExprPcodeArithmetic<span class="op">.</span><span class="fu">forLanguage</span><span class="op">(</span>language<span class="op">));</span></span>
|
||||
<span id="cb8-38"><a href="#cb8-38"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-39"><a href="#cb8-39"></a></span>
|
||||
<span id="cb8-40"><a href="#cb8-40"></a> <span class="kw">protected</span> <span class="kw">abstract</span> AbstractSpaceMap<span class="op"><</span>S<span class="op">></span> <span class="fu">newSpaceMap</span><span class="op">();</span></span>
|
||||
<span id="cb8-41"><a href="#cb8-41"></a></span>
|
||||
<span id="cb8-42"><a href="#cb8-42"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-43"><a href="#cb8-43"></a> <span class="kw">public</span> MemBuffer <span class="fu">getConcreteBuffer</span><span class="op">(</span>Address address<span class="op">,</span> Purpose purpose<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-44"><a href="#cb8-44"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span>
|
||||
<span id="cb8-45"><a href="#cb8-45"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-46"><a href="#cb8-46"></a></span>
|
||||
<span id="cb8-47"><a href="#cb8-47"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-48"><a href="#cb8-48"></a> <span class="kw">public</span> <span class="dt">void</span> <span class="fu">clear</span><span class="op">()</span> <span class="op">{</span></span>
|
||||
<span id="cb8-49"><a href="#cb8-49"></a> <span class="cf">for</span> <span class="op">(</span>S space <span class="op">:</span> spaceMap<span class="op">.</span><span class="fu">values</span><span class="op">())</span> <span class="op">{</span></span>
|
||||
<span id="cb8-50"><a href="#cb8-50"></a> space<span class="op">.</span><span class="fu">clear</span><span class="op">();</span></span>
|
||||
<span id="cb8-51"><a href="#cb8-51"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-52"><a href="#cb8-52"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-53"><a href="#cb8-53"></a></span>
|
||||
<span id="cb8-54"><a href="#cb8-54"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-55"><a href="#cb8-55"></a> <span class="kw">protected</span> S <span class="fu">getForSpace</span><span class="op">(</span>AddressSpace space<span class="op">,</span> <span class="dt">boolean</span> toWrite<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-56"><a href="#cb8-56"></a> <span class="cf">return</span> spaceMap<span class="op">.</span><span class="fu">getForSpace</span><span class="op">(</span>space<span class="op">,</span> toWrite<span class="op">);</span></span>
|
||||
<span id="cb8-57"><a href="#cb8-57"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-58"><a href="#cb8-58"></a></span>
|
||||
<span id="cb8-59"><a href="#cb8-59"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-60"><a href="#cb8-60"></a> <span class="kw">protected</span> <span class="dt">void</span> <span class="fu">setInSpace</span><span class="op">(</span>ExprSpace space<span class="op">,</span> <span class="dt">long</span> offset<span class="op">,</span> <span class="dt">int</span> size<span class="op">,</span> Expr val<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-61"><a href="#cb8-61"></a> space<span class="op">.</span><span class="fu">set</span><span class="op">(</span>offset<span class="op">,</span> val<span class="op">);</span></span>
|
||||
<span id="cb8-62"><a href="#cb8-62"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-63"><a href="#cb8-63"></a></span>
|
||||
<span id="cb8-64"><a href="#cb8-64"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-65"><a href="#cb8-65"></a> <span class="kw">protected</span> Expr <span class="fu">getFromSpace</span><span class="op">(</span>S space<span class="op">,</span> <span class="dt">long</span> offset<span class="op">,</span> <span class="dt">int</span> size<span class="op">,</span> Reason reason<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-66"><a href="#cb8-66"></a> <span class="cf">return</span> space<span class="op">.</span><span class="fu">get</span><span class="op">(</span>offset<span class="op">,</span> size<span class="op">);</span></span>
|
||||
<span id="cb8-67"><a href="#cb8-67"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-68"><a href="#cb8-68"></a></span>
|
||||
<span id="cb8-69"><a href="#cb8-69"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-70"><a href="#cb8-70"></a> <span class="kw">protected</span> <span class="bu">Map</span><span class="op"><</span>Register<span class="op">,</span> Expr<span class="op">></span> <span class="fu">getRegisterValuesFromSpace</span><span class="op">(</span>S s<span class="op">,</span> <span class="bu">List</span><span class="op"><</span>Register<span class="op">></span> registers<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-71"><a href="#cb8-71"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span>
|
||||
<span id="cb8-72"><a href="#cb8-72"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-73"><a href="#cb8-73"></a><span class="op">}</span></span>
|
||||
<span id="cb8-74"><a href="#cb8-74"></a></span>
|
||||
<span id="cb8-75"><a href="#cb8-75"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> ExprPcodeExecutorStatePiece</span>
|
||||
<span id="cb8-76"><a href="#cb8-76"></a> <span class="kw">extends</span> AbstractExprPcodeExecutorStatePiece<span class="op"><</span>ExprSpace<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb8-77"><a href="#cb8-77"></a> <span class="kw">public</span> <span class="fu">ExprPcodeExecutorStatePiece</span><span class="op">(</span>Language language<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-78"><a href="#cb8-78"></a> <span class="kw">super</span><span class="op">(</span>language<span class="op">);</span></span>
|
||||
<span id="cb8-79"><a href="#cb8-79"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-80"><a href="#cb8-80"></a></span>
|
||||
<span id="cb8-81"><a href="#cb8-81"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-82"><a href="#cb8-82"></a> <span class="kw">protected</span> AbstractSpaceMap<span class="op"><</span>ExprSpace<span class="op">></span> <span class="fu">newSpaceMap</span><span class="op">()</span> <span class="op">{</span></span>
|
||||
<span id="cb8-83"><a href="#cb8-83"></a> <span class="cf">return</span> <span class="kw">new</span> SimpleSpaceMap<span class="op"><</span>ExprSpace<span class="op">>()</span> <span class="op">{</span></span>
|
||||
<span id="cb8-84"><a href="#cb8-84"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-85"><a href="#cb8-85"></a> <span class="kw">protected</span> ExprSpace <span class="fu">newSpace</span><span class="op">(</span>AddressSpace space<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-86"><a href="#cb8-86"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">ExprSpace</span><span class="op">(</span>space<span class="op">);</span></span>
|
||||
<span id="cb8-87"><a href="#cb8-87"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-88"><a href="#cb8-88"></a> <span class="op">};</span></span>
|
||||
<span id="cb8-89"><a href="#cb8-89"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-90"><a href="#cb8-90"></a><span class="op">}</span></span>
|
||||
<span id="cb8-91"><a href="#cb8-91"></a></span>
|
||||
<span id="cb8-92"><a href="#cb8-92"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> BytesExprPcodeExecutorState <span class="kw">extends</span> PairedPcodeExecutorState<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb8-93"><a href="#cb8-93"></a> <span class="kw">public</span> <span class="fu">BytesExprPcodeExecutorState</span><span class="op">(</span>PcodeExecutorStatePiece<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> <span class="dt">byte</span><span class="op">[]></span> concrete<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-94"><a href="#cb8-94"></a> <span class="kw">super</span><span class="op">(</span><span class="kw">new</span> PairedPcodeExecutorStatePiece<span class="op"><>(</span>concrete<span class="op">,</span></span>
|
||||
<span id="cb8-95"><a href="#cb8-95"></a> <span class="kw">new</span> <span class="fu">ExprPcodeExecutorStatePiece</span><span class="op">(</span>concrete<span class="op">.</span><span class="fu">getLanguage</span><span class="op">())));</span></span>
|
||||
<span id="cb8-96"><a href="#cb8-96"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-97"><a href="#cb8-97"></a><span class="op">}</span></span></code></pre></div>
|
||||
<span id="cb8-23"><a href="#cb8-23"></a> <span class="kw">protected</span> Expr <span class="fu">whenNull</span><span class="op">(</span><span class="dt">long</span> offset<span class="op">,</span> <span class="dt">int</span> size<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-24"><a href="#cb8-24"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">VarExpr</span><span class="op">(</span>space<span class="op">,</span> offset<span class="op">,</span> size<span class="op">);</span></span>
|
||||
<span id="cb8-25"><a href="#cb8-25"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-26"><a href="#cb8-26"></a></span>
|
||||
<span id="cb8-27"><a href="#cb8-27"></a> <span class="kw">public</span> Expr <span class="fu">get</span><span class="op">(</span><span class="dt">long</span> offset<span class="op">,</span> <span class="dt">int</span> size<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-28"><a href="#cb8-28"></a> <span class="co">// </span><span class="al">TODO</span><span class="co">: Handle overlaps / offcut gets and sets</span></span>
|
||||
<span id="cb8-29"><a href="#cb8-29"></a> Expr expr <span class="op">=</span> map<span class="op">.</span><span class="fu">get</span><span class="op">(</span>offset<span class="op">);</span></span>
|
||||
<span id="cb8-30"><a href="#cb8-30"></a> <span class="cf">return</span> expr <span class="op">!=</span> <span class="kw">null</span> <span class="op">?</span> expr <span class="op">:</span> <span class="fu">whenNull</span><span class="op">(</span>offset<span class="op">,</span> size<span class="op">);</span></span>
|
||||
<span id="cb8-31"><a href="#cb8-31"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-32"><a href="#cb8-32"></a><span class="op">}</span></span>
|
||||
<span id="cb8-33"><a href="#cb8-33"></a></span>
|
||||
<span id="cb8-34"><a href="#cb8-34"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">abstract</span> <span class="kw">class</span> AbstractExprPcodeExecutorStatePiece<span class="op"><</span>S <span class="kw">extends</span> ExprSpace<span class="op">></span> <span class="kw">extends</span></span>
|
||||
<span id="cb8-35"><a href="#cb8-35"></a> AbstractLongOffsetPcodeExecutorStatePiece<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">,</span> S<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb8-36"><a href="#cb8-36"></a></span>
|
||||
<span id="cb8-37"><a href="#cb8-37"></a> <span class="kw">protected</span> <span class="dt">final</span> AbstractSpaceMap<span class="op"><</span>S<span class="op">></span> spaceMap <span class="op">=</span> <span class="fu">newSpaceMap</span><span class="op">();</span></span>
|
||||
<span id="cb8-38"><a href="#cb8-38"></a></span>
|
||||
<span id="cb8-39"><a href="#cb8-39"></a> <span class="kw">public</span> <span class="fu">AbstractExprPcodeExecutorStatePiece</span><span class="op">(</span>Language language<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-40"><a href="#cb8-40"></a> <span class="kw">super</span><span class="op">(</span>language<span class="op">,</span> BytesPcodeArithmetic<span class="op">.</span><span class="fu">forLanguage</span><span class="op">(</span>language<span class="op">),</span></span>
|
||||
<span id="cb8-41"><a href="#cb8-41"></a> ExprPcodeArithmetic<span class="op">.</span><span class="fu">forLanguage</span><span class="op">(</span>language<span class="op">));</span></span>
|
||||
<span id="cb8-42"><a href="#cb8-42"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-43"><a href="#cb8-43"></a></span>
|
||||
<span id="cb8-44"><a href="#cb8-44"></a> <span class="kw">protected</span> <span class="kw">abstract</span> AbstractSpaceMap<span class="op"><</span>S<span class="op">></span> <span class="fu">newSpaceMap</span><span class="op">();</span></span>
|
||||
<span id="cb8-45"><a href="#cb8-45"></a></span>
|
||||
<span id="cb8-46"><a href="#cb8-46"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-47"><a href="#cb8-47"></a> <span class="kw">public</span> MemBuffer <span class="fu">getConcreteBuffer</span><span class="op">(</span>Address address<span class="op">,</span> Purpose purpose<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-48"><a href="#cb8-48"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span>
|
||||
<span id="cb8-49"><a href="#cb8-49"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-50"><a href="#cb8-50"></a></span>
|
||||
<span id="cb8-51"><a href="#cb8-51"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-52"><a href="#cb8-52"></a> <span class="kw">public</span> <span class="dt">void</span> <span class="fu">clear</span><span class="op">()</span> <span class="op">{</span></span>
|
||||
<span id="cb8-53"><a href="#cb8-53"></a> <span class="cf">for</span> <span class="op">(</span>S space <span class="op">:</span> spaceMap<span class="op">.</span><span class="fu">values</span><span class="op">())</span> <span class="op">{</span></span>
|
||||
<span id="cb8-54"><a href="#cb8-54"></a> space<span class="op">.</span><span class="fu">clear</span><span class="op">();</span></span>
|
||||
<span id="cb8-55"><a href="#cb8-55"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-56"><a href="#cb8-56"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-57"><a href="#cb8-57"></a></span>
|
||||
<span id="cb8-58"><a href="#cb8-58"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-59"><a href="#cb8-59"></a> <span class="kw">protected</span> S <span class="fu">getForSpace</span><span class="op">(</span>AddressSpace space<span class="op">,</span> <span class="dt">boolean</span> toWrite<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-60"><a href="#cb8-60"></a> <span class="cf">return</span> spaceMap<span class="op">.</span><span class="fu">getForSpace</span><span class="op">(</span>space<span class="op">,</span> toWrite<span class="op">);</span></span>
|
||||
<span id="cb8-61"><a href="#cb8-61"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-62"><a href="#cb8-62"></a></span>
|
||||
<span id="cb8-63"><a href="#cb8-63"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-64"><a href="#cb8-64"></a> <span class="kw">protected</span> <span class="dt">void</span> <span class="fu">setInSpace</span><span class="op">(</span>ExprSpace space<span class="op">,</span> <span class="dt">long</span> offset<span class="op">,</span> <span class="dt">int</span> size<span class="op">,</span> Expr val<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-65"><a href="#cb8-65"></a> space<span class="op">.</span><span class="fu">set</span><span class="op">(</span>offset<span class="op">,</span> val<span class="op">);</span></span>
|
||||
<span id="cb8-66"><a href="#cb8-66"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-67"><a href="#cb8-67"></a></span>
|
||||
<span id="cb8-68"><a href="#cb8-68"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-69"><a href="#cb8-69"></a> <span class="kw">protected</span> Expr <span class="fu">getFromSpace</span><span class="op">(</span>S space<span class="op">,</span> <span class="dt">long</span> offset<span class="op">,</span> <span class="dt">int</span> size<span class="op">,</span> Reason reason<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-70"><a href="#cb8-70"></a> <span class="cf">return</span> space<span class="op">.</span><span class="fu">get</span><span class="op">(</span>offset<span class="op">,</span> size<span class="op">);</span></span>
|
||||
<span id="cb8-71"><a href="#cb8-71"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-72"><a href="#cb8-72"></a></span>
|
||||
<span id="cb8-73"><a href="#cb8-73"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-74"><a href="#cb8-74"></a> <span class="kw">protected</span> <span class="bu">Map</span><span class="op"><</span>Register<span class="op">,</span> Expr<span class="op">></span> <span class="fu">getRegisterValuesFromSpace</span><span class="op">(</span>S s<span class="op">,</span> <span class="bu">List</span><span class="op"><</span>Register<span class="op">></span> registers<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-75"><a href="#cb8-75"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">UnsupportedOperationException</span><span class="op">();</span></span>
|
||||
<span id="cb8-76"><a href="#cb8-76"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-77"><a href="#cb8-77"></a><span class="op">}</span></span>
|
||||
<span id="cb8-78"><a href="#cb8-78"></a></span>
|
||||
<span id="cb8-79"><a href="#cb8-79"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> ExprPcodeExecutorStatePiece</span>
|
||||
<span id="cb8-80"><a href="#cb8-80"></a> <span class="kw">extends</span> AbstractExprPcodeExecutorStatePiece<span class="op"><</span>ExprSpace<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb8-81"><a href="#cb8-81"></a> <span class="kw">public</span> <span class="fu">ExprPcodeExecutorStatePiece</span><span class="op">(</span>Language language<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-82"><a href="#cb8-82"></a> <span class="kw">super</span><span class="op">(</span>language<span class="op">);</span></span>
|
||||
<span id="cb8-83"><a href="#cb8-83"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-84"><a href="#cb8-84"></a></span>
|
||||
<span id="cb8-85"><a href="#cb8-85"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-86"><a href="#cb8-86"></a> <span class="kw">protected</span> AbstractSpaceMap<span class="op"><</span>ExprSpace<span class="op">></span> <span class="fu">newSpaceMap</span><span class="op">()</span> <span class="op">{</span></span>
|
||||
<span id="cb8-87"><a href="#cb8-87"></a> <span class="cf">return</span> <span class="kw">new</span> SimpleSpaceMap<span class="op"><</span>ExprSpace<span class="op">>()</span> <span class="op">{</span></span>
|
||||
<span id="cb8-88"><a href="#cb8-88"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb8-89"><a href="#cb8-89"></a> <span class="kw">protected</span> ExprSpace <span class="fu">newSpace</span><span class="op">(</span>AddressSpace space<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-90"><a href="#cb8-90"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">ExprSpace</span><span class="op">(</span>space<span class="op">);</span></span>
|
||||
<span id="cb8-91"><a href="#cb8-91"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-92"><a href="#cb8-92"></a> <span class="op">};</span></span>
|
||||
<span id="cb8-93"><a href="#cb8-93"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-94"><a href="#cb8-94"></a><span class="op">}</span></span>
|
||||
<span id="cb8-95"><a href="#cb8-95"></a></span>
|
||||
<span id="cb8-96"><a href="#cb8-96"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> BytesExprPcodeExecutorState <span class="kw">extends</span> PairedPcodeExecutorState<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb8-97"><a href="#cb8-97"></a> <span class="kw">public</span> <span class="fu">BytesExprPcodeExecutorState</span><span class="op">(</span>PcodeExecutorStatePiece<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> <span class="dt">byte</span><span class="op">[]></span> concrete<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb8-98"><a href="#cb8-98"></a> <span class="kw">super</span><span class="op">(</span><span class="kw">new</span> PairedPcodeExecutorStatePiece<span class="op"><>(</span>concrete<span class="op">,</span></span>
|
||||
<span id="cb8-99"><a href="#cb8-99"></a> <span class="kw">new</span> <span class="fu">ExprPcodeExecutorStatePiece</span><span class="op">(</span>concrete<span class="op">.</span><span class="fu">getLanguage</span><span class="op">())));</span></span>
|
||||
<span id="cb8-100"><a href="#cb8-100"></a> <span class="op">}</span></span>
|
||||
<span id="cb8-101"><a href="#cb8-101"></a><span class="op">}</span></span></code></pre></div>
|
||||
<p>The abstract class implements a strategy where a dedicated object
|
||||
handles each address space. Each object typically maintains of map of
|
||||
offsets (type <code>long</code>) to the model type, i.e.,
|
||||
@ -859,7 +864,8 @@ 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 <code>RAX</code> followed by a read from
|
||||
<code>EAX</code> will immediately demonstrate this issue. Nevertheless,
|
||||
we leave those details as an exercise.</p>
|
||||
we leave those details as an exercise. We factor <code>whenNull</code>
|
||||
so that it can be overridden later.</p>
|
||||
<p>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
|
||||
@ -1001,8 +1007,8 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
|
||||
<span id="cb10-19"><a href="#cb10-19"></a><span class="op">}</span></span></code></pre></div>
|
||||
<p><strong>NOTE</strong>: 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 <code>fromConst</code> on both arithmetics to generate
|
||||
the pair, so you may be setting the right side to a
|
||||
that it will use <code>fromConst</code> on both sub-arithmetics to
|
||||
generate the pair, so you may be setting the right side to a
|
||||
<code>LitExpr</code>. To modify just one side of the pair, cast the
|
||||
state to <code>PairedPcodeExecutorState</code>, and then use
|
||||
<code>getLeft()</code>, and <code>getRight()</code> to retrieve the
|
||||
@ -1072,7 +1078,7 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
|
||||
<span id="cb11-35"><a href="#cb11-35"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> ExprTracePcodeExecutorStatePiece</span>
|
||||
<span id="cb11-36"><a href="#cb11-36"></a> <span class="kw">extends</span> AbstractExprPcodeExecutorStatePiece<span class="op"><</span>ExprTraceSpace<span class="op">></span></span>
|
||||
<span id="cb11-37"><a href="#cb11-37"></a> <span class="kw">implements</span> TracePcodeExecutorStatePiece<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb11-38"><a href="#cb11-38"></a> <span class="kw">public</span> <span class="dt">static</span> <span class="dt">final</span> <span class="bu">String</span> NAME <span class="op">=</span> <span class="st">"Taint"</span><span class="op">;</span></span>
|
||||
<span id="cb11-38"><a href="#cb11-38"></a> <span class="kw">public</span> <span class="dt">static</span> <span class="dt">final</span> <span class="bu">String</span> NAME <span class="op">=</span> <span class="st">"Expr"</span><span class="op">;</span></span>
|
||||
<span id="cb11-39"><a href="#cb11-39"></a></span>
|
||||
<span id="cb11-40"><a href="#cb11-40"></a> <span class="kw">protected</span> <span class="dt">final</span> PcodeTraceDataAccess data<span class="op">;</span></span>
|
||||
<span id="cb11-41"><a href="#cb11-41"></a> <span class="kw">protected</span> <span class="dt">final</span> PcodeTracePropertyAccess<span class="op"><</span><span class="bu">String</span><span class="op">></span> property<span class="op">;</span></span>
|
||||
@ -1116,7 +1122,15 @@ class="sourceCode numberSource java numberLines"><code class="sourceCode java"><
|
||||
<span id="cb11-79"><a href="#cb11-79"></a> space<span class="op">.</span><span class="fu">writeDown</span><span class="op">(</span>property<span class="op">);</span></span>
|
||||
<span id="cb11-80"><a href="#cb11-80"></a> <span class="op">}</span></span>
|
||||
<span id="cb11-81"><a href="#cb11-81"></a> <span class="op">}</span></span>
|
||||
<span id="cb11-82"><a href="#cb11-82"></a><span class="op">}</span></span></code></pre></div>
|
||||
<span id="cb11-82"><a href="#cb11-82"></a><span class="op">}</span></span>
|
||||
<span id="cb11-83"><a href="#cb11-83"></a></span>
|
||||
<span id="cb11-84"><a href="#cb11-84"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> ExprTracePcodeExecutorState</span>
|
||||
<span id="cb11-85"><a href="#cb11-85"></a> <span class="kw">extends</span> PairedTracePcodeExecutorState<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb11-86"><a href="#cb11-86"></a> <span class="kw">public</span> <span class="fu">ExprTracePcodeExecutorState</span><span class="op">(</span>TracePcodeExecutorStatePiece<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> <span class="dt">byte</span><span class="op">[]></span> concrete<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb11-87"><a href="#cb11-87"></a> <span class="kw">super</span><span class="op">(</span><span class="kw">new</span> PairedTracePcodeExecutorStatePiece<span class="op"><>(</span>concrete<span class="op">,</span></span>
|
||||
<span id="cb11-88"><a href="#cb11-88"></a> <span class="kw">new</span> <span class="fu">ExprTracePcodeExecutorStatePiece</span><span class="op">(</span>concrete<span class="op">.</span><span class="fu">getData</span><span class="op">())));</span></span>
|
||||
<span id="cb11-89"><a href="#cb11-89"></a> <span class="op">}</span></span>
|
||||
<span id="cb11-90"><a href="#cb11-90"></a><span class="op">}</span></span></code></pre></div>
|
||||
<p>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.
|
||||
@ -1124,30 +1138,101 @@ 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 <code>Expr</code>-augmented emulator factory:</p>
|
||||
<div class="sourceCode" id="cb12"><pre
|
||||
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> BytesExprDebuggerPcodeEmulator <span class="kw">extends</span> AuxDebuggerPcodeEmulator<span class="op"><</span>Expr<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb12-2"><a href="#cb12-2"></a> <span class="kw">public</span> <span class="fu">BytesExprDebuggerPcodeEmulator</span><span class="op">(</span>PcodeDebuggerAccess access<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-3"><a href="#cb12-3"></a> <span class="kw">super</span><span class="op">(</span>access<span class="op">);</span></span>
|
||||
<span id="cb12-4"><a href="#cb12-4"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-5"><a href="#cb12-5"></a></span>
|
||||
<span id="cb12-6"><a href="#cb12-6"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-7"><a href="#cb12-7"></a> <span class="kw">protected</span> AuxDebuggerEmulatorPartsFactory<span class="op"><</span>Expr<span class="op">></span> <span class="fu">getPartsFactory</span><span class="op">()</span> <span class="op">{</span></span>
|
||||
<span id="cb12-8"><a href="#cb12-8"></a> <span class="cf">return</span> BytesExprDebuggerEmulatorPartsFactory<span class="op">.</span><span class="fu">INSTANCE</span><span class="op">;</span></span>
|
||||
<span id="cb12-9"><a href="#cb12-9"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-10"><a href="#cb12-10"></a><span class="op">}</span></span>
|
||||
<span id="cb12-11"><a href="#cb12-11"></a></span>
|
||||
<span id="cb12-12"><a href="#cb12-12"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> BytesExprDebuggerPcodeEmulatorFactory</span>
|
||||
<span id="cb12-13"><a href="#cb12-13"></a> <span class="kw">implements</span> DebuggerPcodeEmulatorFactory <span class="op">{</span></span>
|
||||
<span id="cb12-14"><a href="#cb12-14"></a></span>
|
||||
<span id="cb12-15"><a href="#cb12-15"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-16"><a href="#cb12-16"></a> <span class="kw">public</span> <span class="bu">String</span> <span class="fu">getTitle</span><span class="op">()</span> <span class="op">{</span></span>
|
||||
<span id="cb12-17"><a href="#cb12-17"></a> <span class="cf">return</span> <span class="st">"Expr"</span><span class="op">;</span></span>
|
||||
<span id="cb12-18"><a href="#cb12-18"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-19"><a href="#cb12-19"></a></span>
|
||||
<span id="cb12-20"><a href="#cb12-20"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-21"><a href="#cb12-21"></a> <span class="kw">public</span> DebuggerPcodeMachine<span class="op"><?></span> <span class="fu">create</span><span class="op">(</span>PcodeDebuggerAccess access<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-22"><a href="#cb12-22"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">BytesExprDebuggerPcodeEmulator</span><span class="op">(</span>access<span class="op">);</span></span>
|
||||
<span id="cb12-23"><a href="#cb12-23"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-24"><a href="#cb12-24"></a><span class="op">}</span></span></code></pre></div>
|
||||
class="sourceCode numberSource java numberLines"><code class="sourceCode java"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">public</span> <span class="kw">enum</span> BytesExprDebuggerEmulatorPartsFactory</span>
|
||||
<span id="cb12-2"><a href="#cb12-2"></a> <span class="kw">implements</span> AuxDebuggerEmulatorPartsFactory<span class="op"><</span>Expr<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb12-3"><a href="#cb12-3"></a> INSTANCE<span class="op">;</span></span>
|
||||
<span id="cb12-4"><a href="#cb12-4"></a></span>
|
||||
<span id="cb12-5"><a href="#cb12-5"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-6"><a href="#cb12-6"></a> <span class="kw">public</span> PcodeArithmetic<span class="op"><</span>Expr<span class="op">></span> <span class="fu">getArithmetic</span><span class="op">(</span>Language language<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-7"><a href="#cb12-7"></a> <span class="cf">return</span> ExprPcodeArithmetic<span class="op">.</span><span class="fu">forLanguage</span><span class="op">(</span>language<span class="op">);</span></span>
|
||||
<span id="cb12-8"><a href="#cb12-8"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-9"><a href="#cb12-9"></a></span>
|
||||
<span id="cb12-10"><a href="#cb12-10"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-11"><a href="#cb12-11"></a> <span class="kw">public</span> PcodeUseropLibrary<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">>></span> <span class="fu">createSharedUseropLibrary</span><span class="op">(</span></span>
|
||||
<span id="cb12-12"><a href="#cb12-12"></a> AuxPcodeEmulator<span class="op"><</span>Expr<span class="op">></span> emulator<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-13"><a href="#cb12-13"></a> <span class="cf">return</span> PcodeUseropLibrary<span class="op">.</span><span class="fu">nil</span><span class="op">();</span></span>
|
||||
<span id="cb12-14"><a href="#cb12-14"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-15"><a href="#cb12-15"></a></span>
|
||||
<span id="cb12-16"><a href="#cb12-16"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-17"><a href="#cb12-17"></a> <span class="kw">public</span> PcodeUseropLibrary<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">>></span> <span class="fu">createLocalUseropStub</span><span class="op">(</span></span>
|
||||
<span id="cb12-18"><a href="#cb12-18"></a> AuxPcodeEmulator<span class="op"><</span>Expr<span class="op">></span> emulator<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-19"><a href="#cb12-19"></a> <span class="cf">return</span> PcodeUseropLibrary<span class="op">.</span><span class="fu">nil</span><span class="op">();</span></span>
|
||||
<span id="cb12-20"><a href="#cb12-20"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-21"><a href="#cb12-21"></a></span>
|
||||
<span id="cb12-22"><a href="#cb12-22"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-23"><a href="#cb12-23"></a> <span class="kw">public</span> PcodeUseropLibrary<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">>></span> <span class="fu">createLocalUseropLibrary</span><span class="op">(</span></span>
|
||||
<span id="cb12-24"><a href="#cb12-24"></a> AuxPcodeEmulator<span class="op"><</span>Expr<span class="op">></span> emulator<span class="op">,</span> PcodeThread<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">>></span> thread<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-25"><a href="#cb12-25"></a> <span class="cf">return</span> PcodeUseropLibrary<span class="op">.</span><span class="fu">nil</span><span class="op">();</span></span>
|
||||
<span id="cb12-26"><a href="#cb12-26"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-27"><a href="#cb12-27"></a></span>
|
||||
<span id="cb12-28"><a href="#cb12-28"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-29"><a href="#cb12-29"></a> <span class="kw">public</span> PcodeExecutorState<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">>></span> <span class="fu">createSharedState</span><span class="op">(</span></span>
|
||||
<span id="cb12-30"><a href="#cb12-30"></a> AuxPcodeEmulator<span class="op"><</span>Expr<span class="op">></span> emulator<span class="op">,</span> BytesPcodeExecutorStatePiece concrete<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-31"><a href="#cb12-31"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">BytesExprPcodeExecutorState</span><span class="op">(</span>concrete<span class="op">);</span></span>
|
||||
<span id="cb12-32"><a href="#cb12-32"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-33"><a href="#cb12-33"></a></span>
|
||||
<span id="cb12-34"><a href="#cb12-34"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-35"><a href="#cb12-35"></a> <span class="kw">public</span> PcodeExecutorState<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">>></span> <span class="fu">createLocalState</span><span class="op">(</span></span>
|
||||
<span id="cb12-36"><a href="#cb12-36"></a> AuxPcodeEmulator<span class="op"><</span>Expr<span class="op">></span> emulator<span class="op">,</span> PcodeThread<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> Expr<span class="op">>></span> thread<span class="op">,</span></span>
|
||||
<span id="cb12-37"><a href="#cb12-37"></a> BytesPcodeExecutorStatePiece concrete<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-38"><a href="#cb12-38"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">BytesExprPcodeExecutorState</span><span class="op">(</span>concrete<span class="op">);</span></span>
|
||||
<span id="cb12-39"><a href="#cb12-39"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-40"><a href="#cb12-40"></a></span>
|
||||
<span id="cb12-41"><a href="#cb12-41"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-42"><a href="#cb12-42"></a> <span class="kw">public</span> TracePcodeExecutorState<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> ModelingScript<span class="op">.</span><span class="fu">Expr</span><span class="op">>></span> <span class="fu">createTraceSharedState</span><span class="op">(</span></span>
|
||||
<span id="cb12-43"><a href="#cb12-43"></a> AuxTracePcodeEmulator<span class="op"><</span>ModelingScript<span class="op">.</span><span class="fu">Expr</span><span class="op">></span> emulator<span class="op">,</span></span>
|
||||
<span id="cb12-44"><a href="#cb12-44"></a> BytesTracePcodeExecutorStatePiece concrete<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-45"><a href="#cb12-45"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">ExprTracePcodeExecutorState</span><span class="op">(</span>concrete<span class="op">);</span></span>
|
||||
<span id="cb12-46"><a href="#cb12-46"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-47"><a href="#cb12-47"></a></span>
|
||||
<span id="cb12-48"><a href="#cb12-48"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-49"><a href="#cb12-49"></a> <span class="kw">public</span> TracePcodeExecutorState<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> ModelingScript<span class="op">.</span><span class="fu">Expr</span><span class="op">>></span> <span class="fu">createTraceLocalState</span><span class="op">(</span></span>
|
||||
<span id="cb12-50"><a href="#cb12-50"></a> AuxTracePcodeEmulator<span class="op"><</span>ModelingScript<span class="op">.</span><span class="fu">Expr</span><span class="op">></span> emulator<span class="op">,</span></span>
|
||||
<span id="cb12-51"><a href="#cb12-51"></a> PcodeThread<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> ModelingScript<span class="op">.</span><span class="fu">Expr</span><span class="op">>></span> thread<span class="op">,</span></span>
|
||||
<span id="cb12-52"><a href="#cb12-52"></a> BytesTracePcodeExecutorStatePiece concrete<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-53"><a href="#cb12-53"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">ExprTracePcodeExecutorState</span><span class="op">(</span>concrete<span class="op">);</span></span>
|
||||
<span id="cb12-54"><a href="#cb12-54"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-55"><a href="#cb12-55"></a></span>
|
||||
<span id="cb12-56"><a href="#cb12-56"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-57"><a href="#cb12-57"></a> <span class="kw">public</span> TracePcodeExecutorState<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> ModelingScript<span class="op">.</span><span class="fu">Expr</span><span class="op">>></span> <span class="fu">createDebuggerSharedState</span><span class="op">(</span></span>
|
||||
<span id="cb12-58"><a href="#cb12-58"></a> AuxDebuggerPcodeEmulator<span class="op"><</span>ModelingScript<span class="op">.</span><span class="fu">Expr</span><span class="op">></span> emulator<span class="op">,</span></span>
|
||||
<span id="cb12-59"><a href="#cb12-59"></a> RWTargetMemoryPcodeExecutorStatePiece concrete<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-60"><a href="#cb12-60"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">ExprTracePcodeExecutorState</span><span class="op">(</span>concrete<span class="op">);</span></span>
|
||||
<span id="cb12-61"><a href="#cb12-61"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-62"><a href="#cb12-62"></a></span>
|
||||
<span id="cb12-63"><a href="#cb12-63"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-64"><a href="#cb12-64"></a> <span class="kw">public</span> TracePcodeExecutorState<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> ModelingScript<span class="op">.</span><span class="fu">Expr</span><span class="op">>></span> <span class="fu">createDebuggerLocalState</span><span class="op">(</span></span>
|
||||
<span id="cb12-65"><a href="#cb12-65"></a> AuxDebuggerPcodeEmulator<span class="op"><</span>ModelingScript<span class="op">.</span><span class="fu">Expr</span><span class="op">></span> emulator<span class="op">,</span></span>
|
||||
<span id="cb12-66"><a href="#cb12-66"></a> PcodeThread<span class="op"><</span>Pair<span class="op"><</span><span class="dt">byte</span><span class="op">[],</span> ModelingScript<span class="op">.</span><span class="fu">Expr</span><span class="op">>></span> thread<span class="op">,</span></span>
|
||||
<span id="cb12-67"><a href="#cb12-67"></a> RWTargetRegistersPcodeExecutorStatePiece concrete<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-68"><a href="#cb12-68"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">ExprTracePcodeExecutorState</span><span class="op">(</span>concrete<span class="op">);</span></span>
|
||||
<span id="cb12-69"><a href="#cb12-69"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-70"><a href="#cb12-70"></a><span class="op">}</span></span>
|
||||
<span id="cb12-71"><a href="#cb12-71"></a></span>
|
||||
<span id="cb12-72"><a href="#cb12-72"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> BytesExprDebuggerPcodeEmulator <span class="kw">extends</span> AuxDebuggerPcodeEmulator<span class="op"><</span>Expr<span class="op">></span> <span class="op">{</span></span>
|
||||
<span id="cb12-73"><a href="#cb12-73"></a> <span class="kw">public</span> <span class="fu">BytesExprDebuggerPcodeEmulator</span><span class="op">(</span>PcodeDebuggerAccess access<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-74"><a href="#cb12-74"></a> <span class="kw">super</span><span class="op">(</span>access<span class="op">);</span></span>
|
||||
<span id="cb12-75"><a href="#cb12-75"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-76"><a href="#cb12-76"></a></span>
|
||||
<span id="cb12-77"><a href="#cb12-77"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-78"><a href="#cb12-78"></a> <span class="kw">protected</span> AuxDebuggerEmulatorPartsFactory<span class="op"><</span>Expr<span class="op">></span> <span class="fu">getPartsFactory</span><span class="op">()</span> <span class="op">{</span></span>
|
||||
<span id="cb12-79"><a href="#cb12-79"></a> <span class="cf">return</span> BytesExprDebuggerEmulatorPartsFactory<span class="op">.</span><span class="fu">INSTANCE</span><span class="op">;</span></span>
|
||||
<span id="cb12-80"><a href="#cb12-80"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-81"><a href="#cb12-81"></a><span class="op">}</span></span>
|
||||
<span id="cb12-82"><a href="#cb12-82"></a></span>
|
||||
<span id="cb12-83"><a href="#cb12-83"></a><span class="kw">public</span> <span class="dt">static</span> <span class="kw">class</span> BytesExprDebuggerPcodeEmulatorFactory</span>
|
||||
<span id="cb12-84"><a href="#cb12-84"></a> <span class="kw">extends</span> AbstractDebuggerPcodeEmulatorFactory <span class="op">{</span></span>
|
||||
<span id="cb12-85"><a href="#cb12-85"></a></span>
|
||||
<span id="cb12-86"><a href="#cb12-86"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-87"><a href="#cb12-87"></a> <span class="kw">public</span> <span class="bu">String</span> <span class="fu">getTitle</span><span class="op">()</span> <span class="op">{</span></span>
|
||||
<span id="cb12-88"><a href="#cb12-88"></a> <span class="cf">return</span> <span class="st">"Expr"</span><span class="op">;</span></span>
|
||||
<span id="cb12-89"><a href="#cb12-89"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-90"><a href="#cb12-90"></a></span>
|
||||
<span id="cb12-91"><a href="#cb12-91"></a> <span class="at">@Override</span></span>
|
||||
<span id="cb12-92"><a href="#cb12-92"></a> <span class="kw">public</span> DebuggerPcodeMachine<span class="op"><?></span> <span class="fu">create</span><span class="op">(</span>PcodeDebuggerAccess access<span class="op">)</span> <span class="op">{</span></span>
|
||||
<span id="cb12-93"><a href="#cb12-93"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">BytesExprDebuggerPcodeEmulator</span><span class="op">(</span>access<span class="op">);</span></span>
|
||||
<span id="cb12-94"><a href="#cb12-94"></a> <span class="op">}</span></span>
|
||||
<span id="cb12-95"><a href="#cb12-95"></a><span class="op">}</span></span></code></pre></div>
|
||||
<p>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.
|
||||
|
@ -95,12 +95,12 @@ A user would place a breakpoint at either the call site or the call target, have
|
||||
|
||||
### 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.
|
||||
The advantage to Java callbacks is that things are relatively intuitive to do, but the temptation, which we intentionally demonstrated above, 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.
|
||||
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.
|
||||
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}
|
||||
@ -165,9 +165,9 @@ We could just use them directly in the source, but this demonstrates the ability
|
||||
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.
|
||||
The advantage here is that the p-code will 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()`.
|
||||
Some symbolic models might just like to see an abstract call to `strlen()`.
|
||||
|
||||
### Modeling by Structured Sleigh
|
||||
|
||||
@ -276,7 +276,7 @@ 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:
|
||||
If you would like to (temporarily) override the GUI with a custom userop library, you can by setting the GUI's emulator factory:
|
||||
|
||||
```java {.numberLines}
|
||||
public class InstallCustomLibraryScript extends GhidraScript implements FlatDebuggerAPI {
|
||||
@ -517,7 +517,7 @@ The composition of states with the same addressing model is common enough that G
|
||||
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.
|
||||
A "state" is also a "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.
|
||||
|
||||
@ -548,10 +548,14 @@ public static class ExprSpace {
|
||||
map.put(offset, val);
|
||||
}
|
||||
|
||||
protected Expr whenNull(long offset, int size) {
|
||||
return new VarExpr(space, offset, size);
|
||||
}
|
||||
|
||||
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);
|
||||
return expr != null ? expr : whenNull(offset, size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -634,6 +638,7 @@ Notably, we have neglected the possibility that writes overlap or that reads are
|
||||
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.
|
||||
We factor `whenNull` so that it can be overridden later.
|
||||
|
||||
The remaining parts are mostly boilerplate.
|
||||
We implement the "state piece" interface by creating another abstract class.
|
||||
@ -760,7 +765,7 @@ public class ModelingScript extends GhidraScript {
|
||||
```
|
||||
|
||||
**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`.
|
||||
If you use the arithmetic to generate them, remember that it will use `fromConst` on both sub-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
|
||||
@ -820,7 +825,7 @@ public static class ExprTraceSpace extends ExprSpace {
|
||||
public static class ExprTracePcodeExecutorStatePiece
|
||||
extends AbstractExprPcodeExecutorStatePiece<ExprTraceSpace>
|
||||
implements TracePcodeExecutorStatePiece<byte[], Expr> {
|
||||
public static final String NAME = "Taint";
|
||||
public static final String NAME = "Expr";
|
||||
|
||||
protected final PcodeTraceDataAccess data;
|
||||
protected final PcodeTracePropertyAccess<String> property;
|
||||
@ -865,6 +870,14 @@ public static class ExprTracePcodeExecutorStatePiece
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExprTracePcodeExecutorState
|
||||
extends PairedTracePcodeExecutorState<byte[], Expr> {
|
||||
public ExprTracePcodeExecutorState(TracePcodeExecutorStatePiece<byte[], byte[]> concrete) {
|
||||
super(new PairedTracePcodeExecutorStatePiece<>(concrete,
|
||||
new ExprTracePcodeExecutorStatePiece(concrete.getData())));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Because we do not need any additional logic for target integration, we do not need to extend the state pieces any further.
|
||||
@ -873,6 +886,77 @@ 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 enum BytesExprDebuggerEmulatorPartsFactory
|
||||
implements AuxDebuggerEmulatorPartsFactory<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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], ModelingScript.Expr>> createTraceSharedState(
|
||||
AuxTracePcodeEmulator<ModelingScript.Expr> emulator,
|
||||
BytesTracePcodeExecutorStatePiece concrete) {
|
||||
return new ExprTracePcodeExecutorState(concrete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], ModelingScript.Expr>> createTraceLocalState(
|
||||
AuxTracePcodeEmulator<ModelingScript.Expr> emulator,
|
||||
PcodeThread<Pair<byte[], ModelingScript.Expr>> thread,
|
||||
BytesTracePcodeExecutorStatePiece concrete) {
|
||||
return new ExprTracePcodeExecutorState(concrete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], ModelingScript.Expr>> createDebuggerSharedState(
|
||||
AuxDebuggerPcodeEmulator<ModelingScript.Expr> emulator,
|
||||
RWTargetMemoryPcodeExecutorStatePiece concrete) {
|
||||
return new ExprTracePcodeExecutorState(concrete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TracePcodeExecutorState<Pair<byte[], ModelingScript.Expr>> createDebuggerLocalState(
|
||||
AuxDebuggerPcodeEmulator<ModelingScript.Expr> emulator,
|
||||
PcodeThread<Pair<byte[], ModelingScript.Expr>> thread,
|
||||
RWTargetRegistersPcodeExecutorStatePiece concrete) {
|
||||
return new ExprTracePcodeExecutorState(concrete);
|
||||
}
|
||||
}
|
||||
|
||||
public static class BytesExprDebuggerPcodeEmulator extends AuxDebuggerPcodeEmulator<Expr> {
|
||||
public BytesExprDebuggerPcodeEmulator(PcodeDebuggerAccess access) {
|
||||
super(access);
|
||||
@ -885,7 +969,7 @@ public static class BytesExprDebuggerPcodeEmulator extends AuxDebuggerPcodeEmula
|
||||
}
|
||||
|
||||
public static class BytesExprDebuggerPcodeEmulatorFactory
|
||||
implements DebuggerPcodeEmulatorFactory {
|
||||
extends AbstractDebuggerPcodeEmulatorFactory {
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 207 KiB After Width: | Height: | Size: 214 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 190 KiB After Width: | Height: | Size: 199 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@ -109,6 +109,12 @@ img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #444;
|
||||
border-radius: 1pt;
|
||||
padding: 1pt;
|
||||
}
|
||||
|
||||
div.sourceCode {
|
||||
position: relative;
|
||||
color: black;
|
||||
@ -118,6 +124,10 @@ div.sourceCode {
|
||||
box-shadow: 0 0 5px black inset;
|
||||
}
|
||||
|
||||
code.sourceCode {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
pre > code.sourceCode > span > a:first-child::before {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
@ -102,6 +102,8 @@ 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_LaunchGDBDialog.png||GHIDRA||||END|
|
||||
GhidraClass/Debugger/images/GettingStarted_Termmines.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|
|
||||
@ -112,8 +114,9 @@ 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/RemoteTargets_AcceptTraceRmi.png||GHIDRA||||END|
|
||||
GhidraClass/Debugger/images/RemoteTargets_GdbPlusGdbserverViaSsh.png||GHIDRA||||END|
|
||||
GhidraClass/Debugger/images/RemoteTargets_GdbViaSsh.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|
|
||||
|