GP-71: Prepping for source release.

This commit is contained in:
Dan 2020-12-10 09:39:41 -05:00
parent ddbfbfe198
commit 8201baef2b
2705 changed files with 305722 additions and 53 deletions

View file

@ -0,0 +1,48 @@
/* ###
* 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 agent.dbgeng;
import java.util.concurrent.CompletableFuture;
import agent.dbgeng.model.impl.DbgModelImpl;
import ghidra.dbg.DebuggerObjectModel;
import ghidra.dbg.LocalDebuggerModelFactory;
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
import ghidra.util.classfinder.ExtensionPointProperties;
/**
* Note this is in the testing source because it's not meant to be shipped in the release.... That
* may change if it proves stable, though, no?
*/
@FactoryDescription( //
brief = "IN-VM MS dbgeng local debugger", //
htmlDetails = "Launch a dbgeng session in this same JVM" //
)
@ExtensionPointProperties(priority = 80)
public class DbgEngInJvmDebuggerModelFactory implements LocalDebuggerModelFactory {
@Override
public CompletableFuture<? extends DebuggerObjectModel> build() {
DbgModelImpl model = new DbgModelImpl();
return model.startDbgEng(new String[] {}).thenApply(__ -> model);
}
@Override
public boolean isCompatible() {
return System.getProperty("os.name").toLowerCase().contains("windows");
}
}

View file

@ -0,0 +1,72 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.COMException;
import com.sun.jna.platform.win32.COM.COMUtils;
/**
* Utilities for interacting with Microsoft COM objects beyond those provided by {@link COMUtils}.
*
* See the MSDN for details on the meanings of the return codes for the function or method of
* interest.
*/
public interface COMUtilsExtra {
public static HRESULT E_UNEXPECTED = new HRESULT(0x8000FFFF);
public static HRESULT E_BOUNDS = new HRESULT(0x8000000B);
public static HRESULT E_NOTIMPLEMENTED = new HRESULT(0x80004001);
public static HRESULT E_NOINTERFACE = new HRESULT(0x80004002);
public static HRESULT E_COM_EXC = new HRESULT(0x80004003);
public static HRESULT E_FAIL = new HRESULT(0x80004005);
public static HRESULT E_CANTCALLOUT_INASYNCCALL = new HRESULT(0x80010004);
public static HRESULT E_INTERNALEXCEPTION = new HRESULT(0x80040205);
public static HRESULT E_ACCESS_DENIED = new HRESULT(0x80070005);
public static HRESULT E_CANNOT_READ = new HRESULT(0x8007001E);
public static HRESULT E_INVALID_PARAM = new HRESULT(0x80070057);
public static HRESULT E_SCOPE_NOT_FOUND = new HRESULT(0x8007013E);
/**
* Check if the given exception represents an {@code E_NOINTERFACE} result
*
* @param e the exception
* @return true if {@code E_NOINTERFACE}
*/
static boolean isE_NOINTERFACE(COMException e) {
return E_NOINTERFACE.equals(e.getHresult());
}
/**
* Check if the given exception represents an {@code E_UNEXPECTED} result
*
* @param e the exception
* @return true if {@code E_UNEXPECTED}
*/
static boolean isE_UNEXPECTED(COMException e) {
return E_UNEXPECTED.equals(e.getHresult());
}
/**
* Check if the given exception represents an {@code E_INTERNALEXCEPTION} result
*
* @param e the exception
* @return true if {@code E_INTERNALEXCEPTION}
*/
static boolean isE_INTERNALEXCEPTION(COMException e) {
return E_INTERNALEXCEPTION.equals(e.getHresult());
}
}

View file

@ -0,0 +1,163 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import java.lang.ref.Cleaner;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.Kernel32Util;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.platform.win32.COM.IUnknown;
import agent.dbgeng.impl.dbgeng.client.DebugClientInternal;
import agent.dbgeng.jna.dbgeng.DbgEngNative;
import ghidra.comm.util.BitmaskSet;
import ghidra.util.Msg;
/**
* A wrapper for Microsoft's {@code dbgeng.dll} that presents a Java-friendly interface.
*
* This is the "root interface" from which all other interfaces to {@code dbgeng.dll} are generated.
* Not every method listed in the documentation, nor every method present in the header, is
* implemented. Only those that were necessary to implement the SCTL adapter. However, the class and
* interface hierarchy was designed so that adding the remaining methods should be fairly
* straightforward. This wrapper attempts to obtain the most capable COM interfaces for the debug
* client that it knows. Again, newer interfaces should be fairly straightforward to add.
*
* Methods that are "obviously" wrappers for a COM method are left undocumented, unless there is
* some nuance to how it has been wrapped. In many cases, a parameter which is an integer in the COM
* method may be presented as an {@code enum} or {@link BitmaskSet} by the wrapper. Consult the MSDN
* for the meaning of the various values and bit flags.
*
* Each wrapper interface is implemented by several COM interface wrappers: one for each known COM
* interface version. The wrapper is optimistic, in that it declares wrapper methods even for COM
* methods that are only available in later versions. The implementations limited to earlier COM
* interfaces should either emulate the operation, or throw an
* {@link UnsupportedOperationException}. Where a newer method is provided by a newer interface, a
* wrapper implementation should prefer the latest. For example, one series of interfaces introduces
* {@code *Wide} variants of existing methods. Since Java also uses a UTF-16-like string encoding
* internally, JNA permits wide strings to be passed by reference. Thus, the wide variant is always
* preferred.
*
* Pay careful attention to the threading requirements imposed by {@code dbgeng.dll} these can be
* found in the MSDN. As a general rule of thumb, if the method is reentrant (i.e., it can be called
* from any thread), it is declared in the {@code *Reentrant} variant of the wrapper interface.
* There are few of these. Unless the documentation explicitly lists the method as reentrant, do not
* declare it there. Many methods appear to execute successfully from the wrong thread, but cause
* latent issues. A practice to prevent accidental use of non-reentrant methods outside of the
* client's owning thread is to ensure that only the owning thread can see the full interface. All
* other threads should only have access to the reentrant interface.
*
* If you implement methods that introduce a new callback class, use the existing callback type
* hierarchies as a model. There are many classes to implement. Furthermore, be sure to keep a
* reference to any active callback instances within the wrapper that uses them. The JNA has no way
* of knowing whether or not the instance is still being used by the external C/C++ library. If you
* do not store a reference, the JVM will think it's garbage and free it, even though COM is still
* using it. Drop the reference only when you are certain nothing external has a reference to it.
*/
public class DbgEng {
private static final Cleaner CLEANER = Cleaner.create();
private static class ReleaseCOMObject implements Runnable {
private final IUnknown obj;
ReleaseCOMObject(IUnknown obj) {
this.obj = obj;
}
@Override
public void run() {
Msg.debug(this, "Releasing COM object: " + obj);
obj.Release();
}
}
private static class ReleaseHANDLE implements Runnable {
private final HANDLE handle;
public ReleaseHANDLE(HANDLE handle) {
this.handle = handle;
}
@Override
public void run() {
Kernel32Util.closeHandle(handle);
}
}
public static class OpaqueCleanable {
@SuppressWarnings("unused") // A reference to control GC
private final Object state;
@SuppressWarnings("unused") // A reference to control GC
private final Cleaner.Cleanable cleanable;
public OpaqueCleanable(Object state, Cleaner.Cleanable cleanable) {
this.state = state;
this.cleanable = cleanable;
}
}
public static OpaqueCleanable releaseWhenPhantom(Object owner, IUnknown obj) {
ReleaseCOMObject state = new ReleaseCOMObject(obj);
return new OpaqueCleanable(state, CLEANER.register(owner, state));
}
public static OpaqueCleanable releaseWhenPhantom(Object owner, HANDLE handle) {
ReleaseHANDLE state = new ReleaseHANDLE(handle);
return new OpaqueCleanable(state, CLEANER.register(owner, state));
}
/**
* Connect to a debug session.
*
* See {@code DebugConnect} or {@code DebugConnectWide} on the MSDN.
*
* @param remoteOptions the options, like those given to {@code -remote}
* @return a new client connected as specified
*/
public static DebugClient debugConnect(String remoteOptions) {
WString options = new WString(remoteOptions);
return DebugClientInternal.tryPreferredInterfaces((refiid,
ppClient) -> DbgEngNative.INSTANCE.DebugConnectWide(options, refiid, ppClient));
}
/**
* Create a debug client.
*
* Typically, this client is connected to the "local server". See {@code DebugCreate} on the
* MSDN.
*
* @return a new client
*/
public static DebugClient debugCreate() {
return DebugClientInternal.tryPreferredInterfaces(DbgEngNative.INSTANCE::DebugCreate);
}
/**
* Create a debug client with the given options.
*
* See {@code DebugCreateEx} on the MSDN.
*
* @param options the options
* @return a new client
*/
public static DebugClient debugCreate(int options) {
DWORD dwOpts = new DWORD(options);
return DebugClientInternal.tryPreferredInterfaces(
(refiid, ppClient) -> DbgEngNative.INSTANCE.DebugCreateEx(refiid, dwOpts, ppClient));
}
}

View file

@ -0,0 +1,83 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* A wrapper for {@code IDebugAdvanced} and its newer variants.
*/
public interface DebugAdvanced {
public static class DebugThreadBasicInformation {
public final Integer exitStatus;
public final Integer priorityClass;
public final Integer priority;
public final Long createTime;
public final Long exitTime;
public final Long kernelTime;
public final Long userTime;
public final Long startOffset;
public final Long affinity;
public DebugThreadBasicInformation(Integer exitStatus, Integer priorityClass,
Integer priority, Long createTime, Long exitTime, Long kernelTime, Long userTime,
Long startOffset, Long affinity) {
this.exitStatus = exitStatus;
this.priorityClass = priorityClass;
this.priority = priority;
this.createTime = createTime;
this.exitTime = exitTime;
this.kernelTime = kernelTime;
this.userTime = userTime;
this.startOffset = startOffset;
this.affinity = affinity;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("<DebugThreadBasicInformation:\n");
if (exitStatus != null) {
sb.append(" exitStatus: " + exitStatus + "\n");
}
if (priorityClass != null) {
sb.append(" priorityClass: " + priorityClass + "\n");
}
if (priority != null) {
sb.append(" priority: " + priority + "\n");
}
if (createTime != null) {
sb.append(" createTime: " + createTime + "\n");
}
if (exitTime != null) {
sb.append(" exitTime: " + exitTime + "\n");
}
if (kernelTime != null) {
sb.append(" kernelTime: " + kernelTime + "\n");
}
if (userTime != null) {
sb.append(" userTime: " + userTime + "\n");
}
if (startOffset != null) {
sb.append(" startOffset: " + startOffset + "\n");
}
if (affinity != null) {
sb.append(" affinity: " + affinity + "\n");
}
sb.append(">");
return sb.toString();
}
}
DebugThreadBasicInformation getThreadBasicInformation(DebugThreadId tid);
}

View file

