GP-3823: TraceRmi Launcher framework + dbgeng for Windows.

This commit is contained in:
Dan 2023-11-28 10:38:27 -05:00
parent 80d92aa32f
commit c126cf51c0
33 changed files with 1206 additions and 1303 deletions

View file

@ -1,224 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define INITGUID
#include <engextcpp.hpp>
#include <jni.h>
#include "resource.h"
#define CHECK_RESULT(x, y) do { \
HRESULT hr = (x); \
if (hr != S_OK) { \
fprintf(stderr, "HRESULT of %s = %x\n", ##x, hr); \
return y; \
} \
} while (0)
class EXT_CLASS : public ExtExtension {
public:
virtual HRESULT Initialize();
virtual void Uninitialize();
//virtual void OnSessionAccessible(ULONG64 Argument);
EXT_COMMAND_METHOD(java_add_cp);
EXT_COMMAND_METHOD(java_set);
EXT_COMMAND_METHOD(java_get);
EXT_COMMAND_METHOD(java_run);
void run_command(PCSTR name);
};
EXT_DECLARE_GLOBALS();
JavaVM* jvm = NULL;
JNIEnv* env = NULL;
jclass clsCommands = NULL;
char JDK_JVM_DLL_PATH[] = "\\jre\\bin\\server\\jvm.dll";
char JRE_JVM_DLL_PATH[] = "\\bin\\server\\jvm.dll";
typedef jint (_cdecl *CreateJavaVMFunc)(JavaVM**, void**, void*);
HRESULT EXT_CLASS::Initialize() {
HRESULT result = ExtExtension::Initialize();
if (result != S_OK) {
return result;
}
char* env_java_home = getenv("JAVA_HOME");
if (env_java_home == NULL) {
fprintf(stderr, "JAVA_HOME is not set\n");
fflush(stderr);
return E_FAIL;
}
char* java_home = strdup(env_java_home);
size_t home_len = strlen(java_home);
if (java_home[home_len - 1] == '\\') {
java_home[home_len - 1] = '\0';
}
size_t full_len = home_len + sizeof(JDK_JVM_DLL_PATH);
char* full_path = new char[full_len];
HMODULE jvmDll = NULL;
// Try the JRE path first;
strcpy_s(full_path, full_len, java_home);
strcat_s(full_path, full_len, JRE_JVM_DLL_PATH);
fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path);
fflush(stderr);
jvmDll = LoadLibraryA(full_path);
if (jvmDll == NULL) {
// OK, then try the JDK path
strcpy_s(full_path, full_len, java_home);
strcat_s(full_path, full_len, JDK_JVM_DLL_PATH);
fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path);
fflush(stderr);
jvmDll = LoadLibraryA(full_path);
}
free(full_path);
free(java_home);
if (jvmDll == NULL) {
fprintf(stderr, "Could not find the jvm.dll\n");
fflush(stderr);
return E_FAIL;
}
fprintf(stderr, "Found it!\n");
fflush(stderr);
JavaVMOption options[2];
JavaVMInitArgs vm_args = { 0 };
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = sizeof(options)/sizeof(options[0]);
vm_args.options = options;
options[0].optionString = "-Xrs";
options[1].optionString = "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005";
vm_args.ignoreUnrecognized = false;
CreateJavaVMFunc create_jvm = NULL;
//create_jvm = JNI_CreateJavaVM;
create_jvm = (CreateJavaVMFunc) GetProcAddress(jvmDll, "JNI_CreateJavaVM");
jint jni_result = create_jvm(&jvm, (void**)&env, &vm_args);
if (jni_result != JNI_OK) {
jvm = NULL;
fprintf(stderr, "Could not initialize JVM: %d: ", jni_result);
switch (jni_result) {
case JNI_ERR:
fprintf(stderr, "unknown error");
break;
case JNI_EDETACHED:
fprintf(stderr, "thread detached from the VM");
break;
case JNI_EVERSION:
fprintf(stderr, "JNI version error");
break;
case JNI_ENOMEM:
fprintf(stderr, "not enough memory");
break;
case JNI_EEXIST:
fprintf(stderr, "VM already created");
break;
case JNI_EINVAL:
fprintf(stderr, "invalid arguments");
break;
}
fprintf(stderr, "\n");
fflush(stderr);
return E_FAIL;
}
HMODULE hJavaProviderModule = GetModuleHandle(TEXT("javaprovider"));
HRSRC resCommandsClassfile = FindResource(hJavaProviderModule, MAKEINTRESOURCE(IDR_CLASSFILE1), TEXT("Classfile"));
HGLOBAL gblCommandsClassfile = LoadResource(hJavaProviderModule, resCommandsClassfile);
LPVOID lpCommandsClassfile = LockResource(gblCommandsClassfile);
DWORD szCommandsClassfile = SizeofResource(hJavaProviderModule, resCommandsClassfile);
clsCommands = env->DefineClass(
"javaprovider/Commands", NULL, (jbyte*) lpCommandsClassfile, szCommandsClassfile
);
if (clsCommands == NULL) {
fprintf(stderr, "Could not define Commands class\n");
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
return E_FAIL;
}
}
return S_OK;
}
void EXT_CLASS::Uninitialize() {
if (jvm != NULL) {
jvm->DestroyJavaVM();
}
ExtExtension::Uninitialize();
}
void EXT_CLASS::run_command(PCSTR name) {
// TODO: Throw an exception during load, then!
if (jvm == NULL) {
Out("javaprovider extension did not load properly.\n");
return;
}
if (clsCommands == NULL) {
Out("javaprovider extension did not load properly.\n");
return;
}
PCSTR args = GetRawArgStr();
jmethodID mthCommand = env->GetStaticMethodID(clsCommands, name, "(Ljava/lang/String;)V");
if (mthCommand == NULL) {
Out("INTERNAL ERROR: No such command: %s\n", name);
return;
}
jstring argsStr = env->NewStringUTF(args);
if (argsStr == NULL) {
Out("Could not create Java string for arguments.\n");
return;
}
env->CallStaticVoidMethod(clsCommands, mthCommand, argsStr);
env->DeleteLocalRef(argsStr);
if (env->ExceptionCheck()) {
Out("Exception during javaprovider command:\n");
env->ExceptionDescribe(); // TODO: Send this to output callbacks, not console.
env->ExceptionClear();
}
}
EXT_COMMAND(java_add_cp, "Add an element to the class path", "{{custom}}") {
run_command("java_add_cp");
}
EXT_COMMAND(java_set, "Set a Java system property", "{{custom}}") {
run_command("java_set");
}
EXT_COMMAND(java_get, "Get a Java system property", "{{custom}}") {
run_command("java_get");
}
EXT_COMMAND(java_run, "Execute the named java class", "{{custom}}") {
run_command("java_run");
}
#define JNA extern "C" __declspec(dllexport)
JNA HRESULT createClient(PDEBUG_CLIENT* client) {
return g_ExtInstance.m_Client->CreateClient(client);
}

