diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java index ab7390f161..7a2d656f8c 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/DebuggerRegistersProvider.java @@ -35,6 +35,7 @@ import docking.*; import docking.action.*; import docking.action.builder.ActionBuilder; import docking.actions.PopupActionProvider; +import docking.widgets.AbstractGCellRenderer; import docking.widgets.table.*; import docking.widgets.table.ColumnSortState.SortDirection; import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn; @@ -411,28 +412,32 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter } } + public static void applyStateColors(AbstractGCellRenderer renderer, + GTableCellRenderingData data, Predicate isChanged) { + RegisterRow row = (RegisterRow) data.getRowObject(); + if (!row.isKnown()) { + if (data.isSelected()) { + renderer.setForeground(COLOR_FOREGROUND_STALE_SEL); + } + else { + renderer.setForeground(COLOR_FOREGROUND_STALE); + } + } + else if (isChanged.test(row)) { + if (data.isSelected()) { + renderer.setForeground(COLOR_FOREGROUND_CHANGED_SEL); + } + else { + renderer.setForeground(COLOR_FOREGROUND_CHANGED); + } + } + } + static class RegisterValueCellRenderer extends HexDefaultGColumnRenderer { @Override public final Component getTableCellRendererComponent(GTableCellRenderingData data) { super.getTableCellRendererComponent(data); - setFont(getFixedWidthFont()); - RegisterRow row = (RegisterRow) data.getRowObject(); - if (!row.isKnown()) { - if (data.isSelected()) { - setForeground(COLOR_FOREGROUND_STALE_SEL); - } - else { - setForeground(COLOR_FOREGROUND_STALE); - } - } - else if (row.isChanged()) { - if (data.isSelected()) { - setForeground(COLOR_FOREGROUND_CHANGED_SEL); - } - else { - setForeground(COLOR_FOREGROUND_CHANGED); - } - } + applyStateColors(this, data, RegisterRow::isChanged); return this; } } @@ -1055,7 +1060,7 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter if (previous.getThread() == null || current.getThread() == null) { return false; } - if (previous.getPlatform().getLanguage() != current.getPlatform().getLanguage()) { + if (previous.getLanguage() != current.getLanguage()) { return false; } if (!isRegisterKnown(register)) { @@ -1328,6 +1333,10 @@ public class DebuggerRegistersProvider extends ComponentProviderAdapter return current; } + public DebuggerCoordinates getPrevious() { + return previous; + } + private void reportError(String title, String message, Throwable ex) { plugin.getTool().setStatusInfo(message + ": " + ex.getMessage()); if (title != null) { diff --git a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java index 482b4cc10f..f7342e8768 100644 --- a/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java +++ b/Ghidra/Debug/Debugger/src/main/java/ghidra/app/plugin/core/debug/gui/register/RegisterRow.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -211,4 +211,13 @@ public class RegisterRow { public DebuggerCoordinates getCurrent() { return provider.getCurrent(); } + + /** + * Get the table's previous coordinates (for change indication) + * + * @return the coordinates + */ + public DebuggerCoordinates getPrevious() { + return provider.getPrevious(); + } } diff --git a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintDebuggerRegisterColumnFactory.java b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintDebuggerRegisterColumnFactory.java index acd1ca8411..ce95b536c5 100644 --- a/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintDebuggerRegisterColumnFactory.java +++ b/Ghidra/Debug/TaintAnalysis/src/main/java/ghidra/taint/gui/field/TaintDebuggerRegisterColumnFactory.java @@ -15,20 +15,22 @@ */ package ghidra.taint.gui.field; -import docking.widgets.table.AbstractDynamicTableColumn; -import docking.widgets.table.DynamicTableColumn; -import ghidra.app.plugin.core.debug.gui.register.DebuggerRegisterColumnFactory; -import ghidra.app.plugin.core.debug.gui.register.RegisterRow; +import java.awt.Component; +import java.util.Objects; + +import docking.widgets.table.*; +import ghidra.app.plugin.core.debug.gui.register.*; import ghidra.debug.api.tracemgr.DebuggerCoordinates; import ghidra.docking.settings.Settings; import ghidra.framework.plugintool.ServiceProvider; import ghidra.pcode.emu.taint.state.TaintPieceHandler; -import ghidra.program.model.address.Address; -import ghidra.program.model.address.AddressSpace; +import ghidra.program.model.address.*; import ghidra.program.model.lang.Register; -import ghidra.trace.model.Trace; import ghidra.trace.model.property.TracePropertyMap; import ghidra.trace.model.property.TracePropertyMapSpace; +import ghidra.trace.model.thread.TraceThread; +import ghidra.util.table.column.AbstractGColumnRenderer; +import ghidra.util.table.column.GColumnRenderer; /** * A factory for the "Taint" column in the "Registers" panel @@ -38,60 +40,121 @@ import ghidra.trace.model.property.TracePropertyMapSpace; * screen. */ public class TaintDebuggerRegisterColumnFactory implements DebuggerRegisterColumnFactory { + private static TracePropertyMapSpace getTaintSpace(DebuggerCoordinates coords, + Register register) { + TracePropertyMap taintMap = + coords.getTrace().getAddressPropertyManager().getPropertyMap(PROP_NAME, String.class); + + if (taintMap == null) { + return null; + } + + AddressSpace addressSpace = register.getAddressSpace(); + if (addressSpace.isRegisterSpace()) { + return taintMap.getPropertyMapRegisterSpace(coords.getThread(), + coords.getFrame(), false); + } + return taintMap.getPropertyMapSpace(addressSpace, false); + } + + private static String getTaintValue(DebuggerCoordinates coords, + TracePropertyMapSpace taintSpace, Register register) { + // Cheat the deserialization/reserialization here + AddressRange range = coords.getPlatform() + .getConventionalRegisterRange(taintSpace.getAddressSpace(), register); + StringBuffer vec = new StringBuffer(); + for (Address addr : range) { + vec.append('['); + String taint = taintSpace.get(coords.getViewSnap(), addr); + vec.append(taint == null ? "" : taint); + vec.append(']'); + } + return vec.toString(); + } + + private static String getTaintValue(DebuggerCoordinates coords, RegisterRow row) { + TraceThread thread = coords.getThread(); + if (thread == null) { + return null; + } + + Register register = row.getRegister(); + TracePropertyMapSpace taintSpace = getTaintSpace(coords, register); + if (taintSpace == null) { + return null; + } + + return getTaintValue(coords, taintSpace, register); + } + + private static class TaintDebuggerRegisterCellRenderer extends AbstractGColumnRenderer { + @Override + public Component getTableCellRendererComponent(GTableCellRenderingData data) { + super.getTableCellRendererComponent(data); + DebuggerRegistersProvider.applyStateColors(this, data, this::isChanged); + return this; + } + + @Override + public String getFilterString(String t, Settings settings) { + return t; + } + + private boolean isChanged(RegisterRow row) { + if (row.getPrevious().getThread() == null || row.getCurrent().getThread() == null) { + return false; + } + if (row.getPrevious().getLanguage() != row.getCurrent().getLanguage()) { + return false; + } + if (!row.isKnown()) { + return false; + } + Register register = row.getRegister(); + TracePropertyMapSpace curTaintSpace = + getTaintSpace(row.getCurrent(), register); + if (curTaintSpace == null) { + return false; // unlikely + } + TracePropertyMapSpace prevTaintSpace = + getTaintSpace(row.getPrevious(), register); + if (prevTaintSpace == null) { + return false; + } + String curTaintValue = getTaintValue(row.getCurrent(), curTaintSpace, register); + String prevTaintValue = getTaintValue(row.getPrevious(), prevTaintSpace, register); + return !Objects.equals(curTaintValue, prevTaintValue); + } + } + + private static final TaintDebuggerRegisterCellRenderer RENDERER = + new TaintDebuggerRegisterCellRenderer(); + + private static class TaintDebuggerRegisterColumn + extends AbstractDynamicTableColumn { + @Override + public String getColumnName() { + return COL_NAME; + } + + @Override + public GColumnRenderer getColumnRenderer() { + return RENDERER; + } + + @Override + public String getValue(RegisterRow rowObject, Settings settings, Void dataSource, + ServiceProvider serviceProvider) throws IllegalArgumentException { + String value = getTaintValue(rowObject.getCurrent(), rowObject); + return value == null ? "" : value; + } + } + protected static final String PROP_NAME = TaintPieceHandler.NAME; public static final String COL_NAME = "Taint"; @Override public DynamicTableColumn create() { - return new AbstractDynamicTableColumn() { - @Override - public String getColumnName() { - return COL_NAME; - } - - @Override - public String getValue(RegisterRow rowObject, Settings settings, Void dataSource, - ServiceProvider serviceProvider) throws IllegalArgumentException { - DebuggerCoordinates current = rowObject.getCurrent(); - Trace trace = current.getTrace(); - if (trace == null) { - return ""; - } - - TracePropertyMap taintMap = current.getTrace() - .getAddressPropertyManager() - .getPropertyMap(PROP_NAME, String.class); - - if (taintMap == null) { - return ""; - } - - Register register = rowObject.getRegister(); - TracePropertyMapSpace taintSpace; - AddressSpace addressSpace = register.getAddressSpace(); - if (addressSpace.isRegisterSpace()) { - taintSpace = taintMap.getPropertyMapRegisterSpace(current.getThread(), - current.getFrame(), false); - } - else { - taintSpace = taintMap.getPropertyMapSpace(addressSpace, false); - } - if (taintSpace == null) { - return ""; - } - - // Cheat the deserialization/reserialization here - StringBuffer vec = new StringBuffer(); - int count = register.getNumBytes(); - Address start = register.getAddress(); - for (int i = 0; i < count; i++) { - vec.append('['); - String taint = taintSpace.get(current.getViewSnap(), start.addWrap(i)); - vec.append(taint == null ? "" : taint); - vec.append(']'); - } - return vec.toString(); - } - }; + return new TaintDebuggerRegisterColumn(); } }