@ -0,0 +1,122 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import agent.dbgeng.jna.dbgeng.WinNTExtra.Machine;
import ghidra.comm.util.BitmaskSet;
import ghidra.comm.util.BitmaskUniverse;
/**
* A wrapper for {@code IDebugBreakpoint} and its newer variants.
*/
public interface DebugBreakpoint {
public static enum BreakType {
CODE, DATA, TIME, INLINE;
}
public static class BreakFullType {
public final BreakType breakType;
public final Machine procType; // TODO: Guessing the values are from WinNT
public BreakFullType(BreakType breakType, Machine procType) {
this.breakType = breakType;
this.procType = procType;
}
}
public static enum BreakFlags implements BitmaskUniverse {
GO_ONLY(1 << 0), //
DEFERRED(1 << 1), //
ENABLED(1 << 2), //
ADDER_ONLY(1 << 3), //
ONE_SHOT(1 << 4), //
;
private BreakFlags(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public static enum BreakAccess implements BitmaskUniverse {
READ(1 << 0), //
WRITE(1 << 1), //
EXECUTE(1 << 2), //
IO(1 << 3), //
;
private BreakAccess(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public static class BreakDataParameters {
public int size;
public BitmaskSet<BreakAccess> access;
public BreakDataParameters(int size, BitmaskSet<BreakAccess> access) {
this.size = size;
this.access = access;
}
}
void remove();
int getId();
BreakFullType getType();
DebugClient getAdder();
BitmaskSet<BreakFlags> getFlags();
void addFlags(BitmaskSet<BreakFlags> flags);
void addFlags(BreakFlags... flags);
void removeFlags(BitmaskSet<BreakFlags> flags);
void removeFlags(BreakFlags... flags);
void setFlags(BitmaskSet<BreakFlags> flags);
void setFlags(BreakFlags... flags);
long getOffset();
void setOffset(long offset);
BreakDataParameters getDataParameters();
void setDataParameters(BreakDataParameters params);
void setDataParameters(int size, BitmaskSet<BreakAccess> access);
void setDataParameters(int size, BreakAccess... access);
}

View file

@ -0,0 +1,398 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import java.util.List;
import com.sun.jna.platform.win32.WinBase;
import agent.dbgeng.dbgeng.DebugRunningProcess.Description;
import agent.dbgeng.dbgeng.DebugRunningProcess.Description.ProcessDescriptionFlags;
import ghidra.comm.util.BitmaskSet;
import ghidra.comm.util.BitmaskUniverse;
/**
* A wrapper for {@code IDebugClient} and its newer variants.
*/
public interface DebugClient extends DebugClientReentrant {
public static enum ExecutionState {
RUNNING, STOPPED;
}
public static enum DebugStatus {
NO_CHANGE(false, null, 13), //
GO(true, ExecutionState.RUNNING, 10), //
GO_HANDLED(true, ExecutionState.RUNNING, 9), //
GO_NOT_HANDLED(true, ExecutionState.RUNNING, 8), //
STEP_OVER(true, ExecutionState.RUNNING, 7), //
STEP_INTO(true, ExecutionState.RUNNING, 5), //
BREAK(false, ExecutionState.STOPPED, 0), //
NO_DEBUGGEE(true, null, 1), // shouldWait is true to handle process creation
STEP_BRANCH(true, null, 6), //
IGNORE_EVENT(false, null, 11), //
RESTART_REQUESTED(true, null, 12), //
REVERSE_GO(true, null, 0xff), //
REVERSE_STEP_BRANCH(true, null, 0xff), //
REVERSE_STEP_OVER(true, null, 0xff), //
REVERSE_STEP_INTO(true, null, 0xff), //
OUT_OF_SYNC(false, null, 2), //
WAIT_INPUT(false, null, 3), //
TIMEOUT(false, null, 4), //
;
public static final long MASK = 0xaf;
public static final long INSIDE_WAIT = 0x100000000L;
public static final long WAIT_TIMEOUT = 0x200000000L;
DebugStatus(boolean shouldWait, ExecutionState threadState, int precedence) {
this.shouldWait = shouldWait;
this.threadState = threadState;
this.precedence = precedence;
}
public final boolean shouldWait;
public final ExecutionState threadState;
public final int precedence; // 0 is highest
public static DebugStatus fromArgument(long argument) {
return values()[(int) (argument & MASK)];
}
public static boolean isInsideWait(long argument) {
return (argument & INSIDE_WAIT) != 0;
}
public static boolean isWaitTimeout(long argument) {
return (argument & WAIT_TIMEOUT) != 0;
}
}
public static enum SessionStatus {
ACTIVE, //
END_SESSION_ACTIVE_TERMINATE,//
END_SESSION_ACTIVE_DETACH, //
END_SESSION_PASSIVE, //
END, //
REBOOT, //
HIBERNATE, //
FAILURE, //
;
}
public static enum ChangeDebuggeeState implements BitmaskUniverse {
ALL(0xffffffff), //
REGISTERS(1 << 0), //
DATA(1 << 1), //
REFRESH(1 << 2), //
;
private ChangeDebuggeeState(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public static enum ChangeEngineState implements BitmaskUniverse {
ALL(0xffffffff), //
CURRENT_THREAD(1 << 0), //
EFFECTIVE_PROCESSOR(1 << 1), //
BREAKPOINTS(1 << 2), //
CODE_LEVEL(1 << 3), //
EXECUTION_STATUS(1 << 4), //
ENGINE_OPTIONS(1 << 5), //
LOG_FILE(1 << 6), //
RADIX(1 << 7), //
EVENT_FILTERS(1 << 8), //
PROCESS_OPTIONS(1 << 9), //
EXTENSIONS(1 << 10), //
SYSTEMS(1 << 11), //
ASSEMBLY_OPTIONS(1 << 12), //
EXPRESSION_SYNTAX(1 << 13), //
TEXT_REPLACEMENTS(1 << 14), //
;
private ChangeEngineState(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public static enum ChangeSymbolState implements BitmaskUniverse {
ALL(0xffffffff), //
LOADS(1 << 0), //
UNLOADS(1 << 1), //
SCOPE(1 << 2), //
PATHS(1 << 3), //
SYMBOL_OPTIONS(1 << 4), //
TYPE_OPTIONS(1 << 5), //
;
private ChangeSymbolState(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public static enum DebugAttachFlags implements BitmaskUniverse {
DEFAULT(0), //
NONINVASIVE(1 << 0), //
EXISTING(1 << 1), //
NONINVASIVE_NO_SUSPEND(1 << 2), //
INVASIVE_NO_INITIAL_BREAK(1 << 3), //
INVASIVE_RESUME_PROCESS(1 << 4), //
NONINVASIVE_ALLOW_PARTIAL(1 << 5), //
;
DebugAttachFlags(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public static enum DebugCreateFlags implements BitmaskUniverse {
DEBUG_PROCESS(WinBase.DEBUG_PROCESS), //
DEBUG_ONLY_THIS_PROCESS(WinBase.DEBUG_ONLY_THIS_PROCESS), //
CREATE_SUSPENDED(WinBase.CREATE_SUSPENDED), //
DETACHED_PROCESS(WinBase.DETACHED_PROCESS), //
CREATE_NEW_CONSOLE(WinBase.CREATE_NEW_CONSOLE), //
//NORMAL_PRIORITY_CLASS(WinBase.NORMAL_PRIORITY_CLASS), //
//IDLE_PRIORITY_CLASS(WinBase.IDLE_PRIORITY_CLASS), //
//HIGH_PRIORITY_CLASS(WinBase.HIGH_PRIORITY_CLASS), //
//REALTIME_PRIORITY_CLASS(WinBase.REALTIME_PRIORITY_CLASS), //
CREATE_NEW_PROCESS_GROUP(WinBase.CREATE_NEW_PROCESS_GROUP), //
CREATE_UNICODE_ENVIRONMENT(WinBase.CREATE_UNICODE_ENVIRONMENT), //
CREATE_SEPARATE_WOW_VDM(WinBase.CREATE_SEPARATE_WOW_VDM), //
CREATE_SHARED_WOW_VDM(WinBase.CREATE_SHARED_WOW_VDM), //
CREATE_FORCEDOS(WinBase.CREATE_FORCEDOS), //
//BELOW_NORMAL_PRIORITY_CLASS(WinBase.BELOW_NORMAL_PRIORITY_CLASS), //
//ABOVE_NORMAL_PRIORITY_CLASS(WinBase.ABOVE_NORMAL_PRIORITY_CLASS), //
INHERIT_PARENT_AFFINITY(WinBase.INHERIT_PARENT_AFFINITY), //
//INHERIT_CALLER_PRIORITY(WinBase.INHERIT_CALLER_PRIORITY), //
CREATE_PROTECTED_PROCESS(WinBase.CREATE_PROTECTED_PROCESS), //
EXTENDED_STARTUPINFO_PRESENT(WinBase.EXTENDED_STARTUPINFO_PRESENT), //
//PROCESS_MODE_BACKGROUND_BEGIN(WinBase.PROCESS_MODE_BACKGROUND_BEGIN), //
//PROCESS_MODE_BACKGROUND_END(WinBase.PROCESS_MODE_BACKGROUND_END), //
CREATE_BREAKAWAY_FROM_JOB(WinBase.CREATE_BREAKAWAY_FROM_JOB), //
CREATE_PRESERVE_CODE_AUTHZ_LEVEL(WinBase.CREATE_PRESERVE_CODE_AUTHZ_LEVEL), //
CREATE_DEFAULT_ERROR_MODE(WinBase.CREATE_DEFAULT_ERROR_MODE), //
CREATE_NO_WINDOW(WinBase.CREATE_NO_WINDOW), //
//PROFILE_USER(WinBase.PROFILE_USER), //
//PROFILE_KERNEL(WinBase.PROFILE_KERNEL), //
//PROFILE_SERVER(WinBase.PROFILE_SERVER), //
//CREATE_IGNORE_SYSTEM_DEFAULT(WinBase.CREATE_IGNORE_SYSTEM_DEFAULT), //
DEBUG_CREATE_NO_DEBUG_HEAP(0x00000400), //
DEBUG_CREATE_THROUGH_RTL(0x00010000), //
;
DebugCreateFlags(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public enum DebugEndSessionFlags {
DEBUG_END_PASSIVE(0x00000000),
DEBUG_END_ACTIVE_TERMINATE(0x00000001),
DEBUG_END_ACTIVE_DETACH(0x00000002),
DEBUG_END_REENTRANT(0x00000003),
DEBUG_END_DISCONNECT(0x00000004);
DebugEndSessionFlags(int value) {
this.value = value;
}
private final int value;
public long getValue() {
return value;
}
}
public enum DebugOutputFlags {
DEBUG_OUTPUT_NORMAL(0x1), //
DEBUG_OUTPUT_ERROR(0x2), //
DEBUG_OUTPUT_WARNING(0x4), //
DEBUG_OUTPUT_VERBOSE(0x8), //
DEBUG_OUTPUT_PROMPT(0x10), //
DEBUG_OUTPUT_PROMPT_REGISTERS(0x20), //
DEBUG_OUTPUT_EXTENSION_WARNING(0x40), //
DEBUG_OUTPUT_DEBUGGEE(0x80), //
DEBUG_OUTPUT_DEBUGGEE_PROMPT(0x100), //
DEBUG_OUTPUT_SYMBOLS(0x200);
DebugOutputFlags(int value) {
this.value = value;
}
private final int value;
public long getValue() {
return value;
}
}
/**
* Obtain the advanced interface to this client.
*
* @return the advanced interface
*/
DebugAdvanced getAdvanced();
/**
* Obtain the control interface to this client
*
* @return the control interface
*/
@Override
DebugControl getControl();
/**
* Obtain the data spaces interface to this client
*
* @return the data spaces interface
*/
DebugDataSpaces getDataSpaces();
/**
* Obtain the registers interface to this client
*
* @return the registers interface
*/
DebugRegisters getRegisters();
/**
* Obtain the symbols interface to this client
*
* @return the symbols interface
*/
DebugSymbols getSymbols();
/**
* Obtain the system objects interface to this client
*
* @return the system objects interface
*/
DebugSystemObjects getSystemObjects();
/**
* The the ID for the local server
*
* @return the ID
*/
DebugServerId getLocalServer();
void attachKernel(long flags, String options);
void startProcessServer(String options);
DebugServerId connectProcessServer(String options);
boolean dispatchCallbacks(int timeout);
void flushCallbacks();
default void dispatchCallbacks() {
this.dispatchCallbacks(-1);
}
void exitDispatch(DebugClient client);
default void exitDispatch() {
exitDispatch(this);
}
void setInputCallbacks(DebugInputCallbacks cb);
void setOutputCallbacks(DebugOutputCallbacks cb);
void setEventCallbacks(DebugEventCallbacks cb);
List<DebugRunningProcess> getRunningProcesses(DebugServerId server);
Description getProcessDescription(DebugServerId si, int systemId,
BitmaskSet<ProcessDescriptionFlags> flags);
void attachProcess(DebugServerId si, int processId, BitmaskSet<DebugAttachFlags> attachFlags);
void createProcess(DebugServerId si, String commandLine,
BitmaskSet<DebugCreateFlags> createFlags);
void createProcessAndAttach(DebugServerId si, String commandLine,
BitmaskSet<DebugCreateFlags> createFlags, int processId,
BitmaskSet<DebugAttachFlags> attachFlags);
void startServer(String options);
// Only in IDebugClient2
void waitForProcessServerEnd(int timeout);
default void waitForProcessServerEnd() {
waitForProcessServerEnd(-1);
}
void terminateCurrentProcess();
void detachCurrentProcess();
void abandonCurrentProcess();
void connectSession(int flags);
void endSession(DebugEndSessionFlags flags);
// Only in IDebugClient4+
void openDumpFileWide(String fileName);
}

View file

@ -0,0 +1,46 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* An interface containing the subset of {@link DebugClient} methods which are reentrant.
*
* All other methods should be called only by the thread which created the client.
*/
public interface DebugClientReentrant {
/**
* Create a new client for the calling thread, connected to the same session as this client.
*
* @return the new client
*/
DebugClient createClient();
/**
* Get the reentrant control interface to the client
*
* @return the control interface
*/
DebugControlReentrant getControl();
/**
* End a session without acquiring locks
*
* Note. This method calls {@code IDebugClient::EndSession(DEBUG_END_REENTRANT)}. Per the MSDN,
* this may leave the engine in an indeterminate state. The engine should no longer be used by
* this process. It's really only appropriate to use this method when terminating the debugger.
*/
void endSessionReentrant();
}

View file

@ -0,0 +1,315 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.platform.win32.WinBase;
import com.sun.jna.platform.win32.COM.COMException;
import agent.dbgeng.dbgeng.DebugBreakpoint.BreakType;
import agent.dbgeng.dbgeng.DebugClient.DebugStatus;
import ghidra.comm.util.BitmaskSet;
import ghidra.comm.util.BitmaskUniverse;
import ghidra.util.Msg;
/**
* A wrapper for {@code IDebugControl} and its newer variants.
*/
public interface DebugControl extends DebugControlReentrant {
public static final BitmaskSet<DebugOutputControl> SET_ALL_CLIENTS =
BitmaskSet.of(DebugOutputControl.ALL_CLIENTS);
public static final BitmaskSet<DebugExecute> SET_DEFAULT = BitmaskSet.of(DebugExecute.DEFAULT);
public static enum DebugOutputLevel implements BitmaskUniverse {
NORMAL(1 << 0), //
ERROR(1 << 1), //
WARNING(1 << 2), //
VERBOSE(1 << 3), //
PROMPT(1 << 4), //
PROMPT_REGISTERS(1 << 5), //
EXTENSION_WARNING(1 << 6), //
OUTPUT_DEBUGEE(1 << 7), //
OUTPUT_DEBUGEE_PROMPT(1 << 8), //
OUTPUT_SYMBOLS(1 << 9), //
OUTPUT_STATUS(1 << 10), //
;
private final int mask;
DebugOutputLevel(int mask) {
this.mask = mask;
}
@Override
public long getMask() {
return mask;
}
}
public static enum DebugOutputControl implements BitmaskUniverse {
THIS_CLIENT(0), //
ALL_CLIENTS(1), //
ALL_OTHER_CLIENTS(2), //
IGNORE(3), //
LOG_ONLY(4), //
SEND_MASK(7), //
NOT_LOGGED(1 << 3), //
OVERRIDE_MASK(1 << 4), //
DML(1 << 5), //
AMBIENT_DML(0xfffffffe), //
AMBIENT_TEXT(0xffffffff), //
AMBIENT(0xffffffff), //
;
private final int mask;
DebugOutputControl(int mask) {
this.mask = mask;
}
@Override
public long getMask() {
return mask;
}
}
public static enum DebugExecute implements BitmaskUniverse {
DEFAULT(0), //
ECHO(1 << 0), //
NOT_LOGGED(1 << 1), //
NO_REPEAT(1 << 2), //
;
private final int mask;
DebugExecute(int mask) {
this.mask = mask;
}
@Override
public long getMask() {
return mask;
}
}
public static enum DebugInterrupt {
ACTIVE, //
PASSIVE, //
EXIT, //
;
}
boolean getInterrupt();
int getInterruptTimeout();
void setInterruptTimeout(int seconds);
void print(BitmaskSet<DebugOutputLevel> levels, String message);
/**
* A shortcut for {@link #print(BitmaskSet, String)} that includes a newline.
*
* @param levels the log levels for the message
* @param message the message
*/
void println(BitmaskSet<DebugOutputLevel> levels, String message);
/**
* A shortcut for {@link #print(BitmaskSet, String)} that applies to a single level.
*
* @param level the log level for the message
* @param message the message
*/
default void print(DebugOutputLevel level, String message) {
print(BitmaskSet.of(level), message);
}
/**
* A shortcut for {@link #print(BitmaskSet, String)} that includes a newline and applies to a
* single level.
*
* @param level the log level for the message
* @param message the message
*/
default void println(DebugOutputLevel level, String message) {
println(BitmaskSet.of(level), message);
}
/**
* A shortcut for {@link #print(BitmaskSet, String)} at normal level.
*
* @param message the message
*/
default void out(String message) {
print(DebugOutputLevel.NORMAL, message);
}
/**
* A shortcut for {@link #println(BitmaskSet, String)} at normal level.
*
* @param message the message
*/
default void outln(String message) {
println(DebugOutputLevel.NORMAL, message);
}
/**
* A shortcut for {@link #print(BitmaskSet, String)} at warning level.
*
* @param message the message
*/
default void warn(String message) {
print(DebugOutputLevel.WARNING, message);
}
/**
* A shortcut for {@link #println(BitmaskSet, String)} at warning level.
*
* @param message the message
*/
default void warnln(String message) {
println(DebugOutputLevel.WARNING, message);
}
/**
* A shortcut for {@link #print(BitmaskSet, String)} at error level.
*
* @param message the message
*/
default void err(String message) {
print(DebugOutputLevel.ERROR, message);
}
/**
* A shortcut for {@link #println(BitmaskSet, String)} at error level.
*
* @param message the message
*/
default void errln(String message) {
println(DebugOutputLevel.ERROR, message);
}
/**
* A shortcut for {@link #print(BitmaskSet, String)} at verbose level.
*
* @param message the message
*/
default void verb(String message) {
print(DebugOutputLevel.VERBOSE, message);
}
/**
* A shortcut for {@link #println(BitmaskSet, String)} at verbose level.
*
* @param message the message
*/
default void verbln(String message) {
println(DebugOutputLevel.VERBOSE, message);
}
<T extends DebugValue> T evaluate(Class<T> desiredType, String expression);
void execute(BitmaskSet<DebugOutputControl> ctl, String str, BitmaskSet<DebugExecute> flags);
/**
* A shortcut for {@link #execute(BitmaskSet, String, BitmaskSet)} outputting to all clients
* with the default execution flag.
*
* @param str the command string
*/
default void execute(String str) {
execute(SET_ALL_CLIENTS, str, SET_DEFAULT);
}
void prompt(BitmaskSet<DebugOutputControl> ctl, String message);
String getPromptText();
void returnInput(String input);
DebugStatus getExecutionStatus();
void setExecutionStatus(DebugStatus status);
int getNumberBreakpoints();
DebugBreakpoint getBreakpointByIndex(int index);
/**
* Shortcut to retrieve all breakpoints for the current process.
*
* Uses {@link #getNumberBreakpoints()} and {@link #getBreakpointByIndex(int)} to enumerate all
* breakpoints for the current process.
*
* @return the list of retrieved breakpoints.
*/
default List<DebugBreakpoint> getBreakpoints() {
int count = getNumberBreakpoints();
List<DebugBreakpoint> result = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
try {
result.add(getBreakpointByIndex(i));
}
catch (COMException e) {
if (!COMUtilsExtra.isE_NOINTERFACE(e)) {
throw e;
}
Msg.trace(this, "Discarding private breakpoint at index " + i);
}
}
return result;
}
/**
* Get a breakpoint by ID
*
* According to the MSDN, though the IDs may be global, this method should only succeed for
* breakpoints belonging to the current process.
*
* @param id
* @return
*/
DebugBreakpoint getBreakpointById(int id);
DebugBreakpoint addBreakpoint(BreakType type, int desiredId);
DebugBreakpoint addBreakpoint(BreakType type);
void waitForEvent(int timeout);
DebugEventInformation getLastEventInformation();
DebugStackInformation getStackTrace(long frameOffset, long stackOffset, long instructionOffset);
/**
* Shortcut for {@link #waitForEvent(int)} with infinite timeout.
*/
default void waitForEvent() {
waitForEvent(WinBase.INFINITE);
}
int getActualProcessorType();
int getEffectiveProcessorType();
int getExecutingProcessorType();
int getDebuggeeType();
}

View file

@ -0,0 +1,27 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import agent.dbgeng.dbgeng.DebugControl.DebugInterrupt;
/**
* An interface containing the subset of {@link DebugControl} methods which are reentrant.
*
* All other methods should be called only by the thread which created the client.
*/
public interface DebugControlReentrant {
void setInterrupt(DebugInterrupt interrupt);
}

View file

@ -0,0 +1,285 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import java.nio.ByteBuffer;
import java.util.*;
import com.sun.jna.platform.win32.COM.COMException;
import ghidra.comm.util.BitmaskSet;
import ghidra.comm.util.BitmaskUniverse;
import ghidra.util.Msg;
/**
* A wrapper for {@code IDebugDataSpaces} and its newer variants.
*/
public interface DebugDataSpaces {
public enum PageState {
COMMIT(0x1000), FREE(0x10000), RESERVE(0x2000);
private final int val;
private PageState(int val) {
this.val = val;
}
public static PageState byValue(int val) {
for (PageState state : values()) {
if (state.val == val) {
return state;
}
}
Msg.warn(PageState.class, "No such value: 0x" + Integer.toHexString(val));
return null;
}
}
public enum PageProtection implements BitmaskUniverse {
NOACCESS(1 << 0, false, false, false), //
READONLY(1 << 1, true, false, false), //
READWRITE(1 << 2, true, true, false), //
WRITE_COPY(1 << 3, true, true, false), // Becomes READWRITE after copy
EXECUTE(1 << 4, false, false, true), //
EXECUTE_READ(1 << 5, true, false, true), //
EXECUTE_READWRITE(1 << 6, true, true, true), //
EXECUTE_WRITECOPY(1 << 7, true, true, true), //
//
GUARD(1 << 8, false, false, false), //
NOCACHE(1 << 9, false, false, false), //
WRITECOMBINE(1 << 10, false, false, false), //
;
private PageProtection(int mask, boolean isRead, boolean isWrite, boolean isExecute) {
this.mask = mask;
this.isRead = isRead;
this.isWrite = isWrite;
this.isExecute = isExecute;
}
final int mask;
final boolean isRead;
final boolean isWrite;
final boolean isExecute;
@Override
public long getMask() {
return mask;
}
public boolean isRead() {
return isRead;
}
public boolean isWrite() {
return isWrite;
}
public boolean isExecute() {
return isExecute;
}
}
public enum PageType {
NONE(0), //
IMAGE(0x1000000), //
MAPPED(0x40000), //
PRIVATE(0x20000), //
;
private final int val;
private PageType(int val) {
this.val = val;
}
public static PageType byValue(int val) {
for (PageType type : values()) {
if (type.val == val) {
return type;
}
}
Msg.warn(PageType.class, "No such value: 0x" + Integer.toHexString(val));
return null;
}
}
public static class DebugMemoryBasicInformation {
public final long baseAddress;
public final long allocationBase;
public final Set<PageProtection> allocationProtect;
public final long regionSize;
public final PageState state;
public final Set<PageProtection> protect;
public final PageType type;
public DebugMemoryBasicInformation(long baseAddress, long allocationBase,
BitmaskSet<PageProtection> allocationProtect, long regionSize, PageState state,
BitmaskSet<PageProtection> protect, PageType type) {
this.baseAddress = baseAddress;
this.allocationBase = allocationBase;
this.allocationProtect = Collections.unmodifiableSet(allocationProtect);
this.regionSize = regionSize;
this.state = state;
this.protect = Collections.unmodifiableSet(protect);
this.type = type;
}
@Override
public String toString() {
return "<DebugMemoryBasicInformation:\n" + //
" baseAddress=" + Long.toHexString(baseAddress) + "h,\n" + //
" allocationBase=" + Long.toHexString(allocationBase) + "h,\n" + //
" allocationProtect=" + allocationProtect + ",\n" + //
" regionSize=" + Long.toHexString(regionSize) + "h,\n" + //
" state=" + state + ",\n" + //
" protect=" + protect + ",\n" + //
" type=" + type + "\n" + //
">";
}
@Override
public int hashCode() {
return Objects.hash(baseAddress, allocationBase, allocationProtect, regionSize, state,
protect, type);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DebugMemoryBasicInformation)) {
return false;
}
DebugMemoryBasicInformation that = (DebugMemoryBasicInformation) obj;
if (this.baseAddress != that.baseAddress) {
return false;
}
if (this.allocationBase != that.allocationBase) {
return false;
}
if (!this.allocationProtect.equals(that.allocationProtect)) {
return false;
}
if (this.regionSize != that.regionSize) {
return false;
}
if (this.state != that.state) {
return false;
}
if (!this.protect.equals(that.protect)) {
return false;
}
if (this.type != that.type) {
return false;
}
return true;
}
}
int readVirtual(long offset, ByteBuffer into, int len);
int writeVirtual(long offset, ByteBuffer from, int len);
int readVirtualUncached(long offset, ByteBuffer into, int len);
int writeVirtualUncached(long offset, ByteBuffer from, int len);
int readPhysical(long offset, ByteBuffer into, int len);
int writePhysical(long offset, ByteBuffer from, int len);
int readControl(int processor, long offset, ByteBuffer into, int len);
int writeControl(int processor, long offset, ByteBuffer from, int len);
int readBusData(int busDataType, int busNumber, int slotNumber, long offset, ByteBuffer into,
int len);
int writeBusData(int busDataType, int busNumber, int slotNumber, long offset, ByteBuffer from,
int len);
int readIo(int interfaceType, int busNumber, int addressSpace, long offset, ByteBuffer into,
int len);
int writeIo(int interfaceType, int busNumber, int addressSpace, long offset, ByteBuffer from,
int len);
long readMsr(int msr);
void writeMsr(int msr, long value);
int readDebuggerData(int offset, ByteBuffer into, int len);
DebugMemoryBasicInformation queryVirtual(long offset);
/**
* A shortcut for iterating over virtual memory regions.
*
* This operates by calling {@link #queryVirtual(long)} to get each next entry, starting at an
* offset of -start-, adding the size of the returned region to determine the offset for the
* next call.
*
* @param start the starting offset
* @return an iterator over virtual memory regions after the given start
*/
default Iterable<DebugMemoryBasicInformation> iterateVirtual(long start) {
return new Iterable<DebugMemoryBasicInformation>() {
@Override
public Iterator<DebugMemoryBasicInformation> iterator() {
return new Iterator<DebugMemoryBasicInformation>() {
private long last = start;
private long offset = start;
private DebugMemoryBasicInformation next = doGetNext();
private DebugMemoryBasicInformation getNext() {
if (Long.compareUnsigned(last, offset) < 0) {
return doGetNext();
}
return null;
}
private DebugMemoryBasicInformation doGetNext() {
try {
DebugMemoryBasicInformation info = queryVirtual(offset);
last = offset;
if (info != null) {
offset += info.regionSize;
}
return info;
}
catch (COMException e) {
if (!COMUtilsExtra.isE_NOINTERFACE(e)) {
throw e;
}
return null;
}
}
@Override
public boolean hasNext() {
return next != null;
}
@Override
public DebugMemoryBasicInformation next() {
DebugMemoryBasicInformation ret = next;
next = getNext();
return ret;
}
};
}
};
}
}

View file

@ -0,0 +1,116 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import java.lang.annotation.*;
import agent.dbgeng.dbgeng.DebugClient.*;
import ghidra.comm.util.BitmaskSet;
import ghidra.comm.util.BitmaskUniverse;
/**
* The interface for receiving event callbacks via {@code IDebugEventCallbacks} or a newer variant.
*
* Note: The wrapper implementation will select the appropriate native interface version.
*
* Note: Even though {@link #changeDebuggeeState(BitmaskSet, long)},
* {@link #changeEngineState(BitmaskSet, long)} and {@link #changeSymbolState(BitmaskSet, long)}
* purport to return a {@link DebugStatus}, the returned value is ignored by {@code dbgeng.dll}.
*/
public interface DebugEventCallbacks {
public static enum DebugEvent implements BitmaskUniverse {
BREAKPOINT(1 << 0), //
EXCEPTION(1 << 1), //
CREATE_THREAD(1 << 2), //
EXIT_THREAD(1 << 3), //
CREATE_PROCESS(1 << 4), //
EXIT_PROCESS(1 << 5), //
LOAD_MODULE(1 << 6), //
UNLOAD_MODULE(1 << 7), //
SYSTEM_ERROR(1 << 8), //
SESSION_STATUS(1 << 9), //
CHANGE_DEBUGEE_STATE(1 << 10), //
CHANGE_ENGINE_STATE(1 << 11), //
CHANGE_SYMBOL_STATE(1 << 12), //
;
private DebugEvent(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
/**
* An annotation for marking each callback with its interest flag.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
static @interface ForInterest {
/**
* The flag corresponding to the annotated callback method
*
* @return the flag
*/
DebugEvent value();
}
BitmaskSet<DebugEvent> getInterestMask();
@ForInterest(DebugEvent.BREAKPOINT)
DebugStatus breakpoint(DebugBreakpoint bp);
@ForInterest(DebugEvent.EXCEPTION)
DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance);
@ForInterest(DebugEvent.CREATE_THREAD)
DebugStatus createThread(DebugThreadInfo debugThreadInfo);
@ForInterest(DebugEvent.EXIT_THREAD)
DebugStatus exitThread(int exitCode);
@ForInterest(DebugEvent.CREATE_PROCESS)
DebugStatus createProcess(DebugProcessInfo debugProcessInfo);
@ForInterest(DebugEvent.EXIT_PROCESS)
DebugStatus exitProcess(int exitCode);
@ForInterest(DebugEvent.LOAD_MODULE)
DebugStatus loadModule(DebugModuleInfo debugModuleInfo);
@ForInterest(DebugEvent.UNLOAD_MODULE)
DebugStatus unloadModule(String imageBaseName, long baseOffset);
@ForInterest(DebugEvent.SYSTEM_ERROR)
DebugStatus systemError(int error, int level);
@ForInterest(DebugEvent.SESSION_STATUS)
DebugStatus sessionStatus(SessionStatus status);
@ForInterest(DebugEvent.CHANGE_DEBUGEE_STATE)
DebugStatus changeDebuggeeState(BitmaskSet<ChangeDebuggeeState> flags, long argument);
@ForInterest(DebugEvent.CHANGE_ENGINE_STATE)
DebugStatus changeEngineState(BitmaskSet<ChangeEngineState> flags, long argument);
@ForInterest(DebugEvent.CHANGE_SYMBOL_STATE)
DebugStatus changeSymbolState(BitmaskSet<ChangeSymbolState> flags, long argument);
}

View file

@ -0,0 +1,70 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import agent.dbgeng.jna.dbgeng.WinNTExtra;
public class DebugEventInformation {
private int type;
private DebugProcessId pid;
private DebugThreadId tid;
private DebugSessionId sid;
private int executingProcessorType = WinNTExtra.Machine.IMAGE_FILE_MACHINE_AMD64.val;
public DebugEventInformation(int type, int pid, int tid) {
this.type = type;
this.pid = new DebugProcessId(pid);
this.tid = new DebugThreadId(tid);
}
public int getType() {
return type;
}
public DebugSessionId getSessionId() {
return sid;
}
public DebugProcessId getProcessId() {
return pid;
}
public DebugThreadId getThreadId() {
return tid;
}
public void setThread(DebugThreadId tid) {
this.tid = tid;
}
public void setProcess(DebugProcessId pid) {
this.pid = pid;
}
public void setSession(DebugSessionId sid) {
this.sid = sid;
}
public int getExecutingProcessorType() {
return executingProcessorType;
}
public void setExecutingProcessorType(int execType) {
this.executingProcessorType = execType;
}
}

View file

@ -0,0 +1,41 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import java.util.Collections;
import java.util.List;
/**
* Data copied from a {@code EXCEPTION_RECORD64} as defined in {@code winnt.h}.
*
* TODO: Some enums, flags, etc., to help interpret some of the fields.
*/
public class DebugExceptionRecord64 {
public final int code; // TODO: How to interpret
public final int flags; // TODO: How to interpret
public final long record; // TODO: How to interpret
public final long address;
public final List<Long> information;
public DebugExceptionRecord64(int code, int flags, long record, long address,
List<Long> information) {
this.code = code;
this.flags = flags;
this.record = record;
this.address = address;
this.information = Collections.unmodifiableList(information);
}
}

View file

@ -0,0 +1,32 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import java.util.concurrent.CompletableFuture;
/**
* The interface for receiving input callbacks via {@code IDebugInputCallbacks} or a newer variant.
*
* Note: The wrapper implementation will select the appropriate native interface version.
*/
@FunctionalInterface
public interface DebugInputCallbacks {
CompletableFuture<String> startInput();
default void endInput() {
// Optional implementation
}
}

View file

@ -0,0 +1,47 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* Handle to a module (program or library image).
*/
public interface DebugModule {
public enum DebugModuleName {
IMAGE, MODULE, LOADED_IMAGE, SYMBOL_FILE, MAPPED_IMAGE;
}
/**
* Get a name for the module.
*
* @param which identifies which name
* @return the requested name, if available
*/
String getName(DebugModuleName which);
/**
* Get the index assigned to this module.
*
* @return the index
*/
int getIndex();
/**
* Get the base address where this module is loaded, if applicable.
*
* @return the base address
*/
long getBase();
}

View file

@ -0,0 +1,44 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* Information about a module (program or library image).
*
* The fields correspond to the parameters taken by {@code LoadModule} of
* {@code IDebugEventCallbacks}. They also appear as a subset of parameters taken by
* {@code CreateProcess} of {@code IDebugEventCallbacks}.
*/
public class DebugModuleInfo {
public final long imageFileHandle;
public final long baseOffset;
public final int moduleSize;
public final String moduleName;
public final String imageName;
public final int checkSum;
public final int timeDateStamp;
public DebugModuleInfo(long imageFileHandle, long baseOffset, int moduleSize, String moduleName,
String imageName, int checkSum, int timeDateStamp) {
this.imageFileHandle = imageFileHandle;
this.baseOffset = baseOffset;
this.moduleSize = moduleSize;
this.moduleName = moduleName;
this.imageName = imageName;
this.checkSum = checkSum;
this.timeDateStamp = timeDateStamp; // TODO: Convert to DateTime?
}
}

View file

@ -0,0 +1,32 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import agent.dbgeng.dbgeng.DebugControl.DebugOutputLevel;
/**
* The interface for receiving output callbacks via {@code IDebugOutputCallbacks} or a newer
* variant.
*
* Note: The wrapper implementation will select the apprirate native interface version.
*
* TODO: Change {@link #output(int, String)} {@code mask} parameter to use {@link DebugOutputLevel}
* flags.
*/
@FunctionalInterface
public interface DebugOutputCallbacks {
void output(int mask, String text);
}

View file

@ -0,0 +1,61 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* The <em>engine</em> ID assigned to a debugged process.
*
* Note: This is not the same as the "PID." {@code dbgeng.dll} calls that the <em>system</em> ID of
* the process.
*
* This is essentially just a boxed integer, but having an explicit data type prevents confusion
* with other integral values. In particular, this prevents confusion of engine PIDs with system
* PIDs.
*/
public class DebugProcessId implements Comparable<DebugProcessId> {
public final int id;
public DebugProcessId(int id) {
this.id = id;
}
@Override
public int hashCode() {
return Integer.hashCode(id);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DebugProcessId)) {
return false;
}
DebugProcessId that = (DebugProcessId) obj;
if (this.id != that.id) {
return false;
}
return true;
}
@Override
public int compareTo(DebugProcessId that) {
return Integer.compare(this.id, that.id);
}
@Override
public String toString() {
return "<dbgeng.dll Engine PID " + id + ">";
}
}

View file

@ -0,0 +1,36 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* Information about a process.
*
* The fields correspond to parameters taken by {@code CreateProcess} of
* {@code IDebugEventCallbacks}. Note that parameters common to other callbacks have been factored
* into types aggregated here.
*/
public class DebugProcessInfo {
public final long handle;
public final DebugModuleInfo moduleInfo;
public final DebugThreadInfo initialThreadInfo;
public DebugProcessInfo(long handle, DebugModuleInfo moduleInfo,
DebugThreadInfo initialThreadInfo) {
this.handle = handle;
this.moduleInfo = moduleInfo;
this.initialThreadInfo = initialThreadInfo;
}
}

View file

@ -0,0 +1,158 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import java.util.*;
import agent.dbgeng.dbgeng.DebugValue.DebugValueType;
import ghidra.comm.util.BitmaskSet;
import ghidra.comm.util.BitmaskUniverse;
/**
* A wrapper for {@code IDebugRegisters} and its newer variants.
*/
public interface DebugRegisters {
public static enum DebugRegisterSource {
DEBUG_REGSRC_DEBUGGEE, //
DEBUG_REGSRC_EXPLICIT, //
DEBUG_REGSRC_FRAME, //
;
}
public static enum DebugRegisterFlags implements BitmaskUniverse {
SUB_REGISTER(1 << 0), //
;
private DebugRegisterFlags(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public static class DebugRegisterDescription {
public final String name;
public final int index;
public final DebugValueType type;
public final Set<DebugRegisterFlags> flags;
public final int subregMaster;
public final int subregLengthBits;
public final long subregMask;
public final int subregShift;
public DebugRegisterDescription(String name, int index, DebugValueType type,
BitmaskSet<DebugRegisterFlags> flags, int subregMaster, int subregLengthBits,
long subregMask, int subregShift) {
this.name = name;
this.index = index;
this.type = type;
this.flags = Collections.unmodifiableSet(flags);
this.subregMaster = subregMaster;
this.subregLengthBits = subregLengthBits;
this.subregMask = subregMask;
this.subregShift = subregShift;
}
@Override
public String toString() {
return String.format(
"<%s: name='%s' index=%d type=%s flags=%s subregMaster=%d subregLengthBits=%d" +
" subregMask=%x subregShift=%d>",
getClass().getSimpleName(), name, index, type, flags, subregMaster,
subregLengthBits, subregMask, subregShift);
}
}
int getNumberRegisters();
DebugRegisterDescription getDescription(int registerNumber);
/**
* A shortcut to get all register descriptions for the current process.
*
* Uses {@link #getNumberRegisters()} and {@link #getDescription(int)} to retrieve all
* descriptions for the current process.
*
* @return the list of register descriptions
*/
default Set<DebugRegisterDescription> getAllDescriptions() {
Set<DebugRegisterDescription> result = new LinkedHashSet<>();
int count = getNumberRegisters();
for (int i = 0; i < count; i++) {
result.add(getDescription(i));
}
return result;
}
int getIndexByName(String name);
/**
* A shortcut to get many register indices in one call.
*
* Uses {@link #getIndexByName(String)}.
*
* @param names the names whose indices to get
* @return the indices in respective order to the given names
*/
default int[] getIndicesByNames(String... names) {
int[] indices = new int[names.length];
for (int i = 0; i < names.length; i++) {
indices[i] = getIndexByName(names[i]);
}
return indices;
}
DebugValue getValue(int index);
Map<Integer, DebugValue> getValues(DebugRegisterSource source, Collection<Integer> indices);
/**
* A shortcut to get a register value by name.
*
* Uses {@link #getIndexByName(String)} followed by {@link #getValue(int)}.
*
* @param name the name of the register
* @return the value
*/
default DebugValue getValueByName(String name) {
int indexByName = getIndexByName(name);
if (indexByName > 0) {
return getValue(indexByName);
}
return null;
}
void setValue(int index, DebugValue value);
void setValues(DebugRegisterSource source, Map<Integer, DebugValue> values);
/**
* A shortcut to set a register value by name.
*
* Uses {@link #getIndexByName(String)} followed by {@link #setValue(int, DebugValue)}.
*
* @param name the name of the register
* @param value the desired value
*/
default void setValueByName(String name, DebugValue value) {
setValue(getIndexByName(name), value);
}
}

View file

@ -0,0 +1,123 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import agent.dbgeng.dbgeng.DebugRunningProcess.Description.ProcessDescriptionFlags;
import ghidra.comm.util.BitmaskUniverse;
/**
* Information about a running process, not necessarily a debugged process.
*/
public interface DebugRunningProcess {
/**
* Description of a running process
*/
public static class Description {
public static enum ProcessDescriptionFlags implements BitmaskUniverse {
NO_PATHS(1 << 0), //
NO_SERVICES(1 << 1), //
NO_MTS_PACKAGES(1 << 2), //
NO_COMMAND_LINE(1 << 3), //
NO_SESSION_ID(1 << 4), //
NO_USER_NAME(1 << 5), //
;
ProcessDescriptionFlags(int mask) {
this.mask = mask;
}
private final int mask;
@Override
public long getMask() {
return mask;
}
}
public Description(int systemId, String exeName, String description) {
this.systemId = systemId;
this.exeName = exeName;
this.description = description;
}
private final int systemId;
private final String exeName;
private final String description;
/**
* The system ID (PID) for the process.
*
* @return the PID
*/
public int getSystemId() {
return systemId;
}
/**
* The name of the executable defining the process
*
* @return the name
*/
public String getExecutableName() {
return exeName;
}
/**
* A textual description of the process.
*
* @return the description
*/
public String getDescription() {
return description;
}
@Override
public String toString() {
return String.format("PID:%d, EXE:%s, Description:%s", systemId, exeName, description);
}
}
/**
* The system ID (PID) for the process.
*
* @return the PID
*/
int getSystemId();
/**
* Get the "full" description of the process.
*
* @param flags indicate which information to include in the description
* @return the description
*/
Description getFullDescription(ProcessDescriptionFlags... flags);
/**
* The name of the executable defining the process.
*
* @param flags indicate which information to include in the description
* @return the name
*/
String getExecutableName(ProcessDescriptionFlags... flags);
/**
* A textual description of the process.
*
* @param flags indicate which information to include in the description
* @return the description
*/
String getDescription(ProcessDescriptionFlags... flags);
}

View file

@ -0,0 +1,58 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* The ID of a debug server.
*
* Each server to which a client is connected is assigned a server ID. The local server, to which
* every client is connected by default, has the ID 0. This is essentially just a boxed integer, but
* having an explicit data type prevents confusion with other integral values.
*/
public class DebugServerId implements Comparable<DebugServerId> {
public final long id;
public DebugServerId(long id) {
this.id = id;
}
@Override
public int hashCode() {
return Long.hashCode(id);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DebugServerId)) {
return false;
}
DebugServerId that = (DebugServerId) obj;
if (this.id != that.id) {
return false;
}
return true;
}
@Override
public int compareTo(DebugServerId that) {
return Long.compare(this.id, that.id);
}
@Override
public String toString() {
return "<dbgeng.dll Server ID " + id + ">";
}
}

View file

@ -0,0 +1,61 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* The <em>engine</em> ID assigned to a debugged process.
*
* Note: This is not the same as the "PID." {@code dbgeng.dll} calls that the <em>system</em> ID of
* the process.
*
* This is essentially just a boxed integer, but having an explicit data type prevents confusion
* with other integral values. In particular, this prevents confusion of engine PIDs with system
* PIDs.
*/
public class DebugSessionId implements Comparable<DebugSessionId> {
public final int id;
public DebugSessionId(int id) {
this.id = id;
}
@Override
public int hashCode() {
return Integer.hashCode(id);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DebugSessionId)) {
return false;
}
DebugSessionId that = (DebugSessionId) obj;
if (this.id != that.id) {
return false;
}
return true;
}
@Override
public int compareTo(DebugSessionId that) {
return Integer.compare(this.id, that.id);
}
@Override
public String toString() {
return "<dbgeng.dll Engine SYSID " + id + ">";
}
}

View file

@ -0,0 +1,37 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_STACK_FRAME;
public class DebugStackInformation {
private int nFrames;
private DEBUG_STACK_FRAME[] stackFrames;
public DebugStackInformation(int nFrames, DEBUG_STACK_FRAME[] stackFrames) {
this.nFrames = nFrames;
this.stackFrames = stackFrames;
}
public int getNumberOfFrames() {
return nFrames;
}
public DEBUG_STACK_FRAME getFrame(int frameNumber) {
return stackFrames[frameNumber];
}
}

View file

@ -0,0 +1,56 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* Data copied from a {@code DEBUG_SYMBOL_ENTRY} as defined in {@code dbgeng.h}.
*
* TODO: Some enums, flags, etc., to help interpret some of the fields.
*/
public class DebugSymbolEntry {
public final long moduleBase;
public final long offset;
public final long symbolId;
public final long size;
public final int flags;
public final int typeId;
public final String name;
public final int tag;
public DebugSymbolEntry(long moduleBase, long offset, long symbolId, long size, int flags,
int typeId, String name, int tag) {
this.moduleBase = moduleBase;
this.offset = offset;
this.symbolId = symbolId;
this.size = size;
this.flags = flags;
this.typeId = typeId;
this.name = name;
this.tag = tag;
}
@Override
public String toString() {
return String.format("<DebugSymbolEntry %016x:%016x\n" + //
" offset=%016xh,\n" + //
" size=%xh,\n" + //
" flags=%xh,\n" + //
" typeId=%xh,\n" + //
" name='%s',\n" + //
" tag=%xh>", //
moduleBase, symbolId, offset, size, flags, typeId, name, tag);
}
}

View file

@ -0,0 +1,34 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* Symbol identifier, consisting of module ID and symbol index.
*/
public class DebugSymbolId {
public final long moduleBase;
public final long symbolIndex;
public DebugSymbolId(long moduleBase, long symbolIndex) {
this.moduleBase = moduleBase;
this.symbolIndex = symbolIndex;
}
@Override
public String toString() {
return String.format("<DebugSymbolId %016x:%016x>", moduleBase, symbolIndex);
}
}

View file

@ -0,0 +1,34 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* Symbol name, consisting of textual name and offset.
*/
public class DebugSymbolName {
public final String name;
public final long offset;
public DebugSymbolName(String name, long offset) {
this.name = name;
this.offset = offset;
}
@Override
public String toString() {
return String.format("<%016x: %s>", offset, name);
}
}

View file

@ -0,0 +1,78 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import java.util.Iterator;
import java.util.List;
/**
* A wrapper for {@code IDebugSymbols} and its newer variants.
*/
public interface DebugSymbols {
int getNumberLoadedModules();
int getNumberUnloadedModules();
DebugModule getModuleByIndex(int index);
DebugModule getModuleByModuleName(String name, int startIndex);
DebugModule getModuleByOffset(long offset, int startIndex);
/**
* A shortcut for iterating over all loaded modules, lazily.
*
* @param startIndex the module index to start at
* @return an iterator over modules starting at the given index
*/
default Iterable<DebugModule> iterateModules(int startIndex) {
int count = getNumberLoadedModules(); // TODO: What about unloaded?
return new Iterable<DebugModule>() {
@Override
public Iterator<DebugModule> iterator() {
return new Iterator<DebugModule>() {
int cur = startIndex;
@Override
public boolean hasNext() {
return cur < count;
}
@Override
public DebugModule next() {
DebugModule ret = getModuleByIndex(cur);
cur++;
return ret;
}
};
}
};
}
Iterable<DebugSymbolName> iterateSymbolMatches(String pattern);
List<DebugSymbolId> getSymbolIdsByName(String pattern);
DebugSymbolEntry getSymbolEntry(DebugSymbolId id);
String getSymbolPath();
void setSymbolPath(String path);
int getSymbolOptions();
void setSymbolOptions(int options);
}

View file

@ -0,0 +1,102 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import java.util.ArrayList;
import java.util.List;
/**
* A wrapper for {@code IDebugSystemObjects} and its newer variants.
*/
public interface DebugSystemObjects {
DebugThreadId getEventThread();
DebugProcessId getEventProcess();
DebugSessionId getEventSystem();
DebugThreadId getCurrentThreadId();
void setCurrentThreadId(DebugThreadId id);
DebugProcessId getCurrentProcessId();
void setCurrentProcessId(DebugProcessId id);
DebugSessionId getCurrentSystemId();
void setCurrentSystemId(DebugSessionId id);
int getNumberThreads();
int getTotalNumberThreads(); // TODO: LargestProcess?
/**
* Get the threads IDs by index from the current process
*
* @param start the starting index
* @param count the number of threads
* @return the list of thread IDs
*/
List<DebugThreadId> getThreads(int start, int count);
/**
* Get all thread IDs in the current process
*
* @return the list of thread IDs
*/
default List<DebugThreadId> getThreads() {
return getThreads(0, getNumberThreads());
}
DebugThreadId getThreadIdByHandle(long handle);
DebugProcessId getProcessIdByHandle(long handle);
int getNumberSystems();
List<DebugSessionId> getSystems(int start, int count);
default List<DebugSessionId> getSessions() {
int numberSystems = getNumberSystems();
if (numberSystems < 0) {
return new ArrayList<DebugSessionId>();
}
return getSystems(0, numberSystems);
}
int getNumberProcesses();
List<DebugProcessId> getProcesses(int start, int count);
default List<DebugProcessId> getProcesses() {
int numberProcesses = getNumberProcesses();
if (numberProcesses < 0) {
return new ArrayList<DebugProcessId>();
}
return getProcesses(0, numberProcesses);
}
int getCurrentThreadSystemId();
int getCurrentProcessSystemId();
DebugThreadId getThreadIdBySystemId(int systemId);
DebugProcessId getProcessIdBySystemId(int systemId);
}

View file

@ -0,0 +1,61 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* The <em>engine</em> ID assigned to a debugged thread.
*
* Note: This is not the same as the "TID." {@code dbgeng.dll} calls that the <em>system</em> ID of
* the thread.
*
* This is essentially just a boxed integer, but having an explicit data type prevents confusion
* with other integral values. In particular, this prevents confusion of engine TIDs with system
* TIDs.
*/
public class DebugThreadId implements Comparable<DebugThreadId> {
public final int id;
public DebugThreadId(int id) {
this.id = id;
}
@Override
public int hashCode() {
return Integer.hashCode(id);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DebugThreadId)) {
return false;
}
DebugThreadId that = (DebugThreadId) obj;
if (this.id != that.id) {
return false;
}
return true;
}
@Override
public int compareTo(DebugThreadId that) {
return Integer.compare(this.id, that.id);
}
@Override
public String toString() {
return "<dbgeng.dll Engine TID " + id + ">";
}
}

View file

@ -0,0 +1,35 @@
/* ###
* 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 agent.dbgeng.dbgeng;
/**
* Information about a thread.
*
* The fields correspond to parameters taken by {@code CreateThread} of
* {@code IDebugEventCallbacks}. They also appear as a subset of parameters taken by
* {@code CreateProcess} of {@code IDebugEventCallbacks}.
*/
public class DebugThreadInfo {
public final long handle;
public final long dataOffset;
public final long startOffset;
public DebugThreadInfo(long handle, long dataOffset, long startOffset) {
this.handle = handle;
this.dataOffset = dataOffset;
this.startOffset = startOffset;
}
}

View file

@ -0,0 +1,457 @@
/* ###
* 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 agent.dbgeng.dbgeng;
import java.lang.annotation.*;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import agent.dbgeng.dbgeng.DebugValue.DebugValueType;
import agent.dbgeng.dbgeng.DebugValue.ForDebugValueType;
import ghidra.util.NumericUtilities;
/**
* Data copied from a {@code DEBUG_VALUE} as defined in {dbgeng.h}.
*/
@ForDebugValueType(DebugValueType.INVALID)
public interface DebugValue {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public static @interface ForDebugValueType {
DebugValueType value();
}
public static enum DebugValueType {
INVALID(0), //
INT8(Byte.SIZE), //
INT16(Short.SIZE), //
INT32(Integer.SIZE), //
INT64(Long.SIZE), //
FLOAT32(Float.SIZE), //
FLOAT64(Double.SIZE), //
FLOAT80(80), //
FLOAT82(82), //
FLOAT128(128), //
VECTOR64(64), //
VECTOR128(128), //
;
private static final Class<? extends DebugValue>[] CLASSES;
static {
@SuppressWarnings("unchecked")
Class<? extends DebugValue>[] supressed = new Class[DebugValueType.values().length];
CLASSES = supressed;
for (Class<?> cls : DebugValue.class.getDeclaredClasses()) {
if (!DebugValue.class.isAssignableFrom(cls)) {
continue;
}
Class<? extends DebugValue> dvCls = cls.asSubclass(DebugValue.class);
DebugValueType type = getDebugValueTypeForClass(dvCls);
CLASSES[type.ordinal()] = dvCls;
}
}
public static DebugValueType getDebugValueTypeForClass(Class<? extends DebugValue> cls) {
ForDebugValueType annot = cls.getAnnotation(ForDebugValueType.class);
if (annot == null) {
throw new AssertionError(
"INTERNAL: Missing ForDebugValueType annotation on " + cls);
}
return annot.value();
}
public final int bitLength;
public final int byteLength;
private DebugValueType(int bitLength) {
this.bitLength = bitLength;
this.byteLength = (bitLength + 7) / 8;
}
public Class<? extends DebugValue> getDebugValueClass() {
return CLASSES[ordinal()];
}
public DebugValue decodeBytes(byte[] bytes) throws IllegalArgumentException {
try {
return CLASSES[ordinal()].getConstructor(byte[].class).newInstance(bytes);
}
catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| NoSuchMethodException | SecurityException e) {
throw new AssertionError(e);
}
catch (InvocationTargetException e) {
if (e.getCause() instanceof IllegalArgumentException) {
throw (IllegalArgumentException) e.getCause();
}
throw new AssertionError(e);
}
}
}
@ForDebugValueType(DebugValueType.INT8)
public static class DebugInt8Value implements DebugValue {
private final byte value;
public DebugInt8Value(byte value) {
this.value = value;
}
public DebugInt8Value(byte[] bytes) {
if (bytes.length != 1) {
throw new IllegalArgumentException("Must have exactly 1 byte");
}
this.value = bytes[0];
}
public byte byteValue() {
return value;
}
@Override
public byte[] encodeAsBytes() {
return new byte[] { value };
}
@Override
public String toString() {
return "byte " + Integer.toHexString(value) + "h";
}
}
@ForDebugValueType(DebugValueType.INT16)
public static class DebugInt16Value implements DebugValue {
private final short value;
public DebugInt16Value(short value) {
this.value = value;
}
public DebugInt16Value(byte[] bytes) {
if (bytes.length != Short.BYTES) {
throw new IllegalArgumentException("Must have exactly " + Short.BYTES + " bytes");
}
ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
this.value = buf.getShort();
}
public short shortValue() {
return value;
}
@Override
public byte[] encodeAsBytes() {
ByteBuffer buf = ByteBuffer.allocate(Short.BYTES).order(ByteOrder.BIG_ENDIAN);
buf.putShort(value);
return buf.array();
}
@Override
public String toString() {
return "word " + Integer.toHexString(value) + "h";
}
}
@ForDebugValueType(DebugValueType.INT32)
public static class DebugInt32Value implements DebugValue {
private final int value;
public DebugInt32Value(int value) {
this.value = value;
}
public DebugInt32Value(byte[] bytes) {
if (bytes.length != Integer.BYTES) {
throw new IllegalArgumentException("Must have exactly " + Integer.BYTES + " bytes");
}
ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
this.value = buf.getInt();
}
public int intValue() {
return value;
}
@Override
public byte[] encodeAsBytes() {
ByteBuffer buf = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.BIG_ENDIAN);
buf.putInt(value);
return buf.array();
}
@Override
public String toString() {
return "dword " + Integer.toHexString(value) + "h";
}
}
@ForDebugValueType(DebugValueType.INT64)
public static class DebugInt64Value implements DebugValue {
private final long value;
public DebugInt64Value(long value) {
this.value = value;
}
public DebugInt64Value(byte[] bytes) {
if (bytes.length != Long.BYTES) {
throw new IllegalArgumentException("Must have exactly " + Long.BYTES + " bytes");
}
ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
this.value = buf.getLong();
}
public long longValue() {
return value;
}
@Override
public byte[] encodeAsBytes() {
ByteBuffer buf = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.BIG_ENDIAN);
buf.putLong(value);
return buf.array();
}
@Override
public String toString() {
return "qword " + Long.toHexString(value) + "h";
}
}
@ForDebugValueType(DebugValueType.FLOAT32)
public static class DebugFloat32Value implements DebugValue {
private final float value;
public DebugFloat32Value(float value) {
this.value = value;
}
public DebugFloat32Value(byte[] bytes) {
if (bytes.length != Float.BYTES) {
throw new IllegalArgumentException("Must have exactly " + Float.BYTES + " bytes");
}
ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
this.value = buf.getFloat();
}
public float floatValue() {
return value;
}
@Override
public byte[] encodeAsBytes() {
ByteBuffer buf = ByteBuffer.allocate(Float.BYTES).order(ByteOrder.BIG_ENDIAN);
buf.putFloat(value);
return buf.array();
}
@Override
public String toString() {
return "f32 " + value;
}
}
@ForDebugValueType(DebugValueType.FLOAT64)
public static class DebugFloat64Value implements DebugValue {
private final double value;
public DebugFloat64Value(double value) {
this.value = value;
}
public DebugFloat64Value(byte[] bytes) {
if (bytes.length != Double.BYTES) {
throw new IllegalArgumentException("Must have exactly " + Double.BYTES + " bytes");
}
ByteBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
this.value = buf.getDouble();
}
public double doubleValue() {
return value;
}
@Override
public byte[] encodeAsBytes() {
ByteBuffer buf = ByteBuffer.allocate(Double.BYTES).order(ByteOrder.BIG_ENDIAN);
buf.putDouble(value);
return buf.array();
}
@Override
public String toString() {
return "f64 " + value;
}
}
/**
* Extended-precision float
*/
@ForDebugValueType(DebugValueType.FLOAT80)
public static class DebugFloat80Value implements DebugValue {
private final byte[] bytes;
public DebugFloat80Value(byte[] bytes) {
if (bytes.length != 10) {
throw new IllegalArgumentException("Must have exactly 10 bytes");
}
this.bytes = Arrays.copyOf(bytes, 10);
}
public byte[] bytes() {
return bytes;
}
@Override
public byte[] encodeAsBytes() {
return bytes;
}
@Override
public String toString() {
return "f80 " + NumericUtilities.convertBytesToString(bytes);
}
}
/**
* Specific to IA-64 (Itanium) floating-point registers
*
* 17-bit exponent, 64-bit fraction. Not sure how it's aligned in memory, though.
*/
@ForDebugValueType(DebugValueType.FLOAT82)
public static class DebugFloat82Value implements DebugValue {
private final byte[] bytes;
public DebugFloat82Value(byte[] bytes) {
if (bytes.length != 11) {
throw new IllegalArgumentException("Must have exactly 11 bytes");
}
this.bytes = Arrays.copyOf(bytes, 11);
}
public byte[] bytes() {
return bytes;
}
@Override
public byte[] encodeAsBytes() {
return bytes;
}
@Override
public String toString() {
return "f82 " + NumericUtilities.convertBytesToString(bytes);
}
}
/**
* Quadruple-precision float
*/
@ForDebugValueType(DebugValueType.FLOAT128)
public static class DebugFloat128Value implements DebugValue {
private final byte[] bytes;
public DebugFloat128Value(byte[] bytes) {
if (bytes.length != 16) {
throw new IllegalArgumentException("Must have exactly 16 bytes");
}
this.bytes = Arrays.copyOf(bytes, 16);
}
public byte[] bytes() {
return bytes;
}
@Override
public byte[] encodeAsBytes() {
return bytes;
}
@Override
public String toString() {
return "f128 " + NumericUtilities.convertBytesToString(bytes);
}
}
@ForDebugValueType(DebugValueType.VECTOR64)
public static class DebugVector64Value implements DebugValue {
private final byte[] bytes;
public DebugVector64Value(byte[] bytes) {
if (bytes.length != 8) {
throw new IllegalArgumentException("Must have exactly 8 bytes");
}
this.bytes = Arrays.copyOf(bytes, 8);
}
public byte[] vi4() {
return bytes;
}
@Override
public byte[] encodeAsBytes() {
return bytes;
}
@Override
public String toString() {
return "vec64 " + NumericUtilities.convertBytesToString(bytes);
}
}
@ForDebugValueType(DebugValueType.VECTOR128)
public static class DebugVector128Value implements DebugValue {
private final byte[] bytes;
public DebugVector128Value(byte[] bytes) {
if (bytes.length != 16) {
throw new IllegalArgumentException(
"Must have exactly 16 bytes. got " + bytes.length);
}
this.bytes = Arrays.copyOf(bytes, 16);
}
public byte[] vi8() {
return bytes;
}
@Override
public byte[] encodeAsBytes() {
return bytes;
}
@Override
public String toString() {
return "vec128 " + NumericUtilities.convertBytesToString(bytes);
}
}
default DebugValueType getValueType() {
return DebugValueType.getDebugValueTypeForClass(getClass());
}
/**
* TODO: Document me
*
* Encodes the value as an array of bytes in big-endian order
*
* @return the encoded value
*/
public byte[] encodeAsBytes();
}

View file

@ -0,0 +1,29 @@
/* ###
* 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 agent.dbgeng.dbgeng.err;
/**
* The base exception for checked {@code dbgeng.dll} wrapper-related errors.
*/
public class DbgEngException extends Exception {
public DbgEngException() {
super();
}
public DbgEngException(String message) {
super(message);
}
}

View file

@ -0,0 +1,29 @@
/* ###
* 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 agent.dbgeng.dbgeng.err;
/**
* The base exception for unchecked {@code dbgeng.dll} wrapper-related errors.
*/
public class DbgEngRuntimeException extends RuntimeException {
public DbgEngRuntimeException() {
super();
}
public DbgEngRuntimeException(String message) {
super(message);
}
}

View file

@ -0,0 +1,49 @@
/* ###
* 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 agent.dbgeng.dbgeng.ext;
import com.sun.jna.platform.win32.COM.COMUtils;
import com.sun.jna.ptr.PointerByReference;
import agent.dbgeng.dbgeng.DebugClient;
import agent.dbgeng.impl.dbgeng.client.DebugClientInternal;
import agent.dbgeng.jna.dbgeng.client.WrapIDebugClient;
import agent.dbgeng.jna.javaprovider.JavaProviderNative;
/**
* Wrapper for "javaprovider" plugin library
*
* @deprecated In one (abandoned) use case, the SCTL server can be loaded as a
* "{@code engext.cpp}-style" plugin, presumably into any {@code dbgeng.dll}-powered
* debugger. This is accomplished by embedding the JVM into the plugin, and then calling
* an alternative entry point. This plugin also provides a utility function for invoking
* {@code CreateClient} on the client provided to the plugin by the host debugger.
*/
@Deprecated
public class JavaProvider {
public static DebugClient createClient() {
PointerByReference pClient = new PointerByReference();
COMUtils.checkRC(JavaProviderNative.INSTANCE.createClient(pClient.getPointer()));
WrapIDebugClient wrap = new WrapIDebugClient(pClient.getValue());
try {
return DebugClientInternal.tryPreferredInterfaces(wrap::QueryInterface);
}
finally {
wrap.Release();
}
}
}

View file

@ -0,0 +1,125 @@
/* ###
* 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 agent.dbgeng.dbgeng.util;
import java.lang.reflect.Method;
import agent.dbgeng.dbgeng.*;
import agent.dbgeng.dbgeng.DebugClient.*;
import ghidra.comm.util.BitmaskSet;
/**
* A convenient abstract implementation of {@link DebugEventCallbacks}
*
* This implementation automatically computes the value for {@link #getInterestMask()} based on the
* overridden methods. The default implementations all return {@link DebugStatus#NO_CHANGE}, should
* they happen to be called.
*/
public class DebugEventCallbacksAdapter implements DebugEventCallbacks {
private BitmaskSet<DebugEvent> interests = new BitmaskSet<>(DebugEvent.class);
public DebugEventCallbacksAdapter() {
try {
// Compute the interest mask based on methods that are overridden
for (Method im : DebugEventCallbacks.class.getDeclaredMethods()) {
Method m = this.getClass().getMethod(im.getName(), im.getParameterTypes());
if (m.getDeclaringClass() == DebugEventCallbacksAdapter.class) {
continue;
}
// The interface method is overridden, grab the annotation from the interface
ForInterest fi = im.getAnnotation(ForInterest.class);
if (fi == null) {
throw new AssertionError("No ForInterest annotation present on " + m);
}
interests.add(fi.value());
}
}
catch (NoSuchMethodException e) {
throw new AssertionError(e);
}
}
@Override
public BitmaskSet<DebugEvent> getInterestMask() {
return interests;
}
@Override
public DebugStatus breakpoint(DebugBreakpoint bp) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus exception(DebugExceptionRecord64 exception, boolean firstChance) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus createThread(DebugThreadInfo debugThreadInfo) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus exitThread(int exitCode) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus createProcess(DebugProcessInfo debugProcessInfo) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus exitProcess(int exitCode) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus loadModule(DebugModuleInfo debugModuleInfo) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus unloadModule(String imageBaseName, long baseOffset) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus systemError(int error, int level) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus sessionStatus(SessionStatus status) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus changeDebuggeeState(BitmaskSet<ChangeDebuggeeState> flags, long argument) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus changeEngineState(BitmaskSet<ChangeEngineState> flags, long argument) {
return DebugStatus.NO_CHANGE;
}
@Override
public DebugStatus changeSymbolState(BitmaskSet<ChangeSymbolState> flags, long argument) {
return DebugStatus.NO_CHANGE;
}
}

View file

@ -0,0 +1,242 @@
/* ###
* 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 agent.dbgeng.gadp;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import agent.dbgeng.gadp.impl.DbgEngGadpServerImpl;
import ghidra.dbg.agent.AgentWindow;
import ghidra.util.Msg;
public interface DbgEngGadpServer extends AutoCloseable {
public static final String DEFAULT_DBGSRV_TRANSPORT = "tcp:port=11200";
/**
* The entry point for the SCTL-DBGENG server in stand-alone mode
*
* Run it to see help.
*
* @param args the command-line arguments
* @throws IOException if an I/O error occurs
* @throws ExecutionException
* @throws InterruptedException
*/
public static void main(String[] args) throws Exception {
new DbgEngRunner().run(args);
}
/**
* Create a new instance of the server
*
* @param addr the address to bind the SCTL server to
* @param busId the client ID the server should use on the bus for synthesized commands
* @param dbgSrvTransport the transport specification for the {@code dbgeng.dll} server
* @return the server instance
* @throws IOException
*/
public static DbgEngGadpServer newInstance(SocketAddress addr) throws IOException {
return new DbgEngGadpServerImpl(addr);
}
/**
* Runs the server from the command line
*/
public class DbgEngRunner {
protected InetSocketAddress bindTo;
protected List<String> dbgengArgs = new ArrayList<>();
protected byte busId = 1;
protected String dbgSrvTransport = DEFAULT_DBGSRV_TRANSPORT;
protected String remote = null;
public DbgEngRunner() {
}
public void run(String args[])
throws IOException, InterruptedException, ExecutionException {
parseArguments(args);
try (DbgEngGadpServer server = newInstance(bindTo)) {
//TODO: fix/test the debugConnect case when args are passed
server.startDbgEng(dbgengArgs.toArray(new String[] {})).exceptionally(e -> {
Msg.error(this, "Error starting dbgeng/GADP", e);
System.exit(-1);
return null;
});
new AgentWindow("dbgeng.dll Agent for Ghidra", server.getLocalAddress());
while (server.isRunning()) {
// TODO: Put consoleLoop back?
Thread.sleep(1000);
}
System.exit(0);
}
}
protected void parseArguments(String[] args) {
String iface = "localhost";
int port = 12345;
// NOTE: Maybe commons-cli or Argparse4j?
Iterator<String> ait = Arrays.asList(args).iterator();
while (ait.hasNext()) {
String a = ait.next();
if ("-h".equals(a) || "--help".equals(a)) {
printUsage();
System.exit(0);
}
else if ("-p".equals(a) || "--port".equals(a)) {
if (!ait.hasNext()) {
System.err.println("Expected PORT");
printUsage();
System.exit(-1);
}
String portStr = ait.next();
try {
port = Integer.parseInt(portStr);
}
catch (NumberFormatException e) {
System.err.println("Integer required. Got " + portStr);
printUsage();
System.exit(-1);
}
}
else if ("-H".equals(a) || "--host".equals(a)) {
if (!ait.hasNext()) {
System.err.println("Expected HOST/ADDR");
printUsage();
System.exit(-1);
}
iface = ait.next();
}
else if ("-i".equals(a) || "--bus-id".equals(a)) {
if (!ait.hasNext()) {
System.err.println("Expected ID");
printUsage();
System.exit(-1);
}
String busIdStr = ait.next();
try {
busId = Byte.parseByte(busIdStr);
//dbgengArgs.add(busIdStr);
}
catch (NumberFormatException e) {
System.err.println("Byte required. Got " + busIdStr);
printUsage();
System.exit(-1);
}
}
else if ("-t".equals(a) || "--transport".equals(a)) {
if (!ait.hasNext()) {
System.err.println("Expected TRANSPORT");
System.err.println("See the MSDN 'Activating a Process Server'");
printUsage();
System.exit(-1);
}
dbgSrvTransport = ait.next();
dbgengArgs.add(dbgSrvTransport);
}
else if ("-r".equals(a) || "--remote".equals(a)) {
if (!ait.hasNext()) {
System.err.println("Expected TRANSPORT:HOST,PORT");
printUsage();
System.exit(-1);
}
remote = ait.next();
dbgengArgs.add(remote);
}
else {
System.err.println("Unknown option: " + a);
printUsage();
System.exit(-1);
}
}
bindTo = new InetSocketAddress(iface, port);
}
protected void printUsage() {
System.out.println("This is the GADP server for Windows dbgeng.dll. Usage:");
System.out.println();
System.out.println(" [-H HOST/ADDR] [-p PORT] [-i ID] [-t TRANSPORT] [-r REMOTE]");
System.out.println();
System.out.println("Options:");
System.out.println(
" --host/-H The address of the interface on which to listen.");
System.out.println(" Default is localhost");
System.out.println(
" --port/-p The TCP port on which to listen for GADP. Default is 12345");
System.out.println(
" --bus-id/-i The numeric client id for synthetic requests. Default is 1");
System.out.println(
" --transport/-t The transport specification for the Process Server.");
System.out.println(" Default is tcp:port=11200");
System.out.println(
" --remote/-r The transport specification for a remote server.");
}
}
/**
* Start the debugging server
*
* @return a future that completes when the server is ready
*/
CompletableFuture<Void> startDbgEng(String[] args);
/**
* Get the local address to which the SCTL server is bound.
*
* @return the local socket address
*/
SocketAddress getLocalAddress();
/**
* Starts the dbgeng manager's console loop
*
* @throws IOException if an I/O error occurs
*/
//public void consoleLoop() throws IOException;
/**
* Close all connections and ports, GADP and Process Server, and terminate the server
*
* @throws IOException if an I/O error occurs
*/
public void terminate() throws IOException;
/**
* Check if the server is running
*
* This will return false: 1) Before the server has been started, 2) After a call to
* {@link #terminate()}, or 3) When an error occurs causing the server to terminate
* unexpectedly. Otherwise, it returns true.
*
* @returns true if the server is currently running.
*/
public boolean isRunning();
/**
* Calls {@link #terminate()}
*
* @throws IOException if an I/O error occurs
*/
@Override
default void close() throws IOException {
terminate();
}
}

View file

@ -0,0 +1,85 @@
/* ###
* 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 agent.dbgeng.gadp;
import java.util.List;
import ghidra.dbg.gadp.server.AbstractGadpLocalDebuggerModelFactory;
import ghidra.dbg.util.ConfigurableFactory.FactoryDescription;
import ghidra.util.classfinder.ExtensionPointProperties;
@FactoryDescription( //
brief = "MS dbgeng.dll (WinDbg) local agent via GADP/TCP", //
htmlDetails = "Launch a new agent using the Microsoft Debug Engine." //
)
@ExtensionPointProperties(priority = 100)
public class DbgEngLocalDebuggerModelFactory extends AbstractGadpLocalDebuggerModelFactory {
protected String remote = "none"; // Require user to start server
@FactoryOption("DebugConnect options (.server)")
public final Property<String> agentRemoteOption =
Property.fromAccessors(String.class, this::getAgentRemote, this::setAgentRemote);
protected String transport = "none"; // Require user to start server
@FactoryOption("Remote process server options (untested)")
public final Property<String> agentTransportOption =
Property.fromAccessors(String.class, this::getAgentTransport, this::setAgentTransport);
@Override
public boolean isCompatible() {
// TODO: Might instead look for the DLL
return System.getProperty("os.name").toLowerCase().contains("windows");
}
public String getAgentTransport() {
return transport;
}
public void setAgentTransport(String transport) {
this.transport = transport;
}
public String getAgentRemote() {
return remote;
}
public void setAgentRemote(String remote) {
this.remote = remote;
}
@Override
protected String getThreadName() {
return "Local dbgeng.dll Agent stdout";
}
protected Class<?> getServerClass() {
return DbgEngGadpServer.class;
}
@Override
protected void completeCommandLine(List<String> cmd) {
cmd.add(getServerClass().getCanonicalName());
cmd.addAll(List.of("-H", host));
cmd.addAll(List.of("-p", Integer.toString(port)));
//cmd.addAll(List.of("-t", transport));
if (!remote.equals("none")) {
cmd.addAll(List.of("-r", remote));
}
if (!transport.equals("none")) {
cmd.addAll(List.of("-t", transport));
}
}
}

View file

@ -0,0 +1,236 @@
/* ###
* 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 agent.dbgeng.gadp.impl;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import com.sun.jna.platform.win32.COM.COMException;
import agent.dbgeng.dbgeng.DebugClient;
import agent.dbgeng.dbgeng.DebugClient.DebugStatus;
import agent.dbgeng.dbgeng.DebugControl;
import agent.dbgeng.manager.DbgManager;
import ghidra.util.Msg;
/**
* A single-threaded executor which creates and exclusively accesses the {@code dbgeng.dll} client.
*
* The executor also has a priority mechanism, so that callbacks may register follow-on handlers
* which take precedence over other tasks in the queue (which could trigger additional callbacks).
* This is required since certain operation are not allowed during normal callback processing. For
* example, changing the current process is typically not allowed, but it is necessary to retrieve a
* thread's context.
*/
public abstract class AbstractClientThreadExecutor extends AbstractExecutorService {
private static final int DEFAULT_PRIORITY = 10;
protected DebugClient client;
protected boolean shuttingDown = false;
protected final Queue<Entry> queue = new PriorityQueue<>();
protected Thread thread = new Thread(this::run, "DebugClient");
protected final AtomicBoolean waitRegistered = new AtomicBoolean();
protected abstract void init();
public static class Entry implements Comparable<Entry> {
final int priority;
public final Runnable command;
public Entry(int priority, Runnable command) {
this.priority = priority;
this.command = command;
}
@Override
public int compareTo(Entry that) {
return Integer.compare(this.priority, that.priority);
}
}
/**
* Obtain a reference to the client, only if the calling thread is this executor's thread.
*
* @return the client
*/
public DebugClient getClient() {
if (thread != Thread.currentThread()) {
//TODO: throw new AssertionError("Cannot get client outside owning thread");
}
return client;
}
/**
* Instruct the executor to call {@link DebugClient#dispatchCallbacks()} when it next idles.
*/
public void cancelWait() {
waitRegistered.set(false);
}
/**
* Instruct the executor to call {@link DebugControl#waitForEvent()} when it next idles.
*/
public void registerWait() {
waitRegistered.set(true);
}
private Entry pollQueue() {
synchronized (queue) {
return queue.poll();
}
}
private void run() {
/**
* The general idea is to run indefinitely, taking every precaution to protect this thread's
* life, since only it can access the client. Granted, if it turns out to be too difficult,
* we can always create a new thread and client, using the existing client's reentrant
* methods.
*
* As stated in the MSDN, this thread repeatedly calls {@code DispatchEvents} in order to
* receive callbacks regarding events caused by other clients. If, however, an wait is
* registered, or the current engine state indicates that a wait is proper, the thread calls
* {@code WaitForEvent} instead. The thread is occupied until the wait completes, which is
* fine since the engine is inaccessible (except to certain callbacks) until it completes,
* anyway.
*/
try {
init();
while (!shuttingDown) {
Entry next;
while (null != (next = pollQueue())) {
if (shuttingDown) {
return;
}
try {
//System.out.println("Executing: " + next);
next.command.run();
//System.out.println("Done");
}
catch (Throwable t) {
Msg.error(this, "Task in executor threw: " + t);
}
}
DebugStatus status = client.getControl().getExecutionStatus();
if (status.shouldWait && status != DebugStatus.NO_DEBUGGEE ||
waitRegistered.get()) {
waitRegistered.set(false);
try {
getManager().waitForEventEx();
//client.getControl().waitForEvent();
}
catch (COMException e) {
Msg.error(this, "Error during WaitForEvents: " + e);
}
}
else {
try {
client.dispatchCallbacks(100); // TODO: Better synchronization
}
catch (COMException e) {
Msg.error(this, "Error during DispatchCallbacks: " + e);
}
}
}
}
catch (Throwable t) {
Msg.error(this, "Non-respawnable executor terminated unexpectedly", t);
shuttingDown = true;
}
}
@Override
public void shutdown() {
shuttingDown = true;
}
@Override
public List<Runnable> shutdownNow() {
shuttingDown = true;
client.exitDispatch();
thread.interrupt();
List<Runnable> left = new ArrayList<>(queue.size());
for (Entry ent : queue) {
left.add(ent.command);
}
return left;
}
@Override
public boolean isShutdown() {
return shuttingDown;
}
@Override
public boolean isTerminated() {
return !thread.isAlive();
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
long millis = TimeUnit.MILLISECONDS.convert(timeout, unit);
thread.join(millis);
return !thread.isAlive();
}
@Override
public void execute(Runnable command) {
execute(DEFAULT_PRIORITY, command);
}
/**
* Schedule a task with a given priority.
*
* Smaller priority values indicate earlier execution. The default priority is
* {@link #DEFAULT_PRIORITY}.
*
* @param priority the priority
* @param command the task
*/
public void execute(int priority, Runnable command) {
if (shuttingDown) {
// TODO: Is this the correct exception?
throw new RejectedExecutionException("Executor is shutting down");
}
if (!thread.isAlive()) {
throw new RejectedExecutionException("Executor has terminated");
}
synchronized (queue) {
queue.add(new Entry(priority, command));
// TODO: Putting this in causes sync/output flushing problems
//client.exitDispatch();
}
}
/**
* Schedule a task with the given priority, taking a reference to the client.
*
* This is a convenience which spares a call to {@link #getClient()}. See
* {@link #execute(int, Runnable)} about priority.
*
* @param priority the priority
* @param command the task
*/
public void execute(int priority, Consumer<DebugClient> command) {
execute(priority, () -> command.accept(client));
}
public abstract DbgManager getManager();
public abstract void setManager(DbgManager manager);
}

View file

@ -0,0 +1,63 @@
/* ###
* 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 agent.dbgeng.gadp.impl;
import java.util.function.Supplier;
import agent.dbgeng.dbgeng.DebugClient;
import agent.dbgeng.manager.DbgManager;
/**
* A single-threaded executor which creates and exclusively accesses the {@code dbgeng.dll} client.
*
* The executor also has a priority mechanism, so that callbacks may register follow-on handlers
* which take precedence over other tasks in the queue (which could trigger additional callbacks).
* This is required since certain operation are not allowed during normal callback processing. For
* example, changing the current process is typically not allowed, but it is necessary to retrieve a
* thread's context.
*/
public class DbgEngClientThreadExecutor extends AbstractClientThreadExecutor {
private final Supplier<DebugClient> makeClient;
private DbgManager manager;
/**
* Instantiate a new executor, providing a means of creating the client
*
* @param makeClient the callback to create the client
*/
public DbgEngClientThreadExecutor(Supplier<DebugClient> makeClient) {
this.makeClient = makeClient;
thread.setDaemon(true);
thread.start();
}
@Override
protected void init() {
this.client = makeClient.get();
}
@Override
public DbgManager getManager() {
return manager;
}
@Override
public void setManager(DbgManager manager) {
this.manager = manager;
}
}

View file

@ -0,0 +1,64 @@
/* ###
* 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 agent.dbgeng.gadp.impl;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.concurrent.CompletableFuture;
import agent.dbgeng.gadp.DbgEngGadpServer;
import agent.dbgeng.model.AbstractDbgModel;
import agent.dbgeng.model.impl.DbgModelImpl;
import ghidra.dbg.gadp.server.AbstractGadpServer;
public class DbgEngGadpServerImpl implements DbgEngGadpServer {
public class GadpSide extends AbstractGadpServer {
public GadpSide(AbstractDbgModel model, SocketAddress addr)
throws IOException {
super(model, addr);
}
}
protected final AbstractDbgModel model;
protected final GadpSide server;
public DbgEngGadpServerImpl(SocketAddress addr) throws IOException {
super();
this.model = new DbgModelImpl();
this.server = new GadpSide(model, addr);
}
@Override
public CompletableFuture<Void> startDbgEng(String[] args) {
return model.startDbgEng(args).thenCompose(__ -> server.launchAsyncService());
}
@Override
public SocketAddress getLocalAddress() {
return server.getLocalAddress();
}
@Override
public boolean isRunning() {
return model.isRunning();
}
@Override
public void terminate() throws IOException {
model.terminate();
server.terminate();
}
}

View file

@ -0,0 +1,86 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.function.Function;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.*;
import com.sun.jna.ptr.PointerByReference;
import agent.dbgeng.dbgeng.err.DbgEngRuntimeException;
import ghidra.util.Msg;
public abstract class DbgEngUtil {
public static final ULONG DEBUG_ANY_ID = new ULONG(-1);
private DbgEngUtil() {
}
public static interface InterfaceSupplier {
HRESULT get(REFIID refiid, PointerByReference pClient);
}
@SuppressWarnings("unchecked")
public static <I> I tryPreferredInterfaces(Class<I> cls,
Map<REFIID, ? extends Class<?>> preferred, InterfaceSupplier supplier) {
PointerByReference ppClient = new PointerByReference();
for (Map.Entry<REFIID, ? extends Class<?>> ent : preferred.entrySet()) {
try {
COMUtils.checkRC(supplier.get(ent.getKey(), ppClient));
if (ppClient.getValue() == null) {
continue;
}
Object impl =
ent.getValue().getConstructor(Pointer.class).newInstance(ppClient.getValue());
Method instanceFor = cls.getMethod("instanceFor", ent.getValue());
Object instance = instanceFor.invoke(null, impl);
return (I) instance;
}
catch (COMException e) {
Msg.debug(DbgEngUtil.class, e + " (" + ent.getValue() + ")");
// TODO: Only try next on E_NOINTERFACE?
// Try next
}
catch (Exception e) {
throw new AssertionError("INTERNAL: Unexpected exception", e);
}
}
throw new DbgEngRuntimeException("None of the preferred interfaces are supported");
}
public static <T extends Unknown, U> U lazyWeakCache(Map<Pointer, U> cache, T unk,
Function<T, U> forNew) {
U present = cache.get(unk.getPointer());
if (present != null) {
unk.Release();
return present;
}
U absent = forNew.apply(unk);
cache.put(unk.getPointer(), absent);
return absent;
}
public static void dbgline() {
System.out.println(new Exception().getStackTrace()[1]);
System.out.flush();
}
}

View file

@ -0,0 +1,66 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng;
import agent.dbgeng.dbgeng.DebugRunningProcess;
import agent.dbgeng.dbgeng.DebugServerId;
import agent.dbgeng.impl.dbgeng.client.DebugClientInternal;
import ghidra.comm.util.BitmaskSet;
public class DebugRunningProcessImpl
implements DebugRunningProcess, Comparable<DebugRunningProcessImpl> {
public DebugRunningProcessImpl(DebugClientInternal client, DebugServerId server, int systemId) {
this.client = client;
this.server = server;
this.systemId = systemId;
}
protected final DebugClientInternal client;
protected final DebugServerId server;
protected final int systemId;
@Override
public int getSystemId() {
return systemId;
}
@Override
public Description getFullDescription(Description.ProcessDescriptionFlags... flags) {
return client.getProcessDescription(server, systemId, BitmaskSet.of(flags));
}
@Override
public String getExecutableName(Description.ProcessDescriptionFlags... flags) {
return getFullDescription(flags).getExecutableName();
}
@Override
public String getDescription(Description.ProcessDescriptionFlags... flags) {
return getFullDescription(flags).getDescription();
}
@Override
public int compareTo(DebugRunningProcessImpl that) {
int result;
result = Integer.compare(this.systemId, that.systemId);
if (result != 0) {
return result;
}
return 0;
}
}

View file

@ -0,0 +1,38 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.advanced;
import agent.dbgeng.dbgeng.DbgEng;
import agent.dbgeng.dbgeng.DebugThreadId;
import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable;
import agent.dbgeng.jna.dbgeng.advanced.IDebugAdvanced;
public class DebugAdvancedImpl1 implements DebugAdvancedInternal {
@SuppressWarnings("unused")
private final OpaqueCleanable cleanable;
@SuppressWarnings("unused")
private final IDebugAdvanced jnaAdvanced;
public DebugAdvancedImpl1(IDebugAdvanced jnaAdvanced) {
this.cleanable = DbgEng.releaseWhenPhantom(this, jnaAdvanced);
this.jnaAdvanced = jnaAdvanced;
}
@Override
public DebugThreadBasicInformation getThreadBasicInformation(DebugThreadId tid) {
throw new UnsupportedOperationException("Not supported by this interface");
}
}

View file

@ -0,0 +1,85 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.advanced;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinDef.ULONGLONG;
import agent.dbgeng.dbgeng.DebugThreadId;
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_THREAD_BASIC_INFORMATION;
import agent.dbgeng.jna.dbgeng.advanced.IDebugAdvanced2;
import com.sun.jna.platform.win32.COM.COMUtils;
import ghidra.comm.util.BitmaskSet;
public class DebugAdvancedImpl2 extends DebugAdvancedImpl1 {
private final IDebugAdvanced2 jnaAdvanced;
public DebugAdvancedImpl2(IDebugAdvanced2 jnaAdvanced) {
super(jnaAdvanced);
this.jnaAdvanced = jnaAdvanced;
}
@Override
public DebugThreadBasicInformation getThreadBasicInformation(DebugThreadId tid) {
ULONG ulWhich = new ULONG(WhichSystemObjectInformation.THREAD_BASIC_INFORMATION.ordinal());
ULONGLONG ullUnused = new ULONGLONG(0);
ULONG ulThreadId = new ULONG(tid.id);
DEBUG_THREAD_BASIC_INFORMATION sInfo = new DEBUG_THREAD_BASIC_INFORMATION();
ULONG ulBufferSize = new ULONG(sInfo.size());
COMUtils.checkRC(jnaAdvanced.GetSystemObjectInformation(ulWhich, ullUnused, ulThreadId,
sInfo.getPointer(), ulBufferSize, null));
sInfo.read();
Integer exitStatus = null;
Integer priorityClass = null;
Integer priority = null;
Long createTime = null;
Long exitTime = null;
Long kernelTime = null;
Long userTime = null;
Long startOffset = null;
Long affinity = null;
BitmaskSet<ThreadBasicInformationValidBits> valid =
new BitmaskSet<>(ThreadBasicInformationValidBits.class, sInfo.Valid.intValue());
if (valid.contains(ThreadBasicInformationValidBits.EXIT_STATUS)) {
exitStatus = sInfo.ExitStatus.intValue();
}
if (valid.contains(ThreadBasicInformationValidBits.PRIORITY_CLASS)) {
priorityClass = sInfo.PriorityClass.intValue();
}
if (valid.contains(ThreadBasicInformationValidBits.PRIORITY)) {
priority = sInfo.Priority.intValue();
}
if (valid.contains(ThreadBasicInformationValidBits.TIMES)) {
createTime = sInfo.CreateTime.longValue();
exitTime = sInfo.ExitTime.longValue();
kernelTime = sInfo.KernelTime.longValue();
userTime = sInfo.UserTime.longValue();
}
if (valid.contains(ThreadBasicInformationValidBits.START_OFFSET)) {
startOffset = sInfo.StartOffset.longValue();
}
if (valid.contains(ThreadBasicInformationValidBits.AFFINITY)) {
affinity = sInfo.Affinity.longValue();
}
return new DebugThreadBasicInformation(exitStatus, priorityClass, priority, createTime,
exitTime, kernelTime, userTime, startOffset, affinity);
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.advanced;
import agent.dbgeng.jna.dbgeng.advanced.IDebugAdvanced3;
public class DebugAdvancedImpl3 extends DebugAdvancedImpl2 {
@SuppressWarnings("unused")
private final IDebugAdvanced3 jnaAdvanced;
public DebugAdvancedImpl3(IDebugAdvanced3 jnaAdvanced) {
super(jnaAdvanced);
this.jnaAdvanced = jnaAdvanced;
}
}

View file

@ -0,0 +1,88 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.advanced;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import agent.dbgeng.dbgeng.DebugAdvanced;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier;
import agent.dbgeng.jna.dbgeng.advanced.*;
import ghidra.comm.util.BitmaskUniverse;
import ghidra.util.datastruct.WeakValueHashMap;
public interface DebugAdvancedInternal extends DebugAdvanced {
Map<Pointer, DebugAdvancedInternal> CACHE = new WeakValueHashMap<>();
static DebugAdvancedInternal instanceFor(WrapIDebugAdvanced advanced) {
return DbgEngUtil.lazyWeakCache(CACHE, advanced, DebugAdvancedImpl1::new);
}
static DebugAdvancedInternal instanceFor(WrapIDebugAdvanced2 advanced) {
return DbgEngUtil.lazyWeakCache(CACHE, advanced, DebugAdvancedImpl2::new);
}
static DebugAdvancedInternal instanceFor(WrapIDebugAdvanced3 advanced) {
return DbgEngUtil.lazyWeakCache(CACHE, advanced, DebugAdvancedImpl3::new);
}
ImmutableMap.Builder<REFIID, Class<? extends WrapIDebugAdvanced>> PREFERRED_ADVANCED_IIDS_BUILDER =
ImmutableMap.builder();
Map<REFIID, Class<? extends WrapIDebugAdvanced>> PREFERRED_ADVANCED_IIDS =
PREFERRED_ADVANCED_IIDS_BUILDER //
.put(new REFIID(IDebugAdvanced3.IID_IDEBUG_ADVANCED3), WrapIDebugAdvanced3.class) //
.put(new REFIID(IDebugAdvanced2.IID_IDEBUG_ADVANCED2), WrapIDebugAdvanced2.class) //
.put(new REFIID(IDebugAdvanced.IID_IDEBUG_ADVANCED), WrapIDebugAdvanced.class) //
.build();
static DebugAdvancedInternal tryPreferredInterfaces(InterfaceSupplier supplier) {
return DbgEngUtil.tryPreferredInterfaces(DebugAdvancedInternal.class,
PREFERRED_ADVANCED_IIDS, supplier);
}
public enum WhichSystemObjectInformation {
THREAD_BASIC_INFORMATION, //
THREAD_NAME_WIDE, //
CURRENT_PROCESS_COOKIE, //
;
}
public enum ThreadBasicInformationValidBits implements BitmaskUniverse {
EXIT_STATUS(1 << 0), //
PRIORITY_CLASS(1 << 1), //
PRIORITY(1 << 2), //
TIMES(1 << 3), //
START_OFFSET(1 << 4), //
AFFINITY(1 << 5), //
ALL(0x3f);
;
ThreadBasicInformationValidBits(int mask) {
this.mask = mask;
}
int mask;
@Override
public long getMask() {
return mask;
}
}
}

View file

@ -0,0 +1,164 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.breakpoint;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.COM.COMUtils;
import com.sun.jna.ptr.PointerByReference;
import agent.dbgeng.dbgeng.DbgEng;
import agent.dbgeng.dbgeng.DebugClient;
import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable;
import agent.dbgeng.impl.dbgeng.client.DebugClientInternal;
import agent.dbgeng.impl.dbgeng.control.DebugControlInternal;
import agent.dbgeng.jna.dbgeng.WinNTExtra.Machine;
import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint;
import agent.dbgeng.jna.dbgeng.client.WrapIDebugClient;
import ghidra.comm.util.BitmaskSet;
public class DebugBreakpointImpl1 implements DebugBreakpointInternal {
@SuppressWarnings("unused")
private final OpaqueCleanable cleanable;
private IDebugBreakpoint jnaBreakpoint;
private DebugControlInternal control;
public DebugBreakpointImpl1(IDebugBreakpoint jnaBreakpoint) {
this.cleanable = DbgEng.releaseWhenPhantom(this, jnaBreakpoint);
this.jnaBreakpoint = jnaBreakpoint;
}
@Override
public void setControl(DebugControlInternal control) {
this.control = control;
}
@Override
public void remove() {
control.removeBreakpoint(jnaBreakpoint);
// Prevent accidental access. Will be released during GC. NPE is better than segfault.
jnaBreakpoint = null;
}
@Override
public int getId() {
ULONGByReference pulId = new ULONGByReference();
COMUtils.checkRC(jnaBreakpoint.GetId(pulId));
return pulId.getValue().intValue();
}
@Override
public BreakFullType getType() {
ULONGByReference pulBreakType = new ULONGByReference();
ULONGByReference pulProcType = new ULONGByReference();
COMUtils.checkRC(jnaBreakpoint.GetType(pulBreakType, pulProcType));
BreakType breakType = BreakType.values()[pulBreakType.getValue().intValue()];
Machine procType = Machine.getByNumber(pulProcType.getValue().intValue());
return new BreakFullType(breakType, procType);
}
@Override
public DebugClient getAdder() {
PointerByReference pClient = new PointerByReference();
COMUtils.checkRC(jnaBreakpoint.GetAdder(pClient.getPointer()));
WrapIDebugClient wrap = new WrapIDebugClient(pClient.getValue());
try {
return DebugClientInternal.tryPreferredInterfaces(wrap::QueryInterface);
}
finally {
wrap.Release();
}
}
@Override
public BitmaskSet<BreakFlags> getFlags() {
ULONGByReference pulFlags = new ULONGByReference();
COMUtils.checkRC(jnaBreakpoint.GetFlags(pulFlags));
return new BitmaskSet<>(BreakFlags.class, pulFlags.getValue().longValue());
}
@Override
public void addFlags(BitmaskSet<BreakFlags> flags) {
ULONG ulFlags = new ULONG(flags.getBitmask());
COMUtils.checkRC(jnaBreakpoint.AddFlags(ulFlags));
}
@Override
public void addFlags(BreakFlags... flags) {
addFlags(BitmaskSet.of(flags));
}
@Override
public void removeFlags(BitmaskSet<BreakFlags> flags) {
ULONG ulFlags = new ULONG(flags.getBitmask());
COMUtils.checkRC(jnaBreakpoint.RemoveFlags(ulFlags));
}
@Override
public void removeFlags(BreakFlags... flags) {
removeFlags(BitmaskSet.of(flags));
}
@Override
public void setFlags(BitmaskSet<BreakFlags> flags) {
ULONG ulFlags = new ULONG(flags.getBitmask());
COMUtils.checkRC(jnaBreakpoint.SetFlags(ulFlags));
}
@Override
public void setFlags(BreakFlags... flags) {
setFlags(BitmaskSet.of(flags));
}
@Override
public long getOffset() {
ULONGLONGByReference pullOffset = new ULONGLONGByReference();
COMUtils.checkRC(jnaBreakpoint.GetOffset(pullOffset));
return pullOffset.getValue().longValue();
}
@Override
public void setOffset(long offset) {
ULONGLONG ullOffset = new ULONGLONG(offset);
COMUtils.checkRC(jnaBreakpoint.SetOffset(ullOffset));
}
@Override
public BreakDataParameters getDataParameters() {
ULONGByReference pulSize = new ULONGByReference();
ULONGByReference pulAccessType = new ULONGByReference();
COMUtils.checkRC(jnaBreakpoint.GetDataParameters(pulSize, pulAccessType));
return new BreakDataParameters(pulSize.getValue().intValue(),
new BitmaskSet<>(BreakAccess.class, pulAccessType.getValue().intValue()));
}
@Override
public void setDataParameters(BreakDataParameters params) {
setDataParameters(params.size, params.access);
}
@Override
public void setDataParameters(int size, BitmaskSet<BreakAccess> access) {
ULONG ulSize = new ULONG(size);
ULONG ulAccessType = new ULONG(access.getBitmask());
COMUtils.checkRC(jnaBreakpoint.SetDataParameters(ulSize, ulAccessType));
}
@Override
public void setDataParameters(int size, BreakAccess... access) {
setDataParameters(size, BitmaskSet.of(access));
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.breakpoint;
import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint2;
public class DebugBreakpointImpl2 extends DebugBreakpointImpl1 {
@SuppressWarnings("unused")
private final IDebugBreakpoint2 jnaBreakpoint;
public DebugBreakpointImpl2(IDebugBreakpoint2 jnaBreakpoint) {
super(jnaBreakpoint);
this.jnaBreakpoint = jnaBreakpoint;
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.breakpoint;
import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint3;
public class DebugBreakpointImpl3 extends DebugBreakpointImpl2 {
@SuppressWarnings("unused")
private final IDebugBreakpoint3 jnaBreakpoint;
public DebugBreakpointImpl3(IDebugBreakpoint3 jnaBreakpoint) {
super(jnaBreakpoint);
this.jnaBreakpoint = jnaBreakpoint;
}
}

View file

@ -0,0 +1,66 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.breakpoint;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import agent.dbgeng.dbgeng.DebugBreakpoint;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier;
import agent.dbgeng.impl.dbgeng.control.DebugControlInternal;
import agent.dbgeng.jna.dbgeng.breakpoint.*;
import ghidra.util.datastruct.WeakValueHashMap;
public interface DebugBreakpointInternal extends DebugBreakpoint {
Map<Pointer, DebugBreakpointInternal> CACHE = new WeakValueHashMap<>();
static DebugBreakpointInternal instanceFor(WrapIDebugBreakpoint bp) {
return DbgEngUtil.lazyWeakCache(CACHE, bp, DebugBreakpointImpl1::new);
}
static DebugBreakpointInternal instanceFor(WrapIDebugBreakpoint2 bp) {
return DbgEngUtil.lazyWeakCache(CACHE, bp, DebugBreakpointImpl2::new);
}
static DebugBreakpointInternal instanceFor(WrapIDebugBreakpoint3 bp) {
return DbgEngUtil.lazyWeakCache(CACHE, bp, DebugBreakpointImpl3::new);
}
ImmutableMap.Builder<REFIID, Class<? extends WrapIDebugBreakpoint>> PREFERRED_BREAKPOINT_IIDS_BUILDER =
ImmutableMap.builder();
Map<REFIID, Class<? extends WrapIDebugBreakpoint>> PREFERRED_BREAKPOINT_IIDS =
PREFERRED_BREAKPOINT_IIDS_BUILDER //
.put(new REFIID(IDebugBreakpoint3.IID_IDEBUG_BREAKPOINT3),
WrapIDebugBreakpoint3.class) //
.put(new REFIID(IDebugBreakpoint2.IID_IDEBUG_BREAKPOINT2),
WrapIDebugBreakpoint2.class) //
.put(new REFIID(IDebugBreakpoint.IID_IDEBUG_BREAKPOINT), WrapIDebugBreakpoint.class) //
.build();
static DebugBreakpointInternal tryPreferredInterfaces(DebugControlInternal control,
InterfaceSupplier supplier) {
DebugBreakpointInternal bpt = DbgEngUtil.tryPreferredInterfaces(
DebugBreakpointInternal.class, PREFERRED_BREAKPOINT_IIDS, supplier);
bpt.setControl(control);
return bpt;
}
void setControl(DebugControlInternal control);
}

View file

@ -0,0 +1,323 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.client;
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.COMUtils;
import com.sun.jna.ptr.PointerByReference;
import agent.dbgeng.dbgeng.*;
import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable;
import agent.dbgeng.impl.dbgeng.DebugRunningProcessImpl;
import agent.dbgeng.impl.dbgeng.advanced.DebugAdvancedInternal;
import agent.dbgeng.impl.dbgeng.control.DebugControlInternal;
import agent.dbgeng.impl.dbgeng.dataspaces.DebugDataSpacesInternal;
import agent.dbgeng.impl.dbgeng.event.WrapCallbackIDebugEventCallbacks;
import agent.dbgeng.impl.dbgeng.io.WrapCallbackIDebugInputCallbacks;
import agent.dbgeng.impl.dbgeng.io.WrapCallbackIDebugOutputCallbacks;
import agent.dbgeng.impl.dbgeng.registers.DebugRegistersInternal;
import agent.dbgeng.impl.dbgeng.symbols.DebugSymbolsInternal;
import agent.dbgeng.impl.dbgeng.sysobj.DebugSystemObjectsInternal;
import agent.dbgeng.jna.dbgeng.client.IDebugClient;
import agent.dbgeng.jna.dbgeng.event.ListenerIDebugEventCallbacks;
import agent.dbgeng.jna.dbgeng.event.MarkerEventCallbacks;
import agent.dbgeng.jna.dbgeng.io.*;
import ghidra.comm.util.BitmaskSet;
public class DebugClientImpl1 implements DebugClientInternal {
@SuppressWarnings("unused")
private final OpaqueCleanable cleanable;
private final IDebugClient jnaClient;
private DebugAdvancedInternal advanced;
private DebugControlInternal control;
private DebugDataSpaces data;
private DebugRegisters registers;
private DebugSymbols symbols;
private DebugSystemObjects sysobjs;
// Keep references to callbacks here, since JNA doesn't keep one for things handed to natives.
protected MarkerOutputCallbacks listenerOutput;
protected MarkerInputCallbacks listenerInput;
protected MarkerEventCallbacks listenerEvent;
public DebugClientImpl1(IDebugClient jnaClient) {
// TODO: Debug and verify COM resource management
this.cleanable = DbgEng.releaseWhenPhantom(this, jnaClient);
this.jnaClient = jnaClient;
}
@Override
public IDebugClient getJNAClient() {
return jnaClient;
}
@Override
public DebugAdvanced getAdvanced() {
if (advanced == null) {
advanced = DebugAdvancedInternal.tryPreferredInterfaces(jnaClient::QueryInterface);
}
return advanced;
}
@Override
public DebugControlInternal getControlInternal() {
if (control == null) {
control = DebugControlInternal.tryPreferredInterfaces(jnaClient::QueryInterface);
}
return control;
}
@Override
public DebugControl getControl() {
return getControlInternal();
}
@Override
public DebugDataSpaces getDataSpaces() {
if (data == null) {
data = DebugDataSpacesInternal.tryPreferredInterfaces(jnaClient::QueryInterface);
}
return data;
}
@Override
public DebugRegisters getRegisters() {
if (registers == null) {
registers = DebugRegistersInternal.tryPreferredInterfaces(jnaClient::QueryInterface);
}
return registers;
}
@Override
public DebugSymbols getSymbols() {
if (symbols == null) {
symbols = DebugSymbolsInternal.tryPreferredInterfaces(jnaClient::QueryInterface);
}
return symbols;
}
@Override
public DebugSystemObjects getSystemObjects() {
if (sysobjs == null) {
sysobjs = DebugSystemObjectsInternal.tryPreferredInterfaces(jnaClient::QueryInterface);
}
return sysobjs;
}
@Override
public DebugServerId getLocalServer() {
return new DebugServerId(0);
}
@Override
public void startProcessServer(String options) {
COMUtils.checkRC(jnaClient.StartProcessServer(new ULONG(DebugClass.USER_WINDOWS.ordinal()),
options, null));
}
@Override
public DebugServerId connectProcessServer(String options) {
ULONGLONGByReference pulServer = new ULONGLONGByReference();
COMUtils.checkRC(jnaClient.ConnectProcessServer(options, pulServer));
return new DebugServerId(pulServer.getValue().longValue());
}
@Override
public List<DebugRunningProcess> getRunningProcesses(DebugServerId si) {
ULONGLONG server = new ULONGLONG(si.id);
ULONGByReference actualCount = new ULONGByReference();
COMUtils.checkRC(
jnaClient.GetRunningProcessSystemIds(server, null, new ULONG(0), actualCount));
int[] ids = new int[actualCount.getValue().intValue()];
COMUtils.checkRC(
jnaClient.GetRunningProcessSystemIds(server, ids, actualCount.getValue(), null));
List<DebugRunningProcess> result = new ArrayList<>(ids.length);
for (int id : ids) {
result.add(new DebugRunningProcessImpl(this, si, id));
}
return result;
}
@Override
public DebugRunningProcess.Description getProcessDescription(DebugServerId si, int systemId,
BitmaskSet<DebugRunningProcess.Description.ProcessDescriptionFlags> flags) {
ULONGLONG ullServer = new ULONGLONG(si.id);
ULONG ulId = new ULONG(systemId);
ULONG ulFlags = new ULONG(flags.getBitmask());
ULONGByReference actualExeNameSize = new ULONGByReference();
ULONGByReference actualDescriptionSize = new ULONGByReference();
COMUtils.checkRC(jnaClient.GetRunningProcessDescription(ullServer, ulId, ulFlags, null,
new ULONG(0), actualExeNameSize, null, new ULONG(0), actualDescriptionSize));
byte[] exeName = new byte[actualExeNameSize.getValue().intValue()];
byte[] description = new byte[actualDescriptionSize.getValue().intValue()];
COMUtils.checkRC(jnaClient.GetRunningProcessDescription(ullServer, ulId, ulFlags, exeName,
actualExeNameSize.getValue(), null, description, actualDescriptionSize.getValue(),
null));
return new DebugRunningProcess.Description(systemId, Native.toString(exeName),
Native.toString(description));
}
@Override
public void attachProcess(DebugServerId si, int processId,
BitmaskSet<DebugAttachFlags> attachFlags) {
ULONGLONG ullServer = new ULONGLONG(si.id);
ULONG ulPid = new ULONG(processId);
ULONG ulFlags = new ULONG(attachFlags.getBitmask());
COMUtils.checkRC(jnaClient.AttachProcess(ullServer, ulPid, ulFlags));
}
@Override
public void createProcess(DebugServerId si, String commandLine,
BitmaskSet<DebugCreateFlags> createFlags) {
ULONGLONG ullServer = new ULONGLONG(si.id);
ULONG ulFlags = new ULONG(createFlags.getBitmask());
COMUtils.checkRC(jnaClient.CreateProcess(ullServer, commandLine, ulFlags));
}
@Override
public void createProcessAndAttach(DebugServerId si, String commandLine,
BitmaskSet<DebugCreateFlags> createFlags, int processId,
BitmaskSet<DebugAttachFlags> attachFlags) {
ULONGLONG ullServer = new ULONGLONG(si.id);
ULONG ulFlags1 = new ULONG(createFlags.getBitmask());
ULONG ulPid = new ULONG(processId);
ULONG ulFlags2 = new ULONG(attachFlags.getBitmask());
COMUtils.checkRC(
jnaClient.CreateProcessAndAttach(ullServer, commandLine, ulFlags1, ulPid, ulFlags2));
}
@Override
public void startServer(String options) {
COMUtils.checkRC(jnaClient.StartServer(options));
}
@Override
public boolean dispatchCallbacks(int timeout) {
HRESULT hr = jnaClient.DispatchCallbacks(new ULONG(timeout));
COMUtils.checkRC(hr);
return hr.equals(WinNT.S_OK);
}
@Override
public void flushCallbacks() {
HRESULT hr = jnaClient.FlushCallbacks();
COMUtils.checkRC(hr);
}
@Override
public void exitDispatch(DebugClient client) {
DebugClientInternal ic = (DebugClientInternal) client;
COMUtils.checkRC(jnaClient.ExitDispatch(ic.getJNAClient()));
}
@Override
public DebugClient createClient() {
PointerByReference ppClient = new PointerByReference();
COMUtils.checkRC(jnaClient.CreateClient(ppClient));
return DebugClientInternal.tryPreferredInterfaces(jnaClient::QueryInterface);
}
@Override
public void setInputCallbacks(DebugInputCallbacks cb) {
ListenerIDebugInputCallbacks listener = null;
if (cb != null) {
WrapCallbackIDebugInputCallbacks callback =
new WrapCallbackIDebugInputCallbacks(this, cb);
listener = new ListenerIDebugInputCallbacks(callback);
callback.setListener(listener);
}
COMUtils.checkRC(jnaClient.SetInputCallbacks(listener));
listenerInput = listener;
}
@Override
public void setOutputCallbacks(DebugOutputCallbacks cb) {
ListenerIDebugOutputCallbacks listener = null;
if (cb != null) {
WrapCallbackIDebugOutputCallbacks callback = new WrapCallbackIDebugOutputCallbacks(cb);
listener = new ListenerIDebugOutputCallbacks(callback);
callback.setListener(listener);
}
COMUtils.checkRC(jnaClient.SetOutputCallbacks(listener));
listenerOutput = listener;
}
@Override
public void setEventCallbacks(DebugEventCallbacks cb) {
ListenerIDebugEventCallbacks listener = null;
if (cb != null) {
WrapCallbackIDebugEventCallbacks callback =
new WrapCallbackIDebugEventCallbacks(this, cb);
listener = new ListenerIDebugEventCallbacks(callback);
callback.setListener(listener);
}
COMUtils.checkRC(jnaClient.SetEventCallbacks(listener));
listenerEvent = listener;
}
@Override
public void terminateCurrentProcess() {
throw new UnsupportedOperationException("Not implemented by this interface");
}
@Override
public void detachCurrentProcess() {
throw new UnsupportedOperationException("Not implemented by this interface");
}
@Override
public void abandonCurrentProcess() {
throw new UnsupportedOperationException("Not implemented by this interface");
}
@Override
public void waitForProcessServerEnd(int timeout) {
throw new UnsupportedOperationException("Not implemented by this interface");
}
@Override
public void endSession(DebugEndSessionFlags flags) {
COMUtils.checkRC(jnaClient.EndSession(new ULONG(flags.getValue())));
}
@Override
public void connectSession(int flags) {
COMUtils.checkRC(jnaClient.ConnectSession(new ULONG(flags), new ULONG(10000)));
}
@Override
public void openDumpFileWide(String fileName) {
throw new UnsupportedOperationException("Not implemented by this interface");
}
@Override
public void attachKernel(long flags, String options) {
throw new UnsupportedOperationException("Not implemented by this interface");
}
}

View file

@ -0,0 +1,50 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.client;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.COM.COMUtils;
import agent.dbgeng.jna.dbgeng.client.IDebugClient2;
public class DebugClientImpl2 extends DebugClientImpl1 {
private final IDebugClient2 jnaClient;
public DebugClientImpl2(IDebugClient2 jnaClient) {
super(jnaClient);
this.jnaClient = jnaClient;
}
@Override
public void terminateCurrentProcess() {
COMUtils.checkRC(jnaClient.TerminateCurrentProcess());
}
@Override
public void detachCurrentProcess() {
COMUtils.checkRC(jnaClient.DetachCurrentProcess());
}
@Override
public void abandonCurrentProcess() {
COMUtils.checkRC(jnaClient.AbandonCurrentProcess());
}
@Override
public void waitForProcessServerEnd(int timeout) {
COMUtils.checkRC(jnaClient.WaitForProcessServerEnd(new ULONG(timeout)));
}
}

View file

@ -0,0 +1,68 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.client;
import com.sun.jna.Native;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinDef.*;
import agent.dbgeng.dbgeng.DebugRunningProcess;
import agent.dbgeng.dbgeng.DebugServerId;
import agent.dbgeng.dbgeng.DebugRunningProcess.Description.ProcessDescriptionFlags;
import agent.dbgeng.jna.dbgeng.client.IDebugClient3;
import com.sun.jna.platform.win32.COM.COMUtils;
import ghidra.comm.util.BitmaskSet;
public class DebugClientImpl3 extends DebugClientImpl2 {
private final IDebugClient3 jnaClient;
public DebugClientImpl3(IDebugClient3 jnaClient) {
super(jnaClient);
this.jnaClient = jnaClient;
}
@Override
public void createProcess(DebugServerId si, String commandLine,
BitmaskSet<DebugCreateFlags> createFlags) {
ULONGLONG ullServer = new ULONGLONG(si.id);
ULONG ulFlags = new ULONG(createFlags.getBitmask());
COMUtils.checkRC(jnaClient.CreateProcessWide(ullServer, new WString(commandLine), ulFlags));
}
@Override
public DebugRunningProcess.Description getProcessDescription(DebugServerId si, int systemId,
BitmaskSet<ProcessDescriptionFlags> flags) {
ULONGLONG server = new ULONGLONG(si.id);
ULONG id = new ULONG(systemId);
ULONG f = new ULONG(flags.getBitmask());
ULONGByReference actualExeNameSize = new ULONGByReference();
ULONGByReference actualDescriptionSize = new ULONGByReference();
COMUtils.checkRC(jnaClient.GetRunningProcessDescriptionWide(server, id, f, null,
new ULONG(0), actualExeNameSize, null, new ULONG(0), actualDescriptionSize));
char[] exeName = new char[actualExeNameSize.getValue().intValue()];
char[] description = new char[actualDescriptionSize.getValue().intValue()];
COMUtils.checkRC(jnaClient.GetRunningProcessDescriptionWide(server, id, f, exeName,
actualExeNameSize.getValue(), null, description, actualDescriptionSize.getValue(),
null));
return new DebugRunningProcess.Description(systemId, Native.toString(exeName),
Native.toString(description));
}
}

View file

@ -0,0 +1,39 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.client;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinDef.ULONGLONG;
import agent.dbgeng.jna.dbgeng.client.IDebugClient4;
import com.sun.jna.platform.win32.COM.COMUtils;
public class DebugClientImpl4 extends DebugClientImpl3 {
@SuppressWarnings("unused")
private final IDebugClient4 jnaClient;
public DebugClientImpl4(IDebugClient4 jnaClient) {
super(jnaClient);
this.jnaClient = jnaClient;
}
@Override
public void openDumpFileWide(String fileName) {
ULONGLONG ullFileHandle = new ULONGLONG(0);
COMUtils.checkRC(jnaClient.OpenDumpFileWide(new WString(fileName), ullFileHandle));
}
}

View file

@ -0,0 +1,82 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.client;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference;
import com.sun.jna.platform.win32.COM.COMUtils;
import agent.dbgeng.dbgeng.*;
import agent.dbgeng.impl.dbgeng.event.WrapCallbackIDebugEventCallbacksWide;
import agent.dbgeng.impl.dbgeng.io.WrapCallbackIDebugOutputCallbacksWide;
import agent.dbgeng.jna.dbgeng.client.IDebugClient5;
import agent.dbgeng.jna.dbgeng.event.ListenerIDebugEventCallbacksWide;
import agent.dbgeng.jna.dbgeng.io.ListenerIDebugOutputCallbacksWide;
public class DebugClientImpl5 extends DebugClientImpl4 {
private final IDebugClient5 jnaClient;
public DebugClientImpl5(IDebugClient5 jnaClient) {
super(jnaClient);
this.jnaClient = jnaClient;
}
@Override
public void attachKernel(long flags, String options) {
ULONG connectFlags = new ULONG(flags);
COMUtils.checkRC(jnaClient.AttachKernelWide(connectFlags, new WString(options)));
}
@Override
public void startProcessServer(String options) {
COMUtils.checkRC(jnaClient.StartProcessServerWide(
new ULONG(DebugClass.USER_WINDOWS.ordinal()), new WString(options), null));
}
@Override
public DebugServerId connectProcessServer(String options) {
ULONGLONGByReference pulServer = new ULONGLONGByReference();
COMUtils.checkRC(jnaClient.ConnectProcessServerWide(new WString(options), pulServer));
return new DebugServerId(pulServer.getValue().longValue());
}
@Override
public void setOutputCallbacks(DebugOutputCallbacks cb) {
ListenerIDebugOutputCallbacksWide listener = null;
if (cb != null) {
WrapCallbackIDebugOutputCallbacksWide callback =
new WrapCallbackIDebugOutputCallbacksWide(cb);
listener = new ListenerIDebugOutputCallbacksWide(callback);
callback.setListener(listener);
}
COMUtils.checkRC(jnaClient.SetOutputCallbacksWide(listener));
listenerOutput = listener;
}
@Override
public void setEventCallbacks(DebugEventCallbacks cb) {
ListenerIDebugEventCallbacksWide listener = null;
if (cb != null) {
WrapCallbackIDebugEventCallbacksWide callback =
new WrapCallbackIDebugEventCallbacksWide(this, cb);
listener = new ListenerIDebugEventCallbacksWide(callback);
callback.setListener(listener);
}
COMUtils.checkRC(jnaClient.SetEventCallbacksWide(listener));
listenerEvent = listener;
}
}

View file

@ -0,0 +1,35 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.client;
import agent.dbgeng.dbgeng.DebugEventCallbacks;
import agent.dbgeng.jna.dbgeng.client.IDebugClient6;
public class DebugClientImpl6 extends DebugClientImpl5 {
@SuppressWarnings("unused")
private final IDebugClient6 jnaClient;
public DebugClientImpl6(IDebugClient6 jnaClient) {
super(jnaClient);
this.jnaClient = jnaClient;
}
@Override
public void setEventCallbacks(DebugEventCallbacks cb) {
// TODO: Use Context variant. Will require expanding the generic interface.
super.setEventCallbacks(cb);
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.client;
import agent.dbgeng.jna.dbgeng.client.IDebugClient7;
public class DebugClientImpl7 extends DebugClientImpl6 {
@SuppressWarnings("unused")
private final IDebugClient7 jnaClient;
public DebugClientImpl7(IDebugClient7 jnaClient) {
super(jnaClient);
this.jnaClient = jnaClient;
}
}

View file

@ -0,0 +1,96 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.client;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import agent.dbgeng.dbgeng.DebugClient;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier;
import agent.dbgeng.impl.dbgeng.control.DebugControlInternal;
import agent.dbgeng.jna.dbgeng.client.*;
import ghidra.util.datastruct.WeakValueHashMap;
public interface DebugClientInternal extends DebugClient {
Map<Pointer, DebugClientInternal> CACHE = new WeakValueHashMap<>();
enum DebugClass {
UNINITIALIZED, //
KERNEL, //
USER_WINDOWS, //
IMAGE_FILE, //
;
}
static DebugClientInternal instanceFor(WrapIDebugClient client) {
return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl1::new);
}
static DebugClientInternal instanceFor(WrapIDebugClient2 client) {
return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl2::new);
}
static DebugClientInternal instanceFor(WrapIDebugClient3 client) {
return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl3::new);
}
static DebugClientInternal instanceFor(WrapIDebugClient4 client) {
return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl4::new);
}
static DebugClientInternal instanceFor(WrapIDebugClient5 client) {
return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl5::new);
}
static DebugClientInternal instanceFor(WrapIDebugClient6 client) {
return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl6::new);
}
static DebugClientInternal instanceFor(WrapIDebugClient7 client) {
return DbgEngUtil.lazyWeakCache(CACHE, client, DebugClientImpl7::new);
}
ImmutableMap.Builder<REFIID, Class<? extends WrapIDebugClient>> PREFERRED_CLIENT_IIDS_BUILDER =
ImmutableMap.builder();
Map<REFIID, Class<? extends WrapIDebugClient>> PREFERRED_CLIENT_IIDS =
PREFERRED_CLIENT_IIDS_BUILDER //
.put(new REFIID(IDebugClient7.IID_IDEBUG_CLIENT7), WrapIDebugClient7.class) //
.put(new REFIID(IDebugClient6.IID_IDEBUG_CLIENT6), WrapIDebugClient6.class) //
.put(new REFIID(IDebugClient5.IID_IDEBUG_CLIENT5), WrapIDebugClient5.class) //
.put(new REFIID(IDebugClient4.IID_IDEBUG_CLIENT4), WrapIDebugClient4.class) //
.put(new REFIID(IDebugClient3.IID_IDEBUG_CLIENT3), WrapIDebugClient3.class) //
.put(new REFIID(IDebugClient2.IID_IDEBUG_CLIENT2), WrapIDebugClient2.class) //
.put(new REFIID(IDebugClient.IID_IDEBUG_CLIENT), WrapIDebugClient.class) //
.build();
static DebugClientInternal tryPreferredInterfaces(InterfaceSupplier supplier) {
return DbgEngUtil.tryPreferredInterfaces(DebugClientInternal.class, PREFERRED_CLIENT_IIDS,
supplier);
}
IDebugClient getJNAClient();
DebugControlInternal getControlInternal();
@Override
default void endSessionReentrant() {
endSession(DebugEndSessionFlags.DEBUG_END_REENTRANT);
}
}

View file

@ -0,0 +1,288 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.control;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.COMUtils;
import com.sun.jna.ptr.PointerByReference;
import agent.dbgeng.dbgeng.*;
import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable;
import agent.dbgeng.dbgeng.DebugBreakpoint.BreakType;
import agent.dbgeng.dbgeng.DebugClient.DebugStatus;
import agent.dbgeng.dbgeng.DebugValue.DebugValueType;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.impl.dbgeng.breakpoint.DebugBreakpointInternal;
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_STACK_FRAME;
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE;
import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint;
import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint;
import agent.dbgeng.jna.dbgeng.control.IDebugControl;
import ghidra.comm.util.BitmaskSet;
public class DebugControlImpl1 implements DebugControlInternal {
@SuppressWarnings("unused")
private final OpaqueCleanable cleanable;
private final IDebugControl jnaControl;
public DebugControlImpl1(IDebugControl jnaControl) {
this.cleanable = DbgEng.releaseWhenPhantom(this, jnaControl);
this.jnaControl = jnaControl;
}
@Override
public boolean getInterrupt() {
HRESULT interrupt = jnaControl.GetInterrupt();
if (interrupt.equals(WinError.S_OK)) {
return true;
}
if (interrupt.equals(WinError.S_FALSE)) {
return false;
}
COMUtils.checkRC(interrupt);
throw new AssertionError("Shouldn't get here");
}
@Override
public void setInterrupt(DebugInterrupt interrupt) {
ULONG flags = new ULONG(interrupt.ordinal());
COMUtils.checkRC(jnaControl.SetInterrupt(flags));
}
@Override
public int getInterruptTimeout() {
ULONGByReference pulSeconds = new ULONGByReference();
COMUtils.checkRC(jnaControl.GetInterruptTimeout(pulSeconds));
return pulSeconds.getValue().intValue();
}
@Override
public void setInterruptTimeout(int seconds) {
ULONG ulSeconds = new ULONG(seconds);
COMUtils.checkRC(jnaControl.SetInterruptTimeout(ulSeconds));
}
@Override
public void print(BitmaskSet<DebugOutputLevel> levels, String message) {
ULONG mask = new ULONG(levels.getBitmask());
COMUtils.checkRC(jnaControl.Output(mask, "%s", message));
}
@Override
public void println(BitmaskSet<DebugOutputLevel> levels, String message) {
ULONG mask = new ULONG(levels.getBitmask());
COMUtils.checkRC(jnaControl.Output(mask, "%s", message + "\r\n"));
}
@Override
public void prompt(BitmaskSet<DebugOutputControl> ctl, String message) {
ULONG ctlMask = new ULONG(ctl.getBitmask());
COMUtils.checkRC(jnaControl.OutputPrompt(ctlMask, "%s", message));
}
@Override
public String getPromptText() {
ULONGByReference pulTextSize = new ULONGByReference();
COMUtils.checkRC(jnaControl.GetPromptText(null, new ULONG(0), pulTextSize));
byte[] buffer = new byte[pulTextSize.getValue().intValue()];
COMUtils.checkRC(jnaControl.GetPromptText(buffer, pulTextSize.getValue(), null));
return Native.toString(buffer);
}
protected DEBUG_VALUE doEval(DebugValueType type, String expression) {
DEBUG_VALUE.ByReference value = new DEBUG_VALUE.ByReference();
ULONGByReference pulRemainder = new ULONGByReference();
COMUtils.checkRC(
jnaControl.Evaluate(expression, new ULONG(type.ordinal()), value, pulRemainder));
int remainder = pulRemainder.getValue().intValue();
if (remainder != expression.length()) {
throw new RuntimeException("Failed to parse: " + expression.substring(remainder));
}
return value;
}
@Override
public <T extends DebugValue> T evaluate(Class<T> desiredType, String expression) {
DebugValueType type = DebugValueType.getDebugValueTypeForClass(desiredType);
return doEval(type, expression).convertTo(desiredType);
}
@Override
public void execute(BitmaskSet<DebugOutputControl> ctl, String cmd,
BitmaskSet<DebugExecute> flags) {
ULONG ctlMask = new ULONG(ctl.getBitmask());
ULONG flagMask = new ULONG(flags.getBitmask());
COMUtils.checkRC(jnaControl.Execute(ctlMask, cmd, flagMask));
}
@Override
public void returnInput(String input) {
COMUtils.checkRC(jnaControl.ReturnInput(input));
}
@Override
public DebugStatus getExecutionStatus() {
ULONGByReference pulStatus = new ULONGByReference();
COMUtils.checkRC(jnaControl.GetExecutionStatus(pulStatus));
return DebugStatus.values()[pulStatus.getValue().intValue()];
}
@Override
public void setExecutionStatus(DebugStatus status) {
ULONG ulStatus = new ULONG(status.ordinal());
HRESULT hr = jnaControl.SetExecutionStatus(ulStatus);
if (!hr.equals(COMUtilsExtra.E_ACCESS_DENIED)) {
COMUtils.checkRC(hr);
}
}
public DebugBreakpoint doAddBreakpoint(BreakType type, ULONG ulDesiredId) {
ULONG ulType = new ULONG(type.ordinal());
PointerByReference ppBp = new PointerByReference();
COMUtils.checkRC(jnaControl.AddBreakpoint(ulType, ulDesiredId, ppBp));
IDebugBreakpoint Bp = new WrapIDebugBreakpoint(ppBp.getValue());
DebugBreakpoint bpt =
DebugBreakpointInternal.tryPreferredInterfaces(this, Bp::QueryInterface);
// AddRef or no? Probably not.
return bpt;
}
@Override
public int getNumberBreakpoints() {
ULONGByReference ulNumber = new ULONGByReference();
COMUtils.checkRC(jnaControl.GetNumberBreakpoints(ulNumber));
return ulNumber.getValue().intValue();
}
@Override
public DebugBreakpoint getBreakpointByIndex(int index) {
ULONG ulIndex = new ULONG(index);
PointerByReference ppBp = new PointerByReference();
COMUtils.checkRC(jnaControl.GetBreakpointByIndex(ulIndex, ppBp));
IDebugBreakpoint Bp = new WrapIDebugBreakpoint(ppBp.getValue());
DebugBreakpoint bpt =
DebugBreakpointInternal.tryPreferredInterfaces(this, Bp::QueryInterface);
// NOTE: Do not AddRef. dbgeng manages lifecycle
return bpt;
}
@Override
public DebugBreakpoint getBreakpointById(int id) {
ULONG ulId = new ULONG(id);
PointerByReference ppBp = new PointerByReference();
HRESULT hr = jnaControl.GetBreakpointById(ulId, ppBp);
if (hr.equals(COMUtilsExtra.E_NOINTERFACE)) {
return null;
}
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
return null;
}
COMUtils.checkRC(hr);
IDebugBreakpoint Bp = new WrapIDebugBreakpoint(ppBp.getValue());
DebugBreakpoint bpt =
DebugBreakpointInternal.tryPreferredInterfaces(this, Bp::QueryInterface);
// NOTE: Do not AddRef. dbgeng manages lifecycle
return bpt;
}
@Override
public DebugBreakpoint addBreakpoint(BreakType type, int desiredId) {
return doAddBreakpoint(type, new ULONG(desiredId));
}
@Override
public DebugBreakpoint addBreakpoint(BreakType type) {
return doAddBreakpoint(type, DbgEngUtil.DEBUG_ANY_ID);
}
@Override
public void removeBreakpoint(IDebugBreakpoint comBpt) {
COMUtils.checkRC(jnaControl.RemoveBreakpoint(comBpt));
}
@Override
public void waitForEvent(int timeout) {
COMUtils.checkRC(jnaControl.WaitForEvent(new ULONG(0), new ULONG(timeout)));
}
@Override
public DebugEventInformation getLastEventInformation() {
ULONGByReference pulType = new ULONGByReference();
ULONGByReference pulProcessId = new ULONGByReference();
ULONGByReference pulThreadId = new ULONGByReference();
//PointerByReference pExtraInformation = new PointerByReference();
ULONG ulExtraInformationSize = new ULONG(0);
ULONGByReference pulExtraInformationUsed = new ULONGByReference();
//byte[] pstrDescription = new byte[0];
ULONG ulDescriptionSize = new ULONG(0);
ULONGByReference pulDescriptionUsed = new ULONGByReference();
COMUtils.checkRC(jnaControl.GetLastEventInformation(pulType, pulProcessId, pulThreadId,
null, ulExtraInformationSize, pulExtraInformationUsed, null, ulDescriptionSize,
pulDescriptionUsed));
return new DebugEventInformation(pulType.getValue().intValue(),
pulProcessId.getValue().intValue(), pulThreadId.getValue().intValue());
}
@Override
public DebugStackInformation getStackTrace(long frameOffset, long stackOffset,
long instructionOffset) {
ULONGLONG ullFrameOffset = new ULONGLONG(frameOffset);
ULONGLONG ullStackOffset = new ULONGLONG(stackOffset);
ULONGLONG ullInstructionOffset = new ULONGLONG(instructionOffset);
ULONG ulFrameSize = new ULONG(100);
DEBUG_STACK_FRAME[] pParams = new DEBUG_STACK_FRAME[ulFrameSize.intValue()];
ULONGByReference pulFramesFilled = new ULONGByReference();
COMUtils.checkRC(jnaControl.GetStackTrace(ullFrameOffset, ullStackOffset,
ullInstructionOffset, pParams, ulFrameSize, pulFramesFilled));
return new DebugStackInformation(pulFramesFilled.getValue().intValue(), pParams);
}
@Override
public int getActualProcessorType() {
ULONGByReference ulType = new ULONGByReference();
HRESULT hr = jnaControl.GetActualProcessorType(ulType);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
return -1;
}
COMUtils.checkRC(hr);
return ulType.getValue().intValue();
}
@Override
public int getEffectiveProcessorType() {
ULONGByReference ulType = new ULONGByReference();
COMUtils.checkRC(jnaControl.GetEffectiveProcessorType(ulType));
return ulType.getValue().intValue();
}
@Override
public int getExecutingProcessorType() {
ULONGByReference ulType = new ULONGByReference();
COMUtils.checkRC(jnaControl.GetExecutingProcessorType(ulType));
return ulType.getValue().intValue();
}
@Override
public int getDebuggeeType() {
ULONGByReference ulClass = new ULONGByReference();
ULONGByReference ulQualifier = new ULONGByReference();
COMUtils.checkRC(jnaControl.GetDebuggeeType(ulClass, ulQualifier));
return ulClass.getValue().intValue();
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.control;
import agent.dbgeng.jna.dbgeng.control.IDebugControl2;
public class DebugControlImpl2 extends DebugControlImpl1 {
@SuppressWarnings("unused")
private final IDebugControl2 jnaControl;
public DebugControlImpl2(IDebugControl2 jnaControl) {
super(jnaControl);
this.jnaControl = jnaControl;
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.control;
import agent.dbgeng.jna.dbgeng.control.IDebugControl3;
public class DebugControlImpl3 extends DebugControlImpl2 {
@SuppressWarnings("unused")
private final IDebugControl3 jnaControl;
public DebugControlImpl3(IDebugControl3 jnaControl) {
super(jnaControl);
this.jnaControl = jnaControl;
}
}

View file

@ -0,0 +1,95 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.control;
import com.sun.jna.Native;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinDef.ULONGByReference;
import agent.dbgeng.dbgeng.DebugValue.DebugValueType;
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE;
import agent.dbgeng.jna.dbgeng.control.IDebugControl4;
import com.sun.jna.platform.win32.COM.COMUtils;
import ghidra.comm.util.BitmaskSet;
public class DebugControlImpl4 extends DebugControlImpl3 {
private final IDebugControl4 jnaControl;
public DebugControlImpl4(IDebugControl4 jnaControl) {
super(jnaControl);
this.jnaControl = jnaControl;
}
@Override
public void print(BitmaskSet<DebugOutputLevel> levels, String message) {
ULONG mask = new ULONG(levels.getBitmask());
COMUtils.checkRC(jnaControl.OutputWide(mask, new WString("%s"), new WString(message)));
}
@Override
public void println(BitmaskSet<DebugOutputLevel> levels, String message) {
ULONG mask = new ULONG(levels.getBitmask());
COMUtils
.checkRC(
jnaControl.OutputWide(mask, new WString("%s"), new WString(message + "\r\n")));
}
@Override
public void prompt(BitmaskSet<DebugOutputControl> ctl, String message) {
ULONG ctlMask = new ULONG(ctl.getBitmask());
COMUtils
.checkRC(
jnaControl.OutputPromptWide(ctlMask, new WString("%s"), new WString(message)));
}
@Override
public String getPromptText() {
ULONGByReference pulTextSize = new ULONGByReference();
COMUtils.checkRC(jnaControl.GetPromptTextWide(null, new ULONG(0), pulTextSize));
char[] buffer = new char[pulTextSize.getValue().intValue()];
COMUtils.checkRC(jnaControl.GetPromptTextWide(buffer, pulTextSize.getValue(), null));
return Native.toString(buffer);
}
@Override
protected DEBUG_VALUE doEval(DebugValueType type, String expression) {
DEBUG_VALUE.ByReference value = new DEBUG_VALUE.ByReference();
ULONGByReference pulRemainder = new ULONGByReference();
COMUtils.checkRC(jnaControl.EvaluateWide(new WString(expression), new ULONG(type.ordinal()),
value, pulRemainder));
int remainder = pulRemainder.getValue().intValue();
if (remainder != expression.length()) {
throw new RuntimeException("Failed to parse: " + expression.substring(remainder));
}
return value;
}
@Override
public void execute(BitmaskSet<DebugOutputControl> ctl, String cmd,
BitmaskSet<DebugExecute> flags) {
ULONG ctlMask = new ULONG(ctl.getBitmask());
ULONG flagMask = new ULONG(flags.getBitmask());
COMUtils.checkRC(jnaControl.ExecuteWide(ctlMask, new WString(cmd), flagMask));
}
@Override
public void returnInput(String input) {
COMUtils.checkRC(jnaControl.ReturnInputWide(new WString(input)));
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.control;
import agent.dbgeng.jna.dbgeng.control.IDebugControl5;
public class DebugControlImpl5 extends DebugControlImpl4 {
@SuppressWarnings("unused")
private final IDebugControl5 jnaControl;
public DebugControlImpl5(IDebugControl5 jnaControl) {
super(jnaControl);
this.jnaControl = jnaControl;
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.control;
import agent.dbgeng.jna.dbgeng.control.IDebugControl6;
public class DebugControlImpl6 extends DebugControlImpl5 {
@SuppressWarnings("unused")
private final IDebugControl6 jnaControl;
public DebugControlImpl6(IDebugControl6 jnaControl) {
super(jnaControl);
this.jnaControl = jnaControl;
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.control;
import agent.dbgeng.jna.dbgeng.control.IDebugControl7;
public class DebugControlImpl7 extends DebugControlImpl6 {
@SuppressWarnings("unused")
private final IDebugControl7 jnaControl;
public DebugControlImpl7(IDebugControl7 jnaControl) {
super(jnaControl);
this.jnaControl = jnaControl;
}
}

View file

@ -0,0 +1,81 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.control;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import agent.dbgeng.dbgeng.DebugControl;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier;
import agent.dbgeng.jna.dbgeng.breakpoint.IDebugBreakpoint;
import agent.dbgeng.jna.dbgeng.control.*;
import ghidra.util.datastruct.WeakValueHashMap;
public interface DebugControlInternal extends DebugControl {
Map<Pointer, DebugControlInternal> CACHE = new WeakValueHashMap<>();
static DebugControlInternal instanceFor(WrapIDebugControl control) {
return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl1::new);
}
static DebugControlInternal instanceFor(WrapIDebugControl2 control) {
return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl2::new);
}
static DebugControlInternal instanceFor(WrapIDebugControl3 control) {
return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl3::new);
}
static DebugControlInternal instanceFor(WrapIDebugControl4 control) {
return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl4::new);
}
static DebugControlInternal instanceFor(WrapIDebugControl5 control) {
return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl5::new);
}
static DebugControlInternal instanceFor(WrapIDebugControl6 control) {
return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl6::new);
}
static DebugControlInternal instanceFor(WrapIDebugControl7 control) {
return DbgEngUtil.lazyWeakCache(CACHE, control, DebugControlImpl7::new);
}
ImmutableMap.Builder<REFIID, Class<? extends WrapIDebugControl>> PREFERRED_CONTROL_IIDS_BUILDER =
ImmutableMap.builder();
Map<REFIID, Class<? extends WrapIDebugControl>> PREFERRED_CONTROL_IIDS =
PREFERRED_CONTROL_IIDS_BUILDER //
.put(new REFIID(IDebugControl7.IID_IDEBUG_CONTROL7), WrapIDebugControl7.class) //
.put(new REFIID(IDebugControl6.IID_IDEBUG_CONTROL6), WrapIDebugControl6.class) //
.put(new REFIID(IDebugControl5.IID_IDEBUG_CONTROL5), WrapIDebugControl5.class) //
.put(new REFIID(IDebugControl4.IID_IDEBUG_CONTROL4), WrapIDebugControl4.class) //
.put(new REFIID(IDebugControl3.IID_IDEBUG_CONTROL3), WrapIDebugControl3.class) //
.put(new REFIID(IDebugControl2.IID_IDEBUG_CONTROL2), WrapIDebugControl2.class) //
.put(new REFIID(IDebugControl.IID_IDEBUG_CONTROL), WrapIDebugControl.class) //
.build();
static DebugControlInternal tryPreferredInterfaces(InterfaceSupplier supplier) {
return DbgEngUtil.tryPreferredInterfaces(DebugControlInternal.class, PREFERRED_CONTROL_IIDS,
supplier);
}
void removeBreakpoint(IDebugBreakpoint comBpt);
}

View file

@ -0,0 +1,271 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.dataspaces;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import javax.help.UnsupportedOperationException;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.COMUtils;
import agent.dbgeng.dbgeng.COMUtilsExtra;
import agent.dbgeng.dbgeng.DbgEng;
import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable;
import agent.dbgeng.jna.dbgeng.dataspaces.IDebugDataSpaces;
public class DebugDataSpacesImpl1 implements DebugDataSpacesInternal {
@SuppressWarnings("unused")
private final OpaqueCleanable cleanble;
private final IDebugDataSpaces jnaData;
public DebugDataSpacesImpl1(IDebugDataSpaces jnaData) {
this.cleanble = DbgEng.releaseWhenPhantom(this, jnaData);
this.jnaData = jnaData;
}
@Override
public DebugMemoryBasicInformation queryVirtual(long offset) {
throw new UnsupportedOperationException("Not implemented in this interface");
}
@Override
public int readVirtual(long offset, ByteBuffer into, int len) {
if (len > into.remaining()) {
throw new BufferOverflowException();
}
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesRead = new ULONGByReference();
HRESULT hr = jnaData.ReadVirtual(ullOffset, into, ulLen, pulBytesRead);
if (hr.equals(COMUtilsExtra.E_CANNOT_READ)) {
return 0;
}
COMUtils.checkRC(hr);
int read = pulBytesRead.getValue().intValue();
into.position(read + into.position());
return read;
}
@Override
public int writeVirtual(long offset, ByteBuffer from, int len) {
if (len > from.remaining()) {
throw new BufferOverflowException();
}
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesWritten = new ULONGByReference();
COMUtils.checkRC(jnaData.WriteVirtual(ullOffset, from, ulLen, pulBytesWritten));
int written = pulBytesWritten.getValue().intValue();
from.position(written + from.position());
return written;
}
@Override
public int readVirtualUncached(long offset, ByteBuffer into, int len) {
if (len > into.remaining()) {
throw new BufferOverflowException();
}
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesRead = new ULONGByReference();
COMUtils.checkRC(jnaData.ReadVirtualUncached(ullOffset, into, ulLen, pulBytesRead));
int read = pulBytesRead.getValue().intValue();
into.position(read + into.position());
return read;
}
@Override
public int writeVirtualUncached(long offset, ByteBuffer from, int len) {
if (len > from.remaining()) {
throw new BufferOverflowException();
}
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesWritten = new ULONGByReference();
COMUtils.checkRC(jnaData.WriteVirtualUncached(ullOffset, from, ulLen, pulBytesWritten));
int written = pulBytesWritten.getValue().intValue();
from.position(written + from.position());
return written;
}
@Override
public int readPhysical(long offset, ByteBuffer into, int len) {
if (len > into.remaining()) {
throw new BufferOverflowException();
}
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesRead = new ULONGByReference();
COMUtils.checkRC(jnaData.ReadPhysical(ullOffset, into, ulLen, pulBytesRead));
int read = pulBytesRead.getValue().intValue();
into.position(read + into.position());
return read;
}
@Override
public int writePhysical(long offset, ByteBuffer from, int len) {
if (len > from.remaining()) {
throw new BufferOverflowException();
}
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesWritten = new ULONGByReference();
COMUtils.checkRC(jnaData.WritePhysical(ullOffset, from, ulLen, pulBytesWritten));
int written = pulBytesWritten.getValue().intValue();
from.position(written + from.position());
return written;
}
@Override
public int readControl(int processor, long offset, ByteBuffer into, int len) {
if (len > into.remaining()) {
throw new BufferOverflowException();
}
ULONG ulProcessor = new ULONG(processor);
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesRead = new ULONGByReference();
COMUtils.checkRC(jnaData.ReadControl(ulProcessor, ullOffset, into, ulLen, pulBytesRead));
int read = pulBytesRead.getValue().intValue();
into.position(read + into.position());
return read;
}
@Override
public int writeControl(int processor, long offset, ByteBuffer from, int len) {
if (len > from.remaining()) {
throw new BufferOverflowException();
}
ULONG ulProcessor = new ULONG(processor);
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesWritten = new ULONGByReference();
COMUtils.checkRC(
jnaData.WriteControl(ulProcessor, ullOffset, from, ulLen, pulBytesWritten));
int written = pulBytesWritten.getValue().intValue();
from.position(written + from.position());
return written;
}
@Override
public int readBusData(int busDataType, int busNumber, int slotNumber, long offset,
ByteBuffer into, int len) {
if (len > into.remaining()) {
throw new BufferOverflowException();
}
ULONG ulBusDataType = new ULONG(busDataType);
ULONG ulBusNumber = new ULONG(busNumber);
ULONG ulSlotNumber = new ULONG(slotNumber);
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesRead = new ULONGByReference();
COMUtils.checkRC(jnaData.ReadBusData(ulBusDataType, ulBusNumber, ulSlotNumber, ullOffset,
into, ulLen, pulBytesRead));
int read = pulBytesRead.getValue().intValue();
into.position(read + into.position());
return read;
}
@Override
public int writeBusData(int busDataType, int busNumber, int slotNumber, long offset,
ByteBuffer from, int len) {
if (len > from.remaining()) {
throw new BufferOverflowException();
}
ULONG ulBusDataType = new ULONG(busDataType);
ULONG ulBusNumber = new ULONG(busNumber);
ULONG ulSlotNumber = new ULONG(slotNumber);
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesWritten = new ULONGByReference();
COMUtils.checkRC(jnaData.WriteBusData(ulBusDataType, ulBusNumber, ulSlotNumber, ullOffset,
from, ulLen, pulBytesWritten));
int written = pulBytesWritten.getValue().intValue();
from.position(written + from.position());
return written;
}
@Override
public int readIo(int interfaceType, int busNumber, int addressSpace, long offset,
ByteBuffer into, int len) {
if (len > into.remaining()) {
throw new BufferOverflowException();
}
ULONG ulInterfaceType = new ULONG(interfaceType);
ULONG ulBusNumber = new ULONG(busNumber);
ULONG ulAddressSpace = new ULONG(addressSpace);
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesRead = new ULONGByReference();
COMUtils.checkRC(jnaData.ReadIo(ulInterfaceType, ulBusNumber, ulAddressSpace, ullOffset,
into, ulLen, pulBytesRead));
int read = pulBytesRead.getValue().intValue();
into.position(read + into.position());
return read;
}
@Override
public int writeIo(int interfaceType, int busNumber, int addressSpace, long offset,
ByteBuffer from, int len) {
if (len > from.remaining()) {
throw new BufferOverflowException();
}
ULONG ulInterfaceType = new ULONG(interfaceType);
ULONG ulBusNumber = new ULONG(busNumber);
ULONG ulAddressSpace = new ULONG(addressSpace);
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesWritten = new ULONGByReference();
COMUtils.checkRC(jnaData.WriteIo(ulInterfaceType, ulBusNumber, ulAddressSpace, ullOffset,
from, ulLen, pulBytesWritten));
int written = pulBytesWritten.getValue().intValue();
from.position(written + from.position());
return written;
}
@Override
public long readMsr(int msr) {
ULONG ulNumber = new ULONG(msr);
ULONGLONGByReference pulValue = new ULONGLONGByReference();
COMUtils.checkRC(jnaData.ReadMsr(ulNumber, pulValue));
return pulValue.getValue().longValue();
}
@Override
public void writeMsr(int msr, long value) {
ULONG ulNumber = new ULONG(msr);
ULONGLONG ullValue = new ULONGLONG(value);
COMUtils.checkRC(jnaData.WriteMsr(ulNumber, ullValue));
}
@Override
public int readDebuggerData(int offset, ByteBuffer into, int len) {
if (len > into.remaining()) {
throw new BufferOverflowException();
}
ULONG ulOffset = new ULONG(offset);
ULONG ulLen = new ULONG(len);
ULONGByReference pulBytesRead = new ULONGByReference();
COMUtils.checkRC(jnaData.ReadDebuggerData(ulOffset, into, ulLen, pulBytesRead));
int read = pulBytesRead.getValue().intValue();
into.position(read + into.position());
return read;
}
}

View file

@ -0,0 +1,52 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.dataspaces;
import com.sun.jna.platform.win32.WinDef.ULONGLONG;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.COMUtils;
import agent.dbgeng.dbgeng.COMUtilsExtra;
import agent.dbgeng.jna.dbgeng.WinNTExtra.MEMORY_BASIC_INFORMATION64;
import agent.dbgeng.jna.dbgeng.dataspaces.IDebugDataSpaces2;
import ghidra.comm.util.BitmaskSet;
public class DebugDataSpacesImpl2 extends DebugDataSpacesImpl1 {
private final IDebugDataSpaces2 jnaData;
public DebugDataSpacesImpl2(IDebugDataSpaces2 jnaData) {
super(jnaData);
this.jnaData = jnaData;
}
@Override
public DebugMemoryBasicInformation queryVirtual(long offset) {
ULONGLONG ullOffset = new ULONGLONG(offset);
MEMORY_BASIC_INFORMATION64.ByReference pInfo = new MEMORY_BASIC_INFORMATION64.ByReference();
HRESULT hr = jnaData.QueryVirtual(ullOffset, pInfo);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
return null;
}
COMUtils.checkRC(hr);
return new DebugMemoryBasicInformation(pInfo.BaseAddress.longValue(),
pInfo.AllocationBase.longValue(),
new BitmaskSet<>(PageProtection.class, pInfo.AllocationProtect.intValue()),
pInfo.RegionSize.longValue(), PageState.byValue(pInfo.State.intValue()),
new BitmaskSet<>(PageProtection.class, pInfo.Protect.intValue()),
PageType.byValue(pInfo.Type.intValue()));
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.dataspaces;
import agent.dbgeng.jna.dbgeng.dataspaces.IDebugDataSpaces3;
public class DebugDataSpacesImpl3 extends DebugDataSpacesImpl2 {
@SuppressWarnings("unused")
private final IDebugDataSpaces3 jnaData;
public DebugDataSpacesImpl3(IDebugDataSpaces3 jnaData) {
super(jnaData);
this.jnaData = jnaData;
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.dataspaces;
import agent.dbgeng.jna.dbgeng.dataspaces.IDebugDataSpaces4;
public class DebugDataSpacesImpl4 extends DebugDataSpacesImpl3 {
@SuppressWarnings("unused")
private final IDebugDataSpaces4 jnaData;
public DebugDataSpacesImpl4(IDebugDataSpaces4 jnaData) {
super(jnaData);
this.jnaData = jnaData;
}
}

View file

@ -0,0 +1,67 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.dataspaces;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import agent.dbgeng.dbgeng.DebugDataSpaces;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier;
import agent.dbgeng.jna.dbgeng.dataspaces.*;
import ghidra.util.datastruct.WeakValueHashMap;
public interface DebugDataSpacesInternal extends DebugDataSpaces {
Map<Pointer, DebugDataSpacesInternal> CACHE = new WeakValueHashMap<>();
static DebugDataSpacesInternal instanceFor(WrapIDebugDataSpaces data) {
return DbgEngUtil.lazyWeakCache(CACHE, data, DebugDataSpacesImpl1::new);
}
static DebugDataSpacesInternal instanceFor(WrapIDebugDataSpaces2 data) {
return DbgEngUtil.lazyWeakCache(CACHE, data, DebugDataSpacesImpl2::new);
}
static DebugDataSpacesInternal instanceFor(WrapIDebugDataSpaces3 data) {
return DbgEngUtil.lazyWeakCache(CACHE, data, DebugDataSpacesImpl3::new);
}
static DebugDataSpacesInternal instanceFor(WrapIDebugDataSpaces4 data) {
return DbgEngUtil.lazyWeakCache(CACHE, data, DebugDataSpacesImpl4::new);
}
ImmutableMap.Builder<REFIID, Class<? extends WrapIDebugDataSpaces>> PREFERRED_DATA_SPACES_IIDS_BUILDER =
ImmutableMap.builder();
Map<REFIID, Class<? extends WrapIDebugDataSpaces>> PREFERRED_DATA_SPACES_IIDS =
PREFERRED_DATA_SPACES_IIDS_BUILDER //
.put(new REFIID(IDebugDataSpaces4.IID_IDEBUG_DATA_SPACES4),
WrapIDebugDataSpaces4.class) //
.put(new REFIID(IDebugDataSpaces3.IID_IDEBUG_DATA_SPACES3),
WrapIDebugDataSpaces3.class) //
.put(new REFIID(IDebugDataSpaces2.IID_IDEBUG_DATA_SPACES2),
WrapIDebugDataSpaces2.class) //
.put(new REFIID(IDebugDataSpaces.IID_IDEBUG_DATA_SPACES),
WrapIDebugDataSpaces.class) //
.build();
static DebugDataSpacesInternal tryPreferredInterfaces(InterfaceSupplier supplier) {
return DbgEngUtil.tryPreferredInterfaces(DebugDataSpacesInternal.class,
PREFERRED_DATA_SPACES_IIDS, supplier);
}
}

View file

@ -0,0 +1,296 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.event;
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.IUnknown;
import com.sun.jna.ptr.PointerByReference;
import agent.dbgeng.dbgeng.*;
import agent.dbgeng.dbgeng.DebugClient.DebugStatus;
import agent.dbgeng.dbgeng.DebugClient.SessionStatus;
import agent.dbgeng.dbgeng.DebugEventCallbacks.DebugEvent;
import agent.dbgeng.impl.dbgeng.breakpoint.DebugBreakpointInternal;
import agent.dbgeng.impl.dbgeng.client.DebugClientImpl1;
import agent.dbgeng.impl.dbgeng.client.DebugClientInternal;
import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64;
import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint;
import agent.dbgeng.jna.dbgeng.event.*;
import ghidra.comm.util.BitmaskSet;
import ghidra.util.Msg;
public class WrapCallbackIDebugEventCallbacks implements CallbackIDebugEventCallbacks {
private final DebugClientInternal client;
private final DebugEventCallbacks cb;
private ListenerIDebugEventCallbacks listener;
public WrapCallbackIDebugEventCallbacks(DebugClientImpl1 client, DebugEventCallbacks cb) {
this.client = client;
this.cb = cb;
}
public void setListener(ListenerIDebugEventCallbacks listener) {
this.listener = listener;
}
@Override
public Pointer getPointer() {
return listener.getPointer();
}
@Override
public HRESULT QueryInterface(REFIID refid, PointerByReference ppvObject) {
if (null == ppvObject) {
return new HRESULT(WinError.E_POINTER);
}
else if (refid.getValue().equals(IDebugEventCallbacks.IID_IDEBUG_EVENT_CALLBACKS)) {
ppvObject.setValue(this.getPointer());
return WinError.S_OK;
}
else if (refid.getValue().equals(IUnknown.IID_IUNKNOWN)) {
ppvObject.setValue(this.getPointer());
return WinError.S_OK;
}
return new HRESULT(WinError.E_NOINTERFACE);
}
@Override
public int AddRef() {
return 0;
}
@Override
public int Release() {
return 0;
}
@Override
public HRESULT GetInterestMask(ULONGByReference Mask) {
try {
BitmaskSet<DebugEvent> interest = cb.getInterestMask();
ULONG ulInterest = new ULONG(interest.getBitmask());
Mask.setValue(ulInterest);
return WinError.S_OK;
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT Breakpoint(WrapIDebugBreakpoint.ByReference Bp) {
try {
DebugBreakpoint bpt = DebugBreakpointInternal
.tryPreferredInterfaces(client.getControlInternal(), Bp::QueryInterface);
cb.breakpoint(bpt);
return WinError.S_OK;
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT Exception(EXCEPTION_RECORD64.ByReference Exception, ULONG FirstChance) {
try {
int numParams = Exception.NumberParameters.intValue();
List<Long> information = new ArrayList<>(numParams);
for (int i = 0; i < numParams; i++) {
information.set(i, Exception.ExceptionInformation[i].longValue());
}
DebugExceptionRecord64 exc =
new DebugExceptionRecord64(Exception.ExceptionCode.intValue(),
Exception.ExceptionFlags.intValue(), Exception.ExceptionRecord.longValue(),
Exception.ExceptionAddress.longValue(), information);
boolean firstChance = FirstChance.intValue() != 0;
cb.exception(exc, firstChance);
return WinError.S_OK;
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT CreateThread(ULONGLONG Handle, ULONGLONG DataOffset, ULONGLONG StartOffset) {
try {
DebugStatus status = cb.createThread(new DebugThreadInfo(Handle.longValue(),
DataOffset.longValue(), StartOffset.longValue()));
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT ExitThread(ULONG ExitCode) {
try {
DebugStatus status = cb.exitThread(ExitCode.intValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT CreateProcess(ULONGLONG ImageFileHandle, ULONGLONG Handle, ULONGLONG BaseOffset,
ULONG ModuleSize, String ModuleName, String ImageName, ULONG CheckSum,
ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, ULONGLONG ThreadDataOffset,
ULONGLONG StartOffset) {
try {
// TODO: Associate thread with process
// TODO: Record All these other parameters?
DebugStatus status = cb.createProcess(new DebugProcessInfo(Handle.longValue(),
new DebugModuleInfo(ImageFileHandle.longValue(), BaseOffset.longValue(),
ModuleSize.intValue(), ModuleName, ImageName, CheckSum.intValue(),
TimeDateStamp.intValue()),
new DebugThreadInfo(InitialThreadHandle.longValue(), ThreadDataOffset.longValue(),
StartOffset.longValue())));
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT ExitProcess(ULONG ExitCode) {
try {
DebugStatus status = cb.exitProcess(ExitCode.intValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT LoadModule(ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, ULONG ModuleSize,
String ModuleName, String ImageName, ULONG CheckSum, ULONG TimeDateStamp) {
try {
// All of these are potentially null
long imageFileHandle = ImageFileHandle == null ? -1L : ImageFileHandle.longValue();
long baseOffset = BaseOffset == null ? -1L : BaseOffset.longValue();
int moduleSize = ModuleSize == null ? -1 : ModuleSize.intValue();
String moduleName = ModuleName == null ? "" : ModuleName.toString();
String imageName = ImageName == null ? "" : ImageName.toString();
int checkSum = CheckSum == null ? -1 : CheckSum.intValue();
DebugStatus status = cb.loadModule(new DebugModuleInfo(imageFileHandle, baseOffset,
moduleSize, moduleName, imageName, checkSum, TimeDateStamp.intValue()));
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT UnloadModule(String ImageBaseName, ULONGLONG BaseOffset) {
try {
DebugStatus status = cb.unloadModule(ImageBaseName, BaseOffset.longValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT SystemError(ULONG Error, ULONG Level) {
try {
DebugStatus status = cb.systemError(Error.intValue(), Level.intValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT SessionStatus(ULONG Status) {
try {
SessionStatus ss = SessionStatus.values()[Status.intValue()];
DebugStatus status = cb.sessionStatus(ss);
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT ChangeDebuggeeState(ULONG Flags, ULONGLONG Argument) {
try {
BitmaskSet<DebugClient.ChangeDebuggeeState> flags =
new BitmaskSet<>(DebugClient.ChangeDebuggeeState.class, Flags.intValue());
DebugStatus status = cb.changeDebuggeeState(flags, Argument.longValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT ChangeEngineState(ULONG Flags, ULONGLONG Argument) {
try {
BitmaskSet<DebugClient.ChangeEngineState> flags =
new BitmaskSet<>(DebugClient.ChangeEngineState.class, Flags.intValue());
DebugStatus status = cb.changeEngineState(flags, Argument.longValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT ChangeSymbolState(ULONG Flags, ULONGLONG Argument) {
try {
BitmaskSet<DebugClient.ChangeSymbolState> flags =
new BitmaskSet<>(DebugClient.ChangeSymbolState.class, Flags.intValue());
DebugStatus status = cb.changeSymbolState(flags, Argument.longValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
}

View file

@ -0,0 +1,295 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.event;
import java.util.ArrayList;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.Guid.REFIID;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.IUnknown;
import com.sun.jna.ptr.PointerByReference;
import agent.dbgeng.dbgeng.*;
import agent.dbgeng.dbgeng.DebugClient.DebugStatus;
import agent.dbgeng.dbgeng.DebugClient.SessionStatus;
import agent.dbgeng.dbgeng.DebugEventCallbacks.DebugEvent;
import agent.dbgeng.impl.dbgeng.breakpoint.DebugBreakpointInternal;
import agent.dbgeng.impl.dbgeng.client.DebugClientImpl5;
import agent.dbgeng.impl.dbgeng.client.DebugClientInternal;
import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_RECORD64;
import agent.dbgeng.jna.dbgeng.breakpoint.WrapIDebugBreakpoint;
import agent.dbgeng.jna.dbgeng.event.*;
import ghidra.comm.util.BitmaskSet;
import ghidra.util.Msg;
public class WrapCallbackIDebugEventCallbacksWide implements CallbackIDebugEventCallbacksWide {
private final DebugClientInternal client;
private final DebugEventCallbacks cb;
private ListenerIDebugEventCallbacksWide listener;
public WrapCallbackIDebugEventCallbacksWide(DebugClientImpl5 client, DebugEventCallbacks cb) {
this.client = client;
this.cb = cb;
}
public void setListener(ListenerIDebugEventCallbacksWide listener) {
this.listener = listener;
}
@Override
public Pointer getPointer() {
return listener.getPointer();
}
@Override
public HRESULT QueryInterface(REFIID refid, PointerByReference ppvObject) {
if (null == ppvObject) {
return new HRESULT(WinError.E_POINTER);
}
else if (refid.getValue()
.equals(IDebugEventCallbacksWide.IID_IDEBUG_EVENT_CALLBACKS_WIDE)) {
ppvObject.setValue(this.getPointer());
return WinError.S_OK;
}
else if (refid.getValue().equals(IUnknown.IID_IUNKNOWN)) {
ppvObject.setValue(this.getPointer());
return WinError.S_OK;
}
return new HRESULT(WinError.E_NOINTERFACE);
}
@Override
public int AddRef() {
return 0;
}
@Override
public int Release() {
return 0;
}
@Override
public HRESULT GetInterestMask(ULONGByReference Mask) {
try {
BitmaskSet<DebugEvent> interest = cb.getInterestMask();
ULONG ulInterest = new ULONG(interest.getBitmask());
Mask.setValue(ulInterest);
return WinError.S_OK;
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT Breakpoint(WrapIDebugBreakpoint.ByReference Bp) {
try {
DebugBreakpoint bpt = DebugBreakpointInternal
.tryPreferredInterfaces(client.getControlInternal(), Bp::QueryInterface);
cb.breakpoint(bpt);
return WinError.S_OK;
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT Exception(EXCEPTION_RECORD64.ByReference Exception, ULONG FirstChance) {
try {
int numParams = Exception.NumberParameters.intValue();
List<Long> information = new ArrayList<>(numParams);
for (int i = 0; i < numParams; i++) {
information.add(Exception.ExceptionInformation[i].longValue());
}
DebugExceptionRecord64 exc =
new DebugExceptionRecord64(Exception.ExceptionCode.intValue(),
Exception.ExceptionFlags.intValue(), Exception.ExceptionRecord.longValue(),
Exception.ExceptionAddress.longValue(), information);
boolean firstChance = FirstChance.intValue() != 0;
cb.exception(exc, firstChance);
return WinError.S_OK;
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT CreateThread(ULONGLONG Handle, ULONGLONG DataOffset, ULONGLONG StartOffset) {
try {
DebugStatus status = cb.createThread(new DebugThreadInfo(Handle.longValue(),
DataOffset.longValue(), StartOffset.longValue()));
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT ExitThread(ULONG ExitCode) {
try {
DebugStatus status = cb.exitThread(ExitCode.intValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT CreateProcess(ULONGLONG ImageFileHandle, ULONGLONG Handle, ULONGLONG BaseOffset,
ULONG ModuleSize, WString ModuleName, WString ImageName, ULONG CheckSum,
ULONG TimeDateStamp, ULONGLONG InitialThreadHandle, ULONGLONG ThreadDataOffset,
ULONGLONG StartOffset) {
try {
DebugStatus status = cb.createProcess(new DebugProcessInfo(Handle.longValue(),
new DebugModuleInfo(ImageFileHandle.longValue(), BaseOffset.longValue(),
ModuleSize.intValue(), ModuleName.toString(), ImageName.toString(),
CheckSum.intValue(), TimeDateStamp.intValue()),
new DebugThreadInfo(InitialThreadHandle.longValue(), ThreadDataOffset.longValue(),
StartOffset.longValue())));
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT ExitProcess(ULONG ExitCode) {
try {
DebugStatus status = cb.exitProcess(ExitCode.intValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT LoadModule(ULONGLONG ImageFileHandle, ULONGLONG BaseOffset, ULONG ModuleSize,
WString ModuleName, WString ImageName, ULONG CheckSum, ULONG TimeDateStamp) {
try {
long imageFileHandle = ImageFileHandle == null ? -1L : ImageFileHandle.longValue();
long baseOffset = BaseOffset == null ? -1L : BaseOffset.longValue();
int moduleSize = ModuleSize == null ? -1 : ModuleSize.intValue();
String moduleName = ModuleName == null ? "" : ModuleName.toString();
String imageName = ImageName == null ? "" : ImageName.toString();
int checkSum = CheckSum == null ? -1 : CheckSum.intValue();
DebugStatus status = cb.loadModule(new DebugModuleInfo(imageFileHandle, baseOffset,
moduleSize, moduleName, imageName, checkSum, TimeDateStamp.intValue()));
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT UnloadModule(WString ImageBaseName, ULONGLONG BaseOffset) {
try {
DebugStatus status = cb.unloadModule(ImageBaseName.toString(), BaseOffset.longValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT SystemError(ULONG Error, ULONG Level) {
try {
DebugStatus status = cb.systemError(Error.intValue(), Level.intValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT SessionStatus(ULONG Status) {
try {
SessionStatus ss = SessionStatus.values()[Status.intValue()];
DebugStatus status = cb.sessionStatus(ss);
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT ChangeDebuggeeState(ULONG Flags, ULONGLONG Argument) {
try {
BitmaskSet<DebugClient.ChangeDebuggeeState> flags =
new BitmaskSet<>(DebugClient.ChangeDebuggeeState.class, Flags.intValue());
DebugStatus status = cb.changeDebuggeeState(flags, Argument.longValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT ChangeEngineState(ULONG Flags, ULONGLONG Argument) {
try {
BitmaskSet<DebugClient.ChangeEngineState> flags =
new BitmaskSet<>(DebugClient.ChangeEngineState.class, Flags.intValue());
DebugStatus status = cb.changeEngineState(flags, Argument.longValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT ChangeSymbolState(ULONG Flags, ULONGLONG Argument) {
try {
BitmaskSet<DebugClient.ChangeSymbolState> flags =
new BitmaskSet<>(DebugClient.ChangeSymbolState.class, Flags.intValue());
DebugStatus status = cb.changeSymbolState(flags, Argument.longValue());
return new HRESULT(status.ordinal());
}
catch (Throwable e) {
Msg.error(this, "Error during callback", e);
return new HRESULT(WinError.E_UNEXPECTED);
}
}
}

View file

@ -0,0 +1,122 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.io;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.IUnknown;
import com.sun.jna.ptr.PointerByReference;
import agent.dbgeng.dbgeng.DebugInputCallbacks;
import agent.dbgeng.impl.dbgeng.client.DebugClientImpl1;
import agent.dbgeng.jna.dbgeng.io.*;
import ghidra.util.exception.CancelledException;
public class WrapCallbackIDebugInputCallbacks implements CallbackIDebugInputCallbacks {
private final DebugClientImpl1 client;
private final DebugInputCallbacks cb;
private ListenerIDebugInputCallbacks listener;
private final Set<CompletableFuture<String>> futures = new HashSet<>(); // TODO: Just one?
public WrapCallbackIDebugInputCallbacks(DebugClientImpl1 client, DebugInputCallbacks cb) {
this.client = client;
this.cb = cb;
}
public void setListener(ListenerIDebugInputCallbacks listener) {
this.listener = listener;
}
@Override
public Pointer getPointer() {
return listener.getPointer();
}
@Override
public HRESULT QueryInterface(REFIID refid, PointerByReference ppvObject) {
if (null == ppvObject) {
return new HRESULT(WinError.E_POINTER);
}
else if (refid.getValue().equals(IDebugInputCallbacks.IID_IDEBUG_INPUT_CALLBACKS)) {
ppvObject.setValue(this.getPointer());
return WinError.S_OK;
}
else if (refid.getValue().equals(IUnknown.IID_IUNKNOWN)) {
ppvObject.setValue(this.getPointer());
return WinError.S_OK;
}
return new HRESULT(WinError.E_NOINTERFACE);
}
@Override
public int AddRef() {
return 0;
}
@Override
public int Release() {
return 0;
}
@Override
public HRESULT StartInput(ULONG BufferSize) {
try {
CompletableFuture<String> future = cb.startInput();
if (future == null) {
return WinError.S_OK;
}
future.handle((input, exc) -> {
if (exc == null) {
client.getControl().returnInput(input);
}
else if (exc instanceof CancelledException) {
// Normal if another client provides input
}
else {
client.getControl().errln("ERROR getting input: " + exc.getMessage());
}
futures.remove(future);
return null;
});
return WinError.S_OK;
}
catch (Throwable e) {
return new HRESULT(WinError.E_UNEXPECTED);
}
}
@Override
public HRESULT EndInput() {
try {
for (CompletableFuture<String> future : futures) {
future.cancel(true);
}
cb.endInput();
return WinError.S_OK;
}
catch (Throwable e) {
return new HRESULT(WinError.E_UNEXPECTED);
}
}
}

View file

@ -0,0 +1,82 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.io;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.IUnknown;
import com.sun.jna.ptr.PointerByReference;
import agent.dbgeng.dbgeng.DebugOutputCallbacks;
import agent.dbgeng.jna.dbgeng.io.*;
public class WrapCallbackIDebugOutputCallbacks implements CallbackIDebugOutputCallbacks {
private final DebugOutputCallbacks cb;
private ListenerIDebugOutputCallbacks listener;
public WrapCallbackIDebugOutputCallbacks(DebugOutputCallbacks cb) {
this.cb = cb;
}
public void setListener(ListenerIDebugOutputCallbacks listener) {
this.listener = listener;
}
@Override
public Pointer getPointer() {
return listener.getPointer();
}
@Override
public HRESULT QueryInterface(REFIID refid, PointerByReference ppvObject) {
if (null == ppvObject) {
return new HRESULT(WinError.E_POINTER);
}
else if (refid.getValue().equals(IDebugOutputCallbacks.IID_IDEBUG_OUTPUT_CALLBACKS)) {
ppvObject.setValue(this.getPointer());
return WinError.S_OK;
}
else if (refid.getValue().equals(IUnknown.IID_IUNKNOWN)) {
ppvObject.setValue(this.getPointer());
return WinError.S_OK;
}
return new HRESULT(WinError.E_NOINTERFACE);
}
@Override
public int AddRef() {
return 0;
}
@Override
public int Release() {
return 0;
}
@Override
public HRESULT Output(ULONG Mask, String Text) {
try {
cb.output(Mask.intValue(), Text);
return WinError.S_OK;
}
catch (Throwable e) {
return new HRESULT(WinError.E_UNEXPECTED);
}
}
}

View file

@ -0,0 +1,84 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.io;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.Guid.REFIID;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.IUnknown;
import com.sun.jna.ptr.PointerByReference;
import agent.dbgeng.dbgeng.DebugOutputCallbacks;
import agent.dbgeng.jna.dbgeng.io.*;
public class WrapCallbackIDebugOutputCallbacksWide implements CallbackIDebugOutputCallbacksWide {
private final DebugOutputCallbacks cb;
private ListenerIDebugOutputCallbacksWide listener;
public WrapCallbackIDebugOutputCallbacksWide(DebugOutputCallbacks cb) {
this.cb = cb;
}
public void setListener(ListenerIDebugOutputCallbacksWide listener) {
this.listener = listener;
}
@Override
public Pointer getPointer() {
return listener.getPointer();
}
@Override
public HRESULT QueryInterface(REFIID refid, PointerByReference ppvObject) {
if (null == ppvObject) {
return new HRESULT(WinError.E_POINTER);
}
else if (refid.getValue()
.equals(IDebugOutputCallbacksWide.IID_IDEBUG_OUTPUT_CALLBACKS_WIDE)) {
ppvObject.setValue(this.getPointer());
return WinError.S_OK;
}
else if (refid.getValue().equals(IUnknown.IID_IUNKNOWN)) {
ppvObject.setValue(this.getPointer());
return WinError.S_OK;
}
return new HRESULT(WinError.E_NOINTERFACE);
}
@Override
public int AddRef() {
return 0;
}
@Override
public int Release() {
return 0;
}
@Override
public HRESULT Output(ULONG Mask, WString Text) {
try {
cb.output(Mask.intValue(), Text.toString());
return WinError.S_OK;
}
catch (Throwable e) {
return new HRESULT(WinError.E_UNEXPECTED);
}
}
}

View file

@ -0,0 +1,154 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.registers;
import java.util.*;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinDef.ULONGByReference;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.COMUtils;
import agent.dbgeng.dbgeng.*;
import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable;
import agent.dbgeng.dbgeng.DebugValue.DebugValueType;
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_REGISTER_DESCRIPTION;
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE;
import agent.dbgeng.jna.dbgeng.registers.IDebugRegisters;
import ghidra.comm.util.BitmaskSet;
public class DebugRegistersImpl1 implements DebugRegistersInternal {
@SuppressWarnings("unused")
private final OpaqueCleanable cleanable;
private final IDebugRegisters jnaRegisters;
public DebugRegistersImpl1(IDebugRegisters jnaRegisters) {
this.cleanable = DbgEng.releaseWhenPhantom(this, jnaRegisters);
this.jnaRegisters = jnaRegisters;
}
@Override
public int getNumberRegisters() {
ULONGByReference pulNumber = new ULONGByReference();
COMUtils.checkRC(jnaRegisters.GetNumberRegisters(pulNumber));
return pulNumber.getValue().intValue();
}
@Override
public DebugRegisterDescription getDescription(int registerIndex) {
ULONG ulRegIdx = new ULONG(registerIndex);
ULONGByReference pulNameSize = new ULONGByReference();
COMUtils.checkRC(
jnaRegisters.GetDescription(ulRegIdx, null, new ULONG(0), pulNameSize, null));
byte[] name = new byte[pulNameSize.getValue().intValue()];
DEBUG_REGISTER_DESCRIPTION.ByReference desc = new DEBUG_REGISTER_DESCRIPTION.ByReference();
COMUtils.checkRC(
jnaRegisters.GetDescription(ulRegIdx, name, pulNameSize.getValue(), null, desc));
return new DebugRegisterDescription(Native.toString(name), registerIndex,
DebugValueType.values()[desc.Type.intValue()],
new BitmaskSet<>(DebugRegisterFlags.class, desc.Flags.intValue()),
desc.SubregMaster.intValue(), desc.SubregLength.intValue(), desc.SubregMask.longValue(),
desc.SubregShift.intValue());
}
@Override
public int getIndexByName(String name) {
ULONGByReference pulIndex = new ULONGByReference();
HRESULT hr = jnaRegisters.GetIndexByName(name, pulIndex);
if (hr.equals(COMUtilsExtra.E_NOINTERFACE)) {
// This happens for 32-bit WOW execution
return -1;
}
COMUtils.checkRC(hr);
return pulIndex.getValue().intValue();
}
@Override
public DebugValue getValue(int index) {
ULONG ulIndex = new ULONG(index);
DEBUG_VALUE.ByReference dvVal = new DEBUG_VALUE.ByReference();
COMUtils.checkRC(jnaRegisters.GetValue(ulIndex, dvVal));
return dvVal.convertTo(DebugValue.class);
}
protected void doGetValues(DebugRegisterSource source, ULONG ulCount, ULONG[] pulIndices,
DEBUG_VALUE[] pValues) {
if (source != DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE) {
throw new IllegalArgumentException("This interface only permits DEBUG_REGSRC_DEBUGGEE");
}
COMUtils.checkRC(jnaRegisters.GetValues(ulCount, pulIndices, new ULONG(0), pValues));
}
@Override
public Map<Integer, DebugValue> getValues(DebugRegisterSource source,
Collection<Integer> indices) {
if (source != DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE) {
throw new IllegalArgumentException("This interface only permits DEBUG_REGSRC_DEBUGGEE");
}
if (indices.isEmpty()) {
return Collections.emptyMap();
}
List<Integer> li = new ArrayList<>(indices);
ULONG ulCount = new ULONG(li.size());
ULONG[] pulIndices = new ULONG[li.size()];
DEBUG_VALUE[] pValues = (DEBUG_VALUE[]) new DEBUG_VALUE().toArray(li.size());
for (int i = 0; i < indices.size(); i++) {
pulIndices[i] = new ULONG(li.get(i));
}
doGetValues(source, ulCount, pulIndices, pValues);
Map<Integer, DebugValue> result = new LinkedHashMap<>();
for (int i = 0; i < li.size(); i++) {
result.put(li.get(i), pValues[i].convertTo(DebugValue.class));
}
return result;
}
@Override
public void setValue(int index, DebugValue value) {
ULONG ulIndex = new ULONG(index);
DEBUG_VALUE.ByReference dvVal = new DEBUG_VALUE.ByReference();
DEBUG_VALUE.fromDebugValue(dvVal, value);
COMUtils.checkRC(jnaRegisters.SetValue(ulIndex, dvVal));
}
protected void doSetValues(DebugRegisterSource source, ULONG ulCount, ULONG[] pulIndices,
DEBUG_VALUE[] pValues) {
if (source != DebugRegisterSource.DEBUG_REGSRC_DEBUGGEE) {
throw new IllegalArgumentException("This interface only permits DEBUG_REGSRC_DEBUGGEE");
}
COMUtils.checkRC(jnaRegisters.SetValues(ulCount, pulIndices, new ULONG(0), pValues));
}
@Override
public void setValues(DebugRegisterSource source, Map<Integer, DebugValue> values) {
if (values.isEmpty()) {
return;
}
ULONG ulCount = new ULONG(values.size());
ULONG[] pulIndices = new ULONG[values.size()];
DEBUG_VALUE[] pValues = (DEBUG_VALUE[]) new DEBUG_VALUE().toArray(values.size());
int i = 0;
for (Map.Entry<Integer, DebugValue> ent : values.entrySet()) {
pulIndices[i] = new ULONG(ent.getKey());
DEBUG_VALUE.fromDebugValue(pValues[i], ent.getValue());
i++;
}
doSetValues(source, ulCount, pulIndices, pValues);
}
}

View file

@ -0,0 +1,50 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.registers;
import com.sun.jna.platform.win32.WinDef.ULONG;
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_VALUE;
import agent.dbgeng.jna.dbgeng.registers.IDebugRegisters2;
import com.sun.jna.platform.win32.COM.COMUtils;
public class DebugRegistersImpl2 extends DebugRegistersImpl1 {
private final IDebugRegisters2 jnaRegisters;
public DebugRegistersImpl2(IDebugRegisters2 jnaRegisters) {
super(jnaRegisters);
this.jnaRegisters = jnaRegisters;
}
@Override
protected void doGetValues(DebugRegisterSource source, ULONG ulCount, ULONG[] pulIndices,
DEBUG_VALUE[] pValues) {
ULONG ulSource = new ULONG(source.ordinal());
COMUtils
.checkRC(
jnaRegisters.GetValues2(ulSource, ulCount, pulIndices, new ULONG(0), pValues));
}
@Override
protected void doSetValues(DebugRegisterSource source, ULONG ulCount, ULONG[] pulIndices,
DEBUG_VALUE[] pValues) {
ULONG ulSource = new ULONG(source.ordinal());
COMUtils
.checkRC(
jnaRegisters.SetValues2(ulSource, ulCount, pulIndices, new ULONG(0), pValues));
}
}

View file

@ -0,0 +1,53 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.registers;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import agent.dbgeng.dbgeng.DebugRegisters;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier;
import agent.dbgeng.jna.dbgeng.registers.*;
import ghidra.util.datastruct.WeakValueHashMap;
public interface DebugRegistersInternal extends DebugRegisters {
Map<Pointer, DebugRegistersInternal> CACHE = new WeakValueHashMap<>();
static DebugRegistersInternal instanceFor(WrapIDebugRegisters registers) {
return DbgEngUtil.lazyWeakCache(CACHE, registers, DebugRegistersImpl1::new);
}
static DebugRegistersInternal instanceFor(WrapIDebugRegisters2 registers) {
return DbgEngUtil.lazyWeakCache(CACHE, registers, DebugRegistersImpl2::new);
}
ImmutableMap.Builder<REFIID, Class<? extends WrapIDebugRegisters>> PREFERRED_REGISTERS_IIDS_BUILDER =
ImmutableMap.builder();
Map<REFIID, Class<? extends WrapIDebugRegisters>> PREFERRED_REGISTERS_IIDS =
PREFERRED_REGISTERS_IIDS_BUILDER //
.put(new REFIID(IDebugRegisters2.IID_IDEBUG_REGISTERS2), WrapIDebugRegisters2.class) //
.put(new REFIID(IDebugRegisters.IID_IDEBUG_REGISTERS), WrapIDebugRegisters.class) //
.build();
static DebugRegistersInternal tryPreferredInterfaces(InterfaceSupplier supplier) {
return DbgEngUtil.tryPreferredInterfaces(DebugRegistersInternal.class,
PREFERRED_REGISTERS_IIDS, supplier);
}
}

View file

@ -0,0 +1,70 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.symbols;
import java.util.Objects;
import agent.dbgeng.dbgeng.DebugModule;
public class DebugModuleImpl implements DebugModule {
private final DebugSymbolsInternal symbols;
final int index;
final long base;
DebugModuleImpl(DebugSymbolsInternal symbols, int index, long base) {
this.symbols = symbols;
this.index = index;
this.base = base;
}
@Override
public int hashCode() {
return Objects.hash(symbols, index, base);
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof DebugModuleImpl)) {
return false;
}
DebugModuleImpl that = (DebugModuleImpl) obj;
if (!this.symbols.equals(that.symbols)) {
return false;
}
if (this.index != that.index) {
return false;
}
if (this.base != that.base) {
return false;
}
return true;
}
@Override
public String getName(DebugModuleName which) {
return symbols.getModuleName(which, this);
}
@Override
public int getIndex() {
return index;
}
@Override
public long getBase() {
return base;
}
}

View file

@ -0,0 +1,213 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.symbols;
import java.util.Iterator;
import java.util.List;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.*;
import agent.dbgeng.dbgeng.*;
import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable;
import agent.dbgeng.dbgeng.DebugModule.DebugModuleName;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.jna.dbgeng.symbols.IDebugSymbols;
import com.sun.jna.platform.win32.COM.COMException;
import com.sun.jna.platform.win32.COM.COMUtils;
public class DebugSymbolsImpl1 implements DebugSymbolsInternal {
@SuppressWarnings("unused")
private final OpaqueCleanable cleanable;
private final IDebugSymbols jnaSymbols;
public DebugSymbolsImpl1(IDebugSymbols jnaSymbols) {
this.cleanable = DbgEng.releaseWhenPhantom(this, jnaSymbols);
this.jnaSymbols = jnaSymbols;
}
@Override
public int getNumberLoadedModules() {
ULONGByReference pulLoaded = new ULONGByReference();
ULONGByReference pulUnloaded = new ULONGByReference();
COMUtils.checkRC(jnaSymbols.GetNumberModules(pulLoaded, pulUnloaded));
return pulLoaded.getValue().intValue();
}
@Override
public int getNumberUnloadedModules() {
ULONGByReference pulLoaded = new ULONGByReference();
ULONGByReference pulUnloaded = new ULONGByReference();
COMUtils.checkRC(jnaSymbols.GetNumberModules(pulLoaded, pulUnloaded));
return pulUnloaded.getValue().intValue();
}
@Override
public DebugModule getModuleByIndex(int index) {
ULONG ulIndex = new ULONG(index);
ULONGLONGByReference pullBase = new ULONGLONGByReference();
COMUtils.checkRC(jnaSymbols.GetModuleByIndex(ulIndex, pullBase));
return new DebugModuleImpl(this, index, pullBase.getValue().longValue());
}
@Override
public DebugModule getModuleByModuleName(String name, int startIndex) {
ULONG ulStartIndex = new ULONG(startIndex);
ULONGByReference pulIndex = new ULONGByReference();
ULONGLONGByReference pullBase = new ULONGLONGByReference();
COMUtils.checkRC(jnaSymbols.GetModuleByModuleName(name, ulStartIndex, pulIndex, pullBase));
return new DebugModuleImpl(this, pulIndex.getValue().intValue(),
pullBase.getValue().longValue());
}
@Override
public DebugModule getModuleByOffset(long offset, int startIndex) {
ULONGLONG ullOffset = new ULONGLONG(offset);
ULONG ulStartIndex = new ULONG(startIndex);
ULONGByReference pulIndex = new ULONGByReference();
ULONGLONGByReference pullBase = new ULONGLONGByReference();
COMUtils.checkRC(jnaSymbols.GetModuleByOffset(ullOffset, ulStartIndex, pulIndex, pullBase));
return new DebugModuleImpl(this, pulIndex.getValue().intValue(),
pullBase.getValue().longValue());
}
protected void callNamesForWhich(DebugModuleName which, ULONG Index, ULONGLONG Base,
byte[] Buffer, ULONG BufferSize, ULONGByReference NameSize) {
switch (which) {
case IMAGE:
COMUtils.checkRC(jnaSymbols.GetModuleNames(Index, Base, Buffer, BufferSize,
NameSize, null, new ULONG(0), null, null, new ULONG(0), null));
case MODULE:
COMUtils.checkRC(jnaSymbols.GetModuleNames(Index, Base, null, new ULONG(0), null,
Buffer, BufferSize, NameSize, null, new ULONG(0), null));
case LOADED_IMAGE:
COMUtils.checkRC(jnaSymbols.GetModuleNames(Index, Base, null, new ULONG(0), null,
null, new ULONG(0), null, Buffer, BufferSize, NameSize));
default:
throw new UnsupportedOperationException("Interface does not support " + which);
}
}
@Override
public String getModuleName(DebugModuleName which, DebugModule module) {
ULONGLONG ullBase = new ULONGLONG(module.getBase());
ULONGByReference pulNameSize = new ULONGByReference();
callNamesForWhich(which, DbgEngUtil.DEBUG_ANY_ID, ullBase, null, new ULONG(0), pulNameSize);
byte[] aBuffer = new byte[pulNameSize.getValue().intValue()];
callNamesForWhich(which, DbgEngUtil.DEBUG_ANY_ID, ullBase, aBuffer, pulNameSize.getValue(),
null);
return Native.toString(aBuffer);
}
@Override
public Iterable<DebugSymbolName> iterateSymbolMatches(String pattern) {
ULONGLONGByReference pullHandle = new ULONGLONGByReference();
return new Iterable<DebugSymbolName>() {
@Override
public Iterator<DebugSymbolName> iterator() {
COMUtils.checkRC(jnaSymbols.StartSymbolMatch(pattern, pullHandle));
return new Iterator<DebugSymbolName>() {
ULONGByReference pulMatchSize = new ULONGByReference();
ULONGLONGByReference pullOffset = new ULONGLONGByReference();
@Override
public boolean hasNext() {
try {
COMUtils.checkRC(jnaSymbols.GetNextSymbolMatch(pullHandle.getValue(),
null, new ULONG(0), pulMatchSize, null));
}
catch (COMException e) {
if (!COMUtilsExtra.isE_NOINTERFACE(e)) {
throw e;
}
return false;
}
return true;
}
@Override
public DebugSymbolName next() {
try {
if (pulMatchSize.getValue().intValue() == 0) {
COMUtils.checkRC(jnaSymbols.GetNextSymbolMatch(
pullHandle.getValue(), null, new ULONG(0), pulMatchSize, null));
}
byte[] aBuffer = new byte[pulMatchSize.getValue().intValue()];
COMUtils.checkRC(jnaSymbols.GetNextSymbolMatch(pullHandle.getValue(),
aBuffer, pulMatchSize.getValue(), null, pullOffset));
return new DebugSymbolName(Native.toString(aBuffer),
pullOffset.getValue().longValue());
}
catch (COMException e) {
if (!COMUtilsExtra.isE_NOINTERFACE(e)) {
throw e;
}
return null;
}
finally {
pulMatchSize.getValue().setValue(0);
}
}
@Override
protected void finalize() throws Throwable {
COMUtils.checkRC(jnaSymbols.EndSymbolMatch(pullHandle.getValue()));
}
};
}
};
}
@Override
public List<DebugSymbolId> getSymbolIdsByName(String pattern) {
throw new UnsupportedOperationException("Not supported by this interface");
}
@Override
public DebugSymbolEntry getSymbolEntry(DebugSymbolId id) {
throw new UnsupportedOperationException("Not supported by this interface");
}
@Override
public String getSymbolPath() {
ULONGByReference pulPathLength = new ULONGByReference();
COMUtils.checkRC(jnaSymbols.GetSymbolPath(null, new ULONG(0), pulPathLength));
byte[] aBuffer = new byte[pulPathLength.getValue().intValue()];
COMUtils.checkRC(jnaSymbols.GetSymbolPath(aBuffer, pulPathLength.getValue(), null));
return Native.toString(aBuffer);
}
@Override
public void setSymbolPath(String path) {
//WString wPath = new WString(path);
COMUtils.checkRC(jnaSymbols.SetSymbolPath(path));
}
@Override
public int getSymbolOptions() {
ULONGByReference pulOptions = new ULONGByReference();
COMUtils.checkRC(jnaSymbols.GetSymbolPath(null, new ULONG(0), pulOptions));
return pulOptions.getValue().intValue();
}
@Override
public void setSymbolOptions(int options) {
ULONG ulOptions = new ULONG(options);
COMUtils.checkRC(jnaSymbols.SetSymbolOptions(ulOptions));
}
}

View file

@ -0,0 +1,48 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.symbols;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.WinDef.*;
import agent.dbgeng.dbgeng.DebugModule;
import agent.dbgeng.dbgeng.DebugModule.DebugModuleName;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.jna.dbgeng.symbols.IDebugSymbols2;
import com.sun.jna.platform.win32.COM.COMUtils;
public class DebugSymbolsImpl2 extends DebugSymbolsImpl1 {
private final IDebugSymbols2 jnaSymbols;
public DebugSymbolsImpl2(IDebugSymbols2 jnaSymbols) {
super(jnaSymbols);
this.jnaSymbols = jnaSymbols;
}
@Override
public String getModuleName(DebugModuleName which, DebugModule module) {
ULONG ulWhich = new ULONG(which.ordinal());
ULONGLONG ullBase = new ULONGLONG(module.getBase());
ULONGByReference pulNameSize = new ULONGByReference();
COMUtils.checkRC(jnaSymbols.GetModuleNameString(ulWhich, DbgEngUtil.DEBUG_ANY_ID, ullBase,
null, new ULONG(0), pulNameSize));
byte[] aBuffer = new byte[pulNameSize.getValue().intValue()];
COMUtils.checkRC(jnaSymbols.GetModuleNameString(ulWhich, DbgEngUtil.DEBUG_ANY_ID, ullBase,
aBuffer, pulNameSize.getValue(), null));
return Native.toString(aBuffer);
}
}

View file

@ -0,0 +1,101 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.symbols;
import java.util.*;
import com.sun.jna.Native;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinDef.*;
import agent.dbgeng.dbgeng.*;
import agent.dbgeng.dbgeng.DebugModule.DebugModuleName;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_MODULE_AND_ID;
import agent.dbgeng.jna.dbgeng.DbgEngNative.DEBUG_SYMBOL_ENTRY;
import agent.dbgeng.jna.dbgeng.symbols.IDebugSymbols3;
import com.sun.jna.platform.win32.COM.COMUtils;
public class DebugSymbolsImpl3 extends DebugSymbolsImpl2 {
private final IDebugSymbols3 jnaSymbols;
public DebugSymbolsImpl3(IDebugSymbols3 jnaSymbols) {
super(jnaSymbols);
this.jnaSymbols = jnaSymbols;
}
@Override
public DebugModule getModuleByModuleName(String name, int startIndex) {
ULONG ulStartIndex = new ULONG(startIndex);
ULONGByReference pulIndex = new ULONGByReference();
ULONGLONGByReference pullBase = new ULONGLONGByReference();
COMUtils.checkRC(jnaSymbols.GetModuleByModuleNameWide(new WString(name), ulStartIndex,
pulIndex, pullBase));
return new DebugModuleImpl(this, pulIndex.getValue().intValue(),
pullBase.getValue().longValue());
}
@Override
public String getModuleName(DebugModuleName which, DebugModule module) {
ULONG ulWhich = new ULONG(which.ordinal());
ULONGLONG ullBase = new ULONGLONG(module.getBase());
ULONGByReference pulNameSize = new ULONGByReference();
COMUtils.checkRC(jnaSymbols.GetModuleNameStringWide(ulWhich, DbgEngUtil.DEBUG_ANY_ID,
ullBase, null, new ULONG(0), pulNameSize));
char[] aBuffer = new char[pulNameSize.getValue().intValue()];
COMUtils.checkRC(jnaSymbols.GetModuleNameStringWide(ulWhich, DbgEngUtil.DEBUG_ANY_ID,
ullBase, aBuffer, pulNameSize.getValue(), null));
return Native.toString(aBuffer);
}
@Override
public List<DebugSymbolId> getSymbolIdsByName(String pattern) {
ULONGByReference pulEntries = new ULONGByReference();
WString wsPattern = new WString(pattern);
COMUtils.checkRC(jnaSymbols.GetSymbolEntriesByNameWide(wsPattern, new ULONG(0), null,
new ULONG(0), pulEntries));
if (pulEntries.getValue().intValue() == 0) {
return Collections.emptyList();
}
DEBUG_MODULE_AND_ID[] aIds = (DEBUG_MODULE_AND_ID[]) new DEBUG_MODULE_AND_ID()
.toArray(pulEntries.getValue().intValue());
COMUtils.checkRC(jnaSymbols.GetSymbolEntriesByNameWide(wsPattern, new ULONG(0), aIds,
pulEntries.getValue(), null));
List<DebugSymbolId> result = new ArrayList<>(aIds.length);
for (int i = 0; i < aIds.length; i++) {
result.add(new DebugSymbolId(aIds[i].ModuleBase.longValue(), aIds[i].Id.longValue()));
}
return result;
}
@Override
public DebugSymbolEntry getSymbolEntry(DebugSymbolId id) {
DEBUG_MODULE_AND_ID sId = new DEBUG_MODULE_AND_ID();
sId.ModuleBase = new ULONGLONG(id.moduleBase);
sId.Id = new ULONGLONG(id.symbolIndex);
DEBUG_SYMBOL_ENTRY.ByReference pInfo = new DEBUG_SYMBOL_ENTRY.ByReference();
COMUtils.checkRC(jnaSymbols.GetSymbolEntryInformation(sId, pInfo));
// Get the name while I'm here
char[] aName = new char[pInfo.NameSize.intValue() + 1];
COMUtils.checkRC(jnaSymbols.GetSymbolEntryStringWide(sId, new ULONG(0), aName,
new ULONG(aName.length), null));
return new DebugSymbolEntry(pInfo.ModuleBase.longValue(), pInfo.Offset.longValue(),
pInfo.Id.longValue(), pInfo.Size.longValue(), pInfo.Flags.intValue(),
pInfo.TypeId.intValue()/* TODO */, Native.toString(aName),
pInfo.Tag.intValue()/* TODO */);
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.symbols;
import agent.dbgeng.jna.dbgeng.symbols.IDebugSymbols4;
public class DebugSymbolsImpl4 extends DebugSymbolsImpl3 {
@SuppressWarnings("unused")
private final IDebugSymbols4 jnaSymbols;
public DebugSymbolsImpl4(IDebugSymbols4 jnaSymbols) {
super(jnaSymbols);
this.jnaSymbols = jnaSymbols;
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.symbols;
import agent.dbgeng.jna.dbgeng.symbols.IDebugSymbols5;
public class DebugSymbolsImpl5 extends DebugSymbolsImpl4 {
@SuppressWarnings("unused")
private final IDebugSymbols5 jnaSymbols;
public DebugSymbolsImpl5(IDebugSymbols5 jnaSymbols) {
super(jnaSymbols);
this.jnaSymbols = jnaSymbols;
}
}

View file

@ -0,0 +1,72 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.symbols;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import agent.dbgeng.dbgeng.DebugModule;
import agent.dbgeng.dbgeng.DebugSymbols;
import agent.dbgeng.dbgeng.DebugModule.DebugModuleName;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier;
import agent.dbgeng.jna.dbgeng.symbols.*;
import ghidra.util.datastruct.WeakValueHashMap;
public interface DebugSymbolsInternal extends DebugSymbols {
final Map<Pointer, DebugSymbolsInternal> CACHE = new WeakValueHashMap<>();
static DebugSymbolsInternal instanceFor(WrapIDebugSymbols symbols) {
return DbgEngUtil.lazyWeakCache(CACHE, symbols, DebugSymbolsImpl1::new);
}
static DebugSymbolsInternal instanceFor(WrapIDebugSymbols2 symbols) {
return DbgEngUtil.lazyWeakCache(CACHE, symbols, DebugSymbolsImpl2::new);
}
static DebugSymbolsInternal instanceFor(WrapIDebugSymbols3 symbols) {
return DbgEngUtil.lazyWeakCache(CACHE, symbols, DebugSymbolsImpl3::new);
}
static DebugSymbolsInternal instanceFor(WrapIDebugSymbols4 symbols) {
return DbgEngUtil.lazyWeakCache(CACHE, symbols, DebugSymbolsImpl4::new);
}
static DebugSymbolsInternal instanceFor(WrapIDebugSymbols5 symbols) {
return DbgEngUtil.lazyWeakCache(CACHE, symbols, DebugSymbolsImpl5::new);
}
ImmutableMap.Builder<REFIID, Class<? extends WrapIDebugSymbols>> PREFERRED_SYMBOLS_IIDS_BUILDER =
ImmutableMap.builder();
Map<REFIID, Class<? extends WrapIDebugSymbols>> PREFFERED_SYMBOLS_IIDS =
PREFERRED_SYMBOLS_IIDS_BUILDER //
.put(new REFIID(IDebugSymbols5.IID_IDEBUG_SYMBOLS5), WrapIDebugSymbols5.class) //
.put(new REFIID(IDebugSymbols4.IID_IDEBUG_SYMBOLS4), WrapIDebugSymbols4.class) //
.put(new REFIID(IDebugSymbols3.IID_IDEBUG_SYMBOLS3), WrapIDebugSymbols3.class) //
.put(new REFIID(IDebugSymbols2.IID_IDEBUG_SYMBOLS2), WrapIDebugSymbols2.class) //
.put(new REFIID(IDebugSymbols.IID_IDEBUG_SYMBOLS), WrapIDebugSymbols.class) //
.build();
static DebugSymbolsInternal tryPreferredInterfaces(InterfaceSupplier supplier) {
return DbgEngUtil.tryPreferredInterfaces(DebugSymbolsInternal.class, PREFFERED_SYMBOLS_IIDS,
supplier);
}
String getModuleName(DebugModuleName which, DebugModule module);
}

View file

@ -0,0 +1,249 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.sysobj;
import java.util.*;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.COMUtils;
import agent.dbgeng.dbgeng.*;
import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable;
import agent.dbgeng.jna.dbgeng.sysobj.IDebugSystemObjects;
public class DebugSystemObjectsImpl1 implements DebugSystemObjectsInternal {
@SuppressWarnings("unused")
private final OpaqueCleanable cleanable;
private final IDebugSystemObjects jnaSysobj;
public DebugSystemObjectsImpl1(IDebugSystemObjects jnaSysobj) {
this.cleanable = DbgEng.releaseWhenPhantom(this, jnaSysobj);
this.jnaSysobj = jnaSysobj;
}
@Override
public DebugThreadId getEventThread() {
ULONGByReference pulId = new ULONGByReference();
HRESULT hr = jnaSysobj.GetEventThread(pulId);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
return new DebugThreadId(-1);
}
COMUtils.checkRC(hr);
return new DebugThreadId(pulId.getValue().intValue());
}
@Override
public DebugProcessId getEventProcess() {
ULONGByReference pulId = new ULONGByReference();
HRESULT hr = jnaSysobj.GetEventProcess(pulId);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
return new DebugProcessId(-1);
}
COMUtils.checkRC(hr);
return new DebugProcessId(pulId.getValue().intValue());
}
@Override
public DebugThreadId getCurrentThreadId() {
ULONGByReference pulId = new ULONGByReference();
HRESULT hr = jnaSysobj.GetCurrentThreadId(pulId);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
return new DebugThreadId(-1);
}
COMUtils.checkRC(hr);
return new DebugThreadId(pulId.getValue().intValue());
}
@Override
public void setCurrentThreadId(DebugThreadId id) {
HRESULT hr = jnaSysobj.SetCurrentThreadId(new ULONG(id.id));
if (!hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
COMUtils.checkRC(hr);
}
}
@Override
public DebugProcessId getCurrentProcessId() {
ULONGByReference pulId = new ULONGByReference();
HRESULT hr = jnaSysobj.GetCurrentProcessId(pulId);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
return new DebugProcessId(-1);
}
COMUtils.checkRC(hr);
return new DebugProcessId(pulId.getValue().intValue());
}
@Override
public void setCurrentProcessId(DebugProcessId id) {
HRESULT hr = jnaSysobj.SetCurrentProcessId(new ULONG(id.id));
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
//System.err.println("Failure on setCurrentProcessId(" + id + ")");
return;
}
if (hr.equals(COMUtilsExtra.E_NOINTERFACE)) {
return;
}
COMUtils.checkRC(hr);
}
@Override
public int getNumberThreads() {
ULONGByReference pulNumber = new ULONGByReference();
COMUtils.checkRC(jnaSysobj.GetNumberThreads(pulNumber));
return pulNumber.getValue().intValue();
}
@Override
public int getTotalNumberThreads() {
ULONGByReference pulTotal = new ULONGByReference();
ULONGByReference pulLargestProcess = new ULONGByReference();
COMUtils.checkRC(jnaSysobj.GetTotalNumberThreads(pulTotal, pulLargestProcess));
return pulTotal.getValue().intValue();
}
@Override
public List<DebugThreadId> getThreads(int start, int count) {
if (count == 0) {
return Collections.emptyList();
}
// TODO: Does dbgeng do the bounds checking?
ULONG ulStart = new ULONG(start);
ULONG ulCount = new ULONG(count);
ULONG[] aulIds = new ULONG[count];
COMUtils.checkRC(jnaSysobj.GetThreadIdsByIndex(ulStart, ulCount, aulIds, null));
List<DebugThreadId> result = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
result.add(new DebugThreadId(aulIds[i].intValue()));
}
return result;
}
@Override
public DebugThreadId getThreadIdByHandle(long handle) {
ULONGLONG ullHandle = new ULONGLONG(handle);
ULONGByReference pulId = new ULONGByReference();
COMUtils.checkRC(jnaSysobj.GetThreadIdByHandle(ullHandle, pulId));
return new DebugThreadId(pulId.getValue().intValue());
}
@Override
public DebugThreadId getThreadIdBySystemId(int systemId) {
ULONG ulHandle = new ULONG(systemId);
ULONGByReference pulId = new ULONGByReference();
HRESULT hr = jnaSysobj.GetThreadIdBySystemId(ulHandle, pulId);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED) || hr.equals(COMUtilsExtra.E_NOTIMPLEMENTED)) {
return null;
}
COMUtils.checkRC(hr);
return new DebugThreadId(pulId.getValue().intValue());
}
@Override
public DebugProcessId getProcessIdByHandle(long handle) {
ULONGLONG ullHandle = new ULONGLONG(handle);
ULONGByReference pulId = new ULONGByReference();
COMUtils.checkRC(jnaSysobj.GetProcessIdByHandle(ullHandle, pulId));
return new DebugProcessId(pulId.getValue().intValue());
}
@Override
public DebugProcessId getProcessIdBySystemId(int systemId) {
ULONG ulHandle = new ULONG(systemId);
ULONGByReference pulId = new ULONGByReference();
HRESULT hr = jnaSysobj.GetProcessIdBySystemId(ulHandle, pulId);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED) || hr.equals(COMUtilsExtra.E_NOTIMPLEMENTED)) {
return null;
}
COMUtils.checkRC(hr);
return new DebugProcessId(pulId.getValue().intValue());
}
@Override
public int getNumberProcesses() {
ULONGByReference pulNumber = new ULONGByReference();
HRESULT hr = jnaSysobj.GetNumberProcesses(pulNumber);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
return -1;
}
COMUtils.checkRC(hr);
return pulNumber.getValue().intValue();
}
@Override
public List<DebugProcessId> getProcesses(int start, int count) {
if (count == 0) {
return Collections.emptyList();
}
// TODO: Does dbgeng do the bounds checking?
ULONG ulStart = new ULONG(start);
ULONG ulCount = new ULONG(count);
ULONG[] aulIds = new ULONG[count];
COMUtils.checkRC(jnaSysobj.GetProcessIdsByIndex(ulStart, ulCount, aulIds, null));
List<DebugProcessId> result = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
result.add(new DebugProcessId(aulIds[i].intValue()));
}
return result;
}
@Override
public int getCurrentThreadSystemId() {
ULONGByReference pulSysId = new ULONGByReference();
HRESULT hr = jnaSysobj.GetCurrentThreadSystemId(pulSysId);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED) || hr.equals(COMUtilsExtra.E_NOTIMPLEMENTED)) {
return -1;
}
COMUtils.checkRC(hr);
return pulSysId.getValue().intValue();
}
@Override
public int getCurrentProcessSystemId() {
ULONGByReference pulSysId = new ULONGByReference();
HRESULT hr = jnaSysobj.GetCurrentProcessSystemId(pulSysId);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED) || hr.equals(COMUtilsExtra.E_NOTIMPLEMENTED)) {
return -1;
}
COMUtils.checkRC(hr);
return pulSysId.getValue().intValue();
}
@Override
public DebugSessionId getEventSystem() {
throw new UnsupportedOperationException("Not supported by this interface");
}
@Override
public DebugSessionId getCurrentSystemId() {
throw new UnsupportedOperationException("Not supported by this interface");
}
@Override
public void setCurrentSystemId(DebugSessionId id) {
throw new UnsupportedOperationException("Not supported by this interface");
}
@Override
public int getNumberSystems() {
throw new UnsupportedOperationException("Not supported by this interface");
}
@Override
public List<DebugSessionId> getSystems(int start, int count) {
throw new UnsupportedOperationException("Not supported by this interface");
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.sysobj;
import agent.dbgeng.jna.dbgeng.sysobj.IDebugSystemObjects2;
public class DebugSystemObjectsImpl2 extends DebugSystemObjectsImpl1 {
@SuppressWarnings("unused")
private final IDebugSystemObjects2 jnaSysobj;
public DebugSystemObjectsImpl2(IDebugSystemObjects2 jnaSysobj) {
super(jnaSysobj);
this.jnaSysobj = jnaSysobj;
}
}

View file

@ -0,0 +1,96 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.sysobj;
import java.util.*;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinDef.ULONGByReference;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.COMUtils;
import agent.dbgeng.dbgeng.COMUtilsExtra;
import agent.dbgeng.dbgeng.DebugSessionId;
import agent.dbgeng.jna.dbgeng.sysobj.IDebugSystemObjects3;
public class DebugSystemObjectsImpl3 extends DebugSystemObjectsImpl2 {
@SuppressWarnings("unused")
private final IDebugSystemObjects3 jnaSysobj;
public DebugSystemObjectsImpl3(IDebugSystemObjects3 jnaSysobj) {
super(jnaSysobj);
this.jnaSysobj = jnaSysobj;
}
@Override
public DebugSessionId getEventSystem() {
ULONGByReference pulId = new ULONGByReference();
HRESULT hr = jnaSysobj.GetEventSystem(pulId);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
return new DebugSessionId(-1);
}
COMUtils.checkRC(hr);
return new DebugSessionId(pulId.getValue().intValue());
}
@Override
public DebugSessionId getCurrentSystemId() {
ULONGByReference pulId = new ULONGByReference();
HRESULT hr = jnaSysobj.GetCurrentSystemId(pulId);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
return new DebugSessionId(-1);
}
COMUtils.checkRC(hr);
return new DebugSessionId(pulId.getValue().intValue());
}
@Override
public void setCurrentSystemId(DebugSessionId id) {
HRESULT hr = jnaSysobj.SetCurrentSystemId(new ULONG(id.id));
if (!hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
COMUtils.checkRC(hr);
}
}
@Override
public int getNumberSystems() {
ULONGByReference pulNumber = new ULONGByReference();
HRESULT hr = jnaSysobj.GetNumberSystems(pulNumber);
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
return -1;
}
COMUtils.checkRC(hr);
return pulNumber.getValue().intValue();
}
@Override
public List<DebugSessionId> getSystems(int start, int count) {
if (count == 0) {
return Collections.emptyList();
}
// TODO: Does dbgeng do the bounds checking?
ULONG ulStart = new ULONG(start);
ULONG ulCount = new ULONG(count);
ULONG[] aulIds = new ULONG[count];
COMUtils.checkRC(jnaSysobj.GetSystemIdsByIndex(ulStart, ulCount, aulIds, null));
List<DebugSessionId> result = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
result.add(new DebugSessionId(aulIds[i].intValue()));
}
return result;
}
}

View file

@ -0,0 +1,28 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.sysobj;
import agent.dbgeng.jna.dbgeng.sysobj.IDebugSystemObjects4;
public class DebugSystemObjectsImpl4 extends DebugSystemObjectsImpl3 {
@SuppressWarnings("unused")
private final IDebugSystemObjects4 jnaSysobj;
public DebugSystemObjectsImpl4(IDebugSystemObjects4 jnaSysobj) {
super(jnaSysobj);
this.jnaSysobj = jnaSysobj;
}
}

View file

@ -0,0 +1,67 @@
/* ###
* 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 agent.dbgeng.impl.dbgeng.sysobj;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.REFIID;
import agent.dbgeng.dbgeng.DebugSystemObjects;
import agent.dbgeng.impl.dbgeng.DbgEngUtil;
import agent.dbgeng.impl.dbgeng.DbgEngUtil.InterfaceSupplier;
import agent.dbgeng.jna.dbgeng.sysobj.*;
import ghidra.util.datastruct.WeakValueHashMap;
public interface DebugSystemObjectsInternal extends DebugSystemObjects {
Map<Pointer, DebugSystemObjectsInternal> CACHE = new WeakValueHashMap<>();
static DebugSystemObjectsInternal instanceFor(WrapIDebugSystemObjects sysobj) {
return DbgEngUtil.lazyWeakCache(CACHE, sysobj, DebugSystemObjectsImpl1::new);
}
static DebugSystemObjectsInternal instanceFor(WrapIDebugSystemObjects2 sysobj) {
return DbgEngUtil.lazyWeakCache(CACHE, sysobj, DebugSystemObjectsImpl2::new);
}
static DebugSystemObjectsInternal instanceFor(WrapIDebugSystemObjects3 sysobj) {
return DbgEngUtil.lazyWeakCache(CACHE, sysobj, DebugSystemObjectsImpl3::new);
}
static DebugSystemObjectsInternal instanceFor(WrapIDebugSystemObjects4 sysobj) {
return DbgEngUtil.lazyWeakCache(CACHE, sysobj, DebugSystemObjectsImpl4::new);
}
ImmutableMap.Builder<REFIID, Class<? extends WrapIDebugSystemObjects>> PREFERRED_SYSTEM_OBJECTS_IIDS_BUILDER =
ImmutableMap.builder();
Map<REFIID, Class<? extends WrapIDebugSystemObjects>> PREFERRED_SYSTEM_OBJECTS_IIDS =
PREFERRED_SYSTEM_OBJECTS_IIDS_BUILDER //
.put(new REFIID(IDebugSystemObjects4.IID_IDEBUG_SYSTEM_OBJECTS4),
WrapIDebugSystemObjects4.class) //
.put(new REFIID(IDebugSystemObjects3.IID_IDEBUG_SYSTEM_OBJECTS3),
WrapIDebugSystemObjects3.class) //
.put(new REFIID(IDebugSystemObjects2.IID_IDEBUG_SYSTEM_OBJECTS2),
WrapIDebugSystemObjects2.class) //
.put(new REFIID(IDebugSystemObjects.IID_IDEBUG_SYSTEM_OBJECTS),
WrapIDebugSystemObjects.class) //
.build();
static DebugSystemObjectsInternal tryPreferredInterfaces(InterfaceSupplier supplier) {
return DbgEngUtil.tryPreferredInterfaces(DebugSystemObjectsInternal.class,
PREFERRED_SYSTEM_OBJECTS_IIDS, supplier);
}
}

View file

@ -0,0 +1,432 @@
/* ###
* 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 agent.dbgeng.jna.dbgeng;
import java.util.List;
import com.sun.jna.*;
import com.sun.jna.platform.win32.Guid.REFIID;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.ptr.PointerByReference;
import com.sun.jna.win32.StdCallLibrary;
import agent.dbgeng.dbgeng.DebugValue;
import agent.dbgeng.dbgeng.DebugValue.*;
public interface DbgEngNative extends StdCallLibrary {
//DbgEngNative INSTANCE = Native.loadLibrary("dbgeng", DbgEngNative.class);
DbgEngNative INSTANCE = Native.load("dbgeng.dll", DbgEngNative.class);
HRESULT DebugConnect(String RemoteOptions, REFIID InterfaceId, PointerByReference Interface);
HRESULT DebugConnectWide(WString RemoteOptions, REFIID InterfaceId,
PointerByReference Interface);
HRESULT DebugCreate(REFIID InterfaceId, PointerByReference Interface);
HRESULT DebugCreateEx(REFIID InterfaceId, DWORD DbgEngOptions, PointerByReference Interface);
public static class DEBUG_BREAKPOINT_PARAMETERS extends Structure {
public static class ByReference extends DEBUG_BREAKPOINT_PARAMETERS
implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder("Offset", "Id", "BreakType",
"ProcType", "Flags", "DataSize", "DataAccessType", "PassCount", "CurrentPassCount",
"MatchThread", "CommandSize", "OffsetExpressionSize");
public ULONGLONG Offset;
public ULONG Id;
public ULONG BreakType;
public ULONG ProcType;
public ULONG Flags;
public ULONG DataSize;
public ULONG DataAccessType;
public ULONG PassCount;
public ULONG CurrentPassCount;
public ULONG MatchThread;
public ULONG CommandSize;
public ULONG OffsetExpressionSize;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class DEBUG_REGISTER_DESCRIPTION extends Structure {
public static class ByReference extends DEBUG_REGISTER_DESCRIPTION
implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder("Type", "Flags", "SubregMaster",
"SubregLength", "SubregMask", "SubregShift", "Reserved0");
public ULONG Type;
public ULONG Flags;
public ULONG SubregMaster;
public ULONG SubregLength;
public ULONGLONG SubregMask;
public ULONG SubregShift;
public ULONG Reserved0;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class DEBUG_VALUE extends Structure {
public static class ByReference extends DEBUG_VALUE implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder("u", "TailOfRawBytes", "Type");
public static class UNION extends Union {
public static class ByReference extends UNION implements Structure.ByReference {
}
public static class INTEGER64 extends Structure {
public static class ByReference extends UNION implements Structure.ByReference {
}
@SuppressWarnings("hiding")
public static final List<String> FIELDS = createFieldsOrder("I64", "Nat");
public ULONGLONG I64;
public BOOL Nat; // Always false for non-Itanium
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class I64Parts32 extends Structure {
public static class ByReference extends UNION implements Structure.ByReference {
}
@SuppressWarnings("hiding")
public static final List<String> FIELDS = createFieldsOrder("LowPart", "HighPart");
public ULONG LowPart;
public ULONG HighPart;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class F128Parts64 extends Structure {
public static class ByReference extends UNION implements Structure.ByReference {
}
@SuppressWarnings("hiding")
public static final List<String> FIELDS = createFieldsOrder("LowPart", "HighPart");
public ULONGLONG LowPart;
public LONGLONG HighPart;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public UCHAR I8;
public USHORT I16;
public ULONG I32;
public INTEGER64 I64;
public float F32;
public double F64;
public byte[] F80Bytes = new byte[10];
public byte[] F82Bytes = new byte[11];
public byte[] F128Bytes = new byte[16];
public byte[] VI8 = new byte[16];
public short[] VI16 = new short[8];
public int[] VI32 = new int[4];
public long[] VI64 = new long[2];
public float[] VF32 = new float[4];
public double[] VF64 = new double[2];
public I64Parts32 I64Parts32;
public F128Parts64 F128Parts64;
public byte[] RawBytes = new byte[24];
}
public UNION u;
public ULONG TailOfRawBytes;
public ULONG Type;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
protected void doUSetType() {
switch (DebugValueType.values()[Type.intValue()]) {
case INVALID:
u.setType("RawBytes");
break;
case INT8:
u.setType("I8");
break;
case INT16:
u.setType("I16");
break;
case INT32:
u.setType("I32");
break;
case INT64:
u.setType("I64");
break;
case FLOAT32:
u.setType("F32");
break;
case FLOAT64:
u.setType("F64");
break;
case FLOAT80:
u.setType("F80Bytes");
break;
case FLOAT82:
u.setType("F82Bytes");
break;
case FLOAT128:
u.setType("F128Bytes");
break;
case VECTOR64:
u.setType("VI8");
break;
case VECTOR128:
u.setType("VI8");
break; // TODO: Can I activate multiple types?
}
}
@Override
public void read() {
super.read();
doUSetType();
u.read();
}
@Override
public void write() {
super.write();
doUSetType();
u.write();
}
@SuppressWarnings("unchecked")
public <T extends DebugValue> T convertTo(Class<T> desiredType) {
if (desiredType != null && desiredType != DebugValue.class &&
DebugValueType.getDebugValueTypeForClass(
desiredType).ordinal() != Type.intValue()) {
// TODO: Display value in exception
throw new ClassCastException("debug value is not of the desired type");
}
switch (DebugValueType.values()[Type.intValue()]) {
case INVALID:
return null; // TODO: Some DebugInvalidWrapper class?
case INT8:
return (T) new DebugInt8Value(u.I8.byteValue());
case INT16:
return (T) new DebugInt16Value(u.I16.shortValue());
case INT32:
return (T) new DebugInt32Value(u.I32.intValue());
case INT64:
return (T) new DebugInt64Value(u.I64.I64.longValue());
case FLOAT32:
return (T) new DebugFloat32Value(u.F32);
case FLOAT64:
return (T) new DebugFloat64Value(u.F64);
case FLOAT80:
return (T) new DebugFloat80Value(u.F80Bytes);
case FLOAT82:
return (T) new DebugFloat82Value(u.F82Bytes);
case FLOAT128:
return (T) new DebugFloat128Value(u.F128Bytes);
case VECTOR64:
return (T) new DebugVector128Value(u.VI8);
case VECTOR128:
return (T) new DebugVector128Value(u.VI8);
}
throw new AssertionError("INTERNAL: Shouldn't be here");
}
public static DEBUG_VALUE fromDebugValue(DebugValue value) {
DEBUG_VALUE result = new DEBUG_VALUE();
fromDebugValue(result, value);
return result;
}
public static void fromDebugValue(DEBUG_VALUE into, DebugValue value) {
DebugValueType type = DebugValueType.getDebugValueTypeForClass(value.getClass());
into.Type = new ULONG(type.ordinal());
switch (type) {
case INVALID:
break;
case INT8:
DebugInt8Value int8 = (DebugInt8Value) value;
into.u.I8 = new UCHAR(int8.byteValue());
break;
case INT16:
DebugInt16Value int16 = (DebugInt16Value) value;
into.u.I16 = new USHORT(int16.shortValue());
break;
case INT32:
DebugInt32Value int32 = (DebugInt32Value) value;
into.u.I32 = new ULONG(int32.intValue());
break;
case INT64:
DebugInt64Value int64 = (DebugInt64Value) value;
into.u.I64.I64 = new ULONGLONG(int64.longValue());
break;
case FLOAT32:
DebugFloat32Value float32 = (DebugFloat32Value) value;
into.u.F32 = float32.floatValue();
break;
case FLOAT64:
DebugFloat64Value float64 = (DebugFloat64Value) value;
into.u.F64 = float64.doubleValue();
break;
case FLOAT80:
DebugFloat80Value float80 = (DebugFloat80Value) value;
into.u.F80Bytes = float80.bytes();
break;
case FLOAT82:
DebugFloat82Value float82 = (DebugFloat82Value) value;
into.u.F82Bytes = float82.bytes();
break;
case FLOAT128:
DebugFloat128Value float128 = (DebugFloat128Value) value;
into.u.F128Bytes = float128.bytes();
break;
case VECTOR64:
DebugVector64Value vector64 = (DebugVector64Value) value;
into.u.VI8 = vector64.vi4(); // TODO: Copy Into?
break;
case VECTOR128:
DebugVector128Value vector128 = (DebugVector128Value) value;
into.u.VI8 = vector128.vi8(); // TODO: Copy Into?
break;
default:
throw new AssertionError("INTERNAL: Shouldn't be here");
}
}
}
public class DEBUG_MODULE_AND_ID extends Structure {
public static class ByReference extends DEBUG_MODULE_AND_ID
implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder("ModuleBase", "Id");
public ULONGLONG ModuleBase;
public ULONGLONG Id;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public class DEBUG_SYMBOL_ENTRY extends Structure {
public static class ByReference extends DEBUG_SYMBOL_ENTRY
implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder("ModuleBase", "Offset", "Id",
"Arg64", "Size", "Flags", "TypeId", "NameSize", "Token", "Tag", "Arg32", "Reserved");
public ULONGLONG ModuleBase;
public ULONGLONG Offset;
public ULONGLONG Id;
public ULONGLONG Arg64;
public ULONG Size;
public ULONG Flags;
public ULONG TypeId;
public ULONG NameSize;
public ULONG Token;
public ULONG Tag;
public ULONG Arg32;
public ULONG Reserved;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public class DEBUG_THREAD_BASIC_INFORMATION extends Structure {
public static class ByReference extends DEBUG_THREAD_BASIC_INFORMATION
implements Structure.ByReference {
}
public static final List<String> FIELDS =
createFieldsOrder("Valid", "ExitStatus", "PriorityClass", "Priority", "CreateTime",
"ExitTime", "KernelTime", "UserTime", "StartOffset", "Affinity");
public ULONG Valid;
public ULONG ExitStatus;
public ULONG PriorityClass;
public ULONG Priority;
public ULONGLONG CreateTime;
public ULONGLONG ExitTime;
public ULONGLONG KernelTime;
public ULONGLONG UserTime;
public ULONGLONG StartOffset;
public ULONGLONG Affinity;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public class DEBUG_STACK_FRAME extends Structure {
public static class ByReference extends DEBUG_STACK_FRAME implements Structure.ByReference {
}
public static final List<String> FIELDS =
createFieldsOrder("InstructionOffset", "ReturnOffset", "FrameOffset", "StackOffset",
"FuncTableEntry", "Params", "Reserved", "Virtual", "FrameNumber");
public ULONGLONG InstructionOffset;
public ULONGLONG ReturnOffset;
public ULONGLONG FrameOffset;
public ULONGLONG StackOffset;
public ULONGLONG FuncTableEntry;
public ULONGLONG[] Params = new ULONGLONG[4];
public ULONGLONG[] Reserved = new ULONGLONG[6];
public BOOL Virtual;
public ULONG FrameNumber;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
}

View file

@ -0,0 +1,126 @@
/* ###
* 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 agent.dbgeng.jna.dbgeng;
import java.util.List;
import com.sun.jna.*;
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;
import agent.dbgeng.jna.dbgeng.WinNTExtra.EXCEPTION_POINTERS;
public interface Kernel32Extra extends StdCallLibrary {
Kernel32Extra INSTANCE =
Native.loadLibrary("kernel32", Kernel32Extra.class, W32APIOptions.DEFAULT_OPTIONS);
interface VectoredHandlerCallback extends StdCallCallback {
LONG EXCEPTION_CONTINUE_EXECUTION = new LONG(0xffffffff);
LONG EXCEPTION_CONTINUE_SEARCH = new LONG(0x0);
LONG invoke(EXCEPTION_POINTERS.ByReference ExceptionInfo);
}
interface HandlerRoutineCallback extends StdCallCallback {
int CTRL_C_EVENT = 0;
int CTRL_CLOSE_EVENT = 2;
int CTRL_LOGOFF_EVENT = 5;
int CTRL_SHUTDOWN_EVENT = 6;
boolean invoke(DWORD dwCtrlType);
}
public static class PROCESSENTRY32W extends Structure {
public static class ByReference extends PROCESSENTRY32W implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder("dwSize", "cntUsage",
"th32ProcessID", "th32DefaultHeapID", "th32ModuleID", "cntThreads",
"th32ParentProcessID", "pcPriClassBase", "dwFlags", "szExeFile");
public DWORD dwSize;
public DWORD cntUsage;
public DWORD th32ProcessID;
public ULONG_PTR th32DefaultHeapID;
public DWORD th32ModuleID;
public DWORD cntThreads;
public DWORD th32ParentProcessID;
public DWORD pcPriClassBase;
public DWORD dwFlags;
public char[] szExeFile = new char[WinDef.MAX_PATH];
public PROCESSENTRY32W() {
dwSize = new DWORD(size());
}
public PROCESSENTRY32W(Pointer p) {
super(p);
read();
}
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class THREADENTRY32 extends Structure {
public static class ByReference extends THREADENTRY32 implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder("dwSize", "cntUsage",
"th32ThreadID", "th32OwnerProcessID", "tpBasePri", "tpDeltaPri", "dwFlags");
public DWORD dwSize;
public DWORD cntUsage;
public DWORD th32ThreadID;
public DWORD th32OwnerProcessID;
public LONG tpBasePri;
public LONG tpDeltaPri;
public DWORD dwFlags;
public THREADENTRY32() {
dwSize = new DWORD(size());
}
public THREADENTRY32(Pointer p) {
super(p);
read();
}
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
Pointer AddVectoredExceptionHandler(ULONG FirstHandler,
Kernel32Extra.VectoredHandlerCallback VectoredHandler);
boolean SetConsoleCtrlHandler(HandlerRoutineCallback HandlerRoutine, boolean Add);
boolean Process32FirstW(HANDLE hSnapshot, PROCESSENTRY32W lppe);
boolean Process32NextW(HANDLE hSnapshot, PROCESSENTRY32W lppe);
boolean Thread32First(HANDLE hSnapshot, THREADENTRY32 lpte);
boolean Thread32Next(HANDLE hSnapshot, THREADENTRY32 lpte);
}

View file

@ -0,0 +1,107 @@
/* ###
* 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 agent.dbgeng.jna.dbgeng;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import com.sun.jna.platform.win32.*;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import agent.dbgeng.dbgeng.DbgEng;
import agent.dbgeng.dbgeng.DbgEng.OpaqueCleanable;
import agent.dbgeng.jna.dbgeng.Kernel32Extra.PROCESSENTRY32W;
import agent.dbgeng.jna.dbgeng.Kernel32Extra.THREADENTRY32;
import ghidra.comm.util.BitmaskSet;
import ghidra.comm.util.BitmaskUniverse;
public class ToolhelpUtil {
public static class Snapshot {
@SuppressWarnings("unused")
private final OpaqueCleanable cleanable;
private final HANDLE handle;
private Snapshot(HANDLE handle) {
this.cleanable = DbgEng.releaseWhenPhantom(this, handle);
this.handle = handle;
}
protected <T> List<T> getItems(Supplier<T> newStruct, BiFunction<HANDLE, T, Boolean> first,
BiFunction<HANDLE, T, Boolean> next) {
List<T> items = new ArrayList<>();
T entry = newStruct.get();
for (boolean has = first.apply(handle, entry); has; has = next.apply(handle, entry)) {
items.add(entry);
entry = newStruct.get();
}
int lastError = Kernel32.INSTANCE.GetLastError();
if (lastError != W32Errors.ERROR_SUCCESS &&
lastError != W32Errors.ERROR_NO_MORE_FILES) {
throw new Win32Exception(lastError);
}
return items;
}
public List<PROCESSENTRY32W> getProcesses() {
return getItems(PROCESSENTRY32W::new, Kernel32Extra.INSTANCE::Process32FirstW,
Kernel32Extra.INSTANCE::Process32NextW);
}
public List<THREADENTRY32> getThreads() {
return getItems(THREADENTRY32::new, Kernel32Extra.INSTANCE::Thread32First,
Kernel32Extra.INSTANCE::Thread32Next);
}
}
public static enum SnapshotFlags implements BitmaskUniverse {
HEAPLIST(Tlhelp32.TH32CS_SNAPHEAPLIST), //
PROCESS(Tlhelp32.TH32CS_SNAPPROCESS), //
THREAD(Tlhelp32.TH32CS_SNAPTHREAD), //
MODULE(Tlhelp32.TH32CS_SNAPMODULE), //
MODULE32(Tlhelp32.TH32CS_SNAPMODULE32), //
ALL(Tlhelp32.TH32CS_SNAPALL), //
INHERIT(Tlhelp32.TH32CS_INHERIT), //
;
SnapshotFlags(DWORD mask) {
this.mask = mask.intValue();
}
int mask;
@Override
public long getMask() {
return mask;
}
}
public static Snapshot createSnapshot(BitmaskSet<SnapshotFlags> flags, int processId) {
DWORD dwFlags = new DWORD(flags.getBitmask());
DWORD dwPID = new DWORD(processId);
HANDLE hSnap = Kernel32.INSTANCE.CreateToolhelp32Snapshot(dwFlags, dwPID);
if (hSnap == null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
return new Snapshot(hSnap);
}
}

View file

@ -0,0 +1,43 @@
/* ###
* 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 agent.dbgeng.jna.dbgeng;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import com.sun.jna.platform.win32.COM.Unknown;
public class UnknownWithUtils extends Unknown {
public static interface VTableIndex {
int getIndex();
public static <I extends Enum<I> & VTableIndex> int follow(Class<I> prev) {
I[] all = prev.getEnumConstants();
int start = all[0].getIndex() - all[0].ordinal();
return all.length + start;
}
}
public UnknownWithUtils() {
}
public UnknownWithUtils(Pointer pvInstance) {
super(pvInstance);
}
protected HRESULT _invokeHR(VTableIndex idx, Object... args) {
return (HRESULT) this._invokeNativeObject(idx.getIndex(), args, HRESULT.class);
}
}

View file

@ -0,0 +1,281 @@
/* ###
* 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 agent.dbgeng.jna.dbgeng;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import com.sun.jna.platform.win32.WinDef.*;
public class WinNTExtra {
public static enum Machine {
IMAGE_FILE_MACHINE_UNKNOWN(0, "Unknown"), //
IMAGE_FILE_MACHINE_I386(0x014c, "x86"), //
IMAGE_FILE_MACHINE_ARM(0x01c0, "ARM"), //
IMAGE_FILE_MACHINE_IA64(0x0200, "Itanium"), //
IMAGE_FILE_MACHINE_AMD64(0x8664, "AMD64 (K8)"), //
IMAGE_FILE_MACHINE_EBC(0x0EBC, "EFI"), //
;
public static Machine getByNumber(int val) {
for (Machine m : Machine.values()) {
if (m.val == val) {
return m;
}
}
return null;
}
Machine(int val, String description) {
this.val = val;
this.description = description;
}
public final int val;
public final String description;
}
public static class M128A extends Structure {
public static class ByReference extends M128A implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder("Low", "High");
public ULONGLONG Low;
public LONGLONG High;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class CONTEXT_AMD64 extends Structure {
public static class ByReference extends CONTEXT_AMD64 implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder(
"P1Home", "P2Home", "P3Home", "P4Home", "P5Home", "P6Home",
"ContextFlags", "MxCsr",
"SegCs", "SegDs", "SegEs", "SegFs", "SegGs", "SegSs", "EFlags",
"Dr0", "Dr1", "Dr2", "Dr3", "Dr6", "Dr7",
"Rax", "Rcx", "Rdx", "Rbx", "Rsp", "Rbp", "Rsi", "Rdi", "R8", "R9", "R10", "R11", "R12",
"R13", "R14", "R15",
"Rip",
"Header", "Legacy", "Xmm0", "Xmm1", "Xmm2", "Xmm3", "Xmm4", "Xmm5", "Xmm6", "Xmm7",
"Xmm8", "Xmm9", "Xmm10", "Xmm11", "Xmm12", "Xmm13", "Xmm14", "Xmm15",
"VectorRegister", "VectorControl",
"DebugControl", "LastBranchToRip", "LastBranchFromRip", "LastExceptionToRip",
"LastExceptionFromRip");
public DWORDLONG P1Home;
public DWORDLONG P2Home;
public DWORDLONG P3Home;
public DWORDLONG P4Home;
public DWORDLONG P5Home;
public DWORDLONG P6Home;
public DWORD ContextFlags;
public DWORD MxCsr;
public WORD SegCs;
public WORD SegDs;
public WORD SegEs;
public WORD SegFs;
public WORD SegGs;
public WORD SegSs;
public DWORD EFlags;
public DWORDLONG Dr0;
public DWORDLONG Dr1;
public DWORDLONG Dr2;
public DWORDLONG Dr3;
public DWORDLONG Dr6;
public DWORDLONG Dr7;
public DWORDLONG Rax;
public DWORDLONG Rcx;
public DWORDLONG Rdx;
public DWORDLONG Rbx;
public DWORDLONG Rsp;
public DWORDLONG Rbp;
public DWORDLONG Rsi;
public DWORDLONG Rdi;
public DWORDLONG R8;
public DWORDLONG R9;
public DWORDLONG R10;
public DWORDLONG R11;
public DWORDLONG R12;
public DWORDLONG R13;
public DWORDLONG R14;
public DWORDLONG R15;
public DWORDLONG Rip;
public M128A[] Header = new M128A[2];
public M128A[] Legacy = new M128A[8];
public M128A Xmm0;
public M128A Xmm1;
public M128A Xmm2;
public M128A Xmm3;
public M128A Xmm4;
public M128A Xmm5;
public M128A Xmm6;
public M128A Xmm7;
public M128A Xmm8;
public M128A Xmm9;
public M128A Xmm10;
public M128A Xmm11;
public M128A Xmm12;
public M128A Xmm13;
public M128A Xmm14;
public M128A Xmm15;
public M128A[] VectorRegister = new M128A[26];
public DWORDLONG VectorControl;
public DWORDLONG DebugControl;
public DWORDLONG LastBranchToRip;
public DWORDLONG LastBranchFromRip;
public DWORDLONG LastExceptionToRip;
public DWORDLONG LastExceptionFromRip;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class EXCEPTION_RECORD extends Structure {
public static class ByReference extends EXCEPTION_RECORD implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder("ExceptionCode",
"ExceptionFlags", "ExceptionRecord", "ExceptionAddress", "NumberParameters",
"__unusedAlignment", "ExceptionInformation");
public DWORD ExceptionCode;
public DWORD ExceptionFlags;
public EXCEPTION_RECORD.ByReference ExceptionRecord;
public Pointer ExceptionAddress;
public DWORD NumberParameters;
public ULONG_PTR ExceptionInformation[] = new ULONG_PTR[15];
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class EXCEPTION_RECORD32 extends Structure {
public static class ByReference extends EXCEPTION_RECORD64
implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder("ExceptionCode",
"ExceptionFlags", "ExceptionRecord", "ExceptionAddress", "NumberParameters",
"__unusedAlignment", "ExceptionInformation");
public DWORD ExceptionCode;
public DWORD ExceptionFlags;
public DWORD ExceptionRecord;
public DWORD ExceptionAddress;
public DWORD NumberParameters;
public DWORD __unusedAlignment;
public DWORD ExceptionInformation[] = new DWORD[15];
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class EXCEPTION_RECORD64 extends Structure {
public static class ByReference extends EXCEPTION_RECORD64
implements Structure.ByReference {
}
public static final List<String> FIELDS = createFieldsOrder("ExceptionCode",
"ExceptionFlags", "ExceptionRecord", "ExceptionAddress", "NumberParameters",
"__unusedAlignment", "ExceptionInformation");
public DWORD ExceptionCode;
public DWORD ExceptionFlags;
public DWORDLONG ExceptionRecord;
public DWORDLONG ExceptionAddress;
public DWORD NumberParameters;
public DWORD __unusedAlignment;
public DWORDLONG ExceptionInformation[] = new DWORDLONG[15];
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class EXCEPTION_POINTERS extends Structure {
public static class ByReference extends EXCEPTION_POINTERS
implements Structure.ByReference {
}
public static final List<String> FIELDS =
createFieldsOrder("ExceptionRecord", "ContextRecord");
public EXCEPTION_RECORD.ByReference ExceptionRecord;
public CONTEXT_AMD64.ByReference ContextRecord; // TODO: This should be fine for now
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
public static class MEMORY_BASIC_INFORMATION64 extends Structure {
public static class ByReference extends MEMORY_BASIC_INFORMATION64
implements Structure.ByReference {
}
public static final List<String> FIELDS =
createFieldsOrder("BaseAddress", "AllocationBase", "AllocationProtect", "__alignment1",
"RegionSize", "State", "Protect", "Type", "__alignment2");
public ULONGLONG BaseAddress;
public ULONGLONG AllocationBase;
public DWORD AllocationProtect;
public DWORD __alignment1;
public ULONGLONG RegionSize;
public DWORD State;
public DWORD Protect;
public DWORD Type;
public DWORD __alignment2;
@Override
protected List<String> getFieldOrder() {
return FIELDS;
}
}
}

View file

@ -0,0 +1,46 @@
/* ###
* 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 agent.dbgeng.jna.dbgeng.advanced;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.IID;
import com.sun.jna.platform.win32.WinDef.ULONG;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex;
import com.sun.jna.platform.win32.COM.IUnknown;
public interface IDebugAdvanced extends IUnknown {
final IID IID_IDEBUG_ADVANCED = new IID("f2df5f53-071f-47bd-9de6-5734c3fed689");
enum VTIndices implements VTableIndex {
GET_THREAD_CONTEXT, //
SET_THREAD_CONTEXT, //
;
static int start = 3;
@Override
public int getIndex() {
return this.ordinal() + start;
}
}
HRESULT GetThreadContext(Pointer Context, ULONG ContextSize);
HRESULT SetThreadContext(Pointer Context, ULONG ContextSize);
}

View file

@ -0,0 +1,60 @@
/* ###
* 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 agent.dbgeng.jna.dbgeng.advanced;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Guid.IID;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex;
public interface IDebugAdvanced2 extends IDebugAdvanced {
final IID IID_IDEBUG_ADVANCED2 = new IID("716d14c9-119b-4ba5-af1f-0890e672416a");
enum VTIndices2 implements VTableIndex {
REQUEST, //
GET_SOURCE_FILE_INFORMATION, //
FIND_SOURCE_FILE_AND_TOKEN, //
GET_SYMBOL_INFORMATION, //
GET_SYSTEM_OBJECT_INFORMATION, //
;
static int start = VTableIndex.follow(VTIndices.class);
@Override
public int getIndex() {
return this.ordinal() + start;
}
}
HRESULT Request(ULONG Request, Pointer InBuffer, ULONG InBuffserSize, Pointer OutBuffer,
ULONG OutBufferSize, ULONGByReference OutSize);
HRESULT GetSourceFileInformation(ULONG Which, String SourceFile, ULONGLONG Arg64, ULONG Arg32,
Pointer Buffer, ULONG BufferSize, ULONGByReference InfoSize);
HRESULT FindSourceFileAndToken(ULONG StartElement, ULONGLONG ModAddr, String File, ULONG Flags,
Pointer FileToken, ULONG FileTokenSize, ULONGByReference FoundElement, byte[] Buffer,
ULONG BufferSize, ULONGByReference FoundSize);
HRESULT GetSymbolInformation(ULONG Which, ULONGLONG Arg64, ULONG Arg32, Pointer Buffer,
ULONG BufferSize, ULONGByReference InfoSize, byte[] StringBuffer,
ULONG StringBufferSize, ULONGByReference StringSize);
HRESULT GetSystemObjectInformation(ULONG Which, ULONGLONG Arg64, ULONG Arg32, Pointer Buffer,
ULONG BufferSize, ULONGByReference InfoSize);
}

View file

@ -0,0 +1,53 @@
/* ###
* 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 agent.dbgeng.jna.dbgeng.advanced;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.Guid.IID;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinNT.HRESULT;
import agent.dbgeng.jna.dbgeng.UnknownWithUtils.VTableIndex;
public interface IDebugAdvanced3 extends IDebugAdvanced2 {
final IID IID_IDEBUG_ADVANCED3 = new IID("cba4abb4-84c4-444d-87ca-a04e13286739");
enum VTIndices3 implements VTableIndex {
GET_SOURCE_FILE_INFORMATION_WIDE, //
FIND_SOURCE_FILE_AND_TOKEN_WIDE, //
GET_SYMBOL_INFORMATION_WIDE, //
;
static int start = VTableIndex.follow(VTIndices2.class);
@Override
public int getIndex() {
return this.ordinal() + start;
}
}
HRESULT GetSourceFileInformationWide(ULONG Which, WString SourceFile, ULONGLONG Arg64,
ULONG Arg32, Pointer Buffer, ULONG BufferSize, ULONGByReference InfoSize);
HRESULT FindSourceFileAndTokenWide(ULONG StartElement, ULONGLONG ModAddr, String File,
ULONG Flags, Pointer FileToken, ULONG FileTokenSize, ULONGByReference FoundElement,
char[] Buffer, ULONG BufferSize, ULONGByReference FoundSize);
HRESULT GetSymbolInformationWide(ULONG Which, ULONGLONG Arg64, ULONG Arg32, Pointer Buffer,
ULONG BufferSize, ULONGByReference InfoSize, char[] StringBuffer,
ULONG StringBufferSize, ULONGByReference StringSize);
}

Some files were not shown because too many files have changed in this diff Show more