GP-2426: Refactor emulator to use trace access shims. Implement register mapping conventions.

This commit is contained in:
Dan 2022-09-13 16:02:02 -04:00
parent 975db1919c
commit e4f18ad824
202 changed files with 8221 additions and 4199 deletions

View file

@ -17,12 +17,11 @@ package ghidra.pcode.emu.taint;
import org.apache.commons.lang3.tuple.Pair;
import ghidra.app.plugin.core.debug.service.emulation.ReadsTargetMemoryPcodeExecutorStatePiece;
import ghidra.app.plugin.core.debug.service.emulation.ReadsTargetRegistersPcodeExecutorStatePiece;
import ghidra.app.plugin.core.debug.service.emulation.RWTargetMemoryPcodeExecutorStatePiece;
import ghidra.app.plugin.core.debug.service.emulation.RWTargetRegistersPcodeExecutorStatePiece;
import ghidra.pcode.emu.*;
import ghidra.pcode.emu.DefaultPcodeThread.PcodeThreadExecutor;
import ghidra.pcode.emu.auxiliary.AuxPcodeEmulator;
import ghidra.pcode.emu.taint.full.TaintDebuggerPcodeExecutorState;
import ghidra.pcode.emu.taint.plain.TaintPcodeExecutorState;
import ghidra.pcode.emu.taint.trace.TaintTracePcodeExecutorState;
import ghidra.pcode.exec.*;
@ -56,7 +55,7 @@ import ghidra.taint.model.TaintVec;
* <ul>
* <li>Stand alone: {@link TaintPcodeExecutorState}</li>
* <li>Trace integrated: {@link TaintTracePcodeExecutorState}</li>
* <li>Debugger integrated: {@link TaintDebuggerPcodeExecutorState}</li>
* <li>Debugger integrated: {@link TaintTracePcodeExecutorState} (same as Trace integrated)</li>
* </ul>
* </ul>
*
@ -101,8 +100,8 @@ public enum TaintPartsFactory implements AuxDebuggerEmulatorPartsFactory<TaintVe
* {@inheritDoc}
*
* <p>
* We have no thread-specific userops to add, which means we also have no need to stubs, so here
* we just return the empty library.
* We have no thread-specific userops to add, which means we also have no need for stubs, so
* here we just return the empty library.
*/
@Override
public PcodeUseropLibrary<Pair<byte[], TaintVec>> createLocalUseropStub(
@ -202,16 +201,17 @@ public enum TaintPartsFactory implements AuxDebuggerEmulatorPartsFactory<TaintVe
* <p>
* If you're following the {@link ghidra.taint} package documentation, please finish reading
* about the stand-alone and trace-integrated emulators before proceeding to this
* Debugger-integrated part. This part extends the shared state of the trace-integrated emulator
* so that it can also deserialize taint sets from mapped static programs. Since taint is not
* generally a concept understood by a live debugger, we need not retrieve anything (other than
* concrete state) from the target.
* Debugger-integrated part. This part uses the same shared state as the trace-integrated
* emulator but takes a different data access shim, so that it can also deserialize taint sets
* from mapped static programs. Since taint is not generally a concept understood by a live
* debugger, we need not retrieve anything new from the target. Note that the shim is passed to
* us implicitly via the {@code concrete} state.
*/
@Override
public TracePcodeExecutorState<Pair<byte[], TaintVec>> createDebuggerSharedState(
AuxDebuggerPcodeEmulator<TaintVec> emulator,
ReadsTargetMemoryPcodeExecutorStatePiece concrete) {
return new TaintDebuggerPcodeExecutorState(concrete);
RWTargetMemoryPcodeExecutorStatePiece concrete) {
return new TaintTracePcodeExecutorState(concrete);
}
/**
@ -221,16 +221,16 @@ public enum TaintPartsFactory implements AuxDebuggerEmulatorPartsFactory<TaintVe
* If you're following the {@link ghidra.taint} package documentation, please finish reading
* about the stand-alone and trace-integrated emulators before proceeding to this method. Since
* taint is not generally a concept understood by a live debugger, we need not retrieve anything
* (other than concrete state) from the target. Furthermore, because static program mappings do
* not apply to registers, we need not consider them. Thus, we can just re-use the
* trace-integrated local state. The concrete piece given to us, which we just pass to our
* paired state, will handle retrieving concrete values from the live target, if applicable.
* new from the target. Furthermore, because static program mappings do not apply to registers,
* we need not consider them. Thus, we can just re-use the trace-integrated local state. The
* concrete piece given to us, which we just pass to our paired state, will handle retrieving
* concrete values from the live target, if applicable.
*/
@Override
public TracePcodeExecutorState<Pair<byte[], TaintVec>> createDebuggerLocalState(
AuxDebuggerPcodeEmulator<TaintVec> emulator,
PcodeThread<Pair<byte[], TaintVec>> emuThread,
ReadsTargetRegistersPcodeExecutorStatePiece concrete) {
RWTargetRegistersPcodeExecutorStatePiece concrete) {
return new TaintTracePcodeExecutorState(concrete);
}
}

