Add a "find what writes" to java

This commit is contained in:
cheatengine@gmail.com 2014-04-26 00:17:03 +00:00
parent 1ccde5a07e
commit f8c96e7e70
5 changed files with 324 additions and 25 deletions

View File

@ -26,6 +26,12 @@ void JNICALL DynamicCodeGenerated(jvmtiEnv *jvmti_env, const char* name, const v
{
if (eventserver)
eventserver->DynamicCodeGenerated(jvmti_env, name, address,length);
}
void JNICALL FieldModification(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location, jclass field_klass, jobject object, jfieldID field, char signature_type, jvalue new_value)
{
if (eventserver)
eventserver->FieldModification(jvmti_env, jni_env, thread, method, location, field_klass, object, field, signature_type, new_value);
}
@ -76,6 +82,7 @@ CJavaEventServer::CJavaEventServer(jvmtiEnv *jvmti_env)
callbacks.CompiledMethodLoad=::MethodLoad;
callbacks.CompiledMethodUnload=::MethodUnload;
callbacks.DynamicCodeGenerated=::DynamicCodeGenerated;
callbacks.FieldModification=::FieldModification;
error=jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
@ -83,7 +90,7 @@ CJavaEventServer::CJavaEventServer(jvmtiEnv *jvmti_env)
{
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);
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);
}
@ -151,7 +158,7 @@ OutputDebugStringA("MethodLoad");
if (classsig)
{
len=strlen(classsig);
len=(WORD)strlen(classsig);
WriteWord(len);
if (len)
Write(classsig, len);
@ -168,7 +175,7 @@ OutputDebugStringA("MethodLoad");
if (name)
{
len=strlen(name);
len=(WORD)strlen(name);
WriteWord(len);
if (len)
Write(name, len);
@ -180,7 +187,7 @@ OutputDebugStringA("MethodLoad");
if (sig)
{
len=strlen(sig);
len=(WORD)strlen(sig);
WriteWord(len);
if (len)
Write(sig, len);
@ -231,8 +238,8 @@ void CJavaEventServer::DynamicCodeGenerated(jvmtiEnv *jvmti_env, const char* nam
WriteByte(EVENTCMD_DYNAMICCODEGENERATED);
WriteQword((UINT_PTR)address);
WriteDword(length);
WriteWord(strlen(name));
Write((void *)name, strlen(name));
WriteWord((WORD)strlen(name));
Write((void *)name, (int)strlen(name));
}
catch (char *e)
@ -274,3 +281,103 @@ void CJavaEventServer::Terminate(void)
Sleep(500);
}
void CJavaEventServer::FieldModification(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location, jclass field_klass, jobject object, jfieldID field, char signature_type, jvalue new_value)
{
unsigned int i;
//go through the list and check which one caused this
for (i=0; i<FindWhatWritesList.size(); i++)
{
PFindWhatWritesEntry e=FindWhatWritesList[i];
if (e)
{
if ((e->fieldid==field) && ((object==NULL) || (jni_env->IsSameObject(e->object, object))))
{
//signal ce
Lock();
try
{
jvmtiFrameInfo frames[10];
jint count;
WriteByte(EVENTCMD_FIELDMODIFICATION);
WriteDword(i); //id
WriteQword((UINT_PTR)method);
WriteQword((UINT_PTR)location);
//and a stack snapshot as well
if (jvmti_env->GetStackTrace(thread, 0, 10, frames, &count)==JVMTI_ERROR_NONE)
{
int i;
WriteByte((BYTE)count);
for (i=0; i<count; i++)
{
WriteQword((UINT_PTR)frames[i].method);
WriteQword((UINT_PTR)frames[i].location);
}
}
else
WriteByte(0);
}
catch (char *e)
{
OutputDebugStringA(e);
//no connection yet
}
Unlock();
}
}
}
}
int CJavaEventServer::RegisterFindWhatWrites(jobject object, jclass klass, jfieldID fieldid)
{
int id=-1;
//find an usable entry in the FindWhatWritesList
unsigned int i;
for (i=0; i<FindWhatWritesList.size(); i++)
{
if (FindWhatWritesList[i]==NULL)
{
id=i;
break;
}
}
if (id==-1)
{
PFindWhatWritesEntry newentry=(PFindWhatWritesEntry)malloc(sizeof(FindWhatWritesEntry));
newentry->klass=klass;
newentry->fieldid=fieldid;
newentry->object=object;
FindWhatWritesList.push_back(newentry);
id=FindWhatWritesList.size()-1;
}
jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_FIELD_MODIFICATION, NULL);
if (jvmti_env->SetFieldModificationWatch(klass, fieldid)!=JVMTI_ERROR_NONE)
UnregisterFindWhatWrites(id);
return id;
}
void CJavaEventServer::UnregisterFindWhatWrites(int id)
{
if (FindWhatWritesList[id])
{
free(FindWhatWritesList[id]);
FindWhatWritesList[id]=NULL;
}
}

View File

@ -6,8 +6,17 @@
#define EVENTCMD_METHODLOAD 0
#define EVENTCMD_METHODUNLOAD 1
#define EVENTCMD_DYNAMICCODEGENERATED 2
#define EVENTCMD_FIELDMODIFICATION 3
#define EVENTCMD_TERMINATED 255
using namespace std;
typedef struct
{
jfieldID fieldid;
jclass klass;
jobject object;
} FindWhatWritesEntry, *PFindWhatWritesEntry;
class CJavaEventServer :
public Pipe
@ -15,6 +24,7 @@ class CJavaEventServer :
private:
wchar_t pipename[256];
jvmtiEnv *jvmti_env;
vector<PFindWhatWritesEntry> FindWhatWritesList;
public:
CJavaEventServer(jvmtiEnv *jvmti_env);
@ -23,7 +33,9 @@ public:
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 FieldModification(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location, jclass field_klass, jobject object, jfieldID field, char signature_type, jvalue new_value);
int RegisterFindWhatWrites(jobject object, jclass klass, jfieldID fieldid);
void UnregisterFindWhatWrites(int id);
void Terminate(void);
};

