Commit the changes to the java code
This commit is contained in:
parent
7596f934d1
commit
32b8353eb4
@ -8,6 +8,7 @@
|
||||
void JNICALL AgentThread(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
|
||||
{
|
||||
CJavaServer *s=new CJavaServer(jvmti_env, jni_env);
|
||||
|
||||
s->Start();
|
||||
|
||||
delete s;
|
||||
@ -15,10 +16,22 @@ void JNICALL AgentThread(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
jvmtiIterationControl JNICALL initialHeapIterate(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data)
|
||||
{
|
||||
//OutputDebugStringA("Tagging object\n");
|
||||
*tag_ptr=1;
|
||||
return JVMTI_ITERATION_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved)
|
||||
{
|
||||
jvmtiEnv *env;
|
||||
JNIEnv *jni;
|
||||
jvmtiError error;
|
||||
|
||||
jint r=vm->GetEnv((void **)&env, JVMTI_VERSION);
|
||||
if (r!=JNI_OK)
|
||||
{
|
||||
@ -34,6 +47,52 @@ JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* vm, char* options, void* reserved)
|
||||
}
|
||||
|
||||
|
||||
jvmtiCapabilities cap, wantedcap;
|
||||
|
||||
env->GetPotentialCapabilities(&cap);
|
||||
|
||||
|
||||
|
||||
if (cap.can_tag_objects)
|
||||
{
|
||||
env->GetCapabilities(&wantedcap);
|
||||
wantedcap.can_tag_objects=1;
|
||||
error=env->AddCapabilities(&wantedcap);
|
||||
|
||||
/*
|
||||
if (error==JVMTI_ERROR_NONE)
|
||||
{
|
||||
error=env->IterateOverHeap(JVMTI_HEAP_OBJECT_EITHER, initialHeapIterate, NULL);
|
||||
|
||||
if (error==JVMTI_ERROR_NONE)
|
||||
{
|
||||
int i;
|
||||
jlong tags[1];
|
||||
jint count;
|
||||
jobject *list;
|
||||
jlong *taglist;
|
||||
|
||||
tags[0]=1;
|
||||
error=env->GetObjectsWithTags(1, tags, &count, &list, &taglist);
|
||||
for (i=0; i<count; i++)
|
||||
{
|
||||
jobject j1,j2;
|
||||
j1=list[i];
|
||||
j2=jni->NewGlobalRef(j1);
|
||||
|
||||
if (j2==NULL)
|
||||
OutputDebugStringA("Failure\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
jclass threadclass=jni->FindClass("java/lang/Thread");
|
||||
if (threadclass==0)
|
||||
{
|
||||
|
@ -372,6 +372,10 @@
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\JavaEventServer.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\JavaServer.cpp"
|
||||
>
|
||||
@ -422,6 +426,10 @@
|
||||
RelativePath=".\CEJVMTI.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\JavaEventServer.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\JavaServer.h"
|
||||
>
|
||||
|
276
Cheat Engine/Java/CEJVMTI/CEJVMTI/JavaEventServer.cpp
Normal file
276
Cheat Engine/Java/CEJVMTI/CEJVMTI/JavaEventServer.cpp
Normal file
@ -0,0 +1,276 @@
|
||||
#include "StdAfx.h"
|
||||
#include "JavaEventServer.h"
|
||||
|
||||
|
||||
CJavaEventServer *old_eventserver=NULL;
|
||||
CJavaEventServer *eventserver=NULL;
|
||||
|
||||
|
||||
void JNICALL MethodLoad(jvmtiEnv *jvmti_env, jmethodID method, jint code_size, const void* code_addr, jint map_length,
|
||||
const jvmtiAddrLocationMap* map, const void* compile_info)
|
||||
{
|
||||
if (eventserver)
|
||||
eventserver->MethodLoad(jvmti_env, method, code_size, code_addr);
|
||||
|
||||
}
|
||||
|
||||
void JNICALL MethodUnload(jvmtiEnv *jvmti_env, jmethodID method, const void* code_addr)
|
||||
{
|
||||
if (eventserver)
|
||||
eventserver->MethodUnload(jvmti_env, method, code_addr);
|
||||
}
|
||||
|
||||
void JNICALL DynamicCodeGenerated(jvmtiEnv *jvmti_env, const char* name, const void* address, jint length)
|
||||
{
|
||||
if (eventserver)
|
||||
eventserver->DynamicCodeGenerated(jvmti_env, name, address,length);
|
||||
|
||||
}
|
||||
|
||||
CJavaEventServer::CJavaEventServer(jvmtiEnv *jvmti_env)
|
||||
{
|
||||
jvmtiEventCallbacks callbacks;
|
||||
jvmtiError error;
|
||||
jvmtiCapabilities cap, wantedcap;
|
||||
|
||||
this->jvmti_env=jvmti_env;
|
||||
|
||||
if (eventserver) //extra check
|
||||
{
|
||||
CJavaEventServer *old=eventserver;
|
||||
eventserver=NULL;
|
||||
delete eventserver;
|
||||
}
|
||||
|
||||
|
||||
|
||||
swprintf(pipename, 256,L"\\\\.\\pipe\\cejavaevents_pid%d", GetCurrentProcessId());
|
||||
pipehandle=CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1,256*1024, 16, INFINITE, NULL);
|
||||
ConnectNamedPipe(pipehandle, NULL);
|
||||
|
||||
|
||||
eventserver=this;
|
||||
|
||||
|
||||
|
||||
jvmti_env->GetPotentialCapabilities(&cap);
|
||||
|
||||
|
||||
if (cap.can_generate_compiled_method_load_events)
|
||||
{
|
||||
|
||||
jvmti_env->GetCapabilities(&wantedcap);
|
||||
wantedcap.can_generate_compiled_method_load_events=1;
|
||||
error=jvmti_env->AddCapabilities(&wantedcap);
|
||||
if (error!=JVMTI_ERROR_NONE)
|
||||
{
|
||||
OutputDebugStringA("Failure adding can_generate_compiled_method_load_events to my capabilities");
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
|
||||
|
||||
callbacks.CompiledMethodLoad=::MethodLoad;
|
||||
callbacks.CompiledMethodUnload=::MethodUnload;
|
||||
callbacks.DynamicCodeGenerated=::DynamicCodeGenerated;
|
||||
|
||||
error=jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
|
||||
|
||||
jvmti_env->ForceGarbageCollection();
|
||||
|
||||
if (error==JVMTI_ERROR_NONE)
|
||||
{
|
||||
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
|
||||
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
|
||||
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
|
||||
error=jvmti_env->GenerateEvents(JVMTI_EVENT_COMPILED_METHOD_LOAD);
|
||||
error=jvmti_env->GenerateEvents(JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OutputDebugStringA("can_generate_compiled_method_load_events == FALSE");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
CJavaEventServer::~CJavaEventServer(void)
|
||||
{
|
||||
Terminate();
|
||||
|
||||
|
||||
}
|
||||
|
||||
void CJavaEventServer::MethodLoad(jvmtiEnv *jvmti_env, jmethodID method, jint code_size, const void* code_addr)
|
||||
{
|
||||
/*
|
||||
OutputDebugStringA("MethodLoad");
|
||||
char *name=NULL, *sig=NULL, *gen=NULL;
|
||||
char *classsig=NULL, *classgen=NULL;
|
||||
|
||||
jclass klass;
|
||||
|
||||
jvmti_env->GetMethodDeclaringClass(method, &klass);
|
||||
jvmti_env->GetClassSignature(klass, &classsig, &classgen);
|
||||
|
||||
OutputDebugStringA(classsig);
|
||||
|
||||
|
||||
jvmti_env->GetMethodName(method, &name, &sig, &gen);
|
||||
|
||||
OutputDebugStringA(name);
|
||||
OutputDebugStringA("\n");
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
Lock();
|
||||
try
|
||||
{
|
||||
jclass klass;
|
||||
char *name=NULL, *sig=NULL, *gen=NULL;
|
||||
char *classsig=NULL, *classgen=NULL;
|
||||
WORD len;
|
||||
|
||||
WriteByte(EVENTCMD_METHODLOAD);
|
||||
WriteQword((UINT_PTR)method);
|
||||
WriteDword(code_size);
|
||||
WriteQword((UINT_PTR)code_addr);
|
||||
|
||||
jvmti_env->GetMethodDeclaringClass(method, &klass); //when this function returns jklass gets dereferenced. If this was the server I'd have to use DeleteLocalRef on this
|
||||
jvmti_env->GetClassSignature(klass, &classsig, &classgen);
|
||||
jvmti_env->GetMethodName(method, &name, &sig, &gen);
|
||||
|
||||
if (classsig)
|
||||
{
|
||||
len=strlen(classsig);
|
||||
WriteWord(len);
|
||||
if (len)
|
||||
Write(classsig, len);
|
||||
|
||||
jvmti_env->Deallocate((unsigned char *)classsig);
|
||||
}
|
||||
else
|
||||
WriteWord(0);
|
||||
|
||||
|
||||
if (classgen)
|
||||
jvmti_env->Deallocate((unsigned char *)classgen);
|
||||
|
||||
|
||||
if (name)
|
||||
{
|
||||
len=strlen(name);
|
||||
WriteWord(len);
|
||||
if (len)
|
||||
Write(name, len);
|
||||
|
||||
jvmti_env->Deallocate((unsigned char *)name);
|
||||
}
|
||||
else
|
||||
WriteWord(0);
|
||||
|
||||
if (sig)
|
||||
{
|
||||
len=strlen(sig);
|
||||
WriteWord(len);
|
||||
if (len)
|
||||
Write(sig, len);
|
||||
|
||||
jvmti_env->Deallocate((unsigned char *)sig);
|
||||
}
|
||||
else
|
||||
WriteWord(0);
|
||||
|
||||
if (gen)
|
||||
jvmti_env->Deallocate((unsigned char *)gen);
|
||||
|
||||
|
||||
|
||||
}
|
||||
catch (char *e)
|
||||
{
|
||||
OutputDebugStringA(e);
|
||||
//no connection yet
|
||||
}
|
||||
|
||||
Unlock();
|
||||
|
||||
}
|
||||
|
||||
void CJavaEventServer::MethodUnload(jvmtiEnv *jvmti_env, jmethodID method, const void* code_addr)
|
||||
{
|
||||
Lock();
|
||||
try
|
||||
{
|
||||
WriteByte(EVENTCMD_METHODUNLOAD);
|
||||
WriteQword((UINT_PTR)method);
|
||||
WriteQword((UINT_PTR)code_addr);
|
||||
}
|
||||
catch (char *e)
|
||||
{
|
||||
OutputDebugStringA(e);
|
||||
//no connection yet
|
||||
}
|
||||
Unlock();
|
||||
}
|
||||
|
||||
void CJavaEventServer::DynamicCodeGenerated(jvmtiEnv *jvmti_env, const char* name, const void* address, jint length)
|
||||
{
|
||||
Lock();
|
||||
try
|
||||
{
|
||||
WriteByte(EVENTCMD_DYNAMICCODEGENERATED);
|
||||
WriteQword((UINT_PTR)address);
|
||||
WriteDword(length);
|
||||
WriteWord(strlen(name));
|
||||
Write((void *)name, strlen(name));
|
||||
|
||||
}
|
||||
catch (char *e)
|
||||
{
|
||||
OutputDebugStringA(e);
|
||||
//no connection yet
|
||||
}
|
||||
Unlock();
|
||||
}
|
||||
|
||||
void CJavaEventServer::Terminate(void)
|
||||
{
|
||||
|
||||
jvmtiCapabilities caps;
|
||||
|
||||
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
|
||||
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
|
||||
jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
|
||||
|
||||
ZeroMemory(&caps, sizeof(caps));
|
||||
|
||||
caps.can_generate_compiled_method_load_events=1;
|
||||
|
||||
jvmti_env->RelinquishCapabilities(&caps);
|
||||
|
||||
eventserver=NULL;
|
||||
|
||||
|
||||
Lock();
|
||||
try
|
||||
{
|
||||
WriteByte(EVENTCMD_TERMINATED);
|
||||
}
|
||||
catch (char *e)
|
||||
{
|
||||
OutputDebugStringA(e);
|
||||
}
|
||||
Unlock();
|
||||
Sleep(500);
|
||||
}
|
||||
|
31
Cheat Engine/Java/CEJVMTI/CEJVMTI/JavaEventServer.h
Normal file
31
Cheat Engine/Java/CEJVMTI/CEJVMTI/JavaEventServer.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "pipe.h"
|
||||
|
||||
//pipe for transmitting java events like method load/free
|
||||
|
||||
#define EVENTCMD_METHODLOAD 0
|
||||
#define EVENTCMD_METHODUNLOAD 1
|
||||
#define EVENTCMD_DYNAMICCODEGENERATED 2
|
||||
#define EVENTCMD_TERMINATED 255
|
||||
|
||||
|
||||
class CJavaEventServer :
|
||||
public Pipe
|
||||
{
|
||||
private:
|
||||
wchar_t pipename[256];
|
||||
jvmtiEnv *jvmti_env;
|
||||
|
||||
public:
|
||||
CJavaEventServer(jvmtiEnv *jvmti_env);
|
||||
~CJavaEventServer(void);
|
||||
|
||||
void MethodLoad(jvmtiEnv *jvmti_env, jmethodID method, jint code_size, const void* code_addr);
|
||||
void MethodUnload(jvmtiEnv *jvmti_env, jmethodID method, const void* code_addr);
|
||||
void DynamicCodeGenerated(jvmtiEnv *jvmti_env, const char* name, const void* address, jint length);
|
||||
|
||||
void Terminate(void);
|
||||
};
|
||||
|
||||
extern CJavaEventServer *eventserver;
|
||||
extern CJavaEventServer *old_eventserver;
|
@ -1,10 +1,35 @@
|
||||
#include "StdAfx.h"
|
||||
#include "JavaServer.h"
|
||||
|
||||
|
||||
CJavaServer::CJavaServer(jvmtiEnv* jvmti_env, JNIEnv* jni_env)
|
||||
{
|
||||
//create a named pipe
|
||||
swprintf(datapipename, 256,L"\\\\.\\pipe\\cejavadc_pid%d", GetCurrentProcessId());
|
||||
jvmtiCapabilities cap;
|
||||
|
||||
|
||||
this->jni=jni_env;
|
||||
this->jvmti=jvmti_env;
|
||||
|
||||
|
||||
jvmti->GetCapabilities(&cap);
|
||||
swprintf(pipename, 256,L"\\\\.\\pipe\\cejavadc_pid%d", GetCurrentProcessId());
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
void CJavaServer::CreatePipeandWaitForconnect(void)
|
||||
{
|
||||
if ((pipehandle) && (pipehandle!=INVALID_HANDLE_VALUE))
|
||||
{
|
||||
CloseHandle(pipehandle);
|
||||
pipehandle=0;
|
||||
}
|
||||
|
||||
pipehandle=CreateNamedPipe(pipename, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1,256*1024, 16, INFINITE, NULL);
|
||||
ConnectNamedPipe(pipehandle, NULL);
|
||||
}
|
||||
|
||||
CJavaServer::~CJavaServer(void)
|
||||
@ -12,7 +37,275 @@ CJavaServer::~CJavaServer(void)
|
||||
|
||||
}
|
||||
|
||||
void CJavaServer::Start(void)
|
||||
void CJavaServer::StartCodeCallbacks(void)
|
||||
{
|
||||
if (old_eventserver)
|
||||
delete old_eventserver;
|
||||
|
||||
eventserver=new CJavaEventServer(jvmti);
|
||||
}
|
||||
|
||||
void CJavaServer::StopCodeCallbacks(void)
|
||||
{
|
||||
if (eventserver)
|
||||
delete eventserver;
|
||||
}
|
||||
|
||||
void CJavaServer::GetLoadedClasses(void)
|
||||
{
|
||||
int i;
|
||||
jint classcount;
|
||||
jclass *classes;
|
||||
if (jvmti->GetLoadedClasses(&classcount, &classes)==JVMTI_ERROR_NONE) //note: this creates to the returned classes. Should be managed
|
||||
{
|
||||
|
||||
|
||||
WriteDword(classcount);
|
||||
for (i=0; i<classcount; i++)
|
||||
{
|
||||
char *sig=NULL;
|
||||
char *gen=NULL;
|
||||
|
||||
WriteQword((UINT_PTR)classes[i]);
|
||||
|
||||
jvmti->SetTag(classes[i], i+1); //let's tag this class while we're at it
|
||||
|
||||
if (jvmti->GetClassSignature(classes[i], &sig, &gen)==JVMTI_ERROR_NONE)
|
||||
{
|
||||
int len;
|
||||
if (sig)
|
||||
{
|
||||
len=(int)strlen(sig);
|
||||
WriteWord(len);
|
||||
Write(sig, len);
|
||||
jvmti->Deallocate((unsigned char *)sig);
|
||||
}
|
||||
else
|
||||
WriteWord(0);
|
||||
|
||||
if (gen)
|
||||
{
|
||||
len=(int)strlen(gen);
|
||||
WriteWord(len);
|
||||
Write(gen, len);
|
||||
jvmti->Deallocate((unsigned char *)gen);
|
||||
}
|
||||
else
|
||||
WriteWord(0);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
jvmti->Deallocate((unsigned char *)classes);
|
||||
}
|
||||
else
|
||||
WriteDword(0); //0 classes
|
||||
}
|
||||
|
||||
void CJavaServer::DereferenceLocalObject(void)
|
||||
{
|
||||
jobject object;
|
||||
object=(jobject)ReadQword();
|
||||
|
||||
jni->DeleteLocalRef(object);
|
||||
|
||||
}
|
||||
|
||||
void CJavaServer::GetClassMethods(void)
|
||||
{
|
||||
jclass klass=(jclass)ReadQword();
|
||||
jint count;
|
||||
jmethodID *methods=NULL;
|
||||
|
||||
if (jvmti->GetClassMethods(klass, &count, &methods)==JVMTI_ERROR_NONE)
|
||||
{
|
||||
int i;
|
||||
WriteDword(count);
|
||||
for (i=0; i<count; i++)
|
||||
{
|
||||
int len;
|
||||
char *name, *sig, *gen;
|
||||
WriteQword(UINT64(methods[i]));
|
||||
if (jvmti->GetMethodName(methods[i], &name, &sig, &gen)==JVMTI_ERROR_NONE)
|
||||
{
|
||||
if (name)
|
||||
{
|
||||
len=(int)strlen(name);
|
||||
WriteWord(len);
|
||||
Write(name, len);
|
||||
jvmti->Deallocate((unsigned char *)name);
|
||||
}
|
||||
else
|
||||
WriteWord(0);
|
||||
|
||||
if (sig)
|
||||
{
|
||||
len=(int)strlen(sig);
|
||||
WriteWord(len);
|
||||
Write(sig, len);
|
||||
jvmti->Deallocate((unsigned char *)sig);
|
||||
}
|
||||
else
|
||||
WriteWord(0);
|
||||
|
||||
if (gen)
|
||||
{
|
||||
len=(int)strlen(gen);
|
||||
WriteWord(len);
|
||||
Write(gen, len);
|
||||
jvmti->Deallocate((unsigned char *)gen);
|
||||
}
|
||||
else
|
||||
WriteWord(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
}
|
||||
|
||||
}
|
||||
jvmti->Deallocate((unsigned char *)methods);
|
||||
|
||||
}
|
||||
else
|
||||
WriteDword(0);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void CJavaServer::GetClassFields(void)
|
||||
{
|
||||
jint count;
|
||||
jfieldID *fields=NULL;
|
||||
jclass klass=(jclass)ReadQword();
|
||||
if (jvmti->GetClassFields(klass, &count, &fields)==JVMTI_ERROR_NONE)
|
||||
{
|
||||
int i;
|
||||
WriteDword(count);
|
||||
for (i=0; i<count; i++)
|
||||
{
|
||||
int len;
|
||||
char *name=NULL;
|
||||
char *sig=NULL;
|
||||
char *gen=NULL;
|
||||
|
||||
WriteQword((UINT_PTR)fields[i]);
|
||||
|
||||
if (jvmti->GetFieldName(klass, fields[i], &name, &sig, &gen)==JVMTI_ERROR_NONE)
|
||||
{
|
||||
if (name)
|
||||
{
|
||||
len=(int)strlen(name);
|
||||
WriteWord(len);
|
||||
Write(name, len);
|
||||
jvmti->Deallocate((unsigned char *)name);
|
||||
}
|
||||
else
|
||||
WriteWord(0);
|
||||
|
||||
if (sig)
|
||||
{
|
||||
len=(int)strlen(sig);
|
||||
WriteWord(len);
|
||||
Write(sig, len);
|
||||
jvmti->Deallocate((unsigned char *)sig);
|
||||
}
|
||||
else
|
||||
WriteWord(0);
|
||||
|
||||
if (gen)
|
||||
{
|
||||
len=(int)strlen(gen);
|
||||
WriteWord(len);
|
||||
Write(gen, len);
|
||||
jvmti->Deallocate((unsigned char *)gen);
|
||||
}
|
||||
else
|
||||
WriteWord(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
WriteWord(0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CJavaServer::Start(void)
|
||||
{
|
||||
BYTE command;
|
||||
while (TRUE)
|
||||
{
|
||||
CreatePipeandWaitForconnect();
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
command=ReadByte();
|
||||
switch (command)
|
||||
{
|
||||
case JAVACMD_STARTCODECALLBACKS:
|
||||
StartCodeCallbacks();
|
||||
break;
|
||||
|
||||
case JAVACMD_STOPCODECALLBACKS:
|
||||
StopCodeCallbacks();
|
||||
break;
|
||||
|
||||
case JAVACMD_GETLOADEDCLASSES:
|
||||
GetLoadedClasses();
|
||||
break;
|
||||
|
||||
case JAVACMD_DEREFERENCELOCALOBJECT:
|
||||
DereferenceLocalObject();
|
||||
break;
|
||||
|
||||
case JAVACMD_GETCLASSMETHODS:
|
||||
GetClassMethods();
|
||||
break;
|
||||
|
||||
case JAVACMD_GETCLASSFIELDS:
|
||||
GetClassFields();
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (char *e)
|
||||
{
|
||||
//Pipe error, or something else that wasn't caught. Exit the connection and start over
|
||||
OutputDebugStringA("Pipe error:\n");
|
||||
OutputDebugStringA(e);
|
||||
|
||||
old_eventserver=eventserver;
|
||||
eventserver=NULL;
|
||||
if (old_eventserver)
|
||||
old_eventserver->Terminate();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
OutputDebugStringA("Unexpected pipe error\n");
|
||||
|
||||
old_eventserver=eventserver;
|
||||
eventserver=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,18 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "JavaEventServer.h"
|
||||
|
||||
#define JAVACMD_STARTCODECALLBACKS 0
|
||||
#define JAVACMD_STOPCODECALLBACKS 1
|
||||
#define JAVACMD_GETLOADEDCLASSES 2
|
||||
#define JAVACMD_DEREFERENCELOCALOBJECT 3
|
||||
#define JAVACMD_GETCLASSMETHODS 4
|
||||
#define JAVACMD_GETCLASSFIELDS 5
|
||||
|
||||
class CJavaServer : Pipe
|
||||
{
|
||||
private:
|
||||
wchar_t pipename[256];
|
||||
jvmtiEnv* jvmti;
|
||||
JNIEnv* jni;
|
||||
|
||||
wchar_t datapipename[256];
|
||||
|
||||
|
||||
|
||||
void CreatePipeandWaitForconnect(void);
|
||||
public:
|
||||
CJavaServer(jvmtiEnv* jvmti_env, JNIEnv* jni_env);
|
||||
~CJavaServer(void);
|
||||
|
||||
void Start(void);
|
||||
|
||||
|
||||
void StartCodeCallbacks(void);
|
||||
void StopCodeCallbacks(void);
|
||||
void GetLoadedClasses(void);
|
||||
void DereferenceLocalObject(void);
|
||||
void GetClassMethods(void);
|
||||
void GetClassFields(void);
|
||||
};
|
||||
|
@ -1,10 +1,19 @@
|
||||
JAVACMD_STARTCODECALLBACKS=0
|
||||
JAVACMD_STOPCODECALLBACKS=1
|
||||
JAVACMD_GETLOADEDCLASSES=2
|
||||
JAVACMD_DEREFERENCELOCALOBJECT=3
|
||||
JAVACMD_GETCLASSMETHODS=4
|
||||
JAVACMD_GETCLASSFIELDS=5
|
||||
|
||||
|
||||
JAVACODECMD_METHODLOAD=0
|
||||
JAVACODECMD_METHODUNLOAD=1
|
||||
JAVACODECMD_DYNAMICCODEGENERATED=2
|
||||
JAVACODECMD_TERMINATED=255
|
||||
|
||||
|
||||
|
||||
JAVA_TIMEOUT=500000 --500 seconds
|
||||
|
||||
|
||||
function getFieldFromType(type, field, infloopprotection)
|
||||
@ -42,6 +51,10 @@ function CollectJavaSymbolsNonInjected(thread)
|
||||
|
||||
|
||||
s=readPointer("jvm.gHotSpotVMStructs")
|
||||
if (s==nil) or (s==0) then
|
||||
return --invalid JVM
|
||||
end
|
||||
|
||||
VMStructEntryTypeNameOffset=readInteger("jvm.gHotSpotVMStructEntryTypeNameOffset")
|
||||
VMStructEntryFieldNameOffset=readInteger("jvm.gHotSpotVMStructEntryFieldNameOffset")
|
||||
VMStructEntryTypestringOffset=readInteger("jvm.gHotSpotVMStructEntryTypestringOffset")
|
||||
@ -54,6 +67,8 @@ function CollectJavaSymbolsNonInjected(thread)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
const char* typeName; // The type name containing the given field (example: "Klass")
|
||||
const char* fieldName; // The field name within the type (example: "_name")
|
||||
@ -238,7 +253,7 @@ function CollectJavaSymbolsNonInjected(thread)
|
||||
CurrentPos=CurrentPos+CodeletSize
|
||||
end
|
||||
|
||||
|
||||
JavaHotSpotFieldsLoaded=true
|
||||
end
|
||||
|
||||
|
||||
@ -252,74 +267,100 @@ function javaInjectAgent()
|
||||
|
||||
createNativeThread(CollectJavaSymbolsNonInjected)
|
||||
|
||||
|
||||
if (JavaEventThread~=nil) then
|
||||
JavaEventListener_Terminated=true
|
||||
JavaEventThread.waitfor()
|
||||
JavaEventThread=nil
|
||||
end
|
||||
|
||||
if (javapipe~=nil) then
|
||||
javapipe.destroy()
|
||||
if (javapipe ~= nil) then
|
||||
javapipe.destroy() --this will cause the pipe listener to destroy the java event server, which will stop the javaeventthread (so no need to wait for that)
|
||||
javapipe=nil
|
||||
end
|
||||
|
||||
autoAssemble([[
|
||||
globalalloc(bla,1024)
|
||||
|
||||
globalalloc(cmd,16)
|
||||
globalalloc(arg0,256)
|
||||
globalalloc(arg1,256)
|
||||
globalalloc(arg2,256)
|
||||
globalalloc(result,4)
|
||||
|
||||
globalalloc(pipename,256)
|
||||
|
||||
cmd:
|
||||
db 'load',0
|
||||
|
||||
arg0:
|
||||
db 'F:\svn\Cheat Engine\bin\autorun\dlls\CEJVMTI64',0
|
||||
|
||||
arg1:
|
||||
db 0
|
||||
|
||||
arg2:
|
||||
db 0
|
||||
|
||||
pipename:
|
||||
db '\\.\pipe\cejavapipe',0
|
||||
|
||||
|
||||
bla:
|
||||
sub rsp,8
|
||||
sub rsp,30
|
||||
local alreadyinjected=false
|
||||
|
||||
mov rcx,cmd
|
||||
mov rdx,arg0
|
||||
mov r8,arg1
|
||||
mov r9,arg2
|
||||
if javaInjectedProcesses==nil then
|
||||
javaInjectedProcesses={}
|
||||
|
||||
mov [rsp],cmd
|
||||
mov [rsp+8],arg0
|
||||
mov [rsp+10],arg1
|
||||
mov [rsp+18],arg2
|
||||
mov [rsp+20],pipename
|
||||
local oldstate=errorOnLookupFailure(false)
|
||||
local address=getAddress('CEJVMTI64.dll')
|
||||
if (address~=nil) and (address~=0) then
|
||||
javaInjectedProcesses[getOpenedProcessID()]=true
|
||||
alreadyinjected=true
|
||||
print("opened a process with the JVMTI agent already running")
|
||||
end
|
||||
|
||||
errorOnLookupFailure(oldstate)
|
||||
|
||||
else
|
||||
--check if already injected
|
||||
alreadyinjected=javaInjectedProcesses[getOpenedProcessID()]==true
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (alreadyinjected==false) then
|
||||
autoAssemble([[
|
||||
globalalloc(bla,1024)
|
||||
|
||||
globalalloc(cmd,16)
|
||||
globalalloc(arg0,256)
|
||||
globalalloc(arg1,256)
|
||||
globalalloc(arg2,256)
|
||||
globalalloc(result,4)
|
||||
|
||||
globalalloc(pipename,256)
|
||||
|
||||
cmd:
|
||||
db 'load',0
|
||||
|
||||
arg0:
|
||||
db 'F:\svn\Cheat Engine\bin\autorun\dlls\CEJVMTI64',0
|
||||
|
||||
arg1:
|
||||
db 0
|
||||
|
||||
arg2:
|
||||
db 0
|
||||
|
||||
pipename:
|
||||
db '\\.\pipe\cejavapipe',0
|
||||
|
||||
|
||||
bla:
|
||||
sub rsp,8
|
||||
sub rsp,30
|
||||
|
||||
mov rcx,cmd
|
||||
mov rdx,arg0
|
||||
mov r8,arg1
|
||||
mov r9,arg2
|
||||
|
||||
mov [rsp],cmd
|
||||
mov [rsp+8],arg0
|
||||
mov [rsp+10],arg1
|
||||
mov [rsp+18],arg2
|
||||
mov [rsp+20],pipename
|
||||
|
||||
call jvm.JVM_EnqueueOperation
|
||||
mov [result],eax
|
||||
|
||||
add rsp,38
|
||||
ret
|
||||
|
||||
createthread(bla)
|
||||
]])
|
||||
|
||||
|
||||
javaInjectedProcesses[getOpenedProcessID()]=true
|
||||
|
||||
end
|
||||
|
||||
call jvm.JVM_EnqueueOperation
|
||||
mov [result],eax
|
||||
|
||||
add rsp,38
|
||||
ret
|
||||
|
||||
createthread(bla)
|
||||
]])
|
||||
|
||||
--wait till attached
|
||||
|
||||
|
||||
|
||||
local timeout=getTickCount()+5000000; --5 seconds
|
||||
local timeout=getTickCount()+JAVA_TIMEOUT
|
||||
while (javapipe==nil) and (getTickCount()<timeout) do
|
||||
javapipe=connectToPipe('cejavadc_pid'..getOpenedProcessID())
|
||||
end
|
||||
@ -330,7 +371,9 @@ function javaInjectAgent()
|
||||
|
||||
java_StartListeneningForEvents()
|
||||
|
||||
JavaSymbols.register()
|
||||
JavaSymbols.register() --make these symbols available to all of cheat engine
|
||||
|
||||
return 1;
|
||||
end
|
||||
|
||||
function JavaEventListener(thread)
|
||||
@ -342,7 +385,7 @@ function JavaEventListener(thread)
|
||||
local JavaEventPipe
|
||||
|
||||
|
||||
local timeout=getTickCount()+5000000; --5 seconds
|
||||
local timeout=getTickCount()+JAVA_TIMEOUT --5 seconds
|
||||
while (JavaEventPipe==nil) and (getTickCount()<timeout) do
|
||||
JavaEventPipe=connectToPipe('cejavaevents_pid'..getOpenedProcessID())
|
||||
end
|
||||
@ -352,31 +395,57 @@ function JavaEventListener(thread)
|
||||
end
|
||||
|
||||
|
||||
while JavaEventListener_Terminated==false do
|
||||
while true do
|
||||
local command=JavaEventPipe.readByte()
|
||||
if command==EVENTCMD_METHODLOAD then --methodload
|
||||
|
||||
local size1, size2, size3,ssize,classname, methodname, methodsig
|
||||
|
||||
local method=JavaEventPipe.readQword()
|
||||
local code_size=JavaEventPipe.readDword()
|
||||
local code_addr=JavaEventPipe.readQword()
|
||||
local ssize=JavaEventPipe.readWord()
|
||||
local classname=JavaEventPipe.readString(ssize)
|
||||
ssize=JavaEventPipe.readWord()
|
||||
local methodname=JavaEventPipe.readString(ssize)
|
||||
size1=JavaEventPipe.readWord()
|
||||
if (size1>0) then
|
||||
classname=JavaEventPipe.readString(size1)
|
||||
else
|
||||
classname=''
|
||||
end
|
||||
|
||||
ssize=JavaEventPipe.readWord()
|
||||
local methodsig=JavaEventPipe.readString(ssize)
|
||||
size2=JavaEventPipe.readWord()
|
||||
if (size2>0) then
|
||||
methodname=JavaEventPipe.readString(size2)
|
||||
else
|
||||
methodname=''
|
||||
end
|
||||
|
||||
local name=classname.."."..methodname..methodsig
|
||||
size3=JavaEventPipe.readWord()
|
||||
if (size3>0) then
|
||||
methodsig=JavaEventPipe.readString(size3)
|
||||
else
|
||||
methodsig=''
|
||||
end
|
||||
|
||||
local endpos=classname:match'^.*();'
|
||||
if endpos~=nil then
|
||||
classname=string.sub(classname,1,endpos-1)
|
||||
end
|
||||
local name=classname.."::"..methodname..methodsig
|
||||
|
||||
|
||||
print(string.format("Methodload: %s - (%x) %x-%x", name, method, code_addr, code_addr+code_size))
|
||||
JavaSymbols.addSymbol("",classname.."::"..methodname,code_addr,code_size)
|
||||
|
||||
--print(string.format("s1=%d s2=%d s3=%d (cn=%s mn=%s ms=%s)", size1,size2,size3, classname, methodname, methodsig))
|
||||
|
||||
--print(string.format("Methodload: %s - (%x) %x-%x", name, method, code_addr, code_addr+code_size))
|
||||
|
||||
|
||||
--
|
||||
elseif command==EVENTCMD_METHODUNLOAD then --methodunload
|
||||
local method=JavaEventPipe.readQword()
|
||||
local code_addr=JavaEventPipe.readQword()
|
||||
|
||||
print("EVENTCMD_METHODUNLOAD")
|
||||
JavaSymbols.deleteSymbol(code_addr)
|
||||
--
|
||||
elseif command==EVENTCMD_DYNAMICCODEGENERATED then --DynamicCodeGenerated
|
||||
local ssize
|
||||
@ -385,12 +454,16 @@ function JavaEventListener(thread)
|
||||
ssize=JavaEventPipe.readWord()
|
||||
local name=JavaEventPipe.readString(ssize)
|
||||
|
||||
print(string.format("DynamicCode: %s - %x-%x", name, address, address+length))
|
||||
--print(string.format("DynamicCode: %s - %x-%x", name, address, address+length))
|
||||
JavaSymbols.addSymbol("",name,address,length)
|
||||
|
||||
|
||||
--
|
||||
elseif command==JAVACODECMD_TERMINATED then
|
||||
--print("eventserver terminated")
|
||||
break
|
||||
elseif command==nil then
|
||||
print("Disconnected")
|
||||
--print("Disconnected")
|
||||
break
|
||||
else
|
||||
print("Unexpected event received") --synchronize isn't necesary for print as that function is designed to synchronize internally
|
||||
@ -406,12 +479,246 @@ function java_StartListeneningForEvents()
|
||||
javapipe.writeByte(JAVACMD_STARTCODECALLBACKS)
|
||||
|
||||
|
||||
--the javapipe will now be frozen until a the javaeventpipe makes an connection
|
||||
JavaEventListener_Terminated=false
|
||||
JavaEventThread=createNativeThread(JavaEventListener);
|
||||
--the javapipe will now be frozen until a javaeventpipe makes an connection
|
||||
createNativeThread(JavaEventListener);
|
||||
|
||||
local result=javapipe.readByte()==1
|
||||
javapipe.unlock();
|
||||
end
|
||||
|
||||
function java_getLoadedClasses()
|
||||
javapipe.lock()
|
||||
javapipe.writeByte(JAVACMD_GETLOADEDCLASSES)
|
||||
|
||||
local classcount=javapipe.readDword()
|
||||
local classes={}
|
||||
|
||||
if (classcount==nil) then
|
||||
return nil
|
||||
end
|
||||
|
||||
if classcount>0 then
|
||||
local i=0
|
||||
local length
|
||||
for i=1,classcount do
|
||||
classes[i]={}
|
||||
classes[i].jclass=javapipe.readQword() --this is a pointer to a pointer to java.lang.class. To get the offset where klass is stored use getFieldFromType("java_lang_Class", "_klass_offset") (The klass contains a _fields field which points to a array which contains the offset of the fields. Might be useful)
|
||||
length=javapipe.readWord()
|
||||
classes[i].signature=javapipe.readString(length);
|
||||
|
||||
length=javapipe.readWord()
|
||||
classes[i].generic=javapipe.readString(length);
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
javapipe.unlock()
|
||||
|
||||
return classes
|
||||
end
|
||||
|
||||
function java_dereferenceLocalObject(object)
|
||||
javapipe.lock()
|
||||
javapipe.writeByte(JAVACMD_DEREFERENCELOCALOBJECT)
|
||||
javapipe.writeQword(object)
|
||||
javapipe.unlock()
|
||||
end
|
||||
|
||||
function java_cleanClasslist(classlist)
|
||||
local i
|
||||
for i=1, #classlist do
|
||||
java_dereferenceLocalObject(classlist[i].jclass)
|
||||
end
|
||||
end
|
||||
|
||||
function java_getClassMethods(class)
|
||||
javapipe.lock()
|
||||
javapipe.writeByte(JAVACMD_GETCLASSMETHODS)
|
||||
javapipe.writeQword(class)
|
||||
local count=javapipe.readDword()
|
||||
local i
|
||||
local result={}
|
||||
local length
|
||||
for i=1,count do
|
||||
result[i]={}
|
||||
result[i].jmethodid=javapipe.readQword()
|
||||
|
||||
length=javapipe.readWord()
|
||||
result[i].name=javapipe.readString(length)
|
||||
|
||||
length=javapipe.readWord()
|
||||
result[i].signature=javapipe.readString(length)
|
||||
|
||||
length=javapipe.readWord()
|
||||
result[i].generic=javapipe.readString(length)
|
||||
end
|
||||
javapipe.unlock()
|
||||
return result
|
||||
end
|
||||
|
||||
function java_getClassFields(class)
|
||||
javapipe.lock()
|
||||
javapipe.writeByte(JAVACMD_GETCLASSFIELDS)
|
||||
javapipe.writeQword(class)
|
||||
local count=javapipe.readDword()
|
||||
local i
|
||||
local result={}
|
||||
local length
|
||||
for i=1,count do
|
||||
result[i]={}
|
||||
result[i].jfieldid=javapipe.readQword()
|
||||
|
||||
length=javapipe.readWord()
|
||||
result[i].name=javapipe.readString(length)
|
||||
|
||||
length=javapipe.readWord()
|
||||
result[i].signature=javapipe.readString(length)
|
||||
|
||||
length=javapipe.readWord()
|
||||
result[i].generic=javapipe.readString(length)
|
||||
end
|
||||
javapipe.unlock()
|
||||
return result
|
||||
end
|
||||
|
||||
function miJavaActivateClick(sender)
|
||||
javaInjectAgent()
|
||||
end
|
||||
|
||||
function javaForm_treeviewExpanding(sender, node)
|
||||
local allow=true
|
||||
|
||||
print("javaForm_treeviewExpanding "..node.level)
|
||||
if node.Level==0 then
|
||||
if node.Count==0 then
|
||||
--expand the class this node describes
|
||||
local jklass=node.Data
|
||||
local methods=java_getClassMethods(jklass);
|
||||
local fields=java_getClassFields(jklass);
|
||||
|
||||
local i
|
||||
for i=1, #fields do
|
||||
node.add(string.format("%x: %s: %s (%s)", fields[i].jfieldid, fields[i].name, fields[i].signature,fields[i].generic))
|
||||
end
|
||||
|
||||
node.add('---');
|
||||
|
||||
|
||||
for i=1, #methods do
|
||||
node.add(string.format("%x: %s(%s) %s", methods[i].jmethodid, methods[i].name, methods[i].signature, methods[i].generic))
|
||||
end
|
||||
|
||||
|
||||
--java_getClassFields(jklass);
|
||||
end
|
||||
end
|
||||
|
||||
return allow
|
||||
end
|
||||
|
||||
function miJavaDissectClick(sender)
|
||||
--I coudl also implement the same method as mono, but as an example I'll be creating the form with code only
|
||||
if (javaForm==nil) then
|
||||
javaForm={}
|
||||
javaForm.form=createForm()
|
||||
javaForm.form.Borderstyle=bsSizeable
|
||||
javaForm.treeview=createTreeview(javaForm.form)
|
||||
javaForm.treeview.align=alClient
|
||||
javaForm.treeview.OnExpanding=javaForm_treeviewExpanding
|
||||
end
|
||||
|
||||
if (java_classlist~=nil) then
|
||||
java_cleanClasslist(java_classlist) --prevent a memory leak
|
||||
end
|
||||
java_classlist=java_getLoadedClasses()
|
||||
|
||||
if (java_classlist~=nil) then
|
||||
local i
|
||||
for i=1,#java_classlist do
|
||||
local node=javaForm.treeview.Items.Add(string.format("%x : %s (%s)", java_classlist[i].jclass, java_classlist[i].signature, java_classlist[i].generic ))
|
||||
|
||||
node.Data=java_classlist[i].jclass
|
||||
node.HasChildren=true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
javaForm.form.show()
|
||||
|
||||
end
|
||||
|
||||
function java_OpenProcessAfterwards()
|
||||
local usesjava=false
|
||||
local m=enumModules()
|
||||
local i
|
||||
|
||||
java_classlist=nil
|
||||
|
||||
for i=1, #m do
|
||||
if m[i].Name=='jvm.dll' then
|
||||
usesjava=true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if usesjava then
|
||||
if (miJavaTopMenuItem==nil) then
|
||||
local mfm=getMainForm().Menu
|
||||
local mi
|
||||
miJavaTopMenuItem=createMenuItem(mfm)
|
||||
miJavaTopMenuItem.Caption="Java"
|
||||
mfm.Items.insert(mfm.Items.Count-1, miJavaTopMenuItem) --add it before help
|
||||
|
||||
mi=createMenuItem(miJavaTopMenuItem)
|
||||
mi.Caption="Activate java features"
|
||||
mi.OnClick=miJavaActivateClick
|
||||
miJavaTopMenuItem.Add(mi)
|
||||
|
||||
mi=createMenuItem(miJavaTopMenuItem)
|
||||
mi.Caption="Dissect java"
|
||||
mi.Shortcut="Ctrl+Alt+J"
|
||||
mi.OnClick=miJavaDissectClick
|
||||
miJavaTopMenuItem.Add(mi)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function java_OpenProcess(processid)
|
||||
if java_oldOnOpenProcess~=nil then
|
||||
java_oldOnOpenProcess(processid)
|
||||
end
|
||||
|
||||
synchronize(java_OpenProcessAfterwards) --call this function when the whole OpenProcess routine is done (next sync check)
|
||||
end
|
||||
|
||||
function javaAA_USEJAVA(parameters, syntaxcheckonly)
|
||||
--called whenever an auto assembler script encounters the USEMONO() line
|
||||
--the value you return will be placed instead of the given line
|
||||
--In this case, returning a empty string is fine
|
||||
--Special behaviour: Returning nil, with a secondary parameter being a string, will raise an exception on the auto assembler with that string
|
||||
|
||||
|
||||
if (syntaxcheckonly==false) and (javaInjectAgent()==0) then
|
||||
return nil,"The java handler failed to initialize"
|
||||
end
|
||||
|
||||
return "" --return an empty string (removes it from the internal aa assemble list)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function java_initialize()
|
||||
--register a function to be called when a process is opened
|
||||
if (java_init1==nil) then
|
||||
java_init1=true
|
||||
java_oldOnOpenProcess=onOpenProcess
|
||||
onOpenProcess=java_OpenProcess
|
||||
|
||||
registerAutoAssemblerCommand("USEJAVA", javaAA_USEJAVA)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
java_initialize()
|
||||
|
Loading…
Reference in New Issue
Block a user