struct _JPLISAgent { JavaVM * mJVM; /* handle to the JVM */ JPLISEnvironment mNormalEnvironment; /* for every thing but retransform stuff */ JPLISEnvironment mRetransformEnvironment;/* for retransform stuff only */ jobject mInstrumentationImpl; /* handle to the Instrumentation instance */ jmethodID mPremainCaller; /* method on the InstrumentationImpl that does the premain stuff (cached to save lots of lookups) */ jmethodID mAgentmainCaller; /* method on the InstrumentationImpl for agents loaded via attach mechanism */ jmethodID mTransform; /* method on the InstrumentationImpl that does the class file transform */ jboolean mRedefineAvailable; /* cached answer to "does this agent support redefine" */ jboolean mRedefineAdded; /* indicates if can_redefine_classes capability has been added */ jboolean mNativeMethodPrefixAvailable; /* cached answer to "does this agent support prefixing" */ jboolean mNativeMethodPrefixAdded; /* indicates if can_set_native_method_prefix capability has been added */ char const * mAgentClassName; /* agent class name */ char const * mOptionsString; /* -javaagent options string */ }; struct _JPLISEnvironment { jvmtiEnv * mJVMTIEnv; /* the JVM TI environment */ JPLISAgent * mAgent; /* corresponding agent */ jboolean mIsRetransformer; /* indicates if special environment */ };
在startJavaAgent的方法中调用了启动JPLISAgent的方式,我们来看invokeJavaAgentMainMethod
jboolean invokeJavaAgentMainMethod( JNIEnv * jnienv, jobject instrumentationImpl, jmethodID mainCallingMethod, jstring className, jstring optionsString) { jboolean errorOutstanding = JNI_FALSE; jplis_assert(mainCallingMethod != NULL); if ( mainCallingMethod != NULL ) { (*jnienv)->CallVoidMethod( jnienv, instrumentationImpl, mainCallingMethod, className, optionsString); errorOutstanding = checkForThrowable(jnienv); if ( errorOutstanding ) { logThrowable(jnienv); } checkForAndClearThrowable(jnienv); } return !errorOutstanding; }
在函数里,实际上是在调用java类sun.instrument.InstrumentationImpl 类里的方法loadClassAndCallPremain
回到JAVA的sun.instrument.InstrumentationImpl类的方法loadclassandstartagent
private void loadClassAndStartAgent( String classname, String methodname, String optionsString) throws Throwable { ... try { m = javaAgentClass.getDeclaredMethod( methodname, new Class<?>[] { String.class, java.lang.instrument.Instrumentation.class } ); twoArgAgent = true; } catch (NoSuchMethodException x) { // remember the NoSuchMethodException firstExc = x; } if (m == null) { // now try the declared 1-arg method try { m = javaAgentClass.getDeclaredMethod(methodname, new Class<?>[] { String.class }); } catch (NoSuchMethodException x) { // ignore this exception because we'll try // two arg inheritance next } } if (m == null) { // now try the inherited 2-arg method try { m = javaAgentClass.getMethod( methodname, new Class<?>[] { String.class, java.lang.instrument.Instrumentation.class } ); twoArgAgent = true; } catch (NoSuchMethodException x) { // ignore this exception because we'll try // one arg inheritance next } } if (m == null) { // finally try the inherited 1-arg method try { m = javaAgentClass.getMethod(methodname, new Class<?>[] { String.class }); } catch (NoSuchMethodException x) { // none of the methods exists so we throw the // first NoSuchMethodException as per 5.0 throw firstExc; } } // the premain method should not be required to be public, // make it accessible so we can call it // Note: The spec says the following: // The agent class must implement a public static premain method... setAccessible(m, true); // invoke the 1 or 2-arg method if (twoArgAgent) { m.invoke(null, new Object[] { optionsString, this }); } else { m.invoke(null, new Object[] { optionsString }); } // don't let others access a non-public premain method setAccessible(m, false); }
在InstrumentationImpl的类中初始化了我们自定义的Transformer的premain方法
public class MyInjectTransformer implements ClassFileTransformer{ public static void premain(String options, Instrumentation ins) { ins.addTransformer(new SQLInjectTransformer()); } @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { return null; } }
钩子函数eventHandlerClassFileLoadHook
void JNICALL eventHandlerClassFileLoadHook( jvmtiEnv * jvmtienv, JNIEnv * jnienv, jclass class_being_redefined, jobject loader, const char* name, jobject protectionDomain, jint class_data_len, const unsigned char* class_data, jint* new_class_data_len, unsigned char** new_class_data) { JPLISEnvironment * environment = NULL; environment = getJPLISEnvironment(jvmtienv); /* if something is internally inconsistent (no agent), just silently return without touching the buffer */ if ( environment != NULL ) { jthrowable outstandingException = preserveThrowable(jnienv); transformClassFile( environment->mAgent, jnienv, loader, name, class_being_redefined, protectionDomain, class_data_len, class_data, new_class_data_len, new_class_data, environment->mIsRetransformer); restoreThrowable(jnienv, outstandingException); } }重要的是transformClassFile函数,我们来看看它究竟做了啥事情
transformedBufferObject = (*jnienv)->CallObjectMethod( jnienv, agent->mInstrumentationImpl, agent->mTransform, loaderObject, classNameStringObject, classBeingRedefined, protectionDomain, classFileBufferObject, is_retransformer);