View file

@ -1,13 +0,0 @@
EXPORTS
; For ExtCpp
DebugExtensionInitialize
DebugExtensionUninitialize
DebugExtensionNotify
help
; My Commands
java_add_cp
java_set
java_get
java_run

View file

@ -1,16 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <Windows.h>

View file

@ -1,31 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by javaprovider.rc
//
#define IDR_CLASSFILE1 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View file

@ -19,6 +19,7 @@ import os.path
import socket
import time
import sys
import re
from ghidratrace import sch
from ghidratrace.client import Client, Address, AddressRange, TraceObject
@ -185,7 +186,7 @@ def compute_name(progname=None):
progname = buffer.decode('utf-8')
except Exception:
return 'pydbg/noname'
return 'pydbg/' + progname.split('/')[-1]
return 'pydbg/' + re.split(r'/|\\', progname)[-1]
def start_trace(name):
@ -1301,7 +1302,36 @@ def ghidra_util_wait_stopped(timeout=1):
time.sleep(0.1)
if time.time() - start > timeout:
raise RuntimeError('Timed out waiting for thread to stop')
def dbg():
return util.get_debugger()
SHOULD_WAIT = ['GO', 'STEP_BRANCH', 'STEP_INTO', 'STEP_OVER']
def repl():
print("This is the dbgeng.dll (WinDbg) REPL. To drop to Python3, press Ctrl-C.")
while True:
# TODO: Implement prompt retrieval in PR to pybag?
print('dbg> ', end='')
try:
cmd = input().strip()
if not cmd:
continue
dbg().cmd(cmd, quiet=False)
stat = dbg().exec_status()
if stat != 'BREAK':
dbg().wait()
else:
pass
#dbg().dispatch_events()
except KeyboardInterrupt as e:
print("")
print("You have left the dbgeng REPL and are now at the Python3 interpreter.")
print("use repl() to re-enter.")
return
except:
# Assume cmd() has already output the error
pass

View file

@ -383,7 +383,7 @@ def interrupt():
@REGISTRY.method(action='step_into')
def step_into(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
"""Step on instruction exactly."""
"""Step one instruction exactly."""
find_thread_by_obj(thread)
dbg().stepi(n)
@ -511,7 +511,7 @@ def write_mem(process: sch.Schema('Process'), address: Address, data: bytes):
@REGISTRY.method
def write_reg(frame: sch.Schema('Frame'), name: str, value: bytes):
def write_reg(frame: sch.Schema('StackFrame'), name: str, value: bytes):
"""Write a register."""
util.select_frame()
nproc = pydbg.selected_process()

View file

@ -1,315 +0,0 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include <string.h>
#define INITGUID
#include <dbgeng.h>
#include <Windows.h>
#include <jni.h>
JavaVM* jvm = NULL;
JNIEnv* env = NULL;
char JDK_JVM_DLL_PATH[] = "\\jre\\bin\\server\\jvm.dll";
char JRE_JVM_DLL_PATH[] = "\\bin\\server\\jvm.dll";
char MAIN_CLASS[] = "sctldbgeng/sctl/DbgEngSctlServer";
char CP_PREFIX[] = "-Djava.class.path=";
typedef jint (_cdecl *CreateJavaVMFunc)(JavaVM**, void**, void*);
#define CHECK_RC(v, f, x) do { \
HRESULT ___hr = (x); \
if (___hr < 0) { \
fprintf(stderr, "FAILED on line %d: HRESULT=%08x\n", __LINE__, ___hr); \
goto f; \
} else if (___hr == S_OK) { \
v = 1; \
} else { \
v = 0; \
} \
} while (0)
#if 0
class MyEventCallbacks : public DebugBaseEventCallbacks {
public:
STDMETHOD_(ULONG, AddRef)(THIS) {
InterlockedIncrement(&m_ulRefCount);
return m_ulRefCount;
}
STDMETHOD_(ULONG, Release)(THIS) {
ULONG ulRefCount = InterlockedDecrement(&m_ulRefCount);
if (m_ulRefCount == 0) {
delete this;
}
return ulRefCount;
}
STDMETHOD(GetInterestMask)(_Out_ PULONG Mask) {
*Mask = DEBUG_EVENT_CREATE_PROCESS | DEBUG_EVENT_CREATE_THREAD;
return S_OK;
}
STDMETHOD(CreateProcess)(
THIS_
_In_ ULONG64 ImageFileHandle,
_In_ ULONG64 Handle,
_In_ ULONG64 BaseOffset,
_In_ ULONG ModuleSize,
_In_ PCSTR ModuleName,
_In_ PCSTR ImageName,
_In_ ULONG CheckSum,
_In_ ULONG TimeDateStamp,
_In_ ULONG64 InitialThreadHandle,
_In_ ULONG64 ThreadDataOffset,
_In_ ULONG64 StartOffset
) {
UNREFERENCED_PARAMETER(ImageFileHandle);
UNREFERENCED_PARAMETER(Handle);
UNREFERENCED_PARAMETER(BaseOffset);
UNREFERENCED_PARAMETER(ModuleSize);
UNREFERENCED_PARAMETER(ModuleName);
UNREFERENCED_PARAMETER(ImageName);
UNREFERENCED_PARAMETER(CheckSum);
UNREFERENCED_PARAMETER(TimeDateStamp);
UNREFERENCED_PARAMETER(InitialThreadHandle);
UNREFERENCED_PARAMETER(ThreadDataOffset);
UNREFERENCED_PARAMETER(StartOffset);
return DEBUG_STATUS_BREAK;
}
STDMETHOD(CreateThread)(
THIS_
_In_ ULONG64 Handle,
_In_ ULONG64 DataOffset,
_In_ ULONG64 StartOffset
) {
UNREFERENCED_PARAMETER(Handle);
UNREFERENCED_PARAMETER(DataOffset);
UNREFERENCED_PARAMETER(StartOffset);
return DEBUG_STATUS_BREAK;
}
private:
ULONG m_ulRefCount = 0;
};
int main_exp00(int argc, char** argv) {
PDEBUG_CLIENT5 pClient5 = NULL;
PDEBUG_CONTROL4 pControl4 = NULL;
PDEBUG_SYMBOLS3 pSymbols3 = NULL;
int ok = 0;
CHECK_RC(ok, EXIT, DebugCreate(IID_IDebugClient5, (PVOID*) &pClient5));
CHECK_RC(ok, EXIT, pClient5->QueryInterface(IID_IDebugControl4, (PVOID*) &pControl4));
CHECK_RC(ok, EXIT, pClient5->QueryInterface(IID_IDebugSymbols3, (PVOID*) &pSymbols3));
pClient5->SetEventCallbacks(new MyEventCallbacks());
CHECK_RC(ok, EXIT, pControl4->Execute(DEBUG_OUTCTL_ALL_CLIENTS, ".create notepad", DEBUG_EXECUTE_ECHO));
CHECK_RC(ok, EXIT, pControl4->WaitForEvent(0, INFINITE));
CHECK_RC(ok, EXIT, pControl4->Execute(DEBUG_OUTCTL_ALL_CLIENTS, "g", DEBUG_EXECUTE_ECHO));
CHECK_RC(ok, EXIT, pControl4->WaitForEvent(0, INFINITE));
ULONG64 ul64MatchHandle = 0;
CHECK_RC(ok, EXIT, pSymbols3->StartSymbolMatch("*", &ul64MatchHandle));
while (true) {
char aBuffer[1024] = { 0 };
ULONG64 ul64Offset = 0;
CHECK_RC(ok, FINISH, pSymbols3->GetNextSymbolMatch(ul64MatchHandle, aBuffer, sizeof(aBuffer), NULL, &ul64Offset));
printf("%016x: %s\n", ul64Offset, aBuffer);
}
FINISH:
fprintf(stderr, "SUCCESS\n");
EXIT:
pClient5->SetEventCallbacks(NULL);
pControl4->Release();
pClient5->Release();
return 0;
}
int main_exp01(int argc, char** argv) {
PDEBUG_CLIENT5 pClient5 = NULL;
int ok = 0;
CHECK_RC(ok, EXIT, DebugCreate(IID_IDebugClient5, (PVOID*) &pClient5));
CHECK_RC(ok, EXIT, pClient5->StartProcessServerWide(DEBUG_CLASS_USER_WINDOWS, L"tcp:port=11200", NULL));
CHECK_RC(ok, EXIT, pClient5->WaitForProcessServerEnd(INFINITE));
EXIT:
if (pClient5 != NULL) {
pClient5->Release();
}
return 0;
}
#endif
int main_sctldbg(int argc, char** argv) {
if (argc < 1) {
fprintf(stderr, "Something is terribly wrong: argc == 0\n");
}
char* env_java_home = getenv("JAVA_HOME");
if (env_java_home == NULL) {
fprintf(stderr, "JAVA_HOME is not set\n");
fflush(stderr);
return -1;
}
char* java_home = strdup(env_java_home);
size_t home_len = strlen(java_home);
if (java_home[home_len - 1] == '\\') {
java_home[home_len - 1] = '\0';
}
size_t full_len = home_len + sizeof(JDK_JVM_DLL_PATH);
char* full_path = new char[full_len];
HMODULE jvmDll = NULL;
// Try the JRE path first;
strcpy_s(full_path, full_len, java_home);
strcat_s(full_path, full_len, JRE_JVM_DLL_PATH);
fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path);
fflush(stderr);
jvmDll = LoadLibraryA(full_path);
if (jvmDll == NULL) {
// OK, then try the JDK path
strcpy_s(full_path, full_len, java_home);
strcat_s(full_path, full_len, JDK_JVM_DLL_PATH);
fprintf(stderr, "Trying to find jvm.dll at %s\n", full_path);
fflush(stderr);
jvmDll = LoadLibraryA(full_path);
}
free(full_path);
free(java_home);
if (jvmDll == NULL) {
fprintf(stderr, "Could not find the jvm.dll\n");
fflush(stderr);
return -1;
}
fprintf(stderr, "Found it!\n");
fflush(stderr);
#define USE_EXE_AS_JAR
#ifdef USE_EXE_AS_JAR
DWORD fullpath_len = GetFullPathNameA(argv[0], 0, NULL, NULL);
char* fullpath = new char[fullpath_len];
GetFullPathNameA(argv[0], fullpath_len, fullpath, NULL);
size_t cp_opt_len = sizeof(CP_PREFIX) + strlen(fullpath);
char* cp_opt = new char[cp_opt_len];
strcpy_s(cp_opt, cp_opt_len, CP_PREFIX);
strcat_s(cp_opt, cp_opt_len, fullpath);
fflush(stderr);
#endif
JavaVMOption options[2];
JavaVMInitArgs vm_args = { 0 };
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = sizeof(options)/sizeof(options[0]);
vm_args.options = options;
options[0].optionString = "-Xrs";
#ifdef USE_EXE_AS_JAR
fprintf(stderr, "Classpath: %s\n", cp_opt);
options[1].optionString = cp_opt;
#else
options[1].optionString = "-Djava.class.path=sctldbgeng.jar";
#endif
//options[2].optionString = "-verbose:class";
vm_args.ignoreUnrecognized = false;
CreateJavaVMFunc create_jvm = NULL;
//create_jvm = JNI_CreateJavaVM;
create_jvm = (CreateJavaVMFunc) GetProcAddress(jvmDll, "JNI_CreateJavaVM");
jint jni_result = create_jvm(&jvm, (void**)&env, &vm_args);
#ifdef USE_EXE_AS_JAR
free(cp_opt);
#endif
if (jni_result != JNI_OK) {
jvm = NULL;
fprintf(stderr, "Could not initialize JVM: %d: ", jni_result);
switch (jni_result) {
case JNI_ERR:
fprintf(stderr, "unknown error");
break;
case JNI_EDETACHED:
fprintf(stderr, "thread detached from the VM");
break;
case JNI_EVERSION:
fprintf(stderr, "JNI version error");
break;
case JNI_ENOMEM:
fprintf(stderr, "not enough memory");
break;
case JNI_EEXIST:
fprintf(stderr, "VM already created");
break;
case JNI_EINVAL:
fprintf(stderr, "invalid arguments");
break;
}
fprintf(stderr, "\n");
fflush(stderr);
return -1;
}
jclass mainCls = env->FindClass(MAIN_CLASS);
if (mainCls == NULL) {
fprintf(stderr, "Could not find main class: %s\n", MAIN_CLASS);
jvm->DestroyJavaVM();
return -1;
}
jmethodID mainMeth = env->GetStaticMethodID(mainCls, "main", "([Ljava/lang/String;)V");
if (mainMeth == NULL) {
fprintf(stderr, "No main(String[] args) method in main class\n");
jvm->DestroyJavaVM();
return -1;
}
jclass stringCls = env->FindClass("java/lang/String");
jobjectArray jargs = env->NewObjectArray(argc - 1, stringCls, NULL);
for (int i = 1; i < argc; i++) {
jstring a = env->NewStringUTF(argv[i]);
if (a == NULL) {
fprintf(stderr, "Could not create Java string for arguments.\n");
jvm->DestroyJavaVM();
return -1;
}
env->SetObjectArrayElement(jargs, i - 1, a);
}
env->CallStaticVoidMethod(mainCls, mainMeth, (jvalue*) jargs);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
jvm->DestroyJavaVM();
return 0;
}
int main(int argc, char** argv) {
main_sctldbg(argc, argv);
}