View file

@ -15,14 +15,12 @@
*/
package ghidra.pcode.emu.taint.full;
import ghidra.app.services.TraceRecorder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.app.plugin.core.debug.service.emulation.data.*;
import ghidra.pcode.emu.taint.TaintPartsFactory;
import ghidra.pcode.emu.taint.plain.TaintPcodeEmulator;
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerEmulatorPartsFactory;
import ghidra.pcode.exec.debug.auxiliary.AuxDebuggerPcodeEmulator;
import ghidra.taint.model.TaintVec;
import ghidra.trace.model.Trace;
/**
* A Debugger-integrated emulator with taint analysis
@ -31,14 +29,10 @@ public class TaintDebuggerPcodeEmulator extends AuxDebuggerPcodeEmulator<TaintVe
/**
* Create an emulator
*
* @param tool the tool creating the emulator
* @param trace the source trace
* @param snap the source snap
* @param recorder if applicable, the recorder for the trace's live target
* @param data the trace-and-debugger access shim
*/
public TaintDebuggerPcodeEmulator(PluginTool tool, Trace trace, long snap,
TraceRecorder recorder) {
super(tool, trace, snap, recorder);
public TaintDebuggerPcodeEmulator(PcodeDebuggerAccess data) {
super(data);
}
/**

View file

@ -17,9 +17,7 @@ package ghidra.pcode.emu.taint.full;
import ghidra.app.plugin.core.debug.service.emulation.DebuggerPcodeEmulatorFactory;
import ghidra.app.plugin.core.debug.service.emulation.DebuggerPcodeMachine;
import ghidra.app.services.TraceRecorder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.trace.model.Trace;
import ghidra.app.plugin.core.debug.service.emulation.data.PcodeDebuggerAccess;
/**
* An emulator factory for making the {@link TaintDebuggerPcodeEmulator} discoverable to the UI
@ -36,8 +34,7 @@ public class TaintDebuggerPcodeEmulatorFactory implements DebuggerPcodeEmulatorF
}
@Override
public DebuggerPcodeMachine<?> create(PluginTool tool, Trace trace, long snap,
TraceRecorder recorder) {
return new TaintDebuggerPcodeEmulator(tool, trace, snap, recorder);
public DebuggerPcodeMachine<?> create(PcodeDebuggerAccess data) {
return new TaintDebuggerPcodeEmulator(data);
}
}

View file

@ -1,53 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.taint.full;
import ghidra.app.plugin.core.debug.service.emulation.ReadsTargetMemoryPcodeExecutorStatePiece;
import ghidra.pcode.emu.taint.trace.TaintTracePcodeExecutorState;
/**
* A paired concrete-plus-taint Debugger-integrated state
*
* <p>
* This contains the emulator's machine state along with the taint markings, just like
* {@link TaintTracePcodeExecutorState}, except that it can also read state from mapped static
* programs. In reality, this just composes concrete and taint state pieces, which actually do all
* the work.
*/
public class TaintDebuggerPcodeExecutorState extends TaintTracePcodeExecutorState {
/**
* Create a state from the two given pieces
*
* @param concrete the concrete piece
* @param the taint piece
*/
public TaintDebuggerPcodeExecutorState(ReadsTargetMemoryPcodeExecutorStatePiece concrete,
TaintDebuggerPcodeExecutorStatePiece taint) {
super(concrete, taint);
}
/**
* Create a state from the given concrete piece and an internally constructed taint piece
*
* @param concrete the concrete piece
*/
public TaintDebuggerPcodeExecutorState(ReadsTargetMemoryPcodeExecutorStatePiece concrete) {
super(concrete, new TaintDebuggerPcodeExecutorStatePiece(
concrete.getTool(), concrete.getTrace(), concrete.getSnap(), concrete.getThread(),
concrete.getFrame(), concrete.getRecorder()));
}
}

View file

@ -1,87 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.taint.full;
import ghidra.app.services.TraceRecorder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.pcode.emu.taint.trace.*;
import ghidra.program.model.address.AddressSpace;
import ghidra.trace.model.Trace;
import ghidra.trace.model.property.TracePropertyMapSpace;
import ghidra.trace.model.thread.TraceThread;
/**
* The Debugger-integrated state piece for holding taint marks
*
* <p>
* Because we don't require a derivative of this class, it is not split into abstract and
* non-abstract classes (like its super-classes were). This substitutes {@link TaintDebuggerSpace}
* for {@link TaintTraceSpace} and introduces parameters for loading information from mapped static
* programs. We take the recorder more as a matter of form, since we don't really need it.
*/
public class TaintDebuggerPcodeExecutorStatePiece
extends AbstractTaintTracePcodeExecutorStatePiece<TaintDebuggerSpace> {
protected final PluginTool tool;
protected final TraceRecorder recorder;
/**
* Create the taint piece
*
* @param tool the tool that created the emulator
* @param trace the trace from which to load taint marks
* @param snap the snap from which to load taint marks
* @param thread if a register space, the thread from which to load taint marks
* @param frame if a register space, the frame
* @param recorder if applicable, the recorder for the trace's live target
*/
public TaintDebuggerPcodeExecutorStatePiece(PluginTool tool, Trace trace, long snap,
TraceThread thread, int frame, TraceRecorder recorder) {
super(trace, snap, thread, frame);
this.tool = tool;
this.recorder = recorder;
}
/**
* {@inheritDoc}
*
* <p>
* Here we create a map that uses {@link TaintDebuggerSpace}s. There is some repeated code with
* {@link TaintTracePcodeExecutorStatePiece#newSpaceMap()}. We could factor that, but I thought
* it a little pedantic.
*/
@Override
protected AbstractSpaceMap<TaintDebuggerSpace> newSpaceMap() {
return new CacheingSpaceMap<TracePropertyMapSpace<String>, TaintDebuggerSpace>() {
@Override
protected TracePropertyMapSpace<String> getBacking(AddressSpace space) {
if (map == null) {
return null;
}
if (space.isRegisterSpace()) {
return map.getPropertyMapRegisterSpace(thread, frame, false);
}
return map.getPropertyMapSpace(space, false);
}
@Override
protected TaintDebuggerSpace newSpace(AddressSpace space,
TracePropertyMapSpace<String> backing) {
return new TaintDebuggerSpace(tool, trace, space, backing, snap);
}
};
}
}

View file

@ -1,90 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.taint.full;
import com.google.common.collect.Range;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.framework.plugintool.PluginTool;
import ghidra.pcode.emu.taint.trace.TaintTracePcodeExecutorStatePiece;
import ghidra.pcode.emu.taint.trace.TaintTraceSpace;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.util.StringPropertyMap;
import ghidra.program.util.ProgramLocation;
import ghidra.taint.model.TaintSet;
import ghidra.trace.model.DefaultTraceLocation;
import ghidra.trace.model.Trace;
import ghidra.trace.model.property.TracePropertyMapSpace;
/**
* The storage space for taint sets in a trace's address space
*
* <p>
* This adds to {@link TaintTraceSpace} the ability to load taint sets from mapped static programs.
*/
public class TaintDebuggerSpace extends TaintTraceSpace {
protected final PluginTool tool;
protected final Trace trace;
/**
* Create the space
*
* @param tool the the tool that created the emulator
* @param trace the trace backing this space
* @param space the address space
* @param backing if present, the backing object
* @param snap the source snap
*/
public TaintDebuggerSpace(PluginTool tool, Trace trace, AddressSpace space,
TracePropertyMapSpace<String> backing, long snap) {
super(space, backing, snap);
this.tool = tool;
this.trace = trace;
}
/**
* {@inheritDoc}
*
* <p>
* The taint trace space will call this when the cache misses and the trace has no taint set
* stored, allowing us to populate it with a taint set stored in a mapped static program. See
* notes in {@link TaintTraceSpace#whenNull(long)}.
*/
@Override
protected TaintSet whenTraceNull(long offset) {
DebuggerStaticMappingService mappingService =
tool.getService(DebuggerStaticMappingService.class);
ProgramLocation sloc =
mappingService.getOpenMappedLocation(new DefaultTraceLocation(trace, null,
Range.singleton(snap), space.getAddress(offset)));
if (sloc == null) {
return super.whenTraceNull(offset);
}
// NB. This is stored in the program, not the user data, despite what the name implies
StringPropertyMap map = sloc.getProgram()
.getUsrPropertyManager()
.getStringPropertyMap(TaintTracePcodeExecutorStatePiece.NAME);
if (map == null) {
return super.whenTraceNull(offset);
}
String string = map.getString(sloc.getAddress());
if (string == null) {
return super.whenTraceNull(offset);
}
return TaintSet.parse(string);
}
}

View file

@ -65,7 +65,7 @@ public class TaintSpace {
* This retrieves as many taint sets as there are elements in the given buffer vector. This first
* element becomes the taint set at the given offset, then each subsequent element becomes the
* taint set at each subsequent offset until the vector is filled. This is analogous to the
* manner in which bytes would be "read" from concrete state, starting at a given ofset, into a
* manner in which bytes would be "read" from concrete state, starting at a given offset, into a
* destination array.
*
* @param offset the offset

View file

@ -1,102 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.pcode.emu.taint.trace;
import ghidra.pcode.emu.taint.AbstractTaintPcodeExecutorStatePiece;
import ghidra.pcode.emu.taint.TaintPcodeArithmetic;
import ghidra.pcode.exec.BytesPcodeArithmetic;
import ghidra.pcode.exec.trace.TracePcodeExecutorStatePiece;
import ghidra.program.model.lang.Language;
import ghidra.taint.model.TaintVec;
import ghidra.trace.model.Trace;
import ghidra.trace.model.property.TracePropertyMap;
import ghidra.trace.model.thread.TraceThread;
/**
* An abstract trace-integrated state piece
*
* <p>
* See {@link AbstractTaintTracePcodeExecutorStatePiece} for framing. This class must remain
* abstract since we need to derive the Debugger-integrated state piece from it. Thus it tightens
* the bound on {@code <S>} and introduces the parameters necessary to source state from a trace.
* We'll store taint sets in the trace's address property map, which is the recommended scheme for
* auxiliary state.
*
* @param <S> the type of spaces
*/
public abstract class AbstractTaintTracePcodeExecutorStatePiece<S extends TaintTraceSpace>
extends AbstractTaintPcodeExecutorStatePiece<S>
implements TracePcodeExecutorStatePiece<byte[], TaintVec> {
public static final String NAME = "Taint";
protected final Trace trace;
protected final long snap;
protected final TraceThread thread;
protected final int frame;
protected final TracePropertyMap<String> map;
/**
* Create a state piece
*
* @param language the emulator's language
* @param trace the trace from which to load taint marks
* @param snap the snap from which to load taint marks
* @param thread if a register space, the thread from which to load taint marks
* @param frame if a register space, the frame
*/
public AbstractTaintTracePcodeExecutorStatePiece(Language language, Trace trace, long snap,
TraceThread thread, int frame) {
super(language,
BytesPcodeArithmetic.forLanguage(language),
TaintPcodeArithmetic.forLanguage(language));
this.trace = trace;
this.snap = snap;
this.thread = thread;
this.frame = frame;
this.map = trace.getAddressPropertyManager().getPropertyMap(NAME, String.class);
}
/**
* Create a state piece
*
* @param trace the trace from which to load taint marks
* @param snap the snap from which to load taint marks
* @param thread if a register space, the thread from which to load taint marks
* @param frame if applicable, the frame
*/
public AbstractTaintTracePcodeExecutorStatePiece(Trace trace, long snap, TraceThread thread,
int frame) {
this(trace.getBaseLanguage(), trace, snap, thread, frame);
}
/**
* {@inheritDoc}
*
* <p>
* This does the inverse of the lazy loading. Serialize the state and store it back into the
* trace. Technically, it could be a different trace, but it must have identically-named
* threads.
*/
@Override
public void writeDown(Trace trace, long snap, TraceThread thread, int frame) {
TracePropertyMap<String> map =
trace.getAddressPropertyManager().getOrCreatePropertyMap(NAME, String.class);
for (TaintTraceSpace space : spaceMap.values()) {
space.writeDown(map, snap, thread, frame);
}
}
}

View file

@ -19,8 +19,9 @@ import ghidra.pcode.emu.taint.TaintPartsFactory;
import ghidra.pcode.emu.taint.plain.TaintPcodeEmulator;
import ghidra.pcode.exec.trace.auxiliary.AuxTraceEmulatorPartsFactory;
import ghidra.pcode.exec.trace.auxiliary.AuxTracePcodeEmulator;
import ghidra.pcode.exec.trace.data.*;
import ghidra.taint.model.TaintVec;
import ghidra.trace.model.Trace;
import ghidra.trace.model.guest.TracePlatform;
/**
* A trace-integrated emulator with taint analysis
@ -29,11 +30,20 @@ public class TaintTracePcodeEmulator extends AuxTracePcodeEmulator<TaintVec> {
/**
* Create an emulator
*
* @param trace the trace the source trace
* @param access the trace access shim
*/
public TaintTracePcodeEmulator(PcodeTraceAccess access) {
super(access);
}
/**
* Create an emulator
*
* @param platform the platform to emulate
* @param snap the source snap
*/
public TaintTracePcodeEmulator(Trace trace, long snap) {
super(trace, snap);
public TaintTracePcodeEmulator(TracePlatform platform, long snap) {
super(platform, snap);
}
/**

View file

@ -36,7 +36,7 @@ public class TaintTracePcodeExecutorState extends PairedTracePcodeExecutorState<
* @param taint the taint piece
*/
public TaintTracePcodeExecutorState(BytesTracePcodeExecutorStatePiece concrete,
AbstractTaintTracePcodeExecutorStatePiece<?> taint) {
TaintTracePcodeExecutorStatePiece taint) {
super(new PairedTracePcodeExecutorStatePiece<>(concrete, taint));
}
@ -44,13 +44,11 @@ public class TaintTracePcodeExecutorState extends PairedTracePcodeExecutorState<
* Create a state from the given concrete piece and an internally constructed taint piece
*
* <p>
* We take all the parameters needed by the taint piece from the concrete piece.
* We take the data access shim needed by the taint piece from the concrete piece.
*
* @param concrete the concrete piece
*/
public TaintTracePcodeExecutorState(BytesTracePcodeExecutorStatePiece concrete) {
this(concrete,
new TaintTracePcodeExecutorStatePiece(concrete.getTrace(), concrete.getSnap(),
concrete.getThread(), concrete.getFrame()));
this(concrete, new TaintTracePcodeExecutorStatePiece(concrete.getData()));
}
}

View file

@ -15,28 +15,47 @@
*/
package ghidra.pcode.emu.taint.trace;
import ghidra.pcode.emu.taint.AbstractTaintPcodeExecutorStatePiece;
import ghidra.pcode.emu.taint.TaintPcodeArithmetic;
import ghidra.pcode.exec.BytesPcodeArithmetic;
import ghidra.pcode.exec.trace.TracePcodeExecutorStatePiece;
import ghidra.pcode.exec.trace.data.PcodeTraceDataAccess;
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
import ghidra.program.model.address.AddressSpace;
import ghidra.trace.model.Trace;
import ghidra.taint.model.TaintVec;
import ghidra.trace.model.property.TracePropertyMapSpace;
import ghidra.trace.model.thread.TraceThread;
/**
* The trace-integrated state piece for holding taint marks
*
* <p>
* See {@link AbstractTaintTracePcodeExecutorStatePiece} for framing. We'll store taint sets in the
* trace's address property map, which is the recommended scheme for auxiliary state.
*/
public class TaintTracePcodeExecutorStatePiece
extends AbstractTaintTracePcodeExecutorStatePiece<TaintTraceSpace> {
extends AbstractTaintPcodeExecutorStatePiece<TaintTraceSpace>
implements TracePcodeExecutorStatePiece<byte[], TaintVec> {
public static final String NAME = "Taint";
protected final PcodeTraceDataAccess data;
protected final PcodeTracePropertyAccess<String> property;
/**
* Create the taint piece
* Create a state piece
*
* @param trace the trace from which to load taint marks
* @param snap the snap from which to load taint marks
* @param thread if a register space, the thread from which to load taint marks
* @param frame if a register space, the frame
* @param data the trace-data access shim
*/
public TaintTracePcodeExecutorStatePiece(Trace trace, long snap, TraceThread thread,
int frame) {
super(trace, snap, thread, frame);
public TaintTracePcodeExecutorStatePiece(PcodeTraceDataAccess data) {
super(data.getLanguage(),
BytesPcodeArithmetic.forLanguage(data.getLanguage()),
TaintPcodeArithmetic.forLanguage(data.getLanguage()));
this.data = data;
this.property = data.getPropertyAccess(NAME, String.class);
}
@Override
public PcodeTraceDataAccess getData() {
return data;
}
/**
@ -52,23 +71,33 @@ public class TaintTracePcodeExecutorStatePiece
*/
@Override
protected AbstractSpaceMap<TaintTraceSpace> newSpaceMap() {
return new CacheingSpaceMap<TracePropertyMapSpace<String>, TaintTraceSpace>() {
return new CacheingSpaceMap<PcodeTracePropertyAccess<String>, TaintTraceSpace>() {
@Override
protected TracePropertyMapSpace<String> getBacking(AddressSpace space) {
if (map == null) {
return null;
}
if (space.isRegisterSpace()) {
return map.getPropertyMapRegisterSpace(thread, frame, false);
}
return map.getPropertyMapSpace(space, false);
protected PcodeTracePropertyAccess<String> getBacking(AddressSpace space) {
return property;
}
@Override
protected TaintTraceSpace newSpace(AddressSpace space,
TracePropertyMapSpace<String> backing) {
return new TaintTraceSpace(space, backing, snap);
PcodeTracePropertyAccess<String> backing) {
return new TaintTraceSpace(space, property);
}
};
}
/**
* {@inheritDoc}
*
* <p>
* This does the inverse of the lazy loading. Serialize the state and store it back into the
* trace. Technically, it could be a different trace, but it must have identically-named
* threads.
*/
@Override
public void writeDown(PcodeTraceDataAccess into) {
PcodeTracePropertyAccess<String> property = into.getPropertyAccess(NAME, String.class);
for (TaintTraceSpace space : spaceMap.values()) {
space.writeDown(property);
}
}
}

View file

@ -17,18 +17,10 @@ package ghidra.pcode.emu.taint.trace;
import java.util.Map.Entry;
import com.google.common.collect.Range;
import ghidra.pcode.emu.taint.full.TaintDebuggerSpace;
import ghidra.pcode.emu.taint.plain.TaintSpace;
import ghidra.pcode.exec.trace.TracePcodeExecutorState;
import ghidra.pcode.exec.trace.data.PcodeTracePropertyAccess;
import ghidra.program.model.address.*;
import ghidra.taint.model.TaintSet;
import ghidra.trace.database.DBTraceUtils;
import ghidra.trace.model.Trace;
import ghidra.trace.model.property.TracePropertyMap;
import ghidra.trace.model.property.TracePropertyMapSpace;
import ghidra.trace.model.thread.TraceThread;
/**
* The storage space for taint sets in a trace's address space
@ -39,8 +31,7 @@ import ghidra.trace.model.thread.TraceThread;
*/
public class TaintTraceSpace extends TaintSpace {
protected final AddressSpace space;
protected final TracePropertyMapSpace<String> backing;
protected final long snap;
protected final PcodeTracePropertyAccess<String> property;
/**
* Create the space
@ -49,10 +40,9 @@ public class TaintTraceSpace extends TaintSpace {
* @param backing if present, the backing object
* @param snap the source snap
*/
public TaintTraceSpace(AddressSpace space, TracePropertyMapSpace<String> backing, long snap) {
public TaintTraceSpace(AddressSpace space, PcodeTracePropertyAccess<String> property) {
this.space = space;
this.backing = backing;
this.snap = snap;
this.property = property;
}
/**
@ -61,73 +51,44 @@ public class TaintTraceSpace extends TaintSpace {
* <p>
* The taint space will call this when the cache misses, allowing us to populate it with a taint
* set stored in the trace. Note that if the emulator writes to this offset <em>before</em>
* reading it, this will not get called for that offset. Here we simply load the string property
* from the map and parse the taint set. We'll also introduce a second extension point for when
* neither the cache nor the trace have a taint set.
* reading it, this will not get called for that offset. Here we simply get the string property
* and parse the taint set.
*/
@Override
protected TaintSet whenNull(long offset) {
if (backing == null) {
return whenTraceNull(offset);
}
String string = backing.get(snap, space.getAddress(offset));
String string = property.get(space.getAddress(offset));
if (string == null) {
return whenTraceNull(offset);
return TaintSet.EMPTY;
}
return TaintSet.parse(string);
}
/**
* Extension point: Behavior when there is neither an in-memory nor a trace-stored taint set at
* the given offset
*
* <p>
* This will be overridden by {@link TaintDebuggerSpace} to implement loading from static mapped
* programs.
*
* @param offset the offset
* @return the taint set to use
*/
protected TaintSet whenTraceNull(long offset) {
return TaintSet.EMPTY;
}
/**
* Write this cache back down into a trace
*
* <p>
* Here we simply iterate over every entry in this space, serialize the taint, and store it into
* the property map at the entry's offset. Because a backing object may not have existed when
* creating this space, we must re-fetch the backing object, creating it if it does not exist.
* We can safely create such spaces, since the client is required to have an open transaction on
* the destination trace while invoking this method (via
* {@link TracePcodeExecutorState#writeDown(Trace, long, TraceThread, int)}).
* Here we simply iterate over every entry in this space, serialize the taint, and put it into
* the property at the entry's offset. If the taint set is empty, we clear the property rather
* than putting the empty taint set into the property.
*
* @param map the backing object, which must now exist
* @param snap the destination snap
* @param thread if a register space, the destination thread
* @param frame if a register space, the destination frame
*/
public void writeDown(TracePropertyMap<String> map, long snap, TraceThread thread, int frame) {
public void writeDown(PcodeTracePropertyAccess<String> into) {
if (space.isUniqueSpace()) {
return;
}
TracePropertyMapSpace<String> backing;
if (space.isRegisterSpace()) {
backing = map.getPropertyMapRegisterSpace(thread, frame, true);
}
else {
backing = map.getPropertyMapSpace(space, true);
}
for (Entry<Long, TaintSet> entry : taints.entrySet()) {
TaintSet taint = entry.getValue();
Range<Long> span = DBTraceUtils.atLeastMaybeScratch(snap);
Address address = space.getAddress(entry.getKey());
if (taint.isEmpty()) {
backing.clear(span, new AddressRangeImpl(address, address));
into.clear(new AddressRangeImpl(address, address));
}
else {
backing.set(span, address, taint.toString());
into.put(address, taint.toString());
}
}
}

View file

@ -23,15 +23,19 @@ import ghidra.app.plugin.core.debug.gui.register.RegisterRow;
import ghidra.docking.settings.Settings;
import ghidra.framework.plugintool.ServiceProvider;
import ghidra.pcode.emu.taint.trace.TaintTracePcodeExecutorStatePiece;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.lang.Register;
import ghidra.trace.model.Trace;
import ghidra.trace.model.property.TracePropertyMap;
import ghidra.trace.model.property.TracePropertyMapSpace;
/**
* A factory for the "Taint" column in the "Registers" panel
*
* <p>
* For the most part, this is just a matter of accessing the property map and rendering the value on
* screen. As a cheap shortcut, we'll just instantiate a taint state piece at the panel's
* coordinates and use it to retrieve the actual taint marks, then render that for display.
* screen.
*/
public class TaintDebuggerRegisterColumnFactory implements DebuggerRegisterColumnFactory {
protected static final String PROP_NAME = TaintTracePcodeExecutorStatePiece.NAME;
@ -46,7 +50,7 @@ public class TaintDebuggerRegisterColumnFactory implements DebuggerRegisterColum
}
@Override
public String getValue(RegisterRow rowObject, Settings settings, Void data,
public String getValue(RegisterRow rowObject, Settings settings, Void dataSource,
ServiceProvider serviceProvider) throws IllegalArgumentException {
DebuggerCoordinates current = rowObject.getCurrent();
Trace trace = current.getTrace();
@ -54,11 +58,39 @@ public class TaintDebuggerRegisterColumnFactory implements DebuggerRegisterColum
return "";
}
TaintTracePcodeExecutorStatePiece piece =
new TaintTracePcodeExecutorStatePiece(current.getTrace(), current.getViewSnap(),
current.getThread(), current.getFrame());
TracePropertyMap<String> taintMap = current.getTrace()
.getAddressPropertyManager()
.getPropertyMap(PROP_NAME, String.class);
return piece.getVar(rowObject.getRegister()).toDisplay();
if (taintMap == null) {
return "";
}
Register register = rowObject.getRegister();
TracePropertyMapSpace<String> 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();
}
};
}

View file

@ -121,9 +121,8 @@ public class TaintDebuggerPcodeEmulatorTest extends AbstractGhidraHeadedDebugger
progTaintMap.add(tb.addr(0x00400800), "test_0");
Assembler asm = Assemblers.getAssembler(program);
// TODO: I should be able to make this use a RIP-relative address
asm.assemble(tb.addr(0x00400000),
"MOV RAX, [0x55550800]"); // was [0x00400800], but fixed address is a problem.
// NOTE: qword ptr [0x00400800] is RIP-relative
asm.assemble(tb.addr(0x00400000), "MOV RAX, qword ptr [0x00400800]");
}
TraceSchedule time = TraceSchedule.parse("0:t0-1");

View file

@ -18,6 +18,8 @@ package ghidra.pcode.emu.taint.trace;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.stream.Collectors;
@ -26,15 +28,27 @@ import org.junit.Test;
import com.google.common.collect.Range;
import ghidra.app.plugin.assembler.*;
import ghidra.dbg.target.schema.SchemaContext;
import ghidra.dbg.target.schema.XmlSchemaContext;
import ghidra.dbg.target.schema.TargetObjectSchema.SchemaName;
import ghidra.pcode.emu.PcodeThread;
import ghidra.pcode.exec.trace.AbstractTracePcodeEmulatorTest;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.taint.model.*;
import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.target.DBTraceObjectManager;
import ghidra.trace.database.target.DBTraceObjectManagerTest;
import ghidra.trace.model.*;
import ghidra.trace.model.guest.TraceGuestPlatform;
import ghidra.trace.model.memory.TraceMemoryManager;
import ghidra.trace.model.memory.TraceMemorySpace;
import ghidra.trace.model.property.TracePropertyMap;
import ghidra.trace.model.property.TracePropertyMapSpace;
import ghidra.trace.model.target.TraceObjectKeyPath;
import ghidra.trace.model.target.TraceObject.ConflictResolution;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.database.UndoableTransaction;
@ -71,7 +85,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
taintMap.set(Range.atLeast(0L), tb.range(0x00400000, 0x00400003), "test_0");
}
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0);
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
emuThread.getExecutor().executeSleigh("RAX = *0x00400000:8;");
@ -101,7 +115,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
mapSpace.set(Range.atLeast(0L), regEBX, "test_0");
}
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0);
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
emuThread.getExecutor().executeSleigh("RAX = RBX;");
@ -121,7 +135,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "x86:LE:64:default")) {
initTrace(tb, "", List.of());
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0);
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
TaintVec taintVal = TaintVec.empties(8);
TaintSet testTaint = TaintSet.of(new TaintMark("test_0", Set.of()));
for (int i = 0; i < 4; i++) {
@ -132,7 +146,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
Pair.of(tb.arr(0, 0, 0, 0, 0, 0, 0, 0), taintVal));
try (UndoableTransaction tid = tb.startTransaction()) {
emu.writeDown(tb.trace, 1, 0);
emu.writeDown(tb.host, 1, 0);
}
TracePropertyMap<String> taintMap =
tb.trace.getAddressPropertyManager().getPropertyMap("Taint", String.class);
@ -148,7 +162,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
AddressSpace rs = tb.language.getAddressFactory().getRegisterSpace();
TraceThread thread = initTrace(tb, "", List.of());
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0);
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
TaintVec taintVal = TaintVec.empties(8);
TaintSet testTaint = TaintSet.of(new TaintMark("test_0", Set.of()));
@ -158,7 +172,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
emuThread.getState().setVar(tb.reg("EAX"), Pair.of(tb.arr(0, 0, 0, 0), taintVal));
try (UndoableTransaction tid = tb.startTransaction()) {
emu.writeDown(tb.trace, 1, 0);
emu.writeDown(tb.host, 1, 0);
}
TracePropertyMap<String> taintMap =
tb.trace.getAddressPropertyManager().getPropertyMap("Taint", String.class);
@ -183,7 +197,7 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
"MOV qword ptr [0x00600000], RAX",
"MOV qword ptr [0x00600000], RBX"));
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0);
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
emuThread.getState()
.setVar(tb.reg("RAX"), Pair.of(
@ -192,11 +206,11 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
emuThread.stepInstruction();
try (UndoableTransaction tid = tb.startTransaction()) {
emu.writeDown(tb.trace, 1, 0);
emu.writeDown(tb.host, 1, 0);
}
emuThread.stepInstruction();
try (UndoableTransaction tid = tb.startTransaction()) {
emu.writeDown(tb.trace, 2, 0);
emu.writeDown(tb.host, 2, 0);
}
TracePropertyMap<String> taintMap =
@ -220,16 +234,19 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
List.of(
"XOR RAX, RAX"));
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0);
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
emuThread.getState()
.setVar(tb.reg("RAX"), Pair.of(
tb.arr(1, 2, 3, 4, 5, 6, 7, 8),
TaintVec.copies(TaintSet.parse("test_0"), 8)));
try (UndoableTransaction tid = tb.startTransaction()) {
emu.writeDown(tb.host, 0, 0);
}
emuThread.stepInstruction();
try (UndoableTransaction tid = tb.startTransaction()) {
emu.writeDown(tb.trace, 1, 0);
emu.writeDown(tb.host, 1, 0);
}
TracePropertyMap<String> taintMap =
@ -251,16 +268,19 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
List.of(
"XOR EAX, EAX"));
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.trace, 0);
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(tb.host, 0);
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
emuThread.getState()
.setVar(tb.reg("RAX"), Pair.of(
tb.arr(1, 2, 3, 4, 5, 6, 7, 8),
TaintVec.copies(TaintSet.parse("test_0"), 8)));
try (UndoableTransaction tid = tb.startTransaction()) {
emu.writeDown(tb.host, 0, 0);
}
emuThread.stepInstruction();
try (UndoableTransaction tid = tb.startTransaction()) {
emu.writeDown(tb.trace, 1, 0);
emu.writeDown(tb.host, 1, 0);
}
TracePropertyMap<String> taintMap =
@ -272,4 +292,64 @@ public class TaintTracePcodeEmulatorTest extends AbstractTracePcodeEmulatorTest
Set.copyOf(mapSpace.getEntries(Range.singleton(1L), tb.reg("RAX"))));
}
}
@Test
public void testGuestEmptyTaintClears() throws Throwable {
try (ToyDBTraceBuilder tb = new ToyDBTraceBuilder("Test", "DATA:BE:64:default")) {
TraceMemoryManager mm = tb.trace.getMemoryManager();
AddressSpace ram = tb.language.getAddressFactory().getDefaultAddressSpace();
TraceThread thread;
TraceGuestPlatform x64;
try (UndoableTransaction tid = tb.startTransaction()) {
SchemaContext ctx = XmlSchemaContext.deserialize(DBTraceObjectManagerTest.XML_CTX);
DBTraceObjectManager objects = tb.trace.getObjectManager();
objects.createRootObject(ctx.getSchema(new SchemaName("Session")));
thread = tb.getOrAddThread("Targets[0].Threads[0]", 0);
x64 = tb.trace.getPlatformManager()
.addGuestPlatform(getSLEIGH_X86_64_LANGUAGE().getDefaultCompilerSpec());
x64.addMappedRegisterRange();
x64.addMappedRange(tb.addr(0x00000000), tb.addr(x64, 0x00400000), 0x10000);
x64.addMappedRange(tb.addr(0x20000000), tb.addr(x64, 0x00600000), 0x10000);
objects.createObject(TraceObjectKeyPath.parse("Targets[0].Threads[0].Registers"))
.insert(Range.atLeast(0L), ConflictResolution.DENY);
// TODO: Make Sleigh work in the guest platform
TraceMemorySpace regs = mm.getMemoryRegisterSpace(thread, 0, true);
regs.setValue(x64, 0,
new RegisterValue(tb.reg(x64, "RIP"), BigInteger.valueOf(0x00400000)));
Assembler asm = Assemblers.getAssembler(x64.getLanguage());
AssemblyBuffer buf = new AssemblyBuffer(asm, tb.addr(x64, 0x00400000));
buf.assemble("MOV qword ptr [0x00600000], RAX");
buf.assemble("MOV qword ptr [0x00600000], RBX");
mm.putBytes(0, tb.addr(0x00000000), ByteBuffer.wrap(buf.getBytes()));
}
TaintTracePcodeEmulator emu = new TaintTracePcodeEmulator(x64, 0);
PcodeThread<Pair<byte[], TaintVec>> emuThread = emu.newThread(thread.getPath());
emuThread.getState()
.setVar(tb.reg(x64, "RAX"), Pair.of(
tb.arr(0, 0, 0, 0, 0, 0, 0, 0),
TaintVec.copies(TaintSet.parse("test_0"), 8)));
emuThread.stepInstruction();
try (UndoableTransaction tid = tb.startTransaction()) {
emu.writeDown(x64, 1, 0);
}
emuThread.stepInstruction();
try (UndoableTransaction tid = tb.startTransaction()) {
emu.writeDown(x64, 2, 0);
}
TracePropertyMap<String> taintMap =
tb.trace.getAddressPropertyManager().getPropertyMap("Taint", String.class);
assertEquals(makeTaintEntries(tb.trace, Range.singleton(1L), ram, Set.of(
0x20000000L, 0x20000001L, 0x20000002L, 0x20000003L,
0x20000004L, 0x20000005L, 0x20000006L, 0x20000007L),
"test_0"),
Set.copyOf(taintMap.getEntries(
Range.singleton(1L), tb.range(0x20000000, 0x20000007))));
}
}
}