View File

@ -1771,6 +1771,36 @@ void CJavaServer::GetScanResults(void)
WriteQword(0); //end of the list
}
void CJavaServer::FindWhatWrites(void)
{
int id=-1;
jobject object=(jobject)ReadQword();
jfieldID fieldid=(jfieldID)ReadQword();
//register a watch on the given field id
if (eventserver)
{
jclass _klass=jni->GetObjectClass(object);
jclass klass=(jclass)jni->NewGlobalRef(_klass);//don't forget to destroy this once unregistered
jni->DeleteLocalRef(_klass);
if (klass)
id=eventserver->RegisterFindWhatWrites(object, klass, fieldid);
}
WriteDword(id);
}
void CJavaServer::StopFindWhatWrites(void)
{
if (eventserver)
eventserver->UnregisterFindWhatWrites(ReadDword());
}
void CJavaServer::Start(void)
{
@ -1906,6 +1936,14 @@ void CJavaServer::Start(void)
GetScanResults();
break;
case JAVACMD_FINDWHATWRITES:
FindWhatWrites();
break;
case JAVACMD_STOPFINDWHATWRITES:
StopFindWhatWrites();
break;
default:
throw("Unexpected command\n");
break;

View File

@ -32,6 +32,8 @@
#define JAVACMD_STARTSCAN 27
#define JAVACMD_REFINESCANRESULTS 28
#define JAVACMD_GETSCANRESULTS 29
#define JAVACMD_FINDWHATWRITES 30
#define JAVACMD_STOPFINDWHATWRITES 31
@ -91,4 +93,7 @@ public:
void RefineScanResults(void);
void GetScanResults(void);
void FindWhatWrites(void);
void StopFindWhatWrites(void);
};

View File

