├── DumperAnalyze.iml ├── src ├── test │ └── java │ │ └── Test.java └── main │ ├── resources │ └── META-INF │ │ └── MANIFEST.MF │ └── java │ └── com │ └── payloads │ └── online │ ├── DumperAgent.java │ └── DefineTransformer.java ├── config.properties ├── README.md ├── pom.xml └── .gitignore /DumperAnalyze.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/test/java/Test.java: -------------------------------------------------------------------------------- 1 | public class Test { 2 | 3 | public static void main(String[] args) { 4 | System.out.println("Hello"); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /config.properties: -------------------------------------------------------------------------------- 1 | # 需要插桩修改的类 2 | class= online.payloads.main.Main 3 | # 需要插桩修改的方法 4 | method=getMain 5 | # 在方法执行前想要执行的代码 (需要Base64编码) 6 | before_code=U3lzdGVtLm91dC5wcmludGxuKCJJbmplY3QiKTsK -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Premain-Class: com.payloads.online.DumperAgent 3 | Agent-Class: com.payloads.online.DumperAgent 4 | Can-Redefine-Classes: true 5 | -------------------------------------------------------------------------------- /src/main/java/com/payloads/online/DumperAgent.java: -------------------------------------------------------------------------------- 1 | package com.payloads.online; 2 | 3 | 4 | import java.io.IOException; 5 | import java.lang.instrument.Instrumentation; 6 | public class DumperAgent { 7 | 8 | public static void premain(String agentArgs, Instrumentation inst) throws IOException { 9 | if (agentArgs != null){ 10 | System.out.println(agentArgs); 11 | inst.addTransformer(new DefineTransformer(agentArgs)); 12 | } 13 | } 14 | 15 | public static void agentmain(String agentArgs, Instrumentation inst) { 16 | System.out.println("agentmain"); 17 | } 18 | 19 | public static void main(String[] args){ 20 | System.out.println("Please use the -javaagent option to load the jar package"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DumperAnalyze 2 | 3 | > 最近审计了一套产品代码,发现使用了JavaAgent+Javassist技术在类加载后进行替换一部分被加密的方法,感觉也可以通过这个技术应用到其他地方,因此写了这个小工具,原理比较简单。 4 | 5 | 通过JavaAgent与Javassist技术对JVM加载的类对象进行动态插桩,可以做一些破解、加密验证的绕过等操作 6 | 7 | 使用方法: 8 | 9 | ``` 10 | java -javaagent:Dumper-Analyze-1.0-SNAPSHOT-jar-with-dependencies.jar=./config.properties -jar SpringApp.jar 11 | ``` 12 | 13 | JavaAgent可以通过`=`传递参数,`./config.properties` 是配置文件。 14 | 15 | 16 | ### 配置文件 17 | 18 | 19 | ```properties 20 | # 需要插桩修改的类 21 | class=online.payloads.main.Main 22 | # 需要插桩修改的方法 23 | method=getMain 24 | # 在方法执行前想要执行的代码 (需要Base64编码) 25 | # System.out.println("Inject"); 26 | before_code=U3lzdGVtLm91dC5wcmludGxuKCJJbmplY3QiKTsK 27 | ``` 28 | 29 | 除了`before_code`还有如下几个方式: 30 | 31 | - before_code 在执行原方法之前插入字节码 32 | - after_code 在原方法执行完毕(return)之前插入字节码 33 | - body_code 覆盖原方法 34 | 35 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | Dumper-Analyze 8 | Dumper-Analyze 9 | 1.0-SNAPSHOT 10 | 11 | 12 | UTF-8 13 | UTF-8 14 | 1.8 15 | 1.8 16 | 1.8 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-assembly-plugin 24 | 25 | 26 | jar-with-dependencies 27 | 28 | 29 | 30 | com.payloads.online.DumperAgent 31 | com.payloads.online.DumperAgent 32 | com.payloads.online.DumperAgent 33 | true 34 | true 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | attached 43 | 44 | package 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.javassist 54 | javassist 55 | 3.26.0-GA 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/**/usage.statistics.xml 10 | .idea/**/dictionaries 11 | .idea/**/shelf 12 | 13 | # Generated files 14 | .idea/**/contentModel.xml 15 | 16 | # Sensitive or high-churn files 17 | .idea/**/dataSources/ 18 | .idea/**/dataSources.ids 19 | .idea/**/dataSources.local.xml 20 | .idea/**/sqlDataSources.xml 21 | .idea/**/dynamic.xml 22 | .idea/**/uiDesigner.xml 23 | .idea/**/dbnavigator.xml 24 | 25 | # Gradle 26 | .idea/**/gradle.xml 27 | .idea/**/libraries 28 | 29 | # Gradle and Maven with auto-import 30 | # When using Gradle or Maven with auto-import, you should exclude module files, 31 | # since they will be recreated, and may cause churn. Uncomment if using 32 | # auto-import. 33 | # .idea/modules.xml 34 | # .idea/*.iml 35 | # .idea/modules 36 | # *.iml 37 | # *.ipr 38 | 39 | # CMake 40 | cmake-build-*/ 41 | 42 | # Mongo Explorer plugin 43 | .idea/**/mongoSettings.xml 44 | 45 | # File-based project format 46 | *.iws 47 | 48 | # IntelliJ 49 | out/ 50 | 51 | # mpeltonen/sbt-idea plugin 52 | .idea_modules/ 53 | 54 | # JIRA plugin 55 | atlassian-ide-plugin.xml 56 | 57 | # Cursive Clojure plugin 58 | .idea/replstate.xml 59 | 60 | # Crashlytics plugin (for Android Studio and IntelliJ) 61 | com_crashlytics_export_strings.xml 62 | crashlytics.properties 63 | crashlytics-build.properties 64 | fabric.properties 65 | 66 | # Editor-based Rest Client 67 | .idea/httpRequests 68 | 69 | # Android studio 3.1+ serialized cache file 70 | .idea/caches/build_file_checksums.ser 71 | 72 | ### Maven template 73 | target/ 74 | pom.xml.tag 75 | pom.xml.releaseBackup 76 | pom.xml.versionsBackup 77 | pom.xml.next 78 | release.properties 79 | dependency-reduced-pom.xml 80 | buildNumber.properties 81 | .mvn/timing.properties 82 | .mvn/wrapper/maven-wrapper.jar 83 | 84 | ### Java template 85 | # Compiled class file 86 | *.class 87 | 88 | # Log file 89 | *.log 90 | 91 | # BlueJ files 92 | *.ctxt 93 | 94 | # Mobile Tools for Java (J2ME) 95 | .mtj.tmp/ 96 | 97 | # Package Files # 98 | *.jar 99 | *.war 100 | *.nar 101 | *.ear 102 | *.zip 103 | *.tar.gz 104 | *.rar 105 | 106 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 107 | hs_err_pid* 108 | 109 | .DS_Store 110 | .idea 111 | -------------------------------------------------------------------------------- /src/main/java/com/payloads/online/DefineTransformer.java: -------------------------------------------------------------------------------- 1 | package com.payloads.online; 2 | import javassist.ClassPool; 3 | import javassist.CtClass; 4 | import javassist.CtMethod; 5 | import javassist.NotFoundException; 6 | import java.util.Base64; 7 | import java.io.IOException; 8 | import java.util.Properties; 9 | import java.io.BufferedInputStream; 10 | import java.io.FileInputStream; 11 | import java.io.InputStream; 12 | import java.lang.instrument.ClassFileTransformer; 13 | import java.lang.instrument.IllegalClassFormatException; 14 | import java.security.ProtectionDomain; 15 | 16 | 17 | public class DefineTransformer implements ClassFileTransformer { 18 | 19 | private Properties propertiesLoader; 20 | 21 | public DefineTransformer(String configPath) throws IOException { 22 | InputStream in = new BufferedInputStream(new FileInputStream(configPath)); 23 | propertiesLoader = new Properties(); 24 | propertiesLoader.load(in); 25 | } 26 | 27 | private String getProperties(String key){ 28 | String value = propertiesLoader.getProperty(key); 29 | if (value == null){ 30 | return new String(); 31 | } 32 | return value; 33 | } 34 | 35 | private String base64decode(String str) { 36 | byte[] decodedBytes = Base64.getDecoder().decode(str); 37 | return new String(decodedBytes); 38 | } 39 | 40 | 41 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 42 | System.out.println("加载类:" + className); 43 | String packageName = className.replaceAll("/","."); 44 | 45 | if (!packageName.equals(getProperties("class"))){ 46 | return null; 47 | } 48 | 49 | ClassPool classPool = ClassPool.getDefault(); 50 | System.out.println("创建 ClassPool ... 处理类 : " + className); 51 | 52 | try { 53 | CtClass clazz = classPool.get(packageName); 54 | CtMethod[] Methods = clazz.getDeclaredMethods(); 55 | 56 | // 获取方法名称 57 | String methodName = getProperties("method"); 58 | if (methodName == null){ 59 | System.out.println("获取不到方法名,请检查配置文件"); 60 | return null; 61 | } 62 | 63 | // 遍历所有的对象方法 64 | for (CtMethod method : Methods){ 65 | if (methodName.equals(method.getName())){ 66 | String afterCode = getProperties("after_code"); 67 | String bodyCode = getProperties("body_code"); 68 | String beforeCode = getProperties("before_code"); 69 | System.out.println("处理方法:" + method.getName()); 70 | 71 | // 在执行原方法之前插入字节码 72 | if (beforeCode.length() > 0){ 73 | method.insertBefore(this.base64decode(beforeCode)); 74 | } 75 | // 在原方法执行完毕之前插入字节码 76 | if (afterCode.length() > 0){ 77 | method.insertAfter(this.base64decode(afterCode)); 78 | } 79 | // 覆盖原方法 80 | if (bodyCode.length() > 0){ 81 | method.setBody(this.base64decode(bodyCode)); 82 | } 83 | 84 | break; 85 | } 86 | } 87 | byte[] byteCode = clazz.toBytecode(); 88 | clazz.detach(); 89 | return byteCode; 90 | } catch (Exception e) { 91 | e.printStackTrace(); 92 | return null; 93 | } 94 | } 95 | } 96 | 97 | --------------------------------------------------------------------------------