├── .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 extends TypeElement> annotations, RoundEnvironment roundEnv) {
23 | for (TypeElement typeElement : annotations) {
24 | // typeElement 表示 MyBuilder 注解
25 | Set extends Element> 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 |
--------------------------------------------------------------------------------