├── .gitignore ├── CustomClassLoaderJniDemo ├── MyService.java ├── encrypt_classes │ └── MyService.class_ ├── pom.xml └── src │ └── main │ ├── c │ ├── Makefile │ ├── decrypt.cpp │ └── decrypt.h │ └── java │ └── me │ └── ya │ └── classloader │ └── MyCustomClassLoader.java ├── README.md ├── crack-demo-jar └── crack-demo.jar ├── jsr269 ├── my-anno-processor-jar │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── me │ │ └── ya │ │ └── annotation │ │ ├── MyAnnotationProcessor.java │ │ └── MyBuilder.java └── my-anno-processor-test │ ├── pom.xml │ └── src │ └── main │ └── java │ └── me │ └── ya │ └── annotation │ └── test │ ├── MyTestMain.java │ └── User.java ├── jvmti-agent ├── Makefile ├── agent.cpp └── agent2.cpp ├── jvmti-encrypt ├── pom.xml └── src │ └── main │ └── java │ └── Encrypt.java ├── my-attach-demo-test ├── pom.xml └── src │ └── main │ └── java │ ├── MyAttachMain.java │ └── MyTestMain.java ├── my-attach-demo ├── pom.xml └── src │ └── main │ └── java │ └── me │ └── ya │ └── agent │ └── AgentMain.java ├── my-trace-agent-test ├── pom.xml └── src │ └── main │ └── java │ └── MyTest.java └── my-trace-agent ├── pom.xml └── src └── main └── java └── me └── ya └── agent └── AgentMain.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | dependency-reduced-pom.xml 4 | *.iml 5 | *.class 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /CustomClassLoaderJniDemo/MyService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created By Arthur Zhang at 2019/10/10 3 | */ 4 | public class MyService { 5 | public MyService() { 6 | System.out.println("init MyService"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /CustomClassLoaderJniDemo/encrypt_classes/MyService.class_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arthur-zhang/jvm-bytecode-book-example/4f639e4befc8ff76570adede46ea65a275bb4a8f/CustomClassLoaderJniDemo/encrypt_classes/MyService.class_ -------------------------------------------------------------------------------- /CustomClassLoaderJniDemo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | CustomClassLoaderDemo 8 | CustomClassLoaderDemo 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.apache.maven.plugins 14 | maven-compiler-plugin 15 | 16 | 6 17 | 6 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | commons-io 27 | commons-io 28 | 2.6 29 | 30 | 31 | 32 | 33 | commons-codec 34 | commons-codec 35 | 1.13 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /CustomClassLoaderJniDemo/src/main/c/Makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | JAVA_HOME = /Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home 3 | 4 | ifeq ($(shell uname), Darwin) 5 | LIBLINK = -dynamiclib 6 | TARGET = libdecrypt.dylib 7 | INCLUDEDIR := -I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/darwin/ 8 | endif 9 | 10 | ifeq ($(shell uname), Linux) 11 | LIBLINK = -shared -fPIC 12 | TARGET = libdecrypt.so 13 | INCLUDEDIR := -I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/ 14 | endif 15 | 16 | all: 17 | $(CC) $(LIBLINK) $(INCLUDEDIR) decrypt.cpp -o $(TARGET) 18 | 19 | clean: 20 | rm $(TARGET) 21 | 22 | -------------------------------------------------------------------------------- /CustomClassLoaderJniDemo/src/main/c/decrypt.cpp: -------------------------------------------------------------------------------- 1 | #include "decrypt.h" 2 | extern "C" 3 | JNIEXPORT jbyteArray JNICALL 4 | Java_me_ya_classloader_MyCustomClassLoader_decryptJni(JNIEnv *env, jclass clazz, 5 | jbyteArray src_bytes) { 6 | jbyte *src = env->GetByteArrayElements(src_bytes, 0); 7 | // 获取 byte 数组的长度 8 | jsize src_Len = env->GetArrayLength(src_bytes); 9 | jbyte buf[src_Len]; 10 | 11 | // 执行 XOR 运算 12 | for (int i = 0; i < src_Len; ++i) { 13 | buf[i] = src[i] ^ 0xFF; 14 | } 15 | 16 | env->ReleaseByteArrayElements(src_bytes, src, 0); 17 | jbyteArray result = env->NewByteArray(src_Len); 18 | env->SetByteArrayRegion(result, 0, src_Len, buf); 19 | return result; 20 | } -------------------------------------------------------------------------------- /CustomClassLoaderJniDemo/src/main/c/decrypt.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class me_ya_classloader_MyCustomClassLoader */ 4 | 5 | #ifndef _Included_me_ya_classloader_MyCustomClassLoader 6 | #define _Included_me_ya_classloader_MyCustomClassLoader 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: me_ya_classloader_MyCustomClassLoader 12 | * Method: decryptJni 13 | * Signature: ([B)[B 14 | */ 15 | JNIEXPORT jbyteArray JNICALL Java_me_ya_classloader_MyCustomClassLoader_decryptJni 16 | (JNIEnv *, jclass, jbyteArray); 17 | 18 | #ifdef __cplusplus 19 | } 20 | #endif 21 | #endif 22 | -------------------------------------------------------------------------------- /CustomClassLoaderJniDemo/src/main/java/me/ya/classloader/MyCustomClassLoader.java: -------------------------------------------------------------------------------- 1 | package me.ya.classloader; 2 | import org.apache.commons.io.FileUtils; 3 | 4 | import java.io.File; 5 | import java.io.IOException; 6 | 7 | /** 8 | * Created By Arthur Zhang at 2019/10/9 9 | */ 10 | public class MyCustomClassLoader extends ClassLoader { 11 | static { 12 | System.load("/Users/arthur/cvt_dev/java/CustomClassLoaderJniDemo/src/main/c/libdecrypt.dylib"); 13 | } 14 | 15 | public static native byte[] decryptJni(byte[] bytes); 16 | 17 | @Override 18 | protected Class findClass(String name) { 19 | byte[] bytes = getClassFileBytesInDir(name); 20 | byte[] decodedBytes = decryptJni(bytes); 21 | return defineClass(name, decodedBytes, 0, bytes.length); 22 | } 23 | 24 | private static byte[] getClassFileBytesInDir(String className) { 25 | try { 26 | return FileUtils.readFileToByteArray(new File("encrypt_classes" + "/" + className + ".class_")); 27 | } catch (IOException e) { 28 | e.printStackTrace(); 29 | } 30 | throw new RuntimeException(""); 31 | } 32 | 33 | public static void main(String[] args) throws Exception { 34 | ClassLoader classLoader = new MyCustomClassLoader(); 35 | Class clz = classLoader.loadClass("MyService"); 36 | clz.newInstance(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jvm-bytecode-book-example 2 | -------------------------------------------------------------------------------- /crack-demo-jar/crack-demo.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arthur-zhang/jvm-bytecode-book-example/4f639e4befc8ff76570adede46ea65a275bb4a8f/crack-demo-jar/crack-demo.jar -------------------------------------------------------------------------------- /jsr269/my-anno-processor-jar/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | my-anno-processor-jar 8 | my-anno-processor-jar 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | org.apache.maven.plugins 15 | maven-compiler-plugin 16 | 17 | 1.8 18 | 1.8 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /jsr269/my-anno-processor-jar/src/main/java/me/ya/annotation/MyAnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | package me.ya.annotation; 2 | 3 | import javax.annotation.processing.AbstractProcessor; 4 | import javax.annotation.processing.RoundEnvironment; 5 | import javax.annotation.processing.SupportedAnnotationTypes; 6 | import javax.annotation.processing.SupportedSourceVersion; 7 | import javax.lang.model.SourceVersion; 8 | import javax.lang.model.element.Element; 9 | import javax.lang.model.element.TypeElement; 10 | import javax.tools.JavaFileObject; 11 | import java.io.IOException; 12 | import java.io.PrintWriter; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.Set; 16 | 17 | @SupportedAnnotationTypes({"me.ya.annotation.MyBuilder"}) 18 | @SupportedSourceVersion(value = SourceVersion.RELEASE_8) 19 | public class MyAnnotationProcessor extends AbstractProcessor { 20 | 21 | @Override 22 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 23 | for (TypeElement typeElement : annotations) { 24 | // typeElement 表示 MyBuilder 注解 25 | Set annotatedElements = roundEnv.getElementsAnnotatedWith(typeElement); 26 | // 包含 MyBuilder 注解的所有字段名和字段类型映射表 27 | Map fieldMap = new HashMap<>(); 28 | for (Element element : annotatedElements) { 29 | fieldMap.put(element.getSimpleName().toString(), element.asType().toString()); 30 | } 31 | // 获取到 MyBuilder 注解所在类的全限定名 32 | String className = ((TypeElement) annotatedElements.iterator().next().getEnclosingElement()).getQualifiedName().toString(); 33 | String packageName = null; 34 | int lastDot = className.lastIndexOf('.'); 35 | if (lastDot > 0) packageName = className.substring(0, lastDot); 36 | // 不包含包名的 class 名 37 | String simpleClassName = className.substring(lastDot + 1); 38 | try { 39 | generateBuilder(simpleClassName, packageName, fieldMap); 40 | } catch (IOException e) { 41 | } 42 | } 43 | return true; 44 | } 45 | 46 | private void generateBuilder(String simpleClassName, String packageName, Map fieldMap) throws IOException { 47 | String builderClassName = simpleClassName + "Builder"; 48 | JavaFileObject builderFile = processingEnv.getFiler().createSourceFile(builderClassName); 49 | 50 | StringBuilder sb = new StringBuilder(); 51 | try (PrintWriter out = new PrintWriter(builderFile.openWriter())) { 52 | sb.append( 53 | "package " + packageName + ";\n" + 54 | "public class " + builderClassName + " {\n" + 55 | " private " + simpleClassName + " object = new " + simpleClassName + "();\n" + 56 | "\n" + 57 | " public " + simpleClassName + " build() {\n" + 58 | " return object;\n" + 59 | " }\n"); 60 | fieldMap.forEach((methodName, argumentType) -> { 61 | String setMethodName = "set" + capitalFirstChar(methodName); 62 | sb.append( 63 | " public " + builderClassName + " " + methodName + "(" + argumentType + " value) {\n" + 64 | " object." + setMethodName + "(value);\n" + 65 | " return this;\n" + 66 | " }\n"); 67 | }); 68 | sb.append("}\n"); 69 | out.write(sb.toString()); 70 | } 71 | } 72 | 73 | private static String capitalFirstChar(String name) { 74 | char[] c = name.toCharArray(); 75 | c[0] = Character.toUpperCase(c[0]); 76 | return new String(c); 77 | } 78 | } -------------------------------------------------------------------------------- /jsr269/my-anno-processor-jar/src/main/java/me/ya/annotation/MyBuilder.java: -------------------------------------------------------------------------------- 1 | package me.ya.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target({ElementType.FIELD}) 9 | @Retention(RetentionPolicy.SOURCE) 10 | public @interface MyBuilder { 11 | } 12 | -------------------------------------------------------------------------------- /jsr269/my-anno-processor-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | my-anno-processor-test 8 | my-anno-processor-test 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | my-anno-processor-jar 14 | my-anno-processor-jar 15 | 1.0-SNAPSHOT 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 25 | 1.8 26 | 1.8 27 | 28 | 29 | me.ya.annotation.MyAnnotationProcessor 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /jsr269/my-anno-processor-test/src/main/java/me/ya/annotation/test/MyTestMain.java: -------------------------------------------------------------------------------- 1 | package me.ya.annotation.test; 2 | 3 | /** 4 | * Created By Arthur Zhang at 2019/9/14 5 | */ 6 | public class MyTestMain { 7 | private String hello; 8 | 9 | public static void main(String[] args) { 10 | User user = new me.ya.annotation.test.UserBuilder().id(1).name("hello").build(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /jsr269/my-anno-processor-test/src/main/java/me/ya/annotation/test/User.java: -------------------------------------------------------------------------------- 1 | package me.ya.annotation.test; 2 | 3 | import me.ya.annotation.MyBuilder; 4 | 5 | /** 6 | * Created By Arthur Zhang at 2019/9/14 7 | */ 8 | public class User { 9 | @MyBuilder 10 | private Integer id; 11 | @MyBuilder 12 | private String name; 13 | 14 | public void setId(Integer id) { 15 | this.id = id; 16 | } 17 | 18 | public void setName(String name) { 19 | this.name = name; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /jvmti-agent/Makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | JAVA_HOME = /Library/Java/JavaVirtualMachines/jdk1.8.0_51.jdk/Contents/Home 3 | 4 | ifeq ($(shell uname), Darwin) 5 | LIBLINK = -dynamiclib 6 | TARGET = myagent.dylib 7 | INCLUDEDIR := -I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/darwin/ 8 | endif 9 | 10 | ifeq ($(shell uname), Linux) 11 | LIBLINK = -shared -fPIC 12 | TARGET = myagent.so 13 | INCLUDEDIR := -I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/ 14 | endif 15 | 16 | all: 17 | $(CC) $(LIBLINK) $(INCLUDEDIR) agent.cpp -o $(TARGET) 18 | 19 | clean: 20 | rm $(TARGET) 21 | 22 | -------------------------------------------------------------------------------- /jvmti-agent/agent.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "jvmti.h" 7 | #include "jni.h" 8 | 9 | void JNICALL 10 | MyClassFileLoadHookHandler( 11 | jvmtiEnv *jvmti_env, 12 | JNIEnv *jni_env, 13 | jclass class_being_redefined, 14 | jobject loader, 15 | const char *name, 16 | jobject protection_domain, 17 | jint class_data_len, 18 | const unsigned char *class_data, 19 | jint *new_class_data_len, 20 | unsigned char **new_class_data) { 21 | printf("loading class: %s\n", name); 22 | 23 | if (strncmp(name, "me/ya/", 6) != 0) { 24 | return; 25 | } 26 | jvmtiError result; 27 | 28 | result = jvmti_env->Allocate(class_data_len, new_class_data); 29 | if (result != JVMTI_ERROR_NONE) return; 30 | 31 | *new_class_data_len = class_data_len; 32 | 33 | unsigned char *output_class_data = *new_class_data; 34 | for (int i = 0; i < class_data_len; ++i) { 35 | output_class_data[i] = class_data[i] ^ 0xFF; 36 | } 37 | } 38 | 39 | JNIEXPORT jint JNICALL 40 | Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { 41 | jvmtiEnv *jvmti_env; 42 | jvmtiError error; 43 | jvmtiEventCallbacks callbacks; 44 | // 获取 JVMTI environment 45 | jint ret = vm->GetEnv((void **) &jvmti_env, JVMTI_VERSION); 46 | if (ret != JNI_OK) { 47 | return ret; 48 | } 49 | //设置类加载事件回调 50 | (void) memset(&callbacks, 0, sizeof(callbacks)); 51 | callbacks.ClassFileLoadHook = &MyClassFileLoadHookHandler; 52 | error = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); 53 | if (error != JVMTI_ERROR_NONE) { 54 | return error; 55 | } 56 | //设置事件通知 57 | error = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, 58 | NULL); 59 | if (error != JVMTI_ERROR_NONE) { 60 | return error; 61 | } 62 | return JNI_OK; 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /jvmti-agent/agent2.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include "jvmti.h" 7 | #include "jni.h" 8 | 9 | void JNICALL 10 | MyClassFileLoadHookHandler( 11 | jvmtiEnv *jvmti_env, 12 | JNIEnv *jni_env, 13 | jclass class_being_redefined, 14 | jobject loader, 15 | const char *name, 16 | jobject protection_domain, 17 | jint class_data_len, 18 | const unsigned char *class_data, 19 | jint *new_class_data_len, 20 | unsigned char **new_class_data) { 21 | printf("loading class: %s\n", name); 22 | } 23 | 24 | JNIEXPORT jint JNICALL 25 | Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { 26 | jvmtiEnv *jvmti_env; 27 | jvmtiError error; 28 | jvmtiEventCallbacks callbacks; 29 | // 获取 JVMTI environment 30 | jint ret = vm->GetEnv((void **) &jvmti_env, JVMTI_VERSION); 31 | if (ret != JNI_OK) { 32 | return ret; 33 | } 34 | //设置类加载事件回调 35 | (void) memset(&callbacks, 0, sizeof(callbacks)); 36 | callbacks.ClassFileLoadHook = &MyClassFileLoadHookHandler; 37 | error = jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)); 38 | if (error != JVMTI_ERROR_NONE) { 39 | return error; 40 | } 41 | //设置事件通知 42 | error = jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, NULL); 43 | if (error != JVMTI_ERROR_NONE) { 44 | return error; 45 | } 46 | return JNI_OK; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /jvmti-encrypt/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | jvmti-encrypt 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | commons-io 14 | commons-io 15 | 2.6 16 | 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 25 | 8 26 | 8 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /jvmti-encrypt/src/main/java/Encrypt.java: -------------------------------------------------------------------------------- 1 | import org.apache.commons.io.IOUtils; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.util.Enumeration; 7 | import java.util.jar.JarEntry; 8 | import java.util.jar.JarFile; 9 | import java.util.jar.JarOutputStream; 10 | 11 | public class Encrypt { 12 | public static void main(String[] args) throws Exception { 13 | String jarPath = args[0]; 14 | File srcFile = new File(jarPath); 15 | File dstFile = new File("encrypt.jar"); 16 | 17 | JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(dstFile)); 18 | JarFile srcJarFile = new JarFile(srcFile); 19 | Enumeration entries = srcJarFile.entries(); 20 | while (entries.hasMoreElements()) { 21 | JarEntry entry = entries.nextElement(); 22 | addFileToJar(jarOutputStream, srcJarFile, entry); 23 | } 24 | jarOutputStream.close(); 25 | srcJarFile.close(); 26 | } 27 | 28 | private static void addFileToJar(JarOutputStream jarOutputStream, 29 | JarFile srcJarFile, JarEntry entry) throws IOException { 30 | String name = entry.getName(); 31 | jarOutputStream.putNextEntry(new JarEntry(name)); 32 | byte[] bytes = IOUtils.toByteArray(srcJarFile.getInputStream(entry)); 33 | // 只转换以 me/ya/ 包名开头的 class 文件 34 | if (name.startsWith("me/ya/") && name.endsWith(".class")) { 35 | System.out.println("encrypting " + name.replaceAll("/", ".")); 36 | bytes = encrypt(bytes); 37 | } 38 | jarOutputStream.write(bytes); 39 | jarOutputStream.closeEntry(); 40 | } 41 | 42 | private static byte[] encrypt(byte[] bytes) { 43 | byte[] decodedBytes = new byte[bytes.length]; 44 | for (int i = 0; i < bytes.length; i++) { 45 | decodedBytes[i] = (byte) (bytes[i] ^ 0xFF); 46 | } 47 | return decodedBytes; 48 | } 49 | } -------------------------------------------------------------------------------- /my-attach-demo-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | my-attach-demo-test 8 | my-attach-demo-test 9 | 1.0-SNAPSHOT 10 | 11 | 12 | -------------------------------------------------------------------------------- /my-attach-demo-test/src/main/java/MyAttachMain.java: -------------------------------------------------------------------------------- 1 | import com.sun.tools.attach.VirtualMachine; 2 | 3 | /** 4 | * Created By Arthur Zhang at 2019/9/4 5 | */ 6 | public class MyAttachMain { 7 | public static void main(String[] args) throws Exception { 8 | VirtualMachine vm = VirtualMachine.attach(args[0]); 9 | try { 10 | vm.loadAgent("/Users/arthur/cvt_dev/java/care/my-attach-demo/target/my-attach-agent.jar"); 11 | // vm.loadAgent("/Users/arthur/cvt_dev/java/care/my-attach-demo-test/src/main/java/my-attach-agent.jar"); 12 | System.in.read(); 13 | } finally { 14 | vm.detach(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /my-attach-demo-test/src/main/java/MyTestMain.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.TimeUnit; 2 | 3 | /** 4 | * Created By Arthur Zhang at 2019/9/4 5 | */ 6 | public class MyTestMain { 7 | public static void main(String[] args) throws InterruptedException { 8 | MyTestMain main = new MyTestMain(); 9 | while (true) { 10 | System.out.println(main.foo()); 11 | TimeUnit.SECONDS.sleep(3); 12 | } 13 | } 14 | 15 | public int foo() { 16 | return 100; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /my-attach-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | my-attach-demo 8 | my-attach-demo 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.ow2.asm 14 | asm 15 | 7.1 16 | 17 | 18 | org.ow2.asm 19 | asm-commons 20 | 7.1 21 | 22 | 23 | 24 | 25 | 26 | my-attach-agent 27 | 28 | 29 | 30 | org.apache.maven.plugins 31 | maven-jar-plugin 32 | 33 | 34 | 35 | me.ya.agent.AgentMain 36 | me.ya.agent.AgentMain 37 | true 38 | true 39 | 40 | 41 | 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-shade-plugin 46 | 3.2.1 47 | 48 | 49 | package 50 | 51 | 52 | shade 53 | 54 | 55 | 56 | 57 | 58 | org.ow2.asm 59 | me.ya.agent.hidden.org.ow2.asm 60 | 61 | 62 | org.objectweb.asm 63 | me.ya.agent.hidden.org.objectweb.asm 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-compiler-plugin 74 | 75 | 8 76 | 8 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /my-attach-demo/src/main/java/me/ya/agent/AgentMain.java: -------------------------------------------------------------------------------- 1 | package me.ya.agent; 2 | 3 | import org.objectweb.asm.ClassReader; 4 | import org.objectweb.asm.ClassVisitor; 5 | import org.objectweb.asm.ClassWriter; 6 | import org.objectweb.asm.MethodVisitor; 7 | import org.objectweb.asm.commons.AdviceAdapter; 8 | 9 | import java.lang.instrument.ClassFileTransformer; 10 | import java.lang.instrument.IllegalClassFormatException; 11 | import java.lang.instrument.Instrumentation; 12 | import java.lang.instrument.UnmodifiableClassException; 13 | import java.security.ProtectionDomain; 14 | 15 | import static org.objectweb.asm.Opcodes.ASM7; 16 | 17 | 18 | /** 19 | * Created By Arthur Zhang at 2019/9/4 20 | */ 21 | public class AgentMain { 22 | public static class MyMethodVisitor extends AdviceAdapter { 23 | protected MyMethodVisitor(MethodVisitor mv, int access, String name, String desc) { 24 | super(ASM7, mv, access, name, desc); 25 | } 26 | 27 | @Override 28 | protected void onMethodEnter() { 29 | // 在方法开始插入 return 50; 30 | mv.visitIntInsn(BIPUSH, 50); 31 | mv.visitInsn(IRETURN); 32 | } 33 | } 34 | 35 | public static class MyClassVisitor extends ClassVisitor { 36 | 37 | public MyClassVisitor(ClassVisitor classVisitor) { 38 | super(ASM7, classVisitor); 39 | } 40 | 41 | @Override 42 | public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { 43 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 44 | // 只转换 foo 方法 45 | if ("foo".equals(name)) { 46 | return new MyMethodVisitor(mv, access, name, descriptor); 47 | } 48 | return mv; 49 | } 50 | } 51 | 52 | public static class MyClassFileTransformer implements ClassFileTransformer { 53 | @Override 54 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, 55 | ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException { 56 | if (!"MyTestMain".equals(className)) return bytes; 57 | ClassReader cr = new ClassReader(bytes); 58 | ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); 59 | ClassVisitor cv = new MyClassVisitor(cw); 60 | cr.accept(cv, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); 61 | return cw.toByteArray(); 62 | } 63 | } 64 | 65 | public static void agentmain(String agentArgs, Instrumentation inst) throws ClassNotFoundException, UnmodifiableClassException { 66 | System.out.println("agentmain called"); 67 | inst.addTransformer(new MyClassFileTransformer(), true); 68 | Class classes[] = inst.getAllLoadedClasses(); 69 | for (int i = 0; i < classes.length; i++) { 70 | if (classes[i].getName().equals("MyTestMain")) { 71 | System.out.println("Reloading: " + classes[i].getName()); 72 | inst.retransformClasses(classes[i]); 73 | break; 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /my-trace-agent-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | my-trace-agent-test 8 | my-trace-agent-test 9 | 1.0-SNAPSHOT 10 | 11 | 12 | -------------------------------------------------------------------------------- /my-trace-agent-test/src/main/java/MyTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created By Arthur Zhang at 2019/9/9 3 | */ 4 | public class MyTest { 5 | public static void main(String[] args) { 6 | new MyTest().foo(); 7 | } 8 | public void foo() { 9 | bar1(); 10 | bar2(); 11 | } 12 | 13 | public void bar1() { 14 | } 15 | 16 | public void bar2() { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /my-trace-agent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | my-trace-agent 8 | my-trace-agent 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.ow2.asm 14 | asm 15 | 7.1 16 | 17 | 18 | org.ow2.asm 19 | asm-commons 20 | 7.1 21 | 22 | 23 | 24 | 25 | 26 | my-trace-agent 27 | 28 | 29 | 30 | org.apache.maven.plugins 31 | maven-jar-plugin 32 | 33 | 34 | 35 | me.ya.agent.AgentMain 36 | me.ya.agent.AgentMain 37 | true 38 | true 39 | 40 | 41 | 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-shade-plugin 46 | 3.2.1 47 | 48 | 49 | package 50 | 51 | 52 | shade 53 | 54 | 55 | 56 | 57 | 58 | org.ow2.asm 59 | me.ya.agent.hidden.org.ow2.asm 60 | 61 | 62 | org.objectweb.asm 63 | me.ya.agent.hidden.org.objectweb.asm 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-compiler-plugin 74 | 75 | 8 76 | 8 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /my-trace-agent/src/main/java/me/ya/agent/AgentMain.java: -------------------------------------------------------------------------------- 1 | package me.ya.agent; 2 | 3 | import org.objectweb.asm.ClassReader; 4 | import org.objectweb.asm.ClassVisitor; 5 | import org.objectweb.asm.ClassWriter; 6 | import org.objectweb.asm.MethodVisitor; 7 | import org.objectweb.asm.commons.AdviceAdapter; 8 | 9 | import java.lang.instrument.ClassFileTransformer; 10 | import java.lang.instrument.IllegalClassFormatException; 11 | import java.lang.instrument.Instrumentation; 12 | import java.lang.instrument.UnmodifiableClassException; 13 | import java.security.ProtectionDomain; 14 | 15 | import static org.objectweb.asm.Opcodes.ASM7; 16 | 17 | 18 | /** 19 | * Created By Arthur Zhang at 2019/9/4 20 | */ 21 | public class AgentMain { 22 | public static class MyMethodVisitor extends AdviceAdapter { 23 | 24 | protected MyMethodVisitor(MethodVisitor mv, int access, String name, String desc) { 25 | super(ASM7, mv, access, name, desc); 26 | } 27 | 28 | @Override 29 | protected void onMethodEnter() { 30 | mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 31 | mv.visitLdcInsn("<<>>exit " + this.getName()); 41 | mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); 42 | } 43 | } 44 | 45 | public static class MyClassVisitor extends ClassVisitor { 46 | 47 | public MyClassVisitor(ClassVisitor classVisitor) { 48 | super(ASM7, classVisitor); 49 | } 50 | 51 | @Override 52 | public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { 53 | MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); 54 | if (name.equals("")) return mv; 55 | return new MyMethodVisitor(mv, access, name, descriptor); 56 | } 57 | } 58 | 59 | public static class MyClassFileTransformer implements ClassFileTransformer { 60 | @Override 61 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, 62 | ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException { 63 | if (!"MyTest".equals(className)) return bytes; 64 | ClassReader cr = new ClassReader(bytes); 65 | ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); 66 | ClassVisitor cv = new MyClassVisitor(cw); 67 | cr.accept(cv, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); 68 | return cw.toByteArray(); 69 | } 70 | } 71 | 72 | public static void premain(String agentArgs, Instrumentation inst) throws ClassNotFoundException, UnmodifiableClassException { 73 | inst.addTransformer(new MyClassFileTransformer(), true); 74 | } 75 | } 76 | --------------------------------------------------------------------------------