@ -29,7 +29,8 @@ JAVACMD_SETFIELD=26
JAVACMD_STARTSCAN=27
JAVACMD_REFINESCANRESULTS=28
JAVACMD_GETSCANRESULTS=29
JAVACMD_FINDWHATWRITES=30
JAVACMD_STOPFINDWHATWRITES=31
@ -432,6 +433,7 @@ function JavaEventListener(thread)
local EVENTCMD_METHODLOAD=0
local EVENTCMD_METHODUNLOAD=1
local EVENTCMD_DYNAMICCODEGENERATED=2
local EVENTCMD_FIELDMODIFICATION=3
local JavaEventPipe
@ -510,11 +512,78 @@ function JavaEventListener(thread)
--
elseif command==EVENTCMD_FIELDMODIFICATION then
print("EVENTCMD_FIELDMODIFICATION")
local id=JavaEventPipe.readDword()
local entry={}
print("id=="..id)
entry.methodid=JavaEventPipe.readQword()
entry.location=JavaEventPipe.readQword()
local stackcount=JavaEventPipe.readByte()
local i
local stack={}
print("stackcount=="..stackcount)
for i=1, stackcount do
stack[i]={}
stack[i].methodid=JavaEventPipe.readQword()
stack[i].location=JavaEventPipe.readQword()
end
entry.stack=stack
if java.findwhatwriteslist~=nil then
local fcd=java.findwhatwriteslist[id]
if fcd~=nil then
--check if this entry is already in the list
local found=false
for i=1, #fcd.entries do
if (fcd.entries[i].methodid==entry.methodid) and (fcd.entries[i].location==entry.location) then
found=true
break
end
end
if not found then
--if not, add it
print("adding to the list")
tventry=fcd.lv.items.add()
local mname=java_getMethodName(entry.methodid)
tventry.Caption='...'
tventry.SubItems.add(string.format('%x: %s', entry.methodid, mname))
tventry.SubItems.add(entry.position)
table.insert(fcd.entries, entry)
else
print("Already in the list")
end
else
print("fcd==nil")
end
else
print("java.findwhatwriteslist==nil")
end
print("done")
elseif command==JAVACODECMD_TERMINATED then
--print("eventserver terminated")
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
@ -522,6 +591,7 @@ function JavaEventListener(thread)
end
end
print("Event handler terminating")
JavaEventPipe.destroy();
end
@ -530,21 +600,21 @@ function java_getCapabilities()
javapipe.lock()
javapipe.writeByte(JAVACMD_GETCAPABILITIES)
result.can_access_local_variables=javapipe.readByte()
result.can_generate_all_class_hook_events=javapipe.readByte()
result.can_generate_breakpoint_events=javapipe.readByte()
result.can_generate_compiled_method_load_events=javapipe.readByte()
result.can_generate_field_access_events=javapipe.readByte()
result.can_generate_field_modification_events=javapipe.readByte()
result.can_generate_single_step_events=javapipe.readByte()
result.can_get_bytecodes=javapipe.readByte()
result.can_get_constant_pool=javapipe.readByte()
result.can_maintain_original_method_order=javapipe.readByte()
result.can_redefine_any_class=javapipe.readByte()
result.can_redefine_classes=javapipe.readByte()
result.can_retransform_any_class=javapipe.readByte()
result.can_retransform_classes=javapipe.readByte()
result.can_tag_objects=javapipe.readByte()
result.can_access_local_variables=javapipe.readByte()==1
result.can_generate_all_class_hook_events=javapipe.readByte()==1
result.can_generate_breakpoint_events=javapipe.readByte()==1
result.can_generate_compiled_method_load_events=javapipe.readByte()==1
result.can_generate_field_access_events=javapipe.readByte()==1
result.can_generate_field_modification_events=javapipe.readByte()==1
result.can_generate_single_step_events=javapipe.readByte()==1
result.can_get_bytecodes=javapipe.readByte()==1
result.can_get_constant_pool=javapipe.readByte()==1
result.can_maintain_original_method_order=javapipe.readByte()==1
result.can_redefine_any_class=javapipe.readByte()==1
result.can_redefine_classes=javapipe.readByte()==1
result.can_retransform_any_class=javapipe.readByte()==1
result.can_retransform_classes=javapipe.readByte()==1
result.can_tag_objects=javapipe.readByte()==1
javapipe.unlock()
@ -1267,6 +1337,73 @@ function java_search_finish()
end
function java_find_what_writes(object, fieldid)
local id=nil
if java.capabilities.can_generate_field_modification_events then
--spawn a window to receive the data
javapipe.lock()
javapipe.writeByte(JAVACMD_FINDWHATWRITES)
javapipe.writeQword(object)
javapipe.writeQword(fieldid)
id=javapipe.readDword()
print("id="..id)
javapipe.unlock()
local fcd={} --found code dialog
fcd.form=createForm()
fcd.form.width=400
fcd.form.height=300
fcd.form.Position=poScreenCenter
fcd.form.BorderStyle=bsSizeable
fcd.form.caption='The following methods accessed the given variable'
fcd.lv=createListView(fcd.form)
fcd.lv.Name='results';
fcd.lv.Align=alClient
fcd.lv.ViewStyle=vsReport
fcd.lv.ReadOnly=true
fcd.lv.RowSelect=true
local c=fcd.lv.Columns.add()
c.caption='Class'
c.width=100
c=fcd.lv.Columns.add()
c.caption='Method'
c.width=100
c=fcd.lv.Columns.add()
c.caption='Position'
c.autosize=true
fcd.entries={}
if java.findwhatwriteslist==nil then
java.findwhatwriteslist={}
end
java.findwhatwriteslist[id]=fcd
else
error('java_find_what_writes only works when the jvmti agent is launched at start')
end
return id
end
function java_stop_find_what_writes(id)
javapipe.lock()
javapipe.writeByte(JAVACMD_STOPFINDWHATWRITES)
javapipe.writeDword(id)
javapipe.unlock()
end
function java_getObjectHandleToAddress(address)